summaryrefslogtreecommitdiff
path: root/indra/llui
diff options
context:
space:
mode:
authorAnsariel <ansariel.hiller@phoenixviewer.com>2024-05-22 19:04:52 +0200
committerAnsariel <ansariel.hiller@phoenixviewer.com>2024-05-22 19:04:52 +0200
commit1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch)
treeab243607f74f78200787bba5b9b88f07ef1b966f /indra/llui
parent6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff)
parente1623bb276f83a43ce7a197e388720c05bdefe61 (diff)
Merge remote-tracking branch 'origin/main' into DRTVWR-600-maint-A
# Conflicts: # autobuild.xml # indra/cmake/CMakeLists.txt # indra/cmake/GoogleMock.cmake # indra/llaudio/llaudioengine_fmodstudio.cpp # indra/llaudio/llaudioengine_fmodstudio.h # indra/llaudio/lllistener_fmodstudio.cpp # indra/llaudio/lllistener_fmodstudio.h # indra/llaudio/llstreamingaudio_fmodstudio.cpp # indra/llaudio/llstreamingaudio_fmodstudio.h # indra/llcharacter/llmultigesture.cpp # indra/llcharacter/llmultigesture.h # indra/llimage/llimage.cpp # indra/llimage/llimagepng.cpp # indra/llimage/llimageworker.cpp # indra/llimage/tests/llimageworker_test.cpp # indra/llmessage/tests/llmockhttpclient.h # indra/llprimitive/llgltfmaterial.h # indra/llrender/llfontfreetype.cpp # indra/llui/llcombobox.cpp # indra/llui/llfolderview.cpp # indra/llui/llfolderviewmodel.h # indra/llui/lllineeditor.cpp # indra/llui/lllineeditor.h # indra/llui/lltextbase.cpp # indra/llui/lltextbase.h # indra/llui/lltexteditor.cpp # indra/llui/lltextvalidate.cpp # indra/llui/lltextvalidate.h # indra/llui/lluictrl.h # indra/llui/llview.cpp # indra/llwindow/llwindowmacosx.cpp # indra/newview/app_settings/settings.xml # indra/newview/llappearancemgr.cpp # indra/newview/llappearancemgr.h # indra/newview/llavatarpropertiesprocessor.cpp # indra/newview/llavatarpropertiesprocessor.h # indra/newview/llbreadcrumbview.cpp # indra/newview/llbreadcrumbview.h # indra/newview/llbreastmotion.cpp # indra/newview/llbreastmotion.h # indra/newview/llconversationmodel.h # indra/newview/lldensityctrl.cpp # indra/newview/lldensityctrl.h # indra/newview/llface.inl # indra/newview/llfloatereditsky.cpp # indra/newview/llfloatereditwater.cpp # indra/newview/llfloateremojipicker.h # indra/newview/llfloaterimsessiontab.cpp # indra/newview/llfloaterprofiletexture.cpp # indra/newview/llfloaterprofiletexture.h # indra/newview/llgesturemgr.cpp # indra/newview/llgesturemgr.h # indra/newview/llimpanel.cpp # indra/newview/llimpanel.h # indra/newview/llinventorybridge.cpp # indra/newview/llinventorybridge.h # indra/newview/llinventoryclipboard.cpp # indra/newview/llinventoryclipboard.h # indra/newview/llinventoryfunctions.cpp # indra/newview/llinventoryfunctions.h # indra/newview/llinventorygallery.cpp # indra/newview/lllistbrowser.cpp # indra/newview/lllistbrowser.h # indra/newview/llpanelobjectinventory.cpp # indra/newview/llpanelprofile.cpp # indra/newview/llpanelprofile.h # indra/newview/llpreviewgesture.cpp # indra/newview/llsavedsettingsglue.cpp # indra/newview/llsavedsettingsglue.h # indra/newview/lltooldraganddrop.cpp # indra/newview/llurllineeditorctrl.cpp # indra/newview/llvectorperfoptions.cpp # indra/newview/llvectorperfoptions.h # indra/newview/llviewerparceloverlay.cpp # indra/newview/llviewertexlayer.cpp # indra/newview/llviewertexturelist.cpp # indra/newview/macmain.h # indra/test/test.cpp
Diffstat (limited to 'indra/llui')
-rw-r--r--indra/llui/llaccordionctrl.cpp1893
-rw-r--r--indra/llui/llaccordionctrl.h398
-rw-r--r--indra/llui/llaccordionctrltab.cpp2252
-rw-r--r--indra/llui/llaccordionctrltab.h502
-rw-r--r--indra/llui/llbadge.cpp778
-rw-r--r--indra/llui/llbadge.h354
-rw-r--r--indra/llui/llbadgeholder.cpp24
-rw-r--r--indra/llui/llbadgeholder.h26
-rw-r--r--indra/llui/llbadgeowner.cpp122
-rw-r--r--indra/llui/llbadgeowner.h34
-rw-r--r--indra/llui/llbutton.cpp2641
-rw-r--r--indra/llui/llbutton.h812
-rw-r--r--indra/llui/llcallbackmap.h48
-rw-r--r--indra/llui/llchat.h224
-rw-r--r--indra/llui/llchatentry.cpp502
-rw-r--r--indra/llui/llchatentry.h212
-rw-r--r--indra/llui/llcheckboxctrl.cpp608
-rw-r--r--indra/llui/llcheckboxctrl.h314
-rw-r--r--indra/llui/llclipboard.cpp132
-rw-r--r--indra/llui/llclipboard.h84
-rw-r--r--indra/llui/llcombobox.cpp2465
-rw-r--r--indra/llui/llcombobox.h568
-rw-r--r--indra/llui/llcommandmanager.cpp174
-rw-r--r--indra/llui/llcommandmanager.h250
-rw-r--r--indra/llui/llconsole.cpp806
-rw-r--r--indra/llui/llconsole.h312
-rw-r--r--indra/llui/llcontainerview.cpp600
-rw-r--r--indra/llui/llcontainerview.h188
-rw-r--r--indra/llui/llctrlselectioninterface.cpp126
-rw-r--r--indra/llui/llctrlselectioninterface.h208
-rw-r--r--indra/llui/lldockablefloater.cpp526
-rw-r--r--indra/llui/lldockablefloater.h304
-rw-r--r--indra/llui/lldockcontrol.cpp624
-rw-r--r--indra/llui/lldockcontrol.h94
-rw-r--r--indra/llui/lldraghandle.cpp774
-rw-r--r--indra/llui/lldraghandle.h278
-rw-r--r--indra/llui/lleditmenuhandler.cpp18
-rw-r--r--indra/llui/lleditmenuhandler.h142
-rw-r--r--indra/llui/llemojihelper.cpp178
-rw-r--r--indra/llui/llemojihelper.h40
-rw-r--r--indra/llui/llf32uictrl.cpp20
-rw-r--r--indra/llui/llf32uictrl.h58
-rw-r--r--indra/llui/llfiltereditor.cpp92
-rw-r--r--indra/llui/llfiltereditor.h36
-rw-r--r--indra/llui/llflashtimer.cpp196
-rw-r--r--indra/llui/llflashtimer.h148
-rw-r--r--indra/llui/llflatlistview.cpp2878
-rw-r--r--indra/llui/llflatlistview.h1070
-rw-r--r--indra/llui/llfloater.cpp7470
-rw-r--r--indra/llui/llfloater.h1278
-rw-r--r--indra/llui/llfloaterreg.cpp1218
-rw-r--r--indra/llui/llfloaterreg.h316
-rw-r--r--indra/llui/llfloaterreglistener.cpp10
-rw-r--r--indra/llui/llfloaterreglistener.h10
-rw-r--r--indra/llui/llflyoutbutton.cpp154
-rw-r--r--indra/llui/llflyoutbutton.h136
-rw-r--r--indra/llui/llfocusmgr.cpp1018
-rw-r--r--indra/llui/llfocusmgr.h320
-rw-r--r--indra/llui/llfolderview.cpp4245
-rw-r--r--indra/llui/llfolderview.h894
-rw-r--r--indra/llui/llfolderviewitem.cpp4738
-rw-r--r--indra/llui/llfolderviewitem.h996
-rw-r--r--indra/llui/llfolderviewmodel.cpp38
-rw-r--r--indra/llui/llfolderviewmodel.h948
-rw-r--r--indra/llui/llfunctorregistry.h138
-rw-r--r--indra/llui/llhelp.h26
-rw-r--r--indra/llui/lliconctrl.cpp346
-rw-r--r--indra/llui/lliconctrl.h214
-rw-r--r--indra/llui/llkeywords.cpp1626
-rw-r--r--indra/llui/llkeywords.h266
-rw-r--r--indra/llui/lllayoutstack.cpp2044
-rw-r--r--indra/llui/lllayoutstack.h432
-rw-r--r--indra/llui/lllazyvalue.h78
-rw-r--r--indra/llui/lllineeditor.cpp5454
-rw-r--r--indra/llui/lllineeditor.h942
-rw-r--r--indra/llui/llloadingindicator.cpp80
-rw-r--r--indra/llui/llloadingindicator.h88
-rw-r--r--indra/llui/lllocalcliprect.cpp220
-rw-r--r--indra/llui/lllocalcliprect.h126
-rw-r--r--indra/llui/llmenubutton.cpp492
-rw-r--r--indra/llui/llmenubutton.h202
-rw-r--r--indra/llui/llmenugl.cpp8809
-rw-r--r--indra/llui/llmenugl.h1956
-rw-r--r--indra/llui/llmodaldialog.cpp695
-rw-r--r--indra/llui/llmodaldialog.h166
-rw-r--r--indra/llui/llmultifloater.cpp1044
-rw-r--r--indra/llui/llmultifloater.h204
-rw-r--r--indra/llui/llmultislider.cpp1760
-rw-r--r--indra/llui/llmultislider.h322
-rw-r--r--indra/llui/llmultisliderctrl.cpp1050
-rw-r--r--indra/llui/llmultisliderctrl.h348
-rw-r--r--indra/llui/llnotificationptr.h8
-rw-r--r--indra/llui/llnotifications.cpp4012
-rw-r--r--indra/llui/llnotifications.h2265
-rw-r--r--indra/llui/llnotificationslistener.cpp80
-rw-r--r--indra/llui/llnotificationslistener.h18
-rw-r--r--indra/llui/llnotificationsutil.cpp82
-rw-r--r--indra/llui/llnotificationsutil.h52
-rw-r--r--indra/llui/llnotificationtemplate.h498
-rw-r--r--indra/llui/llnotificationvisibilityrule.h114
-rw-r--r--indra/llui/llpanel.cpp1750
-rw-r--r--indra/llui/llpanel.h636
-rw-r--r--indra/llui/llprogressbar.cpp42
-rw-r--r--indra/llui/llprogressbar.h48
-rw-r--r--indra/llui/llradiogroup.cpp1042
-rw-r--r--indra/llui/llradiogroup.h228
-rw-r--r--indra/llui/llresizebar.cpp752
-rw-r--r--indra/llui/llresizebar.h176
-rw-r--r--indra/llui/llresizehandle.cpp770
-rw-r--r--indra/llui/llresizehandle.h158
-rw-r--r--indra/llui/llresmgr.cpp534
-rw-r--r--indra/llui/llresmgr.h46
-rw-r--r--indra/llui/llrngwriter.cpp516
-rw-r--r--indra/llui/llrngwriter.h52
-rw-r--r--indra/llui/llscrollbar.cpp1332
-rw-r--r--indra/llui/llscrollbar.h334
-rw-r--r--indra/llui/llscrollcontainer.cpp1604
-rw-r--r--indra/llui/llscrollcontainer.h314
-rw-r--r--indra/llui/llscrollingpanellist.cpp504
-rw-r--r--indra/llui/llscrollingpanellist.h204
-rw-r--r--indra/llui/llscrolllistcell.cpp1342
-rw-r--r--indra/llui/llscrolllistcell.h560
-rw-r--r--indra/llui/llscrolllistcolumn.cpp680
-rw-r--r--indra/llui/llscrolllistcolumn.h344
-rw-r--r--indra/llui/llscrolllistctrl.cpp6896
-rw-r--r--indra/llui/llscrolllistctrl.h1128
-rw-r--r--indra/llui/llscrolllistitem.cpp408
-rw-r--r--indra/llui/llscrolllistitem.h284
-rw-r--r--indra/llui/llsearchablecontrol.h62
-rw-r--r--indra/llui/llsearcheditor.cpp436
-rw-r--r--indra/llui/llsearcheditor.h228
-rw-r--r--indra/llui/llslider.cpp766
-rw-r--r--indra/llui/llslider.h216
-rw-r--r--indra/llui/llsliderctrl.cpp978
-rw-r--r--indra/llui/llsliderctrl.h352
-rw-r--r--indra/llui/llspellcheck.cpp704
-rw-r--r--indra/llui/llspellcheck.h82
-rw-r--r--indra/llui/llspellcheckmenuhandler.h26
-rw-r--r--indra/llui/llspinctrl.cpp1008
-rw-r--r--indra/llui/llspinctrl.h248
-rw-r--r--indra/llui/llstatbar.cpp1432
-rw-r--r--indra/llui/llstatbar.h238
-rw-r--r--indra/llui/llstatgraph.cpp252
-rw-r--r--indra/llui/llstatgraph.h282
-rw-r--r--indra/llui/llstatview.cpp128
-rw-r--r--indra/llui/llstatview.h44
-rw-r--r--indra/llui/llstyle.cpp208
-rw-r--r--indra/llui/llstyle.h236
-rw-r--r--indra/llui/lltabcontainer.cpp4407
-rw-r--r--indra/llui/lltabcontainer.h656
-rw-r--r--indra/llui/lltextbase.cpp7790
-rw-r--r--indra/llui/lltextbase.h1512
-rw-r--r--indra/llui/lltextbox.cpp366
-rw-r--r--indra/llui/lltextbox.h174
-rw-r--r--indra/llui/lltexteditor.cpp6127
-rw-r--r--indra/llui/lltexteditor.h702
-rw-r--r--indra/llui/lltextparser.cpp478
-rw-r--r--indra/llui/lltextparser.h36
-rw-r--r--indra/llui/lltextutil.cpp98
-rw-r--r--indra/llui/lltextutil.h84
-rw-r--r--indra/llui/lltextvalidate.cpp764
-rw-r--r--indra/llui/lltextvalidate.h83
-rw-r--r--indra/llui/lltimectrl.cpp887
-rw-r--r--indra/llui/lltimectrl.h260
-rw-r--r--indra/llui/lltoggleablemenu.cpp216
-rw-r--r--indra/llui/lltoggleablemenu.h142
-rw-r--r--indra/llui/lltoolbar.cpp2533
-rw-r--r--indra/llui/lltoolbar.h662
-rw-r--r--indra/llui/lltooltip.cpp1296
-rw-r--r--indra/llui/lltooltip.h370
-rw-r--r--indra/llui/lltrans.cpp506
-rw-r--r--indra/llui/lltrans.h160
-rw-r--r--indra/llui/lltransutil.cpp148
-rw-r--r--indra/llui/lltransutil.h24
-rw-r--r--indra/llui/llui.cpp1476
-rw-r--r--indra/llui/llui.h708
-rw-r--r--indra/llui/lluicolor.cpp44
-rw-r--r--indra/llui/lluicolor.h48
-rw-r--r--indra/llui/lluicolortable.cpp476
-rw-r--r--indra/llui/lluicolortable.h94
-rw-r--r--indra/llui/lluiconstants.h20
-rw-r--r--indra/llui/lluictrl.cpp2274
-rw-r--r--indra/llui/lluictrl.h680
-rw-r--r--indra/llui/lluictrlfactory.cpp558
-rw-r--r--indra/llui/lluictrlfactory.h424
-rw-r--r--indra/llui/lluifwd.h8
-rw-r--r--indra/llui/lluistring.cpp136
-rw-r--r--indra/llui/lluistring.h94
-rw-r--r--indra/llui/lluiusage.cpp124
-rw-r--r--indra/llui/lluiusage.h30
-rw-r--r--indra/llui/llundo.cpp348
-rw-r--r--indra/llui/llundo.h136
-rw-r--r--indra/llui/llurlaction.cpp234
-rw-r--r--indra/llui/llurlaction.h92
-rw-r--r--indra/llui/llurlentry.cpp3476
-rw-r--r--indra/llui/llurlentry.h1172
-rw-r--r--indra/llui/llurlmatch.cpp66
-rw-r--r--indra/llui/llurlmatch.h106
-rw-r--r--indra/llui/llurlregistry.cpp460
-rw-r--r--indra/llui/llurlregistry.h68
-rw-r--r--indra/llui/llview.cpp5736
-rw-r--r--indra/llui/llview.h1472
-rw-r--r--indra/llui/llviewborder.cpp540
-rw-r--r--indra/llui/llviewborder.h220
-rw-r--r--indra/llui/llviewereventrecorder.cpp592
-rw-r--r--indra/llui/llviewinject.cpp2
-rw-r--r--indra/llui/llviewinject.h2
-rw-r--r--indra/llui/llviewmodel.cpp61
-rw-r--r--indra/llui/llviewmodel.h140
-rw-r--r--indra/llui/llviewquery.cpp272
-rw-r--r--indra/llui/llviewquery.h274
-rw-r--r--indra/llui/llvirtualtrackball.cpp1040
-rw-r--r--indra/llui/llvirtualtrackball.h326
-rw-r--r--indra/llui/llwindowshade.cpp576
-rw-r--r--indra/llui/llwindowshade.h70
-rw-r--r--indra/llui/llxuiparser.cpp3530
-rw-r--r--indra/llui/llxuiparser.h304
-rw-r--r--indra/llui/llxyvector.h244
-rwxr-xr-xindra/llui/tests/llurlentry_stub.cpp136
-rw-r--r--indra/llui/tests/llurlentry_test.cpp1870
-rw-r--r--indra/llui/tests/llurlmatch_test.cpp400
221 files changed, 88865 insertions, 88564 deletions
diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp
index f075aa564e..c40e78233d 100644
--- a/indra/llui/llaccordionctrl.cpp
+++ b/indra/llui/llaccordionctrl.cpp
@@ -1,938 +1,955 @@
-/**
- * @file llaccordionctrl.cpp
- * @brief Accordion panel implementation
- *
- * $LicenseInfo:firstyear=2009&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-#include "linden_common.h"
-
-#include "llaccordionctrl.h"
-#include "llaccordionctrltab.h"
-
-#include "lluictrlfactory.h" // builds floaters from XML
-
-#include "llwindow.h"
-#include "llfocusmgr.h"
-#include "lllocalcliprect.h"
-
-#include "boost/bind.hpp"
-
-static const S32 BORDER_MARGIN = 2;
-static const S32 PARENT_BORDER_MARGIN = 5;
-static const S32 VERTICAL_MULTIPLE = 16;
-static const F32 MIN_AUTO_SCROLL_RATE = 120.f;
-static const F32 MAX_AUTO_SCROLL_RATE = 500.f;
-static const F32 AUTO_SCROLL_RATE_ACCEL = 120.f;
-
-// LLAccordionCtrl =================================================================|
-
-static LLDefaultChildRegistry::Register<LLAccordionCtrl> t2("accordion");
-
-LLAccordionCtrl::LLAccordionCtrl(const Params& params):LLPanel(params)
- , mFitParent(params.fit_parent)
- , mAutoScrolling( false )
- , mAutoScrollRate( 0.f )
- , mSelectedTab( NULL )
- , mTabComparator( NULL )
- , mNoVisibleTabsHelpText(NULL)
- , mNoVisibleTabsOrigString(params.no_visible_tabs_text.initial_value().asString())
- , mSkipScrollToChild(false)
-{
- initNoTabsWidget(params.no_matched_tabs_text);
-
- mSingleExpansion = params.single_expansion;
- if (mFitParent && !mSingleExpansion)
- {
- LL_INFOS() << "fit_parent works best when combined with single_expansion" << LL_ENDL;
- }
-}
-
-LLAccordionCtrl::LLAccordionCtrl() : LLPanel()
- , mAutoScrolling( false )
- , mAutoScrollRate( 0.f )
- , mSelectedTab( NULL )
- , mNoVisibleTabsHelpText(NULL)
-{
- initNoTabsWidget(LLTextBox::Params());
-
- mSingleExpansion = false;
- mFitParent = false;
- buildFromFile( "accordion_parent.xml");
-}
-
-//---------------------------------------------------------------------------------
-void LLAccordionCtrl::draw()
-{
- if (mAutoScrolling)
- {
- // add acceleration to autoscroll
- mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), MAX_AUTO_SCROLL_RATE);
- }
- else
- {
- // reset to minimum for next time
- mAutoScrollRate = MIN_AUTO_SCROLL_RATE;
- }
- // clear this flag to be set on next call to autoScroll
- mAutoScrolling = false;
-
- LLRect local_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
-
- LLLocalClipRect clip(local_rect);
-
- LLPanel::draw();
-}
-
-//---------------------------------------------------------------------------------
-bool LLAccordionCtrl::postBuild()
-{
- static LLUICachedControl<S32> scrollbar_size("UIScrollbarSize", 0);
-
- LLRect scroll_rect;
- scroll_rect.setOriginAndSize(
- getRect().getWidth() - scrollbar_size,
- 1,
- scrollbar_size,
- getRect().getHeight() - 1);
-
- LLScrollbar::Params sbparams;
- sbparams.name("scrollable vertical");
- sbparams.rect(scroll_rect);
- sbparams.orientation(LLScrollbar::VERTICAL);
- sbparams.doc_size(mInnerRect.getHeight());
- sbparams.doc_pos(0);
- sbparams.page_size(mInnerRect.getHeight());
- sbparams.step_size(VERTICAL_MULTIPLE);
- sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
- sbparams.change_callback(boost::bind(&LLAccordionCtrl::onScrollPosChangeCallback, this, _1, _2));
-
- mScrollbar = LLUICtrlFactory::create<LLScrollbar>(sbparams);
- LLView::addChild(mScrollbar);
- mScrollbar->setVisible(false);
- mScrollbar->setFollowsRight();
- mScrollbar->setFollowsTop();
- mScrollbar->setFollowsBottom();
-
- //if it was created from xml...
- std::vector<LLUICtrl*> accordion_tabs;
- for (child_list_const_iter_t it = getChildList()->begin();
- getChildList()->end() != it; ++it)
- {
- LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(*it);
- if (accordion_tab == NULL)
- continue;
- if (std::find(mAccordionTabs.begin(), mAccordionTabs.end(), accordion_tab) == mAccordionTabs.end())
- {
- accordion_tabs.push_back(accordion_tab);
- }
- }
-
- for (std::vector<LLUICtrl*>::reverse_iterator it = accordion_tabs.rbegin();
- it < accordion_tabs.rend(); ++it)
- {
- addCollapsibleCtrl(*it);
- }
-
- arrange();
-
- if (mSingleExpansion)
- {
- if (!mAccordionTabs[0]->getDisplayChildren())
- mAccordionTabs[0]->setDisplayChildren(true);
- for (size_t i = 1; i < mAccordionTabs.size(); ++i)
- {
- if (mAccordionTabs[i]->getDisplayChildren())
- mAccordionTabs[i]->setDisplayChildren(false);
- }
- }
-
- updateNoTabsHelpTextVisibility();
-
- return true;
-}
-
-
-//---------------------------------------------------------------------------------
-LLAccordionCtrl::~LLAccordionCtrl()
-{
- mAccordionTabs.clear();
-}
-
-//---------------------------------------------------------------------------------
-
-void LLAccordionCtrl::reshape(S32 width, S32 height, bool called_from_parent)
-{
- // adjust our rectangle
- LLRect rcLocal = getRect();
- rcLocal.mRight = rcLocal.mLeft + width;
- rcLocal.mTop = rcLocal.mBottom + height;
-
- // get textbox a chance to reshape its content
- mNoVisibleTabsHelpText->reshape(width, height, called_from_parent);
-
- setRect(rcLocal);
-
- // assume that help text is always fit accordion.
- // necessary text paddings can be set via h_pad and v_pad
- mNoVisibleTabsHelpText->setRect(getLocalRect());
-
- arrange();
-}
-
-//---------------------------------------------------------------------------------
-bool LLAccordionCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- return LLPanel::handleRightMouseDown(x, y, mask);
-}
-
-//---------------------------------------------------------------------------------
-void LLAccordionCtrl::shiftAccordionTabs(S16 panel_num, S32 delta)
-{
- for (size_t i = panel_num; i < mAccordionTabs.size(); ++i)
- {
- ctrlShiftVertical(mAccordionTabs[i],delta);
- }
-}
-
-//---------------------------------------------------------------------------------
-void LLAccordionCtrl::onCollapseCtrlCloseOpen(S16 panel_num)
-{
- if (mSingleExpansion)
- {
- for (size_t i = 0; i < mAccordionTabs.size(); ++i)
- {
- if (i == panel_num)
- continue;
- if (mAccordionTabs[i]->getDisplayChildren())
- mAccordionTabs[i]->setDisplayChildren(false);
- }
-
- }
- arrange();
-}
-
-void LLAccordionCtrl::show_hide_scrollbar(S32 width, S32 height)
-{
- calcRecuiredHeight();
- if (getRecuiredHeight() > height)
- showScrollbar(width, height);
- else
- hideScrollbar(width, height);
-}
-
-void LLAccordionCtrl::showScrollbar(S32 width, S32 height)
-{
- bool was_visible = mScrollbar->getVisible();
-
- mScrollbar->setVisible(true);
-
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
-
- ctrlSetLeftTopAndSize(mScrollbar
- , width - scrollbar_size - PARENT_BORDER_MARGIN / 2
- , height - PARENT_BORDER_MARGIN
- , scrollbar_size
- , height - PARENT_BORDER_MARGIN * 2);
-
- mScrollbar->setPageSize(height);
- mScrollbar->setDocParams(mInnerRect.getHeight(), mScrollbar->getDocPos());
-
- if (was_visible)
- {
- S32 scroll_pos = llmin(mScrollbar->getDocPos(), getRecuiredHeight() - height - 1);
- mScrollbar->setDocPos(scroll_pos);
- }
-}
-
-void LLAccordionCtrl::hideScrollbar(S32 width, S32 height)
-{
- if (!mScrollbar->getVisible())
- return;
- mScrollbar->setVisible(false);
-
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
-
- S32 panel_width = width - 2*BORDER_MARGIN;
-
- // Reshape all accordions and shift all draggers
- for (size_t i = 0; i < mAccordionTabs.size(); ++i)
- {
- LLRect panel_rect = mAccordionTabs[i]->getRect();
- ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_rect.mLeft, panel_rect.mTop, panel_width, panel_rect.getHeight());
- }
-
- mScrollbar->setDocPos(0);
-
- if (!mAccordionTabs.empty())
- {
- S32 panel_top = height - BORDER_MARGIN; // Top coordinate of the first panel
- S32 diff = panel_top - mAccordionTabs[0]->getRect().mTop;
- shiftAccordionTabs(0, diff);
- }
-}
-
-//---------------------------------------------------------------------------------
-S32 LLAccordionCtrl::calcRecuiredHeight()
-{
- S32 rec_height = 0;
-
- std::vector<LLAccordionCtrlTab*>::iterator panel;
- for(panel=mAccordionTabs.begin(); panel!=mAccordionTabs.end(); ++panel)
- {
- LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(*panel);
- if(accordion_tab && accordion_tab->getVisible())
- {
- rec_height += accordion_tab->getRect().getHeight();
- }
- }
-
- mInnerRect.setLeftTopAndSize(0, rec_height + BORDER_MARGIN * 2, getRect().getWidth(), rec_height + BORDER_MARGIN);
-
- return mInnerRect.getHeight();
-}
-
-//---------------------------------------------------------------------------------
-void LLAccordionCtrl::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height)
-{
- if (!panel)
- return;
- LLRect panel_rect = panel->getRect();
- panel_rect.setLeftTopAndSize( left, top, width, height);
- panel->reshape( width, height, 1);
- panel->setRect(panel_rect);
-}
-
-void LLAccordionCtrl::ctrlShiftVertical(LLView* panel, S32 delta)
-{
- if (!panel)
- return;
- panel->translate(0,delta);
-}
-
-//---------------------------------------------------------------------------------
-
-void LLAccordionCtrl::addCollapsibleCtrl(LLView* view)
-{
- LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(view);
- if (!accordion_tab)
- return;
- if (std::find(beginChild(), endChild(), accordion_tab) == endChild())
- addChild(accordion_tab);
- mAccordionTabs.push_back(accordion_tab);
-
- accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLAccordionCtrl::onCollapseCtrlCloseOpen, this, (S16)(mAccordionTabs.size() - 1)) );
- arrange();
-}
-
-void LLAccordionCtrl::removeCollapsibleCtrl(LLView* view)
-{
- LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(view);
- if(!accordion_tab)
- return;
-
- if(std::find(beginChild(), endChild(), accordion_tab) != endChild())
- removeChild(accordion_tab);
-
- for (std::vector<LLAccordionCtrlTab*>::iterator iter = mAccordionTabs.begin();
- iter != mAccordionTabs.end(); ++iter)
- {
- if (accordion_tab == (*iter))
- {
- mAccordionTabs.erase(iter);
- break;
- }
- }
-
- // if removed is selected - reset selection
- if (mSelectedTab == view)
- {
- mSelectedTab = NULL;
- }
-}
-
-void LLAccordionCtrl::initNoTabsWidget(const LLTextBox::Params& tb_params)
-{
- LLTextBox::Params tp = tb_params;
- tp.rect(getLocalRect());
- mNoMatchedTabsOrigString = tp.initial_value().asString();
- mNoVisibleTabsHelpText = LLUICtrlFactory::create<LLTextBox>(tp, this);
-}
-
-void LLAccordionCtrl::updateNoTabsHelpTextVisibility()
-{
- bool visible_exists = false;
- std::vector<LLAccordionCtrlTab*>::const_iterator it = mAccordionTabs.begin();
- const std::vector<LLAccordionCtrlTab*>::const_iterator it_end = mAccordionTabs.end();
- while (it < it_end)
- {
- if ((*(it++))->getVisible())
- {
- visible_exists = true;
- break;
- }
- }
-
- mNoVisibleTabsHelpText->setVisible(!visible_exists);
-}
-
-void LLAccordionCtrl::arrangeSingle()
-{
- S32 panel_left = BORDER_MARGIN; // Margin from left side of Splitter
- S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel
- S32 panel_width = getRect().getWidth() - 4;
- S32 panel_height;
-
- S32 collapsed_height = 0;
-
- for (size_t i = 0; i < mAccordionTabs.size(); ++i)
- {
- LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
-
- if (!accordion_tab->getVisible()) // Skip hidden accordion tabs
- continue;
- if (!accordion_tab->isExpanded() )
- {
- collapsed_height+=mAccordionTabs[i]->getRect().getHeight();
- }
- }
-
- S32 expanded_height = getRect().getHeight() - BORDER_MARGIN - collapsed_height;
-
- for (size_t i = 0; i < mAccordionTabs.size(); ++i)
- {
- LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
-
- if (!accordion_tab->getVisible()) // Skip hidden accordion tabs
- continue;
- if (!accordion_tab->isExpanded() )
- {
- panel_height = accordion_tab->getRect().getHeight();
- }
- else
- {
- if (mFitParent)
- {
- panel_height = expanded_height;
- }
- else
- {
- if (accordion_tab->getAccordionView())
- {
- panel_height = accordion_tab->getAccordionView()->getRect().getHeight() +
- accordion_tab->getHeaderHeight() + BORDER_MARGIN * 2;
- }
- else
- {
- panel_height = accordion_tab->getRect().getHeight();
- }
- }
- }
-
- // make sure at least header is shown
- panel_height = llmax(panel_height, accordion_tab->getHeaderHeight());
-
- ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, panel_height);
- panel_top -= mAccordionTabs[i]->getRect().getHeight();
- }
-
- show_hide_scrollbar(getRect().getWidth(), getRect().getHeight());
- updateLayout(getRect().getWidth(), getRect().getHeight());
-}
-
-void LLAccordionCtrl::arrangeMultiple()
-{
- S32 panel_left = BORDER_MARGIN; // Margin from left side of Splitter
- S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel
- S32 panel_width = getRect().getWidth() - 4;
-
- //Calculate params
- for (size_t i = 0; i < mAccordionTabs.size(); i++ )
- {
- LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
-
- if (!accordion_tab->getVisible()) // Skip hidden accordion tabs
- continue;
-
- if (!accordion_tab->isExpanded() )
- {
- ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, accordion_tab->getRect().getHeight());
- panel_top -= mAccordionTabs[i]->getRect().getHeight();
- }
- else
- {
- S32 panel_height = accordion_tab->getRect().getHeight();
-
- if (mFitParent)
- {
- // All expanded tabs will have equal height
- panel_height = calcExpandedTabHeight(i, panel_top);
- ctrlSetLeftTopAndSize(accordion_tab, panel_left, panel_top, panel_width, panel_height);
-
- // Try to make accordion tab fit accordion view height.
- // Accordion View should implement getRequiredRect() and provide valid height
- S32 optimal_height = accordion_tab->getAccordionView()->getRequiredRect().getHeight();
- optimal_height += accordion_tab->getHeaderHeight() + 2 * BORDER_MARGIN;
- if (optimal_height < panel_height)
- {
- panel_height = optimal_height;
- }
-
- // minimum tab height is equal to header height
- if (mAccordionTabs[i]->getHeaderHeight() > panel_height)
- {
- panel_height = mAccordionTabs[i]->getHeaderHeight();
- }
- }
-
- ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, panel_height);
- panel_top -= panel_height;
-
- }
- }
-
- show_hide_scrollbar(getRect().getWidth(), getRect().getHeight());
-
- updateLayout(getRect().getWidth(), getRect().getHeight());
-}
-
-
-void LLAccordionCtrl::arrange()
-{
- updateNoTabsHelpTextVisibility();
-
- if (mAccordionTabs.empty())
- {
- // Nothing to arrange
- return;
- }
-
- if (mAccordionTabs.size() == 1)
- {
- S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel
- S32 panel_width = getRect().getWidth() - 4;
-
- LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[0]);
-
- LLRect panel_rect = accordion_tab->getRect();
-
- S32 panel_height = getRect().getHeight() - BORDER_MARGIN * 2;
- if (accordion_tab->getFitParent())
- panel_height = accordion_tab->getRect().getHeight();
-
- ctrlSetLeftTopAndSize(accordion_tab, panel_rect.mLeft, panel_top, panel_width, panel_height);
-
- show_hide_scrollbar(getRect().getWidth(), getRect().getHeight());
- return;
- }
-
- if (mSingleExpansion)
- arrangeSingle();
- else
- arrangeMultiple();
-}
-
-//---------------------------------------------------------------------------------
-
-bool LLAccordionCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks)
-{
- if (LLPanel::handleScrollWheel(x, y, clicks))
- return true;
- if (mScrollbar->getVisible() && mScrollbar->handleScrollWheel(0, 0, clicks))
- return true;
- return false;
-}
-
-bool LLAccordionCtrl::handleKeyHere(KEY key, MASK mask)
-{
- if (mScrollbar->getVisible() && mScrollbar->handleKeyHere(key, mask))
- return true;
- return LLPanel::handleKeyHere(key, mask);
-}
-
-bool LLAccordionCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask,
- bool drop,
- EDragAndDropType cargo_type,
- void* cargo_data,
- EAcceptance* accept,
- std::string& tooltip_msg)
-{
- // Scroll folder view if needed. Never accepts a drag or drop.
- *accept = ACCEPT_NO;
- bool handled = autoScroll(x, y);
-
- if (!handled)
- {
- handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type,
- cargo_data, accept, tooltip_msg) != NULL;
- }
- return true;
-}
-
-bool LLAccordionCtrl::autoScroll(S32 x, S32 y)
-{
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
-
- bool scrolling = false;
- if (mScrollbar->getVisible())
- {
- LLRect rect_local(0, getRect().getHeight(), getRect().getWidth() - scrollbar_size, 0);
- LLRect screen_local_extents;
-
- // clip rect against root view
- screenRectToLocal(getRootView()->getLocalRect(), &screen_local_extents);
- rect_local.intersectWith(screen_local_extents);
-
- // autoscroll region should take up no more than one third of visible scroller area
- S32 auto_scroll_region_height = llmin(rect_local.getHeight() / 3, 10);
- S32 auto_scroll_speed = ll_round(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32());
-
- LLRect bottom_scroll_rect = screen_local_extents;
- bottom_scroll_rect.mTop = rect_local.mBottom + auto_scroll_region_height;
- if (bottom_scroll_rect.pointInRect( x, y ) && (mScrollbar->getDocPos() < mScrollbar->getDocPosMax()))
- {
- mScrollbar->setDocPos(mScrollbar->getDocPos() + auto_scroll_speed);
- mAutoScrolling = true;
- scrolling = true;
- }
-
- LLRect top_scroll_rect = screen_local_extents;
- top_scroll_rect.mBottom = rect_local.mTop - auto_scroll_region_height;
- if (top_scroll_rect.pointInRect(x, y) && (mScrollbar->getDocPos() > 0))
- {
- mScrollbar->setDocPos(mScrollbar->getDocPos() - auto_scroll_speed);
- mAutoScrolling = true;
- scrolling = true;
- }
- }
-
- return scrolling;
-}
-
-void LLAccordionCtrl::updateLayout(S32 width, S32 height)
-{
- S32 panel_top = height - BORDER_MARGIN ;
- if (mScrollbar->getVisible())
- panel_top += mScrollbar->getDocPos();
-
- S32 panel_width = width - BORDER_MARGIN * 2;
-
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
- if (mScrollbar->getVisible())
- panel_width -= scrollbar_size;
-
- // set sizes for first panels and dragbars
- for (size_t i = 0; i < mAccordionTabs.size(); ++i)
- {
- if (!mAccordionTabs[i]->getVisible())
- continue;
- LLRect panel_rect = mAccordionTabs[i]->getRect();
- ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_rect.mLeft, panel_top, panel_width, panel_rect.getHeight());
- panel_top -= panel_rect.getHeight();
- }
-}
-
-void LLAccordionCtrl::onScrollPosChangeCallback(S32, LLScrollbar*)
-{
- updateLayout(getRect().getWidth(), getRect().getHeight());
-}
-
-// virtual
-void LLAccordionCtrl::onUpdateScrollToChild(const LLUICtrl *cntrl)
-{
- if (mScrollbar && mScrollbar->getVisible() && !mSkipScrollToChild)
- {
- // same as scrollToShowRect
- LLRect rect;
- cntrl->localRectToOtherView(cntrl->getLocalRect(), &rect, this);
-
- // Translate to parent coordinatess to check if we are in visible rectangle
- rect.translate(getRect().mLeft, getRect().mBottom);
-
- if (!getRect().contains(rect))
- {
- // for accordition's scroll, height is in pixels
- // Back to local coords and calculate position for scroller
- S32 bottom = mScrollbar->getDocPos() - rect.mBottom + getRect().mBottom;
- S32 top = mScrollbar->getDocPos() - rect.mTop + getRect().mTop;
-
- S32 scroll_pos = llclamp(mScrollbar->getDocPos(),
- bottom, // min vertical scroll
- top); // max vertical scroll
-
- mScrollbar->setDocPos(scroll_pos);
- }
- }
-
- LLUICtrl::onUpdateScrollToChild(cntrl);
-}
-
-void LLAccordionCtrl::onOpen(const LLSD& key)
-{
- for (size_t i = 0; i < mAccordionTabs.size(); ++i)
- {
- LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
- LLPanel* panel = dynamic_cast<LLPanel*>(accordion_tab->getAccordionView());
- if (panel != NULL)
- {
- panel->onOpen(key);
- }
- }
-}
-
-S32 LLAccordionCtrl::notifyParent(const LLSD& info)
-{
- if (info.has("action"))
- {
- std::string str_action = info["action"];
- if (str_action == "size_changes")
- {
- //
- arrange();
- return 1;
- }
- if (str_action == "select_next")
- {
- for (size_t i = 0; i < mAccordionTabs.size(); ++i)
- {
- LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
- if (accordion_tab->hasFocus())
- {
- while (++i < mAccordionTabs.size())
- {
- if (mAccordionTabs[i]->getVisible())
- break;
- }
- if (i < mAccordionTabs.size())
- {
- accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
- accordion_tab->notify(LLSD().with("action","select_first"));
- return 1;
- }
- break;
- }
- }
- return 0;
- }
- if (str_action == "select_prev")
- {
- for (size_t i = 0; i < mAccordionTabs.size(); ++i)
- {
- LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
- if (accordion_tab->hasFocus() && i > 0)
- {
- bool prev_visible_tab_found = false;
- while (i > 0)
- {
- if (mAccordionTabs[--i]->getVisible())
- {
- prev_visible_tab_found = true;
- break;
- }
- }
-
- if (prev_visible_tab_found)
- {
- accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
- accordion_tab->notify(LLSD().with("action","select_last"));
- return 1;
- }
- break;
- }
- }
- return 0;
- }
- if (str_action == "select_current")
- {
- for (size_t i = 0; i < mAccordionTabs.size(); ++i)
- {
- // Set selection to the currently focused tab.
- if (mAccordionTabs[i]->hasFocus())
- {
- if (mAccordionTabs[i] != mSelectedTab)
- {
- if (mSelectedTab)
- {
- mSelectedTab->setSelected(false);
- }
- mSelectedTab = mAccordionTabs[i];
- mSelectedTab->setSelected(true);
- }
-
- return 1;
- }
- }
- return 0;
- }
- if (str_action == "deselect_current")
- {
- // Reset selection to the currently selected tab.
- if (mSelectedTab)
- {
- mSelectedTab->setSelected(false);
- mSelectedTab = NULL;
- return 1;
- }
- return 0;
- }
- }
- else if (info.has("scrollToShowRect"))
- {
- LLRect screen_rc, local_rc;
- screen_rc.setValue(info["scrollToShowRect"]);
- screenRectToLocal(screen_rc, &local_rc);
-
- // Translate to parent coordinatess to check if we are in visible rectangle
- local_rc.translate(getRect().mLeft, getRect().mBottom);
-
- if (!getRect().contains (local_rc))
- {
- // Back to local coords and calculate position for scroller
- S32 bottom = mScrollbar->getDocPos() - local_rc.mBottom + getRect().mBottom;
- S32 top = mScrollbar->getDocPos() - local_rc.mTop + getRect().mTop;
-
- S32 scroll_pos = llclamp(mScrollbar->getDocPos(),
- bottom, // min vertical scroll
- top); // max vertical scroll
-
- mScrollbar->setDocPos(scroll_pos);
- }
- return 1;
- }
- else if (info.has("child_visibility_change"))
- {
- bool new_visibility = info["child_visibility_change"];
- if (new_visibility)
- {
- // there is at least one visible tab
- mNoVisibleTabsHelpText->setVisible(false);
- }
- else
- {
- // it could be the latest visible tab, check all of them
- updateNoTabsHelpTextVisibility();
- }
- }
- return LLPanel::notifyParent(info);
-}
-
-void LLAccordionCtrl::reset()
-{
- if (mScrollbar)
- mScrollbar->setDocPos(0);
-}
-
-void LLAccordionCtrl::expandDefaultTab()
-{
- if (!mAccordionTabs.empty())
- {
- LLAccordionCtrlTab* tab = mAccordionTabs.front();
-
- if (!tab->getDisplayChildren())
- {
- tab->setDisplayChildren(true);
- }
-
- for (size_t i = 1; i < mAccordionTabs.size(); ++i)
- {
- tab = mAccordionTabs[i];
-
- if (tab->getDisplayChildren())
- {
- tab->setDisplayChildren(false);
- }
- }
-
- arrange();
- }
-}
-
-void LLAccordionCtrl::sort()
-{
- if (!mTabComparator)
- {
- LL_WARNS() << "No comparator specified for sorting accordion tabs." << LL_ENDL;
- return;
- }
-
- std::sort(mAccordionTabs.begin(), mAccordionTabs.end(), LLComparatorAdaptor(*mTabComparator));
- arrange();
-}
-
-void LLAccordionCtrl::setFilterSubString(const std::string& filter_string)
-{
- LLStringUtil::format_map_t args;
- args["[SEARCH_TERM]"] = LLURI::escape(filter_string);
- std::string text = filter_string.empty() ? mNoVisibleTabsOrigString : mNoMatchedTabsOrigString;
- LLStringUtil::format(text, args);
-
- mNoVisibleTabsHelpText->setValue(text);
-}
-
-const LLAccordionCtrlTab* LLAccordionCtrl::getExpandedTab() const
-{
- typedef std::vector<LLAccordionCtrlTab*>::const_iterator tabs_const_iterator;
-
- const LLAccordionCtrlTab* result = 0;
-
- for (tabs_const_iterator i = mAccordionTabs.begin(); i != mAccordionTabs.end(); ++i)
- {
- if ((*i)->isExpanded())
- {
- result = *i;
- break;
- }
- }
-
- return result;
-}
-
-S32 LLAccordionCtrl::calcExpandedTabHeight(S32 tab_index /* = 0 */, S32 available_height /* = 0 */)
-{
- if (tab_index < 0)
- {
- return available_height;
- }
-
- S32 collapsed_tabs_height = 0;
- S32 num_expanded = 0;
-
- for (size_t n = tab_index; n < mAccordionTabs.size(); ++n)
- {
- if (!mAccordionTabs[n]->isExpanded())
- {
- collapsed_tabs_height += mAccordionTabs[n]->getHeaderHeight();
- }
- else
- {
- ++num_expanded;
- }
- }
-
- if (0 == num_expanded)
- {
- return available_height;
- }
-
- S32 expanded_tab_height = available_height - collapsed_tabs_height - BORDER_MARGIN; // top BORDER_MARGIN is added in arrange(), here we add bottom BORDER_MARGIN
- expanded_tab_height /= num_expanded;
- return expanded_tab_height;
-}
+/**
+ * @file llaccordionctrl.cpp
+ * @brief Accordion panel implementation
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+#include "linden_common.h"
+
+#include "llaccordionctrl.h"
+#include "llaccordionctrltab.h"
+
+#include "lluictrlfactory.h" // builds floaters from XML
+
+#include "llwindow.h"
+#include "llfocusmgr.h"
+#include "lllocalcliprect.h"
+
+#include "boost/bind.hpp"
+
+static const S32 BORDER_MARGIN = 2;
+static const S32 PARENT_BORDER_MARGIN = 5;
+static const S32 VERTICAL_MULTIPLE = 16;
+static const F32 MIN_AUTO_SCROLL_RATE = 120.f;
+static const F32 MAX_AUTO_SCROLL_RATE = 500.f;
+static const F32 AUTO_SCROLL_RATE_ACCEL = 120.f;
+
+// LLAccordionCtrl =================================================================|
+
+static LLDefaultChildRegistry::Register<LLAccordionCtrl> t2("accordion");
+
+LLAccordionCtrl::LLAccordionCtrl(const Params& params):LLPanel(params)
+ , mFitParent(params.fit_parent)
+ , mAutoScrolling( false )
+ , mAutoScrollRate( 0.f )
+ , mSelectedTab( NULL )
+ , mTabComparator( NULL )
+ , mNoVisibleTabsHelpText(NULL)
+ , mNoVisibleTabsOrigString(params.no_visible_tabs_text.initial_value().asString())
+ , mSkipScrollToChild(false)
+{
+ initNoTabsWidget(params.no_matched_tabs_text);
+
+ mSingleExpansion = params.single_expansion;
+ if (mFitParent && !mSingleExpansion)
+ {
+ LL_INFOS() << "fit_parent works best when combined with single_expansion" << LL_ENDL;
+ }
+}
+
+LLAccordionCtrl::LLAccordionCtrl() : LLPanel()
+ , mAutoScrolling( false )
+ , mAutoScrollRate( 0.f )
+ , mSelectedTab( NULL )
+ , mNoVisibleTabsHelpText(NULL)
+{
+ initNoTabsWidget(LLTextBox::Params());
+
+ mSingleExpansion = false;
+ mFitParent = false;
+ buildFromFile( "accordion_parent.xml");
+}
+
+//---------------------------------------------------------------------------------
+void LLAccordionCtrl::draw()
+{
+ if (mAutoScrolling)
+ {
+ // add acceleration to autoscroll
+ mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), MAX_AUTO_SCROLL_RATE);
+ }
+ else
+ {
+ // reset to minimum for next time
+ mAutoScrollRate = MIN_AUTO_SCROLL_RATE;
+ }
+ // clear this flag to be set on next call to autoScroll
+ mAutoScrolling = false;
+
+ LLRect local_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
+
+ LLLocalClipRect clip(local_rect);
+
+ LLPanel::draw();
+}
+
+//---------------------------------------------------------------------------------
+bool LLAccordionCtrl::postBuild()
+{
+ static LLUICachedControl<S32> scrollbar_size("UIScrollbarSize", 0);
+
+ LLRect scroll_rect;
+ scroll_rect.setOriginAndSize(
+ getRect().getWidth() - scrollbar_size,
+ 1,
+ scrollbar_size,
+ getRect().getHeight() - 1);
+
+ LLScrollbar::Params sbparams;
+ sbparams.name("scrollable vertical");
+ sbparams.rect(scroll_rect);
+ sbparams.orientation(LLScrollbar::VERTICAL);
+ sbparams.doc_size(mInnerRect.getHeight());
+ sbparams.doc_pos(0);
+ sbparams.page_size(mInnerRect.getHeight());
+ sbparams.step_size(VERTICAL_MULTIPLE);
+ sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
+ sbparams.change_callback(boost::bind(&LLAccordionCtrl::onScrollPosChangeCallback, this, _1, _2));
+
+ mScrollbar = LLUICtrlFactory::create<LLScrollbar>(sbparams);
+ LLView::addChild(mScrollbar);
+ mScrollbar->setVisible(false);
+ mScrollbar->setFollowsRight();
+ mScrollbar->setFollowsTop();
+ mScrollbar->setFollowsBottom();
+
+ //if it was created from xml...
+ std::vector<LLUICtrl*> accordion_tabs;
+ for (child_list_const_iter_t it = getChildList()->begin();
+ getChildList()->end() != it; ++it)
+ {
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(*it);
+ if (accordion_tab == NULL)
+ continue;
+ if (std::find(mAccordionTabs.begin(), mAccordionTabs.end(), accordion_tab) == mAccordionTabs.end())
+ {
+ accordion_tabs.push_back(accordion_tab);
+ }
+ }
+
+ for (std::vector<LLUICtrl*>::reverse_iterator it = accordion_tabs.rbegin();
+ it < accordion_tabs.rend(); ++it)
+ {
+ addCollapsibleCtrl(*it);
+ }
+
+ arrange();
+
+ if (mSingleExpansion)
+ {
+ if (!mAccordionTabs[0]->getDisplayChildren())
+ mAccordionTabs[0]->setDisplayChildren(true);
+ for (size_t i = 1; i < mAccordionTabs.size(); ++i)
+ {
+ if (mAccordionTabs[i]->getDisplayChildren())
+ mAccordionTabs[i]->setDisplayChildren(false);
+ }
+ }
+
+ updateNoTabsHelpTextVisibility();
+
+ return true;
+}
+
+
+//---------------------------------------------------------------------------------
+LLAccordionCtrl::~LLAccordionCtrl()
+{
+ mAccordionTabs.clear();
+}
+
+//---------------------------------------------------------------------------------
+
+void LLAccordionCtrl::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ // adjust our rectangle
+ LLRect rcLocal = getRect();
+ rcLocal.mRight = rcLocal.mLeft + width;
+ rcLocal.mTop = rcLocal.mBottom + height;
+
+ // get textbox a chance to reshape its content
+ mNoVisibleTabsHelpText->reshape(width, height, called_from_parent);
+
+ setRect(rcLocal);
+
+ // assume that help text is always fit accordion.
+ // necessary text paddings can be set via h_pad and v_pad
+ mNoVisibleTabsHelpText->setRect(getLocalRect());
+
+ arrange();
+}
+
+//---------------------------------------------------------------------------------
+bool LLAccordionCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ return LLPanel::handleRightMouseDown(x, y, mask);
+}
+
+//---------------------------------------------------------------------------------
+void LLAccordionCtrl::shiftAccordionTabs(S16 panel_num, S32 delta)
+{
+ for (size_t i = panel_num; i < mAccordionTabs.size(); ++i)
+ {
+ ctrlShiftVertical(mAccordionTabs[i],delta);
+ }
+}
+
+//---------------------------------------------------------------------------------
+void LLAccordionCtrl::onCollapseCtrlCloseOpen(S16 panel_num)
+{
+ if (mSingleExpansion)
+ {
+ for (size_t i = 0; i < mAccordionTabs.size(); ++i)
+ {
+ if (i == panel_num)
+ continue;
+ if (mAccordionTabs[i]->getDisplayChildren())
+ mAccordionTabs[i]->setDisplayChildren(false);
+ }
+
+ }
+ arrange();
+}
+
+void LLAccordionCtrl::show_hide_scrollbar(S32 width, S32 height)
+{
+ calcRecuiredHeight();
+ if (getRecuiredHeight() > height)
+ showScrollbar(width, height);
+ else
+ hideScrollbar(width, height);
+}
+
+void LLAccordionCtrl::showScrollbar(S32 width, S32 height)
+{
+ bool was_visible = mScrollbar->getVisible();
+
+ mScrollbar->setVisible(true);
+
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+
+ ctrlSetLeftTopAndSize(mScrollbar
+ , width - scrollbar_size - PARENT_BORDER_MARGIN / 2
+ , height - PARENT_BORDER_MARGIN
+ , scrollbar_size
+ , height - PARENT_BORDER_MARGIN * 2);
+
+ mScrollbar->setPageSize(height);
+ mScrollbar->setDocParams(mInnerRect.getHeight(), mScrollbar->getDocPos());
+
+ if (was_visible)
+ {
+ S32 scroll_pos = llmin(mScrollbar->getDocPos(), getRecuiredHeight() - height - 1);
+ mScrollbar->setDocPos(scroll_pos);
+ }
+}
+
+void LLAccordionCtrl::hideScrollbar(S32 width, S32 height)
+{
+ if (!mScrollbar->getVisible())
+ return;
+ mScrollbar->setVisible(false);
+
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+
+ S32 panel_width = width - 2*BORDER_MARGIN;
+
+ // Reshape all accordions and shift all draggers
+ for (size_t i = 0; i < mAccordionTabs.size(); ++i)
+ {
+ LLRect panel_rect = mAccordionTabs[i]->getRect();
+ ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_rect.mLeft, panel_rect.mTop, panel_width, panel_rect.getHeight());
+ }
+
+ mScrollbar->setDocPos(0);
+
+ if (!mAccordionTabs.empty())
+ {
+ S32 panel_top = height - BORDER_MARGIN; // Top coordinate of the first panel
+ S32 diff = panel_top - mAccordionTabs[0]->getRect().mTop;
+ shiftAccordionTabs(0, diff);
+ }
+}
+
+//---------------------------------------------------------------------------------
+S32 LLAccordionCtrl::calcRecuiredHeight()
+{
+ S32 rec_height = 0;
+
+ std::vector<LLAccordionCtrlTab*>::iterator panel;
+ for(panel=mAccordionTabs.begin(); panel!=mAccordionTabs.end(); ++panel)
+ {
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(*panel);
+ if(accordion_tab && accordion_tab->getVisible())
+ {
+ rec_height += accordion_tab->getRect().getHeight();
+ }
+ }
+
+ mInnerRect.setLeftTopAndSize(0, rec_height + BORDER_MARGIN * 2, getRect().getWidth(), rec_height + BORDER_MARGIN);
+
+ return mInnerRect.getHeight();
+}
+
+//---------------------------------------------------------------------------------
+void LLAccordionCtrl::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height)
+{
+ if (!panel)
+ return;
+ LLRect panel_rect = panel->getRect();
+ panel_rect.setLeftTopAndSize( left, top, width, height);
+ panel->reshape( width, height, 1);
+ panel->setRect(panel_rect);
+}
+
+void LLAccordionCtrl::ctrlShiftVertical(LLView* panel, S32 delta)
+{
+ if (!panel)
+ return;
+ panel->translate(0,delta);
+}
+
+//---------------------------------------------------------------------------------
+
+void LLAccordionCtrl::addCollapsibleCtrl(LLView* view)
+{
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(view);
+ if (!accordion_tab)
+ return;
+ if (std::find(beginChild(), endChild(), accordion_tab) == endChild())
+ addChild(accordion_tab);
+ mAccordionTabs.push_back(accordion_tab);
+
+ accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLAccordionCtrl::onCollapseCtrlCloseOpen, this, (S16)(mAccordionTabs.size() - 1)) );
+ arrange();
+}
+
+void LLAccordionCtrl::removeCollapsibleCtrl(LLView* view)
+{
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(view);
+ if(!accordion_tab)
+ return;
+
+ if(std::find(beginChild(), endChild(), accordion_tab) != endChild())
+ removeChild(accordion_tab);
+
+ for (std::vector<LLAccordionCtrlTab*>::iterator iter = mAccordionTabs.begin();
+ iter != mAccordionTabs.end(); ++iter)
+ {
+ if (accordion_tab == (*iter))
+ {
+ mAccordionTabs.erase(iter);
+ break;
+ }
+ }
+
+ // if removed is selected - reset selection
+ if (mSelectedTab == view)
+ {
+ mSelectedTab = NULL;
+ }
+}
+
+void LLAccordionCtrl::initNoTabsWidget(const LLTextBox::Params& tb_params)
+{
+ LLTextBox::Params tp = tb_params;
+ tp.rect(getLocalRect());
+ mNoMatchedTabsOrigString = tp.initial_value().asString();
+ mNoVisibleTabsHelpText = LLUICtrlFactory::create<LLTextBox>(tp, this);
+}
+
+void LLAccordionCtrl::updateNoTabsHelpTextVisibility()
+{
+ bool visible_exists = false;
+ std::vector<LLAccordionCtrlTab*>::const_iterator it = mAccordionTabs.begin();
+ const std::vector<LLAccordionCtrlTab*>::const_iterator it_end = mAccordionTabs.end();
+ while (it < it_end)
+ {
+ if ((*(it++))->getVisible())
+ {
+ visible_exists = true;
+ break;
+ }
+ }
+
+ mNoVisibleTabsHelpText->setVisible(!visible_exists);
+}
+
+void LLAccordionCtrl::arrangeSingle()
+{
+ S32 panel_left = BORDER_MARGIN; // Margin from left side of Splitter
+ S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel
+ S32 panel_width = getRect().getWidth() - 4;
+ S32 panel_height;
+
+ S32 collapsed_height = 0;
+
+ for (size_t i = 0; i < mAccordionTabs.size(); ++i)
+ {
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
+
+ if (!accordion_tab->getVisible()) // Skip hidden accordion tabs
+ continue;
+ if (!accordion_tab->isExpanded() )
+ {
+ collapsed_height+=mAccordionTabs[i]->getRect().getHeight();
+ }
+ }
+
+ S32 expanded_height = getRect().getHeight() - BORDER_MARGIN - collapsed_height;
+
+ for (size_t i = 0; i < mAccordionTabs.size(); ++i)
+ {
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
+
+ if (!accordion_tab->getVisible()) // Skip hidden accordion tabs
+ continue;
+ if (!accordion_tab->isExpanded() )
+ {
+ panel_height = accordion_tab->getRect().getHeight();
+ }
+ else
+ {
+ if (mFitParent)
+ {
+ panel_height = expanded_height;
+ }
+ else
+ {
+ if (accordion_tab->getAccordionView())
+ {
+ panel_height = accordion_tab->getAccordionView()->getRect().getHeight() +
+ accordion_tab->getHeaderHeight() + BORDER_MARGIN * 2;
+ }
+ else
+ {
+ panel_height = accordion_tab->getRect().getHeight();
+ }
+ }
+ }
+
+ // make sure at least header is shown
+ panel_height = llmax(panel_height, accordion_tab->getHeaderHeight());
+
+ ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, panel_height);
+ panel_top -= mAccordionTabs[i]->getRect().getHeight();
+ }
+
+ show_hide_scrollbar(getRect().getWidth(), getRect().getHeight());
+ updateLayout(getRect().getWidth(), getRect().getHeight());
+}
+
+void LLAccordionCtrl::arrangeMultiple()
+{
+ S32 panel_left = BORDER_MARGIN; // Margin from left side of Splitter
+ S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel
+ S32 panel_width = getRect().getWidth() - 4;
+
+ //Calculate params
+ for (size_t i = 0; i < mAccordionTabs.size(); i++ )
+ {
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
+
+ if (!accordion_tab->getVisible()) // Skip hidden accordion tabs
+ continue;
+
+ if (!accordion_tab->isExpanded() )
+ {
+ ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, accordion_tab->getRect().getHeight());
+ panel_top -= mAccordionTabs[i]->getRect().getHeight();
+ }
+ else
+ {
+ S32 panel_height = accordion_tab->getRect().getHeight();
+
+ if (mFitParent)
+ {
+ // All expanded tabs will have equal height
+ panel_height = calcExpandedTabHeight(i, panel_top);
+ ctrlSetLeftTopAndSize(accordion_tab, panel_left, panel_top, panel_width, panel_height);
+
+ // Try to make accordion tab fit accordion view height.
+ // Accordion View should implement getRequiredRect() and provide valid height
+ S32 optimal_height = accordion_tab->getAccordionView()->getRequiredRect().getHeight();
+ optimal_height += accordion_tab->getHeaderHeight() + 2 * BORDER_MARGIN;
+ if (optimal_height < panel_height)
+ {
+ panel_height = optimal_height;
+ }
+
+ // minimum tab height is equal to header height
+ if (mAccordionTabs[i]->getHeaderHeight() > panel_height)
+ {
+ panel_height = mAccordionTabs[i]->getHeaderHeight();
+ }
+ }
+
+ ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, panel_height);
+ panel_top -= panel_height;
+
+ }
+ }
+
+ show_hide_scrollbar(getRect().getWidth(), getRect().getHeight());
+
+ updateLayout(getRect().getWidth(), getRect().getHeight());
+}
+
+
+void LLAccordionCtrl::arrange()
+{
+ updateNoTabsHelpTextVisibility();
+
+ if (mAccordionTabs.empty())
+ {
+ // Nothing to arrange
+ return;
+ }
+
+ if (mAccordionTabs.size() == 1)
+ {
+ S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel
+ S32 panel_width = getRect().getWidth() - 4;
+
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[0]);
+
+ LLRect panel_rect = accordion_tab->getRect();
+
+ S32 panel_height = getRect().getHeight() - BORDER_MARGIN * 2;
+ if (accordion_tab->getFitParent())
+ panel_height = accordion_tab->getRect().getHeight();
+
+ ctrlSetLeftTopAndSize(accordion_tab, panel_rect.mLeft, panel_top, panel_width, panel_height);
+
+ show_hide_scrollbar(getRect().getWidth(), getRect().getHeight());
+ return;
+ }
+
+ if (mSingleExpansion)
+ arrangeSingle();
+ else
+ arrangeMultiple();
+}
+
+//---------------------------------------------------------------------------------
+
+bool LLAccordionCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ if (LLPanel::handleScrollWheel(x, y, clicks))
+ return true;
+ if (mScrollbar->getVisible() && mScrollbar->handleScrollWheel(0, 0, clicks))
+ return true;
+ return false;
+}
+
+bool LLAccordionCtrl::handleKeyHere(KEY key, MASK mask)
+{
+ if (mScrollbar->getVisible() && mScrollbar->handleKeyHere(key, mask))
+ return true;
+ return LLPanel::handleKeyHere(key, mask);
+}
+
+bool LLAccordionCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask,
+ bool drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ // Scroll folder view if needed. Never accepts a drag or drop.
+ *accept = ACCEPT_NO;
+ bool handled = autoScroll(x, y);
+
+ if (!handled)
+ {
+ handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type,
+ cargo_data, accept, tooltip_msg) != NULL;
+ }
+ return true;
+}
+
+bool LLAccordionCtrl::autoScroll(S32 x, S32 y)
+{
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+
+ bool scrolling = false;
+ if (mScrollbar->getVisible())
+ {
+ LLRect rect_local(0, getRect().getHeight(), getRect().getWidth() - scrollbar_size, 0);
+ LLRect screen_local_extents;
+
+ // clip rect against root view
+ screenRectToLocal(getRootView()->getLocalRect(), &screen_local_extents);
+ rect_local.intersectWith(screen_local_extents);
+
+ // autoscroll region should take up no more than one third of visible scroller area
+ S32 auto_scroll_region_height = llmin(rect_local.getHeight() / 3, 10);
+ S32 auto_scroll_speed = ll_round(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32());
+
+ LLRect bottom_scroll_rect = screen_local_extents;
+ bottom_scroll_rect.mTop = rect_local.mBottom + auto_scroll_region_height;
+ if (bottom_scroll_rect.pointInRect( x, y ) && (mScrollbar->getDocPos() < mScrollbar->getDocPosMax()))
+ {
+ mScrollbar->setDocPos(mScrollbar->getDocPos() + auto_scroll_speed);
+ mAutoScrolling = true;
+ scrolling = true;
+ }
+
+ LLRect top_scroll_rect = screen_local_extents;
+ top_scroll_rect.mBottom = rect_local.mTop - auto_scroll_region_height;
+ if (top_scroll_rect.pointInRect(x, y) && (mScrollbar->getDocPos() > 0))
+ {
+ mScrollbar->setDocPos(mScrollbar->getDocPos() - auto_scroll_speed);
+ mAutoScrolling = true;
+ scrolling = true;
+ }
+ }
+
+ return scrolling;
+}
+
+void LLAccordionCtrl::updateLayout(S32 width, S32 height)
+{
+ S32 panel_top = height - BORDER_MARGIN ;
+ if (mScrollbar->getVisible())
+ panel_top += mScrollbar->getDocPos();
+
+ S32 panel_width = width - BORDER_MARGIN * 2;
+
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+ if (mScrollbar->getVisible())
+ panel_width -= scrollbar_size;
+
+ // set sizes for first panels and dragbars
+ for (size_t i = 0; i < mAccordionTabs.size(); ++i)
+ {
+ if (!mAccordionTabs[i]->getVisible())
+ continue;
+ LLRect panel_rect = mAccordionTabs[i]->getRect();
+ ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_rect.mLeft, panel_top, panel_width, panel_rect.getHeight());
+ panel_top -= panel_rect.getHeight();
+ }
+}
+
+void LLAccordionCtrl::onScrollPosChangeCallback(S32, LLScrollbar*)
+{
+ updateLayout(getRect().getWidth(), getRect().getHeight());
+}
+
+// virtual
+void LLAccordionCtrl::onUpdateScrollToChild(const LLUICtrl *cntrl)
+{
+ if (mScrollbar && mScrollbar->getVisible() && !mSkipScrollToChild)
+ {
+ // same as scrollToShowRect
+ LLRect rect;
+ cntrl->localRectToOtherView(cntrl->getLocalRect(), &rect, this);
+
+ // Translate to parent coordinatess to check if we are in visible rectangle
+ rect.translate(getRect().mLeft, getRect().mBottom);
+
+ if (!getRect().contains(rect))
+ {
+ // for accordition's scroll, height is in pixels
+ // Back to local coords and calculate position for scroller
+ S32 bottom = mScrollbar->getDocPos() - rect.mBottom + getRect().mBottom;
+ S32 top = mScrollbar->getDocPos() - rect.mTop + getRect().mTop;
+
+ S32 scroll_pos = llclamp(mScrollbar->getDocPos(),
+ bottom, // min vertical scroll
+ top); // max vertical scroll
+
+ mScrollbar->setDocPos(scroll_pos);
+ }
+ }
+
+ LLUICtrl::onUpdateScrollToChild(cntrl);
+}
+
+void LLAccordionCtrl::onOpen(const LLSD& key)
+{
+ for (size_t i = 0; i < mAccordionTabs.size(); ++i)
+ {
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
+ LLPanel* panel = dynamic_cast<LLPanel*>(accordion_tab->getAccordionView());
+ if (panel != NULL)
+ {
+ panel->onOpen(key);
+ }
+ }
+}
+
+S32 LLAccordionCtrl::notifyParent(const LLSD& info)
+{
+ if (info.has("action"))
+ {
+ std::string str_action = info["action"];
+ if (str_action == "size_changes")
+ {
+ //
+ arrange();
+ return 1;
+ }
+ if (str_action == "select_next")
+ {
+ for (size_t i = 0; i < mAccordionTabs.size(); ++i)
+ {
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
+ if (accordion_tab->hasFocus())
+ {
+ while (++i < mAccordionTabs.size())
+ {
+ if (mAccordionTabs[i]->getVisible())
+ break;
+ }
+ if (i < mAccordionTabs.size())
+ {
+ accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
+ accordion_tab->notify(LLSD().with("action","select_first"));
+ return 1;
+ }
+ break;
+ }
+ }
+ return 0;
+ }
+ if (str_action == "select_prev")
+ {
+ for (size_t i = 0; i < mAccordionTabs.size(); ++i)
+ {
+ LLAccordionCtrlTab* accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
+ if (accordion_tab->hasFocus() && i > 0)
+ {
+ bool prev_visible_tab_found = false;
+ while (i > 0)
+ {
+ if (mAccordionTabs[--i]->getVisible())
+ {
+ prev_visible_tab_found = true;
+ break;
+ }
+ }
+
+ if (prev_visible_tab_found)
+ {
+ accordion_tab = dynamic_cast<LLAccordionCtrlTab*>(mAccordionTabs[i]);
+ accordion_tab->notify(LLSD().with("action","select_last"));
+ return 1;
+ }
+ break;
+ }
+ }
+ return 0;
+ }
+ if (str_action == "select_current")
+ {
+ for (size_t i = 0; i < mAccordionTabs.size(); ++i)
+ {
+ // Set selection to the currently focused tab.
+ if (mAccordionTabs[i]->hasFocus())
+ {
+ if (mAccordionTabs[i] != mSelectedTab)
+ {
+ if (mSelectedTab)
+ {
+ mSelectedTab->setSelected(false);
+ }
+ mSelectedTab = mAccordionTabs[i];
+ mSelectedTab->setSelected(true);
+ }
+
+ return 1;
+ }
+ }
+ return 0;
+ }
+ if (str_action == "deselect_current")
+ {
+ // Reset selection to the currently selected tab.
+ if (mSelectedTab)
+ {
+ mSelectedTab->setSelected(false);
+ mSelectedTab = NULL;
+ return 1;
+ }
+ return 0;
+ }
+ }
+ else if (info.has("scrollToShowRect"))
+ {
+ LLRect screen_rc, local_rc;
+ screen_rc.setValue(info["scrollToShowRect"]);
+ screenRectToLocal(screen_rc, &local_rc);
+
+ // Translate to parent coordinatess to check if we are in visible rectangle
+ local_rc.translate(getRect().mLeft, getRect().mBottom);
+
+ if (!getRect().contains (local_rc))
+ {
+ // Back to local coords and calculate position for scroller
+ S32 bottom = mScrollbar->getDocPos() - local_rc.mBottom + getRect().mBottom;
+ S32 top = mScrollbar->getDocPos() - local_rc.mTop + getRect().mTop;
+
+ S32 scroll_pos = llclamp(mScrollbar->getDocPos(),
+ bottom, // min vertical scroll
+ top); // max vertical scroll
+
+ mScrollbar->setDocPos(scroll_pos);
+ }
+ return 1;
+ }
+ else if (info.has("child_visibility_change"))
+ {
+ bool new_visibility = info["child_visibility_change"];
+ if (new_visibility)
+ {
+ // there is at least one visible tab
+ mNoVisibleTabsHelpText->setVisible(false);
+ }
+ else
+ {
+ // it could be the latest visible tab, check all of them
+ updateNoTabsHelpTextVisibility();
+ }
+ }
+ return LLPanel::notifyParent(info);
+}
+
+void LLAccordionCtrl::reset()
+{
+ if (mScrollbar)
+ mScrollbar->setDocPos(0);
+}
+
+void LLAccordionCtrl::expandDefaultTab()
+{
+ if (!mAccordionTabs.empty())
+ {
+ LLAccordionCtrlTab* tab = mAccordionTabs.front();
+
+ if (!tab->getDisplayChildren())
+ {
+ tab->setDisplayChildren(true);
+ }
+
+ for (size_t i = 1; i < mAccordionTabs.size(); ++i)
+ {
+ tab = mAccordionTabs[i];
+
+ if (tab->getDisplayChildren())
+ {
+ tab->setDisplayChildren(false);
+ }
+ }
+
+ arrange();
+ }
+}
+
+void LLAccordionCtrl::sort()
+{
+ if (!mTabComparator)
+ {
+ LL_WARNS() << "No comparator specified for sorting accordion tabs." << LL_ENDL;
+ return;
+ }
+
+ std::sort(mAccordionTabs.begin(), mAccordionTabs.end(), LLComparatorAdaptor(*mTabComparator));
+ arrange();
+}
+
+void LLAccordionCtrl::setFilterSubString(const std::string& filter_string)
+{
+ LLStringUtil::format_map_t args;
+ args["[SEARCH_TERM]"] = LLURI::escape(filter_string);
+ std::string text = filter_string.empty() ? mNoVisibleTabsOrigString : mNoMatchedTabsOrigString;
+ LLStringUtil::format(text, args);
+
+ mNoVisibleTabsHelpText->setValue(text);
+}
+
+const LLAccordionCtrlTab* LLAccordionCtrl::getExpandedTab() const
+{
+ typedef std::vector<LLAccordionCtrlTab*>::const_iterator tabs_const_iterator;
+
+ const LLAccordionCtrlTab* result = 0;
+
+ for (tabs_const_iterator i = mAccordionTabs.begin(); i != mAccordionTabs.end(); ++i)
+ {
+ if ((*i)->isExpanded())
+ {
+ result = *i;
+ break;
+ }
+ }
+
+ return result;
+}
+
+S32 LLAccordionCtrl::calcExpandedTabHeight(S32 tab_index /* = 0 */, S32 available_height /* = 0 */)
+{
+ if (tab_index < 0)
+ {
+ return available_height;
+ }
+
+ S32 collapsed_tabs_height = 0;
+ S32 num_expanded = 0;
+
+ for (size_t n = tab_index; n < mAccordionTabs.size(); ++n)
+ {
+ if (!mAccordionTabs[n]->isExpanded())
+ {
+ collapsed_tabs_height += mAccordionTabs[n]->getHeaderHeight();
+ }
+ else
+ {
+ ++num_expanded;
+ }
+ }
+
+ if (0 == num_expanded)
+ {
+ return available_height;
+ }
+
+ S32 expanded_tab_height = available_height - collapsed_tabs_height - BORDER_MARGIN; // top BORDER_MARGIN is added in arrange(), here we add bottom BORDER_MARGIN
+ expanded_tab_height /= num_expanded;
+ return expanded_tab_height;
+}
+
+void LLAccordionCtrl::collapseAllTabs()
+{
+ if (mAccordionTabs.size() > 0)
+ {
+ for (size_t i = 0; i < mAccordionTabs.size(); ++i)
+ {
+ LLAccordionCtrlTab *tab = mAccordionTabs[i];
+
+ if (tab->getDisplayChildren())
+ {
+ tab->setDisplayChildren(false);
+ }
+ }
+ arrange();
+ }
+}
diff --git a/indra/llui/llaccordionctrl.h b/indra/llui/llaccordionctrl.h
index 871c0a8f56..baeed0a3a0 100644
--- a/indra/llui/llaccordionctrl.h
+++ b/indra/llui/llaccordionctrl.h
@@ -1,198 +1,200 @@
-/**
- * @file LLAccordionCtrl.h
- * @brief Accordion Panel implementation
- *
- * $LicenseInfo:firstyear=2004&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_ACCORDIONCTRL_H
-#define LL_ACCORDIONCTRL_H
-
-#include "llpanel.h"
-#include "lltextbox.h"
-#include "llscrollbar.h"
-
-#include <vector>
-#include <algorithm>
-#include <string>
-
-class LLAccordionCtrlTab;
-
-class LLAccordionCtrl: public LLPanel
-{
-private:
-
- std::vector<LLAccordionCtrlTab*> mAccordionTabs;
-
- void ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height);
- void ctrlShiftVertical(LLView* panel,S32 delta);
-
- void onCollapseCtrlCloseOpen(S16 panel_num);
- void shiftAccordionTabs(S16 panel_num, S32 delta);
-
-
-public:
- /**
- * Abstract comparator for accordion tabs.
- */
- class LLTabComparator
- {
- public:
- LLTabComparator() {};
- virtual ~LLTabComparator() {};
-
- /** Returns true if tab1 < tab2, false otherwise */
- virtual bool compare(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2) const = 0;
- };
-
- struct Params
- : public LLInitParam::Block<Params, LLPanel::Params>
- {
- Optional<bool> single_expansion,
- fit_parent; /* Accordion will fit its parent size, controls that are placed into
- accordion tabs are responsible for scrolling their content.
- *NOTE fit_parent works best when combined with single_expansion.
- Accordion view should implement getRequiredRect() and provide valid height*/
- Optional<LLTextBox::Params> no_matched_tabs_text;
- Optional<LLTextBox::Params> no_visible_tabs_text;
-
- Params()
- : single_expansion("single_expansion",false)
- , fit_parent("fit_parent", false)
- , no_matched_tabs_text("no_matched_tabs_text")
- , no_visible_tabs_text("no_visible_tabs_text")
- {};
- };
-
- LLAccordionCtrl(const Params& params);
-
- LLAccordionCtrl();
- virtual ~LLAccordionCtrl();
-
- virtual bool postBuild();
-
- virtual bool handleRightMouseDown ( S32 x, S32 y, MASK mask);
- virtual bool handleScrollWheel ( S32 x, S32 y, S32 clicks );
- virtual bool handleKeyHere (KEY key, MASK mask);
- virtual bool handleDragAndDrop (S32 x, S32 y, MASK mask, bool drop,
- EDragAndDropType cargo_type,
- void* cargo_data,
- EAcceptance* accept,
- std::string& tooltip_msg);
- //
-
- // Call reshape after changing splitter's size
- virtual void reshape(S32 width, S32 height, bool called_from_parent = true);
-
- void addCollapsibleCtrl(LLView* view);
- void removeCollapsibleCtrl(LLView* view);
- void arrange();
-
-
- void draw();
-
- void onScrollPosChangeCallback(S32, LLScrollbar*);
- virtual void onUpdateScrollToChild(const LLUICtrl * cntrl);
-
- void onOpen (const LLSD& key);
- S32 notifyParent(const LLSD& info);
-
- void reset ();
- void expandDefaultTab();
-
- void setComparator(const LLTabComparator* comp) { mTabComparator = comp; }
- void sort();
-
- /**
- * Sets filter substring as a search_term for help text when there are no any visible tabs.
- */
- void setFilterSubString(const std::string& filter_string);
-
- /**
- * This method returns the first expanded accordion tab.
- * It is expected to be called for accordion which doesn't allow multiple
- * tabs to be expanded. Use with care.
- */
- const LLAccordionCtrlTab* getExpandedTab() const;
-
- LLAccordionCtrlTab* getSelectedTab() const { return mSelectedTab; }
-
- bool getFitParent() const {return mFitParent;}
-
- void setSkipScrollToChild(bool skip) { mSkipScrollToChild = skip; }
-
-private:
- void initNoTabsWidget(const LLTextBox::Params& tb_params);
- void updateNoTabsHelpTextVisibility();
-
- void arrangeSingle();
- void arrangeMultiple();
-
- // Calc Splitter's height that is necessary to display all child content
- S32 calcRecuiredHeight();
- S32 getRecuiredHeight() const { return mInnerRect.getHeight(); }
- S32 calcExpandedTabHeight(S32 tab_index = 0, S32 available_height = 0);
-
- void updateLayout (S32 width, S32 height);
-
- void show_hide_scrollbar (S32 width, S32 height);
-
- void showScrollbar (S32 width, S32 height);
- void hideScrollbar (S32 width, S32 height);
-
- bool autoScroll (S32 x, S32 y);
-
- /**
- * An adaptor for LLTabComparator
- */
- struct LLComparatorAdaptor
- {
- LLComparatorAdaptor(const LLTabComparator& comparator) : mComparator(comparator) {};
-
- bool operator()(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2)
- {
- return mComparator.compare(tab1, tab2);
- }
-
- const LLTabComparator& mComparator;
- };
-
-private:
- LLRect mInnerRect;
- LLScrollbar* mScrollbar;
- bool mSingleExpansion;
- bool mFitParent;
- bool mAutoScrolling;
- F32 mAutoScrollRate;
- LLTextBox* mNoVisibleTabsHelpText;
-
- bool mSkipScrollToChild;
-
- std::string mNoMatchedTabsOrigString;
- std::string mNoVisibleTabsOrigString;
-
- LLAccordionCtrlTab* mSelectedTab;
- const LLTabComparator* mTabComparator;
-};
-
-
-#endif // LL_LLSPLITTER_H
+/**
+ * @file LLAccordionCtrl.h
+ * @brief Accordion Panel implementation
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_ACCORDIONCTRL_H
+#define LL_ACCORDIONCTRL_H
+
+#include "llpanel.h"
+#include "lltextbox.h"
+#include "llscrollbar.h"
+
+#include <vector>
+#include <algorithm>
+#include <string>
+
+class LLAccordionCtrlTab;
+
+class LLAccordionCtrl: public LLPanel
+{
+private:
+
+ std::vector<LLAccordionCtrlTab*> mAccordionTabs;
+
+ void ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height);
+ void ctrlShiftVertical(LLView* panel,S32 delta);
+
+ void onCollapseCtrlCloseOpen(S16 panel_num);
+ void shiftAccordionTabs(S16 panel_num, S32 delta);
+
+
+public:
+ /**
+ * Abstract comparator for accordion tabs.
+ */
+ class LLTabComparator
+ {
+ public:
+ LLTabComparator() {};
+ virtual ~LLTabComparator() {};
+
+ /** Returns true if tab1 < tab2, false otherwise */
+ virtual bool compare(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2) const = 0;
+ };
+
+ struct Params
+ : public LLInitParam::Block<Params, LLPanel::Params>
+ {
+ Optional<bool> single_expansion,
+ fit_parent; /* Accordion will fit its parent size, controls that are placed into
+ accordion tabs are responsible for scrolling their content.
+ *NOTE fit_parent works best when combined with single_expansion.
+ Accordion view should implement getRequiredRect() and provide valid height*/
+ Optional<LLTextBox::Params> no_matched_tabs_text;
+ Optional<LLTextBox::Params> no_visible_tabs_text;
+
+ Params()
+ : single_expansion("single_expansion",false)
+ , fit_parent("fit_parent", false)
+ , no_matched_tabs_text("no_matched_tabs_text")
+ , no_visible_tabs_text("no_visible_tabs_text")
+ {};
+ };
+
+ LLAccordionCtrl(const Params& params);
+
+ LLAccordionCtrl();
+ virtual ~LLAccordionCtrl();
+
+ virtual bool postBuild();
+
+ virtual bool handleRightMouseDown ( S32 x, S32 y, MASK mask);
+ virtual bool handleScrollWheel ( S32 x, S32 y, S32 clicks );
+ virtual bool handleKeyHere (KEY key, MASK mask);
+ virtual bool handleDragAndDrop (S32 x, S32 y, MASK mask, bool drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg);
+ //
+
+ // Call reshape after changing splitter's size
+ virtual void reshape(S32 width, S32 height, bool called_from_parent = true);
+
+ void addCollapsibleCtrl(LLView* view);
+ void removeCollapsibleCtrl(LLView* view);
+ void arrange();
+
+
+ void draw();
+
+ void onScrollPosChangeCallback(S32, LLScrollbar*);
+ virtual void onUpdateScrollToChild(const LLUICtrl * cntrl);
+
+ void onOpen (const LLSD& key);
+ S32 notifyParent(const LLSD& info);
+
+ void reset ();
+ void expandDefaultTab();
+
+ void setComparator(const LLTabComparator* comp) { mTabComparator = comp; }
+ void sort();
+
+ void collapseAllTabs();
+
+ /**
+ * Sets filter substring as a search_term for help text when there are no any visible tabs.
+ */
+ void setFilterSubString(const std::string& filter_string);
+
+ /**
+ * This method returns the first expanded accordion tab.
+ * It is expected to be called for accordion which doesn't allow multiple
+ * tabs to be expanded. Use with care.
+ */
+ const LLAccordionCtrlTab* getExpandedTab() const;
+
+ LLAccordionCtrlTab* getSelectedTab() const { return mSelectedTab; }
+
+ bool getFitParent() const {return mFitParent;}
+
+ void setSkipScrollToChild(bool skip) { mSkipScrollToChild = skip; }
+
+private:
+ void initNoTabsWidget(const LLTextBox::Params& tb_params);
+ void updateNoTabsHelpTextVisibility();
+
+ void arrangeSingle();
+ void arrangeMultiple();
+
+ // Calc Splitter's height that is necessary to display all child content
+ S32 calcRecuiredHeight();
+ S32 getRecuiredHeight() const { return mInnerRect.getHeight(); }
+ S32 calcExpandedTabHeight(S32 tab_index = 0, S32 available_height = 0);
+
+ void updateLayout (S32 width, S32 height);
+
+ void show_hide_scrollbar (S32 width, S32 height);
+
+ void showScrollbar (S32 width, S32 height);
+ void hideScrollbar (S32 width, S32 height);
+
+ bool autoScroll (S32 x, S32 y);
+
+ /**
+ * An adaptor for LLTabComparator
+ */
+ struct LLComparatorAdaptor
+ {
+ LLComparatorAdaptor(const LLTabComparator& comparator) : mComparator(comparator) {};
+
+ bool operator()(const LLAccordionCtrlTab* tab1, const LLAccordionCtrlTab* tab2)
+ {
+ return mComparator.compare(tab1, tab2);
+ }
+
+ const LLTabComparator& mComparator;
+ };
+
+private:
+ LLRect mInnerRect;
+ LLScrollbar* mScrollbar;
+ bool mSingleExpansion;
+ bool mFitParent;
+ bool mAutoScrolling;
+ F32 mAutoScrollRate;
+ LLTextBox* mNoVisibleTabsHelpText;
+
+ bool mSkipScrollToChild;
+
+ std::string mNoMatchedTabsOrigString;
+ std::string mNoVisibleTabsOrigString;
+
+ LLAccordionCtrlTab* mSelectedTab;
+ const LLTabComparator* mTabComparator;
+};
+
+
+#endif // LL_LLSPLITTER_H
diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp
index 5945c31407..17f9da8707 100644
--- a/indra/llui/llaccordionctrltab.cpp
+++ b/indra/llui/llaccordionctrltab.cpp
@@ -1,1126 +1,1126 @@
-/**
- * @file LLAccordionCtrlTab.cpp
- * @brief Collapsible control implementation
- *
- * $LicenseInfo:firstyear=2009&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llaccordionctrltab.h"
-#include "llaccordionctrl.h"
-
-#include "lllocalcliprect.h"
-#include "llscrollbar.h"
-#include "lltextbox.h"
-#include "lltextutil.h"
-#include "lluictrl.h"
-
-static const std::string DD_BUTTON_NAME = "dd_button";
-static const std::string DD_TEXTBOX_NAME = "dd_textbox";
-static const std::string DD_HEADER_NAME = "dd_header";
-
-static const S32 HEADER_HEIGHT = 23;
-static const S32 HEADER_IMAGE_LEFT_OFFSET = 5;
-static const S32 HEADER_TEXT_LEFT_OFFSET = 30;
-static const F32 AUTO_OPEN_TIME = 1.f;
-static const S32 VERTICAL_MULTIPLE = 16;
-static const S32 PARENT_BORDER_MARGIN = 5;
-
-static LLDefaultChildRegistry::Register<LLAccordionCtrlTab> t1("accordion_tab");
-
-class LLAccordionCtrlTab::LLAccordionCtrlTabHeader : public LLUICtrl
-{
-public:
- friend class LLUICtrlFactory;
-
- struct Params : public LLInitParam::Block<Params, LLAccordionCtrlTab::Params>
- {
- Params();
- };
-
- LLAccordionCtrlTabHeader(const LLAccordionCtrlTabHeader::Params& p);
-
- virtual ~LLAccordionCtrlTabHeader();
-
- virtual void draw();
-
- virtual void reshape(S32 width, S32 height, bool called_from_parent = true);
-
- virtual bool postBuild();
-
- std::string getTitle();
- void setTitle(const std::string& title, const std::string& hl);
-
- void setTitleFontStyle(std::string style);
-
- void setTitleColor(LLUIColor);
-
- void setSelected(bool is_selected) { mIsSelected = is_selected; }
-
- virtual void onMouseEnter(S32 x, S32 y, MASK mask);
- virtual void onMouseLeave(S32 x, S32 y, MASK mask);
- virtual bool handleKey(KEY key, MASK mask, bool called_from_parent);
- virtual bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
- EDragAndDropType cargo_type,
- void* cargo_data,
- EAcceptance* accept,
- std::string& tooltip_msg);
-
-private:
- LLTextBox* mHeaderTextbox;
-
- // Overlay images (arrows)
- LLPointer<LLUIImage> mImageCollapsed;
- LLPointer<LLUIImage> mImageExpanded;
- LLPointer<LLUIImage> mImageCollapsedPressed;
- LLPointer<LLUIImage> mImageExpandedPressed;
-
- // Background images
- LLPointer<LLUIImage> mImageHeader;
- LLPointer<LLUIImage> mImageHeaderOver;
- LLPointer<LLUIImage> mImageHeaderPressed;
- LLPointer<LLUIImage> mImageHeaderFocused;
-
- // style saved when applying it in setTitleFontStyle
- LLStyle::Params mStyleParams;
-
- LLUIColor mHeaderBGColor;
-
- bool mNeedsHighlight;
- bool mIsSelected;
-
- LLFrameTimer mAutoOpenTimer;
-};
-
-LLAccordionCtrlTab::LLAccordionCtrlTabHeader::Params::Params()
-{
-}
-
-LLAccordionCtrlTab::LLAccordionCtrlTabHeader::LLAccordionCtrlTabHeader(
- const LLAccordionCtrlTabHeader::Params& p)
-: LLUICtrl(p)
-, mHeaderBGColor(p.header_bg_color())
-, mNeedsHighlight(false)
-, mIsSelected(false),
- mImageCollapsed(p.header_collapse_img),
- mImageCollapsedPressed(p.header_collapse_img_pressed),
- mImageExpanded(p.header_expand_img),
- mImageExpandedPressed(p.header_expand_img_pressed),
- mImageHeader(p.header_image),
- mImageHeaderOver(p.header_image_over),
- mImageHeaderPressed(p.header_image_pressed),
- mImageHeaderFocused(p.header_image_focused)
-{
- LLTextBox::Params textboxParams;
- textboxParams.name(DD_TEXTBOX_NAME);
- textboxParams.initial_value(p.title());
- textboxParams.text_color(p.header_text_color());
- textboxParams.follows.flags(FOLLOWS_NONE);
- textboxParams.font( p.font() );
- textboxParams.font_shadow(LLFontGL::NO_SHADOW);
- textboxParams.use_ellipses = true;
- textboxParams.bg_visible = false;
- textboxParams.mouse_opaque = false;
- textboxParams.parse_urls = false;
- mHeaderTextbox = LLUICtrlFactory::create<LLTextBox>(textboxParams);
- addChild(mHeaderTextbox);
-}
-
-LLAccordionCtrlTab::LLAccordionCtrlTabHeader::~LLAccordionCtrlTabHeader()
-{
-}
-
-bool LLAccordionCtrlTab::LLAccordionCtrlTabHeader::postBuild()
-{
- return true;
-}
-
-std::string LLAccordionCtrlTab::LLAccordionCtrlTabHeader::getTitle()
-{
- if (mHeaderTextbox)
- {
- return mHeaderTextbox->getText();
- }
-
- return LLStringUtil::null;
-}
-
-void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitle(const std::string& title, const std::string& hl)
-{
- if (mHeaderTextbox)
- {
- LLTextUtil::textboxSetHighlightedVal(
- mHeaderTextbox,
- mStyleParams,
- title,
- hl);
- }
-}
-
-void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitleFontStyle(std::string style)
-{
- if (mHeaderTextbox)
- {
- std::string text = mHeaderTextbox->getText();
- mStyleParams.font(mHeaderTextbox->getFont());
- mStyleParams.font.style(style);
- mHeaderTextbox->setText(text, mStyleParams);
- }
-}
-
-void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitleColor(LLUIColor color)
-{
- if (mHeaderTextbox)
- {
- mHeaderTextbox->setColor(color);
- }
-}
-
-void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw()
-{
- S32 width = getRect().getWidth();
- S32 height = getRect().getHeight();
-
- F32 alpha = getCurrentTransparency();
- gl_rect_2d(0, 0, width - 1, height - 1, mHeaderBGColor.get() % alpha, true);
-
- LLAccordionCtrlTab* parent = dynamic_cast<LLAccordionCtrlTab*>(getParent());
- bool collapsible = parent && parent->getCollapsible();
- bool expanded = parent && parent->getDisplayChildren();
-
- // Handle overlay images, if needed
- // Only show green "focus" background image if the accordion is open,
- // because the user's mental model of focus is that it goes away after
- // the accordion is closed.
- if (getParent()->hasFocus() || mIsSelected
- /*&& !(collapsible && !expanded)*/ // WHY??
- )
- {
- mImageHeaderFocused->draw(0, 0, width, height);
- }
- else
- {
- mImageHeader->draw(0, 0, width, height);
- }
-
- if (mNeedsHighlight)
- {
- mImageHeaderOver->draw(0, 0, width, height);
- }
-
- if (collapsible)
- {
- LLPointer<LLUIImage> overlay_image;
- if (expanded)
- {
- overlay_image = mImageExpanded;
- }
- else
- {
- overlay_image = mImageCollapsed;
- }
- overlay_image->draw(HEADER_IMAGE_LEFT_OFFSET, (height - overlay_image->getHeight()) / 2);
- }
-
- LLUICtrl::draw();
-}
-
-void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::reshape(S32 width, S32 height, bool called_from_parent /* = true */)
-{
- S32 header_height = mHeaderTextbox->getTextPixelHeight();
-
- LLRect textboxRect(HEADER_TEXT_LEFT_OFFSET, (height + header_height) / 2, width, (height - header_height) / 2);
- mHeaderTextbox->reshape(textboxRect.getWidth(), textboxRect.getHeight());
- mHeaderTextbox->setRect(textboxRect);
-
- if (mHeaderTextbox->getTextPixelWidth() > mHeaderTextbox->getRect().getWidth())
- {
- setToolTip(mHeaderTextbox->getText());
- }
- else
- {
- setToolTip(LLStringUtil::null);
- }
-}
-
-void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::onMouseEnter(S32 x, S32 y, MASK mask)
-{
- LLUICtrl::onMouseEnter(x, y, mask);
- mNeedsHighlight = true;
-}
-
-void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::onMouseLeave(S32 x, S32 y, MASK mask)
-{
- LLUICtrl::onMouseLeave(x, y, mask);
- mNeedsHighlight = false;
- mAutoOpenTimer.stop();
-}
-
-bool LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleKey(KEY key, MASK mask, bool called_from_parent)
-{
- if ((key == KEY_LEFT || key == KEY_RIGHT) && mask == MASK_NONE)
- {
- return getParent()->handleKey(key, mask, called_from_parent);
- }
-
- return LLUICtrl::handleKey(key, mask, called_from_parent);
-}
-
-bool LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleDragAndDrop(S32 x, S32 y, MASK mask,
- bool drop,
- EDragAndDropType cargo_type,
- void* cargo_data,
- EAcceptance* accept,
- std::string& tooltip_msg)
-{
- LLAccordionCtrlTab* parent = dynamic_cast<LLAccordionCtrlTab*>(getParent());
-
- if (parent && !parent->getDisplayChildren() && parent->getCollapsible() && parent->canOpenClose())
- {
- if (mAutoOpenTimer.getStarted())
- {
- if (mAutoOpenTimer.getElapsedTimeF32() > AUTO_OPEN_TIME)
- {
- parent->changeOpenClose(false);
- mAutoOpenTimer.stop();
- return true;
- }
- }
- else
- {
- mAutoOpenTimer.start();
- }
- }
-
- return LLUICtrl::handleDragAndDrop(x, y, mask, drop, cargo_type,
- cargo_data, accept, tooltip_msg);
-}
-
-LLAccordionCtrlTab::Params::Params()
- : title("title")
- ,display_children("expanded", true)
- ,header_height("header_height", HEADER_HEIGHT),
- min_width("min_width", 0),
- min_height("min_height", 0)
- ,collapsible("collapsible", true)
- ,header_bg_color("header_bg_color")
- ,dropdown_bg_color("dropdown_bg_color")
- ,header_visible("header_visible",true)
- ,padding_left("padding_left",2)
- ,padding_right("padding_right",2)
- ,padding_top("padding_top",2)
- ,padding_bottom("padding_bottom",2)
- ,header_expand_img("header_expand_img")
- ,header_expand_img_pressed("header_expand_img_pressed")
- ,header_collapse_img("header_collapse_img")
- ,header_collapse_img_pressed("header_collapse_img_pressed")
- ,header_image("header_image")
- ,header_image_over("header_image_over")
- ,header_image_pressed("header_image_pressed")
- ,header_image_focused("header_image_focused")
- ,header_text_color("header_text_color")
- ,fit_panel("fit_panel",true)
- ,selection_enabled("selection_enabled", false)
-{
- changeDefault(mouse_opaque, false);
-}
-
-LLAccordionCtrlTab::LLAccordionCtrlTab(const LLAccordionCtrlTab::Params&p)
- : LLUICtrl(p)
- ,mDisplayChildren(p.display_children)
- ,mCollapsible(p.collapsible)
- ,mExpandedHeight(0)
- ,mDropdownBGColor(p.dropdown_bg_color())
- ,mHeaderVisible(p.header_visible)
- ,mPaddingLeft(p.padding_left)
- ,mPaddingRight(p.padding_right)
- ,mPaddingTop(p.padding_top)
- ,mPaddingBottom(p.padding_bottom)
- ,mCanOpenClose(true)
- ,mFitPanel(p.fit_panel)
- ,mSelectionEnabled(p.selection_enabled)
- ,mContainerPanel(NULL)
- ,mScrollbar(NULL)
-{
- mStoredOpenCloseState = false;
- mWasStateStored = false;
- mSkipChangesOnNotifyParent = false;
-
- mDropdownBGColor = LLColor4::white;
- LLAccordionCtrlTabHeader::Params headerParams;
- headerParams.name(DD_HEADER_NAME);
- headerParams.title(p.title);
- mHeader = LLUICtrlFactory::create<LLAccordionCtrlTabHeader>(headerParams);
- addChild(mHeader, 1);
-
- LLFocusableElement::setFocusReceivedCallback(boost::bind(&LLAccordionCtrlTab::selectOnFocusReceived, this));
-
- if (!p.selection_enabled)
- {
- LLFocusableElement::setFocusLostCallback(boost::bind(&LLAccordionCtrlTab::deselectOnFocusLost, this));
- }
-
- reshape(100, 200,false);
-}
-
-LLAccordionCtrlTab::~LLAccordionCtrlTab()
-{
-}
-
-void LLAccordionCtrlTab::setDisplayChildren(bool display)
-{
- mDisplayChildren = display;
- LLRect rect = getRect();
-
- rect.mBottom = rect.mTop - (getDisplayChildren() ? mExpandedHeight : HEADER_HEIGHT);
- setRect(rect);
-
- if (mContainerPanel)
- {
- mContainerPanel->setVisible(getDisplayChildren());
- }
-
- if (mDisplayChildren)
- {
- adjustContainerPanel();
- }
- else
- {
- if (mScrollbar)
- mScrollbar->setVisible(false);
- }
-}
-
-void LLAccordionCtrlTab::reshape(S32 width, S32 height, bool called_from_parent /* = true */)
-{
- LLRect headerRect;
-
- headerRect.setLeftTopAndSize(0, height, width, HEADER_HEIGHT);
- mHeader->setRect(headerRect);
- mHeader->reshape(headerRect.getWidth(), headerRect.getHeight());
-
- if (!mDisplayChildren)
- return;
-
- LLRect childRect;
-
- childRect.setLeftTopAndSize(
- getPaddingLeft(),
- height - getHeaderHeight() - getPaddingTop(),
- width - getPaddingLeft() - getPaddingRight(),
- height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() );
-
- adjustContainerPanel(childRect);
-}
-
-void LLAccordionCtrlTab::changeOpenClose(bool is_open)
-{
- if (is_open)
- mExpandedHeight = getRect().getHeight();
-
- setDisplayChildren(!is_open);
- reshape(getRect().getWidth(), getRect().getHeight(), false);
- if (mCommitSignal)
- {
- (*mCommitSignal)(this, getDisplayChildren());
- }
-}
-
-void LLAccordionCtrlTab::onVisibilityChange(bool new_visibility)
-{
- LLUICtrl::onVisibilityChange(new_visibility);
-
- notifyParent(LLSD().with("child_visibility_change", new_visibility));
-}
-
-// virtual
-void LLAccordionCtrlTab::onUpdateScrollToChild(const LLUICtrl *cntrl)
-{
- if (mScrollbar && mScrollbar->getVisible())
- {
- LLRect rect;
- cntrl->localRectToOtherView(cntrl->getLocalRect(), &rect, this);
-
- // Translate to parent coordinatess to check if we are in visible rectangle
- rect.translate(getRect().mLeft, getRect().mBottom);
-
- if (!getRect().contains(rect))
- {
- // for accordition's scroll, height is in pixels
- // Back to local coords and calculate position for scroller
- S32 bottom = mScrollbar->getDocPos() - rect.mBottom + getRect().mBottom;
- S32 top = mScrollbar->getDocPos() - rect.mTop + getRect().mTop;
-
- S32 scroll_pos = llclamp(mScrollbar->getDocPos(),
- bottom, // min vertical scroll
- top); // max vertical scroll
-
- mScrollbar->setDocPos(scroll_pos);
- }
- }
-
- LLUICtrl::onUpdateScrollToChild(cntrl);
-}
-
-bool LLAccordionCtrlTab::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- if (mCollapsible && mHeaderVisible && mCanOpenClose)
- {
- if (y >= (getRect().getHeight() - HEADER_HEIGHT))
- {
- mHeader->setFocus(true);
- changeOpenClose(getDisplayChildren());
-
- // Reset stored state
- mWasStateStored = false;
- return true;
- }
- }
- return LLUICtrl::handleMouseDown(x,y,mask);
-}
-
-bool LLAccordionCtrlTab::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- return LLUICtrl::handleMouseUp(x,y,mask);
-}
-
-boost::signals2::connection LLAccordionCtrlTab::setDropDownStateChangedCallback(commit_callback_t cb)
-{
- return setCommitCallback(cb);
-}
-
-bool LLAccordionCtrlTab::addChild(LLView* child, S32 tab_group)
-{
- if (DD_HEADER_NAME != child->getName())
- {
- reshape(child->getRect().getWidth() , child->getRect().getHeight() + HEADER_HEIGHT );
- mExpandedHeight = getRect().getHeight();
- }
-
- bool res = LLUICtrl::addChild(child, tab_group);
-
- if (DD_HEADER_NAME != child->getName())
- {
- if (!mCollapsible)
- setDisplayChildren(true);
- else
- setDisplayChildren(getDisplayChildren());
- }
-
- if (!mContainerPanel)
- mContainerPanel = findContainerView();
-
- return res;
-}
-
-void LLAccordionCtrlTab::setAccordionView(LLView* panel)
-{
- addChild(panel, 0);
-}
-
-std::string LLAccordionCtrlTab::getTitle() const
-{
- if (mHeader)
- {
- return mHeader->getTitle();
- }
-
- return LLStringUtil::null;
-}
-
-void LLAccordionCtrlTab::setTitle(const std::string& title, const std::string& hl)
-{
- if (mHeader)
- {
- mHeader->setTitle(title, hl);
- }
-}
-
-void LLAccordionCtrlTab::setTitleFontStyle(std::string style)
-{
- if (mHeader)
- {
- mHeader->setTitleFontStyle(style);
- }
-}
-
-void LLAccordionCtrlTab::setTitleColor(LLUIColor color)
-{
- if (mHeader)
- {
- mHeader->setTitleColor(color);
- }
-}
-
-boost::signals2::connection LLAccordionCtrlTab::setFocusReceivedCallback(const focus_signal_t::slot_type& cb)
-{
- if (mHeader)
- {
- return mHeader->setFocusReceivedCallback(cb);
- }
-
- return boost::signals2::connection();
-}
-
-boost::signals2::connection LLAccordionCtrlTab::setFocusLostCallback(const focus_signal_t::slot_type& cb)
-{
- if (mHeader)
- {
- return mHeader->setFocusLostCallback(cb);
- }
-
- return boost::signals2::connection();
-}
-
-void LLAccordionCtrlTab::setSelected(bool is_selected)
-{
- if (mHeader)
- {
- mHeader->setSelected(is_selected);
- }
-}
-
-LLView* LLAccordionCtrlTab::findContainerView()
-{
- child_list_const_iter_t it = getChildList()->begin(), it_end = getChildList()->end();
- while (it != it_end)
- {
- LLView* child = *(it++);
- if (DD_HEADER_NAME != child->getName() && child->getVisible())
- return child;
- }
-
- return NULL;
-}
-
-void LLAccordionCtrlTab::selectOnFocusReceived()
-{
- if (getParent()) // A parent may not be set if tabs are added dynamically.
- {
- getParent()->notifyParent(LLSD().with("action", "select_current"));
- }
-}
-
-void LLAccordionCtrlTab::deselectOnFocusLost()
-{
- if (getParent()) // A parent may not be set if tabs are added dynamically.
- {
- getParent()->notifyParent(LLSD().with("action", "deselect_current"));
- }
-}
-
-S32 LLAccordionCtrlTab::getHeaderHeight()
-{
- return mHeaderVisible ? HEADER_HEIGHT : 0;
-}
-
-void LLAccordionCtrlTab::setHeaderVisible(bool value)
-{
- if (mHeaderVisible == value)
- return;
-
- mHeaderVisible = value;
-
- if (mHeader)
- {
- mHeader->setVisible(value);
- }
-
- reshape(getRect().getWidth(), getRect().getHeight(), false);
-};
-
-//virtual
-bool LLAccordionCtrlTab::postBuild()
-{
- if (mHeader)
- {
- mHeader->setVisible(mHeaderVisible);
- }
-
- static LLUICachedControl<S32> scrollbar_size("UIScrollbarSize", 0);
-
- LLRect scroll_rect;
- scroll_rect.setOriginAndSize(
- getRect().getWidth() - scrollbar_size,
- 1,
- scrollbar_size,
- getRect().getHeight() - 1);
-
- mContainerPanel = findContainerView();
-
- if (!mFitPanel)
- {
- LLScrollbar::Params sbparams;
- sbparams.name("scrollable vertical");
- sbparams.rect(scroll_rect);
- sbparams.orientation(LLScrollbar::VERTICAL);
- sbparams.doc_size(getRect().getHeight());
- sbparams.doc_pos(0);
- sbparams.page_size(getRect().getHeight());
- sbparams.step_size(VERTICAL_MULTIPLE);
- sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
- sbparams.change_callback(boost::bind(&LLAccordionCtrlTab::onScrollPosChangeCallback, this, _1, _2));
-
- mScrollbar = LLUICtrlFactory::create<LLScrollbar>(sbparams);
- LLView::addChild(mScrollbar);
- mScrollbar->setFollowsRight();
- mScrollbar->setFollowsTop();
- mScrollbar->setFollowsBottom();
-
- mScrollbar->setVisible(false);
- }
-
- if (mContainerPanel)
- {
- mContainerPanel->setVisible(mDisplayChildren);
- }
-
- return LLUICtrl::postBuild();
-}
-
-bool LLAccordionCtrlTab::notifyChildren (const LLSD& info)
-{
- if (info.has("action"))
- {
- std::string str_action = info["action"];
- if (str_action == "store_state")
- {
- storeOpenCloseState();
- return true;
- }
-
- if (str_action == "restore_state")
- {
- restoreOpenCloseState();
- return true;
- }
- }
-
- return LLUICtrl::notifyChildren(info);
-}
-
-S32 LLAccordionCtrlTab::notifyParent(const LLSD& info)
-{
- if (info.has("action"))
- {
- std::string str_action = info["action"];
- if (str_action == "size_changes")
- {
- S32 height = info["height"];
- height = llmax(height, 10) + HEADER_HEIGHT + getPaddingTop() + getPaddingBottom();
-
- mExpandedHeight = height;
-
- if (isExpanded() && !mSkipChangesOnNotifyParent)
- {
- LLRect panel_rect = getRect();
- panel_rect.setLeftTopAndSize( panel_rect.mLeft, panel_rect.mTop, panel_rect.getWidth(), height);
- reshape(getRect().getWidth(),height);
- setRect(panel_rect);
- }
-
- // LLAccordionCtrl should rearrange accordion tab if one of accordions changed its size
- if (getParent()) // A parent may not be set if tabs are added dynamically.
- getParent()->notifyParent(info);
- return 1;
- }
-
- if (str_action == "select_prev")
- {
- showAndFocusHeader();
- return 1;
- }
- }
- else if (info.has("scrollToShowRect"))
- {
- LLAccordionCtrl* parent = dynamic_cast<LLAccordionCtrl*>(getParent());
- if (parent && parent->getFitParent())
- {
- // EXT-8285 ('No attachments worn' text appears at the bottom of blank 'Attachments' accordion)
- // The problem was in passing message "scrollToShowRect" IN LLAccordionCtrlTab::notifyParent
- // FROM child LLScrollContainer TO parent LLAccordionCtrl with "it_parent" set to true.
-
- // It is wrong notification for parent accordion which leads to recursive call of adjustContainerPanel
- // As the result of recursive call of adjustContainerPanel we got LLAccordionCtrlTab
- // that reshaped and re-sized with different rectangles.
-
- // LLAccordionCtrl has own scrollContainer and LLAccordionCtrlTab has own scrollContainer
- // both should handle own scroll container's event.
- // So, if parent accordion "fit_parent" accordion tab should handle its scroll container events itself.
-
- return 1;
- }
-
- if (!getDisplayChildren())
- {
- // Don't pass scrolling event further if our contents are invisible (STORM-298).
- return 1;
- }
- }
-
- return LLUICtrl::notifyParent(info);
-}
-
-S32 LLAccordionCtrlTab::notify(const LLSD& info)
-{
- if (info.has("action"))
- {
- std::string str_action = info["action"];
- if (str_action == "select_first")
- {
- showAndFocusHeader();
- return 1;
- }
-
- if (str_action == "select_last")
- {
- if (!getDisplayChildren())
- {
- showAndFocusHeader();
- }
- else
- {
- LLView* view = getAccordionView();
- if (view)
- {
- view->notify(LLSD().with("action", "select_last"));
- }
- }
- }
- }
-
- return 0;
-}
-
-bool LLAccordionCtrlTab::handleKey(KEY key, MASK mask, bool called_from_parent)
-{
- if (!mHeader->hasFocus())
- return LLUICtrl::handleKey(key, mask, called_from_parent);
-
- if ((key == KEY_RETURN) && mask == MASK_NONE)
- {
- changeOpenClose(getDisplayChildren());
- return true;
- }
-
- if ((key == KEY_ADD || key == KEY_RIGHT) && mask == MASK_NONE)
- {
- if (!getDisplayChildren())
- {
- changeOpenClose(getDisplayChildren());
- return true;
- }
- }
-
- if ((key == KEY_SUBTRACT || key == KEY_LEFT) && mask == MASK_NONE)
- {
- if (getDisplayChildren())
- {
- changeOpenClose(getDisplayChildren());
- return true;
- }
- }
-
- if (key == KEY_DOWN && mask == MASK_NONE)
- {
- // if collapsed go to the next accordion
- if (!getDisplayChildren())
- {
- // we're processing notifyParent so let call parent directly
- getParent()->notifyParent(LLSD().with("action", "select_next"));
- }
- else
- {
- getAccordionView()->notify(LLSD().with("action", "select_first"));
- }
- return true;
- }
-
- if (key == KEY_UP && mask == MASK_NONE)
- {
- // go to the previous accordion
-
- // we're processing notifyParent so let call parent directly
- getParent()->notifyParent(LLSD().with("action", "select_prev"));
- return true;
- }
-
- return LLUICtrl::handleKey(key, mask, called_from_parent);
-}
-
-void LLAccordionCtrlTab::showAndFocusHeader()
-{
- if (!mHeader)
- {
- return;
- }
-
- mHeader->setFocus(true);
- mHeader->setSelected(mSelectionEnabled);
-
- LLRect screen_rc;
- LLRect selected_rc = mHeader->getRect();
- localRectToScreen(selected_rc, &screen_rc);
-
- // This call to notifyParent() is intended to deliver "scrollToShowRect" command
- // to the parent LLAccordionCtrl so by calling it from the direct parent of this
- // accordion tab (assuming that the parent is an LLAccordionCtrl) the calls chain
- // is shortened and messages from inside the collapsed tabs are avoided.
- // See STORM-536.
- getParent()->notifyParent(LLSD().with("scrollToShowRect", screen_rc.getValue()));
-}
-
-void LLAccordionCtrlTab::storeOpenCloseState()
-{
- if (mWasStateStored)
- return;
- mStoredOpenCloseState = getDisplayChildren();
- mWasStateStored = true;
-}
-
-void LLAccordionCtrlTab::restoreOpenCloseState()
-{
- if (!mWasStateStored)
- return;
- if (getDisplayChildren() != mStoredOpenCloseState)
- {
- changeOpenClose(getDisplayChildren());
- }
- mWasStateStored = false;
-}
-
-void LLAccordionCtrlTab::adjustContainerPanel()
-{
- S32 width = getRect().getWidth();
- S32 height = getRect().getHeight();
-
- LLRect child_rect;
- child_rect.setLeftTopAndSize(
- getPaddingLeft(),
- height - getHeaderHeight() - getPaddingTop(),
- width - getPaddingLeft() - getPaddingRight(),
- height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() );
-
- adjustContainerPanel(child_rect);
-}
-
-void LLAccordionCtrlTab::adjustContainerPanel(const LLRect& child_rect)
-{
- if (!mContainerPanel)
- return;
-
- if (!mFitPanel)
- {
- show_hide_scrollbar(child_rect);
- updateLayout(child_rect);
- }
- else
- {
- mContainerPanel->reshape(child_rect.getWidth(), child_rect.getHeight());
- mContainerPanel->setRect(child_rect);
- }
-}
-
-S32 LLAccordionCtrlTab::getChildViewHeight()
-{
- if (!mContainerPanel)
- return 0;
- return mContainerPanel->getRect().getHeight();
-}
-
-void LLAccordionCtrlTab::show_hide_scrollbar(const LLRect& child_rect)
-{
- if (getChildViewHeight() > child_rect.getHeight())
- showScrollbar(child_rect);
- else
- hideScrollbar(child_rect);
-}
-
-void LLAccordionCtrlTab::showScrollbar(const LLRect& child_rect)
-{
- if (!mContainerPanel || !mScrollbar)
- return;
- bool was_visible = mScrollbar->getVisible();
- mScrollbar->setVisible(true);
-
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
-
- ctrlSetLeftTopAndSize(mScrollbar,
- child_rect.getWidth() - scrollbar_size,
- child_rect.getHeight() - PARENT_BORDER_MARGIN,
- scrollbar_size,
- child_rect.getHeight() - PARENT_BORDER_MARGIN * 2);
-
- LLRect orig_rect = mContainerPanel->getRect();
-
- mScrollbar->setPageSize(child_rect.getHeight());
- mScrollbar->setDocParams(orig_rect.getHeight(), mScrollbar->getDocPos());
-
- if (was_visible)
- {
- S32 scroll_pos = llmin(mScrollbar->getDocPos(), orig_rect.getHeight() - child_rect.getHeight() - 1);
- mScrollbar->setDocPos(scroll_pos);
- }
- else // Shrink child panel
- {
- updateLayout(child_rect);
- }
-}
-
-void LLAccordionCtrlTab::hideScrollbar(const LLRect& child_rect)
-{
- if (!mContainerPanel || !mScrollbar)
- return;
-
- if (!mScrollbar->getVisible())
- return;
-
- mScrollbar->setVisible(false);
- mScrollbar->setDocPos(0);
-
- //shrink child panel
- updateLayout(child_rect);
-}
-
-void LLAccordionCtrlTab::onScrollPosChangeCallback(S32, LLScrollbar*)
-{
- LLRect child_rect;
-
- S32 width = getRect().getWidth();
- S32 height = getRect().getHeight();
-
- child_rect.setLeftTopAndSize(
- getPaddingLeft(),
- height - getHeaderHeight() - getPaddingTop(),
- width - getPaddingLeft() - getPaddingRight(),
- height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() );
-
- updateLayout(child_rect);
-}
-
-void LLAccordionCtrlTab::drawChild(const LLRect& root_rect, LLView* child)
-{
- if (child && child->getVisible() && child->getRect().isValid())
- {
- LLRect screen_rect;
- localRectToScreen(child->getRect(), &screen_rect);
-
- if (root_rect.overlaps(screen_rect) && sDirtyRect.overlaps(screen_rect))
- {
- gGL.matrixMode(LLRender::MM_MODELVIEW);
- LLUI::pushMatrix();
- {
- LLUI::translate((F32)child->getRect().mLeft, (F32)child->getRect().mBottom);
- child->draw();
- }
- LLUI::popMatrix();
- }
- }
-}
-
-void LLAccordionCtrlTab::draw()
-{
- if (mFitPanel)
- {
- LLUICtrl::draw();
- }
- else
- {
- LLRect root_rect(getRootView()->getRect());
- drawChild(root_rect, mHeader);
- drawChild(root_rect, mScrollbar);
-
- LLRect child_rect;
-
- S32 width = getRect().getWidth();
- S32 height = getRect().getHeight();
-
- child_rect.setLeftTopAndSize(
- getPaddingLeft(),
- height - getHeaderHeight() - getPaddingTop(),
- width - getPaddingLeft() - getPaddingRight(),
- height - getHeaderHeight() - getPaddingTop() - getPaddingBottom());
-
- LLLocalClipRect clip(child_rect);
- drawChild(root_rect,mContainerPanel);
- }
-}
-
-void LLAccordionCtrlTab::updateLayout(const LLRect& child_rect)
-{
- LLView* child = getAccordionView();
- if (!mContainerPanel)
- return;
-
- S32 panel_top = child_rect.getHeight();
- S32 panel_width = child_rect.getWidth();
-
- static LLUICachedControl<S32> scrollbar_size("UIScrollbarSize", 0);
- if (mScrollbar && mScrollbar->getVisible())
- {
- panel_top += mScrollbar->getDocPos();
- panel_width -= scrollbar_size;
- }
-
- // Set sizes for first panels and dragbars
- LLRect panel_rect = child->getRect();
- ctrlSetLeftTopAndSize(mContainerPanel, child_rect.mLeft, panel_top, panel_width, panel_rect.getHeight());
-}
-
-void LLAccordionCtrlTab::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height)
-{
- if (!panel)
- return;
- LLRect panel_rect = panel->getRect();
- panel_rect.setLeftTopAndSize(left, top, width, height);
- panel->reshape( width, height, 1);
- panel->setRect(panel_rect);
-}
-
-bool LLAccordionCtrlTab::handleToolTip(S32 x, S32 y, MASK mask)
-{
- //header may be not the first child but we need to process it first
- if (y >= (getRect().getHeight() - HEADER_HEIGHT - HEADER_HEIGHT / 2))
- {
- //inside tab header
- //fix for EXT-6619
- mHeader->handleToolTip(x, y, mask);
- return true;
- }
- return LLUICtrl::handleToolTip(x, y, mask);
-}
-
-bool LLAccordionCtrlTab::handleScrollWheel(S32 x, S32 y, S32 clicks)
-{
- if (LLUICtrl::handleScrollWheel(x, y, clicks))
- {
- return true;
- }
-
- if (mScrollbar && mScrollbar->getVisible() && mScrollbar->handleScrollWheel(0, 0, clicks))
- {
- return true;
- }
-
- return false;
-}
+/**
+ * @file LLAccordionCtrlTab.cpp
+ * @brief Collapsible control implementation
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llaccordionctrltab.h"
+#include "llaccordionctrl.h"
+
+#include "lllocalcliprect.h"
+#include "llscrollbar.h"
+#include "lltextbox.h"
+#include "lltextutil.h"
+#include "lluictrl.h"
+
+static const std::string DD_BUTTON_NAME = "dd_button";
+static const std::string DD_TEXTBOX_NAME = "dd_textbox";
+static const std::string DD_HEADER_NAME = "dd_header";
+
+static const S32 HEADER_HEIGHT = 23;
+static const S32 HEADER_IMAGE_LEFT_OFFSET = 5;
+static const S32 HEADER_TEXT_LEFT_OFFSET = 30;
+static const F32 AUTO_OPEN_TIME = 1.f;
+static const S32 VERTICAL_MULTIPLE = 16;
+static const S32 PARENT_BORDER_MARGIN = 5;
+
+static LLDefaultChildRegistry::Register<LLAccordionCtrlTab> t1("accordion_tab");
+
+class LLAccordionCtrlTab::LLAccordionCtrlTabHeader : public LLUICtrl
+{
+public:
+ friend class LLUICtrlFactory;
+
+ struct Params : public LLInitParam::Block<Params, LLAccordionCtrlTab::Params>
+ {
+ Params();
+ };
+
+ LLAccordionCtrlTabHeader(const LLAccordionCtrlTabHeader::Params& p);
+
+ virtual ~LLAccordionCtrlTabHeader();
+
+ virtual void draw();
+
+ virtual void reshape(S32 width, S32 height, bool called_from_parent = true);
+
+ virtual bool postBuild();
+
+ std::string getTitle();
+ void setTitle(const std::string& title, const std::string& hl);
+
+ void setTitleFontStyle(std::string style);
+
+ void setTitleColor(LLUIColor);
+
+ void setSelected(bool is_selected) { mIsSelected = is_selected; }
+
+ virtual void onMouseEnter(S32 x, S32 y, MASK mask);
+ virtual void onMouseLeave(S32 x, S32 y, MASK mask);
+ virtual bool handleKey(KEY key, MASK mask, bool called_from_parent);
+ virtual bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg);
+
+private:
+ LLTextBox* mHeaderTextbox;
+
+ // Overlay images (arrows)
+ LLPointer<LLUIImage> mImageCollapsed;
+ LLPointer<LLUIImage> mImageExpanded;
+ LLPointer<LLUIImage> mImageCollapsedPressed;
+ LLPointer<LLUIImage> mImageExpandedPressed;
+
+ // Background images
+ LLPointer<LLUIImage> mImageHeader;
+ LLPointer<LLUIImage> mImageHeaderOver;
+ LLPointer<LLUIImage> mImageHeaderPressed;
+ LLPointer<LLUIImage> mImageHeaderFocused;
+
+ // style saved when applying it in setTitleFontStyle
+ LLStyle::Params mStyleParams;
+
+ LLUIColor mHeaderBGColor;
+
+ bool mNeedsHighlight;
+ bool mIsSelected;
+
+ LLFrameTimer mAutoOpenTimer;
+};
+
+LLAccordionCtrlTab::LLAccordionCtrlTabHeader::Params::Params()
+{
+}
+
+LLAccordionCtrlTab::LLAccordionCtrlTabHeader::LLAccordionCtrlTabHeader(
+ const LLAccordionCtrlTabHeader::Params& p)
+: LLUICtrl(p)
+, mHeaderBGColor(p.header_bg_color())
+, mNeedsHighlight(false)
+, mIsSelected(false),
+ mImageCollapsed(p.header_collapse_img),
+ mImageCollapsedPressed(p.header_collapse_img_pressed),
+ mImageExpanded(p.header_expand_img),
+ mImageExpandedPressed(p.header_expand_img_pressed),
+ mImageHeader(p.header_image),
+ mImageHeaderOver(p.header_image_over),
+ mImageHeaderPressed(p.header_image_pressed),
+ mImageHeaderFocused(p.header_image_focused)
+{
+ LLTextBox::Params textboxParams;
+ textboxParams.name(DD_TEXTBOX_NAME);
+ textboxParams.initial_value(p.title());
+ textboxParams.text_color(p.header_text_color());
+ textboxParams.follows.flags(FOLLOWS_NONE);
+ textboxParams.font( p.font() );
+ textboxParams.font_shadow(LLFontGL::NO_SHADOW);
+ textboxParams.use_ellipses = true;
+ textboxParams.bg_visible = false;
+ textboxParams.mouse_opaque = false;
+ textboxParams.parse_urls = false;
+ mHeaderTextbox = LLUICtrlFactory::create<LLTextBox>(textboxParams);
+ addChild(mHeaderTextbox);
+}
+
+LLAccordionCtrlTab::LLAccordionCtrlTabHeader::~LLAccordionCtrlTabHeader()
+{
+}
+
+bool LLAccordionCtrlTab::LLAccordionCtrlTabHeader::postBuild()
+{
+ return true;
+}
+
+std::string LLAccordionCtrlTab::LLAccordionCtrlTabHeader::getTitle()
+{
+ if (mHeaderTextbox)
+ {
+ return mHeaderTextbox->getText();
+ }
+
+ return LLStringUtil::null;
+}
+
+void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitle(const std::string& title, const std::string& hl)
+{
+ if (mHeaderTextbox)
+ {
+ LLTextUtil::textboxSetHighlightedVal(
+ mHeaderTextbox,
+ mStyleParams,
+ title,
+ hl);
+ }
+}
+
+void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitleFontStyle(std::string style)
+{
+ if (mHeaderTextbox)
+ {
+ std::string text = mHeaderTextbox->getText();
+ mStyleParams.font(mHeaderTextbox->getFont());
+ mStyleParams.font.style(style);
+ mHeaderTextbox->setText(text, mStyleParams);
+ }
+}
+
+void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitleColor(LLUIColor color)
+{
+ if (mHeaderTextbox)
+ {
+ mHeaderTextbox->setColor(color);
+ }
+}
+
+void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw()
+{
+ S32 width = getRect().getWidth();
+ S32 height = getRect().getHeight();
+
+ F32 alpha = getCurrentTransparency();
+ gl_rect_2d(0, 0, width - 1, height - 1, mHeaderBGColor.get() % alpha, true);
+
+ LLAccordionCtrlTab* parent = dynamic_cast<LLAccordionCtrlTab*>(getParent());
+ bool collapsible = parent && parent->getCollapsible();
+ bool expanded = parent && parent->getDisplayChildren();
+
+ // Handle overlay images, if needed
+ // Only show green "focus" background image if the accordion is open,
+ // because the user's mental model of focus is that it goes away after
+ // the accordion is closed.
+ if (getParent()->hasFocus() || mIsSelected
+ /*&& !(collapsible && !expanded)*/ // WHY??
+ )
+ {
+ mImageHeaderFocused->draw(0, 0, width, height);
+ }
+ else
+ {
+ mImageHeader->draw(0, 0, width, height);
+ }
+
+ if (mNeedsHighlight)
+ {
+ mImageHeaderOver->draw(0, 0, width, height);
+ }
+
+ if (collapsible)
+ {
+ LLPointer<LLUIImage> overlay_image;
+ if (expanded)
+ {
+ overlay_image = mImageExpanded;
+ }
+ else
+ {
+ overlay_image = mImageCollapsed;
+ }
+ overlay_image->draw(HEADER_IMAGE_LEFT_OFFSET, (height - overlay_image->getHeight()) / 2);
+ }
+
+ LLUICtrl::draw();
+}
+
+void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::reshape(S32 width, S32 height, bool called_from_parent /* = true */)
+{
+ S32 header_height = mHeaderTextbox->getTextPixelHeight();
+
+ LLRect textboxRect(HEADER_TEXT_LEFT_OFFSET, (height + header_height) / 2, width, (height - header_height) / 2);
+ mHeaderTextbox->reshape(textboxRect.getWidth(), textboxRect.getHeight());
+ mHeaderTextbox->setRect(textboxRect);
+
+ if (mHeaderTextbox->getTextPixelWidth() > mHeaderTextbox->getRect().getWidth())
+ {
+ setToolTip(mHeaderTextbox->getText());
+ }
+ else
+ {
+ setToolTip(LLStringUtil::null);
+ }
+}
+
+void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::onMouseEnter(S32 x, S32 y, MASK mask)
+{
+ LLUICtrl::onMouseEnter(x, y, mask);
+ mNeedsHighlight = true;
+}
+
+void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+ LLUICtrl::onMouseLeave(x, y, mask);
+ mNeedsHighlight = false;
+ mAutoOpenTimer.stop();
+}
+
+bool LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleKey(KEY key, MASK mask, bool called_from_parent)
+{
+ if ((key == KEY_LEFT || key == KEY_RIGHT) && mask == MASK_NONE)
+ {
+ return getParent()->handleKey(key, mask, called_from_parent);
+ }
+
+ return LLUICtrl::handleKey(key, mask, called_from_parent);
+}
+
+bool LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleDragAndDrop(S32 x, S32 y, MASK mask,
+ bool drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ LLAccordionCtrlTab* parent = dynamic_cast<LLAccordionCtrlTab*>(getParent());
+
+ if (parent && !parent->getDisplayChildren() && parent->getCollapsible() && parent->canOpenClose())
+ {
+ if (mAutoOpenTimer.getStarted())
+ {
+ if (mAutoOpenTimer.getElapsedTimeF32() > AUTO_OPEN_TIME)
+ {
+ parent->changeOpenClose(false);
+ mAutoOpenTimer.stop();
+ return true;
+ }
+ }
+ else
+ {
+ mAutoOpenTimer.start();
+ }
+ }
+
+ return LLUICtrl::handleDragAndDrop(x, y, mask, drop, cargo_type,
+ cargo_data, accept, tooltip_msg);
+}
+
+LLAccordionCtrlTab::Params::Params()
+ : title("title")
+ ,display_children("expanded", true)
+ ,header_height("header_height", HEADER_HEIGHT),
+ min_width("min_width", 0),
+ min_height("min_height", 0)
+ ,collapsible("collapsible", true)
+ ,header_bg_color("header_bg_color")
+ ,dropdown_bg_color("dropdown_bg_color")
+ ,header_visible("header_visible",true)
+ ,padding_left("padding_left",2)
+ ,padding_right("padding_right",2)
+ ,padding_top("padding_top",2)
+ ,padding_bottom("padding_bottom",2)
+ ,header_expand_img("header_expand_img")
+ ,header_expand_img_pressed("header_expand_img_pressed")
+ ,header_collapse_img("header_collapse_img")
+ ,header_collapse_img_pressed("header_collapse_img_pressed")
+ ,header_image("header_image")
+ ,header_image_over("header_image_over")
+ ,header_image_pressed("header_image_pressed")
+ ,header_image_focused("header_image_focused")
+ ,header_text_color("header_text_color")
+ ,fit_panel("fit_panel",true)
+ ,selection_enabled("selection_enabled", false)
+{
+ changeDefault(mouse_opaque, false);
+}
+
+LLAccordionCtrlTab::LLAccordionCtrlTab(const LLAccordionCtrlTab::Params&p)
+ : LLUICtrl(p)
+ ,mDisplayChildren(p.display_children)
+ ,mCollapsible(p.collapsible)
+ ,mExpandedHeight(0)
+ ,mDropdownBGColor(p.dropdown_bg_color())
+ ,mHeaderVisible(p.header_visible)
+ ,mPaddingLeft(p.padding_left)
+ ,mPaddingRight(p.padding_right)
+ ,mPaddingTop(p.padding_top)
+ ,mPaddingBottom(p.padding_bottom)
+ ,mCanOpenClose(true)
+ ,mFitPanel(p.fit_panel)
+ ,mSelectionEnabled(p.selection_enabled)
+ ,mContainerPanel(NULL)
+ ,mScrollbar(NULL)
+{
+ mStoredOpenCloseState = false;
+ mWasStateStored = false;
+ mSkipChangesOnNotifyParent = false;
+
+ mDropdownBGColor = LLColor4::white;
+ LLAccordionCtrlTabHeader::Params headerParams;
+ headerParams.name(DD_HEADER_NAME);
+ headerParams.title(p.title);
+ mHeader = LLUICtrlFactory::create<LLAccordionCtrlTabHeader>(headerParams);
+ addChild(mHeader, 1);
+
+ LLFocusableElement::setFocusReceivedCallback(boost::bind(&LLAccordionCtrlTab::selectOnFocusReceived, this));
+
+ if (!p.selection_enabled)
+ {
+ LLFocusableElement::setFocusLostCallback(boost::bind(&LLAccordionCtrlTab::deselectOnFocusLost, this));
+ }
+
+ reshape(100, 200,false);
+}
+
+LLAccordionCtrlTab::~LLAccordionCtrlTab()
+{
+}
+
+void LLAccordionCtrlTab::setDisplayChildren(bool display)
+{
+ mDisplayChildren = display;
+ LLRect rect = getRect();
+
+ rect.mBottom = rect.mTop - (getDisplayChildren() ? mExpandedHeight : HEADER_HEIGHT);
+ setRect(rect);
+
+ if (mContainerPanel)
+ {
+ mContainerPanel->setVisible(getDisplayChildren());
+ }
+
+ if (mDisplayChildren)
+ {
+ adjustContainerPanel();
+ }
+ else
+ {
+ if (mScrollbar)
+ mScrollbar->setVisible(false);
+ }
+}
+
+void LLAccordionCtrlTab::reshape(S32 width, S32 height, bool called_from_parent /* = true */)
+{
+ LLRect headerRect;
+
+ headerRect.setLeftTopAndSize(0, height, width, HEADER_HEIGHT);
+ mHeader->setRect(headerRect);
+ mHeader->reshape(headerRect.getWidth(), headerRect.getHeight());
+
+ if (!mDisplayChildren)
+ return;
+
+ LLRect childRect;
+
+ childRect.setLeftTopAndSize(
+ getPaddingLeft(),
+ height - getHeaderHeight() - getPaddingTop(),
+ width - getPaddingLeft() - getPaddingRight(),
+ height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() );
+
+ adjustContainerPanel(childRect);
+}
+
+void LLAccordionCtrlTab::changeOpenClose(bool is_open)
+{
+ if (is_open)
+ mExpandedHeight = getRect().getHeight();
+
+ setDisplayChildren(!is_open);
+ reshape(getRect().getWidth(), getRect().getHeight(), false);
+ if (mCommitSignal)
+ {
+ (*mCommitSignal)(this, getDisplayChildren());
+ }
+}
+
+void LLAccordionCtrlTab::onVisibilityChange(bool new_visibility)
+{
+ LLUICtrl::onVisibilityChange(new_visibility);
+
+ notifyParent(LLSD().with("child_visibility_change", new_visibility));
+}
+
+// virtual
+void LLAccordionCtrlTab::onUpdateScrollToChild(const LLUICtrl *cntrl)
+{
+ if (mScrollbar && mScrollbar->getVisible())
+ {
+ LLRect rect;
+ cntrl->localRectToOtherView(cntrl->getLocalRect(), &rect, this);
+
+ // Translate to parent coordinatess to check if we are in visible rectangle
+ rect.translate(getRect().mLeft, getRect().mBottom);
+
+ if (!getRect().contains(rect))
+ {
+ // for accordition's scroll, height is in pixels
+ // Back to local coords and calculate position for scroller
+ S32 bottom = mScrollbar->getDocPos() - rect.mBottom + getRect().mBottom;
+ S32 top = mScrollbar->getDocPos() - rect.mTop + getRect().mTop;
+
+ S32 scroll_pos = llclamp(mScrollbar->getDocPos(),
+ bottom, // min vertical scroll
+ top); // max vertical scroll
+
+ mScrollbar->setDocPos(scroll_pos);
+ }
+ }
+
+ LLUICtrl::onUpdateScrollToChild(cntrl);
+}
+
+bool LLAccordionCtrlTab::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (mCollapsible && mHeaderVisible && mCanOpenClose)
+ {
+ if (y >= (getRect().getHeight() - HEADER_HEIGHT))
+ {
+ mHeader->setFocus(true);
+ changeOpenClose(getDisplayChildren());
+
+ // Reset stored state
+ mWasStateStored = false;
+ return true;
+ }
+ }
+ return LLUICtrl::handleMouseDown(x,y,mask);
+}
+
+bool LLAccordionCtrlTab::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ return LLUICtrl::handleMouseUp(x,y,mask);
+}
+
+boost::signals2::connection LLAccordionCtrlTab::setDropDownStateChangedCallback(commit_callback_t cb)
+{
+ return setCommitCallback(cb);
+}
+
+bool LLAccordionCtrlTab::addChild(LLView* child, S32 tab_group)
+{
+ if (DD_HEADER_NAME != child->getName())
+ {
+ reshape(child->getRect().getWidth() , child->getRect().getHeight() + HEADER_HEIGHT );
+ mExpandedHeight = getRect().getHeight();
+ }
+
+ bool res = LLUICtrl::addChild(child, tab_group);
+
+ if (DD_HEADER_NAME != child->getName())
+ {
+ if (!mCollapsible)
+ setDisplayChildren(true);
+ else
+ setDisplayChildren(getDisplayChildren());
+ }
+
+ if (!mContainerPanel)
+ mContainerPanel = findContainerView();
+
+ return res;
+}
+
+void LLAccordionCtrlTab::setAccordionView(LLView* panel)
+{
+ addChild(panel, 0);
+}
+
+std::string LLAccordionCtrlTab::getTitle() const
+{
+ if (mHeader)
+ {
+ return mHeader->getTitle();
+ }
+
+ return LLStringUtil::null;
+}
+
+void LLAccordionCtrlTab::setTitle(const std::string& title, const std::string& hl)
+{
+ if (mHeader)
+ {
+ mHeader->setTitle(title, hl);
+ }
+}
+
+void LLAccordionCtrlTab::setTitleFontStyle(std::string style)
+{
+ if (mHeader)
+ {
+ mHeader->setTitleFontStyle(style);
+ }
+}
+
+void LLAccordionCtrlTab::setTitleColor(LLUIColor color)
+{
+ if (mHeader)
+ {
+ mHeader->setTitleColor(color);
+ }
+}
+
+boost::signals2::connection LLAccordionCtrlTab::setFocusReceivedCallback(const focus_signal_t::slot_type& cb)
+{
+ if (mHeader)
+ {
+ return mHeader->setFocusReceivedCallback(cb);
+ }
+
+ return boost::signals2::connection();
+}
+
+boost::signals2::connection LLAccordionCtrlTab::setFocusLostCallback(const focus_signal_t::slot_type& cb)
+{
+ if (mHeader)
+ {
+ return mHeader->setFocusLostCallback(cb);
+ }
+
+ return boost::signals2::connection();
+}
+
+void LLAccordionCtrlTab::setSelected(bool is_selected)
+{
+ if (mHeader)
+ {
+ mHeader->setSelected(is_selected);
+ }
+}
+
+LLView* LLAccordionCtrlTab::findContainerView()
+{
+ child_list_const_iter_t it = getChildList()->begin(), it_end = getChildList()->end();
+ while (it != it_end)
+ {
+ LLView* child = *(it++);
+ if (DD_HEADER_NAME != child->getName() && child->getVisible())
+ return child;
+ }
+
+ return NULL;
+}
+
+void LLAccordionCtrlTab::selectOnFocusReceived()
+{
+ if (getParent()) // A parent may not be set if tabs are added dynamically.
+ {
+ getParent()->notifyParent(LLSD().with("action", "select_current"));
+ }
+}
+
+void LLAccordionCtrlTab::deselectOnFocusLost()
+{
+ if (getParent()) // A parent may not be set if tabs are added dynamically.
+ {
+ getParent()->notifyParent(LLSD().with("action", "deselect_current"));
+ }
+}
+
+S32 LLAccordionCtrlTab::getHeaderHeight()
+{
+ return mHeaderVisible ? HEADER_HEIGHT : 0;
+}
+
+void LLAccordionCtrlTab::setHeaderVisible(bool value)
+{
+ if (mHeaderVisible == value)
+ return;
+
+ mHeaderVisible = value;
+
+ if (mHeader)
+ {
+ mHeader->setVisible(value);
+ }
+
+ reshape(getRect().getWidth(), getRect().getHeight(), false);
+};
+
+//virtual
+bool LLAccordionCtrlTab::postBuild()
+{
+ if (mHeader)
+ {
+ mHeader->setVisible(mHeaderVisible);
+ }
+
+ static LLUICachedControl<S32> scrollbar_size("UIScrollbarSize", 0);
+
+ LLRect scroll_rect;
+ scroll_rect.setOriginAndSize(
+ getRect().getWidth() - scrollbar_size,
+ 1,
+ scrollbar_size,
+ getRect().getHeight() - 1);
+
+ mContainerPanel = findContainerView();
+
+ if (!mFitPanel)
+ {
+ LLScrollbar::Params sbparams;
+ sbparams.name("scrollable vertical");
+ sbparams.rect(scroll_rect);
+ sbparams.orientation(LLScrollbar::VERTICAL);
+ sbparams.doc_size(getRect().getHeight());
+ sbparams.doc_pos(0);
+ sbparams.page_size(getRect().getHeight());
+ sbparams.step_size(VERTICAL_MULTIPLE);
+ sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
+ sbparams.change_callback(boost::bind(&LLAccordionCtrlTab::onScrollPosChangeCallback, this, _1, _2));
+
+ mScrollbar = LLUICtrlFactory::create<LLScrollbar>(sbparams);
+ LLView::addChild(mScrollbar);
+ mScrollbar->setFollowsRight();
+ mScrollbar->setFollowsTop();
+ mScrollbar->setFollowsBottom();
+
+ mScrollbar->setVisible(false);
+ }
+
+ if (mContainerPanel)
+ {
+ mContainerPanel->setVisible(mDisplayChildren);
+ }
+
+ return LLUICtrl::postBuild();
+}
+
+bool LLAccordionCtrlTab::notifyChildren (const LLSD& info)
+{
+ if (info.has("action"))
+ {
+ std::string str_action = info["action"];
+ if (str_action == "store_state")
+ {
+ storeOpenCloseState();
+ return true;
+ }
+
+ if (str_action == "restore_state")
+ {
+ restoreOpenCloseState();
+ return true;
+ }
+ }
+
+ return LLUICtrl::notifyChildren(info);
+}
+
+S32 LLAccordionCtrlTab::notifyParent(const LLSD& info)
+{
+ if (info.has("action"))
+ {
+ std::string str_action = info["action"];
+ if (str_action == "size_changes")
+ {
+ S32 height = info["height"];
+ height = llmax(height, 10) + HEADER_HEIGHT + getPaddingTop() + getPaddingBottom();
+
+ mExpandedHeight = height;
+
+ if (isExpanded() && !mSkipChangesOnNotifyParent)
+ {
+ LLRect panel_rect = getRect();
+ panel_rect.setLeftTopAndSize( panel_rect.mLeft, panel_rect.mTop, panel_rect.getWidth(), height);
+ reshape(getRect().getWidth(),height);
+ setRect(panel_rect);
+ }
+
+ // LLAccordionCtrl should rearrange accordion tab if one of accordions changed its size
+ if (getParent()) // A parent may not be set if tabs are added dynamically.
+ getParent()->notifyParent(info);
+ return 1;
+ }
+
+ if (str_action == "select_prev")
+ {
+ showAndFocusHeader();
+ return 1;
+ }
+ }
+ else if (info.has("scrollToShowRect"))
+ {
+ LLAccordionCtrl* parent = dynamic_cast<LLAccordionCtrl*>(getParent());
+ if (parent && parent->getFitParent())
+ {
+ // EXT-8285 ('No attachments worn' text appears at the bottom of blank 'Attachments' accordion)
+ // The problem was in passing message "scrollToShowRect" IN LLAccordionCtrlTab::notifyParent
+ // FROM child LLScrollContainer TO parent LLAccordionCtrl with "it_parent" set to true.
+
+ // It is wrong notification for parent accordion which leads to recursive call of adjustContainerPanel
+ // As the result of recursive call of adjustContainerPanel we got LLAccordionCtrlTab
+ // that reshaped and re-sized with different rectangles.
+
+ // LLAccordionCtrl has own scrollContainer and LLAccordionCtrlTab has own scrollContainer
+ // both should handle own scroll container's event.
+ // So, if parent accordion "fit_parent" accordion tab should handle its scroll container events itself.
+
+ return 1;
+ }
+
+ if (!getDisplayChildren())
+ {
+ // Don't pass scrolling event further if our contents are invisible (STORM-298).
+ return 1;
+ }
+ }
+
+ return LLUICtrl::notifyParent(info);
+}
+
+S32 LLAccordionCtrlTab::notify(const LLSD& info)
+{
+ if (info.has("action"))
+ {
+ std::string str_action = info["action"];
+ if (str_action == "select_first")
+ {
+ showAndFocusHeader();
+ return 1;
+ }
+
+ if (str_action == "select_last")
+ {
+ if (!getDisplayChildren())
+ {
+ showAndFocusHeader();
+ }
+ else
+ {
+ LLView* view = getAccordionView();
+ if (view)
+ {
+ view->notify(LLSD().with("action", "select_last"));
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+bool LLAccordionCtrlTab::handleKey(KEY key, MASK mask, bool called_from_parent)
+{
+ if (!mHeader->hasFocus())
+ return LLUICtrl::handleKey(key, mask, called_from_parent);
+
+ if ((key == KEY_RETURN) && mask == MASK_NONE)
+ {
+ changeOpenClose(getDisplayChildren());
+ return true;
+ }
+
+ if ((key == KEY_ADD || key == KEY_RIGHT) && mask == MASK_NONE)
+ {
+ if (!getDisplayChildren())
+ {
+ changeOpenClose(getDisplayChildren());
+ return true;
+ }
+ }
+
+ if ((key == KEY_SUBTRACT || key == KEY_LEFT) && mask == MASK_NONE)
+ {
+ if (getDisplayChildren())
+ {
+ changeOpenClose(getDisplayChildren());
+ return true;
+ }
+ }
+
+ if (key == KEY_DOWN && mask == MASK_NONE)
+ {
+ // if collapsed go to the next accordion
+ if (!getDisplayChildren())
+ {
+ // we're processing notifyParent so let call parent directly
+ getParent()->notifyParent(LLSD().with("action", "select_next"));
+ }
+ else
+ {
+ getAccordionView()->notify(LLSD().with("action", "select_first"));
+ }
+ return true;
+ }
+
+ if (key == KEY_UP && mask == MASK_NONE)
+ {
+ // go to the previous accordion
+
+ // we're processing notifyParent so let call parent directly
+ getParent()->notifyParent(LLSD().with("action", "select_prev"));
+ return true;
+ }
+
+ return LLUICtrl::handleKey(key, mask, called_from_parent);
+}
+
+void LLAccordionCtrlTab::showAndFocusHeader()
+{
+ if (!mHeader)
+ {
+ return;
+ }
+
+ mHeader->setFocus(true);
+ mHeader->setSelected(mSelectionEnabled);
+
+ LLRect screen_rc;
+ LLRect selected_rc = mHeader->getRect();
+ localRectToScreen(selected_rc, &screen_rc);
+
+ // This call to notifyParent() is intended to deliver "scrollToShowRect" command
+ // to the parent LLAccordionCtrl so by calling it from the direct parent of this
+ // accordion tab (assuming that the parent is an LLAccordionCtrl) the calls chain
+ // is shortened and messages from inside the collapsed tabs are avoided.
+ // See STORM-536.
+ getParent()->notifyParent(LLSD().with("scrollToShowRect", screen_rc.getValue()));
+}
+
+void LLAccordionCtrlTab::storeOpenCloseState()
+{
+ if (mWasStateStored)
+ return;
+ mStoredOpenCloseState = getDisplayChildren();
+ mWasStateStored = true;
+}
+
+void LLAccordionCtrlTab::restoreOpenCloseState()
+{
+ if (!mWasStateStored)
+ return;
+ if (getDisplayChildren() != mStoredOpenCloseState)
+ {
+ changeOpenClose(getDisplayChildren());
+ }
+ mWasStateStored = false;
+}
+
+void LLAccordionCtrlTab::adjustContainerPanel()
+{
+ S32 width = getRect().getWidth();
+ S32 height = getRect().getHeight();
+
+ LLRect child_rect;
+ child_rect.setLeftTopAndSize(
+ getPaddingLeft(),
+ height - getHeaderHeight() - getPaddingTop(),
+ width - getPaddingLeft() - getPaddingRight(),
+ height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() );
+
+ adjustContainerPanel(child_rect);
+}
+
+void LLAccordionCtrlTab::adjustContainerPanel(const LLRect& child_rect)
+{
+ if (!mContainerPanel)
+ return;
+
+ if (!mFitPanel)
+ {
+ show_hide_scrollbar(child_rect);
+ updateLayout(child_rect);
+ }
+ else
+ {
+ mContainerPanel->reshape(child_rect.getWidth(), child_rect.getHeight());
+ mContainerPanel->setRect(child_rect);
+ }
+}
+
+S32 LLAccordionCtrlTab::getChildViewHeight()
+{
+ if (!mContainerPanel)
+ return 0;
+ return mContainerPanel->getRect().getHeight();
+}
+
+void LLAccordionCtrlTab::show_hide_scrollbar(const LLRect& child_rect)
+{
+ if (getChildViewHeight() > child_rect.getHeight())
+ showScrollbar(child_rect);
+ else
+ hideScrollbar(child_rect);
+}
+
+void LLAccordionCtrlTab::showScrollbar(const LLRect& child_rect)
+{
+ if (!mContainerPanel || !mScrollbar)
+ return;
+ bool was_visible = mScrollbar->getVisible();
+ mScrollbar->setVisible(true);
+
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+
+ ctrlSetLeftTopAndSize(mScrollbar,
+ child_rect.getWidth() - scrollbar_size,
+ child_rect.getHeight() - PARENT_BORDER_MARGIN,
+ scrollbar_size,
+ child_rect.getHeight() - PARENT_BORDER_MARGIN * 2);
+
+ LLRect orig_rect = mContainerPanel->getRect();
+
+ mScrollbar->setPageSize(child_rect.getHeight());
+ mScrollbar->setDocParams(orig_rect.getHeight(), mScrollbar->getDocPos());
+
+ if (was_visible)
+ {
+ S32 scroll_pos = llmin(mScrollbar->getDocPos(), orig_rect.getHeight() - child_rect.getHeight() - 1);
+ mScrollbar->setDocPos(scroll_pos);
+ }
+ else // Shrink child panel
+ {
+ updateLayout(child_rect);
+ }
+}
+
+void LLAccordionCtrlTab::hideScrollbar(const LLRect& child_rect)
+{
+ if (!mContainerPanel || !mScrollbar)
+ return;
+
+ if (!mScrollbar->getVisible())
+ return;
+
+ mScrollbar->setVisible(false);
+ mScrollbar->setDocPos(0);
+
+ //shrink child panel
+ updateLayout(child_rect);
+}
+
+void LLAccordionCtrlTab::onScrollPosChangeCallback(S32, LLScrollbar*)
+{
+ LLRect child_rect;
+
+ S32 width = getRect().getWidth();
+ S32 height = getRect().getHeight();
+
+ child_rect.setLeftTopAndSize(
+ getPaddingLeft(),
+ height - getHeaderHeight() - getPaddingTop(),
+ width - getPaddingLeft() - getPaddingRight(),
+ height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() );
+
+ updateLayout(child_rect);
+}
+
+void LLAccordionCtrlTab::drawChild(const LLRect& root_rect, LLView* child)
+{
+ if (child && child->getVisible() && child->getRect().isValid())
+ {
+ LLRect screen_rect;
+ localRectToScreen(child->getRect(), &screen_rect);
+
+ if (root_rect.overlaps(screen_rect) && sDirtyRect.overlaps(screen_rect))
+ {
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ LLUI::pushMatrix();
+ {
+ LLUI::translate((F32)child->getRect().mLeft, (F32)child->getRect().mBottom);
+ child->draw();
+ }
+ LLUI::popMatrix();
+ }
+ }
+}
+
+void LLAccordionCtrlTab::draw()
+{
+ if (mFitPanel)
+ {
+ LLUICtrl::draw();
+ }
+ else
+ {
+ LLRect root_rect(getRootView()->getRect());
+ drawChild(root_rect, mHeader);
+ drawChild(root_rect, mScrollbar);
+
+ LLRect child_rect;
+
+ S32 width = getRect().getWidth();
+ S32 height = getRect().getHeight();
+
+ child_rect.setLeftTopAndSize(
+ getPaddingLeft(),
+ height - getHeaderHeight() - getPaddingTop(),
+ width - getPaddingLeft() - getPaddingRight(),
+ height - getHeaderHeight() - getPaddingTop() - getPaddingBottom());
+
+ LLLocalClipRect clip(child_rect);
+ drawChild(root_rect,mContainerPanel);
+ }
+}
+
+void LLAccordionCtrlTab::updateLayout(const LLRect& child_rect)
+{
+ LLView* child = getAccordionView();
+ if (!mContainerPanel)
+ return;
+
+ S32 panel_top = child_rect.getHeight();
+ S32 panel_width = child_rect.getWidth();
+
+ static LLUICachedControl<S32> scrollbar_size("UIScrollbarSize", 0);
+ if (mScrollbar && mScrollbar->getVisible())
+ {
+ panel_top += mScrollbar->getDocPos();
+ panel_width -= scrollbar_size;
+ }
+
+ // Set sizes for first panels and dragbars
+ LLRect panel_rect = child->getRect();
+ ctrlSetLeftTopAndSize(mContainerPanel, child_rect.mLeft, panel_top, panel_width, panel_rect.getHeight());
+}
+
+void LLAccordionCtrlTab::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height)
+{
+ if (!panel)
+ return;
+ LLRect panel_rect = panel->getRect();
+ panel_rect.setLeftTopAndSize(left, top, width, height);
+ panel->reshape( width, height, 1);
+ panel->setRect(panel_rect);
+}
+
+bool LLAccordionCtrlTab::handleToolTip(S32 x, S32 y, MASK mask)
+{
+ //header may be not the first child but we need to process it first
+ if (y >= (getRect().getHeight() - HEADER_HEIGHT - HEADER_HEIGHT / 2))
+ {
+ //inside tab header
+ //fix for EXT-6619
+ mHeader->handleToolTip(x, y, mask);
+ return true;
+ }
+ return LLUICtrl::handleToolTip(x, y, mask);
+}
+
+bool LLAccordionCtrlTab::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ if (LLUICtrl::handleScrollWheel(x, y, clicks))
+ {
+ return true;
+ }
+
+ if (mScrollbar && mScrollbar->getVisible() && mScrollbar->handleScrollWheel(0, 0, clicks))
+ {
+ return true;
+ }
+
+ return false;
+}
diff --git a/indra/llui/llaccordionctrltab.h b/indra/llui/llaccordionctrltab.h
index 70e59d7b0b..b03ea53609 100644
--- a/indra/llui/llaccordionctrltab.h
+++ b/indra/llui/llaccordionctrltab.h
@@ -1,251 +1,251 @@
-/**
- * @file LLAccordionCtrlTab.h
- * @brief Collapsible box control implementation
- *
- * $LicenseInfo:firstyear=2004&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_ACCORDIONCTRLTAB_H_
-#define LL_ACCORDIONCTRLTAB_H_
-
-#include <string>
-#include "llrect.h"
-#include "lluictrl.h"
-#include "lluicolor.h"
-#include "llstyle.h"
-
-class LLUICtrlFactory;
-class LLUIImage;
-class LLButton;
-class LLTextBox;
-class LLScrollbar;
-
-
-
-// LLAccordionCtrlTab is a container for other controls.
-// It has a Header, by clicking on which hosted controls are shown or hidden.
-// When hosted controls are show - LLAccordionCtrlTab is expanded.
-// When hosted controls are hidden - LLAccordionCtrlTab is collapsed.
-
-class LLAccordionCtrlTab : public LLUICtrl
-{
-// Interface
-public:
-
- struct Params
- : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Optional<bool> display_children, //expanded or collapsed after initialization
- collapsible;
-
- Optional<std::string> title;
-
- Optional<S32> header_height,
- min_width,
- min_height;
-
- // Overlay images (arrows on the left)
- Mandatory<LLUIImage*> header_expand_img,
- header_expand_img_pressed,
- header_collapse_img,
- header_collapse_img_pressed;
-
- // Background images for the accordion tabs
- Mandatory<LLUIImage*> header_image,
- header_image_over,
- header_image_pressed,
- header_image_focused;
-
- Optional<LLUIColor> header_bg_color,
- header_text_color,
- dropdown_bg_color;
-
- Optional<bool> header_visible;
-
- Optional<bool> fit_panel;
-
- Optional<bool> selection_enabled;
-
- Optional<S32> padding_left,
- padding_right,
- padding_top,
- padding_bottom;
-
- Params();
- };
-
- typedef LLDefaultChildRegistry child_registry_t;
-
- virtual ~LLAccordionCtrlTab();
-
- // Registers callback for expand/collapse events.
- boost::signals2::connection setDropDownStateChangedCallback(commit_callback_t cb);
-
- // Changes expand/collapse state
- virtual void setDisplayChildren(bool display);
-
- // Returns expand/collapse state
- virtual bool getDisplayChildren() const { return mDisplayChildren; };
-
- //set LLAccordionCtrlTab panel
- void setAccordionView(LLView* panel);
- LLView* getAccordionView() { return mContainerPanel; };
-
- std::string getTitle() const;
-
- // Set text and highlight substring in LLAccordionCtrlTabHeader
- void setTitle(const std::string& title, const std::string& hl = LLStringUtil::null);
-
- // Set text font style in LLAccordionCtrlTabHeader
- void setTitleFontStyle(std::string style);
-
- // Set text color in LLAccordionCtrlTabHeader
- void setTitleColor(LLUIColor color);
-
- boost::signals2::connection setFocusReceivedCallback(const focus_signal_t::slot_type& cb);
- boost::signals2::connection setFocusLostCallback(const focus_signal_t::slot_type& cb);
-
- void setSelected(bool is_selected);
-
- bool getCollapsible() { return mCollapsible; };
-
- void setCollapsible(bool collapsible) { mCollapsible = collapsible; };
- void changeOpenClose(bool is_open);
-
- void canOpenClose(bool can_open_close) { mCanOpenClose = can_open_close; };
- bool canOpenClose() const { return mCanOpenClose; };
-
- virtual bool postBuild();
-
- S32 notifyParent(const LLSD& info);
- S32 notify(const LLSD& info);
- bool notifyChildren(const LLSD& info);
-
- void draw();
-
- void storeOpenCloseState();
- void restoreOpenCloseState();
-
-protected:
- LLAccordionCtrlTab(const LLAccordionCtrlTab::Params&);
- friend class LLUICtrlFactory;
-
-// Overrides
-public:
-
- // Call reshape after changing size
- virtual void reshape(S32 width, S32 height, bool called_from_parent = true);
-
- /**
- * Raises notifyParent event with "child_visibility_change" = new_visibility
- */
- void onVisibilityChange(bool new_visibility);
- virtual void onUpdateScrollToChild(const LLUICtrl * cntrl);
-
- // Changes expand/collapse state and triggers expand/collapse callbacks
- virtual bool handleMouseDown(S32 x, S32 y, MASK mask);
-
- virtual bool handleMouseUp(S32 x, S32 y, MASK mask);
- virtual bool handleKey(KEY key, MASK mask, bool called_from_parent);
-
- virtual bool handleToolTip(S32 x, S32 y, MASK mask);
- virtual bool handleScrollWheel( S32 x, S32 y, S32 clicks );
-
-
- virtual bool addChild(LLView* child, S32 tab_group = 0 );
-
- bool isExpanded() const { return mDisplayChildren; }
-
- S32 getHeaderHeight();
-
- // Min size functions
-
- void setHeaderVisible(bool value);
-
- bool getHeaderVisible() { return mHeaderVisible;}
-
- S32 mExpandedHeight; // Height of expanded ctrl.
- // Used to restore height after expand.
-
- S32 getPaddingLeft() const { return mPaddingLeft;}
- S32 getPaddingRight() const { return mPaddingRight;}
- S32 getPaddingTop() const { return mPaddingTop;}
- S32 getPaddingBottom() const { return mPaddingBottom;}
-
- void showAndFocusHeader();
-
- void setFitPanel( bool fit ) { mFitPanel = true; }
- bool getFitParent() const { return mFitPanel; }
-
- void setIgnoreResizeNotification(bool ignore) { mSkipChangesOnNotifyParent = ignore;}
-
-protected:
- void adjustContainerPanel (const LLRect& child_rect);
- void adjustContainerPanel ();
- S32 getChildViewHeight ();
-
- void onScrollPosChangeCallback(S32, LLScrollbar*);
-
- void show_hide_scrollbar (const LLRect& child_rect);
- void showScrollbar (const LLRect& child_rect);
- void hideScrollbar (const LLRect& child_rect);
-
- void updateLayout ( const LLRect& child_rect );
- void ctrlSetLeftTopAndSize (LLView* panel, S32 left, S32 top, S32 width, S32 height);
-
- void drawChild(const LLRect& root_rect,LLView* child);
-
- LLView* findContainerView ();
-
- void selectOnFocusReceived();
- void deselectOnFocusLost();
-
-private:
-
- class LLAccordionCtrlTabHeader;
- LLAccordionCtrlTabHeader* mHeader; //Header
-
- bool mDisplayChildren; //Expanded/collapsed
- bool mCollapsible;
- bool mHeaderVisible;
-
- bool mCanOpenClose;
- bool mFitPanel;
-
- S32 mPaddingLeft;
- S32 mPaddingRight;
- S32 mPaddingTop;
- S32 mPaddingBottom;
-
- bool mStoredOpenCloseState;
- bool mWasStateStored;
- bool mSkipChangesOnNotifyParent;
-
- bool mSelectionEnabled;
-
- LLScrollbar* mScrollbar;
- LLView* mContainerPanel;
-
- LLUIColor mDropdownBGColor;
-};
-
-#endif
+/**
+ * @file LLAccordionCtrlTab.h
+ * @brief Collapsible box control implementation
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_ACCORDIONCTRLTAB_H_
+#define LL_ACCORDIONCTRLTAB_H_
+
+#include <string>
+#include "llrect.h"
+#include "lluictrl.h"
+#include "lluicolor.h"
+#include "llstyle.h"
+
+class LLUICtrlFactory;
+class LLUIImage;
+class LLButton;
+class LLTextBox;
+class LLScrollbar;
+
+
+
+// LLAccordionCtrlTab is a container for other controls.
+// It has a Header, by clicking on which hosted controls are shown or hidden.
+// When hosted controls are show - LLAccordionCtrlTab is expanded.
+// When hosted controls are hidden - LLAccordionCtrlTab is collapsed.
+
+class LLAccordionCtrlTab : public LLUICtrl
+{
+// Interface
+public:
+
+ struct Params
+ : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<bool> display_children, //expanded or collapsed after initialization
+ collapsible;
+
+ Optional<std::string> title;
+
+ Optional<S32> header_height,
+ min_width,
+ min_height;
+
+ // Overlay images (arrows on the left)
+ Mandatory<LLUIImage*> header_expand_img,
+ header_expand_img_pressed,
+ header_collapse_img,
+ header_collapse_img_pressed;
+
+ // Background images for the accordion tabs
+ Mandatory<LLUIImage*> header_image,
+ header_image_over,
+ header_image_pressed,
+ header_image_focused;
+
+ Optional<LLUIColor> header_bg_color,
+ header_text_color,
+ dropdown_bg_color;
+
+ Optional<bool> header_visible;
+
+ Optional<bool> fit_panel;
+
+ Optional<bool> selection_enabled;
+
+ Optional<S32> padding_left,
+ padding_right,
+ padding_top,
+ padding_bottom;
+
+ Params();
+ };
+
+ typedef LLDefaultChildRegistry child_registry_t;
+
+ virtual ~LLAccordionCtrlTab();
+
+ // Registers callback for expand/collapse events.
+ boost::signals2::connection setDropDownStateChangedCallback(commit_callback_t cb);
+
+ // Changes expand/collapse state
+ virtual void setDisplayChildren(bool display);
+
+ // Returns expand/collapse state
+ virtual bool getDisplayChildren() const { return mDisplayChildren; };
+
+ //set LLAccordionCtrlTab panel
+ void setAccordionView(LLView* panel);
+ LLView* getAccordionView() { return mContainerPanel; };
+
+ std::string getTitle() const;
+
+ // Set text and highlight substring in LLAccordionCtrlTabHeader
+ void setTitle(const std::string& title, const std::string& hl = LLStringUtil::null);
+
+ // Set text font style in LLAccordionCtrlTabHeader
+ void setTitleFontStyle(std::string style);
+
+ // Set text color in LLAccordionCtrlTabHeader
+ void setTitleColor(LLUIColor color);
+
+ boost::signals2::connection setFocusReceivedCallback(const focus_signal_t::slot_type& cb);
+ boost::signals2::connection setFocusLostCallback(const focus_signal_t::slot_type& cb);
+
+ void setSelected(bool is_selected);
+
+ bool getCollapsible() { return mCollapsible; };
+
+ void setCollapsible(bool collapsible) { mCollapsible = collapsible; };
+ void changeOpenClose(bool is_open);
+
+ void canOpenClose(bool can_open_close) { mCanOpenClose = can_open_close; };
+ bool canOpenClose() const { return mCanOpenClose; };
+
+ virtual bool postBuild();
+
+ S32 notifyParent(const LLSD& info);
+ S32 notify(const LLSD& info);
+ bool notifyChildren(const LLSD& info);
+
+ void draw();
+
+ void storeOpenCloseState();
+ void restoreOpenCloseState();
+
+protected:
+ LLAccordionCtrlTab(const LLAccordionCtrlTab::Params&);
+ friend class LLUICtrlFactory;
+
+// Overrides
+public:
+
+ // Call reshape after changing size
+ virtual void reshape(S32 width, S32 height, bool called_from_parent = true);
+
+ /**
+ * Raises notifyParent event with "child_visibility_change" = new_visibility
+ */
+ void onVisibilityChange(bool new_visibility);
+ virtual void onUpdateScrollToChild(const LLUICtrl * cntrl);
+
+ // Changes expand/collapse state and triggers expand/collapse callbacks
+ virtual bool handleMouseDown(S32 x, S32 y, MASK mask);
+
+ virtual bool handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual bool handleKey(KEY key, MASK mask, bool called_from_parent);
+
+ virtual bool handleToolTip(S32 x, S32 y, MASK mask);
+ virtual bool handleScrollWheel( S32 x, S32 y, S32 clicks );
+
+
+ virtual bool addChild(LLView* child, S32 tab_group = 0 );
+
+ bool isExpanded() const { return mDisplayChildren; }
+
+ S32 getHeaderHeight();
+
+ // Min size functions
+
+ void setHeaderVisible(bool value);
+
+ bool getHeaderVisible() { return mHeaderVisible;}
+
+ S32 mExpandedHeight; // Height of expanded ctrl.
+ // Used to restore height after expand.
+
+ S32 getPaddingLeft() const { return mPaddingLeft;}
+ S32 getPaddingRight() const { return mPaddingRight;}
+ S32 getPaddingTop() const { return mPaddingTop;}
+ S32 getPaddingBottom() const { return mPaddingBottom;}
+
+ void showAndFocusHeader();
+
+ void setFitPanel( bool fit ) { mFitPanel = true; }
+ bool getFitParent() const { return mFitPanel; }
+
+ void setIgnoreResizeNotification(bool ignore) { mSkipChangesOnNotifyParent = ignore;}
+
+protected:
+ void adjustContainerPanel (const LLRect& child_rect);
+ void adjustContainerPanel ();
+ S32 getChildViewHeight ();
+
+ void onScrollPosChangeCallback(S32, LLScrollbar*);
+
+ void show_hide_scrollbar (const LLRect& child_rect);
+ void showScrollbar (const LLRect& child_rect);
+ void hideScrollbar (const LLRect& child_rect);
+
+ void updateLayout ( const LLRect& child_rect );
+ void ctrlSetLeftTopAndSize (LLView* panel, S32 left, S32 top, S32 width, S32 height);
+
+ void drawChild(const LLRect& root_rect,LLView* child);
+
+ LLView* findContainerView ();
+
+ void selectOnFocusReceived();
+ void deselectOnFocusLost();
+
+private:
+
+ class LLAccordionCtrlTabHeader;
+ LLAccordionCtrlTabHeader* mHeader; //Header
+
+ bool mDisplayChildren; //Expanded/collapsed
+ bool mCollapsible;
+ bool mHeaderVisible;
+
+ bool mCanOpenClose;
+ bool mFitPanel;
+
+ S32 mPaddingLeft;
+ S32 mPaddingRight;
+ S32 mPaddingTop;
+ S32 mPaddingBottom;
+
+ bool mStoredOpenCloseState;
+ bool mWasStateStored;
+ bool mSkipChangesOnNotifyParent;
+
+ bool mSelectionEnabled;
+
+ LLScrollbar* mScrollbar;
+ LLView* mContainerPanel;
+
+ LLUIColor mDropdownBGColor;
+};
+
+#endif
diff --git a/indra/llui/llbadge.cpp b/indra/llui/llbadge.cpp
index f404b2e03b..1fa24afe8c 100644
--- a/indra/llui/llbadge.cpp
+++ b/indra/llui/llbadge.cpp
@@ -1,389 +1,389 @@
-/**
- * @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)
- , mDrawAtParentTop(false)
-{
- 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 && owner_view->isInVisibleChain())
- {
- //
- // 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)
- {
- if(mDrawAtParentTop)
- {
- badge_center_y = owner_rect.mTop - badge_height * 0.5f - 1;
- }
- else
- {
- 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
+/**
+ * @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)
+ , mDrawAtParentTop(false)
+{
+ 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 && owner_view->isInVisibleChain())
+ {
+ //
+ // 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)
+ {
+ if(mDrawAtParentTop)
+ {
+ badge_center_y = owner_rect.mTop - badge_height * 0.5f - 1;
+ }
+ else
+ {
+ 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
index d6d74bcd0b..d4b146d57b 100644
--- a/indra/llui/llbadge.h
+++ b/indra/llui/llbadge.h
@@ -1,177 +1,177 @@
-/**
- * @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);
-
- void setDrawAtParentTop(bool draw_at_top) { mDrawAtParentTop = draw_at_top;}
-
-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;
- bool mDrawAtParentTop;
-};
-
-// 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
+/**
+ * @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);
+
+ void setDrawAtParentTop(bool draw_at_top) { mDrawAtParentTop = draw_at_top;}
+
+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;
+ bool mDrawAtParentTop;
+};
+
+// 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/llbadgeholder.cpp b/indra/llui/llbadgeholder.cpp
index 1f786f36ae..bc22969ab4 100644
--- a/indra/llui/llbadgeholder.cpp
+++ b/indra/llui/llbadgeholder.cpp
@@ -1,25 +1,25 @@
-/**
+/**
* @file llbadgeholder.cpp
* @brief Source 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$
*/
@@ -32,14 +32,14 @@
bool LLBadgeHolder::addBadge(LLBadge * badge)
{
- bool badge_added = false;
+ bool badge_added = false;
- LLView * this_view = dynamic_cast<LLView *>(this);
+ LLView * this_view = dynamic_cast<LLView *>(this);
- if (this_view && mAcceptsBadge)
- {
- badge_added = badge->addToView(this_view);
- }
+ if (this_view && mAcceptsBadge)
+ {
+ badge_added = badge->addToView(this_view);
+ }
- return badge_added;
+ return badge_added;
}
diff --git a/indra/llui/llbadgeholder.h b/indra/llui/llbadgeholder.h
index 2538eaae91..470baf7a9a 100644
--- a/indra/llui/llbadgeholder.h
+++ b/indra/llui/llbadgeholder.h
@@ -1,25 +1,25 @@
-/**
+/**
* @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$
*/
@@ -37,19 +37,19 @@ class LLBadgeHolder
{
public:
- LLBadgeHolder(bool acceptsBadge)
- : mAcceptsBadge(acceptsBadge)
- {
- }
+ LLBadgeHolder(bool acceptsBadge)
+ : mAcceptsBadge(acceptsBadge)
+ {
+ }
- void setAcceptsBadge(bool acceptsBadge) { mAcceptsBadge = acceptsBadge; }
- bool acceptsBadge() const { return mAcceptsBadge; }
+ void setAcceptsBadge(bool acceptsBadge) { mAcceptsBadge = acceptsBadge; }
+ bool acceptsBadge() const { return mAcceptsBadge; }
- virtual bool addBadge(LLBadge * badge);
+ virtual bool addBadge(LLBadge * badge);
private:
- bool mAcceptsBadge;
+ bool mAcceptsBadge;
};
diff --git a/indra/llui/llbadgeowner.cpp b/indra/llui/llbadgeowner.cpp
index 5f11c383ef..3194a4b56f 100644
--- a/indra/llui/llbadgeowner.cpp
+++ b/indra/llui/llbadgeowner.cpp
@@ -1,25 +1,25 @@
-/**
+/**
* @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$
*/
@@ -35,86 +35,86 @@
//
LLBadgeOwner::LLBadgeOwner(LLHandle< LLView > viewHandle)
- : mHasBadgeHolderParent(false),
- mBadge(NULL),
- mBadgeOwnerView(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);
- }
- }
+ 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::reshapeBadge(const LLRect& new_rect)
{
- if (mBadge)
- {
- mBadge->setShape(new_rect);
- }
+ if (mBadge)
+ {
+ mBadge->setShape(new_rect);
+ }
}
void LLBadgeOwner::setBadgeVisibility(bool visible)
{
- if (mBadge)
- {
- mBadge->setVisible(visible);
- }
+ if (mBadge)
+ {
+ mBadge->setVisible(visible);
+ }
}
void LLBadgeOwner::setDrawBadgeAtTop(bool draw_at_top)
{
- if (mBadge)
- {
- mBadge->setDrawAtParentTop(draw_at_top);
- }
+ if (mBadge)
+ {
+ mBadge->setDrawAtParentTop(draw_at_top);
+ }
}
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);
- }
- }
+ 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;
+ LLBadge::Params badge_params(p);
+ badge_params.owner = mBadgeOwnerView;
- return LLUICtrlFactory::create<LLBadge>(badge_params);
+ return LLUICtrlFactory::create<LLBadge>(badge_params);
}
diff --git a/indra/llui/llbadgeowner.h b/indra/llui/llbadgeowner.h
index 4ce208fa0d..d2ac17cef0 100644
--- a/indra/llui/llbadgeowner.h
+++ b/indra/llui/llbadgeowner.h
@@ -1,25 +1,25 @@
-/**
+/**
* @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$
*/
@@ -38,24 +38,24 @@ class LLBadgeOwner
{
public:
- LLBadgeOwner(LLHandle< LLView > viewHandle);
+ LLBadgeOwner(LLHandle< LLView > viewHandle);
+
+ void initBadgeParams(const LLBadge::Params& p);
+ void addBadgeToParentHolder();
- void initBadgeParams(const LLBadge::Params& p);
- void addBadgeToParentHolder();
-
- bool hasBadgeHolderParent() const { return mHasBadgeHolderParent; };
- void setBadgeVisibility(bool visible);
- void setDrawBadgeAtTop(bool draw_at_top);
- void reshapeBadge(const LLRect& new_rect);
+ bool hasBadgeHolderParent() const { return mHasBadgeHolderParent; };
+ void setBadgeVisibility(bool visible);
+ void setDrawBadgeAtTop(bool draw_at_top);
+ void reshapeBadge(const LLRect& new_rect);
private:
- LLBadge* createBadge(const LLBadge::Params& p);
+ LLBadge* createBadge(const LLBadge::Params& p);
private:
- bool mHasBadgeHolderParent;
- LLBadge* mBadge;
- LLHandle< LLView > mBadgeOwnerView;
+ bool mHasBadgeHolderParent;
+ LLBadge* mBadge;
+ LLHandle< LLView > mBadgeOwnerView;
};
#endif // LL_LLBADGEOWNER_H
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index f34398cb6b..b343f5b4b4 100644
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -1,1320 +1,1321 @@
-
-/**
- * @file llbutton.cpp
- * @brief LLButton base class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#define LLBUTTON_CPP
-#include "llbutton.h"
-
-// Linden library includes
-#include "v4color.h"
-#include "llstring.h"
-
-// Project includes
-#include "llkeyboard.h"
-#include "llui.h"
-#include "lluiconstants.h"
-#include "llresmgr.h"
-#include "llcriticaldamp.h"
-#include "llfloater.h"
-#include "llfloaterreg.h"
-#include "llfocusmgr.h"
-#include "llwindow.h"
-#include "llnotificationsutil.h"
-#include "llrender.h"
-#include "lluictrlfactory.h"
-#include "lluiusage.h"
-#include "llhelp.h"
-#include "lldockablefloater.h"
-#include "llviewereventrecorder.h"
-
-static LLDefaultChildRegistry::Register<LLButton> r("button");
-
-// Compiler optimization, generate extern template
-template class LLButton* LLView::getChild<class LLButton>(
- const std::string& name, bool recurse) const;
-
-// globals loaded from settings.xml
-S32 LLBUTTON_H_PAD = 0;
-S32 BTN_HEIGHT_SMALL= 0;
-S32 BTN_HEIGHT = 0;
-
-LLButton::Params::Params()
-: label_selected("label_selected"), // requires is_toggle true
- label_shadow("label_shadow", true),
- auto_resize("auto_resize", false),
- use_ellipses("use_ellipses", false),
- use_font_color("use_font_color", true),
- image_unselected("image_unselected"),
- image_selected("image_selected"),
- image_hover_selected("image_hover_selected"),
- image_hover_unselected("image_hover_unselected"),
- image_disabled_selected("image_disabled_selected"),
- image_disabled("image_disabled"),
- image_pressed("image_pressed"),
- image_pressed_selected("image_pressed_selected"),
- image_overlay("image_overlay"),
- image_overlay_alignment("image_overlay_alignment", std::string("center")),
- image_top_pad("image_top_pad"),
- image_bottom_pad("image_bottom_pad"),
- imgoverlay_label_space("imgoverlay_label_space", 1),
- label_color("label_color"),
- label_color_selected("label_color_selected"), // requires is_toggle true
- label_color_disabled("label_color_disabled"),
- label_color_disabled_selected("label_color_disabled_selected"),
- image_color("image_color"),
- image_color_disabled("image_color_disabled"),
- 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::getInstance()->mSettingGroups["config"]->getS32("ButtonHPad")),
- pad_left("pad_left", LLUI::getInstance()->mSettingGroups["config"]->getS32("ButtonHPad")),
- pad_bottom("pad_bottom"),
- click_callback("click_callback"),
- mouse_down_callback("mouse_down_callback"),
- mouse_up_callback("mouse_up_callback"),
- mouse_held_callback("mouse_held_callback"),
- is_toggle("is_toggle", false),
- scale_image("scale_image", true),
- hover_glow_amount("hover_glow_amount"),
- commit_on_return("commit_on_return", true),
- commit_on_capture_lost("commit_on_capture_lost", false),
- 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");
- changeDefault(initial_value, LLSD(false));
-}
-
-
-LLButton::LLButton(const LLButton::Params& p)
-: LLUICtrl(p),
- LLBadgeOwner(getHandle()),
- mMouseDownFrame(0),
- mMouseHeldDownCount(0),
- mBorderEnabled( false ),
- mFlashing( false ),
- mCurGlowStrength(0.f),
- mNeedsHighlight(false),
- mUnselectedLabel(p.label()),
- mSelectedLabel(p.label_selected()),
- mGLFont(p.font),
- mHeldDownDelay(p.held_down_delay.seconds), // seconds until held-down callback is called
- mHeldDownFrameDelay(p.held_down_delay.frames),
- mImageUnselected(p.image_unselected),
- mImageSelected(p.image_selected),
- mImageDisabled(p.image_disabled),
- mImageDisabledSelected(p.image_disabled_selected),
- mImageFlash(p.image_flash),
- mImagePressed(p.image_pressed),
- mImagePressedSelected(p.image_pressed_selected),
- mImageHoverSelected(p.image_hover_selected),
- mImageHoverUnselected(p.image_hover_unselected),
- mUnselectedLabelColor(p.label_color()),
- mSelectedLabelColor(p.label_color_selected()),
- mDisabledLabelColor(p.label_color_disabled()),
- mDisabledSelectedLabelColor(p.label_color_disabled_selected()),
- 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),
- mImgOverlayLabelSpace(p.imgoverlay_label_space),
- mIsToggle(p.is_toggle),
- mScaleImage(p.scale_image),
- mDropShadowedText(p.label_shadow),
- mAutoResize(p.auto_resize),
- mUseEllipses( p.use_ellipses ),
- mUseFontColor( p.use_font_color),
- mHAlign(p.font_halign),
- mLeftHPad(p.pad_left),
- mRightHPad(p.pad_right),
- mBottomVPad(p.pad_bottom),
- mHoverGlowStrength(p.hover_glow_amount),
- mCommitOnReturn(p.commit_on_return),
- mCommitOnCaptureLost(p.commit_on_capture_lost),
- mFadeWhenDisabled(false),
- mForcePressedState(false),
- mDisplayPressedState(p.display_pressed_state),
- mLastDrawCharsCount(0),
- mMouseDownSignal(NULL),
- mMouseUpSignal(NULL),
- mHeldDownSignal(NULL),
- 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>());
-
- if (!p.label_selected.isProvided())
- {
- mSelectedLabel = mUnselectedLabel;
- }
-
- // Hack to make sure there is space for at least one character
- if (getRect().mRight >= 0 && getRect().getWidth() > 0 &&
- getRect().getWidth() - (mRightHPad + mLeftHPad) < mGLFont->getWidth(std::string(" ")))
- {
- // Use old defaults
- mLeftHPad = llbutton_orig_h_pad;
- mRightHPad = llbutton_orig_h_pad;
- }
-
- mMouseDownTimer.stop();
-
- // if custom unselected button image provided...
- if (p.image_unselected != default_params.image_unselected)
- {
- //...fade it out for disabled image by default...
- if (p.image_disabled() == default_params.image_disabled() )
- {
- mImageDisabled = p.image_unselected;
- mFadeWhenDisabled = true;
- }
-
- if (p.image_pressed_selected == default_params.image_pressed_selected)
- {
- mImagePressedSelected = mImageUnselected;
- }
- }
-
- // if custom selected button image provided...
- if (p.image_selected != default_params.image_selected)
- {
- //...fade it out for disabled image by default...
- if (p.image_disabled_selected() == default_params.image_disabled_selected())
- {
- mImageDisabledSelected = p.image_selected;
- mFadeWhenDisabled = true;
- }
-
- if (p.image_pressed == default_params.image_pressed)
- {
- mImagePressed = mImageSelected;
- }
- }
-
- if (!p.image_pressed.isProvided())
- {
- mImagePressed = mImageSelected;
- }
-
- if (!p.image_pressed_selected.isProvided())
- {
- mImagePressedSelected = mImageUnselected;
- }
-
- if (mImageUnselected.isNull())
- {
- LL_WARNS() << "Button: " << getName() << " with no image!" << LL_ENDL;
- }
-
- if (p.click_callback.isProvided())
- {
- setCommitCallback(initCommitCallback(p.click_callback)); // alias -> commit_callback
- }
- if (p.mouse_down_callback.isProvided())
- {
- setMouseDownCallback(initCommitCallback(p.mouse_down_callback));
- }
- if (p.mouse_up_callback.isProvided())
- {
- setMouseUpCallback(initCommitCallback(p.mouse_up_callback));
- }
- if (p.mouse_held_callback.isProvided())
- {
- setHeldDownCallback(initCommitCallback(p.mouse_held_callback));
- }
-
- if (p.badge.isProvided())
- {
- LLBadgeOwner::initBadgeParams(p.badge());
- }
-}
-
-LLButton::~LLButton()
-{
- delete mMouseDownSignal;
- delete mMouseUpSignal;
- delete mHeldDownSignal;
-
- if (mFlashingTimer)
- {
- mFlashingTimer->unset();
- }
-}
-
-// HACK: Committing a button is the same as instantly clicking it.
-// virtual
-void LLButton::onCommit()
-{
- // WARNING: Sometimes clicking a button destroys the floater or
- // panel containing it. Therefore we need to call LLUICtrl::onCommit()
- // LAST, otherwise this becomes deleted memory.
-
- if (mMouseDownSignal) (*mMouseDownSignal)(this, LLSD());
-
- if (mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
-
- if (getSoundFlags() & MOUSE_DOWN)
- {
- make_ui_sound("UISndClick");
- }
-
- if (getSoundFlags() & MOUSE_UP)
- {
- make_ui_sound("UISndClickRelease");
- }
-
- if (mIsToggle)
- {
- toggleState();
- }
-
- // do this last, as it can result in destroying this button
- 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();
- return mCommitSignal->connect(cb);
-}
-boost::signals2::connection LLButton::setMouseDownCallback( const commit_signal_t::slot_type& cb )
-{
- if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t();
- return mMouseDownSignal->connect(cb);
-}
-boost::signals2::connection LLButton::setMouseUpCallback( const commit_signal_t::slot_type& cb )
-{
- if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t();
- return mMouseUpSignal->connect(cb);
-}
-boost::signals2::connection LLButton::setHeldDownCallback( const commit_signal_t::slot_type& cb )
-{
- if (!mHeldDownSignal) mHeldDownSignal = new commit_signal_t();
- return mHeldDownSignal->connect(cb);
-}
-
-
-// *TODO: Deprecate (for backwards compatibility only)
-boost::signals2::connection LLButton::setClickedCallback( button_callback_t cb, void* data )
-{
- return setClickedCallback(boost::bind(cb, data));
-}
-boost::signals2::connection LLButton::setMouseDownCallback( button_callback_t cb, void* data )
-{
- return setMouseDownCallback(boost::bind(cb, data));
-}
-boost::signals2::connection LLButton::setMouseUpCallback( button_callback_t cb, void* data )
-{
- return setMouseUpCallback(boost::bind(cb, data));
-}
-boost::signals2::connection LLButton::setHeldDownCallback( button_callback_t cb, void* data )
-{
- return setHeldDownCallback(boost::bind(cb, data));
-}
-
-bool LLButton::postBuild()
-{
- autoResize();
-
- addBadgeToParentHolder();
-
- return LLUICtrl::postBuild();
-}
-
-bool LLButton::handleUnicodeCharHere(llwchar uni_char)
-{
- bool handled = false;
- if(' ' == uni_char
- && !gKeyboard->getKeyRepeated(' '))
- {
- if (mIsToggle)
- {
- toggleState();
- }
-
- LLUICtrl::onCommit();
-
- handled = true;
- }
- return handled;
-}
-
-bool LLButton::handleKeyHere(KEY key, MASK mask )
-{
- bool handled = false;
- if( mCommitOnReturn && KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key))
- {
- if (mIsToggle)
- {
- toggleState();
- }
-
- handled = true;
-
- LLUICtrl::onCommit();
- }
- return handled;
-}
-
-
-bool LLButton::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- if (!childrenHandleMouseDown(x, y, mask))
- {
- // Route future Mouse messages here preemptively. (Release on mouse up.)
- gFocusMgr.setMouseCapture( this );
-
- if (hasTabStop() && !getIsChrome())
- {
- setFocus(true);
- }
-
- if (!mFunctionName.empty())
- {
- LL_DEBUGS("UIUsage") << "calling mouse down function " << mFunctionName << LL_ENDL;
- LLUIUsage::instance().logCommand(mFunctionName);
- LLUIUsage::instance().logControl(getPathname());
- }
-
- /*
- * ATTENTION! This call fires another mouse down callback.
- * If you wish to remove this call emit that signal directly
- * by calling LLUICtrl::mMouseDownSignal(x, y, mask);
- */
- LLUICtrl::handleMouseDown(x, y, mask);
-
- LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
-
- if(mMouseDownSignal) (*mMouseDownSignal)(this, LLSD());
-
- mMouseDownTimer.start();
- mMouseDownFrame = (S32) LLFrameTimer::getFrameCount();
- mMouseHeldDownCount = 0;
-
-
- if (getSoundFlags() & MOUSE_DOWN)
- {
- make_ui_sound("UISndClick");
- }
- }
- return true;
-}
-
-
-bool LLButton::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- // We only handle the click if the click both started and ended within us
- if( hasMouseCapture() )
- {
- // reset timers before focus change, to not cause
- // additional commits if mCommitOnCaptureLost.
- resetMouseDownTimer();
-
- // Always release the mouse
- gFocusMgr.setMouseCapture( NULL );
-
- /*
- * ATTENTION! This call fires another mouse up callback.
- * If you wish to remove this call emit that signal directly
- * by calling LLUICtrl::mMouseUpSignal(x, y, mask);
- */
- LLUICtrl::handleMouseUp(x, y, mask);
- LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
-
- // Regardless of where mouseup occurs, handle callback
- if(mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
-
- // DO THIS AT THE VERY END to allow the button to be destroyed as a result of being clicked.
- // If mouseup in the widget, it's been clicked
- if (pointInView(x, y))
- {
- if (getSoundFlags() & MOUSE_UP)
- {
- make_ui_sound("UISndClickRelease");
- }
-
- if (mIsToggle)
- {
- toggleState();
- }
-
- LLUICtrl::onCommit();
- }
- }
- else
- {
- childrenHandleMouseUp(x, y, mask);
- }
-
- return true;
-}
-
-bool LLButton::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- if (mHandleRightMouse && !childrenHandleRightMouseDown(x, y, mask))
- {
- // Route future Mouse messages here preemptively. (Release on mouse up.)
- gFocusMgr.setMouseCapture( this );
-
- if (hasTabStop() && !getIsChrome())
- {
- setFocus(true);
- }
-
-// if (pointInView(x, y))
-// {
-// }
- // send the mouse down signal
- LLUICtrl::handleRightMouseDown(x,y,mask);
- // *TODO: Return result of LLUICtrl call above? Should defer to base class
- // but this might change the mouse handling of existing buttons in a bad way
- // if they are not mouse opaque.
- }
-
- return true;
-}
-
-bool LLButton::handleRightMouseUp(S32 x, S32 y, MASK mask)
-{
- if (mHandleRightMouse)
- {
- // 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);
- }
-
- // 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::onMouseLeave(S32 x, S32 y, MASK mask)
-{
- LLUICtrl::onMouseLeave(x, y, mask);
-
- mNeedsHighlight = false;
-}
-
-void LLButton::setHighlight(bool b)
-{
- mNeedsHighlight = 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())
- {
- F32 elapsed = getHeldDownTime();
- if( mHeldDownDelay <= elapsed && mHeldDownFrameDelay <= (S32)LLFrameTimer::getFrameCount() - mMouseDownFrame)
- {
- LLSD param;
- param["count"] = mMouseHeldDownCount++;
- if (mHeldDownSignal) (*mHeldDownSignal)(this, param);
- }
- }
-
- // We only handle the click if the click both started and ended within us
- getWindow()->setCursor(UI_CURSOR_ARROW);
- 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::getInstance()->mSettingGroups["config"], "EnableButtonFlashing", true);
- F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency();
-
- bool pressed_by_keyboard = false;
- if (hasFocus())
- {
- pressed_by_keyboard = gKeyboard->getKeyDown(' ') || (mCommitOnReturn && gKeyboard->getKeyDown(KEY_RETURN));
- }
-
- bool mouse_pressed_and_over = false;
- if (hasMouseCapture())
- {
- S32 local_mouse_x ;
- S32 local_mouse_y;
- LLUI::getInstance()->getMousePositionLocal(this, &local_mouse_x, &local_mouse_y);
- mouse_pressed_and_over = pointInView(local_mouse_x, local_mouse_y);
- }
-
- bool enabled = isInEnabledChain();
-
- bool pressed = pressed_by_keyboard
- || mouse_pressed_and_over
- || mForcePressedState;
- 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;
- LLUIImage* image_glow = NULL;
-
- // Cancel sticking of color, if the button is pressed,
- // or when a flashing of the previously selected button is ended
- if (mFlashingTimer
- && ((selected && !mFlashingTimer->isFlashingInProgress() && !mForceFlashing) || pressed))
- {
- mFlashing = false;
- }
-
- bool flash = mFlashing && sEnableButtonFlashing;
-
- if (pressed && mDisplayPressedState)
- {
- imagep = selected ? mImagePressedSelected : mImagePressed;
- }
- else if ( mNeedsHighlight )
- {
- if (selected)
- {
- if (mImageHoverSelected)
- {
- imagep = mImageHoverSelected;
- }
- else
- {
- imagep = mImageSelected;
- use_glow_effect = true;
- }
- }
- else
- {
- if (mImageHoverUnselected)
- {
- imagep = mImageHoverUnselected;
- }
- else
- {
- imagep = mImageUnselected;
- use_glow_effect = true;
- }
- }
- }
- else
- {
- imagep = selected ? mImageSelected : mImageUnselected;
- }
-
- // Override if more data is available
- // HACK: Use gray checked state to mean either:
- // enabled and tentative
- // or
- // disabled but checked
- if (!mImageDisabledSelected.isNull()
- &&
- ( (enabled && getTentative())
- || (!enabled && selected ) ) )
- {
- imagep = mImageDisabledSelected;
- }
- else if (!mImageDisabled.isNull()
- && !enabled
- && !selected)
- {
- imagep = mImageDisabled;
- }
-
- image_glow = imagep;
-
- if (mFlashing)
- {
- if (flash && mImageFlash)
- {
- // if button should flash and we have icon for flashing, use it as image for button
- image_glow = mImageFlash;
- }
-
- // provide fade-in and fade-out via flash_color
- if (mFlashingTimer)
- {
- LLColor4 flash_color = mFlashBgColor.get();
- use_glow_effect = true;
- glow_type = LLRender::BT_ALPHA; // blend the glow
-
- if (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress())
- {
- glow_color = flash_color;
- }
- else if (mNeedsHighlight)
- {
- glow_color = highlighting_color;
- }
- else
- {
- // will fade from highlight color
- glow_color = flash_color;
- }
- }
- }
-
- if (mNeedsHighlight && !imagep)
- {
- use_glow_effect = true;
- }
-
- // Figure out appropriate color for the text
- LLColor4 label_color;
-
- // label changes when button state changes, not when pressed
- if ( enabled )
- {
- if ( getToggleState() )
- {
- label_color = mSelectedLabelColor.get();
- }
- else
- {
- label_color = mUnselectedLabelColor.get();
- }
- }
- else
- {
- if ( getToggleState() )
- {
- label_color = mDisabledSelectedLabelColor.get();
- }
- else
- {
- label_color = mDisabledLabelColor.get();
- }
- }
-
- // Highlight if needed
- if( ll::ui::SearchableControl::getHighlighted() )
- label_color = ll::ui::SearchableControl::getHighlightColor();
-
- // Unselected label assignments
- LLWString label = getCurrentLabel();
-
- // overlay with keyboard focus border
- if (hasFocus())
- {
- F32 lerp_amt = gFocusMgr.getFocusFlashAmt();
- drawBorder(imagep, gFocusMgr.getFocusColor() % alpha, ll_round(lerp(1.f, 3.f, lerp_amt)));
- }
-
- if (use_glow_effect)
- {
- mCurGlowStrength = lerp(mCurGlowStrength,
- mFlashing ? (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress() || mNeedsHighlight? 1.f : 0.f) : mHoverGlowStrength,
- LLSmoothInterpolation::getInterpolant(0.05f));
- }
- else
- {
- mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLSmoothInterpolation::getInterpolant(0.05f));
- }
-
- // Draw button image, if available.
- // Otherwise draw basic rectangular button.
- if (imagep != NULL)
- {
- // apply automatic 50% alpha fade to disabled image
- LLColor4 disabled_color = mFadeWhenDisabled ? mDisabledImageColor.get() % 0.5f : mDisabledImageColor.get();
- if ( mScaleImage)
- {
- imagep->draw(getLocalRect(), (enabled ? mImageColor.get() : disabled_color) % alpha );
- if (mCurGlowStrength > 0.01f)
- {
- gGL.setSceneBlendType(glow_type);
- image_glow->drawSolid(0, 0, getRect().getWidth(), getRect().getHeight(), glow_color % (mCurGlowStrength * alpha));
- gGL.setSceneBlendType(LLRender::BT_ALPHA);
- }
- }
- else
- {
- S32 y = getLocalRect().getHeight() - imagep->getHeight();
- imagep->draw(0, y, (enabled ? mImageColor.get() : disabled_color) % alpha);
- if (mCurGlowStrength > 0.01f)
- {
- gGL.setSceneBlendType(glow_type);
- image_glow->drawSolid(0, y, glow_color % (mCurGlowStrength * alpha));
- gGL.setSceneBlendType(LLRender::BT_ALPHA);
- }
- }
- }
- else
- {
- // no image
- 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);
- }
-
- // let overlay image and text play well together
- S32 text_left = mLeftHPad;
- S32 text_right = getRect().getWidth() - mRightHPad;
- S32 text_width = getRect().getWidth() - mLeftHPad - mRightHPad;
-
- // draw overlay image
- if (mImageOverlay.notNull())
- {
- // get max width and height (discard level 0)
- S32 overlay_width;
- S32 overlay_height;
-
- getOverlayImageSize(overlay_width, overlay_height);
-
- S32 center_x = getLocalRect().getCenterX();
- S32 center_y = getLocalRect().getCenterY();
-
- //FUGLY HACK FOR "DEPRESSED" BUTTONS
- if (pressed && mDisplayPressedState)
- {
- center_y--;
- center_x++;
- }
-
- center_y += (mImageOverlayBottomPad - mImageOverlayTopPad);
- // fade out overlay images on disabled buttons
- LLColor4 overlay_color = mImageOverlayColor.get();
- if (!enabled)
- {
- overlay_color = mImageOverlayDisabledColor.get();
- }
- else if (getToggleState())
- {
- overlay_color = mImageOverlaySelectedColor.get();
- }
- overlay_color.mV[VALPHA] *= alpha;
-
- switch(mImageOverlayAlignment)
- {
- case LLFontGL::LEFT:
- text_left += overlay_width + mImgOverlayLabelSpace;
- text_width -= overlay_width + mImgOverlayLabelSpace;
- mImageOverlay->draw(
- mLeftHPad,
- center_y - (overlay_height / 2),
- overlay_width,
- overlay_height,
- overlay_color);
- break;
- case LLFontGL::HCENTER:
- mImageOverlay->draw(
- center_x - (overlay_width / 2),
- center_y - (overlay_height / 2),
- overlay_width,
- overlay_height,
- overlay_color);
- 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),
- overlay_width,
- overlay_height,
- overlay_color);
- break;
- default:
- // draw nothing
- break;
- }
- }
-
- // Draw label
- if( !label.empty() )
- {
- LLWStringUtil::trim(label);
-
- S32 x;
- switch( mHAlign )
- {
- case LLFontGL::RIGHT:
- x = text_right;
- break;
- case LLFontGL::HCENTER:
- x = text_left + (text_width / 2);
- break;
- case LLFontGL::LEFT:
- default:
- x = text_left;
- break;
- }
-
- if (pressed && mDisplayPressedState)
- {
- x++;
- }
-
- // *NOTE: mantipov: before mUseEllipses is implemented in EXT-279 U32_MAX has been passed as
- // max_chars.
- // LLFontGL::render expects S32 max_chars variable but process in a separate way -1 value.
- // Due to U32_MAX is equal to S32 -1 value I have rest this value for non-ellipses mode.
- // Not sure if it is really needed. Probably S32_MAX should be always passed as max_chars.
- mLastDrawCharsCount = mGLFont->render(label, 0,
- (F32)x,
- (F32)(getRect().getHeight() / 2 + mBottomVPad),
- label_color % alpha,
- mHAlign, LLFontGL::VCENTER,
- LLFontGL::NORMAL,
- mDropShadowedText ? LLFontGL::DROP_SHADOW_SOFT : LLFontGL::NO_SHADOW,
- S32_MAX, text_width,
- NULL, mUseEllipses, mUseFontColor);
- }
-
- LLUICtrl::draw();
-}
-
-void LLButton::drawBorder(LLUIImage* imagep, const LLColor4& color, S32 size)
-{
- if (imagep == NULL) return;
- if (mScaleImage)
- {
- imagep->drawBorder(getLocalRect(), color, size);
- }
- else
- {
- S32 y = getLocalRect().getHeight() - imagep->getHeight();
- imagep->drawBorder(0, y, color, size);
- }
-}
-
-bool LLButton::getToggleState() const
-{
- return getValue().asBoolean();
-}
-
-void LLButton::setToggleState(bool b)
-{
- if( b != getToggleState() )
- {
- 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, bool force_flashing/* = false */)
-{
- mForceFlashing = force_flashing;
- if (mFlashingTimer)
- {
- mFlashing = b;
- (b ? mFlashingTimer->startFlashing() : mFlashingTimer->stopFlashing());
- }
- else if (b != mFlashing)
- {
- mFlashing = b;
- mFrameTimer.reset();
- }
-}
-
-bool LLButton::toggleState()
-{
- bool flipped = ! getToggleState();
- setToggleState(flipped);
-
- return flipped;
-}
-
-void LLButton::setLabel( const std::string& label )
-{
- mUnselectedLabel = mSelectedLabel = label;
-}
-
-void LLButton::setLabel( const LLUIString& label )
-{
- mUnselectedLabel = mSelectedLabel = label;
-}
-
-void LLButton::setLabel( const LLStringExplicit& label )
-{
- setLabelUnselected(label);
- setLabelSelected(label);
-}
-
-//virtual
-bool LLButton::setLabelArg( const std::string& key, const LLStringExplicit& text )
-{
- mUnselectedLabel.setArg(key, text);
- mSelectedLabel.setArg(key, text);
- return true;
-}
-
-void LLButton::setLabelUnselected( const LLStringExplicit& label )
-{
- mUnselectedLabel = label;
-}
-
-void LLButton::setLabelSelected( const LLStringExplicit& label )
-{
- mSelectedLabel = label;
-}
-
-bool LLButton::labelIsTruncated() const
-{
- return getCurrentLabel().getString().size() > mLastDrawCharsCount;
-}
-
-const LLUIString& LLButton::getCurrentLabel() const
-{
- return getToggleState() ? mSelectedLabel : mUnselectedLabel;
-}
-
-void LLButton::setImageUnselected(LLPointer<LLUIImage> image)
-{
- mImageUnselected = image;
- if (mImageUnselected.isNull())
- {
- LL_WARNS() << "Setting default button image for: " << getName() << " to NULL" << LL_ENDL;
- }
-}
-
-void LLButton::autoResize()
-{
- resize(getCurrentLabel());
-}
-
-void LLButton::resize(LLUIString label)
-{
- // get label length
- S32 label_width = mGLFont->getWidth(label.getString());
- // get current btn length
- S32 btn_width =getRect().getWidth();
- // check if it need resize
- if (mAutoResize)
- {
- S32 min_width = label_width + mLeftHPad + mRightHPad;
- if (mImageOverlay)
- {
- 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());
- }
- }
-}
-void LLButton::setImages( const std::string &image_name, const std::string &selected_name )
-{
- setImageUnselected(LLUI::getUIImage(image_name));
- setImageSelected(LLUI::getUIImage(selected_name));
-}
-
-void LLButton::setImageSelected(LLPointer<LLUIImage> image)
-{
- mImageSelected = image;
-}
-
-void LLButton::setImageColor(const LLColor4& c)
-{
- mImageColor = c;
-}
-
-void LLButton::setColor(const LLColor4& color)
-{
- setImageColor(color);
-}
-
-void LLButton::setImageDisabled(LLPointer<LLUIImage> image)
-{
- mImageDisabled = image;
- mDisabledImageColor = mImageColor;
- mFadeWhenDisabled = true;
-}
-
-void LLButton::setImageDisabledSelected(LLPointer<LLUIImage> image)
-{
- mImageDisabledSelected = image;
- mDisabledImageColor = mImageColor;
- mFadeWhenDisabled = true;
-}
-
-void LLButton::setImagePressed(LLPointer<LLUIImage> image)
-{
- mImagePressed = image;
-}
-
-void LLButton::setImageHoverSelected(LLPointer<LLUIImage> image)
-{
- mImageHoverSelected = image;
-}
-
-void LLButton::setImageHoverUnselected(LLPointer<LLUIImage> image)
-{
- mImageHoverUnselected = image;
-}
-
-void LLButton::setImageFlash(LLPointer<LLUIImage> image)
-{
- mImageFlash = image;
-}
-
-void LLButton::setImageOverlay(const std::string& image_name, LLFontGL::HAlign alignment, const LLColor4& color)
-{
- if (image_name.empty())
- {
- mImageOverlay = NULL;
- }
- else
- {
- mImageOverlay = LLUI::getUIImage(image_name);
- mImageOverlayAlignment = alignment;
- mImageOverlayColor = color;
- }
-}
-
-void LLButton::setImageOverlay(const LLUUID& image_id, LLFontGL::HAlign alignment, const LLColor4& color)
-{
- if (image_id.isNull())
- {
- mImageOverlay = NULL;
- }
- else
- {
- mImageOverlay = LLUI::getUIImageByID(image_id);
- mImageOverlayAlignment = alignment;
- mImageOverlayColor = color;
- }
-}
-
-void LLButton::onMouseCaptureLost()
-{
- if (mCommitOnCaptureLost
- && mMouseDownTimer.getStarted())
- {
- if (mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
-
- if (mIsToggle)
- {
- toggleState();
- }
-
- LLUICtrl::onCommit();
- }
- resetMouseDownTimer();
-}
-
-//-------------------------------------------------------------------------
-// Utilities
-//-------------------------------------------------------------------------
-S32 round_up(S32 grid, S32 value)
-{
- S32 mod = value % grid;
-
- if (mod > 0)
- {
- // not even multiple
- return value + (grid - mod);
- }
- else
- {
- return value;
- }
-}
-
-void LLButton::addImageAttributeToXML(LLXMLNodePtr node,
- const std::string& image_name,
- const LLUUID& image_id,
- const std::string& xml_tag_name) const
-{
- if( !image_name.empty() )
- {
- node->createChild(xml_tag_name.c_str(), true)->setStringValue(image_name);
- }
- else if( image_id != LLUUID::null )
- {
- node->createChild((xml_tag_name + "_id").c_str(), true)->setUUIDValue(image_id);
- }
-}
-
-
-// static
-void LLButton::toggleFloaterAndSetToggleState(LLUICtrl* ctrl, const LLSD& sdname)
-{
- bool floater_vis = LLFloaterReg::toggleInstance(sdname.asString());
- LLButton* button = dynamic_cast<LLButton*>(ctrl);
- if (button)
- button->setToggleState(floater_vis);
-}
-
-// static
-// Gets called once
-void LLButton::setFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname)
-{
- LLButton* button = dynamic_cast<LLButton*>(ctrl);
- if (!button)
- return;
- // Get the visibility control name for the floater
- std::string vis_control_name = LLFloaterReg::declareVisibilityControl(sdname.asString());
- // Set the button control value (toggle state) to the floater visibility control (Sets the value as well)
- button->setControlVariable(LLFloater::getControlGroup()->getControl(vis_control_name));
- // Set the clicked callback to toggle the floater
- button->setClickedCallback(boost::bind(&LLFloaterReg::toggleInstance, sdname, LLSD()));
-}
-
-// static
-void LLButton::setDockableFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname)
-{
- LLButton* button = dynamic_cast<LLButton*>(ctrl);
- if (!button)
- return;
- // Get the visibility control name for the floater
- std::string vis_control_name = LLFloaterReg::declareVisibilityControl(sdname.asString());
- // Set the button control value (toggle state) to the floater visibility control (Sets the value as well)
- button->setControlVariable(LLFloater::getControlGroup()->getControl(vis_control_name));
- // Set the clicked callback to toggle the floater
- button->setClickedCallback(boost::bind(&LLDockableFloater::toggleInstance, sdname));
-}
-
-// static
-void LLButton::showHelp(LLUICtrl* ctrl, const LLSD& sdname)
-{
- // search back through the button's parents for a panel
- // with a help_topic string defined
- std::string help_topic;
- if (LLUI::getInstance()->mHelpImpl &&
- ctrl->findHelpTopic(help_topic))
- {
- LLUI::getInstance()->mHelpImpl->showTopic(help_topic);
- return; // success
- }
-
- // display an error if we can't find a help_topic string.
- // fix this by adding a help_topic attribute to the xui file
- LLNotificationsUtil::add("UnableToFindHelpTopic");
-}
-
-void LLButton::resetMouseDownTimer()
-{
- mMouseDownTimer.stop();
- mMouseDownTimer.reset();
-}
-
-bool LLButton::handleDoubleClick(S32 x, S32 y, MASK mask)
-{
- // just treat a double click as a second click
- return handleMouseDown(x, y, mask);
-}
+
+/**
+ * @file llbutton.cpp
+ * @brief LLButton base class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#define LLBUTTON_CPP
+#include "llbutton.h"
+
+// Linden library includes
+#include "v4color.h"
+#include "llstring.h"
+
+// Project includes
+#include "llkeyboard.h"
+#include "llui.h"
+#include "lluiconstants.h"
+#include "llresmgr.h"
+#include "llcriticaldamp.h"
+#include "llfloater.h"
+#include "llfloaterreg.h"
+#include "llfocusmgr.h"
+#include "llwindow.h"
+#include "llnotificationsutil.h"
+#include "llrender.h"
+#include "lluictrlfactory.h"
+#include "lluiusage.h"
+#include "llhelp.h"
+#include "lldockablefloater.h"
+#include "llviewereventrecorder.h"
+
+static LLDefaultChildRegistry::Register<LLButton> r("button");
+
+// Compiler optimization, generate extern template
+template class LLButton* LLView::getChild<class LLButton>(
+ const std::string& name, bool recurse) const;
+
+// globals
+S32 LLBUTTON_H_PAD = 4;
+S32 BTN_HEIGHT_SMALL= 23;
+S32 BTN_HEIGHT = 23;
+S32 BTN_DROP_SHADOW = 2;
+
+LLButton::Params::Params()
+: label_selected("label_selected"), // requires is_toggle true
+ label_shadow("label_shadow", true),
+ auto_resize("auto_resize", false),
+ use_ellipses("use_ellipses", false),
+ use_font_color("use_font_color", true),
+ image_unselected("image_unselected"),
+ image_selected("image_selected"),
+ image_hover_selected("image_hover_selected"),
+ image_hover_unselected("image_hover_unselected"),
+ image_disabled_selected("image_disabled_selected"),
+ image_disabled("image_disabled"),
+ image_pressed("image_pressed"),
+ image_pressed_selected("image_pressed_selected"),
+ image_overlay("image_overlay"),
+ image_overlay_alignment("image_overlay_alignment", std::string("center")),
+ image_top_pad("image_top_pad"),
+ image_bottom_pad("image_bottom_pad"),
+ imgoverlay_label_space("imgoverlay_label_space", 1),
+ label_color("label_color"),
+ label_color_selected("label_color_selected"), // requires is_toggle true
+ label_color_disabled("label_color_disabled"),
+ label_color_disabled_selected("label_color_disabled_selected"),
+ image_color("image_color"),
+ image_color_disabled("image_color_disabled"),
+ 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", LLBUTTON_H_PAD),
+ pad_left("pad_left", LLBUTTON_H_PAD),
+ pad_bottom("pad_bottom"),
+ click_callback("click_callback"),
+ mouse_down_callback("mouse_down_callback"),
+ mouse_up_callback("mouse_up_callback"),
+ mouse_held_callback("mouse_held_callback"),
+ is_toggle("is_toggle", false),
+ scale_image("scale_image", true),
+ hover_glow_amount("hover_glow_amount"),
+ commit_on_return("commit_on_return", true),
+ commit_on_capture_lost("commit_on_capture_lost", false),
+ 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");
+ changeDefault(initial_value, LLSD(false));
+}
+
+
+LLButton::LLButton(const LLButton::Params& p)
+: LLUICtrl(p),
+ LLBadgeOwner(getHandle()),
+ mMouseDownFrame(0),
+ mMouseHeldDownCount(0),
+ mBorderEnabled( false ),
+ mFlashing( false ),
+ mCurGlowStrength(0.f),
+ mNeedsHighlight(false),
+ mUnselectedLabel(p.label()),
+ mSelectedLabel(p.label_selected()),
+ mGLFont(p.font),
+ mHeldDownDelay(p.held_down_delay.seconds), // seconds until held-down callback is called
+ mHeldDownFrameDelay(p.held_down_delay.frames),
+ mImageUnselected(p.image_unselected),
+ mImageSelected(p.image_selected),
+ mImageDisabled(p.image_disabled),
+ mImageDisabledSelected(p.image_disabled_selected),
+ mImageFlash(p.image_flash),
+ mImagePressed(p.image_pressed),
+ mImagePressedSelected(p.image_pressed_selected),
+ mImageHoverSelected(p.image_hover_selected),
+ mImageHoverUnselected(p.image_hover_unselected),
+ mUnselectedLabelColor(p.label_color()),
+ mSelectedLabelColor(p.label_color_selected()),
+ mDisabledLabelColor(p.label_color_disabled()),
+ mDisabledSelectedLabelColor(p.label_color_disabled_selected()),
+ 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),
+ mImgOverlayLabelSpace(p.imgoverlay_label_space),
+ mIsToggle(p.is_toggle),
+ mScaleImage(p.scale_image),
+ mDropShadowedText(p.label_shadow),
+ mAutoResize(p.auto_resize),
+ mUseEllipses( p.use_ellipses ),
+ mUseFontColor( p.use_font_color),
+ mHAlign(p.font_halign),
+ mLeftHPad(p.pad_left),
+ mRightHPad(p.pad_right),
+ mBottomVPad(p.pad_bottom),
+ mHoverGlowStrength(p.hover_glow_amount),
+ mCommitOnReturn(p.commit_on_return),
+ mCommitOnCaptureLost(p.commit_on_capture_lost),
+ mFadeWhenDisabled(false),
+ mForcePressedState(false),
+ mDisplayPressedState(p.display_pressed_state),
+ mLastDrawCharsCount(0),
+ mMouseDownSignal(NULL),
+ mMouseUpSignal(NULL),
+ mHeldDownSignal(NULL),
+ 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>());
+
+ if (!p.label_selected.isProvided())
+ {
+ mSelectedLabel = mUnselectedLabel;
+ }
+
+ // Hack to make sure there is space for at least one character
+ if (getRect().mRight >= 0 && getRect().getWidth() > 0 &&
+ getRect().getWidth() - (mRightHPad + mLeftHPad) < mGLFont->getWidth(std::string(" ")))
+ {
+ // Use old defaults
+ mLeftHPad = llbutton_orig_h_pad;
+ mRightHPad = llbutton_orig_h_pad;
+ }
+
+ mMouseDownTimer.stop();
+
+ // if custom unselected button image provided...
+ if (p.image_unselected != default_params.image_unselected)
+ {
+ //...fade it out for disabled image by default...
+ if (p.image_disabled() == default_params.image_disabled() )
+ {
+ mImageDisabled = p.image_unselected;
+ mFadeWhenDisabled = true;
+ }
+
+ if (p.image_pressed_selected == default_params.image_pressed_selected)
+ {
+ mImagePressedSelected = mImageUnselected;
+ }
+ }
+
+ // if custom selected button image provided...
+ if (p.image_selected != default_params.image_selected)
+ {
+ //...fade it out for disabled image by default...
+ if (p.image_disabled_selected() == default_params.image_disabled_selected())
+ {
+ mImageDisabledSelected = p.image_selected;
+ mFadeWhenDisabled = true;
+ }
+
+ if (p.image_pressed == default_params.image_pressed)
+ {
+ mImagePressed = mImageSelected;
+ }
+ }
+
+ if (!p.image_pressed.isProvided())
+ {
+ mImagePressed = mImageSelected;
+ }
+
+ if (!p.image_pressed_selected.isProvided())
+ {
+ mImagePressedSelected = mImageUnselected;
+ }
+
+ if (mImageUnselected.isNull())
+ {
+ LL_WARNS() << "Button: " << getName() << " with no image!" << LL_ENDL;
+ }
+
+ if (p.click_callback.isProvided())
+ {
+ setCommitCallback(initCommitCallback(p.click_callback)); // alias -> commit_callback
+ }
+ if (p.mouse_down_callback.isProvided())
+ {
+ setMouseDownCallback(initCommitCallback(p.mouse_down_callback));
+ }
+ if (p.mouse_up_callback.isProvided())
+ {
+ setMouseUpCallback(initCommitCallback(p.mouse_up_callback));
+ }
+ if (p.mouse_held_callback.isProvided())
+ {
+ setHeldDownCallback(initCommitCallback(p.mouse_held_callback));
+ }
+
+ if (p.badge.isProvided())
+ {
+ LLBadgeOwner::initBadgeParams(p.badge());
+ }
+}
+
+LLButton::~LLButton()
+{
+ delete mMouseDownSignal;
+ delete mMouseUpSignal;
+ delete mHeldDownSignal;
+
+ if (mFlashingTimer)
+ {
+ mFlashingTimer->unset();
+ }
+}
+
+// HACK: Committing a button is the same as instantly clicking it.
+// virtual
+void LLButton::onCommit()
+{
+ // WARNING: Sometimes clicking a button destroys the floater or
+ // panel containing it. Therefore we need to call LLUICtrl::onCommit()
+ // LAST, otherwise this becomes deleted memory.
+
+ if (mMouseDownSignal) (*mMouseDownSignal)(this, LLSD());
+
+ if (mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
+
+ if (getSoundFlags() & MOUSE_DOWN)
+ {
+ make_ui_sound("UISndClick");
+ }
+
+ if (getSoundFlags() & MOUSE_UP)
+ {
+ make_ui_sound("UISndClickRelease");
+ }
+
+ if (mIsToggle)
+ {
+ toggleState();
+ }
+
+ // do this last, as it can result in destroying this button
+ 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();
+ return mCommitSignal->connect(cb);
+}
+boost::signals2::connection LLButton::setMouseDownCallback( const commit_signal_t::slot_type& cb )
+{
+ if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t();
+ return mMouseDownSignal->connect(cb);
+}
+boost::signals2::connection LLButton::setMouseUpCallback( const commit_signal_t::slot_type& cb )
+{
+ if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t();
+ return mMouseUpSignal->connect(cb);
+}
+boost::signals2::connection LLButton::setHeldDownCallback( const commit_signal_t::slot_type& cb )
+{
+ if (!mHeldDownSignal) mHeldDownSignal = new commit_signal_t();
+ return mHeldDownSignal->connect(cb);
+}
+
+
+// *TODO: Deprecate (for backwards compatibility only)
+boost::signals2::connection LLButton::setClickedCallback( button_callback_t cb, void* data )
+{
+ return setClickedCallback(boost::bind(cb, data));
+}
+boost::signals2::connection LLButton::setMouseDownCallback( button_callback_t cb, void* data )
+{
+ return setMouseDownCallback(boost::bind(cb, data));
+}
+boost::signals2::connection LLButton::setMouseUpCallback( button_callback_t cb, void* data )
+{
+ return setMouseUpCallback(boost::bind(cb, data));
+}
+boost::signals2::connection LLButton::setHeldDownCallback( button_callback_t cb, void* data )
+{
+ return setHeldDownCallback(boost::bind(cb, data));
+}
+
+bool LLButton::postBuild()
+{
+ autoResize();
+
+ addBadgeToParentHolder();
+
+ return LLUICtrl::postBuild();
+}
+
+bool LLButton::handleUnicodeCharHere(llwchar uni_char)
+{
+ bool handled = false;
+ if(' ' == uni_char
+ && !gKeyboard->getKeyRepeated(' '))
+ {
+ if (mIsToggle)
+ {
+ toggleState();
+ }
+
+ LLUICtrl::onCommit();
+
+ handled = true;
+ }
+ return handled;
+}
+
+bool LLButton::handleKeyHere(KEY key, MASK mask )
+{
+ bool handled = false;
+ if( mCommitOnReturn && KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key))
+ {
+ if (mIsToggle)
+ {
+ toggleState();
+ }
+
+ handled = true;
+
+ LLUICtrl::onCommit();
+ }
+ return handled;
+}
+
+
+bool LLButton::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (!childrenHandleMouseDown(x, y, mask))
+ {
+ // Route future Mouse messages here preemptively. (Release on mouse up.)
+ gFocusMgr.setMouseCapture( this );
+
+ if (hasTabStop() && !getIsChrome())
+ {
+ setFocus(true);
+ }
+
+ if (!mFunctionName.empty())
+ {
+ LL_DEBUGS("UIUsage") << "calling mouse down function " << mFunctionName << LL_ENDL;
+ LLUIUsage::instance().logCommand(mFunctionName);
+ LLUIUsage::instance().logControl(getPathname());
+ }
+
+ /*
+ * ATTENTION! This call fires another mouse down callback.
+ * If you wish to remove this call emit that signal directly
+ * by calling LLUICtrl::mMouseDownSignal(x, y, mask);
+ */
+ LLUICtrl::handleMouseDown(x, y, mask);
+
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
+
+ if(mMouseDownSignal) (*mMouseDownSignal)(this, LLSD());
+
+ mMouseDownTimer.start();
+ mMouseDownFrame = (S32) LLFrameTimer::getFrameCount();
+ mMouseHeldDownCount = 0;
+
+
+ if (getSoundFlags() & MOUSE_DOWN)
+ {
+ make_ui_sound("UISndClick");
+ }
+ }
+ return true;
+}
+
+
+bool LLButton::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ // We only handle the click if the click both started and ended within us
+ if( hasMouseCapture() )
+ {
+ // reset timers before focus change, to not cause
+ // additional commits if mCommitOnCaptureLost.
+ resetMouseDownTimer();
+
+ // Always release the mouse
+ gFocusMgr.setMouseCapture( NULL );
+
+ /*
+ * ATTENTION! This call fires another mouse up callback.
+ * If you wish to remove this call emit that signal directly
+ * by calling LLUICtrl::mMouseUpSignal(x, y, mask);
+ */
+ LLUICtrl::handleMouseUp(x, y, mask);
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
+
+ // Regardless of where mouseup occurs, handle callback
+ if(mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
+
+ // DO THIS AT THE VERY END to allow the button to be destroyed as a result of being clicked.
+ // If mouseup in the widget, it's been clicked
+ if (pointInView(x, y))
+ {
+ if (getSoundFlags() & MOUSE_UP)
+ {
+ make_ui_sound("UISndClickRelease");
+ }
+
+ if (mIsToggle)
+ {
+ toggleState();
+ }
+
+ LLUICtrl::onCommit();
+ }
+ }
+ else
+ {
+ childrenHandleMouseUp(x, y, mask);
+ }
+
+ return true;
+}
+
+bool LLButton::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (mHandleRightMouse && !childrenHandleRightMouseDown(x, y, mask))
+ {
+ // Route future Mouse messages here preemptively. (Release on mouse up.)
+ gFocusMgr.setMouseCapture( this );
+
+ if (hasTabStop() && !getIsChrome())
+ {
+ setFocus(true);
+ }
+
+// if (pointInView(x, y))
+// {
+// }
+ // send the mouse down signal
+ LLUICtrl::handleRightMouseDown(x,y,mask);
+ // *TODO: Return result of LLUICtrl call above? Should defer to base class
+ // but this might change the mouse handling of existing buttons in a bad way
+ // if they are not mouse opaque.
+ }
+
+ return true;
+}
+
+bool LLButton::handleRightMouseUp(S32 x, S32 y, MASK mask)
+{
+ if (mHandleRightMouse)
+ {
+ // 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);
+ }
+
+ // 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::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+ LLUICtrl::onMouseLeave(x, y, mask);
+
+ mNeedsHighlight = false;
+}
+
+void LLButton::setHighlight(bool b)
+{
+ mNeedsHighlight = 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())
+ {
+ F32 elapsed = getHeldDownTime();
+ if( mHeldDownDelay <= elapsed && mHeldDownFrameDelay <= (S32)LLFrameTimer::getFrameCount() - mMouseDownFrame)
+ {
+ LLSD param;
+ param["count"] = mMouseHeldDownCount++;
+ if (mHeldDownSignal) (*mHeldDownSignal)(this, param);
+ }
+ }
+
+ // We only handle the click if the click both started and ended within us
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ 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::getInstance()->mSettingGroups["config"], "EnableButtonFlashing", true);
+ F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency();
+
+ bool pressed_by_keyboard = false;
+ if (hasFocus())
+ {
+ pressed_by_keyboard = gKeyboard->getKeyDown(' ') || (mCommitOnReturn && gKeyboard->getKeyDown(KEY_RETURN));
+ }
+
+ bool mouse_pressed_and_over = false;
+ if (hasMouseCapture())
+ {
+ S32 local_mouse_x ;
+ S32 local_mouse_y;
+ LLUI::getInstance()->getMousePositionLocal(this, &local_mouse_x, &local_mouse_y);
+ mouse_pressed_and_over = pointInView(local_mouse_x, local_mouse_y);
+ }
+
+ bool enabled = isInEnabledChain();
+
+ bool pressed = pressed_by_keyboard
+ || mouse_pressed_and_over
+ || mForcePressedState;
+ 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;
+ LLUIImage* image_glow = NULL;
+
+ // Cancel sticking of color, if the button is pressed,
+ // or when a flashing of the previously selected button is ended
+ if (mFlashingTimer
+ && ((selected && !mFlashingTimer->isFlashingInProgress() && !mForceFlashing) || pressed))
+ {
+ mFlashing = false;
+ }
+
+ bool flash = mFlashing && sEnableButtonFlashing;
+
+ if (pressed && mDisplayPressedState)
+ {
+ imagep = selected ? mImagePressedSelected : mImagePressed;
+ }
+ else if ( mNeedsHighlight )
+ {
+ if (selected)
+ {
+ if (mImageHoverSelected)
+ {
+ imagep = mImageHoverSelected;
+ }
+ else
+ {
+ imagep = mImageSelected;
+ use_glow_effect = true;
+ }
+ }
+ else
+ {
+ if (mImageHoverUnselected)
+ {
+ imagep = mImageHoverUnselected;
+ }
+ else
+ {
+ imagep = mImageUnselected;
+ use_glow_effect = true;
+ }
+ }
+ }
+ else
+ {
+ imagep = selected ? mImageSelected : mImageUnselected;
+ }
+
+ // Override if more data is available
+ // HACK: Use gray checked state to mean either:
+ // enabled and tentative
+ // or
+ // disabled but checked
+ if (!mImageDisabledSelected.isNull()
+ &&
+ ( (enabled && getTentative())
+ || (!enabled && selected ) ) )
+ {
+ imagep = mImageDisabledSelected;
+ }
+ else if (!mImageDisabled.isNull()
+ && !enabled
+ && !selected)
+ {
+ imagep = mImageDisabled;
+ }
+
+ image_glow = imagep;
+
+ if (mFlashing)
+ {
+ if (flash && mImageFlash)
+ {
+ // if button should flash and we have icon for flashing, use it as image for button
+ image_glow = mImageFlash;
+ }
+
+ // provide fade-in and fade-out via flash_color
+ if (mFlashingTimer)
+ {
+ LLColor4 flash_color = mFlashBgColor.get();
+ use_glow_effect = true;
+ glow_type = LLRender::BT_ALPHA; // blend the glow
+
+ if (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress())
+ {
+ glow_color = flash_color;
+ }
+ else if (mNeedsHighlight)
+ {
+ glow_color = highlighting_color;
+ }
+ else
+ {
+ // will fade from highlight color
+ glow_color = flash_color;
+ }
+ }
+ }
+
+ if (mNeedsHighlight && !imagep)
+ {
+ use_glow_effect = true;
+ }
+
+ // Figure out appropriate color for the text
+ LLColor4 label_color;
+
+ // label changes when button state changes, not when pressed
+ if ( enabled )
+ {
+ if ( getToggleState() )
+ {
+ label_color = mSelectedLabelColor.get();
+ }
+ else
+ {
+ label_color = mUnselectedLabelColor.get();
+ }
+ }
+ else
+ {
+ if ( getToggleState() )
+ {
+ label_color = mDisabledSelectedLabelColor.get();
+ }
+ else
+ {
+ label_color = mDisabledLabelColor.get();
+ }
+ }
+
+ // Highlight if needed
+ if( ll::ui::SearchableControl::getHighlighted() )
+ label_color = ll::ui::SearchableControl::getHighlightColor();
+
+ // Unselected label assignments
+ LLWString label = getCurrentLabel();
+
+ // overlay with keyboard focus border
+ if (hasFocus())
+ {
+ F32 lerp_amt = gFocusMgr.getFocusFlashAmt();
+ drawBorder(imagep, gFocusMgr.getFocusColor() % alpha, ll_round(lerp(1.f, 3.f, lerp_amt)));
+ }
+
+ if (use_glow_effect)
+ {
+ mCurGlowStrength = lerp(mCurGlowStrength,
+ mFlashing ? (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress() || mNeedsHighlight? 1.f : 0.f) : mHoverGlowStrength,
+ LLSmoothInterpolation::getInterpolant(0.05f));
+ }
+ else
+ {
+ mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLSmoothInterpolation::getInterpolant(0.05f));
+ }
+
+ // Draw button image, if available.
+ // Otherwise draw basic rectangular button.
+ if (imagep != NULL)
+ {
+ // apply automatic 50% alpha fade to disabled image
+ LLColor4 disabled_color = mFadeWhenDisabled ? mDisabledImageColor.get() % 0.5f : mDisabledImageColor.get();
+ if ( mScaleImage)
+ {
+ imagep->draw(getLocalRect(), (enabled ? mImageColor.get() : disabled_color) % alpha );
+ if (mCurGlowStrength > 0.01f)
+ {
+ gGL.setSceneBlendType(glow_type);
+ image_glow->drawSolid(0, 0, getRect().getWidth(), getRect().getHeight(), glow_color % (mCurGlowStrength * alpha));
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+ }
+ }
+ else
+ {
+ S32 y = getLocalRect().getHeight() - imagep->getHeight();
+ imagep->draw(0, y, (enabled ? mImageColor.get() : disabled_color) % alpha);
+ if (mCurGlowStrength > 0.01f)
+ {
+ gGL.setSceneBlendType(glow_type);
+ image_glow->drawSolid(0, y, glow_color % (mCurGlowStrength * alpha));
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+ }
+ }
+ }
+ else
+ {
+ // no image
+ 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);
+ }
+
+ // let overlay image and text play well together
+ S32 text_left = mLeftHPad;
+ S32 text_right = getRect().getWidth() - mRightHPad;
+ S32 text_width = getRect().getWidth() - mLeftHPad - mRightHPad;
+
+ // draw overlay image
+ if (mImageOverlay.notNull())
+ {
+ // get max width and height (discard level 0)
+ S32 overlay_width;
+ S32 overlay_height;
+
+ getOverlayImageSize(overlay_width, overlay_height);
+
+ S32 center_x = getLocalRect().getCenterX();
+ S32 center_y = getLocalRect().getCenterY();
+
+ //FUGLY HACK FOR "DEPRESSED" BUTTONS
+ if (pressed && mDisplayPressedState)
+ {
+ center_y--;
+ center_x++;
+ }
+
+ center_y += (mImageOverlayBottomPad - mImageOverlayTopPad);
+ // fade out overlay images on disabled buttons
+ LLColor4 overlay_color = mImageOverlayColor.get();
+ if (!enabled)
+ {
+ overlay_color = mImageOverlayDisabledColor.get();
+ }
+ else if (getToggleState())
+ {
+ overlay_color = mImageOverlaySelectedColor.get();
+ }
+ overlay_color.mV[VALPHA] *= alpha;
+
+ switch(mImageOverlayAlignment)
+ {
+ case LLFontGL::LEFT:
+ text_left += overlay_width + mImgOverlayLabelSpace;
+ text_width -= overlay_width + mImgOverlayLabelSpace;
+ mImageOverlay->draw(
+ mLeftHPad,
+ center_y - (overlay_height / 2),
+ overlay_width,
+ overlay_height,
+ overlay_color);
+ break;
+ case LLFontGL::HCENTER:
+ mImageOverlay->draw(
+ center_x - (overlay_width / 2),
+ center_y - (overlay_height / 2),
+ overlay_width,
+ overlay_height,
+ overlay_color);
+ 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),
+ overlay_width,
+ overlay_height,
+ overlay_color);
+ break;
+ default:
+ // draw nothing
+ break;
+ }
+ }
+
+ // Draw label
+ if( !label.empty() )
+ {
+ LLWStringUtil::trim(label);
+
+ S32 x;
+ switch( mHAlign )
+ {
+ case LLFontGL::RIGHT:
+ x = text_right;
+ break;
+ case LLFontGL::HCENTER:
+ x = text_left + (text_width / 2);
+ break;
+ case LLFontGL::LEFT:
+ default:
+ x = text_left;
+ break;
+ }
+
+ if (pressed && mDisplayPressedState)
+ {
+ x++;
+ }
+
+ // *NOTE: mantipov: before mUseEllipses is implemented in EXT-279 U32_MAX has been passed as
+ // max_chars.
+ // LLFontGL::render expects S32 max_chars variable but process in a separate way -1 value.
+ // Due to U32_MAX is equal to S32 -1 value I have rest this value for non-ellipses mode.
+ // Not sure if it is really needed. Probably S32_MAX should be always passed as max_chars.
+ mLastDrawCharsCount = mGLFont->render(label, 0,
+ (F32)x,
+ (F32)(getRect().getHeight() / 2 + mBottomVPad),
+ label_color % alpha,
+ mHAlign, LLFontGL::VCENTER,
+ LLFontGL::NORMAL,
+ mDropShadowedText ? LLFontGL::DROP_SHADOW_SOFT : LLFontGL::NO_SHADOW,
+ S32_MAX, text_width,
+ NULL, mUseEllipses, mUseFontColor);
+ }
+
+ LLUICtrl::draw();
+}
+
+void LLButton::drawBorder(LLUIImage* imagep, const LLColor4& color, S32 size)
+{
+ if (imagep == NULL) return;
+ if (mScaleImage)
+ {
+ imagep->drawBorder(getLocalRect(), color, size);
+ }
+ else
+ {
+ S32 y = getLocalRect().getHeight() - imagep->getHeight();
+ imagep->drawBorder(0, y, color, size);
+ }
+}
+
+bool LLButton::getToggleState() const
+{
+ return getValue().asBoolean();
+}
+
+void LLButton::setToggleState(bool b)
+{
+ if( b != getToggleState() )
+ {
+ 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, bool force_flashing/* = false */)
+{
+ mForceFlashing = force_flashing;
+ if (mFlashingTimer)
+ {
+ mFlashing = b;
+ (b ? mFlashingTimer->startFlashing() : mFlashingTimer->stopFlashing());
+ }
+ else if (b != mFlashing)
+ {
+ mFlashing = b;
+ mFrameTimer.reset();
+ }
+}
+
+bool LLButton::toggleState()
+{
+ bool flipped = ! getToggleState();
+ setToggleState(flipped);
+
+ return flipped;
+}
+
+void LLButton::setLabel( const std::string& label )
+{
+ mUnselectedLabel = mSelectedLabel = label;
+}
+
+void LLButton::setLabel( const LLUIString& label )
+{
+ mUnselectedLabel = mSelectedLabel = label;
+}
+
+void LLButton::setLabel( const LLStringExplicit& label )
+{
+ setLabelUnselected(label);
+ setLabelSelected(label);
+}
+
+//virtual
+bool LLButton::setLabelArg( const std::string& key, const LLStringExplicit& text )
+{
+ mUnselectedLabel.setArg(key, text);
+ mSelectedLabel.setArg(key, text);
+ return true;
+}
+
+void LLButton::setLabelUnselected( const LLStringExplicit& label )
+{
+ mUnselectedLabel = label;
+}
+
+void LLButton::setLabelSelected( const LLStringExplicit& label )
+{
+ mSelectedLabel = label;
+}
+
+bool LLButton::labelIsTruncated() const
+{
+ return getCurrentLabel().getString().size() > mLastDrawCharsCount;
+}
+
+const LLUIString& LLButton::getCurrentLabel() const
+{
+ return getToggleState() ? mSelectedLabel : mUnselectedLabel;
+}
+
+void LLButton::setImageUnselected(LLPointer<LLUIImage> image)
+{
+ mImageUnselected = image;
+ if (mImageUnselected.isNull())
+ {
+ LL_WARNS() << "Setting default button image for: " << getName() << " to NULL" << LL_ENDL;
+ }
+}
+
+void LLButton::autoResize()
+{
+ resize(getCurrentLabel());
+}
+
+void LLButton::resize(LLUIString label)
+{
+ // get label length
+ S32 label_width = mGLFont->getWidth(label.getString());
+ // get current btn length
+ S32 btn_width =getRect().getWidth();
+ // check if it need resize
+ if (mAutoResize)
+ {
+ S32 min_width = label_width + mLeftHPad + mRightHPad;
+ if (mImageOverlay)
+ {
+ 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());
+ }
+ }
+}
+void LLButton::setImages( const std::string &image_name, const std::string &selected_name )
+{
+ setImageUnselected(LLUI::getUIImage(image_name));
+ setImageSelected(LLUI::getUIImage(selected_name));
+}
+
+void LLButton::setImageSelected(LLPointer<LLUIImage> image)
+{
+ mImageSelected = image;
+}
+
+void LLButton::setImageColor(const LLColor4& c)
+{
+ mImageColor = c;
+}
+
+void LLButton::setColor(const LLColor4& color)
+{
+ setImageColor(color);
+}
+
+void LLButton::setImageDisabled(LLPointer<LLUIImage> image)
+{
+ mImageDisabled = image;
+ mDisabledImageColor = mImageColor;
+ mFadeWhenDisabled = true;
+}
+
+void LLButton::setImageDisabledSelected(LLPointer<LLUIImage> image)
+{
+ mImageDisabledSelected = image;
+ mDisabledImageColor = mImageColor;
+ mFadeWhenDisabled = true;
+}
+
+void LLButton::setImagePressed(LLPointer<LLUIImage> image)
+{
+ mImagePressed = image;
+}
+
+void LLButton::setImageHoverSelected(LLPointer<LLUIImage> image)
+{
+ mImageHoverSelected = image;
+}
+
+void LLButton::setImageHoverUnselected(LLPointer<LLUIImage> image)
+{
+ mImageHoverUnselected = image;
+}
+
+void LLButton::setImageFlash(LLPointer<LLUIImage> image)
+{
+ mImageFlash = image;
+}
+
+void LLButton::setImageOverlay(const std::string& image_name, LLFontGL::HAlign alignment, const LLColor4& color)
+{
+ if (image_name.empty())
+ {
+ mImageOverlay = NULL;
+ }
+ else
+ {
+ mImageOverlay = LLUI::getUIImage(image_name);
+ mImageOverlayAlignment = alignment;
+ mImageOverlayColor = color;
+ }
+}
+
+void LLButton::setImageOverlay(const LLUUID& image_id, LLFontGL::HAlign alignment, const LLColor4& color)
+{
+ if (image_id.isNull())
+ {
+ mImageOverlay = NULL;
+ }
+ else
+ {
+ mImageOverlay = LLUI::getUIImageByID(image_id);
+ mImageOverlayAlignment = alignment;
+ mImageOverlayColor = color;
+ }
+}
+
+void LLButton::onMouseCaptureLost()
+{
+ if (mCommitOnCaptureLost
+ && mMouseDownTimer.getStarted())
+ {
+ if (mMouseUpSignal) (*mMouseUpSignal)(this, LLSD());
+
+ if (mIsToggle)
+ {
+ toggleState();
+ }
+
+ LLUICtrl::onCommit();
+ }
+ resetMouseDownTimer();
+}
+
+//-------------------------------------------------------------------------
+// Utilities
+//-------------------------------------------------------------------------
+S32 round_up(S32 grid, S32 value)
+{
+ S32 mod = value % grid;
+
+ if (mod > 0)
+ {
+ // not even multiple
+ return value + (grid - mod);
+ }
+ else
+ {
+ return value;
+ }
+}
+
+void LLButton::addImageAttributeToXML(LLXMLNodePtr node,
+ const std::string& image_name,
+ const LLUUID& image_id,
+ const std::string& xml_tag_name) const
+{
+ if( !image_name.empty() )
+ {
+ node->createChild(xml_tag_name.c_str(), true)->setStringValue(image_name);
+ }
+ else if( image_id != LLUUID::null )
+ {
+ node->createChild((xml_tag_name + "_id").c_str(), true)->setUUIDValue(image_id);
+ }
+}
+
+
+// static
+void LLButton::toggleFloaterAndSetToggleState(LLUICtrl* ctrl, const LLSD& sdname)
+{
+ bool floater_vis = LLFloaterReg::toggleInstance(sdname.asString());
+ LLButton* button = dynamic_cast<LLButton*>(ctrl);
+ if (button)
+ button->setToggleState(floater_vis);
+}
+
+// static
+// Gets called once
+void LLButton::setFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname)
+{
+ LLButton* button = dynamic_cast<LLButton*>(ctrl);
+ if (!button)
+ return;
+ // Get the visibility control name for the floater
+ std::string vis_control_name = LLFloaterReg::declareVisibilityControl(sdname.asString());
+ // Set the button control value (toggle state) to the floater visibility control (Sets the value as well)
+ button->setControlVariable(LLFloater::getControlGroup()->getControl(vis_control_name));
+ // Set the clicked callback to toggle the floater
+ button->setClickedCallback(boost::bind(&LLFloaterReg::toggleInstance, sdname, LLSD()));
+}
+
+// static
+void LLButton::setDockableFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname)
+{
+ LLButton* button = dynamic_cast<LLButton*>(ctrl);
+ if (!button)
+ return;
+ // Get the visibility control name for the floater
+ std::string vis_control_name = LLFloaterReg::declareVisibilityControl(sdname.asString());
+ // Set the button control value (toggle state) to the floater visibility control (Sets the value as well)
+ button->setControlVariable(LLFloater::getControlGroup()->getControl(vis_control_name));
+ // Set the clicked callback to toggle the floater
+ button->setClickedCallback(boost::bind(&LLDockableFloater::toggleInstance, sdname));
+}
+
+// static
+void LLButton::showHelp(LLUICtrl* ctrl, const LLSD& sdname)
+{
+ // search back through the button's parents for a panel
+ // with a help_topic string defined
+ std::string help_topic;
+ if (LLUI::getInstance()->mHelpImpl &&
+ ctrl->findHelpTopic(help_topic))
+ {
+ LLUI::getInstance()->mHelpImpl->showTopic(help_topic);
+ return; // success
+ }
+
+ // display an error if we can't find a help_topic string.
+ // fix this by adding a help_topic attribute to the xui file
+ LLNotificationsUtil::add("UnableToFindHelpTopic");
+}
+
+void LLButton::resetMouseDownTimer()
+{
+ mMouseDownTimer.stop();
+ mMouseDownTimer.reset();
+}
+
+bool LLButton::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ // just treat a double click as a second click
+ return handleMouseDown(x, y, mask);
+}
diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h
index 2e6ac29bc0..371b716779 100644
--- a/indra/llui/llbutton.h
+++ b/indra/llui/llbutton.h
@@ -1,406 +1,406 @@
-/**
- * @file llbutton.h
- * @brief Header for buttons
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLBUTTON_H
-#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"
-#include "llfontgl.h"
-#include "lluiimage.h"
-#include "lluistring.h"
-
-//
-// Constants
-//
-
-// PLEASE please use these "constants" when building your own buttons.
-// They are loaded from settings.xml at run time.
-extern S32 LLBUTTON_H_PAD;
-extern S32 BTN_HEIGHT_SMALL;
-extern S32 BTN_HEIGHT;
-
-//
-// Helpful functions
-//
-S32 round_up(S32 grid, S32 value);
-
-
-class LLUICtrlFactory;
-
-//
-// Classes
-//
-
-class LLButton
-: public LLUICtrl, public LLBadgeOwner
-, public ll::ui::SearchableControl
-{
-public:
- struct Params
- : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- // text label
- Optional<std::string> label_selected;
- Optional<bool> label_shadow;
- Optional<bool> auto_resize;
- Optional<bool> use_ellipses;
- Optional<bool> use_font_color;
-
- // images
- Optional<LLUIImage*> image_unselected,
- image_selected,
- image_hover_selected,
- image_hover_unselected,
- image_disabled_selected,
- image_disabled,
- image_flash,
- image_pressed,
- image_pressed_selected,
- image_overlay;
-
- Optional<std::string> image_overlay_alignment;
-
- // colors
- Optional<LLUIColor> label_color,
- label_color_selected,
- label_color_disabled,
- label_color_disabled_selected,
- image_color,
- image_color_disabled,
- image_overlay_color,
- image_overlay_selected_color,
- image_overlay_disabled_color,
- flash_color;
-
- // layout
- Optional<S32> pad_right;
- Optional<S32> pad_left;
- Optional<S32> pad_bottom; // under text label
-
- //image overlay paddings
- Optional<S32> image_top_pad;
- Optional<S32> image_bottom_pad;
-
- /**
- * Space between image_overlay and label
- */
- Optional<S32> imgoverlay_label_space;
-
- // callbacks
- Optional<CommitCallbackParam> click_callback, // alias -> commit_callback
- mouse_down_callback,
- mouse_up_callback,
- mouse_held_callback;
-
- // misc
- Optional<bool> is_toggle,
- scale_image,
- commit_on_return,
- commit_on_capture_lost,
- display_pressed_state;
-
- Optional<F32> hover_glow_amount;
- Optional<TimeIntervalParam> held_down_delay;
-
- 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();
- };
-
-protected:
- friend class LLUICtrlFactory;
- LLButton(const Params&);
-
-public:
-
- ~LLButton();
- // For backward compatability only
- typedef boost::function<void(void*)> button_callback_t;
-
- void addImageAttributeToXML(LLXMLNodePtr node, const std::string& imageName,
- const LLUUID& imageID,const std::string& xmlTagName) const;
- virtual bool handleUnicodeCharHere(llwchar uni_char);
- virtual bool handleKeyHere(KEY key, MASK mask);
- virtual bool handleMouseDown(S32 x, S32 y, MASK mask);
- virtual bool handleMouseUp(S32 x, S32 y, MASK mask);
- virtual bool handleHover(S32 x, S32 y, MASK mask);
- virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask);
- virtual bool handleRightMouseUp(S32 x, S32 y, MASK mask);
- virtual bool handleDoubleClick(S32 x, S32 y, MASK mask);
- virtual void draw();
- /*virtual*/ bool postBuild();
-
- virtual void onMouseLeave(S32 x, S32 y, MASK mask);
- virtual void onMouseCaptureLost();
-
- virtual void onCommit();
-
- void setUnselectedLabelColor( const LLColor4& c ) { mUnselectedLabelColor = c; }
- void setSelectedLabelColor( const LLColor4& c ) { mSelectedLabelColor = c; }
- void setUseEllipses( bool use_ellipses ) { mUseEllipses = use_ellipses; }
- void setUseFontColor( bool use_font_color) { mUseFontColor = use_font_color; }
-
-
- 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
- // Passes a 'count' parameter in the commit param payload, i.e. param["count"])
- boost::signals2::connection setHeldDownCallback( const commit_signal_t::slot_type& cb ); // Mouse button held down and in button
-
-
- // *TODO: Deprecate (for backwards compatability only)
- boost::signals2::connection setClickedCallback( button_callback_t cb, void* data );
- boost::signals2::connection setMouseDownCallback( button_callback_t cb, void* data );
- boost::signals2::connection setMouseUpCallback( button_callback_t cb, void* data );
- boost::signals2::connection setHeldDownCallback( button_callback_t cb, void* data );
-
- void setHeldDownDelay( F32 seconds, S32 frames = 0) { mHeldDownDelay = seconds; mHeldDownFrameDelay = frames; }
-
- F32 getHeldDownTime() const { return mMouseDownTimer.getElapsedTimeF32(); }
-
- bool toggleState();
- bool getToggleState() const;
- void setToggleState(bool b);
-
- void setHighlight(bool b);
- void setFlashing( bool b, bool force_flashing = false );
- bool getFlashing() const { return mFlashing; }
- LLFlashTimer* getFlashTimer() {return mFlashingTimer;}
- void setFlashColor(const LLUIColor &color) { mFlashBgColor = color; };
-
- void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; }
- LLFontGL::HAlign getHAlign() const { return mHAlign; }
- void setLeftHPad( S32 pad ) { mLeftHPad = pad; }
- void setRightHPad( S32 pad ) { mRightHPad = pad; }
-
- void setImageOverlayTopPad( S32 pad ) { mImageOverlayTopPad = pad; }
- S32 getImageOverlayTopPad() const { return mImageOverlayTopPad; }
- void setImageOverlayBottomPad( S32 pad ) { mImageOverlayBottomPad = pad; }
- S32 getImageOverlayBottomPad() const { return mImageOverlayBottomPad; }
-
- const std::string getLabelUnselected() const { return wstring_to_utf8str(mUnselectedLabel); }
- const std::string getLabelSelected() const { return wstring_to_utf8str(mSelectedLabel); }
-
- void setImageColor(const std::string& color_control);
- void setImageColor(const LLColor4& c);
- /*virtual*/ void setColor(const LLColor4& c);
-
- void setImages(const std::string &image_name, const std::string &selected_name);
-
- void setDisabledImageColor(const LLColor4& c) { mDisabledImageColor = c; }
-
- void setDisabledSelectedLabelColor( const LLColor4& c ) { mDisabledSelectedLabelColor = c; }
-
- void setImageOverlay(const std::string& image_name, LLFontGL::HAlign alignment = LLFontGL::HCENTER, const LLColor4& color = LLColor4::white);
- void setImageOverlay(const LLUUID& image_id, LLFontGL::HAlign alignment = LLFontGL::HCENTER, const LLColor4& color = LLColor4::white);
- LLPointer<LLUIImage> getImageOverlay() { return mImageOverlay; }
- LLFontGL::HAlign getImageOverlayHAlign() const { return mImageOverlayAlignment; }
-
- void autoResize(); // resize with label of current btn state
- void resize(LLUIString label); // resize with label input
- void setLabel(const std::string& label);
- void setLabel(const LLUIString& label);
- void setLabel( const LLStringExplicit& label);
- virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text );
- void setLabelUnselected(const LLStringExplicit& label);
- void setLabelSelected(const LLStringExplicit& label);
- void setDisabledLabelColor( const LLColor4& c ) { mDisabledLabelColor = c; }
-
- void setFont(const LLFontGL *font)
- { mGLFont = ( font ? font : LLFontGL::getFontSansSerif()); }
- const LLFontGL* getFont() const { return mGLFont; }
-
-
- S32 getLastDrawCharsCount() const { return mLastDrawCharsCount; }
- bool labelIsTruncated() const;
- const LLUIString& getCurrentLabel() const;
-
- void setScaleImage(bool scale) { mScaleImage = scale; }
- bool getScaleImage() const { return mScaleImage; }
-
- void setDropShadowedText(bool b) { mDropShadowedText = b; }
-
- void setBorderEnabled(bool b) { mBorderEnabled = b; }
-
- void setHoverGlowStrength(F32 strength) { mHoverGlowStrength = strength; }
-
- void setImageUnselected(LLPointer<LLUIImage> image);
- void setImageSelected(LLPointer<LLUIImage> image);
- void setImageHoverSelected(LLPointer<LLUIImage> image);
- void setImageHoverUnselected(LLPointer<LLUIImage> image);
- void setImageDisabled(LLPointer<LLUIImage> image);
- void setImageDisabledSelected(LLPointer<LLUIImage> image);
- void setImageFlash(LLPointer<LLUIImage> image);
- void setImagePressed(LLPointer<LLUIImage> image);
-
- void setCommitOnReturn(bool commit) { mCommitOnReturn = commit; }
- bool getCommitOnReturn() const { return mCommitOnReturn; }
-
- static void onHeldDown(void *userdata); // to be called by gIdleCallbacks
- static void toggleFloaterAndSetToggleState(LLUICtrl* ctrl, const LLSD& sdname);
- static void setFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname);
- static void setDockableFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname);
- static void showHelp(LLUICtrl* ctrl, const LLSD& sdname);
-
- void setForcePressedState(bool b) { mForcePressedState = b; }
-
- void setAutoResize(bool auto_resize) { mAutoResize = auto_resize; }
-
-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;
-
- void drawBorder(LLUIImage* imagep, const LLColor4& color, S32 size);
- void resetMouseDownTimer();
-
- commit_signal_t* mMouseDownSignal;
- commit_signal_t* mMouseUpSignal;
- commit_signal_t* mHeldDownSignal;
-
- const LLFontGL* mGLFont;
-
- S32 mMouseDownFrame;
- S32 mMouseHeldDownCount; // Counter for parameter passed to held-down callback
- F32 mHeldDownDelay; // seconds, after which held-down callbacks get called
- S32 mHeldDownFrameDelay; // frames, after which held-down callbacks get called
- S32 mLastDrawCharsCount;
-
- LLPointer<LLUIImage> mImageOverlay;
- LLFontGL::HAlign mImageOverlayAlignment;
- LLUIColor mImageOverlayColor;
- LLUIColor mImageOverlaySelectedColor;
- LLUIColor mImageOverlayDisabledColor;
-
- LLPointer<LLUIImage> mImageUnselected;
- LLUIString mUnselectedLabel;
- LLUIColor mUnselectedLabelColor;
-
- LLPointer<LLUIImage> mImageSelected;
- LLUIString mSelectedLabel;
- LLUIColor mSelectedLabelColor;
-
- LLPointer<LLUIImage> mImageHoverSelected;
-
- LLPointer<LLUIImage> mImageHoverUnselected;
-
- LLPointer<LLUIImage> mImageDisabled;
- LLUIColor mDisabledLabelColor;
-
- LLPointer<LLUIImage> mImageDisabledSelected;
- LLUIString mDisabledSelectedLabel;
- LLUIColor mDisabledSelectedLabelColor;
-
- LLPointer<LLUIImage> mImagePressed;
- LLPointer<LLUIImage> mImagePressedSelected;
-
- /* There are two ways an image can flash- by making changes in color according to flash_color attribute
- or by changing icon from current to the one specified in image_flash. Second way is used only if
- flash icon name is set in attributes(by default it isn't). First way is used otherwise. */
- LLPointer<LLUIImage> mImageFlash;
-
- LLUIColor mFlashBgColor;
-
- LLUIColor mImageColor;
- LLUIColor mDisabledImageColor;
-
- bool mIsToggle;
- bool mScaleImage;
-
- bool mDropShadowedText;
- bool mAutoResize;
- bool mUseEllipses;
- bool mUseFontColor;
- bool mBorderEnabled;
- bool mFlashing;
-
- LLFontGL::HAlign mHAlign;
- S32 mLeftHPad;
- S32 mRightHPad;
- S32 mBottomVPad; // under text label
-
- S32 mImageOverlayTopPad;
- S32 mImageOverlayBottomPad;
-
- bool mUseDrawContextAlpha;
-
- /*
- * Space between image_overlay and label
- */
- S32 mImgOverlayLabelSpace;
-
- F32 mHoverGlowStrength;
- F32 mCurGlowStrength;
-
- bool mCommitOnReturn;
- bool mCommitOnCaptureLost;
- bool mFadeWhenDisabled;
- bool mForcePressedState;
- bool mDisplayPressedState;
-
- LLFrameTimer mFrameTimer;
- LLFlashTimer * mFlashingTimer;
- bool mForceFlashing; // Stick flashing color even if button is pressed
- bool mHandleRightMouse;
-
-protected:
- virtual std::string _getSearchText() const
- {
- return getLabelUnselected() + getToolTip();
- }
-};
-
-// Build time optimization, generate once in .cpp file
-#ifndef LLBUTTON_CPP
-extern template class LLButton* LLView::getChild<class LLButton>(
- const std::string& name, bool recurse) const;
-#endif
-
-#endif // LL_LLBUTTON_H
+/**
+ * @file llbutton.h
+ * @brief Header for buttons
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLBUTTON_H
+#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"
+#include "llfontgl.h"
+#include "lluiimage.h"
+#include "lluistring.h"
+
+//
+// Constants
+//
+
+// PLEASE please use these "constants" when building your own buttons.
+extern S32 LLBUTTON_H_PAD;
+extern S32 BTN_HEIGHT_SMALL;
+extern S32 BTN_HEIGHT;
+extern S32 BTN_DROP_SHADOW;
+
+//
+// Helpful functions
+//
+S32 round_up(S32 grid, S32 value);
+
+
+class LLUICtrlFactory;
+
+//
+// Classes
+//
+
+class LLButton
+: public LLUICtrl, public LLBadgeOwner
+, public ll::ui::SearchableControl
+{
+public:
+ struct Params
+ : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ // text label
+ Optional<std::string> label_selected;
+ Optional<bool> label_shadow;
+ Optional<bool> auto_resize;
+ Optional<bool> use_ellipses;
+ Optional<bool> use_font_color;
+
+ // images
+ Optional<LLUIImage*> image_unselected,
+ image_selected,
+ image_hover_selected,
+ image_hover_unselected,
+ image_disabled_selected,
+ image_disabled,
+ image_flash,
+ image_pressed,
+ image_pressed_selected,
+ image_overlay;
+
+ Optional<std::string> image_overlay_alignment;
+
+ // colors
+ Optional<LLUIColor> label_color,
+ label_color_selected,
+ label_color_disabled,
+ label_color_disabled_selected,
+ image_color,
+ image_color_disabled,
+ image_overlay_color,
+ image_overlay_selected_color,
+ image_overlay_disabled_color,
+ flash_color;
+
+ // layout
+ Optional<S32> pad_right;
+ Optional<S32> pad_left;
+ Optional<S32> pad_bottom; // under text label
+
+ //image overlay paddings
+ Optional<S32> image_top_pad;
+ Optional<S32> image_bottom_pad;
+
+ /**
+ * Space between image_overlay and label
+ */
+ Optional<S32> imgoverlay_label_space;
+
+ // callbacks
+ Optional<CommitCallbackParam> click_callback, // alias -> commit_callback
+ mouse_down_callback,
+ mouse_up_callback,
+ mouse_held_callback;
+
+ // misc
+ Optional<bool> is_toggle,
+ scale_image,
+ commit_on_return,
+ commit_on_capture_lost,
+ display_pressed_state;
+
+ Optional<F32> hover_glow_amount;
+ Optional<TimeIntervalParam> held_down_delay;
+
+ 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();
+ };
+
+protected:
+ friend class LLUICtrlFactory;
+ LLButton(const Params&);
+
+public:
+
+ ~LLButton();
+ // For backward compatability only
+ typedef boost::function<void(void*)> button_callback_t;
+
+ void addImageAttributeToXML(LLXMLNodePtr node, const std::string& imageName,
+ const LLUUID& imageID,const std::string& xmlTagName) const;
+ virtual bool handleUnicodeCharHere(llwchar uni_char);
+ virtual bool handleKeyHere(KEY key, MASK mask);
+ virtual bool handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual bool handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual bool handleHover(S32 x, S32 y, MASK mask);
+ virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask);
+ virtual bool handleRightMouseUp(S32 x, S32 y, MASK mask);
+ virtual bool handleDoubleClick(S32 x, S32 y, MASK mask);
+ virtual void draw();
+ /*virtual*/ bool postBuild();
+
+ virtual void onMouseLeave(S32 x, S32 y, MASK mask);
+ virtual void onMouseCaptureLost();
+
+ virtual void onCommit();
+
+ void setUnselectedLabelColor( const LLColor4& c ) { mUnselectedLabelColor = c; }
+ void setSelectedLabelColor( const LLColor4& c ) { mSelectedLabelColor = c; }
+ void setUseEllipses( bool use_ellipses ) { mUseEllipses = use_ellipses; }
+ void setUseFontColor( bool use_font_color) { mUseFontColor = use_font_color; }
+
+
+ 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
+ // Passes a 'count' parameter in the commit param payload, i.e. param["count"])
+ boost::signals2::connection setHeldDownCallback( const commit_signal_t::slot_type& cb ); // Mouse button held down and in button
+
+
+ // *TODO: Deprecate (for backwards compatability only)
+ boost::signals2::connection setClickedCallback( button_callback_t cb, void* data );
+ boost::signals2::connection setMouseDownCallback( button_callback_t cb, void* data );
+ boost::signals2::connection setMouseUpCallback( button_callback_t cb, void* data );
+ boost::signals2::connection setHeldDownCallback( button_callback_t cb, void* data );
+
+ void setHeldDownDelay( F32 seconds, S32 frames = 0) { mHeldDownDelay = seconds; mHeldDownFrameDelay = frames; }
+
+ F32 getHeldDownTime() const { return mMouseDownTimer.getElapsedTimeF32(); }
+
+ bool toggleState();
+ bool getToggleState() const;
+ void setToggleState(bool b);
+
+ void setHighlight(bool b);
+ void setFlashing( bool b, bool force_flashing = false );
+ bool getFlashing() const { return mFlashing; }
+ LLFlashTimer* getFlashTimer() {return mFlashingTimer;}
+ void setFlashColor(const LLUIColor &color) { mFlashBgColor = color; };
+
+ void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; }
+ LLFontGL::HAlign getHAlign() const { return mHAlign; }
+ void setLeftHPad( S32 pad ) { mLeftHPad = pad; }
+ void setRightHPad( S32 pad ) { mRightHPad = pad; }
+
+ void setImageOverlayTopPad( S32 pad ) { mImageOverlayTopPad = pad; }
+ S32 getImageOverlayTopPad() const { return mImageOverlayTopPad; }
+ void setImageOverlayBottomPad( S32 pad ) { mImageOverlayBottomPad = pad; }
+ S32 getImageOverlayBottomPad() const { return mImageOverlayBottomPad; }
+
+ const std::string getLabelUnselected() const { return wstring_to_utf8str(mUnselectedLabel); }
+ const std::string getLabelSelected() const { return wstring_to_utf8str(mSelectedLabel); }
+
+ void setImageColor(const std::string& color_control);
+ void setImageColor(const LLColor4& c);
+ /*virtual*/ void setColor(const LLColor4& c);
+
+ void setImages(const std::string &image_name, const std::string &selected_name);
+
+ void setDisabledImageColor(const LLColor4& c) { mDisabledImageColor = c; }
+
+ void setDisabledSelectedLabelColor( const LLColor4& c ) { mDisabledSelectedLabelColor = c; }
+
+ void setImageOverlay(const std::string& image_name, LLFontGL::HAlign alignment = LLFontGL::HCENTER, const LLColor4& color = LLColor4::white);
+ void setImageOverlay(const LLUUID& image_id, LLFontGL::HAlign alignment = LLFontGL::HCENTER, const LLColor4& color = LLColor4::white);
+ LLPointer<LLUIImage> getImageOverlay() { return mImageOverlay; }
+ LLFontGL::HAlign getImageOverlayHAlign() const { return mImageOverlayAlignment; }
+
+ void autoResize(); // resize with label of current btn state
+ void resize(LLUIString label); // resize with label input
+ void setLabel(const std::string& label);
+ void setLabel(const LLUIString& label);
+ void setLabel( const LLStringExplicit& label);
+ virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text );
+ void setLabelUnselected(const LLStringExplicit& label);
+ void setLabelSelected(const LLStringExplicit& label);
+ void setDisabledLabelColor( const LLColor4& c ) { mDisabledLabelColor = c; }
+
+ void setFont(const LLFontGL *font)
+ { mGLFont = ( font ? font : LLFontGL::getFontSansSerif()); }
+ const LLFontGL* getFont() const { return mGLFont; }
+ const std::string& getText() const { return getCurrentLabel().getString(); }
+
+ S32 getLastDrawCharsCount() const { return mLastDrawCharsCount; }
+ bool labelIsTruncated() const;
+ const LLUIString& getCurrentLabel() const;
+
+ void setScaleImage(bool scale) { mScaleImage = scale; }
+ bool getScaleImage() const { return mScaleImage; }
+
+ void setDropShadowedText(bool b) { mDropShadowedText = b; }
+
+ void setBorderEnabled(bool b) { mBorderEnabled = b; }
+
+ void setHoverGlowStrength(F32 strength) { mHoverGlowStrength = strength; }
+
+ void setImageUnselected(LLPointer<LLUIImage> image);
+ void setImageSelected(LLPointer<LLUIImage> image);
+ void setImageHoverSelected(LLPointer<LLUIImage> image);
+ void setImageHoverUnselected(LLPointer<LLUIImage> image);
+ void setImageDisabled(LLPointer<LLUIImage> image);
+ void setImageDisabledSelected(LLPointer<LLUIImage> image);
+ void setImageFlash(LLPointer<LLUIImage> image);
+ void setImagePressed(LLPointer<LLUIImage> image);
+
+ void setCommitOnReturn(bool commit) { mCommitOnReturn = commit; }
+ bool getCommitOnReturn() const { return mCommitOnReturn; }
+
+ static void onHeldDown(void *userdata); // to be called by gIdleCallbacks
+ static void toggleFloaterAndSetToggleState(LLUICtrl* ctrl, const LLSD& sdname);
+ static void setFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname);
+ static void setDockableFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname);
+ static void showHelp(LLUICtrl* ctrl, const LLSD& sdname);
+
+ void setForcePressedState(bool b) { mForcePressedState = b; }
+
+ void setAutoResize(bool auto_resize) { mAutoResize = auto_resize; }
+
+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;
+
+ void drawBorder(LLUIImage* imagep, const LLColor4& color, S32 size);
+ void resetMouseDownTimer();
+
+ commit_signal_t* mMouseDownSignal;
+ commit_signal_t* mMouseUpSignal;
+ commit_signal_t* mHeldDownSignal;
+
+ const LLFontGL* mGLFont;
+
+ S32 mMouseDownFrame;
+ S32 mMouseHeldDownCount; // Counter for parameter passed to held-down callback
+ F32 mHeldDownDelay; // seconds, after which held-down callbacks get called
+ S32 mHeldDownFrameDelay; // frames, after which held-down callbacks get called
+ S32 mLastDrawCharsCount;
+
+ LLPointer<LLUIImage> mImageOverlay;
+ LLFontGL::HAlign mImageOverlayAlignment;
+ LLUIColor mImageOverlayColor;
+ LLUIColor mImageOverlaySelectedColor;
+ LLUIColor mImageOverlayDisabledColor;
+
+ LLPointer<LLUIImage> mImageUnselected;
+ LLUIString mUnselectedLabel;
+ LLUIColor mUnselectedLabelColor;
+
+ LLPointer<LLUIImage> mImageSelected;
+ LLUIString mSelectedLabel;
+ LLUIColor mSelectedLabelColor;
+
+ LLPointer<LLUIImage> mImageHoverSelected;
+
+ LLPointer<LLUIImage> mImageHoverUnselected;
+
+ LLPointer<LLUIImage> mImageDisabled;
+ LLUIColor mDisabledLabelColor;
+
+ LLPointer<LLUIImage> mImageDisabledSelected;
+ LLUIString mDisabledSelectedLabel;
+ LLUIColor mDisabledSelectedLabelColor;
+
+ LLPointer<LLUIImage> mImagePressed;
+ LLPointer<LLUIImage> mImagePressedSelected;
+
+ /* There are two ways an image can flash- by making changes in color according to flash_color attribute
+ or by changing icon from current to the one specified in image_flash. Second way is used only if
+ flash icon name is set in attributes(by default it isn't). First way is used otherwise. */
+ LLPointer<LLUIImage> mImageFlash;
+
+ LLUIColor mFlashBgColor;
+
+ LLUIColor mImageColor;
+ LLUIColor mDisabledImageColor;
+
+ bool mIsToggle;
+ bool mScaleImage;
+
+ bool mDropShadowedText;
+ bool mAutoResize;
+ bool mUseEllipses;
+ bool mUseFontColor;
+ bool mBorderEnabled;
+ bool mFlashing;
+
+ LLFontGL::HAlign mHAlign;
+ S32 mLeftHPad;
+ S32 mRightHPad;
+ S32 mBottomVPad; // under text label
+
+ S32 mImageOverlayTopPad;
+ S32 mImageOverlayBottomPad;
+
+ bool mUseDrawContextAlpha;
+
+ /*
+ * Space between image_overlay and label
+ */
+ S32 mImgOverlayLabelSpace;
+
+ F32 mHoverGlowStrength;
+ F32 mCurGlowStrength;
+
+ bool mCommitOnReturn;
+ bool mCommitOnCaptureLost;
+ bool mFadeWhenDisabled;
+ bool mForcePressedState;
+ bool mDisplayPressedState;
+
+ LLFrameTimer mFrameTimer;
+ LLFlashTimer * mFlashingTimer;
+ bool mForceFlashing; // Stick flashing color even if button is pressed
+ bool mHandleRightMouse;
+
+protected:
+ virtual std::string _getSearchText() const
+ {
+ return getLabelUnselected() + getToolTip();
+ }
+};
+
+// Build time optimization, generate once in .cpp file
+#ifndef LLBUTTON_CPP
+extern template class LLButton* LLView::getChild<class LLButton>(
+ const std::string& name, bool recurse) const;
+#endif
+
+#endif // LL_LLBUTTON_H
diff --git a/indra/llui/llcallbackmap.h b/indra/llui/llcallbackmap.h
index 0a10877c09..07775dc30f 100644
--- a/indra/llui/llcallbackmap.h
+++ b/indra/llui/llcallbackmap.h
@@ -1,25 +1,25 @@
-/**
+/**
* @file llcallbackmap.h
* @brief LLCallbackMap base class
*
* $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$
*/
@@ -34,25 +34,25 @@
class LLCallbackMap
{
public:
- // callback definition.
- typedef boost::function<void* (void* data)> callback_t;
-
- typedef std::map<std::string, LLCallbackMap> map_t;
- typedef map_t::iterator map_iter_t;
- typedef map_t::const_iterator map_const_iter_t;
-
- template <class T>
- static void* buildPanel(void* data)
- {
- T* panel = new T();
- return (void*)panel;
- }
-
- LLCallbackMap() : mCallback(NULL), mData(NULL) { }
- LLCallbackMap(callback_t callback, void* data = NULL) : mCallback(callback), mData(data) { }
-
- callback_t mCallback;
- void* mData;
+ // callback definition.
+ typedef boost::function<void* (void* data)> callback_t;
+
+ typedef std::map<std::string, LLCallbackMap> map_t;
+ typedef map_t::iterator map_iter_t;
+ typedef map_t::const_iterator map_const_iter_t;
+
+ template <class T>
+ static void* buildPanel(void* data)
+ {
+ T* panel = new T();
+ return (void*)panel;
+ }
+
+ LLCallbackMap() : mCallback(NULL), mData(NULL) { }
+ LLCallbackMap(callback_t callback, void* data = NULL) : mCallback(callback), mData(data) { }
+
+ callback_t mCallback;
+ void* mData;
};
#endif // LLCALLBACKMAP_H
diff --git a/indra/llui/llchat.h b/indra/llui/llchat.h
index 102889867e..318e7e327e 100644
--- a/indra/llui/llchat.h
+++ b/indra/llui/llchat.h
@@ -1,112 +1,112 @@
-/**
- * @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_TELEPORT = 3,
- CHAT_SOURCE_UNKNOWN = 4,
- CHAT_SOURCE_REGION = 5,
-} 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,
- CHAT_STYLE_TELEPORT_SEP
-}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
+/**
+ * @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_TELEPORT = 3,
+ CHAT_SOURCE_UNKNOWN = 4,
+ CHAT_SOURCE_REGION = 5,
+} 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,
+ CHAT_STYLE_TELEPORT_SEP
+}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
index e7b7874b4b..3aa65355e5 100644
--- a/indra/llui/llchatentry.cpp
+++ b/indra/llui/llchatentry.cpp
@@ -1,251 +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::getInstance()->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::getInstance()->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);
-}
+/**
+ * @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::getInstance()->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::getInstance()->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
index 06d9d462b4..5fc336c8cc 100644
--- a/indra/llui/llchatentry.h
+++ b/indra/llui/llchatentry.h
@@ -1,106 +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_ */
+/**
+ * @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 171ef1b51b..8578059fab 100644
--- a/indra/llui/llcheckboxctrl.cpp
+++ b/indra/llui/llcheckboxctrl.cpp
@@ -1,304 +1,304 @@
-/**
- * @file llcheckboxctrl.cpp
- * @brief LLCheckBoxCtrl 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$
- */
-
-// The mutants are coming!
-#include "linden_common.h"
-
-#define LLCHECKBOXCTRL_CPP
-#include "llcheckboxctrl.h"
-
-#include "llgl.h"
-#include "llui.h"
-#include "lluiconstants.h"
-#include "lluictrlfactory.h"
-#include "llcontrol.h"
-
-#include "llstring.h"
-#include "llfontgl.h"
-#include "lltextbox.h"
-#include "llkeyboard.h"
-
-static LLDefaultChildRegistry::Register<LLCheckBoxCtrl> r("check_box");
-
-// Compiler optimization, generate extern template
-template class LLCheckBoxCtrl* LLView::getChild<class LLCheckBoxCtrl>(
- const std::string& name, bool recurse) const;
-
-void LLCheckBoxCtrl::WordWrap::declareValues()
-{
- declare("none", EWordWrap::WRAP_NONE);
- declare("down", EWordWrap::WRAP_DOWN);
- declare("up", EWordWrap::WRAP_UP);
-}
-
-LLCheckBoxCtrl::Params::Params()
-: initial_value("initial_value", false),
- label_text("label_text"),
- check_button("check_button"),
- word_wrap("word_wrap", EWordWrap::WRAP_NONE),
- radio_style("radio_style")
-{}
-
-
-LLCheckBoxCtrl::LLCheckBoxCtrl(const LLCheckBoxCtrl::Params& p)
-: LLUICtrl(p),
- mTextEnabledColor(p.label_text.text_color()),
- mTextDisabledColor(p.label_text.text_readonly_color()),
- mFont(p.font()),
- mWordWrap(p.word_wrap)
-{
- mViewModel->setValue(LLSD(p.initial_value));
- mViewModel->resetDirty();
- static LLUICachedControl<S32> llcheckboxctrl_spacing ("UICheckboxctrlSpacing", 0);
- static LLUICachedControl<S32> llcheckboxctrl_hpad ("UICheckboxctrlHPad", 0);
- static LLUICachedControl<S32> llcheckboxctrl_vpad ("UICheckboxctrlVPad", 0);
-
- // must be big enough to hold all children
- setUseBoundingRect(true);
-
- // *HACK Get rid of this with SL-55508...
- // this allows blank check boxes and radio boxes for now
- std::string local_label = p.label;
- if(local_label.empty())
- {
- local_label = " ";
- }
-
- LLTextBox::Params tbparams = p.label_text;
- tbparams.initial_value(local_label);
- if (p.font.isProvided())
- {
- tbparams.font(p.font);
- }
-
- mLabel = LLUICtrlFactory::create<LLTextBox>(tbparams);
- if (mWordWrap != WRAP_NONE)
- {
- // Not setWordWrap(mWordWrap != WRAP_NONE) because there might be some old lurking code that sets it manually
- mLabel->setWordWrap(true);
- S32 new_width = getRect().getWidth() - p.check_button.rect().getWidth() - llcheckboxctrl_hpad;
- LLRect label_rect = mLabel->getRect();
- label_rect.mRight = label_rect.mLeft + new_width;
- mLabel->setRect(label_rect);
- }
- mLabel->reshapeToFitText();
-
- LLRect label_rect = mLabel->getRect();
- if (mLabel->getLineCount() > 1)
- {
- if (mWordWrap == WRAP_DOWN)
- {
- // reshapeToFitText uses LLView::reshape() which always reshapes
- // from bottom to top, but we want to extend the bottom
- // Note: might be better idea to use getRect().mTop of LLCheckBoxCtrl (+pad) as top point of new rect
- S32 delta = ll_round((F32)mLabel->getFont()->getLineHeight() * mLabel->getLineSpacingMult()) - label_rect.getHeight();
- label_rect.translate(0, delta);
- mLabel->setRect(label_rect);
- }
- // else
- // WRAP_UP is essentially done by reshapeToFitText() (extends from bottom to top)
- // howhever it doesn't respect rect of checkbox
- // todo: this should be fixed, but there are at least couple checkboxes that use this feature as is.
- }
-
- addChild(mLabel);
-
- // Button
- // Note: button cover the label by extending all the way to the right and down.
- LLRect btn_rect = p.check_button.rect();
- btn_rect.setOriginAndSize(
- btn_rect.mLeft,
- llmin(btn_rect.mBottom, label_rect.mBottom),
- llmax(btn_rect.mRight, label_rect.mRight - btn_rect.mLeft),
- llmax(label_rect.getHeight(), btn_rect.mTop));
- std::string active_true_id, active_false_id;
- std::string inactive_true_id, inactive_false_id;
-
- LLButton::Params params = p.check_button;
- params.rect(btn_rect);
- //params.control_name(p.control_name);
- 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.
- params.initial_value(LLSD(p.initial_value));
- params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
-
- mButton = LLUICtrlFactory::create<LLButton>(params);
- addChild(mButton);
-}
-
-LLCheckBoxCtrl::~LLCheckBoxCtrl()
-{
- // Children all cleaned up by default view destructor.
-}
-
-void LLCheckBoxCtrl::onCommit()
-{
- if( getEnabled() )
- {
- setTentative(false);
- setControlValue(getValue());
- LLUICtrl::onCommit();
- }
-}
-
-void LLCheckBoxCtrl::setEnabled(bool b)
-{
- LLView::setEnabled(b);
-
- if (b)
- {
- mLabel->setColor( mTextEnabledColor.get() );
- }
- else
- {
- mLabel->setColor( mTextDisabledColor.get() );
- }
-}
-
-void LLCheckBoxCtrl::clear()
-{
- setValue( false );
-}
-
-void LLCheckBoxCtrl::reshape(S32 width, S32 height, bool called_from_parent)
-{
- LLRect rect = getRect();
- S32 delta_width = width - rect.getWidth();
- S32 delta_height = height - rect.getHeight();
-
- if (delta_width || delta_height)
- {
- // adjust our rectangle
- rect.mRight = getRect().mLeft + width;
- rect.mTop = getRect().mBottom + height;
- setRect(rect);
- }
-
- // reshapeToFitText reshapes label to minimal size according to last bounding box
- // it will work fine in case of decrease of space, but if we get more space or text
- // becomes longer, label will fail to grow so reinit label's dimentions.
-
- LLRect label_rect = mLabel->getRect();
- S32 new_width = rect.getWidth() - label_rect.mLeft;
- mLabel->reshape(new_width, label_rect.getHeight(), true);
-
- S32 label_top = label_rect.mTop;
- mLabel->reshapeToFitText(true);
-
- label_rect = mLabel->getRect();
- if (label_top != label_rect.mTop && mWordWrap == WRAP_DOWN)
- {
- // reshapeToFitText uses LLView::reshape() which always reshapes
- // from bottom to top, but we want to extend the bottom so
- // reposition control
- S32 delta = label_top - label_rect.mTop;
- label_rect.translate(0, delta);
- mLabel->setRect(label_rect);
- }
-
- // Button
- // Note: button cover the label by extending all the way to the right and down.
- LLRect btn_rect = mButton->getRect();
- btn_rect.setOriginAndSize(
- btn_rect.mLeft,
- llmin(btn_rect.mBottom, label_rect.mBottom),
- llmax(btn_rect.getWidth(), label_rect.mRight - btn_rect.mLeft),
- llmax(label_rect.mTop - btn_rect.mBottom, btn_rect.getHeight()));
- mButton->setShape(btn_rect);
-
- updateBoundingRect();
-}
-
-//virtual
-void LLCheckBoxCtrl::setValue(const LLSD& value )
-{
- mButton->setValue( value );
-}
-
-//virtual
-LLSD LLCheckBoxCtrl::getValue() const
-{
- return mButton->getValue();
-}
-
-//virtual
-void LLCheckBoxCtrl::setTentative(bool b)
-{
- mButton->setTentative(b);
-}
-
-//virtual
-bool LLCheckBoxCtrl::getTentative() const
-{
- return mButton->getTentative();
-}
-
-void LLCheckBoxCtrl::setLabel( const LLStringExplicit& label )
-{
- mLabel->setText( label );
- reshape(getRect().getWidth(), getRect().getHeight(), false);
-}
-
-std::string LLCheckBoxCtrl::getLabel() const
-{
- return mLabel->getText();
-}
-
-bool LLCheckBoxCtrl::setLabelArg( const std::string& key, const LLStringExplicit& text )
-{
- bool res = mLabel->setTextArg(key, text);
- reshape(getRect().getWidth(), getRect().getHeight(), false);
- return res;
-}
-
-// virtual
-void LLCheckBoxCtrl::setControlName(const std::string& control_name, LLView* context)
-{
- mButton->setControlName(control_name, context);
-}
-
-
-// virtual Returns true if the user has modified this control.
-bool LLCheckBoxCtrl::isDirty() const
-{
- if ( mButton )
- {
- return mButton->isDirty();
- }
- return false; // Shouldn't get here
-}
-
-
-// virtual Clear dirty state
-void LLCheckBoxCtrl::resetDirty()
-{
- if ( mButton )
- {
- mButton->resetDirty();
- }
-}
+/**
+ * @file llcheckboxctrl.cpp
+ * @brief LLCheckBoxCtrl 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$
+ */
+
+// The mutants are coming!
+#include "linden_common.h"
+
+#define LLCHECKBOXCTRL_CPP
+#include "llcheckboxctrl.h"
+
+#include "llgl.h"
+#include "llui.h"
+#include "lluiconstants.h"
+#include "lluictrlfactory.h"
+#include "llcontrol.h"
+
+#include "llstring.h"
+#include "llfontgl.h"
+#include "lltextbox.h"
+#include "llkeyboard.h"
+
+static LLDefaultChildRegistry::Register<LLCheckBoxCtrl> r("check_box");
+
+// Compiler optimization, generate extern template
+template class LLCheckBoxCtrl* LLView::getChild<class LLCheckBoxCtrl>(
+ const std::string& name, bool recurse) const;
+
+void LLCheckBoxCtrl::WordWrap::declareValues()
+{
+ declare("none", EWordWrap::WRAP_NONE);
+ declare("down", EWordWrap::WRAP_DOWN);
+ declare("up", EWordWrap::WRAP_UP);
+}
+
+LLCheckBoxCtrl::Params::Params()
+: initial_value("initial_value", false),
+ label_text("label_text"),
+ check_button("check_button"),
+ word_wrap("word_wrap", EWordWrap::WRAP_NONE),
+ radio_style("radio_style")
+{}
+
+
+LLCheckBoxCtrl::LLCheckBoxCtrl(const LLCheckBoxCtrl::Params& p)
+: LLUICtrl(p),
+ mTextEnabledColor(p.label_text.text_color()),
+ mTextDisabledColor(p.label_text.text_readonly_color()),
+ mFont(p.font()),
+ mWordWrap(p.word_wrap)
+{
+ mViewModel->setValue(LLSD(p.initial_value));
+ mViewModel->resetDirty();
+ static LLUICachedControl<S32> llcheckboxctrl_spacing ("UICheckboxctrlSpacing", 0);
+ static LLUICachedControl<S32> llcheckboxctrl_hpad ("UICheckboxctrlHPad", 0);
+ static LLUICachedControl<S32> llcheckboxctrl_vpad ("UICheckboxctrlVPad", 0);
+
+ // must be big enough to hold all children
+ setUseBoundingRect(true);
+
+ // *HACK Get rid of this with SL-55508...
+ // this allows blank check boxes and radio boxes for now
+ std::string local_label = p.label;
+ if(local_label.empty())
+ {
+ local_label = " ";
+ }
+
+ LLTextBox::Params tbparams = p.label_text;
+ tbparams.initial_value(local_label);
+ if (p.font.isProvided())
+ {
+ tbparams.font(p.font);
+ }
+
+ mLabel = LLUICtrlFactory::create<LLTextBox>(tbparams);
+ if (mWordWrap != WRAP_NONE)
+ {
+ // Not setWordWrap(mWordWrap != WRAP_NONE) because there might be some old lurking code that sets it manually
+ mLabel->setWordWrap(true);
+ S32 new_width = getRect().getWidth() - p.check_button.rect().getWidth() - llcheckboxctrl_hpad;
+ LLRect label_rect = mLabel->getRect();
+ label_rect.mRight = label_rect.mLeft + new_width;
+ mLabel->setRect(label_rect);
+ }
+ mLabel->reshapeToFitText();
+
+ LLRect label_rect = mLabel->getRect();
+ if (mLabel->getLineCount() > 1)
+ {
+ if (mWordWrap == WRAP_DOWN)
+ {
+ // reshapeToFitText uses LLView::reshape() which always reshapes
+ // from bottom to top, but we want to extend the bottom
+ // Note: might be better idea to use getRect().mTop of LLCheckBoxCtrl (+pad) as top point of new rect
+ S32 delta = ll_round((F32)mLabel->getFont()->getLineHeight() * mLabel->getLineSpacingMult()) - label_rect.getHeight();
+ label_rect.translate(0, delta);
+ mLabel->setRect(label_rect);
+ }
+ // else
+ // WRAP_UP is essentially done by reshapeToFitText() (extends from bottom to top)
+ // howhever it doesn't respect rect of checkbox
+ // todo: this should be fixed, but there are at least couple checkboxes that use this feature as is.
+ }
+
+ addChild(mLabel);
+
+ // Button
+ // Note: button cover the label by extending all the way to the right and down.
+ LLRect btn_rect = p.check_button.rect();
+ btn_rect.setOriginAndSize(
+ btn_rect.mLeft,
+ llmin(btn_rect.mBottom, label_rect.mBottom),
+ llmax(btn_rect.mRight, label_rect.mRight - btn_rect.mLeft),
+ llmax(label_rect.getHeight(), btn_rect.mTop));
+ std::string active_true_id, active_false_id;
+ std::string inactive_true_id, inactive_false_id;
+
+ LLButton::Params params = p.check_button;
+ params.rect(btn_rect);
+ //params.control_name(p.control_name);
+ 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.
+ params.initial_value(LLSD(p.initial_value));
+ params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
+
+ mButton = LLUICtrlFactory::create<LLButton>(params);
+ addChild(mButton);
+}
+
+LLCheckBoxCtrl::~LLCheckBoxCtrl()
+{
+ // Children all cleaned up by default view destructor.
+}
+
+void LLCheckBoxCtrl::onCommit()
+{
+ if( getEnabled() )
+ {
+ setTentative(false);
+ setControlValue(getValue());
+ LLUICtrl::onCommit();
+ }
+}
+
+void LLCheckBoxCtrl::setEnabled(bool b)
+{
+ LLView::setEnabled(b);
+
+ if (b)
+ {
+ mLabel->setColor( mTextEnabledColor.get() );
+ }
+ else
+ {
+ mLabel->setColor( mTextDisabledColor.get() );
+ }
+}
+
+void LLCheckBoxCtrl::clear()
+{
+ setValue( false );
+}
+
+void LLCheckBoxCtrl::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ LLRect rect = getRect();
+ S32 delta_width = width - rect.getWidth();
+ S32 delta_height = height - rect.getHeight();
+
+ if (delta_width || delta_height)
+ {
+ // adjust our rectangle
+ rect.mRight = getRect().mLeft + width;
+ rect.mTop = getRect().mBottom + height;
+ setRect(rect);
+ }
+
+ // reshapeToFitText reshapes label to minimal size according to last bounding box
+ // it will work fine in case of decrease of space, but if we get more space or text
+ // becomes longer, label will fail to grow so reinit label's dimentions.
+
+ LLRect label_rect = mLabel->getRect();
+ S32 new_width = rect.getWidth() - label_rect.mLeft;
+ mLabel->reshape(new_width, label_rect.getHeight(), true);
+
+ S32 label_top = label_rect.mTop;
+ mLabel->reshapeToFitText(true);
+
+ label_rect = mLabel->getRect();
+ if (label_top != label_rect.mTop && mWordWrap == WRAP_DOWN)
+ {
+ // reshapeToFitText uses LLView::reshape() which always reshapes
+ // from bottom to top, but we want to extend the bottom so
+ // reposition control
+ S32 delta = label_top - label_rect.mTop;
+ label_rect.translate(0, delta);
+ mLabel->setRect(label_rect);
+ }
+
+ // Button
+ // Note: button cover the label by extending all the way to the right and down.
+ LLRect btn_rect = mButton->getRect();
+ btn_rect.setOriginAndSize(
+ btn_rect.mLeft,
+ llmin(btn_rect.mBottom, label_rect.mBottom),
+ llmax(btn_rect.getWidth(), label_rect.mRight - btn_rect.mLeft),
+ llmax(label_rect.mTop - btn_rect.mBottom, btn_rect.getHeight()));
+ mButton->setShape(btn_rect);
+
+ updateBoundingRect();
+}
+
+//virtual
+void LLCheckBoxCtrl::setValue(const LLSD& value )
+{
+ mButton->setValue( value );
+}
+
+//virtual
+LLSD LLCheckBoxCtrl::getValue() const
+{
+ return mButton->getValue();
+}
+
+//virtual
+void LLCheckBoxCtrl::setTentative(bool b)
+{
+ mButton->setTentative(b);
+}
+
+//virtual
+bool LLCheckBoxCtrl::getTentative() const
+{
+ return mButton->getTentative();
+}
+
+void LLCheckBoxCtrl::setLabel( const LLStringExplicit& label )
+{
+ mLabel->setText( label );
+ reshape(getRect().getWidth(), getRect().getHeight(), false);
+}
+
+std::string LLCheckBoxCtrl::getLabel() const
+{
+ return mLabel->getText();
+}
+
+bool LLCheckBoxCtrl::setLabelArg( const std::string& key, const LLStringExplicit& text )
+{
+ bool res = mLabel->setTextArg(key, text);
+ reshape(getRect().getWidth(), getRect().getHeight(), false);
+ return res;
+}
+
+// virtual
+void LLCheckBoxCtrl::setControlName(const std::string& control_name, LLView* context)
+{
+ mButton->setControlName(control_name, context);
+}
+
+
+// virtual Returns true if the user has modified this control.
+bool LLCheckBoxCtrl::isDirty() const
+{
+ if ( mButton )
+ {
+ return mButton->isDirty();
+ }
+ return false; // Shouldn't get here
+}
+
+
+// virtual Clear dirty state
+void LLCheckBoxCtrl::resetDirty()
+{
+ if ( mButton )
+ {
+ mButton->resetDirty();
+ }
+}
diff --git a/indra/llui/llcheckboxctrl.h b/indra/llui/llcheckboxctrl.h
index 71b7d27629..0312e57511 100644
--- a/indra/llui/llcheckboxctrl.h
+++ b/indra/llui/llcheckboxctrl.h
@@ -1,157 +1,157 @@
-/**
- * @file llcheckboxctrl.h
- * @brief LLCheckBoxCtrl 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$
- */
-
-#ifndef LL_LLCHECKBOXCTRL_H
-#define LL_LLCHECKBOXCTRL_H
-
-#include "lluictrl.h"
-#include "llbutton.h"
-#include "lltextbox.h"
-#include "v4color.h"
-
-//
-// Constants
-//
-
-const bool RADIO_STYLE = true;
-const bool CHECK_STYLE = false;
-
-//
-// Classes
-//
-class LLFontGL;
-class LLViewBorder;
-
-class LLCheckBoxCtrl
-: public LLUICtrl
-, public ll::ui::SearchableControl
-{
-public:
-
- enum EWordWrap
- {
- WRAP_NONE,
- WRAP_UP,
- WRAP_DOWN
- };
-
- struct WordWrap : public LLInitParam::TypeValuesHelper<EWordWrap, WordWrap>
- {
- static void declareValues();
- };
-
- struct Params
- : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Optional<bool> initial_value; // override LLUICtrl initial_value
-
- Optional<LLTextBox::Params> label_text;
- Optional<LLButton::Params> check_button;
-
- Optional<EWordWrap, WordWrap> word_wrap;
-
- Ignored radio_style;
-
- Params();
- };
-
- virtual ~LLCheckBoxCtrl();
-
-protected:
- LLCheckBoxCtrl(const Params&);
- friend class LLUICtrlFactory;
-
-public:
- // LLView interface
-
- virtual void setEnabled( bool b );
-
- virtual void reshape(S32 width, S32 height, bool called_from_parent = true);
-
- // LLUICtrl interface
- virtual void setValue(const LLSD& value );
- virtual LLSD getValue() const;
- bool get() { return (bool)getValue().asBoolean(); }
- void set(bool value) { setValue(value); }
-
- virtual void setTentative(bool b);
- virtual bool getTentative() const;
-
- virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text );
-
- virtual void clear();
- virtual void onCommit();
-
- // LLCheckBoxCtrl interface
- virtual bool toggle() { return mButton->toggleState(); } // returns new state
-
- void setBtnFocus() { mButton->setFocus(true); }
-
- void setEnabledColor( const LLColor4 &color ) { mTextEnabledColor = color; }
- void setDisabledColor( const LLColor4 &color ) { mTextDisabledColor = color; }
-
- void setLabel( const LLStringExplicit& label );
- std::string getLabel() const;
-
- void setFont( const LLFontGL* font ) { mFont = font; }
- const LLFontGL* getFont() { return mFont; }
-
- virtual void setControlName(const std::string& control_name, LLView* context);
-
- virtual bool isDirty() const; // Returns true if the user has modified this control.
- virtual void resetDirty(); // Clear dirty state
-
-protected:
- virtual std::string _getSearchText() const
- {
- return getLabel() + getToolTip();
- }
-
- virtual void onSetHighlight() const // When highlight, really do highlight the label
- {
- if( mLabel )
- mLabel->ll::ui::SearchableControl::setHighlighted( ll::ui::SearchableControl::getHighlighted() );
- }
-
-protected:
- // note: value is stored in toggle state of button
- LLButton* mButton;
- LLTextBox* mLabel;
- const LLFontGL* mFont;
-
- LLUIColor mTextEnabledColor;
- LLUIColor mTextDisabledColor;
-
- EWordWrap mWordWrap; // off, shifts text up, shifts text down
-};
-
-// Build time optimization, generate once in .cpp file
-#ifndef LLCHECKBOXCTRL_CPP
-extern template class LLCheckBoxCtrl* LLView::getChild<class LLCheckBoxCtrl>(
- const std::string& name, bool recurse) const;
-#endif
-
-#endif // LL_LLCHECKBOXCTRL_H
+/**
+ * @file llcheckboxctrl.h
+ * @brief LLCheckBoxCtrl 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$
+ */
+
+#ifndef LL_LLCHECKBOXCTRL_H
+#define LL_LLCHECKBOXCTRL_H
+
+#include "lluictrl.h"
+#include "llbutton.h"
+#include "lltextbox.h"
+#include "v4color.h"
+
+//
+// Constants
+//
+
+const bool RADIO_STYLE = true;
+const bool CHECK_STYLE = false;
+
+//
+// Classes
+//
+class LLFontGL;
+class LLViewBorder;
+
+class LLCheckBoxCtrl
+: public LLUICtrl
+, public ll::ui::SearchableControl
+{
+public:
+
+ enum EWordWrap
+ {
+ WRAP_NONE,
+ WRAP_UP,
+ WRAP_DOWN
+ };
+
+ struct WordWrap : public LLInitParam::TypeValuesHelper<EWordWrap, WordWrap>
+ {
+ static void declareValues();
+ };
+
+ struct Params
+ : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<bool> initial_value; // override LLUICtrl initial_value
+
+ Optional<LLTextBox::Params> label_text;
+ Optional<LLButton::Params> check_button;
+
+ Optional<EWordWrap, WordWrap> word_wrap;
+
+ Ignored radio_style;
+
+ Params();
+ };
+
+ virtual ~LLCheckBoxCtrl();
+
+protected:
+ LLCheckBoxCtrl(const Params&);
+ friend class LLUICtrlFactory;
+
+public:
+ // LLView interface
+
+ virtual void setEnabled( bool b );
+
+ virtual void reshape(S32 width, S32 height, bool called_from_parent = true);
+
+ // LLUICtrl interface
+ virtual void setValue(const LLSD& value );
+ virtual LLSD getValue() const;
+ bool get() { return (bool)getValue().asBoolean(); }
+ void set(bool value) { setValue(value); }
+
+ virtual void setTentative(bool b);
+ virtual bool getTentative() const;
+
+ virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text );
+
+ virtual void clear();
+ virtual void onCommit();
+
+ // LLCheckBoxCtrl interface
+ virtual bool toggle() { return mButton->toggleState(); } // returns new state
+
+ void setBtnFocus() { mButton->setFocus(true); }
+
+ void setEnabledColor( const LLColor4 &color ) { mTextEnabledColor = color; }
+ void setDisabledColor( const LLColor4 &color ) { mTextDisabledColor = color; }
+
+ void setLabel( const LLStringExplicit& label );
+ std::string getLabel() const;
+
+ void setFont( const LLFontGL* font ) { mFont = font; }
+ const LLFontGL* getFont() const { return mFont; }
+
+ virtual void setControlName(const std::string& control_name, LLView* context);
+
+ virtual bool isDirty() const; // Returns true if the user has modified this control.
+ virtual void resetDirty(); // Clear dirty state
+
+protected:
+ virtual std::string _getSearchText() const
+ {
+ return getLabel() + getToolTip();
+ }
+
+ virtual void onSetHighlight() const // When highlight, really do highlight the label
+ {
+ if( mLabel )
+ mLabel->ll::ui::SearchableControl::setHighlighted( ll::ui::SearchableControl::getHighlighted() );
+ }
+
+protected:
+ // note: value is stored in toggle state of button
+ LLButton* mButton;
+ LLTextBox* mLabel;
+ const LLFontGL* mFont;
+
+ LLUIColor mTextEnabledColor;
+ LLUIColor mTextDisabledColor;
+
+ EWordWrap mWordWrap; // off, shifts text up, shifts text down
+};
+
+// Build time optimization, generate once in .cpp file
+#ifndef LLCHECKBOXCTRL_CPP
+extern template class LLCheckBoxCtrl* LLView::getChild<class LLCheckBoxCtrl>(
+ const std::string& name, bool recurse) const;
+#endif
+
+#endif // LL_LLCHECKBOXCTRL_H
diff --git a/indra/llui/llclipboard.cpp b/indra/llui/llclipboard.cpp
index 06fac190ed..5132d33bbf 100644
--- a/indra/llui/llclipboard.cpp
+++ b/indra/llui/llclipboard.cpp
@@ -1,25 +1,25 @@
-/**
+/**
* @file llclipboard.cpp
* @brief LLClipboard 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$
*/
@@ -35,121 +35,121 @@
#include "llwindow.h"
LLClipboard::LLClipboard() :
- mGeneration(0)
+ mGeneration(0)
{
- reset();
+ reset();
}
LLClipboard::~LLClipboard()
{
- reset();
+ reset();
}
void LLClipboard::reset()
{
- // Increment the clipboard count
- mGeneration++;
- // Clear the clipboard
- mObjects.clear();
- mCutMode = false;
- mString = LLWString();
+ // Increment the clipboard count
+ mGeneration++;
+ // Clear the clipboard
+ mObjects.clear();
+ mCutMode = false;
+ mString = LLWString();
}
// Copy the input uuid to the LL clipboard
bool LLClipboard::copyToClipboard(const LLUUID& src, const LLAssetType::EType type)
{
- reset();
- return addToClipboard(src, type);
+ reset();
+ return addToClipboard(src, type);
}
// 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)
{
- bool res = false;
- if (src.notNull())
- {
- res = true;
- if (LLAssetType::lookupIsAssetIDKnowable(type))
- {
- LLWString source = utf8str_to_wstring(src.asString());
- res = addToClipboard(source, 0, source.size());
- }
- if (res)
- {
- mObjects.push_back(src);
- mGeneration++;
- }
- }
- return res;
+ bool res = false;
+ if (src.notNull())
+ {
+ res = true;
+ if (LLAssetType::lookupIsAssetIDKnowable(type))
+ {
+ LLWString source = utf8str_to_wstring(src.asString());
+ res = addToClipboard(source, 0, source.size());
+ }
+ if (res)
+ {
+ mObjects.push_back(src);
+ mGeneration++;
+ }
+ }
+ return res;
}
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)
- {
- res = true;
- inv_objects.clear();
- for (S32 i = 0; i < count; i++)
- {
- inv_objects.push_back(mObjects[i]);
- }
- }
- return res;
+ bool res = false;
+ S32 count = mObjects.size();
+ inv_objects.reserve(inv_objects.size() + count);
+ if (count > 0)
+ {
+ res = true;
+ inv_objects.clear();
+ for (S32 i = 0; i < count; i++)
+ {
+ inv_objects.push_back(mObjects[i]);
+ }
+ }
+ return res;
}
// Returns true if the LL Clipboard has pasteable items in it
bool LLClipboard::hasContents() const
{
- return (mObjects.size() > 0);
+ return (mObjects.size() > 0);
}
// Returns true if the input uuid is in the list of clipboard objects
bool LLClipboard::isOnClipboard(const LLUUID& object) const
{
- std::vector<LLUUID>::const_iterator iter = std::find(mObjects.begin(), mObjects.end(), object);
- return (iter != mObjects.end());
+ 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);
+ return addToClipboard(src, pos, len, use_primary);
}
// Concatenate the input string to the LL and the system clipboard
bool LLClipboard::addToClipboard(const LLWString &src, S32 pos, S32 len, bool use_primary)
{
- try
- {
- mString = src.substr(pos, len);
- }
- catch (const std::exception& e)
- {
- LL_WARNS() << "Can't add the substring to clipboard: " << e.what() << LL_ENDL;
- return false;
- }
- return (use_primary ? LLView::getWindow()->copyTextToPrimary(mString) : LLView::getWindow()->copyTextToClipboard(mString));
+ try
+ {
+ mString = src.substr(pos, len);
+ }
+ catch (const std::exception& e)
+ {
+ LL_WARNS() << "Can't add the substring to clipboard: " << e.what() << LL_ENDL;
+ return false;
+ }
+ return (use_primary ? LLView::getWindow()->copyTextToPrimary(mString) : LLView::getWindow()->copyTextToClipboard(mString));
}
// Copy the System clipboard to the output string.
// Manage the LL Clipboard / System clipboard consistency
bool LLClipboard::pasteFromClipboard(LLWString &dst, bool use_primary)
{
- bool res = (use_primary ? LLView::getWindow()->pasteTextFromPrimary(dst) : LLView::getWindow()->pasteTextFromClipboard(dst));
- if (res)
- {
- mString = dst;
- }
- return res;
+ bool res = (use_primary ? LLView::getWindow()->pasteTextFromPrimary(dst) : LLView::getWindow()->pasteTextFromClipboard(dst));
+ if (res)
+ {
+ mString = dst;
+ }
+ return res;
}
// Return true if there's something on the System clipboard
bool LLClipboard::isTextAvailable(bool use_primary) const
{
- return (use_primary ? LLView::getWindow()->isPrimaryTextAvailable() : LLView::getWindow()->isClipboardTextAvailable());
+ return (use_primary ? LLView::getWindow()->isPrimaryTextAvailable() : LLView::getWindow()->isClipboardTextAvailable());
}
diff --git a/indra/llui/llclipboard.h b/indra/llui/llclipboard.h
index a668ac1ac6..12d8233e0a 100644
--- a/indra/llui/llclipboard.h
+++ b/indra/llui/llclipboard.h
@@ -1,25 +1,25 @@
-/**
+/**
* @file llclipboard.h
* @brief LLClipboard 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$
*/
@@ -38,56 +38,56 @@
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Class LLClipboard
//
-// This class is used to cut/copy/paste text strings and inventory items around
+// 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 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 : public LLSingleton<LLClipboard>
{
- LLSINGLETON(LLClipboard);
- ~LLClipboard();
-
+ LLSINGLETON(LLClipboard);
+ ~LLClipboard();
+
public:
- // 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; }
+ // 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; }
+
+ // 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;
- // 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
+ 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
- bool isCutMode() const { return mCutMode; }
- void setCutMode(bool mode) { mCutMode = mode; mGeneration++; }
+ bool isCutMode() const { return mCutMode; }
+ void setCutMode(bool mode) { mCutMode = mode; mGeneration++; }
private:
- 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.
+ 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.
};
#endif // LL_LLCLIPBOARD_H
diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp
index 07d1be3762..305263e0cc 100644
--- a/indra/llui/llcombobox.cpp
+++ b/indra/llui/llcombobox.cpp
@@ -1,1234 +1,1231 @@
-/**
- * @file llcombobox.cpp
- * @brief LLComboBox 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$
- */
-
-// A control that displays the name of the chosen item, which when
-// clicked shows a scrolling box of options.
-
-#include "linden_common.h"
-
-// file includes
-#include "llcombobox.h"
-
-// common includes
-#include "llstring.h"
-
-// newview includes
-#include "llbutton.h"
-#include "llkeyboard.h"
-#include "llscrolllistctrl.h"
-#include "llwindow.h"
-#include "llfloater.h"
-#include "llscrollbar.h"
-#include "llscrolllistcell.h"
-#include "llscrolllistitem.h"
-#include "llcontrol.h"
-#include "llfocusmgr.h"
-#include "lllineeditor.h"
-#include "v2math.h"
-#include "lluictrlfactory.h"
-#include "lltooltip.h"
-
-// Globals
-S32 MAX_COMBO_WIDTH = 500;
-
-static LLDefaultChildRegistry::Register<LLComboBox> register_combo_box("combo_box");
-
-void LLComboBox::PreferredPositionValues::declareValues()
-{
- declare("above", ABOVE);
- declare("below", BELOW);
-}
-
-LLComboBox::ItemParams::ItemParams()
-: label("label")
-{
-}
-
-
-LLComboBox::Params::Params()
-: allow_text_entry("allow_text_entry", false),
- allow_new_values("allow_new_values", false),
- show_text_as_tentative("show_text_as_tentative", true),
- max_chars("max_chars", 20),
- list_position("list_position", BELOW),
- items("item"),
- combo_button("combo_button"),
- combo_list("combo_list"),
- combo_editor("combo_editor"),
- drop_down_button("drop_down_button")
-{
- addSynonym(items, "combo_item");
-}
-
-
-LLComboBox::LLComboBox(const LLComboBox::Params& p)
-: LLUICtrl(p),
- mTextEntry(NULL),
- mTextEntryTentative(p.show_text_as_tentative),
- mHasAutocompletedText(false),
- mAllowTextEntry(p.allow_text_entry),
- mAllowNewValues(p.allow_new_values),
- mMaxChars(p.max_chars),
- mPrearrangeCallback(p.prearrange_callback()),
- mTextEntryCallback(p.text_entry_callback()),
- mTextChangedCallback(p.text_changed_callback()),
- mListPosition(p.list_position),
- mLastSelectedIndex(-1),
- mLabel(p.label)
-{
- // Text label button
-
- LLButton::Params button_params = (mAllowTextEntry ? p.combo_button : p.drop_down_button);
- button_params.mouse_down_callback.function(
- boost::bind(&LLComboBox::onButtonMouseDown, this));
- button_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_BOTTOM|FOLLOWS_RIGHT);
- button_params.rect(p.rect);
-
- if(mAllowTextEntry)
- {
- button_params.pad_right(2);
- }
-
- mArrowImage = button_params.image_unselected;
- if (mArrowImage.notNull())
- {
- mImageLoadedConnection = mArrowImage->addLoadedCallback(boost::bind(&LLComboBox::imageLoaded, this));
- }
-
- mButton = LLUICtrlFactory::create<LLButton>(button_params);
-
-
- if(mAllowTextEntry)
- {
- //redo to compensate for button hack that leaves space for a character
- //unless it is a "minimal combobox"(drop down)
- mButton->setRightHPad(2);
- }
- addChild(mButton);
-
- LLScrollListCtrl::Params params = p.combo_list;
- params.name("ComboBox");
- params.commit_callback.function(boost::bind(&LLComboBox::onItemSelected, this, _2));
- params.visible(false);
- params.commit_on_keyboard_movement(false);
-
- mList = LLUICtrlFactory::create<LLScrollListCtrl>(params);
- addChild(mList);
-
- // Mouse-down on button will transfer mouse focus to the list
- // Grab the mouse-up event and make sure the button state is correct
- mList->setMouseUpCallback(boost::bind(&LLComboBox::onListMouseUp, this));
-
- for (LLInitParam::ParamIterator<ItemParams>::const_iterator it = p.items.begin();
- it != p.items.end();
- ++it)
- {
- LLScrollListItem::Params item_params = *it;
- if (it->label.isProvided())
- {
- item_params.columns.add().value(it->label());
- }
-
- mList->addRow(item_params);
- }
-
- createLineEditor(p);
-
- mTopLostSignalConnection = setTopLostCallback(boost::bind(&LLComboBox::hideList, this));
-}
-
-void LLComboBox::initFromParams(const LLComboBox::Params& p)
-{
- LLUICtrl::initFromParams(p);
-
- if (!acceptsTextInput() && mLabel.empty())
- {
- selectFirstItem();
- }
-}
-
-// virtual
-bool LLComboBox::postBuild()
-{
- if (mControlVariable)
- {
- setValue(mControlVariable->getValue()); // selects the appropriate item
- }
- return true;
-}
-
-
-LLComboBox::~LLComboBox()
-{
- // children automatically deleted, including mMenu, mButton
-
- // explicitly disconect this signal, since base class destructor might fire top lost
- mTopLostSignalConnection.disconnect();
- mImageLoadedConnection.disconnect();
-}
-
-
-void LLComboBox::clear()
-{
- if (mTextEntry)
- {
- mTextEntry->setText(LLStringUtil::null);
- }
- mButton->setLabelSelected(LLStringUtil::null);
- mButton->setLabelUnselected(LLStringUtil::null);
- mList->deselectAllItems();
- mLastSelectedIndex = -1;
-}
-
-void LLComboBox::onCommit()
-{
- if (mAllowTextEntry && getCurrentIndex() != -1)
- {
- // we have selected an existing item, blitz the manual text entry with
- // the properly capitalized item
- mTextEntry->setValue(getSimple());
- mTextEntry->setTentative(false);
- }
- setControlValue(getValue());
- LLUICtrl::onCommit();
-}
-
-// virtual
-bool LLComboBox::isDirty() const
-{
- bool grubby = false;
- if ( mList )
- {
- grubby = mList->isDirty();
- }
- return grubby;
-}
-
-// virtual Clear dirty state
-void LLComboBox::resetDirty()
-{
- if ( mList )
- {
- mList->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)
-{
- LLScrollListItem* item = mList->addSimpleElement(name, pos);
- item->setEnabled(enabled);
- if (!mAllowTextEntry && mLabel.empty())
- {
- if (mControlVariable)
- {
- setValue(mControlVariable->getValue()); // selects the appropriate item
- }
- else
- {
- selectFirstItem();
- }
- }
- return item;
-}
-
-// add item "name" with a unique id to menu
-LLScrollListItem* LLComboBox::add(const std::string& name, const LLUUID& id, EAddPosition pos, bool enabled )
-{
- LLScrollListItem* item = mList->addSimpleElement(name, pos, id);
- item->setEnabled(enabled);
- if (!mAllowTextEntry && mLabel.empty())
- {
- if (mControlVariable)
- {
- setValue(mControlVariable->getValue()); // selects the appropriate item
- }
- else
- {
- selectFirstItem();
- }
- }
- return item;
-}
-
-// add item "name" with attached userdata
-LLScrollListItem* LLComboBox::add(const std::string& name, void* userdata, EAddPosition pos, bool enabled )
-{
- LLScrollListItem* item = mList->addSimpleElement(name, pos);
- item->setEnabled(enabled);
- item->setUserdata( userdata );
- if (!mAllowTextEntry && mLabel.empty())
- {
- if (mControlVariable)
- {
- setValue(mControlVariable->getValue()); // selects the appropriate item
- }
- else
- {
- selectFirstItem();
- }
- }
- return item;
-}
-
-// add item "name" with attached generic data
-LLScrollListItem* LLComboBox::add(const std::string& name, LLSD value, EAddPosition pos, bool enabled )
-{
- LLScrollListItem* item = mList->addSimpleElement(name, pos, value);
- item->setEnabled(enabled);
- if (!mAllowTextEntry && mLabel.empty())
- {
- if (mControlVariable)
- {
- setValue(mControlVariable->getValue()); // selects the appropriate item
- }
- else
- {
- selectFirstItem();
- }
- }
- return item;
-}
-
-LLScrollListItem* LLComboBox::addSeparator(EAddPosition pos)
-{
- return mList->addSeparator(pos);
-}
-
-void LLComboBox::sortByName(bool ascending)
-{
- mList->sortOnce(0, ascending);
-}
-
-
-// Choose an item with a given name in the menu.
-// Returns true if the item was found.
-bool LLComboBox::setSimple(const LLStringExplicit& name)
-{
- bool found = mList->selectItemByLabel(name, false);
-
- if (found)
- {
- setLabel(name);
- mLastSelectedIndex = mList->getFirstSelectedIndex();
- }
-
- return found;
-}
-
-// virtual
-void LLComboBox::setValue(const LLSD& value)
-{
- bool found = mList->selectByValue(value);
- if (found)
- {
- LLScrollListItem* item = mList->getFirstSelected();
- if (item)
- {
- updateLabel();
- }
- mLastSelectedIndex = mList->getFirstSelectedIndex();
- }
- else
- {
- mLastSelectedIndex = -1;
- }
-}
-
-const std::string LLComboBox::getSimple() const
-{
- const std::string res = getSelectedItemLabel();
- if (res.empty() && mAllowTextEntry)
- {
- return mTextEntry->getText();
- }
- else
- {
- return res;
- }
-}
-
-const std::string LLComboBox::getSelectedItemLabel(S32 column) const
-{
- return mList->getSelectedItemLabel(column);
-}
-
-// virtual
-LLSD LLComboBox::getValue() const
-{
- LLScrollListItem* item = mList->getFirstSelected();
- if( item )
- {
- return item->getValue();
- }
- else if (mAllowTextEntry)
- {
- return mTextEntry->getValue();
- }
- else
- {
- return LLSD();
- }
-}
-
-void LLComboBox::setLabel(const LLStringExplicit& name)
-{
- if ( mTextEntry )
- {
- mTextEntry->setText(name);
- if (mList->selectItemByLabel(name, false))
- {
- mTextEntry->setTentative(false);
- mLastSelectedIndex = mList->getFirstSelectedIndex();
- }
- else
- {
- mTextEntry->setTentative(mTextEntryTentative);
- }
- }
-
- if (!mAllowTextEntry)
- {
- mButton->setLabel(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)
-{
- bool found = mList->selectItemByLabel(name);
-
- if (found)
- {
- LLScrollListItem* item = mList->getFirstSelected();
- if (item)
- {
- mList->deleteSingleItem(mList->getItemIndex(item));
- }
- mLastSelectedIndex = mList->getFirstSelectedIndex();
- }
-
- return found;
-}
-
-bool LLComboBox::remove(S32 index)
-{
- if (index < mList->getItemCount())
- {
- mList->deleteSingleItem(index);
- setLabel(getSelectedItemLabel());
- return true;
- }
- return false;
-}
-
-// Keyboard focus lost.
-void LLComboBox::onFocusLost()
-{
- hideList();
- // if valid selection
- if (mAllowTextEntry && getCurrentIndex() != -1)
- {
- mTextEntry->selectAll();
- }
- mButton->setForcePressedState(false);
- LLUICtrl::onFocusLost();
-}
-
-void LLComboBox::setButtonVisible(bool visible)
-{
- static LLUICachedControl<S32> drop_shadow_button ("DropShadowButton", 0);
-
- mButton->setVisible(visible);
- if (mTextEntry)
- {
- LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
- if (visible)
- {
- S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0;
- text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * drop_shadow_button;
- }
- //mTextEntry->setRect(text_entry_rect);
- mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), true);
- }
-}
-
-bool LLComboBox::setCurrentByIndex( S32 index )
-{
- bool found = mList->selectNthItem( index );
- if (found)
- {
- setLabel(getSelectedItemLabel());
- mLastSelectedIndex = index;
- }
- return found;
-}
-
-S32 LLComboBox::getCurrentIndex() const
-{
- LLScrollListItem* item = mList->getFirstSelected();
- if( item )
- {
- return mList->getItemIndex( item );
- }
- return -1;
-}
-
-void LLComboBox::setEnabledByValue(const LLSD& value, bool enabled)
-{
- LLScrollListItem *found = mList->getItem(value);
- if (found)
- {
- found->setEnabled(enabled);
- }
-}
-
-void LLComboBox::createLineEditor(const LLComboBox::Params& p)
-{
- static LLUICachedControl<S32> drop_shadow_button ("DropShadowButton", 0);
- LLRect rect = getLocalRect();
- if (mAllowTextEntry)
- {
- S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0;
- S32 shadow_size = drop_shadow_button;
- mButton->setRect(LLRect( getRect().getWidth() - llmax(8,arrow_width) - 2 * shadow_size,
- rect.mTop, rect.mRight, rect.mBottom));
- mButton->setTabStop(false);
- mButton->setHAlign(LLFontGL::HCENTER);
-
- LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
- text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * drop_shadow_button;
- // clear label on button
- std::string cur_label = mButton->getLabelSelected();
- LLLineEditor::Params params = p.combo_editor;
- params.rect(text_entry_rect);
- params.default_text(LLStringUtil::null);
- params.max_length.bytes(mMaxChars);
- params.commit_callback.function(boost::bind(&LLComboBox::onTextCommit, this, _2));
- params.keystroke_callback(boost::bind(&LLComboBox::onTextEntry, this, _1));
- params.commit_on_focus_lost(false);
- params.follows.flags(FOLLOWS_ALL);
- params.label(mLabel);
- mTextEntry = LLUICtrlFactory::create<LLLineEditor> (params);
- mTextEntry->setText(cur_label);
- mTextEntry->setIgnoreTab(true);
- addChild(mTextEntry);
-
- // clear label on button
- setLabel(LLStringUtil::null);
-
- mButton->setFollows(FOLLOWS_BOTTOM | FOLLOWS_TOP | FOLLOWS_RIGHT);
- }
- else
- {
- mButton->setRect(rect);
- mButton->setLabel(mLabel.getString());
-
- if (mTextEntry)
- {
- mTextEntry->setVisible(false);
- }
- }
-}
-
-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();
- if( item )
- {
- return item->getUserdata();
- }
- return NULL;
-}
-
-
-void LLComboBox::showList()
-{
- // Make sure we don't go off top of screen.
- 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::getScaleFactor().mV[VY]) - 50 );
-
- // Make sure that we can see the whole list
- LLRect root_view_local;
- LLView* root_view = getRootView();
- root_view->localRectToOtherView(root_view->getLocalRect(), &root_view_local, this);
-
- LLRect rect = mList->getRect();
-
- S32 min_width = getRect().getWidth();
- S32 max_width = llmax(min_width, MAX_COMBO_WIDTH);
- // make sure we have up to date content width metrics
- S32 list_width = llclamp(mList->calcMaxContentWidth(), min_width, max_width);
-
- if (mListPosition == BELOW)
- {
- if (rect.getHeight() <= -root_view_local.mBottom)
- {
- // Move rect so it hangs off the bottom of this view
- rect.setLeftTopAndSize(0, 0, list_width, rect.getHeight() );
- }
- else
- {
- // stack on top or bottom, depending on which has more room
- if (-root_view_local.mBottom > root_view_local.mTop - getRect().getHeight())
- {
- // Move rect so it hangs off the bottom of this view
- rect.setLeftTopAndSize(0, 0, list_width, llmin(-root_view_local.mBottom, rect.getHeight()));
- }
- else
- {
- // move rect so it stacks on top of this view (clipped to size of screen)
- rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight()));
- }
- }
- }
- else // ABOVE
- {
- if (rect.getHeight() <= root_view_local.mTop - getRect().getHeight())
- {
- // move rect so it stacks on top of this view (clipped to size of screen)
- rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight()));
- }
- else
- {
- // stack on top or bottom, depending on which has more room
- if (-root_view_local.mBottom > root_view_local.mTop - getRect().getHeight())
- {
- // Move rect so it hangs off the bottom of this view
- rect.setLeftTopAndSize(0, 0, list_width, llmin(-root_view_local.mBottom, rect.getHeight()));
- }
- else
- {
- // move rect so it stacks on top of this view (clipped to size of screen)
- rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight()));
- }
- }
-
- }
- mList->setOrigin(rect.mLeft, rect.mBottom);
- mList->reshape(rect.getWidth(), rect.getHeight());
- mList->translateIntoRect(root_view_local);
-
- // Make sure we didn't go off bottom of screen
- S32 x, y;
- mList->localPointToScreen(0, 0, &x, &y);
-
- if (y < 0)
- {
- mList->translate(0, -y);
- }
-
- // NB: this call will trigger the focuslost callback which will hide the list, so do it first
- // before finally showing the list
-
- mList->setFocus(true);
-
- // Show the list and push the button down
- mButton->setToggleState(true);
- mList->setVisible(true);
-
- LLUI::getInstance()->addPopup(this);
-
- setUseBoundingRect(true);
-// updateBoundingRect();
-}
-
-void LLComboBox::hideList()
-{
- if (mList->getVisible())
- {
- // assert selection in list
- if(mAllowNewValues)
- {
- // mLastSelectedIndex = -1 means that we entered a new value, don't select
- // any of existing items in this case.
- if(mLastSelectedIndex >= 0)
- mList->selectNthItem(mLastSelectedIndex);
- }
- else if(mLastSelectedIndex >= 0)
- mList->selectNthItem(mLastSelectedIndex);
-
- mButton->setToggleState(false);
- mList->setVisible(false);
- mList->mouseOverHighlightNthItem(-1);
-
- setUseBoundingRect(false);
- LLUI::getInstance()->removePopup(this);
-// updateBoundingRect();
- }
-}
-
-void LLComboBox::onButtonMouseDown()
-{
- if (!mList->getVisible())
- {
- // this might change selection, so do it first
- prearrangeList();
-
- // highlight the last selected item from the original selection before potentially selecting a new item
- // as visual cue to original value of combo box
- LLScrollListItem* last_selected_item = mList->getLastSelectedItem();
- if (last_selected_item)
- {
- mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item));
- }
-
- if (mList->getItemCount() != 0)
- {
- showList();
- }
-
- setFocus( true );
-
- // pass mouse capture on to list if button is depressed
- if (mButton->hasMouseCapture())
- {
- gFocusMgr.setMouseCapture(mList);
-
- // But keep the "pressed" look, which buttons normally lose when they
- // lose focus
- mButton->setForcePressedState(true);
- }
- }
- else
- {
- hideList();
- }
-
-}
-
-void LLComboBox::onListMouseUp()
-{
- // In some cases this is the termination of a mouse click that started on
- // the button, so clear its pressed state
- mButton->setForcePressedState(false);
-}
-
-//------------------------------------------------------------------
-// static functions
-//------------------------------------------------------------------
-
-void LLComboBox::onItemSelected(const LLSD& data)
-{
- mLastSelectedIndex = getCurrentIndex();
- if (mLastSelectedIndex != -1)
- {
- updateLabel();
-
- if (mAllowTextEntry)
- {
- gFocusMgr.setKeyboardFocus(mTextEntry);
- mTextEntry->selectAll();
- }
- }
- // hiding the list reasserts the old value stored in the text editor/dropdown button
- hideList();
-
- // commit does the reverse, asserting the value in the list
- onCommit();
-}
-
-bool LLComboBox::handleToolTip(S32 x, S32 y, MASK mask)
-{
- std::string tool_tip;
-
- if(LLUICtrl::handleToolTip(x, y, mask))
- {
- return true;
- }
-
- tool_tip = getToolTip();
- if (tool_tip.empty())
- {
- tool_tip = getSelectedItemLabel();
- }
-
- if( !tool_tip.empty() )
- {
- LLToolTipMgr::instance().show(LLToolTip::Params()
- .message(tool_tip)
- .sticky_rect(calcScreenRect()));
- }
- return true;
-}
-
-bool LLComboBox::handleKeyHere(KEY key, MASK mask)
-{
- bool result = false;
- if (hasFocus())
- {
- if (mList->getVisible()
- && key == KEY_ESCAPE && mask == MASK_NONE)
- {
- hideList();
- return true;
- }
- //give list a chance to pop up and handle key
- LLScrollListItem* last_selected_item = mList->getLastSelectedItem();
- if (last_selected_item)
- {
- // highlight the original selection before potentially selecting a new item
- mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item));
- }
- result = mList->handleKeyHere(key, mask);
-
- // will only see return key if it is originating from line editor
- // 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->getCanSelect()
- && !mList->isEmpty()))
- {
- showList();
- }
- }
- return result;
-}
-
-bool LLComboBox::handleUnicodeCharHere(llwchar uni_char)
-{
- bool result = false;
- if (gFocusMgr.childHasKeyboardFocus(this))
- {
- // space bar just shows the list
- if (' ' != uni_char )
- {
- LLScrollListItem* last_selected_item = mList->getLastSelectedItem();
- if (last_selected_item)
- {
- // highlight the original selection before potentially selecting a new item
- mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item));
- }
- result = mList->handleUnicodeCharHere(uni_char);
- if (mList->getLastSelectedItem() != last_selected_item)
- {
- showList();
- }
- }
- }
- return result;
-}
-
-void LLComboBox::setTextEntry(const LLStringExplicit& text)
-{
- if (mTextEntry)
- {
- mTextEntry->setText(text);
- mHasAutocompletedText = false;
- updateSelection();
- }
-}
-
-void LLComboBox::setKeystrokeOnEsc(bool enable)
-{
- if (mTextEntry)
- {
- mTextEntry->setKeystrokeOnEsc(enable);
- }
-}
-
-void LLComboBox::onTextEntry(LLLineEditor* line_editor)
-{
- if (mTextEntryCallback != NULL)
- {
- (mTextEntryCallback)(line_editor, LLSD());
- }
-
- KEY key = gKeyboard->currentKey();
- if (key == KEY_BACKSPACE ||
- key == KEY_DELETE)
- {
- if (mList->selectItemByLabel(line_editor->getText(), false))
- {
- line_editor->setTentative(false);
- mLastSelectedIndex = mList->getFirstSelectedIndex();
- }
- else
- {
- line_editor->setTentative(mTextEntryTentative);
- mList->deselectAllItems();
- mLastSelectedIndex = -1;
- }
- if (mTextChangedCallback != NULL)
- {
- (mTextChangedCallback)(line_editor, LLSD());
- }
- return;
- }
-
- if (key == KEY_LEFT ||
- key == KEY_RIGHT)
- {
- return;
- }
-
- if (key == KEY_DOWN)
- {
- setCurrentByIndex(llmin(getItemCount() - 1, getCurrentIndex() + 1));
- if (!mList->getVisible())
- {
- prearrangeList();
-
- if (mList->getItemCount() != 0)
- {
- showList();
- }
- }
- line_editor->selectAll();
- line_editor->setTentative(false);
- }
- else if (key == KEY_UP)
- {
- setCurrentByIndex(llmax(0, getCurrentIndex() - 1));
- if (!mList->getVisible())
- {
- prearrangeList();
-
- if (mList->getItemCount() != 0)
- {
- showList();
- }
- }
- line_editor->selectAll();
- line_editor->setTentative(false);
- }
- else
- {
- // RN: presumably text entry
- updateSelection();
- }
- if (mTextChangedCallback != NULL)
- {
- (mTextChangedCallback)(line_editor, LLSD());
- }
-}
-
-void LLComboBox::updateSelection()
-{
- LLWString left_wstring = mTextEntry->getWText().substr(0, mTextEntry->getCursor());
- // user-entered portion of string, based on assumption that any selected
- // text was a result of auto-completion
- LLWString user_wstring = mHasAutocompletedText ? left_wstring : mTextEntry->getWText();
- std::string full_string = mTextEntry->getText();
-
- // go ahead and arrange drop down list on first typed character, even
- // though we aren't showing it... some code relies on prearrange
- // callback to populate content
- if( mTextEntry->getWText().size() == 1 )
- {
- prearrangeList(mTextEntry->getText());
- }
-
- if (mList->selectItemByLabel(full_string, false))
- {
- mTextEntry->setTentative(false);
- mLastSelectedIndex = mList->getFirstSelectedIndex();
- }
- else if (mList->selectItemByPrefix(left_wstring, false))
- {
- LLWString selected_item = utf8str_to_wstring(getSelectedItemLabel());
- LLWString wtext = left_wstring + selected_item.substr(left_wstring.size(), selected_item.size());
- mTextEntry->setText(wstring_to_utf8str(wtext));
- mTextEntry->setSelection(left_wstring.size(), mTextEntry->getWText().size());
- mTextEntry->endSelection();
- mTextEntry->setTentative(false);
- mHasAutocompletedText = true;
- mLastSelectedIndex = mList->getFirstSelectedIndex();
- }
- else // no matching items found
- {
- mList->deselectAllItems();
- mTextEntry->setText(wstring_to_utf8str(user_wstring)); // removes text added by autocompletion
- mTextEntry->setTentative(mTextEntryTentative);
- mHasAutocompletedText = false;
- mLastSelectedIndex = -1;
- }
-}
-
-void LLComboBox::onTextCommit(const LLSD& data)
-{
- std::string text = mTextEntry->getText();
- setSimple(text);
- onCommit();
- mTextEntry->selectAll();
-}
-
-void LLComboBox::setFocus(bool b)
-{
- LLUICtrl::setFocus(b);
-
- if (b)
- {
- mList->clearSearchString();
- if (mList->getVisible())
- {
- mList->setFocus(true);
- }
- }
-}
-
-void LLComboBox::prearrangeList(std::string filter)
-{
- if (mPrearrangeCallback)
- {
- mPrearrangeCallback(this, LLSD(filter));
- }
-}
-
-
-//============================================================================
-// ll::ui::SearchableControl functions
-
-//virtual
-std::string LLComboBox::_getSearchText() const
-{
- std::string res;
- if (mList)
- {
- // getAllData returns a full copy of content, might be a
- // better option to implement an mList->getSearchText(column)
- std::vector<LLScrollListItem*> data = mList->getAllData();
- std::vector<LLScrollListItem*>::iterator iter = data.begin();
- while (iter != data.end())
- {
- LLScrollListCell* cell = (*iter)->getColumn(0);
- if (cell)
- {
- std::string whitelist_url = cell->getValue().asString();
- res += cell->getValue().asString();
- }
- iter++;
- }
- }
- return res + getToolTip();
-}
-
-//virtual
-void LLComboBox::onSetHighlight() const
-{
- if (mButton)
- {
- mButton->ll::ui::SearchableControl::setHighlighted(ll::ui::SearchableControl::getHighlighted());
- }
-}
-
-void LLComboBox::imageLoaded()
-{
- static LLUICachedControl<S32> drop_shadow_button("DropShadowButton", 0);
-
- if (mAllowTextEntry)
- {
- LLRect rect = getLocalRect();
- S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0;
- S32 shadow_size = drop_shadow_button;
- mButton->setRect(LLRect(getRect().getWidth() - llmax(8, arrow_width) - 2 * shadow_size,
- rect.mTop, rect.mRight, rect.mBottom));
- if (mButton->getVisible())
- {
- // recalculate field size
- if (mTextEntry)
- {
- LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
- text_entry_rect.mRight -= llmax(8, arrow_width) + 2 * drop_shadow_button;
- mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), true);
- }
- }
- }
-}
-
-//============================================================================
-// LLCtrlListInterface functions
-
-S32 LLComboBox::getItemCount() const
-{
- return mList->getItemCount();
-}
-
-void LLComboBox::addColumn(const LLSD& column, EAddPosition pos)
-{
- mList->clearColumns();
- mList->addColumn(column, pos);
-}
-
-void LLComboBox::clearColumns()
-{
- mList->clearColumns();
-}
-
-void LLComboBox::setColumnLabel(const std::string& column, const std::string& label)
-{
- mList->setColumnLabel(column, label);
-}
-
-LLScrollListItem* LLComboBox::addElement(const LLSD& value, EAddPosition pos, void* userdata)
-{
- return mList->addElement(value, pos, userdata);
-}
-
-LLScrollListItem* LLComboBox::addSimpleElement(const std::string& value, EAddPosition pos, const LLSD& id)
-{
- return mList->addSimpleElement(value, pos, id);
-}
-
-void LLComboBox::clearRows()
-{
- mList->clearRows();
-}
-
-void LLComboBox::sortByColumn(const std::string& name, bool ascending)
-{
- mList->sortByColumn(name, ascending);
-}
-
-//============================================================================
-//LLCtrlSelectionInterface functions
-
-bool LLComboBox::setCurrentByID(const LLUUID& id)
-{
- bool found = mList->selectByID( id );
-
- if (found)
- {
- setLabel(getSelectedItemLabel());
- mLastSelectedIndex = mList->getFirstSelectedIndex();
- }
-
- return found;
-}
-
-LLUUID LLComboBox::getCurrentID() const
-{
- return mList->getStringUUIDSelectedItem();
-}
-bool LLComboBox::setSelectedByValue(const LLSD& value, bool selected)
-{
- bool found = mList->setSelectedByValue(value, selected);
- if (found)
- {
- setLabel(getSelectedItemLabel());
- }
- return found;
-}
-
-LLSD LLComboBox::getSelectedValue()
-{
- return mList->getSelectedValue();
-}
-
-bool LLComboBox::isSelected(const LLSD& value) const
-{
- return mList->isSelected(value);
-}
-
-bool LLComboBox::operateOnSelection(EOperation op)
-{
- if (op == OP_DELETE)
- {
- mList->deleteSelectedItems();
- return true;
- }
- return false;
-}
-
-bool LLComboBox::operateOnAll(EOperation op)
-{
- if (op == OP_DELETE)
- {
- clearRows();
- return true;
- }
- return false;
-}
-
-bool LLComboBox::selectItemRange( S32 first, S32 last )
-{
- return mList->selectItemRange(first, last);
-}
-
-
-static LLDefaultChildRegistry::Register<LLIconsComboBox> register_icons_combo_box("icons_combo_box");
-
-LLIconsComboBox::Params::Params()
-: icon_column("icon_column", ICON_COLUMN),
- label_column("label_column", LABEL_COLUMN)
-{}
-
-LLIconsComboBox::LLIconsComboBox(const LLIconsComboBox::Params& p)
-: LLComboBox(p),
- mIconColumnIndex(p.icon_column),
- mLabelColumnIndex(p.label_column)
-{}
-
-const std::string LLIconsComboBox::getSelectedItemLabel(S32 column) const
-{
- mButton->setImageOverlay(LLComboBox::getSelectedItemLabel(mIconColumnIndex), mButton->getImageOverlayHAlign());
-
- return LLComboBox::getSelectedItemLabel(mLabelColumnIndex);
-}
+/**
+ * @file llcombobox.cpp
+ * @brief LLComboBox 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$
+ */
+
+// A control that displays the name of the chosen item, which when
+// clicked shows a scrolling box of options.
+
+#include "linden_common.h"
+
+// file includes
+#include "llcombobox.h"
+
+// common includes
+#include "llstring.h"
+
+// newview includes
+#include "llbutton.h"
+#include "llkeyboard.h"
+#include "llscrolllistctrl.h"
+#include "llwindow.h"
+#include "llfloater.h"
+#include "llscrollbar.h"
+#include "llscrolllistcell.h"
+#include "llscrolllistitem.h"
+#include "llcontrol.h"
+#include "llfocusmgr.h"
+#include "lllineeditor.h"
+#include "v2math.h"
+#include "lluictrlfactory.h"
+#include "lltooltip.h"
+
+// Globals
+S32 MAX_COMBO_WIDTH = 500;
+
+static LLDefaultChildRegistry::Register<LLComboBox> register_combo_box("combo_box");
+
+void LLComboBox::PreferredPositionValues::declareValues()
+{
+ declare("above", ABOVE);
+ declare("below", BELOW);
+}
+
+LLComboBox::ItemParams::ItemParams()
+: label("label")
+{
+}
+
+
+LLComboBox::Params::Params()
+: allow_text_entry("allow_text_entry", false),
+ allow_new_values("allow_new_values", false),
+ show_text_as_tentative("show_text_as_tentative", true),
+ max_chars("max_chars", 20),
+ list_position("list_position", BELOW),
+ items("item"),
+ combo_button("combo_button"),
+ combo_list("combo_list"),
+ combo_editor("combo_editor"),
+ drop_down_button("drop_down_button")
+{
+ addSynonym(items, "combo_item");
+}
+
+
+LLComboBox::LLComboBox(const LLComboBox::Params& p)
+: LLUICtrl(p),
+ mTextEntry(NULL),
+ mTextEntryTentative(p.show_text_as_tentative),
+ mHasAutocompletedText(false),
+ mAllowTextEntry(p.allow_text_entry),
+ mAllowNewValues(p.allow_new_values),
+ mMaxChars(p.max_chars),
+ mPrearrangeCallback(p.prearrange_callback()),
+ mTextEntryCallback(p.text_entry_callback()),
+ mTextChangedCallback(p.text_changed_callback()),
+ mListPosition(p.list_position),
+ mLastSelectedIndex(-1),
+ mLabel(p.label)
+{
+ // Text label button
+
+ LLButton::Params button_params = (mAllowTextEntry ? p.combo_button : p.drop_down_button);
+ button_params.mouse_down_callback.function(
+ boost::bind(&LLComboBox::onButtonMouseDown, this));
+ button_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_BOTTOM|FOLLOWS_RIGHT);
+ button_params.rect(p.rect);
+
+ if(mAllowTextEntry)
+ {
+ button_params.pad_right(2);
+ }
+
+ mArrowImage = button_params.image_unselected;
+ if (mArrowImage.notNull())
+ {
+ mImageLoadedConnection = mArrowImage->addLoadedCallback(boost::bind(&LLComboBox::imageLoaded, this));
+ }
+
+ mButton = LLUICtrlFactory::create<LLButton>(button_params);
+
+
+ if(mAllowTextEntry)
+ {
+ //redo to compensate for button hack that leaves space for a character
+ //unless it is a "minimal combobox"(drop down)
+ mButton->setRightHPad(2);
+ }
+ addChild(mButton);
+
+ LLScrollListCtrl::Params params = p.combo_list;
+ params.name("ComboBox");
+ params.commit_callback.function(boost::bind(&LLComboBox::onItemSelected, this, _2));
+ params.visible(false);
+ params.commit_on_keyboard_movement(false);
+
+ mList = LLUICtrlFactory::create<LLScrollListCtrl>(params);
+ addChild(mList);
+
+ // Mouse-down on button will transfer mouse focus to the list
+ // Grab the mouse-up event and make sure the button state is correct
+ mList->setMouseUpCallback(boost::bind(&LLComboBox::onListMouseUp, this));
+
+ for (LLInitParam::ParamIterator<ItemParams>::const_iterator it = p.items.begin();
+ it != p.items.end();
+ ++it)
+ {
+ LLScrollListItem::Params item_params = *it;
+ if (it->label.isProvided())
+ {
+ item_params.columns.add().value(it->label());
+ }
+
+ mList->addRow(item_params);
+ }
+
+ createLineEditor(p);
+
+ mTopLostSignalConnection = setTopLostCallback(boost::bind(&LLComboBox::hideList, this));
+}
+
+void LLComboBox::initFromParams(const LLComboBox::Params& p)
+{
+ LLUICtrl::initFromParams(p);
+
+ if (!acceptsTextInput() && mLabel.empty())
+ {
+ selectFirstItem();
+ }
+}
+
+// virtual
+bool LLComboBox::postBuild()
+{
+ if (mControlVariable)
+ {
+ setValue(mControlVariable->getValue()); // selects the appropriate item
+ }
+ return true;
+}
+
+
+LLComboBox::~LLComboBox()
+{
+ // children automatically deleted, including mMenu, mButton
+
+ // explicitly disconect this signal, since base class destructor might fire top lost
+ mTopLostSignalConnection.disconnect();
+ mImageLoadedConnection.disconnect();
+
+ LLUI::getInstance()->removePopup(this);
+}
+
+
+void LLComboBox::clear()
+{
+ if (mTextEntry)
+ {
+ mTextEntry->setText(LLStringUtil::null);
+ }
+ mButton->setLabelSelected(LLStringUtil::null);
+ mButton->setLabelUnselected(LLStringUtil::null);
+ mList->deselectAllItems();
+ mLastSelectedIndex = -1;
+}
+
+void LLComboBox::onCommit()
+{
+ if (mAllowTextEntry && getCurrentIndex() != -1)
+ {
+ // we have selected an existing item, blitz the manual text entry with
+ // the properly capitalized item
+ mTextEntry->setValue(getSimple());
+ mTextEntry->setTentative(false);
+ }
+ setControlValue(getValue());
+ LLUICtrl::onCommit();
+}
+
+// virtual
+bool LLComboBox::isDirty() const
+{
+ bool grubby = false;
+ if ( mList )
+ {
+ grubby = mList->isDirty();
+ }
+ return grubby;
+}
+
+// virtual Clear dirty state
+void LLComboBox::resetDirty()
+{
+ if ( mList )
+ {
+ mList->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)
+{
+ LLScrollListItem* item = mList->addSimpleElement(name, pos);
+ item->setEnabled(enabled);
+ if (!mAllowTextEntry && mLabel.empty())
+ {
+ if (mControlVariable)
+ {
+ setValue(mControlVariable->getValue()); // selects the appropriate item
+ }
+ else
+ {
+ selectFirstItem();
+ }
+ }
+ return item;
+}
+
+// add item "name" with a unique id to menu
+LLScrollListItem* LLComboBox::add(const std::string& name, const LLUUID& id, EAddPosition pos, bool enabled )
+{
+ LLScrollListItem* item = mList->addSimpleElement(name, pos, id);
+ item->setEnabled(enabled);
+ if (!mAllowTextEntry && mLabel.empty())
+ {
+ if (mControlVariable)
+ {
+ setValue(mControlVariable->getValue()); // selects the appropriate item
+ }
+ else
+ {
+ selectFirstItem();
+ }
+ }
+ return item;
+}
+
+// add item "name" with attached userdata
+LLScrollListItem* LLComboBox::add(const std::string& name, void* userdata, EAddPosition pos, bool enabled )
+{
+ LLScrollListItem* item = mList->addSimpleElement(name, pos);
+ item->setEnabled(enabled);
+ item->setUserdata( userdata );
+ if (!mAllowTextEntry && mLabel.empty())
+ {
+ if (mControlVariable)
+ {
+ setValue(mControlVariable->getValue()); // selects the appropriate item
+ }
+ else
+ {
+ selectFirstItem();
+ }
+ }
+ return item;
+}
+
+// add item "name" with attached generic data
+LLScrollListItem* LLComboBox::add(const std::string& name, LLSD value, EAddPosition pos, bool enabled )
+{
+ LLScrollListItem* item = mList->addSimpleElement(name, pos, value);
+ item->setEnabled(enabled);
+ if (!mAllowTextEntry && mLabel.empty())
+ {
+ if (mControlVariable)
+ {
+ setValue(mControlVariable->getValue()); // selects the appropriate item
+ }
+ else
+ {
+ selectFirstItem();
+ }
+ }
+ return item;
+}
+
+LLScrollListItem* LLComboBox::addSeparator(EAddPosition pos)
+{
+ return mList->addSeparator(pos);
+}
+
+void LLComboBox::sortByName(bool ascending)
+{
+ mList->sortOnce(0, ascending);
+}
+
+
+// Choose an item with a given name in the menu.
+// Returns true if the item was found.
+bool LLComboBox::setSimple(const LLStringExplicit& name)
+{
+ bool found = mList->selectItemByLabel(name, false);
+
+ if (found)
+ {
+ setLabel(name);
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
+ }
+
+ return found;
+}
+
+// virtual
+void LLComboBox::setValue(const LLSD& value)
+{
+ bool found = mList->selectByValue(value);
+ if (found)
+ {
+ LLScrollListItem* item = mList->getFirstSelected();
+ if (item)
+ {
+ updateLabel();
+ }
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
+ }
+ else
+ {
+ mLastSelectedIndex = -1;
+ }
+}
+
+const std::string LLComboBox::getSimple() const
+{
+ const std::string res = getSelectedItemLabel();
+ if (res.empty() && mAllowTextEntry)
+ {
+ return mTextEntry->getText();
+ }
+ else
+ {
+ return res;
+ }
+}
+
+const std::string LLComboBox::getSelectedItemLabel(S32 column) const
+{
+ return mList->getSelectedItemLabel(column);
+}
+
+// virtual
+LLSD LLComboBox::getValue() const
+{
+ LLScrollListItem* item = mList->getFirstSelected();
+ if( item )
+ {
+ return item->getValue();
+ }
+ else if (mAllowTextEntry)
+ {
+ return mTextEntry->getValue();
+ }
+ else
+ {
+ return LLSD();
+ }
+}
+
+void LLComboBox::setLabel(const LLStringExplicit& name)
+{
+ if ( mTextEntry )
+ {
+ mTextEntry->setText(name);
+ if (mList->selectItemByLabel(name, false))
+ {
+ mTextEntry->setTentative(false);
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
+ }
+ else
+ {
+ mTextEntry->setTentative(mTextEntryTentative);
+ }
+ }
+
+ if (!mAllowTextEntry)
+ {
+ mButton->setLabel(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)
+{
+ bool found = mList->selectItemByLabel(name);
+
+ if (found)
+ {
+ LLScrollListItem* item = mList->getFirstSelected();
+ if (item)
+ {
+ mList->deleteSingleItem(mList->getItemIndex(item));
+ }
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
+ }
+
+ return found;
+}
+
+bool LLComboBox::remove(S32 index)
+{
+ if (index < mList->getItemCount())
+ {
+ mList->deleteSingleItem(index);
+ setLabel(getSelectedItemLabel());
+ return true;
+ }
+ return false;
+}
+
+// Keyboard focus lost.
+void LLComboBox::onFocusLost()
+{
+ hideList();
+ // if valid selection
+ if (mAllowTextEntry && getCurrentIndex() != -1)
+ {
+ mTextEntry->selectAll();
+ }
+ mButton->setForcePressedState(false);
+ LLUICtrl::onFocusLost();
+}
+
+void LLComboBox::setButtonVisible(bool visible)
+{
+ mButton->setVisible(visible);
+ if (mTextEntry)
+ {
+ LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
+ if (visible)
+ {
+ S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0;
+ text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * BTN_DROP_SHADOW;
+ }
+ //mTextEntry->setRect(text_entry_rect);
+ mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), true);
+ }
+}
+
+bool LLComboBox::setCurrentByIndex( S32 index )
+{
+ bool found = mList->selectNthItem( index );
+ if (found)
+ {
+ setLabel(getSelectedItemLabel());
+ mLastSelectedIndex = index;
+ }
+ return found;
+}
+
+S32 LLComboBox::getCurrentIndex() const
+{
+ LLScrollListItem* item = mList->getFirstSelected();
+ if( item )
+ {
+ return mList->getItemIndex( item );
+ }
+ return -1;
+}
+
+void LLComboBox::setEnabledByValue(const LLSD& value, bool enabled)
+{
+ LLScrollListItem *found = mList->getItem(value);
+ if (found)
+ {
+ found->setEnabled(enabled);
+ }
+}
+
+void LLComboBox::createLineEditor(const LLComboBox::Params& p)
+{
+ LLRect rect = getLocalRect();
+ if (mAllowTextEntry)
+ {
+ S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0;
+ S32 shadow_size = BTN_DROP_SHADOW;
+ mButton->setRect(LLRect( getRect().getWidth() - llmax(8,arrow_width) - 2 * shadow_size,
+ rect.mTop, rect.mRight, rect.mBottom));
+ mButton->setTabStop(false);
+ mButton->setHAlign(LLFontGL::HCENTER);
+
+ LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
+ text_entry_rect.mRight -= llmax(8,arrow_width) + 2 * BTN_DROP_SHADOW;
+ // clear label on button
+ std::string cur_label = mButton->getLabelSelected();
+ LLLineEditor::Params params = p.combo_editor;
+ params.rect(text_entry_rect);
+ params.default_text(LLStringUtil::null);
+ params.max_length.bytes(mMaxChars);
+ params.commit_callback.function(boost::bind(&LLComboBox::onTextCommit, this, _2));
+ params.keystroke_callback(boost::bind(&LLComboBox::onTextEntry, this, _1));
+ params.commit_on_focus_lost(false);
+ params.follows.flags(FOLLOWS_ALL);
+ params.label(mLabel);
+ mTextEntry = LLUICtrlFactory::create<LLLineEditor> (params);
+ mTextEntry->setText(cur_label);
+ mTextEntry->setIgnoreTab(true);
+ addChild(mTextEntry);
+
+ // clear label on button
+ setLabel(LLStringUtil::null);
+
+ mButton->setFollows(FOLLOWS_BOTTOM | FOLLOWS_TOP | FOLLOWS_RIGHT);
+ }
+ else
+ {
+ mButton->setRect(rect);
+ mButton->setLabel(mLabel.getString());
+
+ if (mTextEntry)
+ {
+ mTextEntry->setVisible(false);
+ }
+ }
+}
+
+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();
+ if( item )
+ {
+ return item->getUserdata();
+ }
+ return NULL;
+}
+
+
+void LLComboBox::showList()
+{
+ // Make sure we don't go off top of screen.
+ 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::getScaleFactor().mV[VY]) - 50 );
+
+ // Make sure that we can see the whole list
+ LLRect root_view_local;
+ LLView* root_view = getRootView();
+ root_view->localRectToOtherView(root_view->getLocalRect(), &root_view_local, this);
+
+ LLRect rect = mList->getRect();
+
+ S32 min_width = getRect().getWidth();
+ S32 max_width = llmax(min_width, MAX_COMBO_WIDTH);
+ // make sure we have up to date content width metrics
+ S32 list_width = llclamp(mList->calcMaxContentWidth(), min_width, max_width);
+
+ if (mListPosition == BELOW)
+ {
+ if (rect.getHeight() <= -root_view_local.mBottom)
+ {
+ // Move rect so it hangs off the bottom of this view
+ rect.setLeftTopAndSize(0, 0, list_width, rect.getHeight() );
+ }
+ else
+ {
+ // stack on top or bottom, depending on which has more room
+ if (-root_view_local.mBottom > root_view_local.mTop - getRect().getHeight())
+ {
+ // Move rect so it hangs off the bottom of this view
+ rect.setLeftTopAndSize(0, 0, list_width, llmin(-root_view_local.mBottom, rect.getHeight()));
+ }
+ else
+ {
+ // move rect so it stacks on top of this view (clipped to size of screen)
+ rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight()));
+ }
+ }
+ }
+ else // ABOVE
+ {
+ if (rect.getHeight() <= root_view_local.mTop - getRect().getHeight())
+ {
+ // move rect so it stacks on top of this view (clipped to size of screen)
+ rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight()));
+ }
+ else
+ {
+ // stack on top or bottom, depending on which has more room
+ if (-root_view_local.mBottom > root_view_local.mTop - getRect().getHeight())
+ {
+ // Move rect so it hangs off the bottom of this view
+ rect.setLeftTopAndSize(0, 0, list_width, llmin(-root_view_local.mBottom, rect.getHeight()));
+ }
+ else
+ {
+ // move rect so it stacks on top of this view (clipped to size of screen)
+ rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight()));
+ }
+ }
+
+ }
+ mList->setOrigin(rect.mLeft, rect.mBottom);
+ mList->reshape(rect.getWidth(), rect.getHeight());
+ mList->translateIntoRect(root_view_local);
+
+ // Make sure we didn't go off bottom of screen
+ S32 x, y;
+ mList->localPointToScreen(0, 0, &x, &y);
+
+ if (y < 0)
+ {
+ mList->translate(0, -y);
+ }
+
+ // NB: this call will trigger the focuslost callback which will hide the list, so do it first
+ // before finally showing the list
+
+ mList->setFocus(true);
+
+ // Show the list and push the button down
+ mButton->setToggleState(true);
+ mList->setVisible(true);
+
+ LLUI::getInstance()->addPopup(this);
+
+ setUseBoundingRect(true);
+// updateBoundingRect();
+}
+
+void LLComboBox::hideList()
+{
+ if (mList->getVisible())
+ {
+ // assert selection in list
+ if(mAllowNewValues)
+ {
+ // mLastSelectedIndex = -1 means that we entered a new value, don't select
+ // any of existing items in this case.
+ if(mLastSelectedIndex >= 0)
+ mList->selectNthItem(mLastSelectedIndex);
+ }
+ else if(mLastSelectedIndex >= 0)
+ mList->selectNthItem(mLastSelectedIndex);
+
+ mButton->setToggleState(false);
+ mList->setVisible(false);
+ mList->mouseOverHighlightNthItem(-1);
+
+ setUseBoundingRect(false);
+ LLUI::getInstance()->removePopup(this);
+// updateBoundingRect();
+ }
+}
+
+void LLComboBox::onButtonMouseDown()
+{
+ if (!mList->getVisible())
+ {
+ // this might change selection, so do it first
+ prearrangeList();
+
+ // highlight the last selected item from the original selection before potentially selecting a new item
+ // as visual cue to original value of combo box
+ LLScrollListItem* last_selected_item = mList->getLastSelectedItem();
+ if (last_selected_item)
+ {
+ mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item));
+ }
+
+ if (mList->getItemCount() != 0)
+ {
+ showList();
+ }
+
+ setFocus( true );
+
+ // pass mouse capture on to list if button is depressed
+ if (mButton->hasMouseCapture())
+ {
+ gFocusMgr.setMouseCapture(mList);
+
+ // But keep the "pressed" look, which buttons normally lose when they
+ // lose focus
+ mButton->setForcePressedState(true);
+ }
+ }
+ else
+ {
+ hideList();
+ }
+
+}
+
+void LLComboBox::onListMouseUp()
+{
+ // In some cases this is the termination of a mouse click that started on
+ // the button, so clear its pressed state
+ mButton->setForcePressedState(false);
+}
+
+//------------------------------------------------------------------
+// static functions
+//------------------------------------------------------------------
+
+void LLComboBox::onItemSelected(const LLSD& data)
+{
+ mLastSelectedIndex = getCurrentIndex();
+ if (mLastSelectedIndex != -1)
+ {
+ updateLabel();
+
+ if (mAllowTextEntry)
+ {
+ gFocusMgr.setKeyboardFocus(mTextEntry);
+ mTextEntry->selectAll();
+ }
+ }
+ // hiding the list reasserts the old value stored in the text editor/dropdown button
+ hideList();
+
+ // commit does the reverse, asserting the value in the list
+ onCommit();
+}
+
+bool LLComboBox::handleToolTip(S32 x, S32 y, MASK mask)
+{
+ std::string tool_tip;
+
+ if(LLUICtrl::handleToolTip(x, y, mask))
+ {
+ return true;
+ }
+
+ tool_tip = getToolTip();
+ if (tool_tip.empty())
+ {
+ tool_tip = getSelectedItemLabel();
+ }
+
+ if( !tool_tip.empty() )
+ {
+ LLToolTipMgr::instance().show(LLToolTip::Params()
+ .message(tool_tip)
+ .sticky_rect(calcScreenRect()));
+ }
+ return true;
+}
+
+bool LLComboBox::handleKeyHere(KEY key, MASK mask)
+{
+ bool result = false;
+ if (hasFocus())
+ {
+ if (mList->getVisible()
+ && key == KEY_ESCAPE && mask == MASK_NONE)
+ {
+ hideList();
+ return true;
+ }
+ //give list a chance to pop up and handle key
+ LLScrollListItem* last_selected_item = mList->getLastSelectedItem();
+ if (last_selected_item)
+ {
+ // highlight the original selection before potentially selecting a new item
+ mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item));
+ }
+ result = mList->handleKeyHere(key, mask);
+
+ // will only see return key if it is originating from line editor
+ // 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->getCanSelect()
+ && !mList->isEmpty()))
+ {
+ showList();
+ }
+ }
+ return result;
+}
+
+bool LLComboBox::handleUnicodeCharHere(llwchar uni_char)
+{
+ bool result = false;
+ if (gFocusMgr.childHasKeyboardFocus(this))
+ {
+ // space bar just shows the list
+ if (' ' != uni_char )
+ {
+ LLScrollListItem* last_selected_item = mList->getLastSelectedItem();
+ if (last_selected_item)
+ {
+ // highlight the original selection before potentially selecting a new item
+ mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item));
+ }
+ result = mList->handleUnicodeCharHere(uni_char);
+ if (mList->getLastSelectedItem() != last_selected_item)
+ {
+ showList();
+ }
+ }
+ }
+ return result;
+}
+
+void LLComboBox::setTextEntry(const LLStringExplicit& text)
+{
+ if (mTextEntry)
+ {
+ mTextEntry->setText(text);
+ mHasAutocompletedText = false;
+ updateSelection();
+ }
+}
+
+void LLComboBox::setKeystrokeOnEsc(bool enable)
+{
+ if (mTextEntry)
+ {
+ mTextEntry->setKeystrokeOnEsc(enable);
+ }
+}
+
+void LLComboBox::onTextEntry(LLLineEditor* line_editor)
+{
+ if (mTextEntryCallback != NULL)
+ {
+ (mTextEntryCallback)(line_editor, LLSD());
+ }
+
+ KEY key = gKeyboard->currentKey();
+ if (key == KEY_BACKSPACE ||
+ key == KEY_DELETE)
+ {
+ if (mList->selectItemByLabel(line_editor->getText(), false))
+ {
+ line_editor->setTentative(false);
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
+ }
+ else
+ {
+ line_editor->setTentative(mTextEntryTentative);
+ mList->deselectAllItems();
+ mLastSelectedIndex = -1;
+ }
+ if (mTextChangedCallback != NULL)
+ {
+ (mTextChangedCallback)(line_editor, LLSD());
+ }
+ return;
+ }
+
+ if (key == KEY_LEFT ||
+ key == KEY_RIGHT)
+ {
+ return;
+ }
+
+ if (key == KEY_DOWN)
+ {
+ setCurrentByIndex(llmin(getItemCount() - 1, getCurrentIndex() + 1));
+ if (!mList->getVisible())
+ {
+ prearrangeList();
+
+ if (mList->getItemCount() != 0)
+ {
+ showList();
+ }
+ }
+ line_editor->selectAll();
+ line_editor->setTentative(false);
+ }
+ else if (key == KEY_UP)
+ {
+ setCurrentByIndex(llmax(0, getCurrentIndex() - 1));
+ if (!mList->getVisible())
+ {
+ prearrangeList();
+
+ if (mList->getItemCount() != 0)
+ {
+ showList();
+ }
+ }
+ line_editor->selectAll();
+ line_editor->setTentative(false);
+ }
+ else
+ {
+ // RN: presumably text entry
+ updateSelection();
+ }
+ if (mTextChangedCallback != NULL)
+ {
+ (mTextChangedCallback)(line_editor, LLSD());
+ }
+}
+
+void LLComboBox::updateSelection()
+{
+ LLWString left_wstring = mTextEntry->getWText().substr(0, mTextEntry->getCursor());
+ // user-entered portion of string, based on assumption that any selected
+ // text was a result of auto-completion
+ LLWString user_wstring = mHasAutocompletedText ? left_wstring : mTextEntry->getWText();
+ std::string full_string = mTextEntry->getText();
+
+ // go ahead and arrange drop down list on first typed character, even
+ // though we aren't showing it... some code relies on prearrange
+ // callback to populate content
+ if( mTextEntry->getWText().size() == 1 )
+ {
+ prearrangeList(mTextEntry->getText());
+ }
+
+ if (mList->selectItemByLabel(full_string, false))
+ {
+ mTextEntry->setTentative(false);
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
+ }
+ else if (mList->selectItemByPrefix(left_wstring, false))
+ {
+ LLWString selected_item = utf8str_to_wstring(getSelectedItemLabel());
+ LLWString wtext = left_wstring + selected_item.substr(left_wstring.size(), selected_item.size());
+ mTextEntry->setText(wstring_to_utf8str(wtext));
+ mTextEntry->setSelection(left_wstring.size(), mTextEntry->getWText().size());
+ mTextEntry->endSelection();
+ mTextEntry->setTentative(false);
+ mHasAutocompletedText = true;
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
+ }
+ else // no matching items found
+ {
+ mList->deselectAllItems();
+ mTextEntry->setText(wstring_to_utf8str(user_wstring)); // removes text added by autocompletion
+ mTextEntry->setTentative(mTextEntryTentative);
+ mHasAutocompletedText = false;
+ mLastSelectedIndex = -1;
+ }
+}
+
+void LLComboBox::onTextCommit(const LLSD& data)
+{
+ std::string text = mTextEntry->getText();
+ setSimple(text);
+ onCommit();
+ mTextEntry->selectAll();
+}
+
+void LLComboBox::setFocus(bool b)
+{
+ LLUICtrl::setFocus(b);
+
+ if (b)
+ {
+ mList->clearSearchString();
+ if (mList->getVisible())
+ {
+ mList->setFocus(true);
+ }
+ }
+}
+
+void LLComboBox::prearrangeList(std::string filter)
+{
+ if (mPrearrangeCallback)
+ {
+ mPrearrangeCallback(this, LLSD(filter));
+ }
+}
+
+
+//============================================================================
+// ll::ui::SearchableControl functions
+
+//virtual
+std::string LLComboBox::_getSearchText() const
+{
+ std::string res;
+ if (mList)
+ {
+ // getAllData returns a full copy of content, might be a
+ // better option to implement an mList->getSearchText(column)
+ std::vector<LLScrollListItem*> data = mList->getAllData();
+ std::vector<LLScrollListItem*>::iterator iter = data.begin();
+ while (iter != data.end())
+ {
+ LLScrollListCell* cell = (*iter)->getColumn(0);
+ if (cell)
+ {
+ std::string whitelist_url = cell->getValue().asString();
+ res += cell->getValue().asString();
+ }
+ iter++;
+ }
+ }
+ return res + getToolTip();
+}
+
+//virtual
+void LLComboBox::onSetHighlight() const
+{
+ if (mButton)
+ {
+ mButton->ll::ui::SearchableControl::setHighlighted(ll::ui::SearchableControl::getHighlighted());
+ }
+}
+
+void LLComboBox::imageLoaded()
+{
+ if (mAllowTextEntry)
+ {
+ LLRect rect = getLocalRect();
+ S32 arrow_width = mArrowImage ? mArrowImage->getWidth() : 0;
+ S32 shadow_size = BTN_DROP_SHADOW;
+ mButton->setRect(LLRect(getRect().getWidth() - llmax(8, arrow_width) - 2 * shadow_size,
+ rect.mTop, rect.mRight, rect.mBottom));
+ if (mButton->getVisible())
+ {
+ // recalculate field size
+ if (mTextEntry)
+ {
+ LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
+ text_entry_rect.mRight -= llmax(8, arrow_width) + 2 * BTN_DROP_SHADOW;
+ mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), true);
+ }
+ }
+ }
+}
+
+//============================================================================
+// LLCtrlListInterface functions
+
+S32 LLComboBox::getItemCount() const
+{
+ return mList->getItemCount();
+}
+
+void LLComboBox::addColumn(const LLSD& column, EAddPosition pos)
+{
+ mList->clearColumns();
+ mList->addColumn(column, pos);
+}
+
+void LLComboBox::clearColumns()
+{
+ mList->clearColumns();
+}
+
+void LLComboBox::setColumnLabel(const std::string& column, const std::string& label)
+{
+ mList->setColumnLabel(column, label);
+}
+
+LLScrollListItem* LLComboBox::addElement(const LLSD& value, EAddPosition pos, void* userdata)
+{
+ return mList->addElement(value, pos, userdata);
+}
+
+LLScrollListItem* LLComboBox::addSimpleElement(const std::string& value, EAddPosition pos, const LLSD& id)
+{
+ return mList->addSimpleElement(value, pos, id);
+}
+
+void LLComboBox::clearRows()
+{
+ mList->clearRows();
+}
+
+void LLComboBox::sortByColumn(const std::string& name, bool ascending)
+{
+ mList->sortByColumn(name, ascending);
+}
+
+//============================================================================
+//LLCtrlSelectionInterface functions
+
+bool LLComboBox::setCurrentByID(const LLUUID& id)
+{
+ bool found = mList->selectByID( id );
+
+ if (found)
+ {
+ setLabel(getSelectedItemLabel());
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
+ }
+
+ return found;
+}
+
+LLUUID LLComboBox::getCurrentID() const
+{
+ return mList->getStringUUIDSelectedItem();
+}
+bool LLComboBox::setSelectedByValue(const LLSD& value, bool selected)
+{
+ bool found = mList->setSelectedByValue(value, selected);
+ if (found)
+ {
+ setLabel(getSelectedItemLabel());
+ }
+ return found;
+}
+
+LLSD LLComboBox::getSelectedValue()
+{
+ return mList->getSelectedValue();
+}
+
+bool LLComboBox::isSelected(const LLSD& value) const
+{
+ return mList->isSelected(value);
+}
+
+bool LLComboBox::operateOnSelection(EOperation op)
+{
+ if (op == OP_DELETE)
+ {
+ mList->deleteSelectedItems();
+ return true;
+ }
+ return false;
+}
+
+bool LLComboBox::operateOnAll(EOperation op)
+{
+ if (op == OP_DELETE)
+ {
+ clearRows();
+ return true;
+ }
+ return false;
+}
+
+bool LLComboBox::selectItemRange( S32 first, S32 last )
+{
+ return mList->selectItemRange(first, last);
+}
+
+
+static LLDefaultChildRegistry::Register<LLIconsComboBox> register_icons_combo_box("icons_combo_box");
+
+LLIconsComboBox::Params::Params()
+: icon_column("icon_column", ICON_COLUMN),
+ label_column("label_column", LABEL_COLUMN)
+{}
+
+LLIconsComboBox::LLIconsComboBox(const LLIconsComboBox::Params& p)
+: LLComboBox(p),
+ mIconColumnIndex(p.icon_column),
+ mLabelColumnIndex(p.label_column)
+{}
+
+const std::string LLIconsComboBox::getSelectedItemLabel(S32 column) const
+{
+ mButton->setImageOverlay(LLComboBox::getSelectedItemLabel(mIconColumnIndex), mButton->getImageOverlayHAlign());
+
+ return LLComboBox::getSelectedItemLabel(mLabelColumnIndex);
+}
diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h
index 97ac6653d5..dd827fb548 100644
--- a/indra/llui/llcombobox.h
+++ b/indra/llui/llcombobox.h
@@ -1,284 +1,284 @@
-/**
- * @file llcombobox.h
- * @brief LLComboBox 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$
- */
-
-// A control that displays the name of the chosen item, which when clicked
-// shows a scrolling box of choices.
-
-#ifndef LL_LLCOMBOBOX_H
-#define LL_LLCOMBOBOX_H
-
-#include "llbutton.h"
-#include "lluictrl.h"
-#include "llctrlselectioninterface.h"
-#include "llrect.h"
-#include "llscrolllistctrl.h"
-#include "lllineeditor.h"
-#include <boost/function.hpp>
-
-// Classes
-
-class LLFontGL;
-class LLViewBorder;
-
-class LLComboBox
-: public LLUICtrl
-, public LLCtrlListInterface
-, public ll::ui::SearchableControl
-{
-public:
- typedef enum e_preferred_position
- {
- ABOVE,
- BELOW
- } EPreferredPosition;
-
- struct PreferredPositionValues : public LLInitParam::TypeValuesHelper<EPreferredPosition, PreferredPositionValues>
- {
- static void declareValues();
- };
-
-
- struct ItemParams : public LLInitParam::Block<ItemParams, LLScrollListItem::Params>
- {
- Optional<std::string> label;
- ItemParams();
- };
-
- struct Params
- : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Optional<bool> allow_text_entry,
- show_text_as_tentative,
- allow_new_values;
- Optional<S32> max_chars;
- Optional<commit_callback_t> prearrange_callback,
- text_entry_callback,
- text_changed_callback;
-
- Optional<EPreferredPosition, PreferredPositionValues> list_position;
-
- // components
- Optional<LLButton::Params> combo_button;
- Optional<LLScrollListCtrl::Params> combo_list;
- Optional<LLLineEditor::Params> combo_editor;
-
- Optional<LLButton::Params> drop_down_button;
-
- Multiple<ItemParams> items;
-
- Params();
- };
-
-
- virtual ~LLComboBox();
- /*virtual*/ bool postBuild();
-
-protected:
- friend class LLUICtrlFactory;
- LLComboBox(const Params&);
- void initFromParams(const Params&);
- void prearrangeList(std::string filter = "");
-
- virtual std::string _getSearchText() const;
- virtual void onSetHighlight() const;
-
- void imageLoaded();
-
-public:
- // LLView interface
- virtual void onFocusLost();
-
- virtual bool handleToolTip(S32 x, S32 y, MASK mask);
- virtual bool handleKeyHere(KEY key, MASK mask);
- virtual bool handleUnicodeCharHere(llwchar uni_char);
-
- // LLUICtrl interface
- virtual void clear(); // select nothing
- virtual void onCommit();
- virtual bool acceptsTextInput() const { return mAllowTextEntry; }
- virtual bool isDirty() const; // Returns true if the user has modified this control.
- virtual void resetDirty(); // Clear dirty state
-
- virtual void setFocus(bool b);
-
- // Selects item by underlying LLSD value, using LLSD::asString() matching.
- // For simple items, this is just the name of the label.
- virtual void setValue(const LLSD& value );
-
- // Gets underlying LLSD value for currently selected items. For simple
- // items, this is just the label.
- virtual LLSD getValue() const;
-
- void setTextEntry(const LLStringExplicit& text);
- void setKeystrokeOnEsc(bool enable);
-
- LLScrollListItem* add(const std::string& name, EAddPosition pos = ADD_BOTTOM, bool enabled = true); // add item "name" to menu
- LLScrollListItem* add(const std::string& name, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, bool enabled = true);
- LLScrollListItem* add(const std::string& name, void* userdata, EAddPosition pos = ADD_BOTTOM, bool enabled = true);
- LLScrollListItem* add(const std::string& name, LLSD value, EAddPosition pos = ADD_BOTTOM, bool enabled = true);
- 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
-
- // Select current item by name using selectItemByLabel. Returns false if not found.
- bool setSimple(const LLStringExplicit& name);
- // Get name of current item. Returns an empty string if not found.
- const std::string getSimple() const;
- // Get contents of column x of selected row
- virtual const std::string getSelectedItemLabel(S32 column = 0) const;
-
- // Sets the label, which doesn't have to exist in the label.
- // This is probably a UI abuse.
- 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 );
- S32 getCurrentIndex() const;
-
- void setEnabledByValue(const LLSD& value, bool enabled);
-
- void createLineEditor(const Params&);
-
- //========================================================================
- LLCtrlSelectionInterface* getSelectionInterface() { return (LLCtrlSelectionInterface*)this; };
- LLCtrlListInterface* getListInterface() { return (LLCtrlListInterface*)this; };
-
- // LLCtrlListInterface functions
- // See llscrolllistctrl.h
- virtual S32 getItemCount() const;
- // Overwrites the default column (See LLScrollListCtrl for format)
- virtual void addColumn(const LLSD& column, EAddPosition pos = ADD_BOTTOM);
- virtual void clearColumns();
- virtual void setColumnLabel(const std::string& column, const std::string& label);
- virtual LLScrollListItem* addElement(const LLSD& value, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL);
- virtual LLScrollListItem* addSimpleElement(const std::string& value, EAddPosition pos = ADD_BOTTOM, const LLSD& id = LLSD());
- virtual void clearRows();
- virtual void sortByColumn(const std::string& name, bool ascending);
-
- // LLCtrlSelectionInterface functions
- virtual bool getCanSelect() const { return true; }
- virtual bool selectFirstItem() { return setCurrentByIndex(0); }
- virtual bool selectNthItem( S32 index ) { return setCurrentByIndex(index); }
- virtual bool selectItemRange( S32 first, S32 last );
- virtual S32 getFirstSelectedIndex() const { return getCurrentIndex(); }
- virtual bool setCurrentByID( const LLUUID& id );
- virtual LLUUID getCurrentID() const; // LLUUID::null if no items in menu
- virtual bool setSelectedByValue(const LLSD& value, bool selected);
- virtual LLSD getSelectedValue();
- virtual bool isSelected(const LLSD& value) const;
- virtual bool operateOnSelection(EOperation op);
- virtual bool operateOnAll(EOperation op);
-
- //========================================================================
-
- 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();
- void onListMouseUp();
- void onItemSelected(const LLSD& data);
- void onTextCommit(const LLSD& data);
-
- void updateSelection();
- virtual void showList();
- virtual void hideList();
-
- virtual void onTextEntry(LLLineEditor* line_editor);
-
-protected:
- LLButton* mButton;
- LLLineEditor* mTextEntry;
- LLScrollListCtrl* mList;
- EPreferredPosition mListPosition;
- LLPointer<LLUIImage> mArrowImage;
- LLUIString mLabel;
- bool mHasAutocompletedText;
-
-private:
- bool mAllowTextEntry;
- bool mAllowNewValues;
- S32 mMaxChars;
- bool mTextEntryTentative;
- commit_callback_t mPrearrangeCallback;
- commit_callback_t mTextEntryCallback;
- commit_callback_t mTextChangedCallback;
- commit_callback_t mSelectionCallback;
- boost::signals2::connection mTopLostSignalConnection;
- boost::signals2::connection mImageLoadedConnection;
- commit_signal_t mOnReturnSignal;
- S32 mLastSelectedIndex;
-};
-
-// A combo box with icons for the list of items.
-class LLIconsComboBox
-: public LLComboBox
-{
-public:
- struct Params
- : public LLInitParam::Block<Params, LLComboBox::Params>
- {
- Optional<S32> icon_column,
- label_column;
- Params();
- };
-
- /*virtual*/ const std::string getSelectedItemLabel(S32 column = 0) const;
-
-private:
- enum EColumnIndex
- {
- ICON_COLUMN = 0,
- LABEL_COLUMN
- };
-
- friend class LLUICtrlFactory;
- LLIconsComboBox(const Params&);
- virtual ~LLIconsComboBox() {};
-
- S32 mIconColumnIndex;
- S32 mLabelColumnIndex;
-};
-
-#endif
+/**
+ * @file llcombobox.h
+ * @brief LLComboBox 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$
+ */
+
+// A control that displays the name of the chosen item, which when clicked
+// shows a scrolling box of choices.
+
+#ifndef LL_LLCOMBOBOX_H
+#define LL_LLCOMBOBOX_H
+
+#include "llbutton.h"
+#include "lluictrl.h"
+#include "llctrlselectioninterface.h"
+#include "llrect.h"
+#include "llscrolllistctrl.h"
+#include "lllineeditor.h"
+#include <boost/function.hpp>
+
+// Classes
+
+class LLFontGL;
+class LLViewBorder;
+
+class LLComboBox
+: public LLUICtrl
+, public LLCtrlListInterface
+, public ll::ui::SearchableControl
+{
+public:
+ typedef enum e_preferred_position
+ {
+ ABOVE,
+ BELOW
+ } EPreferredPosition;
+
+ struct PreferredPositionValues : public LLInitParam::TypeValuesHelper<EPreferredPosition, PreferredPositionValues>
+ {
+ static void declareValues();
+ };
+
+
+ struct ItemParams : public LLInitParam::Block<ItemParams, LLScrollListItem::Params>
+ {
+ Optional<std::string> label;
+ ItemParams();
+ };
+
+ struct Params
+ : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<bool> allow_text_entry,
+ show_text_as_tentative,
+ allow_new_values;
+ Optional<S32> max_chars;
+ Optional<commit_callback_t> prearrange_callback,
+ text_entry_callback,
+ text_changed_callback;
+
+ Optional<EPreferredPosition, PreferredPositionValues> list_position;
+
+ // components
+ Optional<LLButton::Params> combo_button;
+ Optional<LLScrollListCtrl::Params> combo_list;
+ Optional<LLLineEditor::Params> combo_editor;
+
+ Optional<LLButton::Params> drop_down_button;
+
+ Multiple<ItemParams> items;
+
+ Params();
+ };
+
+
+ virtual ~LLComboBox();
+ /*virtual*/ bool postBuild();
+
+protected:
+ friend class LLUICtrlFactory;
+ LLComboBox(const Params&);
+ void initFromParams(const Params&);
+ void prearrangeList(std::string filter = "");
+
+ virtual std::string _getSearchText() const;
+ virtual void onSetHighlight() const;
+
+ void imageLoaded();
+
+public:
+ // LLView interface
+ virtual void onFocusLost();
+
+ virtual bool handleToolTip(S32 x, S32 y, MASK mask);
+ virtual bool handleKeyHere(KEY key, MASK mask);
+ virtual bool handleUnicodeCharHere(llwchar uni_char);
+
+ // LLUICtrl interface
+ virtual void clear(); // select nothing
+ virtual void onCommit();
+ virtual bool acceptsTextInput() const { return mAllowTextEntry; }
+ virtual bool isDirty() const; // Returns true if the user has modified this control.
+ virtual void resetDirty(); // Clear dirty state
+
+ virtual void setFocus(bool b);
+
+ // Selects item by underlying LLSD value, using LLSD::asString() matching.
+ // For simple items, this is just the name of the label.
+ virtual void setValue(const LLSD& value );
+
+ // Gets underlying LLSD value for currently selected items. For simple
+ // items, this is just the label.
+ virtual LLSD getValue() const;
+
+ void setTextEntry(const LLStringExplicit& text);
+ void setKeystrokeOnEsc(bool enable);
+
+ LLScrollListItem* add(const std::string& name, EAddPosition pos = ADD_BOTTOM, bool enabled = true); // add item "name" to menu
+ LLScrollListItem* add(const std::string& name, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, bool enabled = true);
+ LLScrollListItem* add(const std::string& name, void* userdata, EAddPosition pos = ADD_BOTTOM, bool enabled = true);
+ LLScrollListItem* add(const std::string& name, LLSD value, EAddPosition pos = ADD_BOTTOM, bool enabled = true);
+ 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
+
+ // Select current item by name using selectItemByLabel. Returns false if not found.
+ bool setSimple(const LLStringExplicit& name);
+ // Get name of current item. Returns an empty string if not found.
+ const std::string getSimple() const;
+ // Get contents of column x of selected row
+ virtual const std::string getSelectedItemLabel(S32 column = 0) const;
+
+ // Sets the label, which doesn't have to exist in the label.
+ // This is probably a UI abuse.
+ 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 );
+ S32 getCurrentIndex() const;
+
+ void setEnabledByValue(const LLSD& value, bool enabled);
+
+ void createLineEditor(const Params&);
+
+ //========================================================================
+ LLCtrlSelectionInterface* getSelectionInterface() { return (LLCtrlSelectionInterface*)this; };
+ LLCtrlListInterface* getListInterface() { return (LLCtrlListInterface*)this; };
+
+ // LLCtrlListInterface functions
+ // See llscrolllistctrl.h
+ virtual S32 getItemCount() const;
+ // Overwrites the default column (See LLScrollListCtrl for format)
+ virtual void addColumn(const LLSD& column, EAddPosition pos = ADD_BOTTOM);
+ virtual void clearColumns();
+ virtual void setColumnLabel(const std::string& column, const std::string& label);
+ virtual LLScrollListItem* addElement(const LLSD& value, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL);
+ virtual LLScrollListItem* addSimpleElement(const std::string& value, EAddPosition pos = ADD_BOTTOM, const LLSD& id = LLSD());
+ virtual void clearRows();
+ virtual void sortByColumn(const std::string& name, bool ascending);
+
+ // LLCtrlSelectionInterface functions
+ virtual bool getCanSelect() const { return true; }
+ virtual bool selectFirstItem() { return setCurrentByIndex(0); }
+ virtual bool selectNthItem( S32 index ) { return setCurrentByIndex(index); }
+ virtual bool selectItemRange( S32 first, S32 last );
+ virtual S32 getFirstSelectedIndex() const { return getCurrentIndex(); }
+ virtual bool setCurrentByID( const LLUUID& id );
+ virtual LLUUID getCurrentID() const; // LLUUID::null if no items in menu
+ virtual bool setSelectedByValue(const LLSD& value, bool selected);
+ virtual LLSD getSelectedValue();
+ virtual bool isSelected(const LLSD& value) const;
+ virtual bool operateOnSelection(EOperation op);
+ virtual bool operateOnAll(EOperation op);
+
+ //========================================================================
+
+ 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();
+ void onListMouseUp();
+ void onItemSelected(const LLSD& data);
+ void onTextCommit(const LLSD& data);
+
+ void updateSelection();
+ virtual void showList();
+ virtual void hideList();
+
+ virtual void onTextEntry(LLLineEditor* line_editor);
+
+protected:
+ LLButton* mButton;
+ LLLineEditor* mTextEntry;
+ LLScrollListCtrl* mList;
+ EPreferredPosition mListPosition;
+ LLPointer<LLUIImage> mArrowImage;
+ LLUIString mLabel;
+ bool mHasAutocompletedText;
+
+private:
+ bool mAllowTextEntry;
+ bool mAllowNewValues;
+ S32 mMaxChars;
+ bool mTextEntryTentative;
+ commit_callback_t mPrearrangeCallback;
+ commit_callback_t mTextEntryCallback;
+ commit_callback_t mTextChangedCallback;
+ commit_callback_t mSelectionCallback;
+ boost::signals2::connection mTopLostSignalConnection;
+ boost::signals2::connection mImageLoadedConnection;
+ commit_signal_t mOnReturnSignal;
+ S32 mLastSelectedIndex;
+};
+
+// A combo box with icons for the list of items.
+class LLIconsComboBox
+: public LLComboBox
+{
+public:
+ struct Params
+ : public LLInitParam::Block<Params, LLComboBox::Params>
+ {
+ Optional<S32> icon_column,
+ label_column;
+ Params();
+ };
+
+ /*virtual*/ const std::string getSelectedItemLabel(S32 column = 0) const;
+
+private:
+ enum EColumnIndex
+ {
+ ICON_COLUMN = 0,
+ LABEL_COLUMN
+ };
+
+ friend class LLUICtrlFactory;
+ LLIconsComboBox(const Params&);
+ virtual ~LLIconsComboBox() {};
+
+ S32 mIconColumnIndex;
+ S32 mLabelColumnIndex;
+};
+
+#endif
diff --git a/indra/llui/llcommandmanager.cpp b/indra/llui/llcommandmanager.cpp
index 8ef7bd837f..270ec86e01 100644
--- a/indra/llui/llcommandmanager.cpp
+++ b/indra/llui/llcommandmanager.cpp
@@ -1,25 +1,25 @@
-/**
+/**
* @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$
*/
@@ -46,43 +46,43 @@ const LLCommandId LLCommandId::null = LLCommandId("null command");
//
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)
+ : 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)
+ : 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)
{
}
@@ -97,95 +97,95 @@ LLCommandManager::LLCommandManager()
LLCommandManager::~LLCommandManager()
{
- for (CommandVector::iterator cmdIt = mCommands.begin(); cmdIt != mCommands.end(); ++cmdIt)
- {
- LLCommand * command = *cmdIt;
+ for (CommandVector::iterator cmdIt = mCommands.begin(); cmdIt != mCommands.end(); ++cmdIt)
+ {
+ LLCommand * command = *cmdIt;
- delete command;
- }
+ delete command;
+ }
}
U32 LLCommandManager::commandCount() const
{
- return mCommands.size();
+ return mCommands.size();
}
LLCommand * LLCommandManager::getCommand(U32 commandIndex)
{
- return mCommands[commandIndex];
+ return mCommands[commandIndex];
}
LLCommand * LLCommandManager::getCommand(const LLCommandId& commandId)
{
- LLCommand * command_match = NULL;
+ LLCommand * command_match = NULL;
+
+ CommandIndexMap::const_iterator found = mCommandIndices.find(commandId.uuid());
- CommandIndexMap::const_iterator found = mCommandIndices.find(commandId.uuid());
-
- if (found != mCommandIndices.end())
- {
- command_match = mCommands[found->second];
- }
+ if (found != mCommandIndices.end())
+ {
+ command_match = mCommands[found->second];
+ }
- return command_match;
+ return command_match;
}
LLCommand * LLCommandManager::getCommand(const std::string& name)
{
- LLCommand * command_match = NULL;
-
- CommandVector::const_iterator it = mCommands.begin();
-
- while (it != mCommands.end())
- {
+ LLCommand * command_match = NULL;
+
+ CommandVector::const_iterator it = mCommands.begin();
+
+ while (it != mCommands.end())
+ {
if ((*it)->name() == name)
{
command_match = *it;
break;
}
it++;
- }
-
- return command_match;
+ }
+
+ return command_match;
}
void LLCommandManager::addCommand(LLCommand * command)
{
- LLCommandId command_id = command->id();
- mCommandIndices[command_id.uuid()] = mCommands.size();
- mCommands.push_back(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;
+ LL_DEBUGS() << "Successfully added command: " << command->name() << LL_ENDL;
}
//static
bool LLCommandManager::load()
{
- LLCommandManager& mgr = LLCommandManager::instance();
+ LLCommandManager& mgr = LLCommandManager::instance();
+
+ std::string commands_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "commands.xml");
- std::string commands_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "commands.xml");
+ LLCommandManager::Params commandsParams;
- LLCommandManager::Params commandsParams;
+ LLSimpleXUIParser parser;
- LLSimpleXUIParser parser;
-
- if (!parser.readXUI(commands_file, commandsParams))
- {
- LL_ERRS() << "Unable to load xml file: " << commands_file << LL_ENDL;
- return false;
- }
+ 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;
- }
+ if (!commandsParams.validateBlock())
+ {
+ LL_ERRS() << "Invalid commands file: " << commands_file << LL_ENDL;
+ return false;
+ }
- for (const LLCommand::Params& commandParams : commandsParams.commands)
- {
- LLCommand * command = new LLCommand(commandParams);
+ for (const LLCommand::Params& commandParams : commandsParams.commands)
+ {
+ LLCommand * command = new LLCommand(commandParams);
- mgr.addCommand(command);
- }
+ mgr.addCommand(command);
+ }
- return true;
+ return true;
}
diff --git a/indra/llui/llcommandmanager.h b/indra/llui/llcommandmanager.h
index 8cec5e2b24..3b2586a5a1 100644
--- a/indra/llui/llcommandmanager.h
+++ b/indra/llui/llcommandmanager.h
@@ -1,25 +1,25 @@
-/**
+/**
* @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$
*/
@@ -38,48 +38,48 @@ 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;
+ 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;
+ LLUUID mUUID;
};
typedef std::list<LLCommandId> command_id_list_t;
@@ -88,121 +88,121 @@ 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;
+ 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;
+ 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> execute_stop_function;
+ Optional<LLSD> execute_stop_parameters;
- Optional<std::string> is_running_function;
- Optional<LLSD> is_running_parameters;
+ Optional<std::string> is_enabled_function;
+ Optional<LLSD> is_enabled_parameters;
- Optional<std::string> is_starting_function;
- Optional<LLSD> is_starting_parameters;
+ Optional<std::string> is_running_function;
+ Optional<LLSD> is_running_parameters;
- Optional<bool> is_flashing_allowed;
+ Optional<std::string> is_starting_function;
+ Optional<LLSD> is_starting_parameters;
- Params();
- };
+ Optional<bool> is_flashing_allowed;
- LLCommand(const LLCommand::Params& p);
+ Params();
+ };
- 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; }
+ LLCommand(const LLCommand::Params& p);
- const std::string& executeFunctionName() const { return mExecuteFunction; }
- const LLSD& executeParameters() const { return mExecuteParameters; }
+ 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& 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& executeFunctionName() const { return mExecuteFunction; }
+ const LLSD& executeParameters() const { return mExecuteParameters; }
- const std::string& isRunningFunctionName() const { return mIsRunningFunction; }
- const LLSD& isRunningParameters() const { return mIsRunningParameters; }
+ const std::string& executeStopFunctionName() const { return mExecuteStopFunction; }
+ const LLSD& executeStopParameters() const { return mExecuteStopParameters; }
- const std::string& isStartingFunctionName() const { return mIsStartingFunction; }
- const LLSD& isStartingParameters() const { return mIsStartingParameters; }
+ const std::string& isEnabledFunctionName() const { return mIsEnabledFunction; }
+ const LLSD& isEnabledParameters() const { return mIsEnabledParameters; }
- bool isFlashingAllowed() const { return mIsFlashingAllowed; }
+ 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;
+ LLCommandId mIdentifier;
- bool mAvailableInToybox;
- std::string mIcon;
- std::string mLabelRef;
- std::string mName;
- std::string mTooltipRef;
+ bool mAvailableInToybox;
+ std::string mIcon;
+ std::string mLabelRef;
+ std::string mName;
+ std::string mTooltipRef;
- std::string mExecuteFunction;
- LLSD mExecuteParameters;
+ std::string mExecuteFunction;
+ LLSD mExecuteParameters;
- std::string mExecuteStopFunction;
- LLSD mExecuteStopParameters;
-
- std::string mIsEnabledFunction;
- LLSD mIsEnabledParameters;
+ std::string mExecuteStopFunction;
+ LLSD mExecuteStopParameters;
- std::string mIsRunningFunction;
- LLSD mIsRunningParameters;
+ std::string mIsEnabledFunction;
+ LLSD mIsEnabledParameters;
- std::string mIsStartingFunction;
- LLSD mIsStartingParameters;
+ std::string mIsRunningFunction;
+ LLSD mIsRunningParameters;
- bool mIsFlashingAllowed;
+ std::string mIsStartingFunction;
+ LLSD mIsStartingParameters;
+
+ bool mIsFlashingAllowed;
};
class LLCommandManager
-: public LLSingleton<LLCommandManager>
+: public LLSingleton<LLCommandManager>
{
- LLSINGLETON(LLCommandManager);
- ~LLCommandManager();
+ LLSINGLETON(LLCommandManager);
+ ~LLCommandManager();
public:
- struct Params : public LLInitParam::Block<Params>
- {
- Multiple< LLCommand::Params, AtLeast<1> > commands;
+ struct Params : public LLInitParam::Block<Params>
+ {
+ Multiple< LLCommand::Params, AtLeast<1> > commands;
- Params()
- : commands("command")
- {
- }
- };
+ Params()
+ : commands("command")
+ {
+ }
+ };
- U32 commandCount() const;
- LLCommand * getCommand(U32 commandIndex);
- LLCommand * getCommand(const LLCommandId& commandId);
- LLCommand * getCommand(const std::string& name);
+ U32 commandCount() const;
+ LLCommand * getCommand(U32 commandIndex);
+ LLCommand * getCommand(const LLCommandId& commandId);
+ LLCommand * getCommand(const std::string& name);
- static bool load();
+ static bool load();
protected:
- void addCommand(LLCommand * command);
+ void addCommand(LLCommand * command);
private:
- typedef std::map<LLUUID, U32> CommandIndexMap;
- typedef std::vector<LLCommand *> CommandVector;
-
- CommandVector mCommands;
- CommandIndexMap mCommandIndices;
+ typedef std::map<LLUUID, U32> CommandIndexMap;
+ typedef std::vector<LLCommand *> CommandVector;
+
+ CommandVector mCommands;
+ CommandIndexMap mCommandIndices;
};
diff --git a/indra/llui/llconsole.cpp b/indra/llui/llconsole.cpp
index c9d5f0bf80..4c163b10b0 100644
--- a/indra/llui/llconsole.cpp
+++ b/indra/llui/llconsole.cpp
@@ -1,403 +1,403 @@
-/**
- * @file llconsole.cpp
- * @brief a scrolling console output device
- *
- * $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 "llviewerprecompiledheaders.h"
-#include "linden_common.h"
-
-#include "llconsole.h"
-
-// linden library includes
-#include "llmath.h"
-//#include "llviewercontrol.h"
-#include "llcriticaldamp.h"
-#include "llfontgl.h"
-#include "llgl.h"
-#include "llui.h"
-#include "lluiimage.h"
-//#include "llviewerimage.h"
-//#include "llviewerimagelist.h"
-//#include "llviewerwindow.h"
-#include "llsd.h"
-#include "llfontgl.h"
-#include "llmath.h"
-
-//#include "llstartup.h"
-
-// Used for LCD display
-extern void AddNewDebugConsoleToLCD(const LLWString &newLine);
-
-LLConsole* gConsole = NULL; // Created and destroyed in LLViewerWindow.
-
-const F32 FADE_DURATION = 2.f;
-
-static LLDefaultChildRegistry::Register<LLConsole> r("console");
-
-LLConsole::LLConsole(const LLConsole::Params& p)
-: LLUICtrl(p),
- LLFixedBuffer(p.max_lines),
- mLinePersistTime(p.persist_time), // seconds
- mFont(p.font),
- mConsoleWidth(0),
- mConsoleHeight(0)
-{
- if (p.font_size_index.isProvided())
- {
- setFontSize(p.font_size_index);
- }
- mFadeTime = mLinePersistTime - FADE_DURATION;
- setMaxLines(LLUI::getInstance()->mSettingGroups["config"]->getS32("ConsoleMaxLines"));
-}
-
-void LLConsole::setLinePersistTime(F32 seconds)
-{
- mLinePersistTime = seconds;
- mFadeTime = mLinePersistTime - FADE_DURATION;
-}
-
-void LLConsole::reshape(S32 width, S32 height, bool called_from_parent)
-{
- S32 new_width = llmax(50, llmin(getRect().getWidth(), width));
- S32 new_height = llmax(llfloor(mFont->getLineHeight()) + 15, llmin(getRect().getHeight(), height));
-
- if ( mConsoleWidth == new_width
- && mConsoleHeight == new_height )
- {
- return;
- }
-
- mConsoleWidth = new_width;
- mConsoleHeight= new_height;
-
- LLUICtrl::reshape(new_width, new_height, called_from_parent);
-
- for(paragraph_t::iterator paragraph_it = mParagraphs.begin(); paragraph_it != mParagraphs.end(); paragraph_it++)
- {
- (*paragraph_it).updateLines((F32)getRect().getWidth(), mFont, true);
- }
-}
-
-void LLConsole::setFontSize(S32 size_index)
-{
- if (-1 == size_index)
- {
- mFont = LLFontGL::getFontMonospace();
- }
- else if (0 == size_index)
- {
- mFont = LLFontGL::getFontSansSerif();
- }
- else if (1 == size_index)
- {
- mFont = LLFontGL::getFontSansSerifBig();
- }
- else
- {
- mFont = LLFontGL::getFontSansSerifHuge();
- }
- // Make sure the font exists
- if (mFont == NULL)
- {
- mFont = LLFontGL::getFontDefault();
- }
-
- for(paragraph_t::iterator paragraph_it = mParagraphs.begin(); paragraph_it != mParagraphs.end(); paragraph_it++)
- {
- (*paragraph_it).updateLines((F32)getRect().getWidth(), mFont, true);
- }
-}
-
-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
- F32 cur_time = mTimer.getElapsedTimeF32();
-
- F32 skip_time = cur_time - mLinePersistTime;
- F32 fade_time = cur_time - mFadeTime;
-
- if (mParagraphs.empty()) //No text to draw.
- {
- return;
- }
-
- U32 num_lines=0;
-
- paragraph_t::reverse_iterator paragraph_it;
- paragraph_it = mParagraphs.rbegin();
- U32 paragraph_num=mParagraphs.size();
-
- while (!mParagraphs.empty() && paragraph_it != mParagraphs.rend())
- {
- num_lines += (*paragraph_it).mLines.size();
- if(num_lines > mMaxLines
- || ( (mLinePersistTime > (F32)0.f) && ((*paragraph_it).mAddTime - skip_time)/(mLinePersistTime - mFadeTime) <= (F32)0.f))
- { //All lines above here are done. Lose them.
- for (U32 i=0;i<paragraph_num;i++)
- {
- if (!mParagraphs.empty())
- mParagraphs.pop_front();
- }
- break;
- }
- paragraph_num--;
- paragraph_it++;
- }
-
- if (mParagraphs.empty())
- {
- return;
- }
-
- // draw remaining lines
- F32 y_pos = 0.f;
-
- LLUIImagePtr imagep = LLUI::getUIImage("transparent");
-
- static LLCachedControl<F32> console_bg_opacity(*LLUI::getInstance()->mSettingGroups["config"], "ConsoleBackgroundOpacity", 0.7f);
- F32 console_opacity = llclamp(console_bg_opacity(), 0.f, 1.f);
-
- LLColor4 color = LLUIColorTable::instance().getColor("ConsoleBackground");
- color.mV[VALPHA] *= console_opacity;
-
- F32 line_height = mFont->getLineHeight();
-
- for(paragraph_it = mParagraphs.rbegin(); paragraph_it != mParagraphs.rend(); paragraph_it++)
- {
- 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);
-
- F32 y_off=0;
-
- F32 alpha;
-
- if ((mLinePersistTime > 0.f) && ((*paragraph_it).mAddTime < fade_time))
- {
- alpha = ((*paragraph_it).mAddTime - skip_time)/(mLinePersistTime - mFadeTime);
- }
- else
- {
- alpha = 1.0f;
- }
-
- if( alpha > 0.f )
- {
- for (lines_t::iterator line_it=(*paragraph_it).mLines.begin();
- line_it != (*paragraph_it).mLines.end();
- line_it ++)
- {
- for (line_color_segments_t::iterator seg_it = (*line_it).mLineColorSegments.begin();
- seg_it != (*line_it).mLineColorSegments.end();
- seg_it++)
- {
- mFont->render((*seg_it).mText, 0, (*seg_it).mXPosition - 8, y_pos - y_off,
- LLColor4(
- (*seg_it).mColor.mV[VRED],
- (*seg_it).mColor.mV[VGREEN],
- (*seg_it).mColor.mV[VBLUE],
- (*seg_it).mColor.mV[VALPHA]*alpha),
- LLFontGL::LEFT,
- LLFontGL::BASELINE,
- LLFontGL::NORMAL,
- LLFontGL::DROP_SHADOW,
- S32_MAX,
- target_width
- );
- }
- y_off += line_height;
- }
- }
- y_pos += padding_vertical;
- }
-}
-
-//Generate highlight color segments for this paragraph. Pass in default color of paragraph.
-void LLConsole::Paragraph::makeParagraphColorSegments (const LLColor4 &color)
-{
- LLSD paragraph_color_segments;
- paragraph_color_segments[0]["text"] =wstring_to_utf8str(mParagraphText);
- LLSD color_sd = color.getValue();
- paragraph_color_segments[0]["color"]=color_sd;
-
- for(LLSD::array_const_iterator color_segment_it = paragraph_color_segments.beginArray();
- color_segment_it != paragraph_color_segments.endArray();
- ++color_segment_it)
- {
- LLSD color_llsd = (*color_segment_it)["color"];
- std::string color_str = (*color_segment_it)["text"].asString();
-
- ParagraphColorSegment color_segment;
-
- color_segment.mColor.setValue(color_llsd);
- color_segment.mNumChars = color_str.length();
-
- mParagraphColorSegments.push_back(color_segment);
- }
-}
-
-//Called when a paragraph is added to the console or window is resized.
-void LLConsole::Paragraph::updateLines(F32 screen_width, const LLFontGL* font, bool force_resize)
-{
- if ( !force_resize )
- {
- if ( mMaxWidth >= 0.0f
- && mMaxWidth < screen_width )
- {
- return; //No resize required.
- }
- }
-
- screen_width = screen_width - 30; //Margin for small windows.
-
- if ( mParagraphText.empty()
- || mParagraphColorSegments.empty()
- || font == NULL)
- {
- return; //Not enough info to complete.
- }
-
- mLines.clear(); //Chuck everything.
- mMaxWidth = 0.0f;
-
- paragraph_color_segments_t::iterator current_color = mParagraphColorSegments.begin();
- U32 current_color_length = (*current_color).mNumChars;
-
- S32 paragraph_offset = 0; //Offset into the paragraph text.
-
- // Wrap lines that are longer than the view is wide.
- while( paragraph_offset < (S32)mParagraphText.length() &&
- mParagraphText[paragraph_offset] != 0)
- {
- S32 skip_chars; // skip '\n'
- // Figure out if a word-wrapped line fits here.
- LLWString::size_type line_end = mParagraphText.find_first_of(llwchar('\n'), paragraph_offset);
- if (line_end != LLWString::npos)
- {
- skip_chars = 1; // skip '\n'
- }
- else
- {
- line_end = mParagraphText.size();
- skip_chars = 0;
- }
-
- U32 drawable = font->maxDrawableChars(mParagraphText.c_str()+paragraph_offset, screen_width, line_end - paragraph_offset, LLFontGL::WORD_BOUNDARY_IF_POSSIBLE);
-
- if (drawable != 0)
- {
- F32 x_position = 0; //Screen X position of text.
-
- mMaxWidth = llmax( mMaxWidth, (F32)font->getWidth( mParagraphText.substr( paragraph_offset, drawable ).c_str() ) );
- Line line;
-
- U32 left_to_draw = drawable;
- U32 drawn = 0;
-
- while (left_to_draw >= current_color_length
- && current_color != mParagraphColorSegments.end() )
- {
- LLWString color_text = mParagraphText.substr( paragraph_offset + drawn, current_color_length );
- line.mLineColorSegments.push_back( LineColorSegment( color_text, //Append segment to line.
- (*current_color).mColor,
- x_position ) );
-
- x_position += font->getWidth( color_text.c_str() ); //Set up next screen position.
-
- drawn += current_color_length;
- left_to_draw -= current_color_length;
-
- current_color++; //Goto next paragraph color record.
-
- if (current_color != mParagraphColorSegments.end())
- {
- current_color_length = (*current_color).mNumChars;
- }
- }
-
- if (left_to_draw > 0 && current_color != mParagraphColorSegments.end() )
- {
- LLWString color_text = mParagraphText.substr( paragraph_offset + drawn, left_to_draw );
-
- line.mLineColorSegments.push_back( LineColorSegment( color_text, //Append segment to line.
- (*current_color).mColor,
- x_position ) );
-
- current_color_length -= left_to_draw;
- }
- mLines.push_back(line); //Append line to paragraph line list.
- }
- paragraph_offset += (drawable + skip_chars);
- }
-}
-
-//Pass in the string and the default color for this block of text.
-LLConsole::Paragraph::Paragraph (LLWString str, const LLColor4 &color, F32 add_time, const LLFontGL* font, F32 screen_width)
-: mParagraphText(str), mAddTime(add_time), mMaxWidth(-1)
-{
- makeParagraphColorSegments(color);
- updateLines( screen_width, font );
-}
-
-// called once per frame regardless of console visibility
-// static
-void LLConsole::updateClass()
-{
- for (auto& con : instance_snapshot())
- {
- con.update();
- }
-}
-
-void LLConsole::update()
-{
- {
- LLMutexLock lock(&mMutex);
-
- while (!mLines.empty())
- {
- mParagraphs.push_back(
- Paragraph( mLines.front(),
- LLColor4::white,
- mTimer.getElapsedTimeF32(),
- mFont,
- (F32)getRect().getWidth()));
- mLines.pop_front();
- }
- }
-
- // remove old paragraphs which can't possibly be visible any more. ::draw() will do something similar but more conservative - we do this here because ::draw() isn't guaranteed to ever be called! (i.e. the console isn't visible)
- while ((S32)mParagraphs.size() > llmax((S32)0, (S32)(mMaxLines)))
- {
- mParagraphs.pop_front();
- }
-}
-
+/**
+ * @file llconsole.cpp
+ * @brief a scrolling console output device
+ *
+ * $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 "llviewerprecompiledheaders.h"
+#include "linden_common.h"
+
+#include "llconsole.h"
+
+// linden library includes
+#include "llmath.h"
+//#include "llviewercontrol.h"
+#include "llcriticaldamp.h"
+#include "llfontgl.h"
+#include "llgl.h"
+#include "llui.h"
+#include "lluiimage.h"
+//#include "llviewerimage.h"
+//#include "llviewerimagelist.h"
+//#include "llviewerwindow.h"
+#include "llsd.h"
+#include "llfontgl.h"
+#include "llmath.h"
+
+//#include "llstartup.h"
+
+// Used for LCD display
+extern void AddNewDebugConsoleToLCD(const LLWString &newLine);
+
+LLConsole* gConsole = NULL; // Created and destroyed in LLViewerWindow.
+
+const F32 FADE_DURATION = 2.f;
+
+static LLDefaultChildRegistry::Register<LLConsole> r("console");
+
+LLConsole::LLConsole(const LLConsole::Params& p)
+: LLUICtrl(p),
+ LLFixedBuffer(p.max_lines),
+ mLinePersistTime(p.persist_time), // seconds
+ mFont(p.font),
+ mConsoleWidth(0),
+ mConsoleHeight(0)
+{
+ if (p.font_size_index.isProvided())
+ {
+ setFontSize(p.font_size_index);
+ }
+ mFadeTime = mLinePersistTime - FADE_DURATION;
+ setMaxLines(LLUI::getInstance()->mSettingGroups["config"]->getS32("ConsoleMaxLines"));
+}
+
+void LLConsole::setLinePersistTime(F32 seconds)
+{
+ mLinePersistTime = seconds;
+ mFadeTime = mLinePersistTime - FADE_DURATION;
+}
+
+void LLConsole::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ S32 new_width = llmax(50, llmin(getRect().getWidth(), width));
+ S32 new_height = llmax(llfloor(mFont->getLineHeight()) + 15, llmin(getRect().getHeight(), height));
+
+ if ( mConsoleWidth == new_width
+ && mConsoleHeight == new_height )
+ {
+ return;
+ }
+
+ mConsoleWidth = new_width;
+ mConsoleHeight= new_height;
+
+ LLUICtrl::reshape(new_width, new_height, called_from_parent);
+
+ for(paragraph_t::iterator paragraph_it = mParagraphs.begin(); paragraph_it != mParagraphs.end(); paragraph_it++)
+ {
+ (*paragraph_it).updateLines((F32)getRect().getWidth(), mFont, true);
+ }
+}
+
+void LLConsole::setFontSize(S32 size_index)
+{
+ if (-1 == size_index)
+ {
+ mFont = LLFontGL::getFontMonospace();
+ }
+ else if (0 == size_index)
+ {
+ mFont = LLFontGL::getFontSansSerif();
+ }
+ else if (1 == size_index)
+ {
+ mFont = LLFontGL::getFontSansSerifBig();
+ }
+ else
+ {
+ mFont = LLFontGL::getFontSansSerifHuge();
+ }
+ // Make sure the font exists
+ if (mFont == NULL)
+ {
+ mFont = LLFontGL::getFontDefault();
+ }
+
+ for(paragraph_t::iterator paragraph_it = mParagraphs.begin(); paragraph_it != mParagraphs.end(); paragraph_it++)
+ {
+ (*paragraph_it).updateLines((F32)getRect().getWidth(), mFont, true);
+ }
+}
+
+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
+ F32 cur_time = mTimer.getElapsedTimeF32();
+
+ F32 skip_time = cur_time - mLinePersistTime;
+ F32 fade_time = cur_time - mFadeTime;
+
+ if (mParagraphs.empty()) //No text to draw.
+ {
+ return;
+ }
+
+ U32 num_lines=0;
+
+ paragraph_t::reverse_iterator paragraph_it;
+ paragraph_it = mParagraphs.rbegin();
+ U32 paragraph_num=mParagraphs.size();
+
+ while (!mParagraphs.empty() && paragraph_it != mParagraphs.rend())
+ {
+ num_lines += (*paragraph_it).mLines.size();
+ if(num_lines > mMaxLines
+ || ( (mLinePersistTime > (F32)0.f) && ((*paragraph_it).mAddTime - skip_time)/(mLinePersistTime - mFadeTime) <= (F32)0.f))
+ { //All lines above here are done. Lose them.
+ for (U32 i=0;i<paragraph_num;i++)
+ {
+ if (!mParagraphs.empty())
+ mParagraphs.pop_front();
+ }
+ break;
+ }
+ paragraph_num--;
+ paragraph_it++;
+ }
+
+ if (mParagraphs.empty())
+ {
+ return;
+ }
+
+ // draw remaining lines
+ F32 y_pos = 0.f;
+
+ LLUIImagePtr imagep = LLUI::getUIImage("transparent");
+
+ static LLCachedControl<F32> console_bg_opacity(*LLUI::getInstance()->mSettingGroups["config"], "ConsoleBackgroundOpacity", 0.7f);
+ F32 console_opacity = llclamp(console_bg_opacity(), 0.f, 1.f);
+
+ LLColor4 color = LLUIColorTable::instance().getColor("ConsoleBackground");
+ color.mV[VALPHA] *= console_opacity;
+
+ F32 line_height = mFont->getLineHeight();
+
+ for(paragraph_it = mParagraphs.rbegin(); paragraph_it != mParagraphs.rend(); paragraph_it++)
+ {
+ 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);
+
+ F32 y_off=0;
+
+ F32 alpha;
+
+ if ((mLinePersistTime > 0.f) && ((*paragraph_it).mAddTime < fade_time))
+ {
+ alpha = ((*paragraph_it).mAddTime - skip_time)/(mLinePersistTime - mFadeTime);
+ }
+ else
+ {
+ alpha = 1.0f;
+ }
+
+ if( alpha > 0.f )
+ {
+ for (lines_t::iterator line_it=(*paragraph_it).mLines.begin();
+ line_it != (*paragraph_it).mLines.end();
+ line_it ++)
+ {
+ for (line_color_segments_t::iterator seg_it = (*line_it).mLineColorSegments.begin();
+ seg_it != (*line_it).mLineColorSegments.end();
+ seg_it++)
+ {
+ mFont->render((*seg_it).mText, 0, (*seg_it).mXPosition - 8, y_pos - y_off,
+ LLColor4(
+ (*seg_it).mColor.mV[VRED],
+ (*seg_it).mColor.mV[VGREEN],
+ (*seg_it).mColor.mV[VBLUE],
+ (*seg_it).mColor.mV[VALPHA]*alpha),
+ LLFontGL::LEFT,
+ LLFontGL::BASELINE,
+ LLFontGL::NORMAL,
+ LLFontGL::DROP_SHADOW,
+ S32_MAX,
+ target_width
+ );
+ }
+ y_off += line_height;
+ }
+ }
+ y_pos += padding_vertical;
+ }
+}
+
+//Generate highlight color segments for this paragraph. Pass in default color of paragraph.
+void LLConsole::Paragraph::makeParagraphColorSegments (const LLColor4 &color)
+{
+ LLSD paragraph_color_segments;
+ paragraph_color_segments[0]["text"] =wstring_to_utf8str(mParagraphText);
+ LLSD color_sd = color.getValue();
+ paragraph_color_segments[0]["color"]=color_sd;
+
+ for(LLSD::array_const_iterator color_segment_it = paragraph_color_segments.beginArray();
+ color_segment_it != paragraph_color_segments.endArray();
+ ++color_segment_it)
+ {
+ LLSD color_llsd = (*color_segment_it)["color"];
+ std::string color_str = (*color_segment_it)["text"].asString();
+
+ ParagraphColorSegment color_segment;
+
+ color_segment.mColor.setValue(color_llsd);
+ color_segment.mNumChars = color_str.length();
+
+ mParagraphColorSegments.push_back(color_segment);
+ }
+}
+
+//Called when a paragraph is added to the console or window is resized.
+void LLConsole::Paragraph::updateLines(F32 screen_width, const LLFontGL* font, bool force_resize)
+{
+ if ( !force_resize )
+ {
+ if ( mMaxWidth >= 0.0f
+ && mMaxWidth < screen_width )
+ {
+ return; //No resize required.
+ }
+ }
+
+ screen_width = screen_width - 30; //Margin for small windows.
+
+ if ( mParagraphText.empty()
+ || mParagraphColorSegments.empty()
+ || font == NULL)
+ {
+ return; //Not enough info to complete.
+ }
+
+ mLines.clear(); //Chuck everything.
+ mMaxWidth = 0.0f;
+
+ paragraph_color_segments_t::iterator current_color = mParagraphColorSegments.begin();
+ U32 current_color_length = (*current_color).mNumChars;
+
+ S32 paragraph_offset = 0; //Offset into the paragraph text.
+
+ // Wrap lines that are longer than the view is wide.
+ while( paragraph_offset < (S32)mParagraphText.length() &&
+ mParagraphText[paragraph_offset] != 0)
+ {
+ S32 skip_chars; // skip '\n'
+ // Figure out if a word-wrapped line fits here.
+ LLWString::size_type line_end = mParagraphText.find_first_of(llwchar('\n'), paragraph_offset);
+ if (line_end != LLWString::npos)
+ {
+ skip_chars = 1; // skip '\n'
+ }
+ else
+ {
+ line_end = mParagraphText.size();
+ skip_chars = 0;
+ }
+
+ U32 drawable = font->maxDrawableChars(mParagraphText.c_str()+paragraph_offset, screen_width, line_end - paragraph_offset, LLFontGL::WORD_BOUNDARY_IF_POSSIBLE);
+
+ if (drawable != 0)
+ {
+ F32 x_position = 0; //Screen X position of text.
+
+ mMaxWidth = llmax( mMaxWidth, (F32)font->getWidth( mParagraphText.substr( paragraph_offset, drawable ).c_str() ) );
+ Line line;
+
+ U32 left_to_draw = drawable;
+ U32 drawn = 0;
+
+ while (left_to_draw >= current_color_length
+ && current_color != mParagraphColorSegments.end() )
+ {
+ LLWString color_text = mParagraphText.substr( paragraph_offset + drawn, current_color_length );
+ line.mLineColorSegments.push_back( LineColorSegment( color_text, //Append segment to line.
+ (*current_color).mColor,
+ x_position ) );
+
+ x_position += font->getWidth( color_text.c_str() ); //Set up next screen position.
+
+ drawn += current_color_length;
+ left_to_draw -= current_color_length;
+
+ current_color++; //Goto next paragraph color record.
+
+ if (current_color != mParagraphColorSegments.end())
+ {
+ current_color_length = (*current_color).mNumChars;
+ }
+ }
+
+ if (left_to_draw > 0 && current_color != mParagraphColorSegments.end() )
+ {
+ LLWString color_text = mParagraphText.substr( paragraph_offset + drawn, left_to_draw );
+
+ line.mLineColorSegments.push_back( LineColorSegment( color_text, //Append segment to line.
+ (*current_color).mColor,
+ x_position ) );
+
+ current_color_length -= left_to_draw;
+ }
+ mLines.push_back(line); //Append line to paragraph line list.
+ }
+ paragraph_offset += (drawable + skip_chars);
+ }
+}
+
+//Pass in the string and the default color for this block of text.
+LLConsole::Paragraph::Paragraph (LLWString str, const LLColor4 &color, F32 add_time, const LLFontGL* font, F32 screen_width)
+: mParagraphText(str), mAddTime(add_time), mMaxWidth(-1)
+{
+ makeParagraphColorSegments(color);
+ updateLines( screen_width, font );
+}
+
+// called once per frame regardless of console visibility
+// static
+void LLConsole::updateClass()
+{
+ for (auto& con : instance_snapshot())
+ {
+ con.update();
+ }
+}
+
+void LLConsole::update()
+{
+ {
+ LLMutexLock lock(&mMutex);
+
+ while (!mLines.empty())
+ {
+ mParagraphs.push_back(
+ Paragraph( mLines.front(),
+ LLColor4::white,
+ mTimer.getElapsedTimeF32(),
+ mFont,
+ (F32)getRect().getWidth()));
+ mLines.pop_front();
+ }
+ }
+
+ // remove old paragraphs which can't possibly be visible any more. ::draw() will do something similar but more conservative - we do this here because ::draw() isn't guaranteed to ever be called! (i.e. the console isn't visible)
+ while ((S32)mParagraphs.size() > llmax((S32)0, (S32)(mMaxLines)))
+ {
+ mParagraphs.pop_front();
+ }
+}
+
diff --git a/indra/llui/llconsole.h b/indra/llui/llconsole.h
index 2b144f03de..86ad1cc2cb 100644
--- a/indra/llui/llconsole.h
+++ b/indra/llui/llconsole.h
@@ -1,156 +1,156 @@
-/**
- * @file llconsole.h
- * @brief a simple console-style output device
- *
- * $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_LLCONSOLE_H
-#define LL_LLCONSOLE_H
-
-#include "llfixedbuffer.h"
-#include "lluictrl.h"
-#include "v4color.h"
-#include <deque>
-
-class LLSD;
-
-class LLConsole : public LLFixedBuffer, public LLUICtrl, public LLInstanceTracker<LLConsole>
-{
-public:
-
- typedef enum e_font_size
- {
- MONOSPACE = -1,
- SMALL = 0,
- BIG = 1
- } EFontSize;
-
- struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Optional<U32> max_lines;
- Optional<F32> persist_time;
- Optional<S32> font_size_index;
- Params()
- : max_lines("max_lines", LLUI::getInstance()->mSettingGroups["config"]->getS32("ConsoleMaxLines")),
- persist_time("persist_time", 0.f), // forever
- font_size_index("font_size_index")
- {
- changeDefault(mouse_opaque, false);
- }
- };
-protected:
- LLConsole(const Params&);
- friend class LLUICtrlFactory;
-
-public:
- // call once per frame to pull data out of LLFixedBuffer
- static void updateClass();
-
- //A paragraph color segment defines the color of text in a line
- //of text that was received for console display. It has no
- //notion of line wraps, screen position, or the text it contains.
- //It is only the number of characters that are a color and the
- //color.
- struct ParagraphColorSegment
- {
- S32 mNumChars;
- LLColor4 mColor;
- };
-
- //A line color segment is a chunk of text, the color associated
- //with it, and the X Position it was calculated to begin at
- //on the screen. X Positions are re-calculated if the
- //screen changes size.
- class LineColorSegment
- {
- public:
- LineColorSegment(LLWString text, LLColor4 color, F32 xpos) : mText(text), mColor(color), mXPosition(xpos) {}
- public:
- LLWString mText;
- LLColor4 mColor;
- F32 mXPosition;
- };
-
- typedef std::list<LineColorSegment> line_color_segments_t;
-
- //A line is composed of one or more color segments.
- class Line
- {
- public:
- line_color_segments_t mLineColorSegments;
- };
-
- typedef std::list<Line> lines_t;
- typedef std::list<ParagraphColorSegment> paragraph_color_segments_t;
-
- //A paragraph is a processed element containing the entire text of the
- //message (used for recalculating positions on screen resize)
- //The time this message was added to the console output
- //The visual screen width of the longest line in this block
- //And a list of one or more lines which are used to display this message.
- class Paragraph
- {
- public:
- Paragraph (LLWString str, const LLColor4 &color, F32 add_time, const LLFontGL* font, F32 screen_width);
- void makeParagraphColorSegments ( const LLColor4 &color);
- void updateLines ( F32 screen_width, const LLFontGL* font, bool force_resize=false );
- public:
- LLWString mParagraphText; //The entire text of the paragraph
- paragraph_color_segments_t mParagraphColorSegments;
- F32 mAddTime; //Time this paragraph was added to the display.
- F32 mMaxWidth; //Width of the widest line of text in this paragraph.
- lines_t mLines;
-
- };
-
- //The console contains a deque of paragraphs which represent the individual messages.
- typedef std::deque<Paragraph> paragraph_t;
- paragraph_t mParagraphs;
-
- ~LLConsole(){};
-
- // each line lasts this long after being added
- void setLinePersistTime(F32 seconds);
-
- void reshape(S32 width, S32 height, bool called_from_parent = true);
-
- // -1 = monospace, 0 means small, font size = 1 means big
- void setFontSize(S32 size_index);
-
-
- // Overrides
- /*virtual*/ void draw();
-private:
- void update();
-
- F32 mLinePersistTime; // Age at which to stop drawing.
- F32 mFadeTime; // Age at which to start fading
- const LLFontGL* mFont;
- S32 mConsoleWidth;
- S32 mConsoleHeight;
-
-};
-
-extern LLConsole* gConsole;
-
-#endif
+/**
+ * @file llconsole.h
+ * @brief a simple console-style output device
+ *
+ * $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_LLCONSOLE_H
+#define LL_LLCONSOLE_H
+
+#include "llfixedbuffer.h"
+#include "lluictrl.h"
+#include "v4color.h"
+#include <deque>
+
+class LLSD;
+
+class LLConsole : public LLFixedBuffer, public LLUICtrl, public LLInstanceTracker<LLConsole>
+{
+public:
+
+ typedef enum e_font_size
+ {
+ MONOSPACE = -1,
+ SMALL = 0,
+ BIG = 1
+ } EFontSize;
+
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<U32> max_lines;
+ Optional<F32> persist_time;
+ Optional<S32> font_size_index;
+ Params()
+ : max_lines("max_lines", LLUI::getInstance()->mSettingGroups["config"]->getS32("ConsoleMaxLines")),
+ persist_time("persist_time", 0.f), // forever
+ font_size_index("font_size_index")
+ {
+ changeDefault(mouse_opaque, false);
+ }
+ };
+protected:
+ LLConsole(const Params&);
+ friend class LLUICtrlFactory;
+
+public:
+ // call once per frame to pull data out of LLFixedBuffer
+ static void updateClass();
+
+ //A paragraph color segment defines the color of text in a line
+ //of text that was received for console display. It has no
+ //notion of line wraps, screen position, or the text it contains.
+ //It is only the number of characters that are a color and the
+ //color.
+ struct ParagraphColorSegment
+ {
+ S32 mNumChars;
+ LLColor4 mColor;
+ };
+
+ //A line color segment is a chunk of text, the color associated
+ //with it, and the X Position it was calculated to begin at
+ //on the screen. X Positions are re-calculated if the
+ //screen changes size.
+ class LineColorSegment
+ {
+ public:
+ LineColorSegment(LLWString text, LLColor4 color, F32 xpos) : mText(text), mColor(color), mXPosition(xpos) {}
+ public:
+ LLWString mText;
+ LLColor4 mColor;
+ F32 mXPosition;
+ };
+
+ typedef std::list<LineColorSegment> line_color_segments_t;
+
+ //A line is composed of one or more color segments.
+ class Line
+ {
+ public:
+ line_color_segments_t mLineColorSegments;
+ };
+
+ typedef std::list<Line> lines_t;
+ typedef std::list<ParagraphColorSegment> paragraph_color_segments_t;
+
+ //A paragraph is a processed element containing the entire text of the
+ //message (used for recalculating positions on screen resize)
+ //The time this message was added to the console output
+ //The visual screen width of the longest line in this block
+ //And a list of one or more lines which are used to display this message.
+ class Paragraph
+ {
+ public:
+ Paragraph (LLWString str, const LLColor4 &color, F32 add_time, const LLFontGL* font, F32 screen_width);
+ void makeParagraphColorSegments ( const LLColor4 &color);
+ void updateLines ( F32 screen_width, const LLFontGL* font, bool force_resize=false );
+ public:
+ LLWString mParagraphText; //The entire text of the paragraph
+ paragraph_color_segments_t mParagraphColorSegments;
+ F32 mAddTime; //Time this paragraph was added to the display.
+ F32 mMaxWidth; //Width of the widest line of text in this paragraph.
+ lines_t mLines;
+
+ };
+
+ //The console contains a deque of paragraphs which represent the individual messages.
+ typedef std::deque<Paragraph> paragraph_t;
+ paragraph_t mParagraphs;
+
+ ~LLConsole(){};
+
+ // each line lasts this long after being added
+ void setLinePersistTime(F32 seconds);
+
+ void reshape(S32 width, S32 height, bool called_from_parent = true);
+
+ // -1 = monospace, 0 means small, font size = 1 means big
+ void setFontSize(S32 size_index);
+
+
+ // Overrides
+ /*virtual*/ void draw();
+private:
+ void update();
+
+ F32 mLinePersistTime; // Age at which to stop drawing.
+ F32 mFadeTime; // Age at which to start fading
+ const LLFontGL* mFont;
+ S32 mConsoleWidth;
+ S32 mConsoleHeight;
+
+};
+
+extern LLConsole* gConsole;
+
+#endif
diff --git a/indra/llui/llcontainerview.cpp b/indra/llui/llcontainerview.cpp
index f7d8b83e62..a47218c4e2 100644
--- a/indra/llui/llcontainerview.cpp
+++ b/indra/llui/llcontainerview.cpp
@@ -1,300 +1,300 @@
-/**
- * @file llcontainerview.cpp
- * @brief Container for all statistics info
- *
- * $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 "llcontainerview.h"
-
-#include "llerror.h"
-#include "llfontgl.h"
-#include "llgl.h"
-#include "llui.h"
-#include "llstring.h"
-#include "llscrollcontainer.h"
-#include "lluictrlfactory.h"
-
-static LLDefaultChildRegistry::Register<LLContainerView> r1("container_view");
-
-#include "llpanel.h"
-#include "llstatview.h"
-static ContainerViewRegistry::Register<LLStatView> r2("stat_view");
-static ContainerViewRegistry::Register<LLPanel> r3("panel", &LLPanel::fromXML);
-
-LLContainerView::LLContainerView(const LLContainerView::Params& p)
-: LLView(p),
- mShowLabel(p.show_label),
- mLabel(p.label),
- mDisplayChildren(p.display_children)
-{
- mScrollContainer = NULL;
-}
-
-LLContainerView::~LLContainerView()
-{
- // Children all cleaned up by default view destructor.
-}
-
-bool LLContainerView::postBuild()
-{
- setDisplayChildren(mDisplayChildren);
- reshape(getRect().getWidth(), getRect().getHeight(), false);
- return true;
-}
-
-bool LLContainerView::addChild(LLView* child, S32 tab_group)
-{
- bool res = LLView::addChild(child, tab_group);
- if (res)
- {
- sendChildToBack(child);
- }
- 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;
- if (mDisplayChildren)
- {
- handled = (LLView::childrenHandleMouseDown(x, y, mask) != NULL);
- }
- if (!handled)
- {
- if( mShowLabel && (y >= getRect().getHeight() - 10) )
- {
- setDisplayChildren(!mDisplayChildren);
- reshape(getRect().getWidth(), getRect().getHeight(), false);
- handled = true;
- }
- }
- return handled;
-}
-
-bool LLContainerView::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
- if (mDisplayChildren)
- {
- handled = (LLView::childrenHandleMouseUp(x, y, mask) != NULL);
- }
- return handled;
-}
-
-
-void LLContainerView::draw()
-{
- {
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4(0.f, 0.f, 0.f, 0.25f));
- }
-
- // Draw the label
- if (mShowLabel)
- {
- LLFontGL::getFontMonospace()->renderUTF8(
- mLabel, 0, 2, getRect().getHeight() - 2, LLColor4(1,1,1,1), LLFontGL::LEFT, LLFontGL::TOP);
- }
-
- LLView::draw();
-}
-
-
-void LLContainerView::reshape(S32 width, S32 height, bool called_from_parent)
-{
- LLRect scroller_rect;
- scroller_rect.setOriginAndSize(0, 0, width, height);
-
- if (mScrollContainer)
- {
- scroller_rect = mScrollContainer->getContentWindowRect();
- }
- else
- {
- // if we're uncontained - make height as small as possible
- scroller_rect.mTop = 0;
- }
-
- arrange(scroller_rect.getWidth(), scroller_rect.getHeight(), called_from_parent);
-
- // sometimes, after layout, our container will change size (scrollbars popping in and out)
- // if so, attempt another layout
- if (mScrollContainer)
- {
- LLRect new_container_rect = mScrollContainer->getContentWindowRect();
-
- if ((new_container_rect.getWidth() != scroller_rect.getWidth()) ||
- (new_container_rect.getHeight() != scroller_rect.getHeight())) // the container size has changed, attempt to arrange again
- {
- arrange(new_container_rect.getWidth(), new_container_rect.getHeight(), called_from_parent);
- }
- }
-}
-
-void LLContainerView::arrange(S32 width, S32 height, bool called_from_parent)
-{
- // Determine the sizes and locations of all contained views
- S32 total_height = 0;
- S32 top, left, right, bottom;
- //LLView *childp;
-
- // These will be used for the children
- left = 10;
- top = getRect().getHeight() - 4;
- right = width - 2;
- bottom = top;
-
- // Leave some space for the top label/grab handle
- if (mShowLabel)
- {
- total_height += 20;
- }
-
- if (mDisplayChildren)
- {
- // Determine total height
- U32 child_height = 0;
- for (child_list_const_iter_t child_iter = getChildList()->begin();
- child_iter != getChildList()->end(); ++child_iter)
- {
- LLView *childp = *child_iter;
- if (!childp->getVisible())
- {
- LL_WARNS() << "Incorrect visibility!" << LL_ENDL;
- }
- LLRect child_rect = childp->getRequiredRect();
- child_height += child_rect.getHeight();
- child_height += 2;
- }
- total_height += child_height;
- }
-
- if (total_height < height)
- total_height = height;
-
- LLRect my_rect = getRect();
- if (followsTop())
- {
- my_rect.mBottom = my_rect.mTop - total_height;
- }
- else
- {
- my_rect.mTop = my_rect.mBottom + total_height;
- }
-
- my_rect.mRight = my_rect.mLeft + width;
- setRect(my_rect);
-
- top = total_height;
- if (mShowLabel)
- {
- top -= 20;
- }
-
- bottom = top;
-
- if (mDisplayChildren)
- {
- // Iterate through all children, and put in container from top down.
- for (child_list_const_iter_t child_iter = getChildList()->begin();
- child_iter != getChildList()->end(); ++child_iter)
- {
- LLView *childp = *child_iter;
- LLRect child_rect = childp->getRequiredRect();
- bottom -= child_rect.getHeight();
- LLRect r(left, bottom + child_rect.getHeight(), right, bottom);
- childp->setRect(r);
- childp->reshape(right - left, top - bottom);
- top = bottom - 2;
- bottom = top;
- }
- }
-
- if (!called_from_parent)
- {
- if (getParent())
- {
- getParent()->reshape(getParent()->getRect().getWidth(), getParent()->getRect().getHeight(), false);
- }
- }
-
-}
-
-LLRect LLContainerView::getRequiredRect()
-{
- LLRect req_rect;
- //LLView *childp;
- U32 total_height = 0;
-
- // Determine the sizes and locations of all contained views
-
- // Leave some space for the top label/grab handle
-
- if (mShowLabel)
- {
- total_height = 20;
- }
-
-
- if (mDisplayChildren)
- {
- // Determine total height
- U32 child_height = 0;
- for (child_list_const_iter_t child_iter = getChildList()->begin();
- child_iter != getChildList()->end(); ++child_iter)
- {
- LLView *childp = *child_iter;
- LLRect child_rect = childp->getRequiredRect();
- child_height += child_rect.getHeight();
- child_height += 2;
- }
-
- total_height += child_height;
- }
- req_rect.mTop = total_height;
- return req_rect;
-}
-
-void LLContainerView::setLabel(const std::string& label)
-{
- mLabel = label;
-}
-
-void LLContainerView::setDisplayChildren(bool displayChildren)
-{
- mDisplayChildren = displayChildren;
- for (child_list_const_iter_t child_iter = getChildList()->begin();
- child_iter != getChildList()->end(); ++child_iter)
- {
- LLView *childp = *child_iter;
- childp->setVisible(mDisplayChildren);
- }
-}
+/**
+ * @file llcontainerview.cpp
+ * @brief Container for all statistics info
+ *
+ * $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 "llcontainerview.h"
+
+#include "llerror.h"
+#include "llfontgl.h"
+#include "llgl.h"
+#include "llui.h"
+#include "llstring.h"
+#include "llscrollcontainer.h"
+#include "lluictrlfactory.h"
+
+static LLDefaultChildRegistry::Register<LLContainerView> r1("container_view");
+
+#include "llpanel.h"
+#include "llstatview.h"
+static ContainerViewRegistry::Register<LLStatView> r2("stat_view");
+static ContainerViewRegistry::Register<LLPanel> r3("panel", &LLPanel::fromXML);
+
+LLContainerView::LLContainerView(const LLContainerView::Params& p)
+: LLView(p),
+ mShowLabel(p.show_label),
+ mLabel(p.label),
+ mDisplayChildren(p.display_children)
+{
+ mScrollContainer = NULL;
+}
+
+LLContainerView::~LLContainerView()
+{
+ // Children all cleaned up by default view destructor.
+}
+
+bool LLContainerView::postBuild()
+{
+ setDisplayChildren(mDisplayChildren);
+ reshape(getRect().getWidth(), getRect().getHeight(), false);
+ return true;
+}
+
+bool LLContainerView::addChild(LLView* child, S32 tab_group)
+{
+ bool res = LLView::addChild(child, tab_group);
+ if (res)
+ {
+ sendChildToBack(child);
+ }
+ 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;
+ if (mDisplayChildren)
+ {
+ handled = (LLView::childrenHandleMouseDown(x, y, mask) != NULL);
+ }
+ if (!handled)
+ {
+ if( mShowLabel && (y >= getRect().getHeight() - 10) )
+ {
+ setDisplayChildren(!mDisplayChildren);
+ reshape(getRect().getWidth(), getRect().getHeight(), false);
+ handled = true;
+ }
+ }
+ return handled;
+}
+
+bool LLContainerView::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ bool handled = false;
+ if (mDisplayChildren)
+ {
+ handled = (LLView::childrenHandleMouseUp(x, y, mask) != NULL);
+ }
+ return handled;
+}
+
+
+void LLContainerView::draw()
+{
+ {
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4(0.f, 0.f, 0.f, 0.25f));
+ }
+
+ // Draw the label
+ if (mShowLabel)
+ {
+ LLFontGL::getFontMonospace()->renderUTF8(
+ mLabel, 0, 2, getRect().getHeight() - 2, LLColor4(1,1,1,1), LLFontGL::LEFT, LLFontGL::TOP);
+ }
+
+ LLView::draw();
+}
+
+
+void LLContainerView::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ LLRect scroller_rect;
+ scroller_rect.setOriginAndSize(0, 0, width, height);
+
+ if (mScrollContainer)
+ {
+ scroller_rect = mScrollContainer->getContentWindowRect();
+ }
+ else
+ {
+ // if we're uncontained - make height as small as possible
+ scroller_rect.mTop = 0;
+ }
+
+ arrange(scroller_rect.getWidth(), scroller_rect.getHeight(), called_from_parent);
+
+ // sometimes, after layout, our container will change size (scrollbars popping in and out)
+ // if so, attempt another layout
+ if (mScrollContainer)
+ {
+ LLRect new_container_rect = mScrollContainer->getContentWindowRect();
+
+ if ((new_container_rect.getWidth() != scroller_rect.getWidth()) ||
+ (new_container_rect.getHeight() != scroller_rect.getHeight())) // the container size has changed, attempt to arrange again
+ {
+ arrange(new_container_rect.getWidth(), new_container_rect.getHeight(), called_from_parent);
+ }
+ }
+}
+
+void LLContainerView::arrange(S32 width, S32 height, bool called_from_parent)
+{
+ // Determine the sizes and locations of all contained views
+ S32 total_height = 0;
+ S32 top, left, right, bottom;
+ //LLView *childp;
+
+ // These will be used for the children
+ left = 10;
+ top = getRect().getHeight() - 4;
+ right = width - 2;
+ bottom = top;
+
+ // Leave some space for the top label/grab handle
+ if (mShowLabel)
+ {
+ total_height += 20;
+ }
+
+ if (mDisplayChildren)
+ {
+ // Determine total height
+ U32 child_height = 0;
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *childp = *child_iter;
+ if (!childp->getVisible())
+ {
+ LL_WARNS() << "Incorrect visibility!" << LL_ENDL;
+ }
+ LLRect child_rect = childp->getRequiredRect();
+ child_height += child_rect.getHeight();
+ child_height += 2;
+ }
+ total_height += child_height;
+ }
+
+ if (total_height < height)
+ total_height = height;
+
+ LLRect my_rect = getRect();
+ if (followsTop())
+ {
+ my_rect.mBottom = my_rect.mTop - total_height;
+ }
+ else
+ {
+ my_rect.mTop = my_rect.mBottom + total_height;
+ }
+
+ my_rect.mRight = my_rect.mLeft + width;
+ setRect(my_rect);
+
+ top = total_height;
+ if (mShowLabel)
+ {
+ top -= 20;
+ }
+
+ bottom = top;
+
+ if (mDisplayChildren)
+ {
+ // Iterate through all children, and put in container from top down.
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *childp = *child_iter;
+ LLRect child_rect = childp->getRequiredRect();
+ bottom -= child_rect.getHeight();
+ LLRect r(left, bottom + child_rect.getHeight(), right, bottom);
+ childp->setRect(r);
+ childp->reshape(right - left, top - bottom);
+ top = bottom - 2;
+ bottom = top;
+ }
+ }
+
+ if (!called_from_parent)
+ {
+ if (getParent())
+ {
+ getParent()->reshape(getParent()->getRect().getWidth(), getParent()->getRect().getHeight(), false);
+ }
+ }
+
+}
+
+LLRect LLContainerView::getRequiredRect()
+{
+ LLRect req_rect;
+ //LLView *childp;
+ U32 total_height = 0;
+
+ // Determine the sizes and locations of all contained views
+
+ // Leave some space for the top label/grab handle
+
+ if (mShowLabel)
+ {
+ total_height = 20;
+ }
+
+
+ if (mDisplayChildren)
+ {
+ // Determine total height
+ U32 child_height = 0;
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *childp = *child_iter;
+ LLRect child_rect = childp->getRequiredRect();
+ child_height += child_rect.getHeight();
+ child_height += 2;
+ }
+
+ total_height += child_height;
+ }
+ req_rect.mTop = total_height;
+ return req_rect;
+}
+
+void LLContainerView::setLabel(const std::string& label)
+{
+ mLabel = label;
+}
+
+void LLContainerView::setDisplayChildren(bool displayChildren)
+{
+ mDisplayChildren = displayChildren;
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *childp = *child_iter;
+ childp->setVisible(mDisplayChildren);
+ }
+}
diff --git a/indra/llui/llcontainerview.h b/indra/llui/llcontainerview.h
index 4ddff88f66..82239eda4e 100644
--- a/indra/llui/llcontainerview.h
+++ b/indra/llui/llcontainerview.h
@@ -1,94 +1,94 @@
-/**
- * @file llcontainerview.h
- * @brief Container for all statistics info.
- *
- * $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_LLCONTAINERVIEW_H
-#define LL_LLCONTAINERVIEW_H
-
-#include "stdtypes.h"
-#include "lltextbox.h"
-#include "llstatbar.h"
-#include "llview.h"
-
-class LLScrollContainer;
-
-struct ContainerViewRegistry : public LLChildRegistry<ContainerViewRegistry>
-{
- LLSINGLETON_EMPTY_CTOR(ContainerViewRegistry);
-};
-
-class LLContainerView : public LLView
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLView::Params>
- {
- Optional<std::string> label;
- Optional<bool> show_label;
- Optional<bool> display_children;
- Params()
- : label("label"),
- show_label("show_label", false),
- display_children("display_children", true)
- {
- changeDefault(mouse_opaque, false);
- }
- };
-
- // my valid children are stored in this registry
- typedef ContainerViewRegistry child_registry_t;
-
-protected:
- LLContainerView(const Params& p);
- friend class LLUICtrlFactory;
-public:
- ~LLContainerView();
-
- /*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);
-
- /*virtual*/ void draw();
- /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true);
- /*virtual*/ LLRect getRequiredRect(); // Return the height of this object, given the set options.
-
- void setLabel(const std::string& label);
- void showLabel(bool show) { mShowLabel = show; }
- void setDisplayChildren(bool displayChildren);
- bool getDisplayChildren() { return mDisplayChildren; }
- void setScrollContainer(LLScrollContainer* scroll) {mScrollContainer = scroll;}
-
- private:
- LLScrollContainer* mScrollContainer;
- void arrange(S32 width, S32 height, bool called_from_parent = true);
- bool mShowLabel;
-
-protected:
- bool mDisplayChildren;
- std::string mLabel;
-};
-#endif // LL_CONTAINERVIEW_
+/**
+ * @file llcontainerview.h
+ * @brief Container for all statistics info.
+ *
+ * $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_LLCONTAINERVIEW_H
+#define LL_LLCONTAINERVIEW_H
+
+#include "stdtypes.h"
+#include "lltextbox.h"
+#include "llstatbar.h"
+#include "llview.h"
+
+class LLScrollContainer;
+
+struct ContainerViewRegistry : public LLChildRegistry<ContainerViewRegistry>
+{
+ LLSINGLETON_EMPTY_CTOR(ContainerViewRegistry);
+};
+
+class LLContainerView : public LLView
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLView::Params>
+ {
+ Optional<std::string> label;
+ Optional<bool> show_label;
+ Optional<bool> display_children;
+ Params()
+ : label("label"),
+ show_label("show_label", false),
+ display_children("display_children", true)
+ {
+ changeDefault(mouse_opaque, false);
+ }
+ };
+
+ // my valid children are stored in this registry
+ typedef ContainerViewRegistry child_registry_t;
+
+protected:
+ LLContainerView(const Params& p);
+ friend class LLUICtrlFactory;
+public:
+ ~LLContainerView();
+
+ /*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);
+
+ /*virtual*/ void draw();
+ /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true);
+ /*virtual*/ LLRect getRequiredRect(); // Return the height of this object, given the set options.
+
+ void setLabel(const std::string& label);
+ void showLabel(bool show) { mShowLabel = show; }
+ void setDisplayChildren(bool displayChildren);
+ bool getDisplayChildren() { return mDisplayChildren; }
+ void setScrollContainer(LLScrollContainer* scroll) {mScrollContainer = scroll;}
+
+ private:
+ LLScrollContainer* mScrollContainer;
+ void arrange(S32 width, S32 height, bool called_from_parent = true);
+ bool mShowLabel;
+
+protected:
+ bool mDisplayChildren;
+ std::string mLabel;
+};
+#endif // LL_CONTAINERVIEW_
diff --git a/indra/llui/llctrlselectioninterface.cpp b/indra/llui/llctrlselectioninterface.cpp
index 87d52da187..55c901b54c 100644
--- a/indra/llui/llctrlselectioninterface.cpp
+++ b/indra/llui/llctrlselectioninterface.cpp
@@ -1,63 +1,63 @@
-/**
- * @file llctrlselectioninterface.cpp
- * @brief Programmatic selection of items in a list.
- *
- * $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$
- */
-#include "linden_common.h"
-
-#include "llctrlselectioninterface.h"
-
-#include "llsd.h"
-
-// virtual
-LLCtrlSelectionInterface::~LLCtrlSelectionInterface()
-{ }
-
-bool LLCtrlSelectionInterface::selectByValue(LLSD value)
-{
- return setSelectedByValue(value, true);
-}
-
-bool LLCtrlSelectionInterface::deselectByValue(LLSD value)
-{
- return setSelectedByValue(value, false);
-}
-
-
-// virtual
-LLCtrlListInterface::~LLCtrlListInterface()
-{ }
-
-LLScrollListItem* LLCtrlListInterface::addSimpleElement(const std::string& value)
-{
- return addSimpleElement(value, ADD_BOTTOM, LLSD());
-}
-
-LLScrollListItem* LLCtrlListInterface::addSimpleElement(const std::string& value, EAddPosition pos)
-{
- return addSimpleElement(value, pos, LLSD());
-}
-
-// virtual
-LLCtrlScrollInterface::~LLCtrlScrollInterface()
-{ }
+/**
+ * @file llctrlselectioninterface.cpp
+ * @brief Programmatic selection of items in a list.
+ *
+ * $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$
+ */
+#include "linden_common.h"
+
+#include "llctrlselectioninterface.h"
+
+#include "llsd.h"
+
+// virtual
+LLCtrlSelectionInterface::~LLCtrlSelectionInterface()
+{ }
+
+bool LLCtrlSelectionInterface::selectByValue(LLSD value)
+{
+ return setSelectedByValue(value, true);
+}
+
+bool LLCtrlSelectionInterface::deselectByValue(LLSD value)
+{
+ return setSelectedByValue(value, false);
+}
+
+
+// virtual
+LLCtrlListInterface::~LLCtrlListInterface()
+{ }
+
+LLScrollListItem* LLCtrlListInterface::addSimpleElement(const std::string& value)
+{
+ return addSimpleElement(value, ADD_BOTTOM, LLSD());
+}
+
+LLScrollListItem* LLCtrlListInterface::addSimpleElement(const std::string& value, EAddPosition pos)
+{
+ return addSimpleElement(value, pos, LLSD());
+}
+
+// virtual
+LLCtrlScrollInterface::~LLCtrlScrollInterface()
+{ }
diff --git a/indra/llui/llctrlselectioninterface.h b/indra/llui/llctrlselectioninterface.h
index 0907b58b5c..01ab9db670 100644
--- a/indra/llui/llctrlselectioninterface.h
+++ b/indra/llui/llctrlselectioninterface.h
@@ -1,104 +1,104 @@
-/**
- * @file llctrlselectioninterface.h
- * @brief Programmatic selection of items in a list.
- *
- * $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 LLCTRLSELECTIONINTERFACE_H
-#define LLCTRLSELECTIONINTERFACE_H
-
-#include "stdtypes.h"
-#include "llstring.h"
-#include "llui.h"
-
-class LLSD;
-class LLUUID;
-class LLScrollListItem;
-
-class LLCtrlSelectionInterface
-{
-public:
- virtual ~LLCtrlSelectionInterface();
-
- enum EOperation
- {
- OP_DELETE = 1,
- OP_SELECT,
- OP_DESELECT,
- };
-
- virtual bool getCanSelect() const = 0;
-
- virtual S32 getItemCount() const = 0;
-
- virtual bool selectFirstItem() = 0;
- virtual bool selectNthItem( S32 index ) = 0;
- virtual bool selectItemRange( S32 first, S32 last ) = 0;
-
- virtual S32 getFirstSelectedIndex() const = 0;
-
- // TomY TODO: Simply cast the UUIDs to LLSDs, using the selectByValue function
- virtual bool setCurrentByID( const LLUUID& id ) = 0;
- virtual LLUUID getCurrentID() const = 0;
-
- bool selectByValue(const LLSD value);
- bool deselectByValue(const LLSD value);
- virtual bool setSelectedByValue(const LLSD& value, bool selected) = 0;
- virtual LLSD getSelectedValue() = 0;
-
- virtual bool isSelected(const LLSD& value) const = 0;
-
- virtual bool operateOnSelection(EOperation op) = 0;
- virtual bool operateOnAll(EOperation op) = 0;
-};
-
-class LLCtrlListInterface : public LLCtrlSelectionInterface
-{
-public:
- virtual ~LLCtrlListInterface();
-
- virtual void addColumn(const LLSD& column, EAddPosition pos = ADD_BOTTOM) = 0;
- virtual void clearColumns() = 0;
- virtual void setColumnLabel(const std::string& column, const std::string& label) = 0;
- // TomY TODO: Document this
- virtual LLScrollListItem* addElement(const LLSD& value, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL) = 0;
-
- LLScrollListItem* addSimpleElement(const std::string& value); // defaults to bottom
- LLScrollListItem* addSimpleElement(const std::string& value, EAddPosition pos); // defaults to no LLSD() id
- virtual LLScrollListItem* addSimpleElement(const std::string& value, EAddPosition pos, const LLSD& id) = 0;
-
- virtual void clearRows() = 0;
- virtual void sortByColumn(const std::string& name, bool ascending) = 0;
-};
-
-class LLCtrlScrollInterface
-{
-public:
- virtual ~LLCtrlScrollInterface();
-
- virtual S32 getScrollPos() const = 0;
- virtual void setScrollPos( S32 pos ) = 0;
- virtual void scrollToShowSelected() = 0;
-};
-
-#endif
+/**
+ * @file llctrlselectioninterface.h
+ * @brief Programmatic selection of items in a list.
+ *
+ * $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 LLCTRLSELECTIONINTERFACE_H
+#define LLCTRLSELECTIONINTERFACE_H
+
+#include "stdtypes.h"
+#include "llstring.h"
+#include "llui.h"
+
+class LLSD;
+class LLUUID;
+class LLScrollListItem;
+
+class LLCtrlSelectionInterface
+{
+public:
+ virtual ~LLCtrlSelectionInterface();
+
+ enum EOperation
+ {
+ OP_DELETE = 1,
+ OP_SELECT,
+ OP_DESELECT,
+ };
+
+ virtual bool getCanSelect() const = 0;
+
+ virtual S32 getItemCount() const = 0;
+
+ virtual bool selectFirstItem() = 0;
+ virtual bool selectNthItem( S32 index ) = 0;
+ virtual bool selectItemRange( S32 first, S32 last ) = 0;
+
+ virtual S32 getFirstSelectedIndex() const = 0;
+
+ // TomY TODO: Simply cast the UUIDs to LLSDs, using the selectByValue function
+ virtual bool setCurrentByID( const LLUUID& id ) = 0;
+ virtual LLUUID getCurrentID() const = 0;
+
+ bool selectByValue(const LLSD value);
+ bool deselectByValue(const LLSD value);
+ virtual bool setSelectedByValue(const LLSD& value, bool selected) = 0;
+ virtual LLSD getSelectedValue() = 0;
+
+ virtual bool isSelected(const LLSD& value) const = 0;
+
+ virtual bool operateOnSelection(EOperation op) = 0;
+ virtual bool operateOnAll(EOperation op) = 0;
+};
+
+class LLCtrlListInterface : public LLCtrlSelectionInterface
+{
+public:
+ virtual ~LLCtrlListInterface();
+
+ virtual void addColumn(const LLSD& column, EAddPosition pos = ADD_BOTTOM) = 0;
+ virtual void clearColumns() = 0;
+ virtual void setColumnLabel(const std::string& column, const std::string& label) = 0;
+ // TomY TODO: Document this
+ virtual LLScrollListItem* addElement(const LLSD& value, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL) = 0;
+
+ LLScrollListItem* addSimpleElement(const std::string& value); // defaults to bottom
+ LLScrollListItem* addSimpleElement(const std::string& value, EAddPosition pos); // defaults to no LLSD() id
+ virtual LLScrollListItem* addSimpleElement(const std::string& value, EAddPosition pos, const LLSD& id) = 0;
+
+ virtual void clearRows() = 0;
+ virtual void sortByColumn(const std::string& name, bool ascending) = 0;
+};
+
+class LLCtrlScrollInterface
+{
+public:
+ virtual ~LLCtrlScrollInterface();
+
+ virtual S32 getScrollPos() const = 0;
+ virtual void setScrollPos( S32 pos ) = 0;
+ virtual void scrollToShowSelected() = 0;
+};
+
+#endif
diff --git a/indra/llui/lldockablefloater.cpp b/indra/llui/lldockablefloater.cpp
index 4495986d81..ab61b4617a 100644
--- a/indra/llui/lldockablefloater.cpp
+++ b/indra/llui/lldockablefloater.cpp
@@ -1,263 +1,263 @@
-/**
- * @file lldockablefloater.cpp
- * @brief Creates a panel of a specific kind for a toast
- *
- * $LicenseInfo:firstyear=2000&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "lldockablefloater.h"
-#include "llfloaterreg.h"
-
-//static
-LLHandle<LLFloater> LLDockableFloater::sInstanceHandle;
-
-//static
-void LLDockableFloater::init(LLDockableFloater* thiz)
-{
- thiz->setDocked(thiz->mDockControl.get() != NULL
- && thiz->mDockControl.get()->isDockVisible());
- thiz->resetInstance();
-
- // all dockable floaters should have close, dock and minimize buttons
- thiz->setCanClose(true);
- thiz->setCanDock(true);
- thiz->setCanMinimize(true);
- thiz->setOverlapsScreenChannel(false);
- thiz->mForceDocking = false;
-}
-
-LLDockableFloater::LLDockableFloater(LLDockControl* dockControl,
- const LLSD& key, const Params& params) :
- LLFloater(key, params), mDockControl(dockControl), mUniqueDocking(true)
-{
- init(this);
- mUseTongue = true;
-}
-
-LLDockableFloater::LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking,
- const LLSD& key, const Params& params) :
- LLFloater(key, params), mDockControl(dockControl), mUniqueDocking(uniqueDocking)
-{
- init(this);
- mUseTongue = true;
-}
-
-LLDockableFloater::LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking,
- bool useTongue, const LLSD& key, const Params& params) :
- LLFloater(key, params), mDockControl(dockControl), mUseTongue(useTongue), mUniqueDocking(uniqueDocking)
-{
- init(this);
-}
-
-LLDockableFloater::~LLDockableFloater()
-{
-}
-
-bool LLDockableFloater::postBuild()
-{
- // Remember we should force docking when the floater is opened for the first time
- if (mIsDockedStateForcedCallback != NULL && mIsDockedStateForcedCallback())
- {
- mForceDocking = true;
- }
-
- mDockTongue = LLUI::getUIImage("Flyout_Pointer");
- LLFloater::setDocked(true);
- return LLView::postBuild();
-}
-
-//static
-void LLDockableFloater::toggleInstance(const LLSD& sdname)
-{
- LLSD key;
- std::string name = sdname.asString();
-
- LLDockableFloater* instance =
- dynamic_cast<LLDockableFloater*> (LLFloaterReg::findInstance(name));
- // if floater closed or docked
- if (instance == NULL || (instance && instance->isDocked()))
- {
- LLFloaterReg::toggleInstance(name, key);
- // restore button toggle state
- if (instance != NULL)
- {
- instance->storeVisibilityControl();
- }
- }
- // if floater undocked
- else if (instance != NULL)
- {
- instance->setMinimized(false);
- if (instance->getVisible())
- {
- instance->setVisible(false);
- }
- else
- {
- instance->setVisible(true);
- gFloaterView->bringToFront(instance);
- }
- }
-}
-
-void LLDockableFloater::resetInstance()
-{
- if (mUniqueDocking && sInstanceHandle.get() != this)
- {
- if (sInstanceHandle.get() != NULL && sInstanceHandle.get()->isDocked())
- {
- sInstanceHandle.get()->setVisible(false);
- }
- sInstanceHandle = getHandle();
- }
-}
-
-void LLDockableFloater::setVisible(bool visible)
-{
- // Force docking if requested
- if (visible && mForceDocking)
- {
- setCanDock(true);
- setDocked(true);
- mForceDocking = false;
- }
-
- if(visible && isDocked())
- {
- resetInstance();
- }
-
- if (visible && mDockControl.get() != NULL)
- {
- mDockControl.get()->repositionDockable();
- }
-
- if (visible && !isMinimized())
- {
- LLFloater::setFrontmost(getAutoFocus());
- }
- LLFloater::setVisible(visible);
-}
-
-void LLDockableFloater::setMinimized(bool minimize)
-{
- if(minimize && isDocked())
- {
- // minimizing a docked floater just hides it
- setVisible(false);
- }
- else
- {
- LLFloater::setMinimized(minimize);
- }
-}
-
-LLView * LLDockableFloater::getDockWidget()
-{
- LLView * res = NULL;
- if (getDockControl() != NULL) {
- res = getDockControl()->getDock();
- }
-
- return res;
-}
-
-void LLDockableFloater::onDockHidden()
-{
- setCanDock(false);
-}
-
-void LLDockableFloater::onDockShown()
-{
- if (!isMinimized())
- {
- setCanDock(true);
- }
-}
-
-void LLDockableFloater::setDocked(bool docked, bool pop_on_undock)
-{
- if (mDockControl.get() != NULL && mDockControl.get()->isDockVisible())
- {
- if (docked)
- {
- resetInstance();
- mDockControl.get()->on();
- }
- else
- {
- mDockControl.get()->off();
- }
-
- if (!docked && pop_on_undock)
- {
- // visually pop up a little bit to emphasize the undocking
- translate(0, UNDOCK_LEAP_HEIGHT);
- }
- }
-
- LLFloater::setDocked(docked, pop_on_undock);
-}
-
-void LLDockableFloater::draw()
-{
- if (mDockControl.get() != NULL)
- {
- mDockControl.get()->repositionDockable();
- if (isDocked())
- {
- mDockControl.get()->drawToungue();
- }
- }
- LLFloater::draw();
-}
-
-void LLDockableFloater::setDockControl(LLDockControl* dockControl)
-{
- mDockControl.reset(dockControl);
- setDocked(isDocked());
-}
-
-const LLUIImagePtr& LLDockableFloater::getDockTongue(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;
-}
-
-LLDockControl* LLDockableFloater::getDockControl()
-{
- return mDockControl.get();
-}
+/**
+ * @file lldockablefloater.cpp
+ * @brief Creates a panel of a specific kind for a toast
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lldockablefloater.h"
+#include "llfloaterreg.h"
+
+//static
+LLHandle<LLFloater> LLDockableFloater::sInstanceHandle;
+
+//static
+void LLDockableFloater::init(LLDockableFloater* thiz)
+{
+ thiz->setDocked(thiz->mDockControl.get() != NULL
+ && thiz->mDockControl.get()->isDockVisible());
+ thiz->resetInstance();
+
+ // all dockable floaters should have close, dock and minimize buttons
+ thiz->setCanClose(true);
+ thiz->setCanDock(true);
+ thiz->setCanMinimize(true);
+ thiz->setOverlapsScreenChannel(false);
+ thiz->mForceDocking = false;
+}
+
+LLDockableFloater::LLDockableFloater(LLDockControl* dockControl,
+ const LLSD& key, const Params& params) :
+ LLFloater(key, params), mDockControl(dockControl), mUniqueDocking(true)
+{
+ init(this);
+ mUseTongue = true;
+}
+
+LLDockableFloater::LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking,
+ const LLSD& key, const Params& params) :
+ LLFloater(key, params), mDockControl(dockControl), mUniqueDocking(uniqueDocking)
+{
+ init(this);
+ mUseTongue = true;
+}
+
+LLDockableFloater::LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking,
+ bool useTongue, const LLSD& key, const Params& params) :
+ LLFloater(key, params), mDockControl(dockControl), mUseTongue(useTongue), mUniqueDocking(uniqueDocking)
+{
+ init(this);
+}
+
+LLDockableFloater::~LLDockableFloater()
+{
+}
+
+bool LLDockableFloater::postBuild()
+{
+ // Remember we should force docking when the floater is opened for the first time
+ if (mIsDockedStateForcedCallback != NULL && mIsDockedStateForcedCallback())
+ {
+ mForceDocking = true;
+ }
+
+ mDockTongue = LLUI::getUIImage("Flyout_Pointer");
+ LLFloater::setDocked(true);
+ return LLView::postBuild();
+}
+
+//static
+void LLDockableFloater::toggleInstance(const LLSD& sdname)
+{
+ LLSD key;
+ std::string name = sdname.asString();
+
+ LLDockableFloater* instance =
+ dynamic_cast<LLDockableFloater*> (LLFloaterReg::findInstance(name));
+ // if floater closed or docked
+ if (instance == NULL || (instance && instance->isDocked()))
+ {
+ LLFloaterReg::toggleInstance(name, key);
+ // restore button toggle state
+ if (instance != NULL)
+ {
+ instance->storeVisibilityControl();
+ }
+ }
+ // if floater undocked
+ else if (instance != NULL)
+ {
+ instance->setMinimized(false);
+ if (instance->getVisible())
+ {
+ instance->setVisible(false);
+ }
+ else
+ {
+ instance->setVisible(true);
+ gFloaterView->bringToFront(instance);
+ }
+ }
+}
+
+void LLDockableFloater::resetInstance()
+{
+ if (mUniqueDocking && sInstanceHandle.get() != this)
+ {
+ if (sInstanceHandle.get() != NULL && sInstanceHandle.get()->isDocked())
+ {
+ sInstanceHandle.get()->setVisible(false);
+ }
+ sInstanceHandle = getHandle();
+ }
+}
+
+void LLDockableFloater::setVisible(bool visible)
+{
+ // Force docking if requested
+ if (visible && mForceDocking)
+ {
+ setCanDock(true);
+ setDocked(true);
+ mForceDocking = false;
+ }
+
+ if(visible && isDocked())
+ {
+ resetInstance();
+ }
+
+ if (visible && mDockControl.get() != NULL)
+ {
+ mDockControl.get()->repositionDockable();
+ }
+
+ if (visible && !isMinimized())
+ {
+ LLFloater::setFrontmost(getAutoFocus());
+ }
+ LLFloater::setVisible(visible);
+}
+
+void LLDockableFloater::setMinimized(bool minimize)
+{
+ if(minimize && isDocked())
+ {
+ // minimizing a docked floater just hides it
+ setVisible(false);
+ }
+ else
+ {
+ LLFloater::setMinimized(minimize);
+ }
+}
+
+LLView * LLDockableFloater::getDockWidget()
+{
+ LLView * res = NULL;
+ if (getDockControl() != NULL) {
+ res = getDockControl()->getDock();
+ }
+
+ return res;
+}
+
+void LLDockableFloater::onDockHidden()
+{
+ setCanDock(false);
+}
+
+void LLDockableFloater::onDockShown()
+{
+ if (!isMinimized())
+ {
+ setCanDock(true);
+ }
+}
+
+void LLDockableFloater::setDocked(bool docked, bool pop_on_undock)
+{
+ if (mDockControl.get() != NULL && mDockControl.get()->isDockVisible())
+ {
+ if (docked)
+ {
+ resetInstance();
+ mDockControl.get()->on();
+ }
+ else
+ {
+ mDockControl.get()->off();
+ }
+
+ if (!docked && pop_on_undock)
+ {
+ // visually pop up a little bit to emphasize the undocking
+ translate(0, UNDOCK_LEAP_HEIGHT);
+ }
+ }
+
+ LLFloater::setDocked(docked, pop_on_undock);
+}
+
+void LLDockableFloater::draw()
+{
+ if (mDockControl.get() != NULL)
+ {
+ mDockControl.get()->repositionDockable();
+ if (isDocked())
+ {
+ mDockControl.get()->drawToungue();
+ }
+ }
+ LLFloater::draw();
+}
+
+void LLDockableFloater::setDockControl(LLDockControl* dockControl)
+{
+ mDockControl.reset(dockControl);
+ setDocked(isDocked());
+}
+
+const LLUIImagePtr& LLDockableFloater::getDockTongue(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;
+}
+
+LLDockControl* LLDockableFloater::getDockControl()
+{
+ return mDockControl.get();
+}
diff --git a/indra/llui/lldockablefloater.h b/indra/llui/lldockablefloater.h
index 03b8be39a6..fd23877e09 100644
--- a/indra/llui/lldockablefloater.h
+++ b/indra/llui/lldockablefloater.h
@@ -1,152 +1,152 @@
-/**
- * @file lldockablefloater.h
- * @brief Creates a panel of a specific kind for a toast.
- *
- * $LicenseInfo:firstyear=2003&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_DOCKABLEFLOATER_H
-#define LL_DOCKABLEFLOATER_H
-
-#include "llerror.h"
-#include "llfloater.h"
-#include "lldockcontrol.h"
-#include <memory>
-
-/**
- * Represents floater that can dock.
- * In case impossibility deriving from LLDockableFloater use LLDockControl.
- */
-class LLDockableFloater : public LLFloater
-{
- static const U32 UNDOCK_LEAP_HEIGHT = 12;
-
- static void init(LLDockableFloater* thiz);
-public:
- LOG_CLASS(LLDockableFloater);
- LLDockableFloater(LLDockControl* dockControl, const LLSD& key,
- const Params& params = getDefaultParams());
-
- /**
- * Constructor.
- * @param dockControl a pointer to the doc control instance
- * @param uniqueDocking - a flag defines is docking should work as tab(at one
- * moment only one docked floater can be shown), also this flag defines is dock
- * tongue should be used.
- * @params key a floater key.
- * @params params a floater parameters
- */
- LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking,
- const LLSD& key, const Params& params = getDefaultParams());
-
- /**
- * Constructor.
- * @param dockControl a pointer to the doc control instance
- * @param uniqueDocking - a flag defines is docking should work as tab(at one
- * moment only one docked floater can be shown).
- * @praram useTongue - a flag defines is dock tongue should be used.
- * @params key a floater key.
- * @params params a floater parameters
- */
- LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking,
- bool useTongue, const LLSD& key,
- const Params& params = getDefaultParams());
-
- virtual ~LLDockableFloater();
-
- static LLHandle<LLFloater> getInstanceHandle() { return sInstanceHandle; }
-
- static void toggleInstance(const LLSD& sdname);
-
- /**
- * If descendant class overrides postBuild() in order to perform specific
- * construction then it must still invoke its superclass' implementation.
- */
- /* virtula */bool postBuild();
- /* virtual */void setDocked(bool docked, bool pop_on_undock = true);
- /* virtual */void draw();
-
- /**
- * If descendant class overrides setVisible() then it must still invoke its
- * superclass' implementation.
- */
- /*virtual*/ void setVisible(bool visible);
-
- /**
- * If descendant class overrides setMinimized() then it must still invoke its
- * superclass' implementation.
- */
- /*virtual*/ void setMinimized(bool minimize);
-
- LLView * getDockWidget();
-
- virtual void onDockHidden();
- virtual void onDockShown();
-
- LLDockControl* getDockControl();
-
- /**
- * Returns true if screen channel should consider floater's size when drawing toasts.
- *
- * By default returns false.
- */
- virtual bool overlapsScreenChannel() { return mOverlapsScreenChannel && getVisible() && isDocked(); }
- virtual void setOverlapsScreenChannel(bool overlaps) { mOverlapsScreenChannel = overlaps; }
-
- bool getUniqueDocking() { return mUniqueDocking; }
- bool getUseTongue() { return mUseTongue; }
-
- void setUseTongue(bool use_tongue) { mUseTongue = use_tongue;}
-private:
- /**
- * Provides unique of dockable floater.
- * If dockable floater already exists it should be closed.
- */
- void resetInstance();
-
-protected:
- void setDockControl(LLDockControl* dockControl);
- const LLUIImagePtr& getDockTongue(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)
- boost::function<bool ()> mIsDockedStateForcedCallback;
-
-private:
- std::unique_ptr<LLDockControl> mDockControl;
- LLUIImagePtr mDockTongue;
- static LLHandle<LLFloater> sInstanceHandle;
- /**
- * Provides possibility to define that dockable floaters can be docked
- * non exclusively.
- */
- bool mUniqueDocking;
-
- bool mUseTongue;
-
- bool mOverlapsScreenChannel;
-
- // Force docking when the floater is being shown for the first time.
- bool mForceDocking;
-};
-
-#endif /* LL_DOCKABLEFLOATER_H */
+/**
+ * @file lldockablefloater.h
+ * @brief Creates a panel of a specific kind for a toast.
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_DOCKABLEFLOATER_H
+#define LL_DOCKABLEFLOATER_H
+
+#include "llerror.h"
+#include "llfloater.h"
+#include "lldockcontrol.h"
+#include <memory>
+
+/**
+ * Represents floater that can dock.
+ * In case impossibility deriving from LLDockableFloater use LLDockControl.
+ */
+class LLDockableFloater : public LLFloater
+{
+ static const U32 UNDOCK_LEAP_HEIGHT = 12;
+
+ static void init(LLDockableFloater* thiz);
+public:
+ LOG_CLASS(LLDockableFloater);
+ LLDockableFloater(LLDockControl* dockControl, const LLSD& key,
+ const Params& params = getDefaultParams());
+
+ /**
+ * Constructor.
+ * @param dockControl a pointer to the doc control instance
+ * @param uniqueDocking - a flag defines is docking should work as tab(at one
+ * moment only one docked floater can be shown), also this flag defines is dock
+ * tongue should be used.
+ * @params key a floater key.
+ * @params params a floater parameters
+ */
+ LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking,
+ const LLSD& key, const Params& params = getDefaultParams());
+
+ /**
+ * Constructor.
+ * @param dockControl a pointer to the doc control instance
+ * @param uniqueDocking - a flag defines is docking should work as tab(at one
+ * moment only one docked floater can be shown).
+ * @praram useTongue - a flag defines is dock tongue should be used.
+ * @params key a floater key.
+ * @params params a floater parameters
+ */
+ LLDockableFloater(LLDockControl* dockControl, bool uniqueDocking,
+ bool useTongue, const LLSD& key,
+ const Params& params = getDefaultParams());
+
+ virtual ~LLDockableFloater();
+
+ static LLHandle<LLFloater> getInstanceHandle() { return sInstanceHandle; }
+
+ static void toggleInstance(const LLSD& sdname);
+
+ /**
+ * If descendant class overrides postBuild() in order to perform specific
+ * construction then it must still invoke its superclass' implementation.
+ */
+ /* virtula */bool postBuild();
+ /* virtual */void setDocked(bool docked, bool pop_on_undock = true);
+ /* virtual */void draw();
+
+ /**
+ * If descendant class overrides setVisible() then it must still invoke its
+ * superclass' implementation.
+ */
+ /*virtual*/ void setVisible(bool visible);
+
+ /**
+ * If descendant class overrides setMinimized() then it must still invoke its
+ * superclass' implementation.
+ */
+ /*virtual*/ void setMinimized(bool minimize);
+
+ LLView * getDockWidget();
+
+ virtual void onDockHidden();
+ virtual void onDockShown();
+
+ LLDockControl* getDockControl();
+
+ /**
+ * Returns true if screen channel should consider floater's size when drawing toasts.
+ *
+ * By default returns false.
+ */
+ virtual bool overlapsScreenChannel() { return mOverlapsScreenChannel && getVisible() && isDocked(); }
+ virtual void setOverlapsScreenChannel(bool overlaps) { mOverlapsScreenChannel = overlaps; }
+
+ bool getUniqueDocking() { return mUniqueDocking; }
+ bool getUseTongue() { return mUseTongue; }
+
+ void setUseTongue(bool use_tongue) { mUseTongue = use_tongue;}
+private:
+ /**
+ * Provides unique of dockable floater.
+ * If dockable floater already exists it should be closed.
+ */
+ void resetInstance();
+
+protected:
+ void setDockControl(LLDockControl* dockControl);
+ const LLUIImagePtr& getDockTongue(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)
+ boost::function<bool ()> mIsDockedStateForcedCallback;
+
+private:
+ std::unique_ptr<LLDockControl> mDockControl;
+ LLUIImagePtr mDockTongue;
+ static LLHandle<LLFloater> sInstanceHandle;
+ /**
+ * Provides possibility to define that dockable floaters can be docked
+ * non exclusively.
+ */
+ bool mUniqueDocking;
+
+ bool mUseTongue;
+
+ bool mOverlapsScreenChannel;
+
+ // Force docking when the floater is being shown for the first time.
+ bool mForceDocking;
+};
+
+#endif /* LL_DOCKABLEFLOATER_H */
diff --git a/indra/llui/lldockcontrol.cpp b/indra/llui/lldockcontrol.cpp
index bd42497cb6..bf0862e8a9 100644
--- a/indra/llui/lldockcontrol.cpp
+++ b/indra/llui/lldockcontrol.cpp
@@ -1,25 +1,25 @@
-/**
+/**
* @file lldockcontrol.cpp
* @brief Creates a panel of a specific kind for a toast
*
* $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -30,50 +30,50 @@
#include "lldockablefloater.h"
LLDockControl::LLDockControl(LLView* dockWidget, LLFloater* dockableFloater,
- const LLUIImagePtr& dockTongue, DocAt dockAt, get_allowed_rect_callback_t get_allowed_rect_callback) :
- mDockableFloater(dockableFloater),
- mDockTongue(dockTongue),
- mDockTongueX(0),
- mDockTongueY(0)
+ const LLUIImagePtr& dockTongue, DocAt dockAt, get_allowed_rect_callback_t get_allowed_rect_callback) :
+ mDockableFloater(dockableFloater),
+ mDockTongue(dockTongue),
+ mDockTongueX(0),
+ mDockTongueY(0)
{
- mDockAt = dockAt;
-
- if (dockWidget != NULL)
- {
- mDockWidgetHandle = dockWidget->getHandle();
- }
-
- if (dockableFloater->isDocked())
- {
- on();
- }
- else
- {
- off();
- }
-
- if (!(get_allowed_rect_callback))
- {
- mGetAllowedRectCallback = boost::bind(&LLDockControl::getAllowedRect, this, _1);
- }
- else
- {
- mGetAllowedRectCallback = get_allowed_rect_callback;
- }
-
- if (dockWidget != NULL)
- {
- repositionDockable();
- }
-
- if (getDock() != NULL)
- {
- mDockWidgetVisible = isDockVisible();
- }
- else
- {
- mDockWidgetVisible = false;
- }
+ mDockAt = dockAt;
+
+ if (dockWidget != NULL)
+ {
+ mDockWidgetHandle = dockWidget->getHandle();
+ }
+
+ if (dockableFloater->isDocked())
+ {
+ on();
+ }
+ else
+ {
+ off();
+ }
+
+ if (!(get_allowed_rect_callback))
+ {
+ mGetAllowedRectCallback = boost::bind(&LLDockControl::getAllowedRect, this, _1);
+ }
+ else
+ {
+ mGetAllowedRectCallback = get_allowed_rect_callback;
+ }
+
+ if (dockWidget != NULL)
+ {
+ repositionDockable();
+ }
+
+ if (getDock() != NULL)
+ {
+ mDockWidgetVisible = isDockVisible();
+ }
+ else
+ {
+ mDockWidgetVisible = false;
+ }
}
LLDockControl::~LLDockControl()
@@ -82,302 +82,302 @@ LLDockControl::~LLDockControl()
void LLDockControl::setDock(LLView* dockWidget)
{
- if (dockWidget != NULL)
- {
- mDockWidgetHandle = dockWidget->getHandle();
- repositionDockable();
- mDockWidgetVisible = isDockVisible();
- }
- else
- {
- mDockWidgetHandle = LLHandle<LLView>();
- mDockWidgetVisible = false;
- }
+ if (dockWidget != NULL)
+ {
+ mDockWidgetHandle = dockWidget->getHandle();
+ repositionDockable();
+ mDockWidgetVisible = isDockVisible();
+ }
+ else
+ {
+ mDockWidgetHandle = LLHandle<LLView>();
+ mDockWidgetVisible = false;
+ }
}
void LLDockControl::getAllowedRect(LLRect& rect)
{
- rect = mDockableFloater->getRootView()->getChild<LLView>("non_toolbar_panel")->getRect();
+ rect = mDockableFloater->getRootView()->getChild<LLView>("non_toolbar_panel")->getRect();
}
void LLDockControl::repositionDockable()
{
- if (!getDock()) return;
- LLRect dockRect = getDock()->calcScreenRect();
- LLRect rootRect;
- LLRect floater_rect = mDockableFloater->calcScreenRect();
- mGetAllowedRectCallback(rootRect);
-
- // 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())
- {
- mDockableFloater->setDocked(false);
- // force off() since dockable may not have dockControll at this time
- off();
- LLDockableFloater* dockable_floater =
- dynamic_cast<LLDockableFloater*> (mDockableFloater);
- if(dockable_floater != NULL)
- {
- dockable_floater->onDockHidden();
- }
- }
- else
- {
- if(mEnabled)
- {
- moveDockable();
- }
- LLDockableFloater* dockable_floater =
- dynamic_cast<LLDockableFloater*> (mDockableFloater);
- if(dockable_floater != NULL)
- {
- dockable_floater->onDockShown();
- }
- }
-
- mPrevDockRect = dockRect;
- mRootRect = rootRect;
- mFloaterRect = floater_rect;
- mRecalculateDockablePosition = false;
- mDockWidgetVisible = isDockVisible();
- }
+ if (!getDock()) return;
+ LLRect dockRect = getDock()->calcScreenRect();
+ LLRect rootRect;
+ LLRect floater_rect = mDockableFloater->calcScreenRect();
+ mGetAllowedRectCallback(rootRect);
+
+ // 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())
+ {
+ mDockableFloater->setDocked(false);
+ // force off() since dockable may not have dockControll at this time
+ off();
+ LLDockableFloater* dockable_floater =
+ dynamic_cast<LLDockableFloater*> (mDockableFloater);
+ if(dockable_floater != NULL)
+ {
+ dockable_floater->onDockHidden();
+ }
+ }
+ else
+ {
+ if(mEnabled)
+ {
+ moveDockable();
+ }
+ LLDockableFloater* dockable_floater =
+ dynamic_cast<LLDockableFloater*> (mDockableFloater);
+ if(dockable_floater != NULL)
+ {
+ dockable_floater->onDockShown();
+ }
+ }
+
+ mPrevDockRect = dockRect;
+ mRootRect = rootRect;
+ mFloaterRect = floater_rect;
+ mRecalculateDockablePosition = false;
+ mDockWidgetVisible = isDockVisible();
+ }
}
bool LLDockControl::isDockVisible()
{
- bool res = true;
-
- if (getDock() != NULL)
- {
- //we should check all hierarchy
- res = getDock()->isInVisibleChain();
- if (res)
- {
- LLRect dockRect = getDock()->calcScreenRect();
-
- switch (mDockAt)
- {
- case LEFT: // to keep compiler happy
- break;
- case BOTTOM:
- case TOP:
- {
- // check is dock inside parent rect
- // assume that parent for all dockable floaters
- // is the root view
- LLRect dockParentRect =
- getDock()->getRootView()->calcScreenRect();
- if (dockRect.mRight <= dockParentRect.mLeft
- || dockRect.mLeft >= dockParentRect.mRight)
- {
- res = false;
- }
- break;
- }
- default:
- break;
- }
- }
- }
-
- return res;
+ bool res = true;
+
+ if (getDock() != NULL)
+ {
+ //we should check all hierarchy
+ res = getDock()->isInVisibleChain();
+ if (res)
+ {
+ LLRect dockRect = getDock()->calcScreenRect();
+
+ switch (mDockAt)
+ {
+ case LEFT: // to keep compiler happy
+ break;
+ case BOTTOM:
+ case TOP:
+ {
+ // check is dock inside parent rect
+ // assume that parent for all dockable floaters
+ // is the root view
+ LLRect dockParentRect =
+ getDock()->getRootView()->calcScreenRect();
+ if (dockRect.mRight <= dockParentRect.mLeft
+ || dockRect.mLeft >= dockParentRect.mRight)
+ {
+ res = false;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ return res;
}
void LLDockControl::moveDockable()
{
- // calculate new dockable position
- LLRect dockRect = getDock()->calcScreenRect();
- LLRect rootRect;
- mGetAllowedRectCallback(rootRect);
-
- bool use_tongue = false;
- LLDockableFloater* dockable_floater =
- dynamic_cast<LLDockableFloater*> (mDockableFloater);
- if (dockable_floater != NULL)
- {
- use_tongue = dockable_floater->getUseTongue();
- }
-
- LLRect dockableRect = mDockableFloater->calcScreenRect();
- S32 x = 0;
- S32 y = 0;
- LLRect dockParentRect;
- switch (mDockAt)
- {
- case LEFT:
-
- x = dockRect.mLeft - dockableRect.getWidth();
- y = dockRect.getCenterY() + dockableRect.getHeight() / 2;
-
- if (use_tongue)
- {
- x -= mDockTongue->getWidth();
- }
-
- 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 += mDockTongue->getWidth();
- }
-
- mDockTongueX = dockRect.mRight;
- mDockTongueY = dockableRect.getCenterY() - mDockTongue->getHeight() / 2;
-
- break;
-
- case TOP:
- x = dockRect.getCenterX() - dockableRect.getWidth() / 2;
- y = dockRect.mTop + dockableRect.getHeight();
- // unique docking used with dock tongue, so add tongue height to the Y coordinate
- if (use_tongue)
- {
- y += mDockTongue->getHeight();
-
- if ( y > rootRect.mTop)
- {
- y = rootRect.mTop;
- }
- }
-
- // check is dockable inside root view rect
- if (x < rootRect.mLeft)
- {
- x = rootRect.mLeft;
- }
- if (x + dockableRect.getWidth() > rootRect.mRight)
- {
- x = rootRect.mRight - dockableRect.getWidth();
- }
-
-
- // calculate dock tongue position
- dockParentRect = getDock()->getParent()->calcScreenRect();
- if (dockRect.getCenterX() < dockParentRect.mLeft)
- {
- mDockTongueX = dockParentRect.mLeft - mDockTongue->getWidth() / 2;
- }
- else if (dockRect.getCenterX() > dockParentRect.mRight)
- {
- mDockTongueX = dockParentRect.mRight - mDockTongue->getWidth() / 2;;
- }
- else
- {
- mDockTongueX = dockRect.getCenterX() - mDockTongue->getWidth() / 2;
- }
- mDockTongueY = dockRect.mTop;
-
- break;
- case BOTTOM:
- x = dockRect.getCenterX() - dockableRect.getWidth() / 2;
- y = dockRect.mBottom;
- // unique docking used with dock tongue, so add tongue height to the Y coordinate
- if (use_tongue)
- {
- y -= mDockTongue->getHeight();
- }
-
- // check is dockable inside root view rect
- if (x < rootRect.mLeft)
- {
- x = rootRect.mLeft;
- }
- if (x + dockableRect.getWidth() > rootRect.mRight)
- {
- x = rootRect.mRight - dockableRect.getWidth();
- }
-
- // calculate dock tongue position
- dockParentRect = getDock()->getParent()->calcScreenRect();
- if (dockRect.getCenterX() < dockParentRect.mLeft)
- {
- mDockTongueX = dockParentRect.mLeft - mDockTongue->getWidth() / 2;
- }
- else if (dockRect.getCenterX() > dockParentRect.mRight)
- {
- mDockTongueX = dockParentRect.mRight - mDockTongue->getWidth() / 2;;
- }
- else
- {
- mDockTongueX = dockRect.getCenterX() - mDockTongue->getWidth() / 2;
- }
- mDockTongueY = dockRect.mBottom - mDockTongue->getHeight();
-
- break;
- }
-
- 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.
- if (use_tongue && dockableRect.getHeight() >= max_available_height)
- {
- dockableRect.setLeftTopAndSize(x, y, dockableRect.getWidth(), max_available_height);
- mDockableFloater->reshape(dockableRect.getWidth(), dockableRect.getHeight());
- }
- else
- {
- // move dockable
- dockableRect.setLeftTopAndSize(x, y, dockableRect.getWidth(),
- dockableRect.getHeight());
- }
-
- LLRect localDocableParentRect;
-
- mDockableFloater->getParent()->screenRectToLocal(dockableRect, &localDocableParentRect);
- mDockableFloater->setRect(localDocableParentRect);
- mDockableFloater->screenPointToLocal(mDockTongueX, mDockTongueY, &mDockTongueX, &mDockTongueY);
+ // calculate new dockable position
+ LLRect dockRect = getDock()->calcScreenRect();
+ LLRect rootRect;
+ mGetAllowedRectCallback(rootRect);
+
+ bool use_tongue = false;
+ LLDockableFloater* dockable_floater =
+ dynamic_cast<LLDockableFloater*> (mDockableFloater);
+ if (dockable_floater != NULL)
+ {
+ use_tongue = dockable_floater->getUseTongue();
+ }
+
+ LLRect dockableRect = mDockableFloater->calcScreenRect();
+ S32 x = 0;
+ S32 y = 0;
+ LLRect dockParentRect;
+ switch (mDockAt)
+ {
+ case LEFT:
+
+ x = dockRect.mLeft - dockableRect.getWidth();
+ y = dockRect.getCenterY() + dockableRect.getHeight() / 2;
+
+ if (use_tongue)
+ {
+ x -= mDockTongue->getWidth();
+ }
+
+ 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 += mDockTongue->getWidth();
+ }
+
+ mDockTongueX = dockRect.mRight;
+ mDockTongueY = dockableRect.getCenterY() - mDockTongue->getHeight() / 2;
+
+ break;
+
+ case TOP:
+ x = dockRect.getCenterX() - dockableRect.getWidth() / 2;
+ y = dockRect.mTop + dockableRect.getHeight();
+ // unique docking used with dock tongue, so add tongue height to the Y coordinate
+ if (use_tongue)
+ {
+ y += mDockTongue->getHeight();
+
+ if ( y > rootRect.mTop)
+ {
+ y = rootRect.mTop;
+ }
+ }
+
+ // check is dockable inside root view rect
+ if (x < rootRect.mLeft)
+ {
+ x = rootRect.mLeft;
+ }
+ if (x + dockableRect.getWidth() > rootRect.mRight)
+ {
+ x = rootRect.mRight - dockableRect.getWidth();
+ }
+
+
+ // calculate dock tongue position
+ dockParentRect = getDock()->getParent()->calcScreenRect();
+ if (dockRect.getCenterX() < dockParentRect.mLeft)
+ {
+ mDockTongueX = dockParentRect.mLeft - mDockTongue->getWidth() / 2;
+ }
+ else if (dockRect.getCenterX() > dockParentRect.mRight)
+ {
+ mDockTongueX = dockParentRect.mRight - mDockTongue->getWidth() / 2;;
+ }
+ else
+ {
+ mDockTongueX = dockRect.getCenterX() - mDockTongue->getWidth() / 2;
+ }
+ mDockTongueY = dockRect.mTop;
+
+ break;
+ case BOTTOM:
+ x = dockRect.getCenterX() - dockableRect.getWidth() / 2;
+ y = dockRect.mBottom;
+ // unique docking used with dock tongue, so add tongue height to the Y coordinate
+ if (use_tongue)
+ {
+ y -= mDockTongue->getHeight();
+ }
+
+ // check is dockable inside root view rect
+ if (x < rootRect.mLeft)
+ {
+ x = rootRect.mLeft;
+ }
+ if (x + dockableRect.getWidth() > rootRect.mRight)
+ {
+ x = rootRect.mRight - dockableRect.getWidth();
+ }
+
+ // calculate dock tongue position
+ dockParentRect = getDock()->getParent()->calcScreenRect();
+ if (dockRect.getCenterX() < dockParentRect.mLeft)
+ {
+ mDockTongueX = dockParentRect.mLeft - mDockTongue->getWidth() / 2;
+ }
+ else if (dockRect.getCenterX() > dockParentRect.mRight)
+ {
+ mDockTongueX = dockParentRect.mRight - mDockTongue->getWidth() / 2;;
+ }
+ else
+ {
+ mDockTongueX = dockRect.getCenterX() - mDockTongue->getWidth() / 2;
+ }
+ mDockTongueY = dockRect.mBottom - mDockTongue->getHeight();
+
+ break;
+ }
+
+ 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.
+ if (use_tongue && dockableRect.getHeight() >= max_available_height)
+ {
+ dockableRect.setLeftTopAndSize(x, y, dockableRect.getWidth(), max_available_height);
+ mDockableFloater->reshape(dockableRect.getWidth(), dockableRect.getHeight());
+ }
+ else
+ {
+ // move dockable
+ dockableRect.setLeftTopAndSize(x, y, dockableRect.getWidth(),
+ dockableRect.getHeight());
+ }
+
+ LLRect localDocableParentRect;
+
+ mDockableFloater->getParent()->screenRectToLocal(dockableRect, &localDocableParentRect);
+ mDockableFloater->setRect(localDocableParentRect);
+ mDockableFloater->screenPointToLocal(mDockTongueX, mDockTongueY, &mDockTongueX, &mDockTongueY);
}
void LLDockControl::on()
{
- if (isDockVisible())
- {
- mEnabled = true;
- mRecalculateDockablePosition = true;
- }
+ if (isDockVisible())
+ {
+ mEnabled = true;
+ mRecalculateDockablePosition = true;
+ }
}
void LLDockControl::off()
{
- mEnabled = false;
+ mEnabled = false;
}
void LLDockControl::forceRecalculatePosition()
{
- mRecalculateDockablePosition = true;
+ mRecalculateDockablePosition = true;
}
void LLDockControl::drawToungue()
{
- bool use_tongue = false;
- LLDockableFloater* dockable_floater =
- dynamic_cast<LLDockableFloater*> (mDockableFloater);
- if (dockable_floater != NULL)
- {
- use_tongue = dockable_floater->getUseTongue();
- }
-
- if (mEnabled && use_tongue)
- {
- mDockTongue->draw(mDockTongueX, mDockTongueY);
- }
+ bool use_tongue = false;
+ LLDockableFloater* dockable_floater =
+ dynamic_cast<LLDockableFloater*> (mDockableFloater);
+ if (dockable_floater != NULL)
+ {
+ use_tongue = dockable_floater->getUseTongue();
+ }
+
+ if (mEnabled && use_tongue)
+ {
+ mDockTongue->draw(mDockTongueX, mDockTongueY);
+ }
}
diff --git a/indra/llui/lldockcontrol.h b/indra/llui/lldockcontrol.h
index 98a9c7236d..fb0bf7d251 100644
--- a/indra/llui/lldockcontrol.h
+++ b/indra/llui/lldockcontrol.h
@@ -1,25 +1,25 @@
-/**
+/**
* @file lldockcontrol.h
* @brief Creates a panel of a specific kind for a toast.
*
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -39,58 +39,58 @@
class LLDockControl
{
public:
- enum DocAt
- {
- TOP,
- LEFT,
- RIGHT,
- BOTTOM
- };
+ enum DocAt
+ {
+ TOP,
+ LEFT,
+ RIGHT,
+ BOTTOM
+ };
public:
- // callback for a function getting a rect valid for control's position
- typedef boost::function<void (LLRect& )> get_allowed_rect_callback_t;
+ // callback for a function getting a rect valid for control's position
+ typedef boost::function<void (LLRect& )> get_allowed_rect_callback_t;
- LOG_CLASS(LLDockControl);
- LLDockControl(LLView* dockWidget, LLFloater* dockableFloater,
- const LLUIImagePtr& dockTongue, DocAt dockAt, get_allowed_rect_callback_t get_rect_callback = NULL);
- virtual ~LLDockControl();
+ LOG_CLASS(LLDockControl);
+ LLDockControl(LLView* dockWidget, LLFloater* dockableFloater,
+ const LLUIImagePtr& dockTongue, DocAt dockAt, get_allowed_rect_callback_t get_rect_callback = NULL);
+ virtual ~LLDockControl();
public:
- void on();
- void off();
- void forceRecalculatePosition();
- void setDock(LLView* dockWidget);
- LLView* getDock()
- {
- return mDockWidgetHandle.get();
- }
- void repositionDockable();
- void drawToungue();
- bool isDockVisible();
+ void on();
+ void off();
+ void forceRecalculatePosition();
+ void setDock(LLView* dockWidget);
+ LLView* getDock()
+ {
+ return mDockWidgetHandle.get();
+ }
+ void repositionDockable();
+ void drawToungue();
+ bool isDockVisible();
- // gets a rect that bounds possible positions for a dockable control (EXT-1111)
- void getAllowedRect(LLRect& rect);
+ // gets a rect that bounds possible positions for a dockable control (EXT-1111)
+ void getAllowedRect(LLRect& rect);
- S32 getTongueWidth() { return mDockTongue->getWidth(); }
- S32 getTongueHeight() { return mDockTongue->getHeight(); }
+ S32 getTongueWidth() { return mDockTongue->getWidth(); }
+ S32 getTongueHeight() { return mDockTongue->getHeight(); }
private:
- virtual void moveDockable();
+ virtual void moveDockable();
private:
- get_allowed_rect_callback_t mGetAllowedRectCallback;
- bool mEnabled;
- bool mRecalculateDockablePosition;
- bool mDockWidgetVisible;
- DocAt mDockAt;
- LLHandle<LLView> mDockWidgetHandle;
- LLRect mPrevDockRect;
- LLRect mRootRect;
- LLRect mFloaterRect;
- LLFloater* mDockableFloater;
- LLUIImagePtr mDockTongue;
- S32 mDockTongueX;
- S32 mDockTongueY;
+ get_allowed_rect_callback_t mGetAllowedRectCallback;
+ bool mEnabled;
+ bool mRecalculateDockablePosition;
+ bool mDockWidgetVisible;
+ DocAt mDockAt;
+ LLHandle<LLView> mDockWidgetHandle;
+ LLRect mPrevDockRect;
+ LLRect mRootRect;
+ LLRect mFloaterRect;
+ LLFloater* mDockableFloater;
+ LLUIImagePtr mDockTongue;
+ S32 mDockTongueX;
+ S32 mDockTongueY;
};
#endif /* LL_DOCKCONTROL_H */
diff --git a/indra/llui/lldraghandle.cpp b/indra/llui/lldraghandle.cpp
index d3b4dd2bd3..78efdfc639 100644
--- a/indra/llui/lldraghandle.cpp
+++ b/indra/llui/lldraghandle.cpp
@@ -1,387 +1,387 @@
-/**
- * @file lldraghandle.cpp
- * @brief LLDragHandle 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$
- */
-
-// A widget for dragging a view around the screen using the mouse.
-
-#include "linden_common.h"
-
-#include "lldraghandle.h"
-
-#include "llmath.h"
-
-//#include "llviewerwindow.h"
-#include "llui.h"
-#include "llmenugl.h"
-#include "lltextbox.h"
-#include "llcontrol.h"
-#include "llfontgl.h"
-#include "llwindow.h"
-#include "llfocusmgr.h"
-#include "lluictrlfactory.h"
-
-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;
-
-S32 LLDragHandle::sSnapMargin = 5;
-
-LLDragHandle::LLDragHandle(const LLDragHandle::Params& p)
-: LLView(p),
- mDragLastScreenX( 0 ),
- mDragLastScreenY( 0 ),
- mLastMouseScreenX( 0 ),
- mLastMouseScreenY( 0 ),
- mTitleBox( NULL ),
- mMaxTitleWidth( 0 ),
- mForeground( true ),
- mDragHighlightColor(p.drag_highlight_color()),
- mDragShadowColor(p.drag_shadow_color())
-
-{
- static LLUICachedControl<S32> snap_margin ("SnapMargin", 0);
- sSnapMargin = snap_margin;
-}
-
-LLDragHandle::~LLDragHandle()
-{
- gFocusMgr.removeKeyboardFocusWithoutCallback(this);
- removeChild(mTitleBox);
- delete mTitleBox;
-}
-
-void LLDragHandle::initFromParams(const LLDragHandle::Params& p)
-{
- LLView::initFromParams(p);
- setTitle( p.label );
-}
-
-void LLDragHandle::setTitleVisible(bool visible)
-{
- if(mTitleBox)
- {
- mTitleBox->setVisible(visible);
- }
-}
-
-void LLDragHandleTop::setTitle(const std::string& title)
-{
- std::string trimmed_title = title;
- LLStringUtil::trim(trimmed_title);
-
- if( mTitleBox )
- {
- mTitleBox->setText(trimmed_title);
- }
- else
- {
- const LLFontGL* font = LLFontGL::getFontSansSerif();
- LLTextBox::Params params;
- params.name("Drag Handle Title");
- params.rect(getRect());
- params.initial_value(trimmed_title);
- params.font(font);
- params.follows.flags(FOLLOWS_TOP | FOLLOWS_LEFT | FOLLOWS_RIGHT);
- params.font_shadow(LLFontGL::DROP_SHADOW_SOFT);
- params.use_ellipses = true;
- params.parse_urls = false; //cancel URL replacement in floater title
- mTitleBox = LLUICtrlFactory::create<LLTextBox> (params);
- addChild( mTitleBox );
- }
-
- reshapeTitleBox();
-}
-
-
-std::string LLDragHandleTop::getTitle() const
-{
- return mTitleBox == NULL ? LLStringUtil::null : mTitleBox->getText();
-}
-
-
-void LLDragHandleLeft::setTitle(const std::string& )
-{
- if( mTitleBox )
- {
- removeChild(mTitleBox);
- delete mTitleBox;
- mTitleBox = NULL;
- }
- /* no title on left edge */
-}
-
-
-std::string LLDragHandleLeft::getTitle() const
-{
- return LLStringUtil::null;
-}
-
-
-void LLDragHandleTop::draw()
-{
- /* Disable lines. Can drag anywhere in most windows. JC
- if( getVisible() && getEnabled() && mForeground)
- {
- const S32 BORDER_PAD = 2;
- const S32 HPAD = 2;
- const S32 VPAD = 2;
- S32 left = BORDER_PAD + HPAD;
- S32 top = getRect().getHeight() - 2 * VPAD;
- S32 right = getRect().getWidth() - HPAD;
-// S32 bottom = VPAD;
-
- // draw lines for drag areas
-
- const S32 LINE_SPACING = (DRAG_HANDLE_HEIGHT - 2 * VPAD) / 4;
- S32 line = top - LINE_SPACING;
-
- LLRect title_rect = mTitleBox->getRect();
- S32 title_right = title_rect.mLeft + mTitleWidth;
- bool show_right_side = title_right < getRect().getWidth();
-
- for( S32 i=0; i<4; i++ )
- {
- gl_line_2d(left, line+1, title_rect.mLeft - LEADING_PAD, line+1, mDragHighlightColor);
- if( show_right_side )
- {
- gl_line_2d(title_right, line+1, right, line+1, mDragHighlightColor);
- }
-
- gl_line_2d(left, line, title_rect.mLeft - LEADING_PAD, line, mDragShadowColor);
- if( show_right_side )
- {
- gl_line_2d(title_right, line, right, line, mDragShadowColor);
- }
- line -= LINE_SPACING;
- }
- }
- */
-
- // Colorize the text to match the frontmost state
- if (mTitleBox)
- {
- mTitleBox->setEnabled(getForeground());
- }
-
- LLView::draw();
-}
-
-
-// assumes GL state is set for 2D
-void LLDragHandleLeft::draw()
-{
- /* Disable lines. Can drag anywhere in most windows. JC
- if( getVisible() && getEnabled() && mForeground )
- {
- const S32 BORDER_PAD = 2;
-// const S32 HPAD = 2;
- const S32 VPAD = 2;
- const S32 LINE_SPACING = 3;
-
- S32 left = BORDER_PAD + LINE_SPACING;
- S32 top = getRect().getHeight() - 2 * VPAD;
-// S32 right = getRect().getWidth() - HPAD;
- S32 bottom = VPAD;
-
- // draw lines for drag areas
-
- // no titles yet
- //LLRect title_rect = mTitleBox->getRect();
- //S32 title_right = title_rect.mLeft + mTitleWidth;
- //bool show_right_side = title_right < getRect().getWidth();
-
- S32 line = left;
- for( S32 i=0; i<4; i++ )
- {
- gl_line_2d(line, top, line, bottom, mDragHighlightColor);
-
- gl_line_2d(line+1, top, line+1, bottom, mDragShadowColor);
-
- line += LINE_SPACING;
- }
- }
- */
-
- // Colorize the text to match the frontmost state
- if (mTitleBox)
- {
- mTitleBox->setEnabled(getForeground());
- }
-
- LLView::draw();
-}
-
-void LLDragHandleTop::reshapeTitleBox()
-{
- static LLUICachedControl<S32> title_vpad("UIFloaterTitleVPad", 0);
- if( ! mTitleBox)
- {
- return;
- }
- const LLFontGL* font = LLFontGL::getFontSansSerif();
- S32 title_width = getRect().getWidth();
- title_width -= LEFT_PAD + 2 * BORDER_PAD + getButtonsRect().getWidth();
- S32 title_height = font->getLineHeight();
- LLRect title_rect;
- title_rect.setLeftTopAndSize(
- LEFT_PAD,
- getRect().getHeight() - title_vpad,
- title_width,
- title_height);
-
- // calls reshape on mTitleBox
- mTitleBox->setShape( title_rect );
-}
-
-void LLDragHandleTop::reshape(S32 width, S32 height, bool called_from_parent)
-{
- LLView::reshape(width, height, called_from_parent);
- reshapeTitleBox();
-}
-
-void LLDragHandleLeft::reshape(S32 width, S32 height, bool called_from_parent)
-{
- LLView::reshape(width, height, called_from_parent);
-}
-
-//-------------------------------------------------------------
-// UI event handling
-//-------------------------------------------------------------
-
-bool LLDragHandle::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- // 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);
-
- localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY);
- mLastMouseScreenX = mDragLastScreenX;
- mLastMouseScreenY = mDragLastScreenY;
-
- // Note: don't pass on to children
- return true;
-}
-
-
-bool LLDragHandle::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- if( hasMouseCapture() )
- {
- // Release the mouse
- gFocusMgr.setMouseCapture( NULL );
- }
-
- // Note: don't pass on to children
- return true;
-}
-
-
-bool LLDragHandle::handleHover(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
-
- // We only handle the click if the click both started and ended within us
- if( hasMouseCapture() )
- {
- S32 screen_x;
- S32 screen_y;
- localPointToScreen(x, y, &screen_x, &screen_y);
-
- // Resize the parent
- S32 delta_x = screen_x - mDragLastScreenX;
- S32 delta_y = screen_y - mDragLastScreenY;
-
- // if dragging a docked floater we want to undock
- LLFloater * parent = dynamic_cast<LLFloater *>(getParent());
- if (parent && parent->isDocked())
- {
- const S32 SLOP = 12;
-
- if (delta_y <= -SLOP ||
- delta_y >= SLOP)
- {
- parent->setDocked(false, false);
- return true;
- }
- else
- {
- return false;
- }
- }
-
- LLRect original_rect = getParent()->getRect();
- LLRect translated_rect = getParent()->getRect();
- translated_rect.translate(delta_x, delta_y);
- // temporarily slam dragged window to new position
- getParent()->setRect(translated_rect);
- S32 pre_snap_x = getParent()->getRect().mLeft;
- S32 pre_snap_y = getParent()->getRect().mBottom;
- mDragLastScreenX = screen_x;
- mDragLastScreenY = screen_y;
-
- LLRect new_rect;
- LLCoordGL mouse_dir;
- // use hysteresis on mouse motion to preserve user intent when mouse stops moving
- mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX;
- mouse_dir.mY = (screen_y == mLastMouseScreenY) ? mLastMouseDir.mY : screen_y - mLastMouseScreenY;
- mLastMouseDir = mouse_dir;
- mLastMouseScreenX = screen_x;
- mLastMouseScreenY = screen_y;
-
- LLView* snap_view = getParent()->findSnapRect(new_rect, mouse_dir, SNAP_PARENT_AND_SIBLINGS, sSnapMargin);
-
- getParent()->setSnappedTo(snap_view);
- delta_x = new_rect.mLeft - pre_snap_x;
- delta_y = new_rect.mBottom - pre_snap_y;
- translated_rect.translate(delta_x, delta_y);
-
- // restore original rect so delta are detected, then call user reshape method to handle snapped floaters, etc
- getParent()->setRect(original_rect);
- getParent()->setShape(translated_rect, true);
-
- mDragLastScreenX += delta_x;
- mDragLastScreenY += delta_y;
-
- getWindow()->setCursor(UI_CURSOR_ARROW);
- LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" <<LL_ENDL;
- handled = true;
- }
- else
- {
- getWindow()->setCursor(UI_CURSOR_ARROW);
- LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL;
- handled = true;
- }
-
- // Note: don't pass on to children
-
- return handled;
-}
-
-void LLDragHandle::setValue(const LLSD& value)
-{
- setTitle(value.asString());
-}
+/**
+ * @file lldraghandle.cpp
+ * @brief LLDragHandle 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$
+ */
+
+// A widget for dragging a view around the screen using the mouse.
+
+#include "linden_common.h"
+
+#include "lldraghandle.h"
+
+#include "llmath.h"
+
+//#include "llviewerwindow.h"
+#include "llui.h"
+#include "llmenugl.h"
+#include "lltextbox.h"
+#include "llcontrol.h"
+#include "llfontgl.h"
+#include "llwindow.h"
+#include "llfocusmgr.h"
+#include "lluictrlfactory.h"
+
+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;
+
+S32 LLDragHandle::sSnapMargin = 5;
+
+LLDragHandle::LLDragHandle(const LLDragHandle::Params& p)
+: LLView(p),
+ mDragLastScreenX( 0 ),
+ mDragLastScreenY( 0 ),
+ mLastMouseScreenX( 0 ),
+ mLastMouseScreenY( 0 ),
+ mTitleBox( NULL ),
+ mMaxTitleWidth( 0 ),
+ mForeground( true ),
+ mDragHighlightColor(p.drag_highlight_color()),
+ mDragShadowColor(p.drag_shadow_color())
+
+{
+ static LLUICachedControl<S32> snap_margin ("SnapMargin", 0);
+ sSnapMargin = snap_margin;
+}
+
+LLDragHandle::~LLDragHandle()
+{
+ gFocusMgr.removeKeyboardFocusWithoutCallback(this);
+ removeChild(mTitleBox);
+ delete mTitleBox;
+}
+
+void LLDragHandle::initFromParams(const LLDragHandle::Params& p)
+{
+ LLView::initFromParams(p);
+ setTitle( p.label );
+}
+
+void LLDragHandle::setTitleVisible(bool visible)
+{
+ if(mTitleBox)
+ {
+ mTitleBox->setVisible(visible);
+ }
+}
+
+void LLDragHandleTop::setTitle(const std::string& title)
+{
+ std::string trimmed_title = title;
+ LLStringUtil::trim(trimmed_title);
+
+ if( mTitleBox )
+ {
+ mTitleBox->setText(trimmed_title);
+ }
+ else
+ {
+ const LLFontGL* font = LLFontGL::getFontSansSerif();
+ LLTextBox::Params params;
+ params.name("Drag Handle Title");
+ params.rect(getRect());
+ params.initial_value(trimmed_title);
+ params.font(font);
+ params.follows.flags(FOLLOWS_TOP | FOLLOWS_LEFT | FOLLOWS_RIGHT);
+ params.font_shadow(LLFontGL::DROP_SHADOW_SOFT);
+ params.use_ellipses = true;
+ params.parse_urls = false; //cancel URL replacement in floater title
+ mTitleBox = LLUICtrlFactory::create<LLTextBox> (params);
+ addChild( mTitleBox );
+ }
+
+ reshapeTitleBox();
+}
+
+
+std::string LLDragHandleTop::getTitle() const
+{
+ return mTitleBox == NULL ? LLStringUtil::null : mTitleBox->getText();
+}
+
+
+void LLDragHandleLeft::setTitle(const std::string& )
+{
+ if( mTitleBox )
+ {
+ removeChild(mTitleBox);
+ delete mTitleBox;
+ mTitleBox = NULL;
+ }
+ /* no title on left edge */
+}
+
+
+std::string LLDragHandleLeft::getTitle() const
+{
+ return LLStringUtil::null;
+}
+
+
+void LLDragHandleTop::draw()
+{
+ /* Disable lines. Can drag anywhere in most windows. JC
+ if( getVisible() && getEnabled() && mForeground)
+ {
+ const S32 BORDER_PAD = 2;
+ const S32 HPAD = 2;
+ const S32 VPAD = 2;
+ S32 left = BORDER_PAD + HPAD;
+ S32 top = getRect().getHeight() - 2 * VPAD;
+ S32 right = getRect().getWidth() - HPAD;
+// S32 bottom = VPAD;
+
+ // draw lines for drag areas
+
+ const S32 LINE_SPACING = (DRAG_HANDLE_HEIGHT - 2 * VPAD) / 4;
+ S32 line = top - LINE_SPACING;
+
+ LLRect title_rect = mTitleBox->getRect();
+ S32 title_right = title_rect.mLeft + mTitleWidth;
+ bool show_right_side = title_right < getRect().getWidth();
+
+ for( S32 i=0; i<4; i++ )
+ {
+ gl_line_2d(left, line+1, title_rect.mLeft - LEADING_PAD, line+1, mDragHighlightColor);
+ if( show_right_side )
+ {
+ gl_line_2d(title_right, line+1, right, line+1, mDragHighlightColor);
+ }
+
+ gl_line_2d(left, line, title_rect.mLeft - LEADING_PAD, line, mDragShadowColor);
+ if( show_right_side )
+ {
+ gl_line_2d(title_right, line, right, line, mDragShadowColor);
+ }
+ line -= LINE_SPACING;
+ }
+ }
+ */
+
+ // Colorize the text to match the frontmost state
+ if (mTitleBox)
+ {
+ mTitleBox->setEnabled(getForeground());
+ }
+
+ LLView::draw();
+}
+
+
+// assumes GL state is set for 2D
+void LLDragHandleLeft::draw()
+{
+ /* Disable lines. Can drag anywhere in most windows. JC
+ if( getVisible() && getEnabled() && mForeground )
+ {
+ const S32 BORDER_PAD = 2;
+// const S32 HPAD = 2;
+ const S32 VPAD = 2;
+ const S32 LINE_SPACING = 3;
+
+ S32 left = BORDER_PAD + LINE_SPACING;
+ S32 top = getRect().getHeight() - 2 * VPAD;
+// S32 right = getRect().getWidth() - HPAD;
+ S32 bottom = VPAD;
+
+ // draw lines for drag areas
+
+ // no titles yet
+ //LLRect title_rect = mTitleBox->getRect();
+ //S32 title_right = title_rect.mLeft + mTitleWidth;
+ //bool show_right_side = title_right < getRect().getWidth();
+
+ S32 line = left;
+ for( S32 i=0; i<4; i++ )
+ {
+ gl_line_2d(line, top, line, bottom, mDragHighlightColor);
+
+ gl_line_2d(line+1, top, line+1, bottom, mDragShadowColor);
+
+ line += LINE_SPACING;
+ }
+ }
+ */
+
+ // Colorize the text to match the frontmost state
+ if (mTitleBox)
+ {
+ mTitleBox->setEnabled(getForeground());
+ }
+
+ LLView::draw();
+}
+
+void LLDragHandleTop::reshapeTitleBox()
+{
+ static LLUICachedControl<S32> title_vpad("UIFloaterTitleVPad", 0);
+ if( ! mTitleBox)
+ {
+ return;
+ }
+ const LLFontGL* font = LLFontGL::getFontSansSerif();
+ S32 title_width = getRect().getWidth();
+ title_width -= LEFT_PAD + 2 * BORDER_PAD + getButtonsRect().getWidth();
+ S32 title_height = font->getLineHeight();
+ LLRect title_rect;
+ title_rect.setLeftTopAndSize(
+ LEFT_PAD,
+ getRect().getHeight() - title_vpad,
+ title_width,
+ title_height);
+
+ // calls reshape on mTitleBox
+ mTitleBox->setShape( title_rect );
+}
+
+void LLDragHandleTop::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ LLView::reshape(width, height, called_from_parent);
+ reshapeTitleBox();
+}
+
+void LLDragHandleLeft::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ LLView::reshape(width, height, called_from_parent);
+}
+
+//-------------------------------------------------------------
+// UI event handling
+//-------------------------------------------------------------
+
+bool LLDragHandle::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // 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);
+
+ localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY);
+ mLastMouseScreenX = mDragLastScreenX;
+ mLastMouseScreenY = mDragLastScreenY;
+
+ // Note: don't pass on to children
+ return true;
+}
+
+
+bool LLDragHandle::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ if( hasMouseCapture() )
+ {
+ // Release the mouse
+ gFocusMgr.setMouseCapture( NULL );
+ }
+
+ // Note: don't pass on to children
+ return true;
+}
+
+
+bool LLDragHandle::handleHover(S32 x, S32 y, MASK mask)
+{
+ bool handled = false;
+
+ // We only handle the click if the click both started and ended within us
+ if( hasMouseCapture() )
+ {
+ S32 screen_x;
+ S32 screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y);
+
+ // Resize the parent
+ S32 delta_x = screen_x - mDragLastScreenX;
+ S32 delta_y = screen_y - mDragLastScreenY;
+
+ // if dragging a docked floater we want to undock
+ LLFloater * parent = dynamic_cast<LLFloater *>(getParent());
+ if (parent && parent->isDocked())
+ {
+ const S32 SLOP = 12;
+
+ if (delta_y <= -SLOP ||
+ delta_y >= SLOP)
+ {
+ parent->setDocked(false, false);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ LLRect original_rect = getParent()->getRect();
+ LLRect translated_rect = getParent()->getRect();
+ translated_rect.translate(delta_x, delta_y);
+ // temporarily slam dragged window to new position
+ getParent()->setRect(translated_rect);
+ S32 pre_snap_x = getParent()->getRect().mLeft;
+ S32 pre_snap_y = getParent()->getRect().mBottom;
+ mDragLastScreenX = screen_x;
+ mDragLastScreenY = screen_y;
+
+ LLRect new_rect;
+ LLCoordGL mouse_dir;
+ // use hysteresis on mouse motion to preserve user intent when mouse stops moving
+ mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX;
+ mouse_dir.mY = (screen_y == mLastMouseScreenY) ? mLastMouseDir.mY : screen_y - mLastMouseScreenY;
+ mLastMouseDir = mouse_dir;
+ mLastMouseScreenX = screen_x;
+ mLastMouseScreenY = screen_y;
+
+ LLView* snap_view = getParent()->findSnapRect(new_rect, mouse_dir, SNAP_PARENT_AND_SIBLINGS, sSnapMargin);
+
+ getParent()->setSnappedTo(snap_view);
+ delta_x = new_rect.mLeft - pre_snap_x;
+ delta_y = new_rect.mBottom - pre_snap_y;
+ translated_rect.translate(delta_x, delta_y);
+
+ // restore original rect so delta are detected, then call user reshape method to handle snapped floaters, etc
+ getParent()->setRect(original_rect);
+ getParent()->setShape(translated_rect, true);
+
+ mDragLastScreenX += delta_x;
+ mDragLastScreenY += delta_y;
+
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" <<LL_ENDL;
+ handled = true;
+ }
+ else
+ {
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL;
+ handled = true;
+ }
+
+ // Note: don't pass on to children
+
+ return handled;
+}
+
+void LLDragHandle::setValue(const LLSD& value)
+{
+ setTitle(value.asString());
+}
diff --git a/indra/llui/lldraghandle.h b/indra/llui/lldraghandle.h
index bb5ee43a70..e7e9469acb 100644
--- a/indra/llui/lldraghandle.h
+++ b/indra/llui/lldraghandle.h
@@ -1,139 +1,139 @@
-/**
- * @file lldraghandle.h
- * @brief LLDragHandle 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$
- */
-
-// A widget for dragging a view around the screen using the mouse.
-
-#ifndef LL_DRAGHANDLE_H
-#define LL_DRAGHANDLE_H
-
-#include "llview.h"
-#include "v4color.h"
-#include "llrect.h"
-#include "llcoord.h"
-
-class LLTextBox;
-
-class LLDragHandle : public LLView
-{
-public:
- struct Params
- : public LLInitParam::Block<Params, LLView::Params>
- {
- Optional<std::string> label;
- Optional<LLUIColor> drag_highlight_color;
- Optional<LLUIColor> drag_shadow_color;
-
- Params()
- : label("label"),
- drag_highlight_color("drag_highlight_color", LLUIColorTable::instance().getColor("DefaultHighlightLight")),
- drag_shadow_color("drag_shadow_color", LLUIColorTable::instance().getColor("DefaultShadowDark"))
- {
- changeDefault(mouse_opaque, true);
- changeDefault(follows.flags, FOLLOWS_ALL);
- }
- };
- void initFromParams(const Params&);
-
- virtual ~LLDragHandle();
-
- virtual void setValue(const LLSD& value);
-
- void setForeground(bool b) { mForeground = b; }
- bool getForeground() const { return mForeground; }
- void setMaxTitleWidth(S32 max_width) {mMaxTitleWidth = llmin(max_width, mMaxTitleWidth); }
- S32 getMaxTitleWidth() const { return mMaxTitleWidth; }
- void setButtonsRect(const LLRect& rect){ mButtonsRect = rect; }
- LLRect getButtonsRect() { return mButtonsRect; }
- void setTitleVisible(bool visible);
-
- virtual void setTitle( const std::string& title ) = 0;
- virtual std::string getTitle() const = 0;
-
- 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);
-
-protected:
- LLDragHandle(const Params&);
- friend class LLUICtrlFactory;
-
-protected:
- LLTextBox* mTitleBox;
-
-private:
- LLRect mButtonsRect;
- S32 mDragLastScreenX;
- S32 mDragLastScreenY;
- S32 mLastMouseScreenX;
- S32 mLastMouseScreenY;
- LLCoordGL mLastMouseDir;
- LLUIColor mDragHighlightColor;
- LLUIColor mDragShadowColor;
- S32 mMaxTitleWidth;
- bool mForeground;
-
- // Pixels near the edge to snap floaters.
- static S32 sSnapMargin;
-};
-
-
-// Use this one for traditional top-of-window draggers
-class LLDragHandleTop
-: public LLDragHandle
-{
-protected:
- LLDragHandleTop(const Params& p) : LLDragHandle(p) {}
- friend class LLUICtrlFactory;
-public:
- virtual void setTitle( const std::string& title );
- virtual std::string getTitle() const;
- virtual void draw();
- virtual void reshape(S32 width, S32 height, bool called_from_parent = true);
-
-private:
- void reshapeTitleBox();
-};
-
-
-// Use this for left-side, vertical text draggers
-class LLDragHandleLeft
-: public LLDragHandle
-{
-protected:
- LLDragHandleLeft(const Params& p) : LLDragHandle(p) {}
- friend class LLUICtrlFactory;
-public:
- virtual void setTitle( const std::string& title );
- virtual std::string getTitle() const;
- virtual void draw();
- virtual void reshape(S32 width, S32 height, bool called_from_parent = true);
-
-};
-
-const S32 DRAG_HANDLE_HEIGHT = 16;
-const S32 DRAG_HANDLE_WIDTH = 16;
-
-#endif // LL_DRAGHANDLE_H
+/**
+ * @file lldraghandle.h
+ * @brief LLDragHandle 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$
+ */
+
+// A widget for dragging a view around the screen using the mouse.
+
+#ifndef LL_DRAGHANDLE_H
+#define LL_DRAGHANDLE_H
+
+#include "llview.h"
+#include "v4color.h"
+#include "llrect.h"
+#include "llcoord.h"
+
+class LLTextBox;
+
+class LLDragHandle : public LLView
+{
+public:
+ struct Params
+ : public LLInitParam::Block<Params, LLView::Params>
+ {
+ Optional<std::string> label;
+ Optional<LLUIColor> drag_highlight_color;
+ Optional<LLUIColor> drag_shadow_color;
+
+ Params()
+ : label("label"),
+ drag_highlight_color("drag_highlight_color", LLUIColorTable::instance().getColor("DefaultHighlightLight")),
+ drag_shadow_color("drag_shadow_color", LLUIColorTable::instance().getColor("DefaultShadowDark"))
+ {
+ changeDefault(mouse_opaque, true);
+ changeDefault(follows.flags, FOLLOWS_ALL);
+ }
+ };
+ void initFromParams(const Params&);
+
+ virtual ~LLDragHandle();
+
+ virtual void setValue(const LLSD& value);
+
+ void setForeground(bool b) { mForeground = b; }
+ bool getForeground() const { return mForeground; }
+ void setMaxTitleWidth(S32 max_width) {mMaxTitleWidth = llmin(max_width, mMaxTitleWidth); }
+ S32 getMaxTitleWidth() const { return mMaxTitleWidth; }
+ void setButtonsRect(const LLRect& rect){ mButtonsRect = rect; }
+ LLRect getButtonsRect() { return mButtonsRect; }
+ void setTitleVisible(bool visible);
+
+ virtual void setTitle( const std::string& title ) = 0;
+ virtual std::string getTitle() const = 0;
+
+ 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);
+
+protected:
+ LLDragHandle(const Params&);
+ friend class LLUICtrlFactory;
+
+protected:
+ LLTextBox* mTitleBox;
+
+private:
+ LLRect mButtonsRect;
+ S32 mDragLastScreenX;
+ S32 mDragLastScreenY;
+ S32 mLastMouseScreenX;
+ S32 mLastMouseScreenY;
+ LLCoordGL mLastMouseDir;
+ LLUIColor mDragHighlightColor;
+ LLUIColor mDragShadowColor;
+ S32 mMaxTitleWidth;
+ bool mForeground;
+
+ // Pixels near the edge to snap floaters.
+ static S32 sSnapMargin;
+};
+
+
+// Use this one for traditional top-of-window draggers
+class LLDragHandleTop
+: public LLDragHandle
+{
+protected:
+ LLDragHandleTop(const Params& p) : LLDragHandle(p) {}
+ friend class LLUICtrlFactory;
+public:
+ virtual void setTitle( const std::string& title );
+ virtual std::string getTitle() const;
+ virtual void draw();
+ virtual void reshape(S32 width, S32 height, bool called_from_parent = true);
+
+private:
+ void reshapeTitleBox();
+};
+
+
+// Use this for left-side, vertical text draggers
+class LLDragHandleLeft
+: public LLDragHandle
+{
+protected:
+ LLDragHandleLeft(const Params& p) : LLDragHandle(p) {}
+ friend class LLUICtrlFactory;
+public:
+ virtual void setTitle( const std::string& title );
+ virtual std::string getTitle() const;
+ virtual void draw();
+ virtual void reshape(S32 width, S32 height, bool called_from_parent = true);
+
+};
+
+const S32 DRAG_HANDLE_HEIGHT = 16;
+const S32 DRAG_HANDLE_WIDTH = 16;
+
+#endif // LL_DRAGHANDLE_H
diff --git a/indra/llui/lleditmenuhandler.cpp b/indra/llui/lleditmenuhandler.cpp
index d48237e377..402628bc6b 100644
--- a/indra/llui/lleditmenuhandler.cpp
+++ b/indra/llui/lleditmenuhandler.cpp
@@ -1,25 +1,25 @@
-/**
+/**
* @file lleditmenuhandler.cpp
* @authors Aaron Yonas, James Cook
*
* $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$
*/
@@ -33,8 +33,8 @@ LLEditMenuHandler* LLEditMenuHandler::gEditMenuHandler = NULL;
LLEditMenuHandler::~LLEditMenuHandler()
{
- if (gEditMenuHandler == this)
- {
- gEditMenuHandler = NULL;
- }
+ if (gEditMenuHandler == this)
+ {
+ gEditMenuHandler = NULL;
+ }
}
diff --git a/indra/llui/lleditmenuhandler.h b/indra/llui/lleditmenuhandler.h
index 32e9aac20b..3770502058 100644
--- a/indra/llui/lleditmenuhandler.h
+++ b/indra/llui/lleditmenuhandler.h
@@ -1,71 +1,71 @@
-/**
-* @file lleditmenuhandler.h
-* @authors Aaron Yonas, James Cook
-*
-* $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 LLEDITMENUHANDLER_H
-#define LLEDITMENUHANDLER_H
-
-// Interface used by menu system for plug-in hotkey/menu handling
-class LLEditMenuHandler
-{
-public:
- // this is needed even though this is just an interface class.
- virtual ~LLEditMenuHandler();
-
- virtual void undo() {};
- virtual bool canUndo() const { return false; }
-
- virtual void redo() {};
- virtual bool canRedo() const { return false; }
-
- virtual void cut() {};
- virtual bool canCut() const { return false; }
-
- virtual void copy() {};
- virtual bool canCopy() const { return false; }
-
- virtual void paste() {};
- virtual bool canPaste() const { return false; }
-
- // "delete" is a keyword
- virtual void doDelete() {};
- virtual bool canDoDelete() const { return false; }
-
- virtual void selectAll() {};
- virtual bool canSelectAll() const { return false; }
-
- virtual void deselect() {};
- virtual bool canDeselect() const { return false; }
-
- // TODO: Instead of being a public data member, it would be better to hide it altogether
- // and have a "set" method and then a bunch of static versions of the cut, copy, paste
- // methods, etc that operate on the current global instance. That would drastically
- // simplify the existing code that accesses this global variable by putting all the
- // null checks in the one implementation of those static methods. -MG
- static LLEditMenuHandler* gEditMenuHandler;
-};
-
-
-#endif
+/**
+* @file lleditmenuhandler.h
+* @authors Aaron Yonas, James Cook
+*
+* $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 LLEDITMENUHANDLER_H
+#define LLEDITMENUHANDLER_H
+
+// Interface used by menu system for plug-in hotkey/menu handling
+class LLEditMenuHandler
+{
+public:
+ // this is needed even though this is just an interface class.
+ virtual ~LLEditMenuHandler();
+
+ virtual void undo() {};
+ virtual bool canUndo() const { return false; }
+
+ virtual void redo() {};
+ virtual bool canRedo() const { return false; }
+
+ virtual void cut() {};
+ virtual bool canCut() const { return false; }
+
+ virtual void copy() {};
+ virtual bool canCopy() const { return false; }
+
+ virtual void paste() {};
+ virtual bool canPaste() const { return false; }
+
+ // "delete" is a keyword
+ virtual void doDelete() {};
+ virtual bool canDoDelete() const { return false; }
+
+ virtual void selectAll() {};
+ virtual bool canSelectAll() const { return false; }
+
+ virtual void deselect() {};
+ virtual bool canDeselect() const { return false; }
+
+ // TODO: Instead of being a public data member, it would be better to hide it altogether
+ // and have a "set" method and then a bunch of static versions of the cut, copy, paste
+ // methods, etc that operate on the current global instance. That would drastically
+ // simplify the existing code that accesses this global variable by putting all the
+ // null checks in the one implementation of those static methods. -MG
+ static LLEditMenuHandler* gEditMenuHandler;
+};
+
+
+#endif
diff --git a/indra/llui/llemojihelper.cpp b/indra/llui/llemojihelper.cpp
index 89e6ddf987..fbe313924c 100644
--- a/indra/llui/llemojihelper.cpp
+++ b/indra/llui/llemojihelper.cpp
@@ -46,124 +46,124 @@ constexpr S32 HELPER_FLOATER_OFFSET_Y = 0;
std::string LLEmojiHelper::getToolTip(llwchar ch) const
{
- return LLEmojiDictionary::instance().getNameFromEmoji(ch);
+ return LLEmojiDictionary::instance().getNameFromEmoji(ch);
}
bool LLEmojiHelper::isActive(const LLUICtrl* ctrl_p) const
{
- return mHostHandle.get() == ctrl_p;
+ return mHostHandle.get() == ctrl_p;
}
// static
bool LLEmojiHelper::isCursorInEmojiCode(const LLWString& wtext, S32 cursorPos, S32* pShortCodePos)
{
- // If the cursor is currently on a colon start the check one character further back
- S32 shortCodePos = (cursorPos == 0 || L':' != wtext[cursorPos - 1]) ? cursorPos : cursorPos - 1;
-
- auto isPartOfShortcode = [](llwchar ch) {
- switch (ch)
- {
- case L'-':
- case L'_':
- case L'+':
- return true;
- default:
- return LLStringOps::isAlnum(ch);
- }
- };
- while (shortCodePos > 1 && isPartOfShortcode(wtext[shortCodePos - 1]))
- {
- shortCodePos--;
- }
-
- bool isShortCode = (L':' == wtext[shortCodePos - 1]) && (cursorPos - shortCodePos >= 2);
- if (pShortCodePos)
- *pShortCodePos = (isShortCode) ? shortCodePos - 1 : -1;
- return isShortCode;
+ // If the cursor is currently on a colon start the check one character further back
+ S32 shortCodePos = (cursorPos == 0 || L':' != wtext[cursorPos - 1]) ? cursorPos : cursorPos - 1;
+
+ auto isPartOfShortcode = [](llwchar ch) {
+ switch (ch)
+ {
+ case L'-':
+ case L'_':
+ case L'+':
+ return true;
+ default:
+ return LLStringOps::isAlnum(ch);
+ }
+ };
+ while (shortCodePos > 1 && isPartOfShortcode(wtext[shortCodePos - 1]))
+ {
+ shortCodePos--;
+ }
+
+ bool isShortCode = (L':' == wtext[shortCodePos - 1]) && (cursorPos - shortCodePos >= 2);
+ if (pShortCodePos)
+ *pShortCodePos = (isShortCode) ? shortCodePos - 1 : -1;
+ return isShortCode;
}
void LLEmojiHelper::showHelper(LLUICtrl* hostctrl_p, S32 local_x, S32 local_y, const std::string& short_code, std::function<void(llwchar)> cb)
{
- // Commit immediately if the user already typed a full shortcode
- if (const auto* emojiDescrp = LLEmojiDictionary::instance().getDescriptorFromShortCode(short_code))
- {
- cb(emojiDescrp->Character);
- hideHelper();
- return;
- }
-
- if (mHelperHandle.isDead())
- {
- LLFloater* pHelperFloater = LLFloaterReg::getInstance(DEFAULT_EMOJI_HELPER_FLOATER);
- mHelperHandle = pHelperFloater->getHandle();
- mHelperCommitConn = pHelperFloater->setCommitCallback(std::bind([&](const LLSD& sdValue) { onCommitEmoji(utf8str_to_wstring(sdValue.asStringRef())[0]); }, std::placeholders::_2));
- }
- setHostCtrl(hostctrl_p);
- mEmojiCommitCb = cb;
-
- S32 floater_x, floater_y;
- if (!hostctrl_p->localPointToOtherView(local_x, local_y, &floater_x, &floater_y, gFloaterView))
- {
- LL_ERRS() << "Cannot show emoji helper for non-floater controls." << LL_ENDL;
- return;
- }
-
- LLFloater* pHelperFloater = mHelperHandle.get();
- LLRect rect = pHelperFloater->getRect();
- S32 left = floater_x - HELPER_FLOATER_OFFSET_X;
- S32 top = floater_y - HELPER_FLOATER_OFFSET_Y + rect.getHeight();
- rect.setLeftTopAndSize(left, top, rect.getWidth(), rect.getHeight());
- pHelperFloater->setRect(rect);
- pHelperFloater->openFloater(LLSD().with("hint", short_code));
+ // Commit immediately if the user already typed a full shortcode
+ if (const auto* emojiDescrp = LLEmojiDictionary::instance().getDescriptorFromShortCode(short_code))
+ {
+ cb(emojiDescrp->Character);
+ hideHelper();
+ return;
+ }
+
+ if (mHelperHandle.isDead())
+ {
+ LLFloater* pHelperFloater = LLFloaterReg::getInstance(DEFAULT_EMOJI_HELPER_FLOATER);
+ mHelperHandle = pHelperFloater->getHandle();
+ mHelperCommitConn = pHelperFloater->setCommitCallback(std::bind([&](const LLSD& sdValue) { onCommitEmoji(utf8str_to_wstring(sdValue.asStringRef())[0]); }, std::placeholders::_2));
+ }
+ setHostCtrl(hostctrl_p);
+ mEmojiCommitCb = cb;
+
+ S32 floater_x, floater_y;
+ if (!hostctrl_p->localPointToOtherView(local_x, local_y, &floater_x, &floater_y, gFloaterView))
+ {
+ LL_ERRS() << "Cannot show emoji helper for non-floater controls." << LL_ENDL;
+ return;
+ }
+
+ LLFloater* pHelperFloater = mHelperHandle.get();
+ LLRect rect = pHelperFloater->getRect();
+ S32 left = floater_x - HELPER_FLOATER_OFFSET_X;
+ S32 top = floater_y - HELPER_FLOATER_OFFSET_Y + rect.getHeight();
+ rect.setLeftTopAndSize(left, top, rect.getWidth(), rect.getHeight());
+ pHelperFloater->setRect(rect);
+ pHelperFloater->openFloater(LLSD().with("hint", short_code));
}
void LLEmojiHelper::hideHelper(const LLUICtrl* ctrl_p, bool strict)
{
- mIsHideDisabled &= !strict;
- if (mIsHideDisabled || (ctrl_p && !isActive(ctrl_p)))
- {
- return;
- }
+ mIsHideDisabled &= !strict;
+ if (mIsHideDisabled || (ctrl_p && !isActive(ctrl_p)))
+ {
+ return;
+ }
- setHostCtrl(nullptr);
+ setHostCtrl(nullptr);
}
bool LLEmojiHelper::handleKey(const LLUICtrl* ctrl_p, KEY key, MASK mask)
{
- if (mHelperHandle.isDead() || !isActive(ctrl_p))
- {
- return false;
- }
+ if (mHelperHandle.isDead() || !isActive(ctrl_p))
+ {
+ return false;
+ }
- return mHelperHandle.get()->handleKey(key, mask, true);
+ return mHelperHandle.get()->handleKey(key, mask, true);
}
void LLEmojiHelper::onCommitEmoji(llwchar emoji)
{
- if (!mHostHandle.isDead() && mEmojiCommitCb)
- {
- mEmojiCommitCb(emoji);
- }
+ if (!mHostHandle.isDead() && mEmojiCommitCb)
+ {
+ mEmojiCommitCb(emoji);
+ }
}
void LLEmojiHelper::setHostCtrl(LLUICtrl* hostctrl_p)
{
- const LLUICtrl* pCurHostCtrl = mHostHandle.get();
- if (pCurHostCtrl != hostctrl_p)
- {
- mHostCtrlFocusLostConn.disconnect();
- mHostHandle.markDead();
- mEmojiCommitCb = {};
-
- if (!mHelperHandle.isDead())
- {
- mHelperHandle.get()->closeFloater();
- }
-
- if (hostctrl_p)
- {
- mHostHandle = hostctrl_p->getHandle();
- mHostCtrlFocusLostConn = hostctrl_p->setFocusLostCallback(std::bind([&]() { hideHelper(getHostCtrl()); }));
- }
- }
+ const LLUICtrl* pCurHostCtrl = mHostHandle.get();
+ if (pCurHostCtrl != hostctrl_p)
+ {
+ mHostCtrlFocusLostConn.disconnect();
+ mHostHandle.markDead();
+ mEmojiCommitCb = {};
+
+ if (!mHelperHandle.isDead())
+ {
+ mHelperHandle.get()->closeFloater();
+ }
+
+ if (hostctrl_p)
+ {
+ mHostHandle = hostctrl_p->getHandle();
+ mHostCtrlFocusLostConn = hostctrl_p->setFocusLostCallback(std::bind([&]() { hideHelper(getHostCtrl()); }));
+ }
+ }
}
diff --git a/indra/llui/llemojihelper.h b/indra/llui/llemojihelper.h
index e826ff93e6..2834b06061 100644
--- a/indra/llui/llemojihelper.h
+++ b/indra/llui/llemojihelper.h
@@ -36,31 +36,31 @@ class LLUICtrl;
class LLEmojiHelper : public LLSingleton<LLEmojiHelper>
{
- LLSINGLETON(LLEmojiHelper) {}
- ~LLEmojiHelper() override {}
+ LLSINGLETON(LLEmojiHelper) {}
+ ~LLEmojiHelper() override {}
public:
- // General
- std::string getToolTip(llwchar ch) const;
- bool isActive(const LLUICtrl* ctrl_p) const;
- static bool isCursorInEmojiCode(const LLWString& wtext, S32 cursor_pos, S32* short_code_pos_p = nullptr);
- void showHelper(LLUICtrl* hostctrl_p, S32 local_x, S32 local_y, const std::string& short_code, std::function<void(llwchar)> commit_cb);
- void hideHelper(const LLUICtrl* ctrl_p = nullptr, bool strict = false);
- void setIsHideDisabled(bool disabled) { mIsHideDisabled = disabled; };
+ // General
+ std::string getToolTip(llwchar ch) const;
+ bool isActive(const LLUICtrl* ctrl_p) const;
+ static bool isCursorInEmojiCode(const LLWString& wtext, S32 cursor_pos, S32* short_code_pos_p = nullptr);
+ void showHelper(LLUICtrl* hostctrl_p, S32 local_x, S32 local_y, const std::string& short_code, std::function<void(llwchar)> commit_cb);
+ void hideHelper(const LLUICtrl* ctrl_p = nullptr, bool strict = false);
+ void setIsHideDisabled(bool disabled) { mIsHideDisabled = disabled; };
- // Eventing
- bool handleKey(const LLUICtrl* ctrl_p, KEY key, MASK mask);
- void onCommitEmoji(llwchar emoji);
+ // Eventing
+ bool handleKey(const LLUICtrl* ctrl_p, KEY key, MASK mask);
+ void onCommitEmoji(llwchar emoji);
protected:
- LLUICtrl* getHostCtrl() const { return mHostHandle.get(); }
- void setHostCtrl(LLUICtrl* hostctrl_p);
+ LLUICtrl* getHostCtrl() const { return mHostHandle.get(); }
+ void setHostCtrl(LLUICtrl* hostctrl_p);
private:
- LLHandle<LLUICtrl> mHostHandle;
- LLHandle<LLFloater> mHelperHandle;
- boost::signals2::connection mHostCtrlFocusLostConn;
- boost::signals2::connection mHelperCommitConn;
- std::function<void(llwchar)> mEmojiCommitCb;
- bool mIsHideDisabled;
+ LLHandle<LLUICtrl> mHostHandle;
+ LLHandle<LLFloater> mHelperHandle;
+ boost::signals2::connection mHostCtrlFocusLostConn;
+ boost::signals2::connection mHelperCommitConn;
+ std::function<void(llwchar)> mEmojiCommitCb;
+ bool mIsHideDisabled;
};
diff --git a/indra/llui/llf32uictrl.cpp b/indra/llui/llf32uictrl.cpp
index d186f085b4..52954ebbbb 100644
--- a/indra/llui/llf32uictrl.cpp
+++ b/indra/llui/llf32uictrl.cpp
@@ -3,25 +3,25 @@
* @author Nat Goodspeed
* @date 2008-09-08
* @brief Implementation for llf32uictrl.
- *
+ *
* $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$
*/
@@ -36,13 +36,13 @@
// other Linden headers
LLF32UICtrl::LLF32UICtrl(const Params& p)
-: LLUICtrl(p),
- mInitialValue(p.initial_value().asReal()),
- mMinValue(p.min_value),
- mMaxValue(p.max_value),
+: LLUICtrl(p),
+ mInitialValue(p.initial_value().asReal()),
+ mMinValue(p.min_value),
+ mMaxValue(p.max_value),
mIncrement(p.increment)
{
- mViewModel->setValue(p.initial_value);
+ mViewModel->setValue(p.initial_value);
}
F32 LLF32UICtrl::getValueF32() const
diff --git a/indra/llui/llf32uictrl.h b/indra/llui/llf32uictrl.h
index 6e648f9102..08b9ec7716 100644
--- a/indra/llui/llf32uictrl.h
+++ b/indra/llui/llf32uictrl.h
@@ -3,25 +3,25 @@
* @author Nat Goodspeed
* @date 2008-09-08
* @brief Base class for float-valued LLUICtrl widgets
- *
+ *
* $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$
*/
@@ -36,42 +36,42 @@ class LLF32UICtrl: public LLUICtrl
public:
struct Params: public LLInitParam::Block<Params, LLUICtrl::Params>
{
- Optional<F32> min_value,
- max_value,
- increment;
+ Optional<F32> min_value,
+ max_value,
+ increment;
- Params()
- : min_value("min_val", 0.f),
- max_value("max_val", 1.f),
- increment("increment", 0.1f)
- {}
+ Params()
+ : min_value("min_val", 0.f),
+ max_value("max_val", 1.f),
+ increment("increment", 0.1f)
+ {}
};
protected:
LLF32UICtrl(const Params& p);
public:
- virtual F32 getValueF32() const;
+ virtual F32 getValueF32() const;
- virtual void setValue(const LLSD& value ) { mViewModel->setValue(value); }
- virtual LLSD getValue() const { return LLSD(getValueF32()); }
+ virtual void setValue(const LLSD& value ) { mViewModel->setValue(value); }
+ virtual LLSD getValue() const { return LLSD(getValueF32()); }
- virtual void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); }
- virtual void setMaxValue(const LLSD& max_value) { setMaxValue((F32)max_value.asReal()); }
+ virtual void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); }
+ virtual void setMaxValue(const LLSD& max_value) { setMaxValue((F32)max_value.asReal()); }
- virtual F32 getInitialValue() const { return mInitialValue; }
- virtual F32 getMinValue() const { return mMinValue; }
- virtual F32 getMaxValue() const { return mMaxValue; }
- virtual F32 getIncrement() const { return mIncrement; }
- virtual void setMinValue(F32 min_value) { mMinValue = min_value; }
- virtual void setMaxValue(F32 max_value) { mMaxValue = max_value; }
- virtual void setIncrement(F32 increment) { mIncrement = increment;}
+ virtual F32 getInitialValue() const { return mInitialValue; }
+ virtual F32 getMinValue() const { return mMinValue; }
+ virtual F32 getMaxValue() const { return mMaxValue; }
+ virtual F32 getIncrement() const { return mIncrement; }
+ virtual void setMinValue(F32 min_value) { mMinValue = min_value; }
+ virtual void setMaxValue(F32 max_value) { mMaxValue = max_value; }
+ virtual void setIncrement(F32 increment) { mIncrement = increment;}
protected:
- F32 mInitialValue;
- F32 mMinValue;
- F32 mMaxValue;
- F32 mIncrement;
+ F32 mInitialValue;
+ F32 mMinValue;
+ F32 mMaxValue;
+ F32 mIncrement;
};
#endif /* ! defined(LL_LLF32UICTRL_H) */
diff --git a/indra/llui/llfiltereditor.cpp b/indra/llui/llfiltereditor.cpp
index 6488bd3941..1311914131 100644
--- a/indra/llui/llfiltereditor.cpp
+++ b/indra/llui/llfiltereditor.cpp
@@ -1,46 +1,46 @@
-/**
- * @file llfiltereditor.cpp
- * @brief LLFilterEditor implementation
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-// Text editor widget to let users enter a single line.
-
-#include "linden_common.h"
-
-#include "llfiltereditor.h"
-
-LLFilterEditor::LLFilterEditor(const LLFilterEditor::Params& p)
-: LLSearchEditor(p)
-{
- setCommitOnFocusLost(false); // we'll commit on every keystroke, don't re-commit when we take focus away (i.e. we go to interact with the actual results!)
-}
-
-
-void LLFilterEditor::handleKeystroke()
-{
- this->LLSearchEditor::handleKeystroke();
-
- // Commit on every keystroke.
- onCommit();
-}
+/**
+ * @file llfiltereditor.cpp
+ * @brief LLFilterEditor implementation
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+// Text editor widget to let users enter a single line.
+
+#include "linden_common.h"
+
+#include "llfiltereditor.h"
+
+LLFilterEditor::LLFilterEditor(const LLFilterEditor::Params& p)
+: LLSearchEditor(p)
+{
+ setCommitOnFocusLost(false); // we'll commit on every keystroke, don't re-commit when we take focus away (i.e. we go to interact with the actual results!)
+}
+
+
+void LLFilterEditor::handleKeystroke()
+{
+ this->LLSearchEditor::handleKeystroke();
+
+ // Commit on every keystroke.
+ onCommit();
+}
diff --git a/indra/llui/llfiltereditor.h b/indra/llui/llfiltereditor.h
index 52cad3bff4..686827d94c 100644
--- a/indra/llui/llfiltereditor.h
+++ b/indra/llui/llfiltereditor.h
@@ -1,34 +1,34 @@
-/**
+/**
* @file llfiltereditor.h
* @brief Text editor widget that represents a filter operation
*
- * Features:
- * Text entry of a single line (text, delete, left and right arrow, insert, return).
- * Callbacks either on every keystroke or just on the return key.
- * Focus (allow multiple text entry widgets)
- * Clipboard (cut, copy, and paste)
- * Horizontal scrolling to allow strings longer than widget size allows
- * Pre-validation (limit which keys can be used)
- * Optional line history so previous entries can be recalled by CTRL UP/DOWN
+ * Features:
+ * Text entry of a single line (text, delete, left and right arrow, insert, return).
+ * Callbacks either on every keystroke or just on the return key.
+ * Focus (allow multiple text entry widgets)
+ * Clipboard (cut, copy, and paste)
+ * Horizontal scrolling to allow strings longer than widget size allows
+ * Pre-validation (limit which keys can be used)
+ * Optional line history so previous entries can be recalled by CTRL UP/DOWN
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -41,15 +41,15 @@
class LLFilterEditor : public LLSearchEditor
{
public:
- struct Params : public LLInitParam::Block<Params, LLSearchEditor::Params>
- {};
+ struct Params : public LLInitParam::Block<Params, LLSearchEditor::Params>
+ {};
virtual ~LLFilterEditor() {}
protected:
- LLFilterEditor(const Params&);
- friend class LLUICtrlFactory;
+ LLFilterEditor(const Params&);
+ friend class LLUICtrlFactory;
- /*virtual*/ void handleKeystroke();
+ /*virtual*/ void handleKeystroke();
};
#endif // LL_FILTEREDITOR_H
diff --git a/indra/llui/llflashtimer.cpp b/indra/llui/llflashtimer.cpp
index d8418f1182..1506279232 100644
--- a/indra/llui/llflashtimer.cpp
+++ b/indra/llui/llflashtimer.cpp
@@ -1,98 +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::getInstance()->mSettingGroups["config"]->getS32("FlashCount"));
- if (mPeriod <= 0)
- {
- mPeriod = LLUI::getInstance()->mSettingGroups["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;
-}
-
-
+/**
+ * @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::getInstance()->mSettingGroups["config"]->getS32("FlashCount"));
+ if (mPeriod <= 0)
+ {
+ mPeriod = LLUI::getInstance()->mSettingGroups["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
index 50c51c0d2a..6da6f55deb 100644
--- a/indra/llui/llflashtimer.h
+++ b/indra/llui/llflashtimer.h
@@ -1,74 +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 */
+/**
+ * @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 a761f54c4d..67a0068b2f 100644
--- a/indra/llui/llflatlistview.cpp
+++ b/indra/llui/llflatlistview.cpp
@@ -1,1439 +1,1439 @@
-/**
- * @file llflatlistview.cpp
- * @brief LLFlatListView base class and extension to support messages for several cases of an empty list.
- *
- * $LicenseInfo:firstyear=2009&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llpanel.h"
-#include "lltextbox.h"
-
-#include "llflatlistview.h"
-
-static const LLDefaultChildRegistry::Register<LLFlatListView> flat_list_view("flat_list_view");
-
-const LLSD SELECTED_EVENT = LLSD().with("selected", true);
-const LLSD UNSELECTED_EVENT = LLSD().with("selected", false);
-
-//forward declaration
-bool llsds_are_equal(const LLSD& llsd_1, const LLSD& llsd_2);
-
-LLFlatListView::Params::Params()
-: item_pad("item_pad"),
- allow_select("allow_select"),
- multi_select("multi_select"),
- keep_one_selected("keep_one_selected"),
- keep_selection_visible_on_reshape("keep_selection_visible_on_reshape",false),
- no_items_text("no_items_text")
-{};
-
-void LLFlatListView::reshape(S32 width, S32 height, bool called_from_parent /* = true */)
-{
- S32 delta = height - getRect().getHeight();
- LLScrollContainer::reshape(width, height, called_from_parent);
- setItemsNoScrollWidth(width);
- rearrangeItems();
-
- if(delta!= 0 && mKeepSelectionVisibleOnReshape)
- {
- ensureSelectedVisible();
- }
-}
-
-const LLRect& LLFlatListView::getItemsRect() const
-{
- return mItemsPanel->getRect();
-}
-
-bool LLFlatListView::addItem(LLPanel * item, const LLSD& value /*= LLUUID::null*/, EAddPosition pos /*= ADD_BOTTOM*/,bool rearrange /*= true*/)
-{
- if (!item) return false;
- if (value.isUndefined()) return false;
-
- //force uniqueness of items, easiest check but unreliable
- if (item->getParent() == mItemsPanel) return false;
-
- item_pair_t* new_pair = new item_pair_t(item, value);
- switch (pos)
- {
- case ADD_TOP:
- mItemPairs.push_front(new_pair);
- //in LLView::draw() children are iterated in backorder
- mItemsPanel->addChildInBack(item);
- break;
- case ADD_BOTTOM:
- mItemPairs.push_back(new_pair);
- mItemsPanel->addChild(item);
- break;
- default:
- break;
- }
-
- //_4 is for MASK
- item->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4));
- item->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4));
-
- // Children don't accept the focus
- item->setTabStop(false);
-
- if (rearrange)
- {
- rearrangeItems();
- notifyParentItemsRectChanged();
- }
- return true;
-}
-
-bool LLFlatListView::addItemPairs(pairs_list_t panel_list, bool rearrange /*= true*/)
-{
- if (!mItemComparator)
- {
- LL_WARNS_ONCE() << "No comparator specified for inserting FlatListView items." << LL_ENDL;
- return false;
- }
- if (panel_list.size() == 0)
- {
- return false;
- }
-
- // presort list so that it will be easier to sort elements into mItemPairs
- panel_list.sort(ComparatorAdaptor(*mItemComparator));
-
- pairs_const_iterator_t new_pair_it = panel_list.begin();
- item_pair_t* new_pair = *new_pair_it;
- pairs_iterator_t pair_it = mItemPairs.begin();
- item_pair_t* item_pair = *pair_it;
-
- // sort panel_list into mItemPars
- while (new_pair_it != panel_list.end() && pair_it != mItemPairs.end())
- {
- if (!new_pair->first || new_pair->first->getParent() == mItemsPanel)
- {
- // iterator already used or we are reusing existing panel
- new_pair_it++;
- new_pair = *new_pair_it;
- }
- else if (mItemComparator->compare(new_pair->first, item_pair->first))
- {
- LLPanel* panel = new_pair->first;
-
- mItemPairs.insert(pair_it, new_pair);
- mItemsPanel->addChild(panel);
-
- //_4 is for MASK
- panel->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4));
- panel->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4));
- // Children don't accept the focus
- panel->setTabStop(false);
- }
- else
- {
- pair_it++;
- item_pair = *pair_it;
- }
- }
-
- // Add what is left of panel_list into the end of mItemPairs.
- for (; new_pair_it != panel_list.end(); ++new_pair_it)
- {
- item_pair_t* item_pair = *new_pair_it;
- LLPanel *panel = item_pair->first;
- if (panel && panel->getParent() != mItemsPanel)
- {
- mItemPairs.push_back(item_pair);
- mItemsPanel->addChild(panel);
-
- //_4 is for MASK
- panel->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, item_pair, _4));
- panel->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, item_pair, _4));
- // Children don't accept the focus
- panel->setTabStop(false);
- }
- }
-
- if (rearrange)
- {
- rearrangeItems();
- notifyParentItemsRectChanged();
- }
- return true;
-}
-
-
-bool LLFlatListView::insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, const LLSD& value /*= LLUUID::null*/)
-{
- if (!after_item) return false;
- if (!item_to_add) return false;
- if (value.isUndefined()) return false;
-
- if (mItemPairs.empty()) return false;
-
- //force uniqueness of items, easiest check but unreliable
- if (item_to_add->getParent() == mItemsPanel) return false;
-
- item_pair_t* after_pair = getItemPair(after_item);
- if (!after_pair) return false;
-
- item_pair_t* new_pair = new item_pair_t(item_to_add, value);
- if (after_pair == mItemPairs.back())
- {
- mItemPairs.push_back(new_pair);
- mItemsPanel->addChild(item_to_add);
- }
- else
- {
- pairs_iterator_t it = mItemPairs.begin();
- for (; it != mItemPairs.end(); ++it)
- {
- if (*it == after_pair)
- {
- // insert new elements before the element at position of passed iterator.
- mItemPairs.insert(++it, new_pair);
- mItemsPanel->addChild(item_to_add);
- break;
- }
- }
- }
-
- //_4 is for MASK
- item_to_add->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4));
- item_to_add->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4));
-
- rearrangeItems();
- notifyParentItemsRectChanged();
- return true;
-}
-
-
-bool LLFlatListView::removeItem(LLPanel* item, bool rearrange)
-{
- if (!item) return false;
- if (item->getParent() != mItemsPanel) return false;
-
- item_pair_t* item_pair = getItemPair(item);
- if (!item_pair) return false;
-
- return removeItemPair(item_pair, rearrange);
-}
-
-bool LLFlatListView::removeItemByValue(const LLSD& value, bool rearrange)
-{
- if (value.isUndefined()) return false;
-
- item_pair_t* item_pair = getItemPair(value);
- if (!item_pair) return false;
-
- return removeItemPair(item_pair, rearrange);
-}
-
-bool LLFlatListView::removeItemByUUID(const LLUUID& uuid, bool rearrange)
-{
- return removeItemByValue(LLSD(uuid), rearrange);
-}
-
-LLPanel* LLFlatListView::getItemByValue(const LLSD& value) const
-{
- if (value.isUndefined()) return NULL;
-
- item_pair_t* pair = getItemPair(value);
- if (pair) return pair->first;
- return NULL;
-}
-
-bool LLFlatListView::selectItem(LLPanel* item, bool select /*= true*/)
-{
- if (!item) return false;
- if (item->getParent() != mItemsPanel) return false;
-
- item_pair_t* item_pair = getItemPair(item);
- if (!item_pair) return false;
-
- return selectItemPair(item_pair, select);
-}
-
-bool LLFlatListView::selectItemByValue(const LLSD& value, bool select /*= true*/)
-{
- if (value.isUndefined()) return false;
-
- item_pair_t* item_pair = getItemPair(value);
- if (!item_pair) return false;
-
- return selectItemPair(item_pair, select);
-}
-
-bool LLFlatListView::selectItemByUUID(const LLUUID& uuid, bool select /* = true*/)
-{
- return selectItemByValue(LLSD(uuid), select);
-}
-
-
-LLSD LLFlatListView::getSelectedValue() const
-{
- if (mSelectedItemPairs.empty()) return LLSD();
-
- item_pair_t* first_selected_pair = mSelectedItemPairs.front();
- return first_selected_pair->second;
-}
-
-void LLFlatListView::getSelectedValues(std::vector<LLSD>& selected_values) const
-{
- if (mSelectedItemPairs.empty()) return;
-
- for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
- {
- selected_values.push_back((*it)->second);
- }
-}
-
-LLUUID LLFlatListView::getSelectedUUID() const
-{
- const LLSD& value = getSelectedValue();
- if (value.isDefined() && value.isUUID())
- {
- return value.asUUID();
- }
- else
- {
- return LLUUID::null;
- }
-}
-
-void LLFlatListView::getSelectedUUIDs(uuid_vec_t& selected_uuids) const
-{
- if (mSelectedItemPairs.empty()) return;
-
- for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
- {
- selected_uuids.push_back((*it)->second.asUUID());
- }
-}
-
-LLPanel* LLFlatListView::getSelectedItem() const
-{
- if (mSelectedItemPairs.empty()) return NULL;
-
- return mSelectedItemPairs.front()->first;
-}
-
-void LLFlatListView::getSelectedItems(std::vector<LLPanel*>& selected_items) const
-{
- if (mSelectedItemPairs.empty()) return;
-
- for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
- {
- selected_items.push_back((*it)->first);
- }
-}
-
-void LLFlatListView::resetSelection(bool no_commit_on_deselection /*= false*/)
-{
- if (mSelectedItemPairs.empty()) return;
-
- for (pairs_iterator_t it= mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
- {
- item_pair_t* pair_to_deselect = *it;
- LLPanel* item = pair_to_deselect->first;
- item->setValue(UNSELECTED_EVENT);
- }
-
- mSelectedItemPairs.clear();
-
- if (mCommitOnSelectionChange && !no_commit_on_deselection)
- {
- onCommit();
- }
-
- // Stretch selected item rect to ensure it won't be clipped
- mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
-}
-
-void LLFlatListView::setNoItemsCommentText(const std::string& comment_text)
-{
- mNoItemsCommentTextbox->setValue(comment_text);
-}
-
-U32 LLFlatListView::size(const bool only_visible_items) const
-{
- if (only_visible_items)
- {
- U32 size = 0;
- for (pairs_const_iterator_t
- iter = mItemPairs.begin(),
- iter_end = mItemPairs.end();
- iter != iter_end; ++iter)
- {
- if ((*iter)->first->getVisible())
- ++size;
- }
- return size;
- }
- else
- {
- return mItemPairs.size();
- }
-}
-
-void LLFlatListView::clear()
-{
- // This will clear mSelectedItemPairs, calling all appropriate callbacks.
- resetSelection();
-
- // do not use LLView::deleteAllChildren to avoid removing nonvisible items. drag-n-drop for ex.
- for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
- {
- mItemsPanel->removeChild((*it)->first);
- (*it)->first->die();
- delete *it;
- }
- mItemPairs.clear();
-
- // also set items panel height to zero. Reshape it to allow reshaping of non-item children
- LLRect rc = mItemsPanel->getRect();
- rc.mBottom = rc.mTop;
- mItemsPanel->reshape(rc.getWidth(), rc.getHeight());
- mItemsPanel->setRect(rc);
-
- setNoItemsCommentVisible(true);
- notifyParentItemsRectChanged();
-}
-
-void LLFlatListView::sort()
-{
- if (!mItemComparator)
- {
- LL_WARNS() << "No comparator specified for sorting FlatListView items." << LL_ENDL;
- return;
- }
-
- mItemPairs.sort(ComparatorAdaptor(*mItemComparator));
- rearrangeItems();
-}
-
-bool LLFlatListView::updateValue(const LLSD& old_value, const LLSD& new_value)
-{
- if (old_value.isUndefined() || new_value.isUndefined()) return false;
- if (llsds_are_equal(old_value, new_value)) return false;
-
- item_pair_t* item_pair = getItemPair(old_value);
- if (!item_pair) return false;
-
- item_pair->second = new_value;
- return true;
-}
-
-//////////////////////////////////////////////////////////////////////////
-// PROTECTED STUFF
-//////////////////////////////////////////////////////////////////////////
-
-LLFlatListView::LLFlatListView(const LLFlatListView::Params& p)
-: LLScrollContainer(p)
- , mItemComparator(NULL)
- , mItemsPanel(NULL)
- , mItemPad(p.item_pad)
- , mAllowSelection(p.allow_select)
- , mMultipleSelection(p.multi_select)
- , mKeepOneItemSelected(p.keep_one_selected)
- , mCommitOnSelectionChange(false)
- , mPrevNotifyParentRect(LLRect())
- , mNoItemsCommentTextbox(NULL)
- , mIsConsecutiveSelection(false)
- , mKeepSelectionVisibleOnReshape(p.keep_selection_visible_on_reshape)
-{
- mBorderThickness = getBorderWidth();
-
- LLRect scroll_rect = getRect();
- LLRect items_rect;
-
- setItemsNoScrollWidth(scroll_rect.getWidth());
- items_rect.setLeftTopAndSize(mBorderThickness, scroll_rect.getHeight() - mBorderThickness, mItemsNoScrollWidth, 0);
-
- LLPanel::Params pp;
- pp.rect(items_rect);
- mItemsPanel = LLUICtrlFactory::create<LLPanel> (pp);
- addChild(mItemsPanel);
-
- //we don't need to stretch in vertical direction on reshaping by a parent
- //no bottom following!
- mItemsPanel->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_TOP);
-
- LLViewBorder::Params params;
- params.name("scroll border");
- params.rect(getLastSelectedItemRect());
- params.visible(false);
- params.bevel_style(LLViewBorder::BEVEL_IN);
- mSelectedItemsBorder = LLUICtrlFactory::create<LLViewBorder> (params);
- mItemsPanel->addChild( mSelectedItemsBorder );
-
- {
- // create textbox for "No Items" comment text
- LLTextBox::Params text_p = p.no_items_text;
- if (!text_p.rect.isProvided())
- {
- LLRect comment_rect = getRect();
- comment_rect.setOriginAndSize(0, 0, comment_rect.getWidth(), comment_rect.getHeight());
- comment_rect.stretch(-getBorderWidth());
- text_p.rect(comment_rect);
- }
- text_p.border_visible(false);
-
- if (!text_p.follows.isProvided())
- {
- text_p.follows.flags(FOLLOWS_ALL);
- }
- mNoItemsCommentTextbox = LLUICtrlFactory::create<LLTextBox>(text_p, this);
- }
-};
-
-LLFlatListView::~LLFlatListView()
-{
- for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
- {
- mItemsPanel->removeChild((*it)->first);
- (*it)->first->die();
- delete *it;
- }
- mItemPairs.clear();
-}
-
-// virtual
-void LLFlatListView::draw()
-{
- // Highlight border if a child of this container has keyboard focus
- if( mSelectedItemsBorder->getVisible() )
- {
- mSelectedItemsBorder->setKeyboardFocusHighlight( hasFocus() );
- }
- LLScrollContainer::draw();
-}
-
-// virtual
-bool LLFlatListView::postBuild()
-{
- setTabStop(true);
- return LLScrollContainer::postBuild();
-}
-
-void LLFlatListView::rearrangeItems()
-{
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
-
- setNoItemsCommentVisible(0==size());
-
- if (mItemPairs.empty()) return;
-
- //calculating required height - assuming items can be of different height
- //list should accommodate all its items
- S32 height = 0;
-
- S32 invisible_children_count = 0;
- pairs_iterator_t it = mItemPairs.begin();
- for (; it != mItemPairs.end(); ++it)
- {
- LLPanel* item = (*it)->first;
-
- // skip invisible child
- if (!item->getVisible())
- {
- ++invisible_children_count;
- continue;
- }
-
- height += item->getRect().getHeight();
- }
-
- // add paddings between items, excluding invisible ones
- height += mItemPad * (mItemPairs.size() - invisible_children_count - 1);
-
- LLRect rc = mItemsPanel->getRect();
- S32 width = mItemsNoScrollWidth;
-
- // update width to avoid horizontal scrollbar
- if (height > getRect().getHeight() - 2 * mBorderThickness)
- width -= scrollbar_size;
-
- //changes the bottom, end of the list goes down in the scroll container
- rc.setLeftTopAndSize(rc.mLeft, rc.mTop, width, height);
- mItemsPanel->setRect(rc);
-
- //reshaping items
- S32 item_new_top = height;
- pairs_iterator_t it2, first_it = mItemPairs.begin();
- for (it2 = first_it; it2 != mItemPairs.end(); ++it2)
- {
- LLPanel* item = (*it2)->first;
-
- // skip invisible child
- if (!item->getVisible())
- continue;
-
- LLRect rc = item->getRect();
- rc.setLeftTopAndSize(rc.mLeft, item_new_top, width, rc.getHeight());
- item->reshape(rc.getWidth(), rc.getHeight());
- item->setRect(rc);
-
- // move top for next item in list
- item_new_top -= (rc.getHeight() + mItemPad);
- }
-
- // Stretch selected item rect to ensure it won't be clipped
- mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
-}
-
-void LLFlatListView::onItemMouseClick(item_pair_t* item_pair, MASK mask)
-{
- if (!item_pair) return;
-
- if (!item_pair->first)
- {
- LL_WARNS() << "Attempt to selet an item pair containing null panel item" << LL_ENDL;
- return;
- }
-
- setFocus(true);
-
- bool select_item = !isSelected(item_pair);
-
- //*TODO find a better place for that enforcing stuff
- if (mKeepOneItemSelected && numSelected() == 1 && !select_item) return;
-
- if ( (mask & MASK_SHIFT) && !(mask & MASK_CONTROL)
- && mMultipleSelection && !mSelectedItemPairs.empty() )
- {
- item_pair_t* last_selected_pair = mSelectedItemPairs.back();
-
- // If item_pair is already selected - do nothing
- if (last_selected_pair == item_pair)
- return;
-
- bool grab_items = false;
- bool reverse = false;
- pairs_list_t pairs_to_select;
-
- // Pick out items from list between last selected and current clicked item_pair.
- for (pairs_iterator_t
- iter = mItemPairs.begin(),
- iter_end = mItemPairs.end();
- iter != iter_end; ++iter)
- {
- item_pair_t* cur = *iter;
- if (cur == last_selected_pair || cur == item_pair)
- {
- // We've got reverse selection if last grabed item isn't a new selection.
- reverse = grab_items && (cur != item_pair);
- grab_items = !grab_items;
- // Skip last selected and current clicked item pairs.
- continue;
- }
- if (!cur->first->getVisible())
- {
- // Skip invisible item pairs.
- continue;
- }
- if (grab_items)
- {
- pairs_to_select.push_back(cur);
- }
- }
-
- if (reverse)
- {
- pairs_to_select.reverse();
- }
-
- pairs_to_select.push_back(item_pair);
-
- for (pairs_iterator_t
- iter = pairs_to_select.begin(),
- iter_end = pairs_to_select.end();
- iter != iter_end; ++iter)
- {
- item_pair_t* pair_to_select = *iter;
- if (isSelected(pair_to_select))
- {
- // Item was already selected but there is a need to keep order from last selected pair to new selection.
- // Do it here to prevent extra mCommitOnSelectionChange in selectItemPair().
- mSelectedItemPairs.remove(pair_to_select);
- mSelectedItemPairs.push_back(pair_to_select);
- }
- else
- {
- selectItemPair(pair_to_select, true);
- }
- }
-
- if (!select_item)
- {
- // Update last selected item border.
- mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
- }
- return;
- }
-
- //no need to do additional commit on selection reset
- if (!(mask & MASK_CONTROL) || !mMultipleSelection) resetSelection(true);
-
- //only CTRL usage allows to deselect an item, usual clicking on an item cannot deselect it
- if (mask & MASK_CONTROL)
- selectItemPair(item_pair, select_item);
- else
- selectItemPair(item_pair, true);
-}
-
-void LLFlatListView::onItemRightMouseClick(item_pair_t* item_pair, MASK mask)
-{
- if (!item_pair)
- return;
-
- // Forbid deselecting of items on right mouse button click if mMultipleSelection flag is set on,
- // because some of derived classes may have context menu and selected items must be kept.
- if ( !(mask & MASK_CONTROL) && mMultipleSelection && isSelected(item_pair) )
- return;
-
- // else got same behavior as at onItemMouseClick
- onItemMouseClick(item_pair, mask);
-}
-
-bool LLFlatListView::handleKeyHere(KEY key, MASK mask)
-{
- bool reset_selection = (mask != MASK_SHIFT);
- bool handled = false;
- switch (key)
- {
- case KEY_RETURN:
- {
- if (mSelectedItemPairs.size() && mask == MASK_NONE)
- {
- mOnReturnSignal(this, getValue());
- handled = true;
- }
- break;
- }
- case KEY_UP:
- {
- if ( !selectNextItemPair(true, reset_selection) && reset_selection)
- {
- // If case we are in accordion tab notify parent to go to the previous accordion
- if(notifyParent(LLSD().with("action","select_prev")) > 0 )//message was processed
- resetSelection();
- }
- break;
- }
- case KEY_DOWN:
- {
- if ( !selectNextItemPair(false, reset_selection) && reset_selection)
- {
- // If case we are in accordion tab notify parent to go to the next accordion
- if( notifyParent(LLSD().with("action","select_next")) > 0 ) //message was processed
- resetSelection();
- }
- break;
- }
- case KEY_ESCAPE:
- {
- if (mask == MASK_NONE)
- {
- setFocus(false); // pass focus to the game area (EXT-8357)
- }
- break;
- }
- default:
- break;
- }
-
- if ( ( key == KEY_UP || key == KEY_DOWN ) && mSelectedItemPairs.size() )
- {
- ensureSelectedVisible();
- /*
- LLRect visible_rc = getVisibleContentRect();
- LLRect selected_rc = getLastSelectedItemRect();
-
- if ( !visible_rc.contains (selected_rc) )
- {
- // But scroll in Items panel coordinates
- scrollToShowRect(selected_rc);
- }
-
- // In case we are in accordion tab notify parent to show selected rectangle
- LLRect screen_rc;
- localRectToScreen(selected_rc, &screen_rc);
- notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue()));*/
-
- handled = true;
- }
-
- return handled ? handled : LLScrollContainer::handleKeyHere(key, mask);
-}
-
-LLFlatListView::item_pair_t* LLFlatListView::getItemPair(LLPanel* item) const
-{
- llassert(item);
-
- for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it)
- {
- item_pair_t* item_pair = *it;
- if (item_pair->first == item) return item_pair;
- }
- return NULL;
-}
-
-//compares two LLSD's
-bool llsds_are_equal(const LLSD& llsd_1, const LLSD& llsd_2)
-{
- llassert(llsd_1.isDefined());
- llassert(llsd_2.isDefined());
-
- if (llsd_1.type() != llsd_2.type()) return false;
-
- if (!llsd_1.isMap())
- {
- if (llsd_1.isUUID()) return llsd_1.asUUID() == llsd_2.asUUID();
-
- //assumptions that string representaion is enough for other types
- return llsd_1.asString() == llsd_2.asString();
- }
-
- if (llsd_1.size() != llsd_2.size()) return false;
-
- LLSD::map_const_iterator llsd_1_it = llsd_1.beginMap();
- LLSD::map_const_iterator llsd_2_it = llsd_2.beginMap();
- for (S32 i = 0; i < llsd_1.size(); ++i)
- {
- if ((*llsd_1_it).first != (*llsd_2_it).first) return false;
- if (!llsds_are_equal((*llsd_1_it).second, (*llsd_2_it).second)) return false;
- ++llsd_1_it;
- ++llsd_2_it;
- }
- return true;
-}
-
-LLFlatListView::item_pair_t* LLFlatListView::getItemPair(const LLSD& value) const
-{
- llassert(value.isDefined());
-
- for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it)
- {
- item_pair_t* item_pair = *it;
- if (llsds_are_equal(item_pair->second, value)) return item_pair;
- }
- return NULL;
-}
-
-bool LLFlatListView::selectItemPair(item_pair_t* item_pair, bool select)
-{
- llassert(item_pair);
-
- if (!mAllowSelection && select) return false;
-
- if (isSelected(item_pair) == select) return true; //already in specified selection state
- if (select)
- {
- mSelectedItemPairs.push_back(item_pair);
- }
- else
- {
- mSelectedItemPairs.remove(item_pair);
- }
-
- //a way of notifying panel of selection state changes
- LLPanel* item = item_pair->first;
- item->setValue(select ? SELECTED_EVENT : UNSELECTED_EVENT);
-
- if (mCommitOnSelectionChange)
- {
- onCommit();
- }
-
- // Stretch selected item rect to ensure it won't be clipped
- mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
- // By default mark it as not consecutive selection
- mIsConsecutiveSelection = false;
-
- return true;
-}
-
-void LLFlatListView::scrollToShowFirstSelectedItem()
-{
- if (!mSelectedItemPairs.size()) return;
-
- LLRect selected_rc = mSelectedItemPairs.front()->first->getRect();
-
- if (selected_rc.isValid())
- {
- scrollToShowRect(selected_rc);
- }
-}
-
-LLRect LLFlatListView::getLastSelectedItemRect()
-{
- if (!mSelectedItemPairs.size())
- {
- return LLRect::null;
- }
-
- return mSelectedItemPairs.back()->first->getRect();
-}
-
-void LLFlatListView::selectFirstItem ()
-{
- // No items - no actions!
- if (0 == size()) return;
-
- // Select first visible item
- for (pairs_iterator_t
- iter = mItemPairs.begin(),
- iter_end = mItemPairs.end();
- iter != iter_end; ++iter)
- {
- // skip invisible items
- if ( (*iter)->first->getVisible() )
- {
- selectItemPair(*iter, true);
- ensureSelectedVisible();
- break;
- }
- }
-}
-
-void LLFlatListView::selectLastItem ()
-{
- // No items - no actions!
- if (0 == size()) return;
-
- // Select last visible item
- for (pairs_list_t::reverse_iterator
- r_iter = mItemPairs.rbegin(),
- r_iter_end = mItemPairs.rend();
- r_iter != r_iter_end; ++r_iter)
- {
- // skip invisible items
- if ( (*r_iter)->first->getVisible() )
- {
- selectItemPair(*r_iter, true);
- ensureSelectedVisible();
- break;
- }
- }
-}
-
-void LLFlatListView::ensureSelectedVisible()
-{
- LLRect selected_rc = getLastSelectedItemRect();
-
- if ( selected_rc.isValid() )
- {
- scrollToShowRect(selected_rc);
- }
-}
-
-
-// virtual
-bool LLFlatListView::selectNextItemPair(bool is_up_direction, bool reset_selection)
-{
- // No items - no actions!
- if ( 0 == size() )
- return false;
-
- if (!mIsConsecutiveSelection)
- {
- // Leave only one item selected if list has not consecutive selection
- if (mSelectedItemPairs.size() && !reset_selection)
- {
- item_pair_t* cur_sel_pair = mSelectedItemPairs.back();
- resetSelection();
- selectItemPair (cur_sel_pair, true);
- }
- }
-
- if ( mSelectedItemPairs.size() )
- {
- item_pair_t* to_sel_pair = NULL;
- item_pair_t* cur_sel_pair = NULL;
-
- // Take the last selected pair
- cur_sel_pair = mSelectedItemPairs.back();
- // Bases on given direction choose next item to select
- if ( is_up_direction )
- {
- // Find current selected item position in mItemPairs list
- pairs_list_t::reverse_iterator sel_it = std::find(mItemPairs.rbegin(), mItemPairs.rend(), cur_sel_pair);
-
- for (;++sel_it != mItemPairs.rend();)
- {
- // skip invisible items
- if ( (*sel_it)->first->getVisible() )
- {
- to_sel_pair = *sel_it;
- break;
- }
- }
- }
- else
- {
- // Find current selected item position in mItemPairs list
- pairs_list_t::iterator sel_it = std::find(mItemPairs.begin(), mItemPairs.end(), cur_sel_pair);
-
- for (;++sel_it != mItemPairs.end();)
- {
- // skip invisible items
- if ( (*sel_it)->first->getVisible() )
- {
- to_sel_pair = *sel_it;
- break;
- }
- }
- }
-
- if ( to_sel_pair )
- {
- bool select = true;
- if ( reset_selection )
- {
- // Reset current selection if we were asked about it
- resetSelection();
- }
- else
- {
- // If item already selected and no reset request than we should deselect last selected item.
- select = (mSelectedItemPairs.end() == std::find(mSelectedItemPairs.begin(), mSelectedItemPairs.end(), to_sel_pair));
- }
- // Select/Deselect next item
- selectItemPair(select ? to_sel_pair : cur_sel_pair, select);
- // Mark it as consecutive selection
- mIsConsecutiveSelection = true;
- return true;
- }
- }
- else
- {
- // If there weren't selected items then choose the first one bases on given direction
- // Force selection to first item
- if (is_up_direction)
- selectLastItem();
- else
- selectFirstItem();
- // Mark it as consecutive selection
- mIsConsecutiveSelection = true;
- return true;
- }
-
- return false;
-}
-
-bool LLFlatListView::canSelectAll() const
-{
- return 0 != size() && mAllowSelection && mMultipleSelection;
-}
-
-void LLFlatListView::selectAll()
-{
- if (!mAllowSelection || !mMultipleSelection)
- return;
-
- mSelectedItemPairs.clear();
-
- for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it)
- {
- item_pair_t* item_pair = *it;
- mSelectedItemPairs.push_back(item_pair);
- //a way of notifying panel of selection state changes
- LLPanel* item = item_pair->first;
- item->setValue(SELECTED_EVENT);
- }
-
- if (mCommitOnSelectionChange)
- {
- onCommit();
- }
-
- // Stretch selected item rect to ensure it won't be clipped
- mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
-}
-
-bool LLFlatListView::isSelected(item_pair_t* item_pair) const
-{
- llassert(item_pair);
-
- pairs_const_iterator_t it_end = mSelectedItemPairs.end();
- return std::find(mSelectedItemPairs.begin(), it_end, item_pair) != it_end;
-}
-
-bool LLFlatListView::removeItemPair(item_pair_t* item_pair, bool rearrange)
-{
- llassert(item_pair);
-
- bool deleted = false;
- bool selection_changed = false;
- for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
- {
- item_pair_t* _item_pair = *it;
- if (_item_pair == item_pair)
- {
- mItemPairs.erase(it);
- deleted = true;
- break;
- }
- }
-
- if (!deleted) return false;
-
- for (pairs_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
- {
- item_pair_t* selected_item_pair = *it;
- if (selected_item_pair == item_pair)
- {
- it = mSelectedItemPairs.erase(it);
- selection_changed = true;
- break;
- }
- }
-
- mItemsPanel->removeChild(item_pair->first);
- item_pair->first->die();
- delete item_pair;
-
- if (rearrange)
- {
- rearrangeItems();
- notifyParentItemsRectChanged();
- }
-
- if (selection_changed && mCommitOnSelectionChange)
- {
- onCommit();
- }
-
- return true;
-}
-
-void LLFlatListView::notifyParentItemsRectChanged()
-{
- S32 comment_height = 0;
-
- // take into account comment text height if exists
- if (mNoItemsCommentTextbox && mNoItemsCommentTextbox->getVisible())
- {
- // top text padding inside the textbox is included into the height
- comment_height = mNoItemsCommentTextbox->getTextPixelHeight();
-
- // take into account a distance from parent's top border to textbox's top
- comment_height += getRect().getHeight() - mNoItemsCommentTextbox->getRect().mTop;
- }
-
- LLRect req_rect = getItemsRect();
-
- // get maximum of items total height and comment text height
- req_rect.setOriginAndSize(req_rect.mLeft, req_rect.mBottom, req_rect.getWidth(), llmax(req_rect.getHeight(), comment_height));
-
- // take into account border size.
- req_rect.stretch(getBorderWidth());
-
- if (req_rect == mPrevNotifyParentRect)
- return;
-
- mPrevNotifyParentRect = req_rect;
-
- LLSD params;
- params["action"] = "size_changes";
- params["width"] = req_rect.getWidth();
- params["height"] = req_rect.getHeight();
-
- if (getParent()) // dummy widgets don't have a parent
- getParent()->notifyParent(params);
-}
-
-void LLFlatListView::setNoItemsCommentVisible(bool visible) const
-{
- if (mNoItemsCommentTextbox)
- {
- mSelectedItemsBorder->setVisible(!visible);
- mNoItemsCommentTextbox->setVisible(visible);
- }
-}
-
-void LLFlatListView::getItems(std::vector<LLPanel*>& items) const
-{
- if (mItemPairs.empty()) return;
-
- items.clear();
- for (pairs_const_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
- {
- items.push_back((*it)->first);
- }
-}
-
-void LLFlatListView::getValues(std::vector<LLSD>& values) const
-{
- if (mItemPairs.empty()) return;
-
- values.clear();
- for (pairs_const_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
- {
- values.push_back((*it)->second);
- }
-}
-
-// virtual
-void LLFlatListView::onFocusReceived()
-{
- if (size())
- {
- mSelectedItemsBorder->setVisible(true);
- }
- gEditMenuHandler = this;
-}
-// virtual
-void LLFlatListView::onFocusLost()
-{
- mSelectedItemsBorder->setVisible(false);
- // Route menu back to the default
- if (gEditMenuHandler == this)
- {
- gEditMenuHandler = NULL;
- }
-}
-
-//virtual
-S32 LLFlatListView::notify(const LLSD& info)
-{
- if (info.has("action"))
- {
- std::string str_action = info["action"];
- if (str_action == "select_first")
- {
- setFocus(true);
- selectFirstItem();
- return 1;
- }
- else if (str_action == "select_last")
- {
- setFocus(true);
- selectLastItem();
- return 1;
- }
- }
- else if (info.has("rearrange"))
- {
- rearrangeItems();
- notifyParentItemsRectChanged();
- return 1;
- }
-
- return 0;
-}
-
-void LLFlatListView::detachItems(std::vector<LLPanel*>& detached_items)
-{
- LLSD action;
- action.with("detach", LLSD());
- // Clear detached_items list
- detached_items.clear();
- // Go through items and detach valid items, remove them from items panel
- // and add to detached_items.
- pairs_iterator_t iter = mItemPairs.begin(), iter_end = mItemPairs.end();
- while (iter != iter_end)
- {
- LLPanel* pItem = (*iter)->first;
- if (1 == pItem->notify(action))
- {
- selectItemPair((*iter), false);
- mItemsPanel->removeChild(pItem);
- detached_items.push_back(pItem);
- }
- iter++;
- }
- if (!detached_items.empty())
- {
- // Some items were detached, clean ourself from unusable memory
- if (detached_items.size() == mItemPairs.size())
- {
- // This way will be faster if all items were disconnected
- pairs_iterator_t iter = mItemPairs.begin(), iter_end = mItemPairs.end();
- while (iter != iter_end)
- {
- (*iter)->first = NULL;
- delete *iter;
- iter++;
- }
- mItemPairs.clear();
- // Also set items panel height to zero.
- // Reshape it to allow reshaping of non-item children.
- LLRect rc = mItemsPanel->getRect();
- rc.mBottom = rc.mTop;
- mItemsPanel->reshape(rc.getWidth(), rc.getHeight());
- mItemsPanel->setRect(rc);
- setNoItemsCommentVisible(true);
- }
- else
- {
- std::vector<LLPanel*>::const_iterator
- detached_iter = detached_items.begin(),
- detached_iter_end = detached_items.end();
- while (detached_iter < detached_iter_end)
- {
- LLPanel* pDetachedItem = *detached_iter;
- pairs_iterator_t iter = mItemPairs.begin(), iter_end = mItemPairs.end();
- while (iter != iter_end)
- {
- item_pair_t* item_pair = *iter;
- if (item_pair->first == pDetachedItem)
- {
- mItemPairs.erase(iter);
- item_pair->first = NULL;
- delete item_pair;
- break;
- }
- iter++;
- }
- detached_iter++;
- }
- rearrangeItems();
- }
- notifyParentItemsRectChanged();
- }
-}
-
-
-/************************************************************************/
-/* LLFlatListViewEx implementation */
-/************************************************************************/
-LLFlatListViewEx::Params::Params()
-: no_items_msg("no_items_msg")
-, no_filtered_items_msg("no_filtered_items_msg")
-{
-}
-
-LLFlatListViewEx::LLFlatListViewEx(const Params& p)
-: LLFlatListView(p)
-, mNoFilteredItemsMsg(p.no_filtered_items_msg)
-, mNoItemsMsg(p.no_items_msg)
-, mForceShowingUnmatchedItems(false)
-, mHasMatchedItems(false)
-{
-}
-
-void LLFlatListViewEx::updateNoItemsMessage(const std::string& filter_string)
-{
- bool items_filtered = !filter_string.empty();
- if (items_filtered)
- {
- // items were filtered
- LLStringUtil::format_map_t args;
- args["[SEARCH_TERM]"] = LLURI::escape(filter_string);
- std::string text = mNoFilteredItemsMsg;
- LLStringUtil::format(text, args);
- setNoItemsCommentText(text);
- }
- else
- {
- // list does not contain any items at all
- setNoItemsCommentText(mNoItemsMsg);
- }
-}
-
-bool LLFlatListViewEx::getForceShowingUnmatchedItems()
-{
- return mForceShowingUnmatchedItems;
-}
-
-void LLFlatListViewEx::setForceShowingUnmatchedItems(bool show)
-{
- mForceShowingUnmatchedItems = show;
-}
-
-void LLFlatListViewEx::setFilterSubString(const std::string& filter_str, bool notify_parent)
-{
- if (0 != LLStringUtil::compareInsensitive(filter_str, mFilterSubString))
- {
- mFilterSubString = filter_str;
- updateNoItemsMessage(mFilterSubString);
- filterItems(false, notify_parent);
- }
-}
-
-bool LLFlatListViewEx::updateItemVisibility(LLPanel* item, const LLSD &action)
-{
- if (!item)
- return false;
-
- bool visible = true;
-
- // 0 signifies that filter is matched,
- // i.e. we don't hide items that don't support 'match_filter' action, separators etc.
- if (0 == item->notify(action))
- {
- mHasMatchedItems = true;
- }
- else
- {
- // TODO: implement (re)storing of current selection.
- if (!mForceShowingUnmatchedItems)
- {
- selectItem(item, false);
- visible = false;
- }
- }
-
- if (item->getVisible() != visible)
- {
- item->setVisible(visible);
- return true;
- }
-
- return false;
-}
-
-void LLFlatListViewEx::filterItems(bool re_sort, bool notify_parent)
-{
- std::string cur_filter = mFilterSubString;
- LLStringUtil::toUpper(cur_filter);
-
- LLSD action;
- action.with("match_filter", cur_filter);
-
- mHasMatchedItems = false;
- bool visibility_changed = false;
- pairs_const_iterator_t iter = getItemPairs().begin(), iter_end = getItemPairs().end();
- while (iter != iter_end)
- {
- LLPanel* pItem = (*(iter++))->first;
- visibility_changed |= updateItemVisibility(pItem, action);
- }
-
- if (re_sort)
- {
- sort();
- }
-
- if (visibility_changed && notify_parent)
- {
- notifyParentItemsRectChanged();
- }
-}
-
-bool LLFlatListViewEx::hasMatchedItems()
-{
- return mHasMatchedItems;
-}
-
-//EOF
+/**
+ * @file llflatlistview.cpp
+ * @brief LLFlatListView base class and extension to support messages for several cases of an empty list.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llpanel.h"
+#include "lltextbox.h"
+
+#include "llflatlistview.h"
+
+static const LLDefaultChildRegistry::Register<LLFlatListView> flat_list_view("flat_list_view");
+
+const LLSD SELECTED_EVENT = LLSD().with("selected", true);
+const LLSD UNSELECTED_EVENT = LLSD().with("selected", false);
+
+//forward declaration
+bool llsds_are_equal(const LLSD& llsd_1, const LLSD& llsd_2);
+
+LLFlatListView::Params::Params()
+: item_pad("item_pad"),
+ allow_select("allow_select"),
+ multi_select("multi_select"),
+ keep_one_selected("keep_one_selected"),
+ keep_selection_visible_on_reshape("keep_selection_visible_on_reshape",false),
+ no_items_text("no_items_text")
+{};
+
+void LLFlatListView::reshape(S32 width, S32 height, bool called_from_parent /* = true */)
+{
+ S32 delta = height - getRect().getHeight();
+ LLScrollContainer::reshape(width, height, called_from_parent);
+ setItemsNoScrollWidth(width);
+ rearrangeItems();
+
+ if(delta!= 0 && mKeepSelectionVisibleOnReshape)
+ {
+ ensureSelectedVisible();
+ }
+}
+
+const LLRect& LLFlatListView::getItemsRect() const
+{
+ return mItemsPanel->getRect();
+}
+
+bool LLFlatListView::addItem(LLPanel * item, const LLSD& value /*= LLUUID::null*/, EAddPosition pos /*= ADD_BOTTOM*/,bool rearrange /*= true*/)
+{
+ if (!item) return false;
+ if (value.isUndefined()) return false;
+
+ //force uniqueness of items, easiest check but unreliable
+ if (item->getParent() == mItemsPanel) return false;
+
+ item_pair_t* new_pair = new item_pair_t(item, value);
+ switch (pos)
+ {
+ case ADD_TOP:
+ mItemPairs.push_front(new_pair);
+ //in LLView::draw() children are iterated in backorder
+ mItemsPanel->addChildInBack(item);
+ break;
+ case ADD_BOTTOM:
+ mItemPairs.push_back(new_pair);
+ mItemsPanel->addChild(item);
+ break;
+ default:
+ break;
+ }
+
+ //_4 is for MASK
+ item->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4));
+ item->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4));
+
+ // Children don't accept the focus
+ item->setTabStop(false);
+
+ if (rearrange)
+ {
+ rearrangeItems();
+ notifyParentItemsRectChanged();
+ }
+ return true;
+}
+
+bool LLFlatListView::addItemPairs(pairs_list_t panel_list, bool rearrange /*= true*/)
+{
+ if (!mItemComparator)
+ {
+ LL_WARNS_ONCE() << "No comparator specified for inserting FlatListView items." << LL_ENDL;
+ return false;
+ }
+ if (panel_list.size() == 0)
+ {
+ return false;
+ }
+
+ // presort list so that it will be easier to sort elements into mItemPairs
+ panel_list.sort(ComparatorAdaptor(*mItemComparator));
+
+ pairs_const_iterator_t new_pair_it = panel_list.begin();
+ item_pair_t* new_pair = *new_pair_it;
+ pairs_iterator_t pair_it = mItemPairs.begin();
+ item_pair_t* item_pair = *pair_it;
+
+ // sort panel_list into mItemPars
+ while (new_pair_it != panel_list.end() && pair_it != mItemPairs.end())
+ {
+ if (!new_pair->first || new_pair->first->getParent() == mItemsPanel)
+ {
+ // iterator already used or we are reusing existing panel
+ new_pair_it++;
+ new_pair = *new_pair_it;
+ }
+ else if (mItemComparator->compare(new_pair->first, item_pair->first))
+ {
+ LLPanel* panel = new_pair->first;
+
+ mItemPairs.insert(pair_it, new_pair);
+ mItemsPanel->addChild(panel);
+
+ //_4 is for MASK
+ panel->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4));
+ panel->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4));
+ // Children don't accept the focus
+ panel->setTabStop(false);
+ }
+ else
+ {
+ pair_it++;
+ item_pair = *pair_it;
+ }
+ }
+
+ // Add what is left of panel_list into the end of mItemPairs.
+ for (; new_pair_it != panel_list.end(); ++new_pair_it)
+ {
+ item_pair_t* item_pair = *new_pair_it;
+ LLPanel *panel = item_pair->first;
+ if (panel && panel->getParent() != mItemsPanel)
+ {
+ mItemPairs.push_back(item_pair);
+ mItemsPanel->addChild(panel);
+
+ //_4 is for MASK
+ panel->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, item_pair, _4));
+ panel->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, item_pair, _4));
+ // Children don't accept the focus
+ panel->setTabStop(false);
+ }
+ }
+
+ if (rearrange)
+ {
+ rearrangeItems();
+ notifyParentItemsRectChanged();
+ }
+ return true;
+}
+
+
+bool LLFlatListView::insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, const LLSD& value /*= LLUUID::null*/)
+{
+ if (!after_item) return false;
+ if (!item_to_add) return false;
+ if (value.isUndefined()) return false;
+
+ if (mItemPairs.empty()) return false;
+
+ //force uniqueness of items, easiest check but unreliable
+ if (item_to_add->getParent() == mItemsPanel) return false;
+
+ item_pair_t* after_pair = getItemPair(after_item);
+ if (!after_pair) return false;
+
+ item_pair_t* new_pair = new item_pair_t(item_to_add, value);
+ if (after_pair == mItemPairs.back())
+ {
+ mItemPairs.push_back(new_pair);
+ mItemsPanel->addChild(item_to_add);
+ }
+ else
+ {
+ pairs_iterator_t it = mItemPairs.begin();
+ for (; it != mItemPairs.end(); ++it)
+ {
+ if (*it == after_pair)
+ {
+ // insert new elements before the element at position of passed iterator.
+ mItemPairs.insert(++it, new_pair);
+ mItemsPanel->addChild(item_to_add);
+ break;
+ }
+ }
+ }
+
+ //_4 is for MASK
+ item_to_add->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4));
+ item_to_add->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4));
+
+ rearrangeItems();
+ notifyParentItemsRectChanged();
+ return true;
+}
+
+
+bool LLFlatListView::removeItem(LLPanel* item, bool rearrange)
+{
+ if (!item) return false;
+ if (item->getParent() != mItemsPanel) return false;
+
+ item_pair_t* item_pair = getItemPair(item);
+ if (!item_pair) return false;
+
+ return removeItemPair(item_pair, rearrange);
+}
+
+bool LLFlatListView::removeItemByValue(const LLSD& value, bool rearrange)
+{
+ if (value.isUndefined()) return false;
+
+ item_pair_t* item_pair = getItemPair(value);
+ if (!item_pair) return false;
+
+ return removeItemPair(item_pair, rearrange);
+}
+
+bool LLFlatListView::removeItemByUUID(const LLUUID& uuid, bool rearrange)
+{
+ return removeItemByValue(LLSD(uuid), rearrange);
+}
+
+LLPanel* LLFlatListView::getItemByValue(const LLSD& value) const
+{
+ if (value.isUndefined()) return NULL;
+
+ item_pair_t* pair = getItemPair(value);
+ if (pair) return pair->first;
+ return NULL;
+}
+
+bool LLFlatListView::selectItem(LLPanel* item, bool select /*= true*/)
+{
+ if (!item) return false;
+ if (item->getParent() != mItemsPanel) return false;
+
+ item_pair_t* item_pair = getItemPair(item);
+ if (!item_pair) return false;
+
+ return selectItemPair(item_pair, select);
+}
+
+bool LLFlatListView::selectItemByValue(const LLSD& value, bool select /*= true*/)
+{
+ if (value.isUndefined()) return false;
+
+ item_pair_t* item_pair = getItemPair(value);
+ if (!item_pair) return false;
+
+ return selectItemPair(item_pair, select);
+}
+
+bool LLFlatListView::selectItemByUUID(const LLUUID& uuid, bool select /* = true*/)
+{
+ return selectItemByValue(LLSD(uuid), select);
+}
+
+
+LLSD LLFlatListView::getSelectedValue() const
+{
+ if (mSelectedItemPairs.empty()) return LLSD();
+
+ item_pair_t* first_selected_pair = mSelectedItemPairs.front();
+ return first_selected_pair->second;
+}
+
+void LLFlatListView::getSelectedValues(std::vector<LLSD>& selected_values) const
+{
+ if (mSelectedItemPairs.empty()) return;
+
+ for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
+ {
+ selected_values.push_back((*it)->second);
+ }
+}
+
+LLUUID LLFlatListView::getSelectedUUID() const
+{
+ const LLSD& value = getSelectedValue();
+ if (value.isDefined() && value.isUUID())
+ {
+ return value.asUUID();
+ }
+ else
+ {
+ return LLUUID::null;
+ }
+}
+
+void LLFlatListView::getSelectedUUIDs(uuid_vec_t& selected_uuids) const
+{
+ if (mSelectedItemPairs.empty()) return;
+
+ for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
+ {
+ selected_uuids.push_back((*it)->second.asUUID());
+ }
+}
+
+LLPanel* LLFlatListView::getSelectedItem() const
+{
+ if (mSelectedItemPairs.empty()) return NULL;
+
+ return mSelectedItemPairs.front()->first;
+}
+
+void LLFlatListView::getSelectedItems(std::vector<LLPanel*>& selected_items) const
+{
+ if (mSelectedItemPairs.empty()) return;
+
+ for (pairs_const_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
+ {
+ selected_items.push_back((*it)->first);
+ }
+}
+
+void LLFlatListView::resetSelection(bool no_commit_on_deselection /*= false*/)
+{
+ if (mSelectedItemPairs.empty()) return;
+
+ for (pairs_iterator_t it= mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
+ {
+ item_pair_t* pair_to_deselect = *it;
+ LLPanel* item = pair_to_deselect->first;
+ item->setValue(UNSELECTED_EVENT);
+ }
+
+ mSelectedItemPairs.clear();
+
+ if (mCommitOnSelectionChange && !no_commit_on_deselection)
+ {
+ onCommit();
+ }
+
+ // Stretch selected item rect to ensure it won't be clipped
+ mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
+}
+
+void LLFlatListView::setNoItemsCommentText(const std::string& comment_text)
+{
+ mNoItemsCommentTextbox->setValue(comment_text);
+}
+
+U32 LLFlatListView::size(const bool only_visible_items) const
+{
+ if (only_visible_items)
+ {
+ U32 size = 0;
+ for (pairs_const_iterator_t
+ iter = mItemPairs.begin(),
+ iter_end = mItemPairs.end();
+ iter != iter_end; ++iter)
+ {
+ if ((*iter)->first->getVisible())
+ ++size;
+ }
+ return size;
+ }
+ else
+ {
+ return mItemPairs.size();
+ }
+}
+
+void LLFlatListView::clear()
+{
+ // This will clear mSelectedItemPairs, calling all appropriate callbacks.
+ resetSelection();
+
+ // do not use LLView::deleteAllChildren to avoid removing nonvisible items. drag-n-drop for ex.
+ for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ mItemsPanel->removeChild((*it)->first);
+ (*it)->first->die();
+ delete *it;
+ }
+ mItemPairs.clear();
+
+ // also set items panel height to zero. Reshape it to allow reshaping of non-item children
+ LLRect rc = mItemsPanel->getRect();
+ rc.mBottom = rc.mTop;
+ mItemsPanel->reshape(rc.getWidth(), rc.getHeight());
+ mItemsPanel->setRect(rc);
+
+ setNoItemsCommentVisible(true);
+ notifyParentItemsRectChanged();
+}
+
+void LLFlatListView::sort()
+{
+ if (!mItemComparator)
+ {
+ LL_WARNS() << "No comparator specified for sorting FlatListView items." << LL_ENDL;
+ return;
+ }
+
+ mItemPairs.sort(ComparatorAdaptor(*mItemComparator));
+ rearrangeItems();
+}
+
+bool LLFlatListView::updateValue(const LLSD& old_value, const LLSD& new_value)
+{
+ if (old_value.isUndefined() || new_value.isUndefined()) return false;
+ if (llsds_are_equal(old_value, new_value)) return false;
+
+ item_pair_t* item_pair = getItemPair(old_value);
+ if (!item_pair) return false;
+
+ item_pair->second = new_value;
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// PROTECTED STUFF
+//////////////////////////////////////////////////////////////////////////
+
+LLFlatListView::LLFlatListView(const LLFlatListView::Params& p)
+: LLScrollContainer(p)
+ , mItemComparator(NULL)
+ , mItemsPanel(NULL)
+ , mItemPad(p.item_pad)
+ , mAllowSelection(p.allow_select)
+ , mMultipleSelection(p.multi_select)
+ , mKeepOneItemSelected(p.keep_one_selected)
+ , mCommitOnSelectionChange(false)
+ , mPrevNotifyParentRect(LLRect())
+ , mNoItemsCommentTextbox(NULL)
+ , mIsConsecutiveSelection(false)
+ , mKeepSelectionVisibleOnReshape(p.keep_selection_visible_on_reshape)
+{
+ mBorderThickness = getBorderWidth();
+
+ LLRect scroll_rect = getRect();
+ LLRect items_rect;
+
+ setItemsNoScrollWidth(scroll_rect.getWidth());
+ items_rect.setLeftTopAndSize(mBorderThickness, scroll_rect.getHeight() - mBorderThickness, mItemsNoScrollWidth, 0);
+
+ LLPanel::Params pp;
+ pp.rect(items_rect);
+ mItemsPanel = LLUICtrlFactory::create<LLPanel> (pp);
+ addChild(mItemsPanel);
+
+ //we don't need to stretch in vertical direction on reshaping by a parent
+ //no bottom following!
+ mItemsPanel->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_TOP);
+
+ LLViewBorder::Params params;
+ params.name("scroll border");
+ params.rect(getLastSelectedItemRect());
+ params.visible(false);
+ params.bevel_style(LLViewBorder::BEVEL_IN);
+ mSelectedItemsBorder = LLUICtrlFactory::create<LLViewBorder> (params);
+ mItemsPanel->addChild( mSelectedItemsBorder );
+
+ {
+ // create textbox for "No Items" comment text
+ LLTextBox::Params text_p = p.no_items_text;
+ if (!text_p.rect.isProvided())
+ {
+ LLRect comment_rect = getRect();
+ comment_rect.setOriginAndSize(0, 0, comment_rect.getWidth(), comment_rect.getHeight());
+ comment_rect.stretch(-getBorderWidth());
+ text_p.rect(comment_rect);
+ }
+ text_p.border_visible(false);
+
+ if (!text_p.follows.isProvided())
+ {
+ text_p.follows.flags(FOLLOWS_ALL);
+ }
+ mNoItemsCommentTextbox = LLUICtrlFactory::create<LLTextBox>(text_p, this);
+ }
+};
+
+LLFlatListView::~LLFlatListView()
+{
+ for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ mItemsPanel->removeChild((*it)->first);
+ (*it)->first->die();
+ delete *it;
+ }
+ mItemPairs.clear();
+}
+
+// virtual
+void LLFlatListView::draw()
+{
+ // Highlight border if a child of this container has keyboard focus
+ if( mSelectedItemsBorder->getVisible() )
+ {
+ mSelectedItemsBorder->setKeyboardFocusHighlight( hasFocus() );
+ }
+ LLScrollContainer::draw();
+}
+
+// virtual
+bool LLFlatListView::postBuild()
+{
+ setTabStop(true);
+ return LLScrollContainer::postBuild();
+}
+
+void LLFlatListView::rearrangeItems()
+{
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+
+ setNoItemsCommentVisible(0==size());
+
+ if (mItemPairs.empty()) return;
+
+ //calculating required height - assuming items can be of different height
+ //list should accommodate all its items
+ S32 height = 0;
+
+ S32 invisible_children_count = 0;
+ pairs_iterator_t it = mItemPairs.begin();
+ for (; it != mItemPairs.end(); ++it)
+ {
+ LLPanel* item = (*it)->first;
+
+ // skip invisible child
+ if (!item->getVisible())
+ {
+ ++invisible_children_count;
+ continue;
+ }
+
+ height += item->getRect().getHeight();
+ }
+
+ // add paddings between items, excluding invisible ones
+ height += mItemPad * (mItemPairs.size() - invisible_children_count - 1);
+
+ LLRect rc = mItemsPanel->getRect();
+ S32 width = mItemsNoScrollWidth;
+
+ // update width to avoid horizontal scrollbar
+ if (height > getRect().getHeight() - 2 * mBorderThickness)
+ width -= scrollbar_size;
+
+ //changes the bottom, end of the list goes down in the scroll container
+ rc.setLeftTopAndSize(rc.mLeft, rc.mTop, width, height);
+ mItemsPanel->setRect(rc);
+
+ //reshaping items
+ S32 item_new_top = height;
+ pairs_iterator_t it2, first_it = mItemPairs.begin();
+ for (it2 = first_it; it2 != mItemPairs.end(); ++it2)
+ {
+ LLPanel* item = (*it2)->first;
+
+ // skip invisible child
+ if (!item->getVisible())
+ continue;
+
+ LLRect rc = item->getRect();
+ rc.setLeftTopAndSize(rc.mLeft, item_new_top, width, rc.getHeight());
+ item->reshape(rc.getWidth(), rc.getHeight());
+ item->setRect(rc);
+
+ // move top for next item in list
+ item_new_top -= (rc.getHeight() + mItemPad);
+ }
+
+ // Stretch selected item rect to ensure it won't be clipped
+ mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
+}
+
+void LLFlatListView::onItemMouseClick(item_pair_t* item_pair, MASK mask)
+{
+ if (!item_pair) return;
+
+ if (!item_pair->first)
+ {
+ LL_WARNS() << "Attempt to selet an item pair containing null panel item" << LL_ENDL;
+ return;
+ }
+
+ setFocus(true);
+
+ bool select_item = !isSelected(item_pair);
+
+ //*TODO find a better place for that enforcing stuff
+ if (mKeepOneItemSelected && numSelected() == 1 && !select_item) return;
+
+ if ( (mask & MASK_SHIFT) && !(mask & MASK_CONTROL)
+ && mMultipleSelection && !mSelectedItemPairs.empty() )
+ {
+ item_pair_t* last_selected_pair = mSelectedItemPairs.back();
+
+ // If item_pair is already selected - do nothing
+ if (last_selected_pair == item_pair)
+ return;
+
+ bool grab_items = false;
+ bool reverse = false;
+ pairs_list_t pairs_to_select;
+
+ // Pick out items from list between last selected and current clicked item_pair.
+ for (pairs_iterator_t
+ iter = mItemPairs.begin(),
+ iter_end = mItemPairs.end();
+ iter != iter_end; ++iter)
+ {
+ item_pair_t* cur = *iter;
+ if (cur == last_selected_pair || cur == item_pair)
+ {
+ // We've got reverse selection if last grabed item isn't a new selection.
+ reverse = grab_items && (cur != item_pair);
+ grab_items = !grab_items;
+ // Skip last selected and current clicked item pairs.
+ continue;
+ }
+ if (!cur->first->getVisible())
+ {
+ // Skip invisible item pairs.
+ continue;
+ }
+ if (grab_items)
+ {
+ pairs_to_select.push_back(cur);
+ }
+ }
+
+ if (reverse)
+ {
+ pairs_to_select.reverse();
+ }
+
+ pairs_to_select.push_back(item_pair);
+
+ for (pairs_iterator_t
+ iter = pairs_to_select.begin(),
+ iter_end = pairs_to_select.end();
+ iter != iter_end; ++iter)
+ {
+ item_pair_t* pair_to_select = *iter;
+ if (isSelected(pair_to_select))
+ {
+ // Item was already selected but there is a need to keep order from last selected pair to new selection.
+ // Do it here to prevent extra mCommitOnSelectionChange in selectItemPair().
+ mSelectedItemPairs.remove(pair_to_select);
+ mSelectedItemPairs.push_back(pair_to_select);
+ }
+ else
+ {
+ selectItemPair(pair_to_select, true);
+ }
+ }
+
+ if (!select_item)
+ {
+ // Update last selected item border.
+ mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
+ }
+ return;
+ }
+
+ //no need to do additional commit on selection reset
+ if (!(mask & MASK_CONTROL) || !mMultipleSelection) resetSelection(true);
+
+ //only CTRL usage allows to deselect an item, usual clicking on an item cannot deselect it
+ if (mask & MASK_CONTROL)
+ selectItemPair(item_pair, select_item);
+ else
+ selectItemPair(item_pair, true);
+}
+
+void LLFlatListView::onItemRightMouseClick(item_pair_t* item_pair, MASK mask)
+{
+ if (!item_pair)
+ return;
+
+ // Forbid deselecting of items on right mouse button click if mMultipleSelection flag is set on,
+ // because some of derived classes may have context menu and selected items must be kept.
+ if ( !(mask & MASK_CONTROL) && mMultipleSelection && isSelected(item_pair) )
+ return;
+
+ // else got same behavior as at onItemMouseClick
+ onItemMouseClick(item_pair, mask);
+}
+
+bool LLFlatListView::handleKeyHere(KEY key, MASK mask)
+{
+ bool reset_selection = (mask != MASK_SHIFT);
+ bool handled = false;
+ switch (key)
+ {
+ case KEY_RETURN:
+ {
+ if (mSelectedItemPairs.size() && mask == MASK_NONE)
+ {
+ mOnReturnSignal(this, getValue());
+ handled = true;
+ }
+ break;
+ }
+ case KEY_UP:
+ {
+ if ( !selectNextItemPair(true, reset_selection) && reset_selection)
+ {
+ // If case we are in accordion tab notify parent to go to the previous accordion
+ if(notifyParent(LLSD().with("action","select_prev")) > 0 )//message was processed
+ resetSelection();
+ }
+ break;
+ }
+ case KEY_DOWN:
+ {
+ if ( !selectNextItemPair(false, reset_selection) && reset_selection)
+ {
+ // If case we are in accordion tab notify parent to go to the next accordion
+ if( notifyParent(LLSD().with("action","select_next")) > 0 ) //message was processed
+ resetSelection();
+ }
+ break;
+ }
+ case KEY_ESCAPE:
+ {
+ if (mask == MASK_NONE)
+ {
+ setFocus(false); // pass focus to the game area (EXT-8357)
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ if ( ( key == KEY_UP || key == KEY_DOWN ) && mSelectedItemPairs.size() )
+ {
+ ensureSelectedVisible();
+ /*
+ LLRect visible_rc = getVisibleContentRect();
+ LLRect selected_rc = getLastSelectedItemRect();
+
+ if ( !visible_rc.contains (selected_rc) )
+ {
+ // But scroll in Items panel coordinates
+ scrollToShowRect(selected_rc);
+ }
+
+ // In case we are in accordion tab notify parent to show selected rectangle
+ LLRect screen_rc;
+ localRectToScreen(selected_rc, &screen_rc);
+ notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue()));*/
+
+ handled = true;
+ }
+
+ return handled ? handled : LLScrollContainer::handleKeyHere(key, mask);
+}
+
+LLFlatListView::item_pair_t* LLFlatListView::getItemPair(LLPanel* item) const
+{
+ llassert(item);
+
+ for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ item_pair_t* item_pair = *it;
+ if (item_pair->first == item) return item_pair;
+ }
+ return NULL;
+}
+
+//compares two LLSD's
+bool llsds_are_equal(const LLSD& llsd_1, const LLSD& llsd_2)
+{
+ llassert(llsd_1.isDefined());
+ llassert(llsd_2.isDefined());
+
+ if (llsd_1.type() != llsd_2.type()) return false;
+
+ if (!llsd_1.isMap())
+ {
+ if (llsd_1.isUUID()) return llsd_1.asUUID() == llsd_2.asUUID();
+
+ //assumptions that string representaion is enough for other types
+ return llsd_1.asString() == llsd_2.asString();
+ }
+
+ if (llsd_1.size() != llsd_2.size()) return false;
+
+ LLSD::map_const_iterator llsd_1_it = llsd_1.beginMap();
+ LLSD::map_const_iterator llsd_2_it = llsd_2.beginMap();
+ for (S32 i = 0; i < llsd_1.size(); ++i)
+ {
+ if ((*llsd_1_it).first != (*llsd_2_it).first) return false;
+ if (!llsds_are_equal((*llsd_1_it).second, (*llsd_2_it).second)) return false;
+ ++llsd_1_it;
+ ++llsd_2_it;
+ }
+ return true;
+}
+
+LLFlatListView::item_pair_t* LLFlatListView::getItemPair(const LLSD& value) const
+{
+ llassert(value.isDefined());
+
+ for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ item_pair_t* item_pair = *it;
+ if (llsds_are_equal(item_pair->second, value)) return item_pair;
+ }
+ return NULL;
+}
+
+bool LLFlatListView::selectItemPair(item_pair_t* item_pair, bool select)
+{
+ llassert(item_pair);
+
+ if (!mAllowSelection && select) return false;
+
+ if (isSelected(item_pair) == select) return true; //already in specified selection state
+ if (select)
+ {
+ mSelectedItemPairs.push_back(item_pair);
+ }
+ else
+ {
+ mSelectedItemPairs.remove(item_pair);
+ }
+
+ //a way of notifying panel of selection state changes
+ LLPanel* item = item_pair->first;
+ item->setValue(select ? SELECTED_EVENT : UNSELECTED_EVENT);
+
+ if (mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+
+ // Stretch selected item rect to ensure it won't be clipped
+ mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
+ // By default mark it as not consecutive selection
+ mIsConsecutiveSelection = false;
+
+ return true;
+}
+
+void LLFlatListView::scrollToShowFirstSelectedItem()
+{
+ if (!mSelectedItemPairs.size()) return;
+
+ LLRect selected_rc = mSelectedItemPairs.front()->first->getRect();
+
+ if (selected_rc.isValid())
+ {
+ scrollToShowRect(selected_rc);
+ }
+}
+
+LLRect LLFlatListView::getLastSelectedItemRect()
+{
+ if (!mSelectedItemPairs.size())
+ {
+ return LLRect::null;
+ }
+
+ return mSelectedItemPairs.back()->first->getRect();
+}
+
+void LLFlatListView::selectFirstItem ()
+{
+ // No items - no actions!
+ if (0 == size()) return;
+
+ // Select first visible item
+ for (pairs_iterator_t
+ iter = mItemPairs.begin(),
+ iter_end = mItemPairs.end();
+ iter != iter_end; ++iter)
+ {
+ // skip invisible items
+ if ( (*iter)->first->getVisible() )
+ {
+ selectItemPair(*iter, true);
+ ensureSelectedVisible();
+ break;
+ }
+ }
+}
+
+void LLFlatListView::selectLastItem ()
+{
+ // No items - no actions!
+ if (0 == size()) return;
+
+ // Select last visible item
+ for (pairs_list_t::reverse_iterator
+ r_iter = mItemPairs.rbegin(),
+ r_iter_end = mItemPairs.rend();
+ r_iter != r_iter_end; ++r_iter)
+ {
+ // skip invisible items
+ if ( (*r_iter)->first->getVisible() )
+ {
+ selectItemPair(*r_iter, true);
+ ensureSelectedVisible();
+ break;
+ }
+ }
+}
+
+void LLFlatListView::ensureSelectedVisible()
+{
+ LLRect selected_rc = getLastSelectedItemRect();
+
+ if ( selected_rc.isValid() )
+ {
+ scrollToShowRect(selected_rc);
+ }
+}
+
+
+// virtual
+bool LLFlatListView::selectNextItemPair(bool is_up_direction, bool reset_selection)
+{
+ // No items - no actions!
+ if ( 0 == size() )
+ return false;
+
+ if (!mIsConsecutiveSelection)
+ {
+ // Leave only one item selected if list has not consecutive selection
+ if (mSelectedItemPairs.size() && !reset_selection)
+ {
+ item_pair_t* cur_sel_pair = mSelectedItemPairs.back();
+ resetSelection();
+ selectItemPair (cur_sel_pair, true);
+ }
+ }
+
+ if ( mSelectedItemPairs.size() )
+ {
+ item_pair_t* to_sel_pair = NULL;
+ item_pair_t* cur_sel_pair = NULL;
+
+ // Take the last selected pair
+ cur_sel_pair = mSelectedItemPairs.back();
+ // Bases on given direction choose next item to select
+ if ( is_up_direction )
+ {
+ // Find current selected item position in mItemPairs list
+ pairs_list_t::reverse_iterator sel_it = std::find(mItemPairs.rbegin(), mItemPairs.rend(), cur_sel_pair);
+
+ for (;++sel_it != mItemPairs.rend();)
+ {
+ // skip invisible items
+ if ( (*sel_it)->first->getVisible() )
+ {
+ to_sel_pair = *sel_it;
+ break;
+ }
+ }
+ }
+ else
+ {
+ // Find current selected item position in mItemPairs list
+ pairs_list_t::iterator sel_it = std::find(mItemPairs.begin(), mItemPairs.end(), cur_sel_pair);
+
+ for (;++sel_it != mItemPairs.end();)
+ {
+ // skip invisible items
+ if ( (*sel_it)->first->getVisible() )
+ {
+ to_sel_pair = *sel_it;
+ break;
+ }
+ }
+ }
+
+ if ( to_sel_pair )
+ {
+ bool select = true;
+ if ( reset_selection )
+ {
+ // Reset current selection if we were asked about it
+ resetSelection();
+ }
+ else
+ {
+ // If item already selected and no reset request than we should deselect last selected item.
+ select = (mSelectedItemPairs.end() == std::find(mSelectedItemPairs.begin(), mSelectedItemPairs.end(), to_sel_pair));
+ }
+ // Select/Deselect next item
+ selectItemPair(select ? to_sel_pair : cur_sel_pair, select);
+ // Mark it as consecutive selection
+ mIsConsecutiveSelection = true;
+ return true;
+ }
+ }
+ else
+ {
+ // If there weren't selected items then choose the first one bases on given direction
+ // Force selection to first item
+ if (is_up_direction)
+ selectLastItem();
+ else
+ selectFirstItem();
+ // Mark it as consecutive selection
+ mIsConsecutiveSelection = true;
+ return true;
+ }
+
+ return false;
+}
+
+bool LLFlatListView::canSelectAll() const
+{
+ return 0 != size() && mAllowSelection && mMultipleSelection;
+}
+
+void LLFlatListView::selectAll()
+{
+ if (!mAllowSelection || !mMultipleSelection)
+ return;
+
+ mSelectedItemPairs.clear();
+
+ for (pairs_const_iterator_t it= mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ item_pair_t* item_pair = *it;
+ mSelectedItemPairs.push_back(item_pair);
+ //a way of notifying panel of selection state changes
+ LLPanel* item = item_pair->first;
+ item->setValue(SELECTED_EVENT);
+ }
+
+ if (mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+
+ // Stretch selected item rect to ensure it won't be clipped
+ mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1));
+}
+
+bool LLFlatListView::isSelected(item_pair_t* item_pair) const
+{
+ llassert(item_pair);
+
+ pairs_const_iterator_t it_end = mSelectedItemPairs.end();
+ return std::find(mSelectedItemPairs.begin(), it_end, item_pair) != it_end;
+}
+
+bool LLFlatListView::removeItemPair(item_pair_t* item_pair, bool rearrange)
+{
+ llassert(item_pair);
+
+ bool deleted = false;
+ bool selection_changed = false;
+ for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ item_pair_t* _item_pair = *it;
+ if (_item_pair == item_pair)
+ {
+ mItemPairs.erase(it);
+ deleted = true;
+ break;
+ }
+ }
+
+ if (!deleted) return false;
+
+ for (pairs_iterator_t it = mSelectedItemPairs.begin(); it != mSelectedItemPairs.end(); ++it)
+ {
+ item_pair_t* selected_item_pair = *it;
+ if (selected_item_pair == item_pair)
+ {
+ it = mSelectedItemPairs.erase(it);
+ selection_changed = true;
+ break;
+ }
+ }
+
+ mItemsPanel->removeChild(item_pair->first);
+ item_pair->first->die();
+ delete item_pair;
+
+ if (rearrange)
+ {
+ rearrangeItems();
+ notifyParentItemsRectChanged();
+ }
+
+ if (selection_changed && mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+
+ return true;
+}
+
+void LLFlatListView::notifyParentItemsRectChanged()
+{
+ S32 comment_height = 0;
+
+ // take into account comment text height if exists
+ if (mNoItemsCommentTextbox && mNoItemsCommentTextbox->getVisible())
+ {
+ // top text padding inside the textbox is included into the height
+ comment_height = mNoItemsCommentTextbox->getTextPixelHeight();
+
+ // take into account a distance from parent's top border to textbox's top
+ comment_height += getRect().getHeight() - mNoItemsCommentTextbox->getRect().mTop;
+ }
+
+ LLRect req_rect = getItemsRect();
+
+ // get maximum of items total height and comment text height
+ req_rect.setOriginAndSize(req_rect.mLeft, req_rect.mBottom, req_rect.getWidth(), llmax(req_rect.getHeight(), comment_height));
+
+ // take into account border size.
+ req_rect.stretch(getBorderWidth());
+
+ if (req_rect == mPrevNotifyParentRect)
+ return;
+
+ mPrevNotifyParentRect = req_rect;
+
+ LLSD params;
+ params["action"] = "size_changes";
+ params["width"] = req_rect.getWidth();
+ params["height"] = req_rect.getHeight();
+
+ if (getParent()) // dummy widgets don't have a parent
+ getParent()->notifyParent(params);
+}
+
+void LLFlatListView::setNoItemsCommentVisible(bool visible) const
+{
+ if (mNoItemsCommentTextbox)
+ {
+ mSelectedItemsBorder->setVisible(!visible);
+ mNoItemsCommentTextbox->setVisible(visible);
+ }
+}
+
+void LLFlatListView::getItems(std::vector<LLPanel*>& items) const
+{
+ if (mItemPairs.empty()) return;
+
+ items.clear();
+ for (pairs_const_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ items.push_back((*it)->first);
+ }
+}
+
+void LLFlatListView::getValues(std::vector<LLSD>& values) const
+{
+ if (mItemPairs.empty()) return;
+
+ values.clear();
+ for (pairs_const_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
+ {
+ values.push_back((*it)->second);
+ }
+}
+
+// virtual
+void LLFlatListView::onFocusReceived()
+{
+ if (size())
+ {
+ mSelectedItemsBorder->setVisible(true);
+ }
+ gEditMenuHandler = this;
+}
+// virtual
+void LLFlatListView::onFocusLost()
+{
+ mSelectedItemsBorder->setVisible(false);
+ // Route menu back to the default
+ if (gEditMenuHandler == this)
+ {
+ gEditMenuHandler = NULL;
+ }
+}
+
+//virtual
+S32 LLFlatListView::notify(const LLSD& info)
+{
+ if (info.has("action"))
+ {
+ std::string str_action = info["action"];
+ if (str_action == "select_first")
+ {
+ setFocus(true);
+ selectFirstItem();
+ return 1;
+ }
+ else if (str_action == "select_last")
+ {
+ setFocus(true);
+ selectLastItem();
+ return 1;
+ }
+ }
+ else if (info.has("rearrange"))
+ {
+ rearrangeItems();
+ notifyParentItemsRectChanged();
+ return 1;
+ }
+
+ return 0;
+}
+
+void LLFlatListView::detachItems(std::vector<LLPanel*>& detached_items)
+{
+ LLSD action;
+ action.with("detach", LLSD());
+ // Clear detached_items list
+ detached_items.clear();
+ // Go through items and detach valid items, remove them from items panel
+ // and add to detached_items.
+ pairs_iterator_t iter = mItemPairs.begin(), iter_end = mItemPairs.end();
+ while (iter != iter_end)
+ {
+ LLPanel* pItem = (*iter)->first;
+ if (1 == pItem->notify(action))
+ {
+ selectItemPair((*iter), false);
+ mItemsPanel->removeChild(pItem);
+ detached_items.push_back(pItem);
+ }
+ iter++;
+ }
+ if (!detached_items.empty())
+ {
+ // Some items were detached, clean ourself from unusable memory
+ if (detached_items.size() == mItemPairs.size())
+ {
+ // This way will be faster if all items were disconnected
+ pairs_iterator_t iter = mItemPairs.begin(), iter_end = mItemPairs.end();
+ while (iter != iter_end)
+ {
+ (*iter)->first = NULL;
+ delete *iter;
+ iter++;
+ }
+ mItemPairs.clear();
+ // Also set items panel height to zero.
+ // Reshape it to allow reshaping of non-item children.
+ LLRect rc = mItemsPanel->getRect();
+ rc.mBottom = rc.mTop;
+ mItemsPanel->reshape(rc.getWidth(), rc.getHeight());
+ mItemsPanel->setRect(rc);
+ setNoItemsCommentVisible(true);
+ }
+ else
+ {
+ std::vector<LLPanel*>::const_iterator
+ detached_iter = detached_items.begin(),
+ detached_iter_end = detached_items.end();
+ while (detached_iter < detached_iter_end)
+ {
+ LLPanel* pDetachedItem = *detached_iter;
+ pairs_iterator_t iter = mItemPairs.begin(), iter_end = mItemPairs.end();
+ while (iter != iter_end)
+ {
+ item_pair_t* item_pair = *iter;
+ if (item_pair->first == pDetachedItem)
+ {
+ mItemPairs.erase(iter);
+ item_pair->first = NULL;
+ delete item_pair;
+ break;
+ }
+ iter++;
+ }
+ detached_iter++;
+ }
+ rearrangeItems();
+ }
+ notifyParentItemsRectChanged();
+ }
+}
+
+
+/************************************************************************/
+/* LLFlatListViewEx implementation */
+/************************************************************************/
+LLFlatListViewEx::Params::Params()
+: no_items_msg("no_items_msg")
+, no_filtered_items_msg("no_filtered_items_msg")
+{
+}
+
+LLFlatListViewEx::LLFlatListViewEx(const Params& p)
+: LLFlatListView(p)
+, mNoFilteredItemsMsg(p.no_filtered_items_msg)
+, mNoItemsMsg(p.no_items_msg)
+, mForceShowingUnmatchedItems(false)
+, mHasMatchedItems(false)
+{
+}
+
+void LLFlatListViewEx::updateNoItemsMessage(const std::string& filter_string)
+{
+ bool items_filtered = !filter_string.empty();
+ if (items_filtered)
+ {
+ // items were filtered
+ LLStringUtil::format_map_t args;
+ args["[SEARCH_TERM]"] = LLURI::escape(filter_string);
+ std::string text = mNoFilteredItemsMsg;
+ LLStringUtil::format(text, args);
+ setNoItemsCommentText(text);
+ }
+ else
+ {
+ // list does not contain any items at all
+ setNoItemsCommentText(mNoItemsMsg);
+ }
+}
+
+bool LLFlatListViewEx::getForceShowingUnmatchedItems()
+{
+ return mForceShowingUnmatchedItems;
+}
+
+void LLFlatListViewEx::setForceShowingUnmatchedItems(bool show)
+{
+ mForceShowingUnmatchedItems = show;
+}
+
+void LLFlatListViewEx::setFilterSubString(const std::string& filter_str, bool notify_parent)
+{
+ if (0 != LLStringUtil::compareInsensitive(filter_str, mFilterSubString))
+ {
+ mFilterSubString = filter_str;
+ updateNoItemsMessage(mFilterSubString);
+ filterItems(false, notify_parent);
+ }
+}
+
+bool LLFlatListViewEx::updateItemVisibility(LLPanel* item, const LLSD &action)
+{
+ if (!item)
+ return false;
+
+ bool visible = true;
+
+ // 0 signifies that filter is matched,
+ // i.e. we don't hide items that don't support 'match_filter' action, separators etc.
+ if (0 == item->notify(action))
+ {
+ mHasMatchedItems = true;
+ }
+ else
+ {
+ // TODO: implement (re)storing of current selection.
+ if (!mForceShowingUnmatchedItems)
+ {
+ selectItem(item, false);
+ visible = false;
+ }
+ }
+
+ if (item->getVisible() != visible)
+ {
+ item->setVisible(visible);
+ return true;
+ }
+
+ return false;
+}
+
+void LLFlatListViewEx::filterItems(bool re_sort, bool notify_parent)
+{
+ std::string cur_filter = mFilterSubString;
+ LLStringUtil::toUpper(cur_filter);
+
+ LLSD action;
+ action.with("match_filter", cur_filter);
+
+ mHasMatchedItems = false;
+ bool visibility_changed = false;
+ pairs_const_iterator_t iter = getItemPairs().begin(), iter_end = getItemPairs().end();
+ while (iter != iter_end)
+ {
+ LLPanel* pItem = (*(iter++))->first;
+ visibility_changed |= updateItemVisibility(pItem, action);
+ }
+
+ if (re_sort)
+ {
+ sort();
+ }
+
+ if (visibility_changed && notify_parent)
+ {
+ notifyParentItemsRectChanged();
+ }
+}
+
+bool LLFlatListViewEx::hasMatchedItems()
+{
+ return mHasMatchedItems;
+}
+
+//EOF
diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h
index af83ef1a23..c24fd34ae6 100644
--- a/indra/llui/llflatlistview.h
+++ b/indra/llui/llflatlistview.h
@@ -1,535 +1,535 @@
-/**
- * @file llflatlistview.h
- * @brief LLFlatListView base class and extension to support messages for several cases of an empty list.
- *
- * $LicenseInfo:firstyear=2009&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLFLATLISTVIEW_H
-#define LL_LLFLATLISTVIEW_H
-
-#include "llpanel.h"
-#include "llscrollcontainer.h"
-#include "lltextbox.h"
-
-
-/**
- * LLFlatListView represents a flat list ui control that operates on items in a form of LLPanel's.
- * LLSD can be associated with each added item, it can keep data from an item in digested form.
- * Associated LLSD's can be of any type (singular, a map etc.).
- * Items (LLPanel's subclasses) can be of different height.
- * The list is LLPanel created in itself and grows in height while new items are added.
- *
- * The control can manage selection of its items when the flag "allow_select" is set. Also ability to select
- * multiple items (by using CTRL) is enabled through setting the flag "multi_select" - if selection is not allowed that flag
- * is ignored. The option "keep_one_selected" forces at least one item to be selected at any time (only for mouse events on items)
- * since any item of the list was selected.
- *
- * Examples of using this control are presented in Picks panel (My Profile and Profile View), where this control is used to
- * manage the list of pick items.
- *
- * ASSUMPTIONS AND STUFF
- * - NULL pointers and undefined LLSD's are not accepted by any method of this class unless specified otherwise
- * - Order of returned selected items are not guaranteed
- * - The control assumes that all items being added are unique.
- */
-class LLFlatListView : public LLScrollContainer, public LLEditMenuHandler
-{
- LOG_CLASS(LLFlatListView);
-public:
-
- /**
- * Abstract comparator for comparing flat list items in a form of LLPanel
- */
- class ItemComparator
- {
- public:
- ItemComparator() {};
- virtual ~ItemComparator() {};
-
- /** Returns true if item1 < item2, false otherwise */
- virtual bool compare(const LLPanel* item1, const LLPanel* item2) const = 0;
- };
-
- /**
- * Represents reverse comparator which acts as a decorator for a comparator that need to be reversed
- */
- class ItemReverseComparator : public ItemComparator
- {
- public:
- ItemReverseComparator(const ItemComparator& comparator) : mComparator(comparator) {};
- virtual ~ItemReverseComparator() {};
-
- virtual bool compare(const LLPanel* item1, const LLPanel* item2) const
- {
- return mComparator.compare(item2, item1);
- }
-
- private:
- const ItemComparator& mComparator;
- };
-
-
- struct Params : public LLInitParam::Block<Params, LLScrollContainer::Params>
- {
- /** turning on/off selection support */
- Optional<bool> allow_select;
-
- /** turning on/off multiple selection (works while clicking and holding CTRL)*/
- Optional<bool> multi_select;
-
- /** don't allow to deselect all selected items (for mouse events on items only) */
- Optional<bool> keep_one_selected;
-
- /** try to keep selection visible after reshape */
- Optional<bool> keep_selection_visible_on_reshape;
-
- /** padding between items */
- Optional<U32> item_pad;
-
- /** textbox with info message when list is empty*/
- Optional<LLTextBox::Params> no_items_text;
-
- Params();
- };
-
- // disable traversal when finding widget to hand focus off to
- /*virtual*/ bool canFocusChildren() const { return false; }
-
- /**
- * Connects callback to signal called when Return key is pressed.
- */
- boost::signals2::connection setReturnCallback( const commit_signal_t::slot_type& cb ) { return mOnReturnSignal.connect(cb); }
-
- /** Overridden LLPanel's reshape, height is ignored, the list sets its height to accommodate all items */
- virtual void reshape(S32 width, S32 height, bool called_from_parent = true);
-
- /** Returns full rect of child panel */
- const LLRect& getItemsRect() const;
-
- LLRect getRequiredRect() { return getItemsRect(); }
-
- /** Returns distance between items */
- const S32 getItemsPad() { return mItemPad; }
-
- /**
- * Adds and item and LLSD value associated with it to the list at specified position
- * @return true if the item was added, false otherwise
- */
- virtual bool addItem(LLPanel * item, const LLSD& value = LLUUID::null, EAddPosition pos = ADD_BOTTOM, bool rearrange = true);
-
- /**
- * Insert item_to_add along with associated value to the list right after the after_item.
- * @return true if the item was successfully added, false otherwise
- */
- virtual bool insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, const LLSD& value = LLUUID::null);
-
- /**
- * Remove specified item
- * @return true if the item was removed, false otherwise
- */
- virtual bool removeItem(LLPanel* item, bool rearrange = true);
-
- /**
- * Remove an item specified by value
- * @return true if the item was removed, false otherwise
- */
- virtual bool removeItemByValue(const LLSD& value, bool rearrange = true);
-
- /**
- * Remove an item specified by uuid
- * @return true if the item was removed, false otherwise
- */
- virtual bool removeItemByUUID(const LLUUID& uuid, bool rearrange = true);
-
- /**
- * Get an item by value
- * @return the item as LLPanel if associated with value, NULL otherwise
- */
- virtual LLPanel* getItemByValue(const LLSD& value) const;
-
- template<class T>
- T* getTypedItemByValue(const LLSD& value) const
- {
- return dynamic_cast<T*>(getItemByValue(value));
- }
-
- /**
- * Select or deselect specified item based on select
- * @return true if succeed, false otherwise
- */
- virtual bool selectItem(LLPanel* item, bool select = true);
-
- /**
- * Select or deselect an item by associated value based on select
- * @return true if succeed, false otherwise
- */
- virtual bool selectItemByValue(const LLSD& value, bool select = true);
-
- /**
- * Select or deselect an item by associated uuid based on select
- * @return true if succeed, false otherwise
- */
- virtual bool selectItemByUUID(const LLUUID& uuid, bool select = true);
-
- /**
- * Get all panels stored in the list.
- */
- virtual void getItems(std::vector<LLPanel*>& items) const;
-
- /**
- * Get all items values.
- */
- virtual void getValues(std::vector<LLSD>& values) const;
-
- /**
- * Get LLSD associated with the first selected item
- */
- virtual LLSD getSelectedValue() const;
-
- /**
- * Get LLSD's associated with selected items.
- * @param selected_values std::vector being populated with LLSD associated with selected items
- */
- virtual void getSelectedValues(std::vector<LLSD>& selected_values) const;
-
-
- /**
- * Get LLUUID associated with selected item
- * @return LLUUID if such was associated with selected item
- */
- virtual LLUUID getSelectedUUID() const;
-
- /**
- * Get LLUUIDs associated with selected items
- * @param selected_uuids An std::vector being populated with LLUUIDs associated with selected items
- */
- virtual void getSelectedUUIDs(uuid_vec_t& selected_uuids) const;
-
- /** Get the top selected item */
- virtual LLPanel* getSelectedItem() const;
-
- /**
- * Get selected items
- * @param selected_items An std::vector being populated with pointers to selected items
- */
- virtual void getSelectedItems(std::vector<LLPanel*>& selected_items) const;
-
-
- /**
- * Resets selection of items.
- *
- * It calls onCommit callback if setCommitOnSelectionChange(bool b) was called with "true"
- * argument for current Flat List.
- * @param no_commit_on_deselection - if true onCommit callback will not be called
- */
- virtual void resetSelection(bool no_commit_on_deselection = false);
-
- /**
- * Sets comment text which will be shown in the list is it is empty.
- *
- * Textbox to hold passed text is created while this method is called at the first time.
- *
- * @param comment_text - string to be shown as a comment.
- */
- void setNoItemsCommentText( const std::string& comment_text);
-
- /** Turn on/off multiple selection support */
- void setAllowMultipleSelection(bool allow) { mMultipleSelection = allow; }
-
- /** Turn on/off selection support */
- void setAllowSelection(bool can_select) { mAllowSelection = can_select; }
-
- /** Sets flag whether onCommit should be fired if selection was changed */
- // FIXME: this should really be a separate signal, since "Commit" implies explicit user action, and selection changes can happen more indirectly.
- void setCommitOnSelectionChange(bool b) { mCommitOnSelectionChange = b; }
-
- /** Get number of selected items in the list */
- U32 numSelected() const {return mSelectedItemPairs.size(); }
-
- /** Get number of (visible) items in the list */
- U32 size(const bool only_visible_items = true) const;
-
- /** Removes all items from the list */
- virtual void clear();
-
- /**
- * Removes all items that can be detached from the list but doesn't destroy
- * them, caller responsible to manage items after they are detached.
- * Detachable item should accept "detach" action via notify() method,
- * where it disconnect all callbacks, does other valuable routines and
- * return 1.
- */
- void detachItems(std::vector<LLPanel*>& detached_items);
-
- /**
- * Set comparator to use for future sorts.
- *
- * This class does NOT manage lifetime of the comparator
- * but assumes that the comparator is always alive.
- */
- void setComparator(const ItemComparator* comp) { mItemComparator = comp; }
- void sort();
-
- bool updateValue(const LLSD& old_value, const LLSD& new_value);
-
- void scrollToShowFirstSelectedItem();
-
- void selectFirstItem ();
- void selectLastItem ();
-
- virtual S32 notify(const LLSD& info) ;
-
- virtual ~LLFlatListView();
-
-protected:
-
- /** Pairs LLpanel representing a single item LLPanel and LLSD associated with it */
- typedef std::pair<LLPanel*, LLSD> item_pair_t;
-
- typedef std::list<item_pair_t*> pairs_list_t;
- typedef pairs_list_t::iterator pairs_iterator_t;
- typedef pairs_list_t::const_iterator pairs_const_iterator_t;
-
- /** An adapter for a ItemComparator */
- struct ComparatorAdaptor
- {
- ComparatorAdaptor(const ItemComparator& comparator) : mComparator(comparator) {};
-
- bool operator()(const item_pair_t* item_pair1, const item_pair_t* item_pair2)
- {
- return mComparator.compare(item_pair1->first, item_pair2->first);
- }
-
- const ItemComparator& mComparator;
- };
-
-
- friend class LLUICtrlFactory;
- LLFlatListView(const LLFlatListView::Params& p);
-
- /** Manage selection on mouse events */
- void onItemMouseClick(item_pair_t* item_pair, MASK mask);
-
- void onItemRightMouseClick(item_pair_t* item_pair, MASK mask);
-
- /**
- * Updates position of items.
- * It does not take into account invisible items.
- */
- virtual void rearrangeItems();
-
- virtual item_pair_t* getItemPair(LLPanel* item) const;
-
- virtual item_pair_t* getItemPair(const LLSD& value) const;
-
- virtual bool selectItemPair(item_pair_t* item_pair, bool select);
-
- virtual bool selectNextItemPair(bool is_up_direction, bool reset_selection);
-
- virtual bool canSelectAll() const;
- virtual void selectAll();
-
- virtual bool isSelected(item_pair_t* item_pair) const;
-
- virtual bool removeItemPair(item_pair_t* item_pair, bool rearrange);
-
- bool addItemPairs(pairs_list_t panel_list, bool rearrange = true);
-
- /**
- * Notify parent about changed size of internal controls with "size_changes" action
- *
- * Size includes Items Rect width and either Items Rect height or comment text height.
- * Comment text height is included if comment text is set and visible.
- * List border size is also included into notified size.
- */
- void notifyParentItemsRectChanged();
-
- virtual bool handleKeyHere(KEY key, MASK mask);
-
- virtual bool postBuild();
-
- virtual void onFocusReceived();
-
- virtual void onFocusLost();
-
- virtual void draw();
-
- LLRect getLastSelectedItemRect();
-
- void ensureSelectedVisible();
-
- const pairs_list_t& getItemPairs() { return mItemPairs; }
-
-private:
-
- void setItemsNoScrollWidth(S32 new_width) {mItemsNoScrollWidth = new_width - 2 * mBorderThickness;}
-
- void setNoItemsCommentVisible(bool visible) const;
-
-protected:
-
- /** Comparator to use when sorting the list. */
- const ItemComparator* mItemComparator;
-
-
-private:
-
- LLPanel* mItemsPanel;
-
- S32 mItemsNoScrollWidth;
-
- S32 mBorderThickness;
-
- /** Items padding */
- S32 mItemPad;
-
- /** Selection support flag */
- bool mAllowSelection;
-
- /** Multiselection support flag, ignored if selection is not supported */
- bool mMultipleSelection;
-
- /**
- * Flag specified whether onCommit be called if selection is changed in the list.
- *
- * Can be ignored in the resetSelection() method.
- * @see resetSelection()
- */
- bool mCommitOnSelectionChange;
-
- bool mKeepOneItemSelected;
-
- bool mIsConsecutiveSelection;
-
- bool mKeepSelectionVisibleOnReshape;
-
- /** All pairs of the list */
- pairs_list_t mItemPairs;
-
- /** Selected pairs for faster access */
- pairs_list_t mSelectedItemPairs;
-
- /**
- * Rectangle contained previous size of items parent notified last time.
- * Is used to reduce amount of parentNotify() calls if size was not changed.
- */
- LLRect mPrevNotifyParentRect;
-
- LLTextBox* mNoItemsCommentTextbox;
-
- LLViewBorder* mSelectedItemsBorder;
-
- commit_signal_t mOnReturnSignal;
-};
-
-/**
- * Extends LLFlatListView functionality to show different messages when there are no items in the
- * list depend on whether they are filtered or not.
- *
- * Class provides one message per case of empty list.
- * It also provides protected updateNoItemsMessage() method to be called each time when derived list
- * is changed to update base mNoItemsCommentTextbox value.
- *
- * It is implemented to avoid duplication of this functionality in concrete implementations of the
- * lists. It is intended to be used as a base class for lists which should support two different
- * messages for empty state. Can be improved to support more than two messages via state-to-message map.
- */
-class LLFlatListViewEx : public LLFlatListView
-{
-public:
- LOG_CLASS(LLFlatListViewEx);
-
- struct Params : public LLInitParam::Block<Params, LLFlatListView::Params>
- {
- /**
- * Contains a message for empty list when it does not contain any items at all.
- */
- Optional<std::string> no_items_msg;
-
- /**
- * Contains a message for empty list when its items are removed by filtering.
- */
- Optional<std::string> no_filtered_items_msg;
- Params();
- };
-
- // *WORKAROUND: two methods to overload appropriate Params due to localization issue:
- // no_items_msg & no_filtered_items_msg attributes are not defined as translatable in VLT. See EXT-5931
- void setNoItemsMsg(const std::string& msg) { mNoItemsMsg = msg; }
- void setNoFilteredItemsMsg(const std::string& msg) { mNoFilteredItemsMsg = msg; }
-
- bool getForceShowingUnmatchedItems();
-
- void setForceShowingUnmatchedItems(bool show);
-
- /**
- * Sets up new filter string and filters the list.
- */
- void setFilterSubString(const std::string& filter_str, bool notify_parent);
- std::string getFilterSubString() { return mFilterSubString; }
-
- /**
- * Filters the list, rearranges and notifies parent about shape changes.
- * Derived classes may want to overload rearrangeItems() to exclude repeated separators after filtration.
- */
- void filterItems(bool re_sort, bool notify_parent);
-
- /**
- * Returns true if last call of filterItems() found at least one matching item
- */
- bool hasMatchedItems();
-
-protected:
- LLFlatListViewEx(const Params& p);
-
- /**
- * Applies a message for empty list depend on passed argument.
- *
- * @param filter_string - if is not empty, message for filtered items will be set, otherwise for
- * completely empty list. Value of filter string will be passed as search_term in SLURL.
- */
- void updateNoItemsMessage(const std::string& filter_string);
-
- /**
- * Applies visibility acording to action and LLFlatListView settings.
- *
- * @param item - item we are changing
- * @param item - action - parameters to determin visibility from
- */
- bool updateItemVisibility(LLPanel* item, const LLSD &action);
-
-private:
- std::string mNoFilteredItemsMsg;
- std::string mNoItemsMsg;
- std::string mFilterSubString;
- /**
- * Show list items that don't match current filter
- */
- bool mForceShowingUnmatchedItems;
- /**
- * True if last call of filterItems() found at least one matching item
- */
- bool mHasMatchedItems;
-};
-
-#endif
+/**
+ * @file llflatlistview.h
+ * @brief LLFlatListView base class and extension to support messages for several cases of an empty list.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLFLATLISTVIEW_H
+#define LL_LLFLATLISTVIEW_H
+
+#include "llpanel.h"
+#include "llscrollcontainer.h"
+#include "lltextbox.h"
+
+
+/**
+ * LLFlatListView represents a flat list ui control that operates on items in a form of LLPanel's.
+ * LLSD can be associated with each added item, it can keep data from an item in digested form.
+ * Associated LLSD's can be of any type (singular, a map etc.).
+ * Items (LLPanel's subclasses) can be of different height.
+ * The list is LLPanel created in itself and grows in height while new items are added.
+ *
+ * The control can manage selection of its items when the flag "allow_select" is set. Also ability to select
+ * multiple items (by using CTRL) is enabled through setting the flag "multi_select" - if selection is not allowed that flag
+ * is ignored. The option "keep_one_selected" forces at least one item to be selected at any time (only for mouse events on items)
+ * since any item of the list was selected.
+ *
+ * Examples of using this control are presented in Picks panel (My Profile and Profile View), where this control is used to
+ * manage the list of pick items.
+ *
+ * ASSUMPTIONS AND STUFF
+ * - NULL pointers and undefined LLSD's are not accepted by any method of this class unless specified otherwise
+ * - Order of returned selected items are not guaranteed
+ * - The control assumes that all items being added are unique.
+ */
+class LLFlatListView : public LLScrollContainer, public LLEditMenuHandler
+{
+ LOG_CLASS(LLFlatListView);
+public:
+
+ /**
+ * Abstract comparator for comparing flat list items in a form of LLPanel
+ */
+ class ItemComparator
+ {
+ public:
+ ItemComparator() {};
+ virtual ~ItemComparator() {};
+
+ /** Returns true if item1 < item2, false otherwise */
+ virtual bool compare(const LLPanel* item1, const LLPanel* item2) const = 0;
+ };
+
+ /**
+ * Represents reverse comparator which acts as a decorator for a comparator that need to be reversed
+ */
+ class ItemReverseComparator : public ItemComparator
+ {
+ public:
+ ItemReverseComparator(const ItemComparator& comparator) : mComparator(comparator) {};
+ virtual ~ItemReverseComparator() {};
+
+ virtual bool compare(const LLPanel* item1, const LLPanel* item2) const
+ {
+ return mComparator.compare(item2, item1);
+ }
+
+ private:
+ const ItemComparator& mComparator;
+ };
+
+
+ struct Params : public LLInitParam::Block<Params, LLScrollContainer::Params>
+ {
+ /** turning on/off selection support */
+ Optional<bool> allow_select;
+
+ /** turning on/off multiple selection (works while clicking and holding CTRL)*/
+ Optional<bool> multi_select;
+
+ /** don't allow to deselect all selected items (for mouse events on items only) */
+ Optional<bool> keep_one_selected;
+
+ /** try to keep selection visible after reshape */
+ Optional<bool> keep_selection_visible_on_reshape;
+
+ /** padding between items */
+ Optional<U32> item_pad;
+
+ /** textbox with info message when list is empty*/
+ Optional<LLTextBox::Params> no_items_text;
+
+ Params();
+ };
+
+ // disable traversal when finding widget to hand focus off to
+ /*virtual*/ bool canFocusChildren() const { return false; }
+
+ /**
+ * Connects callback to signal called when Return key is pressed.
+ */
+ boost::signals2::connection setReturnCallback( const commit_signal_t::slot_type& cb ) { return mOnReturnSignal.connect(cb); }
+
+ /** Overridden LLPanel's reshape, height is ignored, the list sets its height to accommodate all items */
+ virtual void reshape(S32 width, S32 height, bool called_from_parent = true);
+
+ /** Returns full rect of child panel */
+ const LLRect& getItemsRect() const;
+
+ LLRect getRequiredRect() { return getItemsRect(); }
+
+ /** Returns distance between items */
+ const S32 getItemsPad() { return mItemPad; }
+
+ /**
+ * Adds and item and LLSD value associated with it to the list at specified position
+ * @return true if the item was added, false otherwise
+ */
+ virtual bool addItem(LLPanel * item, const LLSD& value = LLUUID::null, EAddPosition pos = ADD_BOTTOM, bool rearrange = true);
+
+ /**
+ * Insert item_to_add along with associated value to the list right after the after_item.
+ * @return true if the item was successfully added, false otherwise
+ */
+ virtual bool insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, const LLSD& value = LLUUID::null);
+
+ /**
+ * Remove specified item
+ * @return true if the item was removed, false otherwise
+ */
+ virtual bool removeItem(LLPanel* item, bool rearrange = true);
+
+ /**
+ * Remove an item specified by value
+ * @return true if the item was removed, false otherwise
+ */
+ virtual bool removeItemByValue(const LLSD& value, bool rearrange = true);
+
+ /**
+ * Remove an item specified by uuid
+ * @return true if the item was removed, false otherwise
+ */
+ virtual bool removeItemByUUID(const LLUUID& uuid, bool rearrange = true);
+
+ /**
+ * Get an item by value
+ * @return the item as LLPanel if associated with value, NULL otherwise
+ */
+ virtual LLPanel* getItemByValue(const LLSD& value) const;
+
+ template<class T>
+ T* getTypedItemByValue(const LLSD& value) const
+ {
+ return dynamic_cast<T*>(getItemByValue(value));
+ }
+
+ /**
+ * Select or deselect specified item based on select
+ * @return true if succeed, false otherwise
+ */
+ virtual bool selectItem(LLPanel* item, bool select = true);
+
+ /**
+ * Select or deselect an item by associated value based on select
+ * @return true if succeed, false otherwise
+ */
+ virtual bool selectItemByValue(const LLSD& value, bool select = true);
+
+ /**
+ * Select or deselect an item by associated uuid based on select
+ * @return true if succeed, false otherwise
+ */
+ virtual bool selectItemByUUID(const LLUUID& uuid, bool select = true);
+
+ /**
+ * Get all panels stored in the list.
+ */
+ virtual void getItems(std::vector<LLPanel*>& items) const;
+
+ /**
+ * Get all items values.
+ */
+ virtual void getValues(std::vector<LLSD>& values) const;
+
+ /**
+ * Get LLSD associated with the first selected item
+ */
+ virtual LLSD getSelectedValue() const;
+
+ /**
+ * Get LLSD's associated with selected items.
+ * @param selected_values std::vector being populated with LLSD associated with selected items
+ */
+ virtual void getSelectedValues(std::vector<LLSD>& selected_values) const;
+
+
+ /**
+ * Get LLUUID associated with selected item
+ * @return LLUUID if such was associated with selected item
+ */
+ virtual LLUUID getSelectedUUID() const;
+
+ /**
+ * Get LLUUIDs associated with selected items
+ * @param selected_uuids An std::vector being populated with LLUUIDs associated with selected items
+ */
+ virtual void getSelectedUUIDs(uuid_vec_t& selected_uuids) const;
+
+ /** Get the top selected item */
+ virtual LLPanel* getSelectedItem() const;
+
+ /**
+ * Get selected items
+ * @param selected_items An std::vector being populated with pointers to selected items
+ */
+ virtual void getSelectedItems(std::vector<LLPanel*>& selected_items) const;
+
+
+ /**
+ * Resets selection of items.
+ *
+ * It calls onCommit callback if setCommitOnSelectionChange(bool b) was called with "true"
+ * argument for current Flat List.
+ * @param no_commit_on_deselection - if true onCommit callback will not be called
+ */
+ virtual void resetSelection(bool no_commit_on_deselection = false);
+
+ /**
+ * Sets comment text which will be shown in the list is it is empty.
+ *
+ * Textbox to hold passed text is created while this method is called at the first time.
+ *
+ * @param comment_text - string to be shown as a comment.
+ */
+ void setNoItemsCommentText( const std::string& comment_text);
+
+ /** Turn on/off multiple selection support */
+ void setAllowMultipleSelection(bool allow) { mMultipleSelection = allow; }
+
+ /** Turn on/off selection support */
+ void setAllowSelection(bool can_select) { mAllowSelection = can_select; }
+
+ /** Sets flag whether onCommit should be fired if selection was changed */
+ // FIXME: this should really be a separate signal, since "Commit" implies explicit user action, and selection changes can happen more indirectly.
+ void setCommitOnSelectionChange(bool b) { mCommitOnSelectionChange = b; }
+
+ /** Get number of selected items in the list */
+ U32 numSelected() const {return mSelectedItemPairs.size(); }
+
+ /** Get number of (visible) items in the list */
+ U32 size(const bool only_visible_items = true) const;
+
+ /** Removes all items from the list */
+ virtual void clear();
+
+ /**
+ * Removes all items that can be detached from the list but doesn't destroy
+ * them, caller responsible to manage items after they are detached.
+ * Detachable item should accept "detach" action via notify() method,
+ * where it disconnect all callbacks, does other valuable routines and
+ * return 1.
+ */
+ void detachItems(std::vector<LLPanel*>& detached_items);
+
+ /**
+ * Set comparator to use for future sorts.
+ *
+ * This class does NOT manage lifetime of the comparator
+ * but assumes that the comparator is always alive.
+ */
+ void setComparator(const ItemComparator* comp) { mItemComparator = comp; }
+ void sort();
+
+ bool updateValue(const LLSD& old_value, const LLSD& new_value);
+
+ void scrollToShowFirstSelectedItem();
+
+ void selectFirstItem ();
+ void selectLastItem ();
+
+ virtual S32 notify(const LLSD& info) ;
+
+ virtual ~LLFlatListView();
+
+protected:
+
+ /** Pairs LLpanel representing a single item LLPanel and LLSD associated with it */
+ typedef std::pair<LLPanel*, LLSD> item_pair_t;
+
+ typedef std::list<item_pair_t*> pairs_list_t;
+ typedef pairs_list_t::iterator pairs_iterator_t;
+ typedef pairs_list_t::const_iterator pairs_const_iterator_t;
+
+ /** An adapter for a ItemComparator */
+ struct ComparatorAdaptor
+ {
+ ComparatorAdaptor(const ItemComparator& comparator) : mComparator(comparator) {};
+
+ bool operator()(const item_pair_t* item_pair1, const item_pair_t* item_pair2)
+ {
+ return mComparator.compare(item_pair1->first, item_pair2->first);
+ }
+
+ const ItemComparator& mComparator;
+ };
+
+
+ friend class LLUICtrlFactory;
+ LLFlatListView(const LLFlatListView::Params& p);
+
+ /** Manage selection on mouse events */
+ void onItemMouseClick(item_pair_t* item_pair, MASK mask);
+
+ void onItemRightMouseClick(item_pair_t* item_pair, MASK mask);
+
+ /**
+ * Updates position of items.
+ * It does not take into account invisible items.
+ */
+ virtual void rearrangeItems();
+
+ virtual item_pair_t* getItemPair(LLPanel* item) const;
+
+ virtual item_pair_t* getItemPair(const LLSD& value) const;
+
+ virtual bool selectItemPair(item_pair_t* item_pair, bool select);
+
+ virtual bool selectNextItemPair(bool is_up_direction, bool reset_selection);
+
+ virtual bool canSelectAll() const;
+ virtual void selectAll();
+
+ virtual bool isSelected(item_pair_t* item_pair) const;
+
+ virtual bool removeItemPair(item_pair_t* item_pair, bool rearrange);
+
+ bool addItemPairs(pairs_list_t panel_list, bool rearrange = true);
+
+ /**
+ * Notify parent about changed size of internal controls with "size_changes" action
+ *
+ * Size includes Items Rect width and either Items Rect height or comment text height.
+ * Comment text height is included if comment text is set and visible.
+ * List border size is also included into notified size.
+ */
+ void notifyParentItemsRectChanged();
+
+ virtual bool handleKeyHere(KEY key, MASK mask);
+
+ virtual bool postBuild();
+
+ virtual void onFocusReceived();
+
+ virtual void onFocusLost();
+
+ virtual void draw();
+
+ LLRect getLastSelectedItemRect();
+
+ void ensureSelectedVisible();
+
+ const pairs_list_t& getItemPairs() { return mItemPairs; }
+
+private:
+
+ void setItemsNoScrollWidth(S32 new_width) {mItemsNoScrollWidth = new_width - 2 * mBorderThickness;}
+
+ void setNoItemsCommentVisible(bool visible) const;
+
+protected:
+
+ /** Comparator to use when sorting the list. */
+ const ItemComparator* mItemComparator;
+
+
+private:
+
+ LLPanel* mItemsPanel;
+
+ S32 mItemsNoScrollWidth;
+
+ S32 mBorderThickness;
+
+ /** Items padding */
+ S32 mItemPad;
+
+ /** Selection support flag */
+ bool mAllowSelection;
+
+ /** Multiselection support flag, ignored if selection is not supported */
+ bool mMultipleSelection;
+
+ /**
+ * Flag specified whether onCommit be called if selection is changed in the list.
+ *
+ * Can be ignored in the resetSelection() method.
+ * @see resetSelection()
+ */
+ bool mCommitOnSelectionChange;
+
+ bool mKeepOneItemSelected;
+
+ bool mIsConsecutiveSelection;
+
+ bool mKeepSelectionVisibleOnReshape;
+
+ /** All pairs of the list */
+ pairs_list_t mItemPairs;
+
+ /** Selected pairs for faster access */
+ pairs_list_t mSelectedItemPairs;
+
+ /**
+ * Rectangle contained previous size of items parent notified last time.
+ * Is used to reduce amount of parentNotify() calls if size was not changed.
+ */
+ LLRect mPrevNotifyParentRect;
+
+ LLTextBox* mNoItemsCommentTextbox;
+
+ LLViewBorder* mSelectedItemsBorder;
+
+ commit_signal_t mOnReturnSignal;
+};
+
+/**
+ * Extends LLFlatListView functionality to show different messages when there are no items in the
+ * list depend on whether they are filtered or not.
+ *
+ * Class provides one message per case of empty list.
+ * It also provides protected updateNoItemsMessage() method to be called each time when derived list
+ * is changed to update base mNoItemsCommentTextbox value.
+ *
+ * It is implemented to avoid duplication of this functionality in concrete implementations of the
+ * lists. It is intended to be used as a base class for lists which should support two different
+ * messages for empty state. Can be improved to support more than two messages via state-to-message map.
+ */
+class LLFlatListViewEx : public LLFlatListView
+{
+public:
+ LOG_CLASS(LLFlatListViewEx);
+
+ struct Params : public LLInitParam::Block<Params, LLFlatListView::Params>
+ {
+ /**
+ * Contains a message for empty list when it does not contain any items at all.
+ */
+ Optional<std::string> no_items_msg;
+
+ /**
+ * Contains a message for empty list when its items are removed by filtering.
+ */
+ Optional<std::string> no_filtered_items_msg;
+ Params();
+ };
+
+ // *WORKAROUND: two methods to overload appropriate Params due to localization issue:
+ // no_items_msg & no_filtered_items_msg attributes are not defined as translatable in VLT. See EXT-5931
+ void setNoItemsMsg(const std::string& msg) { mNoItemsMsg = msg; }
+ void setNoFilteredItemsMsg(const std::string& msg) { mNoFilteredItemsMsg = msg; }
+
+ bool getForceShowingUnmatchedItems();
+
+ void setForceShowingUnmatchedItems(bool show);
+
+ /**
+ * Sets up new filter string and filters the list.
+ */
+ void setFilterSubString(const std::string& filter_str, bool notify_parent);
+ std::string getFilterSubString() { return mFilterSubString; }
+
+ /**
+ * Filters the list, rearranges and notifies parent about shape changes.
+ * Derived classes may want to overload rearrangeItems() to exclude repeated separators after filtration.
+ */
+ void filterItems(bool re_sort, bool notify_parent);
+
+ /**
+ * Returns true if last call of filterItems() found at least one matching item
+ */
+ bool hasMatchedItems();
+
+protected:
+ LLFlatListViewEx(const Params& p);
+
+ /**
+ * Applies a message for empty list depend on passed argument.
+ *
+ * @param filter_string - if is not empty, message for filtered items will be set, otherwise for
+ * completely empty list. Value of filter string will be passed as search_term in SLURL.
+ */
+ void updateNoItemsMessage(const std::string& filter_string);
+
+ /**
+ * Applies visibility acording to action and LLFlatListView settings.
+ *
+ * @param item - item we are changing
+ * @param item - action - parameters to determin visibility from
+ */
+ bool updateItemVisibility(LLPanel* item, const LLSD &action);
+
+private:
+ std::string mNoFilteredItemsMsg;
+ std::string mNoItemsMsg;
+ std::string mFilterSubString;
+ /**
+ * Show list items that don't match current filter
+ */
+ bool mForceShowingUnmatchedItems;
+ /**
+ * True if last call of filterItems() found at least one matching item
+ */
+ bool mHasMatchedItems;
+};
+
+#endif
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index 48693c6267..aa8f2ad04b 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -1,3735 +1,3735 @@
-/**
- * @file llfloater.cpp
- * @brief LLFloater base class
- *
- * $LicenseInfo:firstyear=2002&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$
- */
-
-// Floating "windows" within the GL display, like the inventory floater,
-// mini-map floater, etc.
-
-#include "linden_common.h"
-#include "llviewereventrecorder.h"
-#include "llfloater.h"
-
-#include "llfocusmgr.h"
-
-#include "lluictrlfactory.h"
-#include "llbutton.h"
-#include "llcheckboxctrl.h"
-#include "llcriticaldamp.h" // LLSmoothInterpolation
-#include "lldir.h"
-#include "lldraghandle.h"
-#include "llfloaterreg.h"
-#include "llfocusmgr.h"
-#include "llresizebar.h"
-#include "llresizehandle.h"
-#include "llkeyboard.h"
-#include "llmenugl.h" // MENU_BAR_HEIGHT
-#include "llmodaldialog.h"
-#include "lltextbox.h"
-#include "llresmgr.h"
-#include "llui.h"
-#include "llwindow.h"
-#include "llstl.h"
-#include "llcontrol.h"
-#include "lltabcontainer.h"
-#include "v2math.h"
-#include "lltrans.h"
-#include "llhelp.h"
-#include "llmultifloater.h"
-#include "llsdutil.h"
-#include "lluiusage.h"
-
-
-// use this to control "jumping" behavior when Ctrl-Tabbing
-const S32 TABBED_FLOATER_OFFSET = 0;
-
-const F32 LLFloater::CONTEXT_CONE_IN_ALPHA = 0.0f;
-const F32 LLFloater::CONTEXT_CONE_OUT_ALPHA = 1.f;
-const F32 LLFloater::CONTEXT_CONE_FADE_TIME = 0.08f;
-
-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
- "llfloater_restore_btn", //BUTTON_RESTORE
- "llfloater_minimize_btn", //BUTTON_MINIMIZE
- "llfloater_tear_off_btn", //BUTTON_TEAR_OFF
- "llfloater_dock_btn", //BUTTON_DOCK
- "llfloater_help_btn" //BUTTON_HELP
-};
-
-std::string LLFloater::sButtonToolTips[BUTTON_COUNT];
-
-std::string LLFloater::sButtonToolTipsIndex[BUTTON_COUNT]=
-{
-#ifdef LL_DARWIN
- "BUTTON_CLOSE_DARWIN", //"Close (Cmd-W)", //BUTTON_CLOSE
-#else
- "BUTTON_CLOSE_WIN", //"Close (Ctrl-W)", //BUTTON_CLOSE
-#endif
- "BUTTON_RESTORE", //"Restore", //BUTTON_RESTORE
- "BUTTON_MINIMIZE", //"Minimize", //BUTTON_MINIMIZE
- "BUTTON_TEAR_OFF", //"Tear Off", //BUTTON_TEAR_OFF
- "BUTTON_DOCK",
- "BUTTON_HELP"
-};
-
-LLFloater::click_callback LLFloater::sButtonCallbacks[BUTTON_COUNT] =
-{
- LLFloater::onClickClose, //BUTTON_CLOSE
- LLFloater::onClickMinimize, //BUTTON_RESTORE
- LLFloater::onClickMinimize, //BUTTON_MINIMIZE
- LLFloater::onClickTearOff, //BUTTON_TEAR_OFF
- LLFloater::onClickDock, //BUTTON_DOCK
- LLFloater::onClickHelp //BUTTON_HELP
-};
-
-LLMultiFloater* LLFloater::sHostp = NULL;
-bool LLFloater::sQuitting = false; // Flag to prevent storing visibility controls while quitting
-
-LLFloaterView* gFloaterView = NULL;
-
-/*==========================================================================*|
-// DEV-38598: The fundamental problem with this operation is that it can only
-// support a subset of LLSD values. While it's plausible to compare two arrays
-// lexicographically, what strict ordering can you impose on maps?
-// (LLFloaterTOS's current key is an LLSD map.)
-
-// Of course something like this is necessary if you want to build a std::set
-// or std::map with LLSD keys. Fortunately we're getting by with other
-// container types for now.
-
-//static
-bool LLFloater::KeyCompare::compare(const LLSD& a, const LLSD& b)
-{
- if (a.type() != b.type())
- {
- //LL_ERRS() << "Mismatched LLSD types: (" << a << ") mismatches (" << b << ")" << LL_ENDL;
- return false;
- }
- else if (a.isUndefined())
- return false;
- else if (a.isInteger())
- return a.asInteger() < b.asInteger();
- else if (a.isReal())
- return a.asReal() < b.asReal();
- else if (a.isString())
- return a.asString() < b.asString();
- else if (a.isUUID())
- return a.asUUID() < b.asUUID();
- else if (a.isDate())
- return a.asDate() < b.asDate();
- else if (a.isURI())
- return a.asString() < b.asString(); // compare URIs as strings
- else if (a.isBoolean())
- return a.asBoolean() < b.asBoolean();
- else
- return false; // no valid operation for Binary
-}
-|*==========================================================================*/
-
-bool LLFloater::KeyCompare::equate(const LLSD& a, const LLSD& b)
-{
- return llsd_equals(a, b);
-}
-
-//************************************
-
-LLFloater::Params::Params()
-: title("title"),
- short_title("short_title"),
- single_instance("single_instance", false),
- reuse_instance("reuse_instance", false),
- can_resize("can_resize", false),
- can_minimize("can_minimize", true),
- can_close("can_close", true),
- can_drag_on_left("can_drag_on_left", false),
- can_tear_off("can_tear_off", true),
- save_dock_state("save_dock_state", false),
- save_rect("save_rect", false),
- save_visibility("save_visibility", false),
- can_dock("can_dock", false),
- show_title("show_title", true),
- auto_close("auto_close", false),
- positioning("positioning", LLFloaterEnums::POSITIONING_RELATIVE),
- header_height("header_height", 0),
- legacy_header_height("legacy_header_height", 0),
- close_image("close_image"),
- restore_image("restore_image"),
- minimize_image("minimize_image"),
- tear_off_image("tear_off_image"),
- dock_image("dock_image"),
- help_image("help_image"),
- close_pressed_image("close_pressed_image"),
- restore_pressed_image("restore_pressed_image"),
- minimize_pressed_image("minimize_pressed_image"),
- tear_off_pressed_image("tear_off_pressed_image"),
- dock_pressed_image("dock_pressed_image"),
- help_pressed_image("help_pressed_image"),
- open_callback("open_callback"),
- close_callback("close_callback"),
- follows("follows"),
- rel_x("rel_x", 0),
- rel_y("rel_y", 0)
-{
- changeDefault(visible, false);
-}
-
-
-//static
-const LLFloater::Params& LLFloater::getDefaultParams()
-{
- return LLUICtrlFactory::getDefaultParams<LLFloater>();
-}
-
-//static
-void LLFloater::initClass()
-{
- // translate tooltips for floater buttons
- for (S32 i = 0; i < BUTTON_COUNT; i++)
- {
- sButtonToolTips[i] = LLTrans::getString( sButtonToolTipsIndex[i] );
- }
-
- LLControlVariable* ctrl = LLUI::getInstance()->mSettingGroups["config"]->getControl("ActiveFloaterTransparency").get();
- if (ctrl)
- {
- ctrl->getSignal()->connect(boost::bind(&LLFloater::updateActiveFloaterTransparency));
- updateActiveFloaterTransparency();
- }
-
- ctrl = LLUI::getInstance()->mSettingGroups["config"]->getControl("InactiveFloaterTransparency").get();
- if (ctrl)
- {
- ctrl->getSignal()->connect(boost::bind(&LLFloater::updateInactiveFloaterTransparency));
- updateInactiveFloaterTransparency();
- }
-
-}
-
-// defaults for floater param block pulled from widgets/floater.xml
-static LLWidgetNameRegistry::StaticRegistrar sRegisterFloaterParams(&typeid(LLFloater::Params), "floater");
-
-LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p)
-: LLPanel(), // intentionally do not pass params here, see initFromParams
- mDragHandle(NULL),
- mTitle(p.title),
- mShortTitle(p.short_title),
- mSingleInstance(p.single_instance),
- mReuseInstance(p.reuse_instance.isProvided() ? p.reuse_instance : p.single_instance), // reuse single-instance floaters by default
- mKey(key),
- mCanTearOff(p.can_tear_off),
- mCanMinimize(p.can_minimize),
- mCanClose(p.can_close),
- mDragOnLeft(p.can_drag_on_left),
- mResizable(p.can_resize),
- mAutoClose(p.auto_close),
- mPositioning(p.positioning),
- mMinWidth(p.min_width),
- mMinHeight(p.min_height),
- mHeaderHeight(p.header_height),
- mLegacyHeaderHeight(p.legacy_header_height),
- mDefaultRectForGroup(true),
- mMinimized(false),
- mForeground(false),
- mFirstLook(true),
- mButtonScale(1.0f),
- mAutoFocus(true), // automatically take focus when opened
- mCanDock(false),
- mDocked(false),
- mTornOff(false),
- mHasBeenDraggedWhileMinimized(false),
- mPreviousMinimizedBottom(0),
- mPreviousMinimizedLeft(0),
- mDefaultRelativeX(p.rel_x),
- mDefaultRelativeY(p.rel_y),
- mMinimizeSignal(NULL)
-// mNotificationContext(NULL)
-{
- mPosition.setFloater(*this);
-// mNotificationContext = new LLFloaterNotificationContext(getHandle());
-
- // Clicks stop here.
- setMouseOpaque(true);
-
- // Floaters always draw their background, unlike every other panel.
- setBackgroundVisible(true);
-
- // Floaters start not minimized. When minimized, they save their
- // prior rectangle to be used on restore.
- mExpandedRect.set(0,0,0,0);
-
- memset(mButtonsEnabled, 0, BUTTON_COUNT * sizeof(bool));
- memset(mButtons, 0, BUTTON_COUNT * sizeof(LLButton*));
-
- addDragHandle();
- addResizeCtrls();
-
- initFromParams(p);
-
- initFloater(p);
-}
-
-// Note: Floaters constructed from XML call init() twice!
-void LLFloater::initFloater(const Params& p)
-{
- // Close button.
- if (mCanClose)
- {
- mButtonsEnabled[BUTTON_CLOSE] = true;
- }
-
- // Help button: '?'
- //SL-14050 Disable all Help question marks
- mButtonsEnabled[BUTTON_HELP] = false;
-
- // Minimize button only for top draggers
- if ( !mDragOnLeft && mCanMinimize )
- {
- mButtonsEnabled[BUTTON_MINIMIZE] = true;
- }
-
- if(mCanDock)
- {
- mButtonsEnabled[BUTTON_DOCK] = true;
- }
-
- buildButtons(p);
-
- // Floaters are created in the invisible state
- setVisible(false);
-
- if (!getParent())
- {
- gFloaterView->addChild(this);
- }
-}
-
-void LLFloater::addDragHandle()
-{
- if (!mDragHandle)
- {
- if (mDragOnLeft)
- {
- LLDragHandleLeft::Params p;
- p.name("drag");
- p.follows.flags(FOLLOWS_ALL);
- p.label(mTitle);
- mDragHandle = LLUICtrlFactory::create<LLDragHandleLeft>(p);
- }
- else // drag on top
- {
- LLDragHandleTop::Params p;
- p.name("Drag Handle");
- p.follows.flags(FOLLOWS_ALL);
- p.label(mTitle);
- mDragHandle = LLUICtrlFactory::create<LLDragHandleTop>(p);
- }
- addChild(mDragHandle);
- }
- layoutDragHandle();
- applyTitle();
-}
-
-void LLFloater::layoutDragHandle()
-{
- static LLUICachedControl<S32> floater_close_box_size ("UIFloaterCloseBoxSize", 0);
- S32 close_box_size = mCanClose ? floater_close_box_size : 0;
-
- LLRect rect;
- if (mDragOnLeft)
- {
- rect.setLeftTopAndSize(0, 0, DRAG_HANDLE_WIDTH, getRect().getHeight() - LLPANEL_BORDER_WIDTH - close_box_size);
- }
- else // drag on top
- {
- rect = getLocalRect();
- }
- mDragHandle->setShape(rect);
- updateTitleButtons();
-}
-
-// static
-void LLFloater::updateActiveFloaterTransparency()
-{
- static LLCachedControl<F32> active_transparency(*LLUI::getInstance()->mSettingGroups["config"], "ActiveFloaterTransparency", 1.f);
- sActiveControlTransparency = active_transparency;
-}
-
-// static
-void LLFloater::updateInactiveFloaterTransparency()
-{
- static LLCachedControl<F32> inactive_transparency(*LLUI::getInstance()->mSettingGroups["config"], "InactiveFloaterTransparency", 0.95f);
- sInactiveControlTransparency = inactive_transparency;
-}
-
-void LLFloater::addResizeCtrls()
-{
- // Resize bars (sides)
- LLResizeBar::Params p;
- p.name("resizebar_left");
- p.resizing_view(this);
- p.min_size(mMinWidth);
- p.side(LLResizeBar::LEFT);
- mResizeBar[LLResizeBar::LEFT] = LLUICtrlFactory::create<LLResizeBar>(p);
- addChild( mResizeBar[LLResizeBar::LEFT] );
-
- p.name("resizebar_top");
- p.min_size(mMinHeight);
- p.side(LLResizeBar::TOP);
-
- mResizeBar[LLResizeBar::TOP] = LLUICtrlFactory::create<LLResizeBar>(p);
- addChild( mResizeBar[LLResizeBar::TOP] );
-
- p.name("resizebar_right");
- p.min_size(mMinWidth);
- p.side(LLResizeBar::RIGHT);
- mResizeBar[LLResizeBar::RIGHT] = LLUICtrlFactory::create<LLResizeBar>(p);
- addChild( mResizeBar[LLResizeBar::RIGHT] );
-
- p.name("resizebar_bottom");
- p.min_size(mMinHeight);
- p.side(LLResizeBar::BOTTOM);
- mResizeBar[LLResizeBar::BOTTOM] = LLUICtrlFactory::create<LLResizeBar>(p);
- addChild( mResizeBar[LLResizeBar::BOTTOM] );
-
- // Resize handles (corners)
- LLResizeHandle::Params handle_p;
- // handles must not be mouse-opaque, otherwise they block hover events
- // to other buttons like the close box. JC
- handle_p.mouse_opaque(false);
- handle_p.min_width(mMinWidth);
- handle_p.min_height(mMinHeight);
- handle_p.corner(LLResizeHandle::RIGHT_BOTTOM);
- mResizeHandle[0] = LLUICtrlFactory::create<LLResizeHandle>(handle_p);
- addChild(mResizeHandle[0]);
-
- handle_p.corner(LLResizeHandle::RIGHT_TOP);
- mResizeHandle[1] = LLUICtrlFactory::create<LLResizeHandle>(handle_p);
- addChild(mResizeHandle[1]);
-
- handle_p.corner(LLResizeHandle::LEFT_BOTTOM);
- mResizeHandle[2] = LLUICtrlFactory::create<LLResizeHandle>(handle_p);
- addChild(mResizeHandle[2]);
-
- handle_p.corner(LLResizeHandle::LEFT_TOP);
- mResizeHandle[3] = LLUICtrlFactory::create<LLResizeHandle>(handle_p);
- addChild(mResizeHandle[3]);
-
- layoutResizeCtrls();
-}
-
-void LLFloater::layoutResizeCtrls()
-{
- LLRect rect;
-
- // Resize bars (sides)
- const S32 RESIZE_BAR_THICKNESS = 3;
- rect = LLRect( 0, getRect().getHeight(), RESIZE_BAR_THICKNESS, 0);
- mResizeBar[LLResizeBar::LEFT]->setRect(rect);
-
- rect = LLRect( 0, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_BAR_THICKNESS);
- mResizeBar[LLResizeBar::TOP]->setRect(rect);
-
- rect = LLRect(getRect().getWidth() - RESIZE_BAR_THICKNESS, getRect().getHeight(), getRect().getWidth(), 0);
- mResizeBar[LLResizeBar::RIGHT]->setRect(rect);
-
- rect = LLRect(0, RESIZE_BAR_THICKNESS, getRect().getWidth(), 0);
- mResizeBar[LLResizeBar::BOTTOM]->setRect(rect);
-
- // Resize handles (corners)
- rect = LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT, getRect().getWidth(), 0);
- mResizeHandle[0]->setRect(rect);
-
- rect = LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_HANDLE_HEIGHT);
- mResizeHandle[1]->setRect(rect);
-
- rect = LLRect( 0, RESIZE_HANDLE_HEIGHT, RESIZE_HANDLE_WIDTH, 0 );
- mResizeHandle[2]->setRect(rect);
-
- rect = LLRect( 0, getRect().getHeight(), RESIZE_HANDLE_WIDTH, getRect().getHeight() - RESIZE_HANDLE_HEIGHT );
- mResizeHandle[3]->setRect(rect);
-}
-
-void LLFloater::enableResizeCtrls(bool enable, 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)
- {
- mResizeHandle[i]->setVisible(enable && width && height);
- mResizeHandle[i]->setEnabled(enable && width && height);
- }
-}
-
-void LLFloater::destroy()
-{
- // LLFloaterReg should be synchronized with "dead" floater to avoid returning dead instance before
- // it was deleted via LLMortician::updateClass(). See EXT-8458.
- LLFloaterReg::removeInstance(mInstanceName, mKey);
- die();
-}
-
-// virtual
-LLFloater::~LLFloater()
-{
- if (!isDead())
- {
- // If it's dead, instance is supposed to be already removed, and
- // in case of single instance we can remove new one by accident
- LLFloaterReg::removeInstance(mInstanceName, mKey);
- }
-
- 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 );
-
- delete mDragHandle;
- for (S32 i = 0; i < 4; i++)
- {
- delete mResizeBar[i];
- delete mResizeHandle[i];
- }
-
- setVisible(false); // We're not visible if we're destroyed
- storeVisibilityControl();
- storeDockStateControl();
- delete mMinimizeSignal;
-}
-
-void LLFloater::storeRectControl()
-{
- 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()
-{
- if( !sQuitting && mVisibilityControl.size() > 1 )
- {
- getControlGroup()->setBOOL( mVisibilityControl, getVisible() );
- }
-}
-
-void LLFloater::storeDockStateControl()
-{
- if( !sQuitting && mDocStateControl.size() > 1 )
- {
- getControlGroup()->setBOOL( mDocStateControl, isDocked() );
- }
-}
-
-// static
-std::string LLFloater::getControlName(const std::string& name, const LLSD& key)
-{
- std::string ctrl_name = name;
-
- // Add the key to the control name if appropriate.
- if (key.isString() && !key.asString().empty())
- {
- ctrl_name += "_" + key.asString();
- }
-
- return ctrl_name;
-}
-
-// static
-LLControlGroup* LLFloater::getControlGroup()
-{
- // Floater size, position, visibility, etc are saved in per-account settings.
- return LLUI::getInstance()->mSettingGroups["account"];
-}
-
-void LLFloater::setVisible( bool visible )
-{
- LLPanel::setVisible(visible); // calls onVisibilityChange()
- if( visible && mFirstLook )
- {
- mFirstLook = false;
- }
-
- if( !visible )
- {
- LLUI::getInstance()->removePopup(this);
-
- if( gFocusMgr.childHasMouseCapture( this ) )
- {
- gFocusMgr.setMouseCapture(NULL);
- }
- }
-
- for(handle_set_iter_t dependent_it = mDependents.begin();
- dependent_it != mDependents.end(); )
- {
- LLFloater* floaterp = dependent_it->get();
-
- if (floaterp)
- {
- floaterp->setVisible(visible);
- }
- ++dependent_it;
- }
-
- 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::onVisibilityChange ( bool new_visibility )
-{
- if (new_visibility)
- {
- if (getHost())
- getHost()->setFloaterFlashing(this, false);
- }
- LLPanel::onVisibilityChange ( new_visibility );
-}
-
-void LLFloater::openFloater(const LLSD& key)
-{
- 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
- // don't play open sound for hosted (tabbed) windows
- && !getHost()
- && !getFloaterHost()
- && (!getVisible() || isMinimized()))
- {
- make_ui_sound("UISndWindowOpen");
- }
-
- //RN: for now, we don't allow rehosting from one multifloater to another
- // just need to fix the bugs
- if (getFloaterHost() != NULL && getHost() == NULL)
- {
- // needs a host
- // only select tabs if window they are hosted in is visible
- getFloaterHost()->addFloater(this, getFloaterHost()->getVisible());
- }
-
- if (getHost() != NULL)
- {
- getHost()->setMinimized(false);
- getHost()->setVisibleAndFrontmost(mAutoFocus && !getIsChrome());
- getHost()->showFloater(this);
- }
- 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 && !getIsChrome());
- }
-
- mOpenSignal(this, key);
- onOpen(key);
-
- dirtyRect();
-}
-
-void LLFloater::closeFloater(bool app_quitting)
-{
- 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;
- }
-
- // Always unminimize before trying to close.
- // Most of the time the user will never see this state.
- setMinimized(false);
-
- if (canClose())
- {
- if (getHost())
- {
- ((LLMultiFloater*)getHost())->removeFloater(this);
- gFloaterView->addChild(this);
- }
-
- if (getSoundFlags() != SILENT
- && getVisible()
- && !getHost()
- && !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
- while(mDependents.size() > 0)
- {
- handle_set_iter_t dependent_it = mDependents.begin();
- LLFloater* floaterp = dependent_it->get();
- // normally removeDependentFloater will do this, but in
- // case floaterp is somehow invalid or orphaned, erase now
- mDependents.erase(dependent_it);
- if (floaterp)
- {
- floaterp->mDependeeHandle = LLHandle<LLFloater>();
- floaterp->closeFloater(app_quitting);
- }
- }
-
- cleanupHandles();
-
- dirtyRect();
-
- // Close callbacks
- onClose(app_quitting);
- mCloseSignal(this, LLSD(app_quitting));
-
- // Hide or Destroy
- if (mSingleInstance)
- {
- // Hide the instance
- if (getHost())
- {
- getHost()->setVisible(false);
- }
- else
- {
- setVisible(false);
- if (!mReuseInstance)
- {
- destroy();
- }
- }
- }
- else
- {
- 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);
-}
-
-// virtual
-void LLFloater::translate(S32 x, S32 y)
-{
- LLView::translate(x, y);
-
- if (!mTranslateWithDependents || mDependents.empty())
- return;
-
- for (const LLHandle<LLFloater>& handle : mDependents)
- {
- LLFloater* floater = handle.get();
- if (floater && floater->getSnapTarget() == getHandle())
- {
- floater->LLView::translate(x, y);
- }
- }
-}
-
-void LLFloater::releaseFocus()
-{
- LLUI::getInstance()->removePopup(this);
-
- setFocus(false);
-
- if( gFocusMgr.childHasMouseCapture( this ) )
- {
- gFocusMgr.setMouseCapture(NULL);
- }
-}
-
-
-void LLFloater::setResizeLimits( S32 min_width, S32 min_height )
-{
- mMinWidth = min_width;
- mMinHeight = min_height;
-
- for( S32 i = 0; i < 4; i++ )
- {
- if( mResizeBar[i] )
- {
- if (i == LLResizeBar::LEFT || i == LLResizeBar::RIGHT)
- {
- mResizeBar[i]->setResizeLimits( min_width, S32_MAX );
- }
- else
- {
- mResizeBar[i]->setResizeLimits( min_height, S32_MAX );
- }
- }
- if( mResizeHandle[i] )
- {
- mResizeHandle[i]->setResizeLimits( min_width, min_height );
- }
- }
-}
-
-
-void LLFloater::center()
-{
- if(getHost())
- {
- // hosted floaters can't move
- return;
- }
- centerWithin(gFloaterView->getRect());
-}
-
-LLMultiFloater* LLFloater::getHost()
-{
- return (LLMultiFloater*)mHostHandle.get();
-}
-
-void LLFloater::applyControlsAndPosition(LLFloater* other)
-{
- if (!applyDockState())
- {
- if (!applyRectControl())
- {
- applyPositioning(other, true);
- }
- }
-}
-
-bool LLFloater::applyRectControl()
-{
- 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)
- {
- // other floaters in our group, position ourselves relative to them and don't save the rect
- if (mDefaultRectForGroup)
- {
- mRectControl.clear();
- }
- mPositioning = LLFloaterEnums::POSITIONING_CASCADE_GROUP;
- }
- else
- {
- bool rect_specified = false;
- if (!mRectControl.empty())
- {
- // If we have a saved rect, use it
- const LLRect& rect = getControlGroup()->getRect(mRectControl);
- if (rect.notEmpty()) saved_rect = true;
- if (saved_rect)
- {
- 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;
- }
- else if ((mDefaultRelativeX != 0) && (mDefaultRelativeY != 0))
- {
- mPosition.mX = mDefaultRelativeX;
- mPosition.mY = mDefaultRelativeY;
- 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;
-}
-
-bool LLFloater::applyDockState()
-{
- bool docked = false;
-
- if (mDocStateControl.size() > 1)
- {
- 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()
-{
- if (!mDragHandle)
- {
- return;
- }
-
- if (isMinimized() && !mShortTitle.empty())
- {
- mDragHandle->setTitle( mShortTitle );
- }
- else
- {
- mDragHandle->setTitle ( mTitle );
- }
-
- if (getHost())
- {
- getHost()->updateFloaterTitle(this);
- }
-}
-
-std::string LLFloater::getCurrentTitle() const
-{
- return mDragHandle ? mDragHandle->getTitle() : LLStringUtil::null;
-}
-
-void LLFloater::setTitle( const std::string& title )
-{
- mTitle = title;
- applyTitle();
-}
-
-std::string LLFloater::getTitle() const
-{
- if (mTitle.empty())
- {
- return mDragHandle ? mDragHandle->getTitle() : LLStringUtil::null;
- }
- else
- {
- return mTitle;
- }
-}
-
-void LLFloater::setShortTitle( const std::string& short_title )
-{
- mShortTitle = short_title;
- applyTitle();
-}
-
-std::string LLFloater::getShortTitle() const
-{
- if (mShortTitle.empty())
- {
- return mDragHandle ? mDragHandle->getTitle() : LLStringUtil::null;
- }
- else
- {
- return mShortTitle;
- }
-}
-
-bool LLFloater::canSnapTo(const LLView* other_view)
-{
- if (NULL == other_view)
- {
- 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())
- {
- // this is a dependent that is already snapped to us, so don't snap back to it
- return false;
- }
- }
-
- return LLPanel::canSnapTo(other_view);
-}
-
-void LLFloater::setSnappedTo(const LLView* snap_view)
-{
- if (!snap_view || snap_view == getParent())
- {
- clearSnapTarget();
- }
- else
- {
- //RN: assume it's a floater as it must be a sibling to our parent floater
- const LLFloater* floaterp = dynamic_cast<const LLFloater*>(snap_view);
- if (floaterp)
- {
- setSnapTarget(floaterp->getHandle());
- }
- }
-}
-
-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);
- }
- mPositioning = LLFloaterEnums::POSITIONING_RELATIVE;
- LLRect screen_rect = calcScreenRect();
- mPosition = LLCoordGL(screen_rect.getCenterX(), screen_rect.getCenterY()).convert();
- }
- storeRectControl();
-
- // gather all snapped dependents
- for(handle_set_iter_t dependent_it = mDependents.begin();
- dependent_it != mDependents.end(); ++dependent_it)
- {
- LLFloater* floaterp = dependent_it->get();
- // is a dependent snapped to us?
- if (floaterp && floaterp->getSnapTarget() == getHandle())
- {
- S32 delta_x = 0;
- S32 delta_y = 0;
- // check to see if it snapped to right or top, and move if dependee floater is resizing
- LLRect dependent_rect = floaterp->getRect();
- if (dependent_rect.mLeft - getRect().mLeft >= old_rect.getWidth() || // dependent on my right?
- dependent_rect.mRight == getRect().mLeft + old_rect.getWidth()) // dependent aligned with my right
- {
- // was snapped directly onto right side or aligned with it
- delta_x += new_rect.getWidth() - old_rect.getWidth();
- }
- if (dependent_rect.mBottom - getRect().mBottom >= old_rect.getHeight() ||
- dependent_rect.mTop == getRect().mBottom + old_rect.getHeight())
- {
- // was snapped directly onto top side or aligned with it
- delta_y += new_rect.getHeight() - old_rect.getHeight();
- }
-
- // take translation of dependee floater into account as well
- delta_x += new_rect.mLeft - old_rect.mLeft;
- delta_y += new_rect.mBottom - old_rect.mBottom;
-
- dependent_rect.translate(delta_x, delta_y);
- floaterp->setShape(dependent_rect, by_user);
- }
- }
- }
- else
- {
- // If minimized, and origin has changed, set
- // mHasBeenDraggedWhileMinimized to true
- if ((new_rect.mLeft != old_rect.mLeft) ||
- (new_rect.mBottom != old_rect.mBottom))
- {
- mHasBeenDraggedWhileMinimized = true;
- }
- }
-}
-
-void LLFloater::setMinimized(bool minimize)
-{
- const LLFloater::Params& default_params = LLFloater::getDefaultParams();
- S32 floater_header_size = default_params.header_height;
- static LLUICachedControl<S32> minimized_width ("UIMinimizedWidth", 0);
-
- if (minimize == mMinimized) return;
-
- if (mMinimizeSignal)
- {
- (*mMinimizeSignal)(this, LLSD(minimize));
- }
-
- if (minimize)
- {
- // minimized flag should be turned on before release focus
- mMinimized = true;
- mExpandedRect = getRect();
-
- // If the floater has been dragged while minimized in the
- // past, then locate it at its previous minimized location.
- // Otherwise, ask the view for a minimize position.
- if (mHasBeenDraggedWhileMinimized)
- {
- setOrigin(mPreviousMinimizedLeft, mPreviousMinimizedBottom);
- }
- else
- {
- S32 left, bottom;
- gFloaterView->getMinimizePosition(&left, &bottom);
- setOrigin( left, bottom );
- }
-
- if (mButtonsEnabled[BUTTON_MINIMIZE])
- {
- mButtonsEnabled[BUTTON_MINIMIZE] = false;
- mButtonsEnabled[BUTTON_RESTORE] = true;
- }
-
- setBorderVisible(true);
-
- for(handle_set_iter_t dependent_it = mDependents.begin();
- dependent_it != mDependents.end();
- ++dependent_it)
- {
- LLFloater* floaterp = dependent_it->get();
- if (floaterp)
- {
- if (floaterp->isMinimizeable())
- {
- floaterp->setMinimized(true);
- }
- else if (!floaterp->isMinimized())
- {
- floaterp->setVisible(false);
- }
- }
- }
-
- // Lose keyboard focus when minimized
- releaseFocus();
-
- for (S32 i = 0; i < 4; i++)
- {
- if (mResizeBar[i] != NULL)
- {
- mResizeBar[i]->setEnabled(false);
- }
- if (mResizeHandle[i] != NULL)
- {
- mResizeHandle[i]->setEnabled(false);
- }
- }
-
- // Reshape *after* setting mMinimized
- reshape( minimized_width, floater_header_size, true);
- }
- else
- {
- // If this window has been dragged while minimized (at any time),
- // remember its position for the next time it's minimized.
- if (mHasBeenDraggedWhileMinimized)
- {
- const LLRect& currentRect = getRect();
- mPreviousMinimizedLeft = currentRect.mLeft;
- mPreviousMinimizedBottom = currentRect.mBottom;
- }
-
- setOrigin( mExpandedRect.mLeft, mExpandedRect.mBottom );
- if (mButtonsEnabled[BUTTON_RESTORE])
- {
- mButtonsEnabled[BUTTON_MINIMIZE] = true;
- mButtonsEnabled[BUTTON_RESTORE] = false;
- }
-
- // show dependent floater
- for(handle_set_iter_t dependent_it = mDependents.begin();
- dependent_it != mDependents.end();
- ++dependent_it)
- {
- LLFloater* floaterp = dependent_it->get();
- if (floaterp)
- {
- floaterp->setMinimized(false);
- floaterp->setVisible(true);
- }
- }
-
- for (S32 i = 0; i < 4; i++)
- {
- if (mResizeBar[i] != NULL)
- {
- mResizeBar[i]->setEnabled(isResizable());
- }
- if (mResizeHandle[i] != NULL)
- {
- mResizeHandle[i]->setEnabled(isResizable());
- }
- }
-
- mMinimized = false;
- setFrontmost();
- // Reshape *after* setting mMinimized
- reshape( mExpandedRect.getWidth(), mExpandedRect.getHeight(), true );
- }
-
- make_ui_sound("UISndWindowClose");
- updateTitleButtons();
- applyTitle ();
-}
-
-void LLFloater::setFocus( bool b )
-{
- if (b && getIsChrome())
- {
- return;
- }
- LLView* last_focus = gFocusMgr.getLastFocusForGroup(this);
- // a descendent already has focus
- bool child_had_focus = hasFocus();
-
- // give focus to first valid descendent
- LLPanel::setFocus(b);
-
- if (b)
- {
- // only push focused floaters to front of stack if not in midst of ctrl-tab cycle
- LLFloaterView * parent = dynamic_cast<LLFloaterView *>(getParent());
- if (!getHost() && parent && !parent->getCycleMode())
- {
- if (!isFrontmost())
- {
- setFrontmost();
- }
- }
-
- // when getting focus, delegate to last descendent which had focus
- if (last_focus && !child_had_focus &&
- last_focus->isInEnabledChain() &&
- last_focus->isInVisibleChain())
- {
- // *FIX: should handle case where focus doesn't stick
- last_focus->setFocus(true);
- }
- }
- updateTransparency(b ? TT_ACTIVE : TT_INACTIVE);
-}
-
-// virtual
-void LLFloater::setRect(const LLRect &rect)
-{
- LLPanel::setRect(rect);
- layoutDragHandle();
- layoutResizeCtrls();
-}
-
-// virtual
-void LLFloater::setIsChrome(bool is_chrome)
-{
- // chrome floaters don't take focus at all
- if (is_chrome)
- {
- // remove focus if we're changing to chrome
- setFocus(false);
- // can't Ctrl-Tab to "chrome" floaters
- setFocusRoot(false);
- mButtons[BUTTON_CLOSE]->setToolTip(LLStringExplicit(getButtonTooltip(Params(), BUTTON_CLOSE, is_chrome)));
- }
-
- LLPanel::setIsChrome(is_chrome);
-}
-
-// Change the draw style to account for the foreground state.
-void LLFloater::setForeground(bool front)
-{
- if (front != mForeground)
- {
- mForeground = front;
- if (mDragHandle)
- mDragHandle->setForeground( front );
-
- if (!front)
- {
- releaseFocus();
- }
-
- setBackgroundOpaque( front );
- }
-}
-
-void LLFloater::cleanupHandles()
-{
- // remove handles to non-existent dependents
- for(handle_set_iter_t dependent_it = mDependents.begin();
- dependent_it != mDependents.end(); )
- {
- LLFloater* floaterp = dependent_it->get();
- if (!floaterp)
- {
- dependent_it = mDependents.erase(dependent_it);
- }
- else
- {
- ++dependent_it;
- }
- }
-}
-
-void LLFloater::setHost(LLMultiFloater* host)
-{
- if (mHostHandle.isDead() && host)
- {
- // make buttons smaller for hosted windows to differentiate from parent
- mButtonScale = 0.9f;
-
- // add tear off button
- if (mCanTearOff)
- {
- mButtonsEnabled[BUTTON_TEAR_OFF] = true;
- }
- }
- else if (!mHostHandle.isDead() && !host)
- {
- mButtonScale = 1.f;
- //mButtonsEnabled[BUTTON_TEAR_OFF] = false;
- }
- if (host)
- {
- mHostHandle = host->getHandle();
- mLastHostHandle = host->getHandle();
- }
- else
- {
- mHostHandle.markDead();
- }
-
- updateTitleButtons();
-}
-
-void LLFloater::moveResizeHandlesToFront()
-{
- for( S32 i = 0; i < 4; i++ )
- {
- if( mResizeBar[i] )
- {
- sendChildToFront(mResizeBar[i]);
- }
- }
-
- for( S32 i = 0; i < 4; i++ )
- {
- if( mResizeHandle[i] )
- {
- sendChildToFront(mResizeHandle[i]);
- }
- }
-}
-
-/*virtual*/
-bool LLFloater::isFrontmost()
-{
- LLFloaterView* floater_view = getParentByType<LLFloaterView>();
- return getVisible()
- && (floater_view
- && floater_view->getFrontmost() == this);
-}
-
-void LLFloater::addDependentFloater(LLFloater* floaterp, bool reposition, bool resize)
-{
- mDependents.insert(floaterp->getHandle());
- floaterp->mDependeeHandle = getHandle();
-
- if (reposition)
- {
- LLRect rect = gFloaterView->findNeighboringPosition(this, floaterp);
- if (resize)
- {
- const LLRect& base = getRect();
- if (rect.mTop == base.mTop)
- rect.mBottom = base.mBottom;
- else if (rect.mLeft == base.mLeft)
- rect.mRight = base.mRight;
- floaterp->reshape(rect.getWidth(), rect.getHeight(), false);
- }
- floaterp->setRect(rect);
- floaterp->setSnapTarget(getHandle());
- }
- gFloaterView->adjustToFitScreen(floaterp, false, true);
- if (floaterp->isFrontmost())
- {
- // make sure to bring self and sibling floaters to front
- gFloaterView->bringToFront(floaterp, floaterp->getAutoFocus() && !getIsChrome());
- }
-}
-
-void LLFloater::addDependentFloater(LLHandle<LLFloater> dependent, bool reposition, bool resize)
-{
- LLFloater* dependent_floaterp = dependent.get();
- if(dependent_floaterp)
- {
- addDependentFloater(dependent_floaterp, reposition, resize);
- }
-}
-
-void LLFloater::removeDependentFloater(LLFloater* floaterp)
-{
- mDependents.erase(floaterp->getHandle());
- floaterp->mDependeeHandle = LLHandle<LLFloater>();
-}
-
-void LLFloater::fitWithDependentsOnScreen(const LLRect& left, const LLRect& bottom, const LLRect& right, const LLRect& constraint, S32 min_overlap_pixels)
-{
- LLRect total_rect = getRect();
-
- for (const LLHandle<LLFloater>& handle : mDependents)
- {
- LLFloater* floater = handle.get();
- if (floater && floater->getSnapTarget() == getHandle())
- {
- total_rect.unionWith(floater->getRect());
- }
- }
-
- S32 delta_left = left.notEmpty() ? left.mRight - total_rect.mRight : 0;
- S32 delta_bottom = bottom.notEmpty() ? bottom.mTop - total_rect.mTop : 0;
- S32 delta_right = right.notEmpty() ? right.mLeft - total_rect.mLeft : 0;
-
- // move floater with dependings fully onscreen
- mTranslateWithDependents = true;
- if (translateRectIntoRect(total_rect, constraint, min_overlap_pixels))
- {
- clearSnapTarget();
- }
- else if (delta_left > 0 && total_rect.mTop < left.mTop && total_rect.mBottom > left.mBottom)
- {
- translate(delta_left, 0);
- }
- else if (delta_bottom > 0 && total_rect.mLeft > bottom.mLeft && total_rect.mRight < bottom.mRight)
- {
- translate(0, delta_bottom);
- }
- else if (delta_right < 0 && total_rect.mTop < right.mTop && total_rect.mBottom > right.mBottom)
- {
- translate(delta_right, 0);
- }
- mTranslateWithDependents = false;
-}
-
-bool LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index)
-{
- if( mButtonsEnabled[index] )
- {
- LLButton* my_butt = mButtons[index];
- S32 local_x = x - my_butt->getRect().mLeft;
- S32 local_y = y - my_butt->getRect().mBottom;
-
- if (
- my_butt->pointInView(local_x, local_y) &&
- my_butt->handleMouseDown(local_x, local_y, mask))
- {
- // the button handled it
- return true;
- }
- }
- return false;
-}
-
-bool LLFloater::handleScrollWheel(S32 x, S32 y, S32 clicks)
-{
- LLPanel::handleScrollWheel(x,y,clicks);
- return true;//always
-}
-
-// 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 )
- {
- // Offer the click to titlebar buttons.
- // Note: this block and the offerClickToButton helper method can be removed
- // because the parent container will handle it for us but we'll keep it here
- // for safety until after reworking the panel code to manage hidden children.
- 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;
-
- setFrontmost(true, false);
- // Otherwise pass to drag handle for movement
- return mDragHandle->handleMouseDown(x, y, mask);
- }
- else
- {
- bringToFront( x, y );
- bool handled = LLPanel::handleMouseDown( x, y, mask );
- if (handled) {
- LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
- }
- return handled;
- }
-}
-
-// virtual
-bool LLFloater::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- bool was_minimized = mMinimized;
- bringToFront( x, y );
- return was_minimized || LLPanel::handleRightMouseDown( x, y, mask );
-}
-
-bool LLFloater::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
-{
- bringToFront( x, y );
- return LLPanel::handleMiddleMouseDown( x, y, mask );
-}
-
-
-// virtual
-bool LLFloater::handleDoubleClick(S32 x, S32 y, MASK mask)
-{
- bool was_minimized = mMinimized;
- setMinimized(false);
- return was_minimized || LLPanel::handleDoubleClick(x, y, mask);
-}
-
-// virtual
-void LLFloater::bringToFront( S32 x, S32 y )
-{
- if (getVisible() && pointInView(x, y))
- {
- LLMultiFloater* hostp = getHost();
- if (hostp)
- {
- hostp->showFloater(this);
- }
- else
- {
- LLFloaterView* parent = dynamic_cast<LLFloaterView*>( getParent() );
- if (parent)
- {
- parent->bringToFront(this, !getIsChrome());
- }
- }
- }
-}
-
-// virtual
-void LLFloater::goneFromFront()
-{
- if (mAutoClose)
- {
- closeFloater();
- }
-}
-
-// virtual
-void LLFloater::setVisibleAndFrontmost(bool take_focus,const LLSD& key)
-{
- LLUIUsage::instance().logFloater(getInstanceName());
- LLMultiFloater* hostp = getHost();
- if (hostp)
- {
- hostp->setVisible(true);
- hostp->setFrontmost(take_focus);
- }
- else
- {
- setVisible(true);
- setFrontmost(take_focus);
- }
-}
-
-void LLFloater::setFrontmost(bool take_focus, bool restore)
-{
- LLMultiFloater* hostp = getHost();
- if (hostp)
- {
- // this will bring the host floater to the front and select
- // the appropriate panel
- hostp->showFloater(this);
- }
- else
- {
- // there are more than one floater view
- // so we need to query our parent directly
- LLFloaterView * parent = dynamic_cast<LLFloaterView*>( getParent() );
- if (parent)
- {
- parent->bringToFront(this, take_focus, restore);
- }
-
- // Make sure to set the appropriate transparency type (STORM-732).
- updateTransparency(hasFocus() || getIsChrome() ? TT_ACTIVE : TT_INACTIVE);
- }
-}
-
-void LLFloater::setCanDock(bool b)
-{
- if(b != mCanDock)
- {
- mCanDock = b;
- if(mCanDock)
- {
- mButtonsEnabled[BUTTON_DOCK] = !mDocked;
- }
- else
- {
- mButtonsEnabled[BUTTON_DOCK] = false;
- }
- }
- updateTitleButtons();
-}
-
-void LLFloater::setDocked(bool docked, bool pop_on_undock)
-{
- if(docked != mDocked && mCanDock)
- {
- mDocked = docked;
- mButtonsEnabled[BUTTON_DOCK] = !mDocked;
-
- if (mDocked)
- {
- setMinimized(false);
- mPositioning = LLFloaterEnums::POSITIONING_RELATIVE;
- }
-
- updateTitleButtons();
-
- storeDockStateControl();
- }
-
-}
-
-// static
-void LLFloater::onClickMinimize(LLFloater* self)
-{
- if (!self)
- return;
- self->setMinimized( !self->isMinimized() );
-}
-
-void LLFloater::onClickTearOff(LLFloater* self)
-{
- if (!self)
- return;
- S32 floater_header_size = self->mHeaderHeight;
- LLMultiFloater* host_floater = self->getHost();
- if (host_floater) //Tear off
- {
- LLRect new_rect;
- host_floater->removeFloater(self);
- // reparent to floater view
- gFloaterView->addChild(self);
-
- self->openFloater(self->getKey());
- 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);
- }
- gFloaterView->adjustToFitScreen(self, false);
- // give focus to new window to keep continuity for the user
- self->setFocus(true);
- self->setTornOff(true);
- }
- else //Attach to parent.
- {
- LLMultiFloater* new_host = (LLMultiFloater*)self->mLastHostHandle.get();
- if (new_host)
- {
- if (self->mSaveRect)
- {
- LLRect screen_rect = self->calcScreenRect();
- self->mPosition = LLCoordGL(screen_rect.getCenterX(), screen_rect.getCenterY()).convert();
- self->storeRectControl();
- }
- self->setMinimized(false); // to reenable minimize button if it was minimized
- new_host->showFloater(self);
- // make sure host is visible
- new_host->openFloater(new_host->getKey());
- }
- self->setTornOff(false);
- }
- self->updateTitleButtons();
- self->setOpenPositioning(LLFloaterEnums::POSITIONING_RELATIVE);
-}
-
-// static
-void LLFloater::onClickDock(LLFloater* self)
-{
- if(self && self->mCanDock)
- {
- self->setDocked(!self->mDocked, true);
- }
-}
-
-// static
-void LLFloater::onClickHelp( LLFloater* self )
-{
- if (self && LLUI::getInstance()->mHelpImpl)
- {
- // find the current help context for this floater
- std::string help_topic;
- if (self->findHelpTopic(help_topic))
- {
- LLUI::getInstance()->mHelpImpl->showTopic(help_topic);
- }
- }
-}
-
-void LLFloater::initRectControl()
-{
- // save_rect and save_visibility only apply to registered floaters
- if (mSaveRect)
- {
- std::string ctrl_name = getControlName(mInstanceName, mKey);
- mRectControl = LLFloaterReg::declareRectControl(ctrl_name);
- mPosXControl = LLFloaterReg::declarePosXControl(ctrl_name);
- mPosYControl = LLFloaterReg::declarePosYControl(ctrl_name);
- }
-}
-
-// static
-void LLFloater::closeFrontmostFloater()
-{
- LLFloater* floater_to_close = gFloaterView->getFrontmostClosableFloater();
- if(floater_to_close)
- {
- floater_to_close->closeFloater();
- }
-
- // if nothing took focus after closing focused floater
- // give it to next floater (to allow closing multiple windows via keyboard in rapid succession)
- if (gFocusMgr.getKeyboardFocus() == NULL)
- {
- // HACK: use gFloaterView directly in case we are using Ctrl-W to close snapshot window
- // which sits in gSnapshotFloaterView, and needs to pass focus on to normal floater view
- gFloaterView->focusFrontFloater();
- }
-}
-
-
-// static
-void LLFloater::onClickClose( LLFloater* self )
-{
- if (!self)
- return;
- self->onClickCloseBtn();
-}
-
-void LLFloater::onClickCloseBtn(bool app_quitting)
-{
- closeFloater(false);
-}
-
-
-// virtual
-void LLFloater::draw()
-{
- const F32 alpha = getCurrentTransparency();
-
- // draw background
- if( isBackgroundVisible() )
- {
- drawShadow(this);
-
- S32 left = LLPANEL_BORDER_WIDTH;
- S32 top = getRect().getHeight() - LLPANEL_BORDER_WIDTH;
- S32 right = getRect().getWidth() - LLPANEL_BORDER_WIDTH;
- S32 bottom = LLPANEL_BORDER_WIDTH;
-
- LLUIImage* image = NULL;
- LLColor4 color;
- LLColor4 overlay_color;
- if (isBackgroundOpaque())
- {
- // NOTE: image may not be set
- image = getBackgroundImage();
- color = getBackgroundColor();
- overlay_color = getBackgroundImageOverlay();
- }
- else
- {
- image = getTransparentImage();
- color = getTransparentColor();
- overlay_color = getTransparentImageOverlay();
- }
-
- if (image)
- {
- // We're using images for this floater's backgrounds
- image->draw(getLocalRect(), overlay_color % alpha);
- }
- else
- {
- // We're not using images, use old-school flat colors
- gl_rect_2d( left, top, right, bottom, color % alpha );
-
- // draw highlight on title bar to indicate focus. RDW
- if(hasFocus()
- && !getIsChrome()
- && !getCurrentTitle().empty())
- {
- static LLUIColor titlebar_focus_color = LLUIColorTable::instance().getColor("TitleBarFocusColor");
-
- const LLFontGL* font = LLFontGL::getFontSansSerif();
- LLRect r = getRect();
- gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - font->getLineHeight() - 1,
- titlebar_focus_color % alpha, 0, true);
- }
- }
- }
-
- LLPanel::updateDefaultBtn();
-
- if( getDefaultButton() )
- {
- if (hasFocus() && getDefaultButton()->getEnabled())
- {
- LLFocusableElement* focus_ctrl = gFocusMgr.getKeyboardFocus();
- // is this button a direct descendent and not a nested widget (e.g. checkbox)?
- bool focus_is_child_button = dynamic_cast<LLButton*>(focus_ctrl) != NULL && dynamic_cast<LLButton*>(focus_ctrl)->getParent() == this;
- // only enable default button when current focus is not a button
- getDefaultButton()->setBorderEnabled(!focus_is_child_button);
- }
- else
- {
- getDefaultButton()->setBorderEnabled(false);
- }
- }
- if (isMinimized())
- {
- for (S32 i = 0; i < BUTTON_COUNT; i++)
- {
- drawChild(mButtons[i]);
- }
- drawChild(mDragHandle, 0, 0, true);
- }
- else
- {
- // don't call LLPanel::draw() since we've implemented custom background rendering
- LLView::draw();
- }
-
- // update tearoff button for torn off floaters
- // when last host goes away
- if (mCanTearOff && !getHost())
- {
- LLFloater* old_host = mLastHostHandle.get();
- if (!old_host)
- {
- setCanTearOff(false);
- }
- }
-}
-
-void LLFloater::drawShadow(LLPanel* panel)
-{
- S32 left = LLPANEL_BORDER_WIDTH;
- S32 top = panel->getRect().getHeight() - LLPANEL_BORDER_WIDTH;
- S32 right = panel->getRect().getWidth() - LLPANEL_BORDER_WIDTH;
- S32 bottom = LLPANEL_BORDER_WIDTH;
-
- static LLUICachedControl<S32> shadow_offset_S32 ("DropShadowFloater", 0);
- static LLUIColor shadow_color_cached = LLUIColorTable::instance().getColor("ColorDropShadow");
- LLColor4 shadow_color = shadow_color_cached;
- F32 shadow_offset = (F32)shadow_offset_S32;
-
- if (!panel->isBackgroundOpaque())
- {
- shadow_offset *= 0.2f;
- shadow_color.mV[VALPHA] *= 0.5f;
- }
- gl_drop_shadow(left, top, right, bottom,
- shadow_color % getCurrentTransparency(),
- 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();
-
- LLUICtrl* ctrl = dynamic_cast<LLUICtrl*>(view);
- if (ctrl)
- {
- ctrl->setTransparencyType(transparency_type);
- }
-
- for(; it != children.end(); ++it)
- {
- updateTransparency(*it, transparency_type);
- }
-}
-
-void LLFloater::updateTransparency(ETypeTransparency transparency_type)
-{
- updateTransparency(this, transparency_type);
-}
-
-void LLFloater::setCanMinimize(bool can_minimize)
-{
- // if removing minimize/restore button programmatically,
- // go ahead and unminimize floater
- mCanMinimize = can_minimize;
- if (!can_minimize)
- {
- setMinimized(false);
- }
-
- mButtonsEnabled[BUTTON_MINIMIZE] = can_minimize && !isMinimized();
- mButtonsEnabled[BUTTON_RESTORE] = can_minimize && isMinimized();
-
- updateTitleButtons();
-}
-
-void LLFloater::setCanClose(bool can_close)
-{
- mCanClose = can_close;
- mButtonsEnabled[BUTTON_CLOSE] = can_close;
-
- updateTitleButtons();
-}
-
-void LLFloater::setCanTearOff(bool can_tear_off)
-{
- mCanTearOff = can_tear_off;
- mButtonsEnabled[BUTTON_TEAR_OFF] = mCanTearOff && !mHostHandle.isDead();
-
- updateTitleButtons();
-}
-
-
-void LLFloater::setCanResize(bool can_resize)
-{
- mResizable = can_resize;
- enableResizeCtrls(can_resize);
-}
-
-void LLFloater::setCanDrag(bool can_drag)
-{
- // if we delete drag handle, we no longer have access to the floater's title
- // so just enable/disable it
- if (!can_drag && mDragHandle->getEnabled())
- {
- mDragHandle->setEnabled(false);
- }
- else if (can_drag && !mDragHandle->getEnabled())
- {
- mDragHandle->setEnabled(true);
- }
-}
-
-bool LLFloater::getCanDrag()
-{
- return mDragHandle->getEnabled();
-}
-
-
-void LLFloater::updateTitleButtons()
-{
- static LLUICachedControl<S32> floater_close_box_size ("UIFloaterCloseBoxSize", 0);
- static LLUICachedControl<S32> close_box_from_top ("UICloseBoxFromTop", 0);
- LLRect buttons_rect;
- S32 button_count = 0;
- for (S32 i = 0; i < BUTTON_COUNT; i++)
- {
- if (!mButtons[i])
- {
- continue;
- }
-
- bool enabled = mButtonsEnabled[i];
- if (i == BUTTON_HELP)
- {
- // don't show the help button if the floater is minimized
- // or if it is a docked tear-off floater
- if (isMinimized() || (mButtonsEnabled[BUTTON_TEAR_OFF] && ! mTornOff))
- {
- enabled = false;
- }
- }
- if (i == BUTTON_CLOSE && mButtonScale != 1.f)
- {
- //*HACK: always render close button for hosted floaters so
- //that users don't accidentally hit the button when
- //closing multiple windows in the chatterbox
- enabled = true;
- }
-
- mButtons[i]->setEnabled(enabled);
-
- if (enabled)
- {
- button_count++;
-
- LLRect btn_rect;
- if (mDragOnLeft)
- {
- btn_rect.setLeftTopAndSize(
- LLPANEL_BORDER_WIDTH,
- getRect().getHeight() - close_box_from_top - (floater_close_box_size + 1) * button_count,
- 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,
- ll_round((F32)floater_close_box_size * mButtonScale),
- ll_round((F32)floater_close_box_size * mButtonScale));
- }
-
- // first time here, init 'buttons_rect'
- if(1 == button_count)
- {
- buttons_rect = btn_rect;
- }
- else
- {
- // if mDragOnLeft=true then buttons are on top-left side vertically aligned
- // title is not displayed in this case, calculating 'buttons_rect' for future use
- mDragOnLeft ? buttons_rect.mBottom -= btn_rect.mBottom :
- buttons_rect.mLeft = btn_rect.mLeft;
- }
- mButtons[i]->setRect(btn_rect);
- mButtons[i]->setVisible(true);
- // the restore button should have a tab stop so that it takes action when you Ctrl-Tab to a minimized floater
- mButtons[i]->setTabStop(i == BUTTON_RESTORE);
- }
- else
- {
- mButtons[i]->setVisible(false);
- }
- }
- if (mDragHandle)
- {
- localRectToOtherView(buttons_rect, &buttons_rect, mDragHandle);
- mDragHandle->setButtonsRect(buttons_rect);
- }
-}
-
-void LLFloater::drawConeToOwner(F32 &context_cone_opacity,
- F32 max_cone_opacity,
- LLView *owner_view,
- F32 fade_time,
- F32 contex_cone_in_alpha,
- F32 contex_cone_out_alpha)
-{
- if (owner_view
- && owner_view->isInVisibleChain()
- && hasFocus()
- && context_cone_opacity > 0.001f
- && gFocusMgr.childHasKeyboardFocus(this))
- {
- // draw cone of context pointing back to owner (e.x. texture swatch)
- LLRect owner_rect;
- owner_view->localRectToOtherView(owner_view->getLocalRect(), &owner_rect, this);
- LLRect local_rect = getLocalRect();
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- LLGLEnable(GL_CULL_FACE);
- gGL.begin(LLRender::QUADS);
- {
- gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity);
- gGL.vertex2i(owner_rect.mLeft, owner_rect.mTop);
- gGL.vertex2i(owner_rect.mRight, owner_rect.mTop);
- gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity);
- gGL.vertex2i(local_rect.mRight, local_rect.mTop);
- gGL.vertex2i(local_rect.mLeft, local_rect.mTop);
-
- gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity);
- gGL.vertex2i(local_rect.mLeft, local_rect.mTop);
- gGL.vertex2i(local_rect.mLeft, local_rect.mBottom);
- gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity);
- gGL.vertex2i(owner_rect.mLeft, owner_rect.mBottom);
- gGL.vertex2i(owner_rect.mLeft, owner_rect.mTop);
-
- gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity);
- gGL.vertex2i(local_rect.mRight, local_rect.mBottom);
- gGL.vertex2i(local_rect.mRight, local_rect.mTop);
- gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity);
- gGL.vertex2i(owner_rect.mRight, owner_rect.mTop);
- gGL.vertex2i(owner_rect.mRight, owner_rect.mBottom);
-
-
- gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity);
- gGL.vertex2i(local_rect.mLeft, local_rect.mBottom);
- gGL.vertex2i(local_rect.mRight, local_rect.mBottom);
- gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity);
- gGL.vertex2i(owner_rect.mRight, owner_rect.mBottom);
- gGL.vertex2i(owner_rect.mLeft, owner_rect.mBottom);
- }
- gGL.end();
- }
-
- if (gFocusMgr.childHasMouseCapture(getDragHandle()))
- {
- context_cone_opacity = lerp(context_cone_opacity, max_cone_opacity, LLSmoothInterpolation::getInterpolant(fade_time));
- }
- else
- {
- context_cone_opacity = lerp(context_cone_opacity, 0.f, LLSmoothInterpolation::getInterpolant(fade_time));
- }
-}
-
-void LLFloater::buildButtons(const Params& floater_params)
-{
- static LLUICachedControl<S32> floater_close_box_size ("UIFloaterCloseBoxSize", 0);
- static LLUICachedControl<S32> close_box_from_top ("UICloseBoxFromTop", 0);
- for (S32 i = 0; i < BUTTON_COUNT; i++)
- {
- if (mButtons[i])
- {
- removeChild(mButtons[i]);
- delete mButtons[i];
- mButtons[i] = NULL;
- }
-
- LLRect btn_rect;
- if (mDragOnLeft)
- {
- btn_rect.setLeftTopAndSize(
- LLPANEL_BORDER_WIDTH,
- getRect().getHeight() - close_box_from_top - (floater_close_box_size + 1) * (i + 1),
- 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,
- ll_round(floater_close_box_size * mButtonScale),
- ll_round(floater_close_box_size * mButtonScale));
- }
-
- LLButton::Params p;
- p.name(sButtonNames[i]);
- p.rect(btn_rect);
- p.image_unselected = getButtonImage(floater_params, (EFloaterButton)i);
- // Selected, no matter if hovered or not, is "pressed"
- LLUIImage* pressed_image = getButtonPressedImage(floater_params, (EFloaterButton)i);
- p.image_selected = pressed_image;
- p.image_hover_selected = pressed_image;
- // Use a glow effect when the user hovers over the button
- // These icons are really small, need glow amount increased
- p.hover_glow_amount( 0.33f );
- p.click_callback.function(boost::bind(sButtonCallbacks[i], this));
- p.tab_stop(false);
- p.follows.flags(FOLLOWS_TOP|FOLLOWS_RIGHT);
- p.tool_tip = getButtonTooltip(floater_params, (EFloaterButton)i, getIsChrome());
- p.scale_image(true);
- p.chrome(true);
-
- LLButton* buttonp = LLUICtrlFactory::create<LLButton>(p);
- addChild(buttonp);
- mButtons[i] = buttonp;
- }
-
- updateTitleButtons();
-}
-
-// static
-LLUIImage* LLFloater::getButtonImage(const Params& p, EFloaterButton e)
-{
- switch(e)
- {
- default:
- case BUTTON_CLOSE:
- return p.close_image;
- case BUTTON_RESTORE:
- return p.restore_image;
- case BUTTON_MINIMIZE:
- return p.minimize_image;
- case BUTTON_TEAR_OFF:
- return p.tear_off_image;
- case BUTTON_DOCK:
- return p.dock_image;
- case BUTTON_HELP:
- return p.help_image;
- }
-}
-
-// static
-LLUIImage* LLFloater::getButtonPressedImage(const Params& p, EFloaterButton e)
-{
- switch(e)
- {
- default:
- case BUTTON_CLOSE:
- return p.close_pressed_image;
- case BUTTON_RESTORE:
- return p.restore_pressed_image;
- case BUTTON_MINIMIZE:
- return p.minimize_pressed_image;
- case BUTTON_TEAR_OFF:
- return p.tear_off_pressed_image;
- case BUTTON_DOCK:
- return p.dock_pressed_image;
- case BUTTON_HELP:
- return p.help_pressed_image;
- }
-}
-
-// static
-std::string LLFloater::getButtonTooltip(const Params& p, EFloaterButton e, bool is_chrome)
-{
- // EXT-4081 (Lag Meter: Ctrl+W does not close floater)
- // If floater is chrome set 'Close' text for close button's tooltip
- if(is_chrome && BUTTON_CLOSE == e)
- {
- static std::string close_tooltip_chrome = LLTrans::getString("BUTTON_CLOSE_CHROME");
- return close_tooltip_chrome;
- }
- // TODO: per-floater localizable tooltips set in XML
- return sButtonToolTips[e];
-}
-
-/////////////////////////////////////////////////////
-// LLFloaterView
-
-static LLDefaultChildRegistry::Register<LLFloaterView> r("floater_view");
-
-LLFloaterView::LLFloaterView (const Params& p)
-: LLUICtrl (p),
- mFocusCycleMode(false),
- mMinimizePositionVOffset(0),
- mSnapOffsetBottom(0),
- mSnapOffsetRight(0)
-{
- mSnapView = getHandle();
-}
-
-// By default, adjust vertical.
-void LLFloaterView::reshape(S32 width, S32 height, bool called_from_parent)
-{
- LLView::reshape(width, height, called_from_parent);
-
- mLastSnapRect = getSnapRect();
-
- for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
- {
- LLView* viewp = *child_it;
- LLFloater* floaterp = dynamic_cast<LLFloater*>(viewp);
- if (floaterp->isDependent())
- {
- // dependents are moved with their "dependee"
- continue;
- }
-
- if (!floaterp->isMinimized() && floaterp->getCanDrag())
- {
- LLRect old_rect = floaterp->getRect();
- floaterp->applyPositioning(NULL, false);
- LLRect new_rect = floaterp->getRect();
-
- //LLRect r = floaterp->getRect();
-
- //// 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);
-
- S32 translate_x = new_rect.mLeft - old_rect.mLeft;
- S32 translate_y = new_rect.mBottom - old_rect.mBottom;
-
- //if (left_offset > right_offset)
- //{
- // translate_x = new_right - old_right;
- //}
-
- //if (top_offset < bottom_offset)
- //{
- // translate_y = new_top - old_top;
- //}
-
- // don't reposition immovable floaters
- //if (floaterp->getCanDrag())
- //{
- // floaterp->translate(translate_x, translate_y);
- //}
- for (LLHandle<LLFloater> dependent_floater : floaterp->mDependents)
- {
- if (dependent_floater.get())
- {
- dependent_floater.get()->translate(translate_x, translate_y);
- }
- }
- }
- }
-}
-
-
-void LLFloaterView::restoreAll()
-{
- // make sure all subwindows aren't minimized
- for (auto child : *getChildList())
- {
- LLFloater* floaterp = dynamic_cast<LLFloater*>(child);
- if (floaterp)
- {
- floaterp->setMinimized(false);
- }
- }
-
- // *FIX: make sure dependents are restored
-
- // children then deleted by default view constructor
-}
-
-
-LLRect LLFloaterView::findNeighboringPosition( LLFloater* reference_floater, LLFloater* neighbor )
-{
- LLRect base_rect = reference_floater->getRect();
- LLRect::tCoordType width = neighbor->getRect().getWidth();
- LLRect::tCoordType height = neighbor->getRect().getHeight();
- LLRect new_rect = neighbor->getRect();
-
- LLRect expanded_base_rect = base_rect;
- expanded_base_rect.stretch(10);
- for(LLFloater::handle_set_iter_t dependent_it = reference_floater->mDependents.begin();
- dependent_it != reference_floater->mDependents.end(); ++dependent_it)
- {
- LLFloater* sibling = dependent_it->get();
- // check for dependents within 10 pixels of base floater
- if (sibling &&
- sibling != neighbor &&
- sibling->getVisible() &&
- expanded_base_rect.overlaps(sibling->getRect()))
- {
- base_rect.unionWith(sibling->getRect());
- }
- }
-
- LLRect::tCoordType left_margin = llmax(0, base_rect.mLeft);
- LLRect::tCoordType right_margin = llmax(0, getRect().getWidth() - base_rect.mRight);
- LLRect::tCoordType top_margin = llmax(0, getRect().getHeight() - base_rect.mTop);
- LLRect::tCoordType bottom_margin = llmax(0, base_rect.mBottom);
-
- // find position for floater in following order
- // right->left->bottom->top
- for (S32 i = 0; i < 5; i++)
- {
- if (right_margin > width)
- {
- new_rect.translate(base_rect.mRight - neighbor->getRect().mLeft, base_rect.mTop - neighbor->getRect().mTop);
- return new_rect;
- }
- else if (left_margin > width)
- {
- new_rect.translate(base_rect.mLeft - neighbor->getRect().mRight, base_rect.mTop - neighbor->getRect().mTop);
- return new_rect;
- }
- else if (bottom_margin > height)
- {
- new_rect.translate(base_rect.mLeft - neighbor->getRect().mLeft, base_rect.mBottom - neighbor->getRect().mTop);
- return new_rect;
- }
- else if (top_margin > height)
- {
- new_rect.translate(base_rect.mLeft - neighbor->getRect().mLeft, base_rect.mTop - neighbor->getRect().mBottom);
- return new_rect;
- }
-
- // keep growing margins to find "best" fit
- left_margin += 20;
- right_margin += 20;
- top_margin += 20;
- bottom_margin += 20;
- }
-
- // didn't find anything, return initial rect
- return new_rect;
-}
-
-
-void LLFloaterView::bringToFront(LLFloater* child, bool give_focus, bool restore)
-{
- if (!child)
- return;
-
- LLFloater* front_child = mFrontChildHandle.get();
- if (front_child == child)
- {
- if (give_focus && child->canFocusStealFrontmost() && !gFocusMgr.childHasKeyboardFocus(child))
- {
- child->setFocus(true);
- }
- return;
- }
-
- if (front_child && front_child->getVisible())
- {
- front_child->goneFromFront();
- }
-
- mFrontChildHandle = child->getHandle();
-
- // *TODO: make this respect floater's mAutoFocus value, instead of
- // using parameter
- if (child->getHost())
- {
- // this floater is hosted elsewhere and hence not one of our children, abort
- return;
- }
- std::vector<LLFloater*> floaters_to_move;
- // Look at all floaters...tab
- for (child_list_const_iter_t child_it = beginChild(); child_it != endChild(); ++child_it)
- {
- LLFloater* floater = dynamic_cast<LLFloater*>(*child_it);
-
- // ...but if I'm a dependent floater...
- 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());
-
- 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(); ++dependent_it)
- {
- LLFloater* sibling = dependent_it->get();
- if (sibling)
- {
- floaters_to_move.push_back(sibling);
- }
- }
- //...before bringing my parent to the front...
- floaters_to_move.push_back(floater);
- }
- }
- }
-
- std::vector<LLFloater*>::iterator floater_it;
- for(floater_it = floaters_to_move.begin(); floater_it != floaters_to_move.end(); ++floater_it)
- {
- LLFloater* floaterp = *floater_it;
- sendChildToFront(floaterp);
-
- // always unminimize dependee, but allow dependents to stay minimized
- if (!floaterp->isDependent())
- {
- floaterp->setMinimized(false);
- }
- }
- 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(); ++dependent_it)
- {
- LLFloater* dependent = dependent_it->get();
- if (dependent)
- {
- sendChildToFront(dependent);
- }
- }
-
- // ...and finally bringing myself to front
- // (do this last, so that I'm left in front at end of this call)
- if (*beginChild() != child)
- {
- sendChildToFront(child);
- }
-
- if(restore)
- {
- child->setMinimized(false);
- }
-
- if (give_focus && !gFocusMgr.childHasKeyboardFocus(child))
- {
- child->setFocus(true);
- // floater did not take focus, so relinquish focus to world
- if (!child->hasFocus())
- {
- gFocusMgr.setKeyboardFocus(NULL);
- }
- }
-}
-
-void LLFloaterView::highlightFocusedFloater()
-{
- for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
- {
- LLFloater *floater = (LLFloater *)(*child_it);
-
- // skip dependent floaters, as we'll handle them in a batch along with their dependee(?)
- if (floater->isDependent())
- {
- continue;
- }
-
- bool floater_or_dependent_has_focus = gFocusMgr.childHasKeyboardFocus(floater);
- for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin();
- dependent_it != floater->mDependents.end();
- ++dependent_it)
- {
- LLFloater* dependent_floaterp = dependent_it->get();
- if (dependent_floaterp && gFocusMgr.childHasKeyboardFocus(dependent_floaterp))
- {
- floater_or_dependent_has_focus = true;
- }
- }
-
- // now set this floater and all its dependents
- floater->setForeground(floater_or_dependent_has_focus);
-
- for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin();
- dependent_it != floater->mDependents.end(); )
- {
- LLFloater* dependent_floaterp = dependent_it->get();
- if (dependent_floaterp)
- {
- dependent_floaterp->setForeground(floater_or_dependent_has_focus);
- }
- ++dependent_it;
- }
-
- floater->cleanupHandles();
- }
-}
-
-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)
- {
- LLFloater *floater = (LLFloater *)(*child_it);
-
- floater->setForeground(false);
- }
-}
-
-void LLFloaterView::focusFrontFloater()
-{
- LLFloater* floaterp = getFrontmost();
- if (floaterp)
- {
- floaterp->setFocus(true);
- }
-}
-
-void LLFloaterView::getMinimizePosition(S32 *left, S32 *bottom)
-{
- const LLFloater::Params& default_params = LLFloater::getDefaultParams();
- S32 floater_header_size = default_params.header_height;
- static LLUICachedControl<S32> minimized_width ("UIMinimizedWidth", 0);
- LLRect snap_rect_local = getLocalSnapRect();
- snap_rect_local.mTop += mMinimizePositionVOffset;
- for(S32 col = snap_rect_local.mLeft;
- col < snap_rect_local.getWidth() - minimized_width;
- col += minimized_width)
- {
- for(S32 row = snap_rect_local.mTop - floater_header_size;
- row > floater_header_size;
- row -= floater_header_size ) //loop rows
- {
-
- bool foundGap = true;
- for(child_list_const_iter_t child_it = getChildList()->begin();
- child_it != getChildList()->end();
- ++child_it) //loop floaters
- {
- // Examine minimized children.
- LLFloater* floater = dynamic_cast<LLFloater*>(*child_it);
- if(floater->isMinimized())
- {
- LLRect r = floater->getRect();
- if((r.mBottom < (row + floater_header_size))
- && (r.mBottom > (row - floater_header_size))
- && (r.mLeft < (col + minimized_width))
- && (r.mLeft > (col - minimized_width)))
- {
- // needs the check for off grid. can't drag,
- // but window resize makes them off
- foundGap = false;
- break;
- }
- }
- } //done floaters
- if(foundGap)
- {
- *left = col;
- *bottom = row;
- return; //done
- }
- } //done this col
- }
-
- // crude - stack'em all at 0,0 when screen is full of minimized
- // floaters.
- *left = snap_rect_local.mLeft;
- *bottom = snap_rect_local.mBottom;
-}
-
-
-void LLFloaterView::destroyAllChildren()
-{
- LLView::deleteAllChildren();
-}
-
-void LLFloaterView::closeAllChildren(bool app_quitting)
-{
- // iterate over a copy of the list, because closing windows will destroy
- // some windows on the list.
- child_list_t child_list = *(getChildList());
-
- for (child_list_const_iter_t it = child_list.begin(); it != child_list.end(); ++it)
- {
- LLView* viewp = *it;
- child_list_const_iter_t exists = std::find(getChildList()->begin(), getChildList()->end(), viewp);
- if (exists == getChildList()->end())
- {
- // this floater has already been removed
- continue;
- }
-
- LLFloater* floaterp = dynamic_cast<LLFloater*>(viewp);
-
- // Attempt to close floater. This will cause the "do you want to save"
- // dialogs to appear.
- // Skip invisible floaters if we're not quitting (STORM-192).
- if (floaterp->canClose() && !floaterp->isDead() &&
- (app_quitting || floaterp->getVisible()))
- {
- floaterp->closeFloater(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()
-{
- // see if there are any visible floaters (some floaters "close"
- // by setting themselves invisible)
- for (child_list_const_iter_t it = getChildList()->begin(); it != getChildList()->end(); ++it)
- {
- LLFloater* floaterp = dynamic_cast<LLFloater*>(*it);
-
- if (floaterp->getVisible() && !floaterp->isDead() && floaterp->isCloseable())
- {
- return false;
- }
- }
- return true;
-}
-
-void LLFloaterView::shiftFloaters(S32 x_offset, S32 y_offset)
-{
- for (child_list_const_iter_t it = getChildList()->begin(); it != getChildList()->end(); ++it)
- {
- LLFloater* floaterp = dynamic_cast<LLFloater*>(*it);
-
- if (floaterp && floaterp->isMinimized())
- {
- floaterp->translate(x_offset, y_offset);
- }
- }
-}
-
-void LLFloaterView::refresh()
-{
- 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)
- {
- LLFloater* floaterp = dynamic_cast<LLFloater*>(*child_it);
- if (floaterp && floaterp->getVisible() )
- {
- // minimized floaters are kept fully onscreen
- adjustToFitScreen(floaterp, !floaterp->isMinimized());
- }
- }
-}
-
-void LLFloaterView::adjustToFitScreen(LLFloater* floater, bool allow_partial_outside, bool snap_in_toolbars/* = false*/)
-{
- if (floater->getParent() != this)
- {
- // floater is hosted elsewhere, so ignore
- return;
- }
-
- if (floater->getDependee() &&
- floater->getDependee() == floater->getSnapTarget().get())
- {
- // floater depends on other and snaps to it, so ignore
- return;
- }
-
- LLRect::tCoordType screen_width = getSnapRect().getWidth();
- LLRect::tCoordType screen_height = getSnapRect().getHeight();
-
- // only automatically resize non-minimized, resizable floaters
- if( floater->isResizable() && !floater->isMinimized() )
- {
- LLRect view_rect = floater->getRect();
- S32 old_width = view_rect.getWidth();
- S32 old_height = view_rect.getHeight();
- S32 min_width;
- S32 min_height;
- floater->getResizeLimits( &min_width, &min_height );
-
- // Make sure floater isn't already smaller than its min height/width?
- S32 new_width = llmax( min_width, old_width );
- S32 new_height = llmax( min_height, old_height);
-
- if((new_width > screen_width) || (new_height > screen_height))
- {
- // We have to make this window able to fit on screen
- new_width = llmin(new_width, screen_width);
- new_height = llmin(new_height, screen_height);
-
- // ...while respecting minimum width/height
- new_width = llmax(new_width, min_width);
- new_height = llmax(new_height, min_height);
-
- LLRect new_rect;
- new_rect.setLeftTopAndSize(view_rect.mLeft,view_rect.mTop,new_width, new_height);
-
- floater->setShape(new_rect);
-
- if (floater->followsRight())
- {
- floater->translate(old_width - new_width, 0);
- }
-
- if (floater->followsTop())
- {
- floater->translate(0, old_height - new_height);
- }
- }
- }
-
- const LLRect& constraint = snap_in_toolbars ? getSnapRect() : gFloaterView->getRect();
- S32 min_overlap_pixels = allow_partial_outside ? FLOATER_MIN_VISIBLE_PIXELS : S32_MAX;
-
- floater->fitWithDependentsOnScreen(mToolbarLeftRect, mToolbarBottomRect, mToolbarRightRect, constraint, min_overlap_pixels);
-}
-
-void LLFloaterView::draw()
-{
- refresh();
-
- // hide focused floater if in cycle mode, so that it can be drawn on top
- LLFloater* focused_floater = getFocusedFloater();
-
- if (mFocusCycleMode && focused_floater)
- {
- child_list_const_iter_t child_it = getChildList()->begin();
- for (;child_it != getChildList()->end(); ++child_it)
- {
- if ((*child_it) != focused_floater)
- {
- drawChild(*child_it);
- }
- }
-
- drawChild(focused_floater, -TABBED_FLOATER_OFFSET, TABBED_FLOATER_OFFSET);
- }
- else
- {
- LLView::draw();
- }
-}
-
-LLRect LLFloaterView::getSnapRect() const
-{
- LLRect snap_rect = getLocalRect();
-
- LLView* snap_view = mSnapView.get();
- if (snap_view)
- {
- snap_view->localRectToOtherView(snap_view->getLocalRect(), &snap_rect, this);
- }
-
- return snap_rect;
-}
-
-LLFloater *LLFloaterView::getFocusedFloater() const
-{
- for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
- {
- if ((*child_it)->isCtrl())
- {
- LLFloater* ctrlp = dynamic_cast<LLFloater*>(*child_it);
- if ( ctrlp && ctrlp->hasFocus() )
- {
- return ctrlp;
- }
- }
- }
- return NULL;
-}
-
-LLFloater *LLFloaterView::getFrontmost() const
-{
- for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
- {
- LLView* viewp = *child_it;
- if ( viewp->getVisible() && !viewp->isDead())
- {
- return (LLFloater *)viewp;
- }
- }
- return NULL;
-}
-
-LLFloater *LLFloaterView::getBackmost() const
-{
- LLFloater* back_most = NULL;
- for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
- {
- LLView* viewp = *child_it;
- if ( viewp->getVisible() )
- {
- back_most = (LLFloater *)viewp;
- }
- }
- return back_most;
-}
-
-void LLFloaterView::syncFloaterTabOrder()
-{
- LLFloater* front_child = mFrontChildHandle.get();
- if (front_child && front_child->getIsChrome())
- return;
-
- // look for a visible modal dialog, starting from first
- LLModalDialog* modal_dialog = NULL;
- for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
- {
- LLModalDialog* dialog = dynamic_cast<LLModalDialog*>(*child_it);
- if (dialog && dialog->isModal() && dialog->getVisible())
- {
- modal_dialog = dialog;
- break;
- }
- }
-
- if (modal_dialog)
- {
- // If we have a visible modal dialog, make sure that it has focus
- LLUI::getInstance()->addPopup(modal_dialog);
-
- if( !gFocusMgr.childHasKeyboardFocus( modal_dialog ) )
- {
- modal_dialog->setFocus(true);
- }
-
- if( !gFocusMgr.childHasMouseCapture( modal_dialog ) )
- {
- gFocusMgr.setMouseCapture( modal_dialog );
- }
- }
- else
- {
- // otherwise, make sure the focused floater is in the front of the child list
- for ( child_list_const_reverse_iter_t child_it = getChildList()->rbegin(); child_it != getChildList()->rend(); ++child_it)
- {
- LLFloater* floaterp = dynamic_cast<LLFloater*>(*child_it);
- if (gFocusMgr.childHasKeyboardFocus(floaterp))
- {
- LLFloater* front_child = mFrontChildHandle.get();
- if (front_child != floaterp)
- {
- // Grab a list of the top floaters that want to stay on top of the focused floater
- std::list<LLFloater*> listTop;
- if (front_child && !front_child->canFocusStealFrontmost())
- {
- for (LLView* childp : *getChildList())
- {
- LLFloater* child_floaterp = static_cast<LLFloater*>(childp);
- if (child_floaterp->canFocusStealFrontmost())
- break;
- listTop.push_back(child_floaterp);
- }
- }
-
- bringToFront(floaterp, false);
-
- // Restore top floaters
- if (!listTop.empty())
- {
- for (LLView* childp : listTop)
- {
- sendChildToFront(childp);
- }
- mFrontChildHandle = listTop.back()->getHandle();
- }
- }
-
- break;
- }
- }
- }
-}
-
-LLFloater* LLFloaterView::getParentFloater(LLView* viewp) const
-{
- LLView* parentp = viewp->getParent();
-
- while(parentp && parentp != this)
- {
- viewp = parentp;
- parentp = parentp->getParent();
- }
-
- if (parentp == this)
- {
- return dynamic_cast<LLFloater*>(viewp);
- }
-
- return NULL;
-}
-
-S32 LLFloaterView::getZOrder(LLFloater* child)
-{
- S32 rv = 0;
- for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
- {
- LLView* viewp = *child_it;
- if(viewp == child)
- {
- break;
- }
- ++rv;
- }
- return rv;
-}
-
-void LLFloaterView::pushVisibleAll(bool visible, const skip_list_t& skip_list)
-{
- for (child_list_const_iter_t child_iter = getChildList()->begin();
- child_iter != getChildList()->end(); ++child_iter)
- {
- LLView *view = *child_iter;
- if (skip_list.find(view) == skip_list.end())
- {
- view->pushVisible(visible);
- }
- }
-
- LLFloaterReg::blockShowFloaters(true);
-}
-
-void LLFloaterView::popVisibleAll(const skip_list_t& skip_list)
-{
- // make a copy of the list since some floaters change their
- // order in the childList when changing visibility.
- child_list_t child_list_copy = *getChildList();
-
- for (child_list_const_iter_t child_iter = child_list_copy.begin();
- child_iter != child_list_copy.end(); ++child_iter)
- {
- LLView *view = *child_iter;
- if (skip_list.find(view) == skip_list.end())
- {
- view->popVisible();
- }
- }
-
- 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)
- {
- llassert_always(mInstanceName.empty());
- mInstanceName = name;
- if (!mInstanceName.empty())
- {
- std::string ctrl_name = getControlName(mInstanceName, mKey);
- initRectControl();
- if (!mVisibilityControl.empty())
- {
- mVisibilityControl = LLFloaterReg::declareVisibilityControl(ctrl_name);
- }
- if(!mDocStateControl.empty())
- {
- mDocStateControl = LLFloaterReg::declareDockStateControl(ctrl_name);
- }
- }
-}
-}
-
-void LLFloater::setKey(const LLSD& newkey)
-{
- // Note: We don't have to do anything special with registration when we change keys
- mKey = newkey;
-}
-
-//static
-void LLFloater::setupParamsForExport(Params& p, LLView* parent)
-{
- // Do rectangle munging to topleft layout first
- LLPanel::setupParamsForExport(p, parent);
-
- // Copy the rectangle out to apply layout constraints
- LLRect rect = p.rect;
-
- // Null out other settings
- p.rect.left.setProvided(false);
- p.rect.top.setProvided(false);
- p.rect.right.setProvided(false);
- p.rect.bottom.setProvided(false);
-
- // Explicitly set width/height
- p.rect.width.set( rect.getWidth(), true );
- p.rect.height.set( rect.getHeight(), true );
-
- // If you can't resize this floater, don't export min_height
- // and min_width
- bool can_resize = p.can_resize;
- if (!can_resize)
- {
- p.min_height.setProvided(false);
- p.min_width.setProvided(false);
- }
-}
-
-void LLFloater::initFromParams(const LLFloater::Params& p)
-{
- // *NOTE: We have too many classes derived from LLFloater to retrofit them
- // all to pass in params via constructors. So we use this method.
-
- // control_name, tab_stop, focus_lost_callback, initial_value, rect, enabled, visible
- LLPanel::initFromParams(p);
-
- // override any follows flags
- if (mPositioning != LLFloaterEnums::POSITIONING_SPECIFIED)
- {
- setFollows(FOLLOWS_NONE);
- }
-
- mTitle = p.title;
- mShortTitle = p.short_title;
- applyTitle();
-
- setCanTearOff(p.can_tear_off);
- setCanMinimize(p.can_minimize);
- setCanClose(p.can_close);
- setCanDock(p.can_dock);
- setCanResize(p.can_resize);
- setResizeLimits(p.min_width, p.min_height);
-
- mDragOnLeft = p.can_drag_on_left;
- mHeaderHeight = p.header_height;
- mLegacyHeaderHeight = p.legacy_header_height;
- mSingleInstance = p.single_instance;
- mReuseInstance = p.reuse_instance.isProvided() ? p.reuse_instance : p.single_instance;
-
- mDefaultRelativeX = p.rel_x;
- mDefaultRelativeY = p.rel_y;
-
- mPositioning = p.positioning;
- mAutoClose = p.auto_close;
-
- 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
- }
-
- // open callback
- if (p.open_callback.isProvided())
- {
- setOpenCallback(initCommitCallback(p.open_callback));
- }
- // close callback
- if (p.close_callback.isProvided())
- {
- setCloseCallback(initCommitCallback(p.close_callback));
- }
-
- if (mDragHandle)
- {
- mDragHandle->setTitleVisible(p.show_title);
- }
-}
-
-boost::signals2::connection LLFloater::setMinimizeCallback( const commit_signal_t::slot_type& cb )
-{
- if (!mMinimizeSignal) mMinimizeSignal = new commit_signal_t();
- return mMinimizeSignal->connect(cb);
-}
-
-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);
-}
-
-bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::string& filename, LLXMLNodePtr output_node)
-{
- LL_PROFILE_ZONE_SCOPED;
- 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);
-
- 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);
- output_node->setName(node->getName()->mString);
- parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &default_params);
- }
-
- // Default floater position to top-left corner of screen
- // However, some legacy floaters have explicit top or bottom
- // coordinates set, so respect their wishes.
- if (!params.rect.top.isProvided() && !params.rect.bottom.isProvided())
- {
- params.rect.top.set(0);
- }
- if (!params.rect.left.isProvided() && !params.rect.right.isProvided())
- {
- params.rect.left.set(0);
- }
- params.from_xui = true;
- applyXUILayout(params, parent, parent == gFloaterView ? gFloaterView->getSnapRect() : parent->getLocalRect());
- initFromParams(params);
-
- initFloater(params);
-
- LLMultiFloater* last_host = LLFloater::getFloaterHost();
- if (node->hasName("multi_floater"))
- {
- LLFloater::setFloaterHost((LLMultiFloater*) this);
- }
-
- LLUICtrlFactory::createChildren(this, node, child_registry_t::instance(), output_node);
-
- if (node->hasName("multi_floater"))
- {
- LLFloater::setFloaterHost(last_host);
- }
-
- // HACK: When we changed the header height to 25 pixels in Viewer 2, rather
- // than re-layout all the floaters we use this value in pixels to make the
- // whole floater bigger and change the top-left coordinate for widgets.
- // The goal is to eventually set mLegacyHeaderHeight to zero, which would
- // make the top-left corner for widget layout the same as the top-left
- // corner of the window's content area. James
- S32 header_stretch = (mHeaderHeight - mLegacyHeaderHeight);
- if (header_stretch > 0)
- {
- // Stretch the floater vertically, don't move widgets
- LLRect rect = getRect();
- rect.mTop += header_stretch;
-
- // This will also update drag handle, title bar, close box, etc.
- setRect(rect);
- }
-
- bool result;
- result = postBuild();
-
- if (!result)
- {
- LL_ERRS() << "Failed to construct floater " << getName() << LL_ENDL;
- }
-
- applyRectControl(); // If we have a saved rect control, apply it
- gFloaterView->adjustToFitScreen(this, false); // Floaters loaded from XML should all fit on screen
-
- moveResizeHandlesToFront();
-
- applyDockState();
-
- return true; // *TODO: Error checking
-}
-
-bool LLFloater::isShown() const
-{
- return ! isMinimized() && isInVisibleChain();
-}
-
-bool LLFloater::isDetachedAndNotMinimized()
-{
- return !getHost() && !isMinimized();
-}
-
-/* static */
-bool LLFloater::isShown(const LLFloater* floater)
-{
- return floater && floater->isShown();
-}
-
-/* static */
-bool LLFloater::isMinimized(const LLFloater* floater)
-{
- return floater && floater->isMinimized();
-}
-
-/* static */
-bool LLFloater::isVisible(const LLFloater* floater)
-{
- return floater && floater->getVisible();
-}
-
-bool LLFloater::buildFromFile(const std::string& filename)
-{
- LL_PROFILE_ZONE_SCOPED;
- LLXMLNodePtr root;
-
- if (!LLUICtrlFactory::getLayeredXMLNode(filename, root))
- {
- 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")) )
- {
- LL_WARNS() << "Root node should be named floater in: " << filename << LL_ENDL;
- return false;
- }
-
- bool res = true;
-
- LL_DEBUGS() << "Building floater " << filename << LL_ENDL;
- LLUICtrlFactory::instance().pushFileName(filename);
- {
- if (!getFactoryMap().empty())
- {
- LLPanel::sFactoryStack.push_front(&getFactoryMap());
- }
-
- // for local registry callbacks; define in constructor, referenced in XUI or postBuild
- getCommitCallbackRegistrar().pushScope();
- getEnableCallbackRegistrar().pushScope();
-
- res = initFloaterXML(root, getParent(), filename, NULL);
-
- setXMLFilename(filename);
-
- getCommitCallbackRegistrar().popScope();
- getEnableCallbackRegistrar().popScope();
-
- if (!getFactoryMap().empty())
- {
- LLPanel::sFactoryStack.pop_front();
- }
- }
- LLUICtrlFactory::instance().popFileName();
-
- 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);
-
- const LLRect& rect = getControlGroup()->getRect(mRectControl);
- if (rect.notEmpty() && !mDefaultRectForGroup && mResizable)
- {
- next_rect.setLeftTopAndSize(next_rect.mLeft, next_rect.mTop, llmax(mMinWidth, rect.getWidth()), llmax(mMinHeight, rect.getHeight()));
- }
- else
- {
- 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);
- }
-}
+/**
+ * @file llfloater.cpp
+ * @brief LLFloater base class
+ *
+ * $LicenseInfo:firstyear=2002&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$
+ */
+
+// Floating "windows" within the GL display, like the inventory floater,
+// mini-map floater, etc.
+
+#include "linden_common.h"
+#include "llviewereventrecorder.h"
+#include "llfloater.h"
+
+#include "llfocusmgr.h"
+
+#include "lluictrlfactory.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcriticaldamp.h" // LLSmoothInterpolation
+#include "lldir.h"
+#include "lldraghandle.h"
+#include "llfloaterreg.h"
+#include "llfocusmgr.h"
+#include "llresizebar.h"
+#include "llresizehandle.h"
+#include "llkeyboard.h"
+#include "llmenugl.h" // MENU_BAR_HEIGHT
+#include "llmodaldialog.h"
+#include "lltextbox.h"
+#include "llresmgr.h"
+#include "llui.h"
+#include "llwindow.h"
+#include "llstl.h"
+#include "llcontrol.h"
+#include "lltabcontainer.h"
+#include "v2math.h"
+#include "lltrans.h"
+#include "llhelp.h"
+#include "llmultifloater.h"
+#include "llsdutil.h"
+#include "lluiusage.h"
+
+
+// use this to control "jumping" behavior when Ctrl-Tabbing
+const S32 TABBED_FLOATER_OFFSET = 0;
+
+const F32 LLFloater::CONTEXT_CONE_IN_ALPHA = 0.0f;
+const F32 LLFloater::CONTEXT_CONE_OUT_ALPHA = 1.f;
+const F32 LLFloater::CONTEXT_CONE_FADE_TIME = 0.08f;
+
+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
+ "llfloater_restore_btn", //BUTTON_RESTORE
+ "llfloater_minimize_btn", //BUTTON_MINIMIZE
+ "llfloater_tear_off_btn", //BUTTON_TEAR_OFF
+ "llfloater_dock_btn", //BUTTON_DOCK
+ "llfloater_help_btn" //BUTTON_HELP
+};
+
+std::string LLFloater::sButtonToolTips[BUTTON_COUNT];
+
+std::string LLFloater::sButtonToolTipsIndex[BUTTON_COUNT]=
+{
+#ifdef LL_DARWIN
+ "BUTTON_CLOSE_DARWIN", //"Close (Cmd-W)", //BUTTON_CLOSE
+#else
+ "BUTTON_CLOSE_WIN", //"Close (Ctrl-W)", //BUTTON_CLOSE
+#endif
+ "BUTTON_RESTORE", //"Restore", //BUTTON_RESTORE
+ "BUTTON_MINIMIZE", //"Minimize", //BUTTON_MINIMIZE
+ "BUTTON_TEAR_OFF", //"Tear Off", //BUTTON_TEAR_OFF
+ "BUTTON_DOCK",
+ "BUTTON_HELP"
+};
+
+LLFloater::click_callback LLFloater::sButtonCallbacks[BUTTON_COUNT] =
+{
+ LLFloater::onClickClose, //BUTTON_CLOSE
+ LLFloater::onClickMinimize, //BUTTON_RESTORE
+ LLFloater::onClickMinimize, //BUTTON_MINIMIZE
+ LLFloater::onClickTearOff, //BUTTON_TEAR_OFF
+ LLFloater::onClickDock, //BUTTON_DOCK
+ LLFloater::onClickHelp //BUTTON_HELP
+};
+
+LLMultiFloater* LLFloater::sHostp = NULL;
+bool LLFloater::sQuitting = false; // Flag to prevent storing visibility controls while quitting
+
+LLFloaterView* gFloaterView = NULL;
+
+/*==========================================================================*|
+// DEV-38598: The fundamental problem with this operation is that it can only
+// support a subset of LLSD values. While it's plausible to compare two arrays
+// lexicographically, what strict ordering can you impose on maps?
+// (LLFloaterTOS's current key is an LLSD map.)
+
+// Of course something like this is necessary if you want to build a std::set
+// or std::map with LLSD keys. Fortunately we're getting by with other
+// container types for now.
+
+//static
+bool LLFloater::KeyCompare::compare(const LLSD& a, const LLSD& b)
+{
+ if (a.type() != b.type())
+ {
+ //LL_ERRS() << "Mismatched LLSD types: (" << a << ") mismatches (" << b << ")" << LL_ENDL;
+ return false;
+ }
+ else if (a.isUndefined())
+ return false;
+ else if (a.isInteger())
+ return a.asInteger() < b.asInteger();
+ else if (a.isReal())
+ return a.asReal() < b.asReal();
+ else if (a.isString())
+ return a.asString() < b.asString();
+ else if (a.isUUID())
+ return a.asUUID() < b.asUUID();
+ else if (a.isDate())
+ return a.asDate() < b.asDate();
+ else if (a.isURI())
+ return a.asString() < b.asString(); // compare URIs as strings
+ else if (a.isBoolean())
+ return a.asBoolean() < b.asBoolean();
+ else
+ return false; // no valid operation for Binary
+}
+|*==========================================================================*/
+
+bool LLFloater::KeyCompare::equate(const LLSD& a, const LLSD& b)
+{
+ return llsd_equals(a, b);
+}
+
+//************************************
+
+LLFloater::Params::Params()
+: title("title"),
+ short_title("short_title"),
+ single_instance("single_instance", false),
+ reuse_instance("reuse_instance", false),
+ can_resize("can_resize", false),
+ can_minimize("can_minimize", true),
+ can_close("can_close", true),
+ can_drag_on_left("can_drag_on_left", false),
+ can_tear_off("can_tear_off", true),
+ save_dock_state("save_dock_state", false),
+ save_rect("save_rect", false),
+ save_visibility("save_visibility", false),
+ can_dock("can_dock", false),
+ show_title("show_title", true),
+ auto_close("auto_close", false),
+ positioning("positioning", LLFloaterEnums::POSITIONING_RELATIVE),
+ header_height("header_height", 0),
+ legacy_header_height("legacy_header_height", 0),
+ close_image("close_image"),
+ restore_image("restore_image"),
+ minimize_image("minimize_image"),
+ tear_off_image("tear_off_image"),
+ dock_image("dock_image"),
+ help_image("help_image"),
+ close_pressed_image("close_pressed_image"),
+ restore_pressed_image("restore_pressed_image"),
+ minimize_pressed_image("minimize_pressed_image"),
+ tear_off_pressed_image("tear_off_pressed_image"),
+ dock_pressed_image("dock_pressed_image"),
+ help_pressed_image("help_pressed_image"),
+ open_callback("open_callback"),
+ close_callback("close_callback"),
+ follows("follows"),
+ rel_x("rel_x", 0),
+ rel_y("rel_y", 0)
+{
+ changeDefault(visible, false);
+}
+
+
+//static
+const LLFloater::Params& LLFloater::getDefaultParams()
+{
+ return LLUICtrlFactory::getDefaultParams<LLFloater>();
+}
+
+//static
+void LLFloater::initClass()
+{
+ // translate tooltips for floater buttons
+ for (S32 i = 0; i < BUTTON_COUNT; i++)
+ {
+ sButtonToolTips[i] = LLTrans::getString( sButtonToolTipsIndex[i] );
+ }
+
+ LLControlVariable* ctrl = LLUI::getInstance()->mSettingGroups["config"]->getControl("ActiveFloaterTransparency").get();
+ if (ctrl)
+ {
+ ctrl->getSignal()->connect(boost::bind(&LLFloater::updateActiveFloaterTransparency));
+ updateActiveFloaterTransparency();
+ }
+
+ ctrl = LLUI::getInstance()->mSettingGroups["config"]->getControl("InactiveFloaterTransparency").get();
+ if (ctrl)
+ {
+ ctrl->getSignal()->connect(boost::bind(&LLFloater::updateInactiveFloaterTransparency));
+ updateInactiveFloaterTransparency();
+ }
+
+}
+
+// defaults for floater param block pulled from widgets/floater.xml
+static LLWidgetNameRegistry::StaticRegistrar sRegisterFloaterParams(&typeid(LLFloater::Params), "floater");
+
+LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p)
+: LLPanel(), // intentionally do not pass params here, see initFromParams
+ mDragHandle(NULL),
+ mTitle(p.title),
+ mShortTitle(p.short_title),
+ mSingleInstance(p.single_instance),
+ mReuseInstance(p.reuse_instance.isProvided() ? p.reuse_instance : p.single_instance), // reuse single-instance floaters by default
+ mKey(key),
+ mCanTearOff(p.can_tear_off),
+ mCanMinimize(p.can_minimize),
+ mCanClose(p.can_close),
+ mDragOnLeft(p.can_drag_on_left),
+ mResizable(p.can_resize),
+ mAutoClose(p.auto_close),
+ mPositioning(p.positioning),
+ mMinWidth(p.min_width),
+ mMinHeight(p.min_height),
+ mHeaderHeight(p.header_height),
+ mLegacyHeaderHeight(p.legacy_header_height),
+ mDefaultRectForGroup(true),
+ mMinimized(false),
+ mForeground(false),
+ mFirstLook(true),
+ mButtonScale(1.0f),
+ mAutoFocus(true), // automatically take focus when opened
+ mCanDock(false),
+ mDocked(false),
+ mTornOff(false),
+ mHasBeenDraggedWhileMinimized(false),
+ mPreviousMinimizedBottom(0),
+ mPreviousMinimizedLeft(0),
+ mDefaultRelativeX(p.rel_x),
+ mDefaultRelativeY(p.rel_y),
+ mMinimizeSignal(NULL)
+// mNotificationContext(NULL)
+{
+ mPosition.setFloater(*this);
+// mNotificationContext = new LLFloaterNotificationContext(getHandle());
+
+ // Clicks stop here.
+ setMouseOpaque(true);
+
+ // Floaters always draw their background, unlike every other panel.
+ setBackgroundVisible(true);
+
+ // Floaters start not minimized. When minimized, they save their
+ // prior rectangle to be used on restore.
+ mExpandedRect.set(0,0,0,0);
+
+ memset(mButtonsEnabled, 0, BUTTON_COUNT * sizeof(bool));
+ memset(mButtons, 0, BUTTON_COUNT * sizeof(LLButton*));
+
+ addDragHandle();
+ addResizeCtrls();
+
+ initFromParams(p);
+
+ initFloater(p);
+}
+
+// Note: Floaters constructed from XML call init() twice!
+void LLFloater::initFloater(const Params& p)
+{
+ // Close button.
+ if (mCanClose)
+ {
+ mButtonsEnabled[BUTTON_CLOSE] = true;
+ }
+
+ // Help button: '?'
+ //SL-14050 Disable all Help question marks
+ mButtonsEnabled[BUTTON_HELP] = false;
+
+ // Minimize button only for top draggers
+ if ( !mDragOnLeft && mCanMinimize )
+ {
+ mButtonsEnabled[BUTTON_MINIMIZE] = true;
+ }
+
+ if(mCanDock)
+ {
+ mButtonsEnabled[BUTTON_DOCK] = true;
+ }
+
+ buildButtons(p);
+
+ // Floaters are created in the invisible state
+ setVisible(false);
+
+ if (!getParent())
+ {
+ gFloaterView->addChild(this);
+ }
+}
+
+void LLFloater::addDragHandle()
+{
+ if (!mDragHandle)
+ {
+ if (mDragOnLeft)
+ {
+ LLDragHandleLeft::Params p;
+ p.name("drag");
+ p.follows.flags(FOLLOWS_ALL);
+ p.label(mTitle);
+ mDragHandle = LLUICtrlFactory::create<LLDragHandleLeft>(p);
+ }
+ else // drag on top
+ {
+ LLDragHandleTop::Params p;
+ p.name("Drag Handle");
+ p.follows.flags(FOLLOWS_ALL);
+ p.label(mTitle);
+ mDragHandle = LLUICtrlFactory::create<LLDragHandleTop>(p);
+ }
+ addChild(mDragHandle);
+ }
+ layoutDragHandle();
+ applyTitle();
+}
+
+void LLFloater::layoutDragHandle()
+{
+ static LLUICachedControl<S32> floater_close_box_size ("UIFloaterCloseBoxSize", 0);
+ S32 close_box_size = mCanClose ? floater_close_box_size : 0;
+
+ LLRect rect;
+ if (mDragOnLeft)
+ {
+ rect.setLeftTopAndSize(0, 0, DRAG_HANDLE_WIDTH, getRect().getHeight() - LLPANEL_BORDER_WIDTH - close_box_size);
+ }
+ else // drag on top
+ {
+ rect = getLocalRect();
+ }
+ mDragHandle->setShape(rect);
+ updateTitleButtons();
+}
+
+// static
+void LLFloater::updateActiveFloaterTransparency()
+{
+ static LLCachedControl<F32> active_transparency(*LLUI::getInstance()->mSettingGroups["config"], "ActiveFloaterTransparency", 1.f);
+ sActiveControlTransparency = active_transparency;
+}
+
+// static
+void LLFloater::updateInactiveFloaterTransparency()
+{
+ static LLCachedControl<F32> inactive_transparency(*LLUI::getInstance()->mSettingGroups["config"], "InactiveFloaterTransparency", 0.95f);
+ sInactiveControlTransparency = inactive_transparency;
+}
+
+void LLFloater::addResizeCtrls()
+{
+ // Resize bars (sides)
+ LLResizeBar::Params p;
+ p.name("resizebar_left");
+ p.resizing_view(this);
+ p.min_size(mMinWidth);
+ p.side(LLResizeBar::LEFT);
+ mResizeBar[LLResizeBar::LEFT] = LLUICtrlFactory::create<LLResizeBar>(p);
+ addChild( mResizeBar[LLResizeBar::LEFT] );
+
+ p.name("resizebar_top");
+ p.min_size(mMinHeight);
+ p.side(LLResizeBar::TOP);
+
+ mResizeBar[LLResizeBar::TOP] = LLUICtrlFactory::create<LLResizeBar>(p);
+ addChild( mResizeBar[LLResizeBar::TOP] );
+
+ p.name("resizebar_right");
+ p.min_size(mMinWidth);
+ p.side(LLResizeBar::RIGHT);
+ mResizeBar[LLResizeBar::RIGHT] = LLUICtrlFactory::create<LLResizeBar>(p);
+ addChild( mResizeBar[LLResizeBar::RIGHT] );
+
+ p.name("resizebar_bottom");
+ p.min_size(mMinHeight);
+ p.side(LLResizeBar::BOTTOM);
+ mResizeBar[LLResizeBar::BOTTOM] = LLUICtrlFactory::create<LLResizeBar>(p);
+ addChild( mResizeBar[LLResizeBar::BOTTOM] );
+
+ // Resize handles (corners)
+ LLResizeHandle::Params handle_p;
+ // handles must not be mouse-opaque, otherwise they block hover events
+ // to other buttons like the close box. JC
+ handle_p.mouse_opaque(false);
+ handle_p.min_width(mMinWidth);
+ handle_p.min_height(mMinHeight);
+ handle_p.corner(LLResizeHandle::RIGHT_BOTTOM);
+ mResizeHandle[0] = LLUICtrlFactory::create<LLResizeHandle>(handle_p);
+ addChild(mResizeHandle[0]);
+
+ handle_p.corner(LLResizeHandle::RIGHT_TOP);
+ mResizeHandle[1] = LLUICtrlFactory::create<LLResizeHandle>(handle_p);
+ addChild(mResizeHandle[1]);
+
+ handle_p.corner(LLResizeHandle::LEFT_BOTTOM);
+ mResizeHandle[2] = LLUICtrlFactory::create<LLResizeHandle>(handle_p);
+ addChild(mResizeHandle[2]);
+
+ handle_p.corner(LLResizeHandle::LEFT_TOP);
+ mResizeHandle[3] = LLUICtrlFactory::create<LLResizeHandle>(handle_p);
+ addChild(mResizeHandle[3]);
+
+ layoutResizeCtrls();
+}
+
+void LLFloater::layoutResizeCtrls()
+{
+ LLRect rect;
+
+ // Resize bars (sides)
+ const S32 RESIZE_BAR_THICKNESS = 3;
+ rect = LLRect( 0, getRect().getHeight(), RESIZE_BAR_THICKNESS, 0);
+ mResizeBar[LLResizeBar::LEFT]->setRect(rect);
+
+ rect = LLRect( 0, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_BAR_THICKNESS);
+ mResizeBar[LLResizeBar::TOP]->setRect(rect);
+
+ rect = LLRect(getRect().getWidth() - RESIZE_BAR_THICKNESS, getRect().getHeight(), getRect().getWidth(), 0);
+ mResizeBar[LLResizeBar::RIGHT]->setRect(rect);
+
+ rect = LLRect(0, RESIZE_BAR_THICKNESS, getRect().getWidth(), 0);
+ mResizeBar[LLResizeBar::BOTTOM]->setRect(rect);
+
+ // Resize handles (corners)
+ rect = LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT, getRect().getWidth(), 0);
+ mResizeHandle[0]->setRect(rect);
+
+ rect = LLRect( getRect().getWidth() - RESIZE_HANDLE_WIDTH, getRect().getHeight(), getRect().getWidth(), getRect().getHeight() - RESIZE_HANDLE_HEIGHT);
+ mResizeHandle[1]->setRect(rect);
+
+ rect = LLRect( 0, RESIZE_HANDLE_HEIGHT, RESIZE_HANDLE_WIDTH, 0 );
+ mResizeHandle[2]->setRect(rect);
+
+ rect = LLRect( 0, getRect().getHeight(), RESIZE_HANDLE_WIDTH, getRect().getHeight() - RESIZE_HANDLE_HEIGHT );
+ mResizeHandle[3]->setRect(rect);
+}
+
+void LLFloater::enableResizeCtrls(bool enable, 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)
+ {
+ mResizeHandle[i]->setVisible(enable && width && height);
+ mResizeHandle[i]->setEnabled(enable && width && height);
+ }
+}
+
+void LLFloater::destroy()
+{
+ // LLFloaterReg should be synchronized with "dead" floater to avoid returning dead instance before
+ // it was deleted via LLMortician::updateClass(). See EXT-8458.
+ LLFloaterReg::removeInstance(mInstanceName, mKey);
+ die();
+}
+
+// virtual
+LLFloater::~LLFloater()
+{
+ if (!isDead())
+ {
+ // If it's dead, instance is supposed to be already removed, and
+ // in case of single instance we can remove new one by accident
+ LLFloaterReg::removeInstance(mInstanceName, mKey);
+ }
+
+ 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 );
+
+ delete mDragHandle;
+ for (S32 i = 0; i < 4; i++)
+ {
+ delete mResizeBar[i];
+ delete mResizeHandle[i];
+ }
+
+ setVisible(false); // We're not visible if we're destroyed
+ storeVisibilityControl();
+ storeDockStateControl();
+ delete mMinimizeSignal;
+}
+
+void LLFloater::storeRectControl()
+{
+ 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()
+{
+ if( !sQuitting && mVisibilityControl.size() > 1 )
+ {
+ getControlGroup()->setBOOL( mVisibilityControl, getVisible() );
+ }
+}
+
+void LLFloater::storeDockStateControl()
+{
+ if( !sQuitting && mDocStateControl.size() > 1 )
+ {
+ getControlGroup()->setBOOL( mDocStateControl, isDocked() );
+ }
+}
+
+// static
+std::string LLFloater::getControlName(const std::string& name, const LLSD& key)
+{
+ std::string ctrl_name = name;
+
+ // Add the key to the control name if appropriate.
+ if (key.isString() && !key.asString().empty())
+ {
+ ctrl_name += "_" + key.asString();
+ }
+
+ return ctrl_name;
+}
+
+// static
+LLControlGroup* LLFloater::getControlGroup()
+{
+ // Floater size, position, visibility, etc are saved in per-account settings.
+ return LLUI::getInstance()->mSettingGroups["account"];
+}
+
+void LLFloater::setVisible( bool visible )
+{
+ LLPanel::setVisible(visible); // calls onVisibilityChange()
+ if( visible && mFirstLook )
+ {
+ mFirstLook = false;
+ }
+
+ if( !visible )
+ {
+ LLUI::getInstance()->removePopup(this);
+
+ if( gFocusMgr.childHasMouseCapture( this ) )
+ {
+ gFocusMgr.setMouseCapture(NULL);
+ }
+ }
+
+ for(handle_set_iter_t dependent_it = mDependents.begin();
+ dependent_it != mDependents.end(); )
+ {
+ LLFloater* floaterp = dependent_it->get();
+
+ if (floaterp)
+ {
+ floaterp->setVisible(visible);
+ }
+ ++dependent_it;
+ }
+
+ 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::onVisibilityChange ( bool new_visibility )
+{
+ if (new_visibility)
+ {
+ if (getHost())
+ getHost()->setFloaterFlashing(this, false);
+ }
+ LLPanel::onVisibilityChange ( new_visibility );
+}
+
+void LLFloater::openFloater(const LLSD& key)
+{
+ 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
+ // don't play open sound for hosted (tabbed) windows
+ && !getHost()
+ && !getFloaterHost()
+ && (!getVisible() || isMinimized()))
+ {
+ make_ui_sound("UISndWindowOpen");
+ }
+
+ //RN: for now, we don't allow rehosting from one multifloater to another
+ // just need to fix the bugs
+ if (getFloaterHost() != NULL && getHost() == NULL)
+ {
+ // needs a host
+ // only select tabs if window they are hosted in is visible
+ getFloaterHost()->addFloater(this, getFloaterHost()->getVisible());
+ }
+
+ if (getHost() != NULL)
+ {
+ getHost()->setMinimized(false);
+ getHost()->setVisibleAndFrontmost(mAutoFocus && !getIsChrome());
+ getHost()->showFloater(this);
+ }
+ 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 && !getIsChrome());
+ }
+
+ mOpenSignal(this, key);
+ onOpen(key);
+
+ dirtyRect();
+}
+
+void LLFloater::closeFloater(bool app_quitting)
+{
+ 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;
+ }
+
+ // Always unminimize before trying to close.
+ // Most of the time the user will never see this state.
+ setMinimized(false);
+
+ if (canClose())
+ {
+ if (getHost())
+ {
+ ((LLMultiFloater*)getHost())->removeFloater(this);
+ gFloaterView->addChild(this);
+ }
+
+ if (getSoundFlags() != SILENT
+ && getVisible()
+ && !getHost()
+ && !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
+ while(mDependents.size() > 0)
+ {
+ handle_set_iter_t dependent_it = mDependents.begin();
+ LLFloater* floaterp = dependent_it->get();
+ // normally removeDependentFloater will do this, but in
+ // case floaterp is somehow invalid or orphaned, erase now
+ mDependents.erase(dependent_it);
+ if (floaterp)
+ {
+ floaterp->mDependeeHandle = LLHandle<LLFloater>();
+ floaterp->closeFloater(app_quitting);
+ }
+ }
+
+ cleanupHandles();
+
+ dirtyRect();
+
+ // Close callbacks
+ onClose(app_quitting);
+ mCloseSignal(this, LLSD(app_quitting));
+
+ // Hide or Destroy
+ if (mSingleInstance)
+ {
+ // Hide the instance
+ if (getHost())
+ {
+ getHost()->setVisible(false);
+ }
+ else
+ {
+ setVisible(false);
+ if (!mReuseInstance)
+ {
+ destroy();
+ }
+ }
+ }
+ else
+ {
+ 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);
+}
+
+// virtual
+void LLFloater::translate(S32 x, S32 y)
+{
+ LLView::translate(x, y);
+
+ if (!mTranslateWithDependents || mDependents.empty())
+ return;
+
+ for (const LLHandle<LLFloater>& handle : mDependents)
+ {
+ LLFloater* floater = handle.get();
+ if (floater && floater->getSnapTarget() == getHandle())
+ {
+ floater->LLView::translate(x, y);
+ }
+ }
+}
+
+void LLFloater::releaseFocus()
+{
+ LLUI::getInstance()->removePopup(this);
+
+ setFocus(false);
+
+ if( gFocusMgr.childHasMouseCapture( this ) )
+ {
+ gFocusMgr.setMouseCapture(NULL);
+ }
+}
+
+
+void LLFloater::setResizeLimits( S32 min_width, S32 min_height )
+{
+ mMinWidth = min_width;
+ mMinHeight = min_height;
+
+ for( S32 i = 0; i < 4; i++ )
+ {
+ if( mResizeBar[i] )
+ {
+ if (i == LLResizeBar::LEFT || i == LLResizeBar::RIGHT)
+ {
+ mResizeBar[i]->setResizeLimits( min_width, S32_MAX );
+ }
+ else
+ {
+ mResizeBar[i]->setResizeLimits( min_height, S32_MAX );
+ }
+ }
+ if( mResizeHandle[i] )
+ {
+ mResizeHandle[i]->setResizeLimits( min_width, min_height );
+ }
+ }
+}
+
+
+void LLFloater::center()
+{
+ if(getHost())
+ {
+ // hosted floaters can't move
+ return;
+ }
+ centerWithin(gFloaterView->getRect());
+}
+
+LLMultiFloater* LLFloater::getHost()
+{
+ return (LLMultiFloater*)mHostHandle.get();
+}
+
+void LLFloater::applyControlsAndPosition(LLFloater* other)
+{
+ if (!applyDockState())
+ {
+ if (!applyRectControl())
+ {
+ applyPositioning(other, true);
+ }
+ }
+}
+
+bool LLFloater::applyRectControl()
+{
+ 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)
+ {
+ // other floaters in our group, position ourselves relative to them and don't save the rect
+ if (mDefaultRectForGroup)
+ {
+ mRectControl.clear();
+ }
+ mPositioning = LLFloaterEnums::POSITIONING_CASCADE_GROUP;
+ }
+ else
+ {
+ bool rect_specified = false;
+ if (!mRectControl.empty())
+ {
+ // If we have a saved rect, use it
+ const LLRect& rect = getControlGroup()->getRect(mRectControl);
+ if (rect.notEmpty()) saved_rect = true;
+ if (saved_rect)
+ {
+ 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;
+ }
+ else if ((mDefaultRelativeX != 0) && (mDefaultRelativeY != 0))
+ {
+ mPosition.mX = mDefaultRelativeX;
+ mPosition.mY = mDefaultRelativeY;
+ 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;
+}
+
+bool LLFloater::applyDockState()
+{
+ bool docked = false;
+
+ if (mDocStateControl.size() > 1)
+ {
+ 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()
+{
+ if (!mDragHandle)
+ {
+ return;
+ }
+
+ if (isMinimized() && !mShortTitle.empty())
+ {
+ mDragHandle->setTitle( mShortTitle );
+ }
+ else
+ {
+ mDragHandle->setTitle ( mTitle );
+ }
+
+ if (getHost())
+ {
+ getHost()->updateFloaterTitle(this);
+ }
+}
+
+std::string LLFloater::getCurrentTitle() const
+{
+ return mDragHandle ? mDragHandle->getTitle() : LLStringUtil::null;
+}
+
+void LLFloater::setTitle( const std::string& title )
+{
+ mTitle = title;
+ applyTitle();
+}
+
+std::string LLFloater::getTitle() const
+{
+ if (mTitle.empty())
+ {
+ return mDragHandle ? mDragHandle->getTitle() : LLStringUtil::null;
+ }
+ else
+ {
+ return mTitle;
+ }
+}
+
+void LLFloater::setShortTitle( const std::string& short_title )
+{
+ mShortTitle = short_title;
+ applyTitle();
+}
+
+std::string LLFloater::getShortTitle() const
+{
+ if (mShortTitle.empty())
+ {
+ return mDragHandle ? mDragHandle->getTitle() : LLStringUtil::null;
+ }
+ else
+ {
+ return mShortTitle;
+ }
+}
+
+bool LLFloater::canSnapTo(const LLView* other_view)
+{
+ if (NULL == other_view)
+ {
+ 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())
+ {
+ // this is a dependent that is already snapped to us, so don't snap back to it
+ return false;
+ }
+ }
+
+ return LLPanel::canSnapTo(other_view);
+}
+
+void LLFloater::setSnappedTo(const LLView* snap_view)
+{
+ if (!snap_view || snap_view == getParent())
+ {
+ clearSnapTarget();
+ }
+ else
+ {
+ //RN: assume it's a floater as it must be a sibling to our parent floater
+ const LLFloater* floaterp = dynamic_cast<const LLFloater*>(snap_view);
+ if (floaterp)
+ {
+ setSnapTarget(floaterp->getHandle());
+ }
+ }
+}
+
+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);
+ }
+ mPositioning = LLFloaterEnums::POSITIONING_RELATIVE;
+ LLRect screen_rect = calcScreenRect();
+ mPosition = LLCoordGL(screen_rect.getCenterX(), screen_rect.getCenterY()).convert();
+ }
+ storeRectControl();
+
+ // gather all snapped dependents
+ for(handle_set_iter_t dependent_it = mDependents.begin();
+ dependent_it != mDependents.end(); ++dependent_it)
+ {
+ LLFloater* floaterp = dependent_it->get();
+ // is a dependent snapped to us?
+ if (floaterp && floaterp->getSnapTarget() == getHandle())
+ {
+ S32 delta_x = 0;
+ S32 delta_y = 0;
+ // check to see if it snapped to right or top, and move if dependee floater is resizing
+ LLRect dependent_rect = floaterp->getRect();
+ if (dependent_rect.mLeft - getRect().mLeft >= old_rect.getWidth() || // dependent on my right?
+ dependent_rect.mRight == getRect().mLeft + old_rect.getWidth()) // dependent aligned with my right
+ {
+ // was snapped directly onto right side or aligned with it
+ delta_x += new_rect.getWidth() - old_rect.getWidth();
+ }
+ if (dependent_rect.mBottom - getRect().mBottom >= old_rect.getHeight() ||
+ dependent_rect.mTop == getRect().mBottom + old_rect.getHeight())
+ {
+ // was snapped directly onto top side or aligned with it
+ delta_y += new_rect.getHeight() - old_rect.getHeight();
+ }
+
+ // take translation of dependee floater into account as well
+ delta_x += new_rect.mLeft - old_rect.mLeft;
+ delta_y += new_rect.mBottom - old_rect.mBottom;
+
+ dependent_rect.translate(delta_x, delta_y);
+ floaterp->setShape(dependent_rect, by_user);
+ }
+ }
+ }
+ else
+ {
+ // If minimized, and origin has changed, set
+ // mHasBeenDraggedWhileMinimized to true
+ if ((new_rect.mLeft != old_rect.mLeft) ||
+ (new_rect.mBottom != old_rect.mBottom))
+ {
+ mHasBeenDraggedWhileMinimized = true;
+ }
+ }
+}
+
+void LLFloater::setMinimized(bool minimize)
+{
+ const LLFloater::Params& default_params = LLFloater::getDefaultParams();
+ S32 floater_header_size = default_params.header_height;
+ static LLUICachedControl<S32> minimized_width ("UIMinimizedWidth", 0);
+
+ if (minimize == mMinimized) return;
+
+ if (mMinimizeSignal)
+ {
+ (*mMinimizeSignal)(this, LLSD(minimize));
+ }
+
+ if (minimize)
+ {
+ // minimized flag should be turned on before release focus
+ mMinimized = true;
+ mExpandedRect = getRect();
+
+ // If the floater has been dragged while minimized in the
+ // past, then locate it at its previous minimized location.
+ // Otherwise, ask the view for a minimize position.
+ if (mHasBeenDraggedWhileMinimized)
+ {
+ setOrigin(mPreviousMinimizedLeft, mPreviousMinimizedBottom);
+ }
+ else
+ {
+ S32 left, bottom;
+ gFloaterView->getMinimizePosition(&left, &bottom);
+ setOrigin( left, bottom );
+ }
+
+ if (mButtonsEnabled[BUTTON_MINIMIZE])
+ {
+ mButtonsEnabled[BUTTON_MINIMIZE] = false;
+ mButtonsEnabled[BUTTON_RESTORE] = true;
+ }
+
+ setBorderVisible(true);
+
+ for(handle_set_iter_t dependent_it = mDependents.begin();
+ dependent_it != mDependents.end();
+ ++dependent_it)
+ {
+ LLFloater* floaterp = dependent_it->get();
+ if (floaterp)
+ {
+ if (floaterp->isMinimizeable())
+ {
+ floaterp->setMinimized(true);
+ }
+ else if (!floaterp->isMinimized())
+ {
+ floaterp->setVisible(false);
+ }
+ }
+ }
+
+ // Lose keyboard focus when minimized
+ releaseFocus();
+
+ for (S32 i = 0; i < 4; i++)
+ {
+ if (mResizeBar[i] != NULL)
+ {
+ mResizeBar[i]->setEnabled(false);
+ }
+ if (mResizeHandle[i] != NULL)
+ {
+ mResizeHandle[i]->setEnabled(false);
+ }
+ }
+
+ // Reshape *after* setting mMinimized
+ reshape( minimized_width, floater_header_size, true);
+ }
+ else
+ {
+ // If this window has been dragged while minimized (at any time),
+ // remember its position for the next time it's minimized.
+ if (mHasBeenDraggedWhileMinimized)
+ {
+ const LLRect& currentRect = getRect();
+ mPreviousMinimizedLeft = currentRect.mLeft;
+ mPreviousMinimizedBottom = currentRect.mBottom;
+ }
+
+ setOrigin( mExpandedRect.mLeft, mExpandedRect.mBottom );
+ if (mButtonsEnabled[BUTTON_RESTORE])
+ {
+ mButtonsEnabled[BUTTON_MINIMIZE] = true;
+ mButtonsEnabled[BUTTON_RESTORE] = false;
+ }
+
+ // show dependent floater
+ for(handle_set_iter_t dependent_it = mDependents.begin();
+ dependent_it != mDependents.end();
+ ++dependent_it)
+ {
+ LLFloater* floaterp = dependent_it->get();
+ if (floaterp)
+ {
+ floaterp->setMinimized(false);
+ floaterp->setVisible(true);
+ }
+ }
+
+ for (S32 i = 0; i < 4; i++)
+ {
+ if (mResizeBar[i] != NULL)
+ {
+ mResizeBar[i]->setEnabled(isResizable());
+ }
+ if (mResizeHandle[i] != NULL)
+ {
+ mResizeHandle[i]->setEnabled(isResizable());
+ }
+ }
+
+ mMinimized = false;
+ setFrontmost();
+ // Reshape *after* setting mMinimized
+ reshape( mExpandedRect.getWidth(), mExpandedRect.getHeight(), true );
+ }
+
+ make_ui_sound("UISndWindowClose");
+ updateTitleButtons();
+ applyTitle ();
+}
+
+void LLFloater::setFocus( bool b )
+{
+ if (b && getIsChrome())
+ {
+ return;
+ }
+ LLView* last_focus = gFocusMgr.getLastFocusForGroup(this);
+ // a descendent already has focus
+ bool child_had_focus = hasFocus();
+
+ // give focus to first valid descendent
+ LLPanel::setFocus(b);
+
+ if (b)
+ {
+ // only push focused floaters to front of stack if not in midst of ctrl-tab cycle
+ LLFloaterView * parent = dynamic_cast<LLFloaterView *>(getParent());
+ if (!getHost() && parent && !parent->getCycleMode())
+ {
+ if (!isFrontmost())
+ {
+ setFrontmost();
+ }
+ }
+
+ // when getting focus, delegate to last descendent which had focus
+ if (last_focus && !child_had_focus &&
+ last_focus->isInEnabledChain() &&
+ last_focus->isInVisibleChain())
+ {
+ // *FIX: should handle case where focus doesn't stick
+ last_focus->setFocus(true);
+ }
+ }
+ updateTransparency(b ? TT_ACTIVE : TT_INACTIVE);
+}
+
+// virtual
+void LLFloater::setRect(const LLRect &rect)
+{
+ LLPanel::setRect(rect);
+ layoutDragHandle();
+ layoutResizeCtrls();
+}
+
+// virtual
+void LLFloater::setIsChrome(bool is_chrome)
+{
+ // chrome floaters don't take focus at all
+ if (is_chrome)
+ {
+ // remove focus if we're changing to chrome
+ setFocus(false);
+ // can't Ctrl-Tab to "chrome" floaters
+ setFocusRoot(false);
+ mButtons[BUTTON_CLOSE]->setToolTip(LLStringExplicit(getButtonTooltip(Params(), BUTTON_CLOSE, is_chrome)));
+ }
+
+ LLPanel::setIsChrome(is_chrome);
+}
+
+// Change the draw style to account for the foreground state.
+void LLFloater::setForeground(bool front)
+{
+ if (front != mForeground)
+ {
+ mForeground = front;
+ if (mDragHandle)
+ mDragHandle->setForeground( front );
+
+ if (!front)
+ {
+ releaseFocus();
+ }
+
+ setBackgroundOpaque( front );
+ }
+}
+
+void LLFloater::cleanupHandles()
+{
+ // remove handles to non-existent dependents
+ for(handle_set_iter_t dependent_it = mDependents.begin();
+ dependent_it != mDependents.end(); )
+ {
+ LLFloater* floaterp = dependent_it->get();
+ if (!floaterp)
+ {
+ dependent_it = mDependents.erase(dependent_it);
+ }
+ else
+ {
+ ++dependent_it;
+ }
+ }
+}
+
+void LLFloater::setHost(LLMultiFloater* host)
+{
+ if (mHostHandle.isDead() && host)
+ {
+ // make buttons smaller for hosted windows to differentiate from parent
+ mButtonScale = 0.9f;
+
+ // add tear off button
+ if (mCanTearOff)
+ {
+ mButtonsEnabled[BUTTON_TEAR_OFF] = true;
+ }
+ }
+ else if (!mHostHandle.isDead() && !host)
+ {
+ mButtonScale = 1.f;
+ //mButtonsEnabled[BUTTON_TEAR_OFF] = false;
+ }
+ if (host)
+ {
+ mHostHandle = host->getHandle();
+ mLastHostHandle = host->getHandle();
+ }
+ else
+ {
+ mHostHandle.markDead();
+ }
+
+ updateTitleButtons();
+}
+
+void LLFloater::moveResizeHandlesToFront()
+{
+ for( S32 i = 0; i < 4; i++ )
+ {
+ if( mResizeBar[i] )
+ {
+ sendChildToFront(mResizeBar[i]);
+ }
+ }
+
+ for( S32 i = 0; i < 4; i++ )
+ {
+ if( mResizeHandle[i] )
+ {
+ sendChildToFront(mResizeHandle[i]);
+ }
+ }
+}
+
+/*virtual*/
+bool LLFloater::isFrontmost()
+{
+ LLFloaterView* floater_view = getParentByType<LLFloaterView>();
+ return getVisible()
+ && (floater_view
+ && floater_view->getFrontmost() == this);
+}
+
+void LLFloater::addDependentFloater(LLFloater* floaterp, bool reposition, bool resize)
+{
+ mDependents.insert(floaterp->getHandle());
+ floaterp->mDependeeHandle = getHandle();
+
+ if (reposition)
+ {
+ LLRect rect = gFloaterView->findNeighboringPosition(this, floaterp);
+ if (resize)
+ {
+ const LLRect& base = getRect();
+ if (rect.mTop == base.mTop)
+ rect.mBottom = base.mBottom;
+ else if (rect.mLeft == base.mLeft)
+ rect.mRight = base.mRight;
+ floaterp->reshape(rect.getWidth(), rect.getHeight(), false);
+ }
+ floaterp->setRect(rect);
+ floaterp->setSnapTarget(getHandle());
+ }
+ gFloaterView->adjustToFitScreen(floaterp, false, true);
+ if (floaterp->isFrontmost())
+ {
+ // make sure to bring self and sibling floaters to front
+ gFloaterView->bringToFront(floaterp, floaterp->getAutoFocus() && !getIsChrome());
+ }
+}
+
+void LLFloater::addDependentFloater(LLHandle<LLFloater> dependent, bool reposition, bool resize)
+{
+ LLFloater* dependent_floaterp = dependent.get();
+ if(dependent_floaterp)
+ {
+ addDependentFloater(dependent_floaterp, reposition, resize);
+ }
+}
+
+void LLFloater::removeDependentFloater(LLFloater* floaterp)
+{
+ mDependents.erase(floaterp->getHandle());
+ floaterp->mDependeeHandle = LLHandle<LLFloater>();
+}
+
+void LLFloater::fitWithDependentsOnScreen(const LLRect& left, const LLRect& bottom, const LLRect& right, const LLRect& constraint, S32 min_overlap_pixels)
+{
+ LLRect total_rect = getRect();
+
+ for (const LLHandle<LLFloater>& handle : mDependents)
+ {
+ LLFloater* floater = handle.get();
+ if (floater && floater->getSnapTarget() == getHandle())
+ {
+ total_rect.unionWith(floater->getRect());
+ }
+ }
+
+ S32 delta_left = left.notEmpty() ? left.mRight - total_rect.mRight : 0;
+ S32 delta_bottom = bottom.notEmpty() ? bottom.mTop - total_rect.mTop : 0;
+ S32 delta_right = right.notEmpty() ? right.mLeft - total_rect.mLeft : 0;
+
+ // move floater with dependings fully onscreen
+ mTranslateWithDependents = true;
+ if (translateRectIntoRect(total_rect, constraint, min_overlap_pixels))
+ {
+ clearSnapTarget();
+ }
+ else if (delta_left > 0 && total_rect.mTop < left.mTop && total_rect.mBottom > left.mBottom)
+ {
+ translate(delta_left, 0);
+ }
+ else if (delta_bottom > 0 && total_rect.mLeft > bottom.mLeft && total_rect.mRight < bottom.mRight)
+ {
+ translate(0, delta_bottom);
+ }
+ else if (delta_right < 0 && total_rect.mTop < right.mTop && total_rect.mBottom > right.mBottom)
+ {
+ translate(delta_right, 0);
+ }
+ mTranslateWithDependents = false;
+}
+
+bool LLFloater::offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index)
+{
+ if( mButtonsEnabled[index] )
+ {
+ LLButton* my_butt = mButtons[index];
+ S32 local_x = x - my_butt->getRect().mLeft;
+ S32 local_y = y - my_butt->getRect().mBottom;
+
+ if (
+ my_butt->pointInView(local_x, local_y) &&
+ my_butt->handleMouseDown(local_x, local_y, mask))
+ {
+ // the button handled it
+ return true;
+ }
+ }
+ return false;
+}
+
+bool LLFloater::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ LLPanel::handleScrollWheel(x,y,clicks);
+ return true;//always
+}
+
+// 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 )
+ {
+ // Offer the click to titlebar buttons.
+ // Note: this block and the offerClickToButton helper method can be removed
+ // because the parent container will handle it for us but we'll keep it here
+ // for safety until after reworking the panel code to manage hidden children.
+ 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;
+
+ setFrontmost(true, false);
+ // Otherwise pass to drag handle for movement
+ return mDragHandle->handleMouseDown(x, y, mask);
+ }
+ else
+ {
+ bringToFront( x, y );
+ bool handled = LLPanel::handleMouseDown( x, y, mask );
+ if (handled) {
+ LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname());
+ }
+ return handled;
+ }
+}
+
+// virtual
+bool LLFloater::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ bool was_minimized = mMinimized;
+ bringToFront( x, y );
+ return was_minimized || LLPanel::handleRightMouseDown( x, y, mask );
+}
+
+bool LLFloater::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
+{
+ bringToFront( x, y );
+ return LLPanel::handleMiddleMouseDown( x, y, mask );
+}
+
+
+// virtual
+bool LLFloater::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ bool was_minimized = mMinimized;
+ setMinimized(false);
+ return was_minimized || LLPanel::handleDoubleClick(x, y, mask);
+}
+
+// virtual
+void LLFloater::bringToFront( S32 x, S32 y )
+{
+ if (getVisible() && pointInView(x, y))
+ {
+ LLMultiFloater* hostp = getHost();
+ if (hostp)
+ {
+ hostp->showFloater(this);
+ }
+ else
+ {
+ LLFloaterView* parent = dynamic_cast<LLFloaterView*>( getParent() );
+ if (parent)
+ {
+ parent->bringToFront(this, !getIsChrome());
+ }
+ }
+ }
+}
+
+// virtual
+void LLFloater::goneFromFront()
+{
+ if (mAutoClose)
+ {
+ closeFloater();
+ }
+}
+
+// virtual
+void LLFloater::setVisibleAndFrontmost(bool take_focus,const LLSD& key)
+{
+ LLUIUsage::instance().logFloater(getInstanceName());
+ LLMultiFloater* hostp = getHost();
+ if (hostp)
+ {
+ hostp->setVisible(true);
+ hostp->setFrontmost(take_focus);
+ }
+ else
+ {
+ setVisible(true);
+ setFrontmost(take_focus);
+ }
+}
+
+void LLFloater::setFrontmost(bool take_focus, bool restore)
+{
+ LLMultiFloater* hostp = getHost();
+ if (hostp)
+ {
+ // this will bring the host floater to the front and select
+ // the appropriate panel
+ hostp->showFloater(this);
+ }
+ else
+ {
+ // there are more than one floater view
+ // so we need to query our parent directly
+ LLFloaterView * parent = dynamic_cast<LLFloaterView*>( getParent() );
+ if (parent)
+ {
+ parent->bringToFront(this, take_focus, restore);
+ }
+
+ // Make sure to set the appropriate transparency type (STORM-732).
+ updateTransparency(hasFocus() || getIsChrome() ? TT_ACTIVE : TT_INACTIVE);
+ }
+}
+
+void LLFloater::setCanDock(bool b)
+{
+ if(b != mCanDock)
+ {
+ mCanDock = b;
+ if(mCanDock)
+ {
+ mButtonsEnabled[BUTTON_DOCK] = !mDocked;
+ }
+ else
+ {
+ mButtonsEnabled[BUTTON_DOCK] = false;
+ }
+ }
+ updateTitleButtons();
+}
+
+void LLFloater::setDocked(bool docked, bool pop_on_undock)
+{
+ if(docked != mDocked && mCanDock)
+ {
+ mDocked = docked;
+ mButtonsEnabled[BUTTON_DOCK] = !mDocked;
+
+ if (mDocked)
+ {
+ setMinimized(false);
+ mPositioning = LLFloaterEnums::POSITIONING_RELATIVE;
+ }
+
+ updateTitleButtons();
+
+ storeDockStateControl();
+ }
+
+}
+
+// static
+void LLFloater::onClickMinimize(LLFloater* self)
+{
+ if (!self)
+ return;
+ self->setMinimized( !self->isMinimized() );
+}
+
+void LLFloater::onClickTearOff(LLFloater* self)
+{
+ if (!self)
+ return;
+ S32 floater_header_size = self->mHeaderHeight;
+ LLMultiFloater* host_floater = self->getHost();
+ if (host_floater) //Tear off
+ {
+ LLRect new_rect;
+ host_floater->removeFloater(self);
+ // reparent to floater view
+ gFloaterView->addChild(self);
+
+ self->openFloater(self->getKey());
+ 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);
+ }
+ gFloaterView->adjustToFitScreen(self, false);
+ // give focus to new window to keep continuity for the user
+ self->setFocus(true);
+ self->setTornOff(true);
+ }
+ else //Attach to parent.
+ {
+ LLMultiFloater* new_host = (LLMultiFloater*)self->mLastHostHandle.get();
+ if (new_host)
+ {
+ if (self->mSaveRect)
+ {
+ LLRect screen_rect = self->calcScreenRect();
+ self->mPosition = LLCoordGL(screen_rect.getCenterX(), screen_rect.getCenterY()).convert();
+ self->storeRectControl();
+ }
+ self->setMinimized(false); // to reenable minimize button if it was minimized
+ new_host->showFloater(self);
+ // make sure host is visible
+ new_host->openFloater(new_host->getKey());
+ }
+ self->setTornOff(false);
+ }
+ self->updateTitleButtons();
+ self->setOpenPositioning(LLFloaterEnums::POSITIONING_RELATIVE);
+}
+
+// static
+void LLFloater::onClickDock(LLFloater* self)
+{
+ if(self && self->mCanDock)
+ {
+ self->setDocked(!self->mDocked, true);
+ }
+}
+
+// static
+void LLFloater::onClickHelp( LLFloater* self )
+{
+ if (self && LLUI::getInstance()->mHelpImpl)
+ {
+ // find the current help context for this floater
+ std::string help_topic;
+ if (self->findHelpTopic(help_topic))
+ {
+ LLUI::getInstance()->mHelpImpl->showTopic(help_topic);
+ }
+ }
+}
+
+void LLFloater::initRectControl()
+{
+ // save_rect and save_visibility only apply to registered floaters
+ if (mSaveRect)
+ {
+ std::string ctrl_name = getControlName(mInstanceName, mKey);
+ mRectControl = LLFloaterReg::declareRectControl(ctrl_name);
+ mPosXControl = LLFloaterReg::declarePosXControl(ctrl_name);
+ mPosYControl = LLFloaterReg::declarePosYControl(ctrl_name);
+ }
+}
+
+// static
+void LLFloater::closeFrontmostFloater()
+{
+ LLFloater* floater_to_close = gFloaterView->getFrontmostClosableFloater();
+ if(floater_to_close)
+ {
+ floater_to_close->closeFloater();
+ }
+
+ // if nothing took focus after closing focused floater
+ // give it to next floater (to allow closing multiple windows via keyboard in rapid succession)
+ if (gFocusMgr.getKeyboardFocus() == NULL)
+ {
+ // HACK: use gFloaterView directly in case we are using Ctrl-W to close snapshot window
+ // which sits in gSnapshotFloaterView, and needs to pass focus on to normal floater view
+ gFloaterView->focusFrontFloater();
+ }
+}
+
+
+// static
+void LLFloater::onClickClose( LLFloater* self )
+{
+ if (!self)
+ return;
+ self->onClickCloseBtn();
+}
+
+void LLFloater::onClickCloseBtn(bool app_quitting)
+{
+ closeFloater(false);
+}
+
+
+// virtual
+void LLFloater::draw()
+{
+ const F32 alpha = getCurrentTransparency();
+
+ // draw background
+ if( isBackgroundVisible() )
+ {
+ drawShadow(this);
+
+ S32 left = LLPANEL_BORDER_WIDTH;
+ S32 top = getRect().getHeight() - LLPANEL_BORDER_WIDTH;
+ S32 right = getRect().getWidth() - LLPANEL_BORDER_WIDTH;
+ S32 bottom = LLPANEL_BORDER_WIDTH;
+
+ LLUIImage* image = NULL;
+ LLColor4 color;
+ LLColor4 overlay_color;
+ if (isBackgroundOpaque())
+ {
+ // NOTE: image may not be set
+ image = getBackgroundImage();
+ color = getBackgroundColor();
+ overlay_color = getBackgroundImageOverlay();
+ }
+ else
+ {
+ image = getTransparentImage();
+ color = getTransparentColor();
+ overlay_color = getTransparentImageOverlay();
+ }
+
+ if (image)
+ {
+ // We're using images for this floater's backgrounds
+ image->draw(getLocalRect(), overlay_color % alpha);
+ }
+ else
+ {
+ // We're not using images, use old-school flat colors
+ gl_rect_2d( left, top, right, bottom, color % alpha );
+
+ // draw highlight on title bar to indicate focus. RDW
+ if(hasFocus()
+ && !getIsChrome()
+ && !getCurrentTitle().empty())
+ {
+ static LLUIColor titlebar_focus_color = LLUIColorTable::instance().getColor("TitleBarFocusColor");
+
+ const LLFontGL* font = LLFontGL::getFontSansSerif();
+ LLRect r = getRect();
+ gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - font->getLineHeight() - 1,
+ titlebar_focus_color % alpha, 0, true);
+ }
+ }
+ }
+
+ LLPanel::updateDefaultBtn();
+
+ if( getDefaultButton() )
+ {
+ if (hasFocus() && getDefaultButton()->getEnabled())
+ {
+ LLFocusableElement* focus_ctrl = gFocusMgr.getKeyboardFocus();
+ // is this button a direct descendent and not a nested widget (e.g. checkbox)?
+ bool focus_is_child_button = dynamic_cast<LLButton*>(focus_ctrl) != NULL && dynamic_cast<LLButton*>(focus_ctrl)->getParent() == this;
+ // only enable default button when current focus is not a button
+ getDefaultButton()->setBorderEnabled(!focus_is_child_button);
+ }
+ else
+ {
+ getDefaultButton()->setBorderEnabled(false);
+ }
+ }
+ if (isMinimized())
+ {
+ for (S32 i = 0; i < BUTTON_COUNT; i++)
+ {
+ drawChild(mButtons[i]);
+ }
+ drawChild(mDragHandle, 0, 0, true);
+ }
+ else
+ {
+ // don't call LLPanel::draw() since we've implemented custom background rendering
+ LLView::draw();
+ }
+
+ // update tearoff button for torn off floaters
+ // when last host goes away
+ if (mCanTearOff && !getHost())
+ {
+ LLFloater* old_host = mLastHostHandle.get();
+ if (!old_host)
+ {
+ setCanTearOff(false);
+ }
+ }
+}
+
+void LLFloater::drawShadow(LLPanel* panel)
+{
+ S32 left = LLPANEL_BORDER_WIDTH;
+ S32 top = panel->getRect().getHeight() - LLPANEL_BORDER_WIDTH;
+ S32 right = panel->getRect().getWidth() - LLPANEL_BORDER_WIDTH;
+ S32 bottom = LLPANEL_BORDER_WIDTH;
+
+ static LLUIColor shadow_color_cached = LLUIColorTable::instance().getColor("ColorDropShadow");
+ LLColor4 shadow_color = shadow_color_cached;
+ F32 shadow_offset = (F32)DROP_SHADOW_FLOATER;
+
+ if (!panel->isBackgroundOpaque())
+ {
+ shadow_offset *= 0.2f;
+ shadow_color.mV[VALPHA] *= 0.5f;
+ }
+ gl_drop_shadow(left, top, right, bottom,
+ shadow_color % getCurrentTransparency(),
+ 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();
+
+ LLUICtrl* ctrl = dynamic_cast<LLUICtrl*>(view);
+ if (ctrl)
+ {
+ ctrl->setTransparencyType(transparency_type);
+ }
+
+ for(; it != children.end(); ++it)
+ {
+ updateTransparency(*it, transparency_type);
+ }
+}
+
+void LLFloater::updateTransparency(ETypeTransparency transparency_type)
+{
+ updateTransparency(this, transparency_type);
+}
+
+void LLFloater::setCanMinimize(bool can_minimize)
+{
+ // if removing minimize/restore button programmatically,
+ // go ahead and unminimize floater
+ mCanMinimize = can_minimize;
+ if (!can_minimize)
+ {
+ setMinimized(false);
+ }
+
+ mButtonsEnabled[BUTTON_MINIMIZE] = can_minimize && !isMinimized();
+ mButtonsEnabled[BUTTON_RESTORE] = can_minimize && isMinimized();
+
+ updateTitleButtons();
+}
+
+void LLFloater::setCanClose(bool can_close)
+{
+ mCanClose = can_close;
+ mButtonsEnabled[BUTTON_CLOSE] = can_close;
+
+ updateTitleButtons();
+}
+
+void LLFloater::setCanTearOff(bool can_tear_off)
+{
+ mCanTearOff = can_tear_off;
+ mButtonsEnabled[BUTTON_TEAR_OFF] = mCanTearOff && !mHostHandle.isDead();
+
+ updateTitleButtons();
+}
+
+
+void LLFloater::setCanResize(bool can_resize)
+{
+ mResizable = can_resize;
+ enableResizeCtrls(can_resize);
+}
+
+void LLFloater::setCanDrag(bool can_drag)
+{
+ // if we delete drag handle, we no longer have access to the floater's title
+ // so just enable/disable it
+ if (!can_drag && mDragHandle->getEnabled())
+ {
+ mDragHandle->setEnabled(false);
+ }
+ else if (can_drag && !mDragHandle->getEnabled())
+ {
+ mDragHandle->setEnabled(true);
+ }
+}
+
+bool LLFloater::getCanDrag()
+{
+ return mDragHandle->getEnabled();
+}
+
+
+void LLFloater::updateTitleButtons()
+{
+ static LLUICachedControl<S32> floater_close_box_size ("UIFloaterCloseBoxSize", 0);
+ static LLUICachedControl<S32> close_box_from_top ("UICloseBoxFromTop", 0);
+ LLRect buttons_rect;
+ S32 button_count = 0;
+ for (S32 i = 0; i < BUTTON_COUNT; i++)
+ {
+ if (!mButtons[i])
+ {
+ continue;
+ }
+
+ bool enabled = mButtonsEnabled[i];
+ if (i == BUTTON_HELP)
+ {
+ // don't show the help button if the floater is minimized
+ // or if it is a docked tear-off floater
+ if (isMinimized() || (mButtonsEnabled[BUTTON_TEAR_OFF] && ! mTornOff))
+ {
+ enabled = false;
+ }
+ }
+ if (i == BUTTON_CLOSE && mButtonScale != 1.f)
+ {
+ //*HACK: always render close button for hosted floaters so
+ //that users don't accidentally hit the button when
+ //closing multiple windows in the chatterbox
+ enabled = true;
+ }
+
+ mButtons[i]->setEnabled(enabled);
+
+ if (enabled)
+ {
+ button_count++;
+
+ LLRect btn_rect;
+ if (mDragOnLeft)
+ {
+ btn_rect.setLeftTopAndSize(
+ LLPANEL_BORDER_WIDTH,
+ getRect().getHeight() - close_box_from_top - (floater_close_box_size + 1) * button_count,
+ 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,
+ ll_round((F32)floater_close_box_size * mButtonScale),
+ ll_round((F32)floater_close_box_size * mButtonScale));
+ }
+
+ // first time here, init 'buttons_rect'
+ if(1 == button_count)
+ {
+ buttons_rect = btn_rect;
+ }
+ else
+ {
+ // if mDragOnLeft=true then buttons are on top-left side vertically aligned
+ // title is not displayed in this case, calculating 'buttons_rect' for future use
+ mDragOnLeft ? buttons_rect.mBottom -= btn_rect.mBottom :
+ buttons_rect.mLeft = btn_rect.mLeft;
+ }
+ mButtons[i]->setRect(btn_rect);
+ mButtons[i]->setVisible(true);
+ // the restore button should have a tab stop so that it takes action when you Ctrl-Tab to a minimized floater
+ mButtons[i]->setTabStop(i == BUTTON_RESTORE);
+ }
+ else
+ {
+ mButtons[i]->setVisible(false);
+ }
+ }
+ if (mDragHandle)
+ {
+ localRectToOtherView(buttons_rect, &buttons_rect, mDragHandle);
+ mDragHandle->setButtonsRect(buttons_rect);
+ }
+}
+
+void LLFloater::drawConeToOwner(F32 &context_cone_opacity,
+ F32 max_cone_opacity,
+ LLView *owner_view,
+ F32 fade_time,
+ F32 contex_cone_in_alpha,
+ F32 contex_cone_out_alpha)
+{
+ if (owner_view
+ && owner_view->isInVisibleChain()
+ && hasFocus()
+ && context_cone_opacity > 0.001f
+ && gFocusMgr.childHasKeyboardFocus(this))
+ {
+ // draw cone of context pointing back to owner (e.x. texture swatch)
+ LLRect owner_rect;
+ owner_view->localRectToOtherView(owner_view->getLocalRect(), &owner_rect, this);
+ LLRect local_rect = getLocalRect();
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ LLGLEnable(GL_CULL_FACE);
+ gGL.begin(LLRender::QUADS);
+ {
+ gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity);
+ gGL.vertex2i(owner_rect.mLeft, owner_rect.mTop);
+ gGL.vertex2i(owner_rect.mRight, owner_rect.mTop);
+ gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity);
+ gGL.vertex2i(local_rect.mRight, local_rect.mTop);
+ gGL.vertex2i(local_rect.mLeft, local_rect.mTop);
+
+ gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity);
+ gGL.vertex2i(local_rect.mLeft, local_rect.mTop);
+ gGL.vertex2i(local_rect.mLeft, local_rect.mBottom);
+ gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity);
+ gGL.vertex2i(owner_rect.mLeft, owner_rect.mBottom);
+ gGL.vertex2i(owner_rect.mLeft, owner_rect.mTop);
+
+ gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity);
+ gGL.vertex2i(local_rect.mRight, local_rect.mBottom);
+ gGL.vertex2i(local_rect.mRight, local_rect.mTop);
+ gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity);
+ gGL.vertex2i(owner_rect.mRight, owner_rect.mTop);
+ gGL.vertex2i(owner_rect.mRight, owner_rect.mBottom);
+
+
+ gGL.color4f(0.f, 0.f, 0.f, contex_cone_out_alpha * context_cone_opacity);
+ gGL.vertex2i(local_rect.mLeft, local_rect.mBottom);
+ gGL.vertex2i(local_rect.mRight, local_rect.mBottom);
+ gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity);
+ gGL.vertex2i(owner_rect.mRight, owner_rect.mBottom);
+ gGL.vertex2i(owner_rect.mLeft, owner_rect.mBottom);
+ }
+ gGL.end();
+ }
+
+ if (gFocusMgr.childHasMouseCapture(getDragHandle()))
+ {
+ context_cone_opacity = lerp(context_cone_opacity, max_cone_opacity, LLSmoothInterpolation::getInterpolant(fade_time));
+ }
+ else
+ {
+ context_cone_opacity = lerp(context_cone_opacity, 0.f, LLSmoothInterpolation::getInterpolant(fade_time));
+ }
+}
+
+void LLFloater::buildButtons(const Params& floater_params)
+{
+ static LLUICachedControl<S32> floater_close_box_size ("UIFloaterCloseBoxSize", 0);
+ static LLUICachedControl<S32> close_box_from_top ("UICloseBoxFromTop", 0);
+ for (S32 i = 0; i < BUTTON_COUNT; i++)
+ {
+ if (mButtons[i])
+ {
+ removeChild(mButtons[i]);
+ delete mButtons[i];
+ mButtons[i] = NULL;
+ }
+
+ LLRect btn_rect;
+ if (mDragOnLeft)
+ {
+ btn_rect.setLeftTopAndSize(
+ LLPANEL_BORDER_WIDTH,
+ getRect().getHeight() - close_box_from_top - (floater_close_box_size + 1) * (i + 1),
+ 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,
+ ll_round(floater_close_box_size * mButtonScale),
+ ll_round(floater_close_box_size * mButtonScale));
+ }
+
+ LLButton::Params p;
+ p.name(sButtonNames[i]);
+ p.rect(btn_rect);
+ p.image_unselected = getButtonImage(floater_params, (EFloaterButton)i);
+ // Selected, no matter if hovered or not, is "pressed"
+ LLUIImage* pressed_image = getButtonPressedImage(floater_params, (EFloaterButton)i);
+ p.image_selected = pressed_image;
+ p.image_hover_selected = pressed_image;
+ // Use a glow effect when the user hovers over the button
+ // These icons are really small, need glow amount increased
+ p.hover_glow_amount( 0.33f );
+ p.click_callback.function(boost::bind(sButtonCallbacks[i], this));
+ p.tab_stop(false);
+ p.follows.flags(FOLLOWS_TOP|FOLLOWS_RIGHT);
+ p.tool_tip = getButtonTooltip(floater_params, (EFloaterButton)i, getIsChrome());
+ p.scale_image(true);
+ p.chrome(true);
+
+ LLButton* buttonp = LLUICtrlFactory::create<LLButton>(p);
+ addChild(buttonp);
+ mButtons[i] = buttonp;
+ }
+
+ updateTitleButtons();
+}
+
+// static
+LLUIImage* LLFloater::getButtonImage(const Params& p, EFloaterButton e)
+{
+ switch(e)
+ {
+ default:
+ case BUTTON_CLOSE:
+ return p.close_image;
+ case BUTTON_RESTORE:
+ return p.restore_image;
+ case BUTTON_MINIMIZE:
+ return p.minimize_image;
+ case BUTTON_TEAR_OFF:
+ return p.tear_off_image;
+ case BUTTON_DOCK:
+ return p.dock_image;
+ case BUTTON_HELP:
+ return p.help_image;
+ }
+}
+
+// static
+LLUIImage* LLFloater::getButtonPressedImage(const Params& p, EFloaterButton e)
+{
+ switch(e)
+ {
+ default:
+ case BUTTON_CLOSE:
+ return p.close_pressed_image;
+ case BUTTON_RESTORE:
+ return p.restore_pressed_image;
+ case BUTTON_MINIMIZE:
+ return p.minimize_pressed_image;
+ case BUTTON_TEAR_OFF:
+ return p.tear_off_pressed_image;
+ case BUTTON_DOCK:
+ return p.dock_pressed_image;
+ case BUTTON_HELP:
+ return p.help_pressed_image;
+ }
+}
+
+// static
+std::string LLFloater::getButtonTooltip(const Params& p, EFloaterButton e, bool is_chrome)
+{
+ // EXT-4081 (Lag Meter: Ctrl+W does not close floater)
+ // If floater is chrome set 'Close' text for close button's tooltip
+ if(is_chrome && BUTTON_CLOSE == e)
+ {
+ static std::string close_tooltip_chrome = LLTrans::getString("BUTTON_CLOSE_CHROME");
+ return close_tooltip_chrome;
+ }
+ // TODO: per-floater localizable tooltips set in XML
+ return sButtonToolTips[e];
+}
+
+/////////////////////////////////////////////////////
+// LLFloaterView
+
+static LLDefaultChildRegistry::Register<LLFloaterView> r("floater_view");
+
+LLFloaterView::LLFloaterView (const Params& p)
+: LLUICtrl (p),
+ mFocusCycleMode(false),
+ mMinimizePositionVOffset(0),
+ mSnapOffsetBottom(0),
+ mSnapOffsetRight(0)
+{
+ mSnapView = getHandle();
+}
+
+// By default, adjust vertical.
+void LLFloaterView::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ LLView::reshape(width, height, called_from_parent);
+
+ mLastSnapRect = getSnapRect();
+
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ LLFloater* floaterp = dynamic_cast<LLFloater*>(viewp);
+ if (floaterp->isDependent())
+ {
+ // dependents are moved with their "dependee"
+ continue;
+ }
+
+ if (!floaterp->isMinimized() && floaterp->getCanDrag())
+ {
+ LLRect old_rect = floaterp->getRect();
+ floaterp->applyPositioning(NULL, false);
+ LLRect new_rect = floaterp->getRect();
+
+ //LLRect r = floaterp->getRect();
+
+ //// 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);
+
+ S32 translate_x = new_rect.mLeft - old_rect.mLeft;
+ S32 translate_y = new_rect.mBottom - old_rect.mBottom;
+
+ //if (left_offset > right_offset)
+ //{
+ // translate_x = new_right - old_right;
+ //}
+
+ //if (top_offset < bottom_offset)
+ //{
+ // translate_y = new_top - old_top;
+ //}
+
+ // don't reposition immovable floaters
+ //if (floaterp->getCanDrag())
+ //{
+ // floaterp->translate(translate_x, translate_y);
+ //}
+ for (LLHandle<LLFloater> dependent_floater : floaterp->mDependents)
+ {
+ if (dependent_floater.get())
+ {
+ dependent_floater.get()->translate(translate_x, translate_y);
+ }
+ }
+ }
+ }
+}
+
+
+void LLFloaterView::restoreAll()
+{
+ // make sure all subwindows aren't minimized
+ child_list_t child_list = *(getChildList());
+ for (LLView* child : child_list)
+ {
+ LLFloater* floaterp = dynamic_cast<LLFloater*>(child);
+ if (floaterp)
+ {
+ floaterp->setMinimized(false);
+ }
+ }
+
+ // *FIX: make sure dependents are restored
+
+ // children then deleted by default view constructor
+}
+
+
+LLRect LLFloaterView::findNeighboringPosition( LLFloater* reference_floater, LLFloater* neighbor )
+{
+ LLRect base_rect = reference_floater->getRect();
+ LLRect::tCoordType width = neighbor->getRect().getWidth();
+ LLRect::tCoordType height = neighbor->getRect().getHeight();
+ LLRect new_rect = neighbor->getRect();
+
+ LLRect expanded_base_rect = base_rect;
+ expanded_base_rect.stretch(10);
+ for(LLFloater::handle_set_iter_t dependent_it = reference_floater->mDependents.begin();
+ dependent_it != reference_floater->mDependents.end(); ++dependent_it)
+ {
+ LLFloater* sibling = dependent_it->get();
+ // check for dependents within 10 pixels of base floater
+ if (sibling &&
+ sibling != neighbor &&
+ sibling->getVisible() &&
+ expanded_base_rect.overlaps(sibling->getRect()))
+ {
+ base_rect.unionWith(sibling->getRect());
+ }
+ }
+
+ LLRect::tCoordType left_margin = llmax(0, base_rect.mLeft);
+ LLRect::tCoordType right_margin = llmax(0, getRect().getWidth() - base_rect.mRight);
+ LLRect::tCoordType top_margin = llmax(0, getRect().getHeight() - base_rect.mTop);
+ LLRect::tCoordType bottom_margin = llmax(0, base_rect.mBottom);
+
+ // find position for floater in following order
+ // right->left->bottom->top
+ for (S32 i = 0; i < 5; i++)
+ {
+ if (right_margin > width)
+ {
+ new_rect.translate(base_rect.mRight - neighbor->getRect().mLeft, base_rect.mTop - neighbor->getRect().mTop);
+ return new_rect;
+ }
+ else if (left_margin > width)
+ {
+ new_rect.translate(base_rect.mLeft - neighbor->getRect().mRight, base_rect.mTop - neighbor->getRect().mTop);
+ return new_rect;
+ }
+ else if (bottom_margin > height)
+ {
+ new_rect.translate(base_rect.mLeft - neighbor->getRect().mLeft, base_rect.mBottom - neighbor->getRect().mTop);
+ return new_rect;
+ }
+ else if (top_margin > height)
+ {
+ new_rect.translate(base_rect.mLeft - neighbor->getRect().mLeft, base_rect.mTop - neighbor->getRect().mBottom);
+ return new_rect;
+ }
+
+ // keep growing margins to find "best" fit
+ left_margin += 20;
+ right_margin += 20;
+ top_margin += 20;
+ bottom_margin += 20;
+ }
+
+ // didn't find anything, return initial rect
+ return new_rect;
+}
+
+
+void LLFloaterView::bringToFront(LLFloater* child, bool give_focus, bool restore)
+{
+ if (!child)
+ return;
+
+ LLFloater* front_child = mFrontChildHandle.get();
+ if (front_child == child)
+ {
+ if (give_focus && child->canFocusStealFrontmost() && !gFocusMgr.childHasKeyboardFocus(child))
+ {
+ child->setFocus(true);
+ }
+ return;
+ }
+
+ if (front_child && front_child->getVisible())
+ {
+ front_child->goneFromFront();
+ }
+
+ mFrontChildHandle = child->getHandle();
+
+ // *TODO: make this respect floater's mAutoFocus value, instead of
+ // using parameter
+ if (child->getHost())
+ {
+ // this floater is hosted elsewhere and hence not one of our children, abort
+ return;
+ }
+ std::vector<LLFloater*> floaters_to_move;
+ // Look at all floaters...tab
+ for (child_list_const_iter_t child_it = beginChild(); child_it != endChild(); ++child_it)
+ {
+ LLFloater* floater = dynamic_cast<LLFloater*>(*child_it);
+
+ // ...but if I'm a dependent floater...
+ 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());
+
+ 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(); ++dependent_it)
+ {
+ LLFloater* sibling = dependent_it->get();
+ if (sibling)
+ {
+ floaters_to_move.push_back(sibling);
+ }
+ }
+ //...before bringing my parent to the front...
+ floaters_to_move.push_back(floater);
+ }
+ }
+ }
+
+ std::vector<LLFloater*>::iterator floater_it;
+ for(floater_it = floaters_to_move.begin(); floater_it != floaters_to_move.end(); ++floater_it)
+ {
+ LLFloater* floaterp = *floater_it;
+ sendChildToFront(floaterp);
+
+ // always unminimize dependee, but allow dependents to stay minimized
+ if (!floaterp->isDependent())
+ {
+ floaterp->setMinimized(false);
+ }
+ }
+ 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(); ++dependent_it)
+ {
+ LLFloater* dependent = dependent_it->get();
+ if (dependent)
+ {
+ sendChildToFront(dependent);
+ }
+ }
+
+ // ...and finally bringing myself to front
+ // (do this last, so that I'm left in front at end of this call)
+ if (*beginChild() != child)
+ {
+ sendChildToFront(child);
+ }
+
+ if(restore)
+ {
+ child->setMinimized(false);
+ }
+
+ if (give_focus && !gFocusMgr.childHasKeyboardFocus(child))
+ {
+ child->setFocus(true);
+ // floater did not take focus, so relinquish focus to world
+ if (!child->hasFocus())
+ {
+ gFocusMgr.setKeyboardFocus(NULL);
+ }
+ }
+}
+
+void LLFloaterView::highlightFocusedFloater()
+{
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLFloater *floater = (LLFloater *)(*child_it);
+
+ // skip dependent floaters, as we'll handle them in a batch along with their dependee(?)
+ if (floater->isDependent())
+ {
+ continue;
+ }
+
+ bool floater_or_dependent_has_focus = gFocusMgr.childHasKeyboardFocus(floater);
+ for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin();
+ dependent_it != floater->mDependents.end();
+ ++dependent_it)
+ {
+ LLFloater* dependent_floaterp = dependent_it->get();
+ if (dependent_floaterp && gFocusMgr.childHasKeyboardFocus(dependent_floaterp))
+ {
+ floater_or_dependent_has_focus = true;
+ }
+ }
+
+ // now set this floater and all its dependents
+ floater->setForeground(floater_or_dependent_has_focus);
+
+ for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin();
+ dependent_it != floater->mDependents.end(); )
+ {
+ LLFloater* dependent_floaterp = dependent_it->get();
+ if (dependent_floaterp)
+ {
+ dependent_floaterp->setForeground(floater_or_dependent_has_focus);
+ }
+ ++dependent_it;
+ }
+
+ floater->cleanupHandles();
+ }
+}
+
+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)
+ {
+ LLFloater *floater = (LLFloater *)(*child_it);
+
+ floater->setForeground(false);
+ }
+}
+
+void LLFloaterView::focusFrontFloater()
+{
+ LLFloater* floaterp = getFrontmost();
+ if (floaterp)
+ {
+ floaterp->setFocus(true);
+ }
+}
+
+void LLFloaterView::getMinimizePosition(S32 *left, S32 *bottom)
+{
+ const LLFloater::Params& default_params = LLFloater::getDefaultParams();
+ S32 floater_header_size = default_params.header_height;
+ static LLUICachedControl<S32> minimized_width ("UIMinimizedWidth", 0);
+ LLRect snap_rect_local = getLocalSnapRect();
+ snap_rect_local.mTop += mMinimizePositionVOffset;
+ for(S32 col = snap_rect_local.mLeft;
+ col < snap_rect_local.getWidth() - minimized_width;
+ col += minimized_width)
+ {
+ for(S32 row = snap_rect_local.mTop - floater_header_size;
+ row > floater_header_size;
+ row -= floater_header_size ) //loop rows
+ {
+
+ bool foundGap = true;
+ for(child_list_const_iter_t child_it = getChildList()->begin();
+ child_it != getChildList()->end();
+ ++child_it) //loop floaters
+ {
+ // Examine minimized children.
+ LLFloater* floater = dynamic_cast<LLFloater*>(*child_it);
+ if(floater->isMinimized())
+ {
+ LLRect r = floater->getRect();
+ if((r.mBottom < (row + floater_header_size))
+ && (r.mBottom > (row - floater_header_size))
+ && (r.mLeft < (col + minimized_width))
+ && (r.mLeft > (col - minimized_width)))
+ {
+ // needs the check for off grid. can't drag,
+ // but window resize makes them off
+ foundGap = false;
+ break;
+ }
+ }
+ } //done floaters
+ if(foundGap)
+ {
+ *left = col;
+ *bottom = row;
+ return; //done
+ }
+ } //done this col
+ }
+
+ // crude - stack'em all at 0,0 when screen is full of minimized
+ // floaters.
+ *left = snap_rect_local.mLeft;
+ *bottom = snap_rect_local.mBottom;
+}
+
+
+void LLFloaterView::destroyAllChildren()
+{
+ LLView::deleteAllChildren();
+}
+
+void LLFloaterView::closeAllChildren(bool app_quitting)
+{
+ // iterate over a copy of the list, because closing windows will destroy
+ // some windows on the list.
+ child_list_t child_list = *(getChildList());
+
+ for (child_list_const_iter_t it = child_list.begin(); it != child_list.end(); ++it)
+ {
+ LLView* viewp = *it;
+ child_list_const_iter_t exists = std::find(getChildList()->begin(), getChildList()->end(), viewp);
+ if (exists == getChildList()->end())
+ {
+ // this floater has already been removed
+ continue;
+ }
+
+ LLFloater* floaterp = dynamic_cast<LLFloater*>(viewp);
+
+ // Attempt to close floater. This will cause the "do you want to save"
+ // dialogs to appear.
+ // Skip invisible floaters if we're not quitting (STORM-192).
+ if (floaterp->canClose() && !floaterp->isDead() &&
+ (app_quitting || floaterp->getVisible()))
+ {
+ floaterp->closeFloater(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()
+{
+ // see if there are any visible floaters (some floaters "close"
+ // by setting themselves invisible)
+ for (child_list_const_iter_t it = getChildList()->begin(); it != getChildList()->end(); ++it)
+ {
+ LLFloater* floaterp = dynamic_cast<LLFloater*>(*it);
+
+ if (floaterp->getVisible() && !floaterp->isDead() && floaterp->isCloseable())
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+void LLFloaterView::shiftFloaters(S32 x_offset, S32 y_offset)
+{
+ for (child_list_const_iter_t it = getChildList()->begin(); it != getChildList()->end(); ++it)
+ {
+ LLFloater* floaterp = dynamic_cast<LLFloater*>(*it);
+
+ if (floaterp && floaterp->isMinimized())
+ {
+ floaterp->translate(x_offset, y_offset);
+ }
+ }
+}
+
+void LLFloaterView::refresh()
+{
+ 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)
+ {
+ LLFloater* floaterp = dynamic_cast<LLFloater*>(*child_it);
+ if (floaterp && floaterp->getVisible() )
+ {
+ // minimized floaters are kept fully onscreen
+ adjustToFitScreen(floaterp, !floaterp->isMinimized());
+ }
+ }
+}
+
+void LLFloaterView::adjustToFitScreen(LLFloater* floater, bool allow_partial_outside, bool snap_in_toolbars/* = false*/)
+{
+ if (floater->getParent() != this)
+ {
+ // floater is hosted elsewhere, so ignore
+ return;
+ }
+
+ if (floater->getDependee() &&
+ floater->getDependee() == floater->getSnapTarget().get())
+ {
+ // floater depends on other and snaps to it, so ignore
+ return;
+ }
+
+ LLRect::tCoordType screen_width = getSnapRect().getWidth();
+ LLRect::tCoordType screen_height = getSnapRect().getHeight();
+
+ // only automatically resize non-minimized, resizable floaters
+ if( floater->isResizable() && !floater->isMinimized() )
+ {
+ LLRect view_rect = floater->getRect();
+ S32 old_width = view_rect.getWidth();
+ S32 old_height = view_rect.getHeight();
+ S32 min_width;
+ S32 min_height;
+ floater->getResizeLimits( &min_width, &min_height );
+
+ // Make sure floater isn't already smaller than its min height/width?
+ S32 new_width = llmax( min_width, old_width );
+ S32 new_height = llmax( min_height, old_height);
+
+ if((new_width > screen_width) || (new_height > screen_height))
+ {
+ // We have to make this window able to fit on screen
+ new_width = llmin(new_width, screen_width);
+ new_height = llmin(new_height, screen_height);
+
+ // ...while respecting minimum width/height
+ new_width = llmax(new_width, min_width);
+ new_height = llmax(new_height, min_height);
+
+ LLRect new_rect;
+ new_rect.setLeftTopAndSize(view_rect.mLeft,view_rect.mTop,new_width, new_height);
+
+ floater->setShape(new_rect);
+
+ if (floater->followsRight())
+ {
+ floater->translate(old_width - new_width, 0);
+ }
+
+ if (floater->followsTop())
+ {
+ floater->translate(0, old_height - new_height);
+ }
+ }
+ }
+
+ const LLRect& constraint = snap_in_toolbars ? getSnapRect() : gFloaterView->getRect();
+ S32 min_overlap_pixels = allow_partial_outside ? FLOATER_MIN_VISIBLE_PIXELS : S32_MAX;
+
+ floater->fitWithDependentsOnScreen(mToolbarLeftRect, mToolbarBottomRect, mToolbarRightRect, constraint, min_overlap_pixels);
+}
+
+void LLFloaterView::draw()
+{
+ refresh();
+
+ // hide focused floater if in cycle mode, so that it can be drawn on top
+ LLFloater* focused_floater = getFocusedFloater();
+
+ if (mFocusCycleMode && focused_floater)
+ {
+ child_list_const_iter_t child_it = getChildList()->begin();
+ for (;child_it != getChildList()->end(); ++child_it)
+ {
+ if ((*child_it) != focused_floater)
+ {
+ drawChild(*child_it);
+ }
+ }
+
+ drawChild(focused_floater, -TABBED_FLOATER_OFFSET, TABBED_FLOATER_OFFSET);
+ }
+ else
+ {
+ LLView::draw();
+ }
+}
+
+LLRect LLFloaterView::getSnapRect() const
+{
+ LLRect snap_rect = getLocalRect();
+
+ LLView* snap_view = mSnapView.get();
+ if (snap_view)
+ {
+ snap_view->localRectToOtherView(snap_view->getLocalRect(), &snap_rect, this);
+ }
+
+ return snap_rect;
+}
+
+LLFloater *LLFloaterView::getFocusedFloater() const
+{
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ if ((*child_it)->isCtrl())
+ {
+ LLFloater* ctrlp = dynamic_cast<LLFloater*>(*child_it);
+ if ( ctrlp && ctrlp->hasFocus() )
+ {
+ return ctrlp;
+ }
+ }
+ }
+ return NULL;
+}
+
+LLFloater *LLFloaterView::getFrontmost() const
+{
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if ( viewp->getVisible() && !viewp->isDead())
+ {
+ return (LLFloater *)viewp;
+ }
+ }
+ return NULL;
+}
+
+LLFloater *LLFloaterView::getBackmost() const
+{
+ LLFloater* back_most = NULL;
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if ( viewp->getVisible() )
+ {
+ back_most = (LLFloater *)viewp;
+ }
+ }
+ return back_most;
+}
+
+void LLFloaterView::syncFloaterTabOrder()
+{
+ LLFloater* front_child = mFrontChildHandle.get();
+ if (front_child && front_child->getIsChrome())
+ return;
+
+ // look for a visible modal dialog, starting from first
+ LLModalDialog* modal_dialog = NULL;
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLModalDialog* dialog = dynamic_cast<LLModalDialog*>(*child_it);
+ if (dialog && dialog->isModal() && dialog->getVisible())
+ {
+ modal_dialog = dialog;
+ break;
+ }
+ }
+
+ if (modal_dialog)
+ {
+ // If we have a visible modal dialog, make sure that it has focus
+ LLUI::getInstance()->addPopup(modal_dialog);
+
+ if( !gFocusMgr.childHasKeyboardFocus( modal_dialog ) )
+ {
+ modal_dialog->setFocus(true);
+ }
+
+ if( !gFocusMgr.childHasMouseCapture( modal_dialog ) )
+ {
+ gFocusMgr.setMouseCapture( modal_dialog );
+ }
+ }
+ else
+ {
+ // otherwise, make sure the focused floater is in the front of the child list
+ for ( child_list_const_reverse_iter_t child_it = getChildList()->rbegin(); child_it != getChildList()->rend(); ++child_it)
+ {
+ LLFloater* floaterp = dynamic_cast<LLFloater*>(*child_it);
+ if (gFocusMgr.childHasKeyboardFocus(floaterp))
+ {
+ LLFloater* front_child = mFrontChildHandle.get();
+ if (front_child != floaterp)
+ {
+ // Grab a list of the top floaters that want to stay on top of the focused floater
+ std::list<LLFloater*> listTop;
+ if (front_child && !front_child->canFocusStealFrontmost())
+ {
+ for (LLView* childp : *getChildList())
+ {
+ LLFloater* child_floaterp = static_cast<LLFloater*>(childp);
+ if (child_floaterp->canFocusStealFrontmost())
+ break;
+ listTop.push_back(child_floaterp);
+ }
+ }
+
+ bringToFront(floaterp, false);
+
+ // Restore top floaters
+ if (!listTop.empty())
+ {
+ for (LLView* childp : listTop)
+ {
+ sendChildToFront(childp);
+ }
+ mFrontChildHandle = listTop.back()->getHandle();
+ }
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+LLFloater* LLFloaterView::getParentFloater(LLView* viewp) const
+{
+ LLView* parentp = viewp->getParent();
+
+ while(parentp && parentp != this)
+ {
+ viewp = parentp;
+ parentp = parentp->getParent();
+ }
+
+ if (parentp == this)
+ {
+ return dynamic_cast<LLFloater*>(viewp);
+ }
+
+ return NULL;
+}
+
+S32 LLFloaterView::getZOrder(LLFloater* child)
+{
+ S32 rv = 0;
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if(viewp == child)
+ {
+ break;
+ }
+ ++rv;
+ }
+ return rv;
+}
+
+void LLFloaterView::pushVisibleAll(bool visible, const skip_list_t& skip_list)
+{
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *view = *child_iter;
+ if (skip_list.find(view) == skip_list.end())
+ {
+ view->pushVisible(visible);
+ }
+ }
+
+ LLFloaterReg::blockShowFloaters(true);
+}
+
+void LLFloaterView::popVisibleAll(const skip_list_t& skip_list)
+{
+ // make a copy of the list since some floaters change their
+ // order in the childList when changing visibility.
+ child_list_t child_list_copy = *getChildList();
+
+ for (child_list_const_iter_t child_iter = child_list_copy.begin();
+ child_iter != child_list_copy.end(); ++child_iter)
+ {
+ LLView *view = *child_iter;
+ if (skip_list.find(view) == skip_list.end())
+ {
+ view->popVisible();
+ }
+ }
+
+ 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)
+ {
+ llassert_always(mInstanceName.empty());
+ mInstanceName = name;
+ if (!mInstanceName.empty())
+ {
+ std::string ctrl_name = getControlName(mInstanceName, mKey);
+ initRectControl();
+ if (!mVisibilityControl.empty())
+ {
+ mVisibilityControl = LLFloaterReg::declareVisibilityControl(ctrl_name);
+ }
+ if(!mDocStateControl.empty())
+ {
+ mDocStateControl = LLFloaterReg::declareDockStateControl(ctrl_name);
+ }
+ }
+}
+}
+
+void LLFloater::setKey(const LLSD& newkey)
+{
+ // Note: We don't have to do anything special with registration when we change keys
+ mKey = newkey;
+}
+
+//static
+void LLFloater::setupParamsForExport(Params& p, LLView* parent)
+{
+ // Do rectangle munging to topleft layout first
+ LLPanel::setupParamsForExport(p, parent);
+
+ // Copy the rectangle out to apply layout constraints
+ LLRect rect = p.rect;
+
+ // Null out other settings
+ p.rect.left.setProvided(false);
+ p.rect.top.setProvided(false);
+ p.rect.right.setProvided(false);
+ p.rect.bottom.setProvided(false);
+
+ // Explicitly set width/height
+ p.rect.width.set( rect.getWidth(), true );
+ p.rect.height.set( rect.getHeight(), true );
+
+ // If you can't resize this floater, don't export min_height
+ // and min_width
+ bool can_resize = p.can_resize;
+ if (!can_resize)
+ {
+ p.min_height.setProvided(false);
+ p.min_width.setProvided(false);
+ }
+}
+
+void LLFloater::initFromParams(const LLFloater::Params& p)
+{
+ // *NOTE: We have too many classes derived from LLFloater to retrofit them
+ // all to pass in params via constructors. So we use this method.
+
+ // control_name, tab_stop, focus_lost_callback, initial_value, rect, enabled, visible
+ LLPanel::initFromParams(p);
+
+ // override any follows flags
+ if (mPositioning != LLFloaterEnums::POSITIONING_SPECIFIED)
+ {
+ setFollows(FOLLOWS_NONE);
+ }
+
+ mTitle = p.title;
+ mShortTitle = p.short_title;
+ applyTitle();
+
+ setCanTearOff(p.can_tear_off);
+ setCanMinimize(p.can_minimize);
+ setCanClose(p.can_close);
+ setCanDock(p.can_dock);
+ setCanResize(p.can_resize);
+ setResizeLimits(p.min_width, p.min_height);
+
+ mDragOnLeft = p.can_drag_on_left;
+ mHeaderHeight = p.header_height;
+ mLegacyHeaderHeight = p.legacy_header_height;
+ mSingleInstance = p.single_instance;
+ mReuseInstance = p.reuse_instance.isProvided() ? p.reuse_instance : p.single_instance;
+
+ mDefaultRelativeX = p.rel_x;
+ mDefaultRelativeY = p.rel_y;
+
+ mPositioning = p.positioning;
+ mAutoClose = p.auto_close;
+
+ 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
+ }
+
+ // open callback
+ if (p.open_callback.isProvided())
+ {
+ setOpenCallback(initCommitCallback(p.open_callback));
+ }
+ // close callback
+ if (p.close_callback.isProvided())
+ {
+ setCloseCallback(initCommitCallback(p.close_callback));
+ }
+
+ if (mDragHandle)
+ {
+ mDragHandle->setTitleVisible(p.show_title);
+ }
+}
+
+boost::signals2::connection LLFloater::setMinimizeCallback( const commit_signal_t::slot_type& cb )
+{
+ if (!mMinimizeSignal) mMinimizeSignal = new commit_signal_t();
+ return mMinimizeSignal->connect(cb);
+}
+
+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);
+}
+
+bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::string& filename, LLXMLNodePtr output_node)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ 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);
+
+ 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);
+ output_node->setName(node->getName()->mString);
+ parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &default_params);
+ }
+
+ // Default floater position to top-left corner of screen
+ // However, some legacy floaters have explicit top or bottom
+ // coordinates set, so respect their wishes.
+ if (!params.rect.top.isProvided() && !params.rect.bottom.isProvided())
+ {
+ params.rect.top.set(0);
+ }
+ if (!params.rect.left.isProvided() && !params.rect.right.isProvided())
+ {
+ params.rect.left.set(0);
+ }
+ params.from_xui = true;
+ applyXUILayout(params, parent, parent == gFloaterView ? gFloaterView->getSnapRect() : parent->getLocalRect());
+ initFromParams(params);
+
+ initFloater(params);
+
+ LLMultiFloater* last_host = LLFloater::getFloaterHost();
+ if (node->hasName("multi_floater"))
+ {
+ LLFloater::setFloaterHost((LLMultiFloater*) this);
+ }
+
+ LLUICtrlFactory::createChildren(this, node, child_registry_t::instance(), output_node);
+
+ if (node->hasName("multi_floater"))
+ {
+ LLFloater::setFloaterHost(last_host);
+ }
+
+ // HACK: When we changed the header height to 25 pixels in Viewer 2, rather
+ // than re-layout all the floaters we use this value in pixels to make the
+ // whole floater bigger and change the top-left coordinate for widgets.
+ // The goal is to eventually set mLegacyHeaderHeight to zero, which would
+ // make the top-left corner for widget layout the same as the top-left
+ // corner of the window's content area. James
+ S32 header_stretch = (mHeaderHeight - mLegacyHeaderHeight);
+ if (header_stretch > 0)
+ {
+ // Stretch the floater vertically, don't move widgets
+ LLRect rect = getRect();
+ rect.mTop += header_stretch;
+
+ // This will also update drag handle, title bar, close box, etc.
+ setRect(rect);
+ }
+
+ bool result;
+ result = postBuild();
+
+ if (!result)
+ {
+ LL_ERRS() << "Failed to construct floater " << getName() << LL_ENDL;
+ }
+
+ applyRectControl(); // If we have a saved rect control, apply it
+ gFloaterView->adjustToFitScreen(this, false); // Floaters loaded from XML should all fit on screen
+
+ moveResizeHandlesToFront();
+
+ applyDockState();
+
+ return true; // *TODO: Error checking
+}
+
+bool LLFloater::isShown() const
+{
+ return ! isMinimized() && isInVisibleChain();
+}
+
+bool LLFloater::isDetachedAndNotMinimized()
+{
+ return !getHost() && !isMinimized();
+}
+
+/* static */
+bool LLFloater::isShown(const LLFloater* floater)
+{
+ return floater && floater->isShown();
+}
+
+/* static */
+bool LLFloater::isMinimized(const LLFloater* floater)
+{
+ return floater && floater->isMinimized();
+}
+
+/* static */
+bool LLFloater::isVisible(const LLFloater* floater)
+{
+ return floater && floater->getVisible();
+}
+
+bool LLFloater::buildFromFile(const std::string& filename)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ LLXMLNodePtr root;
+
+ if (!LLUICtrlFactory::getLayeredXMLNode(filename, root))
+ {
+ 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")) )
+ {
+ LL_WARNS() << "Root node should be named floater in: " << filename << LL_ENDL;
+ return false;
+ }
+
+ bool res = true;
+
+ LL_DEBUGS() << "Building floater " << filename << LL_ENDL;
+ LLUICtrlFactory::instance().pushFileName(filename);
+ {
+ if (!getFactoryMap().empty())
+ {
+ LLPanel::sFactoryStack.push_front(&getFactoryMap());
+ }
+
+ // for local registry callbacks; define in constructor, referenced in XUI or postBuild
+ getCommitCallbackRegistrar().pushScope();
+ getEnableCallbackRegistrar().pushScope();
+
+ res = initFloaterXML(root, getParent(), filename, NULL);
+
+ setXMLFilename(filename);
+
+ getCommitCallbackRegistrar().popScope();
+ getEnableCallbackRegistrar().popScope();
+
+ if (!getFactoryMap().empty())
+ {
+ LLPanel::sFactoryStack.pop_front();
+ }
+ }
+ LLUICtrlFactory::instance().popFileName();
+
+ 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);
+
+ const LLRect& rect = getControlGroup()->getRect(mRectControl);
+ if (rect.notEmpty() && !mDefaultRectForGroup && mResizable)
+ {
+ next_rect.setLeftTopAndSize(next_rect.mLeft, next_rect.mTop, llmax(mMinWidth, rect.getWidth()), llmax(mMinHeight, rect.getHeight()));
+ }
+ else
+ {
+ 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 f7e121ed7e..ed3aed4db6 100644
--- a/indra/llui/llfloater.h
+++ b/indra/llui/llfloater.h
@@ -1,637 +1,641 @@
-/**
- * @file llfloater.h
- * @brief LLFloater base class
- *
- * $LicenseInfo:firstyear=2002&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$
- */
-
-// Floating "windows" within the GL display, like the inventory floater,
-// mini-map floater, etc.
-
-
-#ifndef LL_FLOATER_H
-#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;
-class LLResizeBar;
-class LLButton;
-class LLMultiFloater;
-class LLFloater;
-
-
-const bool RESIZE_YES = true;
-const bool RESIZE_NO = false;
-
-const bool DRAG_ON_TOP = false;
-const bool DRAG_ON_LEFT = true;
-
-const bool MINIMIZE_YES = true;
-const bool MINIMIZE_NO = false;
-
-const bool CLOSE_YES = true;
-const bool CLOSE_NO = false;
-
-const bool ADJUST_VERTICAL_YES = true;
-const bool ADJUST_VERTICAL_NO = false;
-
-namespace LLFloaterEnums
-{
- 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);
- static bool equate(const LLSD& a, const LLSD& b);
-/*==========================================================================*|
- bool operator()(const LLSD& a, const LLSD& b) const
- {
- return compare(a, b);
- }
-|*==========================================================================*/
- };
-
- enum EFloaterButton
- {
- BUTTON_CLOSE = 0,
- BUTTON_RESTORE,
- BUTTON_MINIMIZE,
- BUTTON_TEAR_OFF,
- BUTTON_DOCK,
- BUTTON_HELP,
- BUTTON_COUNT
- };
-
- struct Params
- : public LLInitParam::Block<Params, LLPanel::Params>
- {
- Optional<std::string> title,
- short_title;
-
- Optional<bool> single_instance,
- reuse_instance,
- can_resize,
- can_minimize,
- can_close,
- can_drag_on_left,
- can_tear_off,
- save_rect,
- save_visibility,
- save_dock_state,
- can_dock,
- show_title,
- auto_close;
-
- Optional<LLFloaterEnums::EOpenPositioning> positioning;
-
- Optional<S32> header_height,
- legacy_header_height; // HACK see initFromXML()
-
- Optional<F32> rel_x,
- rel_y;
-
- // Images for top-right controls
- Optional<LLUIImage*> close_image,
- restore_image,
- minimize_image,
- tear_off_image,
- dock_image,
- help_image;
- Optional<LLUIImage*> close_pressed_image,
- restore_pressed_image,
- minimize_pressed_image,
- tear_off_pressed_image,
- dock_pressed_image,
- help_pressed_image;
-
- Optional<CommitCallbackParam> open_callback,
- close_callback;
-
- Ignored follows;
-
- Params();
- };
-
- // use this to avoid creating your own default LLFloater::Param instance
- static const Params& getDefaultParams();
-
- // Load translations for tooltips for standard buttons
- static void initClass();
-
- LLFloater(const LLSD& key, const Params& params = getDefaultParams());
-
- virtual ~LLFloater();
-
- // Don't export top/left for rect, only height/width
- static void setupParamsForExport(Params& p, LLView* parent);
- 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*/ 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);
- bool getIsSingleInstance() { return mSingleInstance; }
-
- void initFloater(const Params& p);
-
- void openFloater(const LLSD& key = LLSD());
-
- // If allowed, close the floater cleanly, releasing focus.
- 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);
- /*virtual*/ void translate(S32 x, S32 y);
-
- // Release keyboard and mouse focus
- void releaseFocus();
-
- // moves to center of gFloaterView
- void center();
-
- LLMultiFloater* getHost();
- bool isDetachedAndNotMinimized();
-
- void applyTitle();
- std::string getCurrentTitle() const;
- void setTitle( const std::string& title);
- std::string getTitle() const;
- void setShortTitle( const std::string& short_title );
- std::string getShortTitle() const;
- virtual void setMinimized(bool b);
- void moveResizeHandlesToFront();
- void addDependentFloater(LLFloater* dependent, bool reposition = true, bool resize = false);
- void addDependentFloater(LLHandle<LLFloater> dependent_handle, bool reposition = true, bool resize = false);
- LLFloater* getDependee() { return (LLFloater*)mDependeeHandle.get(); }
- void removeDependentFloater(LLFloater* dependent);
- void fitWithDependentsOnScreen(const LLRect& left, const LLRect& bottom, const LLRect& right, const LLRect& constraint, S32 min_overlap_pixels);
- bool isMinimized() const { return mMinimized; }
- /// isShown() differs from getVisible() in that isShown() also considers
- /// isMinimized(). isShown() is true only if visible and not minimized.
- bool isShown() const;
- /// The static isShown() can accept a NULL pointer (which of course
- /// returns false). When non-NULL, it calls the non-static isShown().
- static bool isShown(const LLFloater* floater);
- static bool isVisible(const LLFloater* floater);
- static bool isMinimized(const LLFloater* floater);
- bool isFirstLook() { return mFirstLook; } // EXT-2653: This function is necessary to prevent overlapping for secondary showed toasts
- 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; }
-
- static std::string getControlName(const std::string& name, const LLSD& key);
- static LLControlGroup* getControlGroup();
-
- bool isMinimizeable() const{ return mCanMinimize; }
- bool isCloseable() const{ return mCanClose; }
- bool isDragOnLeft() const{ return mDragOnLeft; }
- S32 getMinWidth() const{ return mMinWidth; }
- S32 getMinHeight() const{ return mMinHeight; }
- S32 getHeaderHeight() const { return mHeaderHeight; }
-
- virtual bool handleMouseDown(S32 x, S32 y, MASK mask);
- virtual bool 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);
-
- virtual bool handleScrollWheel(S32 x, S32 y, S32 mask);
-
- virtual void draw();
- virtual void drawShadow(LLPanel* panel);
-
- virtual void onOpen(const LLSD& key) {}
- virtual void onClose(bool app_quitting) {}
-
- // This cannot be "const" until all derived floater canClose()
- // methods are const as well. JC
- virtual bool canClose() { return true; }
-
- /*virtual*/ void setVisible(bool visible); // do not override
- /*virtual*/ void onVisibilityChange ( bool new_visibility ); // do not override
-
- bool canFocusStealFrontmost() const { return mFocusStealsFrontmost; }
- void setFocusStealsFrontmost(bool wants_frontmost) { mFocusStealsFrontmost = wants_frontmost; }
-
- void setFrontmost(bool take_focus = true, bool restore = true);
- virtual void setVisibleAndFrontmost(bool take_focus = true, const LLSD& key = LLSD());
-
- // Defaults to false.
- virtual bool canSaveAs() const { return false; }
-
- virtual void saveAs() {}
-
- void setSnapTarget(LLHandle<LLFloater> handle) { mSnappedTo = handle; }
- void clearSnapTarget() { mSnappedTo.markDead(); }
- LLHandle<LLFloater> getSnapTarget() const { return mSnappedTo; }
-
- LLHandle<LLFloater> getHandle() const { return getDerivedHandle<LLFloater>(); }
- const LLSD& getKey() { return mKey; }
- virtual bool matchesKey(const LLSD& key) { return mSingleInstance || KeyCompare::equate(key, mKey); }
-
- const std::string& getInstanceName() { return mInstanceName; }
-
- bool isDockable() const { return mCanDock; }
- void setCanDock(bool b);
-
- bool isDocked() const { return mDocked; }
- virtual void setDocked(bool docked, bool pop_on_undock = true);
-
- virtual void setTornOff(bool torn_off) { mTornOff = torn_off; }
- bool isTornOff() {return mTornOff;}
- void setOpenPositioning(LLFloaterEnums::EOpenPositioning pos) {mPositioning = pos;}
-
-
- // Close the floater returned by getFrontmostClosableFloater() and
- // handle refocusing.
- static void closeFrontmostFloater();
-
- static bool isQuitRequested() { return sQuitting; }
-
-// LLNotification::Params contextualNotification(const std::string& name)
-// {
-// return LLNotification::Params(name).context(mNotificationContext);
-// }
-
- static void onClickClose(LLFloater* floater);
- static void onClickMinimize(LLFloater* floater);
- static void onClickTearOff(LLFloater* floater);
- static void onClickDock(LLFloater* floater);
- static void onClickHelp(LLFloater* floater);
-
- static void setFloaterHost(LLMultiFloater* hostp) {sHostp = hostp; }
- static LLMultiFloater* getFloaterHost() {return sHostp; }
-
- 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 stackWith(LLFloater& other);
-
- virtual void initRectControl();
- virtual bool applyRectControl();
- bool applyDockState();
- void applyPositioning(LLFloater* other, bool on_open);
- void applyRelativePosition();
-
- void storeRectControl();
- void storeVisibilityControl();
- void storeDockStateControl();
-
- void setKey(const LLSD& key);
- void setInstanceName(const std::string& name);
-
- virtual void bringToFront(S32 x, S32 y);
- virtual void goneFromFront();
-
- void setExpandedRect(const LLRect& rect) { mExpandedRect = rect; } // size when not minimized
- const LLRect& getExpandedRect() const { return mExpandedRect; }
-
- void setAutoFocus(bool focus) { mAutoFocus = focus; } // whether to automatically take focus when opened
- bool getAutoFocus() const { return mAutoFocus; }
- LLDragHandle* getDragHandle() const { return mDragHandle; }
-
- void destroy(); // Don't call this directly. You probably want to call closeFloater()
-
- virtual void onClickCloseBtn(bool app_quitting = false);
-
- virtual void updateTitleButtons();
-
- // Draws a cone from this floater to parent floater or view (owner)
- // Modifies context_cone_opacity (interpolates according to fade time and returns new value)
- void drawConeToOwner(F32 &context_cone_opacity,
- F32 max_cone_opacity,
- LLView *owner_view,
- F32 context_fade_time = CONTEXT_CONE_FADE_TIME,
- F32 contex_cone_in_alpha = CONTEXT_CONE_IN_ALPHA,
- F32 contex_cone_out_alpha = CONTEXT_CONE_OUT_ALPHA);
-
-private:
- void setForeground(bool b); // called only by floaterview
- void cleanupHandles(); // remove handles to dead floaters
- void createMinimizeButton();
- void buildButtons(const Params& p);
-
- // Images and tooltips are named in the XML, but we want to look them
- // up by index.
- static LLUIImage* getButtonImage(const Params& p, EFloaterButton e);
- static LLUIImage* getButtonPressedImage(const Params& p, EFloaterButton e);
-
- /**
- * @params is_chrome - if floater is Chrome it means that floater will never get focus.
- * Therefore it can't be closed with 'Ctrl+W'. So the tooltip text of close button( X )
- * should be 'Close' not 'Close(Ctrl+W)' as for usual floaters.
- */
- static std::string getButtonTooltip(const Params& p, EFloaterButton e, bool is_chrome);
-
- bool offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index);
- void addResizeCtrls();
- void layoutResizeCtrls();
- void addDragHandle();
- void layoutDragHandle(); // repair layout
-
- static void updateActiveFloaterTransparency();
- static void updateInactiveFloaterTransparency();
- void updateTransparency(LLView* view, ETypeTransparency transparency_type);
-
-public:
- static const F32 CONTEXT_CONE_IN_ALPHA;
- static const F32 CONTEXT_CONE_OUT_ALPHA;
- static const F32 CONTEXT_CONE_FADE_TIME;
-
- // Called when floater is opened, passes mKey
- // Public so external views or floaters can watch for this floater opening
- commit_signal_t mOpenSignal;
-
- // Called when floater is closed, passes app_qitting as LLSD()
- // Public so external views or floaters can watch for this floater closing
- commit_signal_t mCloseSignal;
-
- commit_signal_t* mMinimizeSignal;
-
-protected:
- bool mSaveRect;
- bool mDefaultRectForGroup;
- 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
-
- LLDragHandle* mDragHandle;
- LLResizeBar* mResizeBar[4];
- LLResizeHandle* mResizeHandle[4];
-
- LLButton* mButtons[BUTTON_COUNT];
-private:
- LLRect mExpandedRect;
-
- LLUIString mTitle;
- LLUIString mShortTitle;
-
- bool mSingleInstance; // true if there is only ever one instance of the floater
- bool mReuseInstance; // true if we want to hide the floater when we close it instead of destroying it
- 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 mFocusStealsFrontmost = true; // false if we don't want the currently focused floater to cover this floater without user interaction
- bool mDragOnLeft;
- bool mResizable;
- bool mAutoClose;
-
- LLFloaterEnums::EOpenPositioning mPositioning;
- LLCoordFloater mPosition;
-
- S32 mMinWidth;
- S32 mMinHeight;
- S32 mHeaderHeight; // height in pixels of header for title, drag bar
- S32 mLegacyHeaderHeight;// HACK see initFloaterXML()
-
- bool mMinimized;
- bool mForeground;
- LLHandle<LLFloater> mDependeeHandle;
-
-
- bool mFirstLook; // true if the _next_ time this floater is visible will be the first time in the session that it is visible.
-
- typedef std::set<LLHandle<LLFloater> > handle_set_t;
- typedef std::set<LLHandle<LLFloater> >::iterator handle_set_iter_t;
- handle_set_t mDependents;
- bool mTranslateWithDependents { false };
-
- bool mButtonsEnabled[BUTTON_COUNT];
- F32 mButtonScale;
- bool mAutoFocus;
- LLHandle<LLFloater> mSnappedTo;
-
- LLHandle<LLFloater> mHostHandle;
- LLHandle<LLFloater> mLastHostHandle;
-
- bool mCanDock;
- bool mDocked;
- bool mTornOff;
-
- static LLMultiFloater* sHostp;
- static bool sQuitting;
- static std::string sButtonNames[BUTTON_COUNT];
- static std::string sButtonToolTips[BUTTON_COUNT];
- static std::string sButtonToolTipsIndex[BUTTON_COUNT];
-
- typedef void(*click_callback)(LLFloater*);
- static click_callback sButtonCallbacks[BUTTON_COUNT];
-
- bool mHasBeenDraggedWhileMinimized;
- S32 mPreviousMinimizedBottom;
- S32 mPreviousMinimizedLeft;
-
- F32 mDefaultRelativeX;
- F32 mDefaultRelativeY;
-};
-
-
-/////////////////////////////////////////////////////////////
-// LLFloaterView
-// Parent of all floating panels
-
-const S32 FLOATER_MIN_VISIBLE_PIXELS = 16;
-
-class LLFloaterView : public LLUICtrl
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>{};
-
-protected:
- LLFloaterView (const Params& p);
- friend class LLUICtrlFactory;
-
-public:
-
- /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true);
- /*virtual*/ void draw();
- /*virtual*/ LLRect getSnapRect() const;
- /*virtual*/ void refresh();
-
- 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, bool snap_in_toolbars = false);
-
- void setMinimizePositionVerticalOffset(S32 offset) { mMinimizePositionVOffset = offset; }
- void getMinimizePosition( S32 *left, S32 *bottom);
- void restoreAll(); // un-minimize all floaters
- typedef std::set<LLView*> skip_list_t;
- void pushVisibleAll(bool visible, const skip_list_t& skip_list = skip_list_t());
- void popVisibleAll(const skip_list_t& skip_list = skip_list_t());
-
- void setCycleMode(bool mode) { mFocusCycleMode = mode; }
- bool getCycleMode() const { return mFocusCycleMode; }
- void bringToFront( LLFloater* child, bool give_focus = true, bool restore = true );
- void highlightFocusedFloater();
- void unhighlightFocusedFloater();
- void focusFrontFloater();
- void destroyAllChildren();
- // attempt to close all floaters
- void closeAllChildren(bool app_quitting);
- 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;
- LLFloater* getFocusedFloater() const;
- void syncFloaterTabOrder();
-
- // Returns z order of child provided. 0 is closest, larger numbers
- // are deeper in the screen. If there is no such child, the return
- // value is not defined.
- S32 getZOrder(LLFloater* child);
-
- 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;
- LLHandle<LLFloater> mFrontChildHandle;
-};
-
-//
-// Globals
-//
-
-extern LLFloaterView* gFloaterView;
-
-#endif // LL_FLOATER_H
-
-
-
+/**
+ * @file llfloater.h
+ * @brief LLFloater base class
+ *
+ * $LicenseInfo:firstyear=2002&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$
+ */
+
+// Floating "windows" within the GL display, like the inventory floater,
+// mini-map floater, etc.
+
+
+#ifndef LL_FLOATER_H
+#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;
+class LLResizeBar;
+class LLButton;
+class LLMultiFloater;
+class LLFloater;
+
+
+const bool RESIZE_YES = true;
+const bool RESIZE_NO = false;
+
+const bool DRAG_ON_TOP = false;
+const bool DRAG_ON_LEFT = true;
+
+const bool MINIMIZE_YES = true;
+const bool MINIMIZE_NO = false;
+
+const bool CLOSE_YES = true;
+const bool CLOSE_NO = false;
+
+const bool ADJUST_VERTICAL_YES = true;
+const bool ADJUST_VERTICAL_NO = false;
+
+const F32 CONTEXT_CONE_IN_ALPHA = 0.f;
+const F32 CONTEXT_CONE_OUT_ALPHA = 1.f;
+const F32 CONTEXT_CONE_FADE_TIME = .08f;
+
+namespace LLFloaterEnums
+{
+ 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);
+ static bool equate(const LLSD& a, const LLSD& b);
+/*==========================================================================*|
+ bool operator()(const LLSD& a, const LLSD& b) const
+ {
+ return compare(a, b);
+ }
+|*==========================================================================*/
+ };
+
+ enum EFloaterButton
+ {
+ BUTTON_CLOSE = 0,
+ BUTTON_RESTORE,
+ BUTTON_MINIMIZE,
+ BUTTON_TEAR_OFF,
+ BUTTON_DOCK,
+ BUTTON_HELP,
+ BUTTON_COUNT
+ };
+
+ struct Params
+ : public LLInitParam::Block<Params, LLPanel::Params>
+ {
+ Optional<std::string> title,
+ short_title;
+
+ Optional<bool> single_instance,
+ reuse_instance,
+ can_resize,
+ can_minimize,
+ can_close,
+ can_drag_on_left,
+ can_tear_off,
+ save_rect,
+ save_visibility,
+ save_dock_state,
+ can_dock,
+ show_title,
+ auto_close;
+
+ Optional<LLFloaterEnums::EOpenPositioning> positioning;
+
+ Optional<S32> header_height,
+ legacy_header_height; // HACK see initFromXML()
+
+ Optional<F32> rel_x,
+ rel_y;
+
+ // Images for top-right controls
+ Optional<LLUIImage*> close_image,
+ restore_image,
+ minimize_image,
+ tear_off_image,
+ dock_image,
+ help_image;
+ Optional<LLUIImage*> close_pressed_image,
+ restore_pressed_image,
+ minimize_pressed_image,
+ tear_off_pressed_image,
+ dock_pressed_image,
+ help_pressed_image;
+
+ Optional<CommitCallbackParam> open_callback,
+ close_callback;
+
+ Ignored follows;
+
+ Params();
+ };
+
+ // use this to avoid creating your own default LLFloater::Param instance
+ static const Params& getDefaultParams();
+
+ // Load translations for tooltips for standard buttons
+ static void initClass();
+
+ LLFloater(const LLSD& key, const Params& params = getDefaultParams());
+
+ virtual ~LLFloater();
+
+ // Don't export top/left for rect, only height/width
+ static void setupParamsForExport(Params& p, LLView* parent);
+ 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*/ 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);
+ bool getIsSingleInstance() { return mSingleInstance; }
+
+ void initFloater(const Params& p);
+
+ void openFloater(const LLSD& key = LLSD());
+
+ // If allowed, close the floater cleanly, releasing focus.
+ 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);
+ /*virtual*/ void translate(S32 x, S32 y);
+
+ // Release keyboard and mouse focus
+ void releaseFocus();
+
+ // moves to center of gFloaterView
+ void center();
+
+ LLMultiFloater* getHost();
+ bool isDetachedAndNotMinimized();
+
+ void applyTitle();
+ std::string getCurrentTitle() const;
+ void setTitle( const std::string& title);
+ std::string getTitle() const;
+ void setShortTitle( const std::string& short_title );
+ std::string getShortTitle() const;
+ virtual void setMinimized(bool b);
+ void moveResizeHandlesToFront();
+ void addDependentFloater(LLFloater* dependent, bool reposition = true, bool resize = false);
+ void addDependentFloater(LLHandle<LLFloater> dependent_handle, bool reposition = true, bool resize = false);
+ LLFloater* getDependee() { return (LLFloater*)mDependeeHandle.get(); }
+ void removeDependentFloater(LLFloater* dependent);
+ void fitWithDependentsOnScreen(const LLRect& left, const LLRect& bottom, const LLRect& right, const LLRect& constraint, S32 min_overlap_pixels);
+ bool isMinimized() const { return mMinimized; }
+ /// isShown() differs from getVisible() in that isShown() also considers
+ /// isMinimized(). isShown() is true only if visible and not minimized.
+ bool isShown() const;
+ /// The static isShown() can accept a NULL pointer (which of course
+ /// returns false). When non-NULL, it calls the non-static isShown().
+ static bool isShown(const LLFloater* floater);
+ static bool isVisible(const LLFloater* floater);
+ static bool isMinimized(const LLFloater* floater);
+ bool isFirstLook() { return mFirstLook; } // EXT-2653: This function is necessary to prevent overlapping for secondary showed toasts
+ 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; }
+
+ static std::string getControlName(const std::string& name, const LLSD& key);
+ static LLControlGroup* getControlGroup();
+
+ bool isMinimizeable() const{ return mCanMinimize; }
+ bool isCloseable() const{ return mCanClose; }
+ bool isDragOnLeft() const{ return mDragOnLeft; }
+ S32 getMinWidth() const{ return mMinWidth; }
+ S32 getMinHeight() const{ return mMinHeight; }
+ S32 getHeaderHeight() const { return mHeaderHeight; }
+
+ virtual bool handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual bool 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);
+
+ virtual bool handleScrollWheel(S32 x, S32 y, S32 mask);
+
+ virtual void draw();
+ virtual void drawShadow(LLPanel* panel);
+
+ virtual void onOpen(const LLSD& key) {}
+ virtual void onClose(bool app_quitting) {}
+
+ // This cannot be "const" until all derived floater canClose()
+ // methods are const as well. JC
+ virtual bool canClose() { return true; }
+
+ /*virtual*/ void setVisible(bool visible); // do not override
+ /*virtual*/ void onVisibilityChange ( bool new_visibility ); // do not override
+
+ bool canFocusStealFrontmost() const { return mFocusStealsFrontmost; }
+ void setFocusStealsFrontmost(bool wants_frontmost) { mFocusStealsFrontmost = wants_frontmost; }
+
+ void setFrontmost(bool take_focus = true, bool restore = true);
+ virtual void setVisibleAndFrontmost(bool take_focus = true, const LLSD& key = LLSD());
+
+ // Defaults to false.
+ virtual bool canSaveAs() const { return false; }
+
+ virtual void saveAs() {}
+
+ void setSnapTarget(LLHandle<LLFloater> handle) { mSnappedTo = handle; }
+ void clearSnapTarget() { mSnappedTo.markDead(); }
+ LLHandle<LLFloater> getSnapTarget() const { return mSnappedTo; }
+
+ LLHandle<LLFloater> getHandle() const { return getDerivedHandle<LLFloater>(); }
+ const LLSD& getKey() { return mKey; }
+ virtual bool matchesKey(const LLSD& key) { return mSingleInstance || KeyCompare::equate(key, mKey); }
+
+ const std::string& getInstanceName() { return mInstanceName; }
+
+ bool isDockable() const { return mCanDock; }
+ void setCanDock(bool b);
+
+ bool isDocked() const { return mDocked; }
+ virtual void setDocked(bool docked, bool pop_on_undock = true);
+
+ virtual void setTornOff(bool torn_off) { mTornOff = torn_off; }
+ bool isTornOff() {return mTornOff;}
+ void setOpenPositioning(LLFloaterEnums::EOpenPositioning pos) {mPositioning = pos;}
+
+
+ // Close the floater returned by getFrontmostClosableFloater() and
+ // handle refocusing.
+ static void closeFrontmostFloater();
+
+ static bool isQuitRequested() { return sQuitting; }
+
+// LLNotification::Params contextualNotification(const std::string& name)
+// {
+// return LLNotification::Params(name).context(mNotificationContext);
+// }
+
+ static void onClickClose(LLFloater* floater);
+ static void onClickMinimize(LLFloater* floater);
+ static void onClickTearOff(LLFloater* floater);
+ static void onClickDock(LLFloater* floater);
+ static void onClickHelp(LLFloater* floater);
+
+ static void setFloaterHost(LLMultiFloater* hostp) {sHostp = hostp; }
+ static LLMultiFloater* getFloaterHost() {return sHostp; }
+
+ 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 stackWith(LLFloater& other);
+
+ virtual void initRectControl();
+ virtual bool applyRectControl();
+ bool applyDockState();
+ void applyPositioning(LLFloater* other, bool on_open);
+ void applyRelativePosition();
+
+ void storeRectControl();
+ void storeVisibilityControl();
+ void storeDockStateControl();
+
+ void setKey(const LLSD& key);
+ void setInstanceName(const std::string& name);
+
+ virtual void bringToFront(S32 x, S32 y);
+ virtual void goneFromFront();
+
+ void setExpandedRect(const LLRect& rect) { mExpandedRect = rect; } // size when not minimized
+ const LLRect& getExpandedRect() const { return mExpandedRect; }
+
+ void setAutoFocus(bool focus) { mAutoFocus = focus; } // whether to automatically take focus when opened
+ bool getAutoFocus() const { return mAutoFocus; }
+ LLDragHandle* getDragHandle() const { return mDragHandle; }
+
+ void destroy(); // Don't call this directly. You probably want to call closeFloater()
+
+ virtual void onClickCloseBtn(bool app_quitting = false);
+
+ virtual void updateTitleButtons();
+
+ // Draws a cone from this floater to parent floater or view (owner)
+ // Modifies context_cone_opacity (interpolates according to fade time and returns new value)
+ void drawConeToOwner(F32 &context_cone_opacity,
+ F32 max_cone_opacity,
+ LLView *owner_view,
+ F32 context_fade_time = CONTEXT_CONE_FADE_TIME,
+ F32 contex_cone_in_alpha = CONTEXT_CONE_IN_ALPHA,
+ F32 contex_cone_out_alpha = CONTEXT_CONE_OUT_ALPHA);
+
+private:
+ void setForeground(bool b); // called only by floaterview
+ void cleanupHandles(); // remove handles to dead floaters
+ void createMinimizeButton();
+ void buildButtons(const Params& p);
+
+ // Images and tooltips are named in the XML, but we want to look them
+ // up by index.
+ static LLUIImage* getButtonImage(const Params& p, EFloaterButton e);
+ static LLUIImage* getButtonPressedImage(const Params& p, EFloaterButton e);
+
+ /**
+ * @params is_chrome - if floater is Chrome it means that floater will never get focus.
+ * Therefore it can't be closed with 'Ctrl+W'. So the tooltip text of close button( X )
+ * should be 'Close' not 'Close(Ctrl+W)' as for usual floaters.
+ */
+ static std::string getButtonTooltip(const Params& p, EFloaterButton e, bool is_chrome);
+
+ bool offerClickToButton(S32 x, S32 y, MASK mask, EFloaterButton index);
+ void addResizeCtrls();
+ void layoutResizeCtrls();
+ void addDragHandle();
+ void layoutDragHandle(); // repair layout
+
+ static void updateActiveFloaterTransparency();
+ static void updateInactiveFloaterTransparency();
+ void updateTransparency(LLView* view, ETypeTransparency transparency_type);
+
+public:
+ static const F32 CONTEXT_CONE_IN_ALPHA;
+ static const F32 CONTEXT_CONE_OUT_ALPHA;
+ static const F32 CONTEXT_CONE_FADE_TIME;
+
+ // Called when floater is opened, passes mKey
+ // Public so external views or floaters can watch for this floater opening
+ commit_signal_t mOpenSignal;
+
+ // Called when floater is closed, passes app_qitting as LLSD()
+ // Public so external views or floaters can watch for this floater closing
+ commit_signal_t mCloseSignal;
+
+ commit_signal_t* mMinimizeSignal;
+
+protected:
+ bool mSaveRect;
+ bool mDefaultRectForGroup;
+ 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
+
+ LLDragHandle* mDragHandle;
+ LLResizeBar* mResizeBar[4];
+ LLResizeHandle* mResizeHandle[4];
+
+ LLButton* mButtons[BUTTON_COUNT];
+private:
+ LLRect mExpandedRect;
+
+ LLUIString mTitle;
+ LLUIString mShortTitle;
+
+ bool mSingleInstance; // true if there is only ever one instance of the floater
+ bool mReuseInstance; // true if we want to hide the floater when we close it instead of destroying it
+ 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 mFocusStealsFrontmost = true; // false if we don't want the currently focused floater to cover this floater without user interaction
+ bool mDragOnLeft;
+ bool mResizable;
+ bool mAutoClose;
+
+ LLFloaterEnums::EOpenPositioning mPositioning;
+ LLCoordFloater mPosition;
+
+ S32 mMinWidth;
+ S32 mMinHeight;
+ S32 mHeaderHeight; // height in pixels of header for title, drag bar
+ S32 mLegacyHeaderHeight;// HACK see initFloaterXML()
+
+ bool mMinimized;
+ bool mForeground;
+ LLHandle<LLFloater> mDependeeHandle;
+
+
+ bool mFirstLook; // true if the _next_ time this floater is visible will be the first time in the session that it is visible.
+
+ typedef std::set<LLHandle<LLFloater> > handle_set_t;
+ typedef std::set<LLHandle<LLFloater> >::iterator handle_set_iter_t;
+ handle_set_t mDependents;
+ bool mTranslateWithDependents { false };
+
+ bool mButtonsEnabled[BUTTON_COUNT];
+ F32 mButtonScale;
+ bool mAutoFocus;
+ LLHandle<LLFloater> mSnappedTo;
+
+ LLHandle<LLFloater> mHostHandle;
+ LLHandle<LLFloater> mLastHostHandle;
+
+ bool mCanDock;
+ bool mDocked;
+ bool mTornOff;
+
+ static LLMultiFloater* sHostp;
+ static bool sQuitting;
+ static std::string sButtonNames[BUTTON_COUNT];
+ static std::string sButtonToolTips[BUTTON_COUNT];
+ static std::string sButtonToolTipsIndex[BUTTON_COUNT];
+
+ typedef void(*click_callback)(LLFloater*);
+ static click_callback sButtonCallbacks[BUTTON_COUNT];
+
+ bool mHasBeenDraggedWhileMinimized;
+ S32 mPreviousMinimizedBottom;
+ S32 mPreviousMinimizedLeft;
+
+ F32 mDefaultRelativeX;
+ F32 mDefaultRelativeY;
+};
+
+
+/////////////////////////////////////////////////////////////
+// LLFloaterView
+// Parent of all floating panels
+
+const S32 FLOATER_MIN_VISIBLE_PIXELS = 16;
+
+class LLFloaterView : public LLUICtrl
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>{};
+
+protected:
+ LLFloaterView (const Params& p);
+ friend class LLUICtrlFactory;
+
+public:
+
+ /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true);
+ /*virtual*/ void draw();
+ /*virtual*/ LLRect getSnapRect() const;
+ /*virtual*/ void refresh();
+
+ 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, bool snap_in_toolbars = false);
+
+ void setMinimizePositionVerticalOffset(S32 offset) { mMinimizePositionVOffset = offset; }
+ void getMinimizePosition( S32 *left, S32 *bottom);
+ void restoreAll(); // un-minimize all floaters
+ typedef std::set<LLView*> skip_list_t;
+ void pushVisibleAll(bool visible, const skip_list_t& skip_list = skip_list_t());
+ void popVisibleAll(const skip_list_t& skip_list = skip_list_t());
+
+ void setCycleMode(bool mode) { mFocusCycleMode = mode; }
+ bool getCycleMode() const { return mFocusCycleMode; }
+ void bringToFront( LLFloater* child, bool give_focus = true, bool restore = true );
+ void highlightFocusedFloater();
+ void unhighlightFocusedFloater();
+ void focusFrontFloater();
+ void destroyAllChildren();
+ // attempt to close all floaters
+ void closeAllChildren(bool app_quitting);
+ 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;
+ LLFloater* getFocusedFloater() const;
+ void syncFloaterTabOrder();
+
+ // Returns z order of child provided. 0 is closest, larger numbers
+ // are deeper in the screen. If there is no such child, the return
+ // value is not defined.
+ S32 getZOrder(LLFloater* child);
+
+ 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;
+ LLHandle<LLFloater> mFrontChildHandle;
+};
+
+//
+// Globals
+//
+
+extern LLFloaterView* gFloaterView;
+
+#endif // LL_FLOATER_H
+
+
+
diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp
index 1c3efaa9cf..fad1155d90 100644
--- a/indra/llui/llfloaterreg.cpp
+++ b/indra/llui/llfloaterreg.cpp
@@ -1,609 +1,609 @@
-/**
- * @file llfloaterreg.cpp
- * @brief LLFloaterReg Floater Registration Class
- *
- * $LicenseInfo:firstyear=2002&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 "llfloaterreg.h"
-
-//#include "llagent.h"
-#include "llfloater.h"
-#include "llmultifloater.h"
-#include "llfloaterreglistener.h"
-#include "lluiusage.h"
-
-//*******************************************************
-
-//static
-LLFloaterReg::instance_list_t LLFloaterReg::sNullInstanceList;
-LLFloaterReg::instance_map_t LLFloaterReg::sInstanceMap;
-LLFloaterReg::build_map_t LLFloaterReg::sBuildMap;
-std::map<std::string,std::string> LLFloaterReg::sGroupMap;
-bool LLFloaterReg::sBlockShowFloaters = false;
-std::set<std::string> LLFloaterReg::sAlwaysShowableList;
-
-static LLFloaterRegListener sFloaterRegListener;
-
-//*******************************************************
-
-//static
-void LLFloaterReg::add(const std::string& name, const std::string& filename, const LLFloaterBuildFunc& func, const std::string& groupname)
-{
- sBuildMap[name].mFunc = func;
- sBuildMap[name].mFile = filename;
- sGroupMap[name] = groupname.empty() ? name : groupname;
- sGroupMap[groupname] = groupname; // for referencing directly by group name
-}
-
-//static
-bool LLFloaterReg::isRegistered(const std::string& name)
-{
- return sBuildMap.find(name) != sBuildMap.end();
-}
-
-//static
-LLFloater* LLFloaterReg::getLastFloaterInGroup(const std::string& name)
-{
- const std::string& groupname = sGroupMap[name];
- if (!groupname.empty())
- {
- instance_list_t& list = sInstanceMap[groupname];
- if (!list.empty())
- {
- for (instance_list_t::reverse_iterator iter = list.rbegin(); iter != list.rend(); ++iter)
- {
- LLFloater* inst = *iter;
-
- if (inst->getVisible() && !inst->isMinimized())
- {
- return inst;
- }
- }
- }
- }
- 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)))
- {
- if (candidate_rect.mTop > inst->getRect().mTop)
- {
- candidate_floater = inst;
- candidate_rect = inst->getRect();
- }
- }
- }
- }
-
- return candidate_floater;
-}
-
-//static
-LLFloater* LLFloaterReg::findInstance(const std::string& name, const LLSD& key)
-{
- LLFloater* res = NULL;
- const std::string& groupname = sGroupMap[name];
- if (!groupname.empty())
- {
- instance_list_t& list = sInstanceMap[groupname];
- for (instance_list_t::iterator iter = list.begin(); iter != list.end(); ++iter)
- {
- LLFloater* inst = *iter;
- if (inst->matchesKey(key))
- {
- res = inst;
- break;
- }
- }
- }
- return res;
-}
-
-//static
-LLFloater* LLFloaterReg::getInstance(const std::string& name, const LLSD& key)
-{
- LLFloater* res = findInstance(name, key);
- if (!res)
- {
- const LLFloaterBuildFunc& build_func = sBuildMap[name].mFunc;
- const std::string& xui_file = sBuildMap[name].mFile;
- if (build_func)
- {
- const std::string& groupname = sGroupMap[name];
- if (!groupname.empty())
- {
- instance_list_t& list = sInstanceMap[groupname];
-
- res = build_func(key);
- if (!res)
- {
- LL_WARNS() << "Failed to build floater type: '" << name << "'." << LL_ENDL;
- return NULL;
- }
- bool success = res->buildFromFile(xui_file);
- if (!success)
- {
- 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
- if (res->mKey.isUndefined())
- {
- 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)
- {
- LL_WARNS() << "Floater type: '" << name << "' not registered." << LL_ENDL;
- }
- }
- return res;
-}
-
-//static
-LLFloater* LLFloaterReg::removeInstance(const std::string& name, const LLSD& key)
-{
- LLFloater* res = NULL;
- const std::string& groupname = sGroupMap[name];
- if (!groupname.empty())
- {
- instance_list_t& list = sInstanceMap[groupname];
- for (instance_list_t::iterator iter = list.begin(); iter != list.end(); ++iter)
- {
- LLFloater* inst = *iter;
- if (inst->matchesKey(key))
- {
- res = inst;
- list.erase(iter);
- break;
- }
- }
- }
- return res;
-}
-
-//static
-// returns true if the instance existed
-bool LLFloaterReg::destroyInstance(const std::string& name, const LLSD& key)
-{
- LLFloater* inst = removeInstance(name, key);
- if (inst)
- {
- delete inst;
- return true;
- }
- else
- {
- return false;
- }
-}
-
-// Iterators
-//static
-LLFloaterReg::const_instance_list_t& LLFloaterReg::getFloaterList(const std::string& name)
-{
- instance_map_t::iterator iter = sInstanceMap.find(name);
- if (iter != sInstanceMap.end())
- {
- return iter->second;
- }
- else
- {
- return sNullInstanceList;
- }
-}
-
-// Visibility Management
-
-//static
-LLFloater* LLFloaterReg::showInstance(const std::string& name, const LLSD& key, bool focus)
-{
- if( sBlockShowFloaters
- // see EXT-7090
- && sAlwaysShowableList.find(name) == sAlwaysShowableList.end())
- return 0;//
- LLFloater* instance = getInstance(name, key);
- if (instance)
- {
- instance->openFloater(key);
- if (focus)
- instance->setFocus(true);
- }
- return instance;
-}
-
-//static
-// returns true if the instance exists
-bool LLFloaterReg::hideInstance(const std::string& name, const LLSD& key)
-{
- LLFloater* instance = findInstance(name, key);
- if (instance)
- {
- instance->closeHostedFloater();
- }
- return (instance != NULL);
-}
-
-//static
-// returns true if the instance is visible when completed
-bool LLFloaterReg::toggleInstance(const std::string& name, const LLSD& key)
-{
- LLFloater* instance = findInstance(name, key);
- if (instance && instance->isShown())
- {
- instance->closeHostedFloater();
- return false;
- }
-
- instance = showInstance(name, key, true);
-
- return instance != nullptr;
-}
-
-//static
-// returns true if the instance exists and is visible (doesnt matter minimized or not)
-bool LLFloaterReg::instanceVisible(const std::string& name, const LLSD& key)
-{
- LLFloater* instance = findInstance(name, key);
- return LLFloater::isVisible(instance);
-}
-
-//static
-void LLFloaterReg::showInitialVisibleInstances()
-{
- // Iterate through alll registered instance names and show any with a save visible state
- for (build_map_t::iterator iter = sBuildMap.begin(); iter != sBuildMap.end(); ++iter)
- {
- const std::string& name = iter->first;
- std::string controlname = getVisibilityControlName(name);
- if (LLFloater::getControlGroup()->controlExists(controlname))
- {
- bool isvis = LLFloater::getControlGroup()->getBOOL(controlname);
- if (isvis)
- {
- showInstance(name, LLSD()); // keyed floaters shouldn't set save_vis to true
- }
- }
- }
-}
-
-//static
-void LLFloaterReg::hideVisibleInstances(const std::set<std::string>& exceptions)
-{
- // Iterate through alll active instances and hide them
- for (instance_map_t::iterator iter = sInstanceMap.begin(); iter != sInstanceMap.end(); ++iter)
- {
- const std::string& name = iter->first;
- if (exceptions.find(name) != exceptions.end())
- continue;
- instance_list_t& list = iter->second;
- for (instance_list_t::iterator iter = list.begin(); iter != list.end(); ++iter)
- {
- LLFloater* floater = *iter;
- floater->pushVisible(false);
- }
- }
-}
-
-//static
-void LLFloaterReg::restoreVisibleInstances()
-{
- // Iterate through all active instances and restore visibility
- for (instance_map_t::iterator iter = sInstanceMap.begin(); iter != sInstanceMap.end(); ++iter)
- {
- instance_list_t& list = iter->second;
- for (instance_list_t::iterator iter = list.begin(); iter != list.end(); ++iter)
- {
- LLFloater* floater = *iter;
- floater->popVisible();
- }
- }
-}
-
-//static
-std::string LLFloaterReg::getRectControlName(const std::string& name)
-{
- return std::string("floater_rect_") + getBaseControlName(name);
-}
-
-//static
-std::string LLFloaterReg::declareRectControl(const std::string& name)
-{
- std::string controlname = getRectControlName(name);
- LLFloater::getControlGroup()->declareRect(controlname, LLRect(),
- 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)
-{
- 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()),
- LLControlVariable::PERSIST_NONDFT);
- return controlname;
-}
-
-//static
-std::string LLFloaterReg::declareDockStateControl(const std::string& name)
-{
- std::string controlname = getDockStateControlName(name);
- LLFloater::getControlGroup()->declareBOOL(controlname, true,
- llformat("Window Docking state for %s", name.c_str()),
- LLControlVariable::PERSIST_NONDFT);
- return controlname;
-
-}
-
-//static
-std::string LLFloaterReg::getDockStateControlName(const std::string& name)
-{
- std::string res = std::string("floater_dock_") + name;
- LLStringUtil::replaceChar( res, ' ', '_' );
- return res;
-}
-
-
-//static
-void LLFloaterReg::registerControlVariables()
-{
- // Iterate through alll registered instance names and register rect and visibility control variables
- for (build_map_t::iterator iter = sBuildMap.begin(); iter != sBuildMap.end(); ++iter)
- {
- const std::string& name = iter->first;
- if (LLFloater::getControlGroup()->controlExists(getRectControlName(name)))
- {
- declareRectControl(name);
- }
- if (LLFloater::getControlGroup()->controlExists(getVisibilityControlName(name)))
- {
- declareVisibilityControl(name);
- }
- }
-
- const LLSD& exclude_list = LLUI::getInstance()->mSettingGroups["config"]->getLLSD("always_showable_floaters");
- for (LLSD::array_const_iterator iter = exclude_list.beginArray();
- iter != exclude_list.endArray();
- iter++)
- {
- sAlwaysShowableList.insert(iter->asString());
- }
-}
-
-//static
-void LLFloaterReg::toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD& key)
-{
- //
- // 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);
-
- 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
- {
- 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
-// Same as toggleInstanceOrBringToFront but does not close floater.
-// unlike showInstance() does not trigger onOpen() if already open
-void LLFloaterReg::showInstanceOrBringToFront(const LLSD& sdname, const LLSD& key)
-{
- std::string name = sdname.asString();
- LLFloater* instance = getInstance(name, key);
-
-
- 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
- {
- 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);
- }
- }
-}
-
-// static
-U32 LLFloaterReg::getVisibleFloaterInstanceCount()
-{
- U32 count = 0;
-
- 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->isMinimized())
- {
- count++;
- }
- }
- }
-
- return count;
-}
+/**
+ * @file llfloaterreg.cpp
+ * @brief LLFloaterReg Floater Registration Class
+ *
+ * $LicenseInfo:firstyear=2002&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 "llfloaterreg.h"
+
+//#include "llagent.h"
+#include "llfloater.h"
+#include "llmultifloater.h"
+#include "llfloaterreglistener.h"
+#include "lluiusage.h"
+
+//*******************************************************
+
+//static
+LLFloaterReg::instance_list_t LLFloaterReg::sNullInstanceList;
+LLFloaterReg::instance_map_t LLFloaterReg::sInstanceMap;
+LLFloaterReg::build_map_t LLFloaterReg::sBuildMap;
+std::map<std::string,std::string> LLFloaterReg::sGroupMap;
+bool LLFloaterReg::sBlockShowFloaters = false;
+std::set<std::string> LLFloaterReg::sAlwaysShowableList;
+
+static LLFloaterRegListener sFloaterRegListener;
+
+//*******************************************************
+
+//static
+void LLFloaterReg::add(const std::string& name, const std::string& filename, const LLFloaterBuildFunc& func, const std::string& groupname)
+{
+ sBuildMap[name].mFunc = func;
+ sBuildMap[name].mFile = filename;
+ sGroupMap[name] = groupname.empty() ? name : groupname;
+ sGroupMap[groupname] = groupname; // for referencing directly by group name
+}
+
+//static
+bool LLFloaterReg::isRegistered(const std::string& name)
+{
+ return sBuildMap.find(name) != sBuildMap.end();
+}
+
+//static
+LLFloater* LLFloaterReg::getLastFloaterInGroup(const std::string& name)
+{
+ const std::string& groupname = sGroupMap[name];
+ if (!groupname.empty())
+ {
+ instance_list_t& list = sInstanceMap[groupname];
+ if (!list.empty())
+ {
+ for (instance_list_t::reverse_iterator iter = list.rbegin(); iter != list.rend(); ++iter)
+ {
+ LLFloater* inst = *iter;
+
+ if (inst->getVisible() && !inst->isMinimized())
+ {
+ return inst;
+ }
+ }
+ }
+ }
+ 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)))
+ {
+ if (candidate_rect.mTop > inst->getRect().mTop)
+ {
+ candidate_floater = inst;
+ candidate_rect = inst->getRect();
+ }
+ }
+ }
+ }
+
+ return candidate_floater;
+}
+
+//static
+LLFloater* LLFloaterReg::findInstance(const std::string& name, const LLSD& key)
+{
+ LLFloater* res = NULL;
+ const std::string& groupname = sGroupMap[name];
+ if (!groupname.empty())
+ {
+ instance_list_t& list = sInstanceMap[groupname];
+ for (instance_list_t::iterator iter = list.begin(); iter != list.end(); ++iter)
+ {
+ LLFloater* inst = *iter;
+ if (inst->matchesKey(key))
+ {
+ res = inst;
+ break;
+ }
+ }
+ }
+ return res;
+}
+
+//static
+LLFloater* LLFloaterReg::getInstance(const std::string& name, const LLSD& key)
+{
+ LLFloater* res = findInstance(name, key);
+ if (!res)
+ {
+ const LLFloaterBuildFunc& build_func = sBuildMap[name].mFunc;
+ const std::string& xui_file = sBuildMap[name].mFile;
+ if (build_func)
+ {
+ const std::string& groupname = sGroupMap[name];
+ if (!groupname.empty())
+ {
+ instance_list_t& list = sInstanceMap[groupname];
+
+ res = build_func(key);
+ if (!res)
+ {
+ LL_WARNS() << "Failed to build floater type: '" << name << "'." << LL_ENDL;
+ return NULL;
+ }
+ bool success = res->buildFromFile(xui_file);
+ if (!success)
+ {
+ 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
+ if (res->mKey.isUndefined())
+ {
+ 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)
+ {
+ LL_WARNS() << "Floater type: '" << name << "' not registered." << LL_ENDL;
+ }
+ }
+ return res;
+}
+
+//static
+LLFloater* LLFloaterReg::removeInstance(const std::string& name, const LLSD& key)
+{
+ LLFloater* res = NULL;
+ const std::string& groupname = sGroupMap[name];
+ if (!groupname.empty())
+ {
+ instance_list_t& list = sInstanceMap[groupname];
+ for (instance_list_t::iterator iter = list.begin(); iter != list.end(); ++iter)
+ {
+ LLFloater* inst = *iter;
+ if (inst->matchesKey(key))
+ {
+ res = inst;
+ list.erase(iter);
+ break;
+ }
+ }
+ }
+ return res;
+}
+
+//static
+// returns true if the instance existed
+bool LLFloaterReg::destroyInstance(const std::string& name, const LLSD& key)
+{
+ LLFloater* inst = removeInstance(name, key);
+ if (inst)
+ {
+ delete inst;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// Iterators
+//static
+LLFloaterReg::const_instance_list_t& LLFloaterReg::getFloaterList(const std::string& name)
+{
+ instance_map_t::iterator iter = sInstanceMap.find(name);
+ if (iter != sInstanceMap.end())
+ {
+ return iter->second;
+ }
+ else
+ {
+ return sNullInstanceList;
+ }
+}
+
+// Visibility Management
+
+//static
+LLFloater* LLFloaterReg::showInstance(const std::string& name, const LLSD& key, bool focus)
+{
+ if( sBlockShowFloaters
+ // see EXT-7090
+ && sAlwaysShowableList.find(name) == sAlwaysShowableList.end())
+ return 0;//
+ LLFloater* instance = getInstance(name, key);
+ if (instance)
+ {
+ instance->openFloater(key);
+ if (focus)
+ instance->setFocus(true);
+ }
+ return instance;
+}
+
+//static
+// returns true if the instance exists
+bool LLFloaterReg::hideInstance(const std::string& name, const LLSD& key)
+{
+ LLFloater* instance = findInstance(name, key);
+ if (instance)
+ {
+ instance->closeHostedFloater();
+ }
+ return (instance != NULL);
+}
+
+//static
+// returns true if the instance is visible when completed
+bool LLFloaterReg::toggleInstance(const std::string& name, const LLSD& key)
+{
+ LLFloater* instance = findInstance(name, key);
+ if (instance && instance->isShown())
+ {
+ instance->closeHostedFloater();
+ return false;
+ }
+
+ instance = showInstance(name, key, true);
+
+ return instance != nullptr;
+}
+
+//static
+// returns true if the instance exists and is visible (doesnt matter minimized or not)
+bool LLFloaterReg::instanceVisible(const std::string& name, const LLSD& key)
+{
+ LLFloater* instance = findInstance(name, key);
+ return LLFloater::isVisible(instance);
+}
+
+//static
+void LLFloaterReg::showInitialVisibleInstances()
+{
+ // Iterate through alll registered instance names and show any with a save visible state
+ for (build_map_t::iterator iter = sBuildMap.begin(); iter != sBuildMap.end(); ++iter)
+ {
+ const std::string& name = iter->first;
+ std::string controlname = getVisibilityControlName(name);
+ if (LLFloater::getControlGroup()->controlExists(controlname))
+ {
+ bool isvis = LLFloater::getControlGroup()->getBOOL(controlname);
+ if (isvis)
+ {
+ showInstance(name, LLSD()); // keyed floaters shouldn't set save_vis to true
+ }
+ }
+ }
+}
+
+//static
+void LLFloaterReg::hideVisibleInstances(const std::set<std::string>& exceptions)
+{
+ // Iterate through alll active instances and hide them
+ for (instance_map_t::iterator iter = sInstanceMap.begin(); iter != sInstanceMap.end(); ++iter)
+ {
+ const std::string& name = iter->first;
+ if (exceptions.find(name) != exceptions.end())
+ continue;
+ instance_list_t& list = iter->second;
+ for (instance_list_t::iterator iter = list.begin(); iter != list.end(); ++iter)
+ {
+ LLFloater* floater = *iter;
+ floater->pushVisible(false);
+ }
+ }
+}
+
+//static
+void LLFloaterReg::restoreVisibleInstances()
+{
+ // Iterate through all active instances and restore visibility
+ for (instance_map_t::iterator iter = sInstanceMap.begin(); iter != sInstanceMap.end(); ++iter)
+ {
+ instance_list_t& list = iter->second;
+ for (instance_list_t::iterator iter = list.begin(); iter != list.end(); ++iter)
+ {
+ LLFloater* floater = *iter;
+ floater->popVisible();
+ }
+ }
+}
+
+//static
+std::string LLFloaterReg::getRectControlName(const std::string& name)
+{
+ return std::string("floater_rect_") + getBaseControlName(name);
+}
+
+//static
+std::string LLFloaterReg::declareRectControl(const std::string& name)
+{
+ std::string controlname = getRectControlName(name);
+ LLFloater::getControlGroup()->declareRect(controlname, LLRect(),
+ 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)
+{
+ 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()),
+ LLControlVariable::PERSIST_NONDFT);
+ return controlname;
+}
+
+//static
+std::string LLFloaterReg::declareDockStateControl(const std::string& name)
+{
+ std::string controlname = getDockStateControlName(name);
+ LLFloater::getControlGroup()->declareBOOL(controlname, true,
+ llformat("Window Docking state for %s", name.c_str()),
+ LLControlVariable::PERSIST_NONDFT);
+ return controlname;
+
+}
+
+//static
+std::string LLFloaterReg::getDockStateControlName(const std::string& name)
+{
+ std::string res = std::string("floater_dock_") + name;
+ LLStringUtil::replaceChar( res, ' ', '_' );
+ return res;
+}
+
+
+//static
+void LLFloaterReg::registerControlVariables()
+{
+ // Iterate through alll registered instance names and register rect and visibility control variables
+ for (build_map_t::iterator iter = sBuildMap.begin(); iter != sBuildMap.end(); ++iter)
+ {
+ const std::string& name = iter->first;
+ if (LLFloater::getControlGroup()->controlExists(getRectControlName(name)))
+ {
+ declareRectControl(name);
+ }
+ if (LLFloater::getControlGroup()->controlExists(getVisibilityControlName(name)))
+ {
+ declareVisibilityControl(name);
+ }
+ }
+
+ const LLSD& exclude_list = LLUI::getInstance()->mSettingGroups["config"]->getLLSD("always_showable_floaters");
+ for (LLSD::array_const_iterator iter = exclude_list.beginArray();
+ iter != exclude_list.endArray();
+ iter++)
+ {
+ sAlwaysShowableList.insert(iter->asString());
+ }
+}
+
+//static
+void LLFloaterReg::toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD& key)
+{
+ //
+ // 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);
+
+ 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
+ {
+ 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
+// Same as toggleInstanceOrBringToFront but does not close floater.
+// unlike showInstance() does not trigger onOpen() if already open
+void LLFloaterReg::showInstanceOrBringToFront(const LLSD& sdname, const LLSD& key)
+{
+ std::string name = sdname.asString();
+ LLFloater* instance = getInstance(name, key);
+
+
+ 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
+ {
+ 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);
+ }
+ }
+}
+
+// static
+U32 LLFloaterReg::getVisibleFloaterInstanceCount()
+{
+ U32 count = 0;
+
+ 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->isMinimized())
+ {
+ count++;
+ }
+ }
+ }
+
+ return count;
+}
diff --git a/indra/llui/llfloaterreg.h b/indra/llui/llfloaterreg.h
index 85d6ad6b12..988372e8fe 100644
--- a/indra/llui/llfloaterreg.h
+++ b/indra/llui/llfloaterreg.h
@@ -1,158 +1,158 @@
-/**
- * @file llfloaterreg.h
- * @brief LLFloaterReg Floater Registration Class
- *
- * $LicenseInfo:firstyear=2002&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 LLFLOATERREG_H
-#define LLFLOATERREG_H
-
-/// llcommon
-#include "llrect.h"
-#include "llsd.h"
-
-#include <list>
-#include <boost/function.hpp>
-
-//*******************************************************
-//
-// Floater Class Registry
-//
-
-class LLFloater;
-class LLUICtrl;
-
-typedef boost::function<LLFloater* (const LLSD& key)> LLFloaterBuildFunc;
-
-class LLFloaterReg
-{
-public:
- // We use a list of LLFloater's instead of a set for two reasons:
- // 1) With a list we have a predictable ordering, useful for finding the last opened floater of a given type.
- // 2) We can change the key of a floater without altering the list.
- typedef std::list<LLFloater*> instance_list_t;
- typedef const instance_list_t const_instance_list_t;
- typedef std::map<std::string, instance_list_t> instance_map_t;
-
- struct BuildData
- {
- LLFloaterBuildFunc mFunc;
- std::string mFile;
- };
- typedef std::map<std::string, BuildData> build_map_t;
-
-private:
- friend class LLFloaterRegListener;
- static instance_list_t sNullInstanceList;
- static instance_map_t sInstanceMap;
- static build_map_t sBuildMap;
- static std::map<std::string,std::string> sGroupMap;
- static bool sBlockShowFloaters;
- /**
- * Defines list of floater names that can be shown despite state of sBlockShowFloaters.
- */
- static std::set<std::string> sAlwaysShowableList;
-
-public:
- // Registration
-
- // usage: LLFloaterClassRegistry::add("foo", (LLFloaterBuildFunc)&LLFloaterClassRegistry::build<LLFloaterFoo>);
- template <class T>
- static LLFloater* build(const LLSD& key)
- {
- T* floater = new T(key);
- return floater;
- }
-
- static void add(const std::string& name, const std::string& file, const LLFloaterBuildFunc& func,
- const std::string& groupname = LLStringUtil::null);
- static bool isRegistered(const std::string& name);
-
- // Helpers
- 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());
- static LLFloater* getInstance(const std::string& name, const LLSD& key = LLSD());
- static LLFloater* removeInstance(const std::string& name, const LLSD& key = LLSD());
- static bool destroyInstance(const std::string& name, const LLSD& key = LLSD());
-
- // Iterators
- static const_instance_list_t& getFloaterList(const std::string& name);
-
- // Visibility Management
- // return NULL if instance not found or can't create instance (no builder)
- static LLFloater* showInstance(const std::string& name, const LLSD& key = LLSD(), bool focus = false);
- // Close a floater (may destroy or set invisible)
- // return false if can't find instance
- static bool hideInstance(const std::string& name, const LLSD& key = LLSD());
- // return true if instance is visible:
- static bool toggleInstance(const std::string& name, const LLSD& key = LLSD());
- static bool instanceVisible(const std::string& name, const LLSD& key = LLSD());
-
- static void showInitialVisibleInstances();
- static void hideVisibleInstances(const std::set<std::string>& exceptions = std::set<std::string>());
- static void restoreVisibleInstances();
-
- // 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 toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD& key = LLSD());
- static void showInstanceOrBringToFront(const LLSD& sdname, const LLSD& key = LLSD());
-
- // Typed find / get / show
- template <class T>
- static T* findTypedInstance(const std::string& name, const LLSD& key = LLSD())
- {
- return dynamic_cast<T*>(findInstance(name, key));
- }
-
- template <class T>
- static T* getTypedInstance(const std::string& name, const LLSD& key = LLSD())
- {
- return dynamic_cast<T*>(getInstance(name, key));
- }
-
- template <class T>
- static T* showTypedInstance(const std::string& name, const LLSD& key = LLSD(), bool focus = false)
- {
- return dynamic_cast<T*>(showInstance(name, key, focus));
- }
-
- static void blockShowFloaters(bool value) { sBlockShowFloaters = value;}
-
- static U32 getVisibleFloaterInstanceCount();
-};
-
-#endif
+/**
+ * @file llfloaterreg.h
+ * @brief LLFloaterReg Floater Registration Class
+ *
+ * $LicenseInfo:firstyear=2002&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 LLFLOATERREG_H
+#define LLFLOATERREG_H
+
+/// llcommon
+#include "llrect.h"
+#include "llsd.h"
+
+#include <list>
+#include <boost/function.hpp>
+
+//*******************************************************
+//
+// Floater Class Registry
+//
+
+class LLFloater;
+class LLUICtrl;
+
+typedef boost::function<LLFloater* (const LLSD& key)> LLFloaterBuildFunc;
+
+class LLFloaterReg
+{
+public:
+ // We use a list of LLFloater's instead of a set for two reasons:
+ // 1) With a list we have a predictable ordering, useful for finding the last opened floater of a given type.
+ // 2) We can change the key of a floater without altering the list.
+ typedef std::list<LLFloater*> instance_list_t;
+ typedef const instance_list_t const_instance_list_t;
+ typedef std::map<std::string, instance_list_t> instance_map_t;
+
+ struct BuildData
+ {
+ LLFloaterBuildFunc mFunc;
+ std::string mFile;
+ };
+ typedef std::map<std::string, BuildData> build_map_t;
+
+private:
+ friend class LLFloaterRegListener;
+ static instance_list_t sNullInstanceList;
+ static instance_map_t sInstanceMap;
+ static build_map_t sBuildMap;
+ static std::map<std::string,std::string> sGroupMap;
+ static bool sBlockShowFloaters;
+ /**
+ * Defines list of floater names that can be shown despite state of sBlockShowFloaters.
+ */
+ static std::set<std::string> sAlwaysShowableList;
+
+public:
+ // Registration
+
+ // usage: LLFloaterClassRegistry::add("foo", (LLFloaterBuildFunc)&LLFloaterClassRegistry::build<LLFloaterFoo>);
+ template <class T>
+ static LLFloater* build(const LLSD& key)
+ {
+ T* floater = new T(key);
+ return floater;
+ }
+
+ static void add(const std::string& name, const std::string& file, const LLFloaterBuildFunc& func,
+ const std::string& groupname = LLStringUtil::null);
+ static bool isRegistered(const std::string& name);
+
+ // Helpers
+ 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());
+ static LLFloater* getInstance(const std::string& name, const LLSD& key = LLSD());
+ static LLFloater* removeInstance(const std::string& name, const LLSD& key = LLSD());
+ static bool destroyInstance(const std::string& name, const LLSD& key = LLSD());
+
+ // Iterators
+ static const_instance_list_t& getFloaterList(const std::string& name);
+
+ // Visibility Management
+ // return NULL if instance not found or can't create instance (no builder)
+ static LLFloater* showInstance(const std::string& name, const LLSD& key = LLSD(), bool focus = false);
+ // Close a floater (may destroy or set invisible)
+ // return false if can't find instance
+ static bool hideInstance(const std::string& name, const LLSD& key = LLSD());
+ // return true if instance is visible:
+ static bool toggleInstance(const std::string& name, const LLSD& key = LLSD());
+ static bool instanceVisible(const std::string& name, const LLSD& key = LLSD());
+
+ static void showInitialVisibleInstances();
+ static void hideVisibleInstances(const std::set<std::string>& exceptions = std::set<std::string>());
+ static void restoreVisibleInstances();
+
+ // 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 toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD& key = LLSD());
+ static void showInstanceOrBringToFront(const LLSD& sdname, const LLSD& key = LLSD());
+
+ // Typed find / get / show
+ template <class T>
+ static T* findTypedInstance(const std::string& name, const LLSD& key = LLSD())
+ {
+ return dynamic_cast<T*>(findInstance(name, key));
+ }
+
+ template <class T>
+ static T* getTypedInstance(const std::string& name, const LLSD& key = LLSD())
+ {
+ return dynamic_cast<T*>(getInstance(name, key));
+ }
+
+ template <class T>
+ static T* showTypedInstance(const std::string& name, const LLSD& key = LLSD(), bool focus = false)
+ {
+ return dynamic_cast<T*>(showInstance(name, key, focus));
+ }
+
+ static void blockShowFloaters(bool value) { sBlockShowFloaters = value;}
+
+ static U32 getVisibleFloaterInstanceCount();
+};
+
+#endif
diff --git a/indra/llui/llfloaterreglistener.cpp b/indra/llui/llfloaterreglistener.cpp
index 7525b8cab3..aa3d1a1171 100644
--- a/indra/llui/llfloaterreglistener.cpp
+++ b/indra/llui/llfloaterreglistener.cpp
@@ -3,25 +3,25 @@
* @author Nat Goodspeed
* @date 2009-08-12
* @brief Implementation for llfloaterreglistener.
- *
+ *
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llui/llfloaterreglistener.h b/indra/llui/llfloaterreglistener.h
index 24311a2dfa..a36072892c 100644
--- a/indra/llui/llfloaterreglistener.h
+++ b/indra/llui/llfloaterreglistener.h
@@ -3,25 +3,25 @@
* @author Nat Goodspeed
* @date 2009-08-12
* @brief Wrap (subset of) LLFloaterReg API with an event API
- *
+ *
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llui/llflyoutbutton.cpp b/indra/llui/llflyoutbutton.cpp
index 392bfb8bf4..1682e195df 100644
--- a/indra/llui/llflyoutbutton.cpp
+++ b/indra/llui/llflyoutbutton.cpp
@@ -1,77 +1,77 @@
-/**
- * @file llflyoutbutton.cpp
- * @brief LLFlyoutButton base class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-// file includes
-#include "llflyoutbutton.h"
-
-//static LLDefaultChildRegistry::Register<LLFlyoutButton> r2("flyout_button");
-
-const S32 FLYOUT_BUTTON_ARROW_WIDTH = 24;
-
-LLFlyoutButton::LLFlyoutButton(const Params& p)
-: LLComboBox(p),
- mToggleState(false),
- mActionButton(NULL)
-{
- // Always use text box
- // Text label button
- LLButton::Params bp(p.action_button);
- bp.name(p.label);
- bp.label(p.label);
- bp.rect.left(0).bottom(0).width(getRect().getWidth() - FLYOUT_BUTTON_ARROW_WIDTH).height(getRect().getHeight());
- bp.click_callback.function(boost::bind(&LLFlyoutButton::onActionButtonClick, this, _2));
- bp.follows.flags(FOLLOWS_ALL);
-
- mActionButton = LLUICtrlFactory::create<LLButton>(bp);
- addChild(mActionButton);
-}
-
-void LLFlyoutButton::onActionButtonClick(const LLSD& data)
-{
- // remember last list selection?
- mList->deselect();
- onCommit();
-}
-
-void LLFlyoutButton::draw()
-{
- mActionButton->setToggleState(mToggleState);
- mButton->setToggleState(mToggleState);
-
- //FIXME: this should be an attribute of comboboxes, whether they have a distinct label or
- // the label reflects the last selected item, for now we have to manually remove the label
- setLabel(LLStringUtil::null);
- LLComboBox::draw();
-}
-
-void LLFlyoutButton::setToggleState(bool state)
-{
- mToggleState = state;
-}
-
-
+/**
+ * @file llflyoutbutton.cpp
+ * @brief LLFlyoutButton base class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+// file includes
+#include "llflyoutbutton.h"
+
+//static LLDefaultChildRegistry::Register<LLFlyoutButton> r2("flyout_button");
+
+const S32 FLYOUT_BUTTON_ARROW_WIDTH = 24;
+
+LLFlyoutButton::LLFlyoutButton(const Params& p)
+: LLComboBox(p),
+ mToggleState(false),
+ mActionButton(NULL)
+{
+ // Always use text box
+ // Text label button
+ LLButton::Params bp(p.action_button);
+ bp.name(p.label);
+ bp.label(p.label);
+ bp.rect.left(0).bottom(0).width(getRect().getWidth() - FLYOUT_BUTTON_ARROW_WIDTH).height(getRect().getHeight());
+ bp.click_callback.function(boost::bind(&LLFlyoutButton::onActionButtonClick, this, _2));
+ bp.follows.flags(FOLLOWS_ALL);
+
+ mActionButton = LLUICtrlFactory::create<LLButton>(bp);
+ addChild(mActionButton);
+}
+
+void LLFlyoutButton::onActionButtonClick(const LLSD& data)
+{
+ // remember last list selection?
+ mList->deselect();
+ onCommit();
+}
+
+void LLFlyoutButton::draw()
+{
+ mActionButton->setToggleState(mToggleState);
+ mButton->setToggleState(mToggleState);
+
+ //FIXME: this should be an attribute of comboboxes, whether they have a distinct label or
+ // the label reflects the last selected item, for now we have to manually remove the label
+ setLabel(LLStringUtil::null);
+ LLComboBox::draw();
+}
+
+void LLFlyoutButton::setToggleState(bool state)
+{
+ mToggleState = state;
+}
+
+
diff --git a/indra/llui/llflyoutbutton.h b/indra/llui/llflyoutbutton.h
index 15c7b4600f..3992bb1e01 100644
--- a/indra/llui/llflyoutbutton.h
+++ b/indra/llui/llflyoutbutton.h
@@ -1,68 +1,68 @@
-/**
- * @file llflyoutbutton.h
- * @brief LLFlyoutButton 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$
- */
-
-// A control that displays the name of the chosen item, which when clicked
-// shows a scrolling box of choices.
-
-#ifndef LL_LLFLYOUTBUTTON_H
-#define LL_LLFLYOUTBUTTON_H
-
-#include "llcombobox.h"
-
-// Classes
-
-class LLFlyoutButton : public LLComboBox
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLComboBox::Params>
- {
- Optional<LLButton::Params> action_button;
- Deprecated allow_text_entry;
-
- Params()
- : action_button("action_button"),
- allow_text_entry("allow_text_entry")
- {
- changeDefault(LLComboBox::Params::allow_text_entry, false);
- }
-
- };
-protected:
- LLFlyoutButton(const Params&);
- friend class LLUICtrlFactory;
-public:
- virtual void draw();
-
- void setToggleState(bool state);
-
- void onActionButtonClick(const LLSD& data);
-
-protected:
- LLButton* mActionButton;
- bool mToggleState;
-};
-
-#endif // LL_LLFLYOUTBUTTON_H
+/**
+ * @file llflyoutbutton.h
+ * @brief LLFlyoutButton 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$
+ */
+
+// A control that displays the name of the chosen item, which when clicked
+// shows a scrolling box of choices.
+
+#ifndef LL_LLFLYOUTBUTTON_H
+#define LL_LLFLYOUTBUTTON_H
+
+#include "llcombobox.h"
+
+// Classes
+
+class LLFlyoutButton : public LLComboBox
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLComboBox::Params>
+ {
+ Optional<LLButton::Params> action_button;
+ Deprecated allow_text_entry;
+
+ Params()
+ : action_button("action_button"),
+ allow_text_entry("allow_text_entry")
+ {
+ changeDefault(LLComboBox::Params::allow_text_entry, false);
+ }
+
+ };
+protected:
+ LLFlyoutButton(const Params&);
+ friend class LLUICtrlFactory;
+public:
+ virtual void draw();
+
+ void setToggleState(bool state);
+
+ void onActionButtonClick(const LLSD& data);
+
+protected:
+ LLButton* mActionButton;
+ bool mToggleState;
+};
+
+#endif // LL_LLFLYOUTBUTTON_H
diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp
index c0fdcf8bf6..02d02a15f4 100644
--- a/indra/llui/llfocusmgr.cpp
+++ b/indra/llui/llfocusmgr.cpp
@@ -1,509 +1,509 @@
-/**
- * @file llfocusmgr.cpp
- * @brief LLFocusMgr base class
- *
- * $LicenseInfo:firstyear=2002&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 "llfocusmgr.h"
-#include "lluictrl.h"
-#include "v4color.h"
-
-const F32 FOCUS_FADE_TIME = 0.3f;
-
-LLFocusableElement::LLFocusableElement()
-: mFocusLostCallback(NULL),
- mFocusReceivedCallback(NULL),
- mFocusChangedCallback(NULL),
- mTopLostCallback(NULL)
-{
-}
-
-// virtual
-bool LLFocusableElement::handleKey(KEY key, MASK mask, bool called_from_parent)
-{
- return false;
-}
-
-// virtual
-bool LLFocusableElement::handleKeyUp(KEY key, MASK mask, bool called_from_parent)
-{
- return false;
-}
-
-// virtual
-bool LLFocusableElement::handleUnicodeChar(llwchar uni_char, bool called_from_parent)
-{
- return false;
-}
-
-// virtual
-bool LLFocusableElement::wantsKeyUpKeyDown() const
-{
- return false;
-}
-
-//virtual
-bool LLFocusableElement::wantsReturnKey() const
-{
- return false;
-}
-
-// virtual
-LLFocusableElement::~LLFocusableElement()
-{
- delete mFocusLostCallback;
- delete mFocusReceivedCallback;
- delete mFocusChangedCallback;
- delete mTopLostCallback;
-}
-
-void LLFocusableElement::onFocusReceived()
-{
- if (mFocusReceivedCallback) (*mFocusReceivedCallback)(this);
- if (mFocusChangedCallback) (*mFocusChangedCallback)(this);
-}
-
-void LLFocusableElement::onFocusLost()
-{
- if (mFocusLostCallback) (*mFocusLostCallback)(this);
- if (mFocusChangedCallback) (*mFocusChangedCallback)(this);
-}
-
-void LLFocusableElement::onTopLost()
-{
- if (mTopLostCallback) (*mTopLostCallback)(this);
-}
-
-bool LLFocusableElement::hasFocus() const
-{
- return gFocusMgr.getKeyboardFocus() == this;
-}
-
-void LLFocusableElement::setFocus(bool b)
-{
-}
-
-boost::signals2::connection LLFocusableElement::setFocusLostCallback( const focus_signal_t::slot_type& cb)
-{
- if (!mFocusLostCallback) mFocusLostCallback = new focus_signal_t();
- return mFocusLostCallback->connect(cb);
-}
-
-boost::signals2::connection LLFocusableElement::setFocusReceivedCallback(const focus_signal_t::slot_type& cb)
-{
- if (!mFocusReceivedCallback) mFocusReceivedCallback = new focus_signal_t();
- return mFocusReceivedCallback->connect(cb);
-}
-
-boost::signals2::connection LLFocusableElement::setFocusChangedCallback(const focus_signal_t::slot_type& cb)
-{
- if (!mFocusChangedCallback) mFocusChangedCallback = new focus_signal_t();
- return mFocusChangedCallback->connect(cb);
-}
-
-boost::signals2::connection LLFocusableElement::setTopLostCallback(const focus_signal_t::slot_type& cb)
-{
- if (!mTopLostCallback) mTopLostCallback = new focus_signal_t();
- return mTopLostCallback->connect(cb);
-}
-
-
-
-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()
-: mLockedView( NULL ),
- mMouseCaptor( NULL ),
- mKeyboardFocus( NULL ),
- mLastKeyboardFocus( NULL ),
- mDefaultKeyboardFocus( NULL ),
- mKeystrokesOnly(false),
- mTopCtrl( NULL ),
- 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 )
-{
- if( childHasMouseCapture( view ) )
- {
- setMouseCapture( NULL );
- }
-
- if( childHasKeyboardFocus( view ))
- {
- if (view == mLockedView)
- {
- mLockedView = NULL;
- setKeyboardFocus( NULL );
- }
- else
- {
- setKeyboardFocus( mLockedView );
- }
- }
-
- LLUI::getInstance()->removePopup(view);
-}
-
-void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, bool lock, bool keystrokes_only)
-{
- // notes if keyboard focus is changed again (by onFocusLost/onFocusReceived)
- // making the rest of our processing unnecessary since it will already be
- // handled by the recursive call
- static bool focus_dirty;
- focus_dirty = false;
-
- if (mLockedView &&
- (new_focus == NULL ||
- (new_focus != mLockedView
- && dynamic_cast<LLView*>(new_focus)
- && !dynamic_cast<LLView*>(new_focus)->hasAncestor(mLockedView))))
- {
- // don't allow focus to go to anything that is not the locked focus
- // or one of its descendants
- return;
- }
-
- mKeystrokesOnly = keystrokes_only;
-
- if( new_focus != mKeyboardFocus )
- {
- mLastKeyboardFocus = mKeyboardFocus;
- mKeyboardFocus = new_focus;
-
- // list of the focus and it's ancestors
- 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
- for (LLView* ctrl = dynamic_cast<LLView*>(mKeyboardFocus); ctrl; ctrl = ctrl->getParent())
- {
- new_focus_list.push_back(ctrl->getHandle());
- }
-
- // remove all common ancestors since their focus is unchanged
- while (!new_focus_list.empty() &&
- !old_focus_list.empty() &&
- new_focus_list.back() == old_focus_list.back())
- {
- new_focus_list.pop_back();
- old_focus_list.pop_back();
- }
-
- // walk up the old focus branch calling onFocusLost
- // we bubble up the tree to release focus, and back down to add
- for (view_handle_list_t::iterator old_focus_iter = old_focus_list.begin();
- old_focus_iter != old_focus_list.end() && !focus_dirty;
- old_focus_iter++)
- {
- LLView* old_focus_view = old_focus_iter->get();
- if (old_focus_view)
- {
- mImpl->mCachedKeyboardFocusList.pop_front();
- old_focus_view->onFocusLost();
- }
- }
-
- // walk down the new focus branch calling onFocusReceived
- for (view_handle_list_t::reverse_iterator new_focus_riter = new_focus_list.rbegin();
- new_focus_riter != new_focus_list.rend() && !focus_dirty;
- new_focus_riter++)
- {
- LLView* new_focus_view = new_focus_riter->get();
- if (new_focus_view)
- {
- mImpl->mCachedKeyboardFocusList.push_front(new_focus_view->getHandle());
- new_focus_view->onFocusReceived();
- }
- }
-
- // if focus was changed as part of an onFocusLost or onFocusReceived call
- // stop iterating on current list since it is now invalid
- if (focus_dirty)
- {
- return;
- }
-
- // If we've got a default keyboard focus, and the caller is
- // releasing keyboard focus, move to the default.
- if (mDefaultKeyboardFocus != NULL && mKeyboardFocus == NULL)
- {
- mDefaultKeyboardFocus->setFocus(true);
- }
-
- LLView* focus_subtree = dynamic_cast<LLView*>(mKeyboardFocus);
- LLView* viewp = dynamic_cast<LLView*>(mKeyboardFocus);
- // find root-most focus root
- while(viewp)
- {
- if (viewp->isFocusRoot())
- {
- focus_subtree = viewp;
- }
- viewp = viewp->getParent();
- }
-
-
- if (focus_subtree)
- {
- LLView* focused_view = dynamic_cast<LLView*>(mKeyboardFocus);
- mImpl->mFocusHistory[focus_subtree->getHandle()] = focused_view ? focused_view->getHandle() : LLHandle<LLView>();
- }
- }
-
- if (lock)
- {
- lockFocus();
- }
-
- focus_dirty = true;
-}
-
-
-// Returns true is parent or any descedent of parent has keyboard focus.
-bool LLFocusMgr::childHasKeyboardFocus(const LLView* parent ) const
-{
- LLView* focus_view = dynamic_cast<LLView*>(mKeyboardFocus);
- while( focus_view )
- {
- if( focus_view == parent )
- {
- return true;
- }
- focus_view = focus_view->getParent();
- }
- return false;
-}
-
-// Returns true is parent or any descedent of parent is the mouse captor.
-bool LLFocusMgr::childHasMouseCapture( const LLView* parent ) const
-{
- if( mMouseCaptor && dynamic_cast<LLView*>(mMouseCaptor) != NULL )
- {
- LLView* captor_view = (LLView*)mMouseCaptor;
- while( captor_view )
- {
- if( captor_view == parent )
- {
- return true;
- }
- captor_view = captor_view->getParent();
- }
- }
- return false;
-}
-
-void LLFocusMgr::removeKeyboardFocusWithoutCallback( const LLFocusableElement* focus )
-{
- // should be ok to unlock here, as you have to know the locked view
- // in order to unlock it
- if (focus == mLockedView)
- {
- mLockedView = NULL;
- }
-
- if( mKeyboardFocus == focus )
- {
- mKeyboardFocus = NULL;
- }
-}
-
-bool LLFocusMgr::keyboardFocusHasAccelerators() const
-{
- LLView* focus_view = dynamic_cast<LLView*>(mKeyboardFocus);
- while( focus_view )
- {
- if(focus_view->hasAccelerators())
- {
- return true;
- }
-
- focus_view = focus_view->getParent();
- }
- return false;
-}
-
-void LLFocusMgr::setMouseCapture( LLMouseHandler* new_captor )
-{
- if( new_captor != mMouseCaptor )
- {
- LLMouseHandler* old_captor = mMouseCaptor;
- mMouseCaptor = new_captor;
-
- if (LLView::sDebugMouseHandling)
- {
- if (new_captor)
- {
- LL_INFOS() << "New mouse captor: " << new_captor->getName() << LL_ENDL;
- }
- else
- {
- LL_INFOS() << "New mouse captor: NULL" << LL_ENDL;
- }
- }
-
- if( old_captor )
- {
- old_captor->onMouseCaptureLost();
- }
-
- }
-}
-
-void LLFocusMgr::removeMouseCaptureWithoutCallback( const LLMouseHandler* captor )
-{
- if( mMouseCaptor == captor )
- {
- mMouseCaptor = NULL;
- }
-}
-
-
-bool LLFocusMgr::childIsTopCtrl( const LLView* parent ) const
-{
- LLView* top_view = (LLView*)mTopCtrl;
- while( top_view )
- {
- if( top_view == parent )
- {
- return true;
- }
- top_view = top_view->getParent();
- }
- return false;
-}
-
-
-
-// set new_top = NULL to release top_view.
-void LLFocusMgr::setTopCtrl( LLUICtrl* new_top )
-{
- LLUICtrl* old_top = mTopCtrl;
- if( new_top != old_top )
- {
- mTopCtrl = new_top;
-
- if (old_top)
- {
- old_top->onTopLost();
- }
- }
-}
-
-void LLFocusMgr::removeTopCtrlWithoutCallback( const LLUICtrl* top_view )
-{
- if( mTopCtrl == top_view )
- {
- mTopCtrl = NULL;
- }
-}
-
-void LLFocusMgr::lockFocus()
-{
- mLockedView = dynamic_cast<LLUICtrl*>(mKeyboardFocus);
-}
-
-void LLFocusMgr::unlockFocus()
-{
- mLockedView = NULL;
-}
-
-F32 LLFocusMgr::getFocusFlashAmt() const
-{
- return clamp_rescale(mFocusFlashTimer.getElapsedTimeF32(), 0.f, FOCUS_FADE_TIME, 1.f, 0.f);
-}
-
-LLColor4 LLFocusMgr::getFocusColor() const
-{
- static LLUIColor focus_color_cached = LLUIColorTable::instance().getColor("FocusColor");
- LLColor4 focus_color = lerp(focus_color_cached, LLColor4::white, getFocusFlashAmt());
- // de-emphasize keyboard focus when app has lost focus (to avoid typing into wrong window problem)
- if (!mAppHasFocus)
- {
- focus_color.mV[VALPHA] *= 0.4f;
- }
- return focus_color;
-}
-
-void LLFocusMgr::triggerFocusFlash()
-{
- mFocusFlashTimer.reset();
-}
-
-void LLFocusMgr::setAppHasFocus(bool focus)
-{
- if (!mAppHasFocus && focus)
- {
- triggerFocusFlash();
- }
-
- // release focus from "top ctrl"s, which generally hides them
- if (!focus)
- {
- LLUI::getInstance()->clearPopups();
- }
- mAppHasFocus = focus;
-}
-
-LLView* LLFocusMgr::getLastFocusForGroup(LLView* subtree_root) const
-{
- if (subtree_root)
- {
- 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 found_it->second.get();
- }
- }
- return NULL;
-}
-
-void LLFocusMgr::clearLastFocusForGroup(LLView* subtree_root)
-{
- if (subtree_root)
- {
- mImpl->mFocusHistory.erase(subtree_root->getHandle());
- }
-}
+/**
+ * @file llfocusmgr.cpp
+ * @brief LLFocusMgr base class
+ *
+ * $LicenseInfo:firstyear=2002&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 "llfocusmgr.h"
+#include "lluictrl.h"
+#include "v4color.h"
+
+const F32 FOCUS_FADE_TIME = 0.3f;
+
+LLFocusableElement::LLFocusableElement()
+: mFocusLostCallback(NULL),
+ mFocusReceivedCallback(NULL),
+ mFocusChangedCallback(NULL),
+ mTopLostCallback(NULL)
+{
+}
+
+// virtual
+bool LLFocusableElement::handleKey(KEY key, MASK mask, bool called_from_parent)
+{
+ return false;
+}
+
+// virtual
+bool LLFocusableElement::handleKeyUp(KEY key, MASK mask, bool called_from_parent)
+{
+ return false;
+}
+
+// virtual
+bool LLFocusableElement::handleUnicodeChar(llwchar uni_char, bool called_from_parent)
+{
+ return false;
+}
+
+// virtual
+bool LLFocusableElement::wantsKeyUpKeyDown() const
+{
+ return false;
+}
+
+//virtual
+bool LLFocusableElement::wantsReturnKey() const
+{
+ return false;
+}
+
+// virtual
+LLFocusableElement::~LLFocusableElement()
+{
+ delete mFocusLostCallback;
+ delete mFocusReceivedCallback;
+ delete mFocusChangedCallback;
+ delete mTopLostCallback;
+}
+
+void LLFocusableElement::onFocusReceived()
+{
+ if (mFocusReceivedCallback) (*mFocusReceivedCallback)(this);
+ if (mFocusChangedCallback) (*mFocusChangedCallback)(this);
+}
+
+void LLFocusableElement::onFocusLost()
+{
+ if (mFocusLostCallback) (*mFocusLostCallback)(this);
+ if (mFocusChangedCallback) (*mFocusChangedCallback)(this);
+}
+
+void LLFocusableElement::onTopLost()
+{
+ if (mTopLostCallback) (*mTopLostCallback)(this);
+}
+
+bool LLFocusableElement::hasFocus() const
+{
+ return gFocusMgr.getKeyboardFocus() == this;
+}
+
+void LLFocusableElement::setFocus(bool b)
+{
+}
+
+boost::signals2::connection LLFocusableElement::setFocusLostCallback( const focus_signal_t::slot_type& cb)
+{
+ if (!mFocusLostCallback) mFocusLostCallback = new focus_signal_t();
+ return mFocusLostCallback->connect(cb);
+}
+
+boost::signals2::connection LLFocusableElement::setFocusReceivedCallback(const focus_signal_t::slot_type& cb)
+{
+ if (!mFocusReceivedCallback) mFocusReceivedCallback = new focus_signal_t();
+ return mFocusReceivedCallback->connect(cb);
+}
+
+boost::signals2::connection LLFocusableElement::setFocusChangedCallback(const focus_signal_t::slot_type& cb)
+{
+ if (!mFocusChangedCallback) mFocusChangedCallback = new focus_signal_t();
+ return mFocusChangedCallback->connect(cb);
+}
+
+boost::signals2::connection LLFocusableElement::setTopLostCallback(const focus_signal_t::slot_type& cb)
+{
+ if (!mTopLostCallback) mTopLostCallback = new focus_signal_t();
+ return mTopLostCallback->connect(cb);
+}
+
+
+
+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()
+: mLockedView( NULL ),
+ mMouseCaptor( NULL ),
+ mKeyboardFocus( NULL ),
+ mLastKeyboardFocus( NULL ),
+ mDefaultKeyboardFocus( NULL ),
+ mKeystrokesOnly(false),
+ mTopCtrl( NULL ),
+ 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 )
+{
+ if( childHasMouseCapture( view ) )
+ {
+ setMouseCapture( NULL );
+ }
+
+ if( childHasKeyboardFocus( view ))
+ {
+ if (view == mLockedView)
+ {
+ mLockedView = NULL;
+ setKeyboardFocus( NULL );
+ }
+ else
+ {
+ setKeyboardFocus( mLockedView );
+ }
+ }
+
+ LLUI::getInstance()->removePopup(view);
+}
+
+void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, bool lock, bool keystrokes_only)
+{
+ // notes if keyboard focus is changed again (by onFocusLost/onFocusReceived)
+ // making the rest of our processing unnecessary since it will already be
+ // handled by the recursive call
+ static bool focus_dirty;
+ focus_dirty = false;
+
+ if (mLockedView &&
+ (new_focus == NULL ||
+ (new_focus != mLockedView
+ && dynamic_cast<LLView*>(new_focus)
+ && !dynamic_cast<LLView*>(new_focus)->hasAncestor(mLockedView))))
+ {
+ // don't allow focus to go to anything that is not the locked focus
+ // or one of its descendants
+ return;
+ }
+
+ mKeystrokesOnly = keystrokes_only;
+
+ if( new_focus != mKeyboardFocus )
+ {
+ mLastKeyboardFocus = mKeyboardFocus;
+ mKeyboardFocus = new_focus;
+
+ // list of the focus and it's ancestors
+ 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
+ for (LLView* ctrl = dynamic_cast<LLView*>(mKeyboardFocus); ctrl; ctrl = ctrl->getParent())
+ {
+ new_focus_list.push_back(ctrl->getHandle());
+ }
+
+ // remove all common ancestors since their focus is unchanged
+ while (!new_focus_list.empty() &&
+ !old_focus_list.empty() &&
+ new_focus_list.back() == old_focus_list.back())
+ {
+ new_focus_list.pop_back();
+ old_focus_list.pop_back();
+ }
+
+ // walk up the old focus branch calling onFocusLost
+ // we bubble up the tree to release focus, and back down to add
+ for (view_handle_list_t::iterator old_focus_iter = old_focus_list.begin();
+ old_focus_iter != old_focus_list.end() && !focus_dirty;
+ old_focus_iter++)
+ {
+ LLView* old_focus_view = old_focus_iter->get();
+ if (old_focus_view)
+ {
+ mImpl->mCachedKeyboardFocusList.pop_front();
+ old_focus_view->onFocusLost();
+ }
+ }
+
+ // walk down the new focus branch calling onFocusReceived
+ for (view_handle_list_t::reverse_iterator new_focus_riter = new_focus_list.rbegin();
+ new_focus_riter != new_focus_list.rend() && !focus_dirty;
+ new_focus_riter++)
+ {
+ LLView* new_focus_view = new_focus_riter->get();
+ if (new_focus_view)
+ {
+ mImpl->mCachedKeyboardFocusList.push_front(new_focus_view->getHandle());
+ new_focus_view->onFocusReceived();
+ }
+ }
+
+ // if focus was changed as part of an onFocusLost or onFocusReceived call
+ // stop iterating on current list since it is now invalid
+ if (focus_dirty)
+ {
+ return;
+ }
+
+ // If we've got a default keyboard focus, and the caller is
+ // releasing keyboard focus, move to the default.
+ if (mDefaultKeyboardFocus != NULL && mKeyboardFocus == NULL)
+ {
+ mDefaultKeyboardFocus->setFocus(true);
+ }
+
+ LLView* focus_subtree = dynamic_cast<LLView*>(mKeyboardFocus);
+ LLView* viewp = dynamic_cast<LLView*>(mKeyboardFocus);
+ // find root-most focus root
+ while(viewp)
+ {
+ if (viewp->isFocusRoot())
+ {
+ focus_subtree = viewp;
+ }
+ viewp = viewp->getParent();
+ }
+
+
+ if (focus_subtree)
+ {
+ LLView* focused_view = dynamic_cast<LLView*>(mKeyboardFocus);
+ mImpl->mFocusHistory[focus_subtree->getHandle()] = focused_view ? focused_view->getHandle() : LLHandle<LLView>();
+ }
+ }
+
+ if (lock)
+ {
+ lockFocus();
+ }
+
+ focus_dirty = true;
+}
+
+
+// Returns true is parent or any descedent of parent has keyboard focus.
+bool LLFocusMgr::childHasKeyboardFocus(const LLView* parent ) const
+{
+ LLView* focus_view = dynamic_cast<LLView*>(mKeyboardFocus);
+ while( focus_view )
+ {
+ if( focus_view == parent )
+ {
+ return true;
+ }
+ focus_view = focus_view->getParent();
+ }
+ return false;
+}
+
+// Returns true is parent or any descedent of parent is the mouse captor.
+bool LLFocusMgr::childHasMouseCapture( const LLView* parent ) const
+{
+ if( mMouseCaptor && dynamic_cast<LLView*>(mMouseCaptor) != NULL )
+ {
+ LLView* captor_view = (LLView*)mMouseCaptor;
+ while( captor_view )
+ {
+ if( captor_view == parent )
+ {
+ return true;
+ }
+ captor_view = captor_view->getParent();
+ }
+ }
+ return false;
+}
+
+void LLFocusMgr::removeKeyboardFocusWithoutCallback( const LLFocusableElement* focus )
+{
+ // should be ok to unlock here, as you have to know the locked view
+ // in order to unlock it
+ if (focus == mLockedView)
+ {
+ mLockedView = NULL;
+ }
+
+ if( mKeyboardFocus == focus )
+ {
+ mKeyboardFocus = NULL;
+ }
+}
+
+bool LLFocusMgr::keyboardFocusHasAccelerators() const
+{
+ LLView* focus_view = dynamic_cast<LLView*>(mKeyboardFocus);
+ while( focus_view )
+ {
+ if(focus_view->hasAccelerators())
+ {
+ return true;
+ }
+
+ focus_view = focus_view->getParent();
+ }
+ return false;
+}
+
+void LLFocusMgr::setMouseCapture( LLMouseHandler* new_captor )
+{
+ if( new_captor != mMouseCaptor )
+ {
+ LLMouseHandler* old_captor = mMouseCaptor;
+ mMouseCaptor = new_captor;
+
+ if (LLView::sDebugMouseHandling)
+ {
+ if (new_captor)
+ {
+ LL_INFOS() << "New mouse captor: " << new_captor->getName() << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS() << "New mouse captor: NULL" << LL_ENDL;
+ }
+ }
+
+ if( old_captor )
+ {
+ old_captor->onMouseCaptureLost();
+ }
+
+ }
+}
+
+void LLFocusMgr::removeMouseCaptureWithoutCallback( const LLMouseHandler* captor )
+{
+ if( mMouseCaptor == captor )
+ {
+ mMouseCaptor = NULL;
+ }
+}
+
+
+bool LLFocusMgr::childIsTopCtrl( const LLView* parent ) const
+{
+ LLView* top_view = (LLView*)mTopCtrl;
+ while( top_view )
+ {
+ if( top_view == parent )
+ {
+ return true;
+ }
+ top_view = top_view->getParent();
+ }
+ return false;
+}
+
+
+
+// set new_top = NULL to release top_view.
+void LLFocusMgr::setTopCtrl( LLUICtrl* new_top )
+{
+ LLUICtrl* old_top = mTopCtrl;
+ if( new_top != old_top )
+ {
+ mTopCtrl = new_top;
+
+ if (old_top)
+ {
+ old_top->onTopLost();
+ }
+ }
+}
+
+void LLFocusMgr::removeTopCtrlWithoutCallback( const LLUICtrl* top_view )
+{
+ if( mTopCtrl == top_view )
+ {
+ mTopCtrl = NULL;
+ }
+}
+
+void LLFocusMgr::lockFocus()
+{
+ mLockedView = dynamic_cast<LLUICtrl*>(mKeyboardFocus);
+}
+
+void LLFocusMgr::unlockFocus()
+{
+ mLockedView = NULL;
+}
+
+F32 LLFocusMgr::getFocusFlashAmt() const
+{
+ return clamp_rescale(mFocusFlashTimer.getElapsedTimeF32(), 0.f, FOCUS_FADE_TIME, 1.f, 0.f);
+}
+
+LLColor4 LLFocusMgr::getFocusColor() const
+{
+ static LLUIColor focus_color_cached = LLUIColorTable::instance().getColor("FocusColor");
+ LLColor4 focus_color = lerp(focus_color_cached, LLColor4::white, getFocusFlashAmt());
+ // de-emphasize keyboard focus when app has lost focus (to avoid typing into wrong window problem)
+ if (!mAppHasFocus)
+ {
+ focus_color.mV[VALPHA] *= 0.4f;
+ }
+ return focus_color;
+}
+
+void LLFocusMgr::triggerFocusFlash()
+{
+ mFocusFlashTimer.reset();
+}
+
+void LLFocusMgr::setAppHasFocus(bool focus)
+{
+ if (!mAppHasFocus && focus)
+ {
+ triggerFocusFlash();
+ }
+
+ // release focus from "top ctrl"s, which generally hides them
+ if (!focus)
+ {
+ LLUI::getInstance()->clearPopups();
+ }
+ mAppHasFocus = focus;
+}
+
+LLView* LLFocusMgr::getLastFocusForGroup(LLView* subtree_root) const
+{
+ if (subtree_root)
+ {
+ 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 found_it->second.get();
+ }
+ }
+ return NULL;
+}
+
+void LLFocusMgr::clearLastFocusForGroup(LLView* subtree_root)
+{
+ if (subtree_root)
+ {
+ mImpl->mFocusHistory.erase(subtree_root->getHandle());
+ }
+}
diff --git a/indra/llui/llfocusmgr.h b/indra/llui/llfocusmgr.h
index 3276135faf..6bdd3e8e9a 100644
--- a/indra/llui/llfocusmgr.h
+++ b/indra/llui/llfocusmgr.h
@@ -1,160 +1,160 @@
-/**
- * @file llfocusmgr.h
- * @brief LLFocusMgr base class
- *
- * $LicenseInfo:firstyear=2002&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$
- */
-
-// Singleton that manages keyboard and mouse focus
-
-#ifndef LL_LLFOCUSMGR_H
-#define LL_LLFOCUSMGR_H
-
-#include "llstring.h"
-#include "llframetimer.h"
-#include "llui.h"
-
-class LLUICtrl;
-class LLMouseHandler;
-class LLView;
-
-// NOTE: the LLFocusableElement class declaration has been moved here from lluictrl.h.
-class LLFocusableElement
-{
- friend class LLFocusMgr; // allow access to focus change handlers
-public:
- LLFocusableElement();
- virtual ~LLFocusableElement();
-
- virtual void setFocus( bool b );
- virtual bool hasFocus() const;
-
- typedef boost::signals2::signal<void(LLFocusableElement*)> focus_signal_t;
-
- boost::signals2::connection setFocusLostCallback( const focus_signal_t::slot_type& cb);
- boost::signals2::connection setFocusReceivedCallback(const focus_signal_t::slot_type& cb);
- boost::signals2::connection setFocusChangedCallback(const focus_signal_t::slot_type& cb);
- boost::signals2::connection setTopLostCallback(const focus_signal_t::slot_type& cb);
-
- // These were brought up the hierarchy from LLView so that we don't have to use dynamic_cast when dealing with keyboard focus.
- virtual bool handleKey(KEY key, MASK mask, bool called_from_parent);
- virtual bool handleKeyUp(KEY key, MASK mask, bool called_from_parent);
- virtual bool handleUnicodeChar(llwchar uni_char, bool called_from_parent);
-
- /**
- * If true this LLFocusableElement wants to receive KEYUP and KEYDOWN messages
- * even for normal character strokes.
- * Default implementation returns false.
- */
- virtual bool wantsKeyUpKeyDown() const;
- virtual bool wantsReturnKey() const;
-
- virtual void onTopLost(); // called when registered as top ctrl and user clicks elsewhere
-protected:
- virtual void onFocusReceived();
- virtual void onFocusLost();
- focus_signal_t* mFocusLostCallback;
- focus_signal_t* mFocusReceivedCallback;
- focus_signal_t* mFocusChangedCallback;
- focus_signal_t* mTopLostCallback;
-};
-
-
-class LLFocusMgr
-{
-public:
- LLFocusMgr();
- ~LLFocusMgr();
-
- // Mouse Captor
- void setMouseCapture(LLMouseHandler* new_captor); // new_captor = NULL to release the mouse.
- LLMouseHandler* getMouseCapture() const { return mMouseCaptor; }
- void removeMouseCaptureWithoutCallback( const LLMouseHandler* captor );
- bool childHasMouseCapture( const LLView* parent ) const;
-
- // Keyboard Focus
- void setKeyboardFocus(LLFocusableElement* new_focus, bool lock = false, bool keystrokes_only = false); // new_focus = NULL to release the focus.
- LLFocusableElement* getKeyboardFocus() const { return mKeyboardFocus; }
- LLFocusableElement* getLastKeyboardFocus() const { return mLastKeyboardFocus; }
- bool childHasKeyboardFocus( const LLView* parent ) const;
- void removeKeyboardFocusWithoutCallback( const LLFocusableElement* focus );
- bool getKeystrokesOnly() { return mKeystrokesOnly; }
- void setKeystrokesOnly(bool keystrokes_only) { mKeystrokesOnly = keystrokes_only; }
-
- F32 getFocusFlashAmt() const;
- 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);
- LLView* getLastFocusForGroup(LLView* subtree_root) const;
- void clearLastFocusForGroup(LLView* subtree_root);
-
- // If setKeyboardFocus(NULL) is called, and there is a non-NULL default
- // keyboard focus view, focus goes there. JC
- void setDefaultKeyboardFocus(LLFocusableElement* default_focus) { mDefaultKeyboardFocus = default_focus; }
- LLFocusableElement* getDefaultKeyboardFocus() const { return mDefaultKeyboardFocus; }
-
-
- // Top View
- void setTopCtrl(LLUICtrl* new_top);
- LLUICtrl* getTopCtrl() const { return mTopCtrl; }
- void removeTopCtrlWithoutCallback( const LLUICtrl* top_view );
- bool childIsTopCtrl( const LLView* parent ) const;
-
- // All Three
- void releaseFocusIfNeeded( LLView* top_view );
- void lockFocus();
- void unlockFocus();
- bool focusLocked() const { return mLockedView != NULL; }
-
- bool keyboardFocusHasAccelerators() const;
-
- struct Impl;
-
-private:
- LLUICtrl* mLockedView;
-
- // Mouse Captor
- LLMouseHandler* mMouseCaptor; // Mouse events are premptively routed to this object
-
- // Keyboard Focus
- LLFocusableElement* mKeyboardFocus; // Keyboard events are preemptively routed to this object
- LLFocusableElement* mLastKeyboardFocus; // who last had focus
- LLFocusableElement* mDefaultKeyboardFocus;
- bool mKeystrokesOnly;
-
- // Top View
- LLUICtrl* mTopCtrl;
-
- LLFrameTimer mFocusFlashTimer;
-
- bool mAppHasFocus;
-
- Impl * mImpl;
-};
-
-extern LLFocusMgr gFocusMgr;
-
-#endif // LL_LLFOCUSMGR_H
-
-
+/**
+ * @file llfocusmgr.h
+ * @brief LLFocusMgr base class
+ *
+ * $LicenseInfo:firstyear=2002&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$
+ */
+
+// Singleton that manages keyboard and mouse focus
+
+#ifndef LL_LLFOCUSMGR_H
+#define LL_LLFOCUSMGR_H
+
+#include "llstring.h"
+#include "llframetimer.h"
+#include "llui.h"
+
+class LLUICtrl;
+class LLMouseHandler;
+class LLView;
+
+// NOTE: the LLFocusableElement class declaration has been moved here from lluictrl.h.
+class LLFocusableElement
+{
+ friend class LLFocusMgr; // allow access to focus change handlers
+public:
+ LLFocusableElement();
+ virtual ~LLFocusableElement();
+
+ virtual void setFocus( bool b );
+ virtual bool hasFocus() const;
+
+ typedef boost::signals2::signal<void(LLFocusableElement*)> focus_signal_t;
+
+ boost::signals2::connection setFocusLostCallback( const focus_signal_t::slot_type& cb);
+ boost::signals2::connection setFocusReceivedCallback(const focus_signal_t::slot_type& cb);
+ boost::signals2::connection setFocusChangedCallback(const focus_signal_t::slot_type& cb);
+ boost::signals2::connection setTopLostCallback(const focus_signal_t::slot_type& cb);
+
+ // These were brought up the hierarchy from LLView so that we don't have to use dynamic_cast when dealing with keyboard focus.
+ virtual bool handleKey(KEY key, MASK mask, bool called_from_parent);
+ virtual bool handleKeyUp(KEY key, MASK mask, bool called_from_parent);
+ virtual bool handleUnicodeChar(llwchar uni_char, bool called_from_parent);
+
+ /**
+ * If true this LLFocusableElement wants to receive KEYUP and KEYDOWN messages
+ * even for normal character strokes.
+ * Default implementation returns false.
+ */
+ virtual bool wantsKeyUpKeyDown() const;
+ virtual bool wantsReturnKey() const;
+
+ virtual void onTopLost(); // called when registered as top ctrl and user clicks elsewhere
+protected:
+ virtual void onFocusReceived();
+ virtual void onFocusLost();
+ focus_signal_t* mFocusLostCallback;
+ focus_signal_t* mFocusReceivedCallback;
+ focus_signal_t* mFocusChangedCallback;
+ focus_signal_t* mTopLostCallback;
+};
+
+
+class LLFocusMgr
+{
+public:
+ LLFocusMgr();
+ ~LLFocusMgr();
+
+ // Mouse Captor
+ void setMouseCapture(LLMouseHandler* new_captor); // new_captor = NULL to release the mouse.
+ LLMouseHandler* getMouseCapture() const { return mMouseCaptor; }
+ void removeMouseCaptureWithoutCallback( const LLMouseHandler* captor );
+ bool childHasMouseCapture( const LLView* parent ) const;
+
+ // Keyboard Focus
+ void setKeyboardFocus(LLFocusableElement* new_focus, bool lock = false, bool keystrokes_only = false); // new_focus = NULL to release the focus.
+ LLFocusableElement* getKeyboardFocus() const { return mKeyboardFocus; }
+ LLFocusableElement* getLastKeyboardFocus() const { return mLastKeyboardFocus; }
+ bool childHasKeyboardFocus( const LLView* parent ) const;
+ void removeKeyboardFocusWithoutCallback( const LLFocusableElement* focus );
+ bool getKeystrokesOnly() { return mKeystrokesOnly; }
+ void setKeystrokesOnly(bool keystrokes_only) { mKeystrokesOnly = keystrokes_only; }
+
+ F32 getFocusFlashAmt() const;
+ 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);
+ LLView* getLastFocusForGroup(LLView* subtree_root) const;
+ void clearLastFocusForGroup(LLView* subtree_root);
+
+ // If setKeyboardFocus(NULL) is called, and there is a non-NULL default
+ // keyboard focus view, focus goes there. JC
+ void setDefaultKeyboardFocus(LLFocusableElement* default_focus) { mDefaultKeyboardFocus = default_focus; }
+ LLFocusableElement* getDefaultKeyboardFocus() const { return mDefaultKeyboardFocus; }
+
+
+ // Top View
+ void setTopCtrl(LLUICtrl* new_top);
+ LLUICtrl* getTopCtrl() const { return mTopCtrl; }
+ void removeTopCtrlWithoutCallback( const LLUICtrl* top_view );
+ bool childIsTopCtrl( const LLView* parent ) const;
+
+ // All Three
+ void releaseFocusIfNeeded( LLView* top_view );
+ void lockFocus();
+ void unlockFocus();
+ bool focusLocked() const { return mLockedView != NULL; }
+
+ bool keyboardFocusHasAccelerators() const;
+
+ struct Impl;
+
+private:
+ LLUICtrl* mLockedView;
+
+ // Mouse Captor
+ LLMouseHandler* mMouseCaptor; // Mouse events are premptively routed to this object
+
+ // Keyboard Focus
+ LLFocusableElement* mKeyboardFocus; // Keyboard events are preemptively routed to this object
+ LLFocusableElement* mLastKeyboardFocus; // who last had focus
+ LLFocusableElement* mDefaultKeyboardFocus;
+ bool mKeystrokesOnly;
+
+ // Top View
+ LLUICtrl* mTopCtrl;
+
+ LLFrameTimer mFocusFlashTimer;
+
+ bool mAppHasFocus;
+
+ Impl * mImpl;
+};
+
+extern LLFocusMgr gFocusMgr;
+
+#endif // LL_LLFOCUSMGR_H
+
+
diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp
index 0ac04e374d..c01e811477 100644
--- a/indra/llui/llfolderview.cpp
+++ b/indra/llui/llfolderview.cpp
@@ -1,2116 +1,2129 @@
-/**
- * @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)
-{ }
-
-//---------------------------------------------------------------------------
-
-void LLAllDescendentsPassedFilter::doFolder(LLFolderViewFolder* folder)
-{
- mAllDescendentsPassedFilter &= (folder) && (folder->passedFilter()) && (folder->descendantsPassedFilter());
-}
-
-void LLAllDescendentsPassedFilter::doItem(LLFolderViewItem* item)
-{
- mAllDescendentsPassedFilter &= (item) && (item->passedFilter());
-}
-
-///----------------------------------------------------------------------------
-/// 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),
- allow_drag("allow_drag", true),
- show_empty_message("show_empty_message", true),
- suppress_folder_menu("suppress_folder_menu", false),
- use_ellipses("use_ellipses", false),
- options_menu("options_menu", "")
-{
- folder_indentation = -4;
-}
-
-
-// Default constructor
-LLFolderView::LLFolderView(const Params& p)
-: LLFolderViewFolder(p),
- mScrollContainer( NULL ),
- mPopupMenuHandle(),
- mMenuFileName(p.options_menu),
- mAllowMultiSelect(p.allow_multiselect),
- mAllowDrag(p.allow_drag),
- mShowEmptyMessage(p.show_empty_message),
- mShowFolderHierarchy(false),
- mRenameItem( NULL ),
- mNeedsScroll( false ),
- mUseLabelSuffix(p.use_label_suffix),
- mSuppressFolderMenu(p.suppress_folder_menu),
- mPinningSelectedItem(false),
- mNeedsAutoSelect( false ),
- mAutoSelectOverride(false),
- mNeedsAutoRename(false),
- mShowSelectionContext(false),
- mShowSingleSelection(false),
- mArrangeGeneration(0),
- mSignalSelectCallback(0),
- mMinWidth(0),
- mDragAndDropThisFrame(false),
- mCallbackRegistrar(NULL),
- mEnableRegistrar(NULL),
- mUseEllipses(p.use_ellipses),
- mDraggingOverItem(NULL),
- mStatusTextBox(NULL),
- mShowItemLinkOverlays(p.show_item_link_overlays),
- mViewModel(p.view_model),
- mGroupedItemModel(p.grouped_item_model),
- mForceArrange(false),
- mSingleFolderMode(false)
-{
- 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);
-
- mViewModelItem->openItem();
-
- mAreChildrenInited = true; // root folder is a special case due to not being loaded normally, assume that it's inited.
-}
-
-// 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();
- mPopupMenuHandle.markDead();
-
- 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);
-}
-
-void LLFolderView::filter( LLFolderViewFilter& filter )
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
- static LLCachedControl<S32> time_visible(*LLUI::getInstance()->mSettingGroups["config"], "FilterItemsMaxTimePerFrameVisible", 10);
- static LLCachedControl<S32> time_invisible(*LLUI::getInstance()->mSettingGroups["config"], "FilterItemsMaxTimePerFrameUnvisible", 1);
- filter.resetTime(llclamp((mParentPanel.get()->getVisible() ? time_visible() : time_invisible()), 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;
-}
-
-void LLFolderView::sanitizeSelection()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
- // 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();
- mNeedsScroll = false;
-}
-
-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();
- }
-
- static LLCachedControl<F32> type_ahead_timeout(*LLUI::getInstance()->mSettingGroups["config"], "TypeAheadTimeout", 1.5f);
- if (mSearchTimer.getElapsedTimeF32() > type_ahead_timeout || !mSearchString.size())
- {
- mSearchString.clear();
- }
-
- if (hasVisibleChildren())
- {
- mStatusTextBox->setVisible( false );
- }
- else if (mShowEmptyMessage)
- {
- mStatusTextBox->setValue(getFolderViewModel()->getStatusText(mItems.empty() && mFolders.empty()));
- 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);
- }
- }
-
- if (mRenameItem
- && mRenamer
- && mRenamer->getVisible()
- && !getVisibleRect().overlaps(mRenamer->getRect()))
- {
- // renamer is not connected to the item we are renaming in any form so manage it manually
- // TODO: consider stopping on any scroll action instead of when out of visible area
- LL_DEBUGS("Inventory") << "Renamer out of bounds, hiding" << LL_ENDL;
- finishRenamingItem();
- }
-
- // 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::getInstance()->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);
- if(!item->isSingleFolderMode())
- {
- 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();
- }
- }
-
- // 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 )
-{
- LL_DEBUGS("Inventory") << "Starting inventory renamer" << LL_ENDL;
-
- // 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::getInstance()->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();
- if(last_selected && last_selected->isSingleFolderMode())
- {
- handled = false;
- break;
- }
- 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::getInstance()->mSettingGroups["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 = static_cast<LLMenuGL*>(mPopupMenuHandle.get());
- if (!menu)
- {
- if (mCallbackRegistrar)
- {
- mCallbackRegistrar->pushScope();
- }
- if (mEnableRegistrar)
- {
- mEnableRegistrar->pushScope();
- }
- llassert(LLMenuGL::sMenuContainer != NULL);
- menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>(mMenuFileName, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
- if (!menu)
- {
- menu = LLUICtrlFactory::getDefaultWidget<LLMenuGL>("inventory_menu");
- }
- menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor"));
- mPopupMenuHandle = menu->getHandle();
- if (mEnableRegistrar)
- {
- mEnableRegistrar->popScope();
- }
- if (mCallbackRegistrar)
- {
- mCallbackRegistrar->popScope();
- }
- }
-
- bool item_clicked{ false };
- for (const auto item : mSelectedItems)
- {
- item_clicked |= item->getRect().pointInRect(x, y);
- }
- if(!item_clicked && mSingleFolderMode)
- {
- clearSelection();
- }
- bool hide_folder_menu = mSuppressFolderMenu && isFolderSelected();
- if (menu && (mSingleFolderMode || (handled
- && ( count > 0 && (hasVisibleChildren()) ))) && // show menu only if selected items are visible
- !hide_folder_menu)
- {
- if (mCallbackRegistrar)
- {
- mCallbackRegistrar->pushScope();
- }
- if (mEnableRegistrar)
- {
- mEnableRegistrar->pushScope();
- }
-
- updateMenuOptions(menu);
-
- menu->updateParent(LLMenuGL::sMenuContainer);
- LLMenuGL::showPopup(this, menu, x, y);
- if (mEnableRegistrar)
- {
- mEnableRegistrar->popScope();
- }
- 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 );
-}
-
-LLFolderViewItem* LLFolderView::getHoveredItem() const
-{
- return dynamic_cast<LLFolderViewItem*>(mHoveredItem.get());
-}
-
-void LLFolderView::setHoveredItem(LLFolderViewItem* itemp)
-{
- if (mHoveredItem.get() != itemp)
- {
- if (itemp)
- mHoveredItem = itemp->getHandle();
- else
- mHoveredItem.markDead();
- }
-}
-
-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.markDead();
- 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_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_PROFILE_ZONE_SCOPED_CATEGORY_UI; //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
- bool filter_modified = filter_object.isModified();
- if (filter_modified && (!filter_object.isTimedOut()))
- {
- filter_object.clearModified();
- }
-
- // automatically show matching items, and select first one if we had a selection
- if (mNeedsAutoSelect)
- {
- // select new item only if a filtered item not currently selected and there was a selection
- 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_modified));
- 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() || mForceArrange;
-
- //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)
- {
- LLFolderViewItem* scroll_to_item = mSelectedItems.back();
- scrollToShowItem(scroll_to_item, constraint_rect);
- // continue scrolling until animated layout change is done
- bool selected_filter_finished = getRoot()->getViewModelItem()->getLastFilterGeneration() >= filter_object.getFirstSuccessGeneration();
- if (selected_filter_finished && scroll_to_item && scroll_to_item->getViewModelItem())
- {
- selected_filter_finished = scroll_to_item->getViewModelItem()->getLastFilterGeneration() >= filter_object.getFirstSuccessGeneration();
- }
- if (filter_finished && selected_filter_finished)
- {
- bool needs_arrange = needsArrange() || getRoot()->needsArrange();
- if (mParentFolder)
- {
- needs_arrange |= (bool)mParentFolder->needsArrange();
- }
- if (!needs_arrange || !is_visible)
- {
- mNeedsScroll = false;
- }
- }
- }
-
- if (mSelectedItems.size())
- {
- LLFolderViewItem* item = mSelectedItems.back();
- // If the goal is to show renamer, don't callback untill
- // item is visible or is no longer being scrolled to.
- // Otherwise renamer will be instantly closed
- // Todo: consider moving renamer out of selection callback
- if (!mNeedsAutoRename || !mNeedsScroll || item->getVisible())
- {
- 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;
- }
- }
- else
- {
- 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::getInstance()->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;
- }
-
- if(mSingleFolderMode && (mSelectedItems.size() == 0))
- {
- buildContextMenu(*menu, flags);
- }
-
- // 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::isFolderSelected()
-{
- selected_items_t::iterator item_iter;
- for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter)
- {
- LLFolderViewFolder* folder = dynamic_cast<LLFolderViewFolder*>(*item_iter);
- if (folder != NULL)
- {
- return true;
- }
- }
- return false;
-}
-
-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() const
-{
- if(!hasVisibleChildren())
-{
- //We need to display status textbox, let's reserve some place for it
- return llmax(0, mStatusTextBox->getTextPixelHeight());
-}
- return 0;
-}
+/**
+ * @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)
+{ }
+
+//---------------------------------------------------------------------------
+
+void LLAllDescendentsPassedFilter::doFolder(LLFolderViewFolder* folder)
+{
+ mAllDescendentsPassedFilter &= (folder) && (folder->passedFilter()) && (folder->descendantsPassedFilter());
+}
+
+void LLAllDescendentsPassedFilter::doItem(LLFolderViewItem* item)
+{
+ mAllDescendentsPassedFilter &= (item) && (item->passedFilter());
+}
+
+///----------------------------------------------------------------------------
+/// 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),
+ allow_drag("allow_drag", true),
+ show_empty_message("show_empty_message", true),
+ suppress_folder_menu("suppress_folder_menu", false),
+ use_ellipses("use_ellipses", false),
+ options_menu("options_menu", "")
+{
+ folder_indentation = -4;
+}
+
+
+// Default constructor
+LLFolderView::LLFolderView(const Params& p)
+: LLFolderViewFolder(p),
+ mScrollContainer( NULL ),
+ mPopupMenuHandle(),
+ mMenuFileName(p.options_menu),
+ mAllowMultiSelect(p.allow_multiselect),
+ mAllowDrag(p.allow_drag),
+ mShowEmptyMessage(p.show_empty_message),
+ mShowFolderHierarchy(false),
+ mRenameItem( NULL ),
+ mNeedsScroll( false ),
+ mUseLabelSuffix(p.use_label_suffix),
+ mSuppressFolderMenu(p.suppress_folder_menu),
+ mPinningSelectedItem(false),
+ mNeedsAutoSelect( false ),
+ mAutoSelectOverride(false),
+ mNeedsAutoRename(false),
+ mShowSelectionContext(false),
+ mShowSingleSelection(false),
+ mArrangeGeneration(0),
+ mSignalSelectCallback(0),
+ mMinWidth(0),
+ mDragAndDropThisFrame(false),
+ mCallbackRegistrar(NULL),
+ mEnableRegistrar(NULL),
+ mUseEllipses(p.use_ellipses),
+ mDraggingOverItem(NULL),
+ mStatusTextBox(NULL),
+ mShowItemLinkOverlays(p.show_item_link_overlays),
+ mViewModel(p.view_model),
+ mGroupedItemModel(p.grouped_item_model),
+ mForceArrange(false),
+ mSingleFolderMode(false)
+{
+ 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.prevalidator(&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);
+
+ mViewModelItem->openItem();
+
+ mAreChildrenInited = true; // root folder is a special case due to not being loaded normally, assume that it's inited.
+}
+
+// Destroys the object
+LLFolderView::~LLFolderView( void )
+{
+ mRenamerTopLostSignalConnection.disconnect();
+ if (mRenamer)
+ {
+ // instead of using closeRenamer remove it directly,
+ // since it might already be hidden
+ LLUI::getInstance()->removePopup(mRenamer);
+ }
+
+ // 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();
+ mPopupMenuHandle.markDead();
+
+ 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);
+}
+
+void LLFolderView::filter( LLFolderViewFilter& filter )
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+ const S32 TIME_VISIBLE = 10; // in milliseconds
+ const S32 TIME_INVISIBLE = 1;
+ filter.resetTime(llclamp((mParentPanel.get()->getVisible() ? TIME_VISIBLE : TIME_INVISIBLE), 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;
+}
+
+void LLFolderView::sanitizeSelection()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+ // 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();
+ mNeedsScroll = false;
+}
+
+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();
+ }
+
+ static LLCachedControl<F32> type_ahead_timeout(*LLUI::getInstance()->mSettingGroups["config"], "TypeAheadTimeout", 1.5f);
+ if (mSearchTimer.getElapsedTimeF32() > type_ahead_timeout || !mSearchString.size())
+ {
+ mSearchString.clear();
+ }
+
+ if (hasVisibleChildren())
+ {
+ mStatusTextBox->setVisible( false );
+ }
+ else if (mShowEmptyMessage)
+ {
+ mStatusTextBox->setValue(getFolderViewModel()->getStatusText(mItems.empty() && mFolders.empty()));
+ 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);
+ }
+ }
+
+ if (mRenameItem
+ && mRenamer
+ && mRenamer->getVisible()
+ && !getVisibleRect().overlaps(mRenamer->getRect()))
+ {
+ // renamer is not connected to the item we are renaming in any form so manage it manually
+ // TODO: consider stopping on any scroll action instead of when out of visible area
+ LL_DEBUGS("Inventory") << "Renamer out of bounds, hiding" << LL_ENDL;
+ finishRenamingItem();
+ }
+
+ // 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::getInstance()->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_DEBUGS() << "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);
+ if(!item->isSingleFolderMode())
+ {
+ 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();
+ }
+ }
+
+ // 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 )
+{
+ LL_DEBUGS("Inventory") << "Starting inventory renamer" << LL_ENDL;
+
+ // 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 );
+ if (!mRenamerTopLostSignalConnection.connected())
+ {
+ mRenamerTopLostSignalConnection = mRenamer->setTopLostCallback(boost::bind(&LLFolderView::onRenamerLost, this));
+ }
+ LLUI::getInstance()->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();
+ if(last_selected && last_selected->isSingleFolderMode())
+ {
+ handled = false;
+ break;
+ }
+ 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::getInstance()->mSettingGroups["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 = static_cast<LLMenuGL*>(mPopupMenuHandle.get());
+ if (!menu)
+ {
+ if (mCallbackRegistrar)
+ {
+ mCallbackRegistrar->pushScope();
+ }
+ if (mEnableRegistrar)
+ {
+ mEnableRegistrar->pushScope();
+ }
+ llassert(LLMenuGL::sMenuContainer != NULL);
+ menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>(mMenuFileName, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
+ if (!menu)
+ {
+ menu = LLUICtrlFactory::getDefaultWidget<LLMenuGL>("inventory_menu");
+ }
+ menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor"));
+ mPopupMenuHandle = menu->getHandle();
+ if (mEnableRegistrar)
+ {
+ mEnableRegistrar->popScope();
+ }
+ if (mCallbackRegistrar)
+ {
+ mCallbackRegistrar->popScope();
+ }
+ }
+
+ bool item_clicked{ false };
+ for (const auto item : mSelectedItems)
+ {
+ item_clicked |= item->getRect().pointInRect(x, y);
+ }
+ if(!item_clicked && mSingleFolderMode)
+ {
+ clearSelection();
+ }
+ bool hide_folder_menu = mSuppressFolderMenu && isFolderSelected();
+ if (menu && (mSingleFolderMode || (handled
+ && ( count > 0 && (hasVisibleChildren()) ))) && // show menu only if selected items are visible
+ !hide_folder_menu)
+ {
+ if (mCallbackRegistrar)
+ {
+ mCallbackRegistrar->pushScope();
+ }
+ if (mEnableRegistrar)
+ {
+ mEnableRegistrar->pushScope();
+ }
+
+ updateMenuOptions(menu);
+
+ menu->updateParent(LLMenuGL::sMenuContainer);
+ LLMenuGL::showPopup(this, menu, x, y);
+ if (mEnableRegistrar)
+ {
+ mEnableRegistrar->popScope();
+ }
+ 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 );
+}
+
+LLFolderViewItem* LLFolderView::getHoveredItem() const
+{
+ return dynamic_cast<LLFolderViewItem*>(mHoveredItem.get());
+}
+
+void LLFolderView::setHoveredItem(LLFolderViewItem* itemp)
+{
+ if (mHoveredItem.get() != itemp)
+ {
+ if (itemp)
+ mHoveredItem = itemp->getHandle();
+ else
+ mHoveredItem.markDead();
+ }
+}
+
+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()
+{
+ mRenamerTopLostSignalConnection.disconnect();
+ if (mRenamer)
+ {
+ LLUI::getInstance()->removePopup(mRenamer);
+ }
+ if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die();
+ mPopupMenuHandle.markDead();
+ 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_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_PROFILE_ZONE_SCOPED_CATEGORY_UI; //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
+ bool filter_modified = filter_object.isModified();
+ if (filter_modified && (!filter_object.isTimedOut()))
+ {
+ filter_object.clearModified();
+ }
+
+ // automatically show matching items, and select first one if we had a selection
+ if (mNeedsAutoSelect)
+ {
+ // select new item only if a filtered item not currently selected and there was a selection
+ 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_modified));
+ 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() || mForceArrange;
+
+ //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)
+ {
+ LLFolderViewItem* scroll_to_item = mSelectedItems.back();
+ scrollToShowItem(scroll_to_item, constraint_rect);
+ // continue scrolling until animated layout change is done
+ bool selected_filter_finished = getRoot()->getViewModelItem()->getLastFilterGeneration() >= filter_object.getFirstSuccessGeneration();
+ if (selected_filter_finished && scroll_to_item && scroll_to_item->getViewModelItem())
+ {
+ selected_filter_finished = scroll_to_item->getViewModelItem()->getLastFilterGeneration() >= filter_object.getFirstSuccessGeneration();
+ }
+ if (filter_finished && selected_filter_finished)
+ {
+ bool needs_arrange = needsArrange() || getRoot()->needsArrange();
+ if (mParentFolder)
+ {
+ needs_arrange |= (bool)mParentFolder->needsArrange();
+ }
+ if (!needs_arrange || !is_visible)
+ {
+ mNeedsScroll = false;
+ }
+ }
+ }
+
+ if (mSelectedItems.size())
+ {
+ LLFolderViewItem* item = mSelectedItems.back();
+ // If the goal is to show renamer, don't callback untill
+ // item is visible or is no longer being scrolled to.
+ // Otherwise renamer will be instantly closed
+ // Todo: consider moving renamer out of selection callback
+ if (!mNeedsAutoRename || !mNeedsScroll || item->getVisible())
+ {
+ 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;
+ }
+ }
+ else
+ {
+ 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::getInstance()->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;
+ }
+
+ if(mSingleFolderMode && (mSelectedItems.size() == 0))
+ {
+ buildContextMenu(*menu, flags);
+ }
+
+ // 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::isFolderSelected()
+{
+ selected_items_t::iterator item_iter;
+ for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter)
+ {
+ LLFolderViewFolder* folder = dynamic_cast<LLFolderViewFolder*>(*item_iter);
+ if (folder != NULL)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+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() const
+{
+ 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
index 3c604503f6..7c3167c156 100644
--- a/indra/llui/llfolderview.h
+++ b/indra/llui/llfolderview.h
@@ -1,446 +1,448 @@
-/**
- * @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,
- allow_drag,
- show_empty_message,
- use_ellipses,
- show_item_link_overlays,
- suppress_folder_menu;
- 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; }
- bool getAllowDrag() { return mAllowDrag; }
-
- void setSingleFolderMode(bool is_single_mode) { mSingleFolderMode = is_single_mode; }
- bool isSingleFolderMode() { return mSingleFolderMode; }
-
- // 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() const;
-
- void arrangeAll() { mArrangeGeneration++; }
- S32 getArrangeGeneration() { return mArrangeGeneration; }
-
- // applies filters to control visibility of items
- virtual void filter( LLFolderViewFilter& filter);
-
- void clearHoveredItem() { setHoveredItem(nullptr); }
- LLFolderViewItem* getHoveredItem() const;
- void setHoveredItem(LLFolderViewItem* itemp);
-
- // 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 stopAutoScollining() {mNeedsScroll = false;}
- 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; }
- void setEnableRegistrar(LLUICtrl::EnableCallbackRegistry::ScopedRegistrar* registrar) { mEnableRegistrar = registrar; }
-
- void setForceArrange(bool force) { mForceArrange = force; }
-
- LLPanel* getParentPanel() { return mParentPanel.get(); }
- // DEBUG only
- void dumpSelectionInformation();
-
- virtual S32 notify(const LLSD& info) ;
-
- void setShowEmptyMessage(bool show_msg) { mShowEmptyMessage = show_msg; }
-
- bool useLabelSuffix() { return mUseLabelSuffix; }
- virtual void updateMenu();
-
- void finishRenamingItem( void );
-
- // 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 closeRenamer( void );
-
- bool isFolderSelected();
-
- bool selectFirstItem();
- bool selectLastItem();
-
- bool addNoOptions(LLMenuGL* menu) const;
-
-
-protected:
- LLHandle<LLView> mPopupMenuHandle;
- std::string mMenuFileName;
-
- LLHandle<LLView> mHoveredItem;
- selected_items_t mSelectedItems;
- bool mKeyboardSelection,
- mAllowMultiSelect,
- mAllowDrag,
- mShowEmptyMessage,
- mShowFolderHierarchy,
- mNeedsScroll,
- mPinningSelectedItem,
- mNeedsAutoSelect,
- mAutoSelectOverride,
- mNeedsAutoRename,
- mUseLabelSuffix,
- mDragAndDropThisFrame,
- mShowItemLinkOverlays,
- mShowSelectionContext,
- mShowSingleSelection,
- mSuppressFolderMenu,
- mSingleFolderMode;
-
- // 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;
- LLUICtrl::EnableCallbackRegistry::ScopedRegistrar* mEnableRegistrar;
-
- bool mForceArrange;
-
-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);
-};
-
-class LLAllDescendentsPassedFilter : public LLFolderViewFunctor
-{
-public:
- LLAllDescendentsPassedFilter() : mAllDescendentsPassedFilter(true) {}
- /*virtual*/ ~LLAllDescendentsPassedFilter() {}
- /*virtual*/ void doFolder(LLFolderViewFolder* folder);
- /*virtual*/ void doItem(LLFolderViewItem* item);
- bool allDescendentsPassedFilter() const { return mAllDescendentsPassedFilter; }
-protected:
- bool mAllDescendentsPassedFilter;
-};
-
-// 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
+/**
+ * @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,
+ allow_drag,
+ show_empty_message,
+ use_ellipses,
+ show_item_link_overlays,
+ suppress_folder_menu;
+ 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; }
+ bool getAllowDrag() { return mAllowDrag; }
+
+ void setSingleFolderMode(bool is_single_mode) { mSingleFolderMode = is_single_mode; }
+ bool isSingleFolderMode() { return mSingleFolderMode; }
+
+ // 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() const;
+
+ void arrangeAll() { mArrangeGeneration++; }
+ S32 getArrangeGeneration() { return mArrangeGeneration; }
+
+ // applies filters to control visibility of items
+ virtual void filter( LLFolderViewFilter& filter);
+
+ void clearHoveredItem() { setHoveredItem(nullptr); }
+ LLFolderViewItem* getHoveredItem() const;
+ void setHoveredItem(LLFolderViewItem* itemp);
+
+ // 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 stopAutoScollining() {mNeedsScroll = false;}
+ 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; }
+ void setEnableRegistrar(LLUICtrl::EnableCallbackRegistry::ScopedRegistrar* registrar) { mEnableRegistrar = registrar; }
+
+ void setForceArrange(bool force) { mForceArrange = force; }
+
+ LLPanel* getParentPanel() { return mParentPanel.get(); }
+ // DEBUG only
+ void dumpSelectionInformation();
+
+ virtual S32 notify(const LLSD& info) ;
+
+ void setShowEmptyMessage(bool show_msg) { mShowEmptyMessage = show_msg; }
+
+ bool useLabelSuffix() { return mUseLabelSuffix; }
+ virtual void updateMenu();
+
+ void finishRenamingItem( void );
+
+ // 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 closeRenamer( void );
+
+ bool isFolderSelected();
+
+ bool selectFirstItem();
+ bool selectLastItem();
+
+ bool addNoOptions(LLMenuGL* menu) const;
+
+
+protected:
+ LLHandle<LLView> mPopupMenuHandle;
+ std::string mMenuFileName;
+
+ LLHandle<LLView> mHoveredItem;
+ selected_items_t mSelectedItems;
+ bool mKeyboardSelection,
+ mAllowMultiSelect,
+ mAllowDrag,
+ mShowEmptyMessage,
+ mShowFolderHierarchy,
+ mNeedsScroll,
+ mPinningSelectedItem,
+ mNeedsAutoSelect,
+ mAutoSelectOverride,
+ mNeedsAutoRename,
+ mUseLabelSuffix,
+ mDragAndDropThisFrame,
+ mShowItemLinkOverlays,
+ mShowSelectionContext,
+ mShowSingleSelection,
+ mSuppressFolderMenu,
+ mSingleFolderMode;
+
+ // 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;
+ LLUICtrl::EnableCallbackRegistry::ScopedRegistrar* mEnableRegistrar;
+
+ boost::signals2::connection mRenamerTopLostSignalConnection;
+
+ bool mForceArrange;
+
+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);
+};
+
+class LLAllDescendentsPassedFilter : public LLFolderViewFunctor
+{
+public:
+ LLAllDescendentsPassedFilter() : mAllDescendentsPassedFilter(true) {}
+ /*virtual*/ ~LLAllDescendentsPassedFilter() {}
+ /*virtual*/ void doFolder(LLFolderViewFolder* folder);
+ /*virtual*/ void doItem(LLFolderViewItem* item);
+ bool allDescendentsPassedFilter() const { return mAllDescendentsPassedFilter; }
+protected:
+ bool mAllDescendentsPassedFilter;
+};
+
+// 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
index 311056f541..2bbf23c4bf 100644
--- a/indra/llui/llfolderviewitem.cpp
+++ b/indra/llui/llfolderviewitem.cpp
@@ -1,2370 +1,2368 @@
-/**
-* @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 "llcallbacklist.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_wear("allow_wear", true),
- allow_drop("allow_drop", 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),
- single_folder_mode("single_folder_mode", false),
- double_click_override("double_click_override", false),
- 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),
- mSuffixNeedsRefresh(false),
- mLabelPaddingRight(DEFAULT_LABEL_PADDING_RIGHT),
- mParentFolder( NULL ),
- mIsSelected( false ),
- mIsCurSelection( false ),
- mSelectPending(false),
- mIsItemCut(false),
- mCutGeneration(0),
- 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),
- mAllowWear(p.allow_wear),
- mAllowDrop(p.allow_drop),
- 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),
- mSingleFolderMode(p.single_folder_mode),
- mMaxFolderItemOverlap(p.max_folder_item_overlap),
- mDoubleClickOverride(p.double_click_override)
-{
- 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("InventoryItemLinkColor", DEFAULT_WHITE);
- sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE);
- sColorSetInitialized = true;
- }
-
- if (mViewModelItem)
- {
- mViewModelItem->setFolderViewItem(this);
- }
-}
-
-// Destroys the object
-LLFolderViewItem::~LLFolderViewItem()
-{
- mViewModelItem = NULL;
- gFocusMgr.removeKeyboardFocusWithoutCallback(this);
-}
-
-bool LLFolderViewItem::postBuild()
-{
- LLFolderViewModelItem* vmi = getViewModelItem();
- llassert(vmi); // not supposed to happen, if happens, find out why and fix
- if (vmi)
- {
- // getDisplayName() is expensive (due to internal getLabelSuffix() and name building)
- // it also sets search strings so it requires a filter reset
- mLabel = vmi->getDisplayName();
- setToolTip(vmi->getName());
-
- // Dirty the filter flag of the model from the view (CHUI-849)
- vmi->dirtyFilter();
- }
-
- // Don't do full refresh on constructor if it is possible to avoid
- // it significantly slows down bulk view creation.
- // Todo: Ideally we need to move getDisplayName() out of constructor as well.
- // Like: make a logic that will let filter update search string,
- // while LLFolderViewItem::arrange() updates visual part
- mSuffixNeedsRefresh = true;
- mLabelWidthDirty = true;
- 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());
- // icons are slightly expensive to get, can be optimized
- // see LLInventoryIcon::getIcon()
- mIcon = vmi.getIcon();
- mIconOpen = vmi.getIconOpen();
- mIconOverlay = vmi.getIconOverlay();
-
- if (mRoot->useLabelSuffix())
- {
- // Very Expensive!
- // Can do a number of expensive checks, like checking active motions, wearables or friend list
- mLabelStyle = vmi.getLabelStyle();
- mLabelSuffix = vmi.getLabelSuffix();
- }
-
- // Dirty the filter flag of the model from the view (CHUI-849)
- vmi.dirtyFilter();
-
- mLabelWidthDirty = true;
- mSuffixNeedsRefresh = false;
-}
-
-void LLFolderViewItem::refreshSuffix()
-{
- LLFolderViewModelItem const* vmi = getViewModelItem();
-
- // icons are slightly expensive to get, can be optimized
- // see LLInventoryIcon::getIcon()
- mIcon = vmi->getIcon();
- mIconOpen = vmi->getIconOpen();
- mIconOverlay = vmi->getIconOverlay();
-
- if (mRoot->useLabelSuffix())
- {
- // Very Expensive!
- // Can do a number of expensive checks, like checking active motions, wearables or friend list
- mLabelStyle = vmi->getLabelStyle();
- mLabelSuffix = vmi->getLabelSuffix();
- }
-
- mLabelWidthDirty = true;
- mSuffixNeedsRefresh = false;
-}
-
-// 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)
- {
- if (mSuffixNeedsRefresh)
- {
- // Expensive. But despite refreshing label,
- // it is purely visual, so it is fine to do at our laisure
- refreshSuffix();
- }
- mLabelWidth = getLabelXPos() + getLabelFontForStyle(mLabelStyle)->getWidth(mLabel) + getLabelFontForStyle(LLFontGL::NORMAL)->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() const
-{
- 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)
- {
- 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 (mAllowWear || !getViewModelItem()->isItemWearable())
- {
- 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::getInstance()->mSettingGroups["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->getAllowDrag()
- && 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);
- }
-
- root->clearHoveredItem();
- return true;
- }
- else
- {
- LLFolderView* pRoot = getRoot();
- pRoot->setHoveredItem(this);
- pRoot->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;
-
- // NOTE: LLViewerWindow::updateUI() calls "enter" before "leave"; if the mouse moved to another item, we can't just outright clear it
- LLFolderView* pRoot = getRoot();
- if (this == pRoot->getHoveredItem())
- {
- pRoot->clearHoveredItem();
- }
-}
-
-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() || !isFolderComplete())
- {
- 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;
-}
-
-/*virtual*/ bool LLFolderViewItem::isFadeItem()
-{
- LLClipboard& clipboard = LLClipboard::instance();
- if (mCutGeneration != clipboard.getGeneration())
- {
- mCutGeneration = clipboard.getGeneration();
- mIsItemCut = clipboard.isCutMode()
- && ((getParentFolder() && getParentFolder()->isFadeItem())
- || getViewModelItem()->isCutToClipboard());
- }
- return mIsItemCut;
-}
-
-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, /*use_ellipses*/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();
-
- if(!mSingleFolderMode)
- {
- 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;
-
- const LLFontGL* suffix_font = getLabelFontForStyle(LLFontGL::NORMAL);
- S32 filter_offset = mViewModelItem->getFilterStringOffset();
- if (filter_string_length > 0)
- {
- S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD);
- S32 top = getRect().getHeight() - TOP_PAD;
- if(mLabelSuffix.empty() || (font == suffix_font))
- {
- 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;
-
- LLUIImage* box_image = default_params.selection_image;
- LLRect box_rect(left, top, right, bottom);
- box_image->draw(box_rect, sFilterBGColor);
- }
- else
- {
- S32 label_filter_length = llmin((S32)mLabel.size() - filter_offset, (S32)filter_string_length);
- if(label_filter_length > 0)
- {
- S32 left = ll_round(text_left) + font->getWidthF32(mLabel, 0, llmin(filter_offset, (S32)mLabel.size())) - 2;
- S32 right = left + font->getWidthF32(mLabel, filter_offset, label_filter_length) + 2;
- LLUIImage* box_image = default_params.selection_image;
- LLRect box_rect(left, top, right, bottom);
- box_image->draw(box_rect, sFilterBGColor);
- }
- S32 suffix_filter_length = label_filter_length > 0 ? filter_string_length - label_filter_length : filter_string_length;
- if(suffix_filter_length > 0)
- {
- S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size());
- S32 left = ll_round(text_left) + font->getWidthF32(mLabel, 0, mLabel.size()) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset) - 2;
- S32 right = left + suffix_font->getWidthF32(mLabelSuffix, suffix_offset, suffix_filter_length) + 2;
- 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;
-
- if (isFadeItem())
- {
- // Fade out item color to indicate it's being cut
- color.mV[VALPHA] *= 0.5f;
- }
- drawLabel(font, text_left, y, color, right_x);
-
- //--------------------------------------------------------------------------------//
- // Draw label suffix
- //
- if (!mLabelSuffix.empty())
- {
- suffix_font->renderUTF8( mLabelSuffix, 0, right_x, y, isFadeItem() ? color : (LLColor4)sSuffixColor,
- LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
- S32_MAX, S32_MAX, &right_x);
- }
-
- //--------------------------------------------------------------------------------//
- // Highlight string match
- //
- if (filter_string_length > 0)
- {
- if(mLabelSuffix.empty() || (font == suffix_font))
- {
- F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, filter_offset + filter_string_length) - font->getWidthF32(combined_string, filter_offset, filter_string_length);
- F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD;
- font->renderUTF8(combined_string, filter_offset, match_string_left, yy,
- sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
- filter_string_length, S32_MAX, &right_x);
- }
- else
- {
- S32 label_filter_length = llmin((S32)mLabel.size() - filter_offset, (S32)filter_string_length);
- if(label_filter_length > 0)
- {
- F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, filter_offset + label_filter_length) - font->getWidthF32(mLabel, filter_offset, label_filter_length);
- F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD;
- font->renderUTF8(mLabel, filter_offset, match_string_left, yy,
- sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
- label_filter_length, S32_MAX, &right_x);
- }
-
- S32 suffix_filter_length = label_filter_length > 0 ? filter_string_length - label_filter_length : filter_string_length;
- if(suffix_filter_length > 0)
- {
- S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size());
- F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, mLabel.size()) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset + suffix_filter_length) - suffix_font->getWidthF32(mLabelSuffix, suffix_offset, suffix_filter_length);
- F32 yy = (F32)getRect().getHeight() - suffix_font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD;
- suffix_font->renderUTF8(mLabelSuffix, suffix_offset, match_string_left, yy, sFilterTextColor,
- LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
- suffix_filter_length, S32_MAX, &right_x);
- }
- }
-
- }
-
- //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),
- mIsFolderComplete(false), // folder might have children that are not loaded yet.
- mAreChildrenInited(false), // folder might have children that are not built yet.
- 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;
-
- if(isOpen() && folder->isOpen())
- {
- requestArrange();
- }
-}
-
-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)
- if (mAreChildrenInited)
- {
- getRoot()->getFolderViewModel()->sort(this);
- }
-
- LL_RECORD_BLOCK_TIME(FTM_ARRANGE);
-
- // evaluate mHasVisibleChildren
- mHasVisibleChildren = false;
- if (mAreChildrenInited && 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;
- }
- if (!mIsFolderComplete && mAreChildrenInited)
- {
- mIsFolderComplete = getFolderViewModel()->isFolderComplete(this);
- }
-
-
-
- // 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();
-}
-
-bool LLFolderViewFolder::descendantsPassedFilter(S32 filter_generation)
-{
- return getViewModelItem()->descendantsPassedFilter(filter_generation);
-}
-
-// 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 && !mSingleFolderMode)
- {
- 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 && (*it)->getVisible())
- {
- 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 && (*it)->getVisible())
- {
- 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 && (*it)->getVisible())
- {
- 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 && (*it)->getVisible())
- {
- items.push_back(*it);
- }
-
- if (*it == start)
- {
- selecting = true;
- }
- }
- }
-}
-
-void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection)
-{
- if (!getRoot()->getAllowMultiSelect())
- 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();
-
- bool selection_reverse = new_selection->isSelected(); //indication that some elements are being deselected
-
- // array always go from 'will be selected' to ' will be unselected', iterate
- // in opposite direction to simplify identification of 'point of origin' in
- // case it is in the list we are working with
- for (std::vector<LLFolderViewItem*>::reverse_iterator it = items_to_select_forward.rbegin(), end_it = items_to_select_forward.rend();
- it != end_it;
- ++it)
- {
- LLFolderViewItem* item = *it;
- bool selected = item->isSelected();
- if (!selection_reverse && selected)
- {
- // it is our 'point of origin' where we shift/expand from
- // don't deselect it
- selection_reverse = true;
- }
- else
- {
- root->changeSelection(item, !selected);
- }
- }
-
- if (selection_reverse)
- {
- // at some point we reversed selection, first element should be deselected
- root->changeSelection(last_selected_item_from_cur, false);
- }
-
- // element we expand to should always be selected
- root->changeSelection(new_selection, true);
-}
-
-
-void LLFolderViewFolder::destroyView()
-{
- while (!mItems.empty())
- {
- LLFolderViewItem *itemp = mItems.back();
- mItems.pop_back();
- itemp->destroyView(); // LLFolderViewItem::destroyView() removes entry from mItems
- }
-
- while (!mFolders.empty())
- {
- LLFolderViewFolder *folderp = mFolders.back();
- mFolders.pop_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, bool deparent_model )
-{
- 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
- if (deparent_model)
- {
- // in some cases model does not belong to parent view, is shared between views
- 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;
-}
-
-void LLFolderViewFolder::destroyRoot()
-{
- delete this;
-}
-
-// 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)
-{
- if(mSingleFolderMode)
- {
- // navigateToFolder can destroy this view
- // delay it in case setOpen was called from click or key processing
- doOnIdleOneTime([this]()
- {
- getViewModelItem()->navigateToFolder();
- });
- }
- else
- {
- setOpenArrangeRecursively(openitem);
- }
-}
-
-void LLFolderViewFolder::setOpenArrangeRecursively(bool openitem, ERecurseType recurse)
-{
- bool was_open = isOpen();
- mIsOpen = openitem;
- if(!was_open && openitem)
- {
- getViewModelItem()->openItem();
- // openItem() will request content, it won't be incomplete
- mIsFolderComplete = true;
- }
- 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)
-{
- if (!mAllowDrop)
- {
- *accept = ACCEPT_NO;
- tooltip_msg = LLTrans::getString("TooltipOutboxCannotDropOnRoot");
- return true;
- }
-
- 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)
- && !mSingleFolderMode)
- {
- 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(mSingleFolderMode)
- {
- static LLUICachedControl<bool> double_click_new_window("SingleModeDoubleClickOpenWindow", false);
- if (double_click_new_window)
- {
- getViewModelItem()->navigateToFolder(true);
- }
- else
- {
- // navigating is going to destroy views and change children
- // delay it untill handleDoubleClick processing is complete
- doOnIdleOneTime([this]()
- {
- getViewModelItem()->navigateToFolder(false);
- });
- }
- return true;
- }
-
- if( isOpen() )
- {
- handled = childrenHandleDoubleClick( x, y, mask ) != NULL;
- }
- if( !handled )
- {
- if(mDoubleClickOverride)
- {
- static LLUICachedControl<U32> double_click_action("MultiModeDoubleClickFolder", false);
- if (double_click_action == 1)
- {
- getViewModelItem()->navigateToFolder(true);
- return true;
- }
- if (double_click_action == 2)
- {
- getViewModelItem()->navigateToFolder(false, true);
- return true;
- }
- }
- 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;
-}
-
+/**
+* @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 "llcallbacklist.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_wear("allow_wear", true),
+ allow_drop("allow_drop", 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),
+ single_folder_mode("single_folder_mode", false),
+ double_click_override("double_click_override", false),
+ 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),
+ mSuffixNeedsRefresh(false),
+ mLabelPaddingRight(DEFAULT_LABEL_PADDING_RIGHT),
+ mParentFolder( NULL ),
+ mIsSelected( false ),
+ mIsCurSelection( false ),
+ mSelectPending(false),
+ mIsItemCut(false),
+ mCutGeneration(0),
+ 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),
+ mAllowWear(p.allow_wear),
+ mAllowDrop(p.allow_drop),
+ 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),
+ mSingleFolderMode(p.single_folder_mode),
+ mMaxFolderItemOverlap(p.max_folder_item_overlap),
+ mDoubleClickOverride(p.double_click_override)
+{
+ 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("InventoryItemLinkColor", DEFAULT_WHITE);
+ sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE);
+ sColorSetInitialized = true;
+ }
+
+ if (mViewModelItem)
+ {
+ mViewModelItem->setFolderViewItem(this);
+ }
+}
+
+// Destroys the object
+LLFolderViewItem::~LLFolderViewItem()
+{
+ mViewModelItem = NULL;
+ gFocusMgr.removeKeyboardFocusWithoutCallback(this);
+}
+
+bool LLFolderViewItem::postBuild()
+{
+ LLFolderViewModelItem* vmi = getViewModelItem();
+ llassert(vmi); // not supposed to happen, if happens, find out why and fix
+ if (vmi)
+ {
+ // getDisplayName() is expensive (due to internal getLabelSuffix() and name building)
+ // it also sets search strings so it requires a filter reset
+ mLabel = vmi->getDisplayName();
+ setToolTip(vmi->getName());
+
+ // Dirty the filter flag of the model from the view (CHUI-849)
+ vmi->dirtyFilter();
+ }
+
+ // Don't do full refresh on constructor if it is possible to avoid
+ // it significantly slows down bulk view creation.
+ // Todo: Ideally we need to move getDisplayName() out of constructor as well.
+ // Like: make a logic that will let filter update search string,
+ // while LLFolderViewItem::arrange() updates visual part
+ mSuffixNeedsRefresh = true;
+ mLabelWidthDirty = true;
+ 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());
+ // icons are slightly expensive to get, can be optimized
+ // see LLInventoryIcon::getIcon()
+ mIcon = vmi.getIcon();
+ mIconOpen = vmi.getIconOpen();
+ mIconOverlay = vmi.getIconOverlay();
+
+ if (mRoot->useLabelSuffix())
+ {
+ // Very Expensive!
+ // Can do a number of expensive checks, like checking active motions, wearables or friend list
+ mLabelStyle = vmi.getLabelStyle();
+ mLabelSuffix = vmi.getLabelSuffix();
+ }
+
+ // Dirty the filter flag of the model from the view (CHUI-849)
+ vmi.dirtyFilter();
+
+ mLabelWidthDirty = true;
+ mSuffixNeedsRefresh = false;
+}
+
+void LLFolderViewItem::refreshSuffix()
+{
+ LLFolderViewModelItem const* vmi = getViewModelItem();
+
+ // icons are slightly expensive to get, can be optimized
+ // see LLInventoryIcon::getIcon()
+ mIcon = vmi->getIcon();
+ mIconOpen = vmi->getIconOpen();
+ mIconOverlay = vmi->getIconOverlay();
+
+ if (mRoot->useLabelSuffix())
+ {
+ // Very Expensive!
+ // Can do a number of expensive checks, like checking active motions, wearables or friend list
+ mLabelStyle = vmi->getLabelStyle();
+ mLabelSuffix = vmi->getLabelSuffix();
+ }
+
+ mLabelWidthDirty = true;
+ mSuffixNeedsRefresh = false;
+}
+
+// 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)
+ {
+ if (mSuffixNeedsRefresh)
+ {
+ // Expensive. But despite refreshing label,
+ // it is purely visual, so it is fine to do at our laisure
+ refreshSuffix();
+ }
+ mLabelWidth = getLabelXPos() + getLabelFontForStyle(mLabelStyle)->getWidth(mLabel) + getLabelFontForStyle(LLFontGL::NORMAL)->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() const
+{
+ 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)
+ {
+ 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 (mAllowWear || !getViewModelItem()->isItemWearable())
+ {
+ 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 )
+{
+ mIsMouseOverTitle = (y > (getRect().getHeight() - mItemHeight));
+
+ if( hasMouseCapture() && isMovable() )
+ {
+ LLFolderView* root = getRoot();
+
+ if( (x - mDragStartX) * (x - mDragStartX) + (y - mDragStartY) * (y - mDragStartY) > DRAG_N_DROP_DISTANCE_THRESHOLD * DRAG_N_DROP_DISTANCE_THRESHOLD
+ && root->getAllowDrag()
+ && 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);
+ }
+
+ root->clearHoveredItem();
+ return true;
+ }
+ else
+ {
+ LLFolderView* pRoot = getRoot();
+ pRoot->setHoveredItem(this);
+ pRoot->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;
+
+ // NOTE: LLViewerWindow::updateUI() calls "enter" before "leave"; if the mouse moved to another item, we can't just outright clear it
+ LLFolderView* pRoot = getRoot();
+ if (this == pRoot->getHoveredItem())
+ {
+ pRoot->clearHoveredItem();
+ }
+}
+
+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() || !isFolderComplete())
+ {
+ 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;
+}
+
+/*virtual*/ bool LLFolderViewItem::isFadeItem()
+{
+ LLClipboard& clipboard = LLClipboard::instance();
+ if (mCutGeneration != clipboard.getGeneration())
+ {
+ mCutGeneration = clipboard.getGeneration();
+ mIsItemCut = clipboard.isCutMode()
+ && ((getParentFolder() && getParentFolder()->isFadeItem())
+ || getViewModelItem()->isCutToClipboard());
+ }
+ return mIsItemCut;
+}
+
+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, /*use_ellipses*/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();
+
+ if(!mSingleFolderMode)
+ {
+ 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;
+
+ const LLFontGL* suffix_font = getLabelFontForStyle(LLFontGL::NORMAL);
+ S32 filter_offset = mViewModelItem->getFilterStringOffset();
+ if (filter_string_length > 0)
+ {
+ S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD);
+ S32 top = getRect().getHeight() - TOP_PAD;
+ if(mLabelSuffix.empty() || (font == suffix_font))
+ {
+ 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;
+
+ LLUIImage* box_image = default_params.selection_image;
+ LLRect box_rect(left, top, right, bottom);
+ box_image->draw(box_rect, sFilterBGColor);
+ }
+ else
+ {
+ S32 label_filter_length = llmin((S32)mLabel.size() - filter_offset, (S32)filter_string_length);
+ if(label_filter_length > 0)
+ {
+ S32 left = ll_round(text_left) + font->getWidthF32(mLabel, 0, llmin(filter_offset, (S32)mLabel.size())) - 2;
+ S32 right = left + font->getWidthF32(mLabel, filter_offset, label_filter_length) + 2;
+ LLUIImage* box_image = default_params.selection_image;
+ LLRect box_rect(left, top, right, bottom);
+ box_image->draw(box_rect, sFilterBGColor);
+ }
+ S32 suffix_filter_length = label_filter_length > 0 ? filter_string_length - label_filter_length : filter_string_length;
+ if(suffix_filter_length > 0)
+ {
+ S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size());
+ S32 left = ll_round(text_left) + font->getWidthF32(mLabel, 0, mLabel.size()) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset) - 2;
+ S32 right = left + suffix_font->getWidthF32(mLabelSuffix, suffix_offset, suffix_filter_length) + 2;
+ 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;
+
+ if (isFadeItem())
+ {
+ // Fade out item color to indicate it's being cut
+ color.mV[VALPHA] *= 0.5f;
+ }
+ drawLabel(font, text_left, y, color, right_x);
+
+ //--------------------------------------------------------------------------------//
+ // Draw label suffix
+ //
+ if (!mLabelSuffix.empty())
+ {
+ suffix_font->renderUTF8( mLabelSuffix, 0, right_x, y, isFadeItem() ? color : (LLColor4)sSuffixColor,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
+ S32_MAX, S32_MAX, &right_x);
+ }
+
+ //--------------------------------------------------------------------------------//
+ // Highlight string match
+ //
+ if (filter_string_length > 0)
+ {
+ if(mLabelSuffix.empty() || (font == suffix_font))
+ {
+ F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, filter_offset + filter_string_length) - font->getWidthF32(combined_string, filter_offset, filter_string_length);
+ F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD;
+ font->renderUTF8(combined_string, filter_offset, match_string_left, yy,
+ sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
+ filter_string_length, S32_MAX, &right_x);
+ }
+ else
+ {
+ S32 label_filter_length = llmin((S32)mLabel.size() - filter_offset, (S32)filter_string_length);
+ if(label_filter_length > 0)
+ {
+ F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, filter_offset + label_filter_length) - font->getWidthF32(mLabel, filter_offset, label_filter_length);
+ F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD;
+ font->renderUTF8(mLabel, filter_offset, match_string_left, yy,
+ sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
+ label_filter_length, S32_MAX, &right_x);
+ }
+
+ S32 suffix_filter_length = label_filter_length > 0 ? filter_string_length - label_filter_length : filter_string_length;
+ if(suffix_filter_length > 0)
+ {
+ S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size());
+ F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, mLabel.size()) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset + suffix_filter_length) - suffix_font->getWidthF32(mLabelSuffix, suffix_offset, suffix_filter_length);
+ F32 yy = (F32)getRect().getHeight() - suffix_font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD;
+ suffix_font->renderUTF8(mLabelSuffix, suffix_offset, match_string_left, yy, sFilterTextColor,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
+ suffix_filter_length, S32_MAX, &right_x);
+ }
+ }
+
+ }
+
+ //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),
+ mIsFolderComplete(false), // folder might have children that are not loaded yet.
+ mAreChildrenInited(false), // folder might have children that are not built yet.
+ 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;
+
+ if(isOpen() && folder->isOpen())
+ {
+ requestArrange();
+ }
+}
+
+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)
+ if (mAreChildrenInited)
+ {
+ getRoot()->getFolderViewModel()->sort(this);
+ }
+
+ LL_RECORD_BLOCK_TIME(FTM_ARRANGE);
+
+ // evaluate mHasVisibleChildren
+ mHasVisibleChildren = false;
+ if (mAreChildrenInited && 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;
+ }
+ if (!mIsFolderComplete && mAreChildrenInited)
+ {
+ mIsFolderComplete = getFolderViewModel()->isFolderComplete(this);
+ }
+
+
+
+ // 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();
+}
+
+bool LLFolderViewFolder::descendantsPassedFilter(S32 filter_generation)
+{
+ return getViewModelItem()->descendantsPassedFilter(filter_generation);
+}
+
+// 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 && !mSingleFolderMode)
+ {
+ 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 && (*it)->getVisible())
+ {
+ 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 && (*it)->getVisible())
+ {
+ 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 && (*it)->getVisible())
+ {
+ 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 && (*it)->getVisible())
+ {
+ items.push_back(*it);
+ }
+
+ if (*it == start)
+ {
+ selecting = true;
+ }
+ }
+ }
+}
+
+void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection)
+{
+ if (!getRoot()->getAllowMultiSelect())
+ 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();
+
+ bool selection_reverse = new_selection->isSelected(); //indication that some elements are being deselected
+
+ // array always go from 'will be selected' to ' will be unselected', iterate
+ // in opposite direction to simplify identification of 'point of origin' in
+ // case it is in the list we are working with
+ for (std::vector<LLFolderViewItem*>::reverse_iterator it = items_to_select_forward.rbegin(), end_it = items_to_select_forward.rend();
+ it != end_it;
+ ++it)
+ {
+ LLFolderViewItem* item = *it;
+ bool selected = item->isSelected();
+ if (!selection_reverse && selected)
+ {
+ // it is our 'point of origin' where we shift/expand from
+ // don't deselect it
+ selection_reverse = true;
+ }
+ else
+ {
+ root->changeSelection(item, !selected);
+ }
+ }
+
+ if (selection_reverse)
+ {
+ // at some point we reversed selection, first element should be deselected
+ root->changeSelection(last_selected_item_from_cur, false);
+ }
+
+ // element we expand to should always be selected
+ root->changeSelection(new_selection, true);
+}
+
+
+void LLFolderViewFolder::destroyView()
+{
+ while (!mItems.empty())
+ {
+ LLFolderViewItem *itemp = mItems.back();
+ mItems.pop_back();
+ itemp->destroyView(); // LLFolderViewItem::destroyView() removes entry from mItems
+ }
+
+ while (!mFolders.empty())
+ {
+ LLFolderViewFolder *folderp = mFolders.back();
+ mFolders.pop_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, bool deparent_model )
+{
+ 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
+ if (deparent_model)
+ {
+ // in some cases model does not belong to parent view, is shared between views
+ 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;
+}
+
+void LLFolderViewFolder::destroyRoot()
+{
+ delete this;
+}
+
+// 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)
+{
+ if(mSingleFolderMode)
+ {
+ // navigateToFolder can destroy this view
+ // delay it in case setOpen was called from click or key processing
+ doOnIdleOneTime([this]()
+ {
+ getViewModelItem()->navigateToFolder();
+ });
+ }
+ else
+ {
+ setOpenArrangeRecursively(openitem);
+ }
+}
+
+void LLFolderViewFolder::setOpenArrangeRecursively(bool openitem, ERecurseType recurse)
+{
+ bool was_open = isOpen();
+ mIsOpen = openitem;
+ if(!was_open && openitem)
+ {
+ getViewModelItem()->openItem();
+ // openItem() will request content, it won't be incomplete
+ mIsFolderComplete = true;
+ }
+ 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)
+{
+ if (!mAllowDrop)
+ {
+ *accept = ACCEPT_NO;
+ tooltip_msg = LLTrans::getString("TooltipOutboxCannotDropOnRoot");
+ return true;
+ }
+
+ 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)
+ && !mSingleFolderMode)
+ {
+ 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(mSingleFolderMode)
+ {
+ static LLUICachedControl<bool> double_click_new_window("SingleModeDoubleClickOpenWindow", false);
+ if (double_click_new_window)
+ {
+ getViewModelItem()->navigateToFolder(true);
+ }
+ else
+ {
+ // navigating is going to destroy views and change children
+ // delay it untill handleDoubleClick processing is complete
+ doOnIdleOneTime([this]()
+ {
+ getViewModelItem()->navigateToFolder(false);
+ });
+ }
+ return true;
+ }
+
+ if( isOpen() )
+ {
+ handled = childrenHandleDoubleClick( x, y, mask ) != NULL;
+ }
+ if( !handled )
+ {
+ if(mDoubleClickOverride)
+ {
+ static LLUICachedControl<U32> double_click_action("MultiModeDoubleClickFolder", false);
+ if (double_click_action == 1)
+ {
+ getViewModelItem()->navigateToFolder(true);
+ return true;
+ }
+ if (double_click_action == 2)
+ {
+ getViewModelItem()->navigateToFolder(false, true);
+ return true;
+ }
+ }
+ 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
index 9d6b90dc3b..d72b6804ad 100644
--- a/indra/llui/llfolderviewitem.h
+++ b/indra/llui/llfolderviewitem.h
@@ -1,498 +1,498 @@
-/**
-* @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_wear;
- Optional<bool> allow_drop;
-
- 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;
- Optional<bool> single_folder_mode,
- double_click_override;
- 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;
- bool mSuffixNeedsRefresh; //suffix and icons
- 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,
- mAllowWear,
- mAllowDrop,
- mSingleFolderMode,
- mDoubleClickOverride,
- mSelectPending,
- mIsItemCut;
-
- S32 mCutGeneration;
-
- 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 isFadeItem();
- 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() const;
- 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() const { return mIsCurSelection; }
-
- bool hasVisibleChildren() const { return mHasVisibleChildren; }
-
- // true if object can't have children
- virtual bool isFolderComplete() { return true; }
- // true if object can't have children
- virtual bool areChildrenInited() { return true; }
- virtual void setChildrenInited(bool inited) { }
-
- // 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() const { return mIndentation; }
-
- virtual bool passedFilter(S32 filter_generation = -1);
- virtual bool isPotentiallyVisible(S32 filter_generation = -1);
-
- // refresh information from the object being viewed.
- // refreshes label, suffixes and sets icons. Expensive!
- // Causes filter update
- virtual void refresh();
- // refreshes suffixes and sets icons. Expensive!
- // Does not need filter update
- virtual void refreshSuffix();
-
- bool isSingleFolderMode() { return mSingleFolderMode; }
-
- // 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 mIsFolderComplete; // indicates that some children were not loaded/added yet
- bool mAreChildrenInited; // indicates that no children were initialized
-
-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();
- void destroyRoot();
-
- // whether known children are fully loaded (arrange sets to true)
- virtual bool isFolderComplete() { return mIsFolderComplete; }
-
- // whether known children are fully built
- virtual bool areChildrenInited() { return mAreChildrenInited; }
- virtual void setChildrenInited(bool inited) { mAreChildrenInited = inited; }
-
- // extractItem() removes the specified item from the folder, but
- // doesn't delete it.
- virtual void extractItem( LLFolderViewItem* item, bool deparent_model = true);
-
- // 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
+/**
+* @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_wear;
+ Optional<bool> allow_drop;
+
+ 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;
+ Optional<bool> single_folder_mode,
+ double_click_override;
+ 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;
+ bool mSuffixNeedsRefresh; //suffix and icons
+ 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,
+ mAllowWear,
+ mAllowDrop,
+ mSingleFolderMode,
+ mDoubleClickOverride,
+ mSelectPending,
+ mIsItemCut;
+
+ S32 mCutGeneration;
+
+ 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 isFadeItem();
+ 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() const;
+ 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() const { return mIsCurSelection; }
+
+ bool hasVisibleChildren() const { return mHasVisibleChildren; }
+
+ // true if object can't have children
+ virtual bool isFolderComplete() { return true; }
+ // true if object can't have children
+ virtual bool areChildrenInited() { return true; }
+ virtual void setChildrenInited(bool inited) { }
+
+ // 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() const { return mIndentation; }
+
+ virtual bool passedFilter(S32 filter_generation = -1);
+ virtual bool isPotentiallyVisible(S32 filter_generation = -1);
+
+ // refresh information from the object being viewed.
+ // refreshes label, suffixes and sets icons. Expensive!
+ // Causes filter update
+ virtual void refresh();
+ // refreshes suffixes and sets icons. Expensive!
+ // Does not need filter update
+ virtual void refreshSuffix();
+
+ bool isSingleFolderMode() { return mSingleFolderMode; }
+
+ // 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 mIsFolderComplete; // indicates that some children were not loaded/added yet
+ bool mAreChildrenInited; // indicates that no children were initialized
+
+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();
+ void destroyRoot();
+
+ // whether known children are fully loaded (arrange sets to true)
+ virtual bool isFolderComplete() { return mIsFolderComplete; }
+
+ // whether known children are fully built
+ virtual bool areChildrenInited() { return mAreChildrenInited; }
+ virtual void setChildrenInited(bool inited) { mAreChildrenInited = inited; }
+
+ // extractItem() removes the specified item from the folder, but
+ // doesn't delete it.
+ virtual void extractItem( LLFolderViewItem* item, bool deparent_model = true);
+
+ // 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
index f217b743a0..d9e6567cd6 100644
--- a/indra/llui/llfolderviewmodel.cpp
+++ b/indra/llui/llfolderviewmodel.cpp
@@ -1,25 +1,25 @@
-/**
+/**
* @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$
*/
@@ -31,39 +31,39 @@
bool LLFolderViewModelCommon::needsSort(LLFolderViewModelItem* item)
{
- return item->getSortVersion() < mTargetSortVersion;
+ return item->getSortVersion() < mTargetSortVersion;
}
std::string LLFolderViewModelCommon::getStatusText(bool is_empty_folder)
{
- if (!contentsReady() || mFolderView->getViewModelItem()->getLastFilterGeneration() < getFilter().getCurrentGeneration())
- {
- return LLTrans::getString("Searching");
- }
- else
- {
- return getFilter().getEmptyLookupMessage(is_empty_folder);
- }
+ if (!contentsReady() || mFolderView->getViewModelItem()->getLastFilterGeneration() < getFilter().getCurrentGeneration())
+ {
+ return LLTrans::getString("Searching");
+ }
+ else
+ {
+ return getFilter().getEmptyLookupMessage(is_empty_folder);
+ }
}
void LLFolderViewModelCommon::filter()
{
- static LLCachedControl<S32> max_time(*LLUI::getInstance()->mSettingGroups["config"], "FilterItemsMaxTimePerFrameVisible", 10);
- getFilter().resetTime(llclamp(max_time(), 1, 100));
+ const S32 MAX_FILTER_TIME = 10;
+ getFilter().resetTime(MAX_FILTER_TIME);
mFolderView->getViewModelItem()->filter(getFilter());
}
bool LLFolderViewModelItemCommon::hasFilterStringMatch()
{
- return mStringMatchOffsetFilter != std::string::npos;
+ return mStringMatchOffsetFilter != std::string::npos;
}
std::string::size_type LLFolderViewModelItemCommon::getFilterStringOffset()
{
- return mStringMatchOffsetFilter;
+ return mStringMatchOffsetFilter;
}
std::string::size_type LLFolderViewModelItemCommon::getFilterStringSize()
{
- return mRootViewModel.getFilter().getFilterStringSize();
+ return mRootViewModel.getFilter().getFilterStringSize();
}
diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h
index a91f202b2b..27d1f1dbe0 100644
--- a/indra/llui/llfolderviewmodel.h
+++ b/indra/llui/llfolderviewmodel.h
@@ -1,474 +1,474 @@
-/**
- * @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(bool is_empty_folder = false) 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:
- LLFolderViewModelInterface()
- {}
-
- virtual ~LLFolderViewModelInterface() {}
- virtual void requestSortAll() = 0;
-
- virtual void sort(class LLFolderViewFolder*) = 0;
- virtual void filter() = 0;
-
- virtual bool contentsReady() = 0;
- virtual bool isFolderComplete(class LLFolderViewFolder*) = 0;
- virtual void setFolderView(LLFolderView* folder_view) = 0;
- virtual LLFolderViewFilter& getFilter() = 0;
- virtual const LLFolderViewFilter& getFilter() const = 0;
- virtual std::string getStatusText(bool is_empty_folder = false) = 0;
-
- virtual bool startDrag(std::vector<LLFolderViewModelItem*>& items) = 0;
-};
-
-// This is an abstract base class that users of the folderview classes
-// would use to bridge the folder view with the underlying data
-class LLFolderViewModelItem : public LLRefCount
-{
-public:
- LLFolderViewModelItem()
- {}
-
- virtual ~LLFolderViewModelItem() { }
-
- virtual void update() {} //called when drawing
- virtual const std::string& getName() const = 0;
- virtual const std::string& getDisplayName() const = 0;
- virtual const std::string& getSearchableName() const = 0;
-
- virtual std::string getSearchableDescription() const = 0;
- virtual std::string getSearchableCreatorName()const = 0;
- virtual std::string getSearchableUUIDString() 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 void navigateToFolder(bool new_window = false, bool change_mode = false) = 0;
-
- virtual bool isItemWearable() const { return false; }
-
- 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(bool can_copy_as_link = true) const = 0;
- virtual bool copyToClipboard() const = 0;
- virtual bool cutToClipboard() = 0;
- virtual bool isCutToClipboard() { return false; };
-
- 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;
- virtual void clearChildren() = 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)
- {
- 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()
- {
- // We are working with models that belong to views as LLPointers, clean the list, let poiters handle the rest
- std::for_each(mChildren.begin(), mChildren.end(), [](LLFolderViewModelItem* c) {c->setParent(NULL); });
- 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(bool is_empty_folder = false);
- 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() {}
-
- virtual SortType& getSorter() { return *mSorter; }
- virtual const SortType& getSorter() const { return *mSorter; }
- virtual void setSorter(const SortType& sorter) { mSorter.reset(new SortType(sorter)); requestSortAll(); }
-
- virtual FilterType& getFilter() { return *mFilter; }
- virtual const FilterType& getFilter() const { return *mFilter; }
- virtual void setFilter(const FilterType& filter) { mFilter.reset(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; }
- virtual bool isFolderComplete(LLFolderViewFolder* folder) { 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:
- std::unique_ptr<SortType> mSorter;
- std::unique_ptr<FilterType> mFilter;
-};
-
-#endif // LLFOLDERVIEWMODEL_H
+/**
+ * @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(bool is_empty_folder = false) 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:
+ LLFolderViewModelInterface()
+ {}
+
+ virtual ~LLFolderViewModelInterface() {}
+ virtual void requestSortAll() = 0;
+
+ virtual void sort(class LLFolderViewFolder*) = 0;
+ virtual void filter() = 0;
+
+ virtual bool contentsReady() = 0;
+ virtual bool isFolderComplete(class LLFolderViewFolder*) = 0;
+ virtual void setFolderView(LLFolderView* folder_view) = 0;
+ virtual LLFolderViewFilter& getFilter() = 0;
+ virtual const LLFolderViewFilter& getFilter() const = 0;
+ virtual std::string getStatusText(bool is_empty_folder = false) = 0;
+
+ virtual bool startDrag(std::vector<LLFolderViewModelItem*>& items) = 0;
+};
+
+// This is an abstract base class that users of the folderview classes
+// would use to bridge the folder view with the underlying data
+class LLFolderViewModelItem : public LLRefCount
+{
+public:
+ LLFolderViewModelItem()
+ {}
+
+ virtual ~LLFolderViewModelItem() { }
+
+ virtual void update() {} //called when drawing
+ virtual const std::string& getName() const = 0;
+ virtual const std::string& getDisplayName() const = 0;
+ virtual const std::string& getSearchableName() const = 0;
+
+ virtual std::string getSearchableDescription() const = 0;
+ virtual std::string getSearchableCreatorName()const = 0;
+ virtual std::string getSearchableUUIDString() 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 void navigateToFolder(bool new_window = false, bool change_mode = false) = 0;
+
+ virtual bool isItemWearable() const { return false; }
+
+ 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( bool check_worn = true ) const = 0; // Can be destroyed
+ virtual bool removeItem() = 0;
+ virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch) = 0;
+
+ virtual bool isItemCopyable(bool can_copy_as_link = true) const = 0;
+ virtual bool copyToClipboard() const = 0;
+ virtual bool cutToClipboard() = 0;
+ virtual bool isCutToClipboard() { return false; };
+
+ 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;
+ virtual void clearChildren() = 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)
+ {
+ 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()
+ {
+ // We are working with models that belong to views as LLPointers, clean the list, let poiters handle the rest
+ std::for_each(mChildren.begin(), mChildren.end(), [](LLFolderViewModelItem* c) {c->setParent(NULL); });
+ 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(bool is_empty_folder = false);
+ 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() {}
+
+ virtual SortType& getSorter() { return *mSorter; }
+ virtual const SortType& getSorter() const { return *mSorter; }
+ virtual void setSorter(const SortType& sorter) { mSorter.reset(new SortType(sorter)); requestSortAll(); }
+
+ virtual FilterType& getFilter() { return *mFilter; }
+ virtual const FilterType& getFilter() const { return *mFilter; }
+ virtual void setFilter(const FilterType& filter) { mFilter.reset(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; }
+ virtual bool isFolderComplete(LLFolderViewFolder* folder) { 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:
+ std::unique_ptr<SortType> mSorter;
+ std::unique_ptr<FilterType> mFilter;
+};
+
+#endif // LLFOLDERVIEWMODEL_H
diff --git a/indra/llui/llfunctorregistry.h b/indra/llui/llfunctorregistry.h
index e43974bc52..da5570d922 100644
--- a/indra/llui/llfunctorregistry.h
+++ b/indra/llui/llfunctorregistry.h
@@ -6,21 +6,21 @@
* $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$
*/
@@ -47,93 +47,93 @@
* across restarts of the viewer; we couldn't store functors that way.
* Using this registry, systems that require a functor to be maintained
* long term can register it at system startup, and then pass in the
- * functor by name.
+ * functor by name.
*/
template <typename FUNCTOR_TYPE>
class LLFunctorRegistry : public LLSingleton<LLFunctorRegistry<FUNCTOR_TYPE> >
{
- LLSINGLETON(LLFunctorRegistry);
- LOG_CLASS(LLFunctorRegistry);
+ LLSINGLETON(LLFunctorRegistry);
+ LOG_CLASS(LLFunctorRegistry);
public:
- typedef FUNCTOR_TYPE ResponseFunctor;
- typedef typename std::map<std::string, FUNCTOR_TYPE> FunctorMap;
-
- bool registerFunctor(const std::string& name, ResponseFunctor f)
- {
- bool retval = true;
- if (mMap.count(name) == 0)
- {
- mMap[name] = f;
- }
- else
- {
- LL_ERRS() << "attempt to store duplicate name '" << name << "' in LLFunctorRegistry. NOT ADDED." << LL_ENDL;
- retval = false;
- }
-
- return retval;
- }
-
- bool unregisterFunctor(const std::string& name)
- {
- if (mMap.count(name) == 0)
- {
- LL_WARNS() << "trying to remove '" << name << "' from LLFunctorRegistry but it's not there." << LL_ENDL;
- return false;
- }
- mMap.erase(name);
- return true;
- }
-
- FUNCTOR_TYPE getFunctor(const std::string& name)
- {
- if (mMap.count(name) != 0)
- {
- return mMap[name];
- }
- else
- {
- LL_DEBUGS() << "tried to find '" << name << "' in LLFunctorRegistry, but it wasn't there." << LL_ENDL;
- return mMap[LOGFUNCTOR];
- }
- }
-
- const std::string LOGFUNCTOR;
- const std::string DONOTHING;
-
+ typedef FUNCTOR_TYPE ResponseFunctor;
+ typedef typename std::map<std::string, FUNCTOR_TYPE> FunctorMap;
+
+ bool registerFunctor(const std::string& name, ResponseFunctor f)
+ {
+ bool retval = true;
+ if (mMap.count(name) == 0)
+ {
+ mMap[name] = f;
+ }
+ else
+ {
+ LL_ERRS() << "attempt to store duplicate name '" << name << "' in LLFunctorRegistry. NOT ADDED." << LL_ENDL;
+ retval = false;
+ }
+
+ return retval;
+ }
+
+ bool unregisterFunctor(const std::string& name)
+ {
+ if (mMap.count(name) == 0)
+ {
+ LL_WARNS() << "trying to remove '" << name << "' from LLFunctorRegistry but it's not there." << LL_ENDL;
+ return false;
+ }
+ mMap.erase(name);
+ return true;
+ }
+
+ FUNCTOR_TYPE getFunctor(const std::string& name)
+ {
+ if (mMap.count(name) != 0)
+ {
+ return mMap[name];
+ }
+ else
+ {
+ LL_DEBUGS() << "tried to find '" << name << "' in LLFunctorRegistry, but it wasn't there." << LL_ENDL;
+ return mMap[LOGFUNCTOR];
+ }
+ }
+
+ const std::string LOGFUNCTOR;
+ const std::string DONOTHING;
+
private:
- static void log_functor(const LLSD& notification, const LLSD& payload)
- {
- LL_DEBUGS() << "log_functor called with payload: " << payload << LL_ENDL;
- }
+ static void log_functor(const LLSD& notification, const LLSD& payload)
+ {
+ LL_DEBUGS() << "log_functor called with payload: " << payload << LL_ENDL;
+ }
- static void do_nothing(const LLSD& notification, const LLSD& payload)
- {
- // what the sign sez
- }
+ static void do_nothing(const LLSD& notification, const LLSD& payload)
+ {
+ // what the sign sez
+ }
- FunctorMap mMap;
+ FunctorMap mMap;
};
template <typename FUNCTOR_TYPE>
LLFunctorRegistry<FUNCTOR_TYPE>::LLFunctorRegistry() :
- LOGFUNCTOR("LogFunctor"), DONOTHING("DoNothing")
+ LOGFUNCTOR("LogFunctor"), DONOTHING("DoNothing")
{
- mMap[LOGFUNCTOR] = log_functor;
- mMap[DONOTHING] = do_nothing;
+ mMap[LOGFUNCTOR] = log_functor;
+ mMap[DONOTHING] = do_nothing;
}
template <typename FUNCTOR_TYPE>
class LLFunctorRegistration
{
public:
- LLFunctorRegistration(const std::string& name, FUNCTOR_TYPE functor)
- {
- LLFunctorRegistry<FUNCTOR_TYPE>::instance().registerFunctor(name, functor);
- }
+ LLFunctorRegistration(const std::string& name, FUNCTOR_TYPE functor)
+ {
+ LLFunctorRegistry<FUNCTOR_TYPE>::instance().registerFunctor(name, functor);
+ }
};
#endif//LL_LLFUNCTORREGISTRY_H
diff --git a/indra/llui/llhelp.h b/indra/llui/llhelp.h
index 1726347a78..829a080655 100644
--- a/indra/llui/llhelp.h
+++ b/indra/llui/llhelp.h
@@ -1,4 +1,4 @@
-/**
+/**
* @file llhelp.h
* @brief Abstract interface to the Help system
* @author Tofu Linden
@@ -6,21 +6,21 @@
* $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$
*/
@@ -31,14 +31,14 @@
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
- virtual std::string preLoginTopic() = 0;
- // return topic to use for the top-level help, invoked by F1
- virtual std::string f1HelpTopic() = 0;
+ 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
+ virtual std::string preLoginTopic() = 0;
+ // return topic to use for the top-level help, invoked by F1
+ virtual std::string f1HelpTopic() = 0;
};
#endif // headerguard
diff --git a/indra/llui/lliconctrl.cpp b/indra/llui/lliconctrl.cpp
index 0738900f8c..7492c44aea 100644
--- a/indra/llui/lliconctrl.cpp
+++ b/indra/llui/lliconctrl.cpp
@@ -1,173 +1,173 @@
-/**
- * @file lliconctrl.cpp
- * @brief LLIconCtrl base class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "lliconctrl.h"
-
-// Linden library includes
-
-// Project includes
-#include "llcontrol.h"
-#include "llui.h"
-#include "lluictrlfactory.h"
-#include "lluiimage.h"
-#include "llwindow.h"
-
-#include "llgltexture.h"
-
-static LLDefaultChildRegistry::Register<LLIconCtrl> r("icon");
-
-LLIconCtrl::Params::Params()
-: image("image_name"),
- color("color"),
- use_draw_context_alpha("use_draw_context_alpha", true),
- interactable("interactable", false),
- scale_image("scale_image"),
- min_width("min_width", 0),
- min_height("min_height", 0)
-{}
-
-LLIconCtrl::LLIconCtrl(const LLIconCtrl::Params& p)
-: LLUICtrl(p),
- mColor(p.color()),
- mImagep(p.image),
- mUseDrawContextAlpha(p.use_draw_context_alpha),
- mInteractable(p.interactable),
- mPriority(0),
- mMinWidth(p.min_width),
- mMinHeight(p.min_height),
- mMaxWidth(0),
- mMaxHeight(0)
-{
- if (mImagep.notNull())
- {
- LLUICtrl::setValue(mImagep->getName());
- }
-}
-
-LLIconCtrl::~LLIconCtrl()
-{
- mImagep = NULL;
-}
-
-
-void LLIconCtrl::draw()
-{
- if( mImagep.notNull() )
- {
- const F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency();
- mImagep->draw(getLocalRect(), mColor.get() % alpha );
- }
-
- LLUICtrl::draw();
-}
-
-bool LLIconCtrl::handleHover(S32 x, S32 y, MASK mask)
-{
- if (mInteractable && getEnabled())
- {
- getWindow()->setCursor(UI_CURSOR_HAND);
- return true;
- }
- return LLUICtrl::handleHover(x, y, mask);
-}
-
-void LLIconCtrl::onVisibilityChange(bool new_visibility)
-{
- LLUICtrl::onVisibilityChange(new_visibility);
- if (mPriority == LLGLTexture::BOOST_ICON)
- {
- if (new_visibility)
- {
- loadImage(getValue(), mPriority);
- }
- else
- {
- mImagep = nullptr;
- }
- }
-}
-
-// virtual
-// value might be a string or a UUID
-void LLIconCtrl::setValue(const LLSD& value)
-{
- setValue(value, mPriority);
-}
-
-void LLIconCtrl::setValue(const LLSD& value, S32 priority)
-{
- LLSD tvalue(value);
- if (value.isString() && LLUUID::validate(value.asString()))
- {
- //RN: support UUIDs masquerading as strings
- tvalue = LLSD(LLUUID(value.asString()));
- }
- LLUICtrl::setValue(tvalue);
-
- loadImage(tvalue, priority);
-}
-
-void LLIconCtrl::loadImage(const LLSD& tvalue, S32 priority)
-{
- if(mPriority == LLGLTexture::BOOST_ICON && !getVisible()) return;
-
- if (tvalue.isUUID())
- {
- mImagep = LLUI::getUIImageByID(tvalue.asUUID(), priority);
- }
- else
- {
- mImagep = LLUI::getUIImage(tvalue.asString(), priority);
- }
-
- if(mImagep.notNull()
- && mImagep->getImage().notNull()
- && mMinWidth
- && mMinHeight)
- {
- S32 desired_draw_width = llmax(mMinWidth, mImagep->getWidth());
- S32 desired_draw_height = llmax(mMinHeight, mImagep->getHeight());
- if (mMaxWidth && mMaxHeight)
- {
- desired_draw_width = llmin(desired_draw_width, mMaxWidth);
- desired_draw_height = llmin(desired_draw_height, mMaxHeight);
- }
-
- mImagep->getImage()->setKnownDrawSize(desired_draw_width, desired_draw_height);
- }
-}
-
-std::string LLIconCtrl::getImageName() const
-{
- if (getValue().isString())
- return getValue().asString();
- else
- return std::string();
-}
-
-
+/**
+ * @file lliconctrl.cpp
+ * @brief LLIconCtrl base class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lliconctrl.h"
+
+// Linden library includes
+
+// Project includes
+#include "llcontrol.h"
+#include "llui.h"
+#include "lluictrlfactory.h"
+#include "lluiimage.h"
+#include "llwindow.h"
+
+#include "llgltexture.h"
+
+static LLDefaultChildRegistry::Register<LLIconCtrl> r("icon");
+
+LLIconCtrl::Params::Params()
+: image("image_name"),
+ color("color"),
+ use_draw_context_alpha("use_draw_context_alpha", true),
+ interactable("interactable", false),
+ scale_image("scale_image"),
+ min_width("min_width", 0),
+ min_height("min_height", 0)
+{}
+
+LLIconCtrl::LLIconCtrl(const LLIconCtrl::Params& p)
+: LLUICtrl(p),
+ mColor(p.color()),
+ mImagep(p.image),
+ mUseDrawContextAlpha(p.use_draw_context_alpha),
+ mInteractable(p.interactable),
+ mPriority(0),
+ mMinWidth(p.min_width),
+ mMinHeight(p.min_height),
+ mMaxWidth(0),
+ mMaxHeight(0)
+{
+ if (mImagep.notNull())
+ {
+ LLUICtrl::setValue(mImagep->getName());
+ }
+}
+
+LLIconCtrl::~LLIconCtrl()
+{
+ mImagep = NULL;
+}
+
+
+void LLIconCtrl::draw()
+{
+ if( mImagep.notNull() )
+ {
+ const F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency();
+ mImagep->draw(getLocalRect(), mColor.get() % alpha );
+ }
+
+ LLUICtrl::draw();
+}
+
+bool LLIconCtrl::handleHover(S32 x, S32 y, MASK mask)
+{
+ if (mInteractable && getEnabled())
+ {
+ getWindow()->setCursor(UI_CURSOR_HAND);
+ return true;
+ }
+ return LLUICtrl::handleHover(x, y, mask);
+}
+
+void LLIconCtrl::onVisibilityChange(bool new_visibility)
+{
+ LLUICtrl::onVisibilityChange(new_visibility);
+ if (mPriority == LLGLTexture::BOOST_ICON)
+ {
+ if (new_visibility)
+ {
+ loadImage(getValue(), mPriority);
+ }
+ else
+ {
+ mImagep = nullptr;
+ }
+ }
+}
+
+// virtual
+// value might be a string or a UUID
+void LLIconCtrl::setValue(const LLSD& value)
+{
+ setValue(value, mPriority);
+}
+
+void LLIconCtrl::setValue(const LLSD& value, S32 priority)
+{
+ LLSD tvalue(value);
+ if (value.isString() && LLUUID::validate(value.asString()))
+ {
+ //RN: support UUIDs masquerading as strings
+ tvalue = LLSD(LLUUID(value.asString()));
+ }
+ LLUICtrl::setValue(tvalue);
+
+ loadImage(tvalue, priority);
+}
+
+void LLIconCtrl::loadImage(const LLSD& tvalue, S32 priority)
+{
+ if(mPriority == LLGLTexture::BOOST_ICON && !getVisible()) return;
+
+ if (tvalue.isUUID())
+ {
+ mImagep = LLUI::getUIImageByID(tvalue.asUUID(), priority);
+ }
+ else
+ {
+ mImagep = LLUI::getUIImage(tvalue.asString(), priority);
+ }
+
+ if(mImagep.notNull()
+ && mImagep->getImage().notNull()
+ && mMinWidth
+ && mMinHeight)
+ {
+ S32 desired_draw_width = llmax(mMinWidth, mImagep->getWidth());
+ S32 desired_draw_height = llmax(mMinHeight, mImagep->getHeight());
+ if (mMaxWidth && mMaxHeight)
+ {
+ desired_draw_width = llmin(desired_draw_width, mMaxWidth);
+ desired_draw_height = llmin(desired_draw_height, mMaxHeight);
+ }
+
+ mImagep->getImage()->setKnownDrawSize(desired_draw_width, desired_draw_height);
+ }
+}
+
+std::string LLIconCtrl::getImageName() const
+{
+ if (getValue().isString())
+ return getValue().asString();
+ else
+ return std::string();
+}
+
+
diff --git a/indra/llui/lliconctrl.h b/indra/llui/lliconctrl.h
index 416de0cc9f..04910c123c 100644
--- a/indra/llui/lliconctrl.h
+++ b/indra/llui/lliconctrl.h
@@ -1,107 +1,107 @@
-/**
- * @file lliconctrl.h
- * @brief LLIconCtrl 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$
- */
-
-#ifndef LL_LLICONCTRL_H
-#define LL_LLICONCTRL_H
-
-#include "lluuid.h"
-#include "v4color.h"
-#include "lluictrl.h"
-#include "lluiimage.h"
-
-class LLTextBox;
-class LLUICtrlFactory;
-
-//
-// Classes
-//
-
-// Class for diplaying named UI textures
-// Do not use for displaying textures from network,
-// UI textures are stored permanently!
-class LLIconCtrl
-: public LLUICtrl
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Optional<LLUIImage*> image;
- Optional<LLUIColor> color;
- Optional<bool> use_draw_context_alpha,
- interactable;
- Optional<S32> min_width,
- min_height;
- Ignored scale_image;
-
- Params();
- };
-protected:
- LLIconCtrl(const Params&);
- friend class LLUICtrlFactory;
-
- void setValue(const LLSD& value, S32 priority);
-
-public:
- virtual ~LLIconCtrl();
-
- // llview overrides
- virtual void draw();
-
- // llview overrides
- virtual bool handleHover(S32 x, S32 y, MASK mask);
-
- // lluictrl overrides
- void onVisibilityChange(bool new_visibility);
- virtual void setValue(const LLSD& value );
-
- std::string getImageName() const;
-
- void setColor(const LLColor4& color) { mColor = color; }
- void setImage(LLPointer<LLUIImage> image) { mImagep = image; }
- const LLPointer<LLUIImage> getImage() { return mImagep; }
-
-protected:
- S32 mPriority;
-
- //the output size of the icon image if set.
- S32 mMinWidth,
- mMinHeight,
- mMaxWidth,
- mMaxHeight;
-
- // If set to true (default), use the draw context transparency.
- // If false, will use transparency returned by getCurrentTransparency(). See STORM-698.
- bool mUseDrawContextAlpha;
- bool mInteractable;
-
-private:
- void loadImage(const LLSD& value, S32 priority);
-
- LLUIColor mColor;
- LLPointer<LLUIImage> mImagep;
-};
-
-#endif
+/**
+ * @file lliconctrl.h
+ * @brief LLIconCtrl 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$
+ */
+
+#ifndef LL_LLICONCTRL_H
+#define LL_LLICONCTRL_H
+
+#include "lluuid.h"
+#include "v4color.h"
+#include "lluictrl.h"
+#include "lluiimage.h"
+
+class LLTextBox;
+class LLUICtrlFactory;
+
+//
+// Classes
+//
+
+// Class for diplaying named UI textures
+// Do not use for displaying textures from network,
+// UI textures are stored permanently!
+class LLIconCtrl
+: public LLUICtrl
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<LLUIImage*> image;
+ Optional<LLUIColor> color;
+ Optional<bool> use_draw_context_alpha,
+ interactable;
+ Optional<S32> min_width,
+ min_height;
+ Ignored scale_image;
+
+ Params();
+ };
+protected:
+ LLIconCtrl(const Params&);
+ friend class LLUICtrlFactory;
+
+ void setValue(const LLSD& value, S32 priority);
+
+public:
+ virtual ~LLIconCtrl();
+
+ // llview overrides
+ virtual void draw();
+
+ // llview overrides
+ virtual bool handleHover(S32 x, S32 y, MASK mask);
+
+ // lluictrl overrides
+ void onVisibilityChange(bool new_visibility);
+ virtual void setValue(const LLSD& value );
+
+ std::string getImageName() const;
+
+ void setColor(const LLColor4& color) { mColor = color; }
+ void setImage(LLPointer<LLUIImage> image) { mImagep = image; }
+ const LLPointer<LLUIImage> getImage() { return mImagep; }
+
+protected:
+ S32 mPriority;
+
+ //the output size of the icon image if set.
+ S32 mMinWidth,
+ mMinHeight,
+ mMaxWidth,
+ mMaxHeight;
+
+ // If set to true (default), use the draw context transparency.
+ // If false, will use transparency returned by getCurrentTransparency(). See STORM-698.
+ bool mUseDrawContextAlpha;
+ bool mInteractable;
+
+private:
+ void loadImage(const LLSD& value, S32 priority);
+
+ LLUIColor mColor;
+ LLPointer<LLUIImage> mImagep;
+};
+
+#endif
diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp
index fd8ad0721a..6eeb98fc53 100644
--- a/indra/llui/llkeywords.cpp
+++ b/indra/llui/llkeywords.cpp
@@ -1,813 +1,813 @@
-/**
- * @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$
- */
-
-#include "linden_common.h"
-
-#include <iostream>
-#include <fstream>
-
-#include "llkeywords.h"
-#include "llsdserialize.h"
-#include "lltexteditor.h"
-#include "llstl.h"
-
-inline bool LLKeywordToken::isHead(const llwchar* s) const
-{
- // strncmp is much faster than string compare
- 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;
- 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()
-{
- 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();
-}
-
-// 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)
-{
- 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);
-
- 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;
-
- 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;
-
- default:
- llassert(0);
- }
-}
-
-std::string LLKeywords::getArguments(LLSD& arguments)
-{
- std::string argString = "";
-
- if (arguments.isArray())
- {
- 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;
-}
-
-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
- {
- LL_WARNS("SyntaxLSL") << "Color key '" << key_in << "' not recognized." << LL_ENDL;
- }
-
- return LLUIColorTable::instance().getColor(color_group);
-}
-
-void LLKeywords::initialize(LLSD SyntaxXML)
-{
- mSyntax = SyntaxXML;
- mLoaded = true;
-}
-
-void LLKeywords::processTokens()
-{
- if (!mLoaded)
- {
- return;
- }
-
- // 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", "\"" );
-
- LLSD::map_iterator itr = mSyntax.beginMap();
- for ( ; itr != mSyntax.endMap(); ++itr)
- {
- if (itr->first == "llsd-lsl-syntax-version")
- {
- // Skip over version key.
- }
- else
- {
- if (itr->second.isMap())
- {
- processTokensGroup(itr->second, itr->first);
- }
- else
- {
- LL_WARNS("LSL-Tokens-Processing") << "Map for " + itr->first + " entries is missing! Ignoring." << LL_ENDL;
- }
- }
- }
- LL_INFOS("SyntaxLSL") << "Finished processing tokens." << LL_ENDL;
-}
-
-void LLKeywords::processTokensGroup(const LLSD& tokens, const std::string& group)
-{
- 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")
- {
- 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;
- }
-
- color_group = getColorGroup(group);
- LL_DEBUGS("SyntaxLSL") << "Group: '" << group << "', using color: '" << color_group << "'" << LL_ENDL;
-
- 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;
- }
- }
-
- std::string tooltip = "";
- switch (token_type)
- {
- case LLKeywordToken::TT_CONSTANT:
- if (getAttribute("type").length() > 0)
- {
- color_group = getColorGroup(group + "-" + getAttribute("type"));
- }
- else
- {
- color_group = getColorGroup(group);
- }
- 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)
- {
- copyData(other.mData, other.mLength);
- }
- else
- {
- mOwner = false;
- mLength = other.mLength;
- mData = other.mData;
- }
-}
-
-LLKeywords::WStringMapIndex::WStringMapIndex(const LLWString& str)
-{
- copyData(str.data(), str.size());
-}
-
-LLKeywords::WStringMapIndex::WStringMapIndex(const llwchar *start, size_t length)
-: mData(start)
-, mLength(length)
-, mOwner(false)
-{
-}
-
-LLKeywords::WStringMapIndex::~WStringMapIndex()
-{
- if (mOwner)
- {
- delete[] mData;
- }
-}
-
-void LLKeywords::WStringMapIndex::copyData(const llwchar *start, size_t length)
-{
- llwchar *data = new llwchar[length];
- memcpy((void*)data, (const void*)start, length * sizeof(llwchar));
-
- mOwner = true;
- mLength = length;
- mData = data;
-}
-
-bool LLKeywords::WStringMapIndex::operator<(const LLKeywords::WStringMapIndex &other) const
-{
- // NOTE: Since this is only used to organize a std::map, it doesn't matter if it uses correct collate order or not.
- // The comparison only needs to strictly order all possible strings, and be stable.
-
- bool result = false;
- const llwchar* self_iter = mData;
- const llwchar* self_end = mData + mLength;
- const llwchar* other_iter = other.mData;
- const llwchar* other_end = other.mData + other.mLength;
-
- while(true)
- {
- if(other_iter >= other_end)
- {
- // We've hit the end of other.
- // This covers two cases: other being shorter than self, or the strings being equal.
- // In either case, we want to return false.
- result = false;
- break;
- }
- else if(self_iter >= self_end)
- {
- // self is shorter than other.
- result = true;
- break;
- }
- else if(*self_iter != *other_iter)
- {
- // The current character differs. The strings are not equal.
- result = *self_iter < *other_iter;
- break;
- }
-
- self_iter++;
- other_iter++;
- }
-
- return result;
-}
-
-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, LLTextEditor& editor, LLStyleConstSP style)
-{
- 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( style, 0, text_len, editor ) );
-
- const llwchar* base = wtext.c_str();
- const llwchar* cur = base;
- while( *cur )
- {
- if( *cur == '\n' || cur == base )
- {
- if( *cur == '\n' )
- {
- LLTextSegmentPtr text_segment = new LLLineBreakTextSegment(style, cur-base);
- text_segment->setToken( 0 );
- insertSegment( *seg_list, text_segment, text_len, style, editor);
- cur++;
- if( !*cur || *cur == '\n' )
- {
- continue;
- }
- }
-
- // Skip white space
- while( *cur && iswspace(*cur) && (*cur != '\n') )
- {
- cur++;
- }
- if( !*cur || *cur == '\n' )
- {
- continue;
- }
-
- // cur is now at the first non-whitespace character of a new line
-
- // Line start tokens
- {
- bool line_done = false;
- for (token_list_t::iterator iter = mLineTokenList.begin();
- iter != mLineTokenList.end(); ++iter)
- {
- LLKeywordToken* cur_token = *iter;
- if( cur_token->isHead( cur ) )
- {
- S32 seg_start = cur - base;
- while( *cur && *cur != '\n' )
- {
- // skip the rest of the line
- 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, style, editor);
- line_done = true; // to break out of second loop.
- break;
- }
- }
-
- if( line_done )
- {
- continue;
- }
- }
- }
-
- // Skip white space
- while( *cur && iswspace(*cur) && (*cur != '\n') )
- {
- cur++;
- }
-
- while( *cur && *cur != '\n' )
- {
- // Check against delimiters
- {
- S32 seg_start = 0;
- LLKeywordToken* cur_delimiter = NULL;
- for (token_list_t::iterator iter = mDelimiterTokenList.begin();
- iter != mDelimiterTokenList.end(); ++iter)
- {
- LLKeywordToken* delimiter = *iter;
- if( delimiter->isHead( cur ) )
- {
- cur_delimiter = delimiter;
- break;
- }
- }
-
- if( cur_delimiter )
- {
- S32 between_delimiters = 0;
- S32 seg_end = 0;
-
- seg_start = cur - base;
- 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->isTail(cur))
- {
- // Check for an escape sequence.
- if (type == LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS && *cur == '\\')
- {
- // Count the number of backslashes.
- S32 num_backslashes = 0;
- while (*cur == '\\')
- {
- num_backslashes++;
- between_delimiters++;
- cur++;
- }
- // If the next character is the end delimiter?
- if (cur_delimiter->isTail(cur))
- {
- // If there was an odd number of backslashes, then this delimiter
- // does not end the sequence.
- if (num_backslashes % 2 == 1)
- {
- between_delimiters++;
- cur++;
- }
- else
- {
- // This is an end delimiter.
- break;
- }
- }
- }
- else
- {
- between_delimiters++;
- cur++;
- }
- }
-
- if( *cur )
- {
- 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->getLengthHead();
- }
- }
- else
- {
- 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->getLengthHead();
- }
-
- insertSegments(wtext, *seg_list,cur_delimiter, text_len, seg_start, seg_end, style, editor);
- /*
- LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_delimiter->getColor(), seg_start, seg_end, editor );
- text_segment->setToken( cur_delimiter );
- insertSegment( seg_list, text_segment, text_len, defaultColor, editor);
- */
- // Note: we don't increment cur, since the end of one delimited seg may be immediately
- // followed by the start of another one.
- continue;
- }
- }
-
- // check against words
- llwchar prev = cur > base ? *(cur-1) : 0;
- if( !iswalnum( prev ) && (prev != '_') )
- {
- const llwchar* p = cur;
- while( iswalnum( *p ) || (*p == '_') )
- {
- p++;
- }
- S32 seg_len = p - cur;
- if( seg_len > 0 )
- {
- WStringMapIndex word( cur, seg_len );
- word_token_map_t::iterator map_iter = mWordTokenMap.find(word);
- if( map_iter != mWordTokenMap.end() )
- {
- LLKeywordToken* cur_token = map_iter->second;
- S32 seg_start = cur - base;
- S32 seg_end = seg_start + seg_len;
-
- // LL_INFOS("SyntaxLSL") << "Seg: [" << word.c_str() << "]" << LL_ENDL;
-
- insertSegments(wtext, *seg_list,cur_token, text_len, seg_start, seg_end, style, editor);
- }
- cur += seg_len;
- continue;
- }
- }
-
- if( *cur && *cur != '\n' )
- {
- cur++;
- }
- }
- }
-}
-
-void LLKeywords::insertSegments(const LLWString& wtext, std::vector<LLTextSegmentPtr>& seg_list, LLKeywordToken* cur_token, S32 text_len, S32 seg_start, S32 seg_end, LLStyleConstSP style, LLTextEditor& editor )
-{
- std::string::size_type pos = wtext.find('\n',seg_start);
-
- LLStyleConstSP cur_token_style = new LLStyle(LLStyle::Params().font(style->getFont()).color(cur_token->getColor()));
-
- while (pos!=-1 && pos < (std::string::size_type)seg_end)
- {
- if (pos!=seg_start)
- {
- LLTextSegmentPtr text_segment = new LLNormalTextSegment(cur_token_style, seg_start, pos, editor);
- text_segment->setToken( cur_token );
- insertSegment( seg_list, text_segment, text_len, style, editor);
- }
-
- LLTextSegmentPtr text_segment = new LLLineBreakTextSegment(style, pos);
- text_segment->setToken( cur_token );
- insertSegment( seg_list, text_segment, text_len, style, editor);
-
- seg_start = pos+1;
- pos = wtext.find('\n',seg_start);
- }
-
- LLTextSegmentPtr text_segment = new LLNormalTextSegment(cur_token_style, seg_start, seg_end, editor);
- text_segment->setToken( cur_token );
- insertSegment( seg_list, text_segment, text_len, style, editor);
-}
-
-void LLKeywords::insertSegment(std::vector<LLTextSegmentPtr>& seg_list, LLTextSegmentPtr new_segment, S32 text_len, const LLColor4 &defaultColor, LLTextEditor& editor )
-{
- LLTextSegmentPtr last = seg_list.back();
- S32 new_seg_end = new_segment->getEnd();
-
- if( new_segment->getStart() == last->getStart() )
- {
- seg_list.pop_back();
- }
- else
- {
- last->setEnd( new_segment->getStart() );
- }
- seg_list.push_back( new_segment );
-
- if( new_seg_end < text_len )
- {
- seg_list.push_back( new LLNormalTextSegment( defaultColor, new_seg_end, text_len, editor ) );
- }
-}
-
-void LLKeywords::insertSegment(std::vector<LLTextSegmentPtr>& seg_list, LLTextSegmentPtr new_segment, S32 text_len, LLStyleConstSP style, LLTextEditor& editor )
-{
- LLTextSegmentPtr last = seg_list.back();
- S32 new_seg_end = new_segment->getEnd();
-
- if( new_segment->getStart() == last->getStart() )
- {
- seg_list.pop_back();
- }
- else
- {
- last->setEnd( new_segment->getStart() );
- }
- seg_list.push_back( new_segment );
-
- if( new_seg_end < text_len )
- {
- seg_list.push_back( new LLNormalTextSegment( style, new_seg_end, text_len, editor ) );
- }
-}
-
-#ifdef _DEBUG
-void LLKeywords::dump()
-{
- LL_INFOS() << "LLKeywords" << LL_ENDL;
-
-
- LL_INFOS() << "LLKeywords::sWordTokenMap" << LL_ENDL;
- word_token_map_t::iterator word_token_iter = mWordTokenMap.begin();
- while( word_token_iter != mWordTokenMap.end() )
- {
- LLKeywordToken* word_token = word_token_iter->second;
- word_token->dump();
- ++word_token_iter;
- }
-
- LL_INFOS() << "LLKeywords::sLineTokenList" << LL_ENDL;
- for (token_list_t::iterator iter = mLineTokenList.begin();
- iter != mLineTokenList.end(); ++iter)
- {
- LLKeywordToken* line_token = *iter;
- line_token->dump();
- }
-
-
- LL_INFOS() << "LLKeywords::sDelimiterTokenList" << LL_ENDL;
- for (token_list_t::iterator iter = mDelimiterTokenList.begin();
- iter != mDelimiterTokenList.end(); ++iter)
- {
- LLKeywordToken* delimiter_token = *iter;
- delimiter_token->dump();
- }
-}
-
-void LLKeywordToken::dump()
-{
- LL_INFOS() << "[" <<
- mColor.mV[VX] << ", " <<
- mColor.mV[VY] << ", " <<
- mColor.mV[VZ] << "] [" <<
- wstring_to_utf8str(mToken) << "]" <<
- LL_ENDL;
-}
-
-#endif // DEBUG
+/**
+ * @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$
+ */
+
+#include "linden_common.h"
+
+#include <iostream>
+#include <fstream>
+
+#include "llkeywords.h"
+#include "llsdserialize.h"
+#include "lltexteditor.h"
+#include "llstl.h"
+
+inline bool LLKeywordToken::isHead(const llwchar* s) const
+{
+ // strncmp is much faster than string compare
+ 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;
+ 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()
+{
+ 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();
+}
+
+// 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)
+{
+ 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);
+
+ 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;
+
+ 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;
+
+ default:
+ llassert(0);
+ }
+}
+
+std::string LLKeywords::getArguments(LLSD& arguments)
+{
+ std::string argString = "";
+
+ if (arguments.isArray())
+ {
+ 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;
+}
+
+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
+ {
+ LL_WARNS("SyntaxLSL") << "Color key '" << key_in << "' not recognized." << LL_ENDL;
+ }
+
+ return LLUIColorTable::instance().getColor(color_group);
+}
+
+void LLKeywords::initialize(LLSD SyntaxXML)
+{
+ mSyntax = SyntaxXML;
+ mLoaded = true;
+}
+
+void LLKeywords::processTokens()
+{
+ if (!mLoaded)
+ {
+ return;
+ }
+
+ // 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", "\"" );
+
+ LLSD::map_iterator itr = mSyntax.beginMap();
+ for ( ; itr != mSyntax.endMap(); ++itr)
+ {
+ if (itr->first == "llsd-lsl-syntax-version")
+ {
+ // Skip over version key.
+ }
+ else
+ {
+ if (itr->second.isMap())
+ {
+ processTokensGroup(itr->second, itr->first);
+ }
+ else
+ {
+ LL_WARNS("LSL-Tokens-Processing") << "Map for " + itr->first + " entries is missing! Ignoring." << LL_ENDL;
+ }
+ }
+ }
+ LL_INFOS("SyntaxLSL") << "Finished processing tokens." << LL_ENDL;
+}
+
+void LLKeywords::processTokensGroup(const LLSD& tokens, const std::string& group)
+{
+ 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")
+ {
+ 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;
+ }
+
+ color_group = getColorGroup(group);
+ LL_DEBUGS("SyntaxLSL") << "Group: '" << group << "', using color: '" << color_group << "'" << LL_ENDL;
+
+ 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;
+ }
+ }
+
+ std::string tooltip = "";
+ switch (token_type)
+ {
+ case LLKeywordToken::TT_CONSTANT:
+ if (getAttribute("type").length() > 0)
+ {
+ color_group = getColorGroup(group + "-" + getAttribute("type"));
+ }
+ else
+ {
+ color_group = getColorGroup(group);
+ }
+ 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)
+ {
+ copyData(other.mData, other.mLength);
+ }
+ else
+ {
+ mOwner = false;
+ mLength = other.mLength;
+ mData = other.mData;
+ }
+}
+
+LLKeywords::WStringMapIndex::WStringMapIndex(const LLWString& str)
+{
+ copyData(str.data(), str.size());
+}
+
+LLKeywords::WStringMapIndex::WStringMapIndex(const llwchar *start, size_t length)
+: mData(start)
+, mLength(length)
+, mOwner(false)
+{
+}
+
+LLKeywords::WStringMapIndex::~WStringMapIndex()
+{
+ if (mOwner)
+ {
+ delete[] mData;
+ }
+}
+
+void LLKeywords::WStringMapIndex::copyData(const llwchar *start, size_t length)
+{
+ llwchar *data = new llwchar[length];
+ memcpy((void*)data, (const void*)start, length * sizeof(llwchar));
+
+ mOwner = true;
+ mLength = length;
+ mData = data;
+}
+
+bool LLKeywords::WStringMapIndex::operator<(const LLKeywords::WStringMapIndex &other) const
+{
+ // NOTE: Since this is only used to organize a std::map, it doesn't matter if it uses correct collate order or not.
+ // The comparison only needs to strictly order all possible strings, and be stable.
+
+ bool result = false;
+ const llwchar* self_iter = mData;
+ const llwchar* self_end = mData + mLength;
+ const llwchar* other_iter = other.mData;
+ const llwchar* other_end = other.mData + other.mLength;
+
+ while(true)
+ {
+ if(other_iter >= other_end)
+ {
+ // We've hit the end of other.
+ // This covers two cases: other being shorter than self, or the strings being equal.
+ // In either case, we want to return false.
+ result = false;
+ break;
+ }
+ else if(self_iter >= self_end)
+ {
+ // self is shorter than other.
+ result = true;
+ break;
+ }
+ else if(*self_iter != *other_iter)
+ {
+ // The current character differs. The strings are not equal.
+ result = *self_iter < *other_iter;
+ break;
+ }
+
+ self_iter++;
+ other_iter++;
+ }
+
+ return result;
+}
+
+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, LLTextEditor& editor, LLStyleConstSP style)
+{
+ 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( style, 0, text_len, editor ) );
+
+ const llwchar* base = wtext.c_str();
+ const llwchar* cur = base;
+ while( *cur )
+ {
+ if( *cur == '\n' || cur == base )
+ {
+ if( *cur == '\n' )
+ {
+ LLTextSegmentPtr text_segment = new LLLineBreakTextSegment(style, cur-base);
+ text_segment->setToken( 0 );
+ insertSegment( *seg_list, text_segment, text_len, style, editor);
+ cur++;
+ if( !*cur || *cur == '\n' )
+ {
+ continue;
+ }
+ }
+
+ // Skip white space
+ while( *cur && iswspace(*cur) && (*cur != '\n') )
+ {
+ cur++;
+ }
+ if( !*cur || *cur == '\n' )
+ {
+ continue;
+ }
+
+ // cur is now at the first non-whitespace character of a new line
+
+ // Line start tokens
+ {
+ bool line_done = false;
+ for (token_list_t::iterator iter = mLineTokenList.begin();
+ iter != mLineTokenList.end(); ++iter)
+ {
+ LLKeywordToken* cur_token = *iter;
+ if( cur_token->isHead( cur ) )
+ {
+ S32 seg_start = cur - base;
+ while( *cur && *cur != '\n' )
+ {
+ // skip the rest of the line
+ 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, style, editor);
+ line_done = true; // to break out of second loop.
+ break;
+ }
+ }
+
+ if( line_done )
+ {
+ continue;
+ }
+ }
+ }
+
+ // Skip white space
+ while( *cur && iswspace(*cur) && (*cur != '\n') )
+ {
+ cur++;
+ }
+
+ while( *cur && *cur != '\n' )
+ {
+ // Check against delimiters
+ {
+ S32 seg_start = 0;
+ LLKeywordToken* cur_delimiter = NULL;
+ for (token_list_t::iterator iter = mDelimiterTokenList.begin();
+ iter != mDelimiterTokenList.end(); ++iter)
+ {
+ LLKeywordToken* delimiter = *iter;
+ if( delimiter->isHead( cur ) )
+ {
+ cur_delimiter = delimiter;
+ break;
+ }
+ }
+
+ if( cur_delimiter )
+ {
+ S32 between_delimiters = 0;
+ S32 seg_end = 0;
+
+ seg_start = cur - base;
+ 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->isTail(cur))
+ {
+ // Check for an escape sequence.
+ if (type == LLKeywordToken::TT_DOUBLE_QUOTATION_MARKS && *cur == '\\')
+ {
+ // Count the number of backslashes.
+ S32 num_backslashes = 0;
+ while (*cur == '\\')
+ {
+ num_backslashes++;
+ between_delimiters++;
+ cur++;
+ }
+ // If the next character is the end delimiter?
+ if (cur_delimiter->isTail(cur))
+ {
+ // If there was an odd number of backslashes, then this delimiter
+ // does not end the sequence.
+ if (num_backslashes % 2 == 1)
+ {
+ between_delimiters++;
+ cur++;
+ }
+ else
+ {
+ // This is an end delimiter.
+ break;
+ }
+ }
+ }
+ else
+ {
+ between_delimiters++;
+ cur++;
+ }
+ }
+
+ if( *cur )
+ {
+ 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->getLengthHead();
+ }
+ }
+ else
+ {
+ 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->getLengthHead();
+ }
+
+ insertSegments(wtext, *seg_list,cur_delimiter, text_len, seg_start, seg_end, style, editor);
+ /*
+ LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_delimiter->getColor(), seg_start, seg_end, editor );
+ text_segment->setToken( cur_delimiter );
+ insertSegment( seg_list, text_segment, text_len, defaultColor, editor);
+ */
+ // Note: we don't increment cur, since the end of one delimited seg may be immediately
+ // followed by the start of another one.
+ continue;
+ }
+ }
+
+ // check against words
+ llwchar prev = cur > base ? *(cur-1) : 0;
+ if( !iswalnum( prev ) && (prev != '_') )
+ {
+ const llwchar* p = cur;
+ while( iswalnum( *p ) || (*p == '_') )
+ {
+ p++;
+ }
+ S32 seg_len = p - cur;
+ if( seg_len > 0 )
+ {
+ WStringMapIndex word( cur, seg_len );
+ word_token_map_t::iterator map_iter = mWordTokenMap.find(word);
+ if( map_iter != mWordTokenMap.end() )
+ {
+ LLKeywordToken* cur_token = map_iter->second;
+ S32 seg_start = cur - base;
+ S32 seg_end = seg_start + seg_len;
+
+ // LL_INFOS("SyntaxLSL") << "Seg: [" << word.c_str() << "]" << LL_ENDL;
+
+ insertSegments(wtext, *seg_list,cur_token, text_len, seg_start, seg_end, style, editor);
+ }
+ cur += seg_len;
+ continue;
+ }
+ }
+
+ if( *cur && *cur != '\n' )
+ {
+ cur++;
+ }
+ }
+ }
+}
+
+void LLKeywords::insertSegments(const LLWString& wtext, std::vector<LLTextSegmentPtr>& seg_list, LLKeywordToken* cur_token, S32 text_len, S32 seg_start, S32 seg_end, LLStyleConstSP style, LLTextEditor& editor )
+{
+ std::string::size_type pos = wtext.find('\n',seg_start);
+
+ LLStyleConstSP cur_token_style = new LLStyle(LLStyle::Params().font(style->getFont()).color(cur_token->getColor()));
+
+ while (pos!=-1 && pos < (std::string::size_type)seg_end)
+ {
+ if (pos!=seg_start)
+ {
+ LLTextSegmentPtr text_segment = new LLNormalTextSegment(cur_token_style, seg_start, pos, editor);
+ text_segment->setToken( cur_token );
+ insertSegment( seg_list, text_segment, text_len, style, editor);
+ }
+
+ LLTextSegmentPtr text_segment = new LLLineBreakTextSegment(style, pos);
+ text_segment->setToken( cur_token );
+ insertSegment( seg_list, text_segment, text_len, style, editor);
+
+ seg_start = pos+1;
+ pos = wtext.find('\n',seg_start);
+ }
+
+ LLTextSegmentPtr text_segment = new LLNormalTextSegment(cur_token_style, seg_start, seg_end, editor);
+ text_segment->setToken( cur_token );
+ insertSegment( seg_list, text_segment, text_len, style, editor);
+}
+
+void LLKeywords::insertSegment(std::vector<LLTextSegmentPtr>& seg_list, LLTextSegmentPtr new_segment, S32 text_len, const LLColor4 &defaultColor, LLTextEditor& editor )
+{
+ LLTextSegmentPtr last = seg_list.back();
+ S32 new_seg_end = new_segment->getEnd();
+
+ if( new_segment->getStart() == last->getStart() )
+ {
+ seg_list.pop_back();
+ }
+ else
+ {
+ last->setEnd( new_segment->getStart() );
+ }
+ seg_list.push_back( new_segment );
+
+ if( new_seg_end < text_len )
+ {
+ seg_list.push_back( new LLNormalTextSegment( defaultColor, new_seg_end, text_len, editor ) );
+ }
+}
+
+void LLKeywords::insertSegment(std::vector<LLTextSegmentPtr>& seg_list, LLTextSegmentPtr new_segment, S32 text_len, LLStyleConstSP style, LLTextEditor& editor )
+{
+ LLTextSegmentPtr last = seg_list.back();
+ S32 new_seg_end = new_segment->getEnd();
+
+ if( new_segment->getStart() == last->getStart() )
+ {
+ seg_list.pop_back();
+ }
+ else
+ {
+ last->setEnd( new_segment->getStart() );
+ }
+ seg_list.push_back( new_segment );
+
+ if( new_seg_end < text_len )
+ {
+ seg_list.push_back( new LLNormalTextSegment( style, new_seg_end, text_len, editor ) );
+ }
+}
+
+#ifdef _DEBUG
+void LLKeywords::dump()
+{
+ LL_INFOS() << "LLKeywords" << LL_ENDL;
+
+
+ LL_INFOS() << "LLKeywords::sWordTokenMap" << LL_ENDL;
+ word_token_map_t::iterator word_token_iter = mWordTokenMap.begin();
+ while( word_token_iter != mWordTokenMap.end() )
+ {
+ LLKeywordToken* word_token = word_token_iter->second;
+ word_token->dump();
+ ++word_token_iter;
+ }
+
+ LL_INFOS() << "LLKeywords::sLineTokenList" << LL_ENDL;
+ for (token_list_t::iterator iter = mLineTokenList.begin();
+ iter != mLineTokenList.end(); ++iter)
+ {
+ LLKeywordToken* line_token = *iter;
+ line_token->dump();
+ }
+
+
+ LL_INFOS() << "LLKeywords::sDelimiterTokenList" << LL_ENDL;
+ for (token_list_t::iterator iter = mDelimiterTokenList.begin();
+ iter != mDelimiterTokenList.end(); ++iter)
+ {
+ LLKeywordToken* delimiter_token = *iter;
+ delimiter_token->dump();
+ }
+}
+
+void LLKeywordToken::dump()
+{
+ LL_INFOS() << "[" <<
+ mColor.mV[VX] << ", " <<
+ mColor.mV[VY] << ", " <<
+ mColor.mV[VZ] << "] [" <<
+ wstring_to_utf8str(mToken) << "]" <<
+ LL_ENDL;
+}
+
+#endif // DEBUG
diff --git a/indra/llui/llkeywords.h b/indra/llui/llkeywords.h
index 2410fe7d5a..9dcdea121b 100644
--- a/indra/llui/llkeywords.h
+++ b/indra/llui/llkeywords.h
@@ -44,162 +44,162 @@ typedef LLPointer<LLTextSegment> LLTextSegmentPtr;
class LLKeywordToken
{
public:
- /**
- * @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 ),
- mDelimiter( delimiter ) // right delimiter
- {
- }
-
- 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 LLColor4& getColor() const { return mColor; }
- ETokenType getType() const { return mType; }
- const LLWString& getToolTip() const { return mToolTip; }
- const LLWString& getDelimiter() const { return mDelimiter; }
+ /**
+ * @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 ),
+ mDelimiter( delimiter ) // right delimiter
+ {
+ }
+
+ 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 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();
+ void dump();
#endif
private:
- ETokenType mType;
- LLWString mToken;
- LLColor4 mColor;
- LLWString mToolTip;
- LLWString mDelimiter;
+ ETokenType mType;
+ LLWString mToken;
+ LLColor4 mColor;
+ LLWString mToolTip;
+ LLWString mDelimiter;
};
class LLKeywords
{
public:
- LLKeywords();
- ~LLKeywords();
+ LLKeywords();
+ ~LLKeywords();
- void clearLoaded() { mLoaded = false; }
- LLColor4 getColorGroup(const std::string& key_in);
- 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,
- class LLTextEditor& editor,
+ void findSegments(std::vector<LLTextSegmentPtr> *seg_list,
+ const LLWString& text,
+ class LLTextEditor& editor,
LLStyleConstSP style);
- void initialize(LLSD SyntaxXML);
- void processTokens();
-
- // Add the token as described
- void addToken(LLKeywordToken::ETokenType type,
- const std::string& key,
- 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
- // because it's not possible to search such a map without creating an LLWString.
- // Using this class as the map index instead allows us to search using segments of an existing
- // text run without copying them first, which greatly reduces overhead in LLKeywords::findSegments().
- class WStringMapIndex
- {
- public:
- // copy constructor
- WStringMapIndex(const WStringMapIndex& other);
- // constructor from a string (copies the string's data into the new object)
- WStringMapIndex(const LLWString& str);
- // constructor from pointer and length
- // NOTE: does NOT copy data, caller must ensure that the lifetime of the pointer exceeds that of the new object!
- WStringMapIndex(const llwchar *start, size_t length);
- ~WStringMapIndex();
- bool operator<(const WStringMapIndex &other) const;
- private:
- void copyData(const llwchar *start, size_t length);
- const llwchar *mData;
- size_t mLength;
- bool mOwner;
-
-
- LLColor4 mColor;
- };
-
- typedef std::map<WStringMapIndex, LLKeywordToken*> word_token_map_t;
- typedef word_token_map_t::const_iterator keyword_iterator_t;
- keyword_iterator_t begin() const { return mWordTokenMap.begin(); }
- keyword_iterator_t end() const { return mWordTokenMap.end(); }
-
- typedef std::map<WStringMapIndex, LLColor4> group_color_map_t;
- typedef group_color_map_t::const_iterator color_iterator_t;
- group_color_map_t mColorGroupMap;
+ void initialize(LLSD SyntaxXML);
+ void processTokens();
+
+ // Add the token as described
+ void addToken(LLKeywordToken::ETokenType type,
+ const std::string& key,
+ 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
+ // because it's not possible to search such a map without creating an LLWString.
+ // Using this class as the map index instead allows us to search using segments of an existing
+ // text run without copying them first, which greatly reduces overhead in LLKeywords::findSegments().
+ class WStringMapIndex
+ {
+ public:
+ // copy constructor
+ WStringMapIndex(const WStringMapIndex& other);
+ // constructor from a string (copies the string's data into the new object)
+ WStringMapIndex(const LLWString& str);
+ // constructor from pointer and length
+ // NOTE: does NOT copy data, caller must ensure that the lifetime of the pointer exceeds that of the new object!
+ WStringMapIndex(const llwchar *start, size_t length);
+ ~WStringMapIndex();
+ bool operator<(const WStringMapIndex &other) const;
+ private:
+ void copyData(const llwchar *start, size_t length);
+ const llwchar *mData;
+ size_t mLength;
+ bool mOwner;
+
+
+ LLColor4 mColor;
+ };
+
+ typedef std::map<WStringMapIndex, LLKeywordToken*> word_token_map_t;
+ typedef word_token_map_t::const_iterator keyword_iterator_t;
+ keyword_iterator_t begin() const { return mWordTokenMap.begin(); }
+ keyword_iterator_t end() const { return mWordTokenMap.end(); }
+
+ 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();
+ void dump();
#endif
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,
- LLStyleConstSP style,
- LLTextEditor& editor);
+ 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,
+ LLStyleConstSP style,
+ LLTextEditor& editor);
void insertSegment(std::vector<LLTextSegmentPtr>& seg_list, LLTextSegmentPtr new_segment, S32 text_len, LLStyleConstSP style, 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;
+ 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);
+ 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);
+ std::string getArguments(LLSD& arguments);
};
#endif // LL_LLKEYWORDS_H
diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp
index cf8cfa6ea6..1b6a785f27 100644
--- a/indra/llui/lllayoutstack.cpp
+++ b/indra/llui/lllayoutstack.cpp
@@ -1,1022 +1,1022 @@
-/**
- * @file lllayoutstack.cpp
- * @brief LLLayout class - dynamic stacking of UI elements
- *
- * $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$
- */
-
-// Opaque view with a background and a border. Can contain LLUICtrls.
-
-#include "linden_common.h"
-
-#include "lllayoutstack.h"
-
-#include "lllocalcliprect.h"
-#include "llpanel.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");
-
-//
-// 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),
- mAutoResize(p.auto_resize),
- mUserResize(p.user_resize),
- mCollapsed(false),
- mCollapseAmt(0.f),
- mVisibleAmt(1.f), // default to fully visible
- mResizeBar(NULL),
- mFractionalSize(0.f),
- mTargetDim(0),
- mIgnoreReshape(false),
- mOrientation(LLLayoutStack::HORIZONTAL)
-{
- // panels initialized as hidden should not start out partially visible
- if (!getVisible())
- {
- mVisibleAmt = 0.f;
- }
-}
-
-void LLLayoutPanel::initFromParams(const Params& p)
-{
- LLPanel::initFromParams(p);
- setFollowsNone();
-}
-
-
-LLLayoutPanel::~LLLayoutPanel()
-{
- // probably not necessary, but...
- delete mResizeBar;
- mResizeBar = NULL;
-
- gFocusMgr.removeKeyboardFocusWithoutCallback(this);
-}
-
-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)
-{
- LLRect new_rect(getRect());
- if (mOrientation == LLLayoutStack::HORIZONTAL)
- {
- new_rect.mRight = new_rect.mLeft + value;
- }
- else
- {
- 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 && mUserResize && 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() && !LLView::sForceReshape) return;
-
- if (!mIgnoreReshape && !mAutoResize)
- {
- 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);
-}
-
-//
-// LLLayoutStack
-//
-
-LLLayoutStack::Params::Params()
-: orientation("orientation"),
- animate("animate", true),
- clip("clip", true),
- open_time_constant("open_time_constant", 0.02f),
- close_time_constant("close_time_constant", 0.03f),
- resize_bar_overlap("resize_bar_overlap", 1),
- border_size("border_size", LLCachedControl<S32>(*LLUI::getInstance()->mSettingGroups["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),
- drag_handle_color("drag_handle_color", LLUIColorTable::instance().getColor("ResizebarBody"))
-{
- addSynonym(border_size, "drag_handle_gap");
-}
-
-LLLayoutStack::LLLayoutStack(const LLLayoutStack::Params& p)
-: LLView(p),
- 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),
- 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),
- mDragHandleColor(p.drag_handle_color())
-{
-}
-
-LLLayoutStack::~LLLayoutStack()
-{
- e_panel_list_t panels = mPanels; // copy list of panel pointers
- mPanels.clear(); // clear so that removeChild() calls don't cause trouble
- std::for_each(panels.begin(), panels.end(), DeletePointer());
-}
-
-void LLLayoutStack::draw()
-{
- updateLayout();
-
- // always clip to stack itself
- LLLocalClipRect clip(getLocalRect());
- for (LLLayoutPanel* panelp : mPanels)
- {
- if ((!panelp->getVisible() || panelp->mCollapsed)
- && (panelp->mVisibleAmt < 0.001f || !mAnimate))
- {
- // essentially invisible
- continue;
- }
- // clip to layout rectangle, not bounding rectangle
- LLRect clip_rect = panelp->getRect();
- // scale clipping rectangle by visible amount
- if (mOrientation == HORIZONTAL)
- {
- clip_rect.mRight = clip_rect.mLeft + panelp->getVisibleDim();
- }
- else
- {
- clip_rect.mBottom = clip_rect.mTop - panelp->getVisibleDim();
- }
-
- {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());
- }
- }
-}
-
-void LLLayoutStack::deleteAllChildren()
-{
- mPanels.clear();
- LLView::deleteAllChildren();
-
- // Not really needed since nothing is left to
- // display, but for the sake of consistency
- updateFractionalSizes();
- mNeedsLayout = true;
-}
-
-void LLLayoutStack::removeChild(LLView* view)
-{
- LLLayoutPanel* embedded_panelp = findEmbeddedPanel(dynamic_cast<LLPanel*>(view));
-
- if (embedded_panelp)
- {
- mPanels.erase(std::find(mPanels.begin(), mPanels.end(), embedded_panelp));
- LLView::removeChild(view);
- updateFractionalSizes();
- mNeedsLayout = true;
- }
- else
- {
- LLView::removeChild(view);
- }
-}
-
-bool LLLayoutStack::postBuild()
-{
- updateLayout();
- return true;
-}
-
-bool LLLayoutStack::addChild(LLView* child, S32 tab_group)
-{
- LLLayoutPanel* panelp = dynamic_cast<LLLayoutPanel*>(child);
- if (panelp)
- {
- panelp->setOrientation(mOrientation);
- mPanels.push_back(panelp);
- createResizeBar(panelp);
- mNeedsLayout = true;
- }
- bool result = LLView::addChild(child, tab_group);
-
- updateFractionalSizes();
- return result;
-}
-
-void LLLayoutStack::addPanel(LLLayoutPanel* panel, EAnimate animate)
-{
- addChild(panel);
-
- // panel starts off invisible (collapsed)
- if (animate == ANIMATE)
- {
- panel->mVisibleAmt = 0.f;
- panel->setVisible(true);
- }
-}
-
-void LLLayoutStack::collapsePanel(LLPanel* panel, bool collapsed)
-{
- LLLayoutPanel* panel_container = findEmbeddedPanel(panel);
- if (!panel_container) return;
-
- panel_container->mCollapsed = collapsed;
- mNeedsLayout = true;
-}
-
-class LLImagePanel : public LLPanel
-{
-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() {}
-
- void draw()
- {
- 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();
- }
-
-private:
- bool mHorizontal;
-};
-
-void LLLayoutStack::updateLayout()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
-
- if (!mNeedsLayout) return;
-
- bool continue_animating = animatePanels();
- F32 total_visible_fraction = 0.f;
- S32 space_to_distribute = (mOrientation == HORIZONTAL)
- ? getRect().getWidth()
- : getRect().getHeight();
-
- // first, assign minimum dimensions
- for (LLLayoutPanel* panelp : mPanels)
- {
- 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();
- }
-
- llassert(total_visible_fraction < 1.05f);
-
- // don't need spacing after last panel
- if (!mPanels.empty())
- {
- space_to_distribute += ll_round(F32(mPanelSpacing) * mPanels.back()->getVisibleAmount());
- }
-
- S32 remaining_space = space_to_distribute;
- if (space_to_distribute > 0 && total_visible_fraction > 0.f)
- { // give space proportionally to visible auto resize panels
- for (LLLayoutPanel* panelp : mPanels)
- {
- if (panelp->mAutoResize)
- {
- F32 fraction_to_distribute = (panelp->mFractionalSize * panelp->getAutoResizeFactor()) / (total_visible_fraction);
- S32 delta = ll_round((F32)space_to_distribute * fraction_to_distribute);
- panelp->mTargetDim += delta;
- remaining_space -= delta;
- }
- }
- }
-
- // distribute any left over pixels to non-collapsed, visible panels
- for (LLLayoutPanel* panelp : mPanels)
- {
- if (remaining_space == 0) break;
-
- if (panelp->mAutoResize
- && !panelp->mCollapsed
- && panelp->getVisible())
- {
- 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();
-
- for (LLLayoutPanel* panelp : mPanels)
- {
- F32 panel_dim = llmax(panelp->getExpandedMinDim(), panelp->mTargetDim);
-
- LLRect panel_rect;
- if (mOrientation == HORIZONTAL)
- {
- panel_rect.setLeftTopAndSize(ll_round(cur_pos),
- getRect().getHeight(),
- ll_round(panel_dim),
- getRect().getHeight());
- }
- else
- {
- 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)
- {
- cur_pos += panel_visible_dim + panel_spacing;
-
- if (mShowDragHandle && panel_spacing_round > mDragHandleThickness)
- {
- resize_bar_rect.mLeft = panel_rect.mRight + mDragHandleShift;
- resize_bar_rect.mRight = resize_bar_rect.mLeft + mDragHandleThickness;
- }
- else
- {
- resize_bar_rect.mLeft = panel_rect.mRight - mResizeBarOverlap;
- resize_bar_rect.mRight = panel_rect.mRight + panel_spacing_round + mResizeBarOverlap;
- }
-
- if (mShowDragHandle)
- {
- resize_bar_rect.mBottom += mDragHandleSecondIndent;
- resize_bar_rect.mTop -= mDragHandleFirstIndent;
- }
-
- }
- else //VERTICAL
- {
- cur_pos -= panel_visible_dim + panel_spacing;
-
- if (mShowDragHandle && panel_spacing_round > mDragHandleThickness)
- {
- resize_bar_rect.mTop = panel_rect.mBottom - mDragHandleShift;
- resize_bar_rect.mBottom = resize_bar_rect.mTop - mDragHandleThickness;
- }
- else
- {
- resize_bar_rect.mTop = panel_rect.mBottom + mResizeBarOverlap;
- resize_bar_rect.mBottom = panel_rect.mBottom - panel_spacing_round - mResizeBarOverlap;
- }
-
- if (mShowDragHandle)
- {
- 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);
- }
-
- 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
-
-void LLLayoutStack::setPanelSpacing(S32 val)
-{
- if (mPanelSpacing != val)
- {
- mPanelSpacing = val;
- mNeedsLayout = true;
- }
-}
-
-LLLayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const
-{
- if (!panelp) return NULL;
-
- for (LLLayoutPanel* p : mPanels)
- {
- if (p == panelp)
- {
- return p;
- }
- }
- return NULL;
-}
-
-LLLayoutPanel* LLLayoutStack::findEmbeddedPanelByName(const std::string& name) const
-{
- LLLayoutPanel* result = NULL;
-
- for (LLLayoutPanel* p : mPanels)
- {
- if (p->getName() == name)
- {
- result = p;
- break;
- }
- }
-
- return result;
-}
-
-void LLLayoutStack::createResizeBar(LLLayoutPanel* panelp)
-{
- for (LLLayoutPanel* lp : mPanels)
- {
- if (lp->mResizeBar == NULL)
- {
- 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)
- {
- 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 = mDragHandleColor;
- 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);
- }
-
- /*if (mShowDragHandle)
- {
- 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);
- }
- }
- // 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)
- {
- LLResizeBar* resize_barp = (*panel_it)->mResizeBar;
- sendChildToFront(resize_barp);
- }
-}
-
-// 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 (auto& layout : instance_snapshot())
- {
- layout.updateLayout();
- layout.mAnimatedThisFrame = false;
- }
-}
-
-void LLLayoutStack::updateFractionalSizes()
-{
- F32 total_resizable_dim = 0.f;
-
- for (LLLayoutPanel* panelp : mPanels)
- {
- if (panelp->mAutoResize)
- {
- total_resizable_dim += llmax(MIN_FRACTIONAL_SIZE, (F32)(panelp->getLayoutDim() - panelp->getRelevantMinDim()));
- }
- }
-
- for (LLLayoutPanel* panelp : mPanels)
- {
- 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));
- }
- }
-
- normalizeFractionalSizes();
-}
-
-
-void LLLayoutStack::normalizeFractionalSizes()
-{
- S32 num_auto_resize_panels = 0;
- F32 total_fractional_size = 0.f;
-
- for (LLLayoutPanel* panelp : mPanels)
- {
- if (panelp->mAutoResize)
- {
- total_fractional_size += panelp->mFractionalSize;
- num_auto_resize_panels++;
- }
- }
-
- if (total_fractional_size == 0.f)
- { // equal distribution
- for (LLLayoutPanel* panelp : mPanels)
- {
- if (panelp->mAutoResize)
- {
- panelp->mFractionalSize = MAX_FRACTIONAL_SIZE / (F32)num_auto_resize_panels;
- }
- }
- }
- else
- { // renormalize
- for (LLLayoutPanel* panelp : mPanels)
- {
- if (panelp->mAutoResize)
- {
- panelp->mFractionalSize /= total_fractional_size;
- }
- }
- }
-}
-
-bool LLLayoutStack::animatePanels()
-{
- bool continue_animating = false;
-
- //
- // animate visibility
- //
- for (LLLayoutPanel* panelp : mPanels)
- {
- if (panelp->getVisible())
- {
- if (mAnimate && panelp->mVisibleAmt < 1.f)
- {
- if (!mAnimatedThisFrame)
- {
- panelp->mVisibleAmt = lerp(panelp->mVisibleAmt, 1.f, LLSmoothInterpolation::getInterpolant(mOpenTimeConstant));
- if (panelp->mVisibleAmt > 0.99f)
- {
- panelp->mVisibleAmt = 1.f;
- }
- }
-
- mAnimatedThisFrame = true;
- continue_animating = true;
- }
- else
- {
- if (panelp->mVisibleAmt != 1.f)
- {
- panelp->mVisibleAmt = 1.f;
- mAnimatedThisFrame = true;
- }
- }
- }
- else // not visible
- {
- if (mAnimate && panelp->mVisibleAmt > 0.f)
- {
- if (!mAnimatedThisFrame)
- {
- panelp->mVisibleAmt = lerp(panelp->mVisibleAmt, 0.f, LLSmoothInterpolation::getInterpolant(mCloseTimeConstant));
- if (panelp->mVisibleAmt < 0.001f)
- {
- panelp->mVisibleAmt = 0.f;
- }
- }
-
- continue_animating = true;
- mAnimatedThisFrame = true;
- }
- else
- {
- if (panelp->mVisibleAmt != 0.f)
- {
- panelp->mVisibleAmt = 0.f;
- mAnimatedThisFrame = true;
- }
- }
- }
-
- F32 collapse_state = panelp->mCollapsed ? 1.f : 0.f;
- if (panelp->mCollapseAmt != collapse_state)
- {
- if (mAnimate)
- {
- 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
- {
- panelp->mCollapseAmt = collapse_state;
- mAnimatedThisFrame = true;
- }
- }
- }
-
- if (mAnimatedThisFrame) mNeedsLayout = true;
- return continue_animating;
-}
-
-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;
-
- F32 total_visible_fraction = 0.f;
- F32 delta_auto_resize_headroom = 0.f;
- F32 old_auto_resize_headroom = 0.f;
-
- LLLayoutPanel* other_resize_panel = NULL;
- LLLayoutPanel* following_panel = NULL;
-
- BOOST_REVERSE_FOREACH(LLLayoutPanel* panelp, mPanels) // Should replace this when C++20 reverse view adaptor becomes available...
- {
- if (panelp->mAutoResize)
- {
- old_auto_resize_headroom += (F32)(panelp->mTargetDim - panelp->getRelevantMinDim());
- if (panelp->getVisible() && !panelp->mCollapsed)
- {
- total_visible_fraction += panelp->mFractionalSize;
- }
- }
-
- if (panelp == resized_panel)
- {
- other_resize_panel = following_panel;
- }
-
- if (panelp->getVisible() && !panelp->mCollapsed)
- {
- following_panel = panelp;
- }
- }
-
- if (resized_panel->mAutoResize)
- {
- if (!other_resize_panel || !other_resize_panel->mAutoResize)
- {
- delta_auto_resize_headroom += delta_panel_dim;
- }
- }
- else
- {
- if (!other_resize_panel || other_resize_panel->mAutoResize)
- {
- delta_auto_resize_headroom -= delta_panel_dim;
- }
- }
-
- F32 fraction_given_up = 0.f;
- F32 fraction_remaining = 1.f;
- F32 new_auto_resize_headroom = old_auto_resize_headroom + delta_auto_resize_headroom;
-
- enum
- {
- BEFORE_RESIZED_PANEL,
- RESIZED_PANEL,
- NEXT_PANEL,
- AFTER_RESIZED_PANEL
- } which_panel = BEFORE_RESIZED_PANEL;
-
- for (LLLayoutPanel* panelp : mPanels)
- {
- if (!panelp->getVisible() || panelp->mCollapsed)
- {
- if (panelp->mAutoResize)
- {
- fraction_remaining -= panelp->mFractionalSize;
- }
- continue;
- }
-
- if (panelp == resized_panel)
- {
- which_panel = RESIZED_PANEL;
- }
-
- switch(which_panel)
- {
- 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;
- }
-
- 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)
- {
- panelp->mFractionalSize = llclamp(panelp->mFractionalSize + (panelp->mFractionalSize / fraction_remaining) * fraction_given_up,
- MIN_FRACTIONAL_SIZE,
- MAX_FRACTIONAL_SIZE);
- }
- break;
- default:
- break;
- }
- }
- updateLayout();
- //normalizeFractionalSizes();
-}
-
-void LLLayoutStack::reshape(S32 width, S32 height, bool called_from_parent)
-{
- mNeedsLayout = true;
- LLView::reshape(width, height, called_from_parent);
-}
-
-void LLLayoutStack::updateResizeBarLimits()
-{
- LLLayoutPanel* previous_visible_panelp{ nullptr };
- BOOST_REVERSE_FOREACH(LLLayoutPanel* visible_panelp, mPanels) // Should replace this when C++20 reverse view adaptor becomes available...
- {
- 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;
- }
-}
-
+/**
+ * @file lllayoutstack.cpp
+ * @brief LLLayout class - dynamic stacking of UI elements
+ *
+ * $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$
+ */
+
+// Opaque view with a background and a border. Can contain LLUICtrls.
+
+#include "linden_common.h"
+
+#include "lllayoutstack.h"
+
+#include "lllocalcliprect.h"
+#include "llpanel.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");
+
+//
+// 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),
+ mAutoResize(p.auto_resize),
+ mUserResize(p.user_resize),
+ mCollapsed(false),
+ mCollapseAmt(0.f),
+ mVisibleAmt(1.f), // default to fully visible
+ mResizeBar(NULL),
+ mFractionalSize(0.f),
+ mTargetDim(0),
+ mIgnoreReshape(false),
+ mOrientation(LLLayoutStack::HORIZONTAL)
+{
+ // panels initialized as hidden should not start out partially visible
+ if (!getVisible())
+ {
+ mVisibleAmt = 0.f;
+ }
+}
+
+void LLLayoutPanel::initFromParams(const Params& p)
+{
+ LLPanel::initFromParams(p);
+ setFollowsNone();
+}
+
+
+LLLayoutPanel::~LLLayoutPanel()
+{
+ // probably not necessary, but...
+ delete mResizeBar;
+ mResizeBar = NULL;
+
+ gFocusMgr.removeKeyboardFocusWithoutCallback(this);
+}
+
+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)
+{
+ LLRect new_rect(getRect());
+ if (mOrientation == LLLayoutStack::HORIZONTAL)
+ {
+ new_rect.mRight = new_rect.mLeft + value;
+ }
+ else
+ {
+ 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 && mUserResize && 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() && !LLView::sForceReshape) return;
+
+ if (!mIgnoreReshape && !mAutoResize)
+ {
+ 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);
+}
+
+//
+// LLLayoutStack
+//
+
+LLLayoutStack::Params::Params()
+: orientation("orientation"),
+ animate("animate", true),
+ clip("clip", true),
+ open_time_constant("open_time_constant", 0.02f),
+ close_time_constant("close_time_constant", 0.03f),
+ resize_bar_overlap("resize_bar_overlap", 1),
+ border_size("border_size", LLCachedControl<S32>(*LLUI::getInstance()->mSettingGroups["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),
+ drag_handle_color("drag_handle_color", LLUIColorTable::instance().getColor("ResizebarBody"))
+{
+ addSynonym(border_size, "drag_handle_gap");
+}
+
+LLLayoutStack::LLLayoutStack(const LLLayoutStack::Params& p)
+: LLView(p),
+ 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),
+ 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),
+ mDragHandleColor(p.drag_handle_color())
+{
+}
+
+LLLayoutStack::~LLLayoutStack()
+{
+ e_panel_list_t panels = mPanels; // copy list of panel pointers
+ mPanels.clear(); // clear so that removeChild() calls don't cause trouble
+ std::for_each(panels.begin(), panels.end(), DeletePointer());
+}
+
+void LLLayoutStack::draw()
+{
+ updateLayout();
+
+ // always clip to stack itself
+ LLLocalClipRect clip(getLocalRect());
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ if ((!panelp->getVisible() || panelp->mCollapsed)
+ && (panelp->mVisibleAmt < 0.001f || !mAnimate))
+ {
+ // essentially invisible
+ continue;
+ }
+ // clip to layout rectangle, not bounding rectangle
+ LLRect clip_rect = panelp->getRect();
+ // scale clipping rectangle by visible amount
+ if (mOrientation == HORIZONTAL)
+ {
+ clip_rect.mRight = clip_rect.mLeft + panelp->getVisibleDim();
+ }
+ else
+ {
+ clip_rect.mBottom = clip_rect.mTop - panelp->getVisibleDim();
+ }
+
+ {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());
+ }
+ }
+}
+
+void LLLayoutStack::deleteAllChildren()
+{
+ mPanels.clear();
+ LLView::deleteAllChildren();
+
+ // Not really needed since nothing is left to
+ // display, but for the sake of consistency
+ updateFractionalSizes();
+ mNeedsLayout = true;
+}
+
+void LLLayoutStack::removeChild(LLView* view)
+{
+ LLLayoutPanel* embedded_panelp = findEmbeddedPanel(dynamic_cast<LLPanel*>(view));
+
+ if (embedded_panelp)
+ {
+ mPanels.erase(std::find(mPanels.begin(), mPanels.end(), embedded_panelp));
+ LLView::removeChild(view);
+ updateFractionalSizes();
+ mNeedsLayout = true;
+ }
+ else
+ {
+ LLView::removeChild(view);
+ }
+}
+
+bool LLLayoutStack::postBuild()
+{
+ updateLayout();
+ return true;
+}
+
+bool LLLayoutStack::addChild(LLView* child, S32 tab_group)
+{
+ LLLayoutPanel* panelp = dynamic_cast<LLLayoutPanel*>(child);
+ if (panelp)
+ {
+ panelp->setOrientation(mOrientation);
+ mPanels.push_back(panelp);
+ createResizeBar(panelp);
+ mNeedsLayout = true;
+ }
+ bool result = LLView::addChild(child, tab_group);
+
+ updateFractionalSizes();
+ return result;
+}
+
+void LLLayoutStack::addPanel(LLLayoutPanel* panel, EAnimate animate)
+{
+ addChild(panel);
+
+ // panel starts off invisible (collapsed)
+ if (animate == ANIMATE)
+ {
+ panel->mVisibleAmt = 0.f;
+ panel->setVisible(true);
+ }
+}
+
+void LLLayoutStack::collapsePanel(LLPanel* panel, bool collapsed)
+{
+ LLLayoutPanel* panel_container = findEmbeddedPanel(panel);
+ if (!panel_container) return;
+
+ panel_container->mCollapsed = collapsed;
+ mNeedsLayout = true;
+}
+
+class LLImagePanel : public LLPanel
+{
+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() {}
+
+ void draw()
+ {
+ 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();
+ }
+
+private:
+ bool mHorizontal;
+};
+
+void LLLayoutStack::updateLayout()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+
+ if (!mNeedsLayout) return;
+
+ bool continue_animating = animatePanels();
+ F32 total_visible_fraction = 0.f;
+ S32 space_to_distribute = (mOrientation == HORIZONTAL)
+ ? getRect().getWidth()
+ : getRect().getHeight();
+
+ // first, assign minimum dimensions
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ 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();
+ }
+
+ llassert(total_visible_fraction < 1.05f);
+
+ // don't need spacing after last panel
+ if (!mPanels.empty())
+ {
+ space_to_distribute += ll_round(F32(mPanelSpacing) * mPanels.back()->getVisibleAmount());
+ }
+
+ S32 remaining_space = space_to_distribute;
+ if (space_to_distribute > 0 && total_visible_fraction > 0.f)
+ { // give space proportionally to visible auto resize panels
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ if (panelp->mAutoResize)
+ {
+ F32 fraction_to_distribute = (panelp->mFractionalSize * panelp->getAutoResizeFactor()) / (total_visible_fraction);
+ S32 delta = ll_round((F32)space_to_distribute * fraction_to_distribute);
+ panelp->mTargetDim += delta;
+ remaining_space -= delta;
+ }
+ }
+ }
+
+ // distribute any left over pixels to non-collapsed, visible panels
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ if (remaining_space == 0) break;
+
+ if (panelp->mAutoResize
+ && !panelp->mCollapsed
+ && panelp->getVisible())
+ {
+ 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();
+
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ F32 panel_dim = llmax(panelp->getExpandedMinDim(), panelp->mTargetDim);
+
+ LLRect panel_rect;
+ if (mOrientation == HORIZONTAL)
+ {
+ panel_rect.setLeftTopAndSize(ll_round(cur_pos),
+ getRect().getHeight(),
+ ll_round(panel_dim),
+ getRect().getHeight());
+ }
+ else
+ {
+ 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)
+ {
+ cur_pos += panel_visible_dim + panel_spacing;
+
+ if (mShowDragHandle && panel_spacing_round > mDragHandleThickness)
+ {
+ resize_bar_rect.mLeft = panel_rect.mRight + mDragHandleShift;
+ resize_bar_rect.mRight = resize_bar_rect.mLeft + mDragHandleThickness;
+ }
+ else
+ {
+ resize_bar_rect.mLeft = panel_rect.mRight - mResizeBarOverlap;
+ resize_bar_rect.mRight = panel_rect.mRight + panel_spacing_round + mResizeBarOverlap;
+ }
+
+ if (mShowDragHandle)
+ {
+ resize_bar_rect.mBottom += mDragHandleSecondIndent;
+ resize_bar_rect.mTop -= mDragHandleFirstIndent;
+ }
+
+ }
+ else //VERTICAL
+ {
+ cur_pos -= panel_visible_dim + panel_spacing;
+
+ if (mShowDragHandle && panel_spacing_round > mDragHandleThickness)
+ {
+ resize_bar_rect.mTop = panel_rect.mBottom - mDragHandleShift;
+ resize_bar_rect.mBottom = resize_bar_rect.mTop - mDragHandleThickness;
+ }
+ else
+ {
+ resize_bar_rect.mTop = panel_rect.mBottom + mResizeBarOverlap;
+ resize_bar_rect.mBottom = panel_rect.mBottom - panel_spacing_round - mResizeBarOverlap;
+ }
+
+ if (mShowDragHandle)
+ {
+ 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);
+ }
+
+ 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
+
+void LLLayoutStack::setPanelSpacing(S32 val)
+{
+ if (mPanelSpacing != val)
+ {
+ mPanelSpacing = val;
+ mNeedsLayout = true;
+ }
+}
+
+LLLayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const
+{
+ if (!panelp) return NULL;
+
+ for (LLLayoutPanel* p : mPanels)
+ {
+ if (p == panelp)
+ {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+LLLayoutPanel* LLLayoutStack::findEmbeddedPanelByName(const std::string& name) const
+{
+ LLLayoutPanel* result = NULL;
+
+ for (LLLayoutPanel* p : mPanels)
+ {
+ if (p->getName() == name)
+ {
+ result = p;
+ break;
+ }
+ }
+
+ return result;
+}
+
+void LLLayoutStack::createResizeBar(LLLayoutPanel* panelp)
+{
+ for (LLLayoutPanel* lp : mPanels)
+ {
+ if (lp->mResizeBar == NULL)
+ {
+ 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)
+ {
+ 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 = mDragHandleColor;
+ 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);
+ }
+
+ /*if (mShowDragHandle)
+ {
+ 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);
+ }
+ }
+ // 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)
+ {
+ LLResizeBar* resize_barp = (*panel_it)->mResizeBar;
+ sendChildToFront(resize_barp);
+ }
+}
+
+// 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 (auto& layout : instance_snapshot())
+ {
+ layout.updateLayout();
+ layout.mAnimatedThisFrame = false;
+ }
+}
+
+void LLLayoutStack::updateFractionalSizes()
+{
+ F32 total_resizable_dim = 0.f;
+
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ if (panelp->mAutoResize)
+ {
+ total_resizable_dim += llmax(MIN_FRACTIONAL_SIZE, (F32)(panelp->getLayoutDim() - panelp->getRelevantMinDim()));
+ }
+ }
+
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ 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));
+ }
+ }
+
+ normalizeFractionalSizes();
+}
+
+
+void LLLayoutStack::normalizeFractionalSizes()
+{
+ S32 num_auto_resize_panels = 0;
+ F32 total_fractional_size = 0.f;
+
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ if (panelp->mAutoResize)
+ {
+ total_fractional_size += panelp->mFractionalSize;
+ num_auto_resize_panels++;
+ }
+ }
+
+ if (total_fractional_size == 0.f)
+ { // equal distribution
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ if (panelp->mAutoResize)
+ {
+ panelp->mFractionalSize = MAX_FRACTIONAL_SIZE / (F32)num_auto_resize_panels;
+ }
+ }
+ }
+ else
+ { // renormalize
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ if (panelp->mAutoResize)
+ {
+ panelp->mFractionalSize /= total_fractional_size;
+ }
+ }
+ }
+}
+
+bool LLLayoutStack::animatePanels()
+{
+ bool continue_animating = false;
+
+ //
+ // animate visibility
+ //
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ if (panelp->getVisible())
+ {
+ if (mAnimate && panelp->mVisibleAmt < 1.f)
+ {
+ if (!mAnimatedThisFrame)
+ {
+ panelp->mVisibleAmt = lerp(panelp->mVisibleAmt, 1.f, LLSmoothInterpolation::getInterpolant(mOpenTimeConstant));
+ if (panelp->mVisibleAmt > 0.99f)
+ {
+ panelp->mVisibleAmt = 1.f;
+ }
+ }
+
+ mAnimatedThisFrame = true;
+ continue_animating = true;
+ }
+ else
+ {
+ if (panelp->mVisibleAmt != 1.f)
+ {
+ panelp->mVisibleAmt = 1.f;
+ mAnimatedThisFrame = true;
+ }
+ }
+ }
+ else // not visible
+ {
+ if (mAnimate && panelp->mVisibleAmt > 0.f)
+ {
+ if (!mAnimatedThisFrame)
+ {
+ panelp->mVisibleAmt = lerp(panelp->mVisibleAmt, 0.f, LLSmoothInterpolation::getInterpolant(mCloseTimeConstant));
+ if (panelp->mVisibleAmt < 0.001f)
+ {
+ panelp->mVisibleAmt = 0.f;
+ }
+ }
+
+ continue_animating = true;
+ mAnimatedThisFrame = true;
+ }
+ else
+ {
+ if (panelp->mVisibleAmt != 0.f)
+ {
+ panelp->mVisibleAmt = 0.f;
+ mAnimatedThisFrame = true;
+ }
+ }
+ }
+
+ F32 collapse_state = panelp->mCollapsed ? 1.f : 0.f;
+ if (panelp->mCollapseAmt != collapse_state)
+ {
+ if (mAnimate)
+ {
+ 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
+ {
+ panelp->mCollapseAmt = collapse_state;
+ mAnimatedThisFrame = true;
+ }
+ }
+ }
+
+ if (mAnimatedThisFrame) mNeedsLayout = true;
+ return continue_animating;
+}
+
+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;
+
+ F32 total_visible_fraction = 0.f;
+ F32 delta_auto_resize_headroom = 0.f;
+ F32 old_auto_resize_headroom = 0.f;
+
+ LLLayoutPanel* other_resize_panel = NULL;
+ LLLayoutPanel* following_panel = NULL;
+
+ BOOST_REVERSE_FOREACH(LLLayoutPanel* panelp, mPanels) // Should replace this when C++20 reverse view adaptor becomes available...
+ {
+ if (panelp->mAutoResize)
+ {
+ old_auto_resize_headroom += (F32)(panelp->mTargetDim - panelp->getRelevantMinDim());
+ if (panelp->getVisible() && !panelp->mCollapsed)
+ {
+ total_visible_fraction += panelp->mFractionalSize;
+ }
+ }
+
+ if (panelp == resized_panel)
+ {
+ other_resize_panel = following_panel;
+ }
+
+ if (panelp->getVisible() && !panelp->mCollapsed)
+ {
+ following_panel = panelp;
+ }
+ }
+
+ if (resized_panel->mAutoResize)
+ {
+ if (!other_resize_panel || !other_resize_panel->mAutoResize)
+ {
+ delta_auto_resize_headroom += delta_panel_dim;
+ }
+ }
+ else
+ {
+ if (!other_resize_panel || other_resize_panel->mAutoResize)
+ {
+ delta_auto_resize_headroom -= delta_panel_dim;
+ }
+ }
+
+ F32 fraction_given_up = 0.f;
+ F32 fraction_remaining = 1.f;
+ F32 new_auto_resize_headroom = old_auto_resize_headroom + delta_auto_resize_headroom;
+
+ enum
+ {
+ BEFORE_RESIZED_PANEL,
+ RESIZED_PANEL,
+ NEXT_PANEL,
+ AFTER_RESIZED_PANEL
+ } which_panel = BEFORE_RESIZED_PANEL;
+
+ for (LLLayoutPanel* panelp : mPanels)
+ {
+ if (!panelp->getVisible() || panelp->mCollapsed)
+ {
+ if (panelp->mAutoResize)
+ {
+ fraction_remaining -= panelp->mFractionalSize;
+ }
+ continue;
+ }
+
+ if (panelp == resized_panel)
+ {
+ which_panel = RESIZED_PANEL;
+ }
+
+ switch(which_panel)
+ {
+ 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;
+ }
+
+ 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)
+ {
+ panelp->mFractionalSize = llclamp(panelp->mFractionalSize + (panelp->mFractionalSize / fraction_remaining) * fraction_given_up,
+ MIN_FRACTIONAL_SIZE,
+ MAX_FRACTIONAL_SIZE);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ updateLayout();
+ //normalizeFractionalSizes();
+}
+
+void LLLayoutStack::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ mNeedsLayout = true;
+ LLView::reshape(width, height, called_from_parent);
+}
+
+void LLLayoutStack::updateResizeBarLimits()
+{
+ LLLayoutPanel* previous_visible_panelp{ nullptr };
+ BOOST_REVERSE_FOREACH(LLLayoutPanel* visible_panelp, mPanels) // Should replace this when C++20 reverse view adaptor becomes available...
+ {
+ 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 e3f8629425..080559477b 100644
--- a/indra/llui/lllayoutstack.h
+++ b/indra/llui/lllayoutstack.h
@@ -1,216 +1,216 @@
-/**
- * @file lllayoutstack.h
- * @author Richard Nelson
- * @brief LLLayout class - dynamic stacking of UI elements
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * 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
- * 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_LLLAYOUTSTACK_H
-#define LL_LLLAYOUTSTACK_H
-
-#include "llpanel.h"
-#include "llresizebar.h"
-
-
-class LLLayoutPanel;
-
-
-class LLLayoutStack : public LLView, public LLInstanceTracker<LLLayoutStack>
-{
-public:
-
- struct LayoutStackRegistry : public LLChildRegistry<LayoutStackRegistry>
- {
- LLSINGLETON_EMPTY_CTOR(LayoutStackRegistry);
- };
-
- struct Params : public LLInitParam::Block<Params, LLView::Params>
- {
- 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;
-
- Optional<LLUIColor> drag_handle_color;
-
- Params();
- };
-
- typedef LayoutStackRegistry child_registry_t;
-
- virtual ~LLLayoutStack();
-
- /*virtual*/ void draw();
- /*virtual*/ void deleteAllChildren();
- /*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);
-
- typedef enum e_animate
- {
- NO_ANIMATE,
- ANIMATE
- } EAnimate;
-
- void addPanel(LLLayoutPanel* panel, EAnimate animate = NO_ANIMATE);
- void collapsePanel(LLPanel* panel, bool collapsed = true);
- S32 getNumPanels() { return mPanels.size(); }
-
- void updateLayout();
-
- S32 getPanelSpacing() const { return mPanelSpacing; }
- void setPanelSpacing(S32 val);
-
- static void updateClass();
-
-protected:
- LLLayoutStack(const Params&);
- friend class LLUICtrlFactory;
- friend class LLLayoutPanel;
-
-private:
- void updateResizeBarLimits();
- bool animatePanels();
- void createResizeBar(LLLayoutPanel* panel);
-
- 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 mPanelSpacing;
-
- // true if we already applied animation this frame
- bool mAnimatedThisFrame;
- bool mAnimate;
- bool mClip;
- F32 mOpenTimeConstant;
- F32 mCloseTimeConstant;
- bool mNeedsLayout;
- S32 mResizeBarOverlap;
- bool mShowDragHandle;
- S32 mDragHandleFirstIndent;
- S32 mDragHandleSecondIndent;
- S32 mDragHandleThickness;
- S32 mDragHandleShift;
- LLUIColor mDragHandleColor;
-}; // end class LLLayoutStack
-
-
-class LLLayoutPanel : public LLPanel
-{
-friend class LLLayoutStack;
-friend class LLUICtrlFactory;
-public:
- struct Params : public LLInitParam::Block<Params, LLPanel::Params>
- {
- Optional<S32> expanded_min_dim,
- min_dim;
- Optional<bool> user_resize,
- auto_resize;
-
- Params();
- };
-
- ~LLLayoutPanel();
-
- void initFromParams(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; }
-
- 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;}
-
- 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;
-};
-
-
-#endif
+/**
+ * @file lllayoutstack.h
+ * @author Richard Nelson
+ * @brief LLLayout class - dynamic stacking of UI elements
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * 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
+ * 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_LLLAYOUTSTACK_H
+#define LL_LLLAYOUTSTACK_H
+
+#include "llpanel.h"
+#include "llresizebar.h"
+
+
+class LLLayoutPanel;
+
+
+class LLLayoutStack : public LLView, public LLInstanceTracker<LLLayoutStack>
+{
+public:
+
+ struct LayoutStackRegistry : public LLChildRegistry<LayoutStackRegistry>
+ {
+ LLSINGLETON_EMPTY_CTOR(LayoutStackRegistry);
+ };
+
+ struct Params : public LLInitParam::Block<Params, LLView::Params>
+ {
+ 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;
+
+ Optional<LLUIColor> drag_handle_color;
+
+ Params();
+ };
+
+ typedef LayoutStackRegistry child_registry_t;
+
+ virtual ~LLLayoutStack();
+
+ /*virtual*/ void draw();
+ /*virtual*/ void deleteAllChildren();
+ /*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);
+
+ typedef enum e_animate
+ {
+ NO_ANIMATE,
+ ANIMATE
+ } EAnimate;
+
+ void addPanel(LLLayoutPanel* panel, EAnimate animate = NO_ANIMATE);
+ void collapsePanel(LLPanel* panel, bool collapsed = true);
+ S32 getNumPanels() { return mPanels.size(); }
+
+ void updateLayout();
+
+ S32 getPanelSpacing() const { return mPanelSpacing; }
+ void setPanelSpacing(S32 val);
+
+ static void updateClass();
+
+protected:
+ LLLayoutStack(const Params&);
+ friend class LLUICtrlFactory;
+ friend class LLLayoutPanel;
+
+private:
+ void updateResizeBarLimits();
+ bool animatePanels();
+ void createResizeBar(LLLayoutPanel* panel);
+
+ 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 mPanelSpacing;
+
+ // true if we already applied animation this frame
+ bool mAnimatedThisFrame;
+ bool mAnimate;
+ bool mClip;
+ F32 mOpenTimeConstant;
+ F32 mCloseTimeConstant;
+ bool mNeedsLayout;
+ S32 mResizeBarOverlap;
+ bool mShowDragHandle;
+ S32 mDragHandleFirstIndent;
+ S32 mDragHandleSecondIndent;
+ S32 mDragHandleThickness;
+ S32 mDragHandleShift;
+ LLUIColor mDragHandleColor;
+}; // end class LLLayoutStack
+
+
+class LLLayoutPanel : public LLPanel
+{
+friend class LLLayoutStack;
+friend class LLUICtrlFactory;
+public:
+ struct Params : public LLInitParam::Block<Params, LLPanel::Params>
+ {
+ Optional<S32> expanded_min_dim,
+ min_dim;
+ Optional<bool> user_resize,
+ auto_resize;
+
+ Params();
+ };
+
+ ~LLLayoutPanel();
+
+ void initFromParams(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; }
+
+ 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;}
+
+ 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;
+};
+
+
+#endif
diff --git a/indra/llui/lllazyvalue.h b/indra/llui/lllazyvalue.h
index 0fc95d9efa..622e69ce95 100644
--- a/indra/llui/lllazyvalue.h
+++ b/indra/llui/lllazyvalue.h
@@ -1,4 +1,4 @@
-/**
+/**
* @file lllazyvalue.h
* @brief generic functor/value abstraction for lazy evaluation of a value
* parsing construction parameters from xml and LLSD
@@ -6,21 +6,21 @@
* $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$
*/
@@ -35,48 +35,48 @@ template<typename T>
class LLLazyValue
{
public:
- typedef typename boost::add_reference<typename boost::add_const<T>::type>::type T_const_ref;
- typedef typename boost::function<T_const_ref (void)> function_type;
+ typedef typename boost::add_reference<typename boost::add_const<T>::type>::type T_const_ref;
+ typedef typename boost::function<T_const_ref (void)> function_type;
public:
- LLLazyValue(const function_type& value)
- : mValueGetter(value)
- {}
- LLLazyValue(T_const_ref value)
- : mValue(value)
- {}
- LLLazyValue()
- : mValue()
- {}
+ LLLazyValue(const function_type& value)
+ : mValueGetter(value)
+ {}
+ LLLazyValue(T_const_ref value)
+ : mValue(value)
+ {}
+ LLLazyValue()
+ : mValue()
+ {}
- void set(const LLLazyValue& val)
- {
- mValueGetter = val.mValueGetter;
- }
+ void set(const LLLazyValue& val)
+ {
+ mValueGetter = val.mValueGetter;
+ }
- void set(T_const_ref val)
- {
- mValue = val;
- mValueGetter = NULL;
- }
+ void set(T_const_ref val)
+ {
+ mValue = val;
+ mValueGetter = NULL;
+ }
- T_const_ref get() const
- {
- if (!mValueGetter.empty())
- {
- return mValueGetter();
- }
- return mValue;
- }
+ T_const_ref get() const
+ {
+ if (!mValueGetter.empty())
+ {
+ return mValueGetter();
+ }
+ return mValue;
+ }
- bool isUsingFunction() const
- {
- return mValueGetter != NULL;
- }
+ bool isUsingFunction() const
+ {
+ return mValueGetter != NULL;
+ }
private:
- function_type mValueGetter;
- T mValue;
+ function_type mValueGetter;
+ T mValue;
};
#endif // LL_LAZY_VALUE_H
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index 8a04342af0..8c5e69fbb2 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -1,2719 +1,2735 @@
-/**
- * @file lllineeditor.cpp
- * @brief LLLineEditor 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$
- */
-
-// Text editor widget to let users enter a single line.
-
-#include "linden_common.h"
-
-#define LLLINEEDITOR_CPP
-#include "lllineeditor.h"
-
-#include "lltexteditor.h"
-#include "llmath.h"
-#include "llfontgl.h"
-#include "llgl.h"
-#include "lltimer.h"
-
-#include "llcalc.h"
-//#include "llclipboard.h"
-#include "llcontrol.h"
-#include "llbutton.h"
-#include "llfocusmgr.h"
-#include "llkeyboard.h"
-#include "llrect.h"
-#include "llresmgr.h"
-#include "llspellcheck.h"
-#include "llstring.h"
-#include "llwindow.h"
-#include "llui.h"
-#include "lluictrlfactory.h"
-#include "llclipboard.h"
-#include "llmenugl.h"
-
-//
-// Imported globals
-//
-
-//
-// Constants
-//
-
-const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds
-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
-
-static LLDefaultChildRegistry::Register<LLLineEditor> r1("line_editor");
-
-// Compiler optimization, generate extern template
-template class LLLineEditor* LLView::getChild<class LLLineEditor>(
- const std::string& name, bool recurse) const;
-
-//
-// Member functions
-//
-
-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),
- show_label_focused("show_label_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),
- cursor_color("cursor_color"),
- use_bg_color("use_bg_color", false),
- bg_color("bg_color"),
- text_color("text_color"),
- text_readonly_color("text_readonly_color"),
- text_tentative_color("text_tentative_color"),
- highlight_color("highlight_color"),
- preedit_bg_color("preedit_bg_color"),
- border(""),
- bg_visible("bg_visible"),
- text_pad_left("text_pad_left"),
- text_pad_right("text_pad_right"),
- default_text("default_text")
-{
- 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)
-: LLUICtrl(p),
- mMaxLengthBytes(p.max_length.bytes),
- mMaxLengthChars(p.max_length.chars),
- mCursorPos( 0 ),
- mScrollHPos( 0 ),
- mTextPadLeft(p.text_pad_left),
- mTextPadRight(p.text_pad_right),
- mTextLeftEdge(0), // computed in updateTextPadding() below
- mTextRightEdge(0), // computed in updateTextPadding() below
- mCommitOnFocusLost( p.commit_on_focus_lost ),
- mKeystrokeOnEsc(false),
- mRevertOnEsc( p.revert_on_esc ),
- mKeystrokeCallback( p.keystroke_callback() ),
- mIsSelecting( false ),
- mSelectionStart( 0 ),
- mSelectionEnd( 0 ),
- mLastSelectionX(-1),
- mLastSelectionY(-1),
- mLastSelectionStart(-1),
- mLastSelectionEnd(-1),
- mBorderThickness( 0 ),
- 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 ),
- mShowLabelFocused( p.show_label_focused ),
- mUseBgColor(p.use_bg_color),
- mHaveHistory(false),
- mReplaceNewlinesWithSpaces( true ),
- mLabel(p.label),
- mCursorColor(p.cursor_color()),
- mBgColor(p.bg_color()),
- mFgColor(p.text_color()),
- mReadOnlyFgColor(p.text_readonly_color()),
- mTentativeFgColor(p.text_tentative_color()),
- mHighlightColor(p.highlight_color()),
- mPreeditBgColor(p.preedit_bg_color()),
- mGLFont(p.font),
- mContextMenuHandle(),
- mShowContextMenu(true)
-{
- llassert( mMaxLengthBytes > 0 );
-
- LLUICtrl::setEnabled(true);
- setEnabled(p.enabled);
-
- mScrollTimer.reset();
- mTripleClickTimer.reset();
- setText(p.default_text());
-
- if (p.initial_value.isProvided()
- && !p.control_name.isProvided())
- {
- // Initial value often is descriptive, like "Type some ID here"
- // and can be longer than size limitation, ignore size
- setText(p.initial_value.getValue().asString(), false);
- }
-
- // Initialize current history line iterator
- mCurrentHistoryLine = mLineHistory.begin();
-
- LLRect border_rect(getLocalRect());
- // adjust for gl line drawing glitch
- border_rect.mTop -= 1;
- border_rect.mRight -=1;
- LLViewBorder::Params border_p(p.border);
- border_p.rect = border_rect;
- border_p.follows.flags = FOLLOWS_ALL;
- border_p.bevel_style = LLViewBorder::BEVEL_IN;
- mBorder = LLUICtrlFactory::create<LLViewBorder>(border_p);
- addChild( mBorder );
-
- // clamp text padding to current editor size
- updateTextPadding();
- setCursor(mText.length());
-
- if (mSpellCheck)
- {
- LLSpellChecker::setSettingsChangeCallback(boost::bind(&LLLineEditor::onSpellCheckSettingsChange, this));
- }
- mSpellCheckTimer.reset();
-
- setPrevalidateInput(p.prevalidate_input_callback());
- setPrevalidate(p.prevalidate_callback());
-}
-
-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::initFromParams(const LLLineEditor::Params& params)
-{
- LLUICtrl::initFromParams(params);
- LLUICtrl::setEnabled(true);
- setEnabled(params.enabled);
-}
-
-void LLLineEditor::onFocusReceived()
-{
- gEditMenuHandler = this;
- LLUICtrl::onFocusReceived();
- updateAllowingLanguageInput();
-}
-
-void LLLineEditor::onFocusLost()
-{
- // The call to updateAllowLanguageInput()
- // when loosing the keyboard focus *may*
- // indirectly invoke handleUnicodeCharHere(),
- // so it must be called before onCommit.
- updateAllowingLanguageInput();
-
- if( mCommitOnFocusLost && mText.getString() != mPrevText)
- {
- onCommit();
- }
-
- if( gEditMenuHandler == this )
- {
- gEditMenuHandler = NULL;
- }
-
- getWindow()->showCursorFromMouseMove();
-
- LLUICtrl::onFocusLost();
-}
-
-// virtual
-void LLLineEditor::onCommit()
-{
- // put current line into the line history
- updateHistory();
-
- setControlValue(getValue());
- LLUICtrl::onCommit();
- 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
-// virtual
-bool LLLineEditor::isDirty() const
-{
- return mText.getString() != mPrevText;
-}
-
-// Clear dirty state
-// virtual
-void LLLineEditor::resetDirty()
-{
- mPrevText = mText.getString();
-}
-
-// assumes UTF8 text
-// virtual
-void LLLineEditor::setValue(const LLSD& value )
-{
- setText(value.asString());
-}
-
-//virtual
-LLSD LLLineEditor::getValue() const
-{
- return LLSD(getText());
-}
-
-
-// line history support
-void LLLineEditor::updateHistory()
-{
- // On history enabled line editors, 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( mHaveHistory && getLength() )
- {
- if( !mLineHistory.empty() )
- {
- // When not empty, last line of history should always be blank.
- if( mLineHistory.back().empty() )
- {
- // discard the empty line
- mLineHistory.pop_back();
- }
- else
- {
- LL_WARNS("") << "Last line of history was not blank." << LL_ENDL;
- }
- }
-
- // Add text to history, ignoring duplicates
- if( mLineHistory.empty() || getText() != mLineHistory.back() )
- {
- mLineHistory.push_back( getText() );
- }
-
- // Restore the blank line and set mCurrentHistoryLine to point at it
- mLineHistory.push_back( "" );
- mCurrentHistoryLine = mLineHistory.end() - 1;
- }
-}
-
-void LLLineEditor::reshape(S32 width, S32 height, bool called_from_parent)
-{
- LLUICtrl::reshape(width, height, called_from_parent);
- updateTextPadding(); // For clamping side-effect.
- setCursor(mCursorPos); // For clamping side-effect.
-}
-
-void LLLineEditor::setEnabled(bool enabled)
-{
- mReadOnly = !enabled;
- setTabStop(!mReadOnly);
- updateAllowingLanguageInput();
-}
-
-
-void LLLineEditor::setMaxTextLength(S32 max_text_length)
-{
- S32 max_len = llmax(0, max_text_length);
- mMaxLengthBytes = max_len;
-}
-
-void LLLineEditor::setMaxTextChars(S32 max_text_chars)
-{
- S32 max_chars = llmax(0, max_text_chars);
- mMaxLengthChars = max_chars;
-}
-
-void LLLineEditor::getTextPadding(S32 *left, S32 *right)
-{
- *left = mTextPadLeft;
- *right = mTextPadRight;
-}
-
-void LLLineEditor::setTextPadding(S32 left, S32 right)
-{
- mTextPadLeft = left;
- mTextPadRight = right;
- updateTextPadding();
-}
-
-void LLLineEditor::updateTextPadding()
-{
- mTextLeftEdge = llclamp(mTextPadLeft, 0, getRect().getWidth());
- mTextRightEdge = getRect().getWidth() - llclamp(mTextPadRight, 0, getRect().getWidth());
-}
-
-
-void LLLineEditor::setText(const LLStringExplicit &new_text)
-{
- setText(new_text, true);
-}
-
-void LLLineEditor::setText(const LLStringExplicit &new_text, bool use_size_limit)
-{
- // If new text is identical, don't copy and don't move insertion point
- if (mText.getString() == new_text)
- {
- return;
- }
-
- // Check to see if entire field is selected.
- S32 len = mText.length();
- bool all_selected = (len > 0)
- && (( mSelectionStart == 0 && mSelectionEnd == len )
- || ( mSelectionStart == len && mSelectionEnd == 0 ));
-
- // Do safe truncation so we don't split multi-byte characters
- // also consider entire string selected when mSelectAllonFocusReceived is set on an empty, focused line editor
- all_selected = all_selected || (len == 0 && hasFocus() && mSelectAllonFocusReceived);
-
- std::string truncated_utf8 = new_text;
- if (use_size_limit && truncated_utf8.size() > (U32)mMaxLengthBytes)
- {
- truncated_utf8 = utf8str_truncate(new_text, mMaxLengthBytes);
- }
- mText.assign(truncated_utf8);
-
- if (use_size_limit && mMaxLengthChars)
- {
- mText.assign(utf8str_symbol_truncate(truncated_utf8, mMaxLengthChars));
- }
-
- if (all_selected)
- {
- // ...keep whole thing selected
- selectAll();
- }
- else
- {
- // try to preserve insertion point, but deselect text
- deselect();
- }
- setCursor(llmin((S32)mText.length(), getCursor()));
-
- // Set current history line to end of history.
- if (mLineHistory.empty())
- {
- mCurrentHistoryLine = mLineHistory.end();
- }
- else
- {
- mCurrentHistoryLine = mLineHistory.end() - 1;
- }
-
- mPrevText = mText;
-}
-
-
-// Picks a new cursor position based on the actual screen size of text being drawn.
-void LLLineEditor::setCursorAtLocalPos( S32 local_mouse_x )
-{
- 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;
-
- setCursor(cursor_pos);
-}
-
-void LLLineEditor::setCursor( S32 pos )
-{
- S32 old_cursor_pos = getCursor();
- mCursorPos = llclamp( pos, 0, mText.length());
-
- // position of end of next character after cursor
- S32 pixels_after_scroll = findPixelNearestPos();
- if( pixels_after_scroll > mTextRightEdge )
- {
- S32 width_chars_to_left = mGLFont->getWidth(mText.getWString().c_str(), 0, mScrollHPos);
- S32 last_visible_char = mGLFont->maxDrawableChars(mText.getWString().c_str(), llmax(0.f, (F32)(mTextRightEdge - mTextLeftEdge + width_chars_to_left)));
- // character immediately to left of cursor should be last one visible (SCROLL_INCREMENT_ADD will scroll in more characters)
- // or first character if cursor is at beginning
- S32 new_last_visible_char = llmax(0, getCursor() - 1);
- S32 min_scroll = mGLFont->firstDrawableChar(mText.getWString().c_str(), (F32)(mTextRightEdge - mTextLeftEdge), mText.length(), new_last_visible_char);
- if (old_cursor_pos == last_visible_char)
- {
- mScrollHPos = llmin(mText.length(), llmax(min_scroll, mScrollHPos + SCROLL_INCREMENT_ADD));
- }
- else
- {
- mScrollHPos = min_scroll;
- }
- }
- else if (getCursor() < mScrollHPos)
- {
- if (old_cursor_pos == mScrollHPos)
- {
- mScrollHPos = llmax(0, llmin(getCursor(), mScrollHPos - SCROLL_INCREMENT_DEL));
- }
- else
- {
- mScrollHPos = getCursor();
- }
- }
-}
-
-
-void LLLineEditor::setCursorToEnd()
-{
- setCursor(mText.length());
- deselect();
-}
-
-void LLLineEditor::resetScrollPosition()
-{
- mScrollHPos = 0;
- // make sure cursor says in visible range
- setCursor(getCursor());
-}
-
-bool LLLineEditor::canDeselect() const
-{
- return hasSelection();
-}
-
-void LLLineEditor::deselect()
-{
- mSelectionStart = 0;
- mSelectionEnd = 0;
- mIsSelecting = false;
-}
-
-
-void LLLineEditor::startSelection()
-{
- mIsSelecting = true;
- mSelectionStart = getCursor();
- mSelectionEnd = getCursor();
-}
-
-void LLLineEditor::endSelection()
-{
- if( mIsSelecting )
- {
- mIsSelecting = false;
- mSelectionEnd = getCursor();
- }
-}
-
-bool LLLineEditor::canSelectAll() const
-{
- return true;
-}
-
-void LLLineEditor::selectAll()
-{
- if (!prevalidateInput(mText.getWString()))
- {
- return;
- }
-
- mSelectionStart = mText.length();
- mSelectionEnd = 0;
- setCursor(mSelectionEnd);
- //mScrollHPos = 0;
- mIsSelecting = true;
- 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)
-{
- setFocus( true );
- mTripleClickTimer.setTimerExpirySec(TRIPLE_CLICK_INTERVAL);
-
- if (mSelectionEnd == 0 && mSelectionStart == mText.length())
- {
- // if everything is selected, handle this as a normal click to change insertion point
- handleMouseDown(x, y, mask);
- }
- else
- {
- const LLWString& wtext = mText.getWString();
-
- bool doSelectAll = true;
-
- // Select the word we're on
- if( LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) )
- {
- S32 old_selection_start = mLastSelectionStart;
- S32 old_selection_end = mLastSelectionEnd;
-
- // Select word the cursor is over
- while ((mCursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[mCursorPos-1] ))
- { // Find the start of the word
- mCursorPos--;
- }
- startSelection();
-
- while ((mCursorPos < (S32)wtext.length()) && LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) )
- { // Find the end of the word
- mCursorPos++;
- }
- mSelectionEnd = mCursorPos;
-
- // If nothing changed, then the word was already selected. Select the whole line.
- doSelectAll = (old_selection_start == mSelectionStart) &&
- (old_selection_end == mSelectionEnd);
- }
-
- if ( doSelectAll )
- { // Select everything
- selectAll();
- }
- }
-
- // We don't want handleMouseUp() to "finish" the selection (and thereby
- // set mSelectionEnd to where the mouse is), so we finish the selection
- // here.
- mIsSelecting = false;
-
- // delay cursor flashing
- mKeystrokeTimer.reset();
-
- // take selection to 'primary' clipboard
- updatePrimary();
-
- return true;
-}
-
-bool LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- // Check first whether the "clear search" button wants to deal with this.
- if(childrenHandleMouseDown(x, y, mask) != NULL)
- {
- return true;
- }
-
- if (!mSelectAllonFocusReceived
- || gFocusMgr.getKeyboardFocus() == this)
- {
- mLastSelectionStart = -1;
- mLastSelectionStart = -1;
-
- if (mask & MASK_SHIFT)
- {
- // assume we're starting a drag select
- mIsSelecting = true;
-
- // Handle selection extension
- S32 old_cursor_pos = getCursor();
- setCursorAtLocalPos(x);
-
- if (hasSelection())
- {
- /* Mac-like behavior - extend selection towards the cursor
- if (getCursor() < mSelectionStart
- && getCursor() < mSelectionEnd)
- {
- // ...left of selection
- mSelectionStart = llmax(mSelectionStart, mSelectionEnd);
- mSelectionEnd = getCursor();
- }
- else if (getCursor() > mSelectionStart
- && getCursor() > mSelectionEnd)
- {
- // ...right of selection
- mSelectionStart = llmin(mSelectionStart, mSelectionEnd);
- mSelectionEnd = getCursor();
- }
- else
- {
- mSelectionEnd = getCursor();
- }
- */
- // Windows behavior
- mSelectionEnd = getCursor();
- }
- else
- {
- mSelectionStart = old_cursor_pos;
- mSelectionEnd = getCursor();
- }
- }
- else
- {
- if (mTripleClickTimer.hasExpired())
- {
- // Save selection for word/line selecting on double-click
- mLastSelectionStart = mSelectionStart;
- mLastSelectionEnd = mSelectionEnd;
-
- // Move cursor and deselect for regular click
- setCursorAtLocalPos( x );
- deselect();
- startSelection();
- }
- else // handle triple click
- {
- selectAll();
- // We don't want handleMouseUp() to "finish" the selection (and thereby
- // set mSelectionEnd to where the mouse is), so we finish the selection
- // here.
- mIsSelecting = false;
- }
- }
-
- gFocusMgr.setMouseCapture( this );
- }
-
- setFocus(true);
-
- // delay cursor flashing
- mKeystrokeTimer.reset();
-
- if (mMouseDownSignal)
- (*mMouseDownSignal)(this,x,y,mask);
-
- return true;
-}
-
-bool LLLineEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
-{
- // LL_INFOS() << "MiddleMouseDown" << LL_ENDL;
- setFocus( true );
- if( canPastePrimary() )
- {
- setCursorAtLocalPos(x);
- pastePrimary();
- }
- return true;
-}
-
-bool LLLineEditor::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- setFocus(true);
- if (!LLUICtrl::handleRightMouseDown(x, y, mask) && getShowContextMenu())
- {
- showContextMenu(x, y);
- }
- return true;
-}
-
-bool LLLineEditor::handleHover(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
- // Check first whether the "clear search" button wants to deal with this.
- if(!hasMouseCapture())
- {
- if(childrenHandleHover(x, y, mask) != NULL)
- {
- return true;
- }
- }
-
- if( (hasMouseCapture()) && mIsSelecting )
- {
- if (x != mLastSelectionX || y != mLastSelectionY)
- {
- mLastSelectionX = x;
- mLastSelectionY = y;
- }
- // Scroll if mouse cursor outside of bounds
- if (mScrollTimer.hasExpired())
- {
- S32 increment = ll_round(mScrollTimer.getElapsedTimeF32() / AUTO_SCROLL_TIME);
- mScrollTimer.reset();
- mScrollTimer.setTimerExpirySec(AUTO_SCROLL_TIME);
- if( (x < mTextLeftEdge) && (mScrollHPos > 0 ) )
- {
- // Scroll to the left
- mScrollHPos = llclamp(mScrollHPos - increment, 0, mText.length());
- }
- else
- if( (x > mTextRightEdge) && (mCursorPos < (S32)mText.length()) )
- {
- // If scrolling one pixel would make a difference...
- S32 pixels_after_scrolling_one_char = findPixelNearestPos(1);
- if( pixels_after_scrolling_one_char >= mTextRightEdge )
- {
- // ...scroll to the right
- mScrollHPos = llclamp(mScrollHPos + increment, 0, mText.length());
- }
- }
- }
-
- setCursorAtLocalPos( x );
- mSelectionEnd = getCursor();
-
- // delay cursor flashing
- mKeystrokeTimer.reset();
-
- getWindow()->setCursor(UI_CURSOR_IBEAM);
- LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL;
- handled = true;
- }
-
- if( !handled )
- {
- getWindow()->setCursor(UI_CURSOR_IBEAM);
- LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL;
- handled = true;
- }
-
- return handled;
-}
-
-
-bool LLLineEditor::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
-
- if( hasMouseCapture() )
- {
- gFocusMgr.setMouseCapture( NULL );
- handled = true;
- }
-
- // Check first whether the "clear search" button wants to deal with this.
- if(!handled && childrenHandleMouseUp(x, y, mask) != NULL)
- {
- return true;
- }
-
- if( mIsSelecting )
- {
- setCursorAtLocalPos( x );
- mSelectionEnd = getCursor();
-
- handled = true;
- }
-
- if( handled )
- {
- // delay cursor flashing
- mKeystrokeTimer.reset();
-
- // take selection to 'primary' clipboard
- updatePrimary();
- }
-
- // We won't call LLUICtrl::handleMouseUp to avoid double calls of childrenHandleMouseUp().Just invoke the signal manually.
- if (mMouseUpSignal)
- (*mMouseUpSignal)(this,x,y, mask);
- return handled;
-}
-
-
-// Remove a single character from the text
-void LLLineEditor::removeChar()
-{
- if( getCursor() > 0 )
- {
- if (!prevalidateInput(mText.getWString().substr(getCursor()-1, 1)))
- return;
-
- mText.erase(getCursor() - 1, 1);
-
- setCursor(getCursor() - 1);
- }
- else
- {
- LLUI::getInstance()->reportBadKeystroke();
- }
-}
-
-
-void LLLineEditor::addChar(const llwchar uni_char)
-{
- llwchar new_c = uni_char;
- if (hasSelection())
- {
- deleteSelection();
- }
- else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
- {
- if (!prevalidateInput(mText.getWString().substr(getCursor(), 1)))
- return;
-
- mText.erase(getCursor(), 1);
- }
-
- S32 cur_bytes = mText.getString().size();
-
- S32 new_bytes = wchar_utf8_length(new_c);
-
- bool allow_char = true;
-
- // Check byte length limit
- if ((new_bytes + cur_bytes) > mMaxLengthBytes)
- {
- allow_char = false;
- }
- else if (mMaxLengthChars)
- {
- S32 wide_chars = mText.getWString().size();
- if ((wide_chars + 1) > mMaxLengthChars)
- {
- allow_char = false;
- }
- }
-
- if (allow_char)
- {
- // Will we need to scroll?
- LLWString w_buf;
- w_buf.assign(1, new_c);
-
- mText.insert(getCursor(), w_buf);
- setCursor(getCursor() + 1);
- }
- else
- {
- LLUI::getInstance()->reportBadKeystroke();
- }
-
- getWindow()->hideCursorUntilMouseMove();
-}
-
-// Extends the selection box to the new cursor position
-void LLLineEditor::extendSelection( S32 new_cursor_pos )
-{
- if( !mIsSelecting )
- {
- 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();
-}
-
-
-void LLLineEditor::setSelection(S32 start, S32 end)
-{
- S32 len = mText.length();
-
- mIsSelecting = true;
-
- // JC, yes, this seems odd, but I think you have to presume a
- // selection dragged from the end towards the start.
- mSelectionStart = llclamp(end, 0, len);
- mSelectionEnd = llclamp(start, 0, len);
- setCursor(start);
-}
-
-void LLLineEditor::setDrawAsterixes(bool b)
-{
- mDrawAsterixes = b;
- updateAllowingLanguageInput();
-}
-
-S32 LLLineEditor::prevWordPos(S32 cursorPos) const
-{
- const LLWString& wtext = mText.getWString();
- while( (cursorPos > 0) && (wtext[cursorPos-1] == ' ') )
- {
- cursorPos--;
- }
- while( (cursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[cursorPos-1] ) )
- {
- cursorPos--;
- }
- return cursorPos;
-}
-
-S32 LLLineEditor::nextWordPos(S32 cursorPos) const
-{
- const LLWString& wtext = mText.getWString();
- while( (cursorPos < getLength()) && LLWStringUtil::isPartOfWord( wtext[cursorPos] ) )
- {
- cursorPos++;
- }
- while( (cursorPos < getLength()) && (wtext[cursorPos] == ' ') )
- {
- cursorPos++;
- }
- return cursorPos;
-}
-
-
-bool LLLineEditor::handleSelectionKey(KEY key, MASK mask)
-{
- bool handled = false;
-
- if( mask & MASK_SHIFT )
- {
- handled = true;
-
- switch( key )
- {
- case KEY_LEFT:
- if( 0 < getCursor() )
- {
- S32 cursorPos = getCursor() - 1;
- if( mask & MASK_CONTROL )
- {
- cursorPos = prevWordPos(cursorPos);
- }
- extendSelection( cursorPos );
- }
- else
- {
- LLUI::getInstance()->reportBadKeystroke();
- }
- break;
-
- case KEY_RIGHT:
- if( getCursor() < mText.length())
- {
- S32 cursorPos = getCursor() + 1;
- if( mask & MASK_CONTROL )
- {
- cursorPos = nextWordPos(cursorPos);
- }
- extendSelection( cursorPos );
- }
- else
- {
- LLUI::getInstance()->reportBadKeystroke();
- }
- break;
-
- case KEY_PAGE_UP:
- case KEY_HOME:
- extendSelection( 0 );
- break;
-
- case KEY_PAGE_DOWN:
- case KEY_END:
- {
- S32 len = mText.length();
- if( len )
- {
- extendSelection( len );
- }
- break;
- }
-
- default:
- handled = false;
- break;
- }
- }
-
- if(handled)
- {
- // take selection to 'primary' clipboard
- updatePrimary();
- }
-
- return handled;
-}
-
-void LLLineEditor::deleteSelection()
-{
- if( !mReadOnly && hasSelection() )
- {
- 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();
- setCursor(left_pos);
- }
-}
-
-bool LLLineEditor::canCut() const
-{
- return !mReadOnly && !mDrawAsterixes && hasSelection();
-}
-
-// cut selection to clipboard
-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 );
-
- LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length );
- deleteSelection();
-
- // Validate new string and rollback the if needed.
- bool need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) );
- if( need_to_rollback )
- {
- rollback.doRollback( this );
- LLUI::getInstance()->reportBadKeystroke();
- }
- else
- {
- onKeystroke();
- }
- }
-}
-
-bool LLLineEditor::canCopy() const
-{
- return !mDrawAsterixes && hasSelection();
-}
-
-
-// copy selection to clipboard
-void LLLineEditor::copy()
-{
- if( canCopy() )
- {
- S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
- S32 length = llabs( mSelectionStart - mSelectionEnd );
- LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length );
- }
-}
-
-bool LLLineEditor::canPaste() const
-{
- return !mReadOnly && LLClipboard::instance().isTextAvailable();
-}
-
-void LLLineEditor::paste()
-{
- bool is_primary = false;
- pasteHelper(is_primary);
-}
-
-void LLLineEditor::pastePrimary()
-{
- bool is_primary = true;
- pasteHelper(is_primary);
-}
-
-// paste from primary (is_primary==true) or clipboard (is_primary==false)
-void LLLineEditor::pasteHelper(bool is_primary)
-{
- bool can_paste_it;
- if (is_primary)
- {
- can_paste_it = canPastePrimary();
- }
- else
- {
- can_paste_it = canPaste();
- }
-
- if (can_paste_it)
- {
- LLWString paste;
- LLClipboard::instance().pasteFromClipboard(paste, is_primary);
-
- if (!paste.empty())
- {
- if (!prevalidateInput(paste))
- return;
-
- // Prepare for possible rollback
- LLLineEditorRollback rollback(this);
-
- // Delete any selected characters
- if ((!is_primary) && hasSelection())
- {
- deleteSelection();
- }
-
- // Clean up string (replace tabs and returns and remove characters that our fonts don't support.)
- LLWString clean_string(paste);
- LLWStringUtil::replaceTabsWithSpaces(clean_string, 1);
- //clean_string = wstring_detabify(paste, 1);
- LLWStringUtil::replaceChar(clean_string, '\n', mReplaceNewlinesWithSpaces ? ' ' : 182); // 182 == paragraph character
-
- // Insert the string
-
- // Check to see that the size isn't going to be larger than the max number of bytes
- U32 available_bytes = mMaxLengthBytes - wstring_utf8_length(mText);
-
- if ( available_bytes < (U32) wstring_utf8_length(clean_string) )
- { // Doesn't all fit
- llwchar current_symbol = clean_string[0];
- U32 wchars_that_fit = 0;
- U32 total_bytes = wchar_utf8_length(current_symbol);
-
- //loop over the "wide" characters (symbols)
- //and check to see how large (in bytes) each symbol is.
- while ( total_bytes <= available_bytes )
- {
- //while we still have available bytes
- //"accept" the current symbol and check the size
- //of the next one
- current_symbol = clean_string[++wchars_that_fit];
- total_bytes += wchar_utf8_length(current_symbol);
- }
- // Truncate the clean string at the limit of what will fit
- clean_string = clean_string.substr(0, wchars_that_fit);
- LLUI::getInstance()->reportBadKeystroke();
- }
-
- if (mMaxLengthChars)
- {
- U32 available_chars = mMaxLengthChars - mText.getWString().size();
-
- if (available_chars < clean_string.size())
- {
- clean_string = clean_string.substr(0, available_chars);
- }
-
- LLUI::getInstance()->reportBadKeystroke();
- }
-
- mText.insert(getCursor(), clean_string);
- setCursor( getCursor() + (S32)clean_string.length() );
- deselect();
-
- // Validate new string and rollback the if needed.
- bool need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) );
- if( need_to_rollback )
- {
- rollback.doRollback( this );
- LLUI::getInstance()->reportBadKeystroke();
- }
- else
- {
- onKeystroke();
- }
- }
- }
-}
-
-// copy selection to primary
-void LLLineEditor::copyPrimary()
-{
- if( canCopy() )
- {
- S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
- S32 length = llabs( mSelectionStart - mSelectionEnd );
- LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length, true);
- }
-}
-
-bool LLLineEditor::canPastePrimary() const
-{
- return !mReadOnly && LLClipboard::instance().isTextAvailable(true);
-}
-
-void LLLineEditor::updatePrimary()
-{
- if(canCopy() )
- {
- copyPrimary();
- }
-}
-
-bool LLLineEditor::handleSpecialKey(KEY key, MASK mask)
-{
- bool handled = false;
-
- switch( key )
- {
- case KEY_INSERT:
- if (mask == MASK_NONE)
- {
- gKeyboard->toggleInsertMode();
- }
-
- handled = true;
- break;
-
- case KEY_BACKSPACE:
- if (!mReadOnly)
- {
- //LL_INFOS() << "Handling backspace" << LL_ENDL;
- if( hasSelection() )
- {
- deleteSelection();
- }
- else
- if( 0 < getCursor() )
- {
- removeChar();
- }
- else
- {
- LLUI::getInstance()->reportBadKeystroke();
- }
- }
- handled = true;
- break;
-
- case KEY_PAGE_UP:
- case KEY_HOME:
- if (!mIgnoreArrowKeys)
- {
- setCursor(0);
- handled = true;
- }
- break;
-
- case KEY_PAGE_DOWN:
- case KEY_END:
- if (!mIgnoreArrowKeys)
- {
- S32 len = mText.length();
- if( len )
- {
- setCursor(len);
- }
- handled = true;
- }
- break;
-
- case KEY_LEFT:
- if (mIgnoreArrowKeys && mask == MASK_NONE)
- break;
- if ((mask & MASK_ALT) == 0)
- {
- if( hasSelection() )
- {
- setCursor(llmin( getCursor() - 1, mSelectionStart, mSelectionEnd ));
- }
- else
- if( 0 < getCursor() )
- {
- S32 cursorPos = getCursor() - 1;
- if( mask & MASK_CONTROL )
- {
- cursorPos = prevWordPos(cursorPos);
- }
- setCursor(cursorPos);
- }
- else
- {
- LLUI::getInstance()->reportBadKeystroke();
- }
- handled = true;
- }
- break;
-
- case KEY_RIGHT:
- if (mIgnoreArrowKeys && mask == MASK_NONE)
- break;
- if ((mask & MASK_ALT) == 0)
- {
- if (hasSelection())
- {
- setCursor(llmax(getCursor() + 1, mSelectionStart, mSelectionEnd));
- }
- else
- if (getCursor() < mText.length())
- {
- S32 cursorPos = getCursor() + 1;
- if( mask & MASK_CONTROL )
- {
- cursorPos = nextWordPos(cursorPos);
- }
- setCursor(cursorPos);
- }
- else
- {
- LLUI::getInstance()->reportBadKeystroke();
- }
- handled = true;
- }
- break;
-
- // handle ctrl-uparrow if we have a history enabled line editor.
- case KEY_UP:
- if (mHaveHistory && (!mIgnoreArrowKeys || (MASK_CONTROL == mask)))
- {
- if (mCurrentHistoryLine > mLineHistory.begin())
- {
- mText.assign(*(--mCurrentHistoryLine));
- setCursorToEnd();
- }
- else
- {
- LLUI::getInstance()->reportBadKeystroke();
- }
- handled = true;
- }
- break;
-
- // handle [ctrl]-downarrow if we have a history enabled line editor
- case KEY_DOWN:
- if (mHaveHistory && (!mIgnoreArrowKeys || (MASK_CONTROL == mask)))
- {
- if (!mLineHistory.empty() && mCurrentHistoryLine < mLineHistory.end() - 1)
- {
- mText.assign( *(++mCurrentHistoryLine) );
- setCursorToEnd();
- }
- else
- {
- LLUI::getInstance()->reportBadKeystroke();
- }
- handled = true;
- }
- break;
-
- case KEY_RETURN:
- // store sent line in history
- updateHistory();
- break;
-
- case KEY_ESCAPE:
- if (mRevertOnEsc && mText.getString() != mPrevText)
- {
- setText(mPrevText);
- // Note, don't set handled, still want to loose focus (won't commit becase text is now unchanged)
- if (mKeystrokeOnEsc)
- {
- onKeystroke();
- }
- }
- break;
-
- default:
- break;
- }
-
- return handled;
-}
-
-
-bool LLLineEditor::handleKeyHere(KEY key, MASK mask )
-{
- bool handled = false;
- bool selection_modified = false;
-
- if ( gFocusMgr.getKeyboardFocus() == this )
- {
- LLLineEditorRollback rollback( this );
-
- if( !handled )
- {
- handled = handleSelectionKey( key, mask );
- selection_modified = handled;
- }
-
- // Handle most keys only if the text editor is writeable.
- if ( !mReadOnly )
- {
- if( !handled )
- {
- handled = handleSpecialKey( key, mask );
- }
- }
-
- if( handled )
- {
- mKeystrokeTimer.reset();
-
- // Most keystrokes will make the selection box go away, but not all will.
- if( !selection_modified &&
- KEY_SHIFT != key &&
- KEY_CONTROL != key &&
- KEY_ALT != key &&
- KEY_CAPSLOCK != key)
- {
- deselect();
- }
-
- bool need_to_rollback = false;
-
- // If read-only, don't allow changes
- need_to_rollback |= (mReadOnly && (mText.getString() == rollback.getText()));
-
- // Validate new string and rollback the keystroke if needed.
- need_to_rollback |= (mPrevalidateFunc && !mPrevalidateFunc(mText.getWString()));
-
- if (need_to_rollback)
- {
- rollback.doRollback(this);
-
- LLUI::getInstance()->reportBadKeystroke();
- }
-
- // Notify owner if requested
- if (!need_to_rollback && handled)
- {
- onKeystroke();
- if ( (!selection_modified) && (KEY_BACKSPACE == key) )
- {
- mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
- }
- }
- }
- }
-
- return handled;
-}
-
-
-bool LLLineEditor::handleUnicodeCharHere(llwchar uni_char)
-{
- if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL
- {
- return false;
- }
-
- bool handled = false;
-
- if ( (gFocusMgr.getKeyboardFocus() == this) && getVisible() && !mReadOnly)
- {
- handled = true;
-
- LLLineEditorRollback rollback( this );
-
- {
- LLWString u_char;
- u_char.assign(1, uni_char);
- if (!prevalidateInput(u_char))
- return handled;
- }
-
- addChar(uni_char);
-
- mKeystrokeTimer.reset();
-
- deselect();
-
- bool need_to_rollback = false;
-
- // Validate new string and rollback the keystroke if needed.
- need_to_rollback |= ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) );
-
- if( need_to_rollback )
- {
- rollback.doRollback( this );
-
- LLUI::getInstance()->reportBadKeystroke();
- }
-
- // Notify owner if requested
- if( !need_to_rollback && handled )
- {
- // 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;
-}
-
-
-bool LLLineEditor::canDoDelete() const
-{
- return ( !mReadOnly && (!mPassDelete || (hasSelection() || (getCursor() < mText.length()))) );
-}
-
-void LLLineEditor::doDelete()
-{
- if (canDoDelete() && mText.length() > 0)
- {
- // Prepare for possible rollback
- LLLineEditorRollback rollback( this );
-
- if (hasSelection())
- {
- deleteSelection();
- }
- 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();
- }
-
- // Validate new string and rollback the if needed.
- bool need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) );
- if( need_to_rollback )
- {
- rollback.doRollback( this );
- LLUI::getInstance()->reportBadKeystroke();
- }
- else
- {
- onKeystroke();
-
- mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
- }
- }
-}
-
-
-void LLLineEditor::drawBackground()
-{
- F32 alpha = getCurrentTransparency();
- if (mUseBgColor)
- {
- gl_rect_2d(getLocalRect(), mBgColor % alpha, true);
- }
- else
- {
- bool has_focus = hasFocus();
- LLUIImage* image;
- if (mReadOnly)
- {
- image = mBgImageDisabled;
- }
- else if (has_focus || mShowImageFocused)
- {
- image = mBgImageFocused;
- }
- else
- {
- image = mBgImage;
- }
-
- if (!image) return;
- // optionally draw programmatic border
- if (has_focus)
- {
- LLColor4 tmp_color = gFocusMgr.getFocusColor();
- tmp_color.setAlpha(alpha);
- image->drawBorder(0, 0, getRect().getWidth(), getRect().getHeight(),
- tmp_color,
- gFocusMgr.getFocusFlashWidth());
- }
- LLColor4 tmp_color = UI_VERTEX_COLOR;
- tmp_color.setAlpha(alpha);
- image->draw(getLocalRect(), tmp_color);
- }
-}
-
-//virtual
-const std::string LLLineEditor::getToolTip() const
-{
- if (sDebugUnicode)
- {
- std::string text = getText();
- std::string tooltip = utf8str_showBytesUTF8(text);
- return tooltip;
- }
-
- return LLUICtrl::getToolTip();
-}
-
-//virtual
-void LLLineEditor::draw()
-{
- F32 alpha = getDrawContext().mAlpha;
- S32 text_len = mText.length();
- static LLUICachedControl<S32> lineeditor_cursor_thickness ("UILineEditorCursorThickness", 0);
- static LLUICachedControl<F32> preedit_marker_brightness ("UIPreeditMarkerBrightness", 0);
- static LLUICachedControl<S32> preedit_marker_gap ("UIPreeditMarkerGap", 0);
- static LLUICachedControl<S32> preedit_marker_position ("UIPreeditMarkerPosition", 0);
- static LLUICachedControl<S32> preedit_marker_thickness ("UIPreeditMarkerThickness", 0);
- static LLUICachedControl<F32> preedit_standout_brightness ("UIPreeditStandoutBrightness", 0);
- static LLUICachedControl<S32> preedit_standout_gap ("UIPreeditStandoutGap", 0);
- static LLUICachedControl<S32> preedit_standout_position ("UIPreeditStandoutPosition", 0);
- static LLUICachedControl<S32> preedit_standout_thickness ("UIPreeditStandoutThickness", 0);
-
- std::string saved_text;
- if (mDrawAsterixes)
- {
- saved_text = mText.getString();
- std::string text;
- for (S32 i = 0; i < mText.length(); i++)
- {
- text += PASSWORD_ASTERISK;
- }
- mText = text;
- }
-
- // draw rectangle for the background
- LLRect background( 0, getRect().getHeight(), getRect().getWidth(), 0 );
- background.stretch( -mBorderThickness );
-
- S32 lineeditor_v_pad = (background.getHeight() - mGLFont->getLineHeight()) / 2;
- if (mSpellCheck)
- {
- lineeditor_v_pad += 1;
- }
-
- drawBackground();
-
- // draw text
-
- // With viewer-2 art files, input region is 2 pixels up
- S32 cursor_bottom = background.mBottom + 2;
- S32 cursor_top = background.mTop - 1;
-
- LLColor4 text_color;
- if (!mReadOnly)
- {
- if (!getTentative())
- {
- text_color = mFgColor.get();
- }
- else
- {
- text_color = mTentativeFgColor.get();
- }
- }
- else
- {
- text_color = mReadOnlyFgColor.get();
- }
- text_color.setAlpha(alpha);
- LLColor4 label_color = mTentativeFgColor.get();
- label_color.setAlpha(alpha);
-
- if (hasPreeditString())
- {
- // Draw preedit markers. This needs to be before drawing letters.
- for (U32 i = 0; i < mPreeditStandouts.size(); i++)
- {
- const S32 preedit_left = mPreeditPositions[i];
- const S32 preedit_right = mPreeditPositions[i + 1];
- if (preedit_right > mScrollHPos)
- {
- S32 preedit_pixels_left = findPixelNearestPos(llmax(preedit_left, mScrollHPos) - getCursor());
- S32 preedit_pixels_right = llmin(findPixelNearestPos(preedit_right - getCursor()), background.mRight);
- if (preedit_pixels_left >= background.mRight)
- {
- break;
- }
- if (mPreeditStandouts[i])
- {
- gl_rect_2d(preedit_pixels_left + preedit_standout_gap,
- background.mBottom + preedit_standout_position,
- preedit_pixels_right - preedit_standout_gap - 1,
- background.mBottom + preedit_standout_position - preedit_standout_thickness,
- (text_color * preedit_standout_brightness
- + mPreeditBgColor * (1 - preedit_standout_brightness)).setAlpha(alpha/*1.0f*/));
- }
- else
- {
- gl_rect_2d(preedit_pixels_left + preedit_marker_gap,
- background.mBottom + preedit_marker_position,
- preedit_pixels_right - preedit_marker_gap - 1,
- background.mBottom + preedit_marker_position - preedit_marker_thickness,
- (text_color * preedit_marker_brightness
- + mPreeditBgColor * (1 - preedit_marker_brightness)).setAlpha(alpha/*1.0f*/));
- }
- }
- }
- }
-
- S32 rendered_text = 0;
- F32 rendered_pixels_right = (F32)mTextLeftEdge;
- F32 text_bottom = (F32)background.mBottom + (F32)lineeditor_v_pad;
-
- if( (gFocusMgr.getKeyboardFocus() == this) && hasSelection() )
- {
- S32 select_left;
- S32 select_right;
- if (mSelectionStart < mSelectionEnd)
- {
- select_left = mSelectionStart;
- select_right = mSelectionEnd;
- }
- else
- {
- select_left = mSelectionEnd;
- select_right = mSelectionStart;
- }
-
- if( select_left > mScrollHPos )
- {
- // unselected, left side
- rendered_text = mGLFont->render(
- mText, mScrollHPos,
- rendered_pixels_right, text_bottom,
- text_color,
- LLFontGL::LEFT, LLFontGL::BOTTOM,
- 0,
- LLFontGL::NO_SHADOW,
- select_left - mScrollHPos,
- mTextRightEdge - ll_round(rendered_pixels_right),
- &rendered_pixels_right);
- }
-
- if( (rendered_pixels_right < (F32)mTextRightEdge) && (rendered_text < text_len) )
- {
- LLColor4 color = mHighlightColor;
- color.setAlpha(alpha);
- // selected middle
- S32 width = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos + rendered_text, select_right - mScrollHPos - rendered_text);
- width = llmin(width, 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(
- mText, mScrollHPos + rendered_text,
- rendered_pixels_right, text_bottom,
- tmp_color,
- LLFontGL::LEFT, LLFontGL::BOTTOM,
- 0,
- LLFontGL::NO_SHADOW,
- select_right - mScrollHPos - rendered_text,
- mTextRightEdge - ll_round(rendered_pixels_right),
- &rendered_pixels_right);
- }
-
- if( (rendered_pixels_right < (F32)mTextRightEdge) && (rendered_text < text_len) )
- {
- // unselected, right side
- rendered_text += mGLFont->render(
- mText, mScrollHPos + rendered_text,
- rendered_pixels_right, text_bottom,
- text_color,
- LLFontGL::LEFT, LLFontGL::BOTTOM,
- 0,
- LLFontGL::NO_SHADOW,
- S32_MAX,
- mTextRightEdge - ll_round(rendered_pixels_right),
- &rendered_pixels_right);
- }
- }
- else
- {
- rendered_text = mGLFont->render(
- mText, mScrollHPos,
- rendered_pixels_right, text_bottom,
- text_color,
- LLFontGL::LEFT, LLFontGL::BOTTOM,
- 0,
- LLFontGL::NO_SHADOW,
- S32_MAX,
- 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())
- {
- //mBorder->setVisible(true); // ok, programmer art just this once.
- // (Flash the cursor every half second)
- if (!mReadOnly && gFocusMgr.getAppHasFocus())
- {
- F32 elapsed = mKeystrokeTimer.getElapsedTimeF32();
- if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) )
- {
- S32 cursor_left = findPixelNearestPos();
- cursor_left -= lineeditor_cursor_thickness / 2;
- S32 cursor_right = cursor_left + lineeditor_cursor_thickness;
- if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
- {
- const LLWString space(utf8str_to_wstring(std::string(" ")));
- S32 wswidth = mGLFont->getWidth(space.c_str());
- S32 width = mGLFont->getWidth(mText.getWString().c_str(), getCursor(), 1) + 1;
- cursor_right = cursor_left + llmax(wswidth, width);
- }
- // Use same color as text for the Cursor
- gl_rect_2d(cursor_left, cursor_top,
- cursor_right, cursor_bottom, text_color);
- if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
- {
- LLColor4 tmp_color( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha );
- mGLFont->render(mText, getCursor(), (F32)(cursor_left + lineeditor_cursor_thickness / 2), text_bottom,
- tmp_color,
- LLFontGL::LEFT, LLFontGL::BOTTOM,
- 0,
- LLFontGL::NO_SHADOW,
- 1);
- }
-
- // Make sure the IME is in the right place
- S32 pixels_after_scroll = findPixelNearestPos(); // RCalculcate for IME position
- 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::getScaleFactor().mV[VX]);
- ime_pos.mY = (S32) (ime_pos.mY * LLUI::getScaleFactor().mV[VY]);
- getWindow()->setLanguageTextInput( ime_pos );
- }
- }
-
- //draw label if no text is provided
- //but we should draw it in a different color
- //to give indication that it is not text you typed in
- if (0 == mText.length() && (mReadOnly || mShowLabelFocused))
- {
- mGLFont->render(mLabel.getWString(), 0,
- mTextLeftEdge, (F32)text_bottom,
- label_color,
- LLFontGL::LEFT,
- LLFontGL::BOTTOM,
- 0,
- LLFontGL::NO_SHADOW,
- S32_MAX,
- mTextRightEdge - ll_round(rendered_pixels_right),
- &rendered_pixels_right, false);
- }
-
-
- // Draw children (border)
- //mBorder->setVisible(true);
- mBorder->setKeyboardFocusHighlight( true );
- LLView::draw();
- mBorder->setKeyboardFocusHighlight( false );
- //mBorder->setVisible(false);
- }
- else // does not have keyboard input
- {
- // draw label if no text provided
- if (0 == mText.length())
- {
- mGLFont->render(mLabel.getWString(), 0,
- mTextLeftEdge, (F32)text_bottom,
- label_color,
- LLFontGL::LEFT,
- LLFontGL::BOTTOM,
- 0,
- LLFontGL::NO_SHADOW,
- S32_MAX,
- mTextRightEdge - ll_round(rendered_pixels_right),
- &rendered_pixels_right);
- }
- // Draw children (border)
- LLView::draw();
- }
-
- if (mDrawAsterixes)
- {
- mText = saved_text;
- }
-}
-
-
-// Returns the local screen space X coordinate associated with the text cursor position.
-S32 LLLineEditor::findPixelNearestPos(const S32 cursor_offset) const
-{
- S32 dpos = getCursor() - mScrollHPos + cursor_offset;
- S32 result = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos, dpos) + mTextLeftEdge;
- 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()
-{
- mText.clear();
- setCursor(0);
-}
-
-//virtual
-void LLLineEditor::onTabInto()
-{
- selectAll();
- LLUICtrl::onTabInto();
-}
-
-//virtual
-bool LLLineEditor::acceptsTextInput() const
-{
- return true;
-}
-
-// Start or stop the editor from accepting text-editing keystrokes
-void LLLineEditor::setFocus( bool new_state )
-{
- bool old_state = hasFocus();
-
- if (!new_state)
- {
- getWindow()->allowLanguageTextInput(this, false);
- }
-
-
- // getting focus when we didn't have it before, and we want to select all
- if (!old_state && new_state && mSelectAllonFocusReceived)
- {
- selectAll();
- // We don't want handleMouseUp() to "finish" the selection (and thereby
- // set mSelectionEnd to where the mouse is), so we finish the selection
- // here.
- mIsSelecting = false;
- }
-
- if( new_state )
- {
- gEditMenuHandler = this;
-
- // Don't start the cursor flashing right away
- mKeystrokeTimer.reset();
- }
- else
- {
- // Not really needed, since loss of keyboard focus should take care of this,
- // but limited paranoia is ok.
- if( gEditMenuHandler == this )
- {
- gEditMenuHandler = NULL;
- }
-
- endSelection();
- }
-
- LLUICtrl::setFocus( new_state );
-
- if (new_state)
- {
- // Allow Language Text Input only when this LineEditor has
- // no prevalidate function attached. This criterion works
- // fine on 1.15.0.2, since all prevalidate func reject any
- // non-ASCII characters. I'm not sure on future versions,
- // however.
- getWindow()->allowLanguageTextInput(this, mPrevalidateFunc == NULL);
- }
-}
-
-//virtual
-void LLLineEditor::setRect(const LLRect& rect)
-{
- LLUICtrl::setRect(rect);
- if (mBorder)
- {
- LLRect border_rect = mBorder->getRect();
- // Scalable UI somehow made these rectangles off-by-one.
- // I don't know why. JC
- border_rect.setOriginAndSize(border_rect.mLeft, border_rect.mBottom,
- rect.getWidth()-1, rect.getHeight()-1);
- mBorder->setRect(border_rect);
- }
-}
-
-void LLLineEditor::setPrevalidate(LLTextValidate::validate_func_t func)
-{
- mPrevalidateFunc = 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)
-{
- LLLocale locale(LLLocale::USER_LOCALE);
-
- bool success = true;
- bool has_decimal = false;
- bool has_digit = false;
-
- LLWString trimmed = utf8str_to_wstring(str);
- LLWStringUtil::trim(trimmed);
- S32 len = trimmed.length();
- if( 0 < len )
- {
- S32 i = 0;
-
- // First character can be a negative sign
- if( '-' == trimmed[0] )
- {
- i++;
- }
-
- // May be a comma or period, depending on the locale
- llwchar decimal_point = (llwchar)LLResMgr::getInstance()->getDecimalPoint();
-
- for( ; i < len; i++ )
- {
- if( decimal_point == trimmed[i] )
- {
- if( has_decimal )
- {
- // can't have two
- success = false;
- break;
- }
- else
- {
- has_decimal = true;
- }
- }
- else
- if( LLStringOps::isDigit( trimmed[i] ) )
- {
- has_digit = true;
- }
- else
- {
- success = false;
- break;
- }
- }
- }
-
- // Gotta have at least one
- success = has_digit;
-
- 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();
-}
-
-
-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)
-{
- mKeystrokeCallback = boost::bind(callback, _1, user_data);
-}
-
-
-bool LLLineEditor::setTextArg( const std::string& key, const LLStringExplicit& text )
-{
- mText.setArg(key, text);
- return true;
-}
-
-bool LLLineEditor::setLabelArg( const std::string& key, const LLStringExplicit& text )
-{
- mLabel.setArg(key, text);
- return true;
-}
-
-
-void LLLineEditor::updateAllowingLanguageInput()
-{
- // Allow Language Text Input only when this LineEditor has
- // no prevalidate function attached (as long as other criteria
- // common to LLTextEditor). This criterion works
- // fine on 1.15.0.2, since all prevalidate func reject any
- // non-ASCII characters. I'm not sure on future versions,
- // however...
- LLWindow* window = getWindow();
- if (!window)
- {
- // test app, no window available
- return;
- }
- if (hasFocus() && !mReadOnly && !mDrawAsterixes && mPrevalidateFunc == NULL)
- {
- window->allowLanguageTextInput(this, true);
- }
- else
- {
- window->allowLanguageTextInput(this, false);
- }
-}
-
-bool LLLineEditor::hasPreeditString() const
-{
- return (mPreeditPositions.size() > 1);
-}
-
-void LLLineEditor::resetPreedit()
-{
- if (hasSelection())
- {
- if (hasPreeditString())
- {
- LL_WARNS() << "Preedit and selection!" << LL_ENDL;
- deselect();
- }
- else
- {
- deleteSelection();
- }
- }
- if (hasPreeditString())
- {
- const S32 preedit_pos = mPreeditPositions.front();
- mText.erase(preedit_pos, mPreeditPositions.back() - preedit_pos);
- mText.insert(preedit_pos, mPreeditOverwrittenWString);
- setCursor(preedit_pos);
-
- mPreeditWString.clear();
- mPreeditOverwrittenWString.clear();
- mPreeditPositions.clear();
-
- // Don't reset key stroke timer nor invoke keystroke callback,
- // because a call to updatePreedit should be follow soon in
- // normal course of operation, and timer and callback will be
- // maintained there. Doing so here made an odd sound. (VWR-3410)
- }
-}
-
-void LLLineEditor::updatePreedit(const LLWString &preedit_string,
- const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position)
-{
- // Just in case.
- if (mReadOnly)
- {
- return;
- }
-
- // Note that call to updatePreedit is always preceeded by resetPreedit,
- // so we have no existing selection/preedit.
-
- S32 insert_preedit_at = getCursor();
-
- mPreeditWString = preedit_string;
- mPreeditPositions.resize(preedit_segment_lengths.size() + 1);
- S32 position = insert_preedit_at;
- for (segment_lengths_t::size_type i = 0; i < preedit_segment_lengths.size(); i++)
- {
- mPreeditPositions[i] = position;
- position += preedit_segment_lengths[i];
- }
- mPreeditPositions.back() = position;
- if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
- {
- mPreeditOverwrittenWString.assign( LLWString( mText, insert_preedit_at, mPreeditWString.length() ) );
- mText.erase(insert_preedit_at, mPreeditWString.length());
- }
- else
- {
- mPreeditOverwrittenWString.clear();
- }
- mText.insert(insert_preedit_at, mPreeditWString);
-
- mPreeditStandouts = preedit_standouts;
-
- setCursor(position);
- setCursor(mPreeditPositions.front() + caret_position);
-
- // Update of the preedit should be caused by some key strokes.
- mKeystrokeTimer.reset();
- onKeystroke();
-
- mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
-}
-
-bool LLLineEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const
-{
- if (control)
- {
- LLRect control_rect_screen;
- localRectToScreen(getRect(), &control_rect_screen);
- LLUI::getInstance()->screenRectToGL(control_rect_screen, control);
- }
-
- S32 preedit_left_column, preedit_right_column;
- if (hasPreeditString())
- {
- preedit_left_column = mPreeditPositions.front();
- preedit_right_column = mPreeditPositions.back();
- }
- else
- {
- preedit_left_column = preedit_right_column = getCursor();
- }
- if (preedit_right_column < mScrollHPos)
- {
- // This should not occure...
- return false;
- }
-
- const S32 query = (query_offset >= 0 ? preedit_left_column + query_offset : getCursor());
- if (query < mScrollHPos || query < preedit_left_column || query > preedit_right_column)
- {
- return false;
- }
-
- if (coord)
- {
- S32 query_local = findPixelNearestPos(query - getCursor());
- S32 query_screen_x, query_screen_y;
- localPointToScreen(query_local, getRect().getHeight() / 2, &query_screen_x, &query_screen_y);
- LLUI::getInstance()->screenPointToGL(query_screen_x, query_screen_y, &coord->mX, &coord->mY);
- }
-
- if (bounds)
- {
- S32 preedit_left_local = findPixelNearestPos(llmax(preedit_left_column, mScrollHPos) - getCursor());
- S32 preedit_right_local = llmin(findPixelNearestPos(preedit_right_column - getCursor()), getRect().getWidth() - mBorderThickness);
- if (preedit_left_local > preedit_right_local)
- {
- // Is this condition possible?
- preedit_right_local = preedit_left_local;
- }
-
- LLRect preedit_rect_local(preedit_left_local, getRect().getHeight(), preedit_right_local, 0);
- LLRect preedit_rect_screen;
- localRectToScreen(preedit_rect_local, &preedit_rect_screen);
- LLUI::getInstance()->screenRectToGL(preedit_rect_screen, bounds);
- }
-
- return true;
-}
-
-void LLLineEditor::getPreeditRange(S32 *position, S32 *length) const
-{
- if (hasPreeditString())
- {
- *position = mPreeditPositions.front();
- *length = mPreeditPositions.back() - mPreeditPositions.front();
- }
- else
- {
- *position = mCursorPos;
- *length = 0;
- }
-}
-
-void LLLineEditor::getSelectionRange(S32 *position, S32 *length) const
-{
- if (hasSelection())
- {
- *position = llmin(mSelectionStart, mSelectionEnd);
- *length = llabs(mSelectionStart - mSelectionEnd);
- }
- else
- {
- *position = mCursorPos;
- *length = 0;
- }
-}
-
-void LLLineEditor::markAsPreedit(S32 position, S32 length)
-{
- deselect();
- setCursor(position);
- if (hasPreeditString())
- {
- LL_WARNS() << "markAsPreedit invoked when hasPreeditString is true." << LL_ENDL;
- }
- mPreeditWString.assign( LLWString( mText.getWString(), position, length ) );
- if (length > 0)
- {
- mPreeditPositions.resize(2);
- mPreeditPositions[0] = position;
- mPreeditPositions[1] = position + length;
- mPreeditStandouts.resize(1);
- mPreeditStandouts[0] = false;
- }
- else
- {
- mPreeditPositions.clear();
- mPreeditStandouts.clear();
- }
- if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
- {
- mPreeditOverwrittenWString = mPreeditWString;
- }
- else
- {
- mPreeditOverwrittenWString.clear();
- }
-}
-
-S32 LLLineEditor::getPreeditFontSize() const
-{
- return ll_round(mGLFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]);
-}
-
-void LLLineEditor::setReplaceNewlinesWithSpaces(bool replace)
-{
- mReplaceNewlinesWithSpaces = replace;
-}
-
-LLWString LLLineEditor::getConvertedText() const
-{
- LLWString text = getWText();
- LLWStringUtil::trim(text);
- if (!mReplaceNewlinesWithSpaces)
- {
- LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines.
- }
- return text;
-}
-
-void LLLineEditor::showContextMenu(S32 x, S32 y)
-{
- LLContextMenu* menu = static_cast<LLContextMenu*>(mContextMenuHandle.get());
- if (!menu)
- {
- llassert(LLMenuGL::sMenuContainer != NULL);
- menu = LLUICtrlFactory::createFromFile<LLContextMenu>
- ("menu_text_editor.xml",
- LLMenuGL::sMenuContainer,
- LLMenuHolderGL::child_registry_t::instance());
- setContextMenu(menu);
- }
-
- if (menu)
- {
- gEditMenuHandler = this;
-
- S32 screen_x, screen_y;
- localPointToScreen(x, y, &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()))
- {
- 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);
- }
-}
-
-void LLLineEditor::setContextMenu(LLContextMenu* new_context_menu)
-{
- LLContextMenu* menu = static_cast<LLContextMenu*>(mContextMenuHandle.get());
- if (menu)
- {
- menu->die();
- mContextMenuHandle.markDead();
- }
-
- if (new_context_menu)
- {
- mContextMenuHandle = new_context_menu->getHandle();
- }
-}
-
-void LLLineEditor::setFont(const LLFontGL* font)
-{
- mGLFont = font;
-}
+/**
+ * @file lllineeditor.cpp
+ * @brief LLLineEditor 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$
+ */
+
+// Text editor widget to let users enter a single line.
+
+#include "linden_common.h"
+
+#define LLLINEEDITOR_CPP
+#include "lllineeditor.h"
+
+#include "lltexteditor.h"
+#include "llmath.h"
+#include "llfontgl.h"
+#include "llgl.h"
+#include "lltimer.h"
+
+#include "llcalc.h"
+//#include "llclipboard.h"
+#include "llcontrol.h"
+#include "llbutton.h"
+#include "llfocusmgr.h"
+#include "llkeyboard.h"
+#include "llrect.h"
+#include "llresmgr.h"
+#include "llspellcheck.h"
+#include "llstring.h"
+#include "llwindow.h"
+#include "llui.h"
+#include "lluictrlfactory.h"
+#include "llclipboard.h"
+#include "llmenugl.h"
+
+//
+// Imported globals
+//
+
+//
+// Constants
+//
+
+const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds
+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
+
+static LLDefaultChildRegistry::Register<LLLineEditor> r1("line_editor");
+
+// Compiler optimization, generate extern template
+template class LLLineEditor* LLView::getChild<class LLLineEditor>(
+ const std::string& name, bool recurse) const;
+
+//
+// Member functions
+//
+
+LLLineEditor::Params::Params()
+: max_length(""),
+ keystroke_callback("keystroke_callback"),
+ prevalidator("prevalidator"),
+ input_prevalidator("input_prevalidator"),
+ 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),
+ show_label_focused("show_label_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),
+ allow_emoji("allow_emoji", true),
+ cursor_color("cursor_color"),
+ use_bg_color("use_bg_color", false),
+ bg_color("bg_color"),
+ text_color("text_color"),
+ text_readonly_color("text_readonly_color"),
+ text_tentative_color("text_tentative_color"),
+ highlight_color("highlight_color"),
+ preedit_bg_color("preedit_bg_color"),
+ border(""),
+ bg_visible("bg_visible"),
+ text_pad_left("text_pad_left"),
+ text_pad_right("text_pad_right"),
+ default_text("default_text")
+{
+ changeDefault(mouse_opaque, true);
+ addSynonym(prevalidator, "prevalidate_callback");
+ addSynonym(input_prevalidator, "prevalidate_input_callback");
+ 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)
+: LLUICtrl(p),
+ mMaxLengthBytes(p.max_length.bytes),
+ mMaxLengthChars(p.max_length.chars),
+ mCursorPos( 0 ),
+ mScrollHPos( 0 ),
+ mTextPadLeft(p.text_pad_left),
+ mTextPadRight(p.text_pad_right),
+ mTextLeftEdge(0), // computed in updateTextPadding() below
+ mTextRightEdge(0), // computed in updateTextPadding() below
+ mCommitOnFocusLost( p.commit_on_focus_lost ),
+ mKeystrokeOnEsc(false),
+ mRevertOnEsc( p.revert_on_esc ),
+ mKeystrokeCallback( p.keystroke_callback() ),
+ mIsSelecting( false ),
+ mSelectionStart( 0 ),
+ mSelectionEnd( 0 ),
+ mLastSelectionX(-1),
+ mLastSelectionY(-1),
+ mLastSelectionStart(-1),
+ mLastSelectionEnd(-1),
+ mBorderThickness( 0 ),
+ mIgnoreArrowKeys( false ),
+ mIgnoreTab( p.ignore_tab ),
+ mDrawAsterixes( p.is_password ),
+ mAllowEmoji( p.allow_emoji ),
+ 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 ),
+ mShowLabelFocused( p.show_label_focused ),
+ mUseBgColor(p.use_bg_color),
+ mHaveHistory(false),
+ mReplaceNewlinesWithSpaces( true ),
+ mPrevalidator(p.prevalidator()),
+ mInputPrevalidator(p.input_prevalidator()),
+ mLabel(p.label),
+ mCursorColor(p.cursor_color()),
+ mBgColor(p.bg_color()),
+ mFgColor(p.text_color()),
+ mReadOnlyFgColor(p.text_readonly_color()),
+ mTentativeFgColor(p.text_tentative_color()),
+ mHighlightColor(p.highlight_color()),
+ mPreeditBgColor(p.preedit_bg_color()),
+ mGLFont(p.font),
+ mContextMenuHandle(),
+ mShowContextMenu(true)
+{
+ llassert( mMaxLengthBytes > 0 );
+
+ LLUICtrl::setEnabled(true);
+ setEnabled(p.enabled);
+
+ mScrollTimer.reset();
+ mTripleClickTimer.reset();
+ setText(p.default_text());
+
+ if (p.initial_value.isProvided()
+ && !p.control_name.isProvided())
+ {
+ // Initial value often is descriptive, like "Type some ID here"
+ // and can be longer than size limitation, ignore size
+ setText(p.initial_value.getValue().asString(), false);
+ }
+
+ // Initialize current history line iterator
+ mCurrentHistoryLine = mLineHistory.begin();
+
+ LLRect border_rect(getLocalRect());
+ // adjust for gl line drawing glitch
+ border_rect.mTop -= 1;
+ border_rect.mRight -=1;
+ LLViewBorder::Params border_p(p.border);
+ border_p.rect = border_rect;
+ border_p.follows.flags = FOLLOWS_ALL;
+ border_p.bevel_style = LLViewBorder::BEVEL_IN;
+ mBorder = LLUICtrlFactory::create<LLViewBorder>(border_p);
+ addChild( mBorder );
+
+ // clamp text padding to current editor size
+ updateTextPadding();
+ setCursor(mText.length());
+
+ if (mSpellCheck)
+ {
+ LLSpellChecker::setSettingsChangeCallback(boost::bind(&LLLineEditor::onSpellCheckSettingsChange, this));
+ }
+ mSpellCheckTimer.reset();
+
+ updateAllowingLanguageInput();
+}
+
+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::initFromParams(const LLLineEditor::Params& params)
+{
+ LLUICtrl::initFromParams(params);
+ LLUICtrl::setEnabled(true);
+ setEnabled(params.enabled);
+}
+
+void LLLineEditor::onFocusReceived()
+{
+ gEditMenuHandler = this;
+ LLUICtrl::onFocusReceived();
+ updateAllowingLanguageInput();
+}
+
+void LLLineEditor::onFocusLost()
+{
+ // The call to updateAllowLanguageInput()
+ // when loosing the keyboard focus *may*
+ // indirectly invoke handleUnicodeCharHere(),
+ // so it must be called before onCommit.
+ updateAllowingLanguageInput();
+
+ if( mCommitOnFocusLost && mText.getString() != mPrevText)
+ {
+ onCommit();
+ }
+
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+
+ getWindow()->showCursorFromMouseMove();
+
+ LLUICtrl::onFocusLost();
+}
+
+// virtual
+void LLLineEditor::onCommit()
+{
+ // put current line into the line history
+ updateHistory();
+
+ setControlValue(getValue());
+ LLUICtrl::onCommit();
+ 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
+// virtual
+bool LLLineEditor::isDirty() const
+{
+ return mText.getString() != mPrevText;
+}
+
+// Clear dirty state
+// virtual
+void LLLineEditor::resetDirty()
+{
+ mPrevText = mText.getString();
+}
+
+// assumes UTF8 text
+// virtual
+void LLLineEditor::setValue(const LLSD& value )
+{
+ setText(value.asString());
+}
+
+//virtual
+LLSD LLLineEditor::getValue() const
+{
+ return LLSD(getText());
+}
+
+
+// line history support
+void LLLineEditor::updateHistory()
+{
+ // On history enabled line editors, 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( mHaveHistory && getLength() )
+ {
+ if( !mLineHistory.empty() )
+ {
+ // When not empty, last line of history should always be blank.
+ if( mLineHistory.back().empty() )
+ {
+ // discard the empty line
+ mLineHistory.pop_back();
+ }
+ else
+ {
+ LL_WARNS("") << "Last line of history was not blank." << LL_ENDL;
+ }
+ }
+
+ // Add text to history, ignoring duplicates
+ if( mLineHistory.empty() || getText() != mLineHistory.back() )
+ {
+ mLineHistory.push_back( getText() );
+ }
+
+ // Restore the blank line and set mCurrentHistoryLine to point at it
+ mLineHistory.push_back( "" );
+ mCurrentHistoryLine = mLineHistory.end() - 1;
+ }
+}
+
+void LLLineEditor::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ LLUICtrl::reshape(width, height, called_from_parent);
+ updateTextPadding(); // For clamping side-effect.
+ setCursor(mCursorPos); // For clamping side-effect.
+}
+
+void LLLineEditor::setEnabled(bool enabled)
+{
+ mReadOnly = !enabled;
+ setTabStop(!mReadOnly);
+ updateAllowingLanguageInput();
+}
+
+
+void LLLineEditor::setMaxTextLength(S32 max_text_length)
+{
+ S32 max_len = llmax(0, max_text_length);
+ mMaxLengthBytes = max_len;
+}
+
+void LLLineEditor::setMaxTextChars(S32 max_text_chars)
+{
+ S32 max_chars = llmax(0, max_text_chars);
+ mMaxLengthChars = max_chars;
+}
+
+void LLLineEditor::getTextPadding(S32 *left, S32 *right)
+{
+ *left = mTextPadLeft;
+ *right = mTextPadRight;
+}
+
+void LLLineEditor::setTextPadding(S32 left, S32 right)
+{
+ mTextPadLeft = left;
+ mTextPadRight = right;
+ updateTextPadding();
+}
+
+void LLLineEditor::updateTextPadding()
+{
+ mTextLeftEdge = llclamp(mTextPadLeft, 0, getRect().getWidth());
+ mTextRightEdge = getRect().getWidth() - llclamp(mTextPadRight, 0, getRect().getWidth());
+}
+
+
+void LLLineEditor::setText(const LLStringExplicit &new_text)
+{
+ setText(new_text, true);
+}
+
+void LLLineEditor::setText(const LLStringExplicit &new_text, bool use_size_limit)
+{
+ // If new text is identical, don't copy and don't move insertion point
+ if (mText.getString() == new_text)
+ {
+ return;
+ }
+
+ // Check to see if entire field is selected.
+ S32 len = mText.length();
+ bool all_selected = (len > 0)
+ && (( mSelectionStart == 0 && mSelectionEnd == len )
+ || ( mSelectionStart == len && mSelectionEnd == 0 ));
+
+ // Do safe truncation so we don't split multi-byte characters
+ // also consider entire string selected when mSelectAllonFocusReceived is set on an empty, focused line editor
+ all_selected = all_selected || (len == 0 && hasFocus() && mSelectAllonFocusReceived);
+
+ std::string truncated_utf8 = new_text;
+ if (!mAllowEmoji)
+ {
+ // Cut emoji symbols if exist
+ utf8str_remove_emojis(truncated_utf8);
+ }
+ if (use_size_limit && truncated_utf8.size() > (U32)mMaxLengthBytes)
+ {
+ truncated_utf8 = utf8str_truncate(new_text, mMaxLengthBytes);
+ }
+ mText.assign(truncated_utf8);
+
+ if (use_size_limit && mMaxLengthChars)
+ {
+ mText.assign(utf8str_symbol_truncate(truncated_utf8, mMaxLengthChars));
+ }
+
+ if (all_selected)
+ {
+ // ...keep whole thing selected
+ selectAll();
+ }
+ else
+ {
+ // try to preserve insertion point, but deselect text
+ deselect();
+ }
+ setCursor(llmin((S32)mText.length(), getCursor()));
+
+ // Set current history line to end of history.
+ if (mLineHistory.empty())
+ {
+ mCurrentHistoryLine = mLineHistory.end();
+ }
+ else
+ {
+ mCurrentHistoryLine = mLineHistory.end() - 1;
+ }
+
+ mPrevText = mText;
+}
+
+
+// Picks a new cursor position based on the actual screen size of text being drawn.
+void LLLineEditor::setCursorAtLocalPos( S32 local_mouse_x )
+{
+ 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;
+
+ setCursor(cursor_pos);
+}
+
+void LLLineEditor::setCursor( S32 pos )
+{
+ S32 old_cursor_pos = getCursor();
+ mCursorPos = llclamp( pos, 0, mText.length());
+
+ // position of end of next character after cursor
+ S32 pixels_after_scroll = findPixelNearestPos();
+ if( pixels_after_scroll > mTextRightEdge )
+ {
+ S32 width_chars_to_left = mGLFont->getWidth(mText.getWString().c_str(), 0, mScrollHPos);
+ S32 last_visible_char = mGLFont->maxDrawableChars(mText.getWString().c_str(), llmax(0.f, (F32)(mTextRightEdge - mTextLeftEdge + width_chars_to_left)));
+ // character immediately to left of cursor should be last one visible (SCROLL_INCREMENT_ADD will scroll in more characters)
+ // or first character if cursor is at beginning
+ S32 new_last_visible_char = llmax(0, getCursor() - 1);
+ S32 min_scroll = mGLFont->firstDrawableChar(mText.getWString().c_str(), (F32)(mTextRightEdge - mTextLeftEdge), mText.length(), new_last_visible_char);
+ if (old_cursor_pos == last_visible_char)
+ {
+ mScrollHPos = llmin(mText.length(), llmax(min_scroll, mScrollHPos + SCROLL_INCREMENT_ADD));
+ }
+ else
+ {
+ mScrollHPos = min_scroll;
+ }
+ }
+ else if (getCursor() < mScrollHPos)
+ {
+ if (old_cursor_pos == mScrollHPos)
+ {
+ mScrollHPos = llmax(0, llmin(getCursor(), mScrollHPos - SCROLL_INCREMENT_DEL));
+ }
+ else
+ {
+ mScrollHPos = getCursor();
+ }
+ }
+}
+
+
+void LLLineEditor::setCursorToEnd()
+{
+ setCursor(mText.length());
+ deselect();
+}
+
+void LLLineEditor::resetScrollPosition()
+{
+ mScrollHPos = 0;
+ // make sure cursor says in visible range
+ setCursor(getCursor());
+}
+
+bool LLLineEditor::canDeselect() const
+{
+ return hasSelection();
+}
+
+void LLLineEditor::deselect()
+{
+ mSelectionStart = 0;
+ mSelectionEnd = 0;
+ mIsSelecting = false;
+}
+
+
+void LLLineEditor::startSelection()
+{
+ mIsSelecting = true;
+ mSelectionStart = getCursor();
+ mSelectionEnd = getCursor();
+}
+
+void LLLineEditor::endSelection()
+{
+ if( mIsSelecting )
+ {
+ mIsSelecting = false;
+ mSelectionEnd = getCursor();
+ }
+}
+
+bool LLLineEditor::canSelectAll() const
+{
+ return true;
+}
+
+void LLLineEditor::selectAll()
+{
+ if (!prevalidateInput(mText.getWString()))
+ {
+ return;
+ }
+
+ mSelectionStart = mText.length();
+ mSelectionEnd = 0;
+ setCursor(mSelectionEnd);
+ //mScrollHPos = 0;
+ mIsSelecting = true;
+ 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) )
+ {
+ LLWString suggestion = utf8str_to_wstring(mSuggestionList[index]);
+ if (!mAllowEmoji)
+ {
+ // Cut emoji symbols if exist
+ wstring_remove_emojis(suggestion);
+ }
+ if (suggestion.empty())
+ return;
+
+ deselect();
+
+ // Delete the misspelled word
+ mText.erase(it->first, it->second - it->first);
+
+ // Insert the suggestion in its place
+ 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)
+{
+ setFocus( true );
+ mTripleClickTimer.setTimerExpirySec(TRIPLE_CLICK_INTERVAL);
+
+ if (mSelectionEnd == 0 && mSelectionStart == mText.length())
+ {
+ // if everything is selected, handle this as a normal click to change insertion point
+ handleMouseDown(x, y, mask);
+ }
+ else
+ {
+ const LLWString& wtext = mText.getWString();
+
+ bool doSelectAll = true;
+
+ // Select the word we're on
+ if( LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) )
+ {
+ S32 old_selection_start = mLastSelectionStart;
+ S32 old_selection_end = mLastSelectionEnd;
+
+ // Select word the cursor is over
+ while ((mCursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[mCursorPos-1] ))
+ { // Find the start of the word
+ mCursorPos--;
+ }
+ startSelection();
+
+ while ((mCursorPos < (S32)wtext.length()) && LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) )
+ { // Find the end of the word
+ mCursorPos++;
+ }
+ mSelectionEnd = mCursorPos;
+
+ // If nothing changed, then the word was already selected. Select the whole line.
+ doSelectAll = (old_selection_start == mSelectionStart) &&
+ (old_selection_end == mSelectionEnd);
+ }
+
+ if ( doSelectAll )
+ { // Select everything
+ selectAll();
+ }
+ }
+
+ // We don't want handleMouseUp() to "finish" the selection (and thereby
+ // set mSelectionEnd to where the mouse is), so we finish the selection
+ // here.
+ mIsSelecting = false;
+
+ // delay cursor flashing
+ mKeystrokeTimer.reset();
+
+ // take selection to 'primary' clipboard
+ updatePrimary();
+
+ return true;
+}
+
+bool LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // Check first whether the "clear search" button wants to deal with this.
+ if(childrenHandleMouseDown(x, y, mask) != NULL)
+ {
+ return true;
+ }
+
+ if (!mSelectAllonFocusReceived
+ || gFocusMgr.getKeyboardFocus() == this)
+ {
+ mLastSelectionStart = -1;
+ mLastSelectionStart = -1;
+
+ if (mask & MASK_SHIFT)
+ {
+ // assume we're starting a drag select
+ mIsSelecting = true;
+
+ // Handle selection extension
+ S32 old_cursor_pos = getCursor();
+ setCursorAtLocalPos(x);
+
+ if (hasSelection())
+ {
+ /* Mac-like behavior - extend selection towards the cursor
+ if (getCursor() < mSelectionStart
+ && getCursor() < mSelectionEnd)
+ {
+ // ...left of selection
+ mSelectionStart = llmax(mSelectionStart, mSelectionEnd);
+ mSelectionEnd = getCursor();
+ }
+ else if (getCursor() > mSelectionStart
+ && getCursor() > mSelectionEnd)
+ {
+ // ...right of selection
+ mSelectionStart = llmin(mSelectionStart, mSelectionEnd);
+ mSelectionEnd = getCursor();
+ }
+ else
+ {
+ mSelectionEnd = getCursor();
+ }
+ */
+ // Windows behavior
+ mSelectionEnd = getCursor();
+ }
+ else
+ {
+ mSelectionStart = old_cursor_pos;
+ mSelectionEnd = getCursor();
+ }
+ }
+ else
+ {
+ if (mTripleClickTimer.hasExpired())
+ {
+ // Save selection for word/line selecting on double-click
+ mLastSelectionStart = mSelectionStart;
+ mLastSelectionEnd = mSelectionEnd;
+
+ // Move cursor and deselect for regular click
+ setCursorAtLocalPos( x );
+ deselect();
+ startSelection();
+ }
+ else // handle triple click
+ {
+ selectAll();
+ // We don't want handleMouseUp() to "finish" the selection (and thereby
+ // set mSelectionEnd to where the mouse is), so we finish the selection
+ // here.
+ mIsSelecting = false;
+ }
+ }
+
+ gFocusMgr.setMouseCapture( this );
+ }
+
+ setFocus(true);
+
+ // delay cursor flashing
+ mKeystrokeTimer.reset();
+
+ if (mMouseDownSignal)
+ (*mMouseDownSignal)(this,x,y,mask);
+
+ return true;
+}
+
+bool LLLineEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // LL_INFOS() << "MiddleMouseDown" << LL_ENDL;
+ setFocus( true );
+ if( canPastePrimary() )
+ {
+ setCursorAtLocalPos(x);
+ pastePrimary();
+ }
+ return true;
+}
+
+bool LLLineEditor::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ setFocus(true);
+ if (!LLUICtrl::handleRightMouseDown(x, y, mask) && getShowContextMenu())
+ {
+ showContextMenu(x, y);
+ }
+ return true;
+}
+
+bool LLLineEditor::handleHover(S32 x, S32 y, MASK mask)
+{
+ bool handled = false;
+ // Check first whether the "clear search" button wants to deal with this.
+ if(!hasMouseCapture())
+ {
+ if(childrenHandleHover(x, y, mask) != NULL)
+ {
+ return true;
+ }
+ }
+
+ if( (hasMouseCapture()) && mIsSelecting )
+ {
+ if (x != mLastSelectionX || y != mLastSelectionY)
+ {
+ mLastSelectionX = x;
+ mLastSelectionY = y;
+ }
+ // Scroll if mouse cursor outside of bounds
+ if (mScrollTimer.hasExpired())
+ {
+ S32 increment = ll_round(mScrollTimer.getElapsedTimeF32() / AUTO_SCROLL_TIME);
+ mScrollTimer.reset();
+ mScrollTimer.setTimerExpirySec(AUTO_SCROLL_TIME);
+ if( (x < mTextLeftEdge) && (mScrollHPos > 0 ) )
+ {
+ // Scroll to the left
+ mScrollHPos = llclamp(mScrollHPos - increment, 0, mText.length());
+ }
+ else
+ if( (x > mTextRightEdge) && (mCursorPos < (S32)mText.length()) )
+ {
+ // If scrolling one pixel would make a difference...
+ S32 pixels_after_scrolling_one_char = findPixelNearestPos(1);
+ if( pixels_after_scrolling_one_char >= mTextRightEdge )
+ {
+ // ...scroll to the right
+ mScrollHPos = llclamp(mScrollHPos + increment, 0, mText.length());
+ }
+ }
+ }
+
+ setCursorAtLocalPos( x );
+ mSelectionEnd = getCursor();
+
+ // delay cursor flashing
+ mKeystrokeTimer.reset();
+
+ getWindow()->setCursor(UI_CURSOR_IBEAM);
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL;
+ handled = true;
+ }
+
+ if( !handled )
+ {
+ getWindow()->setCursor(UI_CURSOR_IBEAM);
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL;
+ handled = true;
+ }
+
+ return handled;
+}
+
+
+bool LLLineEditor::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ bool handled = false;
+
+ if( hasMouseCapture() )
+ {
+ gFocusMgr.setMouseCapture( NULL );
+ handled = true;
+ }
+
+ // Check first whether the "clear search" button wants to deal with this.
+ if(!handled && childrenHandleMouseUp(x, y, mask) != NULL)
+ {
+ return true;
+ }
+
+ if( mIsSelecting )
+ {
+ setCursorAtLocalPos( x );
+ mSelectionEnd = getCursor();
+
+ handled = true;
+ }
+
+ if( handled )
+ {
+ // delay cursor flashing
+ mKeystrokeTimer.reset();
+
+ // take selection to 'primary' clipboard
+ updatePrimary();
+ }
+
+ // We won't call LLUICtrl::handleMouseUp to avoid double calls of childrenHandleMouseUp().Just invoke the signal manually.
+ if (mMouseUpSignal)
+ (*mMouseUpSignal)(this,x,y, mask);
+ return handled;
+}
+
+
+// Remove a single character from the text
+void LLLineEditor::removeChar()
+{
+ if( getCursor() > 0 )
+ {
+ if (!prevalidateInput(mText.getWString().substr(getCursor()-1, 1)))
+ return;
+
+ mText.erase(getCursor() - 1, 1);
+
+ setCursor(getCursor() - 1);
+ }
+ else
+ {
+ LLUI::getInstance()->reportBadKeystroke();
+ }
+}
+
+void LLLineEditor::addChar(const llwchar uni_char)
+{
+ if (!mAllowEmoji && LLStringOps::isEmoji(uni_char))
+ return;
+
+ llwchar new_c = uni_char;
+ if (hasSelection())
+ {
+ deleteSelection();
+ }
+ else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
+ {
+ if (!prevalidateInput(mText.getWString().substr(getCursor(), 1)))
+ return;
+
+ mText.erase(getCursor(), 1);
+ }
+
+ S32 cur_bytes = mText.getString().size();
+
+ S32 new_bytes = wchar_utf8_length(new_c);
+
+ bool allow_char = true;
+
+ // Check byte length limit
+ if ((new_bytes + cur_bytes) > mMaxLengthBytes)
+ {
+ allow_char = false;
+ }
+ else if (mMaxLengthChars)
+ {
+ S32 wide_chars = mText.getWString().size();
+ if ((wide_chars + 1) > mMaxLengthChars)
+ {
+ allow_char = false;
+ }
+ }
+
+ if (allow_char)
+ {
+ // Will we need to scroll?
+ LLWString w_buf;
+ w_buf.assign(1, new_c);
+
+ mText.insert(getCursor(), w_buf);
+ setCursor(getCursor() + 1);
+ }
+ else
+ {
+ LLUI::getInstance()->reportBadKeystroke();
+ }
+
+ getWindow()->hideCursorUntilMouseMove();
+}
+
+// Extends the selection box to the new cursor position
+void LLLineEditor::extendSelection( S32 new_cursor_pos )
+{
+ if( !mIsSelecting )
+ {
+ 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();
+}
+
+
+void LLLineEditor::setSelection(S32 start, S32 end)
+{
+ S32 len = mText.length();
+
+ mIsSelecting = true;
+
+ // JC, yes, this seems odd, but I think you have to presume a
+ // selection dragged from the end towards the start.
+ mSelectionStart = llclamp(end, 0, len);
+ mSelectionEnd = llclamp(start, 0, len);
+ setCursor(start);
+}
+
+void LLLineEditor::setDrawAsterixes(bool b)
+{
+ mDrawAsterixes = b;
+ updateAllowingLanguageInput();
+}
+
+S32 LLLineEditor::prevWordPos(S32 cursorPos) const
+{
+ const LLWString& wtext = mText.getWString();
+ while( (cursorPos > 0) && (wtext[cursorPos-1] == ' ') )
+ {
+ cursorPos--;
+ }
+ while( (cursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[cursorPos-1] ) )
+ {
+ cursorPos--;
+ }
+ return cursorPos;
+}
+
+S32 LLLineEditor::nextWordPos(S32 cursorPos) const
+{
+ const LLWString& wtext = mText.getWString();
+ while( (cursorPos < getLength()) && LLWStringUtil::isPartOfWord( wtext[cursorPos] ) )
+ {
+ cursorPos++;
+ }
+ while( (cursorPos < getLength()) && (wtext[cursorPos] == ' ') )
+ {
+ cursorPos++;
+ }
+ return cursorPos;
+}
+
+
+bool LLLineEditor::handleSelectionKey(KEY key, MASK mask)
+{
+ bool handled = false;
+
+ if( mask & MASK_SHIFT )
+ {
+ handled = true;
+
+ switch( key )
+ {
+ case KEY_LEFT:
+ if( 0 < getCursor() )
+ {
+ S32 cursorPos = getCursor() - 1;
+ if( mask & MASK_CONTROL )
+ {
+ cursorPos = prevWordPos(cursorPos);
+ }
+ extendSelection( cursorPos );
+ }
+ else
+ {
+ LLUI::getInstance()->reportBadKeystroke();
+ }
+ break;
+
+ case KEY_RIGHT:
+ if( getCursor() < mText.length())
+ {
+ S32 cursorPos = getCursor() + 1;
+ if( mask & MASK_CONTROL )
+ {
+ cursorPos = nextWordPos(cursorPos);
+ }
+ extendSelection( cursorPos );
+ }
+ else
+ {
+ LLUI::getInstance()->reportBadKeystroke();
+ }
+ break;
+
+ case KEY_PAGE_UP:
+ case KEY_HOME:
+ extendSelection( 0 );
+ break;
+
+ case KEY_PAGE_DOWN:
+ case KEY_END:
+ {
+ S32 len = mText.length();
+ if( len )
+ {
+ extendSelection( len );
+ }
+ break;
+ }
+
+ default:
+ handled = false;
+ break;
+ }
+ }
+
+ if(handled)
+ {
+ // take selection to 'primary' clipboard
+ updatePrimary();
+ }
+
+ return handled;
+}
+
+void LLLineEditor::deleteSelection()
+{
+ if( !mReadOnly && hasSelection() )
+ {
+ 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();
+ setCursor(left_pos);
+ }
+}
+
+bool LLLineEditor::canCut() const
+{
+ return !mReadOnly && !mDrawAsterixes && hasSelection();
+}
+
+// cut selection to clipboard
+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 );
+
+ LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length );
+ deleteSelection();
+
+ // Validate new string and rollback the if needed.
+ bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString());
+ if (need_to_rollback)
+ {
+ rollback.doRollback( this );
+ LLUI::getInstance()->reportBadKeystroke();
+ mPrevalidator.showLastErrorUsingTimeout();
+ }
+ else
+ {
+ onKeystroke();
+ }
+ }
+}
+
+bool LLLineEditor::canCopy() const
+{
+ return !mDrawAsterixes && hasSelection();
+}
+
+
+// copy selection to clipboard
+void LLLineEditor::copy()
+{
+ if( canCopy() )
+ {
+ S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
+ S32 length = llabs( mSelectionStart - mSelectionEnd );
+ LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length );
+ }
+}
+
+bool LLLineEditor::canPaste() const
+{
+ return !mReadOnly && LLClipboard::instance().isTextAvailable();
+}
+
+void LLLineEditor::paste()
+{
+ bool is_primary = false;
+ pasteHelper(is_primary);
+}
+
+void LLLineEditor::pastePrimary()
+{
+ bool is_primary = true;
+ pasteHelper(is_primary);
+}
+
+// paste from primary (is_primary==true) or clipboard (is_primary==false)
+void LLLineEditor::pasteHelper(bool is_primary)
+{
+ bool can_paste_it;
+ if (is_primary)
+ {
+ can_paste_it = canPastePrimary();
+ }
+ else
+ {
+ can_paste_it = canPaste();
+ }
+
+ if (can_paste_it)
+ {
+ LLWString paste;
+ LLClipboard::instance().pasteFromClipboard(paste, is_primary);
+
+ if (!paste.empty())
+ {
+ if (!mAllowEmoji)
+ {
+ wstring_remove_emojis(paste);
+ }
+
+ if (!prevalidateInput(paste))
+ return;
+
+ // Prepare for possible rollback
+ LLLineEditorRollback rollback(this);
+
+ // Delete any selected characters
+ if ((!is_primary) && hasSelection())
+ {
+ deleteSelection();
+ }
+
+ // Clean up string (replace tabs and returns and remove characters that our fonts don't support.)
+ LLWString clean_string(paste);
+ LLWStringUtil::replaceTabsWithSpaces(clean_string, 1);
+ //clean_string = wstring_detabify(paste, 1);
+ LLWStringUtil::replaceChar(clean_string, '\n', mReplaceNewlinesWithSpaces ? ' ' : 182); // 182 == paragraph character
+
+ // Insert the string
+
+ // Check to see that the size isn't going to be larger than the max number of bytes
+ U32 available_bytes = mMaxLengthBytes - wstring_utf8_length(mText);
+
+ if ( available_bytes < (U32) wstring_utf8_length(clean_string) )
+ { // Doesn't all fit
+ llwchar current_symbol = clean_string[0];
+ U32 wchars_that_fit = 0;
+ U32 total_bytes = wchar_utf8_length(current_symbol);
+
+ //loop over the "wide" characters (symbols)
+ //and check to see how large (in bytes) each symbol is.
+ while ( total_bytes <= available_bytes )
+ {
+ //while we still have available bytes
+ //"accept" the current symbol and check the size
+ //of the next one
+ current_symbol = clean_string[++wchars_that_fit];
+ total_bytes += wchar_utf8_length(current_symbol);
+ }
+ // Truncate the clean string at the limit of what will fit
+ clean_string = clean_string.substr(0, wchars_that_fit);
+ LLUI::getInstance()->reportBadKeystroke();
+ }
+
+ if (mMaxLengthChars)
+ {
+ U32 available_chars = mMaxLengthChars - mText.getWString().size();
+
+ if (available_chars < clean_string.size())
+ {
+ clean_string = clean_string.substr(0, available_chars);
+ }
+
+ LLUI::getInstance()->reportBadKeystroke();
+ }
+
+ mText.insert(getCursor(), clean_string);
+ setCursor( getCursor() + (S32)clean_string.length() );
+ deselect();
+
+ // Validate new string and rollback the if needed.
+ bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString());
+ if (need_to_rollback)
+ {
+ rollback.doRollback( this );
+ LLUI::getInstance()->reportBadKeystroke();
+ mPrevalidator.showLastErrorUsingTimeout();
+ }
+ else
+ {
+ onKeystroke();
+ }
+ }
+ }
+}
+
+// copy selection to primary
+void LLLineEditor::copyPrimary()
+{
+ if( canCopy() )
+ {
+ S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
+ S32 length = llabs( mSelectionStart - mSelectionEnd );
+ LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length, true);
+ }
+}
+
+bool LLLineEditor::canPastePrimary() const
+{
+ return !mReadOnly && LLClipboard::instance().isTextAvailable(true);
+}
+
+void LLLineEditor::updatePrimary()
+{
+ if(canCopy() )
+ {
+ copyPrimary();
+ }
+}
+
+bool LLLineEditor::handleSpecialKey(KEY key, MASK mask)
+{
+ bool handled = false;
+
+ switch( key )
+ {
+ case KEY_INSERT:
+ if (mask == MASK_NONE)
+ {
+ gKeyboard->toggleInsertMode();
+ }
+
+ handled = true;
+ break;
+
+ case KEY_BACKSPACE:
+ if (!mReadOnly)
+ {
+ //LL_INFOS() << "Handling backspace" << LL_ENDL;
+ if( hasSelection() )
+ {
+ deleteSelection();
+ }
+ else
+ if( 0 < getCursor() )
+ {
+ removeChar();
+ }
+ else
+ {
+ LLUI::getInstance()->reportBadKeystroke();
+ }
+ }
+ handled = true;
+ break;
+
+ case KEY_PAGE_UP:
+ case KEY_HOME:
+ if (!mIgnoreArrowKeys)
+ {
+ setCursor(0);
+ handled = true;
+ }
+ break;
+
+ case KEY_PAGE_DOWN:
+ case KEY_END:
+ if (!mIgnoreArrowKeys)
+ {
+ S32 len = mText.length();
+ if( len )
+ {
+ setCursor(len);
+ }
+ handled = true;
+ }
+ break;
+
+ case KEY_LEFT:
+ if (mIgnoreArrowKeys && mask == MASK_NONE)
+ break;
+ if ((mask & MASK_ALT) == 0)
+ {
+ if( hasSelection() )
+ {
+ setCursor(llmin( getCursor() - 1, mSelectionStart, mSelectionEnd ));
+ }
+ else
+ if( 0 < getCursor() )
+ {
+ S32 cursorPos = getCursor() - 1;
+ if( mask & MASK_CONTROL )
+ {
+ cursorPos = prevWordPos(cursorPos);
+ }
+ setCursor(cursorPos);
+ }
+ else
+ {
+ LLUI::getInstance()->reportBadKeystroke();
+ }
+ handled = true;
+ }
+ break;
+
+ case KEY_RIGHT:
+ if (mIgnoreArrowKeys && mask == MASK_NONE)
+ break;
+ if ((mask & MASK_ALT) == 0)
+ {
+ if (hasSelection())
+ {
+ setCursor(llmax(getCursor() + 1, mSelectionStart, mSelectionEnd));
+ }
+ else
+ if (getCursor() < mText.length())
+ {
+ S32 cursorPos = getCursor() + 1;
+ if( mask & MASK_CONTROL )
+ {
+ cursorPos = nextWordPos(cursorPos);
+ }
+ setCursor(cursorPos);
+ }
+ else
+ {
+ LLUI::getInstance()->reportBadKeystroke();
+ }
+ handled = true;
+ }
+ break;
+
+ // handle ctrl-uparrow if we have a history enabled line editor.
+ case KEY_UP:
+ if (mHaveHistory && (!mIgnoreArrowKeys || (MASK_CONTROL == mask)))
+ {
+ if (mCurrentHistoryLine > mLineHistory.begin())
+ {
+ mText.assign(*(--mCurrentHistoryLine));
+ setCursorToEnd();
+ }
+ else
+ {
+ LLUI::getInstance()->reportBadKeystroke();
+ }
+ handled = true;
+ }
+ break;
+
+ // handle [ctrl]-downarrow if we have a history enabled line editor
+ case KEY_DOWN:
+ if (mHaveHistory && (!mIgnoreArrowKeys || (MASK_CONTROL == mask)))
+ {
+ if (!mLineHistory.empty() && mCurrentHistoryLine < mLineHistory.end() - 1)
+ {
+ mText.assign( *(++mCurrentHistoryLine) );
+ setCursorToEnd();
+ }
+ else
+ {
+ LLUI::getInstance()->reportBadKeystroke();
+ }
+ handled = true;
+ }
+ break;
+
+ case KEY_RETURN:
+ // store sent line in history
+ updateHistory();
+ break;
+
+ case KEY_ESCAPE:
+ if (mRevertOnEsc && mText.getString() != mPrevText)
+ {
+ setText(mPrevText);
+ // Note, don't set handled, still want to loose focus (won't commit becase text is now unchanged)
+ if (mKeystrokeOnEsc)
+ {
+ onKeystroke();
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return handled;
+}
+
+
+bool LLLineEditor::handleKeyHere(KEY key, MASK mask )
+{
+ bool handled = false;
+ bool selection_modified = false;
+
+ if ( gFocusMgr.getKeyboardFocus() == this )
+ {
+ LLLineEditorRollback rollback( this );
+
+ if( !handled )
+ {
+ handled = handleSelectionKey( key, mask );
+ selection_modified = handled;
+ }
+
+ // Handle most keys only if the text editor is writeable.
+ if ( !mReadOnly )
+ {
+ if( !handled )
+ {
+ handled = handleSpecialKey( key, mask );
+ }
+ }
+
+ if( handled )
+ {
+ mKeystrokeTimer.reset();
+
+ // Most keystrokes will make the selection box go away, but not all will.
+ if( !selection_modified &&
+ KEY_SHIFT != key &&
+ KEY_CONTROL != key &&
+ KEY_ALT != key &&
+ KEY_CAPSLOCK != key)
+ {
+ deselect();
+ }
+
+ bool prevalidator_failed = false;
+
+ // If read-only, don't allow changes
+ bool need_to_rollback = mReadOnly && (mText.getString() == rollback.getText());
+
+ // Validate new string and rollback the keystroke if needed.
+ if (!need_to_rollback && mPrevalidator)
+ {
+ prevalidator_failed = !mPrevalidator.validate(mText.getWString());
+ need_to_rollback |= prevalidator_failed;
+ }
+
+ if (need_to_rollback)
+ {
+ rollback.doRollback(this);
+
+ LLUI::getInstance()->reportBadKeystroke();
+ if (prevalidator_failed)
+ {
+ mPrevalidator.showLastErrorUsingTimeout();
+ }
+ }
+
+ // Notify owner if requested
+ if (!need_to_rollback && handled)
+ {
+ onKeystroke();
+ if ( (!selection_modified) && (KEY_BACKSPACE == key) )
+ {
+ mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
+ }
+ }
+ }
+ }
+
+ return handled;
+}
+
+
+bool LLLineEditor::handleUnicodeCharHere(llwchar uni_char)
+{
+ if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL
+ {
+ return false;
+ }
+
+ bool handled = false;
+
+ if ( (gFocusMgr.getKeyboardFocus() == this) && getVisible() && !mReadOnly)
+ {
+ handled = true;
+
+ LLLineEditorRollback rollback( this );
+
+ {
+ LLWString u_char;
+ u_char.assign(1, uni_char);
+ if (!prevalidateInput(u_char))
+ return handled;
+ }
+
+ addChar(uni_char);
+
+ mKeystrokeTimer.reset();
+
+ deselect();
+
+ // Validate new string and rollback the keystroke if needed.
+ bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString());
+ if (need_to_rollback)
+ {
+ rollback.doRollback( this );
+
+ LLUI::getInstance()->reportBadKeystroke();
+ mPrevalidator.showLastErrorUsingTimeout();
+ }
+
+ // Notify owner if requested
+ if (!need_to_rollback && handled)
+ {
+ // 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;
+}
+
+
+bool LLLineEditor::canDoDelete() const
+{
+ return ( !mReadOnly && (!mPassDelete || (hasSelection() || (getCursor() < mText.length()))) );
+}
+
+void LLLineEditor::doDelete()
+{
+ if (canDoDelete() && mText.length() > 0)
+ {
+ // Prepare for possible rollback
+ LLLineEditorRollback rollback( this );
+
+ if (hasSelection())
+ {
+ deleteSelection();
+ }
+ 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();
+ }
+
+ // Validate new string and rollback the if needed.
+ bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(mText.getWString());
+ if (need_to_rollback)
+ {
+ rollback.doRollback(this);
+ LLUI::getInstance()->reportBadKeystroke();
+ mPrevalidator.showLastErrorUsingTimeout();
+ }
+ else
+ {
+ onKeystroke();
+
+ mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
+ }
+ }
+}
+
+
+void LLLineEditor::drawBackground()
+{
+ F32 alpha = getCurrentTransparency();
+ if (mUseBgColor)
+ {
+ gl_rect_2d(getLocalRect(), mBgColor % alpha, true);
+ }
+ else
+ {
+ bool has_focus = hasFocus();
+ LLUIImage* image;
+ if (mReadOnly)
+ {
+ image = mBgImageDisabled;
+ }
+ else if (has_focus || mShowImageFocused)
+ {
+ image = mBgImageFocused;
+ }
+ else
+ {
+ image = mBgImage;
+ }
+
+ if (!image) return;
+ // optionally draw programmatic border
+ if (has_focus)
+ {
+ LLColor4 tmp_color = gFocusMgr.getFocusColor();
+ tmp_color.setAlpha(alpha);
+ image->drawBorder(0, 0, getRect().getWidth(), getRect().getHeight(),
+ tmp_color,
+ gFocusMgr.getFocusFlashWidth());
+ }
+ LLColor4 tmp_color = UI_VERTEX_COLOR;
+ tmp_color.setAlpha(alpha);
+ image->draw(getLocalRect(), tmp_color);
+ }
+}
+
+//virtual
+void LLLineEditor::draw()
+{
+ F32 alpha = getDrawContext().mAlpha;
+ S32 text_len = mText.length();
+ static LLUICachedControl<S32> lineeditor_cursor_thickness ("UILineEditorCursorThickness", 0);
+ static LLUICachedControl<F32> preedit_marker_brightness ("UIPreeditMarkerBrightness", 0);
+ static LLUICachedControl<S32> preedit_marker_gap ("UIPreeditMarkerGap", 0);
+ static LLUICachedControl<S32> preedit_marker_position ("UIPreeditMarkerPosition", 0);
+ static LLUICachedControl<S32> preedit_marker_thickness ("UIPreeditMarkerThickness", 0);
+ static LLUICachedControl<F32> preedit_standout_brightness ("UIPreeditStandoutBrightness", 0);
+ static LLUICachedControl<S32> preedit_standout_gap ("UIPreeditStandoutGap", 0);
+ static LLUICachedControl<S32> preedit_standout_position ("UIPreeditStandoutPosition", 0);
+ static LLUICachedControl<S32> preedit_standout_thickness ("UIPreeditStandoutThickness", 0);
+
+ std::string saved_text;
+ if (mDrawAsterixes)
+ {
+ saved_text = mText.getString();
+ std::string text;
+ for (S32 i = 0; i < mText.length(); i++)
+ {
+ text += PASSWORD_ASTERISK;
+ }
+ mText = text;
+ }
+
+ // draw rectangle for the background
+ LLRect background( 0, getRect().getHeight(), getRect().getWidth(), 0 );
+ background.stretch( -mBorderThickness );
+
+ S32 lineeditor_v_pad = (background.getHeight() - mGLFont->getLineHeight()) / 2;
+ if (mSpellCheck)
+ {
+ lineeditor_v_pad += 1;
+ }
+
+ drawBackground();
+
+ // draw text
+
+ // With viewer-2 art files, input region is 2 pixels up
+ S32 cursor_bottom = background.mBottom + 2;
+ S32 cursor_top = background.mTop - 1;
+
+ LLColor4 text_color;
+ if (!mReadOnly)
+ {
+ if (!getTentative())
+ {
+ text_color = mFgColor.get();
+ }
+ else
+ {
+ text_color = mTentativeFgColor.get();
+ }
+ }
+ else
+ {
+ text_color = mReadOnlyFgColor.get();
+ }
+ text_color.setAlpha(alpha);
+ LLColor4 label_color = mTentativeFgColor.get();
+ label_color.setAlpha(alpha);
+
+ if (hasPreeditString())
+ {
+ // Draw preedit markers. This needs to be before drawing letters.
+ for (U32 i = 0; i < mPreeditStandouts.size(); i++)
+ {
+ const S32 preedit_left = mPreeditPositions[i];
+ const S32 preedit_right = mPreeditPositions[i + 1];
+ if (preedit_right > mScrollHPos)
+ {
+ S32 preedit_pixels_left = findPixelNearestPos(llmax(preedit_left, mScrollHPos) - getCursor());
+ S32 preedit_pixels_right = llmin(findPixelNearestPos(preedit_right - getCursor()), background.mRight);
+ if (preedit_pixels_left >= background.mRight)
+ {
+ break;
+ }
+ if (mPreeditStandouts[i])
+ {
+ gl_rect_2d(preedit_pixels_left + preedit_standout_gap,
+ background.mBottom + preedit_standout_position,
+ preedit_pixels_right - preedit_standout_gap - 1,
+ background.mBottom + preedit_standout_position - preedit_standout_thickness,
+ (text_color * preedit_standout_brightness
+ + mPreeditBgColor * (1 - preedit_standout_brightness)).setAlpha(alpha/*1.0f*/));
+ }
+ else
+ {
+ gl_rect_2d(preedit_pixels_left + preedit_marker_gap,
+ background.mBottom + preedit_marker_position,
+ preedit_pixels_right - preedit_marker_gap - 1,
+ background.mBottom + preedit_marker_position - preedit_marker_thickness,
+ (text_color * preedit_marker_brightness
+ + mPreeditBgColor * (1 - preedit_marker_brightness)).setAlpha(alpha/*1.0f*/));
+ }
+ }
+ }
+ }
+
+ S32 rendered_text = 0;
+ F32 rendered_pixels_right = (F32)mTextLeftEdge;
+ F32 text_bottom = (F32)background.mBottom + (F32)lineeditor_v_pad;
+
+ if( (gFocusMgr.getKeyboardFocus() == this) && hasSelection() )
+ {
+ S32 select_left;
+ S32 select_right;
+ if (mSelectionStart < mSelectionEnd)
+ {
+ select_left = mSelectionStart;
+ select_right = mSelectionEnd;
+ }
+ else
+ {
+ select_left = mSelectionEnd;
+ select_right = mSelectionStart;
+ }
+
+ if( select_left > mScrollHPos )
+ {
+ // unselected, left side
+ rendered_text = mGLFont->render(
+ mText, mScrollHPos,
+ rendered_pixels_right, text_bottom,
+ text_color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM,
+ 0,
+ LLFontGL::NO_SHADOW,
+ select_left - mScrollHPos,
+ mTextRightEdge - ll_round(rendered_pixels_right),
+ &rendered_pixels_right);
+ }
+
+ if( (rendered_pixels_right < (F32)mTextRightEdge) && (rendered_text < text_len) )
+ {
+ LLColor4 color = mHighlightColor;
+ color.setAlpha(alpha);
+ // selected middle
+ S32 width = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos + rendered_text, select_right - mScrollHPos - rendered_text);
+ width = llmin(width, 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(
+ mText, mScrollHPos + rendered_text,
+ rendered_pixels_right, text_bottom,
+ tmp_color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM,
+ 0,
+ LLFontGL::NO_SHADOW,
+ select_right - mScrollHPos - rendered_text,
+ mTextRightEdge - ll_round(rendered_pixels_right),
+ &rendered_pixels_right);
+ }
+
+ if( (rendered_pixels_right < (F32)mTextRightEdge) && (rendered_text < text_len) )
+ {
+ // unselected, right side
+ rendered_text += mGLFont->render(
+ mText, mScrollHPos + rendered_text,
+ rendered_pixels_right, text_bottom,
+ text_color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM,
+ 0,
+ LLFontGL::NO_SHADOW,
+ S32_MAX,
+ mTextRightEdge - ll_round(rendered_pixels_right),
+ &rendered_pixels_right);
+ }
+ }
+ else
+ {
+ rendered_text = mGLFont->render(
+ mText, mScrollHPos,
+ rendered_pixels_right, text_bottom,
+ text_color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM,
+ 0,
+ LLFontGL::NO_SHADOW,
+ S32_MAX,
+ 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())
+ {
+ //mBorder->setVisible(true); // ok, programmer art just this once.
+ // (Flash the cursor every half second)
+ if (!mReadOnly && gFocusMgr.getAppHasFocus())
+ {
+ F32 elapsed = mKeystrokeTimer.getElapsedTimeF32();
+ if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) )
+ {
+ S32 cursor_left = findPixelNearestPos();
+ cursor_left -= lineeditor_cursor_thickness / 2;
+ S32 cursor_right = cursor_left + lineeditor_cursor_thickness;
+ if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
+ {
+ const LLWString space(utf8str_to_wstring(std::string(" ")));
+ S32 wswidth = mGLFont->getWidth(space.c_str());
+ S32 width = mGLFont->getWidth(mText.getWString().c_str(), getCursor(), 1) + 1;
+ cursor_right = cursor_left + llmax(wswidth, width);
+ }
+ // Use same color as text for the Cursor
+ gl_rect_2d(cursor_left, cursor_top,
+ cursor_right, cursor_bottom, text_color);
+ if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
+ {
+ LLColor4 tmp_color( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha );
+ mGLFont->render(mText, getCursor(), (F32)(cursor_left + lineeditor_cursor_thickness / 2), text_bottom,
+ tmp_color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM,
+ 0,
+ LLFontGL::NO_SHADOW,
+ 1);
+ }
+
+ // Make sure the IME is in the right place
+ S32 pixels_after_scroll = findPixelNearestPos(); // RCalculcate for IME position
+ 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::getScaleFactor().mV[VX]);
+ ime_pos.mY = (S32) (ime_pos.mY * LLUI::getScaleFactor().mV[VY]);
+ getWindow()->setLanguageTextInput( ime_pos );
+ }
+ }
+
+ //draw label if no text is provided
+ //but we should draw it in a different color
+ //to give indication that it is not text you typed in
+ if (0 == mText.length() && (mReadOnly || mShowLabelFocused))
+ {
+ mGLFont->render(mLabel.getWString(), 0,
+ mTextLeftEdge, (F32)text_bottom,
+ label_color,
+ LLFontGL::LEFT,
+ LLFontGL::BOTTOM,
+ 0,
+ LLFontGL::NO_SHADOW,
+ S32_MAX,
+ mTextRightEdge - ll_round(rendered_pixels_right),
+ &rendered_pixels_right, false);
+ }
+
+
+ // Draw children (border)
+ //mBorder->setVisible(true);
+ mBorder->setKeyboardFocusHighlight( true );
+ LLView::draw();
+ mBorder->setKeyboardFocusHighlight( false );
+ //mBorder->setVisible(false);
+ }
+ else // does not have keyboard input
+ {
+ // draw label if no text provided
+ if (0 == mText.length())
+ {
+ mGLFont->render(mLabel.getWString(), 0,
+ mTextLeftEdge, (F32)text_bottom,
+ label_color,
+ LLFontGL::LEFT,
+ LLFontGL::BOTTOM,
+ 0,
+ LLFontGL::NO_SHADOW,
+ S32_MAX,
+ mTextRightEdge - ll_round(rendered_pixels_right),
+ &rendered_pixels_right);
+ }
+ // Draw children (border)
+ LLView::draw();
+ }
+
+ if (mDrawAsterixes)
+ {
+ mText = saved_text;
+ }
+}
+
+
+// Returns the local screen space X coordinate associated with the text cursor position.
+S32 LLLineEditor::findPixelNearestPos(const S32 cursor_offset) const
+{
+ S32 dpos = getCursor() - mScrollHPos + cursor_offset;
+ S32 result = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos, dpos) + mTextLeftEdge;
+ 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()
+{
+ mText.clear();
+ setCursor(0);
+}
+
+//virtual
+void LLLineEditor::onTabInto()
+{
+ selectAll();
+ LLUICtrl::onTabInto();
+}
+
+//virtual
+bool LLLineEditor::acceptsTextInput() const
+{
+ return true;
+}
+
+// Start or stop the editor from accepting text-editing keystrokes
+void LLLineEditor::setFocus( bool new_state )
+{
+ bool old_state = hasFocus();
+
+ if (!new_state)
+ {
+ getWindow()->allowLanguageTextInput(this, false);
+ }
+
+
+ // getting focus when we didn't have it before, and we want to select all
+ if (!old_state && new_state && mSelectAllonFocusReceived)
+ {
+ selectAll();
+ // We don't want handleMouseUp() to "finish" the selection (and thereby
+ // set mSelectionEnd to where the mouse is), so we finish the selection
+ // here.
+ mIsSelecting = false;
+ }
+
+ if( new_state )
+ {
+ gEditMenuHandler = this;
+
+ // Don't start the cursor flashing right away
+ mKeystrokeTimer.reset();
+ }
+ else
+ {
+ // Not really needed, since loss of keyboard focus should take care of this,
+ // but limited paranoia is ok.
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+
+ endSelection();
+ }
+
+ LLUICtrl::setFocus( new_state );
+
+ if (new_state)
+ {
+ // Allow Language Text Input only when this LineEditor has
+ // no prevalidate function attached. This criterion works
+ // fine on 1.15.0.2, since all prevalidate func reject any
+ // non-ASCII characters. I'm not sure on future versions,
+ // however.
+ getWindow()->allowLanguageTextInput(this, !mPrevalidator);
+ }
+}
+
+//virtual
+void LLLineEditor::setRect(const LLRect& rect)
+{
+ LLUICtrl::setRect(rect);
+ if (mBorder)
+ {
+ LLRect border_rect = mBorder->getRect();
+ // Scalable UI somehow made these rectangles off-by-one.
+ // I don't know why. JC
+ border_rect.setOriginAndSize(border_rect.mLeft, border_rect.mBottom,
+ rect.getWidth()-1, rect.getHeight()-1);
+ mBorder->setRect(border_rect);
+ }
+}
+
+void LLLineEditor::setPrevalidate(LLTextValidate::Validator validator)
+{
+ mPrevalidator = validator;
+ updateAllowingLanguageInput();
+}
+
+void LLLineEditor::setPrevalidateInput(LLTextValidate::Validator validator)
+{
+ mInputPrevalidator = validator;
+ updateAllowingLanguageInput();
+}
+
+bool LLLineEditor::prevalidateInput(const LLWString& wstr)
+{
+ return mInputPrevalidator.validate(wstr);
+}
+
+// static
+bool LLLineEditor::postvalidateFloat(const std::string &str)
+{
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ bool success = true;
+ bool has_decimal = false;
+ bool has_digit = false;
+
+ LLWString trimmed = utf8str_to_wstring(str);
+ LLWStringUtil::trim(trimmed);
+ S32 len = trimmed.length();
+ if( 0 < len )
+ {
+ S32 i = 0;
+
+ // First character can be a negative sign
+ if( '-' == trimmed[0] )
+ {
+ i++;
+ }
+
+ // May be a comma or period, depending on the locale
+ llwchar decimal_point = (llwchar)LLResMgr::getInstance()->getDecimalPoint();
+
+ for( ; i < len; i++ )
+ {
+ if( decimal_point == trimmed[i] )
+ {
+ if( has_decimal )
+ {
+ // can't have two
+ success = false;
+ break;
+ }
+ else
+ {
+ has_decimal = true;
+ }
+ }
+ else
+ if( LLStringOps::isDigit( trimmed[i] ) )
+ {
+ has_digit = true;
+ }
+ else
+ {
+ success = false;
+ break;
+ }
+ }
+ }
+
+ // Gotta have at least one
+ success = has_digit;
+
+ 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();
+}
+
+
+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)
+{
+ mKeystrokeCallback = boost::bind(callback, _1, user_data);
+}
+
+
+bool LLLineEditor::setTextArg( const std::string& key, const LLStringExplicit& text )
+{
+ mText.setArg(key, text);
+ return true;
+}
+
+bool LLLineEditor::setLabelArg( const std::string& key, const LLStringExplicit& text )
+{
+ mLabel.setArg(key, text);
+ return true;
+}
+
+
+void LLLineEditor::updateAllowingLanguageInput()
+{
+ // Allow Language Text Input only when this LineEditor has
+ // no prevalidate function attached (as long as other criteria
+ // common to LLTextEditor). This criterion works
+ // fine on 1.15.0.2, since all prevalidate func reject any
+ // non-ASCII characters. I'm not sure on future versions,
+ // however...
+ LLWindow* window = getWindow();
+ if (!window)
+ {
+ // test app, no window available
+ return;
+ }
+ if (hasFocus() && !mReadOnly && !mDrawAsterixes && !mPrevalidator)
+ {
+ window->allowLanguageTextInput(this, true);
+ }
+ else
+ {
+ window->allowLanguageTextInput(this, false);
+ }
+}
+
+bool LLLineEditor::hasPreeditString() const
+{
+ return (mPreeditPositions.size() > 1);
+}
+
+void LLLineEditor::resetPreedit()
+{
+ if (hasSelection())
+ {
+ if (hasPreeditString())
+ {
+ LL_WARNS() << "Preedit and selection!" << LL_ENDL;
+ deselect();
+ }
+ else
+ {
+ deleteSelection();
+ }
+ }
+ if (hasPreeditString())
+ {
+ const S32 preedit_pos = mPreeditPositions.front();
+ mText.erase(preedit_pos, mPreeditPositions.back() - preedit_pos);
+ mText.insert(preedit_pos, mPreeditOverwrittenWString);
+ setCursor(preedit_pos);
+
+ mPreeditWString.clear();
+ mPreeditOverwrittenWString.clear();
+ mPreeditPositions.clear();
+
+ // Don't reset key stroke timer nor invoke keystroke callback,
+ // because a call to updatePreedit should be follow soon in
+ // normal course of operation, and timer and callback will be
+ // maintained there. Doing so here made an odd sound. (VWR-3410)
+ }
+}
+
+void LLLineEditor::updatePreedit(const LLWString &preedit_string,
+ const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position)
+{
+ // Just in case.
+ if (mReadOnly)
+ {
+ return;
+ }
+
+ // Note that call to updatePreedit is always preceeded by resetPreedit,
+ // so we have no existing selection/preedit.
+
+ S32 insert_preedit_at = getCursor();
+
+ mPreeditWString = preedit_string;
+ mPreeditPositions.resize(preedit_segment_lengths.size() + 1);
+ S32 position = insert_preedit_at;
+ for (segment_lengths_t::size_type i = 0; i < preedit_segment_lengths.size(); i++)
+ {
+ mPreeditPositions[i] = position;
+ position += preedit_segment_lengths[i];
+ }
+ mPreeditPositions.back() = position;
+ if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
+ {
+ mPreeditOverwrittenWString.assign( LLWString( mText, insert_preedit_at, mPreeditWString.length() ) );
+ mText.erase(insert_preedit_at, mPreeditWString.length());
+ }
+ else
+ {
+ mPreeditOverwrittenWString.clear();
+ }
+ mText.insert(insert_preedit_at, mPreeditWString);
+
+ mPreeditStandouts = preedit_standouts;
+
+ setCursor(position);
+ setCursor(mPreeditPositions.front() + caret_position);
+
+ // Update of the preedit should be caused by some key strokes.
+ mKeystrokeTimer.reset();
+ onKeystroke();
+
+ mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
+}
+
+bool LLLineEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const
+{
+ if (control)
+ {
+ LLRect control_rect_screen;
+ localRectToScreen(getRect(), &control_rect_screen);
+ LLUI::getInstance()->screenRectToGL(control_rect_screen, control);
+ }
+
+ S32 preedit_left_column, preedit_right_column;
+ if (hasPreeditString())
+ {
+ preedit_left_column = mPreeditPositions.front();
+ preedit_right_column = mPreeditPositions.back();
+ }
+ else
+ {
+ preedit_left_column = preedit_right_column = getCursor();
+ }
+ if (preedit_right_column < mScrollHPos)
+ {
+ // This should not occure...
+ return false;
+ }
+
+ const S32 query = (query_offset >= 0 ? preedit_left_column + query_offset : getCursor());
+ if (query < mScrollHPos || query < preedit_left_column || query > preedit_right_column)
+ {
+ return false;
+ }
+
+ if (coord)
+ {
+ S32 query_local = findPixelNearestPos(query - getCursor());
+ S32 query_screen_x, query_screen_y;
+ localPointToScreen(query_local, getRect().getHeight() / 2, &query_screen_x, &query_screen_y);
+ LLUI::getInstance()->screenPointToGL(query_screen_x, query_screen_y, &coord->mX, &coord->mY);
+ }
+
+ if (bounds)
+ {
+ S32 preedit_left_local = findPixelNearestPos(llmax(preedit_left_column, mScrollHPos) - getCursor());
+ S32 preedit_right_local = llmin(findPixelNearestPos(preedit_right_column - getCursor()), getRect().getWidth() - mBorderThickness);
+ if (preedit_left_local > preedit_right_local)
+ {
+ // Is this condition possible?
+ preedit_right_local = preedit_left_local;
+ }
+
+ LLRect preedit_rect_local(preedit_left_local, getRect().getHeight(), preedit_right_local, 0);
+ LLRect preedit_rect_screen;
+ localRectToScreen(preedit_rect_local, &preedit_rect_screen);
+ LLUI::getInstance()->screenRectToGL(preedit_rect_screen, bounds);
+ }
+
+ return true;
+}
+
+void LLLineEditor::getPreeditRange(S32 *position, S32 *length) const
+{
+ if (hasPreeditString())
+ {
+ *position = mPreeditPositions.front();
+ *length = mPreeditPositions.back() - mPreeditPositions.front();
+ }
+ else
+ {
+ *position = mCursorPos;
+ *length = 0;
+ }
+}
+
+void LLLineEditor::getSelectionRange(S32 *position, S32 *length) const
+{
+ if (hasSelection())
+ {
+ *position = llmin(mSelectionStart, mSelectionEnd);
+ *length = llabs(mSelectionStart - mSelectionEnd);
+ }
+ else
+ {
+ *position = mCursorPos;
+ *length = 0;
+ }
+}
+
+void LLLineEditor::markAsPreedit(S32 position, S32 length)
+{
+ deselect();
+ setCursor(position);
+ if (hasPreeditString())
+ {
+ LL_WARNS() << "markAsPreedit invoked when hasPreeditString is true." << LL_ENDL;
+ }
+ mPreeditWString.assign( LLWString( mText.getWString(), position, length ) );
+ if (length > 0)
+ {
+ mPreeditPositions.resize(2);
+ mPreeditPositions[0] = position;
+ mPreeditPositions[1] = position + length;
+ mPreeditStandouts.resize(1);
+ mPreeditStandouts[0] = false;
+ }
+ else
+ {
+ mPreeditPositions.clear();
+ mPreeditStandouts.clear();
+ }
+ if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
+ {
+ mPreeditOverwrittenWString = mPreeditWString;
+ }
+ else
+ {
+ mPreeditOverwrittenWString.clear();
+ }
+}
+
+S32 LLLineEditor::getPreeditFontSize() const
+{
+ return ll_round(mGLFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]);
+}
+
+void LLLineEditor::setReplaceNewlinesWithSpaces(bool replace)
+{
+ mReplaceNewlinesWithSpaces = replace;
+}
+
+LLWString LLLineEditor::getConvertedText() const
+{
+ LLWString text = getWText();
+ LLWStringUtil::trim(text);
+ if (!mReplaceNewlinesWithSpaces)
+ {
+ LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines.
+ }
+ return text;
+}
+
+void LLLineEditor::showContextMenu(S32 x, S32 y)
+{
+ LLContextMenu* menu = static_cast<LLContextMenu*>(mContextMenuHandle.get());
+ if (!menu)
+ {
+ llassert(LLMenuGL::sMenuContainer != NULL);
+ menu = LLUICtrlFactory::createFromFile<LLContextMenu>
+ ("menu_text_editor.xml",
+ LLMenuGL::sMenuContainer,
+ LLMenuHolderGL::child_registry_t::instance());
+ setContextMenu(menu);
+ }
+
+ if (menu)
+ {
+ gEditMenuHandler = this;
+
+ S32 screen_x, screen_y;
+ localPointToScreen(x, y, &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()))
+ {
+ 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);
+ }
+}
+
+void LLLineEditor::setContextMenu(LLContextMenu* new_context_menu)
+{
+ LLContextMenu* menu = static_cast<LLContextMenu*>(mContextMenuHandle.get());
+ if (menu)
+ {
+ menu->die();
+ mContextMenuHandle.markDead();
+ }
+
+ if (new_context_menu)
+ {
+ mContextMenuHandle = new_context_menu->getHandle();
+ }
+}
+
+void LLLineEditor::setFont(const LLFontGL* font)
+{
+ mGLFont = font;
+}
diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h
index afc98af3ab..debc19f7a6 100644
--- a/indra/llui/lllineeditor.h
+++ b/indra/llui/lllineeditor.h
@@ -1,470 +1,472 @@
-/**
- * @file lllineeditor.h
- * @brief Text editor widget to let users enter/edit a single line.
- *
- * Features:
- * Text entry of a single line (text, delete, left and right arrow, insert, return).
- * Callbacks either on every keystroke or just on the return key.
- * Focus (allow multiple text entry widgets)
- * Clipboard (cut, copy, and paste)
- * Horizontal scrolling to allow strings longer than widget size allows
- * Pre-validation (limit which keys can be used)
- * Optional line history so previous entries can be recalled by CTRL UP/DOWN
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLLINEEDITOR_H
-#define LL_LLLINEEDITOR_H
-
-#include "v4color.h"
-#include "llframetimer.h"
-
-#include "lleditmenuhandler.h"
-#include "llspellcheckmenuhandler.h"
-#include "lluictrl.h"
-#include "lluiimage.h"
-#include "lluistring.h"
-#include "llviewborder.h"
-
-#include "llpreeditor.h"
-#include "lltextvalidate.h"
-
-class LLFontGL;
-class LLLineEditorRollback;
-class LLButton;
-class LLContextMenu;
-
-class LLLineEditor
-: public LLUICtrl, public LLEditMenuHandler, protected LLPreeditor, public LLSpellCheckMenuHandler
-{
-public:
-
- typedef boost::function<void (LLLineEditor* caller)> keystroke_callback_t;
-
- struct MaxLength : public LLInitParam::ChoiceBlock<MaxLength>
- {
- Alternative<S32> bytes, chars;
-
- MaxLength() : bytes("max_length_bytes", 254),
- chars("max_length_chars", 0)
- {}
- };
-
- struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Optional<std::string> default_text;
- Optional<MaxLength> max_length;
- 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;
-
- Optional<LLUIImage*> background_image,
- background_image_disabled,
- background_image_focused;
-
- Optional<bool> select_on_focus,
- revert_on_esc,
- spellcheck,
- commit_on_focus_lost,
- ignore_tab,
- bg_image_always_focused,
- show_label_focused,
- is_password,
- use_bg_color;
-
- // colors
- Optional<LLUIColor> cursor_color,
- bg_color,
- text_color,
- text_readonly_color,
- text_tentative_color,
- highlight_color,
- preedit_bg_color;
-
- Optional<S32> text_pad_left,
- text_pad_right;
-
- Ignored bg_visible;
-
- Params();
- };
-
- void initFromParams(const LLLineEditor::Params& params);
-
-protected:
- LLLineEditor(const Params&);
- friend class LLUICtrlFactory;
- friend class LLFloaterEditUI;
- void showContextMenu(S32 x, S32 y);
-
-public:
- virtual ~LLLineEditor();
-
- // mousehandler overrides
- /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask) override;
- /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask) override;
- /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask) override;
- /*virtual*/ bool handleDoubleClick(S32 x,S32 y,MASK mask) override;
- /*virtual*/ bool handleMiddleMouseDown(S32 x,S32 y,MASK mask) override;
- /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask) override;
- /*virtual*/ bool handleKeyHere(KEY key, MASK mask) override;
- /*virtual*/ bool handleUnicodeCharHere(llwchar uni_char) override;
- /*virtual*/ void onMouseCaptureLost() override;
-
- // LLEditMenuHandler overrides
- /*virtual*/ void cut() override;
- /*virtual*/ bool canCut() const override;
- /*virtual*/ void copy() override;
- /*virtual*/ bool canCopy() const override;
- /*virtual*/ void paste() override;
- /*virtual*/ bool canPaste() const override;
-
- virtual void updatePrimary();
- virtual void copyPrimary();
- virtual void pastePrimary();
- virtual bool canPastePrimary() const;
-
- /*virtual*/ void doDelete() override;
- /*virtual*/ bool canDoDelete() const override;
-
- /*virtual*/ void selectAll() override;
- /*virtual*/ bool canSelectAll() const override;
-
- /*virtual*/ void deselect() override;
- /*virtual*/ bool canDeselect() const override;
-
- // LLSpellCheckMenuHandler overrides
- /*virtual*/ bool getSpellCheck() const override;
-
- /*virtual*/ const std::string& getSuggestion(U32 index) const override;
- /*virtual*/ U32 getSuggestionCount() const override;
- /*virtual*/ void replaceWithSuggestion(U32 index) override;
-
- /*virtual*/ void addToDictionary() override;
- /*virtual*/ bool canAddToDictionary() const override;
-
- /*virtual*/ void addToIgnore() override;
- /*virtual*/ bool canAddToIgnore() const override;
-
- // Spell checking helper functions
- std::string getMisspelledWord(U32 pos) const;
- bool isMisspelledWord(U32 pos) const;
- void onSpellCheckSettingsChange();
-
- // view overrides
- /*virtual*/ const std::string getToolTip() const override;
- /*virtual*/ void draw() override;
- /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true) override;
- /*virtual*/ void onFocusReceived() override;
- /*virtual*/ void onFocusLost() override;
- /*virtual*/ void setEnabled(bool enabled) override;
-
- // UI control overrides
- /*virtual*/ void clear() override;
- /*virtual*/ void onTabInto() override;
- /*virtual*/ void setFocus(bool b) override;
- /*virtual*/ void setRect(const LLRect& rect) override;
- /*virtual*/ bool acceptsTextInput() const override;
- /*virtual*/ void onCommit() override;
- /*virtual*/ bool isDirty() const override; // Returns true if user changed value at all
- /*virtual*/ void resetDirty() override; // Clear dirty state
-
- // assumes UTF8 text
- /*virtual*/ void setValue(const LLSD& value) override;
- /*virtual*/ LLSD getValue() const override;
- /*virtual*/ bool setTextArg(const std::string& key, const LLStringExplicit& text) override;
- /*virtual*/ bool setLabelArg(const std::string& key, const LLStringExplicit& text) override;
-
- void setLabel(const LLStringExplicit &new_label) { mLabel = new_label; }
- const std::string& getLabel() { return mLabel.getString(); }
-
- void setText(const LLStringExplicit &new_text);
-
- const std::string& getText() const { return mText.getString(); }
- LLWString getWText() const { return mText.getWString(); }
- LLWString getConvertedText() const; // trimmed text with paragraphs converted to newlines
-
- S32 getLength() const { return mText.length(); }
-
- S32 getCursor() const { return mCursorPos; }
- void setCursor( S32 pos );
- void setCursorToEnd();
-
- // set scroll to earliest position it can reasonable set
- void resetScrollPosition();
-
- // Selects characters 'start' to 'end'.
- void setSelection(S32 start, S32 end);
- /*virtual*/ void getSelectionRange(S32 *position, S32 *length) const override;
-
- void setCommitOnFocusLost( bool b ) { mCommitOnFocusLost = b; }
- void setRevertOnEsc( bool b ) { mRevertOnEsc = b; }
- void setKeystrokeOnEsc(bool b) { mKeystrokeOnEsc = b; }
-
- void setCursorColor(const LLColor4& c) { mCursorColor = c; }
- const LLColor4& getCursorColor() const { return mCursorColor.get(); }
-
- void setFgColor( const LLColor4& c ) { mFgColor = c; }
- void setReadOnlyFgColor( const LLColor4& c ) { mReadOnlyFgColor = c; }
- void setTentativeFgColor(const LLColor4& c) { mTentativeFgColor = c; }
-
- const LLColor4& getFgColor() const { return mFgColor.get(); }
- const LLColor4& getReadOnlyFgColor() const { return mReadOnlyFgColor.get(); }
- 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; }
- void setPassDelete(bool b) { mPassDelete = b; }
- void setDrawAsterixes(bool b);
-
- // get the cursor position of the beginning/end of the prev/next word in the text
- S32 prevWordPos(S32 cursorPos) const;
- S32 nextWordPos(S32 cursorPos) const;
-
- bool hasSelection() const { return (mSelectionStart != mSelectionEnd); }
- void startSelection();
- void endSelection();
- void extendSelection(S32 new_cursor_pos);
- 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);
-
- void setMaxTextLength(S32 max_text_length);
- void setMaxTextChars(S32 max_text_chars);
- // Manipulate left and right padding for text
- void getTextPadding(S32 *left, S32 *right);
- void setTextPadding(S32 left, S32 right);
-
- // Prevalidation controls which keystrokes can affect the editor
- void setPrevalidate( 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
-
- void setReplaceNewlinesWithSpaces(bool replace);
-
- void resetContextMenu() { setContextMenu(NULL); };
-
- void setBgImage(LLPointer<LLUIImage> image) { mBgImage = image; }
- void setBgImageFocused(LLPointer<LLUIImage> image) { mBgImageFocused = image; }
-
- void setShowContextMenu(bool show) { mShowContextMenu = show; }
- bool getShowContextMenu() const { return mShowContextMenu; }
-
- private:
- // private helper methods
-
- void pasteHelper(bool is_primary);
-
- void removeChar();
- 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);
- S32 handleCommitKey(KEY key, MASK mask);
- void updateTextPadding();
-
- // Draw the background image depending on enabled/focused state.
- void drawBackground();
-
- //
- // private data members
- //
- void updateAllowingLanguageInput();
- bool hasPreeditString() const;
- // Implementation (overrides) of LLPreeditor
- /*virtual*/ void resetPreedit() override;
- /*virtual*/ void updatePreedit(const LLWString &preedit_string,
- const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position) override;
- /*virtual*/ void markAsPreedit(S32 position, S32 length) override;
- /*virtual*/ void getPreeditRange(S32 *position, S32 *length) const override;
- /*virtual*/ bool getPreeditLocation(S32 query_position, LLCoordGL *coord, LLRect *bounds, LLRect *control) const override;
- /*virtual*/ S32 getPreeditFontSize() const override;
- /*virtual*/ LLWString getPreeditString() const override { return getWText(); }
-
- void setText(const LLStringExplicit &new_text, bool use_size_limit);
-
- void setContextMenu(LLContextMenu* new_context_menu);
-
-protected:
- LLUIString mText; // The string being edited.
- std::string mPrevText; // Saved string for 'ESC' revert
- LLUIString mLabel; // text label that is visible when no user text provided
-
- // line history support:
- bool mHaveHistory; // flag for enabled line history
- typedef std::vector<std::string> line_history_t;
- line_history_t mLineHistory; // line history storage
- line_history_t::iterator mCurrentHistoryLine; // currently browsed history line
-
- LLViewBorder* mBorder;
- const LLFontGL* mGLFont;
- S32 mMaxLengthBytes; // Max length of the UTF8 string in bytes
- S32 mMaxLengthChars; // Maximum number of characters in the string
- S32 mCursorPos; // I-beam is just after the mCursorPos-th character.
- S32 mScrollHPos; // Horizontal offset from the start of mText. Used for scrolling.
- LLFrameTimer mScrollTimer;
- S32 mTextPadLeft; // Used to reserve space before the beginning of the text for children.
- S32 mTextPadRight; // Used to reserve space after the end of the text for children.
- S32 mTextLeftEdge; // Pixels, cached left edge of text based on left padding and width
- S32 mTextRightEdge; // Pixels, cached right edge of text based on right padding and width
-
- bool mCommitOnFocusLost;
- bool mRevertOnEsc;
- bool mKeystrokeOnEsc;
-
- keystroke_callback_t mKeystrokeCallback;
-
- bool mIsSelecting; // Selection for clipboard operations
- S32 mSelectionStart;
- S32 mSelectionEnd;
- S32 mLastSelectionX;
- S32 mLastSelectionY;
- 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;
-
- LLUIColor mCursorColor;
- LLUIColor mBgColor;
- LLUIColor mFgColor;
- LLUIColor mReadOnlyFgColor;
- LLUIColor mTentativeFgColor;
- LLUIColor mHighlightColor; // background for selected text
- LLUIColor mPreeditBgColor; // preedit marker background color
-
- S32 mBorderThickness;
-
- bool mIgnoreArrowKeys;
- bool mIgnoreTab;
- bool mDrawAsterixes;
-
- bool mSelectAllonFocusReceived;
- bool mSelectAllonCommit;
- bool mPassDelete;
-
- bool mReadOnly;
-
- bool mShowImageFocused;
- bool mShowLabelFocused;
-
- bool mUseBgColor;
-
- LLWString mPreeditWString;
- LLWString mPreeditOverwrittenWString;
- std::vector<S32> mPreeditPositions;
- LLPreeditor::standouts_t mPreeditStandouts;
-
- LLHandle<LLContextMenu> mContextMenuHandle;
-
- bool mShowContextMenu;
-
-private:
- // Instances that by default point to the statics but can be overidden in XML.
- LLPointer<LLUIImage> mBgImage;
- LLPointer<LLUIImage> mBgImageDisabled;
- LLPointer<LLUIImage> mBgImageFocused;
-
- bool mReplaceNewlinesWithSpaces; // if false, will replace pasted newlines with paragraph symbol.
-
- // private helper class
- class LLLineEditorRollback
- {
- public:
- LLLineEditorRollback( LLLineEditor* ed )
- :
- mCursorPos( ed->mCursorPos ),
- mScrollHPos( ed->mScrollHPos ),
- mIsSelecting( ed->mIsSelecting ),
- mSelectionStart( ed->mSelectionStart ),
- mSelectionEnd( ed->mSelectionEnd )
- {
- mText = ed->getText();
- }
-
- void doRollback( LLLineEditor* ed )
- {
- ed->mCursorPos = mCursorPos;
- ed->mScrollHPos = mScrollHPos;
- ed->mIsSelecting = mIsSelecting;
- ed->mSelectionStart = mSelectionStart;
- ed->mSelectionEnd = mSelectionEnd;
- ed->mText = mText;
- ed->mPrevText = mText;
- }
-
- std::string getText() { return mText; }
-
- private:
- std::string mText;
- S32 mCursorPos;
- S32 mScrollHPos;
- bool mIsSelecting;
- S32 mSelectionStart;
- S32 mSelectionEnd;
- }; // end class LLLineEditorRollback
-
-}; // end class LLLineEditor
-
-// Build time optimization, generate once in .cpp file
-#ifndef LLLINEEDITOR_CPP
-extern template class LLLineEditor* LLView::getChild<class LLLineEditor>(
- const std::string& name, bool recurse) const;
-#endif
-
-#endif // LL_LINEEDITOR_
+/**
+ * @file lllineeditor.h
+ * @brief Text editor widget to let users enter/edit a single line.
+ *
+ * Features:
+ * Text entry of a single line (text, delete, left and right arrow, insert, return).
+ * Callbacks either on every keystroke or just on the return key.
+ * Focus (allow multiple text entry widgets)
+ * Clipboard (cut, copy, and paste)
+ * Horizontal scrolling to allow strings longer than widget size allows
+ * Pre-validation (limit which keys can be used)
+ * Optional line history so previous entries can be recalled by CTRL UP/DOWN
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLLINEEDITOR_H
+#define LL_LLLINEEDITOR_H
+
+#include "v4color.h"
+#include "llframetimer.h"
+
+#include "lleditmenuhandler.h"
+#include "llspellcheckmenuhandler.h"
+#include "lluictrl.h"
+#include "lluiimage.h"
+#include "lluistring.h"
+#include "llviewborder.h"
+
+#include "llpreeditor.h"
+#include "lltextvalidate.h"
+
+class LLFontGL;
+class LLLineEditorRollback;
+class LLButton;
+class LLContextMenu;
+
+class LLLineEditor
+: public LLUICtrl, public LLEditMenuHandler, protected LLPreeditor, public LLSpellCheckMenuHandler
+{
+public:
+
+ typedef boost::function<void (LLLineEditor* caller)> keystroke_callback_t;
+
+ struct MaxLength : public LLInitParam::ChoiceBlock<MaxLength>
+ {
+ Alternative<S32> bytes, chars;
+
+ MaxLength() : bytes("max_length_bytes", 254),
+ chars("max_length_chars", 0)
+ {}
+ };
+
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<std::string> default_text;
+ Optional<MaxLength> max_length;
+ Optional<keystroke_callback_t> keystroke_callback;
+
+ Optional<LLTextValidate::Validator, LLTextValidate::Validators> prevalidator;
+ Optional<LLTextValidate::Validator, LLTextValidate::Validators> input_prevalidator;
+
+ Optional<LLViewBorder::Params> border;
+
+ Optional<LLUIImage*> background_image,
+ background_image_disabled,
+ background_image_focused;
+
+ Optional<bool> select_on_focus,
+ revert_on_esc,
+ spellcheck,
+ commit_on_focus_lost,
+ ignore_tab,
+ bg_image_always_focused,
+ show_label_focused,
+ is_password,
+ allow_emoji,
+ use_bg_color;
+
+ // colors
+ Optional<LLUIColor> cursor_color,
+ bg_color,
+ text_color,
+ text_readonly_color,
+ text_tentative_color,
+ highlight_color,
+ preedit_bg_color;
+
+ Optional<S32> text_pad_left,
+ text_pad_right;
+
+ Ignored bg_visible;
+
+ Params();
+ };
+
+ void initFromParams(const LLLineEditor::Params& params);
+
+protected:
+ LLLineEditor(const Params&);
+ friend class LLUICtrlFactory;
+ friend class LLFloaterEditUI;
+ void showContextMenu(S32 x, S32 y);
+
+public:
+ virtual ~LLLineEditor();
+
+ // mousehandler overrides
+ /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ bool handleDoubleClick(S32 x,S32 y,MASK mask) override;
+ /*virtual*/ bool handleMiddleMouseDown(S32 x,S32 y,MASK mask) override;
+ /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ bool handleKeyHere(KEY key, MASK mask) override;
+ /*virtual*/ bool handleUnicodeCharHere(llwchar uni_char) override;
+ /*virtual*/ void onMouseCaptureLost() override;
+
+ // LLEditMenuHandler overrides
+ /*virtual*/ void cut() override;
+ /*virtual*/ bool canCut() const override;
+ /*virtual*/ void copy() override;
+ /*virtual*/ bool canCopy() const override;
+ /*virtual*/ void paste() override;
+ /*virtual*/ bool canPaste() const override;
+
+ virtual void updatePrimary();
+ virtual void copyPrimary();
+ virtual void pastePrimary();
+ virtual bool canPastePrimary() const;
+
+ /*virtual*/ void doDelete() override;
+ /*virtual*/ bool canDoDelete() const override;
+
+ /*virtual*/ void selectAll() override;
+ /*virtual*/ bool canSelectAll() const override;
+
+ /*virtual*/ void deselect() override;
+ /*virtual*/ bool canDeselect() const override;
+
+ // LLSpellCheckMenuHandler overrides
+ /*virtual*/ bool getSpellCheck() const override;
+
+ /*virtual*/ const std::string& getSuggestion(U32 index) const override;
+ /*virtual*/ U32 getSuggestionCount() const override;
+ /*virtual*/ void replaceWithSuggestion(U32 index) override;
+
+ /*virtual*/ void addToDictionary() override;
+ /*virtual*/ bool canAddToDictionary() const override;
+
+ /*virtual*/ void addToIgnore() override;
+ /*virtual*/ bool canAddToIgnore() const override;
+
+ // Spell checking helper functions
+ std::string getMisspelledWord(U32 pos) const;
+ bool isMisspelledWord(U32 pos) const;
+ void onSpellCheckSettingsChange();
+
+ // view overrides
+ /*virtual*/ void draw() override;
+ /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true) override;
+ /*virtual*/ void onFocusReceived() override;
+ /*virtual*/ void onFocusLost() override;
+ /*virtual*/ void setEnabled(bool enabled) override;
+
+ // UI control overrides
+ /*virtual*/ void clear() override;
+ /*virtual*/ void onTabInto() override;
+ /*virtual*/ void setFocus(bool b) override;
+ /*virtual*/ void setRect(const LLRect& rect) override;
+ /*virtual*/ bool acceptsTextInput() const override;
+ /*virtual*/ void onCommit() override;
+ /*virtual*/ bool isDirty() const override; // Returns true if user changed value at all
+ /*virtual*/ void resetDirty() override; // Clear dirty state
+
+ // assumes UTF8 text
+ /*virtual*/ void setValue(const LLSD& value) override;
+ /*virtual*/ LLSD getValue() const override;
+ /*virtual*/ bool setTextArg(const std::string& key, const LLStringExplicit& text) override;
+ /*virtual*/ bool setLabelArg(const std::string& key, const LLStringExplicit& text) override;
+
+ void setLabel(const LLStringExplicit &new_label) { mLabel = new_label; }
+ const std::string& getLabel() { return mLabel.getString(); }
+
+ void setText(const LLStringExplicit &new_text);
+
+ const std::string& getText() const override { return mText.getString(); }
+ LLWString getWText() const { return mText.getWString(); }
+ LLWString getConvertedText() const; // trimmed text with paragraphs converted to newlines
+
+ S32 getLength() const { return mText.length(); }
+
+ S32 getCursor() const { return mCursorPos; }
+ void setCursor( S32 pos );
+ void setCursorToEnd();
+
+ // set scroll to earliest position it can reasonable set
+ void resetScrollPosition();
+
+ // Selects characters 'start' to 'end'.
+ void setSelection(S32 start, S32 end);
+ /*virtual*/ void getSelectionRange(S32 *position, S32 *length) const override;
+
+ void setCommitOnFocusLost( bool b ) { mCommitOnFocusLost = b; }
+ void setRevertOnEsc( bool b ) { mRevertOnEsc = b; }
+ void setKeystrokeOnEsc(bool b) { mKeystrokeOnEsc = b; }
+
+ void setCursorColor(const LLColor4& c) { mCursorColor = c; }
+ const LLColor4& getCursorColor() const { return mCursorColor.get(); }
+
+ void setFgColor( const LLColor4& c ) { mFgColor = c; }
+ void setReadOnlyFgColor( const LLColor4& c ) { mReadOnlyFgColor = c; }
+ void setTentativeFgColor(const LLColor4& c) { mTentativeFgColor = c; }
+
+ const LLColor4& getFgColor() const { return mFgColor.get(); }
+ const LLColor4& getReadOnlyFgColor() const { return mReadOnlyFgColor.get(); }
+ const LLColor4& getTentativeFgColor() const { return mTentativeFgColor.get(); }
+
+ const LLFontGL* getFont() const override { return mGLFont; }
+ void setFont(const LLFontGL* font);
+
+ void setIgnoreArrowKeys(bool b) { mIgnoreArrowKeys = b; }
+ void setIgnoreTab(bool b) { mIgnoreTab = b; }
+ void setPassDelete(bool b) { mPassDelete = b; }
+ void setAllowEmoji(bool b) { mAllowEmoji = b; }
+ void setDrawAsterixes(bool b);
+
+ // get the cursor position of the beginning/end of the prev/next word in the text
+ S32 prevWordPos(S32 cursorPos) const;
+ S32 nextWordPos(S32 cursorPos) const;
+
+ bool hasSelection() const { return (mSelectionStart != mSelectionEnd); }
+ void startSelection();
+ void endSelection();
+ void extendSelection(S32 new_cursor_pos);
+ 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);
+
+ void setMaxTextLength(S32 max_text_length);
+ void setMaxTextChars(S32 max_text_chars);
+ // Manipulate left and right padding for text
+ void getTextPadding(S32 *left, S32 *right);
+ void setTextPadding(S32 left, S32 right);
+
+ // Prevalidation controls which keystrokes can affect the editor
+ void setPrevalidate(LLTextValidate::Validator validator);
+ // 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::Validator validator);
+ 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
+
+ void setReplaceNewlinesWithSpaces(bool replace);
+
+ void resetContextMenu() { setContextMenu(NULL); };
+
+ void setBgImage(LLPointer<LLUIImage> image) { mBgImage = image; }
+ void setBgImageFocused(LLPointer<LLUIImage> image) { mBgImageFocused = image; }
+
+ void setShowContextMenu(bool show) { mShowContextMenu = show; }
+ bool getShowContextMenu() const { return mShowContextMenu; }
+
+ private:
+ // private helper methods
+
+ void pasteHelper(bool is_primary);
+
+ void removeChar();
+ 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);
+ S32 handleCommitKey(KEY key, MASK mask);
+ void updateTextPadding();
+
+ // Draw the background image depending on enabled/focused state.
+ void drawBackground();
+
+ //
+ // private data members
+ //
+ void updateAllowingLanguageInput();
+ bool hasPreeditString() const;
+ // Implementation (overrides) of LLPreeditor
+ /*virtual*/ void resetPreedit() override;
+ /*virtual*/ void updatePreedit(const LLWString &preedit_string,
+ const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position) override;
+ /*virtual*/ void markAsPreedit(S32 position, S32 length) override;
+ /*virtual*/ void getPreeditRange(S32 *position, S32 *length) const override;
+ /*virtual*/ bool getPreeditLocation(S32 query_position, LLCoordGL *coord, LLRect *bounds, LLRect *control) const override;
+ /*virtual*/ S32 getPreeditFontSize() const override;
+ /*virtual*/ LLWString getPreeditString() const override { return getWText(); }
+
+ void setText(const LLStringExplicit &new_text, bool use_size_limit);
+
+ void setContextMenu(LLContextMenu* new_context_menu);
+
+protected:
+ LLUIString mText; // The string being edited.
+ std::string mPrevText; // Saved string for 'ESC' revert
+ LLUIString mLabel; // text label that is visible when no user text provided
+
+ // line history support:
+ bool mHaveHistory; // flag for enabled line history
+ typedef std::vector<std::string> line_history_t;
+ line_history_t mLineHistory; // line history storage
+ line_history_t::iterator mCurrentHistoryLine; // currently browsed history line
+
+ LLViewBorder* mBorder;
+ const LLFontGL* mGLFont;
+ S32 mMaxLengthBytes; // Max length of the UTF8 string in bytes
+ S32 mMaxLengthChars; // Maximum number of characters in the string
+ S32 mCursorPos; // I-beam is just after the mCursorPos-th character.
+ S32 mScrollHPos; // Horizontal offset from the start of mText. Used for scrolling.
+ LLFrameTimer mScrollTimer;
+ S32 mTextPadLeft; // Used to reserve space before the beginning of the text for children.
+ S32 mTextPadRight; // Used to reserve space after the end of the text for children.
+ S32 mTextLeftEdge; // Pixels, cached left edge of text based on left padding and width
+ S32 mTextRightEdge; // Pixels, cached right edge of text based on right padding and width
+
+ bool mCommitOnFocusLost;
+ bool mRevertOnEsc;
+ bool mKeystrokeOnEsc;
+
+ keystroke_callback_t mKeystrokeCallback;
+
+ bool mIsSelecting; // Selection for clipboard operations
+ S32 mSelectionStart;
+ S32 mSelectionEnd;
+ S32 mLastSelectionX;
+ S32 mLastSelectionY;
+ 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::Validator mPrevalidator;
+ LLTextValidate::Validator mInputPrevalidator;
+
+ LLFrameTimer mKeystrokeTimer;
+ LLTimer mTripleClickTimer;
+
+ LLUIColor mCursorColor;
+ LLUIColor mBgColor;
+ LLUIColor mFgColor;
+ LLUIColor mReadOnlyFgColor;
+ LLUIColor mTentativeFgColor;
+ LLUIColor mHighlightColor; // background for selected text
+ LLUIColor mPreeditBgColor; // preedit marker background color
+
+ S32 mBorderThickness;
+
+ bool mIgnoreArrowKeys;
+ bool mIgnoreTab;
+ bool mDrawAsterixes;
+
+ bool mSelectAllonFocusReceived;
+ bool mSelectAllonCommit;
+ bool mPassDelete;
+
+ bool mReadOnly;
+
+ bool mShowImageFocused;
+ bool mShowLabelFocused;
+
+ bool mAllowEmoji;
+ bool mUseBgColor;
+
+ LLWString mPreeditWString;
+ LLWString mPreeditOverwrittenWString;
+ std::vector<S32> mPreeditPositions;
+ LLPreeditor::standouts_t mPreeditStandouts;
+
+ LLHandle<LLContextMenu> mContextMenuHandle;
+
+ bool mShowContextMenu;
+
+private:
+ // Instances that by default point to the statics but can be overidden in XML.
+ LLPointer<LLUIImage> mBgImage;
+ LLPointer<LLUIImage> mBgImageDisabled;
+ LLPointer<LLUIImage> mBgImageFocused;
+
+ bool mReplaceNewlinesWithSpaces; // if false, will replace pasted newlines with paragraph symbol.
+
+ // private helper class
+ class LLLineEditorRollback
+ {
+ public:
+ LLLineEditorRollback( LLLineEditor* ed )
+ :
+ mCursorPos( ed->mCursorPos ),
+ mScrollHPos( ed->mScrollHPos ),
+ mIsSelecting( ed->mIsSelecting ),
+ mSelectionStart( ed->mSelectionStart ),
+ mSelectionEnd( ed->mSelectionEnd )
+ {
+ mText = ed->getText();
+ }
+
+ void doRollback( LLLineEditor* ed )
+ {
+ ed->mCursorPos = mCursorPos;
+ ed->mScrollHPos = mScrollHPos;
+ ed->mIsSelecting = mIsSelecting;
+ ed->mSelectionStart = mSelectionStart;
+ ed->mSelectionEnd = mSelectionEnd;
+ ed->mText = mText;
+ ed->mPrevText = mText;
+ }
+
+ std::string getText() { return mText; }
+
+ private:
+ std::string mText;
+ S32 mCursorPos;
+ S32 mScrollHPos;
+ bool mIsSelecting;
+ S32 mSelectionStart;
+ S32 mSelectionEnd;
+ }; // end class LLLineEditorRollback
+
+}; // end class LLLineEditor
+
+// Build time optimization, generate once in .cpp file
+#ifndef LLLINEEDITOR_CPP
+extern template class LLLineEditor* LLView::getChild<class LLLineEditor>(
+ const std::string& name, bool recurse) const;
+#endif
+
+#endif // LL_LINEEDITOR_
diff --git a/indra/llui/llloadingindicator.cpp b/indra/llui/llloadingindicator.cpp
index e8b6b7e43b..70730705e6 100644
--- a/indra/llui/llloadingindicator.cpp
+++ b/indra/llui/llloadingindicator.cpp
@@ -1,25 +1,25 @@
-/**
+/**
* @file llloadingindicator.cpp
* @brief Perpetual loading indicator
*
* $LicenseInfo:firstyear=2010&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -43,57 +43,57 @@
///////////////////////////////////////////////////////////////////////////////
LLLoadingIndicator::LLLoadingIndicator(const Params& p)
-: LLUICtrl(p),
- mImagesPerSec(p.images_per_sec > 0 ? p.images_per_sec : 1.0f),
- mCurImageIdx(0)
+: LLUICtrl(p),
+ mImagesPerSec(p.images_per_sec > 0 ? p.images_per_sec : 1.0f),
+ mCurImageIdx(0)
{
}
void LLLoadingIndicator::initFromParams(const Params& p)
{
- for (LLUIImage* image : p.images().image)
- {
- mImages.push_back(image);
- }
+ for (LLUIImage* image : p.images().image)
+ {
+ mImages.push_back(image);
+ }
- // Start timer for switching images.
- start();
+ // Start timer for switching images.
+ start();
}
void LLLoadingIndicator::draw()
{
- // Time to switch to the next image?
- if (mImageSwitchTimer.getStarted() && mImageSwitchTimer.hasExpired())
- {
- // Switch to the next image.
- if (!mImages.empty())
- {
- mCurImageIdx = (mCurImageIdx + 1) % mImages.size();
- }
-
- // Restart timer.
- start();
- }
-
- LLUIImagePtr cur_image = mImages.empty() ? LLUIImagePtr(NULL) : mImages[mCurImageIdx];
-
- // Draw current image.
- if( cur_image.notNull() )
- {
- cur_image->draw(getLocalRect(), LLColor4::white % getDrawContext().mAlpha);
- }
-
- LLUICtrl::draw();
+ // Time to switch to the next image?
+ if (mImageSwitchTimer.getStarted() && mImageSwitchTimer.hasExpired())
+ {
+ // Switch to the next image.
+ if (!mImages.empty())
+ {
+ mCurImageIdx = (mCurImageIdx + 1) % mImages.size();
+ }
+
+ // Restart timer.
+ start();
+ }
+
+ LLUIImagePtr cur_image = mImages.empty() ? LLUIImagePtr(NULL) : mImages[mCurImageIdx];
+
+ // Draw current image.
+ if( cur_image.notNull() )
+ {
+ cur_image->draw(getLocalRect(), LLColor4::white % getDrawContext().mAlpha);
+ }
+
+ LLUICtrl::draw();
}
void LLLoadingIndicator::stop()
{
- mImageSwitchTimer.stop();
+ mImageSwitchTimer.stop();
}
void LLLoadingIndicator::start()
{
- mImageSwitchTimer.start();
- F32 period = 1.0f / (mImages.size() * mImagesPerSec);
- mImageSwitchTimer.setTimerExpirySec(period);
+ mImageSwitchTimer.start();
+ F32 period = 1.0f / (mImages.size() * mImagesPerSec);
+ mImageSwitchTimer.setTimerExpirySec(period);
}
diff --git a/indra/llui/llloadingindicator.h b/indra/llui/llloadingindicator.h
index ffcb329f42..5e824af993 100644
--- a/indra/llui/llloadingindicator.h
+++ b/indra/llui/llloadingindicator.h
@@ -1,25 +1,25 @@
-/**
+/**
* @file llloadingindicator.h
* @brief Perpetual loading indicator
*
* $LicenseInfo:firstyear=2010&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -36,69 +36,69 @@
/**
* Perpetual loading indicator (a la MacOSX or YouTube)
- *
+ *
* Number of rotations per second can be overridden
* with the "images_per_sec" parameter.
- *
+ *
* Can start/stop spinning.
- *
+ *
* @see start()
* @see stop()
*/
class LLLoadingIndicator
: public LLUICtrl
{
- LOG_CLASS(LLLoadingIndicator);
+ LOG_CLASS(LLLoadingIndicator);
public:
- struct Images : public LLInitParam::Block<Images>
- {
- Multiple<LLUIImage*> image;
+ struct Images : public LLInitParam::Block<Images>
+ {
+ Multiple<LLUIImage*> image;
- Images()
- : image("image")
- {}
- };
+ Images()
+ : image("image")
+ {}
+ };
- struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Optional<F32> images_per_sec;
- Optional<Atomic<Images> > images;
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<F32> images_per_sec;
+ Optional<Atomic<Images> > images;
- Params()
- : images_per_sec("images_per_sec", 1.0f),
- images("images")
- {}
- };
+ Params()
+ : images_per_sec("images_per_sec", 1.0f),
+ images("images")
+ {}
+ };
- virtual ~LLLoadingIndicator() {}
+ virtual ~LLLoadingIndicator() {}
- // llview overrides
- virtual void draw();
+ // llview overrides
+ virtual void draw();
- /**
- * Stop spinning.
- */
- void stop();
+ /**
+ * Stop spinning.
+ */
+ void stop();
- /**
- * Start spinning.
- */
- void start();
+ /**
+ * Start spinning.
+ */
+ void start();
- void reset() { mCurImageIdx = 0; }
+ void reset() { mCurImageIdx = 0; }
private:
- LLLoadingIndicator(const Params&);
- void initFromParams(const Params&);
+ LLLoadingIndicator(const Params&);
+ void initFromParams(const Params&);
- friend class LLUICtrlFactory;
+ friend class LLUICtrlFactory;
- F32 mImagesPerSec;
- S8 mCurImageIdx;
- LLFrameTimer mImageSwitchTimer;
+ F32 mImagesPerSec;
+ S8 mCurImageIdx;
+ LLFrameTimer mImageSwitchTimer;
- std::vector<LLUIImagePtr> mImages;
+ std::vector<LLUIImagePtr> mImages;
};
#endif // LL_LLLOADINGINDICATOR_H
diff --git a/indra/llui/lllocalcliprect.cpp b/indra/llui/lllocalcliprect.cpp
index 45b7a0948a..6508106a9c 100644
--- a/indra/llui/lllocalcliprect.cpp
+++ b/indra/llui/lllocalcliprect.cpp
@@ -1,110 +1,110 @@
-/**
-* @file lllocalcliprect.cpp
-*
-* $LicenseInfo:firstyear=2009&license=viewerlgpl$
-* Second Life Viewer Source Code
-* Copyright (C) 2010, Linden Research, Inc.
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU Lesser General Public
-* License as published by the Free Software Foundation;
-* version 2.1 of the License only.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this library; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-*
-* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
-* $/LicenseInfo$
-*/
-#include "linden_common.h"
-
-#include "lllocalcliprect.h"
-
-#include "llfontgl.h"
-#include "llui.h"
-
-/*static*/ std::stack<LLRect> LLScreenClipRect::sClipRectStack;
-
-
-LLScreenClipRect::LLScreenClipRect(const LLRect& rect, bool enabled)
-: mScissorState(GL_SCISSOR_TEST),
- mEnabled(enabled)
-{
- if (mEnabled)
- {
- pushClipRect(rect);
- mScissorState.setEnabled(!sClipRectStack.empty());
- updateScissorRegion();
- }
-}
-
-LLScreenClipRect::~LLScreenClipRect()
-{
- if (mEnabled)
- {
- popClipRect();
- updateScissorRegion();
- }
-}
-
-//static
-void LLScreenClipRect::pushClipRect(const LLRect& rect)
-{
- LLRect combined_clip_rect = rect;
- if (!sClipRectStack.empty())
- {
- LLRect top = sClipRectStack.top();
- combined_clip_rect.intersectWith(top);
-
- if(combined_clip_rect.isEmpty())
- {
- // avoid artifacts where zero area rects show up as lines
- combined_clip_rect = LLRect::null;
- }
- }
- sClipRectStack.push(combined_clip_rect);
-}
-
-//static
-void LLScreenClipRect::popClipRect()
-{
- sClipRectStack.pop();
-}
-
-//static
-void LLScreenClipRect::updateScissorRegion()
-{
- if (sClipRectStack.empty()) return;
-
- // finish any deferred calls in the old clipping region
- gGL.flush();
-
- LLRect rect = sClipRectStack.top();
- stop_glerror();
- S32 x,y,w,h;
- x = llfloor(rect.mLeft * LLUI::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();
-}
-
-//---------------------------------------------------------------------------
-// LLLocalClipRect
-//---------------------------------------------------------------------------
-LLLocalClipRect::LLLocalClipRect(const LLRect& rect, bool enabled /* = true */)
-: LLScreenClipRect(LLRect(rect.mLeft + LLFontGL::sCurOrigin.mX,
- rect.mTop + LLFontGL::sCurOrigin.mY,
- rect.mRight + LLFontGL::sCurOrigin.mX,
- rect.mBottom + LLFontGL::sCurOrigin.mY), enabled)
-{}
-
-LLLocalClipRect::~LLLocalClipRect()
-{}
+/**
+* @file lllocalcliprect.cpp
+*
+* $LicenseInfo:firstyear=2009&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2010, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+#include "linden_common.h"
+
+#include "lllocalcliprect.h"
+
+#include "llfontgl.h"
+#include "llui.h"
+
+/*static*/ std::stack<LLRect> LLScreenClipRect::sClipRectStack;
+
+
+LLScreenClipRect::LLScreenClipRect(const LLRect& rect, bool enabled)
+: mScissorState(GL_SCISSOR_TEST),
+ mEnabled(enabled)
+{
+ if (mEnabled)
+ {
+ pushClipRect(rect);
+ mScissorState.setEnabled(!sClipRectStack.empty());
+ updateScissorRegion();
+ }
+}
+
+LLScreenClipRect::~LLScreenClipRect()
+{
+ if (mEnabled)
+ {
+ popClipRect();
+ updateScissorRegion();
+ }
+}
+
+//static
+void LLScreenClipRect::pushClipRect(const LLRect& rect)
+{
+ LLRect combined_clip_rect = rect;
+ if (!sClipRectStack.empty())
+ {
+ LLRect top = sClipRectStack.top();
+ combined_clip_rect.intersectWith(top);
+
+ if(combined_clip_rect.isEmpty())
+ {
+ // avoid artifacts where zero area rects show up as lines
+ combined_clip_rect = LLRect::null;
+ }
+ }
+ sClipRectStack.push(combined_clip_rect);
+}
+
+//static
+void LLScreenClipRect::popClipRect()
+{
+ sClipRectStack.pop();
+}
+
+//static
+void LLScreenClipRect::updateScissorRegion()
+{
+ if (sClipRectStack.empty()) return;
+
+ // finish any deferred calls in the old clipping region
+ gGL.flush();
+
+ LLRect rect = sClipRectStack.top();
+ stop_glerror();
+ S32 x,y,w,h;
+ x = llfloor(rect.mLeft * LLUI::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();
+}
+
+//---------------------------------------------------------------------------
+// LLLocalClipRect
+//---------------------------------------------------------------------------
+LLLocalClipRect::LLLocalClipRect(const LLRect& rect, bool enabled /* = true */)
+: LLScreenClipRect(LLRect(rect.mLeft + LLFontGL::sCurOrigin.mX,
+ rect.mTop + LLFontGL::sCurOrigin.mY,
+ rect.mRight + LLFontGL::sCurOrigin.mX,
+ rect.mBottom + LLFontGL::sCurOrigin.mY), enabled)
+{}
+
+LLLocalClipRect::~LLLocalClipRect()
+{}
diff --git a/indra/llui/lllocalcliprect.h b/indra/llui/lllocalcliprect.h
index a258d34f31..c39087070e 100644
--- a/indra/llui/lllocalcliprect.h
+++ b/indra/llui/lllocalcliprect.h
@@ -1,63 +1,63 @@
-/**
-* @file lllocalcliprect.h
-*
-* $LicenseInfo:firstyear=2009&license=viewerlgpl$
-* Second Life Viewer Source Code
-* Copyright (C) 2010, Linden Research, Inc.
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU Lesser General Public
-* License as published by the Free Software Foundation;
-* version 2.1 of the License only.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this library; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-*
-* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
-* $/LicenseInfo$
-*/
-#ifndef LLLOCALCLIPRECT_H
-#define LLLOCALCLIPRECT_H
-
-#include "llgl.h"
-#include "llrect.h" // can't forward declare, it's templated
-#include <stack>
-
-// Clip rendering to a specific rectangle using GL scissor
-// Just create one of these on the stack:
-// {
-// LLLocalClipRect(rect);
-// draw();
-// }
-class LLScreenClipRect
-{
-public:
- LLScreenClipRect(const LLRect& rect, bool enabled = true);
- virtual ~LLScreenClipRect();
-
-private:
- static void pushClipRect(const LLRect& rect);
- static void popClipRect();
- static void updateScissorRegion();
-
-private:
- LLGLState mScissorState;
- bool mEnabled;
-
- static std::stack<LLRect> sClipRectStack;
-};
-
-class LLLocalClipRect : public LLScreenClipRect
-{
-public:
- LLLocalClipRect(const LLRect& rect, bool enabled = true);
- ~LLLocalClipRect();
-};
-
-#endif
+/**
+* @file lllocalcliprect.h
+*
+* $LicenseInfo:firstyear=2009&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2010, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+#ifndef LLLOCALCLIPRECT_H
+#define LLLOCALCLIPRECT_H
+
+#include "llgl.h"
+#include "llrect.h" // can't forward declare, it's templated
+#include <stack>
+
+// Clip rendering to a specific rectangle using GL scissor
+// Just create one of these on the stack:
+// {
+// LLLocalClipRect(rect);
+// draw();
+// }
+class LLScreenClipRect
+{
+public:
+ LLScreenClipRect(const LLRect& rect, bool enabled = true);
+ virtual ~LLScreenClipRect();
+
+private:
+ static void pushClipRect(const LLRect& rect);
+ static void popClipRect();
+ static void updateScissorRegion();
+
+private:
+ LLGLState mScissorState;
+ bool mEnabled;
+
+ static std::stack<LLRect> sClipRectStack;
+};
+
+class LLLocalClipRect : public LLScreenClipRect
+{
+public:
+ LLLocalClipRect(const LLRect& rect, bool enabled = true);
+ ~LLLocalClipRect();
+};
+
+#endif
diff --git a/indra/llui/llmenubutton.cpp b/indra/llui/llmenubutton.cpp
index 5374b7ea73..e3cb35abc7 100644
--- a/indra/llui/llmenubutton.cpp
+++ b/indra/llui/llmenubutton.cpp
@@ -1,246 +1,246 @@
-/**
- * @file llbutton.cpp
- * @brief LLButton base class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llmenubutton.h"
-
-// Linden library includes
-#include "lltoggleablemenu.h"
-#include "llstring.h"
-#include "v4color.h"
-
-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);
- declare("bottomright", MP_BOTTOM_RIGHT);
-}
-
-LLMenuButton::Params::Params()
-: menu_filename("menu_filename"),
- position("menu_position", MP_BOTTOM_LEFT)
-{
- addSynonym(position, "position");
-}
-
-
-LLMenuButton::LLMenuButton(const LLMenuButton::Params& p)
-: LLButton(p),
- mIsMenuShown(false),
- mMenuPosition(p.position),
- mOwnMenu(false)
-{
- std::string menu_filename = p.menu_filename;
-
- setMenu(menu_filename, mMenuPosition);
- updateMenuOrigin();
-}
-
-LLMenuButton::~LLMenuButton()
-{
- cleanup();
-}
-
-boost::signals2::connection LLMenuButton::setMouseDownCallback( const mouse_signal_t::slot_type& cb )
-{
- return LLUICtrl::setMouseDownCallback(cb);
-}
-
-void LLMenuButton::hideMenu()
-{
- LLToggleableMenu* menu = getMenu();
- if (menu)
- {
- menu->setVisible(false);
- }
-}
-
-LLToggleableMenu* LLMenuButton::getMenu()
-{
- return dynamic_cast<LLToggleableMenu*>(mMenuHandle.get());
-}
-
-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 (!getMenu()) return false;
-
- if( KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key))
- {
- // *HACK: We emit the mouse down signal to fire the callback bound to the
- // menu emerging event before actually displaying the menu. See STORM-263.
- LLUICtrl::handleMouseDown(-1, -1, MASK_NONE);
-
- toggleMenu();
- return true;
- }
-
- LLToggleableMenu* menu = getMenu();
- if (menu && menu->getVisible() && key == KEY_ESCAPE && mask == MASK_NONE)
- {
- menu->setVisible(false);
- return true;
- }
-
- return false;
-}
-
-bool LLMenuButton::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- LLButton::handleMouseDown(x, y, mask);
-
- toggleMenu();
-
- return true;
-}
-
-void LLMenuButton::toggleMenu()
-{
- if (mValidateSignal && !(*mValidateSignal)(this, LLSD()))
- {
- return;
- }
-
- LLToggleableMenu* menu = getMenu();
- if (!menu) return;
-
- // Store the button rectangle to toggle menu visibility if a mouse event
- // occurred inside or outside the button rect.
- menu->setButtonRect(this);
-
- if (!menu->toggleVisibility() && mIsMenuShown)
- {
- setForcePressedState(false);
- mIsMenuShown = false;
- }
- else
- {
- menu->buildDrawLabels();
- menu->arrangeAndClear();
- menu->updateParent(LLMenuGL::sMenuContainer);
-
- updateMenuOrigin();
-
- LLMenuGL::showPopup(getParent(), menu, mX, mY);
-
- setForcePressedState(true);
- mIsMenuShown = true;
- }
-}
-
-void LLMenuButton::updateMenuOrigin()
-{
- LLToggleableMenu* menu = getMenu();
- if (!menu) return;
-
- LLRect rect = getRect();
-
- switch (mMenuPosition)
- {
- case MP_TOP_LEFT:
- {
- mX = rect.mLeft;
- mY = rect.mTop + menu->getRect().getHeight();
- break;
- }
- case MP_TOP_RIGHT:
- {
- const LLRect& menu_rect = menu->getRect();
- mX = rect.mRight - menu_rect.getWidth();
- mY = rect.mTop + menu_rect.getHeight();
- break;
- }
- case MP_BOTTOM_LEFT:
- {
- mX = rect.mLeft;
- mY = rect.mBottom;
- break;
- }
- case MP_BOTTOM_RIGHT:
- {
- const LLRect& menu_rect = menu->getRect();
- mX = rect.mRight - menu_rect.getWidth();
- mY = rect.mBottom;
- break;
- }
- }
-}
-
-void LLMenuButton::onMenuVisibilityChange(const LLSD& param)
-{
- bool new_visibility = param["visibility"].asBoolean();
- bool is_closed_by_button_click = param["closed_by_button_click"].asBoolean();
-
- // Reset the button "pressed" state only if the menu is shown by this particular
- // menu button (not any other control) and is not being closed by a click on the button.
- if (!new_visibility && !is_closed_by_button_click && mIsMenuShown)
- {
- setForcePressedState(false);
- mIsMenuShown = false;
- }
-}
-
-void LLMenuButton::cleanup()
-{
- if (mMenuHandle.get() && mOwnMenu)
- {
- mMenuHandle.get()->die();
- }
-}
+/**
+ * @file llbutton.cpp
+ * @brief LLButton base class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llmenubutton.h"
+
+// Linden library includes
+#include "lltoggleablemenu.h"
+#include "llstring.h"
+#include "v4color.h"
+
+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);
+ declare("bottomright", MP_BOTTOM_RIGHT);
+}
+
+LLMenuButton::Params::Params()
+: menu_filename("menu_filename"),
+ position("menu_position", MP_BOTTOM_LEFT)
+{
+ addSynonym(position, "position");
+}
+
+
+LLMenuButton::LLMenuButton(const LLMenuButton::Params& p)
+: LLButton(p),
+ mIsMenuShown(false),
+ mMenuPosition(p.position),
+ mOwnMenu(false)
+{
+ std::string menu_filename = p.menu_filename;
+
+ setMenu(menu_filename, mMenuPosition);
+ updateMenuOrigin();
+}
+
+LLMenuButton::~LLMenuButton()
+{
+ cleanup();
+}
+
+boost::signals2::connection LLMenuButton::setMouseDownCallback( const mouse_signal_t::slot_type& cb )
+{
+ return LLUICtrl::setMouseDownCallback(cb);
+}
+
+void LLMenuButton::hideMenu()
+{
+ LLToggleableMenu* menu = getMenu();
+ if (menu)
+ {
+ menu->setVisible(false);
+ }
+}
+
+LLToggleableMenu* LLMenuButton::getMenu()
+{
+ return dynamic_cast<LLToggleableMenu*>(mMenuHandle.get());
+}
+
+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 (!getMenu()) return false;
+
+ if( KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key))
+ {
+ // *HACK: We emit the mouse down signal to fire the callback bound to the
+ // menu emerging event before actually displaying the menu. See STORM-263.
+ LLUICtrl::handleMouseDown(-1, -1, MASK_NONE);
+
+ toggleMenu();
+ return true;
+ }
+
+ LLToggleableMenu* menu = getMenu();
+ if (menu && menu->getVisible() && key == KEY_ESCAPE && mask == MASK_NONE)
+ {
+ menu->setVisible(false);
+ return true;
+ }
+
+ return false;
+}
+
+bool LLMenuButton::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLButton::handleMouseDown(x, y, mask);
+
+ toggleMenu();
+
+ return true;
+}
+
+void LLMenuButton::toggleMenu()
+{
+ if (mValidateSignal && !(*mValidateSignal)(this, LLSD()))
+ {
+ return;
+ }
+
+ LLToggleableMenu* menu = getMenu();
+ if (!menu) return;
+
+ // Store the button rectangle to toggle menu visibility if a mouse event
+ // occurred inside or outside the button rect.
+ menu->setButtonRect(this);
+
+ if (!menu->toggleVisibility() && mIsMenuShown)
+ {
+ setForcePressedState(false);
+ mIsMenuShown = false;
+ }
+ else
+ {
+ menu->buildDrawLabels();
+ menu->arrangeAndClear();
+ menu->updateParent(LLMenuGL::sMenuContainer);
+
+ updateMenuOrigin();
+
+ LLMenuGL::showPopup(getParent(), menu, mX, mY);
+
+ setForcePressedState(true);
+ mIsMenuShown = true;
+ }
+}
+
+void LLMenuButton::updateMenuOrigin()
+{
+ LLToggleableMenu* menu = getMenu();
+ if (!menu) return;
+
+ LLRect rect = getRect();
+
+ switch (mMenuPosition)
+ {
+ case MP_TOP_LEFT:
+ {
+ mX = rect.mLeft;
+ mY = rect.mTop + menu->getRect().getHeight();
+ break;
+ }
+ case MP_TOP_RIGHT:
+ {
+ const LLRect& menu_rect = menu->getRect();
+ mX = rect.mRight - menu_rect.getWidth();
+ mY = rect.mTop + menu_rect.getHeight();
+ break;
+ }
+ case MP_BOTTOM_LEFT:
+ {
+ mX = rect.mLeft;
+ mY = rect.mBottom;
+ break;
+ }
+ case MP_BOTTOM_RIGHT:
+ {
+ const LLRect& menu_rect = menu->getRect();
+ mX = rect.mRight - menu_rect.getWidth();
+ mY = rect.mBottom;
+ break;
+ }
+ }
+}
+
+void LLMenuButton::onMenuVisibilityChange(const LLSD& param)
+{
+ bool new_visibility = param["visibility"].asBoolean();
+ bool is_closed_by_button_click = param["closed_by_button_click"].asBoolean();
+
+ // Reset the button "pressed" state only if the menu is shown by this particular
+ // menu button (not any other control) and is not being closed by a click on the button.
+ if (!new_visibility && !is_closed_by_button_click && mIsMenuShown)
+ {
+ setForcePressedState(false);
+ 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 1854f459d5..4b66d236b7 100644
--- a/indra/llui/llmenubutton.h
+++ b/indra/llui/llmenubutton.h
@@ -1,101 +1,101 @@
-/**
- * @file llbutton.h
- * @brief Header for buttons
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLMENUBUTTON_H
-#define LL_LLMENUBUTTON_H
-
-#include "llbutton.h"
-
-class LLToggleableMenu;
-
-class LLMenuButton
-: public LLButton
-{
- LOG_CLASS(LLMenuButton);
-
-public:
- typedef enum e_menu_position
- {
- MP_TOP_LEFT,
- MP_TOP_RIGHT,
- MP_BOTTOM_LEFT,
- MP_BOTTOM_RIGHT
- } 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();
- };
-
-
-
- boost::signals2::connection setMouseDownCallback( const mouse_signal_t::slot_type& cb );
-
- /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleKeyHere(KEY key, MASK mask );
-
- void hideMenu();
-
- LLToggleableMenu* getMenu();
- 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();
-
- 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
-};
-
-
-#endif // LL_LLMENUBUTTON_H
+/**
+ * @file llbutton.h
+ * @brief Header for buttons
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLMENUBUTTON_H
+#define LL_LLMENUBUTTON_H
+
+#include "llbutton.h"
+
+class LLToggleableMenu;
+
+class LLMenuButton
+: public LLButton
+{
+ LOG_CLASS(LLMenuButton);
+
+public:
+ typedef enum e_menu_position
+ {
+ MP_TOP_LEFT,
+ MP_TOP_RIGHT,
+ MP_BOTTOM_LEFT,
+ MP_BOTTOM_RIGHT
+ } 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();
+ };
+
+
+
+ boost::signals2::connection setMouseDownCallback( const mouse_signal_t::slot_type& cb );
+
+ /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleKeyHere(KEY key, MASK mask );
+
+ void hideMenu();
+
+ LLToggleableMenu* getMenu();
+ 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();
+
+ 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
+};
+
+
+#endif // LL_LLMENUBUTTON_H
diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp
index 5dc92e555a..9a058f420a 100644
--- a/indra/llui/llmenugl.cpp
+++ b/indra/llui/llmenugl.cpp
@@ -1,4405 +1,4404 @@
-/**
- * @file llmenugl.cpp
- * @brief LLMenuItemGL 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$
- */
-
-//*****************************************************************************
-//
-// This file contains the opengl based menu implementation.
-//
-// NOTES: A menu label is split into 4 columns. The left column, the
-// label colum, the accelerator column, and the right column. The left
-// column is used for displaying boolean values for toggle and check
-// controls. The right column is used for submenus.
-//
-//*****************************************************************************
-
-//#include "llviewerprecompiledheaders.h"
-#include "linden_common.h"
-
-#include "llmenugl.h"
-
-#include "llgl.h"
-#include "llmath.h"
-#include "llrender.h"
-#include "llfocusmgr.h"
-#include "llcoord.h"
-#include "llwindow.h"
-#include "llcriticaldamp.h"
-#include "lluictrlfactory.h"
-
-#include "llbutton.h"
-#include "llfontgl.h"
-#include "llresmgr.h"
-#include "lltrans.h"
-#include "llui.h"
-
-#include "llstl.h"
-
-#include "v2math.h"
-#include <set>
-#include <boost/tokenizer.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;
-
-///============================================================================
-/// Local function declarations, constants, enums, and typedefs
-///============================================================================
-
-const S32 LABEL_BOTTOM_PAD_PIXELS = 2;
-
-const U32 LEFT_PAD_PIXELS = 3;
-const U32 LEFT_WIDTH_PIXELS = 15;
-const U32 LEFT_PLAIN_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS;
-
-const U32 RIGHT_PAD_PIXELS = 7;
-const U32 RIGHT_WIDTH_PIXELS = 15;
-const U32 RIGHT_PLAIN_PIXELS = RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS;
-
-const U32 PLAIN_PAD_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS;
-
-const U32 BRIEF_PAD_PIXELS = 2;
-
-const U32 SEPARATOR_HEIGHT_PIXELS = 8;
-const S32 TEAROFF_SEPARATOR_HEIGHT_PIXELS = 10;
-const S32 MENU_ITEM_PADDING = 4;
-
-const std::string SEPARATOR_NAME("separator");
-const std::string VERTICAL_SEPARATOR_LABEL( "|" );
-
-const std::string LLMenuGL::BOOLEAN_TRUE_PREFIX( "\xE2\x9C\x94" ); // U+2714 HEAVY CHECK MARK
-const std::string LLMenuGL::BRANCH_SUFFIX( "\xe2\x96\xb8" ); // U+25B6 BLACK RIGHT-POINTING TRIANGLE
-const std::string LLMenuGL::ARROW_UP ("^^^^^^^");
-const std::string LLMenuGL::ARROW_DOWN("vvvvvvv");
-
-const F32 MAX_MOUSE_SLOPE_SUB_MENU = 0.9f;
-
-bool LLMenuGL::sKeyboardMode = false;
-
-LLHandle<LLView> LLMenuHolderGL::sItemLastSelectedHandle;
-LLFrameTimer LLMenuHolderGL::sItemActivationTimer;
-
-const F32 ACTIVATE_HIGHLIGHT_TIME = 0.3f;
-
-static MenuRegistry::Register<LLMenuItemGL> register_menu_item("menu_item");
-static MenuRegistry::Register<LLMenuItemSeparatorGL> register_separator("menu_item_separator");
-static MenuRegistry::Register<LLMenuItemCallGL> register_menu_item_call("menu_item_call");
-static MenuRegistry::Register<LLMenuItemCheckGL> register_menu_item_check("menu_item_check");
-// Created programmatically but we need to specify custom colors in xml
-static MenuRegistry::Register<LLMenuItemTearOffGL> register_menu_item_tear_off("menu_item_tear_off");
-static MenuRegistry::Register<LLMenuGL> register_menu("menu");
-
-static LLDefaultChildRegistry::Register<LLMenuGL> register_menu_default("menu");
-
-
-
-///============================================================================
-/// Class LLMenuItemGL
-///============================================================================
-
-LLMenuItemGL::Params::Params()
-: shortcut("shortcut"),
- jump_key("jump_key", KEY_NONE),
- use_mac_ctrl("use_mac_ctrl", false),
- allow_key_repeat("allow_key_repeat", false),
- rect("rect"),
- left("left"),
- top("top"),
- right("right"),
- bottom("bottom"),
- width("width"),
- height("height"),
- bottom_delta("bottom_delta"),
- left_delta("left_delta"),
- enabled_color("enabled_color"),
- disabled_color("disabled_color"),
- highlight_bg_color("highlight_bg_color"),
- highlight_fg_color("highlight_fg_color")
-{
- changeDefault(mouse_opaque, true);
-}
-
-// Default constructor
-LLMenuItemGL::LLMenuItemGL(const LLMenuItemGL::Params& p)
-: LLUICtrl(p),
- mJumpKey(p.jump_key),
- mAllowKeyRepeat(p.allow_key_repeat),
- mHighlight( false ),
- mGotHover( false ),
- mBriefItem( false ),
- mDrawTextDisabled( false ),
- mFont(p.font),
- mAcceleratorKey(KEY_NONE),
- mAcceleratorMask(MASK_NONE),
- mLabel(p.label.isProvided() ? p.label() : p.name()),
- mEnabledColor(p.enabled_color()),
- mDisabledColor(p.disabled_color()),
- mHighlightBackground(p.highlight_bg_color()),
- mHighlightForeground(p.highlight_fg_color())
-{
-#ifdef LL_DARWIN
- // See if this Mac accelerator should really use the ctrl key and not get mapped to cmd
- bool useMacCtrl = p.use_mac_ctrl;
-#endif // LL_DARWIN
-
- std::string shortcut = p.shortcut;
- if (shortcut.find("control") != shortcut.npos)
- {
-#ifdef LL_DARWIN
- if ( useMacCtrl )
- {
- mAcceleratorMask |= MASK_MAC_CONTROL;
- }
-#endif // LL_DARWIN
- mAcceleratorMask |= MASK_CONTROL;
- }
- if (shortcut.find("alt") != shortcut.npos)
- {
- mAcceleratorMask |= MASK_ALT;
- }
- if (shortcut.find("shift") != shortcut.npos)
- {
- mAcceleratorMask |= MASK_SHIFT;
- }
- S32 pipe_pos = shortcut.rfind("|");
- std::string key_str = shortcut.substr(pipe_pos+1);
-
- LLKeyboard::keyFromString(key_str, &mAcceleratorKey);
-
- LL_DEBUGS("HotKeys") << "Process short cut key: shortcut: " << shortcut
- << ", key str: " << key_str
- << ", accelerator mask: " << mAcceleratorMask
- << ", accelerator key: " << mAcceleratorKey
- << LL_ENDL;
-}
-
-//virtual
-void LLMenuItemGL::setValue(const LLSD& value)
-{
- setLabel(value.asString());
-}
-
-//virtual
-LLSD LLMenuItemGL::getValue() const
-{
- return getLabel();
-}
-
-//virtual
-bool LLMenuItemGL::hasAccelerator(const KEY &key, const MASK &mask) const
-{
- return (mAcceleratorKey == key) && (mAcceleratorMask == mask);
-}
-
-//virtual
-bool LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask)
-{
- if( getEnabled() && (!gKeyboard->getKeyRepeated(key) || mAllowKeyRepeat) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) )
- {
- onCommit();
- return true;
- }
- return false;
-}
-
-bool LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask)
-{
- getWindow()->setCursor(UI_CURSOR_ARROW);
- return true;
-}
-
-//virtual
-bool LLMenuItemGL::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- return LLUICtrl::handleRightMouseDown(x,y,mask);
-}
-
-void LLMenuItemGL::onMouseEnter(S32 x, S32 y, MASK mask)
-{
- setHover(true);
- LLUICtrl::onMouseEnter(x,y,mask);
-}
-
-void LLMenuItemGL::onMouseLeave(S32 x, S32 y, MASK mask)
-{
- setHover(false);
- LLUICtrl::onMouseLeave(x,y,mask);
-}
-
-//virtual
-bool LLMenuItemGL::handleRightMouseUp(S32 x, S32 y, MASK mask)
-{
- // If this event came from a right-click context menu spawn,
- // process as a left-click to allow menu items to be hit
- if (LLMenuHolderGL::sContextMenuSpawnPos.mX != S32_MAX
- || LLMenuHolderGL::sContextMenuSpawnPos.mY != S32_MAX)
- {
- bool handled = handleMouseUp(x, y, mask);
- return handled;
- }
- return LLUICtrl::handleRightMouseUp(x,y,mask);
-}
-
-// This function checks to see if the accelerator key is already in use;
-// if not, it will be added to the list
-bool LLMenuItemGL::addToAcceleratorList(std::list <LLMenuKeyboardBinding*> *listp)
-{
- LLMenuKeyboardBinding *accelerator = NULL;
-
- if (mAcceleratorKey != KEY_NONE)
- {
- std::list<LLMenuKeyboardBinding*>::iterator list_it;
- for (list_it = listp->begin(); list_it != listp->end(); ++list_it)
- {
- accelerator = *list_it;
- if ((accelerator->mKey == mAcceleratorKey) && (accelerator->mMask == (mAcceleratorMask & MASK_NORMALKEYS)))
- {
-
- // *NOTE: get calling code to throw up warning or route
- // warning messages back to app-provided output
- // std::string warning;
- // warning.append("Duplicate key binding <");
- // appendAcceleratorString( warning );
- // warning.append("> for menu items:\n ");
- // warning.append(accelerator->mName);
- // warning.append("\n ");
- // warning.append(mLabel);
-
- // LL_WARNS() << warning << LL_ENDL;
- // LLAlertDialog::modalAlert(warning);
- return false;
- }
- }
- if (!accelerator)
- {
- accelerator = new LLMenuKeyboardBinding;
- if (accelerator)
- {
- accelerator->mKey = mAcceleratorKey;
- accelerator->mMask = (mAcceleratorMask & MASK_NORMALKEYS);
-// accelerator->mName = mLabel;
- }
- listp->push_back(accelerator);//addData(accelerator);
- }
- }
- return true;
-}
-
-// This function appends the character string representation of
-// the current accelerator key and mask to the provided string.
-void LLMenuItemGL::appendAcceleratorString( std::string& st ) const
-{
- st = LLKeyboard::stringFromAccelerator( mAcceleratorMask, mAcceleratorKey );
- LL_DEBUGS("HotKeys") << "appendAcceleratorString: " << st << LL_ENDL;
-}
-
-void LLMenuItemGL::setJumpKey(KEY key)
-{
- mJumpKey = LLStringOps::toUpper((char)key);
-}
-
-
-// virtual
-U32 LLMenuItemGL::getNominalHeight( void ) const
-{
- return mFont->getLineHeight() + MENU_ITEM_PADDING;
-}
-
-//virtual
-void LLMenuItemGL::setBriefItem(bool brief)
-{
- mBriefItem = brief;
-}
-
-//virtual
-bool LLMenuItemGL::isBriefItem() const
-{
- return mBriefItem;
-}
-
-// Get the parent menu for this item
-LLMenuGL* LLMenuItemGL::getMenu() const
-{
- return (LLMenuGL*) getParent();
-}
-
-
-// getNominalWidth() - returns the normal width of this control in
-// pixels - this is used for calculating the widest item, as well as
-// for horizontal arrangement.
-U32 LLMenuItemGL::getNominalWidth( void ) const
-{
- U32 width;
-
- if (mBriefItem)
- {
- width = BRIEF_PAD_PIXELS;
- }
- else
- {
- width = PLAIN_PAD_PIXELS;
- }
-
- if( KEY_NONE != mAcceleratorKey )
- {
- width += getMenu()->getShortcutPad();
- std::string temp;
- appendAcceleratorString( temp );
- width += mFont->getWidth( temp );
- }
- width += mFont->getWidth( mLabel.getWString().c_str() );
- return width;
-}
-
-// called to rebuild the draw label
-void LLMenuItemGL::buildDrawLabel( void )
-{
- mDrawAccelLabel.clear();
- std::string st = mDrawAccelLabel.getString();
- appendAcceleratorString( st );
- mDrawAccelLabel = st;
-}
-
-void LLMenuItemGL::onCommit( void )
-{
- // Check torn-off status to allow left-arrow keyboard navigation back
- // to parent menu.
- // Also, don't hide if item triggered by keyboard shortcut (and hence
- // parent not visible).
- if (!getMenu()->getTornOff()
- && getMenu()->getVisible())
- {
- LLMenuGL::sMenuContainer->hideMenus();
- }
-
- LLUICtrl::onCommit();
-}
-
-// set the hover status (called by it's menu)
- void LLMenuItemGL::setHighlight( bool highlight )
-{
- if (highlight)
- {
- getMenu()->clearHoverItem();
- }
-
- if (mHighlight != highlight)
- {
- dirtyRect();
- }
-
- mHighlight = highlight;
-}
-
-
-bool LLMenuItemGL::handleKeyHere( KEY key, MASK mask )
-{
- if (getHighlight() &&
- getMenu()->isOpen())
- {
- if (key == KEY_UP)
- {
- // switch to keyboard navigation mode
- LLMenuGL::setKeyboardMode(true);
-
- getMenu()->highlightPrevItem(this);
- return true;
- }
- else if (key == KEY_DOWN)
- {
- // switch to keyboard navigation mode
- LLMenuGL::setKeyboardMode(true);
-
- getMenu()->highlightNextItem(this);
- return true;
- }
- else if (key == KEY_RETURN && mask == MASK_NONE)
- {
- // switch to keyboard navigation mode
- LLMenuGL::setKeyboardMode(true);
-
- onCommit();
- return true;
- }
- }
-
- return false;
-}
-
-bool LLMenuItemGL::handleMouseUp( S32 x, S32 y, MASK mask)
-{
- // switch to mouse navigation mode
- LLMenuGL::setKeyboardMode(false);
-
- onCommit();
- make_ui_sound("UISndClickRelease");
- return LLView::handleMouseUp(x, y, mask);
-}
-
-bool LLMenuItemGL::handleMouseDown( S32 x, S32 y, MASK mask)
-{
- // switch to mouse navigation mode
- LLMenuGL::setKeyboardMode(false);
-
- setHighlight(true);
- return LLView::handleMouseDown(x, y, mask);
-}
-
-bool LLMenuItemGL::handleScrollWheel( S32 x, S32 y, S32 clicks )
-{
- // If the menu is scrollable let it handle the wheel event.
- return !getMenu()->isScrollable();
-}
-
-void LLMenuItemGL::draw( void )
-{
- // *FIX: This can be optimized by using switches. Want to avoid
- // that until the functionality is finalized.
-
- // HACK: Brief items don't highlight. Pie menu takes care of it. JC
- // let disabled items be highlighted, just don't draw them as such
- if( getEnabled() && getHighlight() && !mBriefItem)
- {
- gGL.color4fv( mHighlightBackground.get().mV );
-
- gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 );
- }
-
- LLColor4 color;
-
- if ( getEnabled() && getHighlight() )
- {
- color = mHighlightForeground.get();
- }
- else if( getEnabled() && !mDrawTextDisabled )
- {
- color = mEnabledColor.get();
- }
- else
- {
- color = mDisabledColor.get();
- }
-
- // Highlight if needed
- if( ll::ui::SearchableControl::getHighlighted() )
- color = ll::ui::SearchableControl::getHighlightColor();
-
- // Draw the text on top.
- if (mBriefItem)
- {
- mFont->render( mLabel, 0, BRIEF_PAD_PIXELS / 2, 0, color,
- LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL);
- }
- else
- {
- if( !mDrawBoolLabel.empty() )
- {
- mFont->render( mDrawBoolLabel.getWString(), 0, (F32)LEFT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), 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), 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), 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), color,
- LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false );
- }
- }
-
- // underline "jump" key only when keyboard navigation has been initiated
- if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode())
- {
- std::string upper_case_label = mLabel.getString();
- LLStringUtil::toUpper(upper_case_label);
- std::string::size_type offset = upper_case_label.find(mJumpKey);
- if (offset != std::string::npos)
- {
- S32 x_begin = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset);
- S32 x_end = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset + 1);
- gl_line_2d(x_begin, (MENU_ITEM_PADDING / 2) + 1, x_end, (MENU_ITEM_PADDING / 2) + 1);
- }
- }
-}
-
-bool LLMenuItemGL::setLabelArg( const std::string& key, const LLStringExplicit& text )
-{
- mLabel.setArg(key, text);
- return true;
-}
-
-void LLMenuItemGL::onVisibilityChange(bool new_visibility)
-{
- if (getMenu())
- {
- getMenu()->needsArrange();
- }
- LLView::onVisibilityChange(new_visibility);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLMenuItemSeparatorGL
-//
-// This class represents a separator.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-LLMenuItemSeparatorGL::Params::Params()
- : on_visible("on_visible")
-{
-}
-
-LLMenuItemSeparatorGL::LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p) :
- LLMenuItemGL( p )
-{
- if (p.on_visible.isProvided())
- {
- mVisibleSignal.connect(initEnableCallback(p.on_visible));
- }
-}
-
-//virtual
-U32 LLMenuItemSeparatorGL::getNominalHeight( void ) const
-{
- return SEPARATOR_HEIGHT_PIXELS;
-}
-
-void LLMenuItemSeparatorGL::draw( void )
-{
- gGL.color4fv( mDisabledColor.get().mV );
- const S32 y = getRect().getHeight() / 2;
- const S32 PAD = 6;
- gl_line_2d( PAD, y, getRect().getWidth() - PAD, y );
-}
-
-void LLMenuItemSeparatorGL::buildDrawLabel( void )
-{
- if (mVisibleSignal.num_slots() > 0)
- {
- bool visible = mVisibleSignal(this, LLSD());
- setVisible(visible);
- }
-}
-
-bool LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- LLMenuGL* parent_menu = getMenu();
- if (y > getRect().getHeight() / 2)
- {
- // 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->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->getVisible() && next_menu_item->getEnabled()) ? next_menu_item->handleMouseDown(x, 0, mask) : false;
- }
-}
-
-bool LLMenuItemSeparatorGL::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- LLMenuGL* parent_menu = getMenu();
- if (y > getRect().getHeight() / 2)
- {
- LLView* prev_menu_item = parent_menu->findNextSibling(this);
- 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->getVisible() && next_menu_item->getEnabled()) ? next_menu_item->handleMouseUp(x, 0, mask) : false;
- }
-}
-
-bool LLMenuItemSeparatorGL::handleHover(S32 x, S32 y, MASK mask)
-{
- LLMenuGL* parent_menu = getMenu();
- if (y > getRect().getHeight() / 2)
- {
- parent_menu->highlightPrevItem(this, false);
- return false;
- }
- else
- {
- parent_menu->highlightNextItem(this, false);
- return false;
- }
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLMenuItemVerticalSeparatorGL
-//
-// This class represents a vertical separator.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-class LLMenuItemVerticalSeparatorGL
-: public LLMenuItemSeparatorGL
-{
-public:
- LLMenuItemVerticalSeparatorGL( void );
-
- virtual bool handleMouseDown(S32 x, S32 y, MASK mask) { return false; }
-};
-
-LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL( void )
-{
- setLabel( VERTICAL_SEPARATOR_LABEL );
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLMenuItemTearOffGL
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-LLMenuItemTearOffGL::LLMenuItemTearOffGL(const LLMenuItemTearOffGL::Params& p)
-: LLMenuItemGL(p)
-{
-}
-
-// Returns the first floater ancestor if there is one
-LLFloater* LLMenuItemTearOffGL::getParentFloater()
-{
- LLView* parent_view = getMenu();
-
- while (parent_view)
- {
- if (dynamic_cast<LLFloater*>(parent_view))
- {
- return dynamic_cast<LLFloater*>(parent_view);
- }
-
- bool parent_is_menu = dynamic_cast<LLMenuGL*>(parent_view) && !dynamic_cast<LLMenuBarGL*>(parent_view);
-
- if (parent_is_menu)
- {
- // use menu parent
- parent_view = dynamic_cast<LLMenuGL*>(parent_view)->getParentMenuItem();
- }
- else
- {
- // just use regular view parent
- parent_view = parent_view->getParent();
- }
- }
-
- return NULL;
-}
-
-void LLMenuItemTearOffGL::onCommit()
-{
- if (getMenu()->getTornOff())
- {
- LLTearOffMenu * torn_off_menu = dynamic_cast<LLTearOffMenu*>(getMenu()->getParent());
- if (torn_off_menu)
- {
- torn_off_menu->closeFloater();
- }
- }
- else
- {
- // transfer keyboard focus and highlight to first real item in list
- if (getHighlight())
- {
- getMenu()->highlightNextItem(this);
- }
-
- getMenu()->needsArrange();
-
- LLFloater* parent_floater = getParentFloater();
- LLFloater* tear_off_menu = LLTearOffMenu::create(getMenu());
-
- if (tear_off_menu)
- {
- if (parent_floater)
- {
- parent_floater->addDependentFloater(tear_off_menu, false);
- }
-
- // give focus to torn off menu because it will have
- // been taken away when parent menu closes
- tear_off_menu->setFocus(true);
- }
- }
- LLMenuItemGL::onCommit();
-}
-
-void LLMenuItemTearOffGL::draw()
-{
- // disabled items can be highlighted, but shouldn't render as such
- if( getEnabled() && getHighlight() && !isBriefItem())
- {
- gGL.color4fv( mHighlightBackground.get().mV );
- gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 );
- }
-
- if (getEnabled())
- {
- gGL.color4fv( mEnabledColor.get().mV );
- }
- else
- {
- gGL.color4fv( mDisabledColor.get().mV );
- }
- const S32 y = getRect().getHeight() / 3;
- const S32 PAD = 6;
- gl_line_2d( PAD, y, getRect().getWidth() - PAD, y );
- gl_line_2d( PAD, y * 2, getRect().getWidth() - PAD, y * 2 );
-}
-
-U32 LLMenuItemTearOffGL::getNominalHeight( void ) const
-{
- return TEAROFF_SEPARATOR_HEIGHT_PIXELS;
-}
-
-///============================================================================
-/// Class LLMenuItemCallGL
-///============================================================================
-
-LLMenuItemCallGL::LLMenuItemCallGL(const LLMenuItemCallGL::Params& p)
-: LLMenuItemGL(p)
-{
-}
-
-void LLMenuItemCallGL::initFromParams(const Params& p)
-{
- if (p.on_visible.isProvided())
- {
- mVisibleSignal.connect(initEnableCallback(p.on_visible));
- }
- if (p.on_enable.isProvided())
- {
- setEnableCallback(initEnableCallback(p.on_enable));
- // Set the enabled control variable (for backwards compatability)
- if (p.on_enable.control_name.isProvided() && !p.on_enable.control_name().empty())
- {
- LLControlVariable* control = findControl(p.on_enable.control_name());
- if (control)
- {
- setEnabledControlVariable(control);
- }
- else
- {
- LL_WARNS() << "Failed to assign 'enabled' control variable to menu " << getName()
- << ": control " << p.on_enable.control_name()
- << " does not exist." << LL_ENDL;
- }
- }
- }
- if (p.on_click.isProvided())
- {
- setCommitCallback(initCommitCallback(p.on_click));
- }
-
- LLUICtrl::initFromParams(p);
-}
-
-void LLMenuItemCallGL::onCommit( void )
-{
- // RN: menu item can be deleted in callback, so beware
- getMenu()->setItemLastSelected( this );
-
- LLMenuItemGL::onCommit();
-}
-
-void LLMenuItemCallGL::updateEnabled( void )
-{
- if (mEnableSignal.num_slots() > 0)
- {
- bool enabled = mEnableSignal(this, LLSD());
- if (mEnabledControlVariable)
- {
- if (!enabled)
- {
- // callback overrides control variable; this will call setEnabled()
- mEnabledControlVariable->set(false);
- }
- }
- else
- {
- setEnabled(enabled);
- }
- }
-}
-
-void LLMenuItemCallGL::updateVisible( void )
-{
- if (mVisibleSignal.num_slots() > 0)
- {
- bool visible = mVisibleSignal(this, LLSD());
- setVisible(visible);
- }
-}
-
-void LLMenuItemCallGL::buildDrawLabel( void )
-{
- updateEnabled();
- updateVisible();
- LLMenuItemGL::buildDrawLabel();
-}
-
-bool LLMenuItemCallGL::handleKeyHere( KEY key, MASK mask )
-{
- return LLMenuItemGL::handleKeyHere(key, mask);
-}
-
-bool LLMenuItemCallGL::handleAcceleratorKey( KEY key, MASK mask )
-{
- if( (!gKeyboard->getKeyRepeated(key) || getAllowKeyRepeat()) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) )
- {
- updateEnabled();
- if (getEnabled())
- {
- onCommit();
- return true;
- }
- }
- return false;
-}
-
-// handleRightMouseUp moved into base class LLMenuItemGL so clicks are
-// handled for all menu item types
-
-///============================================================================
-/// Class LLMenuItemCheckGL
-///============================================================================
-LLMenuItemCheckGL::LLMenuItemCheckGL (const LLMenuItemCheckGL::Params& p)
-: LLMenuItemCallGL(p)
-{
-}
-
-void LLMenuItemCheckGL::initFromParams(const Params& p)
-{
- if (p.on_check.isProvided())
- {
- setCheckCallback(initEnableCallback(p.on_check));
- // Set the control name (for backwards compatability)
- if (p.on_check.control_name.isProvided() && !p.on_check.control_name().empty())
- {
- setControlName(p.on_check.control_name());
- }
- }
-
- LLMenuItemCallGL::initFromParams(p);
-}
-
-void LLMenuItemCheckGL::onCommit( void )
-{
- LLMenuItemCallGL::onCommit();
-}
-
-//virtual
-void LLMenuItemCheckGL::setValue(const LLSD& value)
-{
- LLUICtrl::setValue(value);
- if(value.asBoolean())
- {
- mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX;
- }
- else
- {
- mDrawBoolLabel.clear();
- }
-}
-
-//virtual
-LLSD LLMenuItemCheckGL::getValue() const
-{
- // Get our boolean value from the view model.
- // If we don't override this method then the implementation from
- // LLMenuItemGL will return a string. (EXT-8501)
- return LLUICtrl::getValue();
-}
-
-// called to rebuild the draw label
-void LLMenuItemCheckGL::buildDrawLabel( void )
-{
- // Note: mCheckSignal() returns true if no callbacks are set
- bool checked = mCheckSignal(this, LLSD());
- if (mControlVariable)
- {
- if (!checked)
- setControlValue(false); // callback overrides control variable; this will call setValue()
- }
- else
- {
- setValue(checked);
- }
- if(getValue().asBoolean())
- {
- mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX;
- }
- else
- {
- mDrawBoolLabel.clear();
- }
- LLMenuItemCallGL::buildDrawLabel();
-}
-
-///============================================================================
-/// Class LLMenuItemBranchGL
-///============================================================================
-LLMenuItemBranchGL::LLMenuItemBranchGL(const LLMenuItemBranchGL::Params& p)
- : LLMenuItemGL(p)
-{
- LLMenuGL* branch = p.branch;
- if (branch)
- {
- mBranchHandle = branch->getHandle();
- branch->setVisible(false);
- branch->setParentMenuItem(this);
- }
-}
-
-LLMenuItemBranchGL::~LLMenuItemBranchGL()
-{
- if (mBranchHandle.get())
- {
- mBranchHandle.get()->die();
- }
-}
-
-
-
-// virtual
-LLView* LLMenuItemBranchGL::getChildView(const std::string& name, bool recurse) const
-{
- LLMenuGL* branch = getBranch();
- if (branch)
- {
- if (branch->getName() == name)
- {
- return branch;
- }
-
- // Always recurse on branches
- return branch->getChildView(name, recurse);
- }
-
- return LLView::getChildView(name, recurse);
-}
-
-LLView* LLMenuItemBranchGL::findChildView(const std::string& name, bool recurse) const
-{
- LLMenuGL* branch = getBranch();
- if (branch)
- {
- if (branch->getName() == name)
- {
- return branch;
- }
-
- // Always recurse on branches
- return branch->findChildView(name, recurse);
- }
-
- return LLView::findChildView(name, recurse);
-}
-
-// virtual
-bool LLMenuItemBranchGL::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- // switch to mouse navigation mode
- LLMenuGL::setKeyboardMode(false);
-
- onCommit();
- make_ui_sound("UISndClickRelease");
- return true;
-}
-
-bool LLMenuItemBranchGL::hasAccelerator(const KEY &key, const MASK &mask) const
-{
- return getBranch() && getBranch()->hasAccelerator(key, mask);
-}
-
-bool LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask)
-{
- return getBranch() && getBranch()->handleAcceleratorKey(key, mask);
-}
-
-// This function checks to see if the accelerator key is already in use;
-// if not, it will be added to the list
-bool LLMenuItemBranchGL::addToAcceleratorList(std::list<LLMenuKeyboardBinding*> *listp)
-{
- LLMenuGL* branch = getBranch();
- if (!branch)
- return false;
-
- U32 item_count = branch->getItemCount();
- LLMenuItemGL *item;
-
- while (item_count--)
- {
- if ((item = branch->getItem(item_count)))
- {
- return item->addToAcceleratorList(listp);
- }
- }
-
- return false;
-}
-
-
-// called to rebuild the draw label
-void LLMenuItemBranchGL::buildDrawLabel( void )
-{
- mDrawAccelLabel.clear();
- std::string st = mDrawAccelLabel;
- appendAcceleratorString( st );
- mDrawAccelLabel = st;
- mDrawBranchLabel = LLMenuGL::BRANCH_SUFFIX;
-}
-
-void LLMenuItemBranchGL::onCommit( void )
-{
- openMenu();
-
- // keyboard navigation automatically propagates highlight to sub-menu
- // to facilitate fast menu control via jump keys
- if (LLMenuGL::getKeyboardMode() && getBranch() && !getBranch()->getHighlightedItem())
- {
- getBranch()->highlightNextItem(NULL);
- }
-
- LLUICtrl::onCommit();
-}
-
-bool LLMenuItemBranchGL::handleKey(KEY key, MASK mask, bool called_from_parent)
-{
- bool handled = false;
- if (getBranch() && called_from_parent)
- {
- handled = getBranch()->handleKey(key, mask, called_from_parent);
- }
-
- if (!handled)
- {
- handled = LLMenuItemGL::handleKey(key, mask, called_from_parent);
- }
-
- return handled;
-}
-
-bool LLMenuItemBranchGL::handleUnicodeChar(llwchar uni_char, bool called_from_parent)
-{
- bool handled = false;
- if (getBranch() && called_from_parent)
- {
- handled = getBranch()->handleUnicodeChar(uni_char, true);
- }
-
- if (!handled)
- {
- handled = LLMenuItemGL::handleUnicodeChar(uni_char, called_from_parent);
- }
-
- return handled;
-}
-
-
-void LLMenuItemBranchGL::setHighlight( bool highlight )
-{
- if (highlight == getHighlight())
- return;
-
- LLMenuGL* branch = getBranch();
- if (!branch)
- return;
-
- bool auto_open = getEnabled() && (!branch->getVisible() || branch->getTornOff());
- // torn off menus don't open sub menus on hover unless they have focus
- LLFloater * menu_parent = dynamic_cast<LLFloater *>(getMenu()->getParent());
- if (getMenu()->getTornOff() && menu_parent && !menu_parent->hasFocus())
- {
- auto_open = false;
- }
- // don't auto open torn off sub-menus (need to explicitly active menu item to give them focus)
- if (branch->getTornOff())
- {
- auto_open = false;
- }
- LLMenuItemGL::setHighlight(highlight);
- if( highlight )
- {
- if(auto_open)
- {
- openMenu();
- }
- }
- else
- {
- if (branch->getTornOff())
- {
- LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent());
- if (branch_parent)
- {
- branch_parent->setFocus(false);
- }
- branch->clearHoverItem();
- }
- else
- {
- branch->setVisible( false );
- }
- }
-}
-
-void LLMenuItemBranchGL::draw()
-{
- LLMenuItemGL::draw();
- if (getBranch() && getBranch()->getVisible() && !getBranch()->getTornOff())
- {
- setHighlight(true);
- }
-}
-
-void LLMenuItemBranchGL::updateBranchParent(LLView* parentp)
-{
- if (getBranch() && getBranch()->getParent() == NULL)
- {
- // make the branch menu a sibling of my parent menu
- getBranch()->updateParent(parentp);
- }
-}
-
-void LLMenuItemBranchGL::onVisibilityChange(bool new_visibility)
-{
- if (!new_visibility && getBranch() && !getBranch()->getTornOff())
- {
- getBranch()->setVisible(false);
- }
- LLMenuItemGL::onVisibilityChange(new_visibility);
-}
-
-bool LLMenuItemBranchGL::handleKeyHere(KEY key, MASK mask)
-{
- LLMenuGL* branch = getBranch();
- if (!branch)
- return LLMenuItemGL::handleKeyHere(key, mask);
-
- // an item is highlighted, my menu is open, and I have an active sub menu or we are in
- // keyboard navigation mode
- if (getHighlight()
- && getMenu()->isOpen()
- && (isActive() || LLMenuGL::getKeyboardMode()))
- {
- if (branch->getVisible() && key == KEY_LEFT)
- {
- // switch to keyboard navigation mode
- LLMenuGL::setKeyboardMode(true);
-
- bool handled = branch->clearHoverItem();
- if (branch->getTornOff())
- {
- LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent());
- if (branch_parent)
- {
- branch_parent->setFocus(false);
- }
- }
- if (handled && getMenu()->getTornOff())
- {
- LLFloater * menu_parent = dynamic_cast<LLFloater *>(getMenu()->getParent());
- if (menu_parent)
- {
- menu_parent->setFocus(true);
- }
- }
- return handled;
- }
-
- if (key == KEY_RIGHT && !branch->getHighlightedItem())
- {
- // switch to keyboard navigation mode
- LLMenuGL::setKeyboardMode(true);
-
- LLMenuItemGL* itemp = branch->highlightNextItem(NULL);
- if (itemp)
- {
- return true;
- }
- }
- }
- return LLMenuItemGL::handleKeyHere(key, mask);
-}
-
-//virtual
-bool LLMenuItemBranchGL::isActive() const
-{
- return isOpen() && getBranch() && getBranch()->getHighlightedItem();
-}
-
-//virtual
-bool LLMenuItemBranchGL::isOpen() const
-{
- return getBranch() && getBranch()->isOpen();
-}
-
-void LLMenuItemBranchGL::openMenu()
-{
- LLMenuGL* branch = getBranch();
- if (!branch)
- return;
-
- if (branch->getTornOff())
- {
- 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() )
- {
- // get valid rectangle for menus
- const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect();
-
- branch->arrange();
-
- LLRect branch_rect = branch->getRect();
- // calculate root-view relative position for branch menu
- S32 left = getRect().mRight;
- S32 top = getRect().mTop - getRect().mBottom;
-
- localPointToOtherView(left, top, &left, &top, branch->getParent());
-
- branch_rect.setLeftTopAndSize( left, top,
- branch_rect.getWidth(), branch_rect.getHeight() );
-
- if (branch->getCanTearOff())
- {
- branch_rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS);
- }
- branch->setRect( branch_rect );
-
- // if branch extends outside of menu region change the direction it opens in
- S32 x, y;
- S32 delta_x = 0;
- S32 delta_y = 0;
- branch->localPointToOtherView( 0, 0, &x, &y, branch->getParent() );
- if( y < menu_region_rect.mBottom )
- {
- // open upwards if menu extends past bottom
- // adjust by the height of the menu item branch since it is a submenu
- 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 )
- {
- // move sub-menu over to left side
- delta_x = llmax(-x, ( -(branch_rect.getWidth() + getRect().getWidth())));
- }
- branch->translate( delta_x, delta_y );
-
- branch->setVisible( true );
- branch->getParent()->sendChildToFront(branch);
-
- dirtyRect();
- }
-}
-
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLMenuItemBranchDownGL
-//
-// The LLMenuItemBranchDownGL represents a menu item that has a
-// sub-menu. This is used to make menu bar menus.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-class LLMenuItemBranchDownGL : public LLMenuItemBranchGL
-{
-protected:
-
-public:
- LLMenuItemBranchDownGL( const Params& );
-
- // returns the normal width of this control in pixels - this is
- // used for calculating the widest item, as well as for horizontal
- // arrangement.
- virtual U32 getNominalWidth( void ) const;
-
- // called to rebuild the draw label
- virtual void buildDrawLabel( void );
-
- // handles opening, positioning, and arranging the menu branch associated with this item
- virtual void openMenu( void );
-
- // set the hover status (called by it's menu) and if the object is
- // active. This is used for behavior transfer.
- virtual void setHighlight( bool highlight );
-
- virtual bool isActive( void ) const;
-
- // LLView functionality
- virtual bool handleMouseDown( S32 x, S32 y, MASK mask );
- virtual bool handleMouseUp( S32 x, S32 y, MASK mask );
- virtual void draw( void );
- virtual bool handleKeyHere(KEY key, MASK mask);
-
- virtual bool handleAcceleratorKey(KEY key, MASK mask);
-
- virtual void onFocusLost();
- virtual void setFocus(bool b);
-};
-
-LLMenuItemBranchDownGL::LLMenuItemBranchDownGL( const Params& p) :
- LLMenuItemBranchGL(p)
-{
-}
-
-// returns the normal width of this control in pixels - this is used
-// for calculating the widest item, as well as for horizontal
-// arrangement.
-U32 LLMenuItemBranchDownGL::getNominalWidth( void ) const
-{
- U32 width = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS;
- width += getFont()->getWidth( mLabel.getWString().c_str() );
- return width;
-}
-
-// called to rebuild the draw label
-void LLMenuItemBranchDownGL::buildDrawLabel( void )
-{
- mDrawAccelLabel.clear();
- std::string st = mDrawAccelLabel;
- appendAcceleratorString( st );
- mDrawAccelLabel = st;
-}
-
-void LLMenuItemBranchDownGL::openMenu( void )
-{
- LLMenuGL* branch = getBranch();
- if( branch->getVisible() && !branch->getTornOff() )
- {
- branch->setVisible( false );
- }
- else
- {
- if (branch->getTornOff())
- {
- LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent());
- if (branch_parent)
- {
- gFloaterView->bringToFront(branch_parent);
- }
- }
- else
- {
- // We're showing the drop-down menu, so patch up its labels/rects
- branch->arrange();
-
- LLRect rect = branch->getRect();
- S32 left = 0;
- S32 top = getRect().mBottom;
- localPointToOtherView(left, top, &left, &top, branch->getParent());
-
- rect.setLeftTopAndSize( left, top,
- rect.getWidth(), rect.getHeight() );
- branch->setRect( rect );
- S32 x = 0;
- S32 y = 0;
- branch->localPointToScreen( 0, 0, &x, &y );
- S32 delta_x = 0;
-
- LLCoordScreen window_size;
- LLWindow* windowp = getWindow();
- windowp->getSize(&window_size);
-
- S32 window_width = window_size.mX;
- if( x > window_width - rect.getWidth() )
- {
- delta_x = (window_width - rect.getWidth()) - x;
- }
- branch->translate( delta_x, 0 );
-
- setHighlight(true);
- branch->setVisible( true );
- branch->getParent()->sendChildToFront(branch);
- }
- }
-}
-
-// set the hover status (called by it's menu)
-void LLMenuItemBranchDownGL::setHighlight( bool highlight )
-{
- if (highlight == getHighlight())
- return;
-
- //NOTE: Purposely calling all the way to the base to bypass auto-open.
- LLMenuItemGL::setHighlight(highlight);
-
- LLMenuGL* branch = getBranch();
- if (!branch)
- return;
-
- if( !highlight)
- {
- if (branch->getTornOff())
- {
- LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent());
- if (branch_parent)
- {
- branch_parent->setFocus(false);
- }
- branch->clearHoverItem();
- }
- else
- {
- branch->setVisible( false );
- }
- }
-}
-
-bool LLMenuItemBranchDownGL::isActive() const
-{
- // for top level menus, being open is sufficient to be considered
- // active, because clicking on them with the mouse will open
- // them, without moving keyboard focus to them
- return isOpen();
-}
-
-bool LLMenuItemBranchDownGL::handleMouseDown( S32 x, S32 y, MASK mask )
-{
- // switch to mouse control mode
- LLMenuGL::setKeyboardMode(false);
-
- if (getVisible() && isOpen())
- {
- LLMenuGL::sMenuContainer->hideMenus();
- }
- else
- {
- onCommit();
- }
-
- make_ui_sound("UISndClick");
- return true;
-}
-
-bool LLMenuItemBranchDownGL::handleMouseUp( S32 x, S32 y, MASK mask )
-{
- return true;
-}
-
-
-bool LLMenuItemBranchDownGL::handleAcceleratorKey(KEY key, MASK mask)
-{
- bool branch_visible = getBranch()->getVisible();
- bool handled = getBranch()->handleAcceleratorKey(key, mask);
- if (handled && !branch_visible && isInVisibleChain())
- {
- // flash this menu entry because we triggered an invisible menu item
- LLMenuHolderGL::setActivatedItem(this);
- }
-
- return handled;
-}
-void LLMenuItemBranchDownGL::onFocusLost()
-{
- // needed for tab-based selection
- LLMenuItemBranchGL::onFocusLost();
- LLMenuGL::setKeyboardMode(false);
- setHighlight(false);
-}
-
-void LLMenuItemBranchDownGL::setFocus(bool b)
-{
- // needed for tab-based selection
- LLMenuItemBranchGL::setFocus(b);
- LLMenuGL::setKeyboardMode(b);
- setHighlight(b);
-}
-
-bool LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask)
-{
- bool menu_open = getBranch()->getVisible();
- // don't do keyboard navigation of top-level menus unless in keyboard mode, or menu expanded
- if (getHighlight() && getMenu()->isOpen() && (isActive() || LLMenuGL::getKeyboardMode()))
- {
- if (key == KEY_LEFT)
- {
- // switch to keyboard navigation mode
- LLMenuGL::setKeyboardMode(true);
-
- LLMenuItemGL* itemp = getMenu()->highlightPrevItem(this);
- // open new menu only if previous menu was open
- if (itemp && itemp->getEnabled() && menu_open)
- {
- itemp->onCommit();
- }
-
- return true;
- }
- else if (key == KEY_RIGHT)
- {
- // switch to keyboard navigation mode
- LLMenuGL::setKeyboardMode(true);
-
- LLMenuItemGL* itemp = getMenu()->highlightNextItem(this);
- // open new menu only if previous menu was open
- if (itemp && itemp->getEnabled() && menu_open)
- {
- itemp->onCommit();
- }
-
- return true;
- }
- else if (key == KEY_DOWN)
- {
- // switch to keyboard navigation mode
- LLMenuGL::setKeyboardMode(true);
-
- if (!isActive())
- {
- onCommit();
- }
- getBranch()->highlightNextItem(NULL);
- return true;
- }
- else if (key == KEY_UP)
- {
- // switch to keyboard navigation mode
- LLMenuGL::setKeyboardMode(true);
-
- if (!isActive())
- {
- onCommit();
- }
- getBranch()->highlightPrevItem(NULL);
- return true;
- }
- }
-
- return false;
-}
-
-void LLMenuItemBranchDownGL::draw( void )
-{
- //FIXME: try removing this
- if (getBranch()->getVisible() && !getBranch()->getTornOff())
- {
- setHighlight(true);
- }
-
- if( getHighlight() )
- {
- gGL.color4fv( mHighlightBackground.get().mV );
- gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 );
- }
-
- LLColor4 color;
- if (getHighlight())
- {
- color = mHighlightForeground.get();
- }
- else if( getEnabled() )
- {
- color = mEnabledColor.get();
- }
- else
- {
- color = mDisabledColor.get();
- }
- getFont()->render( mLabel.getWString(), 0, (F32)getRect().getWidth() / 2.f, (F32)LABEL_BOTTOM_PAD_PIXELS, color,
- LLFontGL::HCENTER, LLFontGL::BOTTOM, LLFontGL::NORMAL);
-
-
- // underline navigation key only when keyboard navigation has been initiated
- if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode())
- {
- std::string upper_case_label = mLabel.getString();
- LLStringUtil::toUpper(upper_case_label);
- std::string::size_type offset = upper_case_label.find(getJumpKey());
- if (offset != std::string::npos)
- {
- 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);
- }
- }
-}
-
-
-class LLMenuScrollItem : public LLMenuItemCallGL
-{
-public:
- enum EArrowType
- {
- ARROW_DOWN,
- ARROW_UP
- };
- struct ArrowTypes : public LLInitParam::TypeValuesHelper<EArrowType, ArrowTypes>
- {
- static void declareValues()
- {
- declare("up", ARROW_UP);
- declare("down", ARROW_DOWN);
- }
- };
-
- struct Params : public LLInitParam::Block<Params, LLMenuItemCallGL::Params>
- {
- Optional<EArrowType, ArrowTypes> arrow_type;
- Optional<CommitCallbackParam> scroll_callback;
- };
-
-protected:
- LLMenuScrollItem(const Params&);
- friend class LLUICtrlFactory;
-
-public:
- /*virtual*/ void draw();
- /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent);
- /*virtual*/ void setEnabled(bool enabled);
- virtual void onCommit( void );
-
-private:
- LLButton* mArrowBtn;
-};
-
-LLMenuScrollItem::LLMenuScrollItem(const Params& p)
-: LLMenuItemCallGL(p)
-{
- std::string icon;
- if (p.arrow_type.isProvided() && p.arrow_type == ARROW_UP)
- {
- icon = "arrow_up.tga";
- }
- else
- {
- icon = "arrow_down.tga";
- }
-
- LLButton::Params bparams;
-
- // Disabled the Return key handling by LLMenuScrollItem instead of
- // passing the key press to the currently selected menu item. See STORM-385.
- bparams.commit_on_return(false);
- bparams.mouse_opaque(true);
- bparams.scale_image(false);
- bparams.click_callback(p.scroll_callback);
- bparams.mouse_held_callback(p.scroll_callback);
- bparams.follows.flags(FOLLOWS_ALL);
- std::string background = "transparent.j2c";
- bparams.image_unselected.name(background);
- bparams.image_disabled.name(background);
- bparams.image_selected.name(background);
- bparams.image_hover_selected.name(background);
- bparams.image_disabled_selected.name(background);
- bparams.image_hover_unselected.name(background);
- bparams.image_overlay.name(icon);
-
- mArrowBtn = LLUICtrlFactory::create<LLButton>(bparams);
- addChild(mArrowBtn);
-}
-
-/*virtual*/
-void LLMenuScrollItem::draw()
-{
- LLUICtrl::draw();
-}
-
-/*virtual*/
-void LLMenuScrollItem::reshape(S32 width, S32 height, bool called_from_parent)
-{
- mArrowBtn->reshape(width, height, called_from_parent);
- LLView::reshape(width, height, called_from_parent);
-}
-
-/*virtual*/
-void LLMenuScrollItem::setEnabled(bool enabled)
-{
- mArrowBtn->setEnabled(enabled);
- LLView::setEnabled(enabled);
-}
-
-void LLMenuScrollItem::onCommit( void )
-{
- LLUICtrl::onCommit();
-}
-
-///============================================================================
-/// Class LLMenuGL
-///============================================================================
-
-LLMenuGL::LLMenuGL(const LLMenuGL::Params& p)
-: LLUICtrl(p),
- mBackgroundColor( p.bg_color() ),
- mBgVisible( p.bg_visible ),
- mDropShadowed( p.drop_shadow ),
- mHasSelection(false),
- mHorizontalLayout( p.horizontal_layout ),
- mScrollable(mHorizontalLayout ? false : p.scrollable), // Scrolling is supported only for vertical layout
- mMaxScrollableItems(p.max_scrollable_items),
- mPreferredWidth(p.preferred_width),
- mKeepFixedSize( p.keep_fixed_size ),
- mLabel (p.label),
- mLastMouseX(0),
- mLastMouseY(0),
- mMouseVelX(0),
- mMouseVelY(0),
- mTornOff(false),
- mTearOffItem(NULL),
- mSpilloverBranch(NULL),
- mFirstVisibleItem(NULL),
- mArrowUpItem(NULL),
- mArrowDownItem(NULL),
- mSpilloverMenu(NULL),
- mJumpKey(p.jump_key),
- mCreateJumpKeys(p.create_jump_keys),
- mNeedsArrange(false),
- mAlwaysShowMenu(false),
- mResetScrollPositionOnShow(true),
- mShortcutPad(p.shortcut_pad),
- mFont(p.font)
-{
- typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
- boost::char_separator<char> sep("_");
- tokenizer tokens(p.label(), sep);
- tokenizer::iterator token_iter;
-
- S32 token_count = 0;
- std::string new_menu_label;
- for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
- {
- new_menu_label += (*token_iter);
- if (token_count > 0)
- {
- setJumpKey((*token_iter).c_str()[0]);
- }
- ++token_count;
- }
- setLabel(new_menu_label);
-
- mFadeTimer.stop();
-}
-
-void LLMenuGL::initFromParams(const LLMenuGL::Params& p)
-{
- LLUICtrl::initFromParams(p);
- setCanTearOff(p.can_tear_off);
-}
-
-// Destroys the object
-LLMenuGL::~LLMenuGL( void )
-{
- // delete the branch, as it might not be in view hierarchy
- // leave the menu, because it is always in view hierarchy
- delete mSpilloverBranch;
- mJumpKeys.clear();
-}
-
-void LLMenuGL::setCanTearOff(bool tear_off)
-{
- if (tear_off && mTearOffItem == NULL)
- {
- LLMenuItemTearOffGL::Params p;
- mTearOffItem = LLUICtrlFactory::create<LLMenuItemTearOffGL>(p);
- addChild(mTearOffItem);
- }
- else if (!tear_off && mTearOffItem != NULL)
- {
- mItems.remove(mTearOffItem);
- removeChild(mTearOffItem);
- delete mTearOffItem;
- mTearOffItem = NULL;
- needsArrange();
- }
-}
-
-bool LLMenuGL::addChild(LLView* view, S32 tab_group)
-{
- 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)
- {
- return append(item);
- }
-
- LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view);
- if (menup)
- {
- return appendMenu(menup);
- }
-
- return false;
-}
-
-
-void LLMenuGL::deleteAllChildren()
-{
- mItems.clear();
- LLUICtrl::deleteAllChildren();
-}
-
-void LLMenuGL::removeChild( LLView* ctrl)
-{
- // previously a dynamic_cast with if statement to check validity
- // unfortunately removeChild is called by ~LLView, and at that point the
- // object being deleted is no longer a LLMenuItemGL so a dynamic_cast will fail
- LLMenuItemGL* itemp = static_cast<LLMenuItemGL*>(ctrl);
-
- item_list_t::iterator found_it = std::find(mItems.begin(), mItems.end(), (itemp));
- if (found_it != mItems.end())
- {
- mItems.erase(found_it);
- }
-
- return LLUICtrl::removeChild(ctrl);
-}
-
-bool LLMenuGL::postBuild()
-{
- createJumpKeys();
- return LLUICtrl::postBuild();
-}
-
-// are we the childmost active menu and hence our jump keys should be enabled?
-// or are we a free-standing torn-off menu (which uses jump keys too)
-bool LLMenuGL::jumpKeysActive()
-{
- LLMenuItemGL* highlighted_item = getHighlightedItem();
- bool active = getVisible() && getEnabled();
-
- if (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;
-}
-
-bool LLMenuGL::isOpen()
-{
- if (getTornOff())
- {
- LLMenuItemGL* itemp = getHighlightedItem();
- // if we have an open sub-menu, then we are considered part of
- // the open menu chain even if we don't have focus
- if (itemp && itemp->isOpen())
- {
- return true;
- }
- // otherwise we are only active if we have keyboard focus
- LLFloater * parent = dynamic_cast<LLFloater *>(getParent());
- if (parent)
- {
- return parent->hasFocus();
- }
- return false;
- }
- else
- {
- // normally, menus are hidden as soon as the user focuses
- // on another menu, so just use the visibility criterion
- return getVisible();
- }
-}
-
-
-
-bool LLMenuGL::scrollItems(EScrollingDirection direction)
-{
- // Slowing down items scrolling when arrow button is held
- if (mScrollItemsTimer.hasExpired() && NULL != mFirstVisibleItem)
- {
- mScrollItemsTimer.setTimerExpirySec(.033f);
- }
- else
- {
- return false;
- }
-
- switch (direction)
- {
- case SD_UP:
- {
- item_list_t::iterator cur_item_iter;
- item_list_t::iterator prev_item_iter;
- for (cur_item_iter = mItems.begin(), prev_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++)
- {
- if( (*cur_item_iter) == mFirstVisibleItem)
- {
- break;
- }
- if ((*cur_item_iter)->getVisible())
- {
- prev_item_iter = cur_item_iter;
- }
- }
-
- if ((*prev_item_iter)->getVisible())
- {
- mFirstVisibleItem = *prev_item_iter;
- }
- break;
- }
- case SD_DOWN:
- {
- if (NULL == mFirstVisibleItem)
- {
- mFirstVisibleItem = *mItems.begin();
- }
-
- item_list_t::iterator cur_item_iter;
-
- for (cur_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++)
- {
- if( (*cur_item_iter) == mFirstVisibleItem)
- {
- break;
- }
- }
-
- item_list_t::iterator next_item_iter;
-
- if (cur_item_iter != mItems.end())
- {
- for (next_item_iter = ++cur_item_iter; next_item_iter != mItems.end(); next_item_iter++)
- {
- if( (*next_item_iter)->getVisible())
- {
- break;
- }
- }
-
- if (next_item_iter != mItems.end() &&
- (*next_item_iter)->getVisible())
- {
- mFirstVisibleItem = *next_item_iter;
- }
- }
- break;
- }
- case SD_BEGIN:
- {
- mFirstVisibleItem = *mItems.begin();
- break;
- }
- case SD_END:
- {
- 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, scrollable_items);
- mFirstVisibleItem = *first_visible_item_iter;
- break;
- }
- default:
- LL_WARNS() << "Unknown scrolling direction: " << direction << LL_ENDL;
- }
-
- mNeedsArrange = true;
- arrangeAndClear();
-
- return true;
-}
-
-// rearrange the child rects so they fit the shape of the menu.
-void LLMenuGL::arrange( void )
-{
- // calculate the height & width, and set our rect based on that
- // information.
- const LLRect& initial_rect = getRect();
-
- U32 width = 0, height = MENU_ITEM_PADDING;
-
- cleanupSpilloverBranch();
-
- if( mItems.size() )
- {
- const LLRect menu_region_rect = LLMenuGL::sMenuContainer ? LLMenuGL::sMenuContainer->getMenuRect() : LLRect(0, S32_MAX, S32_MAX, 0);
-
- // 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 = 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 = LLFontGL::getFontSansSerif()->getLineHeight() + MENU_ITEM_PADDING;
-
- // Scrolling support
- item_list_t::iterator first_visible_item_iter;
- item_list_t::iterator first_hidden_item_iter = mItems.end();
- S32 height_before_first_visible_item = -1;
- S32 visible_items_height = 0;
- U32 scrollable_items_cnt = 0;
-
- if (mHorizontalLayout)
- {
- item_list_t::iterator item_iter;
- for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
- {
- // do first so LLMenuGLItemCall can call on_visible to determine if visible
- (*item_iter)->buildDrawLabel();
-
- if ((*item_iter)->getVisible())
- {
- if (!getTornOff()
- && *item_iter != mSpilloverBranch
- && width + (*item_iter)->getNominalWidth() > max_width - spillover_item_width)
- {
- // 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 = llmax(height, mSpilloverBranch->getNominalHeight());
- width += mSpilloverBranch->getNominalWidth();
-
- break;
- }
- else
- {
- // track our rect
- height = llmax(height, (*item_iter)->getNominalHeight());
- width += (*item_iter)->getNominalWidth();
- }
- }
- }
- }
- else
- {
- for (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)
- {
- if ((*item_iter)->getVisible())
- {
- if (!getTornOff()
- && !mScrollable
- && *item_iter != mSpilloverBranch
- && height + (*item_iter)->getNominalHeight() > max_height - spillover_item_height)
- {
- // 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)
- {
- 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() );
-
- break;
- }
- }
-
- // track our rect
- height += (*item_iter)->getNominalHeight();
- width = llmax( width, (*item_iter)->getNominalWidth() );
-
- if (mScrollable)
- {
- // Determining visible items boundaries
- if (NULL == mFirstVisibleItem)
- {
- mFirstVisibleItem = *item_iter;
- }
-
- if (*item_iter == mFirstVisibleItem)
- {
- height_before_first_visible_item = height - (*item_iter)->getNominalHeight();
- first_visible_item_iter = item_iter;
- scrollable_items_cnt = 0;
- }
-
- if (-1 != height_before_first_visible_item && 0 == visible_items_height &&
- (++scrollable_items_cnt > mMaxScrollableItems ||
- height - height_before_first_visible_item > max_height - spillover_item_height * 2 ))
- {
- first_hidden_item_iter = item_iter;
- visible_items_height = height - height_before_first_visible_item - (*item_iter)->getNominalHeight();
- scrollable_items_cnt--;
- }
- }
- }
- }
-
- if (mPreferredWidth < U32_MAX)
- width = llmin(mPreferredWidth, max_width);
-
- if (mScrollable)
- {
- S32 max_items_height = max_height - spillover_item_height * 2;
-
- if (visible_items_height == 0)
- visible_items_height = height - height_before_first_visible_item;
-
- // Fix mFirstVisibleItem value, if it doesn't allow to display all items, that can fit
- if (visible_items_height < max_items_height && scrollable_items_cnt < mMaxScrollableItems)
- {
- item_list_t::iterator tmp_iter(first_visible_item_iter);
- while (visible_items_height < max_items_height &&
- scrollable_items_cnt < mMaxScrollableItems &&
- first_visible_item_iter != mItems.begin())
- {
- if ((*first_visible_item_iter)->getVisible())
- {
- // It keeps visible item, after first_visible_item_iter
- tmp_iter = first_visible_item_iter;
- }
-
- first_visible_item_iter--;
-
- if ((*first_visible_item_iter)->getVisible())
- {
- visible_items_height += (*first_visible_item_iter)->getNominalHeight();
- height_before_first_visible_item -= (*first_visible_item_iter)->getNominalHeight();
- scrollable_items_cnt++;
- }
- }
-
- // Roll back one item, that doesn't fit
- if (visible_items_height > max_items_height)
- {
- visible_items_height -= (*first_visible_item_iter)->getNominalHeight();
- height_before_first_visible_item += (*first_visible_item_iter)->getNominalHeight();
- scrollable_items_cnt--;
- first_visible_item_iter = tmp_iter;
- }
- if (!(*first_visible_item_iter)->getVisible())
- {
- first_visible_item_iter = tmp_iter;
- }
-
- mFirstVisibleItem = *first_visible_item_iter;
- }
- }
- }
-
- S32 cur_height = (S32)llmin(max_height, height);
-
- if (mScrollable &&
- (height_before_first_visible_item > MENU_ITEM_PADDING ||
- height_before_first_visible_item + visible_items_height < (S32)height))
- {
- // Reserving 2 extra slots for arrow items
- cur_height = visible_items_height + spillover_item_height * 2;
- }
-
- setRect(LLRect(getRect().mLeft, getRect().mTop, getRect().mLeft + width, getRect().mTop - cur_height));
-
- S32 cur_width = 0;
- S32 offset = 0;
- if (mScrollable)
- {
- // No space for all items, creating arrow items
- if (height_before_first_visible_item > MENU_ITEM_PADDING ||
- height_before_first_visible_item + visible_items_height < (S32)height)
- {
- if (NULL == mArrowUpItem)
- {
- LLMenuScrollItem::Params item_params;
- item_params.name(ARROW_UP);
- item_params.arrow_type(LLMenuScrollItem::ARROW_UP);
- item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItems, this, SD_UP));
-
- mArrowUpItem = LLUICtrlFactory::create<LLMenuScrollItem>(item_params);
- LLUICtrl::addChild(mArrowUpItem);
-
- }
- if (NULL == mArrowDownItem)
- {
- LLMenuScrollItem::Params item_params;
- item_params.name(ARROW_DOWN);
- item_params.arrow_type(LLMenuScrollItem::ARROW_DOWN);
- item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItems, this, SD_DOWN));
-
- mArrowDownItem = LLUICtrlFactory::create<LLMenuScrollItem>(item_params);
- LLUICtrl::addChild(mArrowDownItem);
- }
-
- LLRect rect;
- mArrowUpItem->setRect(rect.setLeftTopAndSize( 0, cur_height, width, mArrowUpItem->getNominalHeight()));
- mArrowUpItem->setVisible(true);
- mArrowUpItem->setEnabled(height_before_first_visible_item > MENU_ITEM_PADDING);
- mArrowUpItem->reshape(width, mArrowUpItem->getNominalHeight());
- mArrowDownItem->setRect(rect.setLeftTopAndSize( 0, mArrowDownItem->getNominalHeight(), width, mArrowDownItem->getNominalHeight()));
- mArrowDownItem->setVisible(true);
- mArrowDownItem->setEnabled(height_before_first_visible_item + visible_items_height < (S32)height);
- mArrowDownItem->reshape(width, mArrowDownItem->getNominalHeight());
-
- cur_height -= mArrowUpItem->getNominalHeight();
-
- offset = menu_region_rect.mRight; // This moves items behind visible area
- }
- else
- {
- if (NULL != mArrowUpItem)
- {
- mArrowUpItem->setVisible(false);
- }
- if (NULL != mArrowDownItem)
- {
- mArrowDownItem->setVisible(false);
- }
- }
-
- }
-
- item_list_t::iterator item_iter;
- for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
- {
- if ((*item_iter)->getVisible())
- {
- if (mScrollable)
- {
- if (item_iter == first_visible_item_iter)
- {
- offset = 0;
- }
- else if (item_iter == first_hidden_item_iter)
- {
- offset = menu_region_rect.mRight; // This moves items behind visible area
- }
- }
-
- // setup item rect to hold label
- LLRect rect;
- if (mHorizontalLayout)
- {
- rect.setLeftTopAndSize( cur_width, height, (*item_iter)->getNominalWidth(), height);
- cur_width += (*item_iter)->getNominalWidth();
- }
- else
- {
- rect.setLeftTopAndSize( 0 + offset, cur_height, width, (*item_iter)->getNominalHeight());
- if (offset == 0)
- {
- cur_height -= (*item_iter)->getNominalHeight();
- }
- }
- (*item_iter)->setRect( rect );
- }
- }
-
-
- if (getTornOff())
- {
- LLTearOffMenu * torn_off_menu = dynamic_cast<LLTearOffMenu*>(getParent());
- if (torn_off_menu)
- {
- torn_off_menu->updateSize();
- }
- }
- }
- if (mKeepFixedSize)
- {
- reshape(initial_rect.getWidth(), initial_rect.getHeight());
- }
-}
-
-void LLMenuGL::arrangeAndClear( void )
-{
- if (mNeedsArrange)
- {
- arrange();
- mNeedsArrange = false;
- }
-}
-
-void LLMenuGL::createSpilloverBranch()
-{
- if (!mSpilloverBranch)
- {
- // should be NULL but delete anyway
- delete mSpilloverMenu;
- // technically, you can't tear off spillover menus, but we're passing the handle
- // along just to be safe
- LLMenuGL::Params p;
- std::string label = LLTrans::getString("More");
- p.name("More");
- p.label(label);
- p.bg_color(mBackgroundColor);
- p.bg_visible(true);
- p.can_tear_off(false);
- mSpilloverMenu = new LLMenuGL(p);
- mSpilloverMenu->updateParent(LLMenuGL::sMenuContainer);
-
- LLMenuItemBranchGL::Params branch_params;
- branch_params.name = "More";
- 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);
- }
-}
-
-void LLMenuGL::cleanupSpilloverBranch()
-{
- if (mSpilloverBranch && mSpilloverBranch->getParent() == this)
- {
- // head-recursion to propagate items back up to root menu
- mSpilloverMenu->cleanupSpilloverBranch();
-
- // pop off spillover items
- while (mSpilloverMenu->getItemCount())
- {
- LLMenuItemGL* itemp = mSpilloverMenu->getItem(0);
- mSpilloverMenu->removeChild(itemp);
- // put them at the end of our own list
- addChild(itemp);
- }
-
- // Delete the branch, and since the branch will delete the menu,
- // set the menu* to null.
- delete mSpilloverBranch;
- mSpilloverBranch = NULL;
- mSpilloverMenu = NULL;
- }
-}
-
-void LLMenuGL::createJumpKeys()
-{
- if (!mCreateJumpKeys) return;
- mCreateJumpKeys = false;
-
- mJumpKeys.clear();
-
- std::set<std::string> unique_words;
- std::set<std::string> shared_words;
-
- item_list_t::iterator item_it;
- typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
- boost::char_separator<char> sep(" ");
-
- for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
- {
- std::string uppercase_label = (*item_it)->getLabel();
- LLStringUtil::toUpper(uppercase_label);
-
- tokenizer tokens(uppercase_label, sep);
- tokenizer::iterator token_iter;
- for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
- {
- if (unique_words.find(*token_iter) != unique_words.end())
- {
- // this word exists in more than one menu instance
- shared_words.insert(*token_iter);
- }
- else
- {
- // we have a new word, keep track of it
- unique_words.insert(*token_iter);
- }
- }
- }
-
- // pre-assign specified jump keys
- for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
- {
- KEY jump_key = (*item_it)->getJumpKey();
- if(jump_key != KEY_NONE)
- {
- if (mJumpKeys.find(jump_key) == mJumpKeys.end())
- {
- mJumpKeys.insert(std::pair<KEY, LLMenuItemGL*>(jump_key, (*item_it)));
- }
- else
- {
- // this key is already spoken for,
- // so we need to reassign it below
- (*item_it)->setJumpKey(KEY_NONE);
- }
- }
- }
-
- for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
- {
- // skip over items that already have assigned jump keys
- if ((*item_it)->getJumpKey() != KEY_NONE)
- {
- continue;
- }
- std::string uppercase_label = (*item_it)->getLabel();
- LLStringUtil::toUpper(uppercase_label);
-
- tokenizer tokens(uppercase_label, sep);
- tokenizer::iterator token_iter;
-
- bool found_key = false;
- for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
- {
- std::string uppercase_word = *token_iter;
-
- // this word is not shared with other menu entries...
- if (shared_words.find(*token_iter) == shared_words.end())
- {
- S32 i;
- for(i = 0; i < (S32)uppercase_word.size(); i++)
- {
- char jump_key = uppercase_word[i];
-
- if (LLStringOps::isDigit(jump_key) || (LLStringOps::isUpper(jump_key) &&
- mJumpKeys.find(jump_key) == mJumpKeys.end()))
- {
- mJumpKeys.insert(std::pair<KEY, LLMenuItemGL*>(jump_key, (*item_it)));
- (*item_it)->setJumpKey(jump_key);
- found_key = true;
- break;
- }
- }
- }
- if (found_key)
- {
- break;
- }
- }
- }
-}
-
-// remove all items on the menu
-void LLMenuGL::empty( void )
-{
- cleanupSpilloverBranch();
-
- mItems.clear();
- mFirstVisibleItem = NULL;
- mArrowUpItem = NULL;
- mArrowDownItem = NULL;
-
- 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)
-{
- setRect(LLRect(left, getRect().mTop, getRect().mRight, bottom));
- needsArrange();
-}
-
-bool LLMenuGL::handleJumpKey(KEY key)
-{
- // must perform case-insensitive comparison, so just switch to uppercase input key
- key = toupper(key);
- navigation_key_map_t::iterator found_it = mJumpKeys.find(key);
- if(found_it != mJumpKeys.end() && found_it->second->getEnabled())
- {
- // switch to keyboard navigation mode
- LLMenuGL::setKeyboardMode(true);
-
- // force highlight to close old menus and open and sub-menus
- found_it->second->setHighlight(true);
- found_it->second->onCommit();
-
- }
- // if we are navigating the menus, we need to eat the keystroke
- // so rest of UI doesn't handle it
- return true;
-}
-
-
-// Add the menu item to this menu.
-bool LLMenuGL::append( LLMenuItemGL* item )
-{
- if (!item) return false;
- mItems.push_back( item );
- LLUICtrl::addChild(item);
- needsArrange();
- return true;
-}
-
-// add a separator to this menu
-bool LLMenuGL::addSeparator()
-{
- LLMenuItemSeparatorGL::Params p;
- LLMenuItemGL* separator = LLUICtrlFactory::create<LLMenuItemSeparatorGL>(p);
- return addChild(separator);
-}
-
-// add a menu - this will create a cascading menu
-bool LLMenuGL::appendMenu( LLMenuGL* menu )
-{
- if( menu == this )
- {
- LL_ERRS() << "** Attempt to attach menu to itself. This is certainly "
- << "a logic error." << LL_ENDL;
- }
- bool success = true;
-
- LLMenuItemBranchGL::Params p;
- p.name = menu->getName();
- p.label = menu->getLabel();
- p.branch = menu;
- p.jump_key = menu->getJumpKey();
- p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor");
- p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor");
- p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor");
- p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor");
-
- LLMenuItemBranchGL* branch = LLUICtrlFactory::create<LLMenuItemBranchGL>(p);
- success &= append( branch );
-
- // Inherit colors
- menu->setBackgroundColor( mBackgroundColor );
- menu->updateParent(LLMenuGL::sMenuContainer);
- 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);
- item_list_t::iterator item_iter;
- for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
- {
- (*item_iter)->setEnabledSubMenus( enable );
- }
-}
-
-// setItemEnabled() - pass the label and the enable flag for a menu
-// item. true will make sure it's enabled, false will disable it.
-void LLMenuGL::setItemEnabled( const std::string& name, bool enable )
-{
- item_list_t::iterator item_iter;
- for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
- {
- if( (*item_iter)->getName() == name )
- {
- (*item_iter)->setEnabled( enable );
- (*item_iter)->setEnabledSubMenus( enable );
- break;
- }
- }
-}
-
-void LLMenuGL::setItemVisible( const std::string& name, bool visible )
-{
- item_list_t::iterator item_iter;
- for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
- {
- if( (*item_iter)->getName() == name )
- {
- (*item_iter)->setVisible( visible );
- needsArrange();
- break;
- }
- }
-}
-
-
-void LLMenuGL::setItemLabel(const std::string &name, const std::string &label)
-{
- LLMenuItemGL *item = getItem(name);
-
- if (item)
- item->setLabel(label);
-}
-
-void LLMenuGL::setItemLastSelected(LLMenuItemGL* item)
-{
- if (getVisible())
- {
- LLMenuHolderGL::setActivatedItem(item);
- }
-
- // update enabled and checkmark status
- item->buildDrawLabel();
-}
-
-// Set whether drop shadowed
-void LLMenuGL::setDropShadowed( const bool shadowed )
-{
- mDropShadowed = shadowed;
-}
-
-void LLMenuGL::setTornOff(bool torn_off)
-{
- mTornOff = torn_off;
-}
-
-U32 LLMenuGL::getItemCount()
-{
- return mItems.size();
-}
-
-LLMenuItemGL* LLMenuGL::getItem(S32 number)
-{
- if (number >= 0 && number < (S32)mItems.size())
- {
- item_list_t::iterator item_iter;
- for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
- {
- if (number == 0)
- {
- return (*item_iter);
- }
- number--;
- }
- }
- return NULL;
-}
-
-LLMenuItemGL* LLMenuGL::getItem(std::string name)
-{
- item_list_t::iterator item_iter;
- for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
- {
- if ((*item_iter)->getName() == name)
- {
- return (*item_iter);
- }
- }
- return NULL;
-}
-
-LLMenuItemGL* LLMenuGL::getHighlightedItem()
-{
- item_list_t::iterator item_iter;
- for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
- {
- if ((*item_iter)->getHighlight())
- {
- return (*item_iter);
- }
- }
- return NULL;
-}
-
-LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, bool skip_disabled)
-{
- if (mItems.empty()) return NULL;
- // highlighting first item on a torn off menu is the
- // same as giving focus to it
- if (!cur_item && getTornOff())
- {
- LLFloater * parent = dynamic_cast<LLFloater *>(getParent());
- if (parent)
- {
- parent->setFocus(true);
- }
- }
-
- // Current item position in the items list
- item_list_t::iterator cur_item_iter = std::find(mItems.begin(), mItems.end(), cur_item);
-
- item_list_t::iterator next_item_iter;
- if (cur_item_iter == mItems.end())
- {
- next_item_iter = mItems.begin();
- }
- else
- {
- next_item_iter = cur_item_iter;
- next_item_iter++;
-
- // First visible item position in the items list
- item_list_t::iterator first_visible_item_iter = std::find(mItems.begin(), mItems.end(), mFirstVisibleItem);
-
- if (next_item_iter == mItems.end())
- {
- next_item_iter = mItems.begin();
-
- // If current item is the last in the list, the menu is scrolled to the beginning
- // and the first item is highlighted.
- if (mScrollable && !scrollItems(SD_BEGIN))
- {
- return NULL;
- }
- }
- // If current item is the last visible, the menu is scrolled one item down
- // and the next item is highlighted.
- else if (mScrollable &&
- (U32)std::abs(std::distance(first_visible_item_iter, next_item_iter)) >= mMaxScrollableItems)
- {
- // Call highlightNextItem() recursively only if the menu was successfully scrolled down.
- // If scroll timer hasn't expired yet the menu won't be scrolled and calling
- // highlightNextItem() will result in an endless recursion.
- if (scrollItems(SD_DOWN))
- {
- return highlightNextItem(cur_item, skip_disabled);
- }
- else
- {
- return NULL;
- }
- }
- }
-
- // when first highlighting a menu, skip over tear off menu item
- if (mTearOffItem && !cur_item)
- {
- // we know the first item is the tear off menu item
- cur_item_iter = mItems.begin();
- next_item_iter++;
- if (next_item_iter == mItems.end())
- {
- next_item_iter = mItems.begin();
- }
- }
-
- while(1)
- {
- // skip separators and disabled/invisible items
- if ((*next_item_iter)->getEnabled() && (*next_item_iter)->getVisible() && !dynamic_cast<LLMenuItemSeparatorGL*>(*next_item_iter))
- {
- if (cur_item)
- {
- cur_item->setHighlight(false);
- }
- (*next_item_iter)->setHighlight(true);
- return (*next_item_iter);
- }
-
-
- if (!skip_disabled || next_item_iter == cur_item_iter)
- {
- break;
- }
-
- next_item_iter++;
- if (next_item_iter == mItems.end())
- {
- if (cur_item_iter == mItems.end())
- {
- break;
- }
- next_item_iter = mItems.begin();
- }
- }
-
- return NULL;
-}
-
-LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, bool skip_disabled)
-{
- if (mItems.empty()) return NULL;
-
- // highlighting first item on a torn off menu is the
- // same as giving focus to it
- if (!cur_item && getTornOff())
- {
- LLFloater * parent = dynamic_cast<LLFloater *>(getParent());
- if (parent)
- {
- parent->setFocus(true);
- }
- }
-
- // Current item reverse position from the end of the list
- item_list_t::reverse_iterator cur_item_iter = std::find(mItems.rbegin(), mItems.rend(), cur_item);
-
- item_list_t::reverse_iterator prev_item_iter;
- if (cur_item_iter == mItems.rend())
- {
- prev_item_iter = mItems.rbegin();
- }
- else
- {
- prev_item_iter = cur_item_iter;
- prev_item_iter++;
-
- // First visible item reverse position in the items list
- item_list_t::reverse_iterator first_visible_item_iter = std::find(mItems.rbegin(), mItems.rend(), mFirstVisibleItem);
-
- if (prev_item_iter == mItems.rend())
- {
- prev_item_iter = mItems.rbegin();
-
- // If current item is the first in the list, the menu is scrolled to the end
- // and the last item is highlighted.
- if (mScrollable && !scrollItems(SD_END))
- {
- return NULL;
- }
- }
- // If current item is the first visible, the menu is scrolled one item up
- // and the previous item is highlighted.
- else if (mScrollable &&
- std::distance(first_visible_item_iter, cur_item_iter) <= 0)
- {
- // Call highlightNextItem() only if the menu was successfully scrolled up.
- // If scroll timer hasn't expired yet the menu won't be scrolled and calling
- // highlightNextItem() will result in an endless recursion.
- if (scrollItems(SD_UP))
- {
- return highlightPrevItem(cur_item, skip_disabled);
- }
- else
- {
- return NULL;
- }
- }
- }
-
- while(1)
- {
- // skip separators and disabled/invisible items
- if ((*prev_item_iter)->getEnabled() && (*prev_item_iter)->getVisible() && (*prev_item_iter)->getName() != SEPARATOR_NAME)
- {
- (*prev_item_iter)->setHighlight(true);
- return (*prev_item_iter);
- }
-
- if (!skip_disabled || prev_item_iter == cur_item_iter)
- {
- break;
- }
-
- prev_item_iter++;
- if (prev_item_iter == mItems.rend())
- {
- if (cur_item_iter == mItems.rend())
- {
- break;
- }
-
- prev_item_iter = mItems.rbegin();
- }
- }
-
- return NULL;
-}
-
-void LLMenuGL::buildDrawLabels()
-{
- item_list_t::iterator item_iter;
- for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
- {
- (*item_iter)->buildDrawLabel();
- }
-}
-
-void LLMenuGL::updateParent(LLView* parentp)
-{
- if (getParent())
- {
- getParent()->removeChild(this);
- }
- if (parentp)
- {
- parentp->addChild(this);
- }
- item_list_t::iterator item_iter;
- for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
- {
- (*item_iter)->updateBranchParent(parentp);
- }
-}
-
-bool LLMenuGL::hasAccelerator(const KEY &key, const MASK &mask) const
-{
- if (key == KEY_NONE)
- {
- return false;
- }
- // Note: checking this way because mAccelerators seems to be broken
- // mAccelerators probably needs to be cleaned up or fixed
- // It was used for dupplicate accelerator avoidance.
- item_list_t::const_iterator item_iter;
- for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
- {
- LLMenuItemGL* itemp = *item_iter;
- if (itemp->hasAccelerator(key, mask))
- {
- return true;
- }
- }
- return false;
-}
-
-bool LLMenuGL::handleAcceleratorKey(KEY key, MASK mask)
-{
- // don't handle if not enabled
- if(!getEnabled())
- {
- return false;
- }
-
- // Pass down even if not visible
- item_list_t::iterator item_iter;
- for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
- {
- LLMenuItemGL* itemp = *item_iter;
- if (itemp->handleAcceleratorKey(key, mask))
- {
- return true;
- }
- }
-
- return false;
-}
-
-bool LLMenuGL::handleUnicodeCharHere( llwchar uni_char )
-{
- if (jumpKeysActive())
- {
- return handleJumpKey((KEY)uni_char);
- }
- return false;
-}
-
-bool LLMenuGL::handleHover( S32 x, S32 y, MASK mask )
-{
- // leave submenu in place if slope of mouse < MAX_MOUSE_SLOPE_SUB_MENU
- bool no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0;
- S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX;
- S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY;
- LLVector2 mouse_dir((F32)mouse_delta_x, (F32)mouse_delta_y);
- mouse_dir.normVec();
- 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 = 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;
-
- // don't change menu focus unless mouse is moving or alt key is not held down
- if ((llabs(mMouseVelX) > 0 ||
- llabs(mMouseVelY) > 0) &&
- (!mHasSelection ||
- //(mouse_delta_x == 0 && mouse_delta_y == 0) ||
- (mMouseVelX < 0) ||
- llabs((F32)mMouseVelY) / llabs((F32)mMouseVelX) > MAX_MOUSE_SLOPE_SUB_MENU))
- {
- for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
- {
- LLView* viewp = *child_it;
- S32 local_x = x - viewp->getRect().mLeft;
- S32 local_y = y - viewp->getRect().mBottom;
- if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight())
- {
- // moving mouse always highlights new item
- if (mouse_delta_x != 0 || mouse_delta_y != 0)
- {
- ((LLMenuItemGL*)viewp)->setHighlight(false);
- }
- }
- }
-
- for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
- {
- LLView* viewp = *child_it;
- S32 local_x = x - viewp->getRect().mLeft;
- S32 local_y = y - viewp->getRect().mBottom;
- //RN: always call handleHover to track mGotHover status
- // but only set highlight when mouse is moving
- if( viewp->getVisible() &&
- //RN: allow disabled items to be highlighted to preserve "active" menus when
- // moving mouse through them
- //viewp->getEnabled() &&
- viewp->pointInView(local_x, local_y) &&
- viewp->handleHover(local_x, local_y, mask))
- {
- // moving mouse always highlights new item
- if (mouse_delta_x != 0 || mouse_delta_y != 0)
- {
- ((LLMenuItemGL*)viewp)->setHighlight(true);
- LLMenuGL::setKeyboardMode(false);
- }
- mHasSelection = true;
- }
- }
- }
- getWindow()->setCursor(UI_CURSOR_ARROW);
-
- // *HACK Release the mouse capture
- // This is done to release the mouse after the Navigation Bar "Back" or "Forward" button
- // drop-down menu is shown. Otherwise any other view won't be able to handle mouse events
- // until the user chooses one of the drop-down menu items.
-
- return true;
-}
-
-bool LLMenuGL::handleScrollWheel( S32 x, S32 y, S32 clicks )
-{
- if (!mScrollable)
- return blockMouseEvent(x, y);
-
- if( clicks > 0 )
- {
- while( clicks-- )
- scrollItems(SD_DOWN);
- }
- else
- {
- while( clicks++ )
- scrollItems(SD_UP);
- }
-
- return true;
-}
-
-
-void LLMenuGL::draw( void )
-{
- if (mNeedsArrange)
- {
- arrange();
- mNeedsArrange = false;
- }
- if (mDropShadowed && !mTornOff)
- {
- static LLUICachedControl<S32> drop_shadow_floater ("DropShadowFloater", 0);
- static LLUIColor color_drop_shadow = LLUIColorTable::instance().getColor("ColorDropShadow");
- gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0,
- color_drop_shadow, drop_shadow_floater );
- }
-
- if( mBgVisible )
- {
- gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0, mBackgroundColor.get() );
- }
- LLView::draw();
-}
-
-void LLMenuGL::drawBackground(LLMenuItemGL* itemp, F32 alpha)
-{
- LLColor4 color = itemp->getHighlightBgColor() % alpha;
- gGL.color4fv( color.mV );
- LLRect item_rect = itemp->getRect();
- gl_rect_2d( 0, item_rect.getHeight(), item_rect.getWidth(), 0);
-}
-
-void LLMenuGL::setVisible(bool visible)
-{
- if (visible != getVisible())
- {
- if (!visible)
- {
- mFadeTimer.start();
- clearHoverItem();
- // reset last known mouse coordinates so
- // we don't spoof a mouse move next time we're opened
- mLastMouseX = 0;
- mLastMouseY = 0;
- }
- else
- {
- mHasSelection = true;
- mFadeTimer.stop();
- }
-
- LLView::setVisible(visible);
- }
-}
-
-LLMenuGL* LLMenuGL::findChildMenuByName(const std::string& name, bool recurse) const
-{
- LLView* view = findChildView(name, recurse);
- if (view)
- {
- LLMenuItemBranchGL* branch = dynamic_cast<LLMenuItemBranchGL*>(view);
- if (branch)
- {
- return branch->getBranch();
- }
-
- LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view);
- if (menup)
- {
- return menup;
- }
- }
- LL_WARNS() << "Child Menu " << name << " not found in menu " << getName() << LL_ENDL;
- return NULL;
-}
-
-bool LLMenuGL::clearHoverItem()
-{
- for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
- {
- LLMenuItemGL* itemp = (LLMenuItemGL*)*child_it;
- if (itemp->getHighlight())
- {
- itemp->setHighlight(false);
- return true;
- }
- }
- return false;
-}
-
-void hide_top_view( LLView* view )
-{
- if( view ) view->setVisible( false );
-}
-
-
-// x and y are the desired location for the popup, in the spawning_view's
-// coordinate frame, NOT necessarily the mouse location
-// static
-void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y, S32 mouse_x, S32 mouse_y)
-{
- const S32 CURSOR_HEIGHT = 22; // Approximate "normal" cursor size
- const S32 CURSOR_WIDTH = 12;
-
- if (menu->getChildList()->empty())
- {
- return;
- }
-
- menu->setVisible( true );
-
- if(!menu->getAlwaysShowMenu())
- {
- //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;
- }
- }
-
- // Resetting scrolling position
- if (menu->isScrollable() && menu->isScrollPositionOnShowReset())
- {
- menu->mFirstVisibleItem = NULL;
- }
-
- // Fix menu rect if needed.
- menu->needsArrange();
- menu->arrangeAndClear();
-
- if ((mouse_x == 0) || (mouse_y == 0))
-
- {
- // Save click point for detecting cursor moves before mouse-up.
- // Must be in local coords to compare with mouseUp events.
- // If the mouse doesn't move, the menu will stay open ala the Mac.
- // See also LLContextMenu::show()
-
- LLUI::getInstance()->getMousePositionLocal(menu->getParent(), &mouse_x, &mouse_y);
- }
-
-
- LLMenuHolderGL::sContextMenuSpawnPos.set(mouse_x,mouse_y);
-
- const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getRect();
-
- const S32 HPAD = 2;
- LLRect rect = menu->getRect();
- S32 left = x + HPAD;
- S32 top = y;
- spawning_view->localPointToOtherView(left, top, &left, &top, menu->getParent());
- rect.setLeftTopAndSize( left, top,
- rect.getWidth(), rect.getHeight() );
- menu->setRect( rect );
-
-
- // Adjust context menu to fit onscreen
- LLRect mouse_rect;
- const S32 MOUSE_CURSOR_PADDING = 5;
- mouse_rect.setLeftTopAndSize(mouse_x - MOUSE_CURSOR_PADDING,
- mouse_y + MOUSE_CURSOR_PADDING,
- CURSOR_WIDTH + MOUSE_CURSOR_PADDING * 2,
- CURSOR_HEIGHT + MOUSE_CURSOR_PADDING * 2);
- menu->translateIntoRectWithExclusion( menu_region_rect, mouse_rect );
- 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);
-}
-
-///============================================================================
-/// Class LLMenuBarGL
-///============================================================================
-
-static LLDefaultChildRegistry::Register<LLMenuBarGL> r2("menu_bar");
-
-LLMenuBarGL::LLMenuBarGL( const Params& p )
-: LLMenuGL(p),
- mAltKeyTrigger(false)
-{}
-
-// Default destructor
-LLMenuBarGL::~LLMenuBarGL()
-{
- std::for_each(mAccelerators.begin(), mAccelerators.end(), DeletePointer());
- mAccelerators.clear();
-}
-
-bool LLMenuBarGL::handleAcceleratorKey(KEY key, MASK mask)
-{
- if (getHighlightedItem() && mask == MASK_NONE)
- {
- // unmodified key accelerators are ignored when navigating menu
- // (but are used as jump keys so will still work when appropriate menu is up)
- return false;
- }
- bool result = LLMenuGL::handleAcceleratorKey(key, mask);
- if (result && mask & MASK_ALT)
- {
- // ALT key used to trigger hotkey, don't use as shortcut to open menu
- mAltKeyTrigger = false;
- }
-
- if(!result
- && (key == KEY_F10 && mask == MASK_CONTROL)
- && !gKeyboard->getKeyRepeated(key)
- && isInVisibleChain())
- {
- if (getHighlightedItem())
- {
- clearHoverItem();
- LLMenuGL::setKeyboardMode(false);
- }
- else
- {
- // close menus originating from other menu bars when first opening menu via keyboard
- LLMenuGL::sMenuContainer->hideMenus();
- highlightNextItem(NULL);
- LLMenuGL::setKeyboardMode(true);
- }
- return true;
- }
-
- if (result && !getHighlightedItem() && LLMenuGL::sMenuContainer->hasVisibleMenu())
- {
- // close menus originating from other menu bars
- LLMenuGL::sMenuContainer->hideMenus();
- }
-
- return result;
-}
-
-bool LLMenuBarGL::handleKeyHere(KEY key, MASK mask)
-{
- static LLUICachedControl<bool> use_altkey_for_menus ("UseAltKeyForMenus", 0);
- if(key == KEY_ALT && !gKeyboard->getKeyRepeated(key) && use_altkey_for_menus)
- {
- mAltKeyTrigger = true;
- }
- else // if any key other than ALT hit, clear out waiting for Alt key mode
- {
- mAltKeyTrigger = false;
- }
-
- if (key == KEY_ESCAPE && mask == MASK_NONE)
- {
- LLMenuGL::setKeyboardMode(false);
- // if any menus are visible, this will return true, stopping further processing of ESCAPE key
- return LLMenuGL::sMenuContainer->hideMenus();
- }
-
- // before processing any other key, check to see if ALT key has triggered menu access
- checkMenuTrigger();
-
- return LLMenuGL::handleKeyHere(key, mask);
-}
-
-bool LLMenuBarGL::handleJumpKey(KEY key)
-{
- // perform case-insensitive comparison
- key = toupper(key);
- navigation_key_map_t::iterator found_it = mJumpKeys.find(key);
- if(found_it != mJumpKeys.end() && found_it->second->getEnabled())
- {
- // switch to keyboard navigation mode
- LLMenuGL::setKeyboardMode(true);
-
- found_it->second->setHighlight(true);
- found_it->second->onCommit();
- }
- return true;
-}
-
-bool LLMenuBarGL::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- // clicks on menu bar closes existing menus from other contexts but leave
- // own menu open so that we get toggle behavior
- if (!getHighlightedItem() || !getHighlightedItem()->isActive())
- {
- LLMenuGL::sMenuContainer->hideMenus();
- }
-
- return LLMenuGL::handleMouseDown(x, y, mask);
-}
-
-bool LLMenuBarGL::handleDoubleClick(S32 x, S32 y, MASK mask)
-{
- return LLMenuGL::handleMouseDown(x, y, mask);
-}
-
-void LLMenuBarGL::draw()
-{
- LLMenuItemGL* itemp = getHighlightedItem();
- // If we are in mouse-control mode and the mouse cursor is not hovering over
- // the current highlighted menu item and it isn't open, then remove the
- // highlight. This is done via a polling mechanism here, as we don't receive
- // notifications when the mouse cursor moves off of us
- if (itemp && !itemp->isOpen() && !itemp->getHover() && !LLMenuGL::getKeyboardMode())
- {
- clearHoverItem();
- }
-
- checkMenuTrigger();
-
- LLMenuGL::draw();
-}
-
-
-void LLMenuBarGL::checkMenuTrigger()
-{
- // has the ALT key been pressed and subsequently released?
- if (mAltKeyTrigger && !gKeyboard->getKeyDown(KEY_ALT))
- {
- // if alt key was released quickly, treat it as a menu access key
- // otherwise it was probably an Alt-zoom or similar action
- static LLUICachedControl<F32> menu_access_key_time ("MenuAccessKeyTime", 0);
- if (gKeyboard->getKeyElapsedTime(KEY_ALT) <= menu_access_key_time ||
- gKeyboard->getKeyElapsedFrameCount(KEY_ALT) < 2)
- {
- if (getHighlightedItem())
- {
- clearHoverItem();
- }
- else
- {
- // close menus originating from other menu bars
- LLMenuGL::sMenuContainer->hideMenus();
-
- highlightNextItem(NULL);
- LLMenuGL::setKeyboardMode(true);
- }
- }
- mAltKeyTrigger = false;
- }
-}
-
-bool LLMenuBarGL::jumpKeysActive()
-{
- // require user to be in keyboard navigation mode to activate key triggers
- // as menu bars are always visible and it is easy to leave the mouse cursor over them
- return LLMenuGL::getKeyboardMode() && getHighlightedItem() && LLMenuGL::jumpKeysActive();
-}
-
-// rearrange the child rects so they fit the shape of the menu bar.
-void LLMenuBarGL::arrange( void )
-{
- U32 pos = 0;
- LLRect rect( 0, getRect().getHeight(), 0, 0 );
- item_list_t::const_iterator item_iter;
- for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
- {
- LLMenuItemGL* item = *item_iter;
- if (item->getVisible())
- {
- rect.mLeft = pos;
- pos += item->getNominalWidth();
- rect.mRight = pos;
- item->setRect( rect );
- item->buildDrawLabel();
- }
- }
- reshape(rect.mRight, rect.getHeight());
-}
-
-
-S32 LLMenuBarGL::getRightmostMenuEdge()
-{
- // Find the last visible menu
- item_list_t::reverse_iterator item_iter;
- for (item_iter = mItems.rbegin(); item_iter != mItems.rend(); ++item_iter)
- {
- if ((*item_iter)->getVisible())
- {
- break;
- }
- }
-
- if (item_iter == mItems.rend())
- {
- return 0;
- }
- return (*item_iter)->getRect().mRight;
-}
-
-// add a vertical separator to this menu
-bool LLMenuBarGL::addSeparator()
-{
- LLMenuItemGL* separator = new LLMenuItemVerticalSeparatorGL();
- return append( separator );
-}
-
-// add a menu - this will create a drop down menu.
-bool LLMenuBarGL::appendMenu( LLMenuGL* menu )
-{
- if( menu == this )
- {
- LL_ERRS() << "** Attempt to attach menu to itself. This is certainly "
- << "a logic error." << LL_ENDL;
- }
-
- bool success = true;
-
- // *TODO: Hack! Fix this
- LLMenuItemBranchDownGL::Params p;
- p.name = menu->getName();
- p.label = menu->getLabel();
- p.visible = menu->getVisible();
- 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");
- p.font = menu->getFont();
-
- LLMenuItemBranchDownGL* branch = LLUICtrlFactory::create<LLMenuItemBranchDownGL>(p);
- success &= branch->addToAcceleratorList(&mAccelerators);
- success &= append( branch );
- branch->setJumpKey(branch->getJumpKey());
- menu->updateParent(LLMenuGL::sMenuContainer);
-
- return success;
-}
-
-bool LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask )
-{
- bool handled = false;
- LLView* active_menu = NULL;
-
- bool no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0;
- S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX;
- S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY;
- mMouseVelX = (mMouseVelX / 2) + (mouse_delta_x / 2);
- mMouseVelY = (mMouseVelY / 2) + (mouse_delta_y / 2);
- mLastMouseX = x;
- mLastMouseY = y;
-
- // if nothing currently selected or mouse has moved since last call, pick menu item via mouse
- // otherwise let keyboard control it
- if (!getHighlightedItem() || !LLMenuGL::getKeyboardMode() || llabs(mMouseVelX) > 0 || llabs(mMouseVelY) > 0)
- {
- // find current active menu
- for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
- {
- LLView* viewp = *child_it;
- if (((LLMenuItemGL*)viewp)->isOpen())
- {
- active_menu = viewp;
- }
- }
-
- // check for new active menu
- for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
- {
- LLView* viewp = *child_it;
- S32 local_x = x - viewp->getRect().mLeft;
- S32 local_y = y - viewp->getRect().mBottom;
- if( viewp->getVisible() &&
- viewp->getEnabled() &&
- viewp->pointInView(local_x, local_y) &&
- viewp->handleHover(local_x, local_y, mask))
- {
- ((LLMenuItemGL*)viewp)->setHighlight(true);
- handled = true;
- if (active_menu && active_menu != viewp)
- {
- ((LLMenuItemGL*)viewp)->onCommit();
- LLMenuGL::setKeyboardMode(false);
- }
- LLMenuGL::setKeyboardMode(false);
- }
- }
-
- if (handled)
- {
- // set hover false on inactive menus
- for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
- {
- LLView* viewp = *child_it;
- S32 local_x = x - viewp->getRect().mLeft;
- S32 local_y = y - viewp->getRect().mBottom;
- if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight())
- {
- ((LLMenuItemGL*)viewp)->setHighlight(false);
- }
- }
- }
- }
-
- getWindow()->setCursor(UI_CURSOR_ARROW);
-
- return true;
-}
-
-///============================================================================
-/// Class LLMenuHolderGL
-///============================================================================
-LLCoordGL LLMenuHolderGL::sContextMenuSpawnPos(S32_MAX, S32_MAX);
-
-LLMenuHolderGL::LLMenuHolderGL(const LLMenuHolderGL::Params& p)
- : LLPanel(p)
-{
- sItemActivationTimer.stop();
- mCanHide = true;
-}
-
-void LLMenuHolderGL::draw()
-{
- LLView::draw();
- // now draw last selected item as overlay
- LLMenuItemGL* selecteditem = (LLMenuItemGL*)sItemLastSelectedHandle.get();
- if (selecteditem && selecteditem->getVisible() && sItemActivationTimer.getStarted() && sItemActivationTimer.getElapsedTimeF32() < ACTIVATE_HIGHLIGHT_TIME)
- {
- // make sure toggle items, for example, show the proper state when fading out
- selecteditem->buildDrawLabel();
-
- LLRect item_rect;
- selecteditem->localRectToOtherView(selecteditem->getLocalRect(), &item_rect, this);
-
- F32 interpolant = sItemActivationTimer.getElapsedTimeF32() / ACTIVATE_HIGHLIGHT_TIME;
-
- LLUI::pushMatrix();
- {
- LLUI::translate((F32)item_rect.mLeft, (F32)item_rect.mBottom);
- selecteditem->getMenu()->drawBackground(selecteditem, interpolant);
- selecteditem->draw();
- }
- LLUI::popMatrix();
- }
-}
-
-bool LLMenuHolderGL::handleMouseDown( S32 x, S32 y, MASK mask )
-{
- bool handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
- if (!handled)
- {
- LLMenuGL* visible_menu = (LLMenuGL*)getVisibleMenu();
- LLMenuItemGL* parent_menu = visible_menu ? visible_menu->getParentMenuItem() : NULL;
- if (parent_menu && parent_menu->getVisible())
- {
- // don't hide menu if parent was hit
- LLRect parent_rect;
- parent_menu->localRectToOtherView(parent_menu->getLocalRect(), &parent_rect, this);
- if (!parent_rect.pointInRect(x, y))
- {
- // clicked off of menu and parent, hide them all
- hideMenus();
- }
- }
- else
- {
- // no visible parent, clicked off of menu, hide them all
- hideMenus();
- }
- }
- return handled;
-}
-
-bool LLMenuHolderGL::handleRightMouseDown( S32 x, S32 y, MASK mask )
-{
- bool handled = LLView::childrenHandleRightMouseDown(x, y, mask) != NULL;
- if (!handled)
- {
- // clicked off of menu, hide them all
- hideMenus();
- }
- return handled;
-}
-
-// This occurs when you mouse-down to spawn a context menu, hold the button
-// down, move off the menu, then mouse-up. We want this to close the menu.
-bool LLMenuHolderGL::handleRightMouseUp( S32 x, S32 y, MASK mask )
-{
- const S32 SLOP = 2;
- S32 spawn_dx = (x - sContextMenuSpawnPos.mX);
- S32 spawn_dy = (y - sContextMenuSpawnPos.mY);
- if (-SLOP <= spawn_dx && spawn_dx <= SLOP
- && -SLOP <= spawn_dy && spawn_dy <= SLOP)
- {
- // we're still inside the slop region from spawning this menu
- // so interpret the mouse-up as a single-click to show and leave on
- // screen
- sContextMenuSpawnPos.set(S32_MAX, S32_MAX);
- return true;
- }
-
- bool handled = LLView::childrenHandleRightMouseUp(x, y, mask) != NULL;
- if (!handled)
- {
- // clicked off of menu, hide them all
- hideMenus();
- }
- return handled;
-}
-
-bool LLMenuHolderGL::handleKey(KEY key, MASK mask, bool called_from_parent)
-{
- bool handled = false;
- LLMenuGL* const pMenu = dynamic_cast<LLMenuGL*>(getVisibleMenu());
-
- if (pMenu)
- {
- //eat TAB key - EXT-7000
- if (key == KEY_TAB && mask == MASK_NONE)
- {
- return true;
- }
-
- //handle ESCAPE and RETURN key
- handled = LLPanel::handleKey(key, mask, called_from_parent);
- if (!handled)
- {
- if (pMenu->getHighlightedItem())
- {
- handled = pMenu->handleKey(key, mask, true);
- }
- else if (mask == MASK_NONE || (key >= KEY_LEFT && key <= KEY_DOWN))
- {
- //highlight first enabled one
- if(pMenu->highlightNextItem(NULL))
- {
- handled = true;
- }
- }
- }
- }
-
- return handled;
-
-}
-
-void LLMenuHolderGL::reshape(S32 width, S32 height, bool called_from_parent)
-{
- if (width != getRect().getWidth() || height != getRect().getHeight())
- {
- hideMenus();
- }
- LLView::reshape(width, height, called_from_parent);
-}
-
-LLView* const LLMenuHolderGL::getVisibleMenu() const
-{
- for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
- {
- LLView* viewp = *child_it;
- if (viewp->getVisible() && dynamic_cast<LLMenuGL*>(viewp) != NULL)
- {
- return viewp;
- }
- }
- return NULL;
-}
-
-
-bool LLMenuHolderGL::hideMenus()
-{
- if (!mCanHide)
- {
- return false;
- }
- LLMenuGL::setKeyboardMode(false);
- bool menu_visible = hasVisibleMenu();
- if (menu_visible)
- {
- // clicked off of menu, hide them all
- for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
- {
- LLView* viewp = *child_it;
- if (dynamic_cast<LLMenuGL*>(viewp) != NULL && viewp->getVisible())
- {
- viewp->setVisible(false);
- }
- }
- }
- //if (gFocusMgr.childHasKeyboardFocus(this))
- //{
- // gFocusMgr.setKeyboardFocus(NULL);
- //}
-
- return menu_visible;
-}
-
-void LLMenuHolderGL::setActivatedItem(LLMenuItemGL* item)
-{
- sItemLastSelectedHandle = item->getHandle();
- sItemActivationTimer.start();
-}
-
-///============================================================================
-/// Class LLTearOffMenu
-///============================================================================
-LLTearOffMenu::LLTearOffMenu(LLMenuGL* menup) :
- LLFloater(LLSD()),
- mQuitRequested(false)
-{
- S32 floater_header_size = getHeaderHeight();
-
- setName(menup->getName());
- setTitle(menup->getLabel());
- setCanMinimize(false);
- // flag menu as being torn off
- menup->setTornOff(true);
- // update menu layout as torn off menu (no spillover menus)
- menup->needsArrange();
-
- LLRect rect;
- menup->localRectToOtherView(LLRect(-1, menup->getRect().getHeight(), menup->getRect().getWidth() + 3, 0), &rect, gFloaterView);
- // make sure this floater is big enough for menu
- mTargetHeight = rect.getHeight() + floater_header_size;
- reshape(rect.getWidth(), rect.getHeight());
- setRect(rect);
-
- // attach menu to floater
- menup->setFollows( FOLLOWS_LEFT | FOLLOWS_BOTTOM );
- mOldParent = menup->getParent();
- addChild(menup);
- menup->setVisible(true);
- LLRect menu_rect = menup->getRect();
- menu_rect.setOriginAndSize( 1, 1,
- menu_rect.getWidth(), menu_rect.getHeight());
- menup->setRect(menu_rect);
- menup->setDropShadowed(false);
-
- mMenu = menup;
-
- // highlight first item (tear off item will be disabled)
- mMenu->highlightNextItem(NULL);
-
- // Can't do this in postBuild() because that is only called for floaters
- // constructed from XML.
- mCloseSignal.connect(boost::bind(&LLTearOffMenu::closeTearOff, this));
-}
-
-LLTearOffMenu::~LLTearOffMenu()
-{
-}
-
-void LLTearOffMenu::draw()
-{
- mMenu->setBackgroundVisible(isBackgroundOpaque());
-
- if (getRect().getHeight() != mTargetHeight)
- {
- // animate towards target height
- reshape(getRect().getWidth(), llceil(lerp((F32)getRect().getHeight(), (F32)mTargetHeight, LLSmoothInterpolation::getInterpolant(0.05f))));
- }
- mMenu->needsArrange();
- LLFloater::draw();
-}
-
-void LLTearOffMenu::onFocusReceived()
-{
- if (mQuitRequested)
- {
- return;
- }
-
- // if nothing is highlighted, just highlight first item
- if (!mMenu->getHighlightedItem())
- {
- mMenu->highlightNextItem(NULL);
- }
-
- // parent menu items get highlights so navigation logic keeps working
- LLMenuItemGL* parent_menu_item = mMenu->getParentMenuItem();
- while(parent_menu_item)
- {
- if (parent_menu_item->getMenu()->getVisible())
- {
- parent_menu_item->setHighlight(true);
- parent_menu_item = parent_menu_item->getMenu()->getParentMenuItem();
- }
- else
- {
- break;
- }
- }
- LLFloater::onFocusReceived();
-}
-
-void LLTearOffMenu::onFocusLost()
-{
- // remove highlight from parent item and our own menu
- mMenu->clearHoverItem();
- LLFloater::onFocusLost();
-}
-
-bool LLTearOffMenu::handleUnicodeChar(llwchar uni_char, bool called_from_parent)
-{
- // pass keystrokes down to menu
- return mMenu->handleUnicodeChar(uni_char, true);
-}
-
-bool LLTearOffMenu::handleKeyHere(KEY key, MASK mask)
-{
- if (!mMenu->getHighlightedItem())
- {
- if (key == KEY_UP)
- {
- mMenu->highlightPrevItem(NULL);
- return true;
- }
- else if (key == KEY_DOWN)
- {
- mMenu->highlightNextItem(NULL);
- return true;
- }
- }
- // pass keystrokes down to menu
- return mMenu->handleKey(key, mask, true);
-}
-
-void LLTearOffMenu::translate(S32 x, S32 y)
-{
- if (x != 0 && y != 0)
- {
- // hide open sub-menus by clearing current hover item
- mMenu->clearHoverItem();
- }
- LLFloater::translate(x, y);
-}
-
-//static
-LLTearOffMenu* LLTearOffMenu::create(LLMenuGL* menup)
-{
- LLTearOffMenu* tearoffp = new LLTearOffMenu(menup);
- // keep onscreen
- gFloaterView->adjustToFitScreen(tearoffp, false);
- tearoffp->openFloater(LLSD());
-
- return tearoffp;
-}
-
-void LLTearOffMenu::updateSize()
-{
- if (mMenu)
- {
- S32 floater_header_size = getHeaderHeight();
- const LLRect &floater_rect = getRect();
- LLRect new_rect;
- mMenu->localRectToOtherView(LLRect(-1, mMenu->getRect().getHeight() + floater_header_size, mMenu->getRect().getWidth() + 3, 0), &new_rect, gFloaterView);
-
- if (floater_rect.getWidth() != new_rect.getWidth()
- || mTargetHeight != new_rect.getHeight())
- {
- // make sure this floater is big enough for menu
- mTargetHeight = new_rect.getHeight();
- reshape(new_rect.getWidth(), mTargetHeight);
-
- // Restore menu position
- LLRect menu_rect = mMenu->getRect();
- menu_rect.setOriginAndSize(1, 1,
- menu_rect.getWidth(), menu_rect.getHeight());
- mMenu->setRect(menu_rect);
- }
- }
-}
-
-void LLTearOffMenu::closeTearOff()
-{
- removeChild(mMenu);
- mOldParent->addChild(mMenu);
- mMenu->clearHoverItem();
- mMenu->setFollowsNone();
- mMenu->setBackgroundVisible(true);
- mMenu->setVisible(false);
- mMenu->setTornOff(false);
- mMenu->setDropShadowed(true);
- mQuitRequested = true;
-}
-
-LLContextMenuBranch::LLContextMenuBranch(const LLContextMenuBranch::Params& p)
-: LLMenuItemGL(p)
-{
- LLContextMenu* branch = static_cast<LLContextMenu*>(p.branch);
- if (branch)
- {
- mBranch = branch->getHandle();
- branch->hide();
- branch->setParentMenuItem(this);
- }
-}
-
-LLContextMenuBranch::~LLContextMenuBranch()
-{
- if (mBranch.get())
- {
- mBranch.get()->die();
- }
-}
-
-// called to rebuild the draw label
-void LLContextMenuBranch::buildDrawLabel( void )
-{
- auto menu = getBranch();
- if (menu)
- {
- // default enablement is this -- if any of the subitems are
- // enabled, this item is enabled. JC
- U32 sub_count = menu->getItemCount();
- U32 i;
- bool any_enabled = false;
- for (i = 0; i < sub_count; i++)
- {
- LLMenuItemGL* item = menu->getItem(i);
- item->buildDrawLabel();
- if (item->getEnabled() && !item->getDrawTextDisabled() )
- {
- any_enabled = true;
- break;
- }
- }
- setDrawTextDisabled(!any_enabled);
- setEnabled(true);
- }
-
- mDrawAccelLabel.clear();
- std::string st = mDrawAccelLabel;
- appendAcceleratorString( st );
- mDrawAccelLabel = st;
-
- mDrawBranchLabel = LLMenuGL::BRANCH_SUFFIX;
-}
-
-void LLContextMenuBranch::showSubMenu()
-{
- auto menu = getBranch();
- if(menu)
- {
- LLMenuItemGL* menu_item = menu->getParentMenuItem();
- if (menu_item != NULL && menu_item->getVisible())
- {
- S32 center_x;
- S32 center_y;
- localPointToScreen(getRect().getWidth(), getRect().getHeight(), &center_x, &center_y);
- menu->show(center_x, center_y);
- }
- }
-}
-
-// onCommit() - do the primary funcationality of the menu item.
-void LLContextMenuBranch::onCommit( void )
-{
- showSubMenu();
-
-}
-void LLContextMenuBranch::setHighlight( bool highlight )
-{
- if (highlight == getHighlight()) return;
- LLMenuItemGL::setHighlight(highlight);
- auto menu = getBranch();
- if (menu)
- {
- if (highlight)
- {
- showSubMenu();
- }
- else
- {
- menu->hide();
- }
- }
-}
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////
-//-----------------------------------------------------------------------------
-// class LLContextMenu
-// A context menu
-//-----------------------------------------------------------------------------
-static LLDefaultChildRegistry::Register<LLContextMenu> context_menu_register("context_menu");
-static MenuRegistry::Register<LLContextMenu> context_menu_register2("context_menu");
-
-
-LLContextMenu::LLContextMenu(const Params& p)
-: LLMenuGL(p),
- mHoveredAnyItem(false),
- mHoverItem(NULL)
-{
- //setBackgroundVisible(true);
-}
-
-void LLContextMenu::setVisible(bool visible)
-{
- if (!visible)
- hide();
-}
-
-// Takes cursor position in screen space?
-void LLContextMenu::show(S32 x, S32 y, LLView* spawning_view)
-{
- if (getChildList()->empty())
- {
- // nothing to show, so abort
- 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.
- // See also LLMenuGL::showPopup()
- LLMenuHolderGL::sContextMenuSpawnPos.set(x,y);
-
- arrangeAndClear();
-
- S32 width = getRect().getWidth();
- S32 height = getRect().getHeight();
- const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect();
- LLView* parent_view = getParent();
-
- // Open upwards if menu extends past bottom
- if (y - height < menu_region_rect.mBottom)
- {
- if (getParentMenuItem()) // Adjust if this is a submenu
- {
- y += height - getParentMenuItem()->getNominalHeight();
- }
- else
- {
- y += height;
- }
- }
-
- // Open out to the left if menu extends past right edge
- if (x + width > menu_region_rect.mRight)
- {
- if (getParentMenuItem())
- {
- x -= getParentMenuItem()->getRect().getWidth() + width;
- }
- else
- {
- x -= width;
- }
- }
-
- S32 local_x, local_y;
- parent_view->screenPointToLocal(x, y, &local_x, &local_y);
-
- LLRect rect;
- rect.setLeftTopAndSize(local_x, local_y, width, height);
- setRect(rect);
- arrange();
-
- if (spawning_view)
- {
- mSpawningViewHandle = spawning_view->getHandle();
- }
- else
- {
- mSpawningViewHandle.markDead();
- }
- LLView::setVisible(true);
-}
-
-void LLContextMenu::hide()
-{
- if (!getVisible()) return;
-
- LLView::setVisible(false);
-
- if (mHoverItem)
- {
- mHoverItem->setHighlight( false );
- }
- mHoverItem = NULL;
-}
-
-
-bool LLContextMenu::handleHover( S32 x, S32 y, MASK mask )
-{
- LLMenuGL::handleHover(x,y,mask);
-
- bool handled = false;
-
- LLMenuItemGL *item = getHighlightedItem();
-
- if (item && item->getEnabled())
- {
- getWindow()->setCursor(UI_CURSOR_ARROW);
- handled = true;
-
- if (item != mHoverItem)
- {
- if (mHoverItem)
- {
- mHoverItem->setHighlight( false );
- }
- mHoverItem = item;
- mHoverItem->setHighlight( true );
- }
- mHoveredAnyItem = true;
- }
- else
- {
- // clear out our selection
- if (mHoverItem)
- {
- mHoverItem->setHighlight(false);
- mHoverItem = NULL;
- }
- }
-
- if( !handled && pointInView( x, y ) )
- {
- getWindow()->setCursor(UI_CURSOR_ARROW);
- handled = true;
- }
-
- return handled;
-}
-
-// handleMouseDown and handleMouseUp are handled by LLMenuGL
-
-
-bool LLContextMenu::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
-
- // The click was somewhere within our rectangle
- LLMenuItemGL *item = getHighlightedItem();
-
- S32 local_x = x - getRect().mLeft;
- S32 local_y = y - getRect().mBottom;
-
- bool clicked_in_menu = pointInView(local_x, local_y) ;
-
- // grab mouse if right clicking anywhere within pie (even deadzone in middle), to detect drag outside of pie
- if (clicked_in_menu)
- {
- // capture mouse cursor as if on initial menu show
- handled = true;
- }
-
- if (item)
- {
- // lie to the item about where the click happened
- // to make sure it's within the item's rectangle
- if (item->handleMouseDown( 0, 0, mask ))
- {
- handled = true;
- }
- }
-
- return handled;
-}
-
-bool LLContextMenu::handleRightMouseUp( S32 x, S32 y, MASK mask )
-{
- S32 local_x = x - getRect().mLeft;
- S32 local_y = y - getRect().mBottom;
-
- if (!mHoveredAnyItem && !pointInView(local_x, local_y))
- {
- sMenuContainer->hideMenus();
- return true;
- }
-
-
- bool result = handleMouseUp( x, y, mask );
- mHoveredAnyItem = false;
-
- return result;
-}
-
-bool LLContextMenu::addChild(LLView* view, S32 tab_group)
-{
- return addContextChild(view, tab_group);
-}
-
+/**
+ * @file llmenugl.cpp
+ * @brief LLMenuItemGL 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$
+ */
+
+//*****************************************************************************
+//
+// This file contains the opengl based menu implementation.
+//
+// NOTES: A menu label is split into 4 columns. The left column, the
+// label colum, the accelerator column, and the right column. The left
+// column is used for displaying boolean values for toggle and check
+// controls. The right column is used for submenus.
+//
+//*****************************************************************************
+
+//#include "llviewerprecompiledheaders.h"
+#include "linden_common.h"
+
+#include "llmenugl.h"
+
+#include "llgl.h"
+#include "llmath.h"
+#include "llrender.h"
+#include "llfocusmgr.h"
+#include "llcoord.h"
+#include "llwindow.h"
+#include "llcriticaldamp.h"
+#include "lluictrlfactory.h"
+
+#include "llbutton.h"
+#include "llfontgl.h"
+#include "llresmgr.h"
+#include "lltrans.h"
+#include "llui.h"
+
+#include "llstl.h"
+
+#include "v2math.h"
+#include <set>
+#include <boost/tokenizer.hpp>
+
+// static
+LLMenuHolderGL *LLMenuGL::sMenuContainer = NULL;
+view_listener_t::listener_map_t view_listener_t::sListeners;
+
+S32 MENU_BAR_HEIGHT = 18;
+S32 MENU_BAR_WIDTH = 410;
+
+///============================================================================
+/// Local function declarations, constants, enums, and typedefs
+///============================================================================
+
+const S32 LABEL_BOTTOM_PAD_PIXELS = 2;
+
+const U32 LEFT_PAD_PIXELS = 3;
+const U32 LEFT_WIDTH_PIXELS = 15;
+const U32 LEFT_PLAIN_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS;
+
+const U32 RIGHT_PAD_PIXELS = 7;
+const U32 RIGHT_WIDTH_PIXELS = 15;
+const U32 RIGHT_PLAIN_PIXELS = RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS;
+
+const U32 PLAIN_PAD_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS;
+
+const U32 BRIEF_PAD_PIXELS = 2;
+
+const U32 SEPARATOR_HEIGHT_PIXELS = 8;
+const S32 TEAROFF_SEPARATOR_HEIGHT_PIXELS = 10;
+const S32 MENU_ITEM_PADDING = 4;
+
+const std::string SEPARATOR_NAME("separator");
+const std::string VERTICAL_SEPARATOR_LABEL( "|" );
+
+const std::string LLMenuGL::BOOLEAN_TRUE_PREFIX( "\xE2\x9C\x94" ); // U+2714 HEAVY CHECK MARK
+const std::string LLMenuGL::BRANCH_SUFFIX( "\xe2\x96\xb8" ); // U+25B6 BLACK RIGHT-POINTING TRIANGLE
+const std::string LLMenuGL::ARROW_UP ("^^^^^^^");
+const std::string LLMenuGL::ARROW_DOWN("vvvvvvv");
+
+const F32 MAX_MOUSE_SLOPE_SUB_MENU = 0.9f;
+
+bool LLMenuGL::sKeyboardMode = false;
+
+LLHandle<LLView> LLMenuHolderGL::sItemLastSelectedHandle;
+LLFrameTimer LLMenuHolderGL::sItemActivationTimer;
+
+const F32 ACTIVATE_HIGHLIGHT_TIME = 0.3f;
+
+static MenuRegistry::Register<LLMenuItemGL> register_menu_item("menu_item");
+static MenuRegistry::Register<LLMenuItemSeparatorGL> register_separator("menu_item_separator");
+static MenuRegistry::Register<LLMenuItemCallGL> register_menu_item_call("menu_item_call");
+static MenuRegistry::Register<LLMenuItemCheckGL> register_menu_item_check("menu_item_check");
+// Created programmatically but we need to specify custom colors in xml
+static MenuRegistry::Register<LLMenuItemTearOffGL> register_menu_item_tear_off("menu_item_tear_off");
+static MenuRegistry::Register<LLMenuGL> register_menu("menu");
+
+static LLDefaultChildRegistry::Register<LLMenuGL> register_menu_default("menu");
+
+
+
+///============================================================================
+/// Class LLMenuItemGL
+///============================================================================
+
+LLMenuItemGL::Params::Params()
+: shortcut("shortcut"),
+ jump_key("jump_key", KEY_NONE),
+ use_mac_ctrl("use_mac_ctrl", false),
+ allow_key_repeat("allow_key_repeat", false),
+ rect("rect"),
+ left("left"),
+ top("top"),
+ right("right"),
+ bottom("bottom"),
+ width("width"),
+ height("height"),
+ bottom_delta("bottom_delta"),
+ left_delta("left_delta"),
+ enabled_color("enabled_color"),
+ disabled_color("disabled_color"),
+ highlight_bg_color("highlight_bg_color"),
+ highlight_fg_color("highlight_fg_color")
+{
+ changeDefault(mouse_opaque, true);
+}
+
+// Default constructor
+LLMenuItemGL::LLMenuItemGL(const LLMenuItemGL::Params& p)
+: LLUICtrl(p),
+ mJumpKey(p.jump_key),
+ mAllowKeyRepeat(p.allow_key_repeat),
+ mHighlight( false ),
+ mGotHover( false ),
+ mBriefItem( false ),
+ mDrawTextDisabled( false ),
+ mFont(p.font),
+ mAcceleratorKey(KEY_NONE),
+ mAcceleratorMask(MASK_NONE),
+ mLabel(p.label.isProvided() ? p.label() : p.name()),
+ mEnabledColor(p.enabled_color()),
+ mDisabledColor(p.disabled_color()),
+ mHighlightBackground(p.highlight_bg_color()),
+ mHighlightForeground(p.highlight_fg_color())
+{
+#ifdef LL_DARWIN
+ // See if this Mac accelerator should really use the ctrl key and not get mapped to cmd
+ bool useMacCtrl = p.use_mac_ctrl;
+#endif // LL_DARWIN
+
+ std::string shortcut = p.shortcut;
+ if (shortcut.find("control") != shortcut.npos)
+ {
+#ifdef LL_DARWIN
+ if ( useMacCtrl )
+ {
+ mAcceleratorMask |= MASK_MAC_CONTROL;
+ }
+#endif // LL_DARWIN
+ mAcceleratorMask |= MASK_CONTROL;
+ }
+ if (shortcut.find("alt") != shortcut.npos)
+ {
+ mAcceleratorMask |= MASK_ALT;
+ }
+ if (shortcut.find("shift") != shortcut.npos)
+ {
+ mAcceleratorMask |= MASK_SHIFT;
+ }
+ S32 pipe_pos = shortcut.rfind("|");
+ std::string key_str = shortcut.substr(pipe_pos+1);
+
+ LLKeyboard::keyFromString(key_str, &mAcceleratorKey);
+
+ LL_DEBUGS("HotKeys") << "Process short cut key: shortcut: " << shortcut
+ << ", key str: " << key_str
+ << ", accelerator mask: " << mAcceleratorMask
+ << ", accelerator key: " << mAcceleratorKey
+ << LL_ENDL;
+}
+
+//virtual
+void LLMenuItemGL::setValue(const LLSD& value)
+{
+ setLabel(value.asString());
+}
+
+//virtual
+LLSD LLMenuItemGL::getValue() const
+{
+ return getLabel();
+}
+
+//virtual
+bool LLMenuItemGL::hasAccelerator(const KEY &key, const MASK &mask) const
+{
+ return (mAcceleratorKey == key) && (mAcceleratorMask == mask);
+}
+
+//virtual
+bool LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask)
+{
+ if( getEnabled() && (!gKeyboard->getKeyRepeated(key) || mAllowKeyRepeat) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) )
+ {
+ onCommit();
+ return true;
+ }
+ return false;
+}
+
+bool LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask)
+{
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ return true;
+}
+
+//virtual
+bool LLMenuItemGL::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ return LLUICtrl::handleRightMouseDown(x,y,mask);
+}
+
+void LLMenuItemGL::onMouseEnter(S32 x, S32 y, MASK mask)
+{
+ setHover(true);
+ LLUICtrl::onMouseEnter(x,y,mask);
+}
+
+void LLMenuItemGL::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+ setHover(false);
+ LLUICtrl::onMouseLeave(x,y,mask);
+}
+
+//virtual
+bool LLMenuItemGL::handleRightMouseUp(S32 x, S32 y, MASK mask)
+{
+ // If this event came from a right-click context menu spawn,
+ // process as a left-click to allow menu items to be hit
+ if (LLMenuHolderGL::sContextMenuSpawnPos.mX != S32_MAX
+ || LLMenuHolderGL::sContextMenuSpawnPos.mY != S32_MAX)
+ {
+ bool handled = handleMouseUp(x, y, mask);
+ return handled;
+ }
+ return LLUICtrl::handleRightMouseUp(x,y,mask);
+}
+
+// This function checks to see if the accelerator key is already in use;
+// if not, it will be added to the list
+bool LLMenuItemGL::addToAcceleratorList(std::list <LLMenuKeyboardBinding*> *listp)
+{
+ LLMenuKeyboardBinding *accelerator = NULL;
+
+ if (mAcceleratorKey != KEY_NONE)
+ {
+ std::list<LLMenuKeyboardBinding*>::iterator list_it;
+ for (list_it = listp->begin(); list_it != listp->end(); ++list_it)
+ {
+ accelerator = *list_it;
+ if ((accelerator->mKey == mAcceleratorKey) && (accelerator->mMask == (mAcceleratorMask & MASK_NORMALKEYS)))
+ {
+
+ // *NOTE: get calling code to throw up warning or route
+ // warning messages back to app-provided output
+ // std::string warning;
+ // warning.append("Duplicate key binding <");
+ // appendAcceleratorString( warning );
+ // warning.append("> for menu items:\n ");
+ // warning.append(accelerator->mName);
+ // warning.append("\n ");
+ // warning.append(mLabel);
+
+ // LL_WARNS() << warning << LL_ENDL;
+ // LLAlertDialog::modalAlert(warning);
+ return false;
+ }
+ }
+ if (!accelerator)
+ {
+ accelerator = new LLMenuKeyboardBinding;
+ if (accelerator)
+ {
+ accelerator->mKey = mAcceleratorKey;
+ accelerator->mMask = (mAcceleratorMask & MASK_NORMALKEYS);
+// accelerator->mName = mLabel;
+ }
+ listp->push_back(accelerator);//addData(accelerator);
+ }
+ }
+ return true;
+}
+
+// This function appends the character string representation of
+// the current accelerator key and mask to the provided string.
+void LLMenuItemGL::appendAcceleratorString( std::string& st ) const
+{
+ st = LLKeyboard::stringFromAccelerator( mAcceleratorMask, mAcceleratorKey );
+ LL_DEBUGS("HotKeys") << "appendAcceleratorString: " << st << LL_ENDL;
+}
+
+void LLMenuItemGL::setJumpKey(KEY key)
+{
+ mJumpKey = LLStringOps::toUpper((char)key);
+}
+
+
+// virtual
+U32 LLMenuItemGL::getNominalHeight( void ) const
+{
+ return mFont->getLineHeight() + MENU_ITEM_PADDING;
+}
+
+//virtual
+void LLMenuItemGL::setBriefItem(bool brief)
+{
+ mBriefItem = brief;
+}
+
+//virtual
+bool LLMenuItemGL::isBriefItem() const
+{
+ return mBriefItem;
+}
+
+// Get the parent menu for this item
+LLMenuGL* LLMenuItemGL::getMenu() const
+{
+ return (LLMenuGL*) getParent();
+}
+
+
+// getNominalWidth() - returns the normal width of this control in
+// pixels - this is used for calculating the widest item, as well as
+// for horizontal arrangement.
+U32 LLMenuItemGL::getNominalWidth( void ) const
+{
+ U32 width;
+
+ if (mBriefItem)
+ {
+ width = BRIEF_PAD_PIXELS;
+ }
+ else
+ {
+ width = PLAIN_PAD_PIXELS;
+ }
+
+ if( KEY_NONE != mAcceleratorKey )
+ {
+ width += getMenu()->getShortcutPad();
+ std::string temp;
+ appendAcceleratorString( temp );
+ width += mFont->getWidth( temp );
+ }
+ width += mFont->getWidth( mLabel.getWString().c_str() );
+ return width;
+}
+
+// called to rebuild the draw label
+void LLMenuItemGL::buildDrawLabel( void )
+{
+ mDrawAccelLabel.clear();
+ std::string st = mDrawAccelLabel.getString();
+ appendAcceleratorString( st );
+ mDrawAccelLabel = st;
+}
+
+void LLMenuItemGL::onCommit( void )
+{
+ // Check torn-off status to allow left-arrow keyboard navigation back
+ // to parent menu.
+ // Also, don't hide if item triggered by keyboard shortcut (and hence
+ // parent not visible).
+ if (!getMenu()->getTornOff()
+ && getMenu()->getVisible())
+ {
+ LLMenuGL::sMenuContainer->hideMenus();
+ }
+
+ LLUICtrl::onCommit();
+}
+
+// set the hover status (called by it's menu)
+ void LLMenuItemGL::setHighlight( bool highlight )
+{
+ if (highlight)
+ {
+ getMenu()->clearHoverItem();
+ }
+
+ if (mHighlight != highlight)
+ {
+ dirtyRect();
+ }
+
+ mHighlight = highlight;
+}
+
+
+bool LLMenuItemGL::handleKeyHere( KEY key, MASK mask )
+{
+ if (getHighlight() &&
+ getMenu()->isOpen())
+ {
+ if (key == KEY_UP)
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ getMenu()->highlightPrevItem(this);
+ return true;
+ }
+ else if (key == KEY_DOWN)
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ getMenu()->highlightNextItem(this);
+ return true;
+ }
+ else if (key == KEY_RETURN && mask == MASK_NONE)
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ onCommit();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool LLMenuItemGL::handleMouseUp( S32 x, S32 y, MASK mask)
+{
+ // switch to mouse navigation mode
+ LLMenuGL::setKeyboardMode(false);
+
+ onCommit();
+ make_ui_sound("UISndClickRelease");
+ return LLView::handleMouseUp(x, y, mask);
+}
+
+bool LLMenuItemGL::handleMouseDown( S32 x, S32 y, MASK mask)
+{
+ // switch to mouse navigation mode
+ LLMenuGL::setKeyboardMode(false);
+
+ setHighlight(true);
+ return LLView::handleMouseDown(x, y, mask);
+}
+
+bool LLMenuItemGL::handleScrollWheel( S32 x, S32 y, S32 clicks )
+{
+ // If the menu is scrollable let it handle the wheel event.
+ return !getMenu()->isScrollable();
+}
+
+void LLMenuItemGL::draw( void )
+{
+ // *FIX: This can be optimized by using switches. Want to avoid
+ // that until the functionality is finalized.
+
+ // HACK: Brief items don't highlight. Pie menu takes care of it. JC
+ // let disabled items be highlighted, just don't draw them as such
+ if( getEnabled() && getHighlight() && !mBriefItem)
+ {
+ gGL.color4fv( mHighlightBackground.get().mV );
+
+ gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 );
+ }
+
+ LLColor4 color;
+
+ if ( getEnabled() && getHighlight() )
+ {
+ color = mHighlightForeground.get();
+ }
+ else if( getEnabled() && !mDrawTextDisabled )
+ {
+ color = mEnabledColor.get();
+ }
+ else
+ {
+ color = mDisabledColor.get();
+ }
+
+ // Highlight if needed
+ if( ll::ui::SearchableControl::getHighlighted() )
+ color = ll::ui::SearchableControl::getHighlightColor();
+
+ // Draw the text on top.
+ if (mBriefItem)
+ {
+ mFont->render( mLabel, 0, BRIEF_PAD_PIXELS / 2, 0, color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL);
+ }
+ else
+ {
+ if( !mDrawBoolLabel.empty() )
+ {
+ mFont->render( mDrawBoolLabel.getWString(), 0, (F32)LEFT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f), 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), 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), 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), color,
+ LLFontGL::RIGHT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, false );
+ }
+ }
+
+ // underline "jump" key only when keyboard navigation has been initiated
+ if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode())
+ {
+ std::string upper_case_label = mLabel.getString();
+ LLStringUtil::toUpper(upper_case_label);
+ std::string::size_type offset = upper_case_label.find(mJumpKey);
+ if (offset != std::string::npos)
+ {
+ S32 x_begin = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset);
+ S32 x_end = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset + 1);
+ gl_line_2d(x_begin, (MENU_ITEM_PADDING / 2) + 1, x_end, (MENU_ITEM_PADDING / 2) + 1);
+ }
+ }
+}
+
+bool LLMenuItemGL::setLabelArg( const std::string& key, const LLStringExplicit& text )
+{
+ mLabel.setArg(key, text);
+ return true;
+}
+
+void LLMenuItemGL::onVisibilityChange(bool new_visibility)
+{
+ if (getMenu())
+ {
+ getMenu()->needsArrange();
+ }
+ LLView::onVisibilityChange(new_visibility);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemSeparatorGL
+//
+// This class represents a separator.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LLMenuItemSeparatorGL::Params::Params()
+ : on_visible("on_visible")
+{
+}
+
+LLMenuItemSeparatorGL::LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p) :
+ LLMenuItemGL( p )
+{
+ if (p.on_visible.isProvided())
+ {
+ mVisibleSignal.connect(initEnableCallback(p.on_visible));
+ }
+}
+
+//virtual
+U32 LLMenuItemSeparatorGL::getNominalHeight( void ) const
+{
+ return SEPARATOR_HEIGHT_PIXELS;
+}
+
+void LLMenuItemSeparatorGL::draw( void )
+{
+ gGL.color4fv( mDisabledColor.get().mV );
+ const S32 y = getRect().getHeight() / 2;
+ const S32 PAD = 6;
+ gl_line_2d( PAD, y, getRect().getWidth() - PAD, y );
+}
+
+void LLMenuItemSeparatorGL::buildDrawLabel( void )
+{
+ if (mVisibleSignal.num_slots() > 0)
+ {
+ bool visible = mVisibleSignal(this, LLSD());
+ setVisible(visible);
+ }
+}
+
+bool LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLMenuGL* parent_menu = getMenu();
+ if (y > getRect().getHeight() / 2)
+ {
+ // 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->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->getVisible() && next_menu_item->getEnabled()) ? next_menu_item->handleMouseDown(x, 0, mask) : false;
+ }
+}
+
+bool LLMenuItemSeparatorGL::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ LLMenuGL* parent_menu = getMenu();
+ if (y > getRect().getHeight() / 2)
+ {
+ LLView* prev_menu_item = parent_menu->findNextSibling(this);
+ 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->getVisible() && next_menu_item->getEnabled()) ? next_menu_item->handleMouseUp(x, 0, mask) : false;
+ }
+}
+
+bool LLMenuItemSeparatorGL::handleHover(S32 x, S32 y, MASK mask)
+{
+ LLMenuGL* parent_menu = getMenu();
+ if (y > getRect().getHeight() / 2)
+ {
+ parent_menu->highlightPrevItem(this, false);
+ return false;
+ }
+ else
+ {
+ parent_menu->highlightNextItem(this, false);
+ return false;
+ }
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemVerticalSeparatorGL
+//
+// This class represents a vertical separator.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuItemVerticalSeparatorGL
+: public LLMenuItemSeparatorGL
+{
+public:
+ LLMenuItemVerticalSeparatorGL( void );
+
+ virtual bool handleMouseDown(S32 x, S32 y, MASK mask) { return false; }
+};
+
+LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL( void )
+{
+ setLabel( VERTICAL_SEPARATOR_LABEL );
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemTearOffGL
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LLMenuItemTearOffGL::LLMenuItemTearOffGL(const LLMenuItemTearOffGL::Params& p)
+: LLMenuItemGL(p)
+{
+}
+
+// Returns the first floater ancestor if there is one
+LLFloater* LLMenuItemTearOffGL::getParentFloater()
+{
+ LLView* parent_view = getMenu();
+
+ while (parent_view)
+ {
+ if (dynamic_cast<LLFloater*>(parent_view))
+ {
+ return dynamic_cast<LLFloater*>(parent_view);
+ }
+
+ bool parent_is_menu = dynamic_cast<LLMenuGL*>(parent_view) && !dynamic_cast<LLMenuBarGL*>(parent_view);
+
+ if (parent_is_menu)
+ {
+ // use menu parent
+ parent_view = dynamic_cast<LLMenuGL*>(parent_view)->getParentMenuItem();
+ }
+ else
+ {
+ // just use regular view parent
+ parent_view = parent_view->getParent();
+ }
+ }
+
+ return NULL;
+}
+
+void LLMenuItemTearOffGL::onCommit()
+{
+ if (getMenu()->getTornOff())
+ {
+ LLTearOffMenu * torn_off_menu = dynamic_cast<LLTearOffMenu*>(getMenu()->getParent());
+ if (torn_off_menu)
+ {
+ torn_off_menu->closeFloater();
+ }
+ }
+ else
+ {
+ // transfer keyboard focus and highlight to first real item in list
+ if (getHighlight())
+ {
+ getMenu()->highlightNextItem(this);
+ }
+
+ getMenu()->needsArrange();
+
+ LLFloater* parent_floater = getParentFloater();
+ LLFloater* tear_off_menu = LLTearOffMenu::create(getMenu());
+
+ if (tear_off_menu)
+ {
+ if (parent_floater)
+ {
+ parent_floater->addDependentFloater(tear_off_menu, false);
+ }
+
+ // give focus to torn off menu because it will have
+ // been taken away when parent menu closes
+ tear_off_menu->setFocus(true);
+ }
+ }
+ LLMenuItemGL::onCommit();
+}
+
+void LLMenuItemTearOffGL::draw()
+{
+ // disabled items can be highlighted, but shouldn't render as such
+ if( getEnabled() && getHighlight() && !isBriefItem())
+ {
+ gGL.color4fv( mHighlightBackground.get().mV );
+ gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 );
+ }
+
+ if (getEnabled())
+ {
+ gGL.color4fv( mEnabledColor.get().mV );
+ }
+ else
+ {
+ gGL.color4fv( mDisabledColor.get().mV );
+ }
+ const S32 y = getRect().getHeight() / 3;
+ const S32 PAD = 6;
+ gl_line_2d( PAD, y, getRect().getWidth() - PAD, y );
+ gl_line_2d( PAD, y * 2, getRect().getWidth() - PAD, y * 2 );
+}
+
+U32 LLMenuItemTearOffGL::getNominalHeight( void ) const
+{
+ return TEAROFF_SEPARATOR_HEIGHT_PIXELS;
+}
+
+///============================================================================
+/// Class LLMenuItemCallGL
+///============================================================================
+
+LLMenuItemCallGL::LLMenuItemCallGL(const LLMenuItemCallGL::Params& p)
+: LLMenuItemGL(p)
+{
+}
+
+void LLMenuItemCallGL::initFromParams(const Params& p)
+{
+ if (p.on_visible.isProvided())
+ {
+ mVisibleSignal.connect(initEnableCallback(p.on_visible));
+ }
+ if (p.on_enable.isProvided())
+ {
+ setEnableCallback(initEnableCallback(p.on_enable));
+ // Set the enabled control variable (for backwards compatability)
+ if (p.on_enable.control_name.isProvided() && !p.on_enable.control_name().empty())
+ {
+ LLControlVariable* control = findControl(p.on_enable.control_name());
+ if (control)
+ {
+ setEnabledControlVariable(control);
+ }
+ else
+ {
+ LL_WARNS() << "Failed to assign 'enabled' control variable to menu " << getName()
+ << ": control " << p.on_enable.control_name()
+ << " does not exist." << LL_ENDL;
+ }
+ }
+ }
+ if (p.on_click.isProvided())
+ {
+ setCommitCallback(initCommitCallback(p.on_click));
+ }
+
+ LLUICtrl::initFromParams(p);
+}
+
+void LLMenuItemCallGL::onCommit( void )
+{
+ // RN: menu item can be deleted in callback, so beware
+ getMenu()->setItemLastSelected( this );
+
+ LLMenuItemGL::onCommit();
+}
+
+void LLMenuItemCallGL::updateEnabled( void )
+{
+ if (mEnableSignal.num_slots() > 0)
+ {
+ bool enabled = mEnableSignal(this, LLSD());
+ if (mEnabledControlVariable)
+ {
+ if (!enabled)
+ {
+ // callback overrides control variable; this will call setEnabled()
+ mEnabledControlVariable->set(false);
+ }
+ }
+ else
+ {
+ setEnabled(enabled);
+ }
+ }
+}
+
+void LLMenuItemCallGL::updateVisible( void )
+{
+ if (mVisibleSignal.num_slots() > 0)
+ {
+ bool visible = mVisibleSignal(this, LLSD());
+ setVisible(visible);
+ }
+}
+
+void LLMenuItemCallGL::buildDrawLabel( void )
+{
+ updateEnabled();
+ updateVisible();
+ LLMenuItemGL::buildDrawLabel();
+}
+
+bool LLMenuItemCallGL::handleKeyHere( KEY key, MASK mask )
+{
+ return LLMenuItemGL::handleKeyHere(key, mask);
+}
+
+bool LLMenuItemCallGL::handleAcceleratorKey( KEY key, MASK mask )
+{
+ if( (!gKeyboard->getKeyRepeated(key) || getAllowKeyRepeat()) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) )
+ {
+ updateEnabled();
+ if (getEnabled())
+ {
+ onCommit();
+ return true;
+ }
+ }
+ return false;
+}
+
+// handleRightMouseUp moved into base class LLMenuItemGL so clicks are
+// handled for all menu item types
+
+///============================================================================
+/// Class LLMenuItemCheckGL
+///============================================================================
+LLMenuItemCheckGL::LLMenuItemCheckGL (const LLMenuItemCheckGL::Params& p)
+: LLMenuItemCallGL(p)
+{
+}
+
+void LLMenuItemCheckGL::initFromParams(const Params& p)
+{
+ if (p.on_check.isProvided())
+ {
+ setCheckCallback(initEnableCallback(p.on_check));
+ // Set the control name (for backwards compatability)
+ if (p.on_check.control_name.isProvided() && !p.on_check.control_name().empty())
+ {
+ setControlName(p.on_check.control_name());
+ }
+ }
+
+ LLMenuItemCallGL::initFromParams(p);
+}
+
+void LLMenuItemCheckGL::onCommit( void )
+{
+ LLMenuItemCallGL::onCommit();
+}
+
+//virtual
+void LLMenuItemCheckGL::setValue(const LLSD& value)
+{
+ LLUICtrl::setValue(value);
+ if(value.asBoolean())
+ {
+ mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX;
+ }
+ else
+ {
+ mDrawBoolLabel.clear();
+ }
+}
+
+//virtual
+LLSD LLMenuItemCheckGL::getValue() const
+{
+ // Get our boolean value from the view model.
+ // If we don't override this method then the implementation from
+ // LLMenuItemGL will return a string. (EXT-8501)
+ return LLUICtrl::getValue();
+}
+
+// called to rebuild the draw label
+void LLMenuItemCheckGL::buildDrawLabel( void )
+{
+ // Note: mCheckSignal() returns true if no callbacks are set
+ bool checked = mCheckSignal(this, LLSD());
+ if (mControlVariable)
+ {
+ if (!checked)
+ setControlValue(false); // callback overrides control variable; this will call setValue()
+ }
+ else
+ {
+ setValue(checked);
+ }
+ if(getValue().asBoolean())
+ {
+ mDrawBoolLabel = LLMenuGL::BOOLEAN_TRUE_PREFIX;
+ }
+ else
+ {
+ mDrawBoolLabel.clear();
+ }
+ LLMenuItemCallGL::buildDrawLabel();
+}
+
+///============================================================================
+/// Class LLMenuItemBranchGL
+///============================================================================
+LLMenuItemBranchGL::LLMenuItemBranchGL(const LLMenuItemBranchGL::Params& p)
+ : LLMenuItemGL(p)
+{
+ LLMenuGL* branch = p.branch;
+ if (branch)
+ {
+ mBranchHandle = branch->getHandle();
+ branch->setVisible(false);
+ branch->setParentMenuItem(this);
+ }
+}
+
+LLMenuItemBranchGL::~LLMenuItemBranchGL()
+{
+ if (mBranchHandle.get())
+ {
+ mBranchHandle.get()->die();
+ }
+}
+
+
+
+// virtual
+LLView* LLMenuItemBranchGL::getChildView(const std::string& name, bool recurse) const
+{
+ LLMenuGL* branch = getBranch();
+ if (branch)
+ {
+ if (branch->getName() == name)
+ {
+ return branch;
+ }
+
+ // Always recurse on branches
+ return branch->getChildView(name, recurse);
+ }
+
+ return LLView::getChildView(name, recurse);
+}
+
+LLView* LLMenuItemBranchGL::findChildView(const std::string& name, bool recurse) const
+{
+ LLMenuGL* branch = getBranch();
+ if (branch)
+ {
+ if (branch->getName() == name)
+ {
+ return branch;
+ }
+
+ // Always recurse on branches
+ return branch->findChildView(name, recurse);
+ }
+
+ return LLView::findChildView(name, recurse);
+}
+
+// virtual
+bool LLMenuItemBranchGL::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ // switch to mouse navigation mode
+ LLMenuGL::setKeyboardMode(false);
+
+ onCommit();
+ make_ui_sound("UISndClickRelease");
+ return true;
+}
+
+bool LLMenuItemBranchGL::hasAccelerator(const KEY &key, const MASK &mask) const
+{
+ return getBranch() && getBranch()->hasAccelerator(key, mask);
+}
+
+bool LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask)
+{
+ return getBranch() && getBranch()->handleAcceleratorKey(key, mask);
+}
+
+// This function checks to see if the accelerator key is already in use;
+// if not, it will be added to the list
+bool LLMenuItemBranchGL::addToAcceleratorList(std::list<LLMenuKeyboardBinding*> *listp)
+{
+ LLMenuGL* branch = getBranch();
+ if (!branch)
+ return false;
+
+ U32 item_count = branch->getItemCount();
+ LLMenuItemGL *item;
+
+ while (item_count--)
+ {
+ if ((item = branch->getItem(item_count)))
+ {
+ return item->addToAcceleratorList(listp);
+ }
+ }
+
+ return false;
+}
+
+
+// called to rebuild the draw label
+void LLMenuItemBranchGL::buildDrawLabel( void )
+{
+ mDrawAccelLabel.clear();
+ std::string st = mDrawAccelLabel;
+ appendAcceleratorString( st );
+ mDrawAccelLabel = st;
+ mDrawBranchLabel = LLMenuGL::BRANCH_SUFFIX;
+}
+
+void LLMenuItemBranchGL::onCommit( void )
+{
+ openMenu();
+
+ // keyboard navigation automatically propagates highlight to sub-menu
+ // to facilitate fast menu control via jump keys
+ if (LLMenuGL::getKeyboardMode() && getBranch() && !getBranch()->getHighlightedItem())
+ {
+ getBranch()->highlightNextItem(NULL);
+ }
+
+ LLUICtrl::onCommit();
+}
+
+bool LLMenuItemBranchGL::handleKey(KEY key, MASK mask, bool called_from_parent)
+{
+ bool handled = false;
+ if (getBranch() && called_from_parent)
+ {
+ handled = getBranch()->handleKey(key, mask, called_from_parent);
+ }
+
+ if (!handled)
+ {
+ handled = LLMenuItemGL::handleKey(key, mask, called_from_parent);
+ }
+
+ return handled;
+}
+
+bool LLMenuItemBranchGL::handleUnicodeChar(llwchar uni_char, bool called_from_parent)
+{
+ bool handled = false;
+ if (getBranch() && called_from_parent)
+ {
+ handled = getBranch()->handleUnicodeChar(uni_char, true);
+ }
+
+ if (!handled)
+ {
+ handled = LLMenuItemGL::handleUnicodeChar(uni_char, called_from_parent);
+ }
+
+ return handled;
+}
+
+
+void LLMenuItemBranchGL::setHighlight( bool highlight )
+{
+ if (highlight == getHighlight())
+ return;
+
+ LLMenuGL* branch = getBranch();
+ if (!branch)
+ return;
+
+ bool auto_open = getEnabled() && (!branch->getVisible() || branch->getTornOff());
+ // torn off menus don't open sub menus on hover unless they have focus
+ LLFloater * menu_parent = dynamic_cast<LLFloater *>(getMenu()->getParent());
+ if (getMenu()->getTornOff() && menu_parent && !menu_parent->hasFocus())
+ {
+ auto_open = false;
+ }
+ // don't auto open torn off sub-menus (need to explicitly active menu item to give them focus)
+ if (branch->getTornOff())
+ {
+ auto_open = false;
+ }
+ LLMenuItemGL::setHighlight(highlight);
+ if( highlight )
+ {
+ if(auto_open)
+ {
+ openMenu();
+ }
+ }
+ else
+ {
+ if (branch->getTornOff())
+ {
+ LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent());
+ if (branch_parent)
+ {
+ branch_parent->setFocus(false);
+ }
+ branch->clearHoverItem();
+ }
+ else
+ {
+ branch->setVisible( false );
+ }
+ }
+}
+
+void LLMenuItemBranchGL::draw()
+{
+ LLMenuItemGL::draw();
+ if (getBranch() && getBranch()->getVisible() && !getBranch()->getTornOff())
+ {
+ setHighlight(true);
+ }
+}
+
+void LLMenuItemBranchGL::updateBranchParent(LLView* parentp)
+{
+ if (getBranch() && getBranch()->getParent() == NULL)
+ {
+ // make the branch menu a sibling of my parent menu
+ getBranch()->updateParent(parentp);
+ }
+}
+
+void LLMenuItemBranchGL::onVisibilityChange(bool new_visibility)
+{
+ if (!new_visibility && getBranch() && !getBranch()->getTornOff())
+ {
+ getBranch()->setVisible(false);
+ }
+ LLMenuItemGL::onVisibilityChange(new_visibility);
+}
+
+bool LLMenuItemBranchGL::handleKeyHere(KEY key, MASK mask)
+{
+ LLMenuGL* branch = getBranch();
+ if (!branch)
+ return LLMenuItemGL::handleKeyHere(key, mask);
+
+ // an item is highlighted, my menu is open, and I have an active sub menu or we are in
+ // keyboard navigation mode
+ if (getHighlight()
+ && getMenu()->isOpen()
+ && (isActive() || LLMenuGL::getKeyboardMode()))
+ {
+ if (branch->getVisible() && key == KEY_LEFT)
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ bool handled = branch->clearHoverItem();
+ if (branch->getTornOff())
+ {
+ LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent());
+ if (branch_parent)
+ {
+ branch_parent->setFocus(false);
+ }
+ }
+ if (handled && getMenu()->getTornOff())
+ {
+ LLFloater * menu_parent = dynamic_cast<LLFloater *>(getMenu()->getParent());
+ if (menu_parent)
+ {
+ menu_parent->setFocus(true);
+ }
+ }
+ return handled;
+ }
+
+ if (key == KEY_RIGHT && !branch->getHighlightedItem())
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ LLMenuItemGL* itemp = branch->highlightNextItem(NULL);
+ if (itemp)
+ {
+ return true;
+ }
+ }
+ }
+ return LLMenuItemGL::handleKeyHere(key, mask);
+}
+
+//virtual
+bool LLMenuItemBranchGL::isActive() const
+{
+ return isOpen() && getBranch() && getBranch()->getHighlightedItem();
+}
+
+//virtual
+bool LLMenuItemBranchGL::isOpen() const
+{
+ return getBranch() && getBranch()->isOpen();
+}
+
+void LLMenuItemBranchGL::openMenu()
+{
+ LLMenuGL* branch = getBranch();
+ if (!branch)
+ return;
+
+ if (branch->getTornOff())
+ {
+ 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() )
+ {
+ // get valid rectangle for menus
+ const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect();
+
+ branch->arrange();
+
+ LLRect branch_rect = branch->getRect();
+ // calculate root-view relative position for branch menu
+ S32 left = getRect().mRight;
+ S32 top = getRect().mTop - getRect().mBottom;
+
+ localPointToOtherView(left, top, &left, &top, branch->getParent());
+
+ branch_rect.setLeftTopAndSize( left, top,
+ branch_rect.getWidth(), branch_rect.getHeight() );
+
+ if (branch->getCanTearOff())
+ {
+ branch_rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS);
+ }
+ branch->setRect( branch_rect );
+
+ // if branch extends outside of menu region change the direction it opens in
+ S32 x, y;
+ S32 delta_x = 0;
+ S32 delta_y = 0;
+ branch->localPointToOtherView( 0, 0, &x, &y, branch->getParent() );
+ if( y < menu_region_rect.mBottom )
+ {
+ // open upwards if menu extends past bottom
+ // adjust by the height of the menu item branch since it is a submenu
+ 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 )
+ {
+ // move sub-menu over to left side
+ delta_x = llmax(-x, ( -(branch_rect.getWidth() + getRect().getWidth())));
+ }
+ branch->translate( delta_x, delta_y );
+
+ branch->setVisible( true );
+ branch->getParent()->sendChildToFront(branch);
+
+ dirtyRect();
+ }
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemBranchDownGL
+//
+// The LLMenuItemBranchDownGL represents a menu item that has a
+// sub-menu. This is used to make menu bar menus.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuItemBranchDownGL : public LLMenuItemBranchGL
+{
+protected:
+
+public:
+ LLMenuItemBranchDownGL( const Params& );
+
+ // returns the normal width of this control in pixels - this is
+ // used for calculating the widest item, as well as for horizontal
+ // arrangement.
+ virtual U32 getNominalWidth( void ) const;
+
+ // called to rebuild the draw label
+ virtual void buildDrawLabel( void );
+
+ // handles opening, positioning, and arranging the menu branch associated with this item
+ virtual void openMenu( void );
+
+ // set the hover status (called by it's menu) and if the object is
+ // active. This is used for behavior transfer.
+ virtual void setHighlight( bool highlight );
+
+ virtual bool isActive( void ) const;
+
+ // LLView functionality
+ virtual bool handleMouseDown( S32 x, S32 y, MASK mask );
+ virtual bool handleMouseUp( S32 x, S32 y, MASK mask );
+ virtual void draw( void );
+ virtual bool handleKeyHere(KEY key, MASK mask);
+
+ virtual bool handleAcceleratorKey(KEY key, MASK mask);
+
+ virtual void onFocusLost();
+ virtual void setFocus(bool b);
+};
+
+LLMenuItemBranchDownGL::LLMenuItemBranchDownGL( const Params& p) :
+ LLMenuItemBranchGL(p)
+{
+}
+
+// returns the normal width of this control in pixels - this is used
+// for calculating the widest item, as well as for horizontal
+// arrangement.
+U32 LLMenuItemBranchDownGL::getNominalWidth( void ) const
+{
+ U32 width = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS;
+ width += getFont()->getWidth( mLabel.getWString().c_str() );
+ return width;
+}
+
+// called to rebuild the draw label
+void LLMenuItemBranchDownGL::buildDrawLabel( void )
+{
+ mDrawAccelLabel.clear();
+ std::string st = mDrawAccelLabel;
+ appendAcceleratorString( st );
+ mDrawAccelLabel = st;
+}
+
+void LLMenuItemBranchDownGL::openMenu( void )
+{
+ LLMenuGL* branch = getBranch();
+ if( branch->getVisible() && !branch->getTornOff() )
+ {
+ branch->setVisible( false );
+ }
+ else
+ {
+ if (branch->getTornOff())
+ {
+ LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent());
+ if (branch_parent)
+ {
+ gFloaterView->bringToFront(branch_parent);
+ }
+ }
+ else
+ {
+ // We're showing the drop-down menu, so patch up its labels/rects
+ branch->arrange();
+
+ LLRect rect = branch->getRect();
+ S32 left = 0;
+ S32 top = getRect().mBottom;
+ localPointToOtherView(left, top, &left, &top, branch->getParent());
+
+ rect.setLeftTopAndSize( left, top,
+ rect.getWidth(), rect.getHeight() );
+ branch->setRect( rect );
+ S32 x = 0;
+ S32 y = 0;
+ branch->localPointToScreen( 0, 0, &x, &y );
+ S32 delta_x = 0;
+
+ LLCoordScreen window_size;
+ LLWindow* windowp = getWindow();
+ windowp->getSize(&window_size);
+
+ S32 window_width = window_size.mX;
+ if( x > window_width - rect.getWidth() )
+ {
+ delta_x = (window_width - rect.getWidth()) - x;
+ }
+ branch->translate( delta_x, 0 );
+
+ setHighlight(true);
+ branch->setVisible( true );
+ branch->getParent()->sendChildToFront(branch);
+ }
+ }
+}
+
+// set the hover status (called by it's menu)
+void LLMenuItemBranchDownGL::setHighlight( bool highlight )
+{
+ if (highlight == getHighlight())
+ return;
+
+ //NOTE: Purposely calling all the way to the base to bypass auto-open.
+ LLMenuItemGL::setHighlight(highlight);
+
+ LLMenuGL* branch = getBranch();
+ if (!branch)
+ return;
+
+ if( !highlight)
+ {
+ if (branch->getTornOff())
+ {
+ LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent());
+ if (branch_parent)
+ {
+ branch_parent->setFocus(false);
+ }
+ branch->clearHoverItem();
+ }
+ else
+ {
+ branch->setVisible( false );
+ }
+ }
+}
+
+bool LLMenuItemBranchDownGL::isActive() const
+{
+ // for top level menus, being open is sufficient to be considered
+ // active, because clicking on them with the mouse will open
+ // them, without moving keyboard focus to them
+ return isOpen();
+}
+
+bool LLMenuItemBranchDownGL::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ // switch to mouse control mode
+ LLMenuGL::setKeyboardMode(false);
+
+ if (getVisible() && isOpen())
+ {
+ LLMenuGL::sMenuContainer->hideMenus();
+ }
+ else
+ {
+ onCommit();
+ }
+
+ make_ui_sound("UISndClick");
+ return true;
+}
+
+bool LLMenuItemBranchDownGL::handleMouseUp( S32 x, S32 y, MASK mask )
+{
+ return true;
+}
+
+
+bool LLMenuItemBranchDownGL::handleAcceleratorKey(KEY key, MASK mask)
+{
+ bool branch_visible = getBranch()->getVisible();
+ bool handled = getBranch()->handleAcceleratorKey(key, mask);
+ if (handled && !branch_visible && isInVisibleChain())
+ {
+ // flash this menu entry because we triggered an invisible menu item
+ LLMenuHolderGL::setActivatedItem(this);
+ }
+
+ return handled;
+}
+void LLMenuItemBranchDownGL::onFocusLost()
+{
+ // needed for tab-based selection
+ LLMenuItemBranchGL::onFocusLost();
+ LLMenuGL::setKeyboardMode(false);
+ setHighlight(false);
+}
+
+void LLMenuItemBranchDownGL::setFocus(bool b)
+{
+ // needed for tab-based selection
+ LLMenuItemBranchGL::setFocus(b);
+ LLMenuGL::setKeyboardMode(b);
+ setHighlight(b);
+}
+
+bool LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask)
+{
+ bool menu_open = getBranch()->getVisible();
+ // don't do keyboard navigation of top-level menus unless in keyboard mode, or menu expanded
+ if (getHighlight() && getMenu()->isOpen() && (isActive() || LLMenuGL::getKeyboardMode()))
+ {
+ if (key == KEY_LEFT)
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ LLMenuItemGL* itemp = getMenu()->highlightPrevItem(this);
+ // open new menu only if previous menu was open
+ if (itemp && itemp->getEnabled() && menu_open)
+ {
+ itemp->onCommit();
+ }
+
+ return true;
+ }
+ else if (key == KEY_RIGHT)
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ LLMenuItemGL* itemp = getMenu()->highlightNextItem(this);
+ // open new menu only if previous menu was open
+ if (itemp && itemp->getEnabled() && menu_open)
+ {
+ itemp->onCommit();
+ }
+
+ return true;
+ }
+ else if (key == KEY_DOWN)
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ if (!isActive())
+ {
+ onCommit();
+ }
+ getBranch()->highlightNextItem(NULL);
+ return true;
+ }
+ else if (key == KEY_UP)
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ if (!isActive())
+ {
+ onCommit();
+ }
+ getBranch()->highlightPrevItem(NULL);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void LLMenuItemBranchDownGL::draw( void )
+{
+ //FIXME: try removing this
+ if (getBranch()->getVisible() && !getBranch()->getTornOff())
+ {
+ setHighlight(true);
+ }
+
+ if( getHighlight() )
+ {
+ gGL.color4fv( mHighlightBackground.get().mV );
+ gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 );
+ }
+
+ LLColor4 color;
+ if (getHighlight())
+ {
+ color = mHighlightForeground.get();
+ }
+ else if( getEnabled() )
+ {
+ color = mEnabledColor.get();
+ }
+ else
+ {
+ color = mDisabledColor.get();
+ }
+ getFont()->render( mLabel.getWString(), 0, (F32)getRect().getWidth() / 2.f, (F32)LABEL_BOTTOM_PAD_PIXELS, color,
+ LLFontGL::HCENTER, LLFontGL::BOTTOM, LLFontGL::NORMAL);
+
+
+ // underline navigation key only when keyboard navigation has been initiated
+ if (getMenu()->jumpKeysActive() && LLMenuGL::getKeyboardMode())
+ {
+ std::string upper_case_label = mLabel.getString();
+ LLStringUtil::toUpper(upper_case_label);
+ std::string::size_type offset = upper_case_label.find(getJumpKey());
+ if (offset != std::string::npos)
+ {
+ 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);
+ }
+ }
+}
+
+
+class LLMenuScrollItem : public LLMenuItemCallGL
+{
+public:
+ enum EArrowType
+ {
+ ARROW_DOWN,
+ ARROW_UP
+ };
+ struct ArrowTypes : public LLInitParam::TypeValuesHelper<EArrowType, ArrowTypes>
+ {
+ static void declareValues()
+ {
+ declare("up", ARROW_UP);
+ declare("down", ARROW_DOWN);
+ }
+ };
+
+ struct Params : public LLInitParam::Block<Params, LLMenuItemCallGL::Params>
+ {
+ Optional<EArrowType, ArrowTypes> arrow_type;
+ Optional<CommitCallbackParam> scroll_callback;
+ };
+
+protected:
+ LLMenuScrollItem(const Params&);
+ friend class LLUICtrlFactory;
+
+public:
+ /*virtual*/ void draw();
+ /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent);
+ /*virtual*/ void setEnabled(bool enabled);
+ virtual void onCommit( void );
+
+private:
+ LLButton* mArrowBtn;
+};
+
+LLMenuScrollItem::LLMenuScrollItem(const Params& p)
+: LLMenuItemCallGL(p)
+{
+ std::string icon;
+ if (p.arrow_type.isProvided() && p.arrow_type == ARROW_UP)
+ {
+ icon = "arrow_up.tga";
+ }
+ else
+ {
+ icon = "arrow_down.tga";
+ }
+
+ LLButton::Params bparams;
+
+ // Disabled the Return key handling by LLMenuScrollItem instead of
+ // passing the key press to the currently selected menu item. See STORM-385.
+ bparams.commit_on_return(false);
+ bparams.mouse_opaque(true);
+ bparams.scale_image(false);
+ bparams.click_callback(p.scroll_callback);
+ bparams.mouse_held_callback(p.scroll_callback);
+ bparams.follows.flags(FOLLOWS_ALL);
+ std::string background = "transparent.j2c";
+ bparams.image_unselected.name(background);
+ bparams.image_disabled.name(background);
+ bparams.image_selected.name(background);
+ bparams.image_hover_selected.name(background);
+ bparams.image_disabled_selected.name(background);
+ bparams.image_hover_unselected.name(background);
+ bparams.image_overlay.name(icon);
+
+ mArrowBtn = LLUICtrlFactory::create<LLButton>(bparams);
+ addChild(mArrowBtn);
+}
+
+/*virtual*/
+void LLMenuScrollItem::draw()
+{
+ LLUICtrl::draw();
+}
+
+/*virtual*/
+void LLMenuScrollItem::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ mArrowBtn->reshape(width, height, called_from_parent);
+ LLView::reshape(width, height, called_from_parent);
+}
+
+/*virtual*/
+void LLMenuScrollItem::setEnabled(bool enabled)
+{
+ mArrowBtn->setEnabled(enabled);
+ LLView::setEnabled(enabled);
+}
+
+void LLMenuScrollItem::onCommit( void )
+{
+ LLUICtrl::onCommit();
+}
+
+///============================================================================
+/// Class LLMenuGL
+///============================================================================
+
+LLMenuGL::LLMenuGL(const LLMenuGL::Params& p)
+: LLUICtrl(p),
+ mBackgroundColor( p.bg_color() ),
+ mBgVisible( p.bg_visible ),
+ mDropShadowed( p.drop_shadow ),
+ mHasSelection(false),
+ mHorizontalLayout( p.horizontal_layout ),
+ mScrollable(mHorizontalLayout ? false : p.scrollable), // Scrolling is supported only for vertical layout
+ mMaxScrollableItems(p.max_scrollable_items),
+ mPreferredWidth(p.preferred_width),
+ mKeepFixedSize( p.keep_fixed_size ),
+ mLabel (p.label),
+ mLastMouseX(0),
+ mLastMouseY(0),
+ mMouseVelX(0),
+ mMouseVelY(0),
+ mTornOff(false),
+ mTearOffItem(NULL),
+ mSpilloverBranch(NULL),
+ mFirstVisibleItem(NULL),
+ mArrowUpItem(NULL),
+ mArrowDownItem(NULL),
+ mSpilloverMenu(NULL),
+ mJumpKey(p.jump_key),
+ mCreateJumpKeys(p.create_jump_keys),
+ mNeedsArrange(false),
+ mAlwaysShowMenu(false),
+ mResetScrollPositionOnShow(true),
+ mShortcutPad(p.shortcut_pad),
+ mFont(p.font)
+{
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("_");
+ tokenizer tokens(p.label(), sep);
+ tokenizer::iterator token_iter;
+
+ S32 token_count = 0;
+ std::string new_menu_label;
+ for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+ {
+ new_menu_label += (*token_iter);
+ if (token_count > 0)
+ {
+ setJumpKey((*token_iter).c_str()[0]);
+ }
+ ++token_count;
+ }
+ setLabel(new_menu_label);
+
+ mFadeTimer.stop();
+}
+
+void LLMenuGL::initFromParams(const LLMenuGL::Params& p)
+{
+ LLUICtrl::initFromParams(p);
+ setCanTearOff(p.can_tear_off);
+}
+
+// Destroys the object
+LLMenuGL::~LLMenuGL( void )
+{
+ // delete the branch, as it might not be in view hierarchy
+ // leave the menu, because it is always in view hierarchy
+ delete mSpilloverBranch;
+ mJumpKeys.clear();
+}
+
+void LLMenuGL::setCanTearOff(bool tear_off)
+{
+ if (tear_off && mTearOffItem == NULL)
+ {
+ LLMenuItemTearOffGL::Params p;
+ mTearOffItem = LLUICtrlFactory::create<LLMenuItemTearOffGL>(p);
+ addChild(mTearOffItem);
+ }
+ else if (!tear_off && mTearOffItem != NULL)
+ {
+ mItems.remove(mTearOffItem);
+ removeChild(mTearOffItem);
+ delete mTearOffItem;
+ mTearOffItem = NULL;
+ needsArrange();
+ }
+}
+
+bool LLMenuGL::addChild(LLView* view, S32 tab_group)
+{
+ 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)
+ {
+ return append(item);
+ }
+
+ LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view);
+ if (menup)
+ {
+ return appendMenu(menup);
+ }
+
+ return false;
+}
+
+
+void LLMenuGL::deleteAllChildren()
+{
+ mItems.clear();
+ LLUICtrl::deleteAllChildren();
+}
+
+void LLMenuGL::removeChild( LLView* ctrl)
+{
+ // previously a dynamic_cast with if statement to check validity
+ // unfortunately removeChild is called by ~LLView, and at that point the
+ // object being deleted is no longer a LLMenuItemGL so a dynamic_cast will fail
+ LLMenuItemGL* itemp = static_cast<LLMenuItemGL*>(ctrl);
+
+ item_list_t::iterator found_it = std::find(mItems.begin(), mItems.end(), (itemp));
+ if (found_it != mItems.end())
+ {
+ mItems.erase(found_it);
+ }
+
+ return LLUICtrl::removeChild(ctrl);
+}
+
+bool LLMenuGL::postBuild()
+{
+ createJumpKeys();
+ return LLUICtrl::postBuild();
+}
+
+// are we the childmost active menu and hence our jump keys should be enabled?
+// or are we a free-standing torn-off menu (which uses jump keys too)
+bool LLMenuGL::jumpKeysActive()
+{
+ LLMenuItemGL* highlighted_item = getHighlightedItem();
+ bool active = getVisible() && getEnabled();
+
+ if (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;
+}
+
+bool LLMenuGL::isOpen()
+{
+ if (getTornOff())
+ {
+ LLMenuItemGL* itemp = getHighlightedItem();
+ // if we have an open sub-menu, then we are considered part of
+ // the open menu chain even if we don't have focus
+ if (itemp && itemp->isOpen())
+ {
+ return true;
+ }
+ // otherwise we are only active if we have keyboard focus
+ LLFloater * parent = dynamic_cast<LLFloater *>(getParent());
+ if (parent)
+ {
+ return parent->hasFocus();
+ }
+ return false;
+ }
+ else
+ {
+ // normally, menus are hidden as soon as the user focuses
+ // on another menu, so just use the visibility criterion
+ return getVisible();
+ }
+}
+
+
+
+bool LLMenuGL::scrollItems(EScrollingDirection direction)
+{
+ // Slowing down items scrolling when arrow button is held
+ if (mScrollItemsTimer.hasExpired() && NULL != mFirstVisibleItem)
+ {
+ mScrollItemsTimer.setTimerExpirySec(.033f);
+ }
+ else
+ {
+ return false;
+ }
+
+ switch (direction)
+ {
+ case SD_UP:
+ {
+ item_list_t::iterator cur_item_iter;
+ item_list_t::iterator prev_item_iter;
+ for (cur_item_iter = mItems.begin(), prev_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++)
+ {
+ if( (*cur_item_iter) == mFirstVisibleItem)
+ {
+ break;
+ }
+ if ((*cur_item_iter)->getVisible())
+ {
+ prev_item_iter = cur_item_iter;
+ }
+ }
+
+ if ((*prev_item_iter)->getVisible())
+ {
+ mFirstVisibleItem = *prev_item_iter;
+ }
+ break;
+ }
+ case SD_DOWN:
+ {
+ if (NULL == mFirstVisibleItem)
+ {
+ mFirstVisibleItem = *mItems.begin();
+ }
+
+ item_list_t::iterator cur_item_iter;
+
+ for (cur_item_iter = mItems.begin(); cur_item_iter != mItems.end(); cur_item_iter++)
+ {
+ if( (*cur_item_iter) == mFirstVisibleItem)
+ {
+ break;
+ }
+ }
+
+ item_list_t::iterator next_item_iter;
+
+ if (cur_item_iter != mItems.end())
+ {
+ for (next_item_iter = ++cur_item_iter; next_item_iter != mItems.end(); next_item_iter++)
+ {
+ if( (*next_item_iter)->getVisible())
+ {
+ break;
+ }
+ }
+
+ if (next_item_iter != mItems.end() &&
+ (*next_item_iter)->getVisible())
+ {
+ mFirstVisibleItem = *next_item_iter;
+ }
+ }
+ break;
+ }
+ case SD_BEGIN:
+ {
+ mFirstVisibleItem = *mItems.begin();
+ break;
+ }
+ case SD_END:
+ {
+ 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, scrollable_items);
+ mFirstVisibleItem = *first_visible_item_iter;
+ break;
+ }
+ default:
+ LL_WARNS() << "Unknown scrolling direction: " << direction << LL_ENDL;
+ }
+
+ mNeedsArrange = true;
+ arrangeAndClear();
+
+ return true;
+}
+
+// rearrange the child rects so they fit the shape of the menu.
+void LLMenuGL::arrange( void )
+{
+ // calculate the height & width, and set our rect based on that
+ // information.
+ const LLRect& initial_rect = getRect();
+
+ U32 width = 0, height = MENU_ITEM_PADDING;
+
+ cleanupSpilloverBranch();
+
+ if( mItems.size() )
+ {
+ const LLRect menu_region_rect = LLMenuGL::sMenuContainer ? LLMenuGL::sMenuContainer->getMenuRect() : LLRect(0, S32_MAX, S32_MAX, 0);
+
+ // 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 = 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 = LLFontGL::getFontSansSerif()->getLineHeight() + MENU_ITEM_PADDING;
+
+ // Scrolling support
+ item_list_t::iterator first_visible_item_iter;
+ item_list_t::iterator first_hidden_item_iter = mItems.end();
+ S32 height_before_first_visible_item = -1;
+ S32 visible_items_height = 0;
+ U32 scrollable_items_cnt = 0;
+
+ if (mHorizontalLayout)
+ {
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ // do first so LLMenuGLItemCall can call on_visible to determine if visible
+ (*item_iter)->buildDrawLabel();
+
+ if ((*item_iter)->getVisible())
+ {
+ if (!getTornOff()
+ && *item_iter != mSpilloverBranch
+ && width + (*item_iter)->getNominalWidth() > max_width - spillover_item_width)
+ {
+ // 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 = llmax(height, mSpilloverBranch->getNominalHeight());
+ width += mSpilloverBranch->getNominalWidth();
+
+ break;
+ }
+ else
+ {
+ // track our rect
+ height = llmax(height, (*item_iter)->getNominalHeight());
+ width += (*item_iter)->getNominalWidth();
+ }
+ }
+ }
+ }
+ else
+ {
+ for (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)
+ {
+ if ((*item_iter)->getVisible())
+ {
+ if (!getTornOff()
+ && !mScrollable
+ && *item_iter != mSpilloverBranch
+ && height + (*item_iter)->getNominalHeight() > max_height - spillover_item_height)
+ {
+ // 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)
+ {
+ 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() );
+
+ break;
+ }
+ }
+
+ // track our rect
+ height += (*item_iter)->getNominalHeight();
+ width = llmax( width, (*item_iter)->getNominalWidth() );
+
+ if (mScrollable)
+ {
+ // Determining visible items boundaries
+ if (NULL == mFirstVisibleItem)
+ {
+ mFirstVisibleItem = *item_iter;
+ }
+
+ if (*item_iter == mFirstVisibleItem)
+ {
+ height_before_first_visible_item = height - (*item_iter)->getNominalHeight();
+ first_visible_item_iter = item_iter;
+ scrollable_items_cnt = 0;
+ }
+
+ if (-1 != height_before_first_visible_item && 0 == visible_items_height &&
+ (++scrollable_items_cnt > mMaxScrollableItems ||
+ height - height_before_first_visible_item > max_height - spillover_item_height * 2 ))
+ {
+ first_hidden_item_iter = item_iter;
+ visible_items_height = height - height_before_first_visible_item - (*item_iter)->getNominalHeight();
+ scrollable_items_cnt--;
+ }
+ }
+ }
+ }
+
+ if (mPreferredWidth < U32_MAX)
+ width = llmin(mPreferredWidth, max_width);
+
+ if (mScrollable)
+ {
+ S32 max_items_height = max_height - spillover_item_height * 2;
+
+ if (visible_items_height == 0)
+ visible_items_height = height - height_before_first_visible_item;
+
+ // Fix mFirstVisibleItem value, if it doesn't allow to display all items, that can fit
+ if (visible_items_height < max_items_height && scrollable_items_cnt < mMaxScrollableItems)
+ {
+ item_list_t::iterator tmp_iter(first_visible_item_iter);
+ while (visible_items_height < max_items_height &&
+ scrollable_items_cnt < mMaxScrollableItems &&
+ first_visible_item_iter != mItems.begin())
+ {
+ if ((*first_visible_item_iter)->getVisible())
+ {
+ // It keeps visible item, after first_visible_item_iter
+ tmp_iter = first_visible_item_iter;
+ }
+
+ first_visible_item_iter--;
+
+ if ((*first_visible_item_iter)->getVisible())
+ {
+ visible_items_height += (*first_visible_item_iter)->getNominalHeight();
+ height_before_first_visible_item -= (*first_visible_item_iter)->getNominalHeight();
+ scrollable_items_cnt++;
+ }
+ }
+
+ // Roll back one item, that doesn't fit
+ if (visible_items_height > max_items_height)
+ {
+ visible_items_height -= (*first_visible_item_iter)->getNominalHeight();
+ height_before_first_visible_item += (*first_visible_item_iter)->getNominalHeight();
+ scrollable_items_cnt--;
+ first_visible_item_iter = tmp_iter;
+ }
+ if (!(*first_visible_item_iter)->getVisible())
+ {
+ first_visible_item_iter = tmp_iter;
+ }
+
+ mFirstVisibleItem = *first_visible_item_iter;
+ }
+ }
+ }
+
+ S32 cur_height = (S32)llmin(max_height, height);
+
+ if (mScrollable &&
+ (height_before_first_visible_item > MENU_ITEM_PADDING ||
+ height_before_first_visible_item + visible_items_height < (S32)height))
+ {
+ // Reserving 2 extra slots for arrow items
+ cur_height = visible_items_height + spillover_item_height * 2;
+ }
+
+ setRect(LLRect(getRect().mLeft, getRect().mTop, getRect().mLeft + width, getRect().mTop - cur_height));
+
+ S32 cur_width = 0;
+ S32 offset = 0;
+ if (mScrollable)
+ {
+ // No space for all items, creating arrow items
+ if (height_before_first_visible_item > MENU_ITEM_PADDING ||
+ height_before_first_visible_item + visible_items_height < (S32)height)
+ {
+ if (NULL == mArrowUpItem)
+ {
+ LLMenuScrollItem::Params item_params;
+ item_params.name(ARROW_UP);
+ item_params.arrow_type(LLMenuScrollItem::ARROW_UP);
+ item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItems, this, SD_UP));
+
+ mArrowUpItem = LLUICtrlFactory::create<LLMenuScrollItem>(item_params);
+ LLUICtrl::addChild(mArrowUpItem);
+
+ }
+ if (NULL == mArrowDownItem)
+ {
+ LLMenuScrollItem::Params item_params;
+ item_params.name(ARROW_DOWN);
+ item_params.arrow_type(LLMenuScrollItem::ARROW_DOWN);
+ item_params.scroll_callback.function(boost::bind(&LLMenuGL::scrollItems, this, SD_DOWN));
+
+ mArrowDownItem = LLUICtrlFactory::create<LLMenuScrollItem>(item_params);
+ LLUICtrl::addChild(mArrowDownItem);
+ }
+
+ LLRect rect;
+ mArrowUpItem->setRect(rect.setLeftTopAndSize( 0, cur_height, width, mArrowUpItem->getNominalHeight()));
+ mArrowUpItem->setVisible(true);
+ mArrowUpItem->setEnabled(height_before_first_visible_item > MENU_ITEM_PADDING);
+ mArrowUpItem->reshape(width, mArrowUpItem->getNominalHeight());
+ mArrowDownItem->setRect(rect.setLeftTopAndSize( 0, mArrowDownItem->getNominalHeight(), width, mArrowDownItem->getNominalHeight()));
+ mArrowDownItem->setVisible(true);
+ mArrowDownItem->setEnabled(height_before_first_visible_item + visible_items_height < (S32)height);
+ mArrowDownItem->reshape(width, mArrowDownItem->getNominalHeight());
+
+ cur_height -= mArrowUpItem->getNominalHeight();
+
+ offset = menu_region_rect.mRight; // This moves items behind visible area
+ }
+ else
+ {
+ if (NULL != mArrowUpItem)
+ {
+ mArrowUpItem->setVisible(false);
+ }
+ if (NULL != mArrowDownItem)
+ {
+ mArrowDownItem->setVisible(false);
+ }
+ }
+
+ }
+
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if ((*item_iter)->getVisible())
+ {
+ if (mScrollable)
+ {
+ if (item_iter == first_visible_item_iter)
+ {
+ offset = 0;
+ }
+ else if (item_iter == first_hidden_item_iter)
+ {
+ offset = menu_region_rect.mRight; // This moves items behind visible area
+ }
+ }
+
+ // setup item rect to hold label
+ LLRect rect;
+ if (mHorizontalLayout)
+ {
+ rect.setLeftTopAndSize( cur_width, height, (*item_iter)->getNominalWidth(), height);
+ cur_width += (*item_iter)->getNominalWidth();
+ }
+ else
+ {
+ rect.setLeftTopAndSize( 0 + offset, cur_height, width, (*item_iter)->getNominalHeight());
+ if (offset == 0)
+ {
+ cur_height -= (*item_iter)->getNominalHeight();
+ }
+ }
+ (*item_iter)->setRect( rect );
+ }
+ }
+
+
+ if (getTornOff())
+ {
+ LLTearOffMenu * torn_off_menu = dynamic_cast<LLTearOffMenu*>(getParent());
+ if (torn_off_menu)
+ {
+ torn_off_menu->updateSize();
+ }
+ }
+ }
+ if (mKeepFixedSize)
+ {
+ reshape(initial_rect.getWidth(), initial_rect.getHeight());
+ }
+}
+
+void LLMenuGL::arrangeAndClear( void )
+{
+ if (mNeedsArrange)
+ {
+ arrange();
+ mNeedsArrange = false;
+ }
+}
+
+void LLMenuGL::createSpilloverBranch()
+{
+ if (!mSpilloverBranch)
+ {
+ // should be NULL but delete anyway
+ delete mSpilloverMenu;
+ // technically, you can't tear off spillover menus, but we're passing the handle
+ // along just to be safe
+ LLMenuGL::Params p;
+ std::string label = LLTrans::getString("More");
+ p.name("More");
+ p.label(label);
+ p.bg_color(mBackgroundColor);
+ p.bg_visible(true);
+ p.can_tear_off(false);
+ mSpilloverMenu = new LLMenuGL(p);
+ mSpilloverMenu->updateParent(LLMenuGL::sMenuContainer);
+
+ LLMenuItemBranchGL::Params branch_params;
+ branch_params.name = "More";
+ 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);
+ }
+}
+
+void LLMenuGL::cleanupSpilloverBranch()
+{
+ if (mSpilloverBranch && mSpilloverBranch->getParent() == this)
+ {
+ // head-recursion to propagate items back up to root menu
+ mSpilloverMenu->cleanupSpilloverBranch();
+
+ // pop off spillover items
+ while (mSpilloverMenu->getItemCount())
+ {
+ LLMenuItemGL* itemp = mSpilloverMenu->getItem(0);
+ mSpilloverMenu->removeChild(itemp);
+ // put them at the end of our own list
+ addChild(itemp);
+ }
+
+ // Delete the branch, and since the branch will delete the menu,
+ // set the menu* to null.
+ delete mSpilloverBranch;
+ mSpilloverBranch = NULL;
+ mSpilloverMenu = NULL;
+ }
+}
+
+void LLMenuGL::createJumpKeys()
+{
+ if (!mCreateJumpKeys) return;
+ mCreateJumpKeys = false;
+
+ mJumpKeys.clear();
+
+ std::set<std::string> unique_words;
+ std::set<std::string> shared_words;
+
+ item_list_t::iterator item_it;
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(" ");
+
+ for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
+ {
+ std::string uppercase_label = (*item_it)->getLabel();
+ LLStringUtil::toUpper(uppercase_label);
+
+ tokenizer tokens(uppercase_label, sep);
+ tokenizer::iterator token_iter;
+ for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+ {
+ if (unique_words.find(*token_iter) != unique_words.end())
+ {
+ // this word exists in more than one menu instance
+ shared_words.insert(*token_iter);
+ }
+ else
+ {
+ // we have a new word, keep track of it
+ unique_words.insert(*token_iter);
+ }
+ }
+ }
+
+ // pre-assign specified jump keys
+ for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
+ {
+ KEY jump_key = (*item_it)->getJumpKey();
+ if(jump_key != KEY_NONE)
+ {
+ if (mJumpKeys.find(jump_key) == mJumpKeys.end())
+ {
+ mJumpKeys.insert(std::pair<KEY, LLMenuItemGL*>(jump_key, (*item_it)));
+ }
+ else
+ {
+ // this key is already spoken for,
+ // so we need to reassign it below
+ (*item_it)->setJumpKey(KEY_NONE);
+ }
+ }
+ }
+
+ for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
+ {
+ // skip over items that already have assigned jump keys
+ if ((*item_it)->getJumpKey() != KEY_NONE)
+ {
+ continue;
+ }
+ std::string uppercase_label = (*item_it)->getLabel();
+ LLStringUtil::toUpper(uppercase_label);
+
+ tokenizer tokens(uppercase_label, sep);
+ tokenizer::iterator token_iter;
+
+ bool found_key = false;
+ for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+ {
+ std::string uppercase_word = *token_iter;
+
+ // this word is not shared with other menu entries...
+ if (shared_words.find(*token_iter) == shared_words.end())
+ {
+ S32 i;
+ for(i = 0; i < (S32)uppercase_word.size(); i++)
+ {
+ char jump_key = uppercase_word[i];
+
+ if (LLStringOps::isDigit(jump_key) || (LLStringOps::isUpper(jump_key) &&
+ mJumpKeys.find(jump_key) == mJumpKeys.end()))
+ {
+ mJumpKeys.insert(std::pair<KEY, LLMenuItemGL*>(jump_key, (*item_it)));
+ (*item_it)->setJumpKey(jump_key);
+ found_key = true;
+ break;
+ }
+ }
+ }
+ if (found_key)
+ {
+ break;
+ }
+ }
+ }
+}
+
+// remove all items on the menu
+void LLMenuGL::empty( void )
+{
+ cleanupSpilloverBranch();
+
+ mItems.clear();
+ mFirstVisibleItem = NULL;
+ mArrowUpItem = NULL;
+ mArrowDownItem = NULL;
+
+ 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)
+{
+ setRect(LLRect(left, getRect().mTop, getRect().mRight, bottom));
+ needsArrange();
+}
+
+bool LLMenuGL::handleJumpKey(KEY key)
+{
+ // must perform case-insensitive comparison, so just switch to uppercase input key
+ key = toupper(key);
+ navigation_key_map_t::iterator found_it = mJumpKeys.find(key);
+ if(found_it != mJumpKeys.end() && found_it->second->getEnabled())
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ // force highlight to close old menus and open and sub-menus
+ found_it->second->setHighlight(true);
+ found_it->second->onCommit();
+
+ }
+ // if we are navigating the menus, we need to eat the keystroke
+ // so rest of UI doesn't handle it
+ return true;
+}
+
+
+// Add the menu item to this menu.
+bool LLMenuGL::append( LLMenuItemGL* item )
+{
+ if (!item) return false;
+ mItems.push_back( item );
+ LLUICtrl::addChild(item);
+ needsArrange();
+ return true;
+}
+
+// add a separator to this menu
+bool LLMenuGL::addSeparator()
+{
+ LLMenuItemSeparatorGL::Params p;
+ LLMenuItemGL* separator = LLUICtrlFactory::create<LLMenuItemSeparatorGL>(p);
+ return addChild(separator);
+}
+
+// add a menu - this will create a cascading menu
+bool LLMenuGL::appendMenu( LLMenuGL* menu )
+{
+ if( menu == this )
+ {
+ LL_ERRS() << "** Attempt to attach menu to itself. This is certainly "
+ << "a logic error." << LL_ENDL;
+ }
+ bool success = true;
+
+ LLMenuItemBranchGL::Params p;
+ p.name = menu->getName();
+ p.label = menu->getLabel();
+ p.branch = menu;
+ p.jump_key = menu->getJumpKey();
+ p.enabled_color=LLUIColorTable::instance().getColor("MenuItemEnabledColor");
+ p.disabled_color=LLUIColorTable::instance().getColor("MenuItemDisabledColor");
+ p.highlight_bg_color=LLUIColorTable::instance().getColor("MenuItemHighlightBgColor");
+ p.highlight_fg_color=LLUIColorTable::instance().getColor("MenuItemHighlightFgColor");
+
+ LLMenuItemBranchGL* branch = LLUICtrlFactory::create<LLMenuItemBranchGL>(p);
+ success &= append( branch );
+
+ // Inherit colors
+ menu->setBackgroundColor( mBackgroundColor );
+ menu->updateParent(LLMenuGL::sMenuContainer);
+ 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);
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ (*item_iter)->setEnabledSubMenus( enable );
+ }
+}
+
+// setItemEnabled() - pass the label and the enable flag for a menu
+// item. true will make sure it's enabled, false will disable it.
+void LLMenuGL::setItemEnabled( const std::string& name, bool enable )
+{
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if( (*item_iter)->getName() == name )
+ {
+ (*item_iter)->setEnabled( enable );
+ (*item_iter)->setEnabledSubMenus( enable );
+ break;
+ }
+ }
+}
+
+void LLMenuGL::setItemVisible( const std::string& name, bool visible )
+{
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if( (*item_iter)->getName() == name )
+ {
+ (*item_iter)->setVisible( visible );
+ needsArrange();
+ break;
+ }
+ }
+}
+
+
+void LLMenuGL::setItemLabel(const std::string &name, const std::string &label)
+{
+ LLMenuItemGL *item = getItem(name);
+
+ if (item)
+ item->setLabel(label);
+}
+
+void LLMenuGL::setItemLastSelected(LLMenuItemGL* item)
+{
+ if (getVisible())
+ {
+ LLMenuHolderGL::setActivatedItem(item);
+ }
+
+ // update enabled and checkmark status
+ item->buildDrawLabel();
+}
+
+// Set whether drop shadowed
+void LLMenuGL::setDropShadowed( const bool shadowed )
+{
+ mDropShadowed = shadowed;
+}
+
+void LLMenuGL::setTornOff(bool torn_off)
+{
+ mTornOff = torn_off;
+}
+
+U32 LLMenuGL::getItemCount()
+{
+ return mItems.size();
+}
+
+LLMenuItemGL* LLMenuGL::getItem(S32 number)
+{
+ if (number >= 0 && number < (S32)mItems.size())
+ {
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if (number == 0)
+ {
+ return (*item_iter);
+ }
+ number--;
+ }
+ }
+ return NULL;
+}
+
+LLMenuItemGL* LLMenuGL::getItem(std::string name)
+{
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if ((*item_iter)->getName() == name)
+ {
+ return (*item_iter);
+ }
+ }
+ return NULL;
+}
+
+LLMenuItemGL* LLMenuGL::getHighlightedItem()
+{
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if ((*item_iter)->getHighlight())
+ {
+ return (*item_iter);
+ }
+ }
+ return NULL;
+}
+
+LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, bool skip_disabled)
+{
+ if (mItems.empty()) return NULL;
+ // highlighting first item on a torn off menu is the
+ // same as giving focus to it
+ if (!cur_item && getTornOff())
+ {
+ LLFloater * parent = dynamic_cast<LLFloater *>(getParent());
+ if (parent)
+ {
+ parent->setFocus(true);
+ }
+ }
+
+ // Current item position in the items list
+ item_list_t::iterator cur_item_iter = std::find(mItems.begin(), mItems.end(), cur_item);
+
+ item_list_t::iterator next_item_iter;
+ if (cur_item_iter == mItems.end())
+ {
+ next_item_iter = mItems.begin();
+ }
+ else
+ {
+ next_item_iter = cur_item_iter;
+ next_item_iter++;
+
+ // First visible item position in the items list
+ item_list_t::iterator first_visible_item_iter = std::find(mItems.begin(), mItems.end(), mFirstVisibleItem);
+
+ if (next_item_iter == mItems.end())
+ {
+ next_item_iter = mItems.begin();
+
+ // If current item is the last in the list, the menu is scrolled to the beginning
+ // and the first item is highlighted.
+ if (mScrollable && !scrollItems(SD_BEGIN))
+ {
+ return NULL;
+ }
+ }
+ // If current item is the last visible, the menu is scrolled one item down
+ // and the next item is highlighted.
+ else if (mScrollable &&
+ (U32)std::abs(std::distance(first_visible_item_iter, next_item_iter)) >= mMaxScrollableItems)
+ {
+ // Call highlightNextItem() recursively only if the menu was successfully scrolled down.
+ // If scroll timer hasn't expired yet the menu won't be scrolled and calling
+ // highlightNextItem() will result in an endless recursion.
+ if (scrollItems(SD_DOWN))
+ {
+ return highlightNextItem(cur_item, skip_disabled);
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+ }
+
+ // when first highlighting a menu, skip over tear off menu item
+ if (mTearOffItem && !cur_item)
+ {
+ // we know the first item is the tear off menu item
+ cur_item_iter = mItems.begin();
+ next_item_iter++;
+ if (next_item_iter == mItems.end())
+ {
+ next_item_iter = mItems.begin();
+ }
+ }
+
+ while(1)
+ {
+ // skip separators and disabled/invisible items
+ if ((*next_item_iter)->getEnabled() && (*next_item_iter)->getVisible() && !dynamic_cast<LLMenuItemSeparatorGL*>(*next_item_iter))
+ {
+ if (cur_item)
+ {
+ cur_item->setHighlight(false);
+ }
+ (*next_item_iter)->setHighlight(true);
+ return (*next_item_iter);
+ }
+
+
+ if (!skip_disabled || next_item_iter == cur_item_iter)
+ {
+ break;
+ }
+
+ next_item_iter++;
+ if (next_item_iter == mItems.end())
+ {
+ if (cur_item_iter == mItems.end())
+ {
+ break;
+ }
+ next_item_iter = mItems.begin();
+ }
+ }
+
+ return NULL;
+}
+
+LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, bool skip_disabled)
+{
+ if (mItems.empty()) return NULL;
+
+ // highlighting first item on a torn off menu is the
+ // same as giving focus to it
+ if (!cur_item && getTornOff())
+ {
+ LLFloater * parent = dynamic_cast<LLFloater *>(getParent());
+ if (parent)
+ {
+ parent->setFocus(true);
+ }
+ }
+
+ // Current item reverse position from the end of the list
+ item_list_t::reverse_iterator cur_item_iter = std::find(mItems.rbegin(), mItems.rend(), cur_item);
+
+ item_list_t::reverse_iterator prev_item_iter;
+ if (cur_item_iter == mItems.rend())
+ {
+ prev_item_iter = mItems.rbegin();
+ }
+ else
+ {
+ prev_item_iter = cur_item_iter;
+ prev_item_iter++;
+
+ // First visible item reverse position in the items list
+ item_list_t::reverse_iterator first_visible_item_iter = std::find(mItems.rbegin(), mItems.rend(), mFirstVisibleItem);
+
+ if (prev_item_iter == mItems.rend())
+ {
+ prev_item_iter = mItems.rbegin();
+
+ // If current item is the first in the list, the menu is scrolled to the end
+ // and the last item is highlighted.
+ if (mScrollable && !scrollItems(SD_END))
+ {
+ return NULL;
+ }
+ }
+ // If current item is the first visible, the menu is scrolled one item up
+ // and the previous item is highlighted.
+ else if (mScrollable &&
+ std::distance(first_visible_item_iter, cur_item_iter) <= 0)
+ {
+ // Call highlightNextItem() only if the menu was successfully scrolled up.
+ // If scroll timer hasn't expired yet the menu won't be scrolled and calling
+ // highlightNextItem() will result in an endless recursion.
+ if (scrollItems(SD_UP))
+ {
+ return highlightPrevItem(cur_item, skip_disabled);
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+ }
+
+ while(1)
+ {
+ // skip separators and disabled/invisible items
+ if ((*prev_item_iter)->getEnabled() && (*prev_item_iter)->getVisible() && (*prev_item_iter)->getName() != SEPARATOR_NAME)
+ {
+ (*prev_item_iter)->setHighlight(true);
+ return (*prev_item_iter);
+ }
+
+ if (!skip_disabled || prev_item_iter == cur_item_iter)
+ {
+ break;
+ }
+
+ prev_item_iter++;
+ if (prev_item_iter == mItems.rend())
+ {
+ if (cur_item_iter == mItems.rend())
+ {
+ break;
+ }
+
+ prev_item_iter = mItems.rbegin();
+ }
+ }
+
+ return NULL;
+}
+
+void LLMenuGL::buildDrawLabels()
+{
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ (*item_iter)->buildDrawLabel();
+ }
+}
+
+void LLMenuGL::updateParent(LLView* parentp)
+{
+ if (getParent())
+ {
+ getParent()->removeChild(this);
+ }
+ if (parentp)
+ {
+ parentp->addChild(this);
+ }
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ (*item_iter)->updateBranchParent(parentp);
+ }
+}
+
+bool LLMenuGL::hasAccelerator(const KEY &key, const MASK &mask) const
+{
+ if (key == KEY_NONE)
+ {
+ return false;
+ }
+ // Note: checking this way because mAccelerators seems to be broken
+ // mAccelerators probably needs to be cleaned up or fixed
+ // It was used for dupplicate accelerator avoidance.
+ item_list_t::const_iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ LLMenuItemGL* itemp = *item_iter;
+ if (itemp->hasAccelerator(key, mask))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool LLMenuGL::handleAcceleratorKey(KEY key, MASK mask)
+{
+ // don't handle if not enabled
+ if(!getEnabled())
+ {
+ return false;
+ }
+
+ // Pass down even if not visible
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ LLMenuItemGL* itemp = *item_iter;
+ if (itemp->handleAcceleratorKey(key, mask))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool LLMenuGL::handleUnicodeCharHere( llwchar uni_char )
+{
+ if (jumpKeysActive())
+ {
+ return handleJumpKey((KEY)uni_char);
+ }
+ return false;
+}
+
+bool LLMenuGL::handleHover( S32 x, S32 y, MASK mask )
+{
+ // leave submenu in place if slope of mouse < MAX_MOUSE_SLOPE_SUB_MENU
+ bool no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0;
+ S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX;
+ S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY;
+ LLVector2 mouse_dir((F32)mouse_delta_x, (F32)mouse_delta_y);
+ mouse_dir.normVec();
+ 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 = 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;
+
+ // don't change menu focus unless mouse is moving or alt key is not held down
+ if ((llabs(mMouseVelX) > 0 ||
+ llabs(mMouseVelY) > 0) &&
+ (!mHasSelection ||
+ //(mouse_delta_x == 0 && mouse_delta_y == 0) ||
+ (mMouseVelX < 0) ||
+ llabs((F32)mMouseVelY) / llabs((F32)mMouseVelX) > MAX_MOUSE_SLOPE_SUB_MENU))
+ {
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight())
+ {
+ // moving mouse always highlights new item
+ if (mouse_delta_x != 0 || mouse_delta_y != 0)
+ {
+ ((LLMenuItemGL*)viewp)->setHighlight(false);
+ }
+ }
+ }
+
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ //RN: always call handleHover to track mGotHover status
+ // but only set highlight when mouse is moving
+ if( viewp->getVisible() &&
+ //RN: allow disabled items to be highlighted to preserve "active" menus when
+ // moving mouse through them
+ //viewp->getEnabled() &&
+ viewp->pointInView(local_x, local_y) &&
+ viewp->handleHover(local_x, local_y, mask))
+ {
+ // moving mouse always highlights new item
+ if (mouse_delta_x != 0 || mouse_delta_y != 0)
+ {
+ ((LLMenuItemGL*)viewp)->setHighlight(true);
+ LLMenuGL::setKeyboardMode(false);
+ }
+ mHasSelection = true;
+ }
+ }
+ }
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+
+ // *HACK Release the mouse capture
+ // This is done to release the mouse after the Navigation Bar "Back" or "Forward" button
+ // drop-down menu is shown. Otherwise any other view won't be able to handle mouse events
+ // until the user chooses one of the drop-down menu items.
+
+ return true;
+}
+
+bool LLMenuGL::handleScrollWheel( S32 x, S32 y, S32 clicks )
+{
+ if (!mScrollable)
+ return blockMouseEvent(x, y);
+
+ if( clicks > 0 )
+ {
+ while( clicks-- )
+ scrollItems(SD_DOWN);
+ }
+ else
+ {
+ while( clicks++ )
+ scrollItems(SD_UP);
+ }
+
+ return true;
+}
+
+
+void LLMenuGL::draw( void )
+{
+ if (mNeedsArrange)
+ {
+ arrange();
+ mNeedsArrange = false;
+ }
+ if (mDropShadowed && !mTornOff)
+ {
+ static LLUIColor color_drop_shadow = LLUIColorTable::instance().getColor("ColorDropShadow");
+ gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0,
+ color_drop_shadow, DROP_SHADOW_FLOATER);
+ }
+
+ if( mBgVisible )
+ {
+ gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0, mBackgroundColor.get() );
+ }
+ LLView::draw();
+}
+
+void LLMenuGL::drawBackground(LLMenuItemGL* itemp, F32 alpha)
+{
+ LLColor4 color = itemp->getHighlightBgColor() % alpha;
+ gGL.color4fv( color.mV );
+ LLRect item_rect = itemp->getRect();
+ gl_rect_2d( 0, item_rect.getHeight(), item_rect.getWidth(), 0);
+}
+
+void LLMenuGL::setVisible(bool visible)
+{
+ if (visible != getVisible())
+ {
+ if (!visible)
+ {
+ mFadeTimer.start();
+ clearHoverItem();
+ // reset last known mouse coordinates so
+ // we don't spoof a mouse move next time we're opened
+ mLastMouseX = 0;
+ mLastMouseY = 0;
+ }
+ else
+ {
+ mHasSelection = true;
+ mFadeTimer.stop();
+ }
+
+ LLView::setVisible(visible);
+ }
+}
+
+LLMenuGL* LLMenuGL::findChildMenuByName(const std::string& name, bool recurse) const
+{
+ LLView* view = findChildView(name, recurse);
+ if (view)
+ {
+ LLMenuItemBranchGL* branch = dynamic_cast<LLMenuItemBranchGL*>(view);
+ if (branch)
+ {
+ return branch->getBranch();
+ }
+
+ LLMenuGL* menup = dynamic_cast<LLMenuGL*>(view);
+ if (menup)
+ {
+ return menup;
+ }
+ }
+ LL_WARNS() << "Child Menu " << name << " not found in menu " << getName() << LL_ENDL;
+ return NULL;
+}
+
+bool LLMenuGL::clearHoverItem()
+{
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLMenuItemGL* itemp = (LLMenuItemGL*)*child_it;
+ if (itemp->getHighlight())
+ {
+ itemp->setHighlight(false);
+ return true;
+ }
+ }
+ return false;
+}
+
+void hide_top_view( LLView* view )
+{
+ if( view ) view->setVisible( false );
+}
+
+
+// x and y are the desired location for the popup, in the spawning_view's
+// coordinate frame, NOT necessarily the mouse location
+// static
+void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y, S32 mouse_x, S32 mouse_y)
+{
+ const S32 CURSOR_HEIGHT = 22; // Approximate "normal" cursor size
+ const S32 CURSOR_WIDTH = 12;
+
+ if (menu->getChildList()->empty())
+ {
+ return;
+ }
+
+ menu->setVisible( true );
+
+ if(!menu->getAlwaysShowMenu())
+ {
+ //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;
+ }
+ }
+
+ // Resetting scrolling position
+ if (menu->isScrollable() && menu->isScrollPositionOnShowReset())
+ {
+ menu->mFirstVisibleItem = NULL;
+ }
+
+ // Fix menu rect if needed.
+ menu->needsArrange();
+ menu->arrangeAndClear();
+
+ if ((mouse_x == 0) || (mouse_y == 0))
+
+ {
+ // Save click point for detecting cursor moves before mouse-up.
+ // Must be in local coords to compare with mouseUp events.
+ // If the mouse doesn't move, the menu will stay open ala the Mac.
+ // See also LLContextMenu::show()
+
+ LLUI::getInstance()->getMousePositionLocal(menu->getParent(), &mouse_x, &mouse_y);
+ }
+
+
+ LLMenuHolderGL::sContextMenuSpawnPos.set(mouse_x,mouse_y);
+
+ const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getRect();
+
+ const S32 HPAD = 2;
+ LLRect rect = menu->getRect();
+ S32 left = x + HPAD;
+ S32 top = y;
+ spawning_view->localPointToOtherView(left, top, &left, &top, menu->getParent());
+ rect.setLeftTopAndSize( left, top,
+ rect.getWidth(), rect.getHeight() );
+ menu->setRect( rect );
+
+
+ // Adjust context menu to fit onscreen
+ LLRect mouse_rect;
+ const S32 MOUSE_CURSOR_PADDING = 5;
+ mouse_rect.setLeftTopAndSize(mouse_x - MOUSE_CURSOR_PADDING,
+ mouse_y + MOUSE_CURSOR_PADDING,
+ CURSOR_WIDTH + MOUSE_CURSOR_PADDING * 2,
+ CURSOR_HEIGHT + MOUSE_CURSOR_PADDING * 2);
+ menu->translateIntoRectWithExclusion( menu_region_rect, mouse_rect );
+ 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);
+}
+
+///============================================================================
+/// Class LLMenuBarGL
+///============================================================================
+
+static LLDefaultChildRegistry::Register<LLMenuBarGL> r2("menu_bar");
+
+LLMenuBarGL::LLMenuBarGL( const Params& p )
+: LLMenuGL(p),
+ mAltKeyTrigger(false)
+{}
+
+// Default destructor
+LLMenuBarGL::~LLMenuBarGL()
+{
+ std::for_each(mAccelerators.begin(), mAccelerators.end(), DeletePointer());
+ mAccelerators.clear();
+}
+
+bool LLMenuBarGL::handleAcceleratorKey(KEY key, MASK mask)
+{
+ if (getHighlightedItem() && mask == MASK_NONE)
+ {
+ // unmodified key accelerators are ignored when navigating menu
+ // (but are used as jump keys so will still work when appropriate menu is up)
+ return false;
+ }
+ bool result = LLMenuGL::handleAcceleratorKey(key, mask);
+ if (result && mask & MASK_ALT)
+ {
+ // ALT key used to trigger hotkey, don't use as shortcut to open menu
+ mAltKeyTrigger = false;
+ }
+
+ if(!result
+ && (key == KEY_F10 && mask == MASK_CONTROL)
+ && !gKeyboard->getKeyRepeated(key)
+ && isInVisibleChain())
+ {
+ if (getHighlightedItem())
+ {
+ clearHoverItem();
+ LLMenuGL::setKeyboardMode(false);
+ }
+ else
+ {
+ // close menus originating from other menu bars when first opening menu via keyboard
+ LLMenuGL::sMenuContainer->hideMenus();
+ highlightNextItem(NULL);
+ LLMenuGL::setKeyboardMode(true);
+ }
+ return true;
+ }
+
+ if (result && !getHighlightedItem() && LLMenuGL::sMenuContainer->hasVisibleMenu())
+ {
+ // close menus originating from other menu bars
+ LLMenuGL::sMenuContainer->hideMenus();
+ }
+
+ return result;
+}
+
+bool LLMenuBarGL::handleKeyHere(KEY key, MASK mask)
+{
+ static LLUICachedControl<bool> use_altkey_for_menus ("UseAltKeyForMenus", 0);
+ if(key == KEY_ALT && !gKeyboard->getKeyRepeated(key) && use_altkey_for_menus)
+ {
+ mAltKeyTrigger = true;
+ }
+ else // if any key other than ALT hit, clear out waiting for Alt key mode
+ {
+ mAltKeyTrigger = false;
+ }
+
+ if (key == KEY_ESCAPE && mask == MASK_NONE)
+ {
+ LLMenuGL::setKeyboardMode(false);
+ // if any menus are visible, this will return true, stopping further processing of ESCAPE key
+ return LLMenuGL::sMenuContainer->hideMenus();
+ }
+
+ // before processing any other key, check to see if ALT key has triggered menu access
+ checkMenuTrigger();
+
+ return LLMenuGL::handleKeyHere(key, mask);
+}
+
+bool LLMenuBarGL::handleJumpKey(KEY key)
+{
+ // perform case-insensitive comparison
+ key = toupper(key);
+ navigation_key_map_t::iterator found_it = mJumpKeys.find(key);
+ if(found_it != mJumpKeys.end() && found_it->second->getEnabled())
+ {
+ // switch to keyboard navigation mode
+ LLMenuGL::setKeyboardMode(true);
+
+ found_it->second->setHighlight(true);
+ found_it->second->onCommit();
+ }
+ return true;
+}
+
+bool LLMenuBarGL::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // clicks on menu bar closes existing menus from other contexts but leave
+ // own menu open so that we get toggle behavior
+ if (!getHighlightedItem() || !getHighlightedItem()->isActive())
+ {
+ LLMenuGL::sMenuContainer->hideMenus();
+ }
+
+ return LLMenuGL::handleMouseDown(x, y, mask);
+}
+
+bool LLMenuBarGL::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ return LLMenuGL::handleMouseDown(x, y, mask);
+}
+
+void LLMenuBarGL::draw()
+{
+ LLMenuItemGL* itemp = getHighlightedItem();
+ // If we are in mouse-control mode and the mouse cursor is not hovering over
+ // the current highlighted menu item and it isn't open, then remove the
+ // highlight. This is done via a polling mechanism here, as we don't receive
+ // notifications when the mouse cursor moves off of us
+ if (itemp && !itemp->isOpen() && !itemp->getHover() && !LLMenuGL::getKeyboardMode())
+ {
+ clearHoverItem();
+ }
+
+ checkMenuTrigger();
+
+ LLMenuGL::draw();
+}
+
+
+void LLMenuBarGL::checkMenuTrigger()
+{
+ // has the ALT key been pressed and subsequently released?
+ if (mAltKeyTrigger && !gKeyboard->getKeyDown(KEY_ALT))
+ {
+ // if alt key was released quickly, treat it as a menu access key
+ // otherwise it was probably an Alt-zoom or similar action
+ static LLUICachedControl<F32> menu_access_key_time ("MenuAccessKeyTime", 0);
+ if (gKeyboard->getKeyElapsedTime(KEY_ALT) <= menu_access_key_time ||
+ gKeyboard->getKeyElapsedFrameCount(KEY_ALT) < 2)
+ {
+ if (getHighlightedItem())
+ {
+ clearHoverItem();
+ }
+ else
+ {
+ // close menus originating from other menu bars
+ LLMenuGL::sMenuContainer->hideMenus();
+
+ highlightNextItem(NULL);
+ LLMenuGL::setKeyboardMode(true);
+ }
+ }
+ mAltKeyTrigger = false;
+ }
+}
+
+bool LLMenuBarGL::jumpKeysActive()
+{
+ // require user to be in keyboard navigation mode to activate key triggers
+ // as menu bars are always visible and it is easy to leave the mouse cursor over them
+ return LLMenuGL::getKeyboardMode() && getHighlightedItem() && LLMenuGL::jumpKeysActive();
+}
+
+// rearrange the child rects so they fit the shape of the menu bar.
+void LLMenuBarGL::arrange( void )
+{
+ U32 pos = 0;
+ LLRect rect( 0, getRect().getHeight(), 0, 0 );
+ item_list_t::const_iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ LLMenuItemGL* item = *item_iter;
+ if (item->getVisible())
+ {
+ rect.mLeft = pos;
+ pos += item->getNominalWidth();
+ rect.mRight = pos;
+ item->setRect( rect );
+ item->buildDrawLabel();
+ }
+ }
+ reshape(rect.mRight, rect.getHeight());
+}
+
+
+S32 LLMenuBarGL::getRightmostMenuEdge()
+{
+ // Find the last visible menu
+ item_list_t::reverse_iterator item_iter;
+ for (item_iter = mItems.rbegin(); item_iter != mItems.rend(); ++item_iter)
+ {
+ if ((*item_iter)->getVisible())
+ {
+ break;
+ }
+ }
+
+ if (item_iter == mItems.rend())
+ {
+ return 0;
+ }
+ return (*item_iter)->getRect().mRight;
+}
+
+// add a vertical separator to this menu
+bool LLMenuBarGL::addSeparator()
+{
+ LLMenuItemGL* separator = new LLMenuItemVerticalSeparatorGL();
+ return append( separator );
+}
+
+// add a menu - this will create a drop down menu.
+bool LLMenuBarGL::appendMenu( LLMenuGL* menu )
+{
+ if( menu == this )
+ {
+ LL_ERRS() << "** Attempt to attach menu to itself. This is certainly "
+ << "a logic error." << LL_ENDL;
+ }
+
+ bool success = true;
+
+ // *TODO: Hack! Fix this
+ LLMenuItemBranchDownGL::Params p;
+ p.name = menu->getName();
+ p.label = menu->getLabel();
+ p.visible = menu->getVisible();
+ 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");
+ p.font = menu->getFont();
+
+ LLMenuItemBranchDownGL* branch = LLUICtrlFactory::create<LLMenuItemBranchDownGL>(p);
+ success &= branch->addToAcceleratorList(&mAccelerators);
+ success &= append( branch );
+ branch->setJumpKey(branch->getJumpKey());
+ menu->updateParent(LLMenuGL::sMenuContainer);
+
+ return success;
+}
+
+bool LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask )
+{
+ bool handled = false;
+ LLView* active_menu = NULL;
+
+ bool no_mouse_data = mLastMouseX == 0 && mLastMouseY == 0;
+ S32 mouse_delta_x = no_mouse_data ? 0 : x - mLastMouseX;
+ S32 mouse_delta_y = no_mouse_data ? 0 : y - mLastMouseY;
+ mMouseVelX = (mMouseVelX / 2) + (mouse_delta_x / 2);
+ mMouseVelY = (mMouseVelY / 2) + (mouse_delta_y / 2);
+ mLastMouseX = x;
+ mLastMouseY = y;
+
+ // if nothing currently selected or mouse has moved since last call, pick menu item via mouse
+ // otherwise let keyboard control it
+ if (!getHighlightedItem() || !LLMenuGL::getKeyboardMode() || llabs(mMouseVelX) > 0 || llabs(mMouseVelY) > 0)
+ {
+ // find current active menu
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if (((LLMenuItemGL*)viewp)->isOpen())
+ {
+ active_menu = viewp;
+ }
+ }
+
+ // check for new active menu
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ if( viewp->getVisible() &&
+ viewp->getEnabled() &&
+ viewp->pointInView(local_x, local_y) &&
+ viewp->handleHover(local_x, local_y, mask))
+ {
+ ((LLMenuItemGL*)viewp)->setHighlight(true);
+ handled = true;
+ if (active_menu && active_menu != viewp)
+ {
+ ((LLMenuItemGL*)viewp)->onCommit();
+ LLMenuGL::setKeyboardMode(false);
+ }
+ LLMenuGL::setKeyboardMode(false);
+ }
+ }
+
+ if (handled)
+ {
+ // set hover false on inactive menus
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight())
+ {
+ ((LLMenuItemGL*)viewp)->setHighlight(false);
+ }
+ }
+ }
+ }
+
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+
+ return true;
+}
+
+///============================================================================
+/// Class LLMenuHolderGL
+///============================================================================
+LLCoordGL LLMenuHolderGL::sContextMenuSpawnPos(S32_MAX, S32_MAX);
+
+LLMenuHolderGL::LLMenuHolderGL(const LLMenuHolderGL::Params& p)
+ : LLPanel(p)
+{
+ sItemActivationTimer.stop();
+ mCanHide = true;
+}
+
+void LLMenuHolderGL::draw()
+{
+ LLView::draw();
+ // now draw last selected item as overlay
+ LLMenuItemGL* selecteditem = (LLMenuItemGL*)sItemLastSelectedHandle.get();
+ if (selecteditem && selecteditem->getVisible() && sItemActivationTimer.getStarted() && sItemActivationTimer.getElapsedTimeF32() < ACTIVATE_HIGHLIGHT_TIME)
+ {
+ // make sure toggle items, for example, show the proper state when fading out
+ selecteditem->buildDrawLabel();
+
+ LLRect item_rect;
+ selecteditem->localRectToOtherView(selecteditem->getLocalRect(), &item_rect, this);
+
+ F32 interpolant = sItemActivationTimer.getElapsedTimeF32() / ACTIVATE_HIGHLIGHT_TIME;
+
+ LLUI::pushMatrix();
+ {
+ LLUI::translate((F32)item_rect.mLeft, (F32)item_rect.mBottom);
+ selecteditem->getMenu()->drawBackground(selecteditem, interpolant);
+ selecteditem->draw();
+ }
+ LLUI::popMatrix();
+ }
+}
+
+bool LLMenuHolderGL::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ bool handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
+ if (!handled)
+ {
+ LLMenuGL* visible_menu = (LLMenuGL*)getVisibleMenu();
+ LLMenuItemGL* parent_menu = visible_menu ? visible_menu->getParentMenuItem() : NULL;
+ if (parent_menu && parent_menu->getVisible())
+ {
+ // don't hide menu if parent was hit
+ LLRect parent_rect;
+ parent_menu->localRectToOtherView(parent_menu->getLocalRect(), &parent_rect, this);
+ if (!parent_rect.pointInRect(x, y))
+ {
+ // clicked off of menu and parent, hide them all
+ hideMenus();
+ }
+ }
+ else
+ {
+ // no visible parent, clicked off of menu, hide them all
+ hideMenus();
+ }
+ }
+ return handled;
+}
+
+bool LLMenuHolderGL::handleRightMouseDown( S32 x, S32 y, MASK mask )
+{
+ bool handled = LLView::childrenHandleRightMouseDown(x, y, mask) != NULL;
+ if (!handled)
+ {
+ // clicked off of menu, hide them all
+ hideMenus();
+ }
+ return handled;
+}
+
+// This occurs when you mouse-down to spawn a context menu, hold the button
+// down, move off the menu, then mouse-up. We want this to close the menu.
+bool LLMenuHolderGL::handleRightMouseUp( S32 x, S32 y, MASK mask )
+{
+ const S32 SLOP = 2;
+ S32 spawn_dx = (x - sContextMenuSpawnPos.mX);
+ S32 spawn_dy = (y - sContextMenuSpawnPos.mY);
+ if (-SLOP <= spawn_dx && spawn_dx <= SLOP
+ && -SLOP <= spawn_dy && spawn_dy <= SLOP)
+ {
+ // we're still inside the slop region from spawning this menu
+ // so interpret the mouse-up as a single-click to show and leave on
+ // screen
+ sContextMenuSpawnPos.set(S32_MAX, S32_MAX);
+ return true;
+ }
+
+ bool handled = LLView::childrenHandleRightMouseUp(x, y, mask) != NULL;
+ if (!handled)
+ {
+ // clicked off of menu, hide them all
+ hideMenus();
+ }
+ return handled;
+}
+
+bool LLMenuHolderGL::handleKey(KEY key, MASK mask, bool called_from_parent)
+{
+ bool handled = false;
+ LLMenuGL* const pMenu = dynamic_cast<LLMenuGL*>(getVisibleMenu());
+
+ if (pMenu)
+ {
+ //eat TAB key - EXT-7000
+ if (key == KEY_TAB && mask == MASK_NONE)
+ {
+ return true;
+ }
+
+ //handle ESCAPE and RETURN key
+ handled = LLPanel::handleKey(key, mask, called_from_parent);
+ if (!handled)
+ {
+ if (pMenu->getHighlightedItem())
+ {
+ handled = pMenu->handleKey(key, mask, true);
+ }
+ else if (mask == MASK_NONE || (key >= KEY_LEFT && key <= KEY_DOWN))
+ {
+ //highlight first enabled one
+ if(pMenu->highlightNextItem(NULL))
+ {
+ handled = true;
+ }
+ }
+ }
+ }
+
+ return handled;
+
+}
+
+void LLMenuHolderGL::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ if (width != getRect().getWidth() || height != getRect().getHeight())
+ {
+ hideMenus();
+ }
+ LLView::reshape(width, height, called_from_parent);
+}
+
+LLView* const LLMenuHolderGL::getVisibleMenu() const
+{
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if (viewp->getVisible() && dynamic_cast<LLMenuGL*>(viewp) != NULL)
+ {
+ return viewp;
+ }
+ }
+ return NULL;
+}
+
+
+bool LLMenuHolderGL::hideMenus()
+{
+ if (!mCanHide)
+ {
+ return false;
+ }
+ LLMenuGL::setKeyboardMode(false);
+ bool menu_visible = hasVisibleMenu();
+ if (menu_visible)
+ {
+ // clicked off of menu, hide them all
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if (dynamic_cast<LLMenuGL*>(viewp) != NULL && viewp->getVisible())
+ {
+ viewp->setVisible(false);
+ }
+ }
+ }
+ //if (gFocusMgr.childHasKeyboardFocus(this))
+ //{
+ // gFocusMgr.setKeyboardFocus(NULL);
+ //}
+
+ return menu_visible;
+}
+
+void LLMenuHolderGL::setActivatedItem(LLMenuItemGL* item)
+{
+ sItemLastSelectedHandle = item->getHandle();
+ sItemActivationTimer.start();
+}
+
+///============================================================================
+/// Class LLTearOffMenu
+///============================================================================
+LLTearOffMenu::LLTearOffMenu(LLMenuGL* menup) :
+ LLFloater(LLSD()),
+ mQuitRequested(false)
+{
+ S32 floater_header_size = getHeaderHeight();
+
+ setName(menup->getName());
+ setTitle(menup->getLabel());
+ setCanMinimize(false);
+ // flag menu as being torn off
+ menup->setTornOff(true);
+ // update menu layout as torn off menu (no spillover menus)
+ menup->needsArrange();
+
+ LLRect rect;
+ menup->localRectToOtherView(LLRect(-1, menup->getRect().getHeight(), menup->getRect().getWidth() + 3, 0), &rect, gFloaterView);
+ // make sure this floater is big enough for menu
+ mTargetHeight = rect.getHeight() + floater_header_size;
+ reshape(rect.getWidth(), rect.getHeight());
+ setRect(rect);
+
+ // attach menu to floater
+ menup->setFollows( FOLLOWS_LEFT | FOLLOWS_BOTTOM );
+ mOldParent = menup->getParent();
+ addChild(menup);
+ menup->setVisible(true);
+ LLRect menu_rect = menup->getRect();
+ menu_rect.setOriginAndSize( 1, 1,
+ menu_rect.getWidth(), menu_rect.getHeight());
+ menup->setRect(menu_rect);
+ menup->setDropShadowed(false);
+
+ mMenu = menup;
+
+ // highlight first item (tear off item will be disabled)
+ mMenu->highlightNextItem(NULL);
+
+ // Can't do this in postBuild() because that is only called for floaters
+ // constructed from XML.
+ mCloseSignal.connect(boost::bind(&LLTearOffMenu::closeTearOff, this));
+}
+
+LLTearOffMenu::~LLTearOffMenu()
+{
+}
+
+void LLTearOffMenu::draw()
+{
+ mMenu->setBackgroundVisible(isBackgroundOpaque());
+
+ if (getRect().getHeight() != mTargetHeight)
+ {
+ // animate towards target height
+ reshape(getRect().getWidth(), llceil(lerp((F32)getRect().getHeight(), (F32)mTargetHeight, LLSmoothInterpolation::getInterpolant(0.05f))));
+ }
+ mMenu->needsArrange();
+ LLFloater::draw();
+}
+
+void LLTearOffMenu::onFocusReceived()
+{
+ if (mQuitRequested)
+ {
+ return;
+ }
+
+ // if nothing is highlighted, just highlight first item
+ if (!mMenu->getHighlightedItem())
+ {
+ mMenu->highlightNextItem(NULL);
+ }
+
+ // parent menu items get highlights so navigation logic keeps working
+ LLMenuItemGL* parent_menu_item = mMenu->getParentMenuItem();
+ while(parent_menu_item)
+ {
+ if (parent_menu_item->getMenu()->getVisible())
+ {
+ parent_menu_item->setHighlight(true);
+ parent_menu_item = parent_menu_item->getMenu()->getParentMenuItem();
+ }
+ else
+ {
+ break;
+ }
+ }
+ LLFloater::onFocusReceived();
+}
+
+void LLTearOffMenu::onFocusLost()
+{
+ // remove highlight from parent item and our own menu
+ mMenu->clearHoverItem();
+ LLFloater::onFocusLost();
+}
+
+bool LLTearOffMenu::handleUnicodeChar(llwchar uni_char, bool called_from_parent)
+{
+ // pass keystrokes down to menu
+ return mMenu->handleUnicodeChar(uni_char, true);
+}
+
+bool LLTearOffMenu::handleKeyHere(KEY key, MASK mask)
+{
+ if (!mMenu->getHighlightedItem())
+ {
+ if (key == KEY_UP)
+ {
+ mMenu->highlightPrevItem(NULL);
+ return true;
+ }
+ else if (key == KEY_DOWN)
+ {
+ mMenu->highlightNextItem(NULL);
+ return true;
+ }
+ }
+ // pass keystrokes down to menu
+ return mMenu->handleKey(key, mask, true);
+}
+
+void LLTearOffMenu::translate(S32 x, S32 y)
+{
+ if (x != 0 && y != 0)
+ {
+ // hide open sub-menus by clearing current hover item
+ mMenu->clearHoverItem();
+ }
+ LLFloater::translate(x, y);
+}
+
+//static
+LLTearOffMenu* LLTearOffMenu::create(LLMenuGL* menup)
+{
+ LLTearOffMenu* tearoffp = new LLTearOffMenu(menup);
+ // keep onscreen
+ gFloaterView->adjustToFitScreen(tearoffp, false);
+ tearoffp->openFloater(LLSD());
+
+ return tearoffp;
+}
+
+void LLTearOffMenu::updateSize()
+{
+ if (mMenu)
+ {
+ S32 floater_header_size = getHeaderHeight();
+ const LLRect &floater_rect = getRect();
+ LLRect new_rect;
+ mMenu->localRectToOtherView(LLRect(-1, mMenu->getRect().getHeight() + floater_header_size, mMenu->getRect().getWidth() + 3, 0), &new_rect, gFloaterView);
+
+ if (floater_rect.getWidth() != new_rect.getWidth()
+ || mTargetHeight != new_rect.getHeight())
+ {
+ // make sure this floater is big enough for menu
+ mTargetHeight = new_rect.getHeight();
+ reshape(new_rect.getWidth(), mTargetHeight);
+
+ // Restore menu position
+ LLRect menu_rect = mMenu->getRect();
+ menu_rect.setOriginAndSize(1, 1,
+ menu_rect.getWidth(), menu_rect.getHeight());
+ mMenu->setRect(menu_rect);
+ }
+ }
+}
+
+void LLTearOffMenu::closeTearOff()
+{
+ removeChild(mMenu);
+ mOldParent->addChild(mMenu);
+ mMenu->clearHoverItem();
+ mMenu->setFollowsNone();
+ mMenu->setBackgroundVisible(true);
+ mMenu->setVisible(false);
+ mMenu->setTornOff(false);
+ mMenu->setDropShadowed(true);
+ mQuitRequested = true;
+}
+
+LLContextMenuBranch::LLContextMenuBranch(const LLContextMenuBranch::Params& p)
+: LLMenuItemGL(p)
+{
+ LLContextMenu* branch = static_cast<LLContextMenu*>(p.branch);
+ if (branch)
+ {
+ mBranch = branch->getHandle();
+ branch->hide();
+ branch->setParentMenuItem(this);
+ }
+}
+
+LLContextMenuBranch::~LLContextMenuBranch()
+{
+ if (mBranch.get())
+ {
+ mBranch.get()->die();
+ }
+}
+
+// called to rebuild the draw label
+void LLContextMenuBranch::buildDrawLabel( void )
+{
+ auto menu = getBranch();
+ if (menu)
+ {
+ // default enablement is this -- if any of the subitems are
+ // enabled, this item is enabled. JC
+ U32 sub_count = menu->getItemCount();
+ U32 i;
+ bool any_enabled = false;
+ for (i = 0; i < sub_count; i++)
+ {
+ LLMenuItemGL* item = menu->getItem(i);
+ item->buildDrawLabel();
+ if (item->getEnabled() && !item->getDrawTextDisabled() )
+ {
+ any_enabled = true;
+ break;
+ }
+ }
+ setDrawTextDisabled(!any_enabled);
+ setEnabled(true);
+ }
+
+ mDrawAccelLabel.clear();
+ std::string st = mDrawAccelLabel;
+ appendAcceleratorString( st );
+ mDrawAccelLabel = st;
+
+ mDrawBranchLabel = LLMenuGL::BRANCH_SUFFIX;
+}
+
+void LLContextMenuBranch::showSubMenu()
+{
+ auto menu = getBranch();
+ if(menu)
+ {
+ LLMenuItemGL* menu_item = menu->getParentMenuItem();
+ if (menu_item != NULL && menu_item->getVisible())
+ {
+ S32 center_x;
+ S32 center_y;
+ localPointToScreen(getRect().getWidth(), getRect().getHeight(), &center_x, &center_y);
+ menu->show(center_x, center_y);
+ }
+ }
+}
+
+// onCommit() - do the primary funcationality of the menu item.
+void LLContextMenuBranch::onCommit( void )
+{
+ showSubMenu();
+
+}
+void LLContextMenuBranch::setHighlight( bool highlight )
+{
+ if (highlight == getHighlight()) return;
+ LLMenuItemGL::setHighlight(highlight);
+ auto menu = getBranch();
+ if (menu)
+ {
+ if (highlight)
+ {
+ showSubMenu();
+ }
+ else
+ {
+ menu->hide();
+ }
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+//-----------------------------------------------------------------------------
+// class LLContextMenu
+// A context menu
+//-----------------------------------------------------------------------------
+static LLDefaultChildRegistry::Register<LLContextMenu> context_menu_register("context_menu");
+static MenuRegistry::Register<LLContextMenu> context_menu_register2("context_menu");
+
+
+LLContextMenu::LLContextMenu(const Params& p)
+: LLMenuGL(p),
+ mHoveredAnyItem(false),
+ mHoverItem(NULL)
+{
+ //setBackgroundVisible(true);
+}
+
+void LLContextMenu::setVisible(bool visible)
+{
+ if (!visible)
+ hide();
+}
+
+// Takes cursor position in screen space?
+void LLContextMenu::show(S32 x, S32 y, LLView* spawning_view)
+{
+ if (getChildList()->empty())
+ {
+ // nothing to show, so abort
+ 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.
+ // See also LLMenuGL::showPopup()
+ LLMenuHolderGL::sContextMenuSpawnPos.set(x,y);
+
+ arrangeAndClear();
+
+ S32 width = getRect().getWidth();
+ S32 height = getRect().getHeight();
+ const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect();
+ LLView* parent_view = getParent();
+
+ // Open upwards if menu extends past bottom
+ if (y - height < menu_region_rect.mBottom)
+ {
+ if (getParentMenuItem()) // Adjust if this is a submenu
+ {
+ y += height - getParentMenuItem()->getNominalHeight();
+ }
+ else
+ {
+ y += height;
+ }
+ }
+
+ // Open out to the left if menu extends past right edge
+ if (x + width > menu_region_rect.mRight)
+ {
+ if (getParentMenuItem())
+ {
+ x -= getParentMenuItem()->getRect().getWidth() + width;
+ }
+ else
+ {
+ x -= width;
+ }
+ }
+
+ S32 local_x, local_y;
+ parent_view->screenPointToLocal(x, y, &local_x, &local_y);
+
+ LLRect rect;
+ rect.setLeftTopAndSize(local_x, local_y, width, height);
+ setRect(rect);
+ arrange();
+
+ if (spawning_view)
+ {
+ mSpawningViewHandle = spawning_view->getHandle();
+ }
+ else
+ {
+ mSpawningViewHandle.markDead();
+ }
+ LLView::setVisible(true);
+}
+
+void LLContextMenu::hide()
+{
+ if (!getVisible()) return;
+
+ LLView::setVisible(false);
+
+ if (mHoverItem)
+ {
+ mHoverItem->setHighlight( false );
+ }
+ mHoverItem = NULL;
+}
+
+
+bool LLContextMenu::handleHover( S32 x, S32 y, MASK mask )
+{
+ LLMenuGL::handleHover(x,y,mask);
+
+ bool handled = false;
+
+ LLMenuItemGL *item = getHighlightedItem();
+
+ if (item && item->getEnabled())
+ {
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ handled = true;
+
+ if (item != mHoverItem)
+ {
+ if (mHoverItem)
+ {
+ mHoverItem->setHighlight( false );
+ }
+ mHoverItem = item;
+ mHoverItem->setHighlight( true );
+ }
+ mHoveredAnyItem = true;
+ }
+ else
+ {
+ // clear out our selection
+ if (mHoverItem)
+ {
+ mHoverItem->setHighlight(false);
+ mHoverItem = NULL;
+ }
+ }
+
+ if( !handled && pointInView( x, y ) )
+ {
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ handled = true;
+ }
+
+ return handled;
+}
+
+// handleMouseDown and handleMouseUp are handled by LLMenuGL
+
+
+bool LLContextMenu::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ bool handled = false;
+
+ // The click was somewhere within our rectangle
+ LLMenuItemGL *item = getHighlightedItem();
+
+ S32 local_x = x - getRect().mLeft;
+ S32 local_y = y - getRect().mBottom;
+
+ bool clicked_in_menu = pointInView(local_x, local_y) ;
+
+ // grab mouse if right clicking anywhere within pie (even deadzone in middle), to detect drag outside of pie
+ if (clicked_in_menu)
+ {
+ // capture mouse cursor as if on initial menu show
+ handled = true;
+ }
+
+ if (item)
+ {
+ // lie to the item about where the click happened
+ // to make sure it's within the item's rectangle
+ if (item->handleMouseDown( 0, 0, mask ))
+ {
+ handled = true;
+ }
+ }
+
+ return handled;
+}
+
+bool LLContextMenu::handleRightMouseUp( S32 x, S32 y, MASK mask )
+{
+ S32 local_x = x - getRect().mLeft;
+ S32 local_y = y - getRect().mBottom;
+
+ if (!mHoveredAnyItem && !pointInView(local_x, local_y))
+ {
+ sMenuContainer->hideMenus();
+ return true;
+ }
+
+
+ bool result = handleMouseUp( x, y, mask );
+ mHoveredAnyItem = false;
+
+ return result;
+}
+
+bool LLContextMenu::addChild(LLView* view, S32 tab_group)
+{
+ return addContextChild(view, tab_group);
+}
+
diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h
index a0b2d4f7ef..9b22a6e0f2 100644
--- a/indra/llui/llmenugl.h
+++ b/indra/llui/llmenugl.h
@@ -1,978 +1,978 @@
-/**
- * @file llmenugl.h
- * @brief Declaration of the opengl based menu system.
- *
- * $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_LLMENUGL_H
-#define LL_LLMENUGL_H
-
-#include <list>
-
-#include "llstring.h"
-#include "v4color.h"
-#include "llframetimer.h"
-
-#include "llkeyboard.h"
-#include "llfloater.h"
-#include "lluistring.h"
-#include "llview.h"
-#include <boost/function.hpp>
-
-extern S32 MENU_BAR_HEIGHT;
-extern S32 MENU_BAR_WIDTH;
-
-class LLMenuKeyboardBinding
-{
-public:
- KEY mKey;
- MASK mMask;
-};
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLMenuItemGL
-//
-// The LLMenuItemGL represents a single menu item in a menu.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-class LLMenuItemGL: public LLUICtrl, public ll::ui::SearchableControl
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Optional<std::string> shortcut;
- Optional<KEY> jump_key;
- Optional<bool> use_mac_ctrl,
- allow_key_repeat;
-
- Ignored rect,
- left,
- top,
- right,
- bottom,
- width,
- height,
- bottom_delta,
- left_delta;
-
- Optional<LLUIColor> enabled_color,
- disabled_color,
- highlight_bg_color,
- highlight_fg_color;
-
-
- Params();
- };
-
-protected:
- LLMenuItemGL(const Params&);
- friend class LLUICtrlFactory;
-public:
- // LLView overrides
- /*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);
-
- // LLUICtrl overrides
- /*virtual*/ void setValue(const LLSD& value);
- /*virtual*/ LLSD getValue() const;
-
- virtual bool hasAccelerator(const KEY &key, const MASK &mask) const;
- virtual bool handleAcceleratorKey(KEY key, MASK mask);
-
- LLColor4 getHighlightBgColor() { return mHighlightBackground.get(); }
-
- void setJumpKey(KEY key);
- KEY getJumpKey() const { return mJumpKey; }
-
- // set the font used by this item.
- void setFont(const LLFontGL* font) { mFont = font; }
- const LLFontGL* getFont() const { return mFont; }
-
- // returns the height in pixels for the current font.
- virtual U32 getNominalHeight( void ) const;
-
- // Marks item as not needing space for check marks or accelerator keys
- virtual void setBriefItem(bool brief);
- virtual bool isBriefItem() const;
-
- virtual bool addToAcceleratorList(std::list<LLMenuKeyboardBinding*> *listp);
- void setAllowKeyRepeat(bool allow) { mAllowKeyRepeat = allow; }
- bool getAllowKeyRepeat() const { return mAllowKeyRepeat; }
-
- // change the label
- void setLabel( const LLStringExplicit& label ) { mLabel = label; }
- std::string getLabel( void ) const { return mLabel.getString(); }
- virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text );
-
- // Get the parent menu for this item
- virtual class LLMenuGL* getMenu() const;
-
- // returns the normal width of this control in pixels - this is
- // used for calculating the widest item, as well as for horizontal
- // arrangement.
- virtual U32 getNominalWidth( void ) const;
-
- // buildDrawLabel() - constructs the string used during the draw()
- // function. This reduces the overall string manipulation, but can
- // lead to visual errors if the state of the object changes
- // without the knowledge of the menu item. For example, if a
- // boolean being watched is changed outside of the menu item's
- // onCommit() function, the draw buffer will not be updated and will
- // reflect the wrong value. If this ever becomes an issue, there
- // are ways to fix this.
- // Returns the enabled state of the item.
- virtual void buildDrawLabel( void );
-
- // for branching menu items, bring sub menus up to root level of menu hierarchy
- virtual void updateBranchParent( LLView* parentp ){};
-
- virtual void onCommit( void );
-
- virtual void setHighlight( bool highlight );
- virtual bool getHighlight() const { return mHighlight; }
-
- // determine if this represents an active sub-menu
- virtual bool isActive( void ) const { return false; }
-
- // determine if this represents an open sub-menu
- virtual bool isOpen( void ) const { return false; }
-
- virtual void setEnabledSubMenus(bool enable){};
-
- // LLView Functionality
- virtual bool handleKeyHere( KEY key, MASK mask );
- virtual bool handleMouseDown( S32 x, S32 y, MASK mask );
- virtual bool handleMouseUp( S32 x, S32 y, MASK mask );
- virtual bool handleScrollWheel( S32 x, S32 y, S32 clicks );
-
- virtual void onMouseEnter(S32 x, S32 y, MASK mask);
- virtual void onMouseLeave(S32 x, S32 y, MASK mask);
-
- virtual void draw( void );
-
- bool getHover() const { return mGotHover; }
-
- void setDrawTextDisabled(bool disabled) { mDrawTextDisabled = disabled; }
- bool getDrawTextDisabled() const { return mDrawTextDisabled; }
-
-protected:
- void setHover(bool hover) { mGotHover = hover; }
-
- // This function appends the character string representation of
- // the current accelerator key and mask to the provided string.
- void appendAcceleratorString( std::string& st ) const;
-
- virtual std::string _getSearchText() const
- {
- return mLabel.getString();
- }
-
-protected:
- KEY mAcceleratorKey;
- MASK mAcceleratorMask;
- // mLabel contains the actual label specified by the user.
- LLUIString mLabel;
-
- // The draw labels contain some of the labels that we draw during
- // the draw() routine. This optimizes away some of the string
- // manipulation.
- LLUIString mDrawBoolLabel;
- LLUIString mDrawAccelLabel;
- LLUIString mDrawBranchLabel;
-
- LLUIColor mEnabledColor;
- LLUIColor mDisabledColor;
- LLUIColor mHighlightBackground;
- LLUIColor mHighlightForeground;
-
- bool mHighlight;
-private:
- // Keyboard and mouse variables
- bool mAllowKeyRepeat;
- bool mGotHover;
-
- // If true, suppress normal space for check marks on the left and accelerator
- // keys on the right.
- bool mBriefItem;
-
- // Font for this item
- const LLFontGL* mFont;
- bool mDrawTextDisabled;
-
- KEY mJumpKey;
-};
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLMenuItemSeparatorGL
-//
-// This class represents a separator.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-class LLMenuItemSeparatorGL : public LLMenuItemGL
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>
- {
- Optional<EnableCallbackParam > on_visible;
- Params();
- };
-
- LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p = LLMenuItemSeparatorGL::Params());
-
- /*virtual*/ void draw( void );
- /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
-
- virtual void buildDrawLabel();
-
- /*virtual*/ U32 getNominalHeight( void ) const;
-
-private:
- enable_signal_t mVisibleSignal;
-};
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLMenuItemCallGL
-//
-// The LLMenuItemCallerGL represents a single menu item in a menu that
-// calls a user defined callback.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-class LLMenuItemCallGL : public LLMenuItemGL
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>
- {
- Optional<EnableCallbackParam > on_enable;
- Optional<CommitCallbackParam > on_click;
- Optional<EnableCallbackParam > on_visible;
- Params()
- : on_enable("on_enable"),
- on_click("on_click"),
- on_visible("on_visible")
- {}
- };
-protected:
- LLMenuItemCallGL(const Params&);
- friend class LLUICtrlFactory;
- void updateEnabled( void );
- void updateVisible( void );
-
-public:
- void initFromParams(const Params& p);
-
- // called to rebuild the draw label
- virtual void buildDrawLabel( void );
-
- virtual void onCommit( void );
-
- virtual bool handleAcceleratorKey(KEY key, MASK mask);
- virtual bool handleKeyHere(KEY key, MASK mask);
-
- //virtual void draw();
-
- boost::signals2::connection setClickCallback( const commit_signal_t::slot_type& cb )
- {
- return setCommitCallback(cb);
- }
-
- boost::signals2::connection setEnableCallback( const enable_signal_t::slot_type& cb )
- {
- return mEnableSignal.connect(cb);
- }
-
-private:
- enable_signal_t mEnableSignal;
- enable_signal_t mVisibleSignal;
-};
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLMenuItemCheckGL
-//
-// The LLMenuItemCheckGL is an extension of the LLMenuItemCallGL
-// class, by allowing another method to be specified which determines
-// if the menu item should consider itself checked as true or not. Be
-// careful that the provided callback is fast - it needs to be VERY
-// EFFICIENT because it may need to be checked a lot.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-class LLMenuItemCheckGL
-: public LLMenuItemCallGL
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLMenuItemCallGL::Params>
- {
- Optional<EnableCallbackParam > on_check;
- Params()
- : on_check("on_check")
- {}
- };
-
-protected:
- LLMenuItemCheckGL(const Params&);
- friend class LLUICtrlFactory;
-public:
-
- void initFromParams(const Params& p);
-
- virtual void onCommit( void );
-
- virtual void setValue(const LLSD& value);
- virtual LLSD getValue() const;
-
- // called to rebuild the draw label
- virtual void buildDrawLabel( void );
-
- boost::signals2::connection setCheckCallback( const enable_signal_t::slot_type& cb )
- {
- return mCheckSignal.connect(cb);
- }
-
-private:
- enable_signal_t mCheckSignal;
-};
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLMenuGL
-//
-// The Menu class represents a normal rectangular menu somewhere on
-// screen. A Menu can have menu items (described above) or sub-menus
-// attached to it. Sub-menus are implemented via a specialized
-// menu-item type known as a branch. Since it's easy to do wrong, I've
-// taken the branch functionality out of public view, and encapsulate
-// it in the appendMenu() method.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-// child widget registry
-struct MenuRegistry : public LLChildRegistry<MenuRegistry>
-{
- LLSINGLETON_EMPTY_CTOR(MenuRegistry);
-};
-
-
-class LLMenuGL
-: public LLUICtrl
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Optional<KEY> jump_key;
- Optional<bool> horizontal_layout,
- can_tear_off,
- drop_shadow,
- bg_visible,
- create_jump_keys,
- keep_fixed_size,
- scrollable;
- Optional<U32> max_scrollable_items;
- Optional<U32> preferred_width;
- Optional<LLUIColor> bg_color;
- Optional<S32> shortcut_pad;
-
- Params()
- : jump_key("jump_key", KEY_NONE),
- horizontal_layout("horizontal_layout"),
- can_tear_off("tear_off", false),
- drop_shadow("drop_shadow", true),
- bg_visible("bg_visible", true),
- create_jump_keys("create_jump_keys", false),
- 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");
- addSynonym(can_tear_off, "can_tear_off");
- }
- };
-
- // my valid children are contained in MenuRegistry
- typedef MenuRegistry child_registry_t;
-
- void initFromParams(const Params&);
-
- // textual artwork which menugl-imitators may want to match
- static const std::string BOOLEAN_TRUE_PREFIX;
- static const std::string BRANCH_SUFFIX;
- static const std::string ARROW_UP;
- static const std::string ARROW_DOWN;
-
- // for scrollable menus
- typedef enum e_scrolling_direction
- {
- SD_UP = 0,
- SD_DOWN = 1,
- SD_BEGIN = 2,
- SD_END = 3
- } EScrollingDirection;
-
-protected:
- LLMenuGL(const LLMenuGL::Params& p);
- friend class LLUICtrlFactory;
- // let branching menu items use my protected traversal methods
- friend class LLMenuItemBranchGL;
-public:
- virtual ~LLMenuGL( void );
-
- void parseChildXML(LLXMLNodePtr child, LLView* parent);
-
- // LLView Functionality
- /*virtual*/ bool handleUnicodeCharHere( llwchar uni_char );
- /*virtual*/ bool handleHover( S32 x, S32 y, MASK mask );
- /*virtual*/ bool handleScrollWheel( S32 x, S32 y, S32 clicks );
- /*virtual*/ void draw( void );
- /*virtual*/ void drawBackground(LLMenuItemGL* itemp, F32 alpha);
- /*virtual*/ void setVisible(bool visible);
- /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0);
- /*virtual*/ void deleteAllChildren();
- /*virtual*/ void removeChild( LLView* ctrl);
- /*virtual*/ bool postBuild();
-
- virtual bool hasAccelerator(const KEY &key, const MASK &mask) const;
- virtual bool handleAcceleratorKey(KEY key, MASK mask);
-
- LLMenuGL* findChildMenuByName(const std::string& name, bool recurse) const;
-
- bool clearHoverItem();
-
- // return the name label
- const std::string& getLabel( void ) const { return mLabel.getString(); }
- void setLabel(const LLStringExplicit& label) { mLabel = label; }
-
- // background colors
- void setBackgroundColor( const LLUIColor& color ) { mBackgroundColor = color; }
- const LLUIColor& getBackgroundColor() const { return mBackgroundColor; }
- void setBackgroundVisible( bool b ) { mBgVisible = b; }
- void setCanTearOff(bool tear_off);
-
- // add a separator to this menu
- virtual bool addSeparator();
-
- // for branching menu items, bring sub menus up to root level of menu hierarchy
- virtual void updateParent( LLView* parentp );
-
- // setItemEnabled() - pass the name and the enable flag for a
- // menu item. true will make sure it's enabled, false will disable
- // it.
- void setItemEnabled( const std::string& name, bool enable );
-
- // propagate message to submenus
- void setEnabledSubMenus(bool enable);
-
- void setItemVisible( const std::string& name, bool visible);
-
- void setItemLabel(const std::string &name, const std::string &label);
-
- // sets the left,bottom corner of menu, useful for popups
- void setLeftAndBottom(S32 left, S32 bottom);
-
- virtual bool handleJumpKey(KEY key);
-
- virtual bool jumpKeysActive();
-
- virtual bool isOpen();
-
- void needsArrange() { mNeedsArrange = true; }
- // Shape this menu to fit the current state of the children, and
- // adjust the child rects to fit. This is called automatically
- // when you add items. *FIX: We may need to deal with visibility
- // arrangement.
- virtual void arrange( void );
- void arrangeAndClear( void );
-
- // 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
- LLMenuItemGL* getItem(std::string name);
- LLMenuItemGL* getHighlightedItem();
-
- LLMenuItemGL* highlightNextItem(LLMenuItemGL* cur_item, bool skip_disabled = true);
- LLMenuItemGL* highlightPrevItem(LLMenuItemGL* cur_item, bool skip_disabled = true);
-
- void buildDrawLabels();
- void createJumpKeys();
-
- // Show popup at a specific location, in the spawn_view's coordinate frame
- static void showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y, S32 mouse_x = 0, S32 mouse_y = 0);
-
- // Whether to drop shadow menu bar
- void setDropShadowed( const bool shadowed );
-
- void setParentMenuItem( LLMenuItemGL* parent_menu_item ) { mParentMenuItem = parent_menu_item->getHandle(); }
- LLMenuItemGL* getParentMenuItem() const { return dynamic_cast<LLMenuItemGL*>(mParentMenuItem.get()); }
-
- void setTornOff(bool torn_off);
- bool getTornOff() { return mTornOff; }
-
- bool getCanTearOff() { return mTearOffItem != NULL; }
-
- KEY getJumpKey() const { return mJumpKey; }
- void setJumpKey(KEY key) { mJumpKey = key; }
-
- static void setKeyboardMode(bool mode) { sKeyboardMode = mode; }
- static bool getKeyboardMode() { return sKeyboardMode; }
-
- S32 getShortcutPad() { return mShortcutPad; }
-
- bool scrollItems(EScrollingDirection direction);
- bool isScrollable() const { return mScrollable; }
-
- static class LLMenuHolderGL* sMenuContainer;
-
- void resetScrollPositionOnShow(bool reset_scroll_pos) { mResetScrollPositionOnShow = reset_scroll_pos; }
- bool isScrollPositionOnShowReset() { return mResetScrollPositionOnShow; }
-
- void setAlwaysShowMenu(bool show) { mAlwaysShowMenu = show; }
- bool getAlwaysShowMenu() { return mAlwaysShowMenu; }
-
- // add a context menu branch
- bool appendContextSubMenu(LLMenuGL *menu);
-
- const LLFontGL *getFont() const { return mFont; }
-
- protected:
- void createSpilloverBranch();
- void cleanupSpilloverBranch();
- // Add the menu item to this menu.
- virtual bool append( LLMenuItemGL* item );
-
- // 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;
- LLMenuItemGL*mFirstVisibleItem;
- LLMenuItemGL *mArrowUpItem, *mArrowDownItem;
-
- typedef std::map<KEY, LLMenuItemGL*> navigation_key_map_t;
- navigation_key_map_t mJumpKeys;
- S32 mLastMouseX;
- S32 mLastMouseY;
- S32 mMouseVelX;
- S32 mMouseVelY;
- U32 mMaxScrollableItems;
- U32 mPreferredWidth;
- bool mHorizontalLayout;
- bool mScrollable;
- bool mKeepFixedSize;
- bool mNeedsArrange;
-
- // Font for top menu items only
- const LLFontGL* mFont;
-
-private:
-
-
- static LLColor4 sDefaultBackgroundColor;
- static bool sKeyboardMode;
-
- bool mAlwaysShowMenu;
-
- LLUIColor mBackgroundColor;
- bool mBgVisible;
- LLHandle<LLView> mParentMenuItem;
- LLUIString mLabel;
- bool mDropShadowed; // Whether to drop shadow
- bool mHasSelection;
- LLFrameTimer mFadeTimer;
- LLTimer mScrollItemsTimer;
- bool mTornOff;
- class LLMenuItemTearOffGL* mTearOffItem;
- class LLMenuItemBranchGL* mSpilloverBranch;
- LLMenuGL* mSpilloverMenu;
- KEY mJumpKey;
- bool mCreateJumpKeys;
- S32 mShortcutPad;
- bool mResetScrollPositionOnShow;
-}; // end class LLMenuGL
-
-
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLMenuItemBranchGL
-//
-// The LLMenuItemBranchGL represents a menu item that has a
-// sub-menu. This is used to make cascading menus.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-class LLMenuItemBranchGL : public LLMenuItemGL
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>
- {
- Optional<LLMenuGL*> branch;
- };
-
-protected:
- LLMenuItemBranchGL(const Params&);
- friend class LLUICtrlFactory;
-public:
- virtual ~LLMenuItemBranchGL();
-
- virtual bool handleMouseUp(S32 x, S32 y, MASK mask);
-
- virtual bool hasAccelerator(const KEY &key, const MASK &mask) const;
- virtual bool handleAcceleratorKey(KEY key, MASK mask);
-
- // check if we've used these accelerators already
- virtual bool addToAcceleratorList(std::list <LLMenuKeyboardBinding*> *listp);
-
- // called to rebuild the draw label
- virtual void buildDrawLabel( void );
-
- virtual void onCommit( void );
-
- virtual bool handleKey(KEY key, MASK mask, bool called_from_parent);
- virtual bool handleUnicodeChar(llwchar uni_char, bool called_from_parent);
-
- // set the hover status (called by it's menu) and if the object is
- // active. This is used for behavior transfer.
- virtual void setHighlight( bool highlight );
-
- virtual bool handleKeyHere(KEY key, MASK mask);
-
- virtual bool isActive() const;
-
- virtual bool isOpen() const;
-
- LLMenuGL* getBranch() const { return (LLMenuGL*)mBranchHandle.get(); }
-
- virtual void updateBranchParent( LLView* parentp );
-
- // LLView Functionality
- virtual void onVisibilityChange( bool curVisibilityIn );
-
- virtual void draw();
-
- virtual void setEnabledSubMenus(bool enabled) { if (getBranch()) getBranch()->setEnabledSubMenus(enabled); }
-
- virtual void openMenu();
-
- virtual LLView* getChildView(const std::string& name, bool recurse = true) const;
- virtual LLView* findChildView(const std::string& name, bool recurse = true) const;
-
-private:
- LLHandle<LLView> mBranchHandle;
-}; // end class LLMenuItemBranchGL
-
-
-//-----------------------------------------------------------------------------
-// class LLContextMenu
-// A context menu
-//-----------------------------------------------------------------------------
-
-class LLContextMenu
-: public LLMenuGL
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLMenuGL::Params>
- {
- Params()
- {
- changeDefault(visible, false);
- }
- };
-
-protected:
- LLContextMenu(const Params& p);
- friend class LLUICtrlFactory;
-
-public:
- virtual ~LLContextMenu() {}
-
- // LLView Functionality
- // can't set visibility directly, must call show or hide
- virtual void setVisible (bool visible);
-
- virtual void show (S32 x, S32 y, LLView* spawning_view = NULL);
- virtual void hide ();
-
- virtual bool handleHover ( S32 x, S32 y, MASK mask );
- virtual bool handleRightMouseDown( S32 x, S32 y, MASK mask );
- virtual bool handleRightMouseUp ( S32 x, S32 y, MASK mask );
-
- virtual bool addChild (LLView* view, S32 tab_group = 0);
-
- LLHandle<LLContextMenu> getHandle() { return getDerivedHandle<LLContextMenu>(); }
-
- 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;
-};
-
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLMenuBarGL
-//
-// A menu bar displays menus horizontally.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-class LLMenuBarGL : public LLMenuGL
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLMenuGL::Params>
- {};
- LLMenuBarGL( const Params& p );
- virtual ~LLMenuBarGL();
-
- /*virtual*/ bool handleAcceleratorKey(KEY key, MASK mask);
- /*virtual*/ bool handleKeyHere(KEY key, MASK mask);
- /*virtual*/ bool handleJumpKey(KEY key);
- /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask);
-
- /*virtual*/ void draw();
- /*virtual*/ bool jumpKeysActive();
-
- // add a vertical separator to this menu
- virtual bool addSeparator();
-
- // LLView Functionality
- virtual bool handleHover( S32 x, S32 y, MASK mask );
-
- // Returns x position of rightmost child, usually Help menu
- S32 getRightmostMenuEdge();
-
- void resetMenuTrigger() { mAltKeyTrigger = false; }
-
-private:
- // add a menu - this will create a drop down menu.
- virtual bool appendMenu( LLMenuGL* menu );
- // rearrange the child rects so they fit the shape of the menu
- // bar.
- virtual void arrange( void );
-
- void checkMenuTrigger();
-
- std::list <LLMenuKeyboardBinding*> mAccelerators;
- bool mAltKeyTrigger;
-};
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLMenuHolderGL
-//
-// High level view that serves as parent for all menus
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-class LLMenuHolderGL : public LLPanel
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLPanel::Params>
- {};
- LLMenuHolderGL(const Params& p);
- virtual ~LLMenuHolderGL() {}
-
- virtual bool hideMenus();
- void reshape(S32 width, S32 height, bool called_from_parent = true);
- void setCanHide(bool can_hide) { mCanHide = can_hide; }
-
- // LLView functionality
- virtual void draw();
- virtual bool handleMouseDown( S32 x, S32 y, MASK mask );
- virtual bool handleRightMouseDown( S32 x, S32 y, MASK mask );
-
- // Close context menus on right mouse up not handled by menus.
- /*virtual*/ bool handleRightMouseUp( S32 x, S32 y, MASK mask );
-
- virtual bool handleKey(KEY key, MASK mask, bool called_from_parent);
- virtual const LLRect getMenuRect() const { return getLocalRect(); }
- LLView*const getVisibleMenu() const;
- virtual bool hasVisibleMenu() const {return getVisibleMenu() != NULL;}
-
- static void setActivatedItem(LLMenuItemGL* item);
-
- // Need to detect if mouse-up after context menu spawn has moved.
- // If not, need to keep the menu up.
- static LLCoordGL sContextMenuSpawnPos;
-
-private:
- static LLHandle<LLView> sItemLastSelectedHandle;
- static LLFrameTimer sItemActivationTimer;
-
- bool mCanHide;
-};
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLTearOffMenu
-//
-// Floater that hosts a menu
-// https://wiki.lindenlab.com/mediawiki/index.php?title=LLTearOffMenu&oldid=81344
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-class LLTearOffMenu : public LLFloater
-{
-public:
- static LLTearOffMenu* create(LLMenuGL* menup);
- virtual ~LLTearOffMenu();
-
- virtual void draw(void);
- virtual void onFocusReceived();
- virtual void onFocusLost();
- virtual bool handleUnicodeChar(llwchar uni_char, bool called_from_parent);
- virtual bool handleKeyHere(KEY key, MASK mask);
- virtual void translate(S32 x, S32 y);
-
- void updateSize();
-
-private:
- LLTearOffMenu(LLMenuGL* menup);
-
- void closeTearOff();
-
- LLView* mOldParent;
- LLMenuGL* mMenu;
- S32 mTargetHeight;
- bool mQuitRequested;
-};
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Class LLMenuItemTearOffGL
-//
-// This class represents a separator.
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-class LLMenuItemTearOffGL : public LLMenuItemGL
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>
- {};
-
- LLMenuItemTearOffGL( const Params& );
-
- virtual void onCommit(void);
- virtual void draw(void);
- virtual U32 getNominalHeight() const;
-
- LLFloater* getParentFloater();
-};
-
-
-// *TODO: this is currently working, so finish implementation
-class LLEditMenuHandlerMgr
-{
-public:
- LLEditMenuHandlerMgr& getInstance() {
- static LLEditMenuHandlerMgr instance;
- return instance;
- }
- virtual ~LLEditMenuHandlerMgr() {}
-private:
- LLEditMenuHandlerMgr() {};
-};
-
-
-// *TODO: Eliminate
-// For backwards compatability only; generally just use boost::bind
-class view_listener_t : public boost::signals2::trackable
-{
-public:
- virtual bool handleEvent(const LLSD& userdata) = 0;
- view_listener_t() { sListeners.insert(this); }
- virtual ~view_listener_t() { sListeners.erase(this); }
-
- static void addEnable(view_listener_t* listener, const std::string& name)
- {
- LLUICtrl::EnableCallbackRegistry::currentRegistrar().add(name, boost::bind(&view_listener_t::handleEvent, listener, _2));
- }
-
- static void addCommit(view_listener_t* listener, const std::string& name)
- {
- LLUICtrl::CommitCallbackRegistry::currentRegistrar().add(name, boost::bind(&view_listener_t::handleEvent, listener, _2));
- }
-
- static void addMenu(view_listener_t* listener, const std::string& name)
- {
- // For now, add to both click and enable registries
- 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
+/**
+ * @file llmenugl.h
+ * @brief Declaration of the opengl based menu system.
+ *
+ * $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_LLMENUGL_H
+#define LL_LLMENUGL_H
+
+#include <list>
+
+#include "llstring.h"
+#include "v4color.h"
+#include "llframetimer.h"
+
+#include "llkeyboard.h"
+#include "llfloater.h"
+#include "lluistring.h"
+#include "llview.h"
+#include <boost/function.hpp>
+
+extern S32 MENU_BAR_HEIGHT;
+extern S32 MENU_BAR_WIDTH;
+
+class LLMenuKeyboardBinding
+{
+public:
+ KEY mKey;
+ MASK mMask;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemGL
+//
+// The LLMenuItemGL represents a single menu item in a menu.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuItemGL: public LLUICtrl, public ll::ui::SearchableControl
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<std::string> shortcut;
+ Optional<KEY> jump_key;
+ Optional<bool> use_mac_ctrl,
+ allow_key_repeat;
+
+ Ignored rect,
+ left,
+ top,
+ right,
+ bottom,
+ width,
+ height,
+ bottom_delta,
+ left_delta;
+
+ Optional<LLUIColor> enabled_color,
+ disabled_color,
+ highlight_bg_color,
+ highlight_fg_color;
+
+
+ Params();
+ };
+
+protected:
+ LLMenuItemGL(const Params&);
+ friend class LLUICtrlFactory;
+public:
+ // LLView overrides
+ /*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);
+
+ // LLUICtrl overrides
+ /*virtual*/ void setValue(const LLSD& value);
+ /*virtual*/ LLSD getValue() const;
+
+ virtual bool hasAccelerator(const KEY &key, const MASK &mask) const;
+ virtual bool handleAcceleratorKey(KEY key, MASK mask);
+
+ LLColor4 getHighlightBgColor() { return mHighlightBackground.get(); }
+
+ void setJumpKey(KEY key);
+ KEY getJumpKey() const { return mJumpKey; }
+
+ // set the font used by this item.
+ void setFont(const LLFontGL* font) { mFont = font; }
+ const LLFontGL* getFont() const { return mFont; }
+
+ // returns the height in pixels for the current font.
+ virtual U32 getNominalHeight( void ) const;
+
+ // Marks item as not needing space for check marks or accelerator keys
+ virtual void setBriefItem(bool brief);
+ virtual bool isBriefItem() const;
+
+ virtual bool addToAcceleratorList(std::list<LLMenuKeyboardBinding*> *listp);
+ void setAllowKeyRepeat(bool allow) { mAllowKeyRepeat = allow; }
+ bool getAllowKeyRepeat() const { return mAllowKeyRepeat; }
+
+ // change the label
+ void setLabel( const LLStringExplicit& label ) { mLabel = label; }
+ std::string getLabel( void ) const { return mLabel.getString(); }
+ virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text );
+
+ // Get the parent menu for this item
+ virtual class LLMenuGL* getMenu() const;
+
+ // returns the normal width of this control in pixels - this is
+ // used for calculating the widest item, as well as for horizontal
+ // arrangement.
+ virtual U32 getNominalWidth( void ) const;
+
+ // buildDrawLabel() - constructs the string used during the draw()
+ // function. This reduces the overall string manipulation, but can
+ // lead to visual errors if the state of the object changes
+ // without the knowledge of the menu item. For example, if a
+ // boolean being watched is changed outside of the menu item's
+ // onCommit() function, the draw buffer will not be updated and will
+ // reflect the wrong value. If this ever becomes an issue, there
+ // are ways to fix this.
+ // Returns the enabled state of the item.
+ virtual void buildDrawLabel( void );
+
+ // for branching menu items, bring sub menus up to root level of menu hierarchy
+ virtual void updateBranchParent( LLView* parentp ){};
+
+ virtual void onCommit( void );
+
+ virtual void setHighlight( bool highlight );
+ virtual bool getHighlight() const { return mHighlight; }
+
+ // determine if this represents an active sub-menu
+ virtual bool isActive( void ) const { return false; }
+
+ // determine if this represents an open sub-menu
+ virtual bool isOpen( void ) const { return false; }
+
+ virtual void setEnabledSubMenus(bool enable){};
+
+ // LLView Functionality
+ virtual bool handleKeyHere( KEY key, MASK mask );
+ virtual bool handleMouseDown( S32 x, S32 y, MASK mask );
+ virtual bool handleMouseUp( S32 x, S32 y, MASK mask );
+ virtual bool handleScrollWheel( S32 x, S32 y, S32 clicks );
+
+ virtual void onMouseEnter(S32 x, S32 y, MASK mask);
+ virtual void onMouseLeave(S32 x, S32 y, MASK mask);
+
+ virtual void draw( void );
+
+ bool getHover() const { return mGotHover; }
+
+ void setDrawTextDisabled(bool disabled) { mDrawTextDisabled = disabled; }
+ bool getDrawTextDisabled() const { return mDrawTextDisabled; }
+
+protected:
+ void setHover(bool hover) { mGotHover = hover; }
+
+ // This function appends the character string representation of
+ // the current accelerator key and mask to the provided string.
+ void appendAcceleratorString( std::string& st ) const;
+
+ virtual std::string _getSearchText() const
+ {
+ return mLabel.getString();
+ }
+
+protected:
+ KEY mAcceleratorKey;
+ MASK mAcceleratorMask;
+ // mLabel contains the actual label specified by the user.
+ LLUIString mLabel;
+
+ // The draw labels contain some of the labels that we draw during
+ // the draw() routine. This optimizes away some of the string
+ // manipulation.
+ LLUIString mDrawBoolLabel;
+ LLUIString mDrawAccelLabel;
+ LLUIString mDrawBranchLabel;
+
+ LLUIColor mEnabledColor;
+ LLUIColor mDisabledColor;
+ LLUIColor mHighlightBackground;
+ LLUIColor mHighlightForeground;
+
+ bool mHighlight;
+private:
+ // Keyboard and mouse variables
+ bool mAllowKeyRepeat;
+ bool mGotHover;
+
+ // If true, suppress normal space for check marks on the left and accelerator
+ // keys on the right.
+ bool mBriefItem;
+
+ // Font for this item
+ const LLFontGL* mFont;
+ bool mDrawTextDisabled;
+
+ KEY mJumpKey;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemSeparatorGL
+//
+// This class represents a separator.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLMenuItemSeparatorGL : public LLMenuItemGL
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>
+ {
+ Optional<EnableCallbackParam > on_visible;
+ Params();
+ };
+
+ LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p = LLMenuItemSeparatorGL::Params());
+
+ /*virtual*/ void draw( void );
+ /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
+
+ virtual void buildDrawLabel();
+
+ /*virtual*/ U32 getNominalHeight( void ) const;
+
+private:
+ enable_signal_t mVisibleSignal;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemCallGL
+//
+// The LLMenuItemCallerGL represents a single menu item in a menu that
+// calls a user defined callback.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuItemCallGL : public LLMenuItemGL
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>
+ {
+ Optional<EnableCallbackParam > on_enable;
+ Optional<CommitCallbackParam > on_click;
+ Optional<EnableCallbackParam > on_visible;
+ Params()
+ : on_enable("on_enable"),
+ on_click("on_click"),
+ on_visible("on_visible")
+ {}
+ };
+protected:
+ LLMenuItemCallGL(const Params&);
+ friend class LLUICtrlFactory;
+ void updateEnabled( void );
+ void updateVisible( void );
+
+public:
+ void initFromParams(const Params& p);
+
+ // called to rebuild the draw label
+ virtual void buildDrawLabel( void );
+
+ virtual void onCommit( void );
+
+ virtual bool handleAcceleratorKey(KEY key, MASK mask);
+ virtual bool handleKeyHere(KEY key, MASK mask);
+
+ //virtual void draw();
+
+ boost::signals2::connection setClickCallback( const commit_signal_t::slot_type& cb )
+ {
+ return setCommitCallback(cb);
+ }
+
+ boost::signals2::connection setEnableCallback( const enable_signal_t::slot_type& cb )
+ {
+ return mEnableSignal.connect(cb);
+ }
+
+private:
+ enable_signal_t mEnableSignal;
+ enable_signal_t mVisibleSignal;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemCheckGL
+//
+// The LLMenuItemCheckGL is an extension of the LLMenuItemCallGL
+// class, by allowing another method to be specified which determines
+// if the menu item should consider itself checked as true or not. Be
+// careful that the provided callback is fast - it needs to be VERY
+// EFFICIENT because it may need to be checked a lot.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuItemCheckGL
+: public LLMenuItemCallGL
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLMenuItemCallGL::Params>
+ {
+ Optional<EnableCallbackParam > on_check;
+ Params()
+ : on_check("on_check")
+ {}
+ };
+
+protected:
+ LLMenuItemCheckGL(const Params&);
+ friend class LLUICtrlFactory;
+public:
+
+ void initFromParams(const Params& p);
+
+ virtual void onCommit( void );
+
+ virtual void setValue(const LLSD& value);
+ virtual LLSD getValue() const;
+
+ // called to rebuild the draw label
+ virtual void buildDrawLabel( void );
+
+ boost::signals2::connection setCheckCallback( const enable_signal_t::slot_type& cb )
+ {
+ return mCheckSignal.connect(cb);
+ }
+
+private:
+ enable_signal_t mCheckSignal;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuGL
+//
+// The Menu class represents a normal rectangular menu somewhere on
+// screen. A Menu can have menu items (described above) or sub-menus
+// attached to it. Sub-menus are implemented via a specialized
+// menu-item type known as a branch. Since it's easy to do wrong, I've
+// taken the branch functionality out of public view, and encapsulate
+// it in the appendMenu() method.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+// child widget registry
+struct MenuRegistry : public LLChildRegistry<MenuRegistry>
+{
+ LLSINGLETON_EMPTY_CTOR(MenuRegistry);
+};
+
+
+class LLMenuGL
+: public LLUICtrl
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<KEY> jump_key;
+ Optional<bool> horizontal_layout,
+ can_tear_off,
+ drop_shadow,
+ bg_visible,
+ create_jump_keys,
+ keep_fixed_size,
+ scrollable;
+ Optional<U32> max_scrollable_items;
+ Optional<U32> preferred_width;
+ Optional<LLUIColor> bg_color;
+ Optional<S32> shortcut_pad;
+
+ Params()
+ : jump_key("jump_key", KEY_NONE),
+ horizontal_layout("horizontal_layout"),
+ can_tear_off("tear_off", false),
+ drop_shadow("drop_shadow", true),
+ bg_visible("bg_visible", true),
+ create_jump_keys("create_jump_keys", false),
+ 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");
+ addSynonym(can_tear_off, "can_tear_off");
+ }
+ };
+
+ // my valid children are contained in MenuRegistry
+ typedef MenuRegistry child_registry_t;
+
+ void initFromParams(const Params&);
+
+ // textual artwork which menugl-imitators may want to match
+ static const std::string BOOLEAN_TRUE_PREFIX;
+ static const std::string BRANCH_SUFFIX;
+ static const std::string ARROW_UP;
+ static const std::string ARROW_DOWN;
+
+ // for scrollable menus
+ typedef enum e_scrolling_direction
+ {
+ SD_UP = 0,
+ SD_DOWN = 1,
+ SD_BEGIN = 2,
+ SD_END = 3
+ } EScrollingDirection;
+
+protected:
+ LLMenuGL(const LLMenuGL::Params& p);
+ friend class LLUICtrlFactory;
+ // let branching menu items use my protected traversal methods
+ friend class LLMenuItemBranchGL;
+public:
+ virtual ~LLMenuGL( void );
+
+ void parseChildXML(LLXMLNodePtr child, LLView* parent);
+
+ // LLView Functionality
+ /*virtual*/ bool handleUnicodeCharHere( llwchar uni_char );
+ /*virtual*/ bool handleHover( S32 x, S32 y, MASK mask );
+ /*virtual*/ bool handleScrollWheel( S32 x, S32 y, S32 clicks );
+ /*virtual*/ void draw( void );
+ /*virtual*/ void drawBackground(LLMenuItemGL* itemp, F32 alpha);
+ /*virtual*/ void setVisible(bool visible);
+ /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0);
+ /*virtual*/ void deleteAllChildren();
+ /*virtual*/ void removeChild( LLView* ctrl);
+ /*virtual*/ bool postBuild();
+
+ virtual bool hasAccelerator(const KEY &key, const MASK &mask) const;
+ virtual bool handleAcceleratorKey(KEY key, MASK mask);
+
+ LLMenuGL* findChildMenuByName(const std::string& name, bool recurse) const;
+
+ bool clearHoverItem();
+
+ // return the name label
+ const std::string& getLabel( void ) const { return mLabel.getString(); }
+ void setLabel(const LLStringExplicit& label) { mLabel = label; }
+
+ // background colors
+ void setBackgroundColor( const LLUIColor& color ) { mBackgroundColor = color; }
+ const LLUIColor& getBackgroundColor() const { return mBackgroundColor; }
+ void setBackgroundVisible( bool b ) { mBgVisible = b; }
+ void setCanTearOff(bool tear_off);
+
+ // add a separator to this menu
+ virtual bool addSeparator();
+
+ // for branching menu items, bring sub menus up to root level of menu hierarchy
+ virtual void updateParent( LLView* parentp );
+
+ // setItemEnabled() - pass the name and the enable flag for a
+ // menu item. true will make sure it's enabled, false will disable
+ // it.
+ void setItemEnabled( const std::string& name, bool enable );
+
+ // propagate message to submenus
+ void setEnabledSubMenus(bool enable);
+
+ void setItemVisible( const std::string& name, bool visible);
+
+ void setItemLabel(const std::string &name, const std::string &label);
+
+ // sets the left,bottom corner of menu, useful for popups
+ void setLeftAndBottom(S32 left, S32 bottom);
+
+ virtual bool handleJumpKey(KEY key);
+
+ virtual bool jumpKeysActive();
+
+ virtual bool isOpen();
+
+ void needsArrange() { mNeedsArrange = true; }
+ // Shape this menu to fit the current state of the children, and
+ // adjust the child rects to fit. This is called automatically
+ // when you add items. *FIX: We may need to deal with visibility
+ // arrangement.
+ virtual void arrange( void );
+ void arrangeAndClear( void );
+
+ // 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
+ LLMenuItemGL* getItem(std::string name);
+ LLMenuItemGL* getHighlightedItem();
+
+ LLMenuItemGL* highlightNextItem(LLMenuItemGL* cur_item, bool skip_disabled = true);
+ LLMenuItemGL* highlightPrevItem(LLMenuItemGL* cur_item, bool skip_disabled = true);
+
+ void buildDrawLabels();
+ void createJumpKeys();
+
+ // Show popup at a specific location, in the spawn_view's coordinate frame
+ static void showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y, S32 mouse_x = 0, S32 mouse_y = 0);
+
+ // Whether to drop shadow menu bar
+ void setDropShadowed( const bool shadowed );
+
+ void setParentMenuItem( LLMenuItemGL* parent_menu_item ) { mParentMenuItem = parent_menu_item->getHandle(); }
+ LLMenuItemGL* getParentMenuItem() const { return dynamic_cast<LLMenuItemGL*>(mParentMenuItem.get()); }
+
+ void setTornOff(bool torn_off);
+ bool getTornOff() { return mTornOff; }
+
+ bool getCanTearOff() { return mTearOffItem != NULL; }
+
+ KEY getJumpKey() const { return mJumpKey; }
+ void setJumpKey(KEY key) { mJumpKey = key; }
+
+ static void setKeyboardMode(bool mode) { sKeyboardMode = mode; }
+ static bool getKeyboardMode() { return sKeyboardMode; }
+
+ S32 getShortcutPad() { return mShortcutPad; }
+
+ bool scrollItems(EScrollingDirection direction);
+ bool isScrollable() const { return mScrollable; }
+
+ static class LLMenuHolderGL* sMenuContainer;
+
+ void resetScrollPositionOnShow(bool reset_scroll_pos) { mResetScrollPositionOnShow = reset_scroll_pos; }
+ bool isScrollPositionOnShowReset() { return mResetScrollPositionOnShow; }
+
+ void setAlwaysShowMenu(bool show) { mAlwaysShowMenu = show; }
+ bool getAlwaysShowMenu() { return mAlwaysShowMenu; }
+
+ // add a context menu branch
+ bool appendContextSubMenu(LLMenuGL *menu);
+
+ const LLFontGL *getFont() const { return mFont; }
+
+ protected:
+ void createSpilloverBranch();
+ void cleanupSpilloverBranch();
+ // Add the menu item to this menu.
+ virtual bool append( LLMenuItemGL* item );
+
+ // 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;
+ LLMenuItemGL*mFirstVisibleItem;
+ LLMenuItemGL *mArrowUpItem, *mArrowDownItem;
+
+ typedef std::map<KEY, LLMenuItemGL*> navigation_key_map_t;
+ navigation_key_map_t mJumpKeys;
+ S32 mLastMouseX;
+ S32 mLastMouseY;
+ S32 mMouseVelX;
+ S32 mMouseVelY;
+ U32 mMaxScrollableItems;
+ U32 mPreferredWidth;
+ bool mHorizontalLayout;
+ bool mScrollable;
+ bool mKeepFixedSize;
+ bool mNeedsArrange;
+
+ // Font for top menu items only
+ const LLFontGL* mFont;
+
+private:
+
+
+ static LLColor4 sDefaultBackgroundColor;
+ static bool sKeyboardMode;
+
+ bool mAlwaysShowMenu;
+
+ LLUIColor mBackgroundColor;
+ bool mBgVisible;
+ LLHandle<LLView> mParentMenuItem;
+ LLUIString mLabel;
+ bool mDropShadowed; // Whether to drop shadow
+ bool mHasSelection;
+ LLFrameTimer mFadeTimer;
+ LLTimer mScrollItemsTimer;
+ bool mTornOff;
+ class LLMenuItemTearOffGL* mTearOffItem;
+ class LLMenuItemBranchGL* mSpilloverBranch;
+ LLMenuGL* mSpilloverMenu;
+ KEY mJumpKey;
+ bool mCreateJumpKeys;
+ S32 mShortcutPad;
+ bool mResetScrollPositionOnShow;
+}; // end class LLMenuGL
+
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemBranchGL
+//
+// The LLMenuItemBranchGL represents a menu item that has a
+// sub-menu. This is used to make cascading menus.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuItemBranchGL : public LLMenuItemGL
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>
+ {
+ Optional<LLMenuGL*> branch;
+ };
+
+protected:
+ LLMenuItemBranchGL(const Params&);
+ friend class LLUICtrlFactory;
+public:
+ virtual ~LLMenuItemBranchGL();
+
+ virtual bool handleMouseUp(S32 x, S32 y, MASK mask);
+
+ virtual bool hasAccelerator(const KEY &key, const MASK &mask) const;
+ virtual bool handleAcceleratorKey(KEY key, MASK mask);
+
+ // check if we've used these accelerators already
+ virtual bool addToAcceleratorList(std::list <LLMenuKeyboardBinding*> *listp);
+
+ // called to rebuild the draw label
+ virtual void buildDrawLabel( void );
+
+ virtual void onCommit( void );
+
+ virtual bool handleKey(KEY key, MASK mask, bool called_from_parent);
+ virtual bool handleUnicodeChar(llwchar uni_char, bool called_from_parent);
+
+ // set the hover status (called by it's menu) and if the object is
+ // active. This is used for behavior transfer.
+ virtual void setHighlight( bool highlight );
+
+ virtual bool handleKeyHere(KEY key, MASK mask);
+
+ virtual bool isActive() const;
+
+ virtual bool isOpen() const;
+
+ LLMenuGL* getBranch() const { return (LLMenuGL*)mBranchHandle.get(); }
+
+ virtual void updateBranchParent( LLView* parentp );
+
+ // LLView Functionality
+ virtual void onVisibilityChange( bool curVisibilityIn );
+
+ virtual void draw();
+
+ virtual void setEnabledSubMenus(bool enabled) { if (getBranch()) getBranch()->setEnabledSubMenus(enabled); }
+
+ virtual void openMenu();
+
+ virtual LLView* getChildView(const std::string& name, bool recurse = true) const;
+ virtual LLView* findChildView(const std::string& name, bool recurse = true) const;
+
+private:
+ LLHandle<LLView> mBranchHandle;
+}; // end class LLMenuItemBranchGL
+
+
+//-----------------------------------------------------------------------------
+// class LLContextMenu
+// A context menu
+//-----------------------------------------------------------------------------
+
+class LLContextMenu
+: public LLMenuGL
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLMenuGL::Params>
+ {
+ Params()
+ {
+ changeDefault(visible, false);
+ }
+ };
+
+protected:
+ LLContextMenu(const Params& p);
+ friend class LLUICtrlFactory;
+
+public:
+ virtual ~LLContextMenu() {}
+
+ // LLView Functionality
+ // can't set visibility directly, must call show or hide
+ virtual void setVisible (bool visible);
+
+ virtual void show (S32 x, S32 y, LLView* spawning_view = NULL);
+ virtual void hide ();
+
+ virtual bool handleHover ( S32 x, S32 y, MASK mask );
+ virtual bool handleRightMouseDown( S32 x, S32 y, MASK mask );
+ virtual bool handleRightMouseUp ( S32 x, S32 y, MASK mask );
+
+ virtual bool addChild (LLView* view, S32 tab_group = 0);
+
+ LLHandle<LLContextMenu> getHandle() { return getDerivedHandle<LLContextMenu>(); }
+
+ 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;
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuBarGL
+//
+// A menu bar displays menus horizontally.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuBarGL : public LLMenuGL
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLMenuGL::Params>
+ {};
+ LLMenuBarGL( const Params& p );
+ virtual ~LLMenuBarGL();
+
+ /*virtual*/ bool handleAcceleratorKey(KEY key, MASK mask);
+ /*virtual*/ bool handleKeyHere(KEY key, MASK mask);
+ /*virtual*/ bool handleJumpKey(KEY key);
+ /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask);
+
+ /*virtual*/ void draw();
+ /*virtual*/ bool jumpKeysActive();
+
+ // add a vertical separator to this menu
+ virtual bool addSeparator();
+
+ // LLView Functionality
+ virtual bool handleHover( S32 x, S32 y, MASK mask );
+
+ // Returns x position of rightmost child, usually Help menu
+ S32 getRightmostMenuEdge();
+
+ void resetMenuTrigger() { mAltKeyTrigger = false; }
+
+private:
+ // add a menu - this will create a drop down menu.
+ virtual bool appendMenu( LLMenuGL* menu );
+ // rearrange the child rects so they fit the shape of the menu
+ // bar.
+ virtual void arrange( void );
+
+ void checkMenuTrigger();
+
+ std::list <LLMenuKeyboardBinding*> mAccelerators;
+ bool mAltKeyTrigger;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuHolderGL
+//
+// High level view that serves as parent for all menus
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLMenuHolderGL : public LLPanel
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLPanel::Params>
+ {};
+ LLMenuHolderGL(const Params& p);
+ virtual ~LLMenuHolderGL() {}
+
+ virtual bool hideMenus();
+ void reshape(S32 width, S32 height, bool called_from_parent = true);
+ void setCanHide(bool can_hide) { mCanHide = can_hide; }
+
+ // LLView functionality
+ virtual void draw();
+ virtual bool handleMouseDown( S32 x, S32 y, MASK mask );
+ virtual bool handleRightMouseDown( S32 x, S32 y, MASK mask );
+
+ // Close context menus on right mouse up not handled by menus.
+ /*virtual*/ bool handleRightMouseUp( S32 x, S32 y, MASK mask );
+
+ virtual bool handleKey(KEY key, MASK mask, bool called_from_parent);
+ virtual const LLRect getMenuRect() const { return getLocalRect(); }
+ LLView*const getVisibleMenu() const;
+ virtual bool hasVisibleMenu() const {return getVisibleMenu() != NULL;}
+
+ static void setActivatedItem(LLMenuItemGL* item);
+
+ // Need to detect if mouse-up after context menu spawn has moved.
+ // If not, need to keep the menu up.
+ static LLCoordGL sContextMenuSpawnPos;
+
+private:
+ static LLHandle<LLView> sItemLastSelectedHandle;
+ static LLFrameTimer sItemActivationTimer;
+
+ bool mCanHide;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLTearOffMenu
+//
+// Floater that hosts a menu
+// https://wiki.lindenlab.com/mediawiki/index.php?title=LLTearOffMenu&oldid=81344
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLTearOffMenu : public LLFloater
+{
+public:
+ static LLTearOffMenu* create(LLMenuGL* menup);
+ virtual ~LLTearOffMenu();
+
+ virtual void draw(void);
+ virtual void onFocusReceived();
+ virtual void onFocusLost();
+ virtual bool handleUnicodeChar(llwchar uni_char, bool called_from_parent);
+ virtual bool handleKeyHere(KEY key, MASK mask);
+ virtual void translate(S32 x, S32 y);
+
+ void updateSize();
+
+private:
+ LLTearOffMenu(LLMenuGL* menup);
+
+ void closeTearOff();
+
+ LLView* mOldParent;
+ LLMenuGL* mMenu;
+ S32 mTargetHeight;
+ bool mQuitRequested;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemTearOffGL
+//
+// This class represents a separator.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuItemTearOffGL : public LLMenuItemGL
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLMenuItemGL::Params>
+ {};
+
+ LLMenuItemTearOffGL( const Params& );
+
+ virtual void onCommit(void);
+ virtual void draw(void);
+ virtual U32 getNominalHeight() const;
+
+ LLFloater* getParentFloater();
+};
+
+
+// *TODO: this is currently working, so finish implementation
+class LLEditMenuHandlerMgr
+{
+public:
+ LLEditMenuHandlerMgr& getInstance() {
+ static LLEditMenuHandlerMgr instance;
+ return instance;
+ }
+ virtual ~LLEditMenuHandlerMgr() {}
+private:
+ LLEditMenuHandlerMgr() {};
+};
+
+
+// *TODO: Eliminate
+// For backwards compatability only; generally just use boost::bind
+class view_listener_t : public boost::signals2::trackable
+{
+public:
+ virtual bool handleEvent(const LLSD& userdata) = 0;
+ view_listener_t() { sListeners.insert(this); }
+ virtual ~view_listener_t() { sListeners.erase(this); }
+
+ static void addEnable(view_listener_t* listener, const std::string& name)
+ {
+ LLUICtrl::EnableCallbackRegistry::currentRegistrar().add(name, boost::bind(&view_listener_t::handleEvent, listener, _2));
+ }
+
+ static void addCommit(view_listener_t* listener, const std::string& name)
+ {
+ LLUICtrl::CommitCallbackRegistry::currentRegistrar().add(name, boost::bind(&view_listener_t::handleEvent, listener, _2));
+ }
+
+ static void addMenu(view_listener_t* listener, const std::string& name)
+ {
+ // For now, add to both click and enable registries
+ 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 c501d1efc1..087ac325c8 100644
--- a/indra/llui/llmodaldialog.cpp
+++ b/indra/llui/llmodaldialog.cpp
@@ -1,347 +1,348 @@
-/**
- * @file llmodaldialog.cpp
- * @brief LLModalDialog base class
- *
- * $LicenseInfo:firstyear=2002&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 "llmodaldialog.h"
-
-#include "llfocusmgr.h"
-#include "v4color.h"
-#include "v2math.h"
-#include "llui.h"
-#include "llwindow.h"
-#include "llkeyboard.h"
-#include "llmenugl.h"
-// static
-std::list<LLModalDialog*> LLModalDialog::sModalStack;
-
-LLModalDialog::LLModalDialog( const LLSD& key, bool modal )
- : LLFloater(key),
- mModal( modal )
-{
- if (modal)
- {
- setCanMinimize(false);
- setCanClose(false);
- }
- setVisible( false );
- setBackgroundVisible(true);
- setBackgroundOpaque(true);
- centerOnScreen(); // default position
- mCloseSignal.connect(boost::bind(&LLModalDialog::stopModal, this));
-}
-
-LLModalDialog::~LLModalDialog()
-{
- // don't unlock focus unless we have it
- if (gFocusMgr.childHasKeyboardFocus(this))
- {
- gFocusMgr.unlockFocus();
- }
-
- std::list<LLModalDialog*>::iterator iter = std::find(sModalStack.begin(), sModalStack.end(), this);
- if (iter != sModalStack.end())
- {
- LL_ERRS() << "Attempt to delete dialog while still in sModalStack!" << LL_ENDL;
- }
-}
-
-// virtual
-bool LLModalDialog::postBuild()
-{
- return LLFloater::postBuild();
-}
-
-// virtual
-void LLModalDialog::openFloater(const LLSD& key)
-{
- // SJB: Hack! Make sure we don't ever host a modal dialog
- LLMultiFloater* thost = LLFloater::getFloaterHost();
- LLFloater::setFloaterHost(NULL);
- LLFloater::openFloater(key);
- LLFloater::setFloaterHost(thost);
-}
-
-void LLModalDialog::reshape(S32 width, S32 height, bool called_from_parent)
-{
- LLFloater::reshape(width, height, called_from_parent);
- centerOnScreen();
-}
-
-// virtual
-void LLModalDialog::onOpen(const LLSD& key)
-{
- if (mModal)
- {
- // If Modal, Hide the active modal dialog
- if (!sModalStack.empty())
- {
- LLModalDialog* front = sModalStack.front();
- if (front != this)
- {
- front->setVisible(false);
- }
- }
-
- // This is a modal dialog. It sucks up all mouse and keyboard operations.
- gFocusMgr.setMouseCapture( this );
- LLUI::getInstance()->addPopup(this);
- setFocus(true);
-
- std::list<LLModalDialog*>::iterator iter = std::find(sModalStack.begin(), sModalStack.end(), this);
- if (iter != sModalStack.end())
- {
- // if already present, we want to move it to front.
- sModalStack.erase(iter);
- }
-
- sModalStack.push_front(this);
- }
-}
-
-void LLModalDialog::stopModal()
-{
- gFocusMgr.unlockFocus();
- gFocusMgr.releaseFocusIfNeeded( this );
-
- if (mModal)
- {
- std::list<LLModalDialog*>::iterator iter = std::find(sModalStack.begin(), sModalStack.end(), this);
- if (iter != sModalStack.end())
- {
- sModalStack.erase(iter);
- }
- else
- {
- LL_WARNS() << "LLModalDialog::stopModal not in list!" << LL_ENDL;
- }
- }
- if (!sModalStack.empty())
- {
- LLModalDialog* front = sModalStack.front();
- front->setVisible(true);
- }
-}
-
-
-void LLModalDialog::setVisible( bool visible )
-{
- if (mModal)
- {
- if( visible )
- {
- // This is a modal dialog. It sucks up all mouse and keyboard operations.
- gFocusMgr.setMouseCapture( this );
-
- // The dialog view is a root view
- LLUI::getInstance()->addPopup(this);
- setFocus( true );
- }
- else
- {
- gFocusMgr.releaseFocusIfNeeded( this );
- }
- }
-
- LLFloater::setVisible( visible );
-}
-
-bool LLModalDialog::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- LLView* popup_menu = LLMenuGL::sMenuContainer->getVisibleMenu();
- if (popup_menu != NULL)
- {
- S32 mx, my;
- LLUI::getInstance()->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))
- {
- // Click was outside the panel
- make_ui_sound("UISndInvalidOp");
- }
- }
- else
- {
- LLFloater::handleMouseDown(x, y, mask);
- }
-
-
- return true;
-}
-
-bool LLModalDialog::handleHover(S32 x, S32 y, MASK mask)
-{
- if( childrenHandleHover(x, y, mask) == NULL )
- {
- getWindow()->setCursor(UI_CURSOR_ARROW);
- LL_DEBUGS("UserInput") << "hover handled by " << getName() << LL_ENDL;
- }
-
- LLView* popup_menu = LLMenuGL::sMenuContainer->getVisibleMenu();
- if (popup_menu != NULL)
- {
- S32 mx, my;
- LLUI::getInstance()->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;
-}
-
-bool LLModalDialog::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- childrenHandleMouseUp(x, y, mask);
- return true;
-}
-
-bool LLModalDialog::handleScrollWheel(S32 x, S32 y, S32 clicks)
-{
- childrenHandleScrollWheel(x, y, clicks);
- return true;
-}
-
-bool LLModalDialog::handleDoubleClick(S32 x, S32 y, MASK mask)
-{
- if (!LLFloater::handleDoubleClick(x, y, mask))
- {
- // Click outside the panel
- make_ui_sound("UISndInvalidOp");
- }
- return true;
-}
-
-bool LLModalDialog::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- LLMenuGL::sMenuContainer->hideMenus();
- childrenHandleRightMouseDown(x, y, mask);
- return true;
-}
-
-
-bool LLModalDialog::handleKeyHere(KEY key, MASK mask )
-{
- LLFloater::handleKeyHere(key, mask );
-
- if (mModal)
- {
- // Suck up all keystokes except CTRL-Q.
- bool is_quit = ('Q' == key) && (MASK_CONTROL == mask);
- return !is_quit;
- }
- else
- {
- // don't process escape key until message box has been on screen a minimal amount of time
- // to avoid accidentally destroying the message box when user is hitting escape at the time it appears
- bool enough_time_elapsed = mVisibleTime.getElapsedTimeF32() > 1.0f;
- if (enough_time_elapsed && key == KEY_ESCAPE)
- {
- closeFloater();
- return true;
- }
- return false;
- }
-}
-
-// virtual
-void LLModalDialog::draw()
-{
- static LLUIColor shadow_color = LLUIColorTable::instance().getColor("ColorDropShadow");
- static LLUICachedControl<S32> shadow_lines ("DropShadowFloater", 0);
-
- gl_drop_shadow( 0, getRect().getHeight(), getRect().getWidth(), 0,
- shadow_color, shadow_lines);
-
- LLFloater::draw();
-
- // Focus retrieval moved to LLFloaterView::refresh()
-}
-
-void LLModalDialog::centerOnScreen()
-{
- LLVector2 window_size = LLUI::getInstance()->getWindowSize();
- centerWithin(LLRect(0, 0, ll_round(window_size.mV[VX]), ll_round(window_size.mV[VY])));
-}
-
-
-// static
-void LLModalDialog::onAppFocusLost()
-{
- if( !sModalStack.empty() )
- {
- LLModalDialog* instance = LLModalDialog::sModalStack.front();
- if( gFocusMgr.childHasMouseCapture( instance ) )
- {
- gFocusMgr.setMouseCapture( NULL );
- }
-
- instance->setFocus(false);
- }
-}
-
-// static
-void LLModalDialog::onAppFocusGained()
-{
- if( !sModalStack.empty() )
- {
- LLModalDialog* instance = LLModalDialog::sModalStack.front();
-
- // This is a modal dialog. It sucks up all mouse and keyboard operations.
- gFocusMgr.setMouseCapture( instance );
- instance->setFocus(true);
- LLUI::getInstance()->addPopup(instance);
-
- instance->centerOnScreen();
- }
-}
-
-void LLModalDialog::shutdownModals()
-{
- // This method is only for use during app shutdown. ~LLModalDialog()
- // checks sModalStack, and if the dialog instance is still there, it
- // crumps with "Attempt to delete dialog while still in sModalStack!" But
- // at app shutdown, all bets are off. If the user asks to shut down the
- // app, we shouldn't have to care WHAT's open. Put differently, if a modal
- // dialog is so crucial that we can't let the user terminate until s/he
- // addresses it, we should reject a termination request. The current state
- // of affairs is that we accept it, but then produce an LL_ERRS() popup that
- // simply makes our software look unreliable.
- sModalStack.clear();
-}
+/**
+ * @file llmodaldialog.cpp
+ * @brief LLModalDialog base class
+ *
+ * $LicenseInfo:firstyear=2002&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 "llmodaldialog.h"
+
+#include "llfocusmgr.h"
+#include "v4color.h"
+#include "v2math.h"
+#include "llui.h"
+#include "llwindow.h"
+#include "llkeyboard.h"
+#include "llmenugl.h"
+// static
+std::list<LLModalDialog*> LLModalDialog::sModalStack;
+
+LLModalDialog::LLModalDialog( const LLSD& key, bool modal )
+ : LLFloater(key),
+ mModal( modal )
+{
+ if (modal)
+ {
+ setCanMinimize(false);
+ setCanClose(false);
+ }
+ setVisible( false );
+ setBackgroundVisible(true);
+ setBackgroundOpaque(true);
+ centerOnScreen(); // default position
+ mCloseSignal.connect(boost::bind(&LLModalDialog::stopModal, this));
+}
+
+LLModalDialog::~LLModalDialog()
+{
+ // don't unlock focus unless we have it
+ if (gFocusMgr.childHasKeyboardFocus(this))
+ {
+ gFocusMgr.unlockFocus();
+ }
+
+ std::list<LLModalDialog*>::iterator iter = std::find(sModalStack.begin(), sModalStack.end(), this);
+ if (iter != sModalStack.end())
+ {
+ LL_ERRS() << "Attempt to delete dialog while still in sModalStack!" << LL_ENDL;
+ }
+
+ LLUI::getInstance()->removePopup(this);
+}
+
+// virtual
+bool LLModalDialog::postBuild()
+{
+ return LLFloater::postBuild();
+}
+
+// virtual
+void LLModalDialog::openFloater(const LLSD& key)
+{
+ // SJB: Hack! Make sure we don't ever host a modal dialog
+ LLMultiFloater* thost = LLFloater::getFloaterHost();
+ LLFloater::setFloaterHost(NULL);
+ LLFloater::openFloater(key);
+ LLFloater::setFloaterHost(thost);
+}
+
+void LLModalDialog::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ LLFloater::reshape(width, height, called_from_parent);
+ centerOnScreen();
+}
+
+// virtual
+void LLModalDialog::onOpen(const LLSD& key)
+{
+ if (mModal)
+ {
+ // If Modal, Hide the active modal dialog
+ if (!sModalStack.empty())
+ {
+ LLModalDialog* front = sModalStack.front();
+ if (front != this)
+ {
+ front->setVisible(false);
+ }
+ }
+
+ // This is a modal dialog. It sucks up all mouse and keyboard operations.
+ gFocusMgr.setMouseCapture( this );
+ LLUI::getInstance()->addPopup(this);
+ setFocus(true);
+
+ std::list<LLModalDialog*>::iterator iter = std::find(sModalStack.begin(), sModalStack.end(), this);
+ if (iter != sModalStack.end())
+ {
+ // if already present, we want to move it to front.
+ sModalStack.erase(iter);
+ }
+
+ sModalStack.push_front(this);
+ }
+}
+
+void LLModalDialog::stopModal()
+{
+ gFocusMgr.unlockFocus();
+ gFocusMgr.releaseFocusIfNeeded( this );
+
+ if (mModal)
+ {
+ std::list<LLModalDialog*>::iterator iter = std::find(sModalStack.begin(), sModalStack.end(), this);
+ if (iter != sModalStack.end())
+ {
+ sModalStack.erase(iter);
+ }
+ else
+ {
+ LL_WARNS() << "LLModalDialog::stopModal not in list!" << LL_ENDL;
+ }
+ }
+ if (!sModalStack.empty())
+ {
+ LLModalDialog* front = sModalStack.front();
+ front->setVisible(true);
+ }
+}
+
+
+void LLModalDialog::setVisible( bool visible )
+{
+ if (mModal)
+ {
+ if( visible )
+ {
+ // This is a modal dialog. It sucks up all mouse and keyboard operations.
+ gFocusMgr.setMouseCapture( this );
+
+ // The dialog view is a root view
+ LLUI::getInstance()->addPopup(this);
+ setFocus( true );
+ }
+ else
+ {
+ gFocusMgr.releaseFocusIfNeeded( this );
+ }
+ }
+
+ LLFloater::setVisible( visible );
+}
+
+bool LLModalDialog::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLView* popup_menu = LLMenuGL::sMenuContainer->getVisibleMenu();
+ if (popup_menu != NULL)
+ {
+ S32 mx, my;
+ LLUI::getInstance()->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))
+ {
+ // Click was outside the panel
+ make_ui_sound("UISndInvalidOp");
+ }
+ }
+ else
+ {
+ LLFloater::handleMouseDown(x, y, mask);
+ }
+
+
+ return true;
+}
+
+bool LLModalDialog::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( childrenHandleHover(x, y, mask) == NULL )
+ {
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << LL_ENDL;
+ }
+
+ LLView* popup_menu = LLMenuGL::sMenuContainer->getVisibleMenu();
+ if (popup_menu != NULL)
+ {
+ S32 mx, my;
+ LLUI::getInstance()->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;
+}
+
+bool LLModalDialog::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ childrenHandleMouseUp(x, y, mask);
+ return true;
+}
+
+bool LLModalDialog::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ childrenHandleScrollWheel(x, y, clicks);
+ return true;
+}
+
+bool LLModalDialog::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ if (!LLFloater::handleDoubleClick(x, y, mask))
+ {
+ // Click outside the panel
+ make_ui_sound("UISndInvalidOp");
+ }
+ return true;
+}
+
+bool LLModalDialog::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLMenuGL::sMenuContainer->hideMenus();
+ childrenHandleRightMouseDown(x, y, mask);
+ return true;
+}
+
+
+bool LLModalDialog::handleKeyHere(KEY key, MASK mask )
+{
+ LLFloater::handleKeyHere(key, mask );
+
+ if (mModal)
+ {
+ // Suck up all keystokes except CTRL-Q.
+ bool is_quit = ('Q' == key) && (MASK_CONTROL == mask);
+ return !is_quit;
+ }
+ else
+ {
+ // don't process escape key until message box has been on screen a minimal amount of time
+ // to avoid accidentally destroying the message box when user is hitting escape at the time it appears
+ bool enough_time_elapsed = mVisibleTime.getElapsedTimeF32() > 1.0f;
+ if (enough_time_elapsed && key == KEY_ESCAPE)
+ {
+ closeFloater();
+ return true;
+ }
+ return false;
+ }
+}
+
+// virtual
+void LLModalDialog::draw()
+{
+ static LLUIColor shadow_color = LLUIColorTable::instance().getColor("ColorDropShadow");
+
+ gl_drop_shadow( 0, getRect().getHeight(), getRect().getWidth(), 0,
+ shadow_color, DROP_SHADOW_FLOATER);
+
+ LLFloater::draw();
+
+ // Focus retrieval moved to LLFloaterView::refresh()
+}
+
+void LLModalDialog::centerOnScreen()
+{
+ LLVector2 window_size = LLUI::getInstance()->getWindowSize();
+ centerWithin(LLRect(0, 0, ll_round(window_size.mV[VX]), ll_round(window_size.mV[VY])));
+}
+
+
+// static
+void LLModalDialog::onAppFocusLost()
+{
+ if( !sModalStack.empty() )
+ {
+ LLModalDialog* instance = LLModalDialog::sModalStack.front();
+ if( gFocusMgr.childHasMouseCapture( instance ) )
+ {
+ gFocusMgr.setMouseCapture( NULL );
+ }
+
+ instance->setFocus(false);
+ }
+}
+
+// static
+void LLModalDialog::onAppFocusGained()
+{
+ if( !sModalStack.empty() )
+ {
+ LLModalDialog* instance = LLModalDialog::sModalStack.front();
+
+ // This is a modal dialog. It sucks up all mouse and keyboard operations.
+ gFocusMgr.setMouseCapture( instance );
+ instance->setFocus(true);
+ LLUI::getInstance()->addPopup(instance);
+
+ instance->centerOnScreen();
+ }
+}
+
+void LLModalDialog::shutdownModals()
+{
+ // This method is only for use during app shutdown. ~LLModalDialog()
+ // checks sModalStack, and if the dialog instance is still there, it
+ // crumps with "Attempt to delete dialog while still in sModalStack!" But
+ // at app shutdown, all bets are off. If the user asks to shut down the
+ // app, we shouldn't have to care WHAT's open. Put differently, if a modal
+ // dialog is so crucial that we can't let the user terminate until s/he
+ // addresses it, we should reject a termination request. The current state
+ // of affairs is that we accept it, but then produce an 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 0fd1f64033..37637bcb18 100644
--- a/indra/llui/llmodaldialog.h
+++ b/indra/llui/llmodaldialog.h
@@ -1,83 +1,83 @@
-/**
- * @file llmodaldialog.h
- * @brief LLModalDialog base class
- *
- * $LicenseInfo:firstyear=2002&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_LLMODALDIALOG_H
-#define LL_LLMODALDIALOG_H
-
-#include "llfloater.h"
-#include "llframetimer.h"
-
-class LLModalDialog;
-
-// By default, a ModalDialog is modal, i.e. no other window can have focus
-// However, for the sake of code reuse and simplicity, if mModal == false,
-// the dialog behaves like a normal floater
-// https://wiki.lindenlab.com/mediawiki/index.php?title=LLModalDialog&oldid=81385
-class LLModalDialog : public LLFloater
-{
-public:
- LLModalDialog( const LLSD& key, bool modal = true );
- virtual ~LLModalDialog();
-
- /*virtual*/ bool postBuild();
-
- /*virtual*/ void openFloater(const LLSD& key = LLSD());
- /*virtual*/ void onOpen(const LLSD& key);
-
- /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true);
-
- /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks);
- /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleKeyHere(KEY key, MASK mask );
-
- /*virtual*/ void setVisible(bool visible);
- /*virtual*/ void draw();
-
- bool isModal() const { return mModal; }
- void stopModal();
-
- static void onAppFocusLost();
- static void onAppFocusGained();
-
- static S32 activeCount() { return sModalStack.size(); }
- static void shutdownModals();
-
-protected:
- void centerOnScreen();
-
-private:
-
- LLFrameTimer mVisibleTime;
- const bool mModal;
-
- static std::list<LLModalDialog*> sModalStack; // Top of stack is currently being displayed
-};
-
-#endif // LL_LLMODALDIALOG_H
+/**
+ * @file llmodaldialog.h
+ * @brief LLModalDialog base class
+ *
+ * $LicenseInfo:firstyear=2002&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_LLMODALDIALOG_H
+#define LL_LLMODALDIALOG_H
+
+#include "llfloater.h"
+#include "llframetimer.h"
+
+class LLModalDialog;
+
+// By default, a ModalDialog is modal, i.e. no other window can have focus
+// However, for the sake of code reuse and simplicity, if mModal == false,
+// the dialog behaves like a normal floater
+// https://wiki.lindenlab.com/mediawiki/index.php?title=LLModalDialog&oldid=81385
+class LLModalDialog : public LLFloater
+{
+public:
+ LLModalDialog( const LLSD& key, bool modal = true );
+ virtual ~LLModalDialog();
+
+ /*virtual*/ bool postBuild();
+
+ /*virtual*/ void openFloater(const LLSD& key = LLSD());
+ /*virtual*/ void onOpen(const LLSD& key);
+
+ /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true);
+
+ /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks);
+ /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleKeyHere(KEY key, MASK mask );
+
+ /*virtual*/ void setVisible(bool visible);
+ /*virtual*/ void draw();
+
+ bool isModal() const { return mModal; }
+ void stopModal();
+
+ static void onAppFocusLost();
+ static void onAppFocusGained();
+
+ static S32 activeCount() { return sModalStack.size(); }
+ static void shutdownModals();
+
+protected:
+ void centerOnScreen();
+
+private:
+
+ LLFrameTimer mVisibleTime;
+ const bool mModal;
+
+ static std::list<LLModalDialog*> sModalStack; // Top of stack is currently being displayed
+};
+
+#endif // LL_LLMODALDIALOG_H
diff --git a/indra/llui/llmultifloater.cpp b/indra/llui/llmultifloater.cpp
index fe0d88e68f..08e9c763f4 100644
--- a/indra/llui/llmultifloater.cpp
+++ b/indra/llui/llmultifloater.cpp
@@ -1,522 +1,522 @@
-/**
- * @file llmultifloater.cpp
- * @brief LLFloater that hosts other floaters
- *
- * $LicenseInfo:firstyear=2002&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$
- */
-
-// Floating "windows" within the GL display, like the inventory floater,
-// mini-map floater, etc.
-
-#include "linden_common.h"
-
-#include "llmultifloater.h"
-#include "llresizehandle.h"
-
-//
-// LLMultiFloater
-//
-
-LLMultiFloater::LLMultiFloater(const LLSD& key, const LLFloater::Params& params)
- : LLFloater(key),
- mTabContainer(NULL),
- mTabPos(LLTabContainer::TOP),
- mAutoResize(true),
- mOrigMinWidth(params.min_width),
- mOrigMinHeight(params.min_height)
-{
-}
-
-void LLMultiFloater::buildTabContainer()
-{
- const LLFloater::Params& default_params = LLFloater::getDefaultParams();
- S32 floater_header_size = default_params.header_height;
-
- LLTabContainer::Params p;
- p.name(std::string("Preview Tabs"));
- p.rect(LLRect(LLPANEL_BORDER_WIDTH, getRect().getHeight() - floater_header_size, getRect().getWidth() - LLPANEL_BORDER_WIDTH, 0));
- p.tab_position(mTabPos);
- p.follows.flags(FOLLOWS_ALL);
- p.commit_callback.function(boost::bind(&LLMultiFloater::onTabSelected, this));
-
- mTabContainer = LLUICtrlFactory::create<LLTabContainer>(p);
- addChild(mTabContainer);
-
- if (isResizable())
- {
- mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH);
- }
-}
-
-void LLMultiFloater::onClose(bool app_quitting)
-{
- if(isMinimized())
- {
- setMinimized(false);
- }
- LLFloater::onClose(app_quitting);
-}
-
-void LLMultiFloater::draw()
-{
- if (mTabContainer->getTabCount() == 0)
- {
- //RN: could this potentially crash in draw hierarchy?
- closeFloater();
- }
- else
- {
- LLFloater::draw();
- }
-}
-
-bool LLMultiFloater::closeAllFloaters()
-{
- S32 tabToClose = 0;
- S32 lastTabCount = mTabContainer->getTabCount();
- while (tabToClose < mTabContainer->getTabCount())
- {
- LLFloater* first_floater = (LLFloater*)mTabContainer->getPanelByIndex(tabToClose);
- first_floater->closeFloater();
- if(lastTabCount == mTabContainer->getTabCount())
- {
- //Tab did not actually close, possibly due to a pending Save Confirmation dialog..
- //so try and close the next one in the list...
- tabToClose++;
- }
- else
- {
- //Tab closed ok.
- lastTabCount = mTabContainer->getTabCount();
- }
- }
- if( mTabContainer->getTabCount() != 0 )
- return false; // Couldn't close all the tabs (pending save dialog?) so return false.
- return true; //else all tabs were successfully closed...
-}
-
-void LLMultiFloater::growToFit(S32 content_width, S32 content_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;
- S32 new_width = llmax(getRect().getWidth(), content_width + LLPANEL_BORDER_WIDTH * 2);
- S32 new_height = llmax(getRect().getHeight(), content_height + floater_header_size + tabcntr_header_height);
-
- if (isMinimized())
- {
- LLRect newrect;
- newrect.setLeftTopAndSize(getExpandedRect().mLeft, getExpandedRect().mTop, new_width, new_height);
- setExpandedRect(newrect);
- }
- else
- {
- S32 old_height = getRect().getHeight();
- reshape(new_width, new_height);
- // keep top left corner in same position
- translate(0, old_height - new_height);
- }
-}
-
-/**
- void addFloater(LLFloater* floaterp, bool select_added_floater)
-
- Adds the LLFloater pointed to by floaterp to this.
- If floaterp is already hosted by this, then it is re-added to get
- new titles, etc.
- If select_added_floater is true, the LLFloater pointed to by floaterp will
- become the selected tab in this
-
- Affects: mTabContainer, floaterp
-**/
-void LLMultiFloater::addFloater(LLFloater* floaterp, bool select_added_floater, LLTabContainer::eInsertionPoint insertion_point)
-{
- if (!floaterp)
- {
- return;
- }
-
- if (!mTabContainer)
- {
- LL_ERRS() << "Tab Container used without having been initialized." << LL_ENDL;
- return;
- }
-
- if (floaterp->getHost() == this)
- {
- // already hosted by me, remove
- // do this so we get updated title, etc.
- mFloaterDataMap.erase(floaterp->getHandle());
- mTabContainer->removeTabPanel(floaterp);
- }
- else if (floaterp->getHost())
- {
- // floaterp is hosted by somebody else and
- // this is adding it, so remove it from its old host
- floaterp->getHost()->removeFloater(floaterp);
- }
- else if (floaterp->getParent() == gFloaterView)
- {
- // rehost preview floater as child panel
- gFloaterView->removeChild(floaterp);
- }
-
- // store original configuration
- LLFloaterData floater_data;
- floater_data.mWidth = floaterp->getRect().getWidth();
- 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);
-
- if (mAutoResize)
- {
- growToFit(floater_data.mWidth, floater_data.mHeight);
- }
-
- //add the panel, add it to proper maps
- mTabContainer->addTabPanel(
- LLTabContainer::TabPanelParams()
- .panel(floaterp)
- .label(floaterp->getShortTitle())
- .insert_at(insertion_point));
- mFloaterDataMap[floaterp->getHandle()] = floater_data;
-
- updateResizeLimits();
-
- if ( select_added_floater )
- {
- mTabContainer->selectTabPanel(floaterp);
- }
- else
- {
- // reassert visible tab (hiding new floater if necessary)
- mTabContainer->selectTab(mTabContainer->getCurrentPanelIndex());
- }
-
- floaterp->setHost(this);
- if (isMinimized())
- {
- floaterp->setVisible(false);
- }
-
- // Tabs sometimes overlap resize handle
- moveResizeHandlesToFront();
-}
-
-void LLMultiFloater::updateFloaterTitle(LLFloater* floaterp)
-{
- S32 index = mTabContainer->getIndexForPanel(floaterp);
- if (index != -1)
- {
- mTabContainer->setPanelTitle(index, floaterp->getShortTitle());
- }
-}
-
-
-/**
- bool selectFloater(LLFloater* floaterp)
-
- If the LLFloater pointed to by floaterp is hosted by this,
- then its tab is selected and returns true. Otherwise returns false.
-
- Affects: mTabContainer
-**/
-bool LLMultiFloater::selectFloater(LLFloater* floaterp)
-{
- return mTabContainer->selectTabPanel(floaterp);
-}
-
-// virtual
-void LLMultiFloater::selectNextFloater()
-{
- mTabContainer->selectNextTab();
-}
-
-// virtual
-void LLMultiFloater::selectPrevFloater()
-{
- mTabContainer->selectPrevTab();
-}
-
-void LLMultiFloater::showFloater(LLFloater* floaterp, LLTabContainer::eInsertionPoint insertion_point)
-{
- if(!floaterp) return;
- // we won't select a panel that already is selected
- // it is hard to do this internally to tab container
- // as tab selection is handled via index and the tab at a given
- // index might have changed
- if (floaterp != mTabContainer->getCurrentPanel() &&
- !mTabContainer->selectTabPanel(floaterp))
- {
- addFloater(floaterp, true, insertion_point);
- }
-}
-
-void LLMultiFloater::removeFloater(LLFloater* floaterp)
-{
- if (!floaterp || floaterp->getHost() != this )
- return;
-
- floater_data_map_t::iterator found_data_it = mFloaterDataMap.find(floaterp->getHandle());
- if (found_data_it != mFloaterDataMap.end())
- {
- LLFloaterData& floater_data = found_data_it->second;
- floaterp->setCanMinimize(floater_data.mCanMinimize);
- floaterp->mSaveRect = floater_data.mSaveRect;
- if (!floater_data.mCanResize)
- {
- // restore original size
- floaterp->reshape(floater_data.mWidth, floater_data.mHeight);
- }
- floaterp->setCanResize(floater_data.mCanResize);
- mFloaterDataMap.erase(found_data_it);
- }
- mTabContainer->removeTabPanel(floaterp);
- floaterp->setBackgroundVisible(true);
- floaterp->setCanDrag(true);
- floaterp->setHost(NULL);
- floaterp->applyRectControl();
-
- updateResizeLimits();
-
- tabOpen((LLFloater*)mTabContainer->getCurrentPanel(), false);
-}
-
-void LLMultiFloater::tabOpen(LLFloater* opened_floater, bool from_click)
-{
- // default implementation does nothing
-}
-
-void LLMultiFloater::tabClose()
-{
- if (mTabContainer->getTabCount() == 0)
- {
- // no more children, close myself
- closeFloater();
- }
-}
-
-void LLMultiFloater::setVisible(bool visible)
-{
- // *FIX: shouldn't have to do this, fix adding to minimized multifloater
- LLFloater::setVisible(visible);
-
- if (mTabContainer)
- {
- LLPanel* cur_floaterp = mTabContainer->getCurrentPanel();
-
- if (cur_floaterp)
- {
- cur_floaterp->setVisible(visible);
- }
-
- // if no tab selected, and we're being shown,
- // select last tab to be added
- if (visible && !cur_floaterp)
- {
- mTabContainer->selectLastTab();
- }
- }
-}
-
-bool LLMultiFloater::handleKeyHere(KEY key, MASK mask)
-{
- if (key == 'W' && mask == MASK_CONTROL)
- {
- LLFloater* floater = getActiveFloater();
- // is user closeable and is system closeable
- if (floater && floater->canClose() && floater->isCloseable())
- {
- floater->closeFloater();
-
- // EXT-5695 (Tabbed IM window loses focus if close any tabs by Ctrl+W)
- // bring back focus on tab container if there are any tab left
- if(mTabContainer->getTabCount() > 0)
- {
- mTabContainer->setFocus(true);
- }
- }
- return true;
- }
-
- return LLFloater::handleKeyHere(key, mask);
-}
-
-bool LLMultiFloater::addChild(LLView* child, S32 tab_group)
-{
- LLTabContainer* tab_container = dynamic_cast<LLTabContainer*>(child);
- if (tab_container)
- {
- // store pointer to tab container
- setTabContainer(tab_container);
- }
-
- // then go ahead and add child as usual
- return LLFloater::addChild(child, tab_group);
-}
-
-LLFloater* LLMultiFloater::getActiveFloater()
-{
- return (LLFloater*)mTabContainer->getCurrentPanel();
-}
-
-S32 LLMultiFloater::getFloaterCount()
-{
- return mTabContainer->getTabCount();
-}
-
-/**
- bool isFloaterFlashing(LLFloater* floaterp)
-
- Returns true if the LLFloater pointed to by floaterp
- is currently in a flashing state and is hosted by this.
- False otherwise.
-
- Requires: floaterp != NULL
-**/
-bool LLMultiFloater::isFloaterFlashing(LLFloater* floaterp)
-{
- if ( floaterp && floaterp->getHost() == this )
- return mTabContainer->getTabPanelFlashing(floaterp);
-
- return false;
-}
-
-/**
- bool setFloaterFlashing(LLFloater* floaterp, bool flashing)
-
- Sets the current flashing state of the LLFloater pointed
- to by floaterp to be the bool flashing if the LLFloater pointed
- to by floaterp is hosted by this.
-
- Requires: floaterp != NULL
-**/
-void LLMultiFloater::setFloaterFlashing(LLFloater* floaterp, bool flashing)
-{
- if ( floaterp && floaterp->getHost() == this )
- mTabContainer->setTabPanelFlashing(floaterp, flashing);
-}
-
-void LLMultiFloater::onTabSelected()
-{
- LLFloater* floaterp = dynamic_cast<LLFloater*>(mTabContainer->getCurrentPanel());
- if (floaterp)
- {
- tabOpen(floaterp, true);
- }
-}
-
-void LLMultiFloater::setCanResize(bool can_resize)
-{
- LLFloater::setCanResize(can_resize);
- if (!mTabContainer) return;
- if (isResizable() && mTabContainer->getTabPosition() == LLTabContainer::BOTTOM)
- {
- mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH);
- }
- else
- {
- mTabContainer->setRightTabBtnOffset(0);
- }
-}
-
-bool LLMultiFloater::postBuild()
-{
- mCloseSignal.connect(boost::bind(&LLMultiFloater::closeAllFloaters, this));
-
- // remember any original xml minimum size
- getResizeLimits(&mOrigMinWidth, &mOrigMinHeight);
-
- if (mTabContainer)
- {
- return true;
- }
-
- mTabContainer = getChild<LLTabContainer>("Preview Tabs");
-
- setCanResize(mResizable);
- return true;
-}
-
-void LLMultiFloater::updateResizeLimits()
-{
- // initialize minimum size constraint to the original xml values.
- S32 new_min_width = mOrigMinWidth;
- S32 new_min_height = mOrigMinHeight;
-
- computeResizeLimits(new_min_width, new_min_height);
-
- setResizeLimits(new_min_width, new_min_height);
-
- S32 cur_height = getRect().getHeight();
- S32 new_width = llmax(getRect().getWidth(), new_min_width);
- S32 new_height = llmax(getRect().getHeight(), new_min_height);
-
- if (isMinimized())
- {
- const LLRect& expanded = getExpandedRect();
- LLRect newrect;
- newrect.setLeftTopAndSize(expanded.mLeft, expanded.mTop, llmax(expanded.getWidth(), new_width), llmax(expanded.getHeight(), new_height));
- setExpandedRect(newrect);
- }
- else
- {
- reshape(new_width, new_height);
-
- // make sure upper left corner doesn't move
- translate(0, cur_height - getRect().getHeight());
-
- // make sure this window is visible on screen when it has been modified
- // (tab added, etc)
- 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);
- }
- }
-}
+/**
+ * @file llmultifloater.cpp
+ * @brief LLFloater that hosts other floaters
+ *
+ * $LicenseInfo:firstyear=2002&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$
+ */
+
+// Floating "windows" within the GL display, like the inventory floater,
+// mini-map floater, etc.
+
+#include "linden_common.h"
+
+#include "llmultifloater.h"
+#include "llresizehandle.h"
+
+//
+// LLMultiFloater
+//
+
+LLMultiFloater::LLMultiFloater(const LLSD& key, const LLFloater::Params& params)
+ : LLFloater(key),
+ mTabContainer(NULL),
+ mTabPos(LLTabContainer::TOP),
+ mAutoResize(true),
+ mOrigMinWidth(params.min_width),
+ mOrigMinHeight(params.min_height)
+{
+}
+
+void LLMultiFloater::buildTabContainer()
+{
+ const LLFloater::Params& default_params = LLFloater::getDefaultParams();
+ S32 floater_header_size = default_params.header_height;
+
+ LLTabContainer::Params p;
+ p.name(std::string("Preview Tabs"));
+ p.rect(LLRect(LLPANEL_BORDER_WIDTH, getRect().getHeight() - floater_header_size, getRect().getWidth() - LLPANEL_BORDER_WIDTH, 0));
+ p.tab_position(mTabPos);
+ p.follows.flags(FOLLOWS_ALL);
+ p.commit_callback.function(boost::bind(&LLMultiFloater::onTabSelected, this));
+
+ mTabContainer = LLUICtrlFactory::create<LLTabContainer>(p);
+ addChild(mTabContainer);
+
+ if (isResizable())
+ {
+ mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH);
+ }
+}
+
+void LLMultiFloater::onClose(bool app_quitting)
+{
+ if(isMinimized())
+ {
+ setMinimized(false);
+ }
+ LLFloater::onClose(app_quitting);
+}
+
+void LLMultiFloater::draw()
+{
+ if (mTabContainer->getTabCount() == 0)
+ {
+ //RN: could this potentially crash in draw hierarchy?
+ closeFloater();
+ }
+ else
+ {
+ LLFloater::draw();
+ }
+}
+
+bool LLMultiFloater::closeAllFloaters()
+{
+ S32 tabToClose = 0;
+ S32 lastTabCount = mTabContainer->getTabCount();
+ while (tabToClose < mTabContainer->getTabCount())
+ {
+ LLFloater* first_floater = (LLFloater*)mTabContainer->getPanelByIndex(tabToClose);
+ first_floater->closeFloater();
+ if(lastTabCount == mTabContainer->getTabCount())
+ {
+ //Tab did not actually close, possibly due to a pending Save Confirmation dialog..
+ //so try and close the next one in the list...
+ tabToClose++;
+ }
+ else
+ {
+ //Tab closed ok.
+ lastTabCount = mTabContainer->getTabCount();
+ }
+ }
+ if( mTabContainer->getTabCount() != 0 )
+ return false; // Couldn't close all the tabs (pending save dialog?) so return false.
+ return true; //else all tabs were successfully closed...
+}
+
+void LLMultiFloater::growToFit(S32 content_width, S32 content_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;
+ S32 new_width = llmax(getRect().getWidth(), content_width + LLPANEL_BORDER_WIDTH * 2);
+ S32 new_height = llmax(getRect().getHeight(), content_height + floater_header_size + tabcntr_header_height);
+
+ if (isMinimized())
+ {
+ LLRect newrect;
+ newrect.setLeftTopAndSize(getExpandedRect().mLeft, getExpandedRect().mTop, new_width, new_height);
+ setExpandedRect(newrect);
+ }
+ else
+ {
+ S32 old_height = getRect().getHeight();
+ reshape(new_width, new_height);
+ // keep top left corner in same position
+ translate(0, old_height - new_height);
+ }
+}
+
+/**
+ void addFloater(LLFloater* floaterp, bool select_added_floater)
+
+ Adds the LLFloater pointed to by floaterp to this.
+ If floaterp is already hosted by this, then it is re-added to get
+ new titles, etc.
+ If select_added_floater is true, the LLFloater pointed to by floaterp will
+ become the selected tab in this
+
+ Affects: mTabContainer, floaterp
+**/
+void LLMultiFloater::addFloater(LLFloater* floaterp, bool select_added_floater, LLTabContainer::eInsertionPoint insertion_point)
+{
+ if (!floaterp)
+ {
+ return;
+ }
+
+ if (!mTabContainer)
+ {
+ LL_ERRS() << "Tab Container used without having been initialized." << LL_ENDL;
+ return;
+ }
+
+ if (floaterp->getHost() == this)
+ {
+ // already hosted by me, remove
+ // do this so we get updated title, etc.
+ mFloaterDataMap.erase(floaterp->getHandle());
+ mTabContainer->removeTabPanel(floaterp);
+ }
+ else if (floaterp->getHost())
+ {
+ // floaterp is hosted by somebody else and
+ // this is adding it, so remove it from its old host
+ floaterp->getHost()->removeFloater(floaterp);
+ }
+ else if (floaterp->getParent() == gFloaterView)
+ {
+ // rehost preview floater as child panel
+ gFloaterView->removeChild(floaterp);
+ }
+
+ // store original configuration
+ LLFloaterData floater_data;
+ floater_data.mWidth = floaterp->getRect().getWidth();
+ 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);
+
+ if (mAutoResize)
+ {
+ growToFit(floater_data.mWidth, floater_data.mHeight);
+ }
+
+ //add the panel, add it to proper maps
+ mTabContainer->addTabPanel(
+ LLTabContainer::TabPanelParams()
+ .panel(floaterp)
+ .label(floaterp->getShortTitle())
+ .insert_at(insertion_point));
+ mFloaterDataMap[floaterp->getHandle()] = floater_data;
+
+ updateResizeLimits();
+
+ if ( select_added_floater )
+ {
+ mTabContainer->selectTabPanel(floaterp);
+ }
+ else
+ {
+ // reassert visible tab (hiding new floater if necessary)
+ mTabContainer->selectTab(mTabContainer->getCurrentPanelIndex());
+ }
+
+ floaterp->setHost(this);
+ if (isMinimized())
+ {
+ floaterp->setVisible(false);
+ }
+
+ // Tabs sometimes overlap resize handle
+ moveResizeHandlesToFront();
+}
+
+void LLMultiFloater::updateFloaterTitle(LLFloater* floaterp)
+{
+ S32 index = mTabContainer->getIndexForPanel(floaterp);
+ if (index != -1)
+ {
+ mTabContainer->setPanelTitle(index, floaterp->getShortTitle());
+ }
+}
+
+
+/**
+ bool selectFloater(LLFloater* floaterp)
+
+ If the LLFloater pointed to by floaterp is hosted by this,
+ then its tab is selected and returns true. Otherwise returns false.
+
+ Affects: mTabContainer
+**/
+bool LLMultiFloater::selectFloater(LLFloater* floaterp)
+{
+ return mTabContainer->selectTabPanel(floaterp);
+}
+
+// virtual
+void LLMultiFloater::selectNextFloater()
+{
+ mTabContainer->selectNextTab();
+}
+
+// virtual
+void LLMultiFloater::selectPrevFloater()
+{
+ mTabContainer->selectPrevTab();
+}
+
+void LLMultiFloater::showFloater(LLFloater* floaterp, LLTabContainer::eInsertionPoint insertion_point)
+{
+ if(!floaterp) return;
+ // we won't select a panel that already is selected
+ // it is hard to do this internally to tab container
+ // as tab selection is handled via index and the tab at a given
+ // index might have changed
+ if (floaterp != mTabContainer->getCurrentPanel() &&
+ !mTabContainer->selectTabPanel(floaterp))
+ {
+ addFloater(floaterp, true, insertion_point);
+ }
+}
+
+void LLMultiFloater::removeFloater(LLFloater* floaterp)
+{
+ if (!floaterp || floaterp->getHost() != this )
+ return;
+
+ floater_data_map_t::iterator found_data_it = mFloaterDataMap.find(floaterp->getHandle());
+ if (found_data_it != mFloaterDataMap.end())
+ {
+ LLFloaterData& floater_data = found_data_it->second;
+ floaterp->setCanMinimize(floater_data.mCanMinimize);
+ floaterp->mSaveRect = floater_data.mSaveRect;
+ if (!floater_data.mCanResize)
+ {
+ // restore original size
+ floaterp->reshape(floater_data.mWidth, floater_data.mHeight);
+ }
+ floaterp->setCanResize(floater_data.mCanResize);
+ mFloaterDataMap.erase(found_data_it);
+ }
+ mTabContainer->removeTabPanel(floaterp);
+ floaterp->setBackgroundVisible(true);
+ floaterp->setCanDrag(true);
+ floaterp->setHost(NULL);
+ floaterp->applyRectControl();
+
+ updateResizeLimits();
+
+ tabOpen((LLFloater*)mTabContainer->getCurrentPanel(), false);
+}
+
+void LLMultiFloater::tabOpen(LLFloater* opened_floater, bool from_click)
+{
+ // default implementation does nothing
+}
+
+void LLMultiFloater::tabClose()
+{
+ if (mTabContainer->getTabCount() == 0)
+ {
+ // no more children, close myself
+ closeFloater();
+ }
+}
+
+void LLMultiFloater::setVisible(bool visible)
+{
+ // *FIX: shouldn't have to do this, fix adding to minimized multifloater
+ LLFloater::setVisible(visible);
+
+ if (mTabContainer)
+ {
+ LLPanel* cur_floaterp = mTabContainer->getCurrentPanel();
+
+ if (cur_floaterp)
+ {
+ cur_floaterp->setVisible(visible);
+ }
+
+ // if no tab selected, and we're being shown,
+ // select last tab to be added
+ if (visible && !cur_floaterp)
+ {
+ mTabContainer->selectLastTab();
+ }
+ }
+}
+
+bool LLMultiFloater::handleKeyHere(KEY key, MASK mask)
+{
+ if (key == 'W' && mask == MASK_CONTROL)
+ {
+ LLFloater* floater = getActiveFloater();
+ // is user closeable and is system closeable
+ if (floater && floater->canClose() && floater->isCloseable())
+ {
+ floater->closeFloater();
+
+ // EXT-5695 (Tabbed IM window loses focus if close any tabs by Ctrl+W)
+ // bring back focus on tab container if there are any tab left
+ if(mTabContainer->getTabCount() > 0)
+ {
+ mTabContainer->setFocus(true);
+ }
+ }
+ return true;
+ }
+
+ return LLFloater::handleKeyHere(key, mask);
+}
+
+bool LLMultiFloater::addChild(LLView* child, S32 tab_group)
+{
+ LLTabContainer* tab_container = dynamic_cast<LLTabContainer*>(child);
+ if (tab_container)
+ {
+ // store pointer to tab container
+ setTabContainer(tab_container);
+ }
+
+ // then go ahead and add child as usual
+ return LLFloater::addChild(child, tab_group);
+}
+
+LLFloater* LLMultiFloater::getActiveFloater()
+{
+ return (LLFloater*)mTabContainer->getCurrentPanel();
+}
+
+S32 LLMultiFloater::getFloaterCount()
+{
+ return mTabContainer->getTabCount();
+}
+
+/**
+ bool isFloaterFlashing(LLFloater* floaterp)
+
+ Returns true if the LLFloater pointed to by floaterp
+ is currently in a flashing state and is hosted by this.
+ False otherwise.
+
+ Requires: floaterp != NULL
+**/
+bool LLMultiFloater::isFloaterFlashing(LLFloater* floaterp)
+{
+ if ( floaterp && floaterp->getHost() == this )
+ return mTabContainer->getTabPanelFlashing(floaterp);
+
+ return false;
+}
+
+/**
+ bool setFloaterFlashing(LLFloater* floaterp, bool flashing)
+
+ Sets the current flashing state of the LLFloater pointed
+ to by floaterp to be the bool flashing if the LLFloater pointed
+ to by floaterp is hosted by this.
+
+ Requires: floaterp != NULL
+**/
+void LLMultiFloater::setFloaterFlashing(LLFloater* floaterp, bool flashing)
+{
+ if ( floaterp && floaterp->getHost() == this )
+ mTabContainer->setTabPanelFlashing(floaterp, flashing);
+}
+
+void LLMultiFloater::onTabSelected()
+{
+ LLFloater* floaterp = dynamic_cast<LLFloater*>(mTabContainer->getCurrentPanel());
+ if (floaterp)
+ {
+ tabOpen(floaterp, true);
+ }
+}
+
+void LLMultiFloater::setCanResize(bool can_resize)
+{
+ LLFloater::setCanResize(can_resize);
+ if (!mTabContainer) return;
+ if (isResizable() && mTabContainer->getTabPosition() == LLTabContainer::BOTTOM)
+ {
+ mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH);
+ }
+ else
+ {
+ mTabContainer->setRightTabBtnOffset(0);
+ }
+}
+
+bool LLMultiFloater::postBuild()
+{
+ mCloseSignal.connect(boost::bind(&LLMultiFloater::closeAllFloaters, this));
+
+ // remember any original xml minimum size
+ getResizeLimits(&mOrigMinWidth, &mOrigMinHeight);
+
+ if (mTabContainer)
+ {
+ return true;
+ }
+
+ mTabContainer = getChild<LLTabContainer>("Preview Tabs");
+
+ setCanResize(mResizable);
+ return true;
+}
+
+void LLMultiFloater::updateResizeLimits()
+{
+ // initialize minimum size constraint to the original xml values.
+ S32 new_min_width = mOrigMinWidth;
+ S32 new_min_height = mOrigMinHeight;
+
+ computeResizeLimits(new_min_width, new_min_height);
+
+ setResizeLimits(new_min_width, new_min_height);
+
+ S32 cur_height = getRect().getHeight();
+ S32 new_width = llmax(getRect().getWidth(), new_min_width);
+ S32 new_height = llmax(getRect().getHeight(), new_min_height);
+
+ if (isMinimized())
+ {
+ const LLRect& expanded = getExpandedRect();
+ LLRect newrect;
+ newrect.setLeftTopAndSize(expanded.mLeft, expanded.mTop, llmax(expanded.getWidth(), new_width), llmax(expanded.getHeight(), new_height));
+ setExpandedRect(newrect);
+ }
+ else
+ {
+ reshape(new_width, new_height);
+
+ // make sure upper left corner doesn't move
+ translate(0, cur_height - getRect().getHeight());
+
+ // make sure this window is visible on screen when it has been modified
+ // (tab added, etc)
+ 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 47f7b3e8b9..82cff6084a 100644
--- a/indra/llui/llmultifloater.h
+++ b/indra/llui/llmultifloater.h
@@ -1,102 +1,102 @@
-/**
- * @file llmultifloater.h
- * @brief LLFloater that hosts other floaters
- *
- * $LicenseInfo:firstyear=2002&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$
- */
-
-// Floating "windows" within the GL display, like the inventory floater,
-// mini-map floater, etc.
-
-
-#ifndef LL_MULTI_FLOATER_H
-#define LL_MULTI_FLOATER_H
-
-#include "llfloater.h"
-#include "lltabcontainer.h" // for LLTabContainer::eInsertionPoint
-
-// https://wiki.lindenlab.com/mediawiki/index.php?title=LLMultiFloater&oldid=81376
-class LLMultiFloater : public LLFloater
-{
-public:
- LLMultiFloater(const LLSD& key, const Params& params = getDefaultParams());
- virtual ~LLMultiFloater() {};
-
- void buildTabContainer();
-
- virtual bool postBuild();
- /*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);
-
- virtual void setCanResize(bool can_resize);
- virtual void growToFit(S32 content_width, S32 content_height);
- virtual void addFloater(LLFloater* floaterp, bool select_added_floater, LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END);
-
- virtual void showFloater(LLFloater* floaterp, LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END);
- virtual void removeFloater(LLFloater* floaterp);
-
- virtual void tabOpen(LLFloater* opened_floater, bool from_click);
- virtual void tabClose();
-
- virtual bool selectFloater(LLFloater* floaterp);
- virtual void selectNextFloater();
- virtual void selectPrevFloater();
-
- virtual LLFloater* getActiveFloater();
- virtual bool isFloaterFlashing(LLFloater* floaterp);
- virtual S32 getFloaterCount();
-
- virtual void setFloaterFlashing(LLFloater* floaterp, bool flashing);
- virtual bool closeAllFloaters(); //Returns false if the floater could not be closed due to pending confirmation dialogs
- void setTabContainer(LLTabContainer* tab_container) { if (!mTabContainer) mTabContainer = tab_container; }
- void onTabSelected();
-
- virtual void updateResizeLimits();
- virtual void updateFloaterTitle(LLFloater* floaterp);
-
-protected:
- struct LLFloaterData
- {
- S32 mWidth;
- S32 mHeight;
- bool mCanMinimize;
- bool mCanResize;
- bool mSaveRect;
- };
-
- LLTabContainer* mTabContainer;
-
- typedef std::map<LLHandle<LLFloater>, LLFloaterData> floater_data_map_t;
- floater_data_map_t mFloaterDataMap;
-
- 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
+/**
+ * @file llmultifloater.h
+ * @brief LLFloater that hosts other floaters
+ *
+ * $LicenseInfo:firstyear=2002&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$
+ */
+
+// Floating "windows" within the GL display, like the inventory floater,
+// mini-map floater, etc.
+
+
+#ifndef LL_MULTI_FLOATER_H
+#define LL_MULTI_FLOATER_H
+
+#include "llfloater.h"
+#include "lltabcontainer.h" // for LLTabContainer::eInsertionPoint
+
+// https://wiki.lindenlab.com/mediawiki/index.php?title=LLMultiFloater&oldid=81376
+class LLMultiFloater : public LLFloater
+{
+public:
+ LLMultiFloater(const LLSD& key, const Params& params = getDefaultParams());
+ virtual ~LLMultiFloater() {};
+
+ void buildTabContainer();
+
+ virtual bool postBuild();
+ /*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);
+
+ virtual void setCanResize(bool can_resize);
+ virtual void growToFit(S32 content_width, S32 content_height);
+ virtual void addFloater(LLFloater* floaterp, bool select_added_floater, LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END);
+
+ virtual void showFloater(LLFloater* floaterp, LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END);
+ virtual void removeFloater(LLFloater* floaterp);
+
+ virtual void tabOpen(LLFloater* opened_floater, bool from_click);
+ virtual void tabClose();
+
+ virtual bool selectFloater(LLFloater* floaterp);
+ virtual void selectNextFloater();
+ virtual void selectPrevFloater();
+
+ virtual LLFloater* getActiveFloater();
+ virtual bool isFloaterFlashing(LLFloater* floaterp);
+ virtual S32 getFloaterCount();
+
+ virtual void setFloaterFlashing(LLFloater* floaterp, bool flashing);
+ virtual bool closeAllFloaters(); //Returns false if the floater could not be closed due to pending confirmation dialogs
+ void setTabContainer(LLTabContainer* tab_container) { if (!mTabContainer) mTabContainer = tab_container; }
+ void onTabSelected();
+
+ virtual void updateResizeLimits();
+ virtual void updateFloaterTitle(LLFloater* floaterp);
+
+protected:
+ struct LLFloaterData
+ {
+ S32 mWidth;
+ S32 mHeight;
+ bool mCanMinimize;
+ bool mCanResize;
+ bool mSaveRect;
+ };
+
+ LLTabContainer* mTabContainer;
+
+ typedef std::map<LLHandle<LLFloater>, LLFloaterData> floater_data_map_t;
+ floater_data_map_t mFloaterDataMap;
+
+ 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 685b1c8b98..a29ccab737 100644
--- a/indra/llui/llmultislider.cpp
+++ b/indra/llui/llmultislider.cpp
@@ -1,880 +1,880 @@
-/**
- * @file llmultisldr.cpp
- * @brief LLMultiSlider base class
- *
- * $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$
- */
-
-#include "linden_common.h"
-
-#include "llmultislider.h"
-#include "llui.h"
-
-#include "llgl.h"
-#include "llwindow.h"
-#include "llfocusmgr.h"
-#include "llkeyboard.h" // for the MASK constants
-#include "llcontrol.h"
-#include "lluictrlfactory.h"
-#include "lluiimage.h"
-
-#include <sstream>
-
-static LLDefaultChildRegistry::Register<LLMultiSlider> r("multi_slider_bar");
-
-const F32 FLOAT_THRESHOLD = 0.00001f;
-
-S32 LLMultiSlider::mNameCounter = 0;
-
-LLMultiSlider::SliderParams::SliderParams()
-: name("name"),
- value("value", 0.f)
-{
-}
-
-LLMultiSlider::Params::Params()
-: max_sliders("max_sliders", 1),
- allow_overlap("allow_overlap", false),
- loop_overlap("loop_overlap", false),
- orientation("orientation"),
- overlap_threshold("overlap_threshold", 0),
- draw_track("draw_track", true),
- use_triangle("use_triangle", false),
- track_color("track_color"),
- thumb_disabled_color("thumb_disabled_color"),
- thumb_highlight_color("thumb_highlight_color"),
- thumb_outline_color("thumb_outline_color"),
- thumb_center_color("thumb_center_color"),
- thumb_center_selected_color("thumb_center_selected_color"),
- thumb_image("thumb_image"),
- triangle_color("triangle_color"),
- mouse_down_callback("mouse_down_callback"),
- mouse_up_callback("mouse_up_callback"),
- thumb_width("thumb_width"),
- sliders("slider")
-{}
-
-LLMultiSlider::LLMultiSlider(const LLMultiSlider::Params& p)
-: LLF32UICtrl(p),
- mMouseOffset( 0 ),
- mMaxNumSliders(p.max_sliders),
- mAllowOverlap(p.allow_overlap),
- mLoopOverlap(p.loop_overlap),
- mDrawTrack(p.draw_track),
- mUseTriangle(p.use_triangle),
- mTrackColor(p.track_color()),
- mThumbOutlineColor(p.thumb_outline_color()),
- mThumbCenterColor(p.thumb_center_color()),
- mThumbCenterSelectedColor(p.thumb_center_selected_color()),
- mDisabledThumbColor(p.thumb_disabled_color()),
- mTriangleColor(p.triangle_color()),
- mThumbWidth(p.thumb_width),
- mOrientation((p.orientation() == "vertical") ? VERTICAL : HORIZONTAL),
- mMouseDownSignal(NULL),
- mMouseUpSignal(NULL)
-{
- mValue = LLSD::emptyMap();
- mCurSlider = LLStringUtil::null;
-
- if (mOrientation == HORIZONTAL)
- {
- mDragStartThumbRect = LLRect(0, getRect().getHeight(), p.thumb_width, 0);
- }
- else
- {
- mDragStartThumbRect = LLRect(0, p.thumb_width, getRect().getWidth(), 0);
- }
-
- if (p.mouse_down_callback.isProvided())
- {
- setMouseDownCallback(initCommitCallback(p.mouse_down_callback));
- }
- if (p.mouse_up_callback.isProvided())
- {
- setMouseUpCallback(initCommitCallback(p.mouse_up_callback));
- }
-
- if (p.overlap_threshold.isProvided() && p.overlap_threshold > mIncrement)
- {
- mOverlapThreshold = p.overlap_threshold - mIncrement;
- }
- else
- {
- mOverlapThreshold = 0;
- }
-
- for (LLInitParam::ParamIterator<SliderParams>::const_iterator it = p.sliders.begin();
- it != p.sliders.end();
- ++it)
- {
- if (it->name.isProvided())
- {
- addSlider(it->value, it->name);
- }
- else
- {
- addSlider(it->value);
- }
- }
-
- mRoundedSquareImgp = LLUI::getUIImage("Rounded_Square");
- if (p.thumb_image.isProvided())
- {
- mThumbImagep = LLUI::getUIImage(p.thumb_image());
- }
- mThumbHighlightColor = p.thumb_highlight_color.isProvided() ? p.thumb_highlight_color() : static_cast<LLUIColor>(gFocusMgr.getFocusColor());
-}
-
-LLMultiSlider::~LLMultiSlider()
-{
- delete mMouseDownSignal;
- delete mMouseUpSignal;
-}
-
-F32 LLMultiSlider::getNearestIncrement(F32 value) const
-{
- value = llclamp(value, mMinValue, mMaxValue);
-
- // Round to nearest increment (bias towards rounding down)
- value -= mMinValue;
- value += mIncrement / 2.0001f;
- value -= fmod(value, mIncrement);
- return mMinValue + value;
-}
-
-void LLMultiSlider::setSliderValue(const std::string& name, F32 value, bool from_event)
-{
- // exit if not there
- if(!mValue.has(name)) {
- return;
- }
-
- F32 newValue = getNearestIncrement(value);
-
- // now, make sure no overlap
- // if we want that
- if(!mAllowOverlap) {
- bool hit = false;
-
- // look at the current spot
- // and see if anything is there
- LLSD::map_iterator mIt = mValue.beginMap();
-
- // increment is our distance between points, use to eliminate round error
- F32 threshold = mOverlapThreshold + (mIncrement / 4);
- // If loop overlap is enabled, check if we overlap with points 'after' max value (project to lower)
- F32 loop_up_check = (mLoopOverlap && (value + threshold) > mMaxValue) ? (value + threshold - mMaxValue + mMinValue) : mMinValue - 1.0f;
- // If loop overlap is enabled, check if we overlap with points 'before' min value (project to upper)
- F32 loop_down_check = (mLoopOverlap && (value - threshold) < mMinValue) ? (value - threshold - mMinValue + mMaxValue) : mMaxValue + 1.0f;
-
- for(;mIt != mValue.endMap(); mIt++)
- {
- F32 locationVal = (F32)mIt->second.asReal();
- // Check nearby values
- F32 testVal = locationVal - newValue;
- if (testVal > -threshold
- && testVal < threshold
- && mIt->first != name)
- {
- hit = true;
- break;
- }
- if (mLoopOverlap)
- {
- // Check edge overlap values
- if (locationVal < loop_up_check)
- {
- hit = true;
- break;
- }
- if (locationVal > loop_down_check)
- {
- hit = true;
- break;
- }
- }
- }
-
- // if none found, stop
- if(hit) {
- return;
- }
- }
-
-
- // now set it in the map
- mValue[name] = newValue;
-
- // set the control if it's the current slider and not from an event
- if (!from_event && name == mCurSlider)
- {
- setControlValue(mValue);
- }
-
- F32 t = (newValue - mMinValue) / (mMaxValue - mMinValue);
- if (mOrientation == HORIZONTAL)
- {
- S32 left_edge = mThumbWidth/2;
- S32 right_edge = getRect().getWidth() - (mThumbWidth/2);
-
- S32 x = left_edge + S32( t * (right_edge - left_edge) );
-
- mThumbRects[name].mLeft = x - (mThumbWidth / 2);
- mThumbRects[name].mRight = x + (mThumbWidth / 2);
- }
- else
- {
- S32 bottom_edge = mThumbWidth/2;
- S32 top_edge = getRect().getHeight() - (mThumbWidth/2);
-
- S32 x = bottom_edge + S32( t * (top_edge - bottom_edge) );
-
- mThumbRects[name].mTop = x + (mThumbWidth / 2);
- mThumbRects[name].mBottom = x - (mThumbWidth / 2);
- }
-}
-
-void LLMultiSlider::setValue(const LLSD& value)
-{
- // only do if it's a map
- if(value.isMap()) {
-
- // add each value... the first in the map becomes the current
- LLSD::map_const_iterator mIt = value.beginMap();
- mCurSlider = mIt->first;
-
- for(; mIt != value.endMap(); mIt++) {
- setSliderValue(mIt->first, (F32)mIt->second.asReal(), true);
- }
- }
-}
-
-F32 LLMultiSlider::getSliderValue(const std::string& name) const
-{
- if (mValue.has(name))
- {
- return (F32)mValue[name].asReal();
- }
- return 0;
-}
-
-void LLMultiSlider::setCurSlider(const std::string& name)
-{
- if(mValue.has(name)) {
- mCurSlider = name;
- }
-}
-
-F32 LLMultiSlider::getSliderValueFromPos(S32 xpos, S32 ypos) const
-{
- F32 t = 0;
- if (mOrientation == HORIZONTAL)
- {
- S32 left_edge = mThumbWidth / 2;
- S32 right_edge = getRect().getWidth() - (mThumbWidth / 2);
-
- xpos += mMouseOffset;
- xpos = llclamp(xpos, left_edge, right_edge);
-
- t = F32(xpos - left_edge) / (right_edge - left_edge);
- }
- else
- {
- S32 bottom_edge = mThumbWidth / 2;
- S32 top_edge = getRect().getHeight() - (mThumbWidth / 2);
-
- ypos += mMouseOffset;
- ypos = llclamp(ypos, bottom_edge, top_edge);
-
- t = F32(ypos - bottom_edge) / (top_edge - bottom_edge);
- }
-
- return((t * (mMaxValue - mMinValue)) + mMinValue);
-}
-
-
-LLRect LLMultiSlider::getSliderThumbRect(const std::string& name) const
-{
- auto it = mThumbRects.find(name);
- if (it != mThumbRects.end())
- return (*it).second;
- return LLRect();
-}
-
-void LLMultiSlider::setSliderThumbImage(const std::string &name)
-{
- if (!name.empty())
- {
- mThumbImagep = LLUI::getUIImage(name);
- }
- else
- clearSliderThumbImage();
-}
-
-void LLMultiSlider::clearSliderThumbImage()
-{
- mThumbImagep = NULL;
-}
-
-void LLMultiSlider::resetCurSlider()
-{
- mCurSlider = LLStringUtil::null;
-}
-
-const std::string& LLMultiSlider::addSlider()
-{
- return addSlider(mInitialValue);
-}
-
-const std::string& LLMultiSlider::addSlider(F32 val)
-{
- std::stringstream newName;
- F32 initVal = val;
-
- if(mValue.size() >= mMaxNumSliders) {
- return LLStringUtil::null;
- }
-
- // create a new name
- newName << "sldr" << mNameCounter;
- mNameCounter++;
-
- bool foundOne = findUnusedValue(initVal);
- if(!foundOne) {
- return LLStringUtil::null;
- }
-
- // add a new thumb rect
- if (mOrientation == HORIZONTAL)
- {
- mThumbRects[newName.str()] = LLRect(0, getRect().getHeight(), mThumbWidth, 0);
- }
- else
- {
- mThumbRects[newName.str()] = LLRect(0, mThumbWidth, getRect().getWidth(), 0);
- }
-
- // add the value and set the current slider to this one
- mValue.insert(newName.str(), initVal);
- mCurSlider = newName.str();
-
- // move the slider
- setSliderValue(mCurSlider, initVal, true);
-
- return mCurSlider;
-}
-
-bool LLMultiSlider::addSlider(F32 val, const std::string& name)
-{
- F32 initVal = val;
-
- if(mValue.size() >= mMaxNumSliders) {
- return false;
- }
-
- bool foundOne = findUnusedValue(initVal);
- if(!foundOne) {
- return false;
- }
-
- // add a new thumb rect
- if (mOrientation == HORIZONTAL)
- {
- mThumbRects[name] = LLRect(0, getRect().getHeight(), mThumbWidth, 0);
- }
- else
- {
- mThumbRects[name] = LLRect(0, mThumbWidth, getRect().getWidth(), 0);
- }
-
- // add the value and set the current slider to this one
- mValue.insert(name, initVal);
- mCurSlider = name;
-
- // move the slider
- setSliderValue(mCurSlider, initVal, true);
-
- return true;
-}
-
-bool LLMultiSlider::findUnusedValue(F32& initVal)
-{
- bool firstTry = true;
-
- // find the first open slot starting with
- // the initial value
- while(true) {
-
- bool hit = false;
-
- // look at the current spot
- // and see if anything is there
- F32 threshold = mAllowOverlap ? FLOAT_THRESHOLD : mOverlapThreshold + (mIncrement / 4);
- LLSD::map_iterator mIt = mValue.beginMap();
- for(;mIt != mValue.endMap(); mIt++) {
-
- F32 testVal = (F32)mIt->second.asReal() - initVal;
- if(testVal > -threshold && testVal < threshold)
- {
- hit = true;
- break;
- }
- }
-
- // if we found one
- if(!hit) {
- break;
- }
-
- // increment and wrap if need be
- initVal += mIncrement;
- if(initVal > mMaxValue) {
- initVal = mMinValue;
- }
-
- // stop if it's filled
- if(initVal == mInitialValue && !firstTry) {
- LL_WARNS() << "Whoa! Too many multi slider elements to add one to" << LL_ENDL;
- return false;
- }
-
- firstTry = false;
- continue;
- }
-
- return true;
-}
-
-
-void LLMultiSlider::deleteSlider(const std::string& name)
-{
- // can't delete last slider
- if(mValue.size() <= 0) {
- return;
- }
-
- // get rid of value from mValue and its thumb rect
- mValue.erase(name);
- mThumbRects.erase(name);
-
- // set to the last created
- if(mValue.size() > 0) {
- std::map<std::string, LLRect>::iterator mIt = mThumbRects.end();
- mIt--;
- mCurSlider = mIt->first;
- }
-}
-
-void LLMultiSlider::clear()
-{
- while(mThumbRects.size() > 0 && mValue.size() > 0) {
- deleteCurSlider();
- }
-
- if (mThumbRects.size() > 0 || mValue.size() > 0)
- {
- LL_WARNS() << "Failed to fully clear Multi slider" << LL_ENDL;
- }
-
- LLF32UICtrl::clear();
-}
-
-bool LLMultiSlider::handleHover(S32 x, S32 y, MASK mask)
-{
- if( gFocusMgr.getMouseCapture() == this )
- {
- setCurSliderValue(getSliderValueFromPos(x, y));
- onCommit();
-
- getWindow()->setCursor(UI_CURSOR_ARROW);
- LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL;
- }
- else
- {
- if (getEnabled())
- {
- mHoverSlider.clear();
- std::map<std::string, LLRect>::iterator mIt = mThumbRects.begin();
- for (; mIt != mThumbRects.end(); mIt++)
- {
- if (mIt->second.pointInRect(x, y))
- {
- mHoverSlider = mIt->first;
- break;
- }
- }
- }
- else
- {
- mHoverSlider.clear();
- }
-
- getWindow()->setCursor(UI_CURSOR_ARROW);
- LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL;
- }
- return true;
-}
-
-bool LLMultiSlider::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
-
- if( gFocusMgr.getMouseCapture() == this )
- {
- gFocusMgr.setMouseCapture( NULL );
-
- if (mMouseUpSignal)
- (*mMouseUpSignal)( this, LLSD() );
-
- handled = true;
- make_ui_sound("UISndClickRelease");
- }
- else
- {
- handled = true;
- }
-
- return handled;
-}
-
-bool LLMultiSlider::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- // only do sticky-focus on non-chrome widgets
- if (!getIsChrome())
- {
- setFocus(true);
- }
- if (mMouseDownSignal)
- (*mMouseDownSignal)( this, LLSD() );
-
- if (MASK_CONTROL & mask) // if CTRL is modifying
- {
- setCurSliderValue(mInitialValue);
- onCommit();
- }
- else
- {
- // scroll through thumbs to see if we have a new one selected and select that one
- std::map<std::string, LLRect>::iterator mIt = mThumbRects.begin();
- for(; mIt != mThumbRects.end(); mIt++) {
-
- // check if inside. If so, set current slider and continue
- if(mIt->second.pointInRect(x,y)) {
- mCurSlider = mIt->first;
- break;
- }
- }
-
- if (!mCurSlider.empty())
- {
- // Find the offset of the actual mouse location from the center of the thumb.
- if (mThumbRects[mCurSlider].pointInRect(x,y))
- {
- if (mOrientation == HORIZONTAL)
- {
- mMouseOffset = (mThumbRects[mCurSlider].mLeft + mThumbWidth / 2) - x;
- }
- else
- {
- mMouseOffset = (mThumbRects[mCurSlider].mBottom + mThumbWidth / 2) - y;
- }
- }
- else
- {
- mMouseOffset = 0;
- }
-
- // Start dragging the thumb
- // No handler needed for focus lost since this class has no state that depends on it.
- gFocusMgr.setMouseCapture( this );
- mDragStartThumbRect = mThumbRects[mCurSlider];
- }
- }
- make_ui_sound("UISndClick");
-
- return true;
-}
-
-bool LLMultiSlider::handleKeyHere(KEY key, MASK mask)
-{
- bool handled = false;
- switch(key)
- {
- case KEY_UP:
- case KEY_DOWN:
- // eat up and down keys to be consistent
- handled = true;
- break;
- case KEY_LEFT:
- setCurSliderValue(getCurSliderValue() - getIncrement());
- onCommit();
- handled = true;
- break;
- case KEY_RIGHT:
- setCurSliderValue(getCurSliderValue() + getIncrement());
- onCommit();
- handled = true;
- break;
- default:
- break;
- }
- return handled;
-}
-
-/*virtual*/
-void LLMultiSlider::onMouseLeave(S32 x, S32 y, MASK mask)
-{
- mHoverSlider.clear();
- LLF32UICtrl::onMouseLeave(x, y, mask);
-}
-
-void LLMultiSlider::draw()
-{
- static LLUICachedControl<S32> extra_triangle_height ("UIExtraTriangleHeight", 0);
- static LLUICachedControl<S32> extra_triangle_width ("UIExtraTriangleWidth", 0);
- LLColor4 curThumbColor;
-
- std::map<std::string, LLRect>::iterator mIt;
- std::map<std::string, LLRect>::iterator curSldrIt;
- std::map<std::string, LLRect>::iterator hoverSldrIt;
-
- // Draw background and thumb.
-
- // drawing solids requires texturing be disabled
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- LLRect rect(mDragStartThumbRect);
-
- F32 opacity = getEnabled() ? 1.f : 0.3f;
-
- // Track
- static LLUICachedControl<S32> multi_track_height_width ("UIMultiTrackHeight", 0);
- S32 height_offset = 0;
- S32 width_offset = 0;
- if (mOrientation == HORIZONTAL)
- {
- height_offset = (getRect().getHeight() - multi_track_height_width) / 2;
- }
- else
- {
- width_offset = (getRect().getWidth() - multi_track_height_width) / 2;
- }
- LLRect track_rect(width_offset, getRect().getHeight() - height_offset, getRect().getWidth() - width_offset, height_offset);
-
-
- if(mDrawTrack)
- {
- track_rect.stretch(-1);
- mRoundedSquareImgp->draw(track_rect, mTrackColor.get() % opacity);
- }
-
- // if we're supposed to use a drawn triangle
- // simple gl call for the triangle
- if(mUseTriangle) {
-
- for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) {
-
- gl_triangle_2d(
- mIt->second.mLeft - extra_triangle_width,
- mIt->second.mTop + extra_triangle_height,
- mIt->second.mRight + extra_triangle_width,
- mIt->second.mTop + extra_triangle_height,
- mIt->second.mLeft + mIt->second.getWidth() / 2,
- mIt->second.mBottom - extra_triangle_height,
- mTriangleColor.get() % opacity, true);
- }
- }
- else if (!mRoundedSquareImgp && !mThumbImagep)
- {
- // draw all the thumbs
- curSldrIt = mThumbRects.end();
- hoverSldrIt = mThumbRects.end();
- for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) {
-
- // choose the color
- curThumbColor = mThumbCenterColor.get();
- if(mIt->first == mCurSlider) {
-
- curSldrIt = mIt;
- continue;
- }
- if (mIt->first == mHoverSlider && getEnabled() && gFocusMgr.getMouseCapture() != this)
- {
- // draw last, after current one
- hoverSldrIt = mIt;
- continue;
- }
-
- // the draw command
- gl_rect_2d(mIt->second, curThumbColor, true);
- }
-
- // now draw the current and hover sliders
- if(curSldrIt != mThumbRects.end())
- {
- gl_rect_2d(curSldrIt->second, mThumbCenterSelectedColor.get(), true);
- }
-
- // and draw the drag start
- if (gFocusMgr.getMouseCapture() == this)
- {
- gl_rect_2d(mDragStartThumbRect, mThumbCenterColor.get() % opacity, false);
- }
- else if (hoverSldrIt != mThumbRects.end())
- {
- gl_rect_2d(hoverSldrIt->second, mThumbCenterSelectedColor.get(), true);
- }
- }
- else
- {
- LLMouseHandler* capture = gFocusMgr.getMouseCapture();
- if (capture == this)
- {
- // draw drag start (ghost)
- if (mThumbImagep)
- {
- mThumbImagep->draw(mDragStartThumbRect, mThumbCenterColor.get() % 0.3f);
- }
- else
- {
- mRoundedSquareImgp->drawSolid(mDragStartThumbRect, mThumbCenterColor.get() % 0.3f);
- }
- }
-
- // draw the highlight
- if (hasFocus())
- {
- if (!mCurSlider.empty())
- {
- if (mThumbImagep)
- {
- mThumbImagep->drawBorder(mThumbRects[mCurSlider], mThumbHighlightColor, gFocusMgr.getFocusFlashWidth());
- }
- else
- {
- mRoundedSquareImgp->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
- }
- }
- }
- if (!mHoverSlider.empty())
- {
- if (mThumbImagep)
- {
- mThumbImagep->drawBorder(mThumbRects[mHoverSlider], mThumbHighlightColor, gFocusMgr.getFocusFlashWidth());
- }
- else
- {
- mRoundedSquareImgp->drawBorder(mThumbRects[mHoverSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
- }
- }
-
- // draw the thumbs
- curSldrIt = mThumbRects.end();
- hoverSldrIt = mThumbRects.end();
- for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++)
- {
- // choose the color
- curThumbColor = mThumbCenterColor.get();
- if(mIt->first == mCurSlider)
- {
- // don't draw now, draw last
- curSldrIt = mIt;
- continue;
- }
- if (mIt->first == mHoverSlider && getEnabled() && gFocusMgr.getMouseCapture() != this)
- {
- // don't draw now, draw last, after current one
- hoverSldrIt = mIt;
- continue;
- }
-
- // the draw command
- if (mThumbImagep)
- {
- if (getEnabled())
- {
- mThumbImagep->draw(mIt->second);
- }
- else
- {
- mThumbImagep->draw(mIt->second, LLColor4::grey % 0.8f);
- }
- }
- else if (capture == this)
- {
- mRoundedSquareImgp->drawSolid(mIt->second, curThumbColor);
- }
- else
- {
- mRoundedSquareImgp->drawSolid(mIt->second, curThumbColor % opacity);
- }
- }
-
- // draw cur and hover slider last
- if(curSldrIt != mThumbRects.end())
- {
- if (mThumbImagep)
- {
- if (getEnabled())
- {
- mThumbImagep->draw(curSldrIt->second);
- }
- else
- {
- mThumbImagep->draw(curSldrIt->second, LLColor4::grey % 0.8f);
- }
- }
- else if (capture == this)
- {
- mRoundedSquareImgp->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get());
- }
- else
- {
- mRoundedSquareImgp->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get() % opacity);
- }
- }
- if(hoverSldrIt != mThumbRects.end())
- {
- if (mThumbImagep)
- {
- mThumbImagep->draw(hoverSldrIt->second);
- }
- else
- {
- mRoundedSquareImgp->drawSolid(hoverSldrIt->second, mThumbCenterSelectedColor.get());
- }
- }
- }
-
- LLF32UICtrl::draw();
-}
-boost::signals2::connection LLMultiSlider::setMouseDownCallback( const commit_signal_t::slot_type& cb )
-{
- if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t();
- return mMouseDownSignal->connect(cb);
-}
-
-boost::signals2::connection LLMultiSlider::setMouseUpCallback( const commit_signal_t::slot_type& cb )
-{
- if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t();
- return mMouseUpSignal->connect(cb);
-}
+/**
+ * @file llmultisldr.cpp
+ * @brief LLMultiSlider base class
+ *
+ * $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$
+ */
+
+#include "linden_common.h"
+
+#include "llmultislider.h"
+#include "llui.h"
+
+#include "llgl.h"
+#include "llwindow.h"
+#include "llfocusmgr.h"
+#include "llkeyboard.h" // for the MASK constants
+#include "llcontrol.h"
+#include "lluictrlfactory.h"
+#include "lluiimage.h"
+
+#include <sstream>
+
+static LLDefaultChildRegistry::Register<LLMultiSlider> r("multi_slider_bar");
+
+const F32 FLOAT_THRESHOLD = 0.00001f;
+
+S32 LLMultiSlider::mNameCounter = 0;
+
+LLMultiSlider::SliderParams::SliderParams()
+: name("name"),
+ value("value", 0.f)
+{
+}
+
+LLMultiSlider::Params::Params()
+: max_sliders("max_sliders", 1),
+ allow_overlap("allow_overlap", false),
+ loop_overlap("loop_overlap", false),
+ orientation("orientation"),
+ overlap_threshold("overlap_threshold", 0),
+ draw_track("draw_track", true),
+ use_triangle("use_triangle", false),
+ track_color("track_color"),
+ thumb_disabled_color("thumb_disabled_color"),
+ thumb_highlight_color("thumb_highlight_color"),
+ thumb_outline_color("thumb_outline_color"),
+ thumb_center_color("thumb_center_color"),
+ thumb_center_selected_color("thumb_center_selected_color"),
+ thumb_image("thumb_image"),
+ triangle_color("triangle_color"),
+ mouse_down_callback("mouse_down_callback"),
+ mouse_up_callback("mouse_up_callback"),
+ thumb_width("thumb_width"),
+ sliders("slider")
+{}
+
+LLMultiSlider::LLMultiSlider(const LLMultiSlider::Params& p)
+: LLF32UICtrl(p),
+ mMouseOffset( 0 ),
+ mMaxNumSliders(p.max_sliders),
+ mAllowOverlap(p.allow_overlap),
+ mLoopOverlap(p.loop_overlap),
+ mDrawTrack(p.draw_track),
+ mUseTriangle(p.use_triangle),
+ mTrackColor(p.track_color()),
+ mThumbOutlineColor(p.thumb_outline_color()),
+ mThumbCenterColor(p.thumb_center_color()),
+ mThumbCenterSelectedColor(p.thumb_center_selected_color()),
+ mDisabledThumbColor(p.thumb_disabled_color()),
+ mTriangleColor(p.triangle_color()),
+ mThumbWidth(p.thumb_width),
+ mOrientation((p.orientation() == "vertical") ? VERTICAL : HORIZONTAL),
+ mMouseDownSignal(NULL),
+ mMouseUpSignal(NULL)
+{
+ mValue = LLSD::emptyMap();
+ mCurSlider = LLStringUtil::null;
+
+ if (mOrientation == HORIZONTAL)
+ {
+ mDragStartThumbRect = LLRect(0, getRect().getHeight(), p.thumb_width, 0);
+ }
+ else
+ {
+ mDragStartThumbRect = LLRect(0, p.thumb_width, getRect().getWidth(), 0);
+ }
+
+ if (p.mouse_down_callback.isProvided())
+ {
+ setMouseDownCallback(initCommitCallback(p.mouse_down_callback));
+ }
+ if (p.mouse_up_callback.isProvided())
+ {
+ setMouseUpCallback(initCommitCallback(p.mouse_up_callback));
+ }
+
+ if (p.overlap_threshold.isProvided() && p.overlap_threshold > mIncrement)
+ {
+ mOverlapThreshold = p.overlap_threshold - mIncrement;
+ }
+ else
+ {
+ mOverlapThreshold = 0;
+ }
+
+ for (LLInitParam::ParamIterator<SliderParams>::const_iterator it = p.sliders.begin();
+ it != p.sliders.end();
+ ++it)
+ {
+ if (it->name.isProvided())
+ {
+ addSlider(it->value, it->name);
+ }
+ else
+ {
+ addSlider(it->value);
+ }
+ }
+
+ mRoundedSquareImgp = LLUI::getUIImage("Rounded_Square");
+ if (p.thumb_image.isProvided())
+ {
+ mThumbImagep = LLUI::getUIImage(p.thumb_image());
+ }
+ mThumbHighlightColor = p.thumb_highlight_color.isProvided() ? p.thumb_highlight_color() : static_cast<LLUIColor>(gFocusMgr.getFocusColor());
+}
+
+LLMultiSlider::~LLMultiSlider()
+{
+ delete mMouseDownSignal;
+ delete mMouseUpSignal;
+}
+
+F32 LLMultiSlider::getNearestIncrement(F32 value) const
+{
+ value = llclamp(value, mMinValue, mMaxValue);
+
+ // Round to nearest increment (bias towards rounding down)
+ value -= mMinValue;
+ value += mIncrement / 2.0001f;
+ value -= fmod(value, mIncrement);
+ return mMinValue + value;
+}
+
+void LLMultiSlider::setSliderValue(const std::string& name, F32 value, bool from_event)
+{
+ // exit if not there
+ if(!mValue.has(name)) {
+ return;
+ }
+
+ F32 newValue = getNearestIncrement(value);
+
+ // now, make sure no overlap
+ // if we want that
+ if(!mAllowOverlap) {
+ bool hit = false;
+
+ // look at the current spot
+ // and see if anything is there
+ LLSD::map_iterator mIt = mValue.beginMap();
+
+ // increment is our distance between points, use to eliminate round error
+ F32 threshold = mOverlapThreshold + (mIncrement / 4);
+ // If loop overlap is enabled, check if we overlap with points 'after' max value (project to lower)
+ F32 loop_up_check = (mLoopOverlap && (value + threshold) > mMaxValue) ? (value + threshold - mMaxValue + mMinValue) : mMinValue - 1.0f;
+ // If loop overlap is enabled, check if we overlap with points 'before' min value (project to upper)
+ F32 loop_down_check = (mLoopOverlap && (value - threshold) < mMinValue) ? (value - threshold - mMinValue + mMaxValue) : mMaxValue + 1.0f;
+
+ for(;mIt != mValue.endMap(); mIt++)
+ {
+ F32 locationVal = (F32)mIt->second.asReal();
+ // Check nearby values
+ F32 testVal = locationVal - newValue;
+ if (testVal > -threshold
+ && testVal < threshold
+ && mIt->first != name)
+ {
+ hit = true;
+ break;
+ }
+ if (mLoopOverlap)
+ {
+ // Check edge overlap values
+ if (locationVal < loop_up_check)
+ {
+ hit = true;
+ break;
+ }
+ if (locationVal > loop_down_check)
+ {
+ hit = true;
+ break;
+ }
+ }
+ }
+
+ // if none found, stop
+ if(hit) {
+ return;
+ }
+ }
+
+
+ // now set it in the map
+ mValue[name] = newValue;
+
+ // set the control if it's the current slider and not from an event
+ if (!from_event && name == mCurSlider)
+ {
+ setControlValue(mValue);
+ }
+
+ F32 t = (newValue - mMinValue) / (mMaxValue - mMinValue);
+ if (mOrientation == HORIZONTAL)
+ {
+ S32 left_edge = mThumbWidth/2;
+ S32 right_edge = getRect().getWidth() - (mThumbWidth/2);
+
+ S32 x = left_edge + S32( t * (right_edge - left_edge) );
+
+ mThumbRects[name].mLeft = x - (mThumbWidth / 2);
+ mThumbRects[name].mRight = x + (mThumbWidth / 2);
+ }
+ else
+ {
+ S32 bottom_edge = mThumbWidth/2;
+ S32 top_edge = getRect().getHeight() - (mThumbWidth/2);
+
+ S32 x = bottom_edge + S32( t * (top_edge - bottom_edge) );
+
+ mThumbRects[name].mTop = x + (mThumbWidth / 2);
+ mThumbRects[name].mBottom = x - (mThumbWidth / 2);
+ }
+}
+
+void LLMultiSlider::setValue(const LLSD& value)
+{
+ // only do if it's a map
+ if(value.isMap()) {
+
+ // add each value... the first in the map becomes the current
+ LLSD::map_const_iterator mIt = value.beginMap();
+ mCurSlider = mIt->first;
+
+ for(; mIt != value.endMap(); mIt++) {
+ setSliderValue(mIt->first, (F32)mIt->second.asReal(), true);
+ }
+ }
+}
+
+F32 LLMultiSlider::getSliderValue(const std::string& name) const
+{
+ if (mValue.has(name))
+ {
+ return (F32)mValue[name].asReal();
+ }
+ return 0;
+}
+
+void LLMultiSlider::setCurSlider(const std::string& name)
+{
+ if(mValue.has(name)) {
+ mCurSlider = name;
+ }
+}
+
+F32 LLMultiSlider::getSliderValueFromPos(S32 xpos, S32 ypos) const
+{
+ F32 t = 0;
+ if (mOrientation == HORIZONTAL)
+ {
+ S32 left_edge = mThumbWidth / 2;
+ S32 right_edge = getRect().getWidth() - (mThumbWidth / 2);
+
+ xpos += mMouseOffset;
+ xpos = llclamp(xpos, left_edge, right_edge);
+
+ t = F32(xpos - left_edge) / (right_edge - left_edge);
+ }
+ else
+ {
+ S32 bottom_edge = mThumbWidth / 2;
+ S32 top_edge = getRect().getHeight() - (mThumbWidth / 2);
+
+ ypos += mMouseOffset;
+ ypos = llclamp(ypos, bottom_edge, top_edge);
+
+ t = F32(ypos - bottom_edge) / (top_edge - bottom_edge);
+ }
+
+ return((t * (mMaxValue - mMinValue)) + mMinValue);
+}
+
+
+LLRect LLMultiSlider::getSliderThumbRect(const std::string& name) const
+{
+ auto it = mThumbRects.find(name);
+ if (it != mThumbRects.end())
+ return (*it).second;
+ return LLRect();
+}
+
+void LLMultiSlider::setSliderThumbImage(const std::string &name)
+{
+ if (!name.empty())
+ {
+ mThumbImagep = LLUI::getUIImage(name);
+ }
+ else
+ clearSliderThumbImage();
+}
+
+void LLMultiSlider::clearSliderThumbImage()
+{
+ mThumbImagep = NULL;
+}
+
+void LLMultiSlider::resetCurSlider()
+{
+ mCurSlider = LLStringUtil::null;
+}
+
+const std::string& LLMultiSlider::addSlider()
+{
+ return addSlider(mInitialValue);
+}
+
+const std::string& LLMultiSlider::addSlider(F32 val)
+{
+ std::stringstream newName;
+ F32 initVal = val;
+
+ if(mValue.size() >= mMaxNumSliders) {
+ return LLStringUtil::null;
+ }
+
+ // create a new name
+ newName << "sldr" << mNameCounter;
+ mNameCounter++;
+
+ bool foundOne = findUnusedValue(initVal);
+ if(!foundOne) {
+ return LLStringUtil::null;
+ }
+
+ // add a new thumb rect
+ if (mOrientation == HORIZONTAL)
+ {
+ mThumbRects[newName.str()] = LLRect(0, getRect().getHeight(), mThumbWidth, 0);
+ }
+ else
+ {
+ mThumbRects[newName.str()] = LLRect(0, mThumbWidth, getRect().getWidth(), 0);
+ }
+
+ // add the value and set the current slider to this one
+ mValue.insert(newName.str(), initVal);
+ mCurSlider = newName.str();
+
+ // move the slider
+ setSliderValue(mCurSlider, initVal, true);
+
+ return mCurSlider;
+}
+
+bool LLMultiSlider::addSlider(F32 val, const std::string& name)
+{
+ F32 initVal = val;
+
+ if(mValue.size() >= mMaxNumSliders) {
+ return false;
+ }
+
+ bool foundOne = findUnusedValue(initVal);
+ if(!foundOne) {
+ return false;
+ }
+
+ // add a new thumb rect
+ if (mOrientation == HORIZONTAL)
+ {
+ mThumbRects[name] = LLRect(0, getRect().getHeight(), mThumbWidth, 0);
+ }
+ else
+ {
+ mThumbRects[name] = LLRect(0, mThumbWidth, getRect().getWidth(), 0);
+ }
+
+ // add the value and set the current slider to this one
+ mValue.insert(name, initVal);
+ mCurSlider = name;
+
+ // move the slider
+ setSliderValue(mCurSlider, initVal, true);
+
+ return true;
+}
+
+bool LLMultiSlider::findUnusedValue(F32& initVal)
+{
+ bool firstTry = true;
+
+ // find the first open slot starting with
+ // the initial value
+ while(true) {
+
+ bool hit = false;
+
+ // look at the current spot
+ // and see if anything is there
+ F32 threshold = mAllowOverlap ? FLOAT_THRESHOLD : mOverlapThreshold + (mIncrement / 4);
+ LLSD::map_iterator mIt = mValue.beginMap();
+ for(;mIt != mValue.endMap(); mIt++) {
+
+ F32 testVal = (F32)mIt->second.asReal() - initVal;
+ if(testVal > -threshold && testVal < threshold)
+ {
+ hit = true;
+ break;
+ }
+ }
+
+ // if we found one
+ if(!hit) {
+ break;
+ }
+
+ // increment and wrap if need be
+ initVal += mIncrement;
+ if(initVal > mMaxValue) {
+ initVal = mMinValue;
+ }
+
+ // stop if it's filled
+ if(initVal == mInitialValue && !firstTry) {
+ LL_WARNS() << "Whoa! Too many multi slider elements to add one to" << LL_ENDL;
+ return false;
+ }
+
+ firstTry = false;
+ continue;
+ }
+
+ return true;
+}
+
+
+void LLMultiSlider::deleteSlider(const std::string& name)
+{
+ // can't delete last slider
+ if(mValue.size() <= 0) {
+ return;
+ }
+
+ // get rid of value from mValue and its thumb rect
+ mValue.erase(name);
+ mThumbRects.erase(name);
+
+ // set to the last created
+ if(mValue.size() > 0) {
+ std::map<std::string, LLRect>::iterator mIt = mThumbRects.end();
+ mIt--;
+ mCurSlider = mIt->first;
+ }
+}
+
+void LLMultiSlider::clear()
+{
+ while(mThumbRects.size() > 0 && mValue.size() > 0) {
+ deleteCurSlider();
+ }
+
+ if (mThumbRects.size() > 0 || mValue.size() > 0)
+ {
+ LL_WARNS() << "Failed to fully clear Multi slider" << LL_ENDL;
+ }
+
+ LLF32UICtrl::clear();
+}
+
+bool LLMultiSlider::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( gFocusMgr.getMouseCapture() == this )
+ {
+ setCurSliderValue(getSliderValueFromPos(x, y));
+ onCommit();
+
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL;
+ }
+ else
+ {
+ if (getEnabled())
+ {
+ mHoverSlider.clear();
+ std::map<std::string, LLRect>::iterator mIt = mThumbRects.begin();
+ for (; mIt != mThumbRects.end(); mIt++)
+ {
+ if (mIt->second.pointInRect(x, y))
+ {
+ mHoverSlider = mIt->first;
+ break;
+ }
+ }
+ }
+ else
+ {
+ mHoverSlider.clear();
+ }
+
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL;
+ }
+ return true;
+}
+
+bool LLMultiSlider::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ bool handled = false;
+
+ if( gFocusMgr.getMouseCapture() == this )
+ {
+ gFocusMgr.setMouseCapture( NULL );
+
+ if (mMouseUpSignal)
+ (*mMouseUpSignal)( this, LLSD() );
+
+ handled = true;
+ make_ui_sound("UISndClickRelease");
+ }
+ else
+ {
+ handled = true;
+ }
+
+ return handled;
+}
+
+bool LLMultiSlider::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // only do sticky-focus on non-chrome widgets
+ if (!getIsChrome())
+ {
+ setFocus(true);
+ }
+ if (mMouseDownSignal)
+ (*mMouseDownSignal)( this, LLSD() );
+
+ if (MASK_CONTROL & mask) // if CTRL is modifying
+ {
+ setCurSliderValue(mInitialValue);
+ onCommit();
+ }
+ else
+ {
+ // scroll through thumbs to see if we have a new one selected and select that one
+ std::map<std::string, LLRect>::iterator mIt = mThumbRects.begin();
+ for(; mIt != mThumbRects.end(); mIt++) {
+
+ // check if inside. If so, set current slider and continue
+ if(mIt->second.pointInRect(x,y)) {
+ mCurSlider = mIt->first;
+ break;
+ }
+ }
+
+ if (!mCurSlider.empty())
+ {
+ // Find the offset of the actual mouse location from the center of the thumb.
+ if (mThumbRects[mCurSlider].pointInRect(x,y))
+ {
+ if (mOrientation == HORIZONTAL)
+ {
+ mMouseOffset = (mThumbRects[mCurSlider].mLeft + mThumbWidth / 2) - x;
+ }
+ else
+ {
+ mMouseOffset = (mThumbRects[mCurSlider].mBottom + mThumbWidth / 2) - y;
+ }
+ }
+ else
+ {
+ mMouseOffset = 0;
+ }
+
+ // Start dragging the thumb
+ // No handler needed for focus lost since this class has no state that depends on it.
+ gFocusMgr.setMouseCapture( this );
+ mDragStartThumbRect = mThumbRects[mCurSlider];
+ }
+ }
+ make_ui_sound("UISndClick");
+
+ return true;
+}
+
+bool LLMultiSlider::handleKeyHere(KEY key, MASK mask)
+{
+ bool handled = false;
+ switch(key)
+ {
+ case KEY_UP:
+ case KEY_DOWN:
+ // eat up and down keys to be consistent
+ handled = true;
+ break;
+ case KEY_LEFT:
+ setCurSliderValue(getCurSliderValue() - getIncrement());
+ onCommit();
+ handled = true;
+ break;
+ case KEY_RIGHT:
+ setCurSliderValue(getCurSliderValue() + getIncrement());
+ onCommit();
+ handled = true;
+ break;
+ default:
+ break;
+ }
+ return handled;
+}
+
+/*virtual*/
+void LLMultiSlider::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+ mHoverSlider.clear();
+ LLF32UICtrl::onMouseLeave(x, y, mask);
+}
+
+void LLMultiSlider::draw()
+{
+ static LLUICachedControl<S32> extra_triangle_height ("UIExtraTriangleHeight", 0);
+ static LLUICachedControl<S32> extra_triangle_width ("UIExtraTriangleWidth", 0);
+ LLColor4 curThumbColor;
+
+ std::map<std::string, LLRect>::iterator mIt;
+ std::map<std::string, LLRect>::iterator curSldrIt;
+ std::map<std::string, LLRect>::iterator hoverSldrIt;
+
+ // Draw background and thumb.
+
+ // drawing solids requires texturing be disabled
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ LLRect rect(mDragStartThumbRect);
+
+ F32 opacity = getEnabled() ? 1.f : 0.3f;
+
+ // Track
+ static LLUICachedControl<S32> multi_track_height_width ("UIMultiTrackHeight", 0);
+ S32 height_offset = 0;
+ S32 width_offset = 0;
+ if (mOrientation == HORIZONTAL)
+ {
+ height_offset = (getRect().getHeight() - multi_track_height_width) / 2;
+ }
+ else
+ {
+ width_offset = (getRect().getWidth() - multi_track_height_width) / 2;
+ }
+ LLRect track_rect(width_offset, getRect().getHeight() - height_offset, getRect().getWidth() - width_offset, height_offset);
+
+
+ if(mDrawTrack)
+ {
+ track_rect.stretch(-1);
+ mRoundedSquareImgp->draw(track_rect, mTrackColor.get() % opacity);
+ }
+
+ // if we're supposed to use a drawn triangle
+ // simple gl call for the triangle
+ if(mUseTriangle) {
+
+ for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) {
+
+ gl_triangle_2d(
+ mIt->second.mLeft - extra_triangle_width,
+ mIt->second.mTop + extra_triangle_height,
+ mIt->second.mRight + extra_triangle_width,
+ mIt->second.mTop + extra_triangle_height,
+ mIt->second.mLeft + mIt->second.getWidth() / 2,
+ mIt->second.mBottom - extra_triangle_height,
+ mTriangleColor.get() % opacity, true);
+ }
+ }
+ else if (!mRoundedSquareImgp && !mThumbImagep)
+ {
+ // draw all the thumbs
+ curSldrIt = mThumbRects.end();
+ hoverSldrIt = mThumbRects.end();
+ for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) {
+
+ // choose the color
+ curThumbColor = mThumbCenterColor.get();
+ if(mIt->first == mCurSlider) {
+
+ curSldrIt = mIt;
+ continue;
+ }
+ if (mIt->first == mHoverSlider && getEnabled() && gFocusMgr.getMouseCapture() != this)
+ {
+ // draw last, after current one
+ hoverSldrIt = mIt;
+ continue;
+ }
+
+ // the draw command
+ gl_rect_2d(mIt->second, curThumbColor, true);
+ }
+
+ // now draw the current and hover sliders
+ if(curSldrIt != mThumbRects.end())
+ {
+ gl_rect_2d(curSldrIt->second, mThumbCenterSelectedColor.get(), true);
+ }
+
+ // and draw the drag start
+ if (gFocusMgr.getMouseCapture() == this)
+ {
+ gl_rect_2d(mDragStartThumbRect, mThumbCenterColor.get() % opacity, false);
+ }
+ else if (hoverSldrIt != mThumbRects.end())
+ {
+ gl_rect_2d(hoverSldrIt->second, mThumbCenterSelectedColor.get(), true);
+ }
+ }
+ else
+ {
+ LLMouseHandler* capture = gFocusMgr.getMouseCapture();
+ if (capture == this)
+ {
+ // draw drag start (ghost)
+ if (mThumbImagep)
+ {
+ mThumbImagep->draw(mDragStartThumbRect, mThumbCenterColor.get() % 0.3f);
+ }
+ else
+ {
+ mRoundedSquareImgp->drawSolid(mDragStartThumbRect, mThumbCenterColor.get() % 0.3f);
+ }
+ }
+
+ // draw the highlight
+ if (hasFocus())
+ {
+ if (!mCurSlider.empty())
+ {
+ if (mThumbImagep)
+ {
+ mThumbImagep->drawBorder(mThumbRects[mCurSlider], mThumbHighlightColor, gFocusMgr.getFocusFlashWidth());
+ }
+ else
+ {
+ mRoundedSquareImgp->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
+ }
+ }
+ }
+ if (!mHoverSlider.empty())
+ {
+ if (mThumbImagep)
+ {
+ mThumbImagep->drawBorder(mThumbRects[mHoverSlider], mThumbHighlightColor, gFocusMgr.getFocusFlashWidth());
+ }
+ else
+ {
+ mRoundedSquareImgp->drawBorder(mThumbRects[mHoverSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
+ }
+ }
+
+ // draw the thumbs
+ curSldrIt = mThumbRects.end();
+ hoverSldrIt = mThumbRects.end();
+ for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++)
+ {
+ // choose the color
+ curThumbColor = mThumbCenterColor.get();
+ if(mIt->first == mCurSlider)
+ {
+ // don't draw now, draw last
+ curSldrIt = mIt;
+ continue;
+ }
+ if (mIt->first == mHoverSlider && getEnabled() && gFocusMgr.getMouseCapture() != this)
+ {
+ // don't draw now, draw last, after current one
+ hoverSldrIt = mIt;
+ continue;
+ }
+
+ // the draw command
+ if (mThumbImagep)
+ {
+ if (getEnabled())
+ {
+ mThumbImagep->draw(mIt->second);
+ }
+ else
+ {
+ mThumbImagep->draw(mIt->second, LLColor4::grey % 0.8f);
+ }
+ }
+ else if (capture == this)
+ {
+ mRoundedSquareImgp->drawSolid(mIt->second, curThumbColor);
+ }
+ else
+ {
+ mRoundedSquareImgp->drawSolid(mIt->second, curThumbColor % opacity);
+ }
+ }
+
+ // draw cur and hover slider last
+ if(curSldrIt != mThumbRects.end())
+ {
+ if (mThumbImagep)
+ {
+ if (getEnabled())
+ {
+ mThumbImagep->draw(curSldrIt->second);
+ }
+ else
+ {
+ mThumbImagep->draw(curSldrIt->second, LLColor4::grey % 0.8f);
+ }
+ }
+ else if (capture == this)
+ {
+ mRoundedSquareImgp->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get());
+ }
+ else
+ {
+ mRoundedSquareImgp->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get() % opacity);
+ }
+ }
+ if(hoverSldrIt != mThumbRects.end())
+ {
+ if (mThumbImagep)
+ {
+ mThumbImagep->draw(hoverSldrIt->second);
+ }
+ else
+ {
+ mRoundedSquareImgp->drawSolid(hoverSldrIt->second, mThumbCenterSelectedColor.get());
+ }
+ }
+ }
+
+ LLF32UICtrl::draw();
+}
+boost::signals2::connection LLMultiSlider::setMouseDownCallback( const commit_signal_t::slot_type& cb )
+{
+ if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t();
+ return mMouseDownSignal->connect(cb);
+}
+
+boost::signals2::connection LLMultiSlider::setMouseUpCallback( const commit_signal_t::slot_type& cb )
+{
+ if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t();
+ return mMouseUpSignal->connect(cb);
+}
diff --git a/indra/llui/llmultislider.h b/indra/llui/llmultislider.h
index 7195c5d5a3..e0ac563e75 100644
--- a/indra/llui/llmultislider.h
+++ b/indra/llui/llmultislider.h
@@ -1,161 +1,161 @@
-/**
- * @file llmultislider.h
- * @brief A simple multislider
- *
- * $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_MULTI_SLIDER_H
-#define LL_MULTI_SLIDER_H
-
-#include "llf32uictrl.h"
-#include "v4color.h"
-
-class LLUICtrlFactory;
-
-class LLMultiSlider : public LLF32UICtrl
-{
-public:
- struct SliderParams : public LLInitParam::Block<SliderParams>
- {
- Optional<std::string> name;
- Mandatory<F32> value;
- SliderParams();
- };
-
- struct Params : public LLInitParam::Block<Params, LLF32UICtrl::Params>
- {
- Optional<S32> max_sliders;
-
- Optional<bool> allow_overlap,
- loop_overlap,
- draw_track,
- use_triangle;
-
- Optional<F32> overlap_threshold;
-
- Optional<LLUIColor> track_color,
- thumb_disabled_color,
- thumb_highlight_color,
- thumb_outline_color,
- thumb_center_color,
- thumb_center_selected_color,
- triangle_color;
-
- Optional<std::string> orientation,
- thumb_image;
-
- Optional<CommitCallbackParam> mouse_down_callback,
- mouse_up_callback;
- Optional<S32> thumb_width;
-
- Multiple<SliderParams> sliders;
- Params();
- };
-
-protected:
- LLMultiSlider(const Params&);
- friend class LLUICtrlFactory;
-public:
- virtual ~LLMultiSlider();
-
- // Multi-slider rounds values to nearest increments (bias towards rounding down)
- F32 getNearestIncrement(F32 value) const;
-
- void setSliderValue(const std::string& name, F32 value, bool from_event = false);
- F32 getSliderValue(const std::string& name) const;
- F32 getSliderValueFromPos(S32 xpos, S32 ypos) const;
- LLRect getSliderThumbRect(const std::string& name) const;
-
- void setSliderThumbImage(const std::string &name);
- void clearSliderThumbImage();
-
-
- const std::string& getCurSlider() const { return mCurSlider; }
- F32 getCurSliderValue() const { return getSliderValue(mCurSlider); }
- void setCurSlider(const std::string& name);
- void resetCurSlider();
- void setCurSliderValue(F32 val, bool from_event = false) { setSliderValue(mCurSlider, val, from_event); }
-
- /*virtual*/ void setValue(const LLSD& value) override;
- /*virtual*/ LLSD getValue() const override { return mValue; }
-
- boost::signals2::connection setMouseDownCallback( const commit_signal_t::slot_type& cb );
- boost::signals2::connection setMouseUpCallback( const commit_signal_t::slot_type& cb );
-
- bool findUnusedValue(F32& initVal);
- const std::string& addSlider();
- const std::string& addSlider(F32 val);
- bool addSlider(F32 val, const std::string& name);
- void deleteSlider(const std::string& name);
- void deleteCurSlider() { deleteSlider(mCurSlider); }
- /*virtual*/ void clear() override;
-
- /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask) override;
- /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask) override;
- /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask) override;
- /*virtual*/ bool handleKeyHere(KEY key, MASK mask) override;
- /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) override;
- /*virtual*/ void draw() override;
-
- S32 getMaxNumSliders() { return mMaxNumSliders; }
- S32 getCurNumSliders() { return mValue.size(); }
- F32 getOverlapThreshold() { return mOverlapThreshold; }
- bool canAddSliders() { return mValue.size() < mMaxNumSliders; }
-
-
-protected:
- LLSD mValue;
- std::string mCurSlider;
- std::string mHoverSlider;
- static S32 mNameCounter;
-
- S32 mMaxNumSliders;
- bool mAllowOverlap;
- bool mLoopOverlap;
- F32 mOverlapThreshold;
- bool mDrawTrack;
- bool mUseTriangle; /// hacked in toggle to use a triangle
-
- S32 mMouseOffset;
- LLRect mDragStartThumbRect;
- S32 mThumbWidth;
-
- std::map<std::string, LLRect>
- mThumbRects;
- LLUIColor mTrackColor;
- LLUIColor mThumbOutlineColor;
- LLUIColor mThumbHighlightColor;
- LLUIColor mThumbCenterColor;
- LLUIColor mThumbCenterSelectedColor;
- LLUIColor mDisabledThumbColor;
- LLUIColor mTriangleColor;
- LLUIImagePtr mThumbImagep; //blimps on the slider, for now no 'disabled' support
- LLUIImagePtr mRoundedSquareImgp; //blimps on the slider, for now no 'disabled' support
-
- const EOrientation mOrientation;
-
- commit_signal_t* mMouseDownSignal;
- commit_signal_t* mMouseUpSignal;
-};
-
-#endif // LL_MULTI_SLIDER_H
+/**
+ * @file llmultislider.h
+ * @brief A simple multislider
+ *
+ * $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_MULTI_SLIDER_H
+#define LL_MULTI_SLIDER_H
+
+#include "llf32uictrl.h"
+#include "v4color.h"
+
+class LLUICtrlFactory;
+
+class LLMultiSlider : public LLF32UICtrl
+{
+public:
+ struct SliderParams : public LLInitParam::Block<SliderParams>
+ {
+ Optional<std::string> name;
+ Mandatory<F32> value;
+ SliderParams();
+ };
+
+ struct Params : public LLInitParam::Block<Params, LLF32UICtrl::Params>
+ {
+ Optional<S32> max_sliders;
+
+ Optional<bool> allow_overlap,
+ loop_overlap,
+ draw_track,
+ use_triangle;
+
+ Optional<F32> overlap_threshold;
+
+ Optional<LLUIColor> track_color,
+ thumb_disabled_color,
+ thumb_highlight_color,
+ thumb_outline_color,
+ thumb_center_color,
+ thumb_center_selected_color,
+ triangle_color;
+
+ Optional<std::string> orientation,
+ thumb_image;
+
+ Optional<CommitCallbackParam> mouse_down_callback,
+ mouse_up_callback;
+ Optional<S32> thumb_width;
+
+ Multiple<SliderParams> sliders;
+ Params();
+ };
+
+protected:
+ LLMultiSlider(const Params&);
+ friend class LLUICtrlFactory;
+public:
+ virtual ~LLMultiSlider();
+
+ // Multi-slider rounds values to nearest increments (bias towards rounding down)
+ F32 getNearestIncrement(F32 value) const;
+
+ void setSliderValue(const std::string& name, F32 value, bool from_event = false);
+ F32 getSliderValue(const std::string& name) const;
+ F32 getSliderValueFromPos(S32 xpos, S32 ypos) const;
+ LLRect getSliderThumbRect(const std::string& name) const;
+
+ void setSliderThumbImage(const std::string &name);
+ void clearSliderThumbImage();
+
+
+ const std::string& getCurSlider() const { return mCurSlider; }
+ F32 getCurSliderValue() const { return getSliderValue(mCurSlider); }
+ void setCurSlider(const std::string& name);
+ void resetCurSlider();
+ void setCurSliderValue(F32 val, bool from_event = false) { setSliderValue(mCurSlider, val, from_event); }
+
+ /*virtual*/ void setValue(const LLSD& value) override;
+ /*virtual*/ LLSD getValue() const override { return mValue; }
+
+ boost::signals2::connection setMouseDownCallback( const commit_signal_t::slot_type& cb );
+ boost::signals2::connection setMouseUpCallback( const commit_signal_t::slot_type& cb );
+
+ bool findUnusedValue(F32& initVal);
+ const std::string& addSlider();
+ const std::string& addSlider(F32 val);
+ bool addSlider(F32 val, const std::string& name);
+ void deleteSlider(const std::string& name);
+ void deleteCurSlider() { deleteSlider(mCurSlider); }
+ /*virtual*/ void clear() override;
+
+ /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ bool handleKeyHere(KEY key, MASK mask) override;
+ /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ void draw() override;
+
+ S32 getMaxNumSliders() { return mMaxNumSliders; }
+ S32 getCurNumSliders() { return mValue.size(); }
+ F32 getOverlapThreshold() { return mOverlapThreshold; }
+ bool canAddSliders() { return mValue.size() < mMaxNumSliders; }
+
+
+protected:
+ LLSD mValue;
+ std::string mCurSlider;
+ std::string mHoverSlider;
+ static S32 mNameCounter;
+
+ S32 mMaxNumSliders;
+ bool mAllowOverlap;
+ bool mLoopOverlap;
+ F32 mOverlapThreshold;
+ bool mDrawTrack;
+ bool mUseTriangle; /// hacked in toggle to use a triangle
+
+ S32 mMouseOffset;
+ LLRect mDragStartThumbRect;
+ S32 mThumbWidth;
+
+ std::map<std::string, LLRect>
+ mThumbRects;
+ LLUIColor mTrackColor;
+ LLUIColor mThumbOutlineColor;
+ LLUIColor mThumbHighlightColor;
+ LLUIColor mThumbCenterColor;
+ LLUIColor mThumbCenterSelectedColor;
+ LLUIColor mDisabledThumbColor;
+ LLUIColor mTriangleColor;
+ LLUIImagePtr mThumbImagep; //blimps on the slider, for now no 'disabled' support
+ LLUIImagePtr mRoundedSquareImgp; //blimps on the slider, for now no 'disabled' support
+
+ const EOrientation mOrientation;
+
+ commit_signal_t* mMouseDownSignal;
+ commit_signal_t* mMouseUpSignal;
+};
+
+#endif // LL_MULTI_SLIDER_H
diff --git a/indra/llui/llmultisliderctrl.cpp b/indra/llui/llmultisliderctrl.cpp
index 7f5b0ccac3..879fdde254 100644
--- a/indra/llui/llmultisliderctrl.cpp
+++ b/indra/llui/llmultisliderctrl.cpp
@@ -1,525 +1,525 @@
-/**
- * @file llmultisliderctrl.cpp
- * @brief LLMultiSliderCtrl base class
- *
- * $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$
- */
-
-#include "linden_common.h"
-
-#include "llmultisliderctrl.h"
-
-#include "llmath.h"
-#include "llfontgl.h"
-#include "llgl.h"
-#include "llkeyboard.h"
-#include "lllineeditor.h"
-#include "llmultislider.h"
-#include "llstring.h"
-#include "lltextbox.h"
-#include "llui.h"
-#include "lluiconstants.h"
-#include "llcontrol.h"
-#include "llfocusmgr.h"
-#include "llresmgr.h"
-#include "lluictrlfactory.h"
-
-static LLDefaultChildRegistry::Register<LLMultiSliderCtrl> r("multi_slider");
-
-const U32 MAX_STRING_LENGTH = 10;
-LLMultiSliderCtrl::Params::Params()
-: text_width("text_width"),
- label_width("label_width"),
- show_text("show_text", true),
- can_edit_text("can_edit_text", false),
- max_sliders("max_sliders", 1),
- allow_overlap("allow_overlap", false),
- loop_overlap("loop_overlap", false),
- orientation("orientation"),
- thumb_image("thumb_image"),
- thumb_width("thumb_width"),
- thumb_highlight_color("thumb_highlight_color"),
- overlap_threshold("overlap_threshold", 0),
- draw_track("draw_track", true),
- use_triangle("use_triangle", false),
- decimal_digits("decimal_digits", 3),
- text_color("text_color"),
- text_disabled_color("text_disabled_color"),
- mouse_down_callback("mouse_down_callback"),
- mouse_up_callback("mouse_up_callback"),
- sliders("slider")
-{
- mouse_opaque = true;
-}
-
-LLMultiSliderCtrl::LLMultiSliderCtrl(const LLMultiSliderCtrl::Params& p)
-: LLF32UICtrl(p),
- mLabelBox( NULL ),
- mEditor( NULL ),
- mTextBox( NULL ),
- mTextEnabledColor(p.text_color()),
- mTextDisabledColor(p.text_disabled_color())
-{
- static LLUICachedControl<S32> multi_sliderctrl_spacing ("UIMultiSliderctrlSpacing", 0);
-
- S32 top = getRect().getHeight();
- S32 bottom = 0;
- S32 left = 0;
-
- S32 label_width = p.label_width;
- S32 text_width = p.text_width;
-
- // Label
- if( !p.label().empty() )
- {
- if (p.label_width == 0)
- {
- label_width = p.font()->getWidth(p.label);
- }
- LLRect label_rect( left, top, label_width, bottom );
- LLTextBox::Params params;
- params.name("MultiSliderCtrl Label");
- params.rect(label_rect);
- params.initial_value(p.label());
- params.font(p.font);
- mLabelBox = LLUICtrlFactory::create<LLTextBox> (params);
- addChild(mLabelBox);
- }
-
- S32 slider_right = getRect().getWidth();
-
- if (p.show_text)
- {
- if (!p.text_width.isProvided())
- {
- text_width = 0;
- // calculate the size of the text box (log max_value is number of digits - 1 so plus 1)
- if ( p.max_value() )
- text_width = p.font()->getWidth(std::string("0")) * ( static_cast < S32 > ( log10 ( p.max_value ) ) + p.decimal_digits + 1 );
-
- if ( p.increment < 1.0f )
- text_width += p.font()->getWidth(std::string(".")); // (mostly) take account of decimal point in value
-
- if ( p.min_value < 0.0f || p.max_value < 0.0f )
- text_width += p.font()->getWidth(std::string("-")); // (mostly) take account of minus sign
-
- // padding to make things look nicer
- text_width += 8;
- }
- S32 text_left = getRect().getWidth() - text_width;
-
- slider_right = text_left - multi_sliderctrl_spacing;
-
- LLRect text_rect( text_left, top, getRect().getWidth(), bottom );
- if( p.can_edit_text )
- {
- LLLineEditor::Params params;
- params.name("MultiSliderCtrl Editor");
- params.rect(text_rect);
- params.font(p.font);
- params.max_length.bytes(MAX_STRING_LENGTH);
- params.commit_callback.function(LLMultiSliderCtrl::onEditorCommit);
- params.prevalidate_callback(&LLTextValidate::validateFloat);
- params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
- mEditor = LLUICtrlFactory::create<LLLineEditor> (params);
- mEditor->setFocusReceivedCallback( boost::bind(LLMultiSliderCtrl::onEditorGainFocus, _1, this) );
- // don't do this, as selecting the entire text is single clicking in some cases
- // and double clicking in others
- //mEditor->setSelectAllonFocusReceived(true);
- addChild(mEditor);
- }
- else
- {
- LLTextBox::Params params;
- params.name("MultiSliderCtrl Text");
- params.rect(text_rect);
- params.font(p.font);
- params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
- mTextBox = LLUICtrlFactory::create<LLTextBox> (params);
- addChild(mTextBox);
- }
- }
-
- S32 slider_left = label_width ? label_width + multi_sliderctrl_spacing : 0;
- LLRect slider_rect( slider_left, top, slider_right, bottom );
- LLMultiSlider::Params params;
- params.sliders = p.sliders;
- params.rect(slider_rect);
- params.commit_callback.function( LLMultiSliderCtrl::onSliderCommit );
- params.mouse_down_callback( p.mouse_down_callback );
- params.mouse_up_callback( p.mouse_up_callback );
- params.initial_value(p.initial_value());
- params.min_value(p.min_value);
- params.max_value(p.max_value);
- params.increment(p.increment);
- params.max_sliders(p.max_sliders);
- params.allow_overlap(p.allow_overlap);
- params.loop_overlap(p.loop_overlap);
- if (p.overlap_threshold.isProvided())
- {
- params.overlap_threshold = p.overlap_threshold;
- }
- params.orientation(p.orientation);
- params.thumb_image(p.thumb_image);
- params.thumb_highlight_color(p.thumb_highlight_color);
- if (p.thumb_width.isProvided())
- {
- // otherwise should be provided by template
- params.thumb_width(p.thumb_width);
- }
- params.draw_track(p.draw_track);
- params.use_triangle(p.use_triangle);
- params.control_name(p.control_name);
- mMultiSlider = LLUICtrlFactory::create<LLMultiSlider> (params);
- addChild( mMultiSlider );
- mCurValue = mMultiSlider->getCurSliderValue();
-
-
- updateText();
-}
-
-LLMultiSliderCtrl::~LLMultiSliderCtrl()
-{
- // Children all cleaned up by default view destructor.
-}
-
-// static
-void LLMultiSliderCtrl::onEditorGainFocus( LLFocusableElement* caller, void *userdata )
-{
- LLMultiSliderCtrl* self = (LLMultiSliderCtrl*) userdata;
- llassert( caller == self->mEditor );
-
- self->onFocusReceived();
-}
-
-
-void LLMultiSliderCtrl::setValue(const LLSD& value)
-{
- mMultiSlider->setValue(value);
- mCurValue = mMultiSlider->getCurSliderValue();
- updateText();
-}
-
-void LLMultiSliderCtrl::setSliderValue(const std::string& name, F32 v, bool from_event)
-{
- mMultiSlider->setSliderValue(name, v, from_event );
- mCurValue = mMultiSlider->getCurSliderValue();
- updateText();
-}
-
-void LLMultiSliderCtrl::setCurSlider(const std::string& name)
-{
- mMultiSlider->setCurSlider(name);
- mCurValue = mMultiSlider->getCurSliderValue();
-}
-
-void LLMultiSliderCtrl::resetCurSlider()
-{
- mMultiSlider->resetCurSlider();
-}
-
-bool LLMultiSliderCtrl::setLabelArg( const std::string& key, const LLStringExplicit& text )
-{
- bool res = false;
- if (mLabelBox)
- {
- res = mLabelBox->setTextArg(key, text);
- if (res && mLabelWidth == 0)
- {
- S32 label_width = mFont->getWidth(mLabelBox->getText());
- LLRect rect = mLabelBox->getRect();
- S32 prev_right = rect.mRight;
- rect.mRight = rect.mLeft + label_width;
- mLabelBox->setRect(rect);
-
- S32 delta = rect.mRight - prev_right;
- rect = mMultiSlider->getRect();
- S32 left = rect.mLeft + delta;
- static LLUICachedControl<S32> multi_slider_ctrl_spacing ("UIMultiSliderctrlSpacing", 0);
- left = llclamp(left, 0, rect.mRight - multi_slider_ctrl_spacing);
- rect.mLeft = left;
- mMultiSlider->setRect(rect);
- }
- }
- return res;
-}
-
-const std::string& LLMultiSliderCtrl::addSlider()
-{
- const std::string& name = mMultiSlider->addSlider();
-
- // if it returns null, pass it on
- if(name == LLStringUtil::null) {
- return LLStringUtil::null;
- }
-
- // otherwise, update stuff
- mCurValue = mMultiSlider->getCurSliderValue();
- updateText();
- return name;
-}
-
-const std::string& LLMultiSliderCtrl::addSlider(F32 val)
-{
- const std::string& name = mMultiSlider->addSlider(val);
-
- // if it returns null, pass it on
- if(name == LLStringUtil::null) {
- return LLStringUtil::null;
- }
-
- // otherwise, update stuff
- mCurValue = mMultiSlider->getCurSliderValue();
- updateText();
- return name;
-}
-
-bool LLMultiSliderCtrl::addSlider(F32 val, const std::string& name)
-{
- bool res = mMultiSlider->addSlider(val, name);
- if (res)
- {
- mCurValue = mMultiSlider->getCurSliderValue();
- updateText();
- }
- return res;
-}
-
-void LLMultiSliderCtrl::deleteSlider(const std::string& name)
-{
- mMultiSlider->deleteSlider(name);
- mCurValue = mMultiSlider->getCurSliderValue();
- updateText();
-}
-
-
-void LLMultiSliderCtrl::clear()
-{
- setCurSliderValue(0.0f);
- if( mEditor )
- {
- mEditor->setText(std::string(""));
- }
- if( mTextBox )
- {
- mTextBox->setText(std::string(""));
- }
-
- // get rid of sliders
- mMultiSlider->clear();
-
-}
-
-bool LLMultiSliderCtrl::isMouseHeldDown()
-{
- return gFocusMgr.getMouseCapture() == mMultiSlider;
-}
-
-void LLMultiSliderCtrl::updateText()
-{
- if( mEditor || mTextBox )
- {
- LLLocale locale(LLLocale::USER_LOCALE);
-
- // Don't display very small negative values as -0.000
- F32 displayed_value = (F32)(floor(getCurSliderValue() * pow(10.0, (F64)mPrecision) + 0.5) / pow(10.0, (F64)mPrecision));
-
- std::string format = llformat("%%.%df", mPrecision);
- std::string text = llformat(format.c_str(), displayed_value);
- if( mEditor )
- {
- mEditor->setText( text );
- }
- else
- {
- mTextBox->setText( text );
- }
- }
-}
-
-// static
-void LLMultiSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata)
-{
- llassert(ctrl);
- if (!ctrl)
- return;
-
- LLMultiSliderCtrl* self = dynamic_cast<LLMultiSliderCtrl*>(ctrl->getParent());
- llassert(self);
- if (!self) // cast failed - wrong type! :O
- return;
-
- bool success = false;
- F32 val = self->mCurValue;
- F32 saved_val = self->mCurValue;
-
- std::string text = self->mEditor->getText();
- if( LLLineEditor::postvalidateFloat( text ) )
- {
- LLLocale locale(LLLocale::USER_LOCALE);
- val = (F32) atof( text.c_str() );
- if( self->mMultiSlider->getMinValue() <= val && val <= self->mMultiSlider->getMaxValue() )
- {
- self->setCurSliderValue( val ); // set the value temporarily so that the callback can retrieve it.
- if( !self->mValidateSignal || (*(self->mValidateSignal))( self, val ) )
- {
- success = true;
- }
- }
- }
-
- if( success )
- {
- self->onCommit();
- }
- else
- {
- if( self->getCurSliderValue() != saved_val )
- {
- self->setCurSliderValue( saved_val );
- }
- self->reportInvalidData();
- }
- self->updateText();
-}
-
-// static
-void LLMultiSliderCtrl::onSliderCommit(LLUICtrl* ctrl, const LLSD& userdata)
-{
- LLMultiSliderCtrl* self = dynamic_cast<LLMultiSliderCtrl*>(ctrl->getParent());
- if (!self)
- return;
-
- bool success = false;
- F32 saved_val = self->mCurValue;
- F32 new_val = self->mMultiSlider->getCurSliderValue();
-
- self->mCurValue = new_val; // set the value temporarily so that the callback can retrieve it.
- if( !self->mValidateSignal || (*(self->mValidateSignal))( self, new_val ) )
- {
- success = true;
- }
-
- if( success )
- {
- self->onCommit();
- }
- else
- {
- if( self->mCurValue != saved_val )
- {
- self->setCurSliderValue( saved_val );
- }
- self->reportInvalidData();
- }
- self->updateText();
-}
-
-void LLMultiSliderCtrl::setEnabled(bool b)
-{
- LLF32UICtrl::setEnabled( b );
-
- if( mLabelBox )
- {
- mLabelBox->setColor( b ? mTextEnabledColor.get() : mTextDisabledColor.get() );
- }
-
- mMultiSlider->setEnabled( b );
-
- if( mEditor )
- {
- mEditor->setEnabled( b );
- }
-
- if( mTextBox )
- {
- mTextBox->setColor( b ? mTextEnabledColor.get() : mTextDisabledColor.get() );
- }
-}
-
-
-void LLMultiSliderCtrl::setTentative(bool b)
-{
- if( mEditor )
- {
- mEditor->setTentative(b);
- }
- LLF32UICtrl::setTentative(b);
-}
-
-
-void LLMultiSliderCtrl::onCommit()
-{
- setTentative(false);
-
- if( mEditor )
- {
- mEditor->setTentative(false);
- }
-
- setControlValue(getValueF32());
- LLF32UICtrl::onCommit();
-}
-
-
-void LLMultiSliderCtrl::setPrecision(S32 precision)
-{
- if (precision < 0 || precision > 10)
- {
- LL_ERRS() << "LLMultiSliderCtrl::setPrecision - precision out of range" << LL_ENDL;
- return;
- }
-
- mPrecision = precision;
- updateText();
-}
-
-boost::signals2::connection LLMultiSliderCtrl::setSliderMouseDownCallback( const commit_signal_t::slot_type& cb )
-{
- return mMultiSlider->setMouseDownCallback( cb );
-}
-
-boost::signals2::connection LLMultiSliderCtrl::setSliderMouseUpCallback( const commit_signal_t::slot_type& cb )
-{
- return mMultiSlider->setMouseUpCallback( cb );
-}
-
-void LLMultiSliderCtrl::onTabInto()
-{
- if( mEditor )
- {
- mEditor->onTabInto();
- }
- LLF32UICtrl::onTabInto();
-}
-
-void LLMultiSliderCtrl::reportInvalidData()
-{
- make_ui_sound("UISndBadKeystroke");
-}
-
-// virtual
-void LLMultiSliderCtrl::setControlName(const std::string& control_name, LLView* context)
-{
- mMultiSlider->setControlName(control_name, context);
-}
-
+/**
+ * @file llmultisliderctrl.cpp
+ * @brief LLMultiSliderCtrl base class
+ *
+ * $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$
+ */
+
+#include "linden_common.h"
+
+#include "llmultisliderctrl.h"
+
+#include "llmath.h"
+#include "llfontgl.h"
+#include "llgl.h"
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "llmultislider.h"
+#include "llstring.h"
+#include "lltextbox.h"
+#include "llui.h"
+#include "lluiconstants.h"
+#include "llcontrol.h"
+#include "llfocusmgr.h"
+#include "llresmgr.h"
+#include "lluictrlfactory.h"
+
+static LLDefaultChildRegistry::Register<LLMultiSliderCtrl> r("multi_slider");
+
+const U32 MAX_STRING_LENGTH = 10;
+LLMultiSliderCtrl::Params::Params()
+: text_width("text_width"),
+ label_width("label_width"),
+ show_text("show_text", true),
+ can_edit_text("can_edit_text", false),
+ max_sliders("max_sliders", 1),
+ allow_overlap("allow_overlap", false),
+ loop_overlap("loop_overlap", false),
+ orientation("orientation"),
+ thumb_image("thumb_image"),
+ thumb_width("thumb_width"),
+ thumb_highlight_color("thumb_highlight_color"),
+ overlap_threshold("overlap_threshold", 0),
+ draw_track("draw_track", true),
+ use_triangle("use_triangle", false),
+ decimal_digits("decimal_digits", 3),
+ text_color("text_color"),
+ text_disabled_color("text_disabled_color"),
+ mouse_down_callback("mouse_down_callback"),
+ mouse_up_callback("mouse_up_callback"),
+ sliders("slider")
+{
+ mouse_opaque = true;
+}
+
+LLMultiSliderCtrl::LLMultiSliderCtrl(const LLMultiSliderCtrl::Params& p)
+: LLF32UICtrl(p),
+ mLabelBox( NULL ),
+ mEditor( NULL ),
+ mTextBox( NULL ),
+ mTextEnabledColor(p.text_color()),
+ mTextDisabledColor(p.text_disabled_color())
+{
+ static LLUICachedControl<S32> multi_sliderctrl_spacing ("UIMultiSliderctrlSpacing", 0);
+
+ S32 top = getRect().getHeight();
+ S32 bottom = 0;
+ S32 left = 0;
+
+ S32 label_width = p.label_width;
+ S32 text_width = p.text_width;
+
+ // Label
+ if( !p.label().empty() )
+ {
+ if (p.label_width == 0)
+ {
+ label_width = p.font()->getWidth(p.label);
+ }
+ LLRect label_rect( left, top, label_width, bottom );
+ LLTextBox::Params params;
+ params.name("MultiSliderCtrl Label");
+ params.rect(label_rect);
+ params.initial_value(p.label());
+ params.font(p.font);
+ mLabelBox = LLUICtrlFactory::create<LLTextBox> (params);
+ addChild(mLabelBox);
+ }
+
+ S32 slider_right = getRect().getWidth();
+
+ if (p.show_text)
+ {
+ if (!p.text_width.isProvided())
+ {
+ text_width = 0;
+ // calculate the size of the text box (log max_value is number of digits - 1 so plus 1)
+ if ( p.max_value() )
+ text_width = p.font()->getWidth(std::string("0")) * ( static_cast < S32 > ( log10 ( p.max_value ) ) + p.decimal_digits + 1 );
+
+ if ( p.increment < 1.0f )
+ text_width += p.font()->getWidth(std::string(".")); // (mostly) take account of decimal point in value
+
+ if ( p.min_value < 0.0f || p.max_value < 0.0f )
+ text_width += p.font()->getWidth(std::string("-")); // (mostly) take account of minus sign
+
+ // padding to make things look nicer
+ text_width += 8;
+ }
+ S32 text_left = getRect().getWidth() - text_width;
+
+ slider_right = text_left - multi_sliderctrl_spacing;
+
+ LLRect text_rect( text_left, top, getRect().getWidth(), bottom );
+ if( p.can_edit_text )
+ {
+ LLLineEditor::Params params;
+ params.name("MultiSliderCtrl Editor");
+ params.rect(text_rect);
+ params.font(p.font);
+ params.max_length.bytes(MAX_STRING_LENGTH);
+ params.commit_callback.function(LLMultiSliderCtrl::onEditorCommit);
+ params.prevalidator(&LLTextValidate::validateFloat);
+ params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
+ mEditor = LLUICtrlFactory::create<LLLineEditor> (params);
+ mEditor->setFocusReceivedCallback( boost::bind(LLMultiSliderCtrl::onEditorGainFocus, _1, this) );
+ // don't do this, as selecting the entire text is single clicking in some cases
+ // and double clicking in others
+ //mEditor->setSelectAllonFocusReceived(true);
+ addChild(mEditor);
+ }
+ else
+ {
+ LLTextBox::Params params;
+ params.name("MultiSliderCtrl Text");
+ params.rect(text_rect);
+ params.font(p.font);
+ params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
+ mTextBox = LLUICtrlFactory::create<LLTextBox> (params);
+ addChild(mTextBox);
+ }
+ }
+
+ S32 slider_left = label_width ? label_width + multi_sliderctrl_spacing : 0;
+ LLRect slider_rect( slider_left, top, slider_right, bottom );
+ LLMultiSlider::Params params;
+ params.sliders = p.sliders;
+ params.rect(slider_rect);
+ params.commit_callback.function( LLMultiSliderCtrl::onSliderCommit );
+ params.mouse_down_callback( p.mouse_down_callback );
+ params.mouse_up_callback( p.mouse_up_callback );
+ params.initial_value(p.initial_value());
+ params.min_value(p.min_value);
+ params.max_value(p.max_value);
+ params.increment(p.increment);
+ params.max_sliders(p.max_sliders);
+ params.allow_overlap(p.allow_overlap);
+ params.loop_overlap(p.loop_overlap);
+ if (p.overlap_threshold.isProvided())
+ {
+ params.overlap_threshold = p.overlap_threshold;
+ }
+ params.orientation(p.orientation);
+ params.thumb_image(p.thumb_image);
+ params.thumb_highlight_color(p.thumb_highlight_color);
+ if (p.thumb_width.isProvided())
+ {
+ // otherwise should be provided by template
+ params.thumb_width(p.thumb_width);
+ }
+ params.draw_track(p.draw_track);
+ params.use_triangle(p.use_triangle);
+ params.control_name(p.control_name);
+ mMultiSlider = LLUICtrlFactory::create<LLMultiSlider> (params);
+ addChild( mMultiSlider );
+ mCurValue = mMultiSlider->getCurSliderValue();
+
+
+ updateText();
+}
+
+LLMultiSliderCtrl::~LLMultiSliderCtrl()
+{
+ // Children all cleaned up by default view destructor.
+}
+
+// static
+void LLMultiSliderCtrl::onEditorGainFocus( LLFocusableElement* caller, void *userdata )
+{
+ LLMultiSliderCtrl* self = (LLMultiSliderCtrl*) userdata;
+ llassert( caller == self->mEditor );
+
+ self->onFocusReceived();
+}
+
+
+void LLMultiSliderCtrl::setValue(const LLSD& value)
+{
+ mMultiSlider->setValue(value);
+ mCurValue = mMultiSlider->getCurSliderValue();
+ updateText();
+}
+
+void LLMultiSliderCtrl::setSliderValue(const std::string& name, F32 v, bool from_event)
+{
+ mMultiSlider->setSliderValue(name, v, from_event );
+ mCurValue = mMultiSlider->getCurSliderValue();
+ updateText();
+}
+
+void LLMultiSliderCtrl::setCurSlider(const std::string& name)
+{
+ mMultiSlider->setCurSlider(name);
+ mCurValue = mMultiSlider->getCurSliderValue();
+}
+
+void LLMultiSliderCtrl::resetCurSlider()
+{
+ mMultiSlider->resetCurSlider();
+}
+
+bool LLMultiSliderCtrl::setLabelArg( const std::string& key, const LLStringExplicit& text )
+{
+ bool res = false;
+ if (mLabelBox)
+ {
+ res = mLabelBox->setTextArg(key, text);
+ if (res && mLabelWidth == 0)
+ {
+ S32 label_width = mFont->getWidth(mLabelBox->getText());
+ LLRect rect = mLabelBox->getRect();
+ S32 prev_right = rect.mRight;
+ rect.mRight = rect.mLeft + label_width;
+ mLabelBox->setRect(rect);
+
+ S32 delta = rect.mRight - prev_right;
+ rect = mMultiSlider->getRect();
+ S32 left = rect.mLeft + delta;
+ static LLUICachedControl<S32> multi_slider_ctrl_spacing ("UIMultiSliderctrlSpacing", 0);
+ left = llclamp(left, 0, rect.mRight - multi_slider_ctrl_spacing);
+ rect.mLeft = left;
+ mMultiSlider->setRect(rect);
+ }
+ }
+ return res;
+}
+
+const std::string& LLMultiSliderCtrl::addSlider()
+{
+ const std::string& name = mMultiSlider->addSlider();
+
+ // if it returns null, pass it on
+ if(name == LLStringUtil::null) {
+ return LLStringUtil::null;
+ }
+
+ // otherwise, update stuff
+ mCurValue = mMultiSlider->getCurSliderValue();
+ updateText();
+ return name;
+}
+
+const std::string& LLMultiSliderCtrl::addSlider(F32 val)
+{
+ const std::string& name = mMultiSlider->addSlider(val);
+
+ // if it returns null, pass it on
+ if(name == LLStringUtil::null) {
+ return LLStringUtil::null;
+ }
+
+ // otherwise, update stuff
+ mCurValue = mMultiSlider->getCurSliderValue();
+ updateText();
+ return name;
+}
+
+bool LLMultiSliderCtrl::addSlider(F32 val, const std::string& name)
+{
+ bool res = mMultiSlider->addSlider(val, name);
+ if (res)
+ {
+ mCurValue = mMultiSlider->getCurSliderValue();
+ updateText();
+ }
+ return res;
+}
+
+void LLMultiSliderCtrl::deleteSlider(const std::string& name)
+{
+ mMultiSlider->deleteSlider(name);
+ mCurValue = mMultiSlider->getCurSliderValue();
+ updateText();
+}
+
+
+void LLMultiSliderCtrl::clear()
+{
+ setCurSliderValue(0.0f);
+ if( mEditor )
+ {
+ mEditor->setText(std::string(""));
+ }
+ if( mTextBox )
+ {
+ mTextBox->setText(std::string(""));
+ }
+
+ // get rid of sliders
+ mMultiSlider->clear();
+
+}
+
+bool LLMultiSliderCtrl::isMouseHeldDown()
+{
+ return gFocusMgr.getMouseCapture() == mMultiSlider;
+}
+
+void LLMultiSliderCtrl::updateText()
+{
+ if( mEditor || mTextBox )
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ // Don't display very small negative values as -0.000
+ F32 displayed_value = (F32)(floor(getCurSliderValue() * pow(10.0, (F64)mPrecision) + 0.5) / pow(10.0, (F64)mPrecision));
+
+ std::string format = llformat("%%.%df", mPrecision);
+ std::string text = llformat(format.c_str(), displayed_value);
+ if( mEditor )
+ {
+ mEditor->setText( text );
+ }
+ else
+ {
+ mTextBox->setText( text );
+ }
+ }
+}
+
+// static
+void LLMultiSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata)
+{
+ llassert(ctrl);
+ if (!ctrl)
+ return;
+
+ LLMultiSliderCtrl* self = dynamic_cast<LLMultiSliderCtrl*>(ctrl->getParent());
+ llassert(self);
+ if (!self) // cast failed - wrong type! :O
+ return;
+
+ bool success = false;
+ F32 val = self->mCurValue;
+ F32 saved_val = self->mCurValue;
+
+ std::string text = self->mEditor->getText();
+ if( LLLineEditor::postvalidateFloat( text ) )
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+ val = (F32) atof( text.c_str() );
+ if( self->mMultiSlider->getMinValue() <= val && val <= self->mMultiSlider->getMaxValue() )
+ {
+ self->setCurSliderValue( val ); // set the value temporarily so that the callback can retrieve it.
+ if( !self->mValidateSignal || (*(self->mValidateSignal))( self, val ) )
+ {
+ success = true;
+ }
+ }
+ }
+
+ if( success )
+ {
+ self->onCommit();
+ }
+ else
+ {
+ if( self->getCurSliderValue() != saved_val )
+ {
+ self->setCurSliderValue( saved_val );
+ }
+ self->reportInvalidData();
+ }
+ self->updateText();
+}
+
+// static
+void LLMultiSliderCtrl::onSliderCommit(LLUICtrl* ctrl, const LLSD& userdata)
+{
+ LLMultiSliderCtrl* self = dynamic_cast<LLMultiSliderCtrl*>(ctrl->getParent());
+ if (!self)
+ return;
+
+ bool success = false;
+ F32 saved_val = self->mCurValue;
+ F32 new_val = self->mMultiSlider->getCurSliderValue();
+
+ self->mCurValue = new_val; // set the value temporarily so that the callback can retrieve it.
+ if( !self->mValidateSignal || (*(self->mValidateSignal))( self, new_val ) )
+ {
+ success = true;
+ }
+
+ if( success )
+ {
+ self->onCommit();
+ }
+ else
+ {
+ if( self->mCurValue != saved_val )
+ {
+ self->setCurSliderValue( saved_val );
+ }
+ self->reportInvalidData();
+ }
+ self->updateText();
+}
+
+void LLMultiSliderCtrl::setEnabled(bool b)
+{
+ LLF32UICtrl::setEnabled( b );
+
+ if( mLabelBox )
+ {
+ mLabelBox->setColor( b ? mTextEnabledColor.get() : mTextDisabledColor.get() );
+ }
+
+ mMultiSlider->setEnabled( b );
+
+ if( mEditor )
+ {
+ mEditor->setEnabled( b );
+ }
+
+ if( mTextBox )
+ {
+ mTextBox->setColor( b ? mTextEnabledColor.get() : mTextDisabledColor.get() );
+ }
+}
+
+
+void LLMultiSliderCtrl::setTentative(bool b)
+{
+ if( mEditor )
+ {
+ mEditor->setTentative(b);
+ }
+ LLF32UICtrl::setTentative(b);
+}
+
+
+void LLMultiSliderCtrl::onCommit()
+{
+ setTentative(false);
+
+ if( mEditor )
+ {
+ mEditor->setTentative(false);
+ }
+
+ setControlValue(getValueF32());
+ LLF32UICtrl::onCommit();
+}
+
+
+void LLMultiSliderCtrl::setPrecision(S32 precision)
+{
+ if (precision < 0 || precision > 10)
+ {
+ LL_ERRS() << "LLMultiSliderCtrl::setPrecision - precision out of range" << LL_ENDL;
+ return;
+ }
+
+ mPrecision = precision;
+ updateText();
+}
+
+boost::signals2::connection LLMultiSliderCtrl::setSliderMouseDownCallback( const commit_signal_t::slot_type& cb )
+{
+ return mMultiSlider->setMouseDownCallback( cb );
+}
+
+boost::signals2::connection LLMultiSliderCtrl::setSliderMouseUpCallback( const commit_signal_t::slot_type& cb )
+{
+ return mMultiSlider->setMouseUpCallback( cb );
+}
+
+void LLMultiSliderCtrl::onTabInto()
+{
+ if( mEditor )
+ {
+ mEditor->onTabInto();
+ }
+ LLF32UICtrl::onTabInto();
+}
+
+void LLMultiSliderCtrl::reportInvalidData()
+{
+ make_ui_sound("UISndBadKeystroke");
+}
+
+// virtual
+void LLMultiSliderCtrl::setControlName(const std::string& control_name, LLView* context)
+{
+ mMultiSlider->setControlName(control_name, context);
+}
+
diff --git a/indra/llui/llmultisliderctrl.h b/indra/llui/llmultisliderctrl.h
index b58540666b..a369140660 100644
--- a/indra/llui/llmultisliderctrl.h
+++ b/indra/llui/llmultisliderctrl.h
@@ -1,174 +1,174 @@
-/**
- * @file llmultisliderctrl.h
- * @brief LLMultiSliderCtrl base class
- *
- * $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_MULTI_SLIDERCTRL_H
-#define LL_MULTI_SLIDERCTRL_H
-
-#include "llf32uictrl.h"
-#include "v4color.h"
-#include "llmultislider.h"
-#include "lltextbox.h"
-#include "llrect.h"
-
-
-//
-// Classes
-//
-class LLFontGL;
-class LLLineEditor;
-class LLSlider;
-
-
-class LLMultiSliderCtrl : public LLF32UICtrl
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLF32UICtrl::Params>
- {
- Optional<S32> label_width,
- text_width;
- Optional<bool> show_text,
- can_edit_text;
- Optional<S32> decimal_digits,
- thumb_width;
- Optional<S32> max_sliders;
- Optional<bool> allow_overlap,
- loop_overlap,
- draw_track,
- use_triangle;
-
- Optional<std::string> orientation,
- thumb_image;
-
- Optional<F32> overlap_threshold;
-
- Optional<LLUIColor> text_color,
- text_disabled_color,
- thumb_highlight_color;
-
- Optional<CommitCallbackParam> mouse_down_callback,
- mouse_up_callback;
-
- Multiple<LLMultiSlider::SliderParams> sliders;
-
- Params();
- };
-
-protected:
- LLMultiSliderCtrl(const Params&);
- friend class LLUICtrlFactory;
-public:
- virtual ~LLMultiSliderCtrl();
-
- F32 getSliderValue(const std::string& name) const { return mMultiSlider->getSliderValue(name); }
- void setSliderValue(const std::string& name, F32 v, bool from_event = false);
-
- virtual void setValue(const LLSD& value );
- virtual LLSD getValue() const { return mMultiSlider->getValue(); }
- virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text );
-
- const std::string& getCurSlider() const { return mMultiSlider->getCurSlider(); }
- F32 getCurSliderValue() const { return mCurValue; }
- void setCurSlider(const std::string& name);
- void resetCurSlider();
- void setCurSliderValue(F32 val, bool from_event = false) { setSliderValue(mMultiSlider->getCurSlider(), val, from_event); }
-
- virtual void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); }
- virtual void setMaxValue(const LLSD& max_value) { setMaxValue((F32)max_value.asReal()); }
-
- bool isMouseHeldDown();
-
- virtual void setEnabled( bool b );
- virtual void clear();
- virtual void setPrecision(S32 precision);
- void setMinValue(F32 min_value) {mMultiSlider->setMinValue(min_value);}
- void setMaxValue(F32 max_value) {mMultiSlider->setMaxValue(max_value);}
- void setIncrement(F32 increment) {mMultiSlider->setIncrement(increment);}
-
- F32 getNearestIncrement(F32 value) const { return mMultiSlider->getNearestIncrement(value); }
- F32 getSliderValueFromPos(S32 x, S32 y) const { return mMultiSlider->getSliderValueFromPos(x, y); }
- LLRect getSliderThumbRect(const std::string &name) const { return mMultiSlider->getSliderThumbRect(name); }
-
- void setSliderThumbImage(const std::string &name) { mMultiSlider->setSliderThumbImage(name); }
- void clearSliderThumbImage() { mMultiSlider->clearSliderThumbImage(); }
-
- /// for adding and deleting sliders
- const std::string& addSlider();
- const std::string& addSlider(F32 val);
- bool addSlider(F32 val, const std::string& name);
- void deleteSlider(const std::string& name);
- void deleteCurSlider() { deleteSlider(mMultiSlider->getCurSlider()); }
-
- F32 getMinValue() const { return mMultiSlider->getMinValue(); }
- F32 getMaxValue() const { return mMultiSlider->getMaxValue(); }
-
- S32 getMaxNumSliders() { return mMultiSlider->getMaxNumSliders(); }
- S32 getCurNumSliders() { return mMultiSlider->getCurNumSliders(); }
- F32 getOverlapThreshold() { return mMultiSlider->getOverlapThreshold(); }
- bool canAddSliders() { return mMultiSlider->canAddSliders(); }
-
- void setLabel(const std::string& label) { if (mLabelBox) mLabelBox->setText(label); }
- void setLabelColor(const LLColor4& c) { mTextEnabledColor = c; }
- void setDisabledLabelColor(const LLColor4& c) { mTextDisabledColor = c; }
-
- boost::signals2::connection setSliderMouseDownCallback( const commit_signal_t::slot_type& cb );
- boost::signals2::connection setSliderMouseUpCallback( const commit_signal_t::slot_type& cb );
-
- virtual void onTabInto();
-
- virtual void setTentative(bool b); // marks value as tentative
- virtual void onCommit(); // mark not tentative, then commit
-
- virtual void setControlName(const std::string& control_name, LLView* context);
-
- static void onSliderCommit(LLUICtrl* caller, const LLSD& userdata);
-
- static void onEditorCommit(LLUICtrl* ctrl, const LLSD& userdata);
- static void onEditorGainFocus(LLFocusableElement* caller, void *userdata);
- static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata);
-
-private:
- void updateText();
- void reportInvalidData();
-
-private:
- const LLFontGL* mFont;
- bool mShowText;
- bool mCanEditText;
-
- S32 mPrecision;
- LLTextBox* mLabelBox;
- S32 mLabelWidth;
-
- F32 mCurValue;
- LLMultiSlider* mMultiSlider;
- LLLineEditor* mEditor;
- LLTextBox* mTextBox;
-
- LLUIColor mTextEnabledColor;
- LLUIColor mTextDisabledColor;
-};
-
-#endif // LL_MULTI_SLIDERCTRL_H
+/**
+ * @file llmultisliderctrl.h
+ * @brief LLMultiSliderCtrl base class
+ *
+ * $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_MULTI_SLIDERCTRL_H
+#define LL_MULTI_SLIDERCTRL_H
+
+#include "llf32uictrl.h"
+#include "v4color.h"
+#include "llmultislider.h"
+#include "lltextbox.h"
+#include "llrect.h"
+
+
+//
+// Classes
+//
+class LLFontGL;
+class LLLineEditor;
+class LLSlider;
+
+
+class LLMultiSliderCtrl : public LLF32UICtrl
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLF32UICtrl::Params>
+ {
+ Optional<S32> label_width,
+ text_width;
+ Optional<bool> show_text,
+ can_edit_text;
+ Optional<S32> decimal_digits,
+ thumb_width;
+ Optional<S32> max_sliders;
+ Optional<bool> allow_overlap,
+ loop_overlap,
+ draw_track,
+ use_triangle;
+
+ Optional<std::string> orientation,
+ thumb_image;
+
+ Optional<F32> overlap_threshold;
+
+ Optional<LLUIColor> text_color,
+ text_disabled_color,
+ thumb_highlight_color;
+
+ Optional<CommitCallbackParam> mouse_down_callback,
+ mouse_up_callback;
+
+ Multiple<LLMultiSlider::SliderParams> sliders;
+
+ Params();
+ };
+
+protected:
+ LLMultiSliderCtrl(const Params&);
+ friend class LLUICtrlFactory;
+public:
+ virtual ~LLMultiSliderCtrl();
+
+ F32 getSliderValue(const std::string& name) const { return mMultiSlider->getSliderValue(name); }
+ void setSliderValue(const std::string& name, F32 v, bool from_event = false);
+
+ virtual void setValue(const LLSD& value );
+ virtual LLSD getValue() const { return mMultiSlider->getValue(); }
+ virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text );
+
+ const std::string& getCurSlider() const { return mMultiSlider->getCurSlider(); }
+ F32 getCurSliderValue() const { return mCurValue; }
+ void setCurSlider(const std::string& name);
+ void resetCurSlider();
+ void setCurSliderValue(F32 val, bool from_event = false) { setSliderValue(mMultiSlider->getCurSlider(), val, from_event); }
+
+ virtual void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); }
+ virtual void setMaxValue(const LLSD& max_value) { setMaxValue((F32)max_value.asReal()); }
+
+ bool isMouseHeldDown();
+
+ virtual void setEnabled( bool b );
+ virtual void clear();
+ virtual void setPrecision(S32 precision);
+ void setMinValue(F32 min_value) {mMultiSlider->setMinValue(min_value);}
+ void setMaxValue(F32 max_value) {mMultiSlider->setMaxValue(max_value);}
+ void setIncrement(F32 increment) {mMultiSlider->setIncrement(increment);}
+
+ F32 getNearestIncrement(F32 value) const { return mMultiSlider->getNearestIncrement(value); }
+ F32 getSliderValueFromPos(S32 x, S32 y) const { return mMultiSlider->getSliderValueFromPos(x, y); }
+ LLRect getSliderThumbRect(const std::string &name) const { return mMultiSlider->getSliderThumbRect(name); }
+
+ void setSliderThumbImage(const std::string &name) { mMultiSlider->setSliderThumbImage(name); }
+ void clearSliderThumbImage() { mMultiSlider->clearSliderThumbImage(); }
+
+ /// for adding and deleting sliders
+ const std::string& addSlider();
+ const std::string& addSlider(F32 val);
+ bool addSlider(F32 val, const std::string& name);
+ void deleteSlider(const std::string& name);
+ void deleteCurSlider() { deleteSlider(mMultiSlider->getCurSlider()); }
+
+ F32 getMinValue() const { return mMultiSlider->getMinValue(); }
+ F32 getMaxValue() const { return mMultiSlider->getMaxValue(); }
+
+ S32 getMaxNumSliders() { return mMultiSlider->getMaxNumSliders(); }
+ S32 getCurNumSliders() { return mMultiSlider->getCurNumSliders(); }
+ F32 getOverlapThreshold() { return mMultiSlider->getOverlapThreshold(); }
+ bool canAddSliders() { return mMultiSlider->canAddSliders(); }
+
+ void setLabel(const std::string& label) { if (mLabelBox) mLabelBox->setText(label); }
+ void setLabelColor(const LLColor4& c) { mTextEnabledColor = c; }
+ void setDisabledLabelColor(const LLColor4& c) { mTextDisabledColor = c; }
+
+ boost::signals2::connection setSliderMouseDownCallback( const commit_signal_t::slot_type& cb );
+ boost::signals2::connection setSliderMouseUpCallback( const commit_signal_t::slot_type& cb );
+
+ virtual void onTabInto();
+
+ virtual void setTentative(bool b); // marks value as tentative
+ virtual void onCommit(); // mark not tentative, then commit
+
+ virtual void setControlName(const std::string& control_name, LLView* context);
+
+ static void onSliderCommit(LLUICtrl* caller, const LLSD& userdata);
+
+ static void onEditorCommit(LLUICtrl* ctrl, const LLSD& userdata);
+ static void onEditorGainFocus(LLFocusableElement* caller, void *userdata);
+ static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata);
+
+private:
+ void updateText();
+ void reportInvalidData();
+
+private:
+ const LLFontGL* mFont;
+ bool mShowText;
+ bool mCanEditText;
+
+ S32 mPrecision;
+ LLTextBox* mLabelBox;
+ S32 mLabelWidth;
+
+ F32 mCurValue;
+ LLMultiSlider* mMultiSlider;
+ LLLineEditor* mEditor;
+ LLTextBox* mTextBox;
+
+ LLUIColor mTextEnabledColor;
+ LLUIColor mTextDisabledColor;
+};
+
+#endif // LL_MULTI_SLIDERCTRL_H
diff --git a/indra/llui/llnotificationptr.h b/indra/llui/llnotificationptr.h
index 580f353c7d..60ff7711a0 100644
--- a/indra/llui/llnotificationptr.h
+++ b/indra/llui/llnotificationptr.h
@@ -4,21 +4,21 @@
* $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$
*/
diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp
index 9ba8e9e645..9549860972 100644
--- a/indra/llui/llnotifications.cpp
+++ b/indra/llui/llnotifications.cpp
@@ -1,2003 +1,2009 @@
-/**
-* @file llnotifications.cpp
-* @brief Non-UI queue manager for keeping a prioritized list of notifications
-*
-* $LicenseInfo:firstyear=2008&license=viewerlgpl$
-* Second Life Viewer Source Code
-* Copyright (C) 2010, Linden Research, Inc.
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU Lesser General Public
-* License as published by the Free Software Foundation;
-* version 2.1 of the License only.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this library; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-*
-* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
-* $/LicenseInfo$
-*/
-
-#include "linden_common.h"
-
-#include "llnotifications.h"
-#include "llnotificationtemplate.h"
-#include "llnotificationvisibilityrule.h"
-
-#include "llavatarnamecache.h"
-#include "llinstantmessage.h"
-#include "llcachename.h"
-#include "llxmlnode.h"
-#include "lluictrl.h"
-#include "lluictrlfactory.h"
-#include "lldir.h"
-#include "llsdserialize.h"
-#include "lltrans.h"
-#include "llstring.h"
-#include "llsdparam.h"
-#include "llsdutil.h"
-
-#include <algorithm>
-#include <boost/regex.hpp>
-
-
-const std::string NOTIFICATION_PERSIST_VERSION = "0.93";
-
-void NotificationPriorityValues::declareValues()
-{
- declare("low", NOTIFICATION_PRIORITY_LOW);
- declare("normal", NOTIFICATION_PRIORITY_NORMAL);
- declare("high", NOTIFICATION_PRIORITY_HIGH);
- declare("critical", NOTIFICATION_PRIORITY_CRITICAL);
-}
-
-LLNotificationForm::FormElementBase::FormElementBase()
-: name("name"),
- enabled("enabled", true)
-{}
-
-LLNotificationForm::FormIgnore::FormIgnore()
-: text("text"),
- control("control"),
- invert_control("invert_control", false),
- save_option("save_option", false),
- session_only("session_only", false),
- checkbox_only("checkbox_only", false)
-{}
-
-LLNotificationForm::FormButton::FormButton()
-: index("index"),
- text("text"),
- ignore("ignore"),
- is_default("default"),
- width("width", 0),
- type("type")
-{
- // set type here so it gets serialized
- type = "button";
-}
-
-LLNotificationForm::FormInput::FormInput()
-: type("type"),
- text("text"),
- max_length_chars("max_length_chars"),
- width("width", 0),
- value("value")
-{}
-
-LLNotificationForm::FormElement::FormElement()
-: button("button"),
- input("input")
-{}
-
-LLNotificationForm::FormElements::FormElements()
-: elements("")
-{}
-
-LLNotificationForm::Params::Params()
-: name("name"),
- ignore("ignore"),
- form_elements("")
-{}
-
-
-
-bool filterIgnoredNotifications(LLNotificationPtr notification)
-{
- LLNotificationFormPtr form = notification->getForm();
- // Check to see if the user wants to ignore this alert
- return !notification->getForm()->getIgnored();
-}
-
-bool handleIgnoredNotification(const LLSD& payload)
-{
- if (payload["sigtype"].asString() == "add")
- {
- LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
- if (!pNotif) return false;
-
- LLNotificationFormPtr form = pNotif->getForm();
- LLSD response;
- switch(form->getIgnoreType())
- {
- case LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE:
- case LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE_SESSION_ONLY:
- response = pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON);
- break;
- case LLNotificationForm::IGNORE_WITH_LAST_RESPONSE:
- response = LLUI::getInstance()->mSettingGroups["ignores"]->getLLSD("Default" + pNotif->getName());
- break;
- case LLNotificationForm::IGNORE_SHOW_AGAIN:
- break;
- default:
- return false;
- }
- pNotif->setIgnored(true);
- pNotif->respond(response);
- return true; // don't process this item any further
- }
- return false;
-}
-
-bool defaultResponse(const LLSD& payload)
-{
- if (payload["sigtype"].asString() == "add")
- {
- LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
- if (pNotif)
- {
- // supply default response
- pNotif->respond(pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON));
- }
- }
- return false;
-}
-
-bool visibilityRuleMached(const LLSD& payload)
-{
- // This is needed because LLNotifications::isVisibleByRules may have cancelled the notification.
- // Returning true here makes LLNotificationChannelBase::updateItem do an early out, which prevents things from happening in the wrong order.
- return true;
-}
-
-
-namespace LLNotificationFilters
-{
- // a sample filter
- bool includeEverything(LLNotificationPtr p)
- {
- return true;
- }
-};
-
-LLNotificationForm::LLNotificationForm()
-: mIgnore(IGNORE_NO)
-{
-}
-
-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),
- mInvertSetting(false) // ignore settings by default mean true=show, false=ignore
-{
- if (p.ignore.isProvided())
- {
- // For all cases but IGNORE_CHECKBOX_ONLY this is name for use in preferences
- mIgnoreMsg = p.ignore.text;
-
- LLUI *ui_inst = LLUI::getInstance();
- if (p.ignore.checkbox_only)
- {
- mIgnore = IGNORE_CHECKBOX_ONLY;
- }
- else if (!p.ignore.save_option)
- {
- mIgnore = p.ignore.session_only ? IGNORE_WITH_DEFAULT_RESPONSE_SESSION_ONLY : IGNORE_WITH_DEFAULT_RESPONSE;
- }
- else
- {
- // remember last option chosen by user and automatically respond with that in the future
- mIgnore = IGNORE_WITH_LAST_RESPONSE;
- ui_inst->mSettingGroups["ignores"]->declareLLSD(std::string("Default") + name, "", std::string("Default response for notification " + name));
- }
-
- bool show_notification = true;
- if (p.ignore.control.isProvided())
- {
- mIgnoreSetting = ui_inst->mSettingGroups["config"]->getControl(p.ignore.control());
- mInvertSetting = p.ignore.invert_control;
- }
- else if (mIgnore > IGNORE_NO)
- {
- ui_inst->mSettingGroups["ignores"]->declareBOOL(name, show_notification, "Show notification with this name", LLControlVariable::PERSIST_NONDFT);
- mIgnoreSetting = ui_inst->mSettingGroups["ignores"]->getControl(name);
- }
- }
-
- LLParamSDParser parser;
- parser.writeSD(mFormData, p.form_elements);
-
- for (LLSD::array_iterator it = mFormData.beginArray(), end_it = mFormData.endArray();
- it != end_it;
- ++it)
- {
- // lift contents of form element up a level, since element type is already encoded in "type" param
- if (it->isMap() && it->beginMap() != it->endMap())
- {
- *it = it->beginMap()->second;
- }
- }
-
- LL_DEBUGS("Notifications") << name << LL_ENDL;
- LL_DEBUGS("Notifications") << ll_pretty_print_sd(mFormData) << LL_ENDL;
-}
-
-LLNotificationForm::LLNotificationForm(const LLSD& sd)
- : mIgnore(IGNORE_NO)
-{
- if (sd.isArray())
- {
- mFormData = sd;
- }
- else
- {
- LL_WARNS("Notifications") << "Invalid form data " << sd << LL_ENDL;
- mFormData = LLSD::emptyArray();
- }
-}
-
-LLSD LLNotificationForm::asLLSD() const
-{
- return mFormData;
-}
-
-LLSD LLNotificationForm::getElement(const std::string& element_name)
-{
- for (LLSD::array_const_iterator it = mFormData.beginArray();
- it != mFormData.endArray();
- ++it)
- {
- if ((*it)["name"].asString() == element_name) return (*it);
- }
- return LLSD();
-}
-
-
-bool LLNotificationForm::hasElement(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 true;
- }
- return false;
-}
-
-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;
- element["name"] = name;
- element["text"] = name;
- element["value"] = value;
- element["index"] = LLSD::Integer(mFormData.size());
- element["enabled"] = enabled;
- mFormData.append(element);
-}
-
-void LLNotificationForm::append(const LLSD& sub_form)
-{
- if (sub_form.isArray())
- {
- for (LLSD::array_const_iterator it = sub_form.beginArray();
- it != sub_form.endArray();
- ++it)
- {
- mFormData.append(*it);
- }
- }
-}
-
-void LLNotificationForm::formatElements(const LLSD& substitutions)
-{
- for (LLSD::array_iterator it = mFormData.beginArray();
- it != mFormData.endArray();
- ++it)
- {
- // format "text" component of each form element
- if ((*it).has("text"))
- {
- std::string text = (*it)["text"].asString();
- LLStringUtil::format(text, substitutions);
- (*it)["text"] = text;
- }
- if ((*it)["type"].asString() == "text" && (*it).has("value"))
- {
- std::string value = (*it)["value"].asString();
- LLStringUtil::format(value, substitutions);
- (*it)["value"] = value;
- }
- }
-}
-
-std::string LLNotificationForm::getDefaultOption()
-{
- for (LLSD::array_const_iterator it = mFormData.beginArray();
- it != mFormData.endArray();
- ++it)
- {
- if ((*it)["default"]) return (*it)["name"].asString();
- }
- return "";
-}
-
-LLControlVariablePtr LLNotificationForm::getIgnoreSetting()
-{
- return mIgnoreSetting;
-}
-
-bool LLNotificationForm::getIgnored()
-{
- bool show = true;
- if (mIgnore > LLNotificationForm::IGNORE_NO
- && mIgnoreSetting)
- {
- show = mIgnoreSetting->getValue().asBoolean();
- if (mInvertSetting) show = !show;
- }
- return !show;
-}
-
-void LLNotificationForm::setIgnored(bool ignored)
-{
- if (mIgnoreSetting)
- {
- if (mInvertSetting) ignored = !ignored;
- mIgnoreSetting->setValue(!ignored);
- }
-}
-
-LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Params& p)
-: mName(p.name),
- mType(p.type),
- mMessage(p.value),
- mFooter(p.footer.value),
- mLabel(p.label),
- mIcon(p.icon),
- mURL(p.url.value),
- mExpireSeconds(p.duration),
- mExpireOption(p.expire_option),
- mURLOption(p.url.option),
- mURLTarget(p.url.target),
- mForceUrlsExternal(p.force_urls_external),
- mUnique(p.unique.isProvided()),
- mCombineBehavior(p.unique.combine),
- mPriority(p.priority),
- mPersist(p.persist),
- mDefaultFunctor(p.functor.isProvided() ? p.functor() : p.name()),
- mLogToChat(p.log_to_chat),
- mLogToIM(p.log_to_im),
- mShowToast(p.show_toast),
- mFadeToast(p.fade_toast),
- mSoundName("")
-{
- if (p.sound.isProvided()
- && LLUI::getInstance()->mSettingGroups["config"]->controlExists(p.sound))
- {
- mSoundName = p.sound;
- }
-
- for (const LLNotificationTemplate::UniquenessContext& context : p.unique.contexts)
- {
- mUniqueContext.push_back(context.value);
- }
-
- LL_DEBUGS("Notifications") << "notification \"" << mName << "\": tag count is " << p.tags.size() << LL_ENDL;
-
- for (const LLNotificationTemplate::Tag& tag : p.tags)
- {
- LL_DEBUGS("Notifications") << " tag \"" << std::string(tag.value) << "\"" << LL_ENDL;
- mTags.push_back(tag.value);
- }
-
- mForm = LLNotificationFormPtr(new LLNotificationForm(p.name, p.form_ref.form));
-}
-
-LLNotificationVisibilityRule::LLNotificationVisibilityRule(const LLNotificationVisibilityRule::Rule &p)
-{
- if (p.show.isChosen())
- {
- mType = p.show.type;
- mTag = p.show.tag;
- mName = p.show.name;
- mVisible = true;
- }
- else if (p.hide.isChosen())
- {
- mType = p.hide.type;
- mTag = p.hide.tag;
- mName = p.hide.name;
- mVisible = false;
- }
- else if (p.respond.isChosen())
- {
- mType = p.respond.type;
- mTag = p.respond.tag;
- mName = p.respond.name;
- mVisible = false;
- mResponse = p.respond.response;
- }
-}
-
-LLNotification::LLNotification(const LLSDParamAdapter<Params>& p) :
- mTimestamp(p.time_stamp),
- mSubstitutions(p.substitutions),
- mPayload(p.payload),
- mExpiresAt(p.expiry),
- mTemporaryResponder(false),
- mRespondedTo(false),
- mPriority(p.priority),
- mCancelled(false),
- mIgnored(false),
- mResponderObj(NULL),
- mId(p.id.isProvided() ? p.id : LLUUID::generateNewID()),
- mOfferFromAgent(p.offer_from_agent),
- mIsDND(p.is_dnd)
-{
- if (p.functor.name.isChosen())
- {
- mResponseFunctorName = p.functor.name;
- }
- else if (p.functor.function.isChosen())
- {
- mResponseFunctorName = LLUUID::generateNewID().asString();
- LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, p.functor.function());
-
- mTemporaryResponder = true;
- }
- else if(p.functor.responder.isChosen())
- {
- mResponder = p.functor.responder;
- }
-
- if(p.responder.isProvided())
- {
- mResponderObj = p.responder;
- }
-
- init(p.name, p.form_elements);
-}
-
-
-LLSD LLNotification::asLLSD(bool excludeTemplateElements)
-{
- LLParamSDParser parser;
-
- 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;
-}
-
-void LLNotification::update()
-{
- LLNotifications::instance().update(shared_from_this());
-}
-
-void LLNotification::updateFrom(LLNotificationPtr other)
-{
- // can only update from the same notification type
- if (mTemplatep != other->mTemplatep) return;
-
- // NOTE: do NOT change the ID, since it is the key to
- // this given instance, just update all the metadata
- //mId = other->mId;
-
- mPayload = other->mPayload;
- mSubstitutions = other->mSubstitutions;
- mTimestamp = other->mTimestamp;
- mExpiresAt = other->mExpiresAt;
- mCancelled = other->mCancelled;
- mIgnored = other->mIgnored;
- mPriority = other->mPriority;
- mForm = other->mForm;
- mResponseFunctorName = other->mResponseFunctorName;
- mRespondedTo = other->mRespondedTo;
- mResponse = other->mResponse;
- mTemporaryResponder = other->mTemporaryResponder;
-
- update();
-}
-
-const LLNotificationFormPtr LLNotification::getForm()
-{
- return mForm;
-}
-
-void LLNotification::cancel()
-{
- mCancelled = true;
-}
-
-LLSD LLNotification::getResponseTemplate(EResponseTemplateType type)
-{
- LLSD response = LLSD::emptyMap();
- for (S32 element_idx = 0;
- element_idx < mForm->getNumElements();
- ++element_idx)
- {
- LLSD element = mForm->getElement(element_idx);
- if (element.has("name"))
- {
- response[element["name"].asString()] = element["value"];
- }
-
- if ((type == WITH_DEFAULT_BUTTON)
- && element["default"].asBoolean())
- {
- response[element["name"].asString()] = true;
- }
- }
- return response;
-}
-
-//static
-S32 LLNotification::getSelectedOption(const LLSD& notification, const LLSD& response)
-{
- LLNotificationForm form(notification["form"]);
-
- for (S32 element_idx = 0;
- element_idx < form.getNumElements();
- ++element_idx)
- {
- LLSD element = form.getElement(element_idx);
-
- // only look at buttons
- if (element["type"].asString() == "button"
- && response[element["name"].asString()].asBoolean())
- {
- return element["index"].asInteger();
- }
- }
-
- return -1;
-}
-
-//static
-std::string LLNotification::getSelectedOptionName(const LLSD& response)
-{
- for (LLSD::map_const_iterator response_it = response.beginMap();
- response_it != response.endMap();
- ++response_it)
- {
- if (response_it->second.isBoolean() && response_it->second.asBoolean())
- {
- return response_it->first;
- }
- }
- return "";
-}
-
-
-void LLNotification::respond(const LLSD& response)
-{
- // *TODO may remove mRespondedTo and use mResponce.isDefined() in isRespondedTo()
- mRespondedTo = true;
- mResponse = response;
-
- if(mResponder)
- {
- mResponder->handleRespond(asLLSD(), response);
- }
- else if (!mResponseFunctorName.empty())
- {
- // look up the functor
- LLNotificationFunctorRegistry::ResponseFunctor functor =
- LLNotificationFunctorRegistry::instance().getFunctor(mResponseFunctorName);
- // and then call it
- functor(asLLSD(), response);
- }
- else if (mCombinedNotifications.empty())
- {
- // no registered responder
- return;
- }
-
- if (mTemporaryResponder)
- {
- LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
- mResponseFunctorName = "";
- mTemporaryResponder = false;
- }
-
- if (mForm->getIgnoreType() > LLNotificationForm::IGNORE_NO)
- {
- mForm->setIgnored(mIgnored);
- if (mIgnored && mForm->getIgnoreType() == LLNotificationForm::IGNORE_WITH_LAST_RESPONSE)
- {
- LLUI::getInstance()->mSettingGroups["ignores"]->setLLSD("Default" + getName(), response);
- }
- }
-
- for (std::vector<LLNotificationPtr>::const_iterator it = mCombinedNotifications.begin(); it != mCombinedNotifications.end(); ++it)
- {
- if ((*it))
- {
- (*it)->respond(response);
- }
- }
-
- update();
-}
-
-void LLNotification::respondWithDefault()
-{
- respond(getResponseTemplate(WITH_DEFAULT_BUTTON));
-}
-
-
-const std::string& LLNotification::getName() const
-{
- return mTemplatep->mName;
-}
-
-const std::string& LLNotification::getIcon() const
-{
- return mTemplatep->mIcon;
-}
-
-
-bool LLNotification::isPersistent() const
-{
- return mTemplatep->mPersist;
-}
-
-std::string LLNotification::getType() const
-{
- return (mTemplatep ? mTemplatep->mType : "");
-}
-
-S32 LLNotification::getURLOption() const
-{
- return (mTemplatep ? mTemplatep->mURLOption : -1);
-}
-
-S32 LLNotification::getURLOpenExternally() const
-{
- return(mTemplatep? mTemplatep->mURLTarget == "_external": -1);
-}
-
-bool LLNotification::getForceUrlsExternal() const
-{
- return (mTemplatep ? mTemplatep->mForceUrlsExternal : false);
-}
-
-bool LLNotification::hasUniquenessConstraints() const
-{
- return (mTemplatep ? mTemplatep->mUnique : false);
-}
-
-bool LLNotification::matchesTag(const std::string& tag)
-{
- bool result = false;
-
- if(mTemplatep)
- {
- std::list<std::string>::iterator it;
- for(it = mTemplatep->mTags.begin(); it != mTemplatep->mTags.end(); it++)
- {
- if((*it) == tag)
- {
- result = true;
- break;
- }
- }
- }
-
- return result;
-}
-
-void LLNotification::setIgnored(bool ignore)
-{
- mIgnored = ignore;
-}
-
-void LLNotification::setResponseFunctor(std::string const &responseFunctorName)
-{
- if (mTemporaryResponder)
- // get rid of the old one
- LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
- mResponseFunctorName = responseFunctorName;
- mTemporaryResponder = false;
-}
-
-void LLNotification::setResponseFunctor(const LLNotificationFunctorRegistry::ResponseFunctor& cb)
-{
- if(mTemporaryResponder)
- {
- LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
- }
-
- LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, cb);
-}
-
-void LLNotification::setResponseFunctor(const LLNotificationResponderPtr& responder)
-{
- mResponder = responder;
-}
-
-bool LLNotification::isEquivalentTo(LLNotificationPtr that) const
-{
- if (this->mTemplatep->mName != that->mTemplatep->mName)
- {
- return false; // must have the same template name or forget it
- }
- if (this->mTemplatep->mUnique)
- {
- const LLSD& these_substitutions = this->getSubstitutions();
- const LLSD& those_substitutions = that->getSubstitutions();
- const LLSD& this_payload = this->getPayload();
- const LLSD& that_payload = that->getPayload();
-
- // highlander bit sez there can only be one of these
- for (std::vector<std::string>::const_iterator it = mTemplatep->mUniqueContext.begin(), end_it = mTemplatep->mUniqueContext.end();
- it != end_it;
- ++it)
- {
- // if templates differ in either substitution strings or payload with the given field name
- // then they are considered inequivalent
- // use of get() avoids converting the LLSD value to a map as the [] operator would
- if (these_substitutions.get(*it).asString() != those_substitutions.get(*it).asString()
- || this_payload.get(*it).asString() != that_payload.get(*it).asString())
- {
- return false;
- }
- }
- return true;
- }
-
- return false;
-}
-
-void LLNotification::init(const std::string& template_name, const LLSD& form_elements)
-{
- mTemplatep = LLNotifications::instance().getTemplate(template_name);
- if (!mTemplatep) return;
-
- // add default substitutions
- const LLStringUtil::format_map_t& default_args = LLTrans::getDefaultArgs();
- for (LLStringUtil::format_map_t::const_iterator iter = default_args.begin();
- iter != default_args.end(); ++iter)
- {
- mSubstitutions[iter->first] = iter->second;
- }
- mSubstitutions["_URL"] = getURL();
- mSubstitutions["_NAME"] = template_name;
- // TODO: something like this so that a missing alert is sensible:
- //mSubstitutions["_ARGS"] = get_all_arguments_as_text(mSubstitutions);
-
- mForm = LLNotificationFormPtr(new LLNotificationForm(*mTemplatep->mForm));
- mForm->append(form_elements);
-
- // apply substitution to form labels
- mForm->formatElements(mSubstitutions);
-
- mIgnored = mForm->getIgnored();
-
- LLDate rightnow = LLDate::now();
- if (mTemplatep->mExpireSeconds)
- {
- mExpiresAt = LLDate(rightnow.secondsSinceEpoch() + mTemplatep->mExpireSeconds);
- }
-
- if (mPriority == NOTIFICATION_PRIORITY_UNSPECIFIED)
- {
- mPriority = mTemplatep->mPriority;
- }
-}
-
-std::string LLNotification::summarize() const
-{
- std::string s = "Notification(";
- s += getName();
- s += ") : ";
- s += mTemplatep ? mTemplatep->mMessage : "";
- // should also include timestamp and expiration time (but probably not payload)
- return s;
-}
-
-std::string LLNotification::getMessage() const
-{
- // all our callers cache this result, so it gives us more flexibility
- // to do the substitution at call time rather than attempting to
- // cache it in the notification
- if (!mTemplatep)
- return std::string();
-
- std::string message = mTemplatep->mMessage;
- LLStringUtil::format(message, mSubstitutions);
- 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;
- LLStringUtil::format(label, mSubstitutions);
- return (mTemplatep ? label : "");
-}
-
-std::string LLNotification::getURL() const
-{
- if (!mTemplatep)
- return std::string();
- std::string url = mTemplatep->mURL;
- LLStringUtil::format(url, mSubstitutions);
- 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::canFadeToast() const
-{
- return mTemplatep->mFadeToast;
-}
-
-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
-// ---
-LLBoundListener LLNotificationChannelBase::connectChangedImpl(const LLEventListener& slot)
-{
- // when someone wants to connect to a channel, we first throw them
- // all of the notifications that are already in the channel
- // we use a special signal called "load" in case the channel wants to care
- // only about new notifications
- LLMutexLock lock(&mItemsMutex);
- for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
- {
- slot(LLSD().with("sigtype", "load").with("id", (*it)->id()));
- }
- // and then connect the signal so that all future notifications will also be
- // forwarded.
- return mChanged.connect(slot);
-}
-
-LLBoundListener LLNotificationChannelBase::connectAtFrontChangedImpl(const LLEventListener& slot)
-{
- for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
- {
- slot(LLSD().with("sigtype", "load").with("id", (*it)->id()));
- }
- return mChanged.connect(slot, boost::signals2::at_front);
-}
-
-LLBoundListener LLNotificationChannelBase::connectPassedFilterImpl(const LLEventListener& slot)
-{
- // these two filters only fire for notifications added after the current one, because
- // they don't participate in the hierarchy.
- return mPassedFilter.connect(slot);
-}
-
-LLBoundListener LLNotificationChannelBase::connectFailedFilterImpl(const LLEventListener& slot)
-{
- return mFailedFilter.connect(slot);
-}
-
-// external call, conforms to our standard signature
-bool LLNotificationChannelBase::updateItem(const LLSD& payload)
-{
- // first check to see if it's in the master list
- LLNotificationPtr pNotification = LLNotifications::instance().find(payload["id"]);
- if (!pNotification)
- return false; // not found
-
- return updateItem(payload, pNotification);
-}
-
-
-//FIX QUIT NOT WORKING
-
-
-// internal call, for use in avoiding lookup
-bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPtr pNotification)
-{
- std::string cmd = payload["sigtype"];
- LLNotificationSet::iterator foundItem = mItems.find(pNotification);
- bool wasFound = (foundItem != mItems.end());
- 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.
- // If either signal returns true, the change processing is NOT performed
- // (so don't return true unless you know what you're doing!)
- bool abortProcessing = false;
- if (passesFilter)
- {
- onFilterPass(pNotification);
- abortProcessing = mPassedFilter(payload);
- }
- else
- {
- onFilterFail(pNotification);
- abortProcessing = mFailedFilter(payload);
- }
-
- if (abortProcessing)
- {
- return true;
- }
-
- if (cmd == "load")
- {
- // should be no reason we'd ever get a load if we already have it
- // if passes filter send a load message, else do nothing
- assert(!wasFound);
- if (passesFilter)
- {
- // not in our list, add it and say so
- mItems.insert(pNotification);
- onLoad(pNotification);
- abortProcessing = mChanged(payload);
- }
- }
- else if (cmd == "change")
- {
- // if it passes filter now and was found, we just send a change message
- // if it passes filter now and wasn't found, we have to add it
- // if it doesn't pass filter and wasn't found, we do nothing
- // if it doesn't pass filter and was found, we need to delete it
- if (passesFilter)
- {
- if (wasFound)
- {
- // it already existed, so this is a change
- // since it changed in place, all we have to do is resend the signal
- 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);
- }
- }
- else
- {
- if (wasFound)
- {
- // 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);
- }
- // didn't pass, not on our list, do nothing
- }
- }
- else if (cmd == "add")
- {
- // should be no reason we'd ever get an add if we already have it
- // if passes filter send an add message, else do nothing
- assert(!wasFound);
- if (passesFilter)
- {
- // not in our list, add it and say so
- mItems.insert(pNotification);
- onAdd(pNotification);
- abortProcessing = mChanged(payload);
- }
- }
- else if (cmd == "delete")
- {
- // if we have it in our list, pass on the delete, then delete it, else do nothing
- if (wasFound)
- {
- onDelete(pNotification);
- abortProcessing = mChanged(payload);
- mItems.erase(pNotification);
- }
- }
- return abortProcessing;
-}
-
-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())
-{
- for (const std::string& source : p.sources)
- {
- connectToChannel(source);
- }
-}
-
-
-LLNotificationChannel::LLNotificationChannel(const std::string& name,
- const std::string& parent,
- LLNotificationFilter filter)
-: LLNotificationChannelBase(filter),
- LLInstanceTracker<LLNotificationChannel, std::string>(name),
- mName(name)
-{
- // bind to notification broadcast
- connectToChannel(parent);
-}
-
-LLNotificationChannel::~LLNotificationChannel()
-{
- for (LLBoundListener &listener : mListeners)
- {
- listener.disconnect();
- }
-}
-
-bool LLNotificationChannel::isEmpty() const
-{
- return mItems.empty();
-}
-
-S32 LLNotificationChannel::size() const
-{
- return mItems.size();
-}
-
-size_t LLNotificationChannel::size()
-{
- return mItems.size();
-}
-
-void LLNotificationChannel::forEachNotification(NotificationProcess process)
-{
- LLMutexLock lock(&mItemsMutex);
- std::for_each(mItems.begin(), mItems.end(), process);
-}
-
-std::string LLNotificationChannel::summarize()
-{
- std::string s("Channel '");
- s += mName;
- s += "'\n ";
- LLMutexLock lock(&mItemsMutex);
- for (LLNotificationChannel::Iterator it = mItems.begin(); it != mItems.end(); ++it)
- {
- s += (*it)->summarize();
- s += "\n ";
- }
- return s;
-}
-
-void LLNotificationChannel::connectToChannel( const std::string& channel_name )
-{
- if (channel_name.empty())
- {
- mListeners.push_back(LLNotifications::instance().connectChanged(
- boost::bind(&LLNotificationChannelBase::updateItem, this, _1)));
- }
- else
- {
- mParents.push_back(channel_name);
- LLNotificationChannelPtr p = LLNotifications::instance().getChannel(channel_name);
- mListeners.push_back(p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1)));
- }
-}
-
-// ---
-// END OF LLNotificationChannel implementation
-// =========================================================
-
-
-// ============================================== ===========
-// LLNotifications implementation
-// ---
-LLNotifications::LLNotifications()
-: LLNotificationChannelBase(LLNotificationFilters::includeEverything),
- mIgnoreAllNotifications(false)
-{
- mListener.reset(new LLNotificationsListener(*this));
- LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2));
-
- // 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)
-{
- return pNotification->isCancelled() || pNotification->isRespondedTo();
-}
-
-bool LLNotifications::expirationHandler(const LLSD& payload)
-{
- if (payload["sigtype"].asString() != "delete")
- {
- // anything added to this channel actually should be deleted from the master
- cancel(find(payload["id"]));
- return true; // don't process this item any further
- }
- return false;
-}
-
-bool LLNotifications::uniqueFilter(LLNotificationPtr pNotif)
-{
- if (!pNotif->hasUniquenessConstraints())
- {
- return true;
- }
-
- // checks against existing unique notifications
- for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName());
- existing_it != mUniqueNotifications.end();
- ++existing_it)
- {
- LLNotificationPtr existing_notification = existing_it->second;
- if (pNotif != existing_notification
- && pNotif->isEquivalentTo(existing_notification))
- {
- if (pNotif->getCombineBehavior() == LLNotification::CANCEL_OLD)
- {
- cancel(existing_notification);
- return true;
- }
- else
- {
- return false;
- }
- }
- }
-
- return true;
-}
-
-bool LLNotifications::uniqueHandler(const LLSD& payload)
-{
- std::string cmd = payload["sigtype"];
-
- LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
- if (pNotif && pNotif->hasUniquenessConstraints())
- {
- 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 (cmd == "delete")
- {
- mUniqueNotifications.erase(pNotif->getName());
- }
- }
-
- return false;
-}
-
-bool LLNotifications::failedUniquenessTest(const LLSD& payload)
-{
- LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
-
- std::string cmd = payload["sigtype"];
-
- if (!pNotif || cmd != "add")
- {
- return false;
- }
-
- switch(pNotif->getCombineBehavior())
- {
- 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)
- {
- 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::COMBINE_WITH_NEW:
- // Add to 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)
- {
- LLNotificationPtr existing_notification = existing_it->second;
- if (pNotif != existing_notification
- && pNotif->isEquivalentTo(existing_notification))
- {
- // copy the notifications from the newest instance into the oldest
- existing_notification->mCombinedNotifications.push_back(pNotif);
- existing_notification->mCombinedNotifications.insert(existing_notification->mCombinedNotifications.end(),
- pNotif->mCombinedNotifications.begin(), pNotif->mCombinedNotifications.end());
-
- // pop up again
- existing_notification->update();
- }
- }
- break;
- case LLNotification::KEEP_OLD:
- break;
- case LLNotification::CANCEL_OLD:
- // already handled by filter logic
- break;
- default:
- break;
- }
-
- return false;
-}
-
-LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelName)
-{
- return LLNotificationChannelPtr(LLNotificationChannel::getInstance(channelName).get());
-}
-
-
-// this function is called once at construction time, after the object is constructed.
-void LLNotifications::initSingleton()
-{
- loadTemplates();
- loadVisibilityRules();
- createDefaultChannels();
-}
-
-void LLNotifications::cleanupSingleton()
-{
- clear();
-}
-
-void LLNotifications::createDefaultChannels()
-{
- LL_INFOS("Notifications") << "Generating default notification channels" << LL_ENDL;
- // now construct the various channels AFTER loading the notifications,
- // because the history channel is going to rewrite the stored notifications file
- 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
- getChannel("Enabled")->connectFailedFilter(&defaultResponse);
- getChannel("Expiration")->connectChanged(boost::bind(&LLNotifications::expirationHandler, this, _1));
- // uniqueHandler slot should be added as first slot of the signal due to
- // usage LLStopWhenHandled combiner in LLStandardSignal
- getChannel("Unique")->connectAtFrontChanged(boost::bind(&LLNotifications::uniqueHandler, this, _1));
- getChannel("Unique")->connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest, this, _1));
- getChannel("Ignore")->connectFailedFilter(&handleIgnoredNotification);
- getChannel("VisibilityRules")->connectFailedFilter(&visibilityRuleMached);
-}
-
-
-LLNotificationTemplatePtr LLNotifications::getTemplate(const std::string& name)
-{
- if (mTemplates.count(name))
- {
- return mTemplates[name];
- }
- else
- {
- return mTemplates["MissingAlert"];
- }
-}
-
-bool LLNotifications::templateExists(const std::string& name)
-{
- return (mTemplates.count(name) != 0);
-}
-
-void LLNotifications::forceResponse(const LLNotification::Params& params, S32 option)
-{
- LLNotificationPtr temp_notify(new LLNotification(params));
- LLSD response = temp_notify->getResponseTemplate();
- LLSD selected_item = temp_notify->getForm()->getElement(option);
-
- if (selected_item.isUndefined())
- {
- LL_WARNS("Notifications") << "Invalid option" << option << " for notification " << (std::string)params.name << LL_ENDL;
- return;
- }
- response[selected_item["name"].asString()] = true;
-
- temp_notify->respond(response);
-}
-
-LLNotifications::TemplateNames LLNotifications::getTemplateNames() const
-{
- TemplateNames names;
- for (TemplateMap::const_iterator it = mTemplates.begin(); it != mTemplates.end(); ++it)
- {
- names.push_back(it->first);
- }
- return names;
-}
-
-typedef std::map<std::string, std::string> StringMap;
-void replaceSubstitutionStrings(LLXMLNodePtr node, StringMap& replacements)
-{
- // walk the list of attributes looking for replacements
- for (LLXMLAttribList::iterator it=node->mAttributes.begin();
- it != node->mAttributes.end(); ++it)
- {
- std::string value = it->second->getValue();
- if (value[0] == '$')
- {
- value.erase(0, 1); // trim off the $
- std::string replacement;
- StringMap::const_iterator found = replacements.find(value);
- if (found != replacements.end())
- {
- replacement = found->second;
- LL_DEBUGS("Notifications") << "replaceSubstitutionStrings: value: \"" << value << "\" repl: \"" << replacement << "\"." << LL_ENDL;
- it->second->setValue(replacement);
- }
- else
- {
- LL_WARNS("Notifications") << "replaceSubstitutionStrings FAILURE: could not find replacement \"" << value << "\"." << LL_ENDL;
- }
- }
- }
-
- // now walk the list of children and call this recursively.
- for (LLXMLNodePtr child = node->getFirstChild();
- child.notNull(); child = child->getNextSibling())
- {
- replaceSubstitutionStrings(child, replacements);
- }
-}
-
-void replaceFormText(LLNotificationForm::Params& form, const std::string& pattern, const std::string& replace)
-{
- if (form.ignore.isProvided() && form.ignore.text() == pattern)
- {
- form.ignore.text = replace;
- }
-
- for (LLNotificationForm::FormElement& element : form.form_elements.elements)
- {
- if (element.button.isChosen() && element.button.text() == pattern)
- {
- 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()
-{
- LL_INFOS("Notifications") << "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 = LLXMLNode::getLayeredXMLNode(root, search_paths);
-
- if (!success || root.isNull() || !root->hasName( "notifications" ))
- {
- LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
- LL_ERRS() << "Problem reading XML from UI Notifications file: " << base_filename << LL_ENDL;
- return false;
- }
-
- LLNotificationTemplate::Notifications params;
- LLXUIParser parser;
- parser.readXUI(root, params, base_filename);
-
- if(!params.validateBlock())
- {
- LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
- LL_ERRS() << "Problem reading XUI from UI Notifications file: " << base_filename << LL_ENDL;
- return false;
- }
-
- mTemplates.clear();
-
- for (const LLNotificationTemplate::GlobalString& string : params.strings)
- {
- mGlobalStrings[string.name] = string.value;
- }
-
- std::map<std::string, LLNotificationForm::Params> form_templates;
-
- for (const LLNotificationTemplate::Template& notification_template : params.templates)
- {
- form_templates[notification_template.name] = notification_template.form;
- }
-
- for (LLNotificationTemplate::Params& notification : params.notifications)
- {
- if (notification.form_ref.form_template.isChosen())
- {
- // replace form contents from template
- notification.form_ref.form = form_templates[notification.form_ref.form_template.name];
- if(notification.form_ref.form_template.yes_text.isProvided())
- {
- replaceFormText(notification.form_ref.form, "$yestext", notification.form_ref.form_template.yes_text);
- }
- if(notification.form_ref.form_template.no_text.isProvided())
- {
- replaceFormText(notification.form_ref.form, "$notext", notification.form_ref.form_template.no_text);
- }
- if(notification.form_ref.form_template.cancel_text.isProvided())
- {
- replaceFormText(notification.form_ref.form, "$canceltext", notification.form_ref.form_template.cancel_text);
- }
- if(notification.form_ref.form_template.help_text.isProvided())
- {
- 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);
- }
- }
- mTemplates[notification.name] = LLNotificationTemplatePtr(new LLNotificationTemplate(notification));
- }
-
- LL_INFOS("Notifications") << "...done" << LL_ENDL;
-
- return true;
-}
-
-bool LLNotifications::loadVisibilityRules()
-{
- const std::string xml_filename = "notification_visibility.xml";
- // 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;
- parser.readXUI(full_filename, params);
-
- if(!params.validateBlock())
- {
- LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
- LL_ERRS() << "Problem reading UI Notification Visibility Rules file: " << full_filename << LL_ENDL;
- return false;
- }
-
- mVisibilityRules.clear();
-
- for (const LLNotificationVisibilityRule::Rule& rule : params.rules)
- {
- mVisibilityRules.push_back(LLNotificationVisibilityRulePtr(new LLNotificationVisibilityRule(rule)));
- }
-
- return true;
-}
-
-// Add a simple notification (from XUI)
-void LLNotifications::addFromCallback(const LLSD& name)
-{
- add(name.asString(), LLSD(), LLSD());
-}
-
-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)
-{
- LLNotification::Params::Functor functor_p;
- functor_p.name = functor_name;
- return add(LLNotification::Params().name(name)
- .substitutions(substitutions)
- .payload(payload)
- .functor(functor_p));
-}
-
-//virtual
-LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload, 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));
-}
-
-// generalized add function that takes a parameter block object for more complex instantiations
-LLNotificationPtr LLNotifications::add(const LLNotification::Params& p)
-{
- LLNotificationPtr pNotif(new LLNotification(p));
- add(pNotif);
- return pNotif;
-}
-
-
-void LLNotifications::add(const LLNotificationPtr pNotif)
-{
- if (pNotif == NULL) return;
-
- // first see if we already have it -- if so, that's a problem
- LLNotificationSet::iterator it=mItems.find(pNotif);
- if (it != mItems.end())
- {
- LL_ERRS() << "Notification added a second time to the master notification channel." << LL_ENDL;
- }
-
- updateItem(LLSD().with("sigtype", "add").with("id", pNotif->id()), pNotif);
-}
-
-void LLNotifications::load(const LLNotificationPtr pNotif)
-{
- if (pNotif == NULL) return;
-
- // first see if we already have it -- if so, that's a problem
- LLNotificationSet::iterator it=mItems.find(pNotif);
- if (it != mItems.end())
- {
- LL_ERRS() << "Notification loaded a second time to the master notification channel." << LL_ENDL;
- }
-
- updateItem(LLSD().with("sigtype", "load").with("id", pNotif->id()), pNotif);
-}
-
-void LLNotifications::cancel(LLNotificationPtr pNotif)
-{
- if (pNotif == NULL || pNotif->isCancelled()) return;
-
- LLNotificationSet::iterator it=mItems.find(pNotif);
- if (it != mItems.end())
- {
- pNotif->cancel();
- updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
- }
-}
-
-void LLNotifications::cancelByName(const std::string& name)
-{
- LLMutexLock lock(&mItemsMutex);
- std::vector<LLNotificationPtr> notifs_to_cancel;
- for (LLNotificationSet::iterator it=mItems.begin(), end_it = mItems.end();
- it != end_it;
- ++it)
- {
- LLNotificationPtr pNotif = *it;
- if (pNotif->getName() == name)
- {
- notifs_to_cancel.push_back(pNotif);
- }
- }
-
- for (std::vector<LLNotificationPtr>::iterator it = notifs_to_cancel.begin(), end_it = notifs_to_cancel.end();
- it != end_it;
- ++it)
- {
- LLNotificationPtr pNotif = *it;
- pNotif->cancel();
- updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
- }
-}
-
-void LLNotifications::cancelByOwner(const LLUUID ownerId)
-{
- LLMutexLock lock(&mItemsMutex);
- std::vector<LLNotificationPtr> notifs_to_cancel;
- for (LLNotificationSet::iterator it = mItems.begin(), end_it = mItems.end();
- it != end_it;
- ++it)
- {
- LLNotificationPtr pNotif = *it;
- if (pNotif && pNotif->getPayload().get("owner_id").asUUID() == ownerId)
- {
- notifs_to_cancel.push_back(pNotif);
- }
- }
-
- for (std::vector<LLNotificationPtr>::iterator it = notifs_to_cancel.begin(), end_it = notifs_to_cancel.end();
- it != end_it;
- ++it)
- {
- LLNotificationPtr pNotif = *it;
- pNotif->cancel();
- updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
- }
-}
-
-void LLNotifications::update(const LLNotificationPtr pNotif)
-{
- LLNotificationSet::iterator it=mItems.find(pNotif);
- if (it != mItems.end())
- {
- updateItem(LLSD().with("sigtype", "change").with("id", pNotif->id()), pNotif);
- }
-}
-
-
-LLNotificationPtr LLNotifications::find(LLUUID uuid)
-{
- LLNotificationPtr target = LLNotificationPtr(new LLNotification(LLNotification::Params().id(uuid)));
- LLNotificationSet::iterator it=mItems.find(target);
- if (it == mItems.end())
- {
- LL_DEBUGS("Notifications") << "Tried to dereference uuid '" << uuid << "' as a notification key but didn't find it." << LL_ENDL;
- return LLNotificationPtr((LLNotification*)NULL);
- }
- else
- {
- return *it;
- }
-}
-
-std::string LLNotifications::getGlobalString(const std::string& key) const
-{
- GlobalStringMap::const_iterator it = mGlobalStrings.find(key);
- if (it != mGlobalStrings.end())
- {
- return it->second;
- }
- else
- {
- // if we don't have the key as a global, return the key itself so that the error
- // is self-diagnosing.
- return key;
- }
-}
-
-void LLNotifications::setIgnoreAllNotifications(bool setting)
-{
- mIgnoreAllNotifications = setting;
-}
-bool LLNotifications::getIgnoreAllNotifications()
-{
- return mIgnoreAllNotifications;
-}
-
-void LLNotifications::setIgnored(const std::string& name, bool ignored)
-{
- LLNotificationTemplatePtr templatep = getTemplate(name);
- templatep->mForm->setIgnored(ignored);
-}
-
-bool LLNotifications::getIgnored(const std::string& name)
-{
- LLNotificationTemplatePtr templatep = getTemplate(name);
- return (mIgnoreAllNotifications) || ( (templatep->mForm->getIgnoreType() != LLNotificationForm::IGNORE_NO) && (templatep->mForm->getIgnored()) );
-}
-
-bool LLNotifications::isVisibleByRules(LLNotificationPtr n)
-{
- if(n->isRespondedTo())
- {
- // This avoids infinite recursion in the case where the filter calls respond()
- return true;
- }
-
- VisibilityRuleList::iterator it;
-
- 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.
- LL_DEBUGS("Notifications")
- << "notification \"" << n->getName() << "\" "
- << "testing against " << ((*it)->mVisible?"show":"hide") << " rule, "
- << "name = \"" << (*it)->mName << "\" "
- << "tag = \"" << (*it)->mTag << "\" "
- << "type = \"" << (*it)->mType << "\" "
- << LL_ENDL;
-
- if(!(*it)->mType.empty())
- {
- if((*it)->mType != n->getType())
- {
- // Type doesn't match, so skip this rule.
- continue;
- }
- }
-
- if(!(*it)->mTag.empty())
- {
- // check this notification's tag(s) against it->mTag and continue if no match is found.
- if(!n->matchesTag((*it)->mTag))
- {
- // This rule's non-empty tag didn't match one of the notification's tags. Skip this rule.
- continue;
- }
- }
-
- if(!(*it)->mName.empty())
- {
- // check this notification's name against the notification's name and continue if no match is found.
- if((*it)->mName != n->getName())
- {
- // This rule's non-empty name didn't match the notification. Skip this rule.
- continue;
- }
- }
-
- // If we got here, the rule matches. Don't evaluate subsequent rules.
- if(!(*it)->mVisible)
- {
- // This notification is being hidden.
-
- if((*it)->mResponse.empty())
- {
- // Response property is empty. Cancel this notification.
- LL_DEBUGS("Notifications") << "cancelling notification " << n->getName() << LL_ENDL;
-
- cancel(n);
- }
- else
- {
- // Response property is not empty. Return the specified response.
- LLSD response = n->getResponseTemplate(LLNotification::WITHOUT_DEFAULT_BUTTON);
- // TODO: verify that the response template has an item with the correct name
- response[(*it)->mResponse] = true;
-
- LL_DEBUGS("Notifications") << "responding to notification " << n->getName() << " with response = " << response << LL_ENDL;
-
- n->respond(response);
- }
-
- return false;
- }
-
- // If we got here, exit the loop and return true.
- break;
- }
-
- LL_DEBUGS("Notifications") << "allowing notification " << n->getName() << LL_ENDL;
-
- return true;
-}
-
-
-// ---
-// END OF LLNotifications implementation
-// =========================================================
-
-std::ostream& operator<<(std::ostream& s, const LLNotification& notification)
-{
- s << notification.summarize();
- return s;
-}
-
-void LLPostponedNotification::lookupName(const LLUUID& id,
- bool is_group)
-{
- if (is_group)
- {
- gCacheName->getGroup(id,
- boost::bind(&LLPostponedNotification::onGroupNameCache,
- this, _1, _2, _3));
- }
- else
- {
- fetchAvatarName(id);
- }
-}
-
-void LLPostponedNotification::onGroupNameCache(const LLUUID& id,
- const std::string& full_name,
- bool is_group)
-{
- 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())
- {
- LL_WARNS("Notifications") << "Empty name received for Id: " << agent_id << LL_ENDL;
- name = SYSTEM_FROM;
- }
-
- finalizeName(name);
-}
-
-void LLPostponedNotification::finalizeName(const std::string& name)
-{
- mName = name;
- modifyNotificationParams();
- LLNotifications::instance().add(mParams);
- cleanup();
-}
+/**
+* @file llnotifications.cpp
+* @brief Non-UI queue manager for keeping a prioritized list of notifications
+*
+* $LicenseInfo:firstyear=2008&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2010, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+
+#include "linden_common.h"
+
+#include "llnotifications.h"
+#include "llnotificationtemplate.h"
+#include "llnotificationvisibilityrule.h"
+
+#include "llavatarnamecache.h"
+#include "llinstantmessage.h"
+#include "llcachename.h"
+#include "llxmlnode.h"
+#include "lluictrl.h"
+#include "lluictrlfactory.h"
+#include "lldir.h"
+#include "llsdserialize.h"
+#include "lltrans.h"
+#include "llstring.h"
+#include "llsdparam.h"
+#include "llsdutil.h"
+
+#include <algorithm>
+#include <boost/regex.hpp>
+
+
+const std::string NOTIFICATION_PERSIST_VERSION = "0.93";
+
+void NotificationPriorityValues::declareValues()
+{
+ declare("low", NOTIFICATION_PRIORITY_LOW);
+ declare("normal", NOTIFICATION_PRIORITY_NORMAL);
+ declare("high", NOTIFICATION_PRIORITY_HIGH);
+ declare("critical", NOTIFICATION_PRIORITY_CRITICAL);
+}
+
+LLNotificationForm::FormElementBase::FormElementBase()
+: name("name"),
+ enabled("enabled", true)
+{}
+
+LLNotificationForm::FormIgnore::FormIgnore()
+: text("text"),
+ control("control"),
+ invert_control("invert_control", false),
+ save_option("save_option", false),
+ session_only("session_only", false),
+ checkbox_only("checkbox_only", false)
+{}
+
+LLNotificationForm::FormButton::FormButton()
+: index("index"),
+ text("text"),
+ ignore("ignore"),
+ is_default("default"),
+ width("width", 0),
+ type("type")
+{
+ // set type here so it gets serialized
+ type = "button";
+}
+
+LLNotificationForm::FormInput::FormInput()
+: type("type"),
+ text("text"),
+ max_length_chars("max_length_chars"),
+ allow_emoji("allow_emoji"),
+ width("width", 0),
+ value("value")
+{}
+
+LLNotificationForm::FormElement::FormElement()
+: button("button"),
+ input("input")
+{}
+
+LLNotificationForm::FormElements::FormElements()
+: elements("")
+{}
+
+LLNotificationForm::Params::Params()
+: name("name"),
+ ignore("ignore"),
+ form_elements("")
+{}
+
+
+
+bool filterIgnoredNotifications(LLNotificationPtr notification)
+{
+ LLNotificationFormPtr form = notification->getForm();
+ // Check to see if the user wants to ignore this alert
+ return !notification->getForm()->getIgnored();
+}
+
+bool handleIgnoredNotification(const LLSD& payload)
+{
+ if (payload["sigtype"].asString() == "add")
+ {
+ LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
+ if (!pNotif) return false;
+
+ LLNotificationFormPtr form = pNotif->getForm();
+ LLSD response;
+ switch(form->getIgnoreType())
+ {
+ case LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE:
+ case LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE_SESSION_ONLY:
+ response = pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON);
+ break;
+ case LLNotificationForm::IGNORE_WITH_LAST_RESPONSE:
+ response = LLUI::getInstance()->mSettingGroups["ignores"]->getLLSD("Default" + pNotif->getName());
+ break;
+ case LLNotificationForm::IGNORE_SHOW_AGAIN:
+ break;
+ default:
+ return false;
+ }
+ pNotif->setIgnored(true);
+ pNotif->respond(response);
+ return true; // don't process this item any further
+ }
+ return false;
+}
+
+bool defaultResponse(const LLSD& payload)
+{
+ if (payload["sigtype"].asString() == "add")
+ {
+ LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
+ if (pNotif)
+ {
+ // supply default response
+ pNotif->respond(pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON));
+ }
+ }
+ return false;
+}
+
+bool visibilityRuleMached(const LLSD& payload)
+{
+ // This is needed because LLNotifications::isVisibleByRules may have cancelled the notification.
+ // Returning true here makes LLNotificationChannelBase::updateItem do an early out, which prevents things from happening in the wrong order.
+ return true;
+}
+
+
+namespace LLNotificationFilters
+{
+ // a sample filter
+ bool includeEverything(LLNotificationPtr p)
+ {
+ return true;
+ }
+};
+
+LLNotificationForm::LLNotificationForm()
+: mIgnore(IGNORE_NO)
+{
+}
+
+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),
+ mInvertSetting(false) // ignore settings by default mean true=show, false=ignore
+{
+ if (p.ignore.isProvided())
+ {
+ // For all cases but IGNORE_CHECKBOX_ONLY this is name for use in preferences
+ mIgnoreMsg = p.ignore.text;
+
+ LLUI *ui_inst = LLUI::getInstance();
+ if (p.ignore.checkbox_only)
+ {
+ mIgnore = IGNORE_CHECKBOX_ONLY;
+ }
+ else if (!p.ignore.save_option)
+ {
+ mIgnore = p.ignore.session_only ? IGNORE_WITH_DEFAULT_RESPONSE_SESSION_ONLY : IGNORE_WITH_DEFAULT_RESPONSE;
+ }
+ else
+ {
+ // remember last option chosen by user and automatically respond with that in the future
+ mIgnore = IGNORE_WITH_LAST_RESPONSE;
+ ui_inst->mSettingGroups["ignores"]->declareLLSD(std::string("Default") + name, "", std::string("Default response for notification " + name));
+ }
+
+ bool show_notification = true;
+ if (p.ignore.control.isProvided())
+ {
+ mIgnoreSetting = ui_inst->mSettingGroups["config"]->getControl(p.ignore.control());
+ mInvertSetting = p.ignore.invert_control;
+ }
+ else if (mIgnore > IGNORE_NO)
+ {
+ ui_inst->mSettingGroups["ignores"]->declareBOOL(name, show_notification, "Show notification with this name", LLControlVariable::PERSIST_NONDFT);
+ mIgnoreSetting = ui_inst->mSettingGroups["ignores"]->getControl(name);
+ }
+ }
+
+ LLParamSDParser parser;
+ parser.writeSD(mFormData, p.form_elements);
+
+ for (LLSD::array_iterator it = mFormData.beginArray(), end_it = mFormData.endArray();
+ it != end_it;
+ ++it)
+ {
+ // lift contents of form element up a level, since element type is already encoded in "type" param
+ if (it->isMap() && it->beginMap() != it->endMap())
+ {
+ *it = it->beginMap()->second;
+ }
+ }
+
+ LL_DEBUGS("Notifications") << name << LL_ENDL;
+ LL_DEBUGS("Notifications") << ll_pretty_print_sd(mFormData) << LL_ENDL;
+}
+
+LLNotificationForm::LLNotificationForm(const LLSD& sd)
+ : mIgnore(IGNORE_NO)
+{
+ if (sd.isArray())
+ {
+ mFormData = sd;
+ }
+ else
+ {
+ LL_WARNS("Notifications") << "Invalid form data " << sd << LL_ENDL;
+ mFormData = LLSD::emptyArray();
+ }
+}
+
+LLSD LLNotificationForm::asLLSD() const
+{
+ return mFormData;
+}
+
+LLSD LLNotificationForm::getElement(const std::string& element_name)
+{
+ for (LLSD::array_const_iterator it = mFormData.beginArray();
+ it != mFormData.endArray();
+ ++it)
+ {
+ if ((*it)["name"].asString() == element_name) return (*it);
+ }
+ return LLSD();
+}
+
+
+bool LLNotificationForm::hasElement(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 true;
+ }
+ return false;
+}
+
+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;
+ element["name"] = name;
+ element["text"] = name;
+ element["value"] = value;
+ element["index"] = LLSD::Integer(mFormData.size());
+ element["enabled"] = enabled;
+ mFormData.append(element);
+}
+
+void LLNotificationForm::append(const LLSD& sub_form)
+{
+ if (sub_form.isArray())
+ {
+ for (LLSD::array_const_iterator it = sub_form.beginArray();
+ it != sub_form.endArray();
+ ++it)
+ {
+ mFormData.append(*it);
+ }
+ }
+}
+
+void LLNotificationForm::formatElements(const LLSD& substitutions)
+{
+ for (LLSD::array_iterator it = mFormData.beginArray();
+ it != mFormData.endArray();
+ ++it)
+ {
+ // format "text" component of each form element
+ if ((*it).has("text"))
+ {
+ std::string text = (*it)["text"].asString();
+ LLStringUtil::format(text, substitutions);
+ (*it)["text"] = text;
+ }
+ if ((*it)["type"].asString() == "text" && (*it).has("value"))
+ {
+ std::string value = (*it)["value"].asString();
+ LLStringUtil::format(value, substitutions);
+ (*it)["value"] = value;
+ }
+ }
+}
+
+std::string LLNotificationForm::getDefaultOption()
+{
+ for (LLSD::array_const_iterator it = mFormData.beginArray();
+ it != mFormData.endArray();
+ ++it)
+ {
+ if ((*it)["default"]) return (*it)["name"].asString();
+ }
+ return "";
+}
+
+LLControlVariablePtr LLNotificationForm::getIgnoreSetting()
+{
+ return mIgnoreSetting;
+}
+
+bool LLNotificationForm::getIgnored()
+{
+ bool show = true;
+ if (mIgnore > LLNotificationForm::IGNORE_NO
+ && mIgnoreSetting)
+ {
+ show = mIgnoreSetting->getValue().asBoolean();
+ if (mInvertSetting) show = !show;
+ }
+ return !show;
+}
+
+void LLNotificationForm::setIgnored(bool ignored)
+{
+ if (mIgnoreSetting)
+ {
+ if (mInvertSetting) ignored = !ignored;
+ mIgnoreSetting->setValue(!ignored);
+ }
+}
+
+LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Params& p)
+: mName(p.name),
+ mType(p.type),
+ mMessage(p.value),
+ mFooter(p.footer.value),
+ mLabel(p.label),
+ mIcon(p.icon),
+ mURL(p.url.value),
+ mExpireSeconds(p.duration),
+ mExpireOption(p.expire_option),
+ mURLOption(p.url.option),
+ mURLTarget(p.url.target),
+ mForceUrlsExternal(p.force_urls_external),
+ mUnique(p.unique.isProvided()),
+ mCombineBehavior(p.unique.combine),
+ mPriority(p.priority),
+ mPersist(p.persist),
+ mDefaultFunctor(p.functor.isProvided() ? p.functor() : p.name()),
+ mLogToChat(p.log_to_chat),
+ mLogToIM(p.log_to_im),
+ mShowToast(p.show_toast),
+ mFadeToast(p.fade_toast),
+ mSoundName("")
+{
+ if (p.sound.isProvided()
+ && LLUI::getInstance()->mSettingGroups["config"]->controlExists(p.sound))
+ {
+ mSoundName = p.sound;
+ }
+
+ for (const LLNotificationTemplate::UniquenessContext& context : p.unique.contexts)
+ {
+ mUniqueContext.push_back(context.value);
+ }
+
+ LL_DEBUGS("Notifications") << "notification \"" << mName << "\": tag count is " << p.tags.size() << LL_ENDL;
+
+ for (const LLNotificationTemplate::Tag& tag : p.tags)
+ {
+ LL_DEBUGS("Notifications") << " tag \"" << std::string(tag.value) << "\"" << LL_ENDL;
+ mTags.push_back(tag.value);
+ }
+
+ mForm = LLNotificationFormPtr(new LLNotificationForm(p.name, p.form_ref.form));
+}
+
+LLNotificationVisibilityRule::LLNotificationVisibilityRule(const LLNotificationVisibilityRule::Rule &p)
+{
+ if (p.show.isChosen())
+ {
+ mType = p.show.type;
+ mTag = p.show.tag;
+ mName = p.show.name;
+ mVisible = true;
+ }
+ else if (p.hide.isChosen())
+ {
+ mType = p.hide.type;
+ mTag = p.hide.tag;
+ mName = p.hide.name;
+ mVisible = false;
+ }
+ else if (p.respond.isChosen())
+ {
+ mType = p.respond.type;
+ mTag = p.respond.tag;
+ mName = p.respond.name;
+ mVisible = false;
+ mResponse = p.respond.response;
+ }
+}
+
+LLNotification::LLNotification(const LLSDParamAdapter<Params>& p) :
+ mTimestamp(p.time_stamp),
+ mSubstitutions(p.substitutions),
+ mPayload(p.payload),
+ mExpiresAt(p.expiry),
+ mTemporaryResponder(false),
+ mRespondedTo(false),
+ mPriority(p.priority),
+ mCancelled(false),
+ mIgnored(false),
+ mResponderObj(NULL),
+ mId(p.id.isProvided() ? p.id : LLUUID::generateNewID()),
+ mOfferFromAgent(p.offer_from_agent),
+ mIsDND(p.is_dnd)
+{
+ if (p.functor.name.isChosen())
+ {
+ mResponseFunctorName = p.functor.name;
+ }
+ else if (p.functor.function.isChosen())
+ {
+ mResponseFunctorName = LLUUID::generateNewID().asString();
+ LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, p.functor.function());
+
+ mTemporaryResponder = true;
+ }
+ else if(p.functor.responder.isChosen())
+ {
+ mResponder = p.functor.responder;
+ }
+
+ if(p.responder.isProvided())
+ {
+ mResponderObj = p.responder;
+ }
+
+ init(p.name, p.form_elements);
+}
+
+
+LLSD LLNotification::asLLSD(bool excludeTemplateElements)
+{
+ LLParamSDParser parser;
+
+ 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;
+}
+
+void LLNotification::update()
+{
+ LLNotifications::instance().update(shared_from_this());
+}
+
+void LLNotification::updateFrom(LLNotificationPtr other)
+{
+ // can only update from the same notification type
+ if (mTemplatep != other->mTemplatep) return;
+
+ // NOTE: do NOT change the ID, since it is the key to
+ // this given instance, just update all the metadata
+ //mId = other->mId;
+
+ mPayload = other->mPayload;
+ mSubstitutions = other->mSubstitutions;
+ mTimestamp = other->mTimestamp;
+ mExpiresAt = other->mExpiresAt;
+ mCancelled = other->mCancelled;
+ mIgnored = other->mIgnored;
+ mPriority = other->mPriority;
+ mForm = other->mForm;
+ mResponseFunctorName = other->mResponseFunctorName;
+ mRespondedTo = other->mRespondedTo;
+ mResponse = other->mResponse;
+ mTemporaryResponder = other->mTemporaryResponder;
+
+ update();
+}
+
+const LLNotificationFormPtr LLNotification::getForm()
+{
+ return mForm;
+}
+
+void LLNotification::cancel()
+{
+ mCancelled = true;
+}
+
+LLSD LLNotification::getResponseTemplate(EResponseTemplateType type)
+{
+ LLSD response = LLSD::emptyMap();
+ for (S32 element_idx = 0;
+ element_idx < mForm->getNumElements();
+ ++element_idx)
+ {
+ LLSD element = mForm->getElement(element_idx);
+ if (element.has("name"))
+ {
+ response[element["name"].asString()] = element["value"];
+ }
+
+ if ((type == WITH_DEFAULT_BUTTON)
+ && element["default"].asBoolean())
+ {
+ response[element["name"].asString()] = true;
+ }
+ }
+ return response;
+}
+
+//static
+S32 LLNotification::getSelectedOption(const LLSD& notification, const LLSD& response)
+{
+ LLNotificationForm form(notification["form"]);
+
+ for (S32 element_idx = 0;
+ element_idx < form.getNumElements();
+ ++element_idx)
+ {
+ LLSD element = form.getElement(element_idx);
+
+ // only look at buttons
+ if (element["type"].asString() == "button"
+ && response[element["name"].asString()].asBoolean())
+ {
+ return element["index"].asInteger();
+ }
+ }
+
+ return -1;
+}
+
+//static
+std::string LLNotification::getSelectedOptionName(const LLSD& response)
+{
+ for (LLSD::map_const_iterator response_it = response.beginMap();
+ response_it != response.endMap();
+ ++response_it)
+ {
+ if (response_it->second.isBoolean() && response_it->second.asBoolean())
+ {
+ return response_it->first;
+ }
+ }
+ return "";
+}
+
+
+void LLNotification::respond(const LLSD& response)
+{
+ // *TODO may remove mRespondedTo and use mResponce.isDefined() in isRespondedTo()
+ mRespondedTo = true;
+ mResponse = response;
+
+ if(mResponder)
+ {
+ mResponder->handleRespond(asLLSD(), response);
+ }
+ else if (!mResponseFunctorName.empty())
+ {
+ // look up the functor
+ LLNotificationFunctorRegistry::ResponseFunctor functor =
+ LLNotificationFunctorRegistry::instance().getFunctor(mResponseFunctorName);
+ // and then call it
+ functor(asLLSD(), response);
+ }
+ else if (mCombinedNotifications.empty())
+ {
+ // no registered responder
+ return;
+ }
+
+ if (mTemporaryResponder)
+ {
+ LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
+ mResponseFunctorName = "";
+ mTemporaryResponder = false;
+ }
+
+ if (mForm->getIgnoreType() > LLNotificationForm::IGNORE_NO)
+ {
+ mForm->setIgnored(mIgnored);
+ if (mIgnored && mForm->getIgnoreType() == LLNotificationForm::IGNORE_WITH_LAST_RESPONSE)
+ {
+ LLUI::getInstance()->mSettingGroups["ignores"]->setLLSD("Default" + getName(), response);
+ }
+ }
+
+ for (std::vector<LLNotificationPtr>::const_iterator it = mCombinedNotifications.begin(); it != mCombinedNotifications.end(); ++it)
+ {
+ if ((*it))
+ {
+ (*it)->respond(response);
+ }
+ }
+
+ update();
+}
+
+void LLNotification::respondWithDefault()
+{
+ respond(getResponseTemplate(WITH_DEFAULT_BUTTON));
+}
+
+
+const std::string& LLNotification::getName() const
+{
+ return mTemplatep->mName;
+}
+
+const std::string& LLNotification::getIcon() const
+{
+ return mTemplatep->mIcon;
+}
+
+
+bool LLNotification::isPersistent() const
+{
+ return mTemplatep->mPersist;
+}
+
+std::string LLNotification::getType() const
+{
+ return (mTemplatep ? mTemplatep->mType : "");
+}
+
+S32 LLNotification::getURLOption() const
+{
+ return (mTemplatep ? mTemplatep->mURLOption : -1);
+}
+
+S32 LLNotification::getURLOpenExternally() const
+{
+ return(mTemplatep? mTemplatep->mURLTarget == "_external": -1);
+}
+
+bool LLNotification::getForceUrlsExternal() const
+{
+ return (mTemplatep ? mTemplatep->mForceUrlsExternal : false);
+}
+
+bool LLNotification::hasUniquenessConstraints() const
+{
+ return (mTemplatep ? mTemplatep->mUnique : false);
+}
+
+bool LLNotification::matchesTag(const std::string& tag)
+{
+ bool result = false;
+
+ if(mTemplatep)
+ {
+ std::list<std::string>::iterator it;
+ for(it = mTemplatep->mTags.begin(); it != mTemplatep->mTags.end(); it++)
+ {
+ if((*it) == tag)
+ {
+ result = true;
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+void LLNotification::setIgnored(bool ignore)
+{
+ mIgnored = ignore;
+}
+
+void LLNotification::setResponseFunctor(std::string const &responseFunctorName)
+{
+ if (mTemporaryResponder)
+ // get rid of the old one
+ LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
+ mResponseFunctorName = responseFunctorName;
+ mTemporaryResponder = false;
+}
+
+void LLNotification::setResponseFunctor(const LLNotificationFunctorRegistry::ResponseFunctor& cb)
+{
+ if(mTemporaryResponder)
+ {
+ LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName);
+ }
+
+ LLNotificationFunctorRegistry::instance().registerFunctor(mResponseFunctorName, cb);
+}
+
+void LLNotification::setResponseFunctor(const LLNotificationResponderPtr& responder)
+{
+ mResponder = responder;
+}
+
+bool LLNotification::isEquivalentTo(LLNotificationPtr that) const
+{
+ if (this->mTemplatep->mName != that->mTemplatep->mName)
+ {
+ return false; // must have the same template name or forget it
+ }
+ if (this->mTemplatep->mUnique)
+ {
+ const LLSD& these_substitutions = this->getSubstitutions();
+ const LLSD& those_substitutions = that->getSubstitutions();
+ const LLSD& this_payload = this->getPayload();
+ const LLSD& that_payload = that->getPayload();
+
+ // highlander bit sez there can only be one of these
+ for (std::vector<std::string>::const_iterator it = mTemplatep->mUniqueContext.begin(), end_it = mTemplatep->mUniqueContext.end();
+ it != end_it;
+ ++it)
+ {
+ // if templates differ in either substitution strings or payload with the given field name
+ // then they are considered inequivalent
+ // use of get() avoids converting the LLSD value to a map as the [] operator would
+ if (these_substitutions.get(*it).asString() != those_substitutions.get(*it).asString()
+ || this_payload.get(*it).asString() != that_payload.get(*it).asString())
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+void LLNotification::init(const std::string& template_name, const LLSD& form_elements)
+{
+ mTemplatep = LLNotifications::instance().getTemplate(template_name);
+ if (!mTemplatep) return;
+
+ // add default substitutions
+ const LLStringUtil::format_map_t& default_args = LLTrans::getDefaultArgs();
+ for (LLStringUtil::format_map_t::const_iterator iter = default_args.begin();
+ iter != default_args.end(); ++iter)
+ {
+ mSubstitutions[iter->first] = iter->second;
+ }
+ mSubstitutions["_URL"] = getURL();
+ mSubstitutions["_NAME"] = template_name;
+ // TODO: something like this so that a missing alert is sensible:
+ //mSubstitutions["_ARGS"] = get_all_arguments_as_text(mSubstitutions);
+
+ mForm = LLNotificationFormPtr(new LLNotificationForm(*mTemplatep->mForm));
+ mForm->append(form_elements);
+
+ // apply substitution to form labels
+ mForm->formatElements(mSubstitutions);
+
+ mIgnored = mForm->getIgnored();
+
+ LLDate rightnow = LLDate::now();
+ if (mTemplatep->mExpireSeconds)
+ {
+ mExpiresAt = LLDate(rightnow.secondsSinceEpoch() + mTemplatep->mExpireSeconds);
+ }
+
+ if (mPriority == NOTIFICATION_PRIORITY_UNSPECIFIED)
+ {
+ mPriority = mTemplatep->mPriority;
+ }
+}
+
+std::string LLNotification::summarize() const
+{
+ std::string s = "Notification(";
+ s += getName();
+ s += ") : ";
+ s += mTemplatep ? mTemplatep->mMessage : "";
+ // should also include timestamp and expiration time (but probably not payload)
+ return s;
+}
+
+std::string LLNotification::getMessage() const
+{
+ // all our callers cache this result, so it gives us more flexibility
+ // to do the substitution at call time rather than attempting to
+ // cache it in the notification
+ if (!mTemplatep)
+ return std::string();
+
+ std::string message = mTemplatep->mMessage;
+ LLStringUtil::format(message, mSubstitutions);
+ 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;
+ LLStringUtil::format(label, mSubstitutions);
+ return (mTemplatep ? label : "");
+}
+
+std::string LLNotification::getURL() const
+{
+ if (!mTemplatep)
+ return std::string();
+ std::string url = mTemplatep->mURL;
+ LLStringUtil::format(url, mSubstitutions);
+ 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::canFadeToast() const
+{
+ return mTemplatep->mFadeToast;
+}
+
+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
+// ---
+LLBoundListener LLNotificationChannelBase::connectChangedImpl(const LLEventListener& slot)
+{
+ // when someone wants to connect to a channel, we first throw them
+ // all of the notifications that are already in the channel
+ // we use a special signal called "load" in case the channel wants to care
+ // only about new notifications
+ LLMutexLock lock(&mItemsMutex);
+ for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
+ {
+ slot(LLSD().with("sigtype", "load").with("id", (*it)->id()));
+ }
+ // and then connect the signal so that all future notifications will also be
+ // forwarded.
+ return mChanged.connect(slot);
+}
+
+LLBoundListener LLNotificationChannelBase::connectAtFrontChangedImpl(const LLEventListener& slot)
+{
+ for (LLNotificationSet::iterator it = mItems.begin(); it != mItems.end(); ++it)
+ {
+ slot(LLSD().with("sigtype", "load").with("id", (*it)->id()));
+ }
+ return mChanged.connect(slot, boost::signals2::at_front);
+}
+
+LLBoundListener LLNotificationChannelBase::connectPassedFilterImpl(const LLEventListener& slot)
+{
+ // these two filters only fire for notifications added after the current one, because
+ // they don't participate in the hierarchy.
+ return mPassedFilter.connect(slot);
+}
+
+LLBoundListener LLNotificationChannelBase::connectFailedFilterImpl(const LLEventListener& slot)
+{
+ return mFailedFilter.connect(slot);
+}
+
+// external call, conforms to our standard signature
+bool LLNotificationChannelBase::updateItem(const LLSD& payload)
+{
+ // first check to see if it's in the master list
+ LLNotificationPtr pNotification = LLNotifications::instance().find(payload["id"]);
+ if (!pNotification)
+ return false; // not found
+
+ return updateItem(payload, pNotification);
+}
+
+
+//FIX QUIT NOT WORKING
+
+
+// internal call, for use in avoiding lookup
+bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPtr pNotification)
+{
+ std::string cmd = payload["sigtype"];
+ LLNotificationSet::iterator foundItem = mItems.find(pNotification);
+ bool wasFound = (foundItem != mItems.end());
+ 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.
+ // If either signal returns true, the change processing is NOT performed
+ // (so don't return true unless you know what you're doing!)
+ bool abortProcessing = false;
+ if (passesFilter)
+ {
+ onFilterPass(pNotification);
+ abortProcessing = mPassedFilter(payload);
+ }
+ else
+ {
+ onFilterFail(pNotification);
+ abortProcessing = mFailedFilter(payload);
+ }
+
+ if (abortProcessing)
+ {
+ return true;
+ }
+
+ if (cmd == "load")
+ {
+ // should be no reason we'd ever get a load if we already have it
+ // if passes filter send a load message, else do nothing
+ assert(!wasFound);
+ if (passesFilter)
+ {
+ // not in our list, add it and say so
+ mItems.insert(pNotification);
+ onLoad(pNotification);
+ abortProcessing = mChanged(payload);
+ }
+ }
+ else if (cmd == "change")
+ {
+ // if it passes filter now and was found, we just send a change message
+ // if it passes filter now and wasn't found, we have to add it
+ // if it doesn't pass filter and wasn't found, we do nothing
+ // if it doesn't pass filter and was found, we need to delete it
+ if (passesFilter)
+ {
+ if (wasFound)
+ {
+ // it already existed, so this is a change
+ // since it changed in place, all we have to do is resend the signal
+ 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);
+ }
+ }
+ else
+ {
+ if (wasFound)
+ {
+ // 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);
+ }
+ // didn't pass, not on our list, do nothing
+ }
+ }
+ else if (cmd == "add")
+ {
+ // should be no reason we'd ever get an add if we already have it
+ // if passes filter send an add message, else do nothing
+ assert(!wasFound);
+ if (passesFilter)
+ {
+ // not in our list, add it and say so
+ mItems.insert(pNotification);
+ onAdd(pNotification);
+ abortProcessing = mChanged(payload);
+ }
+ }
+ else if (cmd == "delete")
+ {
+ // if we have it in our list, pass on the delete, then delete it, else do nothing
+ if (wasFound)
+ {
+ onDelete(pNotification);
+ abortProcessing = mChanged(payload);
+ mItems.erase(pNotification);
+ }
+ }
+ return abortProcessing;
+}
+
+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())
+{
+ for (const std::string& source : p.sources)
+ {
+ connectToChannel(source);
+ }
+}
+
+
+LLNotificationChannel::LLNotificationChannel(const std::string& name,
+ const std::string& parent,
+ LLNotificationFilter filter)
+: LLNotificationChannelBase(filter),
+ LLInstanceTracker<LLNotificationChannel, std::string>(name),
+ mName(name)
+{
+ // bind to notification broadcast
+ connectToChannel(parent);
+}
+
+LLNotificationChannel::~LLNotificationChannel()
+{
+ for (LLBoundListener &listener : mListeners)
+ {
+ listener.disconnect();
+ }
+}
+
+bool LLNotificationChannel::isEmpty() const
+{
+ return mItems.empty();
+}
+
+S32 LLNotificationChannel::size() const
+{
+ return mItems.size();
+}
+
+size_t LLNotificationChannel::size()
+{
+ return mItems.size();
+}
+
+void LLNotificationChannel::forEachNotification(NotificationProcess process)
+{
+ LLMutexLock lock(&mItemsMutex);
+ std::for_each(mItems.begin(), mItems.end(), process);
+}
+
+std::string LLNotificationChannel::summarize()
+{
+ std::string s("Channel '");
+ s += mName;
+ s += "'\n ";
+ LLMutexLock lock(&mItemsMutex);
+ for (LLNotificationChannel::Iterator it = mItems.begin(); it != mItems.end(); ++it)
+ {
+ s += (*it)->summarize();
+ s += "\n ";
+ }
+ return s;
+}
+
+void LLNotificationChannel::connectToChannel( const std::string& channel_name )
+{
+ if (channel_name.empty())
+ {
+ mListeners.push_back(LLNotifications::instance().connectChanged(
+ boost::bind(&LLNotificationChannelBase::updateItem, this, _1)));
+ }
+ else
+ {
+ mParents.push_back(channel_name);
+ LLNotificationChannelPtr p = LLNotifications::instance().getChannel(channel_name);
+ mListeners.push_back(p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1)));
+ }
+}
+
+// ---
+// END OF LLNotificationChannel implementation
+// =========================================================
+
+
+// ============================================== ===========
+// LLNotifications implementation
+// ---
+LLNotifications::LLNotifications()
+: LLNotificationChannelBase(LLNotificationFilters::includeEverything),
+ mIgnoreAllNotifications(false)
+{
+ mListener.reset(new LLNotificationsListener(*this));
+ LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2));
+
+ // 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)
+{
+ return pNotification->isCancelled() || pNotification->isRespondedTo();
+}
+
+bool LLNotifications::expirationHandler(const LLSD& payload)
+{
+ if (payload["sigtype"].asString() != "delete")
+ {
+ // anything added to this channel actually should be deleted from the master
+ cancel(find(payload["id"]));
+ return true; // don't process this item any further
+ }
+ return false;
+}
+
+bool LLNotifications::uniqueFilter(LLNotificationPtr pNotif)
+{
+ if (!pNotif->hasUniquenessConstraints())
+ {
+ return true;
+ }
+
+ // checks against existing unique notifications
+ for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName());
+ existing_it != mUniqueNotifications.end();
+ ++existing_it)
+ {
+ LLNotificationPtr existing_notification = existing_it->second;
+ if (pNotif != existing_notification
+ && pNotif->isEquivalentTo(existing_notification))
+ {
+ if (pNotif->getCombineBehavior() == LLNotification::CANCEL_OLD)
+ {
+ cancel(existing_notification);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool LLNotifications::uniqueHandler(const LLSD& payload)
+{
+ std::string cmd = payload["sigtype"];
+
+ LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
+ if (pNotif && pNotif->hasUniquenessConstraints())
+ {
+ 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 (cmd == "delete")
+ {
+ mUniqueNotifications.erase(pNotif->getName());
+ }
+ }
+
+ return false;
+}
+
+bool LLNotifications::failedUniquenessTest(const LLSD& payload)
+{
+ LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID());
+
+ std::string cmd = payload["sigtype"];
+
+ if (!pNotif || cmd != "add")
+ {
+ return false;
+ }
+
+ switch(pNotif->getCombineBehavior())
+ {
+ 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)
+ {
+ 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::COMBINE_WITH_NEW:
+ // Add to 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)
+ {
+ LLNotificationPtr existing_notification = existing_it->second;
+ if (pNotif != existing_notification
+ && pNotif->isEquivalentTo(existing_notification))
+ {
+ // copy the notifications from the newest instance into the oldest
+ existing_notification->mCombinedNotifications.push_back(pNotif);
+ existing_notification->mCombinedNotifications.insert(existing_notification->mCombinedNotifications.end(),
+ pNotif->mCombinedNotifications.begin(), pNotif->mCombinedNotifications.end());
+
+ // pop up again
+ existing_notification->update();
+ }
+ }
+ break;
+ case LLNotification::KEEP_OLD:
+ break;
+ case LLNotification::CANCEL_OLD:
+ // already handled by filter logic
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelName)
+{
+ return LLNotificationChannelPtr(LLNotificationChannel::getInstance(channelName).get());
+}
+
+
+// this function is called once at construction time, after the object is constructed.
+void LLNotifications::initSingleton()
+{
+ loadTemplates();
+ loadVisibilityRules();
+ createDefaultChannels();
+}
+
+void LLNotifications::cleanupSingleton()
+{
+ clear();
+}
+
+void LLNotifications::createDefaultChannels()
+{
+ LL_INFOS("Notifications") << "Generating default notification channels" << LL_ENDL;
+ // now construct the various channels AFTER loading the notifications,
+ // because the history channel is going to rewrite the stored notifications file
+ 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
+ getChannel("Enabled")->connectFailedFilter(&defaultResponse);
+ getChannel("Expiration")->connectChanged(boost::bind(&LLNotifications::expirationHandler, this, _1));
+ // uniqueHandler slot should be added as first slot of the signal due to
+ // usage LLStopWhenHandled combiner in LLStandardSignal
+ getChannel("Unique")->connectAtFrontChanged(boost::bind(&LLNotifications::uniqueHandler, this, _1));
+ getChannel("Unique")->connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest, this, _1));
+ getChannel("Ignore")->connectFailedFilter(&handleIgnoredNotification);
+ getChannel("VisibilityRules")->connectFailedFilter(&visibilityRuleMached);
+}
+
+
+LLNotificationTemplatePtr LLNotifications::getTemplate(const std::string& name)
+{
+ if (mTemplates.count(name))
+ {
+ return mTemplates[name];
+ }
+ else
+ {
+ return mTemplates["MissingAlert"];
+ }
+}
+
+bool LLNotifications::templateExists(const std::string& name)
+{
+ return (mTemplates.count(name) != 0);
+}
+
+void LLNotifications::forceResponse(const LLNotification::Params& params, S32 option)
+{
+ LLNotificationPtr temp_notify(new LLNotification(params));
+ LLSD response = temp_notify->getResponseTemplate();
+ LLSD selected_item = temp_notify->getForm()->getElement(option);
+
+ if (selected_item.isUndefined())
+ {
+ LL_WARNS("Notifications") << "Invalid option" << option << " for notification " << (std::string)params.name << LL_ENDL;
+ return;
+ }
+ response[selected_item["name"].asString()] = true;
+
+ temp_notify->respond(response);
+}
+
+LLNotifications::TemplateNames LLNotifications::getTemplateNames() const
+{
+ TemplateNames names;
+ for (TemplateMap::const_iterator it = mTemplates.begin(); it != mTemplates.end(); ++it)
+ {
+ names.push_back(it->first);
+ }
+ return names;
+}
+
+typedef std::map<std::string, std::string> StringMap;
+void replaceSubstitutionStrings(LLXMLNodePtr node, StringMap& replacements)
+{
+ // walk the list of attributes looking for replacements
+ for (LLXMLAttribList::iterator it=node->mAttributes.begin();
+ it != node->mAttributes.end(); ++it)
+ {
+ std::string value = it->second->getValue();
+ if (value[0] == '$')
+ {
+ value.erase(0, 1); // trim off the $
+ std::string replacement;
+ StringMap::const_iterator found = replacements.find(value);
+ if (found != replacements.end())
+ {
+ replacement = found->second;
+ LL_DEBUGS("Notifications") << "replaceSubstitutionStrings: value: \"" << value << "\" repl: \"" << replacement << "\"." << LL_ENDL;
+ it->second->setValue(replacement);
+ }
+ else
+ {
+ LL_WARNS("Notifications") << "replaceSubstitutionStrings FAILURE: could not find replacement \"" << value << "\"." << LL_ENDL;
+ }
+ }
+ }
+
+ // now walk the list of children and call this recursively.
+ for (LLXMLNodePtr child = node->getFirstChild();
+ child.notNull(); child = child->getNextSibling())
+ {
+ replaceSubstitutionStrings(child, replacements);
+ }
+}
+
+void replaceFormText(LLNotificationForm::Params& form, const std::string& pattern, const std::string& replace)
+{
+ if (form.ignore.isProvided() && form.ignore.text() == pattern)
+ {
+ form.ignore.text = replace;
+ }
+
+ for (LLNotificationForm::FormElement& element : form.form_elements.elements)
+ {
+ if (element.button.isChosen() && element.button.text() == pattern)
+ {
+ 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()
+{
+ LL_INFOS("Notifications") << "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);
+ if (search_paths.empty())
+ {
+ LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
+ LL_ERRS() << "Problem finding notifications.xml" << LL_ENDL;
+ }
+
+ std::string base_filename = search_paths.front();
+ LLXMLNodePtr root;
+ bool success = LLXMLNode::getLayeredXMLNode(root, search_paths);
+
+ if (!success || root.isNull() || !root->hasName( "notifications" ))
+ {
+ LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
+ LL_ERRS() << "Problem reading XML from UI Notifications file: " << base_filename << LL_ENDL;
+ return false;
+ }
+
+ LLNotificationTemplate::Notifications params;
+ LLXUIParser parser;
+ parser.readXUI(root, params, base_filename);
+
+ if(!params.validateBlock())
+ {
+ LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
+ LL_ERRS() << "Problem reading XUI from UI Notifications file: " << base_filename << LL_ENDL;
+ return false;
+ }
+
+ mTemplates.clear();
+
+ for (const LLNotificationTemplate::GlobalString& string : params.strings)
+ {
+ mGlobalStrings[string.name] = string.value;
+ }
+
+ std::map<std::string, LLNotificationForm::Params> form_templates;
+
+ for (const LLNotificationTemplate::Template& notification_template : params.templates)
+ {
+ form_templates[notification_template.name] = notification_template.form;
+ }
+
+ for (LLNotificationTemplate::Params& notification : params.notifications)
+ {
+ if (notification.form_ref.form_template.isChosen())
+ {
+ // replace form contents from template
+ notification.form_ref.form = form_templates[notification.form_ref.form_template.name];
+ if(notification.form_ref.form_template.yes_text.isProvided())
+ {
+ replaceFormText(notification.form_ref.form, "$yestext", notification.form_ref.form_template.yes_text);
+ }
+ if(notification.form_ref.form_template.no_text.isProvided())
+ {
+ replaceFormText(notification.form_ref.form, "$notext", notification.form_ref.form_template.no_text);
+ }
+ if(notification.form_ref.form_template.cancel_text.isProvided())
+ {
+ replaceFormText(notification.form_ref.form, "$canceltext", notification.form_ref.form_template.cancel_text);
+ }
+ if(notification.form_ref.form_template.help_text.isProvided())
+ {
+ 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);
+ }
+ }
+ mTemplates[notification.name] = LLNotificationTemplatePtr(new LLNotificationTemplate(notification));
+ }
+
+ LL_INFOS("Notifications") << "...done" << LL_ENDL;
+
+ return true;
+}
+
+bool LLNotifications::loadVisibilityRules()
+{
+ const std::string xml_filename = "notification_visibility.xml";
+ // 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;
+ parser.readXUI(full_filename, params);
+
+ if(!params.validateBlock())
+ {
+ LLError::LLUserWarningMsg::show(LLTrans::getString("MBMissingFile"));
+ LL_ERRS() << "Problem reading UI Notification Visibility Rules file: " << full_filename << LL_ENDL;
+ return false;
+ }
+
+ mVisibilityRules.clear();
+
+ for (const LLNotificationVisibilityRule::Rule& rule : params.rules)
+ {
+ mVisibilityRules.push_back(LLNotificationVisibilityRulePtr(new LLNotificationVisibilityRule(rule)));
+ }
+
+ return true;
+}
+
+// Add a simple notification (from XUI)
+void LLNotifications::addFromCallback(const LLSD& name)
+{
+ add(name.asString(), LLSD(), LLSD());
+}
+
+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)
+{
+ LLNotification::Params::Functor functor_p;
+ functor_p.name = functor_name;
+ return add(LLNotification::Params().name(name)
+ .substitutions(substitutions)
+ .payload(payload)
+ .functor(functor_p));
+}
+
+//virtual
+LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload, 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));
+}
+
+// generalized add function that takes a parameter block object for more complex instantiations
+LLNotificationPtr LLNotifications::add(const LLNotification::Params& p)
+{
+ LLNotificationPtr pNotif(new LLNotification(p));
+ add(pNotif);
+ return pNotif;
+}
+
+
+void LLNotifications::add(const LLNotificationPtr pNotif)
+{
+ if (pNotif == NULL) return;
+
+ // first see if we already have it -- if so, that's a problem
+ LLNotificationSet::iterator it=mItems.find(pNotif);
+ if (it != mItems.end())
+ {
+ LL_ERRS() << "Notification added a second time to the master notification channel." << LL_ENDL;
+ }
+
+ updateItem(LLSD().with("sigtype", "add").with("id", pNotif->id()), pNotif);
+}
+
+void LLNotifications::load(const LLNotificationPtr pNotif)
+{
+ if (pNotif == NULL) return;
+
+ // first see if we already have it -- if so, that's a problem
+ LLNotificationSet::iterator it=mItems.find(pNotif);
+ if (it != mItems.end())
+ {
+ LL_ERRS() << "Notification loaded a second time to the master notification channel." << LL_ENDL;
+ }
+
+ updateItem(LLSD().with("sigtype", "load").with("id", pNotif->id()), pNotif);
+}
+
+void LLNotifications::cancel(LLNotificationPtr pNotif)
+{
+ if (pNotif == NULL || pNotif->isCancelled()) return;
+
+ LLNotificationSet::iterator it=mItems.find(pNotif);
+ if (it != mItems.end())
+ {
+ pNotif->cancel();
+ updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
+ }
+}
+
+void LLNotifications::cancelByName(const std::string& name)
+{
+ LLMutexLock lock(&mItemsMutex);
+ std::vector<LLNotificationPtr> notifs_to_cancel;
+ for (LLNotificationSet::iterator it=mItems.begin(), end_it = mItems.end();
+ it != end_it;
+ ++it)
+ {
+ LLNotificationPtr pNotif = *it;
+ if (pNotif->getName() == name)
+ {
+ notifs_to_cancel.push_back(pNotif);
+ }
+ }
+
+ for (std::vector<LLNotificationPtr>::iterator it = notifs_to_cancel.begin(), end_it = notifs_to_cancel.end();
+ it != end_it;
+ ++it)
+ {
+ LLNotificationPtr pNotif = *it;
+ pNotif->cancel();
+ updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
+ }
+}
+
+void LLNotifications::cancelByOwner(const LLUUID ownerId)
+{
+ LLMutexLock lock(&mItemsMutex);
+ std::vector<LLNotificationPtr> notifs_to_cancel;
+ for (LLNotificationSet::iterator it = mItems.begin(), end_it = mItems.end();
+ it != end_it;
+ ++it)
+ {
+ LLNotificationPtr pNotif = *it;
+ if (pNotif && pNotif->getPayload().get("owner_id").asUUID() == ownerId)
+ {
+ notifs_to_cancel.push_back(pNotif);
+ }
+ }
+
+ for (std::vector<LLNotificationPtr>::iterator it = notifs_to_cancel.begin(), end_it = notifs_to_cancel.end();
+ it != end_it;
+ ++it)
+ {
+ LLNotificationPtr pNotif = *it;
+ pNotif->cancel();
+ updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif);
+ }
+}
+
+void LLNotifications::update(const LLNotificationPtr pNotif)
+{
+ LLNotificationSet::iterator it=mItems.find(pNotif);
+ if (it != mItems.end())
+ {
+ updateItem(LLSD().with("sigtype", "change").with("id", pNotif->id()), pNotif);
+ }
+}
+
+
+LLNotificationPtr LLNotifications::find(LLUUID uuid)
+{
+ LLNotificationPtr target = LLNotificationPtr(new LLNotification(LLNotification::Params().id(uuid)));
+ LLNotificationSet::iterator it=mItems.find(target);
+ if (it == mItems.end())
+ {
+ LL_DEBUGS("Notifications") << "Tried to dereference uuid '" << uuid << "' as a notification key but didn't find it." << LL_ENDL;
+ return LLNotificationPtr((LLNotification*)NULL);
+ }
+ else
+ {
+ return *it;
+ }
+}
+
+std::string LLNotifications::getGlobalString(const std::string& key) const
+{
+ GlobalStringMap::const_iterator it = mGlobalStrings.find(key);
+ if (it != mGlobalStrings.end())
+ {
+ return it->second;
+ }
+ else
+ {
+ // if we don't have the key as a global, return the key itself so that the error
+ // is self-diagnosing.
+ return key;
+ }
+}
+
+void LLNotifications::setIgnoreAllNotifications(bool setting)
+{
+ mIgnoreAllNotifications = setting;
+}
+bool LLNotifications::getIgnoreAllNotifications()
+{
+ return mIgnoreAllNotifications;
+}
+
+void LLNotifications::setIgnored(const std::string& name, bool ignored)
+{
+ LLNotificationTemplatePtr templatep = getTemplate(name);
+ templatep->mForm->setIgnored(ignored);
+}
+
+bool LLNotifications::getIgnored(const std::string& name)
+{
+ LLNotificationTemplatePtr templatep = getTemplate(name);
+ return (mIgnoreAllNotifications) || ( (templatep->mForm->getIgnoreType() != LLNotificationForm::IGNORE_NO) && (templatep->mForm->getIgnored()) );
+}
+
+bool LLNotifications::isVisibleByRules(LLNotificationPtr n)
+{
+ if(n->isRespondedTo())
+ {
+ // This avoids infinite recursion in the case where the filter calls respond()
+ return true;
+ }
+
+ VisibilityRuleList::iterator it;
+
+ 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.
+ LL_DEBUGS("Notifications")
+ << "notification \"" << n->getName() << "\" "
+ << "testing against " << ((*it)->mVisible?"show":"hide") << " rule, "
+ << "name = \"" << (*it)->mName << "\" "
+ << "tag = \"" << (*it)->mTag << "\" "
+ << "type = \"" << (*it)->mType << "\" "
+ << LL_ENDL;
+
+ if(!(*it)->mType.empty())
+ {
+ if((*it)->mType != n->getType())
+ {
+ // Type doesn't match, so skip this rule.
+ continue;
+ }
+ }
+
+ if(!(*it)->mTag.empty())
+ {
+ // check this notification's tag(s) against it->mTag and continue if no match is found.
+ if(!n->matchesTag((*it)->mTag))
+ {
+ // This rule's non-empty tag didn't match one of the notification's tags. Skip this rule.
+ continue;
+ }
+ }
+
+ if(!(*it)->mName.empty())
+ {
+ // check this notification's name against the notification's name and continue if no match is found.
+ if((*it)->mName != n->getName())
+ {
+ // This rule's non-empty name didn't match the notification. Skip this rule.
+ continue;
+ }
+ }
+
+ // If we got here, the rule matches. Don't evaluate subsequent rules.
+ if(!(*it)->mVisible)
+ {
+ // This notification is being hidden.
+
+ if((*it)->mResponse.empty())
+ {
+ // Response property is empty. Cancel this notification.
+ LL_DEBUGS("Notifications") << "cancelling notification " << n->getName() << LL_ENDL;
+
+ cancel(n);
+ }
+ else
+ {
+ // Response property is not empty. Return the specified response.
+ LLSD response = n->getResponseTemplate(LLNotification::WITHOUT_DEFAULT_BUTTON);
+ // TODO: verify that the response template has an item with the correct name
+ response[(*it)->mResponse] = true;
+
+ LL_DEBUGS("Notifications") << "responding to notification " << n->getName() << " with response = " << response << LL_ENDL;
+
+ n->respond(response);
+ }
+
+ return false;
+ }
+
+ // If we got here, exit the loop and return true.
+ break;
+ }
+
+ LL_DEBUGS("Notifications") << "allowing notification " << n->getName() << LL_ENDL;
+
+ return true;
+}
+
+
+// ---
+// END OF LLNotifications implementation
+// =========================================================
+
+std::ostream& operator<<(std::ostream& s, const LLNotification& notification)
+{
+ s << notification.summarize();
+ return s;
+}
+
+void LLPostponedNotification::lookupName(const LLUUID& id,
+ bool is_group)
+{
+ if (is_group)
+ {
+ gCacheName->getGroup(id,
+ boost::bind(&LLPostponedNotification::onGroupNameCache,
+ this, _1, _2, _3));
+ }
+ else
+ {
+ fetchAvatarName(id);
+ }
+}
+
+void LLPostponedNotification::onGroupNameCache(const LLUUID& id,
+ const std::string& full_name,
+ bool is_group)
+{
+ 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())
+ {
+ LL_WARNS("Notifications") << "Empty name received for Id: " << agent_id << LL_ENDL;
+ name = SYSTEM_FROM;
+ }
+
+ finalizeName(name);
+}
+
+void LLPostponedNotification::finalizeName(const std::string& name)
+{
+ mName = name;
+ modifyNotificationParams();
+ LLNotifications::instance().add(mParams);
+ cleanup();
+}
diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h
index ccc8a42a6c..c3796252c7 100644
--- a/indra/llui/llnotifications.h
+++ b/indra/llui/llnotifications.h
@@ -1,1132 +1,1133 @@
-/**
-* @file llnotifications.h
-* @brief Non-UI manager and support for keeping a prioritized list of notifications
-* @author Q (with assistance from Richard and Coco)
-*
-* $LicenseInfo:firstyear=2008&license=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_LLNOTIFICATIONS_H
-#define LL_LLNOTIFICATIONS_H
-
-/**
- * This system is intended to provide a singleton mechanism for adding
- * notifications to one of an arbitrary set of event channels.
- *
- * Controlling JIRA: DEV-9061
- *
- * Every notification has (see code for full list):
- * - a textual name, which is used to look up its template in the XML files
- * - a payload, which is a block of LLSD
- * - a channel, which is normally extracted from the XML files but
- * can be overridden.
- * - a timestamp, used to order the notifications
- * - expiration time -- if nonzero, specifies a time after which the
- * notification will no longer be valid.
- * - a callback name and a couple of status bits related to callbacks (see below)
- *
- * There is a management class called LLNotifications, which is an LLSingleton.
- * The class maintains a collection of all of the notifications received
- * or processed during this session, and also manages the persistence
- * of those notifications that must be persisted.
- *
- * We also have Channels. A channel is a view on a collection of notifications;
- * The collection is defined by a filter function that controls which
- * notifications are in the channel, and its ordering is controlled by
- * a comparator.
- *
- * There is a hierarchy of channels; notifications flow down from
- * the management class (LLNotifications, which itself inherits from
- * The channel base class) to the individual channels.
- * Any change to notifications (add, delete, modify) is
- * automatically propagated through the channel hierarchy.
- *
- * We provide methods for adding a new notification, for removing
- * one, and for managing channels. Channels are relatively cheap to construct
- * and maintain, so in general, human interfaces should use channels to
- * select and manage their lists of notifications.
- *
- * We also maintain a collection of templates that are loaded from the
- * XML file of template translations. The system supports substitution
- * of named variables from the payload into the XML file.
- *
- * By default, only the "unknown message" template is built into the system.
- * It is not an error to add a notification that's not found in the
- * template system, but it is logged.
- *
- */
-
-#include <string>
-#include <list>
-#include <vector>
-#include <map>
-#include <set>
-#include <iomanip>
-#include <sstream>
-
-#include <boost/utility.hpp>
-#include <boost/type_traits.hpp>
-#include <boost/signals2.hpp>
-#include <boost/range.hpp>
-
-#include "llevents.h"
-#include "llfunctorregistry.h"
-#include "llinitparam.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
-{
- NOTIFICATION_PRIORITY_UNSPECIFIED,
- NOTIFICATION_PRIORITY_LOW,
- NOTIFICATION_PRIORITY_NORMAL,
- NOTIFICATION_PRIORITY_HIGH,
- NOTIFICATION_PRIORITY_CRITICAL
-} ENotificationPriority;
-
-struct NotificationPriorityValues : public LLInitParam::TypeValuesHelper<ENotificationPriority, NotificationPriorityValues>
-{
- static void declareValues();
-};
-
-class LLNotificationResponderInterface
-{
-public:
- LLNotificationResponderInterface(){};
- virtual ~LLNotificationResponderInterface(){};
-
- virtual void handleRespond(const LLSD& notification, const LLSD& response) = 0;
-
- virtual LLSD asLLSD() = 0;
-
- virtual void fromLLSD(const LLSD& params) = 0;
-};
-
-typedef boost::function<void (const LLSD&, const LLSD&)> LLNotificationResponder;
-
-typedef std::shared_ptr<LLNotificationResponderInterface> LLNotificationResponderPtr;
-
-typedef LLFunctorRegistry<LLNotificationResponder> LLNotificationFunctorRegistry;
-typedef LLFunctorRegistration<LLNotificationResponder> LLNotificationFunctorRegistration;
-
-// context data that can be looked up via a notification's payload by the display logic
-// derive from this class to implement specific contexts
-class LLNotificationContext : public LLInstanceTracker<LLNotificationContext, LLUUID>
-{
-public:
-
- LLNotificationContext() : LLInstanceTracker<LLNotificationContext, LLUUID>(LLUUID::generateNewID())
- {
- }
-
- virtual ~LLNotificationContext() {}
-
- LLSD asLLSD() const
- {
- return getKey();
- }
-
-private:
-
-};
-
-// Contains notification form data, such as buttons and text fields along with
-// manipulator functions
-class LLNotificationForm
-{
- LOG_CLASS(LLNotificationForm);
-
-public:
- struct FormElementBase : public LLInitParam::Block<FormElementBase>
- {
- Optional<std::string> name;
- Optional<bool> enabled;
-
- FormElementBase();
- };
-
- struct FormIgnore : public LLInitParam::Block<FormIgnore, FormElementBase>
- {
- Optional<std::string> text;
- Optional<bool> save_option;
- Optional<std::string> control;
- Optional<bool> invert_control;
- Optional<bool> session_only;
- Optional<bool> checkbox_only;
-
- FormIgnore();
- };
-
- struct FormButton : public LLInitParam::Block<FormButton, FormElementBase>
- {
- Mandatory<S32> index;
- Mandatory<std::string> text;
- Optional<std::string> ignore;
- Optional<bool> is_default;
- Optional<S32> width;
-
- Mandatory<std::string> type;
-
- FormButton();
- };
-
- struct FormInput : public LLInitParam::Block<FormInput, FormElementBase>
- {
- Mandatory<std::string> type;
- Optional<S32> width;
- Optional<S32> max_length_chars;
- Optional<std::string> text;
-
- Optional<std::string> value;
- FormInput();
- };
-
- struct FormElement : public LLInitParam::ChoiceBlock<FormElement>
- {
- Alternative<FormButton> button;
- Alternative<FormInput> input;
-
- FormElement();
- };
-
- struct FormElements : public LLInitParam::Block<FormElements>
- {
- Multiple<FormElement> elements;
- FormElements();
- };
-
- struct Params : public LLInitParam::Block<Params>
- {
- Optional<std::string> name;
- Optional<FormIgnore> ignore;
- Optional<FormElements> form_elements;
-
- Params();
- };
-
- typedef enum e_ignore_type
- {
- IGNORE_CHECKBOX_ONLY = -1, // ignore won't be handled, will set value/checkbox only
- IGNORE_NO = 0,
- IGNORE_WITH_DEFAULT_RESPONSE,
- IGNORE_WITH_DEFAULT_RESPONSE_SESSION_ONLY,
- IGNORE_WITH_LAST_RESPONSE,
- IGNORE_SHOW_AGAIN
- } 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);
- 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);
- std::string getDefaultOption();
- LLPointer<class LLControlVariable> getIgnoreSetting();
- bool getIgnored();
- void setIgnored(bool ignored);
-
- EIgnoreType getIgnoreType() { return mIgnore; }
- std::string getIgnoreMessage() { return mIgnoreMsg; }
-
-private:
- LLSD mFormData;
- EIgnoreType mIgnore;
- std::string mIgnoreMsg;
- LLPointer<class LLControlVariable> mIgnoreSetting;
- bool mInvertSetting;
-};
-
-typedef std::shared_ptr<LLNotificationForm> LLNotificationFormPtr;
-
-
-struct LLNotificationTemplate;
-
-// we want to keep a map of these by name, and it's best to manage them
-// with smart pointers
-typedef std::shared_ptr<LLNotificationTemplate> LLNotificationTemplatePtr;
-
-
-struct LLNotificationVisibilityRule;
-
-typedef std::shared_ptr<LLNotificationVisibilityRule> LLNotificationVisibilityRulePtr;
-
-/**
- * @class LLNotification
- * @brief The object that expresses the details of a notification
- *
- * We make this noncopyable because
- * we want to manage these through LLNotificationPtr, and only
- * ever create one instance of any given notification.
- *
- * The enable_shared_from_this flag ensures that if we construct
- * a smart pointer from a notification, we'll always get the same
- * shared pointer.
- */
-class LLNotification :
- boost::noncopyable,
- public std::enable_shared_from_this<LLNotification>
-{
-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<LLUUID> id;
- Optional<LLSD> substitutions,
- form_elements,
- payload;
- Optional<ENotificationPriority, NotificationPriorityValues> priority;
- Optional<LLDate> time_stamp,
- expiry;
- Optional<LLNotificationContext*> context;
- Optional<void*> responder;
- Optional<bool> offer_from_agent;
- Optional<bool> is_dnd;
-
- struct Functor : public LLInitParam::ChoiceBlock<Functor>
- {
- Alternative<std::string> name;
- Alternative<LLNotificationFunctorRegistry::ResponseFunctor> function;
- Alternative<LLNotificationResponderPtr> responder;
- Alternative<LLSD> responder_sd;
-
- Functor()
- : name("responseFunctor"),
- function("functor"),
- responder("responder"),
- responder_sd("responder_sd")
- {}
- };
- Optional<Functor> functor;
-
- Params()
- : name("name"),
- id("id"),
- priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED),
- time_stamp("time"),
- payload("payload"),
- 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;
- }
-
- Params(const std::string& _name)
- : name("name"),
- priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED),
- time_stamp("time"),
- payload("payload"),
- form_elements("form"),
- substitutions("substitutions"),
- expiry("expiry"),
- offer_from_agent("offer_from_agent", false),
- is_dnd("is_dnd", false)
- {
- functor.name = _name;
- name = _name;
- time_stamp = LLDate::now();
- responder = NULL;
- }
- };
-
- LLNotificationResponderPtr getResponderPtr() { return mResponder; }
-
-private:
-
- const LLUUID mId;
- LLSD mPayload;
- LLSD mSubstitutions;
- LLDate mTimestamp;
- LLDate mExpiresAt;
- bool mCancelled;
- bool mRespondedTo; // once the notification has been responded to, this becomes true
- LLSD mResponse;
- bool mIgnored;
- ENotificationPriority mPriority;
- LLNotificationFormPtr mForm;
- void* mResponderObj; // TODO - refactor/remove this field
- LLNotificationResponderPtr mResponder;
- bool mOfferFromAgent;
- bool mIsDND;
-
- // a reference to the template
- LLNotificationTemplatePtr mTemplatep;
-
- /*
- We want to be able to store and reload notifications so that they can survive
- a shutdown/restart of the client. So we can't simply pass in callbacks;
- we have to specify a callback mechanism that can be used by name rather than
- by some arbitrary pointer -- and then people have to initialize callbacks
- in some useful location. So we use LLNotificationFunctorRegistry to manage them.
- */
- std::string mResponseFunctorName;
-
- /*
- In cases where we want to specify an explict, non-persisted callback,
- we store that in the callback registry under a dynamically generated
- key, and store the key in the notification, so we can still look it up
- using the same mechanism.
- */
- bool mTemporaryResponder;
-
- // keep track of other notifications combined with COMBINE_WITH_NEW
- std::vector<LLNotificationPtr> mCombinedNotifications;
-
- void init(const std::string& template_name, const LLSD& form_elements);
-
- void cancel();
-
-public:
- LLNotification(const LLSDParamAdapter<Params>& p);
-
- void setResponseFunctor(std::string const &responseFunctorName);
-
- void setResponseFunctor(const LLNotificationFunctorRegistry::ResponseFunctor& cb);
-
- void setResponseFunctor(const LLNotificationResponderPtr& responder);
-
- typedef enum e_response_template_type
- {
- WITHOUT_DEFAULT_BUTTON,
- WITH_DEFAULT_BUTTON
- } EResponseTemplateType;
-
- // return response LLSD filled in with default form contents and (optionally) the default button selected
- LLSD getResponseTemplate(EResponseTemplateType type = WITHOUT_DEFAULT_BUTTON);
-
- // returns index of first button with value==true
- // usually this the button the user clicked on
- // returns -1 if no button clicked (e.g. form has not been displayed)
- static S32 getSelectedOption(const LLSD& notification, const LLSD& response);
- // returns name of first button with value==true
- static std::string getSelectedOptionName(const LLSD& notification);
-
- // after someone responds to a notification (usually by clicking a button,
- // but sometimes by filling out a little form and THEN clicking a button),
- // the result of the response (the name and value of the button clicked,
- // plus any other data) should be packaged up as LLSD, then passed as a
- // parameter to the notification's respond() method here. This will look up
- // and call the appropriate responder.
- //
- // response is notification serialized as LLSD:
- // ["name"] = notification name
- // ["form"] = LLSD tree that includes form description and any prefilled form data
- // ["response"] = form data filled in by user
- // (including, but not limited to which button they clicked on)
- // ["payload"] = transaction specific data, such as ["source_id"] (originator of notification),
- // ["item_id"] (attached inventory item), etc.
- // ["substitutions"] = string substitutions used to generate notification message
- // from the template
- // ["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(bool excludeTemplateElements = false);
-
- const LLNotificationFormPtr getForm();
- void updateForm(const LLNotificationFormPtr& form);
-
- void repost();
-
- void respond(const LLSD& sd);
- void respondWithDefault();
-
- void* getResponder() { return mResponderObj; }
-
- void setResponder(void* responder) { mResponderObj = responder; }
-
- void setIgnored(bool ignore);
-
- bool isCancelled() const
- {
- return mCancelled;
- }
-
- bool isRespondedTo() const
- {
- return mRespondedTo;
- }
-
- bool isActive() const
- {
- return !isRespondedTo()
- && !isCancelled()
- && !isExpired();
- }
-
- const LLSD& getResponse() { return mResponse; }
-
- bool isIgnored() const
- {
- return mIgnored;
- }
-
- const std::string& getName() const;
-
- const std::string& getIcon() const;
-
- bool isPersistent() const;
-
- const LLUUID& id() const
- {
- return mId;
- }
-
- const LLSD& getPayload() const
- {
- return mPayload;
- }
-
- const LLSD& getSubstitutions() const
- {
- return mSubstitutions;
- }
-
- const LLDate& getDate() const
- {
- 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; //for url responce option
- bool getForceUrlsExternal() const;
- bool canLogToChat() const;
- bool canLogToIM() const;
- bool canShowToast() const;
- bool canFadeToast() const;
- bool hasFormElements() const;
- void playSound();
-
- typedef enum e_combine_behavior
- {
- REPLACE_WITH_NEW,
- COMBINE_WITH_NEW,
- KEEP_OLD,
- CANCEL_OLD
-
- } ECombineBehavior;
-
- ECombineBehavior getCombineBehavior() const;
-
- const LLDate getExpiration() const
- {
- return mExpiresAt;
- }
-
- ENotificationPriority getPriority() const
- {
- return mPriority;
- }
-
- const LLUUID getID() const
- {
- return mId;
- }
-
- // comparing two notifications normally means comparing them by UUID (so we can look them
- // up quickly this way)
- bool operator<(const LLNotification& rhs) const
- {
- return mId < rhs.mId;
- }
-
- bool operator==(const LLNotification& rhs) const
- {
- return mId == rhs.mId;
- }
-
- bool operator!=(const LLNotification& rhs) const
- {
- return !operator==(rhs);
- }
-
- bool isSameObjectAs(const LLNotification* rhs) const
- {
- return this == rhs;
- }
-
- // this object has been updated, so tell all our clients
- void update();
-
- void updateFrom(LLNotificationPtr other);
-
- // A fuzzy equals comparator.
- // true only if both notifications have the same template and
- // 1) flagged as unique (there can be only one of these) OR
- // 2) all required payload fields of each also exist in the other.
- bool isEquivalentTo(LLNotificationPtr that) const;
-
- // if the current time is greater than the expiration, the notification is expired
- bool isExpired() const
- {
- if (mExpiresAt.secondsSinceEpoch() == 0)
- {
- return false;
- }
-
- LLDate rightnow = LLDate::now();
- return rightnow > mExpiresAt;
- }
-
- std::string summarize() const;
-
- bool hasUniquenessConstraints() const;
-
- bool matchesTag(const std::string& tag);
-
- virtual ~LLNotification() {}
-};
-
-std::ostream& operator<<(std::ostream& s, const LLNotification& notification);
-
-namespace LLNotificationFilters
-{
- // a sample filter
- bool includeEverything(LLNotificationPtr p);
-
- typedef enum e_comparison
- {
- EQUAL,
- LESS,
- GREATER,
- LESS_EQUAL,
- GREATER_EQUAL
- } EComparison;
-
- // generic filter functor that takes method or member variable reference
- template<typename T>
- struct filterBy
- {
- typedef boost::function<T (LLNotificationPtr)> field_t;
- typedef typename boost::remove_reference<T>::type value_t;
-
- filterBy(field_t field, value_t value, EComparison comparison = EQUAL)
- : mField(field),
- mFilterValue(value),
- mComparison(comparison)
- {
- }
-
- bool operator()(LLNotificationPtr p)
- {
- switch(mComparison)
- {
- case EQUAL:
- return mField(p) == mFilterValue;
- case LESS:
- return mField(p) < mFilterValue;
- case GREATER:
- return mField(p) > mFilterValue;
- case LESS_EQUAL:
- return mField(p) <= mFilterValue;
- case GREATER_EQUAL:
- return mField(p) >= mFilterValue;
- default:
- return false;
- }
- }
-
- field_t mField;
- value_t mFilterValue;
- EComparison mComparison;
- };
-};
-
-namespace LLNotificationComparators
-{
- struct orderByUUID
- {
- bool operator()(LLNotificationPtr lhs, LLNotificationPtr rhs) const
- {
- return lhs->id() < rhs->id();
- }
- };
-};
-
-typedef boost::function<bool (LLNotificationPtr)> LLNotificationFilter;
-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 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).
-// The default hierarchy looks like this:
-//
-// LLNotifications --+-- Expiration --+-- Mute --+-- Ignore --+-- Visible --+-- History
-// +-- Alerts
-// +-- Notifications
-//
-// In general, new channels that want to only see notifications that pass through
-// all of the built-in tests should attach to the "Visible" channel
-//
-class LLNotificationChannelBase :
- public LLEventTrackable,
- public LLRefCount
-{
- LOG_CLASS(LLNotificationChannelBase);
-public:
- LLNotificationChannelBase(LLNotificationFilter filter)
- : mFilter(filter)
- , mItems()
- , mItemsMutex()
- {}
-
- virtual ~LLNotificationChannelBase()
- {
- // explicit cleanup for easier issue detection
- mChanged.disconnect_all_slots();
- mPassedFilter.disconnect_all_slots();
- mFailedFilter.disconnect_all_slots();
- LLMutexLock lock(&mItemsMutex);
- mItems.clear();
- }
- // you can also connect to a Channel, so you can be notified of
- // changes to this channel
- LLBoundListener connectChanged(const LLEventListener& slot)
- {
- // Call this->connectChangedImpl() to actually connect it.
- return connectChangedImpl(slot);
- }
- LLBoundListener connectAtFrontChanged(const LLEventListener& slot)
- {
- return connectAtFrontChangedImpl(slot);
- }
- LLBoundListener connectPassedFilter(const LLEventListener& slot)
- {
- // see comments in connectChanged()
- return connectPassedFilterImpl(slot);
- }
- LLBoundListener connectFailedFilter(const LLEventListener& slot)
- {
- // see comments in connectChanged()
- return connectFailedFilterImpl(slot);
- }
-
- // use this when items change or to add a new one
- bool updateItem(const LLSD& payload);
- const LLNotificationFilter& getFilter() { return mFilter; }
-
-protected:
- LLBoundListener connectChangedImpl(const LLEventListener& slot);
- LLBoundListener connectAtFrontChangedImpl(const LLEventListener& slot);
- LLBoundListener connectPassedFilterImpl(const LLEventListener& slot);
- LLBoundListener connectFailedFilterImpl(const LLEventListener& slot);
-
- LLNotificationSet mItems;
- LLStandardSignal mChanged;
- LLStandardSignal mPassedFilter;
- LLStandardSignal mFailedFilter;
- LLMutex mItemsMutex;
-
- // these are action methods that subclasses can override to take action
- // on specific types of changes; the management of the mItems list is
- // still handled by the generic handler.
- virtual void onLoad(LLNotificationPtr p) {}
- virtual void onAdd(LLNotificationPtr p) {}
- 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;
-};
-
-// The type of the pointers that we're going to manage in the NotificationQueue system
-// Because LLNotifications is a singleton, we don't actually expect to ever
-// 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::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 LLPointer to manage it.
-//
-class LLNotificationChannel :
- boost::noncopyable,
- 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; }
- typedef std::vector<std::string>::const_iterator parents_iter;
- boost::iterator_range<parents_iter> getParents() const
- {
- return boost::iterator_range<parents_iter>(mParents);
- }
-
- bool isEmpty() const;
- S32 size() const;
- size_t size();
-
- typedef boost::function<void(LLNotificationPtr)> NotificationProcess;
- void forEachNotification(NotificationProcess process);
-
- std::string summarize();
-
-protected:
- void connectToChannel(const std::string& channel_name);
-
-private:
- std::string mName;
- std::vector<std::string> mParents;
- std::vector<LLBoundListener> mListeners;
-};
-
-// An interface class to provide a clean linker seam to the LLNotifications class.
-// Extend this interface as needed for your use of LLNotifications.
-class LLNotificationsInterface
-{
-public:
- virtual LLNotificationPtr add(const std::string& name,
- const LLSD& substitutions,
- const LLSD& payload,
- LLNotificationFunctorRegistry::ResponseFunctor functor) = 0;
-};
-
-class LLNotifications :
- public LLNotificationsInterface,
- public LLSingleton<LLNotifications>,
- public LLNotificationChannelBase
-{
- LLSINGLETON(LLNotifications);
- LOG_CLASS(LLNotifications);
- virtual ~LLNotifications() {}
-
-public:
-
- // 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();
-
- // Add a simple notification (from XUI)
- void addFromCallback(const LLSD& name);
-
- // *NOTE: To add simple notifications, #include "llnotificationsutil.h"
- // and use LLNotificationsUtil::add("MyNote") or add("MyNote", args)
- LLNotificationPtr add(const std::string& name,
- const LLSD& substitutions,
- const LLSD& payload);
- LLNotificationPtr add(const std::string& name,
- const LLSD& substitutions,
- const LLSD& payload,
- const std::string& functor_name);
- /* virtual */ LLNotificationPtr add(const std::string& name,
- const LLSD& substitutions,
- const LLSD& payload,
- LLNotificationFunctorRegistry::ResponseFunctor functor) override;
- LLNotificationPtr add(const LLNotification::Params& p);
-
- void add(const LLNotificationPtr pNotif);
- void load(const LLNotificationPtr pNotif);
- void cancel(LLNotificationPtr pNotif);
- void cancelByName(const std::string& name);
- void cancelByOwner(const LLUUID ownerId);
- void update(const LLNotificationPtr pNotif);
-
- LLNotificationPtr find(LLUUID uuid);
-
- // This is all stuff for managing the templates
- // take your template out
- LLNotificationTemplatePtr getTemplate(const std::string& name);
-
- // get the whole collection
- typedef std::vector<std::string> TemplateNames;
- TemplateNames getTemplateNames() const; // returns a list of notification names
-
- typedef std::map<std::string, LLNotificationTemplatePtr> TemplateMap;
-
- TemplateMap::const_iterator templatesBegin() { return mTemplates.begin(); }
- TemplateMap::const_iterator templatesEnd() { return mTemplates.end(); }
-
- // test for existence
- bool templateExists(const std::string& name);
-
- typedef std::list<LLNotificationVisibilityRulePtr> VisibilityRuleList;
-
- void forceResponse(const LLNotification::Params& params, S32 option);
-
- void createDefaultChannels();
-
- LLNotificationChannelPtr getChannel(const std::string& channelName);
-
- std::string getGlobalString(const std::string& key) const;
-
- void setIgnoreAllNotifications(bool ignore);
- bool getIgnoreAllNotifications();
-
- void setIgnored(const std::string& name, bool ignored);
- bool getIgnored(const std::string& name);
-
- bool isVisibleByRules(LLNotificationPtr pNotification);
-
-private:
- /*virtual*/ void initSingleton() override;
- /*virtual*/ void cleanupSingleton() override;
-
- void loadPersistentNotifications();
-
- bool expirationFilter(LLNotificationPtr pNotification);
- bool expirationHandler(const LLSD& payload);
- bool uniqueFilter(LLNotificationPtr pNotification);
- bool uniqueHandler(const LLSD& payload);
- bool failedUniquenessTest(const LLSD& payload);
- LLNotificationChannelPtr pHistoryChannel;
- LLNotificationChannelPtr pExpirationChannel;
-
- TemplateMap mTemplates;
-
- VisibilityRuleList mVisibilityRules;
-
- std::string mFileName;
-
- LLNotificationMap mUniqueNotifications;
-
- typedef std::map<std::string, std::string> GlobalStringMap;
- GlobalStringMap mGlobalStrings;
-
- bool mIgnoreAllNotifications;
-
- std::unique_ptr<LLNotificationsListener> mListener;
-
- std::vector<LLNotificationChannelPtr> mDefaultChannels;
-};
-
-/**
- * Abstract class for postponed notifications.
- * Provides possibility to add notification after specified by id avatar or group will be
- * received from cache name. The object of this type automatically well be deleted
- * by cleanup method after respond will be received from cache name.
- *
- * To add custom postponed notification to the notification system client should:
- * 1 create class derived from LLPostponedNotification;
- * 2 call LLPostponedNotification::add method;
- */
-class LLPostponedNotification : public LLMortician
-{
-public:
- /**
- * Performs hooking cache name callback which will add notification to notifications system.
- * Type of added notification should be specified by template parameter T
- * and non-private derived from LLPostponedNotification class,
- * otherwise compilation error will occur.
- */
- template<class T>
- static void add(const LLNotification::Params& params,
- const LLUUID& id, bool is_group)
- {
- // upcast T to the base type to restrict T derivation from LLPostponedNotification
- LLPostponedNotification* thiz = new T();
- thiz->mParams = params;
-
- // Avoid header file dependency on llcachename.h
- thiz->lookupName(id, is_group);
- }
-
-private:
- 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()
- {
- die();
- }
-
-protected:
- LLPostponedNotification()
- : mParams(),
- mName(),
- mAvatarNameCacheConnection()
- {}
-
- virtual ~LLPostponedNotification()
- {
- if (mAvatarNameCacheConnection.connected())
- {
- mAvatarNameCacheConnection.disconnect();
- }
- }
-
- /**
- * Abstract method provides possibility to modify notification parameters and
- * will be called after cache name retrieve information about avatar or group
- * and before notification will be added to the notification system.
- */
- virtual void modifyNotificationParams() = 0;
-
- LLNotification::Params mParams;
- std::string mName;
- 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)
- {}
-
- virtual ~LLPersistentNotificationChannel()
- {
- mHistory.clear();
- }
-
- 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);
- }
-
- void onLoad(LLNotificationPtr p)
- {
- mHistory.push_back(p);
- }
-
- std::vector<LLNotificationPtr> mHistory;
-};
-
-#endif//LL_LLNOTIFICATIONS_H
-
+/**
+* @file llnotifications.h
+* @brief Non-UI manager and support for keeping a prioritized list of notifications
+* @author Q (with assistance from Richard and Coco)
+*
+* $LicenseInfo:firstyear=2008&license=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_LLNOTIFICATIONS_H
+#define LL_LLNOTIFICATIONS_H
+
+/**
+ * This system is intended to provide a singleton mechanism for adding
+ * notifications to one of an arbitrary set of event channels.
+ *
+ * Controlling JIRA: DEV-9061
+ *
+ * Every notification has (see code for full list):
+ * - a textual name, which is used to look up its template in the XML files
+ * - a payload, which is a block of LLSD
+ * - a channel, which is normally extracted from the XML files but
+ * can be overridden.
+ * - a timestamp, used to order the notifications
+ * - expiration time -- if nonzero, specifies a time after which the
+ * notification will no longer be valid.
+ * - a callback name and a couple of status bits related to callbacks (see below)
+ *
+ * There is a management class called LLNotifications, which is an LLSingleton.
+ * The class maintains a collection of all of the notifications received
+ * or processed during this session, and also manages the persistence
+ * of those notifications that must be persisted.
+ *
+ * We also have Channels. A channel is a view on a collection of notifications;
+ * The collection is defined by a filter function that controls which
+ * notifications are in the channel, and its ordering is controlled by
+ * a comparator.
+ *
+ * There is a hierarchy of channels; notifications flow down from
+ * the management class (LLNotifications, which itself inherits from
+ * The channel base class) to the individual channels.
+ * Any change to notifications (add, delete, modify) is
+ * automatically propagated through the channel hierarchy.
+ *
+ * We provide methods for adding a new notification, for removing
+ * one, and for managing channels. Channels are relatively cheap to construct
+ * and maintain, so in general, human interfaces should use channels to
+ * select and manage their lists of notifications.
+ *
+ * We also maintain a collection of templates that are loaded from the
+ * XML file of template translations. The system supports substitution
+ * of named variables from the payload into the XML file.
+ *
+ * By default, only the "unknown message" template is built into the system.
+ * It is not an error to add a notification that's not found in the
+ * template system, but it is logged.
+ *
+ */
+
+#include <string>
+#include <list>
+#include <vector>
+#include <map>
+#include <set>
+#include <iomanip>
+#include <sstream>
+
+#include <boost/utility.hpp>
+#include <boost/type_traits.hpp>
+#include <boost/signals2.hpp>
+#include <boost/range.hpp>
+
+#include "llevents.h"
+#include "llfunctorregistry.h"
+#include "llinitparam.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
+{
+ NOTIFICATION_PRIORITY_UNSPECIFIED,
+ NOTIFICATION_PRIORITY_LOW,
+ NOTIFICATION_PRIORITY_NORMAL,
+ NOTIFICATION_PRIORITY_HIGH,
+ NOTIFICATION_PRIORITY_CRITICAL
+} ENotificationPriority;
+
+struct NotificationPriorityValues : public LLInitParam::TypeValuesHelper<ENotificationPriority, NotificationPriorityValues>
+{
+ static void declareValues();
+};
+
+class LLNotificationResponderInterface
+{
+public:
+ LLNotificationResponderInterface(){};
+ virtual ~LLNotificationResponderInterface(){};
+
+ virtual void handleRespond(const LLSD& notification, const LLSD& response) = 0;
+
+ virtual LLSD asLLSD() = 0;
+
+ virtual void fromLLSD(const LLSD& params) = 0;
+};
+
+typedef boost::function<void (const LLSD&, const LLSD&)> LLNotificationResponder;
+
+typedef std::shared_ptr<LLNotificationResponderInterface> LLNotificationResponderPtr;
+
+typedef LLFunctorRegistry<LLNotificationResponder> LLNotificationFunctorRegistry;
+typedef LLFunctorRegistration<LLNotificationResponder> LLNotificationFunctorRegistration;
+
+// context data that can be looked up via a notification's payload by the display logic
+// derive from this class to implement specific contexts
+class LLNotificationContext : public LLInstanceTracker<LLNotificationContext, LLUUID>
+{
+public:
+
+ LLNotificationContext() : LLInstanceTracker<LLNotificationContext, LLUUID>(LLUUID::generateNewID())
+ {
+ }
+
+ virtual ~LLNotificationContext() {}
+
+ LLSD asLLSD() const
+ {
+ return getKey();
+ }
+
+private:
+
+};
+
+// Contains notification form data, such as buttons and text fields along with
+// manipulator functions
+class LLNotificationForm
+{
+ LOG_CLASS(LLNotificationForm);
+
+public:
+ struct FormElementBase : public LLInitParam::Block<FormElementBase>
+ {
+ Optional<std::string> name;
+ Optional<bool> enabled;
+
+ FormElementBase();
+ };
+
+ struct FormIgnore : public LLInitParam::Block<FormIgnore, FormElementBase>
+ {
+ Optional<std::string> text;
+ Optional<bool> save_option;
+ Optional<std::string> control;
+ Optional<bool> invert_control;
+ Optional<bool> session_only;
+ Optional<bool> checkbox_only;
+
+ FormIgnore();
+ };
+
+ struct FormButton : public LLInitParam::Block<FormButton, FormElementBase>
+ {
+ Mandatory<S32> index;
+ Mandatory<std::string> text;
+ Optional<std::string> ignore;
+ Optional<bool> is_default;
+ Optional<S32> width;
+
+ Mandatory<std::string> type;
+
+ FormButton();
+ };
+
+ struct FormInput : public LLInitParam::Block<FormInput, FormElementBase>
+ {
+ Mandatory<std::string> type;
+ Optional<S32> width;
+ Optional<S32> max_length_chars;
+ Optional<bool> allow_emoji;
+ Optional<std::string> text;
+
+ Optional<std::string> value;
+ FormInput();
+ };
+
+ struct FormElement : public LLInitParam::ChoiceBlock<FormElement>
+ {
+ Alternative<FormButton> button;
+ Alternative<FormInput> input;
+
+ FormElement();
+ };
+
+ struct FormElements : public LLInitParam::Block<FormElements>
+ {
+ Multiple<FormElement> elements;
+ FormElements();
+ };
+
+ struct Params : public LLInitParam::Block<Params>
+ {
+ Optional<std::string> name;
+ Optional<FormIgnore> ignore;
+ Optional<FormElements> form_elements;
+
+ Params();
+ };
+
+ typedef enum e_ignore_type
+ {
+ IGNORE_CHECKBOX_ONLY = -1, // ignore won't be handled, will set value/checkbox only
+ IGNORE_NO = 0,
+ IGNORE_WITH_DEFAULT_RESPONSE,
+ IGNORE_WITH_DEFAULT_RESPONSE_SESSION_ONLY,
+ IGNORE_WITH_LAST_RESPONSE,
+ IGNORE_SHOW_AGAIN
+ } 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);
+ 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);
+ std::string getDefaultOption();
+ LLPointer<class LLControlVariable> getIgnoreSetting();
+ bool getIgnored();
+ void setIgnored(bool ignored);
+
+ EIgnoreType getIgnoreType() { return mIgnore; }
+ std::string getIgnoreMessage() { return mIgnoreMsg; }
+
+private:
+ LLSD mFormData;
+ EIgnoreType mIgnore;
+ std::string mIgnoreMsg;
+ LLPointer<class LLControlVariable> mIgnoreSetting;
+ bool mInvertSetting;
+};
+
+typedef std::shared_ptr<LLNotificationForm> LLNotificationFormPtr;
+
+
+struct LLNotificationTemplate;
+
+// we want to keep a map of these by name, and it's best to manage them
+// with smart pointers
+typedef std::shared_ptr<LLNotificationTemplate> LLNotificationTemplatePtr;
+
+
+struct LLNotificationVisibilityRule;
+
+typedef std::shared_ptr<LLNotificationVisibilityRule> LLNotificationVisibilityRulePtr;
+
+/**
+ * @class LLNotification
+ * @brief The object that expresses the details of a notification
+ *
+ * We make this noncopyable because
+ * we want to manage these through LLNotificationPtr, and only
+ * ever create one instance of any given notification.
+ *
+ * The enable_shared_from_this flag ensures that if we construct
+ * a smart pointer from a notification, we'll always get the same
+ * shared pointer.
+ */
+class LLNotification :
+ boost::noncopyable,
+ public std::enable_shared_from_this<LLNotification>
+{
+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<LLUUID> id;
+ Optional<LLSD> substitutions,
+ form_elements,
+ payload;
+ Optional<ENotificationPriority, NotificationPriorityValues> priority;
+ Optional<LLDate> time_stamp,
+ expiry;
+ Optional<LLNotificationContext*> context;
+ Optional<void*> responder;
+ Optional<bool> offer_from_agent;
+ Optional<bool> is_dnd;
+
+ struct Functor : public LLInitParam::ChoiceBlock<Functor>
+ {
+ Alternative<std::string> name;
+ Alternative<LLNotificationFunctorRegistry::ResponseFunctor> function;
+ Alternative<LLNotificationResponderPtr> responder;
+ Alternative<LLSD> responder_sd;
+
+ Functor()
+ : name("responseFunctor"),
+ function("functor"),
+ responder("responder"),
+ responder_sd("responder_sd")
+ {}
+ };
+ Optional<Functor> functor;
+
+ Params()
+ : name("name"),
+ id("id"),
+ priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED),
+ time_stamp("time"),
+ payload("payload"),
+ 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;
+ }
+
+ Params(const std::string& _name)
+ : name("name"),
+ priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED),
+ time_stamp("time"),
+ payload("payload"),
+ form_elements("form"),
+ substitutions("substitutions"),
+ expiry("expiry"),
+ offer_from_agent("offer_from_agent", false),
+ is_dnd("is_dnd", false)
+ {
+ functor.name = _name;
+ name = _name;
+ time_stamp = LLDate::now();
+ responder = NULL;
+ }
+ };
+
+ LLNotificationResponderPtr getResponderPtr() { return mResponder; }
+
+private:
+
+ const LLUUID mId;
+ LLSD mPayload;
+ LLSD mSubstitutions;
+ LLDate mTimestamp;
+ LLDate mExpiresAt;
+ bool mCancelled;
+ bool mRespondedTo; // once the notification has been responded to, this becomes true
+ LLSD mResponse;
+ bool mIgnored;
+ ENotificationPriority mPriority;
+ LLNotificationFormPtr mForm;
+ void* mResponderObj; // TODO - refactor/remove this field
+ LLNotificationResponderPtr mResponder;
+ bool mOfferFromAgent;
+ bool mIsDND;
+
+ // a reference to the template
+ LLNotificationTemplatePtr mTemplatep;
+
+ /*
+ We want to be able to store and reload notifications so that they can survive
+ a shutdown/restart of the client. So we can't simply pass in callbacks;
+ we have to specify a callback mechanism that can be used by name rather than
+ by some arbitrary pointer -- and then people have to initialize callbacks
+ in some useful location. So we use LLNotificationFunctorRegistry to manage them.
+ */
+ std::string mResponseFunctorName;
+
+ /*
+ In cases where we want to specify an explict, non-persisted callback,
+ we store that in the callback registry under a dynamically generated
+ key, and store the key in the notification, so we can still look it up
+ using the same mechanism.
+ */
+ bool mTemporaryResponder;
+
+ // keep track of other notifications combined with COMBINE_WITH_NEW
+ std::vector<LLNotificationPtr> mCombinedNotifications;
+
+ void init(const std::string& template_name, const LLSD& form_elements);
+
+ void cancel();
+
+public:
+ LLNotification(const LLSDParamAdapter<Params>& p);
+
+ void setResponseFunctor(std::string const &responseFunctorName);
+
+ void setResponseFunctor(const LLNotificationFunctorRegistry::ResponseFunctor& cb);
+
+ void setResponseFunctor(const LLNotificationResponderPtr& responder);
+
+ typedef enum e_response_template_type
+ {
+ WITHOUT_DEFAULT_BUTTON,
+ WITH_DEFAULT_BUTTON
+ } EResponseTemplateType;
+
+ // return response LLSD filled in with default form contents and (optionally) the default button selected
+ LLSD getResponseTemplate(EResponseTemplateType type = WITHOUT_DEFAULT_BUTTON);
+
+ // returns index of first button with value==true
+ // usually this the button the user clicked on
+ // returns -1 if no button clicked (e.g. form has not been displayed)
+ static S32 getSelectedOption(const LLSD& notification, const LLSD& response);
+ // returns name of first button with value==true
+ static std::string getSelectedOptionName(const LLSD& notification);
+
+ // after someone responds to a notification (usually by clicking a button,
+ // but sometimes by filling out a little form and THEN clicking a button),
+ // the result of the response (the name and value of the button clicked,
+ // plus any other data) should be packaged up as LLSD, then passed as a
+ // parameter to the notification's respond() method here. This will look up
+ // and call the appropriate responder.
+ //
+ // response is notification serialized as LLSD:
+ // ["name"] = notification name
+ // ["form"] = LLSD tree that includes form description and any prefilled form data
+ // ["response"] = form data filled in by user
+ // (including, but not limited to which button they clicked on)
+ // ["payload"] = transaction specific data, such as ["source_id"] (originator of notification),
+ // ["item_id"] (attached inventory item), etc.
+ // ["substitutions"] = string substitutions used to generate notification message
+ // from the template
+ // ["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(bool excludeTemplateElements = false);
+
+ const LLNotificationFormPtr getForm();
+ void updateForm(const LLNotificationFormPtr& form);
+
+ void repost();
+
+ void respond(const LLSD& sd);
+ void respondWithDefault();
+
+ void* getResponder() { return mResponderObj; }
+
+ void setResponder(void* responder) { mResponderObj = responder; }
+
+ void setIgnored(bool ignore);
+
+ bool isCancelled() const
+ {
+ return mCancelled;
+ }
+
+ bool isRespondedTo() const
+ {
+ return mRespondedTo;
+ }
+
+ bool isActive() const
+ {
+ return !isRespondedTo()
+ && !isCancelled()
+ && !isExpired();
+ }
+
+ const LLSD& getResponse() { return mResponse; }
+
+ bool isIgnored() const
+ {
+ return mIgnored;
+ }
+
+ const std::string& getName() const;
+
+ const std::string& getIcon() const;
+
+ bool isPersistent() const;
+
+ const LLUUID& id() const
+ {
+ return mId;
+ }
+
+ const LLSD& getPayload() const
+ {
+ return mPayload;
+ }
+
+ const LLSD& getSubstitutions() const
+ {
+ return mSubstitutions;
+ }
+
+ const LLDate& getDate() const
+ {
+ 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; //for url responce option
+ bool getForceUrlsExternal() const;
+ bool canLogToChat() const;
+ bool canLogToIM() const;
+ bool canShowToast() const;
+ bool canFadeToast() const;
+ bool hasFormElements() const;
+ void playSound();
+
+ typedef enum e_combine_behavior
+ {
+ REPLACE_WITH_NEW,
+ COMBINE_WITH_NEW,
+ KEEP_OLD,
+ CANCEL_OLD
+
+ } ECombineBehavior;
+
+ ECombineBehavior getCombineBehavior() const;
+
+ const LLDate getExpiration() const
+ {
+ return mExpiresAt;
+ }
+
+ ENotificationPriority getPriority() const
+ {
+ return mPriority;
+ }
+
+ const LLUUID getID() const
+ {
+ return mId;
+ }
+
+ // comparing two notifications normally means comparing them by UUID (so we can look them
+ // up quickly this way)
+ bool operator<(const LLNotification& rhs) const
+ {
+ return mId < rhs.mId;
+ }
+
+ bool operator==(const LLNotification& rhs) const
+ {
+ return mId == rhs.mId;
+ }
+
+ bool operator!=(const LLNotification& rhs) const
+ {
+ return !operator==(rhs);
+ }
+
+ bool isSameObjectAs(const LLNotification* rhs) const
+ {
+ return this == rhs;
+ }
+
+ // this object has been updated, so tell all our clients
+ void update();
+
+ void updateFrom(LLNotificationPtr other);
+
+ // A fuzzy equals comparator.
+ // true only if both notifications have the same template and
+ // 1) flagged as unique (there can be only one of these) OR
+ // 2) all required payload fields of each also exist in the other.
+ bool isEquivalentTo(LLNotificationPtr that) const;
+
+ // if the current time is greater than the expiration, the notification is expired
+ bool isExpired() const
+ {
+ if (mExpiresAt.secondsSinceEpoch() == 0)
+ {
+ return false;
+ }
+
+ LLDate rightnow = LLDate::now();
+ return rightnow > mExpiresAt;
+ }
+
+ std::string summarize() const;
+
+ bool hasUniquenessConstraints() const;
+
+ bool matchesTag(const std::string& tag);
+
+ virtual ~LLNotification() {}
+};
+
+std::ostream& operator<<(std::ostream& s, const LLNotification& notification);
+
+namespace LLNotificationFilters
+{
+ // a sample filter
+ bool includeEverything(LLNotificationPtr p);
+
+ typedef enum e_comparison
+ {
+ EQUAL,
+ LESS,
+ GREATER,
+ LESS_EQUAL,
+ GREATER_EQUAL
+ } EComparison;
+
+ // generic filter functor that takes method or member variable reference
+ template<typename T>
+ struct filterBy
+ {
+ typedef boost::function<T (LLNotificationPtr)> field_t;
+ typedef typename boost::remove_reference<T>::type value_t;
+
+ filterBy(field_t field, value_t value, EComparison comparison = EQUAL)
+ : mField(field),
+ mFilterValue(value),
+ mComparison(comparison)
+ {
+ }
+
+ bool operator()(LLNotificationPtr p)
+ {
+ switch(mComparison)
+ {
+ case EQUAL:
+ return mField(p) == mFilterValue;
+ case LESS:
+ return mField(p) < mFilterValue;
+ case GREATER:
+ return mField(p) > mFilterValue;
+ case LESS_EQUAL:
+ return mField(p) <= mFilterValue;
+ case GREATER_EQUAL:
+ return mField(p) >= mFilterValue;
+ default:
+ return false;
+ }
+ }
+
+ field_t mField;
+ value_t mFilterValue;
+ EComparison mComparison;
+ };
+};
+
+namespace LLNotificationComparators
+{
+ struct orderByUUID
+ {
+ bool operator()(LLNotificationPtr lhs, LLNotificationPtr rhs) const
+ {
+ return lhs->id() < rhs->id();
+ }
+ };
+};
+
+typedef boost::function<bool (LLNotificationPtr)> LLNotificationFilter;
+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 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).
+// The default hierarchy looks like this:
+//
+// LLNotifications --+-- Expiration --+-- Mute --+-- Ignore --+-- Visible --+-- History
+// +-- Alerts
+// +-- Notifications
+//
+// In general, new channels that want to only see notifications that pass through
+// all of the built-in tests should attach to the "Visible" channel
+//
+class LLNotificationChannelBase :
+ public LLEventTrackable,
+ public LLRefCount
+{
+ LOG_CLASS(LLNotificationChannelBase);
+public:
+ LLNotificationChannelBase(LLNotificationFilter filter)
+ : mFilter(filter)
+ , mItems()
+ , mItemsMutex()
+ {}
+
+ virtual ~LLNotificationChannelBase()
+ {
+ // explicit cleanup for easier issue detection
+ mChanged.disconnect_all_slots();
+ mPassedFilter.disconnect_all_slots();
+ mFailedFilter.disconnect_all_slots();
+ LLMutexLock lock(&mItemsMutex);
+ mItems.clear();
+ }
+ // you can also connect to a Channel, so you can be notified of
+ // changes to this channel
+ LLBoundListener connectChanged(const LLEventListener& slot)
+ {
+ // Call this->connectChangedImpl() to actually connect it.
+ return connectChangedImpl(slot);
+ }
+ LLBoundListener connectAtFrontChanged(const LLEventListener& slot)
+ {
+ return connectAtFrontChangedImpl(slot);
+ }
+ LLBoundListener connectPassedFilter(const LLEventListener& slot)
+ {
+ // see comments in connectChanged()
+ return connectPassedFilterImpl(slot);
+ }
+ LLBoundListener connectFailedFilter(const LLEventListener& slot)
+ {
+ // see comments in connectChanged()
+ return connectFailedFilterImpl(slot);
+ }
+
+ // use this when items change or to add a new one
+ bool updateItem(const LLSD& payload);
+ const LLNotificationFilter& getFilter() { return mFilter; }
+
+protected:
+ LLBoundListener connectChangedImpl(const LLEventListener& slot);
+ LLBoundListener connectAtFrontChangedImpl(const LLEventListener& slot);
+ LLBoundListener connectPassedFilterImpl(const LLEventListener& slot);
+ LLBoundListener connectFailedFilterImpl(const LLEventListener& slot);
+
+ LLNotificationSet mItems;
+ LLStandardSignal mChanged;
+ LLStandardSignal mPassedFilter;
+ LLStandardSignal mFailedFilter;
+ LLMutex mItemsMutex;
+
+ // these are action methods that subclasses can override to take action
+ // on specific types of changes; the management of the mItems list is
+ // still handled by the generic handler.
+ virtual void onLoad(LLNotificationPtr p) {}
+ virtual void onAdd(LLNotificationPtr p) {}
+ 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;
+};
+
+// The type of the pointers that we're going to manage in the NotificationQueue system
+// Because LLNotifications is a singleton, we don't actually expect to ever
+// 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::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 LLPointer to manage it.
+//
+class LLNotificationChannel :
+ boost::noncopyable,
+ 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; }
+ typedef std::vector<std::string>::const_iterator parents_iter;
+ boost::iterator_range<parents_iter> getParents() const
+ {
+ return boost::iterator_range<parents_iter>(mParents);
+ }
+
+ bool isEmpty() const;
+ S32 size() const;
+ size_t size();
+
+ typedef boost::function<void(LLNotificationPtr)> NotificationProcess;
+ void forEachNotification(NotificationProcess process);
+
+ std::string summarize();
+
+protected:
+ void connectToChannel(const std::string& channel_name);
+
+private:
+ std::string mName;
+ std::vector<std::string> mParents;
+ std::vector<LLBoundListener> mListeners;
+};
+
+// An interface class to provide a clean linker seam to the LLNotifications class.
+// Extend this interface as needed for your use of LLNotifications.
+class LLNotificationsInterface
+{
+public:
+ virtual LLNotificationPtr add(const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload,
+ LLNotificationFunctorRegistry::ResponseFunctor functor) = 0;
+};
+
+class LLNotifications :
+ public LLNotificationsInterface,
+ public LLSingleton<LLNotifications>,
+ public LLNotificationChannelBase
+{
+ LLSINGLETON(LLNotifications);
+ LOG_CLASS(LLNotifications);
+ virtual ~LLNotifications() {}
+
+public:
+
+ // 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();
+
+ // Add a simple notification (from XUI)
+ void addFromCallback(const LLSD& name);
+
+ // *NOTE: To add simple notifications, #include "llnotificationsutil.h"
+ // and use LLNotificationsUtil::add("MyNote") or add("MyNote", args)
+ LLNotificationPtr add(const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload);
+ LLNotificationPtr add(const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload,
+ const std::string& functor_name);
+ /* virtual */ LLNotificationPtr add(const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload,
+ LLNotificationFunctorRegistry::ResponseFunctor functor) override;
+ LLNotificationPtr add(const LLNotification::Params& p);
+
+ void add(const LLNotificationPtr pNotif);
+ void load(const LLNotificationPtr pNotif);
+ void cancel(LLNotificationPtr pNotif);
+ void cancelByName(const std::string& name);
+ void cancelByOwner(const LLUUID ownerId);
+ void update(const LLNotificationPtr pNotif);
+
+ LLNotificationPtr find(LLUUID uuid);
+
+ // This is all stuff for managing the templates
+ // take your template out
+ LLNotificationTemplatePtr getTemplate(const std::string& name);
+
+ // get the whole collection
+ typedef std::vector<std::string> TemplateNames;
+ TemplateNames getTemplateNames() const; // returns a list of notification names
+
+ typedef std::map<std::string, LLNotificationTemplatePtr> TemplateMap;
+
+ TemplateMap::const_iterator templatesBegin() { return mTemplates.begin(); }
+ TemplateMap::const_iterator templatesEnd() { return mTemplates.end(); }
+
+ // test for existence
+ bool templateExists(const std::string& name);
+
+ typedef std::list<LLNotificationVisibilityRulePtr> VisibilityRuleList;
+
+ void forceResponse(const LLNotification::Params& params, S32 option);
+
+ void createDefaultChannels();
+
+ LLNotificationChannelPtr getChannel(const std::string& channelName);
+
+ std::string getGlobalString(const std::string& key) const;
+
+ void setIgnoreAllNotifications(bool ignore);
+ bool getIgnoreAllNotifications();
+
+ void setIgnored(const std::string& name, bool ignored);
+ bool getIgnored(const std::string& name);
+
+ bool isVisibleByRules(LLNotificationPtr pNotification);
+
+private:
+ /*virtual*/ void initSingleton() override;
+ /*virtual*/ void cleanupSingleton() override;
+
+ void loadPersistentNotifications();
+
+ bool expirationFilter(LLNotificationPtr pNotification);
+ bool expirationHandler(const LLSD& payload);
+ bool uniqueFilter(LLNotificationPtr pNotification);
+ bool uniqueHandler(const LLSD& payload);
+ bool failedUniquenessTest(const LLSD& payload);
+ LLNotificationChannelPtr pHistoryChannel;
+ LLNotificationChannelPtr pExpirationChannel;
+
+ TemplateMap mTemplates;
+
+ VisibilityRuleList mVisibilityRules;
+
+ std::string mFileName;
+
+ LLNotificationMap mUniqueNotifications;
+
+ typedef std::map<std::string, std::string> GlobalStringMap;
+ GlobalStringMap mGlobalStrings;
+
+ bool mIgnoreAllNotifications;
+
+ std::unique_ptr<LLNotificationsListener> mListener;
+
+ std::vector<LLNotificationChannelPtr> mDefaultChannels;
+};
+
+/**
+ * Abstract class for postponed notifications.
+ * Provides possibility to add notification after specified by id avatar or group will be
+ * received from cache name. The object of this type automatically well be deleted
+ * by cleanup method after respond will be received from cache name.
+ *
+ * To add custom postponed notification to the notification system client should:
+ * 1 create class derived from LLPostponedNotification;
+ * 2 call LLPostponedNotification::add method;
+ */
+class LLPostponedNotification : public LLMortician
+{
+public:
+ /**
+ * Performs hooking cache name callback which will add notification to notifications system.
+ * Type of added notification should be specified by template parameter T
+ * and non-private derived from LLPostponedNotification class,
+ * otherwise compilation error will occur.
+ */
+ template<class T>
+ static void add(const LLNotification::Params& params,
+ const LLUUID& id, bool is_group)
+ {
+ // upcast T to the base type to restrict T derivation from LLPostponedNotification
+ LLPostponedNotification* thiz = new T();
+ thiz->mParams = params;
+
+ // Avoid header file dependency on llcachename.h
+ thiz->lookupName(id, is_group);
+ }
+
+private:
+ 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()
+ {
+ die();
+ }
+
+protected:
+ LLPostponedNotification()
+ : mParams(),
+ mName(),
+ mAvatarNameCacheConnection()
+ {}
+
+ virtual ~LLPostponedNotification()
+ {
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ }
+
+ /**
+ * Abstract method provides possibility to modify notification parameters and
+ * will be called after cache name retrieve information about avatar or group
+ * and before notification will be added to the notification system.
+ */
+ virtual void modifyNotificationParams() = 0;
+
+ LLNotification::Params mParams;
+ std::string mName;
+ 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)
+ {}
+
+ virtual ~LLPersistentNotificationChannel()
+ {
+ mHistory.clear();
+ }
+
+ 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);
+ }
+
+ void onLoad(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 859222f907..ace9e37e25 100644
--- a/indra/llui/llnotificationslistener.cpp
+++ b/indra/llui/llnotificationslistener.cpp
@@ -3,25 +3,25 @@
* @author Brad Kittenbrink
* @date 2009-07-08
* @brief Implementation for llnotificationslistener.
- *
+ *
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -87,39 +87,39 @@ LLNotificationsListener::~LLNotificationsListener()
void LLNotificationsListener::requestAdd(const LLSD& event_data) const
{
- if(event_data.has("reply"))
- {
- LLSD payload(event_data["payload"]);
- // copy reqid, if provided, to link response with request
- payload["reqid"] = event_data["reqid"];
- mNotifications.add(event_data["name"],
- event_data["substitutions"],
- payload,
- boost::bind(&LLNotificationsListener::NotificationResponder,
- this,
- event_data["reply"].asString(),
- _1, _2
- )
- );
- }
- else
- {
- mNotifications.add(event_data["name"],
- event_data["substitutions"],
- event_data["payload"]);
- }
+ if(event_data.has("reply"))
+ {
+ LLSD payload(event_data["payload"]);
+ // copy reqid, if provided, to link response with request
+ payload["reqid"] = event_data["reqid"];
+ mNotifications.add(event_data["name"],
+ event_data["substitutions"],
+ payload,
+ boost::bind(&LLNotificationsListener::NotificationResponder,
+ this,
+ event_data["reply"].asString(),
+ _1, _2
+ )
+ );
+ }
+ else
+ {
+ mNotifications.add(event_data["name"],
+ event_data["substitutions"],
+ event_data["payload"]);
+ }
}
-void LLNotificationsListener::NotificationResponder(const std::string& reply_pump,
- const LLSD& notification,
- const LLSD& response) const
+void LLNotificationsListener::NotificationResponder(const std::string& reply_pump,
+ const LLSD& notification,
+ const LLSD& response) const
{
- LLSD response_event;
- response_event["notification"] = notification;
- response_event["response"] = response;
- // surface reqid at top level of response for request/response protocol
- response_event["reqid"] = notification["payload"]["reqid"];
- LLEventPumps::getInstance()->obtain(reply_pump).post(response_event);
+ LLSD response_event;
+ response_event["notification"] = notification;
+ response_event["response"] = response;
+ // surface reqid at top level of response for request/response protocol
+ response_event["reqid"] = notification["payload"]["reqid"];
+ LLEventPumps::getInstance()->obtain(reply_pump).post(response_event);
}
void LLNotificationsListener::listChannels(const LLSD& params) const
@@ -191,11 +191,11 @@ void LLNotificationsListener::ignore(const LLSD& params) const
if (params["name"].isDefined())
{
// ["name"] was passed: ignore just that notification
- LLNotificationTemplatePtr templatep = mNotifications.getTemplate(params["name"]);
- if (templatep)
- {
- templatep->mForm->setIgnored(ignore);
- }
+ LLNotificationTemplatePtr templatep = mNotifications.getTemplate(params["name"]);
+ if (templatep)
+ {
+ templatep->mForm->setIgnored(ignore);
+ }
}
else
{
diff --git a/indra/llui/llnotificationslistener.h b/indra/llui/llnotificationslistener.h
index 4bab377626..65993c9ff8 100644
--- a/indra/llui/llnotificationslistener.h
+++ b/indra/llui/llnotificationslistener.h
@@ -3,25 +3,25 @@
* @author Brad Kittenbrink
* @date 2009-07-08
* @brief Wrap subset of LLNotifications API in event API for test scripts.
- *
+ *
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -46,9 +46,9 @@ public:
private:
void requestAdd(LLSD const & event_data) const;
- void NotificationResponder(const std::string& replypump,
- const LLSD& notification,
- const LLSD& response) const;
+ void NotificationResponder(const std::string& replypump,
+ const LLSD& notification,
+ const LLSD& response) const;
void listChannels(const LLSD& params) const;
void listChannelNotifications(const LLSD& params) const;
@@ -62,7 +62,7 @@ private:
class Forwarder;
typedef std::map<std::string, std::shared_ptr<Forwarder> > ForwarderMap;
ForwarderMap mForwarders;
- LLNotifications & mNotifications;
+ LLNotifications & mNotifications;
};
#endif // LL_LLNOTIFICATIONSLISTENER_H
diff --git a/indra/llui/llnotificationsutil.cpp b/indra/llui/llnotificationsutil.cpp
index cc791c26d1..c1cad431c5 100644
--- a/indra/llui/llnotificationsutil.cpp
+++ b/indra/llui/llnotificationsutil.cpp
@@ -4,21 +4,21 @@
* $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$
*/
@@ -28,68 +28,68 @@
#include "llnotifications.h"
#include "llsd.h"
-#include "llxmlnode.h" // apparently needed to call LLNotifications::instance()
+#include "llxmlnode.h" // apparently needed to call LLNotifications::instance()
LLNotificationPtr LLNotificationsUtil::add(const std::string& name)
{
- LLNotification::Params::Functor functor_p;
- functor_p.name = name;
- return LLNotifications::instance().add(
- LLNotification::Params().name(name).substitutions(LLSD()).payload(LLSD()).functor(functor_p));
+ LLNotification::Params::Functor functor_p;
+ functor_p.name = name;
+ return LLNotifications::instance().add(
+ LLNotification::Params().name(name).substitutions(LLSD()).payload(LLSD()).functor(functor_p));
}
-LLNotificationPtr LLNotificationsUtil::add(const std::string& name,
- const LLSD& substitutions)
+LLNotificationPtr LLNotificationsUtil::add(const std::string& name,
+ const LLSD& substitutions)
{
- LLNotification::Params::Functor functor_p;
- functor_p.name = name;
- return LLNotifications::instance().add(
- LLNotification::Params().name(name).substitutions(substitutions).payload(LLSD()).functor(functor_p));
+ LLNotification::Params::Functor functor_p;
+ functor_p.name = name;
+ return LLNotifications::instance().add(
+ LLNotification::Params().name(name).substitutions(substitutions).payload(LLSD()).functor(functor_p));
}
-LLNotificationPtr LLNotificationsUtil::add(const std::string& name,
- const LLSD& substitutions,
- const LLSD& payload)
+LLNotificationPtr LLNotificationsUtil::add(const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload)
{
- LLNotification::Params::Functor functor_p;
- functor_p.name = name;
- return LLNotifications::instance().add(
- LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));
+ LLNotification::Params::Functor functor_p;
+ functor_p.name = name;
+ return LLNotifications::instance().add(
+ LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));
}
-LLNotificationPtr LLNotificationsUtil::add(const std::string& name,
- const LLSD& substitutions,
- const LLSD& payload,
- const std::string& functor_name)
+LLNotificationPtr LLNotificationsUtil::add(const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload,
+ const std::string& functor_name)
{
- LLNotification::Params::Functor functor_p;
- functor_p.name = functor_name;
- return LLNotifications::instance().add(
- LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));
+ LLNotification::Params::Functor functor_p;
+ functor_p.name = functor_name;
+ return LLNotifications::instance().add(
+ LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));
}
-LLNotificationPtr LLNotificationsUtil::add(const std::string& name,
- const LLSD& substitutions,
- const LLSD& payload,
- boost::function<void (const LLSD&, const LLSD&)> functor)
+LLNotificationPtr LLNotificationsUtil::add(const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload,
+ boost::function<void (const LLSD&, const LLSD&)> functor)
{
- LLNotification::Params::Functor functor_p;
- functor_p.function = functor;
- return LLNotifications::instance().add(
- LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));
+ LLNotification::Params::Functor functor_p;
+ functor_p.function = functor;
+ return LLNotifications::instance().add(
+ LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p));
}
S32 LLNotificationsUtil::getSelectedOption(const LLSD& notification, const LLSD& response)
{
- return LLNotification::getSelectedOption(notification, response);
+ return LLNotification::getSelectedOption(notification, response);
}
void LLNotificationsUtil::cancel(LLNotificationPtr pNotif)
{
- LLNotifications::instance().cancel(pNotif);
+ LLNotifications::instance().cancel(pNotif);
}
LLNotificationPtr LLNotificationsUtil::find(LLUUID uuid)
{
- return LLNotifications::instance().find(uuid);
+ return LLNotifications::instance().find(uuid);
}
diff --git a/indra/llui/llnotificationsutil.h b/indra/llui/llnotificationsutil.h
index 9f29087b4a..f21d93a50e 100644
--- a/indra/llui/llnotificationsutil.h
+++ b/indra/llui/llnotificationsutil.h
@@ -4,28 +4,28 @@
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LLNOTIFICATIONSUTIL_H
#define LLNOTIFICATIONSUTIL_H
-// The vast majority of clients of the notifications system just want to add
+// The vast majority of clients of the notifications system just want to add
// a notification to the screen, so define this lightweight public interface
// to avoid including the heavyweight llnotifications.h
@@ -38,30 +38,30 @@ class LLSD;
namespace LLNotificationsUtil
{
- LLNotificationPtr add(const std::string& name);
-
- LLNotificationPtr add(const std::string& name,
- const LLSD& substitutions);
-
- LLNotificationPtr add(const std::string& name,
- const LLSD& substitutions,
- const LLSD& payload);
-
- LLNotificationPtr add(const std::string& name,
- const LLSD& substitutions,
- const LLSD& payload,
- const std::string& functor_name);
+ LLNotificationPtr add(const std::string& name);
+
+ LLNotificationPtr add(const std::string& name,
+ const LLSD& substitutions);
+
+ LLNotificationPtr add(const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload);
+
+ LLNotificationPtr add(const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload,
+ const std::string& functor_name);
+
+ LLNotificationPtr add(const std::string& name,
+ const LLSD& substitutions,
+ const LLSD& payload,
+ boost::function<void (const LLSD&, const LLSD&)> functor);
- LLNotificationPtr add(const std::string& name,
- const LLSD& substitutions,
- const LLSD& payload,
- boost::function<void (const LLSD&, const LLSD&)> functor);
-
- S32 getSelectedOption(const LLSD& notification, const LLSD& response);
+ S32 getSelectedOption(const LLSD& notification, const LLSD& response);
- void cancel(LLNotificationPtr pNotif);
+ void cancel(LLNotificationPtr pNotif);
- LLNotificationPtr find(LLUUID uuid);
+ LLNotificationPtr find(LLUUID uuid);
}
#endif
diff --git a/indra/llui/llnotificationtemplate.h b/indra/llui/llnotificationtemplate.h
index a8902486e4..0f77251ffe 100644
--- a/indra/llui/llnotificationtemplate.h
+++ b/indra/llui/llnotificationtemplate.h
@@ -6,21 +6,21 @@
* $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$
*/
@@ -33,212 +33,212 @@
typedef std::shared_ptr<LLNotificationForm> LLNotificationFormPtr;
-// This is the class of object read from the XML file (notifications.xml,
+// 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("combine_with_new", LLNotification::COMBINE_WITH_NEW);
- declare("keep_old", LLNotification::KEEP_OLD);
- declare("cancel_old", LLNotification::CANCEL_OLD);
- }
- };
-
-
- struct GlobalString : public LLInitParam::Block<GlobalString>
- {
- Mandatory<std::string> name,
- value;
-
- GlobalString()
- : name("name"),
- value("value")
- {}
- };
-
- struct UniquenessContext : public LLInitParam::Block<UniquenessContext>
- {
- Mandatory<std::string> value;
-
- UniquenessContext()
- : value("value")
- {
- addSynonym(value, "key");
- }
-
- };
-
- struct UniquenessConstraint : public LLInitParam::Block<UniquenessConstraint>
- {
- private:
- // this idiom allows
- // <notification> <unique/> </notification>
- // as well as
- // <notification> <unique> <context></context> </unique>...
- 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("")
- {}
- };
-
- // Templates are used to define common form types, such as OK/Cancel dialogs, etc.
-
- struct Template : public LLInitParam::Block<Template>
- {
- Mandatory<std::string> name;
- Mandatory<LLNotificationForm::Params> form;
-
- Template()
- : name("name"),
- form("form")
- {}
- };
-
- // Reference a template to use its form elements
- struct TemplateRef : public LLInitParam::Block<TemplateRef>
- {
- Mandatory<std::string> name;
- Optional<std::string> yes_text,
- no_text,
- cancel_text,
- help_text,
- ignore_text;
-
- TemplateRef()
- : name("name"),
- yes_text("yestext"),
- no_text("notext"),
- cancel_text("canceltext"),
- help_text("helptext"),
- ignore_text("ignoretext")
- {}
- };
-
- struct URL : public LLInitParam::Block<URL>
- {
- Mandatory<S32> option;
- Mandatory<std::string> value;
- Optional<std::string> target;
- Ignored name;
-
- URL()
- : option("option", -1),
- value("value"),
- target("target", "_blank"),
- name("name")
- {}
- };
-
- struct FormRef : public LLInitParam::ChoiceBlock<FormRef>
- {
- Alternative<LLNotificationForm::Params> form;
- Alternative<TemplateRef> form_template;
-
- FormRef()
- : form("form"),
- form_template("usetemplate")
- {}
- };
-
- struct Tag : public LLInitParam::Block<Tag>
- {
- Mandatory<std::string> value;
-
- Tag()
- : value("value")
- {}
- };
-
- 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,
- log_to_im,
- show_toast,
- fade_toast,
- log_to_chat,
- force_urls_external;
- Optional<std::string> functor,
- icon,
- label,
- sound,
- type,
- value;
- Optional<U32> duration;
- Optional<S32> expire_option;
- Optional<URL> url;
- Optional<UniquenessConstraint> unique;
- Optional<FormRef> form_ref;
- Optional<ENotificationPriority,
- NotificationPriorityValues> priority;
- Multiple<Tag> tags;
- Optional<Footer> footer;
-
-
- Params()
- : name("name"),
- persist("persist", false),
- fade_toast("fade_toast", true),
- log_to_im("log_to_im", false),
- show_toast("show_toast", true),
- log_to_chat("log_to_chat", true),
- force_urls_external("force_urls_external", false),
- functor("functor"),
- icon("icon"),
- label("label"),
- priority("priority"),
- sound("sound"),
- type("type"),
- value("value"),
- duration("duration"),
- expire_option("expireOption", -1),
- url("url"),
- unique("unique"),
- form_ref(""),
- tags("tag"),
- footer("footer")
- {}
-
- };
-
- struct Notifications : public LLInitParam::Block<Notifications>
- {
- Multiple<GlobalString> strings;
- Multiple<Template> templates;
- Multiple<Params> notifications;
-
- Notifications()
- : strings("global"),
- notifications("notification"),
- templates("template")
- {}
- };
-
- LLNotificationTemplate(const Params& p);
+ struct CombineBehaviorNames
+ : public LLInitParam::TypeValuesHelper<LLNotification::ECombineBehavior, CombineBehaviorNames>
+ {
+ static void declareValues()
+ {
+ declare("replace_with_new", LLNotification::REPLACE_WITH_NEW);
+ declare("combine_with_new", LLNotification::COMBINE_WITH_NEW);
+ declare("keep_old", LLNotification::KEEP_OLD);
+ declare("cancel_old", LLNotification::CANCEL_OLD);
+ }
+ };
+
+
+ struct GlobalString : public LLInitParam::Block<GlobalString>
+ {
+ Mandatory<std::string> name,
+ value;
+
+ GlobalString()
+ : name("name"),
+ value("value")
+ {}
+ };
+
+ struct UniquenessContext : public LLInitParam::Block<UniquenessContext>
+ {
+ Mandatory<std::string> value;
+
+ UniquenessContext()
+ : value("value")
+ {
+ addSynonym(value, "key");
+ }
+
+ };
+
+ struct UniquenessConstraint : public LLInitParam::Block<UniquenessConstraint>
+ {
+ private:
+ // this idiom allows
+ // <notification> <unique/> </notification>
+ // as well as
+ // <notification> <unique> <context></context> </unique>...
+ 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("")
+ {}
+ };
+
+ // Templates are used to define common form types, such as OK/Cancel dialogs, etc.
+
+ struct Template : public LLInitParam::Block<Template>
+ {
+ Mandatory<std::string> name;
+ Mandatory<LLNotificationForm::Params> form;
+
+ Template()
+ : name("name"),
+ form("form")
+ {}
+ };
+
+ // Reference a template to use its form elements
+ struct TemplateRef : public LLInitParam::Block<TemplateRef>
+ {
+ Mandatory<std::string> name;
+ Optional<std::string> yes_text,
+ no_text,
+ cancel_text,
+ help_text,
+ ignore_text;
+
+ TemplateRef()
+ : name("name"),
+ yes_text("yestext"),
+ no_text("notext"),
+ cancel_text("canceltext"),
+ help_text("helptext"),
+ ignore_text("ignoretext")
+ {}
+ };
+
+ struct URL : public LLInitParam::Block<URL>
+ {
+ Mandatory<S32> option;
+ Mandatory<std::string> value;
+ Optional<std::string> target;
+ Ignored name;
+
+ URL()
+ : option("option", -1),
+ value("value"),
+ target("target", "_blank"),
+ name("name")
+ {}
+ };
+
+ struct FormRef : public LLInitParam::ChoiceBlock<FormRef>
+ {
+ Alternative<LLNotificationForm::Params> form;
+ Alternative<TemplateRef> form_template;
+
+ FormRef()
+ : form("form"),
+ form_template("usetemplate")
+ {}
+ };
+
+ struct Tag : public LLInitParam::Block<Tag>
+ {
+ Mandatory<std::string> value;
+
+ Tag()
+ : value("value")
+ {}
+ };
+
+ 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,
+ log_to_im,
+ show_toast,
+ fade_toast,
+ log_to_chat,
+ force_urls_external;
+ Optional<std::string> functor,
+ icon,
+ label,
+ sound,
+ type,
+ value;
+ Optional<U32> duration;
+ Optional<S32> expire_option;
+ Optional<URL> url;
+ Optional<UniquenessConstraint> unique;
+ Optional<FormRef> form_ref;
+ Optional<ENotificationPriority,
+ NotificationPriorityValues> priority;
+ Multiple<Tag> tags;
+ Optional<Footer> footer;
+
+
+ Params()
+ : name("name"),
+ persist("persist", false),
+ fade_toast("fade_toast", true),
+ log_to_im("log_to_im", false),
+ show_toast("show_toast", true),
+ log_to_chat("log_to_chat", true),
+ force_urls_external("force_urls_external", false),
+ functor("functor"),
+ icon("icon"),
+ label("label"),
+ priority("priority"),
+ sound("sound"),
+ type("type"),
+ value("value"),
+ duration("duration"),
+ expire_option("expireOption", -1),
+ url("url"),
+ unique("unique"),
+ form_ref(""),
+ tags("tag"),
+ footer("footer")
+ {}
+
+ };
+
+ struct Notifications : public LLInitParam::Block<Notifications>
+ {
+ Multiple<GlobalString> strings;
+ Multiple<Template> templates;
+ Multiple<Params> notifications;
+
+ Notifications()
+ : strings("global"),
+ notifications("notification"),
+ templates("template")
+ {}
+ };
+
+ LLNotificationTemplate(const Params& p);
// the name of the notification -- the key used to identify it
- // Ideally, the key should follow variable naming rules
+ // Ideally, the key should follow variable naming rules
// (no spaces or punctuation).
std::string mName;
// The type of the notification
@@ -249,13 +249,13 @@ struct LLNotificationTemplate
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.
- // Replaceable parameters can be used in the label.
- std::string mLabel;
- // The name of the icon image. This should include an extension.
- std::string mIcon;
+ // 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.
+ // Replaceable parameters can be used in the label.
+ std::string mLabel;
+ // The name of the icon image. This should include an extension.
+ std::string mIcon;
// This is the Highlander bit -- "There Can Be Only One"
// An outstanding notification with this bit set
// is updated by an incoming notification with the same name,
@@ -263,25 +263,25 @@ 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;
+ 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
+ // 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
// new and the old notification; otherwise, the notification will be
// duplicated. This is to support suppressing duplicate offers from the same
// sender but still differentiating different offers. Example: Invitation to
// conference chat.
std::vector<std::string> mUniqueContext;
- // If this notification expires automatically, this value will be
+ // If this notification expires automatically, this value will be
// nonzero, and indicates the number of seconds for which the notification
- // will be valid (a teleport offer, for example, might be valid for
- // 300 seconds).
+ // will be valid (a teleport offer, for example, might be valid for
+ // 300 seconds).
U32 mExpireSeconds;
// if the offer expires, one of the options is chosen automatically
- // based on its "value" parameter. This controls which one.
+ // based on its "value" parameter. This controls which one.
// If expireSeconds is specified, expireOption should also be specified.
U32 mExpireOption;
- // if the notification contains a url, it's stored here (and replaced
+ // if the notification contains a url, it's stored here (and replaced
// into the message where [_URL] is found)
std::string mURL;
// if there's a URL in the message, this controls which option visits
@@ -289,36 +289,36 @@ struct LLNotificationTemplate
// messages when we allow clickable URLs in the UI
U32 mURLOption;
- //This is a flag that tells if option url needs to open externally dispite
- //what the user setting is.
- std::string mURLTarget;
-
- // All links clicked inside notification will be opened in external browser
- // Note: Some notifications block and exit viewer, yet they provide a link
- // to click, we should be able to open such links in external browser.
- bool mForceUrlsExternal;
-
- // does this notification persist across sessions? if so, it will be
- // serialized to disk on first receipt and read on startup
- bool mPersist;
- // This is the name of the default functor, if present, to be
- // used for the notification's callback. It is optional, and used only if
- // the notification is constructed without an identified functor.
- std::string mDefaultFunctor;
- // The form data associated with a given notification (buttons, text boxes, etc)
+ //This is a flag that tells if option url needs to open externally dispite
+ //what the user setting is.
+ std::string mURLTarget;
+
+ // All links clicked inside notification will be opened in external browser
+ // Note: Some notifications block and exit viewer, yet they provide a link
+ // to click, we should be able to open such links in external browser.
+ bool mForceUrlsExternal;
+
+ // does this notification persist across sessions? if so, it will be
+ // serialized to disk on first receipt and read on startup
+ bool mPersist;
+ // This is the name of the default functor, if present, to be
+ // used for the notification's callback. It is optional, and used only if
+ // the notification is constructed without an identified functor.
+ std::string mDefaultFunctor;
+ // The form data associated with a given notification (buttons, text boxes, etc)
LLNotificationFormPtr mForm;
- // default priority for notifications of this type
- ENotificationPriority mPriority;
- // 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;
- bool mFadeToast;
+ // default priority for notifications of this type
+ ENotificationPriority mPriority;
+ // 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;
+ bool mFadeToast;
};
#endif //LL_LLNOTIFICATION_TEMPLATE_H
diff --git a/indra/llui/llnotificationvisibilityrule.h b/indra/llui/llnotificationvisibilityrule.h
index 78788a275c..064b3b148e 100644
--- a/indra/llui/llnotificationvisibilityrule.h
+++ b/indra/llui/llnotificationvisibilityrule.h
@@ -1,26 +1,26 @@
/**
* @file llnotificationvisibility.h
-* @brief Rules for
+* @brief Rules for
* @author Monroe
*
* $LicenseInfo:firstyear=2010&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
-*
+*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
-*
+*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
-*
+*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-*
+*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -33,71 +33,71 @@
-// This is the class of object read from the XML file (notification_visibility.xml,
+// This is the class of object read from the XML file (notification_visibility.xml,
// from the appropriate local language directory).
struct LLNotificationVisibilityRule
{
- struct Filter : public LLInitParam::Block<Filter>
- {
- Optional<std::string> type,
- tag,
- name;
-
- Filter()
- : type("type"),
- tag("tag"),
- name("name")
- {}
- };
-
- struct Respond : public LLInitParam::Block<Respond, Filter>
- {
- Mandatory<std::string> response;
-
- Respond()
- : response("response")
- {}
- };
-
- struct Rule : public LLInitParam::ChoiceBlock<Rule>
- {
- Alternative<Filter> show;
- Alternative<Filter> hide;
- Alternative<Respond> respond;
-
- Rule()
- : show("show"),
- hide("hide"),
- respond("respond")
- {}
- };
-
- struct Rules : public LLInitParam::Block<Rules>
- {
- Multiple<Rule> rules;
-
- Rules()
- : rules("")
- {}
- };
-
- LLNotificationVisibilityRule(const Rule& p);
-
+ struct Filter : public LLInitParam::Block<Filter>
+ {
+ Optional<std::string> type,
+ tag,
+ name;
+
+ Filter()
+ : type("type"),
+ tag("tag"),
+ name("name")
+ {}
+ };
+
+ struct Respond : public LLInitParam::Block<Respond, Filter>
+ {
+ Mandatory<std::string> response;
+
+ Respond()
+ : response("response")
+ {}
+ };
+
+ struct Rule : public LLInitParam::ChoiceBlock<Rule>
+ {
+ Alternative<Filter> show;
+ Alternative<Filter> hide;
+ Alternative<Respond> respond;
+
+ Rule()
+ : show("show"),
+ hide("hide"),
+ respond("respond")
+ {}
+ };
+
+ struct Rules : public LLInitParam::Block<Rules>
+ {
+ Multiple<Rule> rules;
+
+ Rules()
+ : rules("")
+ {}
+ };
+
+ LLNotificationVisibilityRule(const Rule& p);
+
// If true, this rule makes matching notifications visible. Otherwise, it makes them invisible.
bool mVisible;
// Which response to give when making a notification invisible. An empty string means the notification should be cancelled instead of responded to.
- std::string mResponse;
+ std::string mResponse;
// String to match against the notification's "type". An empty string matches all notifications.
std::string mType;
-
+
// String to match against the notification's tag(s). An empty string matches all notifications.
- std::string mTag;
+ std::string mTag;
// String to match against the notification's name. An empty string matches all notifications.
- std::string mName;
-
+ std::string mName;
+
};
#endif //LL_LLNOTIFICATION_VISIBILITY_RULE_H
diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp
index ba6a31eb9e..f6758133f3 100644
--- a/indra/llui/llpanel.cpp
+++ b/indra/llui/llpanel.cpp
@@ -1,875 +1,875 @@
-/**
- * @file llpanel.cpp
- * @brief LLPanel 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$
- */
-
-// Opaque view with a background and a border. Can contain LLUICtrls.
-
-#include "linden_common.h"
-
-#define LLPANEL_CPP
-#include "llpanel.h"
-
-#include "llfocusmgr.h"
-#include "llfontgl.h"
-#include "llrect.h"
-#include "llerror.h"
-#include "lldir.h"
-#include "lltimer.h"
-
-#include "llbutton.h"
-#include "llmenugl.h"
-#include "llui.h"
-#include "llkeyboard.h"
-#include "lllineeditor.h"
-#include "llcontrol.h"
-#include "lltextbox.h"
-#include "lluictrl.h"
-#include "lluictrlfactory.h"
-#include "llviewborder.h"
-
-static LLDefaultChildRegistry::Register<LLPanel> r1("panel", &LLPanel::fromXML);
-LLPanel::factory_stack_t LLPanel::sFactoryStack;
-
-
-// Compiler optimization, generate extern template
-template class LLPanel* LLView::getChild<class LLPanel>(
- const std::string& name, bool recurse) const;
-
-LLPanel::LocalizedString::LocalizedString()
-: name("name"),
- value("value")
-{}
-
-const LLPanel::Params& LLPanel::getDefaultParams()
-{
- return LLUICtrlFactory::getDefaultParams<LLPanel>();
-}
-
-LLPanel::Params::Params()
-: has_border("border", false),
- border(""),
- background_visible("background_visible", false),
- background_opaque("background_opaque", false),
- bg_opaque_color("bg_opaque_color"),
- bg_alpha_color("bg_alpha_color"),
- bg_opaque_image_overlay("bg_opaque_image_overlay"),
- bg_alpha_image_overlay("bg_alpha_image_overlay"),
- bg_opaque_image("bg_opaque_image"),
- bg_alpha_image("bg_alpha_image"),
- min_width("min_width", 100),
- min_height("min_height", 100),
- strings("string"),
- filename("filename"),
- class_name("class"),
- help_topic("help_topic"),
- visible_callback("visible_callback"),
- accepts_badge("accepts_badge")
-{
- addSynonym(background_visible, "bg_visible");
- addSynonym(has_border, "border_visible");
- addSynonym(label, "title");
-}
-
-
-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()),
- mBgAlphaColor(p.bg_alpha_color()),
- mBgOpaqueImageOverlay(p.bg_opaque_image_overlay),
- mBgAlphaImageOverlay(p.bg_alpha_image_overlay),
- mBgOpaqueImage(p.bg_opaque_image()),
- mBgAlphaImage(p.bg_alpha_image()),
- mDefaultBtn(NULL),
- mBorder(NULL),
- mLabel(p.label),
- mHelpTopic(p.help_topic),
- mCommitCallbackRegistrar(false),
- mEnableCallbackRegistrar(false),
- mXMLFilename(p.filename),
- mVisibleSignal(NULL)
- // *NOTE: Be sure to also change LLPanel::initFromParams(). We have too
- // many classes derived from LLPanel to retrofit them all to pass in params.
-{
- if (p.has_border)
- {
- addBorder(p.border);
- }
-}
-
-LLPanel::~LLPanel()
-{
- delete mVisibleSignal;
-}
-
-// virtual
-bool LLPanel::isPanel() const
-{
- return true;
-}
-
-void LLPanel::addBorder(LLViewBorder::Params p)
-{
- removeBorder();
- p.rect = getLocalRect();
-
- mBorder = LLUICtrlFactory::create<LLViewBorder>(p);
- addChild( mBorder );
-}
-
-void LLPanel::addBorder()
-{
- LLViewBorder::Params p;
- p.border_thickness(LLPANEL_BORDER_WIDTH);
- addBorder(p);
-}
-
-
-void LLPanel::removeBorder()
-{
- if (mBorder)
- {
- removeChild(mBorder);
- delete mBorder;
- mBorder = NULL;
- }
-}
-
-
-// virtual
-void LLPanel::clearCtrls()
-{
- 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 );
- ctrl->setEnabled( false );
- ctrl->clear();
- }
-}
-
-void LLPanel::setCtrlsEnabled( bool b )
-{
- 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;
-
- // draw background
- if( mBgVisible )
- {
- alpha = getCurrentTransparency();
-
- LLRect local_rect = getLocalRect();
- if (mBgOpaque )
- {
- // opaque, in-front look
- if (mBgOpaqueImage.notNull())
- {
- mBgOpaqueImage->draw( local_rect, mBgOpaqueImageOverlay % alpha );
- }
- else
- {
- // fallback to flat colors when there are no images
- gl_rect_2d( local_rect, mBgOpaqueColor.get() % alpha);
- }
- }
- else
- {
- // transparent, in-back look
- if (mBgAlphaImage.notNull())
- {
- mBgAlphaImage->draw( local_rect, mBgAlphaImageOverlay % alpha );
- }
- else
- {
- gl_rect_2d( local_rect, mBgAlphaColor.get() % alpha );
- }
- }
- }
-
- updateDefaultBtn();
-
- LLView::draw();
-}
-
-void LLPanel::updateDefaultBtn()
-{
- if( mDefaultBtn)
- {
- if (gFocusMgr.childHasKeyboardFocus( this ) && mDefaultBtn->getEnabled())
- {
- LLButton* buttonp = dynamic_cast<LLButton*>(gFocusMgr.getKeyboardFocus());
- bool focus_is_child_button = buttonp && buttonp->getCommitOnReturn();
- // only enable default button when current focus is not a return-capturing button
- mDefaultBtn->setBorderEnabled(!focus_is_child_button);
- }
- else
- {
- mDefaultBtn->setBorderEnabled(false);
- }
- }
-}
-
-void LLPanel::refresh()
-{
- // do nothing by default
- // but is automatically called in setFocus(true)
-}
-
-void LLPanel::setDefaultBtn(LLButton* btn)
-{
- if (mDefaultBtn && mDefaultBtn->getEnabled())
- {
- mDefaultBtn->setBorderEnabled(false);
- }
- mDefaultBtn = btn;
- if (mDefaultBtn)
- {
- mDefaultBtn->setBorderEnabled(true);
- }
-}
-
-void LLPanel::setDefaultBtn(const std::string& id)
-{
- LLButton *button = getChild<LLButton>(id);
- if (button)
- {
- setDefaultBtn(button);
- }
- else
- {
- setDefaultBtn(NULL);
- }
-}
-
-bool LLPanel::handleKeyHere( KEY key, MASK mask )
-{
- bool handled = false;
-
- LLUICtrl* cur_focus = dynamic_cast<LLUICtrl*>(gFocusMgr.getKeyboardFocus());
-
- // handle user hitting ESC to defocus
- if (key == KEY_ESCAPE)
- {
- setFocus(false);
- return true;
- }
- else if( (mask == MASK_SHIFT) && (KEY_TAB == key))
- {
- //SHIFT-TAB
- if (cur_focus)
- {
- LLUICtrl* focus_root = cur_focus->findRootMostFocusRoot();
- if (focus_root)
- {
- handled = focus_root->focusPrevItem(false);
- }
- }
- }
- else if( (mask == MASK_NONE ) && (KEY_TAB == key))
- {
- //TAB
- if (cur_focus)
- {
- LLUICtrl* focus_root = cur_focus->findRootMostFocusRoot();
- if (focus_root)
- {
- handled = focus_root->focusNextItem(false);
- }
- }
- }
-
- // If RETURN was pressed and something has focus, call onCommit()
- if (!handled && cur_focus && key == KEY_RETURN && mask == MASK_NONE)
- {
- LLButton* focused_button = dynamic_cast<LLButton*>(cur_focus);
- if (focused_button && focused_button->getCommitOnReturn())
- {
- // current focus is a return-capturing button,
- // let *that* button handle the return key
- handled = false;
- }
- else if (mDefaultBtn && mDefaultBtn->getVisible() && mDefaultBtn->getEnabled())
- {
- // If we have a default button, click it when return is pressed
- mDefaultBtn->onCommit();
- handled = true;
- }
- else if (cur_focus->acceptsTextInput())
- {
- // call onCommit for text input handling control
- cur_focus->onCommit();
- handled = true;
- }
- }
-
- return handled;
-}
-
-void LLPanel::onVisibilityChange ( bool new_visibility )
-{
- LLUICtrl::onVisibilityChange ( new_visibility );
- if (mVisibleSignal)
- (*mVisibleSignal)(this, LLSD(new_visibility) ); // Pass bool as LLSD
-}
-
-void LLPanel::setFocus(bool b)
-{
- if( b && !hasFocus())
- {
- // give ourselves focus preemptively, to avoid infinite loop
- LLUICtrl::setFocus(true);
- // then try to pass to first valid child
- focusFirstItem();
- }
- else
- {
- LLUICtrl::setFocus(b);
- }
-}
-
-void LLPanel::setBorderVisible(bool b)
-{
- if (mBorder)
- {
- mBorder->setVisible( b );
- }
-}
-
-LLTrace::BlockTimerStatHandle FTM_PANEL_CONSTRUCTION("Panel Construction");
-
-LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLXMLNodePtr output_node)
-{
- std::string name("panel");
- node->getAttributeString("name", name);
-
- std::string class_attr;
- node->getAttributeString("class", class_attr);
-
- LLPanel* panelp = NULL;
-
- { LL_RECORD_BLOCK_TIME(FTM_PANEL_CONSTRUCTION);
-
- if(!class_attr.empty())
- {
- panelp = LLRegisterPanelClass::instance().createPanelClass(class_attr);
- if (!panelp)
- {
- LL_WARNS() << "Panel class \"" << class_attr << "\" not registered." << LL_ENDL;
- }
- }
-
- if (!panelp)
- {
- panelp = createFactoryPanel(name);
- llassert(panelp);
-
- if (!panelp)
- {
- return NULL; // :(
- }
- }
-
- }
- // factory panels may have registered their own factory maps
- if (!panelp->getFactoryMap().empty())
- {
- sFactoryStack.push_back(&panelp->getFactoryMap());
- }
- // for local registry callbacks; define in constructor, referenced in XUI or postBuild
- panelp->mCommitCallbackRegistrar.pushScope();
- panelp->mEnableCallbackRegistrar.pushScope();
-
- panelp->initPanelXML(node, parent, output_node, LLUICtrlFactory::getDefaultParams<LLPanel>());
-
- panelp->mCommitCallbackRegistrar.popScope();
- panelp->mEnableCallbackRegistrar.popScope();
-
- if (!panelp->getFactoryMap().empty())
- {
- sFactoryStack.pop_back();
- }
-
- return panelp;
-}
-
-void LLPanel::initFromParams(const LLPanel::Params& p)
-{
- //setting these here since panel constructor not called with params
- //and LLView::initFromParams will use them to set visible and enabled
- setVisible(p.visible);
- setEnabled(p.enabled);
- setFocusRoot(p.focus_root);
- setSoundFlags(p.sound_flags);
-
- // control_name, tab_stop, focus_lost_callback, initial_value, rect, enabled, visible
- LLUICtrl::initFromParams(p);
-
- // visible callback
- if (p.visible_callback.isProvided())
- {
- setVisibleCallback(initCommitCallback(p.visible_callback));
- }
-
- for (LLInitParam::ParamIterator<LocalizedString>::const_iterator it = p.strings.begin();
- it != p.strings.end();
- ++it)
- {
- mUIStrings[it->name] = it->value;
- }
-
- setLabel(p.label());
- setHelpTopic(p.help_topic);
- setShape(p.rect);
- parseFollowsFlags(p);
-
- setToolTip(p.tool_tip());
- setFromXUI(p.from_xui);
-
- mHoverCursor = getCursorFromString(p.hover_cursor);
-
- if (p.has_border)
- {
- addBorder(p.border);
- }
- // let constructors set this value if not provided
- if (p.use_bounding_rect.isProvided())
- {
- setUseBoundingRect(p.use_bounding_rect);
- }
- setDefaultTabGroup(p.default_tab_group);
- setMouseOpaque(p.mouse_opaque);
-
- setBackgroundVisible(p.background_visible);
- setBackgroundOpaque(p.background_opaque);
- setBackgroundColor(p.bg_opaque_color().get());
- setTransparentColor(p.bg_alpha_color().get());
- mBgOpaqueImage = p.bg_opaque_image();
- mBgAlphaImage = p.bg_alpha_image();
- mBgOpaqueImageOverlay = p.bg_opaque_image_overlay;
- mBgAlphaImageOverlay = p.bg_alpha_image_overlay;
-
- setAcceptsBadge(p.accepts_badge);
-}
-
-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);
- {
- LL_RECORD_BLOCK_TIME(FTM_PANEL_SETUP);
-
- LLXMLNodePtr referenced_xml;
- std::string xml_filename = mXMLFilename;
-
- // if the panel didn't provide a filename, check the node
- if (xml_filename.empty())
- {
- node->getAttributeString("filename", xml_filename);
- setXMLFilename(xml_filename);
- }
-
- LLXUIParser parser;
-
- if (!xml_filename.empty())
- {
- if (output_node)
- {
- //if we are exporting, we want to export the current xml
- //not the referenced xml
- parser.readXUI(node, params, LLUICtrlFactory::getInstance()->getCurFileName());
- Params output_params(params);
- 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_PANEL_LOAD);
- if (!LLUICtrlFactory::getLayeredXMLNode(xml_filename, referenced_xml))
- {
- LL_WARNS() << "Couldn't parse panel from: " << xml_filename << LL_ENDL;
-
- return false;
- }
-
- parser.readXUI(referenced_xml, params, LLUICtrlFactory::getInstance()->getCurFileName());
-
- // add children using dimensions from referenced xml for consistent layout
- setShape(params.rect);
- LLUICtrlFactory::createChildren(this, referenced_xml, child_registry_t::instance());
-
- LLUICtrlFactory::instance().popFileName();
- }
-
- // ask LLUICtrlFactory for filename, since xml_filename might be empty
- parser.readXUI(node, params, LLUICtrlFactory::getInstance()->getCurFileName());
-
- if (output_node)
- {
- Params output_params(params);
- setupParamsForExport(output_params, parent);
- output_node->setName(node->getName()->mString);
- parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &default_params);
- }
-
- params.from_xui = true;
- applyXUILayout(params, parent);
- {
- LL_RECORD_BLOCK_TIME(FTM_PANEL_CONSTRUCTION);
- initFromParams(params);
- }
-
- // add children
- LLUICtrlFactory::createChildren(this, node, child_registry_t::instance(), output_node);
-
- // Connect to parent after children are built, because tab containers
- // do a reshape() on their child panels, which requires that the children
- // be built/added. JC
- if (parent)
- {
- S32 tab_group = params.tab_group.isProvided() ? params.tab_group() : parent->getLastTabGroup();
- parent->addChild(this, tab_group);
- }
-
- {
- LL_RECORD_BLOCK_TIME(FTM_PANEL_POSTBUILD);
- postBuild();
- }
- }
- return true;
-}
-
-bool LLPanel::hasString(const std::string& name)
-{
- return mUIStrings.find(name) != mUIStrings.end();
-}
-
-std::string LLPanel::getString(const std::string& name, const LLStringUtil::format_map_t& args) const
-{
- ui_string_map_t::const_iterator found_it = mUIStrings.find(name);
- if (found_it != mUIStrings.end())
- {
- // make a copy as format works in place
- LLUIString formatted_string = LLUIString(found_it->second);
- formatted_string.setArgList(args);
- return formatted_string.getString();
- }
- std::string err_str("Failed to find string " + name + " in panel " + getName()); //*TODO: Translate
- if(LLUI::getInstance()->mSettingGroups["config"]->getBOOL("QAMode"))
- {
- LL_ERRS() << err_str << LL_ENDL;
- }
- else
- {
- LL_WARNS() << err_str << LL_ENDL;
- }
- return LLStringUtil::null;
-}
-
-std::string LLPanel::getString(const std::string& name) const
-{
- ui_string_map_t::const_iterator found_it = mUIStrings.find(name);
- if (found_it != mUIStrings.end())
- {
- return found_it->second;
- }
- std::string err_str("Failed to find string " + name + " in panel " + getName()); //*TODO: Translate
- if(LLUI::getInstance()->mSettingGroups["config"]->getBOOL("QAMode"))
- {
- LL_ERRS() << err_str << LL_ENDL;
- }
- else
- {
- LL_WARNS() << err_str << LL_ENDL;
- }
- return LLStringUtil::null;
-}
-
-
-void LLPanel::childSetVisible(const std::string& id, bool visible)
-{
- LLView* child = findChild<LLView>(id);
- if (child)
- {
- child->setVisible(visible);
- }
-}
-
-void LLPanel::childSetEnabled(const std::string& id, bool enabled)
-{
- LLView* child = findChild<LLView>(id);
- if (child)
- {
- child->setEnabled(enabled);
- }
-}
-
-void LLPanel::childSetFocus(const std::string& id, bool focus)
-{
- LLUICtrl* child = findChild<LLUICtrl>(id);
- if (child)
- {
- child->setFocus(focus);
- }
-}
-
-bool LLPanel::childHasFocus(const std::string& id)
-{
- LLUICtrl* child = findChild<LLUICtrl>(id);
- if (child)
- {
- return child->hasFocus();
- }
- else
- {
- return false;
- }
-}
-
-// *TODO: Deprecate; for backwards compatability only:
-// Prefer getChild<LLUICtrl>("foo")->setCommitCallback(boost:bind(...)),
-// which takes a generic slot. Or use mCommitCallbackRegistrar.add() with
-// a named callback and reference it in XML.
-void LLPanel::childSetCommitCallback(const std::string& id, boost::function<void (LLUICtrl*,void*)> cb, void* data)
-{
- LLUICtrl* child = findChild<LLUICtrl>(id);
- if (child)
- {
- child->setCommitCallback(boost::bind(cb, child, data));
- }
-}
-
-void LLPanel::childSetColor(const std::string& id, const LLColor4& color)
-{
- LLUICtrl* child = findChild<LLUICtrl>(id);
- if (child)
- {
- child->setColor(color);
- }
-}
-
-LLCtrlSelectionInterface* LLPanel::childGetSelectionInterface(const std::string& id) const
-{
- LLUICtrl* child = findChild<LLUICtrl>(id);
- if (child)
- {
- return child->getSelectionInterface();
- }
- return NULL;
-}
-
-LLCtrlListInterface* LLPanel::childGetListInterface(const std::string& id) const
-{
- LLUICtrl* child = findChild<LLUICtrl>(id);
- if (child)
- {
- return child->getListInterface();
- }
- return NULL;
-}
-
-LLCtrlScrollInterface* LLPanel::childGetScrollInterface(const std::string& id) const
-{
- LLUICtrl* child = findChild<LLUICtrl>(id);
- if (child)
- {
- return child->getScrollInterface();
- }
- return NULL;
-}
-
-void LLPanel::childSetValue(const std::string& id, LLSD value)
-{
- LLUICtrl* child = findChild<LLUICtrl>(id);
- if (child)
- {
- child->setValue(value);
- }
-}
-
-LLSD LLPanel::childGetValue(const std::string& id) const
-{
- LLUICtrl* child = findChild<LLUICtrl>(id);
- if (child)
- {
- return child->getValue();
- }
- // Not found => return undefined
- return LLSD();
-}
-
-bool LLPanel::childSetTextArg(const std::string& id, const std::string& key, const LLStringExplicit& text)
-{
- LLUICtrl* child = findChild<LLUICtrl>(id);
- if (child)
- {
- return child->setTextArg(key, text);
- }
- return false;
-}
-
-bool LLPanel::childSetLabelArg(const std::string& id, const std::string& key, const LLStringExplicit& text)
-{
- LLView* child = findChild<LLView>(id);
- if (child)
- {
- return child->setLabelArg(key, text);
- }
- return false;
-}
-
-void LLPanel::childSetAction(const std::string& id, const commit_signal_t::slot_type& function)
-{
- LLButton* button = findChild<LLButton>(id);
- if (button)
- {
- button->setClickedCallback(function);
- }
-}
-
-void LLPanel::childSetAction(const std::string& id, boost::function<void(void*)> function, void* value)
-{
- LLButton* button = findChild<LLButton>(id);
- if (button)
- {
- button->setClickedCallback(boost::bind(function, value));
- }
-}
-
-boost::signals2::connection LLPanel::setVisibleCallback( const commit_signal_t::slot_type& cb )
-{
- if (!mVisibleSignal)
- {
- mVisibleSignal = new commit_signal_t();
- }
-
- return mVisibleSignal->connect(cb);
-}
-
-//-----------------------------------------------------------------------------
-// buildPanel()
-//-----------------------------------------------------------------------------
-bool LLPanel::buildFromFile(const std::string& filename, const LLPanel::Params& default_params)
-{
- LL_PROFILE_ZONE_SCOPED;
- bool didPost = false;
- LLXMLNodePtr root;
-
- if (!LLUICtrlFactory::getLayeredXMLNode(filename, root))
- {
- LL_WARNS() << "Couldn't parse panel from: " << filename << LL_ENDL;
- return didPost;
- }
-
- // root must be called panel
- if( !root->hasName("panel" ) )
- {
- LL_WARNS() << "Root node should be named panel in : " << filename << LL_ENDL;
- return didPost;
- }
-
- LL_DEBUGS() << "Building panel " << filename << LL_ENDL;
-
- LLUICtrlFactory::instance().pushFileName(filename);
- {
- if (!getFactoryMap().empty())
- {
- sFactoryStack.push_back(&getFactoryMap());
- }
-
- // for local registry callbacks; define in constructor, referenced in XUI or postBuild
- getCommitCallbackRegistrar().pushScope();
- getEnableCallbackRegistrar().pushScope();
-
- didPost = initPanelXML(root, NULL, NULL, default_params);
-
- getCommitCallbackRegistrar().popScope();
- getEnableCallbackRegistrar().popScope();
-
- setXMLFilename(filename);
-
- if (!getFactoryMap().empty())
- {
- sFactoryStack.pop_back();
- }
- }
- LLUICtrlFactory::instance().popFileName();
- return didPost;
-}
-
-//-----------------------------------------------------------------------------
-// createFactoryPanel()
-//-----------------------------------------------------------------------------
-LLPanel* LLPanel::createFactoryPanel(const std::string& name)
-{
- std::deque<const LLCallbackMap::map_t*>::iterator itor;
- for (itor = sFactoryStack.begin(); itor != sFactoryStack.end(); ++itor)
- {
- const LLCallbackMap::map_t* factory_map = *itor;
-
- // Look up this panel's name in the map.
- LLCallbackMap::map_const_iter_t iter = factory_map->find( name );
- if (iter != factory_map->end())
- {
- // Use the factory to create the panel, instead of using a default LLPanel.
- LLPanel *ret = (LLPanel*) iter->second.mCallback( iter->second.mData );
- return ret;
- }
- }
- LLPanel::Params panel_p;
- return LLUICtrlFactory::create<LLPanel>(panel_p);
-}
+/**
+ * @file llpanel.cpp
+ * @brief LLPanel 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$
+ */
+
+// Opaque view with a background and a border. Can contain LLUICtrls.
+
+#include "linden_common.h"
+
+#define LLPANEL_CPP
+#include "llpanel.h"
+
+#include "llfocusmgr.h"
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "lldir.h"
+#include "lltimer.h"
+
+#include "llbutton.h"
+#include "llmenugl.h"
+#include "llui.h"
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "llcontrol.h"
+#include "lltextbox.h"
+#include "lluictrl.h"
+#include "lluictrlfactory.h"
+#include "llviewborder.h"
+
+static LLDefaultChildRegistry::Register<LLPanel> r1("panel", &LLPanel::fromXML);
+LLPanel::factory_stack_t LLPanel::sFactoryStack;
+
+
+// Compiler optimization, generate extern template
+template class LLPanel* LLView::getChild<class LLPanel>(
+ const std::string& name, bool recurse) const;
+
+LLPanel::LocalizedString::LocalizedString()
+: name("name"),
+ value("value")
+{}
+
+const LLPanel::Params& LLPanel::getDefaultParams()
+{
+ return LLUICtrlFactory::getDefaultParams<LLPanel>();
+}
+
+LLPanel::Params::Params()
+: has_border("border", false),
+ border(""),
+ background_visible("background_visible", false),
+ background_opaque("background_opaque", false),
+ bg_opaque_color("bg_opaque_color"),
+ bg_alpha_color("bg_alpha_color"),
+ bg_opaque_image_overlay("bg_opaque_image_overlay"),
+ bg_alpha_image_overlay("bg_alpha_image_overlay"),
+ bg_opaque_image("bg_opaque_image"),
+ bg_alpha_image("bg_alpha_image"),
+ min_width("min_width", 100),
+ min_height("min_height", 100),
+ strings("string"),
+ filename("filename"),
+ class_name("class"),
+ help_topic("help_topic"),
+ visible_callback("visible_callback"),
+ accepts_badge("accepts_badge")
+{
+ addSynonym(background_visible, "bg_visible");
+ addSynonym(has_border, "border_visible");
+ addSynonym(label, "title");
+}
+
+
+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()),
+ mBgAlphaColor(p.bg_alpha_color()),
+ mBgOpaqueImageOverlay(p.bg_opaque_image_overlay),
+ mBgAlphaImageOverlay(p.bg_alpha_image_overlay),
+ mBgOpaqueImage(p.bg_opaque_image()),
+ mBgAlphaImage(p.bg_alpha_image()),
+ mDefaultBtn(NULL),
+ mBorder(NULL),
+ mLabel(p.label),
+ mHelpTopic(p.help_topic),
+ mCommitCallbackRegistrar(false),
+ mEnableCallbackRegistrar(false),
+ mXMLFilename(p.filename),
+ mVisibleSignal(NULL)
+ // *NOTE: Be sure to also change LLPanel::initFromParams(). We have too
+ // many classes derived from LLPanel to retrofit them all to pass in params.
+{
+ if (p.has_border)
+ {
+ addBorder(p.border);
+ }
+}
+
+LLPanel::~LLPanel()
+{
+ delete mVisibleSignal;
+}
+
+// virtual
+bool LLPanel::isPanel() const
+{
+ return true;
+}
+
+void LLPanel::addBorder(LLViewBorder::Params p)
+{
+ removeBorder();
+ p.rect = getLocalRect();
+
+ mBorder = LLUICtrlFactory::create<LLViewBorder>(p);
+ addChild( mBorder );
+}
+
+void LLPanel::addBorder()
+{
+ LLViewBorder::Params p;
+ p.border_thickness(LLPANEL_BORDER_WIDTH);
+ addBorder(p);
+}
+
+
+void LLPanel::removeBorder()
+{
+ if (mBorder)
+ {
+ removeChild(mBorder);
+ delete mBorder;
+ mBorder = NULL;
+ }
+}
+
+
+// virtual
+void LLPanel::clearCtrls()
+{
+ 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 );
+ ctrl->setEnabled( false );
+ ctrl->clear();
+ }
+}
+
+void LLPanel::setCtrlsEnabled( bool b )
+{
+ 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;
+
+ // draw background
+ if( mBgVisible )
+ {
+ alpha = getCurrentTransparency();
+
+ LLRect local_rect = getLocalRect();
+ if (mBgOpaque )
+ {
+ // opaque, in-front look
+ if (mBgOpaqueImage.notNull())
+ {
+ mBgOpaqueImage->draw( local_rect, mBgOpaqueImageOverlay % alpha );
+ }
+ else
+ {
+ // fallback to flat colors when there are no images
+ gl_rect_2d( local_rect, mBgOpaqueColor.get() % alpha);
+ }
+ }
+ else
+ {
+ // transparent, in-back look
+ if (mBgAlphaImage.notNull())
+ {
+ mBgAlphaImage->draw( local_rect, mBgAlphaImageOverlay % alpha );
+ }
+ else
+ {
+ gl_rect_2d( local_rect, mBgAlphaColor.get() % alpha );
+ }
+ }
+ }
+
+ updateDefaultBtn();
+
+ LLView::draw();
+}
+
+void LLPanel::updateDefaultBtn()
+{
+ if( mDefaultBtn)
+ {
+ if (gFocusMgr.childHasKeyboardFocus( this ) && mDefaultBtn->getEnabled())
+ {
+ LLButton* buttonp = dynamic_cast<LLButton*>(gFocusMgr.getKeyboardFocus());
+ bool focus_is_child_button = buttonp && buttonp->getCommitOnReturn();
+ // only enable default button when current focus is not a return-capturing button
+ mDefaultBtn->setBorderEnabled(!focus_is_child_button);
+ }
+ else
+ {
+ mDefaultBtn->setBorderEnabled(false);
+ }
+ }
+}
+
+void LLPanel::refresh()
+{
+ // do nothing by default
+ // but is automatically called in setFocus(true)
+}
+
+void LLPanel::setDefaultBtn(LLButton* btn)
+{
+ if (mDefaultBtn && mDefaultBtn->getEnabled())
+ {
+ mDefaultBtn->setBorderEnabled(false);
+ }
+ mDefaultBtn = btn;
+ if (mDefaultBtn)
+ {
+ mDefaultBtn->setBorderEnabled(true);
+ }
+}
+
+void LLPanel::setDefaultBtn(const std::string& id)
+{
+ LLButton *button = getChild<LLButton>(id);
+ if (button)
+ {
+ setDefaultBtn(button);
+ }
+ else
+ {
+ setDefaultBtn(NULL);
+ }
+}
+
+bool LLPanel::handleKeyHere( KEY key, MASK mask )
+{
+ bool handled = false;
+
+ LLUICtrl* cur_focus = dynamic_cast<LLUICtrl*>(gFocusMgr.getKeyboardFocus());
+
+ // handle user hitting ESC to defocus
+ if (key == KEY_ESCAPE)
+ {
+ setFocus(false);
+ return true;
+ }
+ else if( (mask == MASK_SHIFT) && (KEY_TAB == key))
+ {
+ //SHIFT-TAB
+ if (cur_focus)
+ {
+ LLUICtrl* focus_root = cur_focus->findRootMostFocusRoot();
+ if (focus_root)
+ {
+ handled = focus_root->focusPrevItem(false);
+ }
+ }
+ }
+ else if( (mask == MASK_NONE ) && (KEY_TAB == key))
+ {
+ //TAB
+ if (cur_focus)
+ {
+ LLUICtrl* focus_root = cur_focus->findRootMostFocusRoot();
+ if (focus_root)
+ {
+ handled = focus_root->focusNextItem(false);
+ }
+ }
+ }
+
+ // If RETURN was pressed and something has focus, call onCommit()
+ if (!handled && cur_focus && key == KEY_RETURN && mask == MASK_NONE)
+ {
+ LLButton* focused_button = dynamic_cast<LLButton*>(cur_focus);
+ if (focused_button && focused_button->getCommitOnReturn())
+ {
+ // current focus is a return-capturing button,
+ // let *that* button handle the return key
+ handled = false;
+ }
+ else if (mDefaultBtn && mDefaultBtn->getVisible() && mDefaultBtn->getEnabled())
+ {
+ // If we have a default button, click it when return is pressed
+ mDefaultBtn->onCommit();
+ handled = true;
+ }
+ else if (cur_focus->acceptsTextInput())
+ {
+ // call onCommit for text input handling control
+ cur_focus->onCommit();
+ handled = true;
+ }
+ }
+
+ return handled;
+}
+
+void LLPanel::onVisibilityChange ( bool new_visibility )
+{
+ LLUICtrl::onVisibilityChange ( new_visibility );
+ if (mVisibleSignal)
+ (*mVisibleSignal)(this, LLSD(new_visibility) ); // Pass bool as LLSD
+}
+
+void LLPanel::setFocus(bool b)
+{
+ if( b && !hasFocus())
+ {
+ // give ourselves focus preemptively, to avoid infinite loop
+ LLUICtrl::setFocus(true);
+ // then try to pass to first valid child
+ focusFirstItem();
+ }
+ else
+ {
+ LLUICtrl::setFocus(b);
+ }
+}
+
+void LLPanel::setBorderVisible(bool b)
+{
+ if (mBorder)
+ {
+ mBorder->setVisible( b );
+ }
+}
+
+LLTrace::BlockTimerStatHandle FTM_PANEL_CONSTRUCTION("Panel Construction");
+
+LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLXMLNodePtr output_node)
+{
+ std::string name("panel");
+ node->getAttributeString("name", name);
+
+ std::string class_attr;
+ node->getAttributeString("class", class_attr);
+
+ LLPanel* panelp = NULL;
+
+ { LL_RECORD_BLOCK_TIME(FTM_PANEL_CONSTRUCTION);
+
+ if(!class_attr.empty())
+ {
+ panelp = LLRegisterPanelClass::instance().createPanelClass(class_attr);
+ if (!panelp)
+ {
+ LL_WARNS() << "Panel class \"" << class_attr << "\" not registered." << LL_ENDL;
+ }
+ }
+
+ if (!panelp)
+ {
+ panelp = createFactoryPanel(name);
+ llassert(panelp);
+
+ if (!panelp)
+ {
+ return NULL; // :(
+ }
+ }
+
+ }
+ // factory panels may have registered their own factory maps
+ if (!panelp->getFactoryMap().empty())
+ {
+ sFactoryStack.push_back(&panelp->getFactoryMap());
+ }
+ // for local registry callbacks; define in constructor, referenced in XUI or postBuild
+ panelp->mCommitCallbackRegistrar.pushScope();
+ panelp->mEnableCallbackRegistrar.pushScope();
+
+ panelp->initPanelXML(node, parent, output_node, LLUICtrlFactory::getDefaultParams<LLPanel>());
+
+ panelp->mCommitCallbackRegistrar.popScope();
+ panelp->mEnableCallbackRegistrar.popScope();
+
+ if (!panelp->getFactoryMap().empty())
+ {
+ sFactoryStack.pop_back();
+ }
+
+ return panelp;
+}
+
+void LLPanel::initFromParams(const LLPanel::Params& p)
+{
+ //setting these here since panel constructor not called with params
+ //and LLView::initFromParams will use them to set visible and enabled
+ setVisible(p.visible);
+ setEnabled(p.enabled);
+ setFocusRoot(p.focus_root);
+ setSoundFlags(p.sound_flags);
+
+ // control_name, tab_stop, focus_lost_callback, initial_value, rect, enabled, visible
+ LLUICtrl::initFromParams(p);
+
+ // visible callback
+ if (p.visible_callback.isProvided())
+ {
+ setVisibleCallback(initCommitCallback(p.visible_callback));
+ }
+
+ for (LLInitParam::ParamIterator<LocalizedString>::const_iterator it = p.strings.begin();
+ it != p.strings.end();
+ ++it)
+ {
+ mUIStrings[it->name] = it->value;
+ }
+
+ setLabel(p.label());
+ setHelpTopic(p.help_topic);
+ setShape(p.rect);
+ parseFollowsFlags(p);
+
+ setToolTip(p.tool_tip());
+ setFromXUI(p.from_xui);
+
+ mHoverCursor = getCursorFromString(p.hover_cursor);
+
+ if (p.has_border)
+ {
+ addBorder(p.border);
+ }
+ // let constructors set this value if not provided
+ if (p.use_bounding_rect.isProvided())
+ {
+ setUseBoundingRect(p.use_bounding_rect);
+ }
+ setDefaultTabGroup(p.default_tab_group);
+ setMouseOpaque(p.mouse_opaque);
+
+ setBackgroundVisible(p.background_visible);
+ setBackgroundOpaque(p.background_opaque);
+ setBackgroundColor(p.bg_opaque_color().get());
+ setTransparentColor(p.bg_alpha_color().get());
+ mBgOpaqueImage = p.bg_opaque_image();
+ mBgAlphaImage = p.bg_alpha_image();
+ mBgOpaqueImageOverlay = p.bg_opaque_image_overlay;
+ mBgAlphaImageOverlay = p.bg_alpha_image_overlay;
+
+ setAcceptsBadge(p.accepts_badge);
+}
+
+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);
+ {
+ LL_RECORD_BLOCK_TIME(FTM_PANEL_SETUP);
+
+ LLXMLNodePtr referenced_xml;
+ std::string xml_filename = mXMLFilename;
+
+ // if the panel didn't provide a filename, check the node
+ if (xml_filename.empty())
+ {
+ node->getAttributeString("filename", xml_filename);
+ setXMLFilename(xml_filename);
+ }
+
+ LLXUIParser parser;
+
+ if (!xml_filename.empty())
+ {
+ if (output_node)
+ {
+ //if we are exporting, we want to export the current xml
+ //not the referenced xml
+ parser.readXUI(node, params, LLUICtrlFactory::getInstance()->getCurFileName());
+ Params output_params(params);
+ 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_PANEL_LOAD);
+ if (!LLUICtrlFactory::getLayeredXMLNode(xml_filename, referenced_xml))
+ {
+ LL_WARNS() << "Couldn't parse panel from: " << xml_filename << LL_ENDL;
+
+ return false;
+ }
+
+ parser.readXUI(referenced_xml, params, LLUICtrlFactory::getInstance()->getCurFileName());
+
+ // add children using dimensions from referenced xml for consistent layout
+ setShape(params.rect);
+ LLUICtrlFactory::createChildren(this, referenced_xml, child_registry_t::instance());
+
+ LLUICtrlFactory::instance().popFileName();
+ }
+
+ // ask LLUICtrlFactory for filename, since xml_filename might be empty
+ parser.readXUI(node, params, LLUICtrlFactory::getInstance()->getCurFileName());
+
+ if (output_node)
+ {
+ Params output_params(params);
+ setupParamsForExport(output_params, parent);
+ output_node->setName(node->getName()->mString);
+ parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &default_params);
+ }
+
+ params.from_xui = true;
+ applyXUILayout(params, parent);
+ {
+ LL_RECORD_BLOCK_TIME(FTM_PANEL_CONSTRUCTION);
+ initFromParams(params);
+ }
+
+ // add children
+ LLUICtrlFactory::createChildren(this, node, child_registry_t::instance(), output_node);
+
+ // Connect to parent after children are built, because tab containers
+ // do a reshape() on their child panels, which requires that the children
+ // be built/added. JC
+ if (parent)
+ {
+ S32 tab_group = params.tab_group.isProvided() ? params.tab_group() : parent->getLastTabGroup();
+ parent->addChild(this, tab_group);
+ }
+
+ {
+ LL_RECORD_BLOCK_TIME(FTM_PANEL_POSTBUILD);
+ postBuild();
+ }
+ }
+ return true;
+}
+
+bool LLPanel::hasString(const std::string& name)
+{
+ return mUIStrings.find(name) != mUIStrings.end();
+}
+
+std::string LLPanel::getString(const std::string& name, const LLStringUtil::format_map_t& args) const
+{
+ ui_string_map_t::const_iterator found_it = mUIStrings.find(name);
+ if (found_it != mUIStrings.end())
+ {
+ // make a copy as format works in place
+ LLUIString formatted_string = LLUIString(found_it->second);
+ formatted_string.setArgList(args);
+ return formatted_string.getString();
+ }
+ std::string err_str("Failed to find string " + name + " in panel " + getName()); //*TODO: Translate
+ if(LLUI::getInstance()->mSettingGroups["config"]->getBOOL("QAMode"))
+ {
+ LL_ERRS() << err_str << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS() << err_str << LL_ENDL;
+ }
+ return LLStringUtil::null;
+}
+
+std::string LLPanel::getString(const std::string& name) const
+{
+ ui_string_map_t::const_iterator found_it = mUIStrings.find(name);
+ if (found_it != mUIStrings.end())
+ {
+ return found_it->second;
+ }
+ std::string err_str("Failed to find string " + name + " in panel " + getName()); //*TODO: Translate
+ if(LLUI::getInstance()->mSettingGroups["config"]->getBOOL("QAMode"))
+ {
+ LL_ERRS() << err_str << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS() << err_str << LL_ENDL;
+ }
+ return LLStringUtil::null;
+}
+
+
+void LLPanel::childSetVisible(const std::string& id, bool visible)
+{
+ LLView* child = findChild<LLView>(id);
+ if (child)
+ {
+ child->setVisible(visible);
+ }
+}
+
+void LLPanel::childSetEnabled(const std::string& id, bool enabled)
+{
+ LLView* child = findChild<LLView>(id);
+ if (child)
+ {
+ child->setEnabled(enabled);
+ }
+}
+
+void LLPanel::childSetFocus(const std::string& id, bool focus)
+{
+ LLUICtrl* child = findChild<LLUICtrl>(id);
+ if (child)
+ {
+ child->setFocus(focus);
+ }
+}
+
+bool LLPanel::childHasFocus(const std::string& id)
+{
+ LLUICtrl* child = findChild<LLUICtrl>(id);
+ if (child)
+ {
+ return child->hasFocus();
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// *TODO: Deprecate; for backwards compatability only:
+// Prefer getChild<LLUICtrl>("foo")->setCommitCallback(boost:bind(...)),
+// which takes a generic slot. Or use mCommitCallbackRegistrar.add() with
+// a named callback and reference it in XML.
+void LLPanel::childSetCommitCallback(const std::string& id, boost::function<void (LLUICtrl*,void*)> cb, void* data)
+{
+ LLUICtrl* child = findChild<LLUICtrl>(id);
+ if (child)
+ {
+ child->setCommitCallback(boost::bind(cb, child, data));
+ }
+}
+
+void LLPanel::childSetColor(const std::string& id, const LLColor4& color)
+{
+ LLUICtrl* child = findChild<LLUICtrl>(id);
+ if (child)
+ {
+ child->setColor(color);
+ }
+}
+
+LLCtrlSelectionInterface* LLPanel::childGetSelectionInterface(const std::string& id) const
+{
+ LLUICtrl* child = findChild<LLUICtrl>(id);
+ if (child)
+ {
+ return child->getSelectionInterface();
+ }
+ return NULL;
+}
+
+LLCtrlListInterface* LLPanel::childGetListInterface(const std::string& id) const
+{
+ LLUICtrl* child = findChild<LLUICtrl>(id);
+ if (child)
+ {
+ return child->getListInterface();
+ }
+ return NULL;
+}
+
+LLCtrlScrollInterface* LLPanel::childGetScrollInterface(const std::string& id) const
+{
+ LLUICtrl* child = findChild<LLUICtrl>(id);
+ if (child)
+ {
+ return child->getScrollInterface();
+ }
+ return NULL;
+}
+
+void LLPanel::childSetValue(const std::string& id, LLSD value)
+{
+ LLUICtrl* child = findChild<LLUICtrl>(id);
+ if (child)
+ {
+ child->setValue(value);
+ }
+}
+
+LLSD LLPanel::childGetValue(const std::string& id) const
+{
+ LLUICtrl* child = findChild<LLUICtrl>(id);
+ if (child)
+ {
+ return child->getValue();
+ }
+ // Not found => return undefined
+ return LLSD();
+}
+
+bool LLPanel::childSetTextArg(const std::string& id, const std::string& key, const LLStringExplicit& text)
+{
+ LLUICtrl* child = findChild<LLUICtrl>(id);
+ if (child)
+ {
+ return child->setTextArg(key, text);
+ }
+ return false;
+}
+
+bool LLPanel::childSetLabelArg(const std::string& id, const std::string& key, const LLStringExplicit& text)
+{
+ LLView* child = findChild<LLView>(id);
+ if (child)
+ {
+ return child->setLabelArg(key, text);
+ }
+ return false;
+}
+
+void LLPanel::childSetAction(const std::string& id, const commit_signal_t::slot_type& function)
+{
+ LLButton* button = findChild<LLButton>(id);
+ if (button)
+ {
+ button->setClickedCallback(function);
+ }
+}
+
+void LLPanel::childSetAction(const std::string& id, boost::function<void(void*)> function, void* value)
+{
+ LLButton* button = findChild<LLButton>(id);
+ if (button)
+ {
+ button->setClickedCallback(boost::bind(function, value));
+ }
+}
+
+boost::signals2::connection LLPanel::setVisibleCallback( const commit_signal_t::slot_type& cb )
+{
+ if (!mVisibleSignal)
+ {
+ mVisibleSignal = new commit_signal_t();
+ }
+
+ return mVisibleSignal->connect(cb);
+}
+
+//-----------------------------------------------------------------------------
+// buildPanel()
+//-----------------------------------------------------------------------------
+bool LLPanel::buildFromFile(const std::string& filename, const LLPanel::Params& default_params)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ bool didPost = false;
+ LLXMLNodePtr root;
+
+ if (!LLUICtrlFactory::getLayeredXMLNode(filename, root))
+ {
+ LL_WARNS() << "Couldn't parse panel from: " << filename << LL_ENDL;
+ return didPost;
+ }
+
+ // root must be called panel
+ if( !root->hasName("panel" ) )
+ {
+ LL_WARNS() << "Root node should be named panel in : " << filename << LL_ENDL;
+ return didPost;
+ }
+
+ LL_DEBUGS() << "Building panel " << filename << LL_ENDL;
+
+ LLUICtrlFactory::instance().pushFileName(filename);
+ {
+ if (!getFactoryMap().empty())
+ {
+ sFactoryStack.push_back(&getFactoryMap());
+ }
+
+ // for local registry callbacks; define in constructor, referenced in XUI or postBuild
+ getCommitCallbackRegistrar().pushScope();
+ getEnableCallbackRegistrar().pushScope();
+
+ didPost = initPanelXML(root, NULL, NULL, default_params);
+
+ getCommitCallbackRegistrar().popScope();
+ getEnableCallbackRegistrar().popScope();
+
+ setXMLFilename(filename);
+
+ if (!getFactoryMap().empty())
+ {
+ sFactoryStack.pop_back();
+ }
+ }
+ LLUICtrlFactory::instance().popFileName();
+ return didPost;
+}
+
+//-----------------------------------------------------------------------------
+// createFactoryPanel()
+//-----------------------------------------------------------------------------
+LLPanel* LLPanel::createFactoryPanel(const std::string& name)
+{
+ std::deque<const LLCallbackMap::map_t*>::iterator itor;
+ for (itor = sFactoryStack.begin(); itor != sFactoryStack.end(); ++itor)
+ {
+ const LLCallbackMap::map_t* factory_map = *itor;
+
+ // Look up this panel's name in the map.
+ LLCallbackMap::map_const_iter_t iter = factory_map->find( name );
+ if (iter != factory_map->end())
+ {
+ // Use the factory to create the panel, instead of using a default LLPanel.
+ LLPanel *ret = (LLPanel*) iter->second.mCallback( iter->second.mData );
+ return ret;
+ }
+ }
+ LLPanel::Params panel_p;
+ return LLUICtrlFactory::create<LLPanel>(panel_p);
+}
diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h
index 33883bf6a4..1088af0e04 100644
--- a/indra/llui/llpanel.h
+++ b/indra/llui/llpanel.h
@@ -1,318 +1,318 @@
-/**
- * @file llpanel.h
- * @author James Cook, Tom Yedwab
- * @brief LLPanel 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$
- */
-
-#ifndef LL_LLPANEL_H
-#define LL_LLPANEL_H
-
-
-#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>
-
-const S32 LLPANEL_BORDER_WIDTH = 1;
-const bool BORDER_YES = true;
-const bool BORDER_NO = false;
-
-class LLButton;
-class LLUIImage;
-
-/*
- * General purpose concrete view base class.
- * Transparent or opaque,
- * With or without border,
- * Can contain LLUICtrls.
- */
-class LLPanel : public LLUICtrl, public LLBadgeHolder
-{
-public:
- struct LocalizedString : public LLInitParam::Block<LocalizedString>
- {
- Mandatory<std::string> name;
- Mandatory<std::string> value;
-
- LocalizedString();
- };
-
- struct Params
- : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Optional<bool> has_border;
- Optional<LLViewBorder::Params> border;
-
- Optional<bool> background_visible,
- background_opaque;
-
- Optional<LLUIColor> bg_opaque_color,
- bg_alpha_color,
- bg_opaque_image_overlay,
- bg_alpha_image_overlay;
- // opaque image is for "panel in foreground" look
- Optional<LLUIImage*> bg_opaque_image,
- bg_alpha_image;
-
- Optional<S32> min_width,
- min_height;
-
- Optional<std::string> filename;
- Optional<std::string> class_name;
- Optional<std::string> help_topic;
-
- Multiple<LocalizedString> strings;
-
- Optional<CommitCallbackParam> visible_callback;
-
- Optional<bool> accepts_badge;
-
- Params();
- };
-
-protected:
- friend class LLUICtrlFactory;
- // RN: for some reason you can't just use LLUICtrlFactory::getDefaultParams as a default argument in VC8
- static const LLPanel::Params& getDefaultParams();
-
- // Panels can get constructed directly
- LLPanel(const LLPanel::Params& params = getDefaultParams());
-
-public:
- 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);
-
- /*virtual*/ ~LLPanel();
-
- // LLView interface
- /*virtual*/ bool isPanel() const;
- /*virtual*/ void draw();
- /*virtual*/ bool handleKeyHere( KEY key, MASK mask );
- /*virtual*/ void onVisibilityChange ( bool new_visibility );
-
- // From LLFocusableElement
- /*virtual*/ void setFocus( bool b );
-
- // New virtuals
- virtual void refresh(); // called in setFocus()
- virtual void clearCtrls(); // overridden in LLPanelObject and LLPanelVolume
-
- // Border controls
- const LLViewBorder* getBorder() const { return mBorder; }
- void addBorder( LLViewBorder::Params p);
- void addBorder();
- void removeBorder();
- bool hasBorder() const { return mBorder != NULL; }
- void setBorderVisible( bool b );
-
- void setBackgroundColor( const LLColor4& color ) { mBgOpaqueColor = color; }
- const LLColor4& getBackgroundColor() const { return mBgOpaqueColor; }
- void setTransparentColor(const LLColor4& color) { mBgAlphaColor = color; }
- const LLColor4& getTransparentColor() const { return mBgAlphaColor; }
- 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; }
- LLColor4 getTransparentImageOverlay() { return mBgAlphaImageOverlay; }
- void setBackgroundVisible( bool b ) { mBgVisible = b; }
- bool isBackgroundVisible() const { return mBgVisible; }
- void setBackgroundOpaque(bool b) { mBgOpaque = b; }
- bool isBackgroundOpaque() const { return mBgOpaque; }
- void setDefaultBtn(LLButton* btn = NULL);
- void setDefaultBtn(const std::string& id);
- void updateDefaultBtn();
- void setLabel(const LLStringExplicit& label) { mLabel = label; }
- std::string getLabel() const { return mLabel; }
- void setHelpTopic(const std::string& help_topic) { mHelpTopic = help_topic; }
- std::string getHelpTopic() const { return mHelpTopic; }
-
- void setCtrlsEnabled(bool b);
- ctrl_list_t getCtrlList() const;
-
- LLHandle<LLPanel> getHandle() const { return getDerivedHandle<LLPanel>(); }
-
- const LLCallbackMap::map_t& getFactoryMap() const { return mFactoryMap; }
-
- CommitCallbackRegistry::ScopedRegistrar& getCommitCallbackRegistrar() { return mCommitCallbackRegistrar; }
- EnableCallbackRegistry::ScopedRegistrar& getEnableCallbackRegistrar() { return mEnableCallbackRegistrar; }
-
- void initFromParams(const Params& p);
- bool initPanelXML( LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node, const LLPanel::Params& default_params);
-
- bool hasString(const std::string& name);
- std::string getString(const std::string& name, const LLStringUtil::format_map_t& args) const;
- std::string getString(const std::string& name) const;
-
- // ** Wrappers for setting child properties by name ** -TomY
- // WARNING: These are deprecated, please use getChild<T>("name")->doStuff() idiom instead
-
- // LLView
- void childSetVisible(const std::string& name, bool visible);
-
- 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); };
-
- // LLUICtrl
- void childSetFocus(const std::string& id, bool focus = true);
- bool childHasFocus(const std::string& id);
-
- // *TODO: Deprecate; for backwards compatability only:
- // Prefer getChild<LLUICtrl>("foo")->setCommitCallback(boost:bind(...)),
- // which takes a generic slot. Or use mCommitCallbackRegistrar.add() with
- // a named callback and reference it in XML.
- void childSetCommitCallback(const std::string& id, boost::function<void (LLUICtrl*,void*)> cb, void* data);
- void childSetColor(const std::string& id, const LLColor4& color);
-
- LLCtrlSelectionInterface* childGetSelectionInterface(const std::string& id) const;
- LLCtrlListInterface* childGetListInterface(const std::string& id) const;
- LLCtrlScrollInterface* childGetScrollInterface(const std::string& id) const;
-
- // This is the magic bullet for data-driven UI
- void childSetValue(const std::string& id, LLSD value);
- LLSD childGetValue(const std::string& id) const;
-
- // For setting text / label replacement params, e.g. "Hello [NAME]"
- // 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);
-
- // 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);
-
- static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node = NULL);
-
- //call onOpen to let panel know when it's about to be shown or activated
- virtual void onOpen(const LLSD& key) {}
-
- void setXMLFilename(std::string filename) { mXMLFilename = filename; };
- std::string getXMLFilename() { return mXMLFilename; };
-
- boost::signals2::connection setVisibleCallback( const commit_signal_t::slot_type& cb );
-
-protected:
- // Override to set not found list
- LLButton* getDefaultButton() { return mDefaultBtn; }
- LLCallbackMap::map_t mFactoryMap;
- CommitCallbackRegistry::ScopedRegistrar mCommitCallbackRegistrar;
- EnableCallbackRegistry::ScopedRegistrar mEnableCallbackRegistrar;
-
- commit_signal_t* mVisibleSignal; // Called when visibility changes, passes new visibility as LLSD()
-
- std::string mHelpTopic; // the name of this panel's help topic to display in the Help Viewer
- 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?
- bool mBgOpaque; // use opaque color or image
- LLUIColor mBgOpaqueColor;
- LLUIColor mBgAlphaColor;
- LLUIColor mBgOpaqueImageOverlay;
- LLUIColor mBgAlphaImageOverlay;
- LLPointer<LLUIImage> mBgOpaqueImage; // "panel in front" look
- LLPointer<LLUIImage> mBgAlphaImage; // "panel in back" look
- LLViewBorder* mBorder;
- LLButton* mDefaultBtn;
- LLUIString mLabel;
-
- typedef std::map<std::string, std::string> ui_string_map_t;
- ui_string_map_t mUIStrings;
-
-
-}; // end class LLPanel
-
-// Build time optimization, generate once in .cpp file
-#ifndef LLPANEL_CPP
-extern template class LLPanel* LLView::getChild<class LLPanel>(
- const std::string& name, bool recurse) const;
-#endif
-
-typedef boost::function<LLPanel* (void)> LLPanelClassCreatorFunc;
-
-// local static instance for registering a particular panel class
-
-class LLRegisterPanelClass
-: public LLSingleton< LLRegisterPanelClass >
-{
- LLSINGLETON_EMPTY_CTOR(LLRegisterPanelClass);
-public:
- // register with either the provided builder, or the generic templated builder
- void addPanelClass(const std::string& tag,LLPanelClassCreatorFunc func)
- {
- mPanelClassesNames[tag] = func;
- }
-
- LLPanel* createPanelClass(const std::string& tag)
- {
- param_name_map_t::iterator iT = mPanelClassesNames.find(tag);
- if(iT == mPanelClassesNames.end())
- return 0;
- return iT->second();
- }
- template<typename T>
- static T* defaultPanelClassBuilder()
- {
- T* pT = new T();
- return pT;
- }
-
-private:
- typedef std::map< std::string, LLPanelClassCreatorFunc> param_name_map_t;
-
- param_name_map_t mPanelClassesNames;
-};
-
-
-// local static instance for registering a particular panel class
-template<typename T>
- class LLPanelInjector
-{
-public:
- // register with either the provided builder, or the generic templated builder
- LLPanelInjector(const std::string& tag);
-};
-
-
-template<typename T>
- LLPanelInjector<T>::LLPanelInjector(const std::string& tag)
-{
- LLRegisterPanelClass::instance().addPanelClass(tag,&LLRegisterPanelClass::defaultPanelClassBuilder<T>);
-}
-
-
-#endif
+/**
+ * @file llpanel.h
+ * @author James Cook, Tom Yedwab
+ * @brief LLPanel 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$
+ */
+
+#ifndef LL_LLPANEL_H
+#define LL_LLPANEL_H
+
+
+#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>
+
+const S32 LLPANEL_BORDER_WIDTH = 1;
+const bool BORDER_YES = true;
+const bool BORDER_NO = false;
+
+class LLButton;
+class LLUIImage;
+
+/*
+ * General purpose concrete view base class.
+ * Transparent or opaque,
+ * With or without border,
+ * Can contain LLUICtrls.
+ */
+class LLPanel : public LLUICtrl, public LLBadgeHolder
+{
+public:
+ struct LocalizedString : public LLInitParam::Block<LocalizedString>
+ {
+ Mandatory<std::string> name;
+ Mandatory<std::string> value;
+
+ LocalizedString();
+ };
+
+ struct Params
+ : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<bool> has_border;
+ Optional<LLViewBorder::Params> border;
+
+ Optional<bool> background_visible,
+ background_opaque;
+
+ Optional<LLUIColor> bg_opaque_color,
+ bg_alpha_color,
+ bg_opaque_image_overlay,
+ bg_alpha_image_overlay;
+ // opaque image is for "panel in foreground" look
+ Optional<LLUIImage*> bg_opaque_image,
+ bg_alpha_image;
+
+ Optional<S32> min_width,
+ min_height;
+
+ Optional<std::string> filename;
+ Optional<std::string> class_name;
+ Optional<std::string> help_topic;
+
+ Multiple<LocalizedString> strings;
+
+ Optional<CommitCallbackParam> visible_callback;
+
+ Optional<bool> accepts_badge;
+
+ Params();
+ };
+
+protected:
+ friend class LLUICtrlFactory;
+ // RN: for some reason you can't just use LLUICtrlFactory::getDefaultParams as a default argument in VC8
+ static const LLPanel::Params& getDefaultParams();
+
+ // Panels can get constructed directly
+ LLPanel(const LLPanel::Params& params = getDefaultParams());
+
+public:
+ 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);
+
+ /*virtual*/ ~LLPanel();
+
+ // LLView interface
+ /*virtual*/ bool isPanel() const;
+ /*virtual*/ void draw();
+ /*virtual*/ bool handleKeyHere( KEY key, MASK mask );
+ /*virtual*/ void onVisibilityChange ( bool new_visibility );
+
+ // From LLFocusableElement
+ /*virtual*/ void setFocus( bool b );
+
+ // New virtuals
+ virtual void refresh(); // called in setFocus()
+ virtual void clearCtrls(); // overridden in LLPanelObject and LLPanelVolume
+
+ // Border controls
+ const LLViewBorder* getBorder() const { return mBorder; }
+ void addBorder( LLViewBorder::Params p);
+ void addBorder();
+ void removeBorder();
+ bool hasBorder() const { return mBorder != NULL; }
+ void setBorderVisible( bool b );
+
+ void setBackgroundColor( const LLColor4& color ) { mBgOpaqueColor = color; }
+ const LLColor4& getBackgroundColor() const { return mBgOpaqueColor; }
+ void setTransparentColor(const LLColor4& color) { mBgAlphaColor = color; }
+ const LLColor4& getTransparentColor() const { return mBgAlphaColor; }
+ 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; }
+ LLColor4 getTransparentImageOverlay() { return mBgAlphaImageOverlay; }
+ void setBackgroundVisible( bool b ) { mBgVisible = b; }
+ bool isBackgroundVisible() const { return mBgVisible; }
+ void setBackgroundOpaque(bool b) { mBgOpaque = b; }
+ bool isBackgroundOpaque() const { return mBgOpaque; }
+ void setDefaultBtn(LLButton* btn = NULL);
+ void setDefaultBtn(const std::string& id);
+ void updateDefaultBtn();
+ void setLabel(const LLStringExplicit& label) { mLabel = label; }
+ std::string getLabel() const { return mLabel; }
+ void setHelpTopic(const std::string& help_topic) { mHelpTopic = help_topic; }
+ std::string getHelpTopic() const { return mHelpTopic; }
+
+ void setCtrlsEnabled(bool b);
+ ctrl_list_t getCtrlList() const;
+
+ LLHandle<LLPanel> getHandle() const { return getDerivedHandle<LLPanel>(); }
+
+ const LLCallbackMap::map_t& getFactoryMap() const { return mFactoryMap; }
+
+ CommitCallbackRegistry::ScopedRegistrar& getCommitCallbackRegistrar() { return mCommitCallbackRegistrar; }
+ EnableCallbackRegistry::ScopedRegistrar& getEnableCallbackRegistrar() { return mEnableCallbackRegistrar; }
+
+ void initFromParams(const Params& p);
+ bool initPanelXML( LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node, const LLPanel::Params& default_params);
+
+ bool hasString(const std::string& name);
+ std::string getString(const std::string& name, const LLStringUtil::format_map_t& args) const;
+ std::string getString(const std::string& name) const;
+
+ // ** Wrappers for setting child properties by name ** -TomY
+ // WARNING: These are deprecated, please use getChild<T>("name")->doStuff() idiom instead
+
+ // LLView
+ void childSetVisible(const std::string& name, bool visible);
+
+ 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); };
+
+ // LLUICtrl
+ void childSetFocus(const std::string& id, bool focus = true);
+ bool childHasFocus(const std::string& id);
+
+ // *TODO: Deprecate; for backwards compatability only:
+ // Prefer getChild<LLUICtrl>("foo")->setCommitCallback(boost:bind(...)),
+ // which takes a generic slot. Or use mCommitCallbackRegistrar.add() with
+ // a named callback and reference it in XML.
+ void childSetCommitCallback(const std::string& id, boost::function<void (LLUICtrl*,void*)> cb, void* data);
+ void childSetColor(const std::string& id, const LLColor4& color);
+
+ LLCtrlSelectionInterface* childGetSelectionInterface(const std::string& id) const;
+ LLCtrlListInterface* childGetListInterface(const std::string& id) const;
+ LLCtrlScrollInterface* childGetScrollInterface(const std::string& id) const;
+
+ // This is the magic bullet for data-driven UI
+ void childSetValue(const std::string& id, LLSD value);
+ LLSD childGetValue(const std::string& id) const;
+
+ // For setting text / label replacement params, e.g. "Hello [NAME]"
+ // 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);
+
+ // 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);
+
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node = NULL);
+
+ //call onOpen to let panel know when it's about to be shown or activated
+ virtual void onOpen(const LLSD& key) {}
+
+ void setXMLFilename(std::string filename) { mXMLFilename = filename; };
+ std::string getXMLFilename() { return mXMLFilename; };
+
+ boost::signals2::connection setVisibleCallback( const commit_signal_t::slot_type& cb );
+
+protected:
+ // Override to set not found list
+ LLButton* getDefaultButton() { return mDefaultBtn; }
+ LLCallbackMap::map_t mFactoryMap;
+ CommitCallbackRegistry::ScopedRegistrar mCommitCallbackRegistrar;
+ EnableCallbackRegistry::ScopedRegistrar mEnableCallbackRegistrar;
+
+ commit_signal_t* mVisibleSignal; // Called when visibility changes, passes new visibility as LLSD()
+
+ std::string mHelpTopic; // the name of this panel's help topic to display in the Help Viewer
+ 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?
+ bool mBgOpaque; // use opaque color or image
+ LLUIColor mBgOpaqueColor;
+ LLUIColor mBgAlphaColor;
+ LLUIColor mBgOpaqueImageOverlay;
+ LLUIColor mBgAlphaImageOverlay;
+ LLPointer<LLUIImage> mBgOpaqueImage; // "panel in front" look
+ LLPointer<LLUIImage> mBgAlphaImage; // "panel in back" look
+ LLViewBorder* mBorder;
+ LLButton* mDefaultBtn;
+ LLUIString mLabel;
+
+ typedef std::map<std::string, std::string> ui_string_map_t;
+ ui_string_map_t mUIStrings;
+
+
+}; // end class LLPanel
+
+// Build time optimization, generate once in .cpp file
+#ifndef LLPANEL_CPP
+extern template class LLPanel* LLView::getChild<class LLPanel>(
+ const std::string& name, bool recurse) const;
+#endif
+
+typedef boost::function<LLPanel* (void)> LLPanelClassCreatorFunc;
+
+// local static instance for registering a particular panel class
+
+class LLRegisterPanelClass
+: public LLSingleton< LLRegisterPanelClass >
+{
+ LLSINGLETON_EMPTY_CTOR(LLRegisterPanelClass);
+public:
+ // register with either the provided builder, or the generic templated builder
+ void addPanelClass(const std::string& tag,LLPanelClassCreatorFunc func)
+ {
+ mPanelClassesNames[tag] = func;
+ }
+
+ LLPanel* createPanelClass(const std::string& tag)
+ {
+ param_name_map_t::iterator iT = mPanelClassesNames.find(tag);
+ if(iT == mPanelClassesNames.end())
+ return 0;
+ return iT->second();
+ }
+ template<typename T>
+ static T* defaultPanelClassBuilder()
+ {
+ T* pT = new T();
+ return pT;
+ }
+
+private:
+ typedef std::map< std::string, LLPanelClassCreatorFunc> param_name_map_t;
+
+ param_name_map_t mPanelClassesNames;
+};
+
+
+// local static instance for registering a particular panel class
+template<typename T>
+ class LLPanelInjector
+{
+public:
+ // register with either the provided builder, or the generic templated builder
+ LLPanelInjector(const std::string& tag);
+};
+
+
+template<typename T>
+ LLPanelInjector<T>::LLPanelInjector(const std::string& tag)
+{
+ LLRegisterPanelClass::instance().addPanelClass(tag,&LLRegisterPanelClass::defaultPanelClassBuilder<T>);
+}
+
+
+#endif
diff --git a/indra/llui/llprogressbar.cpp b/indra/llui/llprogressbar.cpp
index cf57b1fe76..02cc1a5fcb 100644
--- a/indra/llui/llprogressbar.cpp
+++ b/indra/llui/llprogressbar.cpp
@@ -1,25 +1,25 @@
-/**
+/**
* @file llprogressbar.cpp
* @brief LLProgressBar class implementation
*
* $LicenseInfo:firstyear=2002&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$
*/
@@ -43,32 +43,32 @@
static LLDefaultChildRegistry::Register<LLProgressBar> r("progress_bar");
LLProgressBar::Params::Params()
-: image_bar("image_bar"),
- image_fill("image_fill"),
- color_bar("color_bar"),
- color_bg("color_bg")
+: image_bar("image_bar"),
+ image_fill("image_fill"),
+ color_bar("color_bar"),
+ color_bg("color_bg")
{}
-LLProgressBar::LLProgressBar(const LLProgressBar::Params& p)
-: LLUICtrl(p),
- mImageBar(p.image_bar),
- mImageFill(p.image_fill),
- mColorBackground(p.color_bg()),
- mColorBar(p.color_bar()),
- mPercentDone(0.f)
+LLProgressBar::LLProgressBar(const LLProgressBar::Params& p)
+: LLUICtrl(p),
+ mImageBar(p.image_bar),
+ mImageFill(p.image_fill),
+ mColorBackground(p.color_bg()),
+ mColorBar(p.color_bar()),
+ mPercentDone(0.f)
{}
LLProgressBar::~LLProgressBar()
{
- gFocusMgr.releaseFocusIfNeeded( this );
+ gFocusMgr.releaseFocusIfNeeded( this );
}
void LLProgressBar::draw()
{
- static LLTimer timer;
- F32 alpha = getDrawContext().mAlpha;
-
+ static LLTimer timer;
+ F32 alpha = getDrawContext().mAlpha;
+
if (mImageBar) // optional according to parameters
{
LLColor4 image_bar_color = mColorBackground.get();
@@ -89,5 +89,5 @@ void LLProgressBar::draw()
void LLProgressBar::setValue(const LLSD& value)
{
- mPercentDone = llclamp((F32)value.asReal(), 0.f, 100.f);
+ mPercentDone = llclamp((F32)value.asReal(), 0.f, 100.f);
}
diff --git a/indra/llui/llprogressbar.h b/indra/llui/llprogressbar.h
index a8ec83ea00..0d5d32cf21 100644
--- a/indra/llui/llprogressbar.h
+++ b/indra/llui/llprogressbar.h
@@ -1,25 +1,25 @@
-/**
+/**
* @file llprogressbar.h
* @brief LLProgressBar class definition
*
* $LicenseInfo:firstyear=2002&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$
*/
@@ -32,35 +32,35 @@
#include "lluiimage.h"
class LLProgressBar
- : public LLUICtrl
+ : public LLUICtrl
{
public:
- struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Optional<LLUIImage*> image_bar,
- image_fill;
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<LLUIImage*> image_bar,
+ image_fill;
- Optional<LLUIColor> color_bar,
- color_bg;
+ Optional<LLUIColor> color_bar,
+ color_bg;
- Params();
- };
- LLProgressBar(const Params&);
- virtual ~LLProgressBar();
+ Params();
+ };
+ LLProgressBar(const Params&);
+ virtual ~LLProgressBar();
- void setValue(const LLSD& value);
+ void setValue(const LLSD& value);
- /*virtual*/ void draw();
+ /*virtual*/ void draw();
private:
- F32 mPercentDone;
+ F32 mPercentDone;
+
+ LLPointer<LLUIImage> mImageBar;
+ LLUIColor mColorBar;
- LLPointer<LLUIImage> mImageBar;
- LLUIColor mColorBar;
+ LLUIColor mColorBackground;
- LLUIColor mColorBackground;
-
- LLPointer<LLUIImage> mImageFill;
+ LLPointer<LLUIImage> mImageFill;
};
#endif // LL_LLPROGRESSBAR_H
diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp
index 2a45a8118e..e542dac019 100644
--- a/indra/llui/llradiogroup.cpp
+++ b/indra/llui/llradiogroup.cpp
@@ -1,521 +1,521 @@
-/**
- * @file llradiogroup.cpp
- * @brief LLRadioGroup base class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-
-#include "linden_common.h"
-
-#include "llboost.h"
-
-#include "llradiogroup.h"
-#include "indra_constants.h"
-
-#include "llviewborder.h"
-#include "llcontrol.h"
-#include "llui.h"
-#include "llfocusmgr.h"
-#include "lluictrlfactory.h"
-#include "llsdutil.h"
-
-static LLDefaultChildRegistry::Register<LLRadioGroup> r1("radio_group");
-
-/*
- * An invisible view containing multiple mutually exclusive toggling
- * buttons (usually radio buttons). Automatically handles the mutex
- * condition by highlighting only one button at a time.
- */
-class LLRadioCtrl : public LLCheckBoxCtrl
-{
-public:
- typedef LLRadioGroup::ItemParams Params;
- /*virtual*/ ~LLRadioCtrl();
- /*virtual*/ void setValue(const LLSD& value);
-
- /*virtual*/ bool postBuild();
- /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
-
- LLSD getPayload() { return mPayload; }
-
- // Ensure label is in an attribute, not the contents
- static void setupParamsForExport(Params& p, LLView* parent);
-
-protected:
- LLRadioCtrl(const LLRadioGroup::ItemParams& p);
- friend class LLUICtrlFactory;
-
- LLSD mPayload; // stores data that this item represents in the radio group
-};
-static LLWidgetNameRegistry::StaticRegistrar register_radio_item(&typeid(LLRadioGroup::ItemParams), "radio_item");
-
-LLRadioGroup::Params::Params()
-: allow_deselect("allow_deselect"),
- items("item")
-{
- addSynonym(items, "radio_item");
-
- // radio items are not tabbable until they are selected
- tab_stop = false;
-}
-
-LLRadioGroup::LLRadioGroup(const LLRadioGroup::Params& p)
-: LLUICtrl(p),
- mFont(p.font.isProvided() ? p.font() : LLFontGL::getFontSansSerifSmall()),
- mSelectedIndex(-1),
- mAllowDeselect(p.allow_deselect)
-{}
-
-void LLRadioGroup::initFromParams(const Params& p)
-{
- for (LLInitParam::ParamIterator<ItemParams>::const_iterator it = p.items.begin();
- it != p.items.end();
- ++it)
- {
- LLRadioGroup::ItemParams item_params(*it);
-
- 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)
- {
- applyXUILayout(item_params, this);
- }
-
- LLRadioCtrl* item = LLUICtrlFactory::create<LLRadioCtrl>(item_params, this);
- mRadioButtons.push_back(item);
- }
-
- // call this *after* setting up mRadioButtons so we can handle setValue() calls
- LLUICtrl::initFromParams(p);
-}
-
-
-LLRadioGroup::~LLRadioGroup()
-{
-}
-
-// virtual
-bool LLRadioGroup::postBuild()
-{
- if (!mRadioButtons.empty())
- {
- mRadioButtons[0]->setTabStop(true);
- }
- return true;
-}
-
-void LLRadioGroup::setIndexEnabled(S32 index, bool enabled)
-{
- S32 count = 0;
- for (button_list_t::iterator iter = mRadioButtons.begin();
- iter != mRadioButtons.end(); ++iter)
- {
- LLRadioCtrl* child = *iter;
- if (count == index)
- {
- child->setEnabled(enabled);
- if (index == mSelectedIndex && !enabled)
- {
- setSelectedIndex(-1);
- }
- break;
- }
- count++;
- }
- count = 0;
- if (mSelectedIndex < 0)
- {
- // Set to highest enabled value < index,
- // or lowest value above index if none lower are enabled
- // or 0 if none are enabled
- for (button_list_t::iterator iter = mRadioButtons.begin();
- iter != mRadioButtons.end(); ++iter)
- {
- LLRadioCtrl* child = *iter;
- if (count >= index && mSelectedIndex >= 0)
- {
- break;
- }
- if (child->getEnabled())
- {
- setSelectedIndex(count);
- }
- count++;
- }
- if (mSelectedIndex < 0)
- {
- setSelectedIndex(0);
- }
- }
-}
-
-bool LLRadioGroup::setSelectedIndex(S32 index, bool from_event)
-{
- if ((S32)mRadioButtons.size() <= index )
- {
- return false;
- }
-
- if (index < -1)
- {
- // less then minimum value
- return false;
- }
-
- if (index < 0 && mSelectedIndex >= 0 && !mAllowDeselect)
- {
- // -1 is "nothing selected"
- return false;
- }
-
- if (mSelectedIndex >= 0)
- {
- LLRadioCtrl* old_radio_item = mRadioButtons[mSelectedIndex];
- old_radio_item->setTabStop(false);
- old_radio_item->setValue( false );
- }
- else
- {
- mRadioButtons[0]->setTabStop(false);
- }
-
- mSelectedIndex = index;
-
- if (mSelectedIndex >= 0)
- {
- LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex];
- radio_item->setTabStop(true);
- radio_item->setValue( true );
-
- if (hasFocus())
- {
- radio_item->focusFirstItem(false, false);
- }
- }
-
- if (!from_event)
- {
- setControlValue(getValue());
- }
-
- return true;
-}
-
-void LLRadioGroup::focusSelectedRadioBtn()
-{
- if (mSelectedIndex >= 0)
- {
- LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex];
- if (radio_item->hasTabStop() && radio_item->getEnabled())
- {
- radio_item->focusFirstItem(false, false);
- }
- }
- else if (mRadioButtons[0]->hasTabStop() || hasTabStop())
- {
- focusFirstItem(false, false);
- }
-}
-
-bool LLRadioGroup::handleKeyHere(KEY key, MASK mask)
-{
- bool handled = false;
- // do any of the tab buttons have keyboard focus?
- if (mask == MASK_NONE)
- {
- switch(key)
- {
- case KEY_DOWN:
- if (!setSelectedIndex((getSelectedIndex() + 1)))
- {
- make_ui_sound("UISndInvalidOp");
- }
- else
- {
- onCommit();
- }
- handled = true;
- break;
- case KEY_UP:
- if (!setSelectedIndex((getSelectedIndex() - 1)))
- {
- make_ui_sound("UISndInvalidOp");
- }
- else
- {
- onCommit();
- }
- handled = true;
- break;
- case KEY_LEFT:
- if (!setSelectedIndex((getSelectedIndex() - 1)))
- {
- make_ui_sound("UISndInvalidOp");
- }
- else
- {
- onCommit();
- }
- handled = true;
- break;
- case KEY_RIGHT:
- if (!setSelectedIndex((getSelectedIndex() + 1)))
- {
- make_ui_sound("UISndInvalidOp");
- }
- else
- {
- onCommit();
- }
- handled = true;
- break;
- default:
- break;
- }
- }
- return handled;
-}
-
-// Handle one button being clicked. All child buttons must have this
-// function as their callback function.
-
-void LLRadioGroup::onClickButton(LLUICtrl* ctrl)
-{
- // LL_INFOS() << "LLRadioGroup::onClickButton" << LL_ENDL;
- LLRadioCtrl* clicked_radio = dynamic_cast<LLRadioCtrl*>(ctrl);
- if (!clicked_radio)
- return;
- S32 index = 0;
- for (button_list_t::iterator iter = mRadioButtons.begin();
- iter != mRadioButtons.end(); ++iter)
- {
- LLRadioCtrl* radio = *iter;
- if (radio == clicked_radio)
- {
- if (index == mSelectedIndex && mAllowDeselect)
- {
- // don't select anything
- setSelectedIndex(-1);
- }
- else
- {
- setSelectedIndex(index);
- }
-
- // BUG: Calls click callback even if button didn't actually change
- onCommit();
-
- return;
- }
-
- index++;
- }
-
- LL_WARNS() << "LLRadioGroup::onClickButton - clicked button that isn't a child" << LL_ENDL;
-}
-
-void LLRadioGroup::setValue( const LLSD& value )
-{
- int idx = 0;
- for (button_list_t::const_iterator iter = mRadioButtons.begin();
- iter != mRadioButtons.end(); ++iter)
- {
- LLRadioCtrl* radio = *iter;
- if (radio->getPayload().asString() == value.asString())
- {
- setSelectedIndex(idx);
- idx = -1;
- break;
- }
- ++idx;
- }
- if (idx != -1)
- {
- // string not found, try integer
- if (value.isInteger())
- {
- setSelectedIndex((S32) value.asInteger(), true);
- }
- else
- {
- setSelectedIndex(-1, true);
- }
- }
-}
-
-LLSD LLRadioGroup::getValue() const
-{
- int index = getSelectedIndex();
- int idx = 0;
- for (button_list_t::const_iterator iter = mRadioButtons.begin();
- iter != mRadioButtons.end(); ++iter)
- {
- if (idx == index) return LLSD((*iter)->getPayload());
- ++idx;
- }
- return LLSD();
-}
-
-// LLCtrlSelectionInterface functions
-bool LLRadioGroup::setCurrentByID( const LLUUID& id )
-{
- return false;
-}
-
-LLUUID LLRadioGroup::getCurrentID() const
-{
- return LLUUID::null;
-}
-
-bool LLRadioGroup::setSelectedByValue(const LLSD& value, bool selected)
-{
- S32 idx = 0;
- for (button_list_t::const_iterator iter = mRadioButtons.begin();
- iter != mRadioButtons.end(); ++iter)
- {
- if((*iter)->getPayload().asString() == value.asString())
- {
- setSelectedIndex(idx);
- return true;
- }
- idx++;
- }
-
- return false;
-}
-
-LLSD LLRadioGroup::getSelectedValue()
-{
- return getValue();
-}
-
-bool LLRadioGroup::isSelected(const LLSD& value) const
-{
- S32 idx = 0;
- for (button_list_t::const_iterator iter = mRadioButtons.begin();
- iter != mRadioButtons.end(); ++iter)
- {
- if((*iter)->getPayload().asString() == value.asString())
- {
- if (idx == mSelectedIndex)
- {
- return true;
- }
- }
- idx++;
- }
- return false;
-}
-
-bool LLRadioGroup::operateOnSelection(EOperation op)
-{
- return false;
-}
-
-bool LLRadioGroup::operateOnAll(EOperation op)
-{
- return false;
-}
-
-LLRadioGroup::ItemParams::ItemParams()
-: value("value")
-{
- addSynonym(value, "initial_value");
-}
-
-LLRadioCtrl::LLRadioCtrl(const LLRadioGroup::ItemParams& p)
-: LLCheckBoxCtrl(p),
- mPayload(p.value)
-{
- // use name as default "Value" for backwards compatibility
- if (!p.value.isProvided())
- {
- mPayload = p.name();
- }
-}
-
-bool LLRadioCtrl::postBuild()
-{
- // Old-style radio_item used the text contents to indicate the label,
- // but new-style radio_item uses label attribute.
- std::string value = getValue().asString();
- if (!value.empty())
- {
- setLabel(value);
- }
- return true;
-}
-
-bool LLRadioCtrl::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- // Grab focus preemptively, before button takes mousecapture
- if (hasTabStop() && getEnabled())
- {
- focusFirstItem(false, false);
- }
- else
- {
- // Only currently selected item in group has tab stop as result it is
- // unclear how focus should behave on click, just let the group handle
- // focus and LLRadioGroup::onClickButton() will set correct state later
- // if needed
- LLRadioGroup* parent = (LLRadioGroup*)getParent();
- if (parent)
- {
- parent->focusSelectedRadioBtn();
- }
- }
-
- return LLCheckBoxCtrl::handleMouseDown(x, y, mask);
-}
-
-LLRadioCtrl::~LLRadioCtrl()
-{
-}
-
-void LLRadioCtrl::setValue(const LLSD& value)
-{
- LLCheckBoxCtrl::setValue(value);
- mButton->setTabStop(value.asBoolean());
-}
-
-// *TODO: Remove this function after the initial XUI XML re-export pass.
-// static
-void LLRadioCtrl::setupParamsForExport(Params& p, LLView* parent)
-{
- std::string label = p.label;
- if (label.empty())
- {
- // We don't have a label attribute, so move the text contents
- // stored in "value" into the label
- std::string initial_value = p.LLUICtrl::Params::initial_value();
- p.label = initial_value;
- p.LLUICtrl::Params::initial_value = LLSD();
- }
-
- LLCheckBoxCtrl::setupParamsForExport(p, parent);
-}
+/**
+ * @file llradiogroup.cpp
+ * @brief LLRadioGroup base class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+#include "linden_common.h"
+
+#include "llboost.h"
+
+#include "llradiogroup.h"
+#include "indra_constants.h"
+
+#include "llviewborder.h"
+#include "llcontrol.h"
+#include "llui.h"
+#include "llfocusmgr.h"
+#include "lluictrlfactory.h"
+#include "llsdutil.h"
+
+static LLDefaultChildRegistry::Register<LLRadioGroup> r1("radio_group");
+
+/*
+ * An invisible view containing multiple mutually exclusive toggling
+ * buttons (usually radio buttons). Automatically handles the mutex
+ * condition by highlighting only one button at a time.
+ */
+class LLRadioCtrl : public LLCheckBoxCtrl
+{
+public:
+ typedef LLRadioGroup::ItemParams Params;
+ /*virtual*/ ~LLRadioCtrl();
+ /*virtual*/ void setValue(const LLSD& value);
+
+ /*virtual*/ bool postBuild();
+ /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
+
+ LLSD getPayload() { return mPayload; }
+
+ // Ensure label is in an attribute, not the contents
+ static void setupParamsForExport(Params& p, LLView* parent);
+
+protected:
+ LLRadioCtrl(const LLRadioGroup::ItemParams& p);
+ friend class LLUICtrlFactory;
+
+ LLSD mPayload; // stores data that this item represents in the radio group
+};
+static LLWidgetNameRegistry::StaticRegistrar register_radio_item(&typeid(LLRadioGroup::ItemParams), "radio_item");
+
+LLRadioGroup::Params::Params()
+: allow_deselect("allow_deselect"),
+ items("item")
+{
+ addSynonym(items, "radio_item");
+
+ // radio items are not tabbable until they are selected
+ tab_stop = false;
+}
+
+LLRadioGroup::LLRadioGroup(const LLRadioGroup::Params& p)
+: LLUICtrl(p),
+ mFont(p.font.isProvided() ? p.font() : LLFontGL::getFontSansSerifSmall()),
+ mSelectedIndex(-1),
+ mAllowDeselect(p.allow_deselect)
+{}
+
+void LLRadioGroup::initFromParams(const Params& p)
+{
+ for (LLInitParam::ParamIterator<ItemParams>::const_iterator it = p.items.begin();
+ it != p.items.end();
+ ++it)
+ {
+ LLRadioGroup::ItemParams item_params(*it);
+
+ 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)
+ {
+ applyXUILayout(item_params, this);
+ }
+
+ LLRadioCtrl* item = LLUICtrlFactory::create<LLRadioCtrl>(item_params, this);
+ mRadioButtons.push_back(item);
+ }
+
+ // call this *after* setting up mRadioButtons so we can handle setValue() calls
+ LLUICtrl::initFromParams(p);
+}
+
+
+LLRadioGroup::~LLRadioGroup()
+{
+}
+
+// virtual
+bool LLRadioGroup::postBuild()
+{
+ if (!mRadioButtons.empty())
+ {
+ mRadioButtons[0]->setTabStop(true);
+ }
+ return true;
+}
+
+void LLRadioGroup::setIndexEnabled(S32 index, bool enabled)
+{
+ S32 count = 0;
+ for (button_list_t::iterator iter = mRadioButtons.begin();
+ iter != mRadioButtons.end(); ++iter)
+ {
+ LLRadioCtrl* child = *iter;
+ if (count == index)
+ {
+ child->setEnabled(enabled);
+ if (index == mSelectedIndex && !enabled)
+ {
+ setSelectedIndex(-1);
+ }
+ break;
+ }
+ count++;
+ }
+ count = 0;
+ if (mSelectedIndex < 0)
+ {
+ // Set to highest enabled value < index,
+ // or lowest value above index if none lower are enabled
+ // or 0 if none are enabled
+ for (button_list_t::iterator iter = mRadioButtons.begin();
+ iter != mRadioButtons.end(); ++iter)
+ {
+ LLRadioCtrl* child = *iter;
+ if (count >= index && mSelectedIndex >= 0)
+ {
+ break;
+ }
+ if (child->getEnabled())
+ {
+ setSelectedIndex(count);
+ }
+ count++;
+ }
+ if (mSelectedIndex < 0)
+ {
+ setSelectedIndex(0);
+ }
+ }
+}
+
+bool LLRadioGroup::setSelectedIndex(S32 index, bool from_event)
+{
+ if ((S32)mRadioButtons.size() <= index )
+ {
+ return false;
+ }
+
+ if (index < -1)
+ {
+ // less then minimum value
+ return false;
+ }
+
+ if (index < 0 && mSelectedIndex >= 0 && !mAllowDeselect)
+ {
+ // -1 is "nothing selected"
+ return false;
+ }
+
+ if (mSelectedIndex >= 0)
+ {
+ LLRadioCtrl* old_radio_item = mRadioButtons[mSelectedIndex];
+ old_radio_item->setTabStop(false);
+ old_radio_item->setValue( false );
+ }
+ else
+ {
+ mRadioButtons[0]->setTabStop(false);
+ }
+
+ mSelectedIndex = index;
+
+ if (mSelectedIndex >= 0)
+ {
+ LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex];
+ radio_item->setTabStop(true);
+ radio_item->setValue( true );
+
+ if (hasFocus())
+ {
+ radio_item->focusFirstItem(false, false);
+ }
+ }
+
+ if (!from_event)
+ {
+ setControlValue(getValue());
+ }
+
+ return true;
+}
+
+void LLRadioGroup::focusSelectedRadioBtn()
+{
+ if (mSelectedIndex >= 0)
+ {
+ LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex];
+ if (radio_item->hasTabStop() && radio_item->getEnabled())
+ {
+ radio_item->focusFirstItem(false, false);
+ }
+ }
+ else if (mRadioButtons[0]->hasTabStop() || hasTabStop())
+ {
+ focusFirstItem(false, false);
+ }
+}
+
+bool LLRadioGroup::handleKeyHere(KEY key, MASK mask)
+{
+ bool handled = false;
+ // do any of the tab buttons have keyboard focus?
+ if (mask == MASK_NONE)
+ {
+ switch(key)
+ {
+ case KEY_DOWN:
+ if (!setSelectedIndex((getSelectedIndex() + 1)))
+ {
+ make_ui_sound("UISndInvalidOp");
+ }
+ else
+ {
+ onCommit();
+ }
+ handled = true;
+ break;
+ case KEY_UP:
+ if (!setSelectedIndex((getSelectedIndex() - 1)))
+ {
+ make_ui_sound("UISndInvalidOp");
+ }
+ else
+ {
+ onCommit();
+ }
+ handled = true;
+ break;
+ case KEY_LEFT:
+ if (!setSelectedIndex((getSelectedIndex() - 1)))
+ {
+ make_ui_sound("UISndInvalidOp");
+ }
+ else
+ {
+ onCommit();
+ }
+ handled = true;
+ break;
+ case KEY_RIGHT:
+ if (!setSelectedIndex((getSelectedIndex() + 1)))
+ {
+ make_ui_sound("UISndInvalidOp");
+ }
+ else
+ {
+ onCommit();
+ }
+ handled = true;
+ break;
+ default:
+ break;
+ }
+ }
+ return handled;
+}
+
+// Handle one button being clicked. All child buttons must have this
+// function as their callback function.
+
+void LLRadioGroup::onClickButton(LLUICtrl* ctrl)
+{
+ // LL_INFOS() << "LLRadioGroup::onClickButton" << LL_ENDL;
+ LLRadioCtrl* clicked_radio = dynamic_cast<LLRadioCtrl*>(ctrl);
+ if (!clicked_radio)
+ return;
+ S32 index = 0;
+ for (button_list_t::iterator iter = mRadioButtons.begin();
+ iter != mRadioButtons.end(); ++iter)
+ {
+ LLRadioCtrl* radio = *iter;
+ if (radio == clicked_radio)
+ {
+ if (index == mSelectedIndex && mAllowDeselect)
+ {
+ // don't select anything
+ setSelectedIndex(-1);
+ }
+ else
+ {
+ setSelectedIndex(index);
+ }
+
+ // BUG: Calls click callback even if button didn't actually change
+ onCommit();
+
+ return;
+ }
+
+ index++;
+ }
+
+ LL_WARNS() << "LLRadioGroup::onClickButton - clicked button that isn't a child" << LL_ENDL;
+}
+
+void LLRadioGroup::setValue( const LLSD& value )
+{
+ int idx = 0;
+ for (button_list_t::const_iterator iter = mRadioButtons.begin();
+ iter != mRadioButtons.end(); ++iter)
+ {
+ LLRadioCtrl* radio = *iter;
+ if (radio->getPayload().asString() == value.asString())
+ {
+ setSelectedIndex(idx);
+ idx = -1;
+ break;
+ }
+ ++idx;
+ }
+ if (idx != -1)
+ {
+ // string not found, try integer
+ if (value.isInteger())
+ {
+ setSelectedIndex((S32) value.asInteger(), true);
+ }
+ else
+ {
+ setSelectedIndex(-1, true);
+ }
+ }
+}
+
+LLSD LLRadioGroup::getValue() const
+{
+ int index = getSelectedIndex();
+ int idx = 0;
+ for (button_list_t::const_iterator iter = mRadioButtons.begin();
+ iter != mRadioButtons.end(); ++iter)
+ {
+ if (idx == index) return LLSD((*iter)->getPayload());
+ ++idx;
+ }
+ return LLSD();
+}
+
+// LLCtrlSelectionInterface functions
+bool LLRadioGroup::setCurrentByID( const LLUUID& id )
+{
+ return false;
+}
+
+LLUUID LLRadioGroup::getCurrentID() const
+{
+ return LLUUID::null;
+}
+
+bool LLRadioGroup::setSelectedByValue(const LLSD& value, bool selected)
+{
+ S32 idx = 0;
+ for (button_list_t::const_iterator iter = mRadioButtons.begin();
+ iter != mRadioButtons.end(); ++iter)
+ {
+ if((*iter)->getPayload().asString() == value.asString())
+ {
+ setSelectedIndex(idx);
+ return true;
+ }
+ idx++;
+ }
+
+ return false;
+}
+
+LLSD LLRadioGroup::getSelectedValue()
+{
+ return getValue();
+}
+
+bool LLRadioGroup::isSelected(const LLSD& value) const
+{
+ S32 idx = 0;
+ for (button_list_t::const_iterator iter = mRadioButtons.begin();
+ iter != mRadioButtons.end(); ++iter)
+ {
+ if((*iter)->getPayload().asString() == value.asString())
+ {
+ if (idx == mSelectedIndex)
+ {
+ return true;
+ }
+ }
+ idx++;
+ }
+ return false;
+}
+
+bool LLRadioGroup::operateOnSelection(EOperation op)
+{
+ return false;
+}
+
+bool LLRadioGroup::operateOnAll(EOperation op)
+{
+ return false;
+}
+
+LLRadioGroup::ItemParams::ItemParams()
+: value("value")
+{
+ addSynonym(value, "initial_value");
+}
+
+LLRadioCtrl::LLRadioCtrl(const LLRadioGroup::ItemParams& p)
+: LLCheckBoxCtrl(p),
+ mPayload(p.value)
+{
+ // use name as default "Value" for backwards compatibility
+ if (!p.value.isProvided())
+ {
+ mPayload = p.name();
+ }
+}
+
+bool LLRadioCtrl::postBuild()
+{
+ // Old-style radio_item used the text contents to indicate the label,
+ // but new-style radio_item uses label attribute.
+ std::string value = getValue().asString();
+ if (!value.empty())
+ {
+ setLabel(value);
+ }
+ return true;
+}
+
+bool LLRadioCtrl::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // Grab focus preemptively, before button takes mousecapture
+ if (hasTabStop() && getEnabled())
+ {
+ focusFirstItem(false, false);
+ }
+ else
+ {
+ // Only currently selected item in group has tab stop as result it is
+ // unclear how focus should behave on click, just let the group handle
+ // focus and LLRadioGroup::onClickButton() will set correct state later
+ // if needed
+ LLRadioGroup* parent = (LLRadioGroup*)getParent();
+ if (parent)
+ {
+ parent->focusSelectedRadioBtn();
+ }
+ }
+
+ return LLCheckBoxCtrl::handleMouseDown(x, y, mask);
+}
+
+LLRadioCtrl::~LLRadioCtrl()
+{
+}
+
+void LLRadioCtrl::setValue(const LLSD& value)
+{
+ LLCheckBoxCtrl::setValue(value);
+ mButton->setTabStop(value.asBoolean());
+}
+
+// *TODO: Remove this function after the initial XUI XML re-export pass.
+// static
+void LLRadioCtrl::setupParamsForExport(Params& p, LLView* parent)
+{
+ std::string label = p.label;
+ if (label.empty())
+ {
+ // We don't have a label attribute, so move the text contents
+ // stored in "value" into the label
+ std::string initial_value = p.LLUICtrl::Params::initial_value();
+ p.label = initial_value;
+ p.LLUICtrl::Params::initial_value = LLSD();
+ }
+
+ LLCheckBoxCtrl::setupParamsForExport(p, parent);
+}
diff --git a/indra/llui/llradiogroup.h b/indra/llui/llradiogroup.h
index d638605bb3..d72fd83668 100644
--- a/indra/llui/llradiogroup.h
+++ b/indra/llui/llradiogroup.h
@@ -1,114 +1,114 @@
-/**
- * @file llradiogroup.h
- * @brief LLRadioGroup 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$
- */
-
-#ifndef LL_LLRADIOGROUP_H
-#define LL_LLRADIOGROUP_H
-
-#include "lluictrl.h"
-#include "llcheckboxctrl.h"
-#include "llctrlselectioninterface.h"
-
-/*
- * An invisible view containing multiple mutually exclusive toggling
- * buttons (usually radio buttons). Automatically handles the mutex
- * condition by highlighting only one button at a time.
- */
-class LLRadioGroup
-: public LLUICtrl, public LLCtrlSelectionInterface
-{
-public:
-
- struct ItemParams : public LLInitParam::Block<ItemParams, LLCheckBoxCtrl::Params>
- {
- Optional<LLSD> value;
- ItemParams();
- };
-
- struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Optional<bool> allow_deselect;
- Multiple<ItemParams, AtLeast<1> > items;
- Params();
- };
-
-protected:
- LLRadioGroup(const Params&);
- friend class LLUICtrlFactory;
-
-public:
-
- /*virtual*/ void initFromParams(const Params&);
-
- virtual ~LLRadioGroup();
-
- virtual bool postBuild();
-
- virtual bool handleKeyHere(KEY key, MASK mask);
-
- void setIndexEnabled(S32 index, bool enabled);
- // return the index value of the selected item
- S32 getSelectedIndex() const { return mSelectedIndex; }
- // set the index value programatically
- bool setSelectedIndex(S32 index, bool from_event = false);
- // foxus child by index if it can get focus
- void focusSelectedRadioBtn();
-
- // Accept and retrieve strings of the radio group control names
- virtual void setValue(const LLSD& value );
- virtual LLSD getValue() const;
-
- // Update the control as needed. Userdata must be a pointer to the button.
- void onClickButton(LLUICtrl* clicked_radio);
-
- //========================================================================
- LLCtrlSelectionInterface* getSelectionInterface() { return (LLCtrlSelectionInterface*)this; };
-
- // LLCtrlSelectionInterface functions
- /*virtual*/ S32 getItemCount() const { return mRadioButtons.size(); }
- /*virtual*/ bool getCanSelect() const { return true; }
- /*virtual*/ bool selectFirstItem() { return setSelectedIndex(0); }
- /*virtual*/ bool selectNthItem( S32 index ) { return setSelectedIndex(index); }
- /*virtual*/ bool selectItemRange( S32 first, S32 last ) { return setSelectedIndex(first); }
- /*virtual*/ S32 getFirstSelectedIndex() const { return getSelectedIndex(); }
- /*virtual*/ bool setCurrentByID( const LLUUID& id );
- /*virtual*/ LLUUID getCurrentID() const; // LLUUID::null if no items in menu
- /*virtual*/ bool setSelectedByValue(const LLSD& value, bool selected);
- /*virtual*/ LLSD getSelectedValue();
- /*virtual*/ bool isSelected(const LLSD& value) const;
- /*virtual*/ bool operateOnSelection(EOperation op);
- /*virtual*/ bool operateOnAll(EOperation op);
-
-private:
- const LLFontGL* mFont;
- S32 mSelectedIndex;
-
- typedef std::vector<class LLRadioCtrl*> button_list_t;
- button_list_t mRadioButtons;
-
- bool mAllowDeselect; // user can click on an already selected option to deselect it
-};
-
-#endif
+/**
+ * @file llradiogroup.h
+ * @brief LLRadioGroup 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$
+ */
+
+#ifndef LL_LLRADIOGROUP_H
+#define LL_LLRADIOGROUP_H
+
+#include "lluictrl.h"
+#include "llcheckboxctrl.h"
+#include "llctrlselectioninterface.h"
+
+/*
+ * An invisible view containing multiple mutually exclusive toggling
+ * buttons (usually radio buttons). Automatically handles the mutex
+ * condition by highlighting only one button at a time.
+ */
+class LLRadioGroup
+: public LLUICtrl, public LLCtrlSelectionInterface
+{
+public:
+
+ struct ItemParams : public LLInitParam::Block<ItemParams, LLCheckBoxCtrl::Params>
+ {
+ Optional<LLSD> value;
+ ItemParams();
+ };
+
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<bool> allow_deselect;
+ Multiple<ItemParams, AtLeast<1> > items;
+ Params();
+ };
+
+protected:
+ LLRadioGroup(const Params&);
+ friend class LLUICtrlFactory;
+
+public:
+
+ /*virtual*/ void initFromParams(const Params&);
+
+ virtual ~LLRadioGroup();
+
+ virtual bool postBuild();
+
+ virtual bool handleKeyHere(KEY key, MASK mask);
+
+ void setIndexEnabled(S32 index, bool enabled);
+ // return the index value of the selected item
+ S32 getSelectedIndex() const { return mSelectedIndex; }
+ // set the index value programatically
+ bool setSelectedIndex(S32 index, bool from_event = false);
+ // foxus child by index if it can get focus
+ void focusSelectedRadioBtn();
+
+ // Accept and retrieve strings of the radio group control names
+ virtual void setValue(const LLSD& value );
+ virtual LLSD getValue() const;
+
+ // Update the control as needed. Userdata must be a pointer to the button.
+ void onClickButton(LLUICtrl* clicked_radio);
+
+ //========================================================================
+ LLCtrlSelectionInterface* getSelectionInterface() { return (LLCtrlSelectionInterface*)this; };
+
+ // LLCtrlSelectionInterface functions
+ /*virtual*/ S32 getItemCount() const { return mRadioButtons.size(); }
+ /*virtual*/ bool getCanSelect() const { return true; }
+ /*virtual*/ bool selectFirstItem() { return setSelectedIndex(0); }
+ /*virtual*/ bool selectNthItem( S32 index ) { return setSelectedIndex(index); }
+ /*virtual*/ bool selectItemRange( S32 first, S32 last ) { return setSelectedIndex(first); }
+ /*virtual*/ S32 getFirstSelectedIndex() const { return getSelectedIndex(); }
+ /*virtual*/ bool setCurrentByID( const LLUUID& id );
+ /*virtual*/ LLUUID getCurrentID() const; // LLUUID::null if no items in menu
+ /*virtual*/ bool setSelectedByValue(const LLSD& value, bool selected);
+ /*virtual*/ LLSD getSelectedValue();
+ /*virtual*/ bool isSelected(const LLSD& value) const;
+ /*virtual*/ bool operateOnSelection(EOperation op);
+ /*virtual*/ bool operateOnAll(EOperation op);
+
+private:
+ const LLFontGL* mFont;
+ S32 mSelectedIndex;
+
+ typedef std::vector<class LLRadioCtrl*> button_list_t;
+ button_list_t mRadioButtons;
+
+ bool mAllowDeselect; // user can click on an already selected option to deselect it
+};
+
+#endif
diff --git a/indra/llui/llresizebar.cpp b/indra/llui/llresizebar.cpp
index c05a7dddf7..85d886a3ed 100644
--- a/indra/llui/llresizebar.cpp
+++ b/indra/llui/llresizebar.cpp
@@ -1,376 +1,376 @@
-/**
- * @file llresizebar.cpp
- * @brief LLResizeBar base class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "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 ),
- mDragLastScreenY( 0 ),
- mLastMouseScreenX( 0 ),
- mLastMouseScreenY( 0 ),
- mMinSize( p.min_size ),
- mMaxSize( p.max_size ),
- mSide( p.side ),
- mSnappingEnabled(p.snapping_enabled),
- mAllowDoubleClickSnapping(p.allow_double_click_snapping),
- mResizingView(p.resizing_view),
- mResizeListener(NULL),
- mImagePanel(NULL)
-{
- setFollowsNone();
- // set up some generically good follow code.
- switch( mSide )
- {
- case LEFT:
- setFollowsLeft();
- setFollowsTop();
- setFollowsBottom();
- break;
- case TOP:
- setFollowsTop();
- setFollowsLeft();
- setFollowsRight();
- break;
- case RIGHT:
- setFollowsRight();
- setFollowsTop();
- setFollowsBottom();
- break;
- case BOTTOM:
- setFollowsBottom();
- setFollowsLeft();
- setFollowsRight();
- break;
- default:
- break;
- }
-}
-
-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 );
-
- localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY);
- mLastMouseScreenX = mDragLastScreenX;
- mLastMouseScreenY = mDragLastScreenY;
-
- return true;
-}
-
-
-bool LLResizeBar::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
-
- if( hasMouseCapture() )
- {
- // Release the mouse
- gFocusMgr.setMouseCapture( NULL );
- handled = true;
- }
- else
- {
- handled = true;
- }
- return handled;
-}
-
-
-bool LLResizeBar::handleHover(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
-
- // We only handle the click if the click both started and ended within us
- if( hasMouseCapture() )
- {
- S32 screen_x;
- S32 screen_y;
- localPointToScreen(x, y, &screen_x, &screen_y);
-
- S32 delta_x = screen_x - mDragLastScreenX;
- S32 delta_y = screen_y - mDragLastScreenY;
-
- LLCoordGL mouse_dir;
- // use hysteresis on mouse motion to preserve user intent when mouse stops moving
- mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX;
- mouse_dir.mY = (screen_y == mLastMouseScreenY) ? mLastMouseDir.mY : screen_y - mLastMouseScreenY;
- mLastMouseDir = mouse_dir;
- mLastMouseScreenX = screen_x;
- mLastMouseScreenY = screen_y;
-
- // Make sure the mouse in still over the application. We don't want to make the parent
- // so big that we can't see the resize handle any more.
- LLRect valid_rect = getRootView()->getRect();
-
- if( valid_rect.localPointInRect( screen_x, screen_y ) && mResizingView )
- {
- // Resize the parent
- LLRect orig_rect = mResizingView->getRect();
- LLRect scaled_rect = orig_rect;
-
- S32 new_width = orig_rect.getWidth();
- S32 new_height = orig_rect.getHeight();
-
- switch( mSide )
- {
- case LEFT:
- new_width = llclamp(orig_rect.getWidth() - delta_x, mMinSize, mMaxSize);
- delta_x = orig_rect.getWidth() - new_width;
- scaled_rect.translate(delta_x, 0);
- break;
-
- case TOP:
- new_height = llclamp(orig_rect.getHeight() + delta_y, mMinSize, mMaxSize);
- delta_y = new_height - orig_rect.getHeight();
- break;
-
- case RIGHT:
- new_width = llclamp(orig_rect.getWidth() + delta_x, mMinSize, mMaxSize);
- delta_x = new_width - orig_rect.getWidth();
- break;
-
- case BOTTOM:
- new_height = llclamp(orig_rect.getHeight() - delta_y, mMinSize, mMaxSize);
- delta_y = orig_rect.getHeight() - new_height;
- scaled_rect.translate(0, delta_y);
- break;
- }
-
- notifyParent(LLSD().with("action", "resize")
- .with("view_name", mResizingView->getName())
- .with("new_height", new_height)
- .with("new_width", new_width));
-
- scaled_rect.mTop = scaled_rect.mBottom + new_height;
- scaled_rect.mRight = scaled_rect.mLeft + new_width;
- mResizingView->setRect(scaled_rect);
-
- LLView* snap_view = NULL;
-
- if (mSnappingEnabled)
- {
- static LLUICachedControl<S32> snap_margin ("SnapMargin", 0);
- switch( mSide )
- {
- case LEFT:
- snap_view = mResizingView->findSnapEdge(scaled_rect.mLeft, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, snap_margin);
- break;
- case TOP:
- snap_view = mResizingView->findSnapEdge(scaled_rect.mTop, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, snap_margin);
- break;
- case RIGHT:
- snap_view = mResizingView->findSnapEdge(scaled_rect.mRight, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, snap_margin);
- break;
- case BOTTOM:
- snap_view = mResizingView->findSnapEdge(scaled_rect.mBottom, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, snap_margin);
- break;
- }
- }
-
- // register "snap" behavior with snapped view
- mResizingView->setSnappedTo(snap_view);
-
- // restore original rectangle so the appropriate changes are detected
- mResizingView->setRect(orig_rect);
- // change view shape as user operation
- mResizingView->setShape(scaled_rect, true);
-
- // update last valid mouse cursor position based on resized view's actual size
- LLRect new_rect = mResizingView->getRect();
-
- switch(mSide)
- {
- case LEFT:
- {
- 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;
- }
- }
-
- handled = true;
- }
- else
- {
- handled = true;
- }
-
- if( handled && canResize() )
- {
- switch( mSide )
- {
- case LEFT:
- case RIGHT:
- getWindow()->setCursor(UI_CURSOR_SIZEWE);
- break;
-
- case TOP:
- case BOTTOM:
- getWindow()->setCursor(UI_CURSOR_SIZENS);
- break;
- }
- }
-
- if (mResizeListener)
- {
- mResizeListener(NULL);
- }
-
- return handled;
-} // end LLResizeBar::handleHover
-
-bool LLResizeBar::handleDoubleClick(S32 x, S32 y, MASK mask)
-{
- LLRect orig_rect = mResizingView->getRect();
- LLRect scaled_rect = orig_rect;
-
- if (mSnappingEnabled && mAllowDoubleClickSnapping)
- {
- switch( mSide )
- {
- case LEFT:
- mResizingView->findSnapEdge(scaled_rect.mLeft, LLCoordGL(0, 0), SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, S32_MAX);
- scaled_rect.mLeft = scaled_rect.mRight - llclamp(scaled_rect.getWidth(), mMinSize, mMaxSize);
- break;
- case TOP:
- mResizingView->findSnapEdge(scaled_rect.mTop, LLCoordGL(0, 0), SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, S32_MAX);
- scaled_rect.mTop = scaled_rect.mBottom + llclamp(scaled_rect.getHeight(), mMinSize, mMaxSize);
- break;
- case RIGHT:
- mResizingView->findSnapEdge(scaled_rect.mRight, LLCoordGL(0, 0), SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, S32_MAX);
- scaled_rect.mRight = scaled_rect.mLeft + llclamp(scaled_rect.getWidth(), mMinSize, mMaxSize);
- break;
- case BOTTOM:
- mResizingView->findSnapEdge(scaled_rect.mBottom, LLCoordGL(0, 0), SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, S32_MAX);
- scaled_rect.mBottom = scaled_rect.mTop - llclamp(scaled_rect.getHeight(), mMinSize, mMaxSize);
- break;
- }
-
- mResizingView->setShape(scaled_rect, true);
- }
-
- 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;
-}
+/**
+ * @file llresizebar.cpp
+ * @brief LLResizeBar base class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "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 ),
+ mDragLastScreenY( 0 ),
+ mLastMouseScreenX( 0 ),
+ mLastMouseScreenY( 0 ),
+ mMinSize( p.min_size ),
+ mMaxSize( p.max_size ),
+ mSide( p.side ),
+ mSnappingEnabled(p.snapping_enabled),
+ mAllowDoubleClickSnapping(p.allow_double_click_snapping),
+ mResizingView(p.resizing_view),
+ mResizeListener(NULL),
+ mImagePanel(NULL)
+{
+ setFollowsNone();
+ // set up some generically good follow code.
+ switch( mSide )
+ {
+ case LEFT:
+ setFollowsLeft();
+ setFollowsTop();
+ setFollowsBottom();
+ break;
+ case TOP:
+ setFollowsTop();
+ setFollowsLeft();
+ setFollowsRight();
+ break;
+ case RIGHT:
+ setFollowsRight();
+ setFollowsTop();
+ setFollowsBottom();
+ break;
+ case BOTTOM:
+ setFollowsBottom();
+ setFollowsLeft();
+ setFollowsRight();
+ break;
+ default:
+ break;
+ }
+}
+
+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 );
+
+ localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY);
+ mLastMouseScreenX = mDragLastScreenX;
+ mLastMouseScreenY = mDragLastScreenY;
+
+ return true;
+}
+
+
+bool LLResizeBar::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ bool handled = false;
+
+ if( hasMouseCapture() )
+ {
+ // Release the mouse
+ gFocusMgr.setMouseCapture( NULL );
+ handled = true;
+ }
+ else
+ {
+ handled = true;
+ }
+ return handled;
+}
+
+
+bool LLResizeBar::handleHover(S32 x, S32 y, MASK mask)
+{
+ bool handled = false;
+
+ // We only handle the click if the click both started and ended within us
+ if( hasMouseCapture() )
+ {
+ S32 screen_x;
+ S32 screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y);
+
+ S32 delta_x = screen_x - mDragLastScreenX;
+ S32 delta_y = screen_y - mDragLastScreenY;
+
+ LLCoordGL mouse_dir;
+ // use hysteresis on mouse motion to preserve user intent when mouse stops moving
+ mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX;
+ mouse_dir.mY = (screen_y == mLastMouseScreenY) ? mLastMouseDir.mY : screen_y - mLastMouseScreenY;
+ mLastMouseDir = mouse_dir;
+ mLastMouseScreenX = screen_x;
+ mLastMouseScreenY = screen_y;
+
+ // Make sure the mouse in still over the application. We don't want to make the parent
+ // so big that we can't see the resize handle any more.
+ LLRect valid_rect = getRootView()->getRect();
+
+ if( valid_rect.localPointInRect( screen_x, screen_y ) && mResizingView )
+ {
+ // Resize the parent
+ LLRect orig_rect = mResizingView->getRect();
+ LLRect scaled_rect = orig_rect;
+
+ S32 new_width = orig_rect.getWidth();
+ S32 new_height = orig_rect.getHeight();
+
+ switch( mSide )
+ {
+ case LEFT:
+ new_width = llclamp(orig_rect.getWidth() - delta_x, mMinSize, mMaxSize);
+ delta_x = orig_rect.getWidth() - new_width;
+ scaled_rect.translate(delta_x, 0);
+ break;
+
+ case TOP:
+ new_height = llclamp(orig_rect.getHeight() + delta_y, mMinSize, mMaxSize);
+ delta_y = new_height - orig_rect.getHeight();
+ break;
+
+ case RIGHT:
+ new_width = llclamp(orig_rect.getWidth() + delta_x, mMinSize, mMaxSize);
+ delta_x = new_width - orig_rect.getWidth();
+ break;
+
+ case BOTTOM:
+ new_height = llclamp(orig_rect.getHeight() - delta_y, mMinSize, mMaxSize);
+ delta_y = orig_rect.getHeight() - new_height;
+ scaled_rect.translate(0, delta_y);
+ break;
+ }
+
+ notifyParent(LLSD().with("action", "resize")
+ .with("view_name", mResizingView->getName())
+ .with("new_height", new_height)
+ .with("new_width", new_width));
+
+ scaled_rect.mTop = scaled_rect.mBottom + new_height;
+ scaled_rect.mRight = scaled_rect.mLeft + new_width;
+ mResizingView->setRect(scaled_rect);
+
+ LLView* snap_view = NULL;
+
+ if (mSnappingEnabled)
+ {
+ static LLUICachedControl<S32> snap_margin ("SnapMargin", 0);
+ switch( mSide )
+ {
+ case LEFT:
+ snap_view = mResizingView->findSnapEdge(scaled_rect.mLeft, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, snap_margin);
+ break;
+ case TOP:
+ snap_view = mResizingView->findSnapEdge(scaled_rect.mTop, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, snap_margin);
+ break;
+ case RIGHT:
+ snap_view = mResizingView->findSnapEdge(scaled_rect.mRight, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, snap_margin);
+ break;
+ case BOTTOM:
+ snap_view = mResizingView->findSnapEdge(scaled_rect.mBottom, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, snap_margin);
+ break;
+ }
+ }
+
+ // register "snap" behavior with snapped view
+ mResizingView->setSnappedTo(snap_view);
+
+ // restore original rectangle so the appropriate changes are detected
+ mResizingView->setRect(orig_rect);
+ // change view shape as user operation
+ mResizingView->setShape(scaled_rect, true);
+
+ // update last valid mouse cursor position based on resized view's actual size
+ LLRect new_rect = mResizingView->getRect();
+
+ switch(mSide)
+ {
+ case LEFT:
+ {
+ 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;
+ }
+ }
+
+ handled = true;
+ }
+ else
+ {
+ handled = true;
+ }
+
+ if( handled && canResize() )
+ {
+ switch( mSide )
+ {
+ case LEFT:
+ case RIGHT:
+ getWindow()->setCursor(UI_CURSOR_SIZEWE);
+ break;
+
+ case TOP:
+ case BOTTOM:
+ getWindow()->setCursor(UI_CURSOR_SIZENS);
+ break;
+ }
+ }
+
+ if (mResizeListener)
+ {
+ mResizeListener(NULL);
+ }
+
+ return handled;
+} // end LLResizeBar::handleHover
+
+bool LLResizeBar::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ LLRect orig_rect = mResizingView->getRect();
+ LLRect scaled_rect = orig_rect;
+
+ if (mSnappingEnabled && mAllowDoubleClickSnapping)
+ {
+ switch( mSide )
+ {
+ case LEFT:
+ mResizingView->findSnapEdge(scaled_rect.mLeft, LLCoordGL(0, 0), SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, S32_MAX);
+ scaled_rect.mLeft = scaled_rect.mRight - llclamp(scaled_rect.getWidth(), mMinSize, mMaxSize);
+ break;
+ case TOP:
+ mResizingView->findSnapEdge(scaled_rect.mTop, LLCoordGL(0, 0), SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, S32_MAX);
+ scaled_rect.mTop = scaled_rect.mBottom + llclamp(scaled_rect.getHeight(), mMinSize, mMaxSize);
+ break;
+ case RIGHT:
+ mResizingView->findSnapEdge(scaled_rect.mRight, LLCoordGL(0, 0), SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, S32_MAX);
+ scaled_rect.mRight = scaled_rect.mLeft + llclamp(scaled_rect.getWidth(), mMinSize, mMaxSize);
+ break;
+ case BOTTOM:
+ mResizingView->findSnapEdge(scaled_rect.mBottom, LLCoordGL(0, 0), SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, S32_MAX);
+ scaled_rect.mBottom = scaled_rect.mTop - llclamp(scaled_rect.getHeight(), mMinSize, mMaxSize);
+ break;
+ }
+
+ mResizingView->setShape(scaled_rect, true);
+ }
+
+ 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 5c53ddfce6..7a6d3fa4c0 100644
--- a/indra/llui/llresizebar.h
+++ b/indra/llui/llresizebar.h
@@ -1,88 +1,88 @@
-/**
- * @file llresizebar.h
- * @brief LLResizeBar 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$
- */
-
-#ifndef LL_RESIZEBAR_H
-#define LL_RESIZEBAR_H
-
-#include "llview.h"
-
-class LLResizeBar : public LLView
-{
-public:
- enum Side { LEFT, TOP, RIGHT, BOTTOM };
-
- struct Params : public LLInitParam::Block<Params, LLView::Params>
- {
- Mandatory<LLView*> resizing_view;
- Mandatory<Side> side;
-
- Optional<S32> min_size;
- Optional<S32> max_size;
- Optional<bool> snapping_enabled;
- Optional<bool> allow_double_click_snapping;
-
- Params();
- };
-
-protected:
- LLResizeBar(const LLResizeBar::Params& p);
- friend class LLUICtrlFactory;
-
-public:
-
- virtual bool handleHover(S32 x, S32 y, MASK mask);
- virtual bool handleMouseDown(S32 x, S32 y, MASK mask);
- virtual bool handleMouseUp(S32 x, S32 y, MASK mask);
- virtual bool handleDoubleClick(S32 x, S32 y, MASK mask);
-
- 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,
- mAllowDoubleClickSnapping;
- LLView* mResizingView;
- boost::function<void(void*)> mResizeListener;
- LLPointer<LLUIImage> mDragHandleImage;
- LLPanel * mImagePanel;
-};
-
-#endif // LL_RESIZEBAR_H
-
-
+/**
+ * @file llresizebar.h
+ * @brief LLResizeBar 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$
+ */
+
+#ifndef LL_RESIZEBAR_H
+#define LL_RESIZEBAR_H
+
+#include "llview.h"
+
+class LLResizeBar : public LLView
+{
+public:
+ enum Side { LEFT, TOP, RIGHT, BOTTOM };
+
+ struct Params : public LLInitParam::Block<Params, LLView::Params>
+ {
+ Mandatory<LLView*> resizing_view;
+ Mandatory<Side> side;
+
+ Optional<S32> min_size;
+ Optional<S32> max_size;
+ Optional<bool> snapping_enabled;
+ Optional<bool> allow_double_click_snapping;
+
+ Params();
+ };
+
+protected:
+ LLResizeBar(const LLResizeBar::Params& p);
+ friend class LLUICtrlFactory;
+
+public:
+
+ virtual bool handleHover(S32 x, S32 y, MASK mask);
+ virtual bool handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual bool handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual bool handleDoubleClick(S32 x, S32 y, MASK mask);
+
+ 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,
+ 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 760174a878..55eeae6963 100644
--- a/indra/llui/llresizehandle.cpp
+++ b/indra/llui/llresizehandle.cpp
@@ -1,385 +1,385 @@
-/**
- * @file llresizehandle.cpp
- * @brief LLResizeHandle base class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llresizehandle.h"
-
-#include "llfocusmgr.h"
-#include "llmath.h"
-#include "llui.h"
-#include "llmenugl.h"
-#include "llcontrol.h"
-#include "llfloater.h"
-#include "llwindow.h"
-
-const S32 RESIZE_BORDER_WIDTH = 3;
-
-LLResizeHandle::Params::Params()
-: corner("corner"),
- min_width("min_width"),
- min_height("min_height")
-{
- name = "resize_handle";
-}
-
-LLResizeHandle::LLResizeHandle(const LLResizeHandle::Params& p)
-: LLView(p),
- mDragLastScreenX( 0 ),
- mDragLastScreenY( 0 ),
- mLastMouseScreenX( 0 ),
- mLastMouseScreenY( 0 ),
- mImage( NULL ),
- mMinWidth( p.min_width ),
- mMinHeight( p.min_height ),
- mCorner( p.corner )
-{
- if( RIGHT_BOTTOM == mCorner)
- {
- mImage = LLUI::getUIImage("Resize_Corner");
- }
- switch( p.corner )
- {
- case LEFT_TOP: setFollows( FOLLOWS_LEFT | FOLLOWS_TOP ); break;
- case LEFT_BOTTOM: setFollows( FOLLOWS_LEFT | FOLLOWS_BOTTOM ); break;
- case RIGHT_TOP: setFollows( FOLLOWS_RIGHT | FOLLOWS_TOP ); break;
- case RIGHT_BOTTOM: setFollows( FOLLOWS_RIGHT | FOLLOWS_BOTTOM ); break;
- }
-}
-
-LLResizeHandle::~LLResizeHandle()
-{
- gFocusMgr.removeKeyboardFocusWithoutCallback(this);
-}
-
-
-bool LLResizeHandle::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
- if( pointInHandle(x, y) )
- {
- handled = true;
- // 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 );
-
- localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY);
- mLastMouseScreenX = mDragLastScreenX;
- mLastMouseScreenY = mDragLastScreenY;
- }
-
- return handled;
-}
-
-
-bool LLResizeHandle::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
-
- if( hasMouseCapture() )
- {
- // Release the mouse
- gFocusMgr.setMouseCapture( NULL );
- handled = true;
- }
- else if( pointInHandle(x, y) )
- {
- handled = true;
- }
-
- return handled;
-}
-
-
-bool LLResizeHandle::handleHover(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
-
- // We only handle the click if the click both started and ended within us
- if( hasMouseCapture() )
- {
- // Make sure the mouse in still over the application. We don't want to make the parent
- // so big that we can't see the resize handle any more.
-
- S32 screen_x;
- S32 screen_y;
- localPointToScreen(x, y, &screen_x, &screen_y);
- const LLRect valid_rect = getRootView()->getRect();
- screen_x = llclamp( screen_x, valid_rect.mLeft, valid_rect.mRight );
- screen_y = llclamp( screen_y, valid_rect.mBottom, valid_rect.mTop );
-
- LLView* resizing_view = getParent();
- if( resizing_view )
- {
- // undock floater when user resize it
- LLFloater* floater_parent = dynamic_cast<LLFloater*>(getParent());
- if (floater_parent && floater_parent->isDocked())
- {
- floater_parent->setDocked(false, false);
- }
-
- // Resize the parent
- LLRect orig_rect = resizing_view->getRect();
- LLRect scaled_rect = orig_rect;
- S32 delta_x = screen_x - mDragLastScreenX;
- S32 delta_y = screen_y - mDragLastScreenY;
- LLCoordGL mouse_dir;
- // use hysteresis on mouse motion to preserve user intent when mouse stops moving
- mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX;
- mouse_dir.mY = (screen_y == mLastMouseScreenY) ? mLastMouseDir.mY : screen_y - mLastMouseScreenY;
- mLastMouseScreenX = screen_x;
- mLastMouseScreenY = screen_y;
- mLastMouseDir = mouse_dir;
-
- S32 x_multiple = 1;
- S32 y_multiple = 1;
- switch( mCorner )
- {
- case LEFT_TOP:
- x_multiple = -1;
- y_multiple = 1;
- break;
- case LEFT_BOTTOM:
- x_multiple = -1;
- y_multiple = -1;
- break;
- case RIGHT_TOP:
- x_multiple = 1;
- y_multiple = 1;
- break;
- case RIGHT_BOTTOM:
- x_multiple = 1;
- y_multiple = -1;
- break;
- }
-
- S32 new_width = orig_rect.getWidth() + x_multiple * delta_x;
- if( new_width < mMinWidth )
- {
- new_width = mMinWidth;
- delta_x = x_multiple * (mMinWidth - orig_rect.getWidth());
- }
-
- S32 new_height = orig_rect.getHeight() + y_multiple * delta_y;
- if( new_height < mMinHeight )
- {
- new_height = mMinHeight;
- delta_y = y_multiple * (mMinHeight - orig_rect.getHeight());
- }
-
- switch( mCorner )
- {
- case LEFT_TOP:
- scaled_rect.translate(delta_x, 0);
- break;
- case LEFT_BOTTOM:
- scaled_rect.translate(delta_x, delta_y);
- break;
- case RIGHT_TOP:
- break;
- case RIGHT_BOTTOM:
- scaled_rect.translate(0, delta_y);
- break;
- }
-
- // temporarily set new parent rect
- scaled_rect.mRight = scaled_rect.mLeft + new_width;
- scaled_rect.mTop = scaled_rect.mBottom + new_height;
- resizing_view->setRect(scaled_rect);
-
- LLView* snap_view = NULL;
- LLView* test_view = NULL;
-
- static LLUICachedControl<S32> snap_margin ("SnapMargin", 0);
- // now do snapping
- switch(mCorner)
- {
- case LEFT_TOP:
- snap_view = resizing_view->findSnapEdge(scaled_rect.mLeft, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, snap_margin);
- test_view = resizing_view->findSnapEdge(scaled_rect.mTop, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, snap_margin);
- if (!snap_view)
- {
- snap_view = test_view;
- }
- break;
- case LEFT_BOTTOM:
- snap_view = resizing_view->findSnapEdge(scaled_rect.mLeft, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, snap_margin);
- test_view = resizing_view->findSnapEdge(scaled_rect.mBottom, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, snap_margin);
- if (!snap_view)
- {
- snap_view = test_view;
- }
- break;
- case RIGHT_TOP:
- snap_view = resizing_view->findSnapEdge(scaled_rect.mRight, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, snap_margin);
- test_view = resizing_view->findSnapEdge(scaled_rect.mTop, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, snap_margin);
- if (!snap_view)
- {
- snap_view = test_view;
- }
- break;
- case RIGHT_BOTTOM:
- snap_view = resizing_view->findSnapEdge(scaled_rect.mRight, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, snap_margin);
- test_view = resizing_view->findSnapEdge(scaled_rect.mBottom, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, snap_margin);
- if (!snap_view)
- {
- snap_view = test_view;
- }
- break;
- }
-
- // register "snap" behavior with snapped view
- resizing_view->setSnappedTo(snap_view);
-
- // reset parent rect
- resizing_view->setRect(orig_rect);
-
- // translate and scale to new shape
- resizing_view->setShape(scaled_rect, true);
-
- // update last valid mouse cursor position based on resized view's actual size
- LLRect new_rect = resizing_view->getRect();
- S32 actual_delta_x = 0;
- S32 actual_delta_y = 0;
- switch(mCorner)
- {
- case LEFT_TOP:
- 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:
- 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:
- 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:
- 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;
- }
- }
-
- handled = true;
- }
- else // don't have mouse capture
- {
- if( pointInHandle( x, y ) )
- {
- handled = true;
- }
- }
-
- if( handled )
- {
- switch( mCorner )
- {
- case RIGHT_BOTTOM:
- case LEFT_TOP:
- getWindow()->setCursor(UI_CURSOR_SIZENWSE);
- break;
- case LEFT_BOTTOM:
- case RIGHT_TOP:
- getWindow()->setCursor(UI_CURSOR_SIZENESW);
- break;
- }
- }
-
- return handled;
-} // end handleHover
-
-
-// assumes GL state is set for 2D
-void LLResizeHandle::draw()
-{
- if( mImage.notNull() && getVisible() && (RIGHT_BOTTOM == mCorner) )
- {
- mImage->draw(0, 0);
- }
-}
-
-
-bool LLResizeHandle::pointInHandle( S32 x, S32 y )
-{
- if( pointInView(x, y) )
- {
- const S32 TOP_BORDER = (getRect().getHeight() - RESIZE_BORDER_WIDTH);
- const S32 RIGHT_BORDER = (getRect().getWidth() - RESIZE_BORDER_WIDTH);
-
- switch( mCorner )
- {
- case LEFT_TOP: return (x <= RESIZE_BORDER_WIDTH) || (y >= TOP_BORDER);
- case LEFT_BOTTOM: return (x <= RESIZE_BORDER_WIDTH) || (y <= RESIZE_BORDER_WIDTH);
- case RIGHT_TOP: return (x >= RIGHT_BORDER) || (y >= TOP_BORDER);
- case RIGHT_BOTTOM: return true;
- }
- }
- return false;
-}
+/**
+ * @file llresizehandle.cpp
+ * @brief LLResizeHandle base class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llresizehandle.h"
+
+#include "llfocusmgr.h"
+#include "llmath.h"
+#include "llui.h"
+#include "llmenugl.h"
+#include "llcontrol.h"
+#include "llfloater.h"
+#include "llwindow.h"
+
+const S32 RESIZE_BORDER_WIDTH = 3;
+
+LLResizeHandle::Params::Params()
+: corner("corner"),
+ min_width("min_width"),
+ min_height("min_height")
+{
+ name = "resize_handle";
+}
+
+LLResizeHandle::LLResizeHandle(const LLResizeHandle::Params& p)
+: LLView(p),
+ mDragLastScreenX( 0 ),
+ mDragLastScreenY( 0 ),
+ mLastMouseScreenX( 0 ),
+ mLastMouseScreenY( 0 ),
+ mImage( NULL ),
+ mMinWidth( p.min_width ),
+ mMinHeight( p.min_height ),
+ mCorner( p.corner )
+{
+ if( RIGHT_BOTTOM == mCorner)
+ {
+ mImage = LLUI::getUIImage("Resize_Corner");
+ }
+ switch( p.corner )
+ {
+ case LEFT_TOP: setFollows( FOLLOWS_LEFT | FOLLOWS_TOP ); break;
+ case LEFT_BOTTOM: setFollows( FOLLOWS_LEFT | FOLLOWS_BOTTOM ); break;
+ case RIGHT_TOP: setFollows( FOLLOWS_RIGHT | FOLLOWS_TOP ); break;
+ case RIGHT_BOTTOM: setFollows( FOLLOWS_RIGHT | FOLLOWS_BOTTOM ); break;
+ }
+}
+
+LLResizeHandle::~LLResizeHandle()
+{
+ gFocusMgr.removeKeyboardFocusWithoutCallback(this);
+}
+
+
+bool LLResizeHandle::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ bool handled = false;
+ if( pointInHandle(x, y) )
+ {
+ handled = true;
+ // 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 );
+
+ localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY);
+ mLastMouseScreenX = mDragLastScreenX;
+ mLastMouseScreenY = mDragLastScreenY;
+ }
+
+ return handled;
+}
+
+
+bool LLResizeHandle::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ bool handled = false;
+
+ if( hasMouseCapture() )
+ {
+ // Release the mouse
+ gFocusMgr.setMouseCapture( NULL );
+ handled = true;
+ }
+ else if( pointInHandle(x, y) )
+ {
+ handled = true;
+ }
+
+ return handled;
+}
+
+
+bool LLResizeHandle::handleHover(S32 x, S32 y, MASK mask)
+{
+ bool handled = false;
+
+ // We only handle the click if the click both started and ended within us
+ if( hasMouseCapture() )
+ {
+ // Make sure the mouse in still over the application. We don't want to make the parent
+ // so big that we can't see the resize handle any more.
+
+ S32 screen_x;
+ S32 screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y);
+ const LLRect valid_rect = getRootView()->getRect();
+ screen_x = llclamp( screen_x, valid_rect.mLeft, valid_rect.mRight );
+ screen_y = llclamp( screen_y, valid_rect.mBottom, valid_rect.mTop );
+
+ LLView* resizing_view = getParent();
+ if( resizing_view )
+ {
+ // undock floater when user resize it
+ LLFloater* floater_parent = dynamic_cast<LLFloater*>(getParent());
+ if (floater_parent && floater_parent->isDocked())
+ {
+ floater_parent->setDocked(false, false);
+ }
+
+ // Resize the parent
+ LLRect orig_rect = resizing_view->getRect();
+ LLRect scaled_rect = orig_rect;
+ S32 delta_x = screen_x - mDragLastScreenX;
+ S32 delta_y = screen_y - mDragLastScreenY;
+ LLCoordGL mouse_dir;
+ // use hysteresis on mouse motion to preserve user intent when mouse stops moving
+ mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX;
+ mouse_dir.mY = (screen_y == mLastMouseScreenY) ? mLastMouseDir.mY : screen_y - mLastMouseScreenY;
+ mLastMouseScreenX = screen_x;
+ mLastMouseScreenY = screen_y;
+ mLastMouseDir = mouse_dir;
+
+ S32 x_multiple = 1;
+ S32 y_multiple = 1;
+ switch( mCorner )
+ {
+ case LEFT_TOP:
+ x_multiple = -1;
+ y_multiple = 1;
+ break;
+ case LEFT_BOTTOM:
+ x_multiple = -1;
+ y_multiple = -1;
+ break;
+ case RIGHT_TOP:
+ x_multiple = 1;
+ y_multiple = 1;
+ break;
+ case RIGHT_BOTTOM:
+ x_multiple = 1;
+ y_multiple = -1;
+ break;
+ }
+
+ S32 new_width = orig_rect.getWidth() + x_multiple * delta_x;
+ if( new_width < mMinWidth )
+ {
+ new_width = mMinWidth;
+ delta_x = x_multiple * (mMinWidth - orig_rect.getWidth());
+ }
+
+ S32 new_height = orig_rect.getHeight() + y_multiple * delta_y;
+ if( new_height < mMinHeight )
+ {
+ new_height = mMinHeight;
+ delta_y = y_multiple * (mMinHeight - orig_rect.getHeight());
+ }
+
+ switch( mCorner )
+ {
+ case LEFT_TOP:
+ scaled_rect.translate(delta_x, 0);
+ break;
+ case LEFT_BOTTOM:
+ scaled_rect.translate(delta_x, delta_y);
+ break;
+ case RIGHT_TOP:
+ break;
+ case RIGHT_BOTTOM:
+ scaled_rect.translate(0, delta_y);
+ break;
+ }
+
+ // temporarily set new parent rect
+ scaled_rect.mRight = scaled_rect.mLeft + new_width;
+ scaled_rect.mTop = scaled_rect.mBottom + new_height;
+ resizing_view->setRect(scaled_rect);
+
+ LLView* snap_view = NULL;
+ LLView* test_view = NULL;
+
+ static LLUICachedControl<S32> snap_margin ("SnapMargin", 0);
+ // now do snapping
+ switch(mCorner)
+ {
+ case LEFT_TOP:
+ snap_view = resizing_view->findSnapEdge(scaled_rect.mLeft, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, snap_margin);
+ test_view = resizing_view->findSnapEdge(scaled_rect.mTop, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, snap_margin);
+ if (!snap_view)
+ {
+ snap_view = test_view;
+ }
+ break;
+ case LEFT_BOTTOM:
+ snap_view = resizing_view->findSnapEdge(scaled_rect.mLeft, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, snap_margin);
+ test_view = resizing_view->findSnapEdge(scaled_rect.mBottom, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, snap_margin);
+ if (!snap_view)
+ {
+ snap_view = test_view;
+ }
+ break;
+ case RIGHT_TOP:
+ snap_view = resizing_view->findSnapEdge(scaled_rect.mRight, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, snap_margin);
+ test_view = resizing_view->findSnapEdge(scaled_rect.mTop, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, snap_margin);
+ if (!snap_view)
+ {
+ snap_view = test_view;
+ }
+ break;
+ case RIGHT_BOTTOM:
+ snap_view = resizing_view->findSnapEdge(scaled_rect.mRight, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, snap_margin);
+ test_view = resizing_view->findSnapEdge(scaled_rect.mBottom, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, snap_margin);
+ if (!snap_view)
+ {
+ snap_view = test_view;
+ }
+ break;
+ }
+
+ // register "snap" behavior with snapped view
+ resizing_view->setSnappedTo(snap_view);
+
+ // reset parent rect
+ resizing_view->setRect(orig_rect);
+
+ // translate and scale to new shape
+ resizing_view->setShape(scaled_rect, true);
+
+ // update last valid mouse cursor position based on resized view's actual size
+ LLRect new_rect = resizing_view->getRect();
+ S32 actual_delta_x = 0;
+ S32 actual_delta_y = 0;
+ switch(mCorner)
+ {
+ case LEFT_TOP:
+ 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:
+ 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:
+ 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:
+ 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;
+ }
+ }
+
+ handled = true;
+ }
+ else // don't have mouse capture
+ {
+ if( pointInHandle( x, y ) )
+ {
+ handled = true;
+ }
+ }
+
+ if( handled )
+ {
+ switch( mCorner )
+ {
+ case RIGHT_BOTTOM:
+ case LEFT_TOP:
+ getWindow()->setCursor(UI_CURSOR_SIZENWSE);
+ break;
+ case LEFT_BOTTOM:
+ case RIGHT_TOP:
+ getWindow()->setCursor(UI_CURSOR_SIZENESW);
+ break;
+ }
+ }
+
+ return handled;
+} // end handleHover
+
+
+// assumes GL state is set for 2D
+void LLResizeHandle::draw()
+{
+ if( mImage.notNull() && getVisible() && (RIGHT_BOTTOM == mCorner) )
+ {
+ mImage->draw(0, 0);
+ }
+}
+
+
+bool LLResizeHandle::pointInHandle( S32 x, S32 y )
+{
+ if( pointInView(x, y) )
+ {
+ const S32 TOP_BORDER = (getRect().getHeight() - RESIZE_BORDER_WIDTH);
+ const S32 RIGHT_BORDER = (getRect().getWidth() - RESIZE_BORDER_WIDTH);
+
+ switch( mCorner )
+ {
+ case LEFT_TOP: return (x <= RESIZE_BORDER_WIDTH) || (y >= TOP_BORDER);
+ case LEFT_BOTTOM: return (x <= RESIZE_BORDER_WIDTH) || (y <= RESIZE_BORDER_WIDTH);
+ case RIGHT_TOP: return (x >= RIGHT_BORDER) || (y >= TOP_BORDER);
+ case RIGHT_BOTTOM: return true;
+ }
+ }
+ return false;
+}
diff --git a/indra/llui/llresizehandle.h b/indra/llui/llresizehandle.h
index 6dddc42f5e..6f4199a782 100644
--- a/indra/llui/llresizehandle.h
+++ b/indra/llui/llresizehandle.h
@@ -1,79 +1,79 @@
-/**
- * @file llresizehandle.h
- * @brief LLResizeHandle 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$
- */
-
-#ifndef LL_RESIZEHANDLE_H
-#define LL_RESIZEHANDLE_H
-
-#include "stdtypes.h"
-#include "llview.h"
-#include "llcoord.h"
-
-
-class LLResizeHandle : public LLView
-{
-public:
- enum ECorner { LEFT_TOP, LEFT_BOTTOM, RIGHT_TOP, RIGHT_BOTTOM };
-
- struct Params : public LLInitParam::Block<Params, LLView::Params>
- {
- Mandatory<ECorner> corner;
- Optional<S32> min_width;
- Optional<S32> min_height;
- Params();
- };
-
- ~LLResizeHandle();
-protected:
- LLResizeHandle(const LLResizeHandle::Params&);
- friend class LLUICtrlFactory;
-public:
- virtual void draw();
- 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);
-
- void setResizeLimits( S32 min_width, S32 min_height ) { mMinWidth = min_width; mMinHeight = min_height; }
-
-private:
- bool pointInHandle( S32 x, S32 y );
-
- S32 mDragLastScreenX;
- S32 mDragLastScreenY;
- S32 mLastMouseScreenX;
- S32 mLastMouseScreenY;
- LLCoordGL mLastMouseDir;
- LLPointer<LLUIImage> mImage;
- S32 mMinWidth;
- S32 mMinHeight;
- const ECorner mCorner;
-};
-
-const S32 RESIZE_HANDLE_HEIGHT = 11;
-const S32 RESIZE_HANDLE_WIDTH = 11;
-
-#endif // LL_RESIZEHANDLE_H
-
-
+/**
+ * @file llresizehandle.h
+ * @brief LLResizeHandle 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$
+ */
+
+#ifndef LL_RESIZEHANDLE_H
+#define LL_RESIZEHANDLE_H
+
+#include "stdtypes.h"
+#include "llview.h"
+#include "llcoord.h"
+
+
+class LLResizeHandle : public LLView
+{
+public:
+ enum ECorner { LEFT_TOP, LEFT_BOTTOM, RIGHT_TOP, RIGHT_BOTTOM };
+
+ struct Params : public LLInitParam::Block<Params, LLView::Params>
+ {
+ Mandatory<ECorner> corner;
+ Optional<S32> min_width;
+ Optional<S32> min_height;
+ Params();
+ };
+
+ ~LLResizeHandle();
+protected:
+ LLResizeHandle(const LLResizeHandle::Params&);
+ friend class LLUICtrlFactory;
+public:
+ virtual void draw();
+ 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);
+
+ void setResizeLimits( S32 min_width, S32 min_height ) { mMinWidth = min_width; mMinHeight = min_height; }
+
+private:
+ bool pointInHandle( S32 x, S32 y );
+
+ S32 mDragLastScreenX;
+ S32 mDragLastScreenY;
+ S32 mLastMouseScreenX;
+ S32 mLastMouseScreenY;
+ LLCoordGL mLastMouseDir;
+ LLPointer<LLUIImage> mImage;
+ S32 mMinWidth;
+ S32 mMinHeight;
+ const ECorner mCorner;
+};
+
+const S32 RESIZE_HANDLE_HEIGHT = 11;
+const S32 RESIZE_HANDLE_WIDTH = 11;
+
+#endif // LL_RESIZEHANDLE_H
+
+
diff --git a/indra/llui/llresmgr.cpp b/indra/llui/llresmgr.cpp
index 1d166c8bbb..342842e328 100644
--- a/indra/llui/llresmgr.cpp
+++ b/indra/llui/llresmgr.cpp
@@ -1,267 +1,267 @@
-/**
- * @file llresmgr.cpp
- * @brief Localized resource manager
- *
- * $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$
- */
-
-// NOTE: this is a MINIMAL implementation. The interface will remain, but the implementation will
-// (when the time is right) become dynamic and probably use external files.
-
-#include "linden_common.h"
-
-#include "llresmgr.h"
-#include "llfontgl.h"
-#include "llerror.h"
-#include "llstring.h"
-
-
-LLResMgr::LLResMgr()
-{
- // Set default
- setLocale( LLLOCALE_USA );
-}
-
-
-void LLResMgr::setLocale( LLLOCALE_ID locale_id )
-{
- mLocale = locale_id;
-}
-
-char LLResMgr::getDecimalPoint() const
-{
- char decimal = localeconv()->decimal_point[0];
-
- return decimal;
-}
-
-char LLResMgr::getThousandsSeparator() const
-{
- char separator = localeconv()->thousands_sep[0];
-
- return separator;
-}
-
-char LLResMgr::getMonetaryDecimalPoint() const
-{
- char decimal = localeconv()->mon_decimal_point[0];
-
- return decimal;
-}
-
-char LLResMgr::getMonetaryThousandsSeparator() const
-{
- char separator = localeconv()->mon_thousands_sep[0];
-
- return separator;
-}
-
-
-// Sets output to a string of integers with monetary separators inserted according to the locale.
-std::string LLResMgr::getMonetaryString( S32 input ) const
-{
- std::string output;
-
- LLLocale locale(LLLocale::USER_LOCALE);
- struct lconv *conv = localeconv();
-
- char* negative_sign = conv->negative_sign;
- char separator = getMonetaryThousandsSeparator();
- char* grouping = conv->mon_grouping;
-
- // Note on mon_grouping:
- // Specifies a string that defines the size of each group of digits in formatted monetary quantities.
- // The operand for the mon_grouping keyword consists of a sequence of semicolon-separated integers.
- // Each integer specifies the number of digits in a group. The initial integer defines the size of
- // the group immediately to the left of the decimal delimiter. The following integers define succeeding
- // groups to the left of the previous group. If the last integer is not -1, the size of the previous
- // group (if any) is repeatedly used for the remainder of the digits. If the last integer is -1, no
- // further grouping is performed.
-
-
- // Note: we assume here that the currency symbol goes on the left. (Hey, it's Lindens! We can just decide.)
- bool negative = (input < 0 );
- bool negative_before = negative && (conv->n_sign_posn != 2);
- bool negative_after = negative && (conv->n_sign_posn == 2);
-
- std::string digits = llformat("%u", abs(input));
- if( !grouping || !grouping[0] )
- {
- if( negative_before )
- {
- output.append( negative_sign );
- }
- output.append( digits );
- if( negative_after )
- {
- output.append( negative_sign );
- }
- return output;
- }
-
- S32 groupings[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
- S32 cur_group;
- for( cur_group = 0; grouping[ cur_group ]; cur_group++ )
- {
- if( grouping[ cur_group ] != ';' )
- {
- groupings[cur_group] = grouping[ cur_group ];
- }
- cur_group++;
-
- if( groupings[cur_group] < 0 )
- {
- break;
- }
- }
- S32 group_count = cur_group;
-
- char reversed_output[20] = ""; /* Flawfinder: ignore */
- char forward_output[20] = ""; /* Flawfinder: ignore */
- S32 output_pos = 0;
-
- cur_group = 0;
- S32 pos = digits.size()-1;
- S32 count_within_group = 0;
- while( (pos >= 0) && (groupings[cur_group] >= 0) )
- {
- count_within_group++;
- if( count_within_group > groupings[cur_group] )
- {
- count_within_group = 1;
- reversed_output[ output_pos++ ] = separator;
-
- if( (cur_group + 1) >= group_count )
- {
- break;
- }
- else
- if( groupings[cur_group + 1] > 0 )
- {
- cur_group++;
- }
- }
- reversed_output[ output_pos++ ] = digits[pos--];
- }
-
- while( pos >= 0 )
- {
- reversed_output[ output_pos++ ] = digits[pos--];
- }
-
-
- reversed_output[ output_pos ] = '\0';
- forward_output[ output_pos ] = '\0';
-
- for( S32 i = 0; i < output_pos; i++ )
- {
- forward_output[ output_pos - 1 - i ] = reversed_output[ i ];
- }
-
- if( negative_before )
- {
- output.append( negative_sign );
- }
- output.append( forward_output );
- if( negative_after )
- {
- output.append( negative_sign );
- }
- return output;
-}
-
-void LLResMgr::getIntegerString( std::string& output, S32 input ) const
-{
- // handle special case of input value being zero
- if (input == 0)
- {
- output = "0";
- return;
- }
-
- // *NOTE: this method does not handle negative input integers correctly
- S32 fraction = 0;
- std::string fraction_string;
- S32 remaining_count = input;
- while(remaining_count > 0)
- {
- fraction = (remaining_count) % 1000;
-
- if (!output.empty())
- {
- if (fraction == remaining_count)
- {
- fraction_string = llformat_to_utf8("%d%c", fraction, getThousandsSeparator());
- }
- else
- {
- fraction_string = llformat_to_utf8("%3.3d%c", fraction, getThousandsSeparator());
- }
- output = fraction_string + output;
- }
- else
- {
- if (fraction == remaining_count)
- {
- fraction_string = llformat("%d", fraction);
- }
- else
- {
- fraction_string = llformat("%3.3d", fraction);
- }
- output = fraction_string;
- }
- remaining_count /= 1000;
- }
-}
-
-#if LL_WINDOWS
-const std::string LLLocale::USER_LOCALE("English_United States.1252");// = LLStringUtil::null;
-const std::string LLLocale::SYSTEM_LOCALE("English_United States.1252");
-#elif LL_DARWIN
-const std::string LLLocale::USER_LOCALE("en_US.iso8859-1");// = LLStringUtil::null;
-const std::string LLLocale::SYSTEM_LOCALE("en_US.iso8859-1");
-#else // LL_LINUX likes this
-const std::string LLLocale::USER_LOCALE("en_US.utf8");
-const std::string LLLocale::SYSTEM_LOCALE("C");
-#endif
-
-
-LLLocale::LLLocale(const std::string& locale_string)
-{
- mPrevLocaleString = setlocale( LC_ALL, NULL );
- char* new_locale_string = setlocale( LC_ALL, locale_string.c_str());
- if ( new_locale_string == NULL)
- {
- LL_WARNS_ONCE("LLLocale") << "Failed to set locale " << locale_string << LL_ENDL;
- setlocale(LC_ALL, SYSTEM_LOCALE.c_str());
- }
- //else
- //{
- // LL_INFOS() << "Set locale to " << new_locale_string << LL_ENDL;
- //}
-}
-
-LLLocale::~LLLocale()
-{
- setlocale( LC_ALL, mPrevLocaleString.c_str() );
-}
+/**
+ * @file llresmgr.cpp
+ * @brief Localized resource manager
+ *
+ * $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$
+ */
+
+// NOTE: this is a MINIMAL implementation. The interface will remain, but the implementation will
+// (when the time is right) become dynamic and probably use external files.
+
+#include "linden_common.h"
+
+#include "llresmgr.h"
+#include "llfontgl.h"
+#include "llerror.h"
+#include "llstring.h"
+
+
+LLResMgr::LLResMgr()
+{
+ // Set default
+ setLocale( LLLOCALE_USA );
+}
+
+
+void LLResMgr::setLocale( LLLOCALE_ID locale_id )
+{
+ mLocale = locale_id;
+}
+
+char LLResMgr::getDecimalPoint() const
+{
+ char decimal = localeconv()->decimal_point[0];
+
+ return decimal;
+}
+
+char LLResMgr::getThousandsSeparator() const
+{
+ char separator = localeconv()->thousands_sep[0];
+
+ return separator;
+}
+
+char LLResMgr::getMonetaryDecimalPoint() const
+{
+ char decimal = localeconv()->mon_decimal_point[0];
+
+ return decimal;
+}
+
+char LLResMgr::getMonetaryThousandsSeparator() const
+{
+ char separator = localeconv()->mon_thousands_sep[0];
+
+ return separator;
+}
+
+
+// Sets output to a string of integers with monetary separators inserted according to the locale.
+std::string LLResMgr::getMonetaryString( S32 input ) const
+{
+ std::string output;
+
+ LLLocale locale(LLLocale::USER_LOCALE);
+ struct lconv *conv = localeconv();
+
+ char* negative_sign = conv->negative_sign;
+ char separator = getMonetaryThousandsSeparator();
+ char* grouping = conv->mon_grouping;
+
+ // Note on mon_grouping:
+ // Specifies a string that defines the size of each group of digits in formatted monetary quantities.
+ // The operand for the mon_grouping keyword consists of a sequence of semicolon-separated integers.
+ // Each integer specifies the number of digits in a group. The initial integer defines the size of
+ // the group immediately to the left of the decimal delimiter. The following integers define succeeding
+ // groups to the left of the previous group. If the last integer is not -1, the size of the previous
+ // group (if any) is repeatedly used for the remainder of the digits. If the last integer is -1, no
+ // further grouping is performed.
+
+
+ // Note: we assume here that the currency symbol goes on the left. (Hey, it's Lindens! We can just decide.)
+ bool negative = (input < 0 );
+ bool negative_before = negative && (conv->n_sign_posn != 2);
+ bool negative_after = negative && (conv->n_sign_posn == 2);
+
+ std::string digits = llformat("%u", abs(input));
+ if( !grouping || !grouping[0] )
+ {
+ if( negative_before )
+ {
+ output.append( negative_sign );
+ }
+ output.append( digits );
+ if( negative_after )
+ {
+ output.append( negative_sign );
+ }
+ return output;
+ }
+
+ S32 groupings[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ S32 cur_group;
+ for( cur_group = 0; grouping[ cur_group ]; cur_group++ )
+ {
+ if( grouping[ cur_group ] != ';' )
+ {
+ groupings[cur_group] = grouping[ cur_group ];
+ }
+ cur_group++;
+
+ if( groupings[cur_group] < 0 )
+ {
+ break;
+ }
+ }
+ S32 group_count = cur_group;
+
+ char reversed_output[20] = ""; /* Flawfinder: ignore */
+ char forward_output[20] = ""; /* Flawfinder: ignore */
+ S32 output_pos = 0;
+
+ cur_group = 0;
+ S32 pos = digits.size()-1;
+ S32 count_within_group = 0;
+ while( (pos >= 0) && (groupings[cur_group] >= 0) )
+ {
+ count_within_group++;
+ if( count_within_group > groupings[cur_group] )
+ {
+ count_within_group = 1;
+ reversed_output[ output_pos++ ] = separator;
+
+ if( (cur_group + 1) >= group_count )
+ {
+ break;
+ }
+ else
+ if( groupings[cur_group + 1] > 0 )
+ {
+ cur_group++;
+ }
+ }
+ reversed_output[ output_pos++ ] = digits[pos--];
+ }
+
+ while( pos >= 0 )
+ {
+ reversed_output[ output_pos++ ] = digits[pos--];
+ }
+
+
+ reversed_output[ output_pos ] = '\0';
+ forward_output[ output_pos ] = '\0';
+
+ for( S32 i = 0; i < output_pos; i++ )
+ {
+ forward_output[ output_pos - 1 - i ] = reversed_output[ i ];
+ }
+
+ if( negative_before )
+ {
+ output.append( negative_sign );
+ }
+ output.append( forward_output );
+ if( negative_after )
+ {
+ output.append( negative_sign );
+ }
+ return output;
+}
+
+void LLResMgr::getIntegerString( std::string& output, S32 input ) const
+{
+ // handle special case of input value being zero
+ if (input == 0)
+ {
+ output = "0";
+ return;
+ }
+
+ // *NOTE: this method does not handle negative input integers correctly
+ S32 fraction = 0;
+ std::string fraction_string;
+ S32 remaining_count = input;
+ while(remaining_count > 0)
+ {
+ fraction = (remaining_count) % 1000;
+
+ if (!output.empty())
+ {
+ if (fraction == remaining_count)
+ {
+ fraction_string = llformat_to_utf8("%d%c", fraction, getThousandsSeparator());
+ }
+ else
+ {
+ fraction_string = llformat_to_utf8("%3.3d%c", fraction, getThousandsSeparator());
+ }
+ output = fraction_string + output;
+ }
+ else
+ {
+ if (fraction == remaining_count)
+ {
+ fraction_string = llformat("%d", fraction);
+ }
+ else
+ {
+ fraction_string = llformat("%3.3d", fraction);
+ }
+ output = fraction_string;
+ }
+ remaining_count /= 1000;
+ }
+}
+
+#if LL_WINDOWS
+const std::string LLLocale::USER_LOCALE("English_United States.1252");// = LLStringUtil::null;
+const std::string LLLocale::SYSTEM_LOCALE("English_United States.1252");
+#elif LL_DARWIN
+const std::string LLLocale::USER_LOCALE("en_US.iso8859-1");// = LLStringUtil::null;
+const std::string LLLocale::SYSTEM_LOCALE("en_US.iso8859-1");
+#else // LL_LINUX likes this
+const std::string LLLocale::USER_LOCALE("en_US.utf8");
+const std::string LLLocale::SYSTEM_LOCALE("C");
+#endif
+
+
+LLLocale::LLLocale(const std::string& locale_string)
+{
+ mPrevLocaleString = setlocale( LC_ALL, NULL );
+ char* new_locale_string = setlocale( LC_ALL, locale_string.c_str());
+ if ( new_locale_string == NULL)
+ {
+ LL_WARNS_ONCE("LLLocale") << "Failed to set locale " << locale_string << LL_ENDL;
+ setlocale(LC_ALL, SYSTEM_LOCALE.c_str());
+ }
+ //else
+ //{
+ // LL_INFOS() << "Set locale to " << new_locale_string << LL_ENDL;
+ //}
+}
+
+LLLocale::~LLLocale()
+{
+ setlocale( LC_ALL, mPrevLocaleString.c_str() );
+}
diff --git a/indra/llui/llresmgr.h b/indra/llui/llresmgr.h
index b19d8d40b8..7ad6659a44 100644
--- a/indra/llui/llresmgr.h
+++ b/indra/llui/llresmgr.h
@@ -1,25 +1,25 @@
-/**
+/**
* @file llresmgr.h
* @brief Localized resource manager
*
* $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$
*/
@@ -35,43 +35,43 @@
enum LLLOCALE_ID
{
- LLLOCALE_USA,
- LLLOCALE_UK,
- LLLOCALE_COUNT // Number of values in this enum. Keep at end.
+ LLLOCALE_USA,
+ LLLOCALE_UK,
+ LLLOCALE_COUNT // Number of values in this enum. Keep at end.
};
class LLResMgr : public LLSingleton<LLResMgr>
{
- LLSINGLETON(LLResMgr);
+ LLSINGLETON(LLResMgr);
public:
- void setLocale( LLLOCALE_ID locale_id );
- LLLOCALE_ID getLocale() const { return mLocale; }
+ void setLocale( LLLOCALE_ID locale_id );
+ LLLOCALE_ID getLocale() const { return mLocale; }
- char getDecimalPoint() const;
- char getThousandsSeparator() const;
+ char getDecimalPoint() const;
+ char getThousandsSeparator() const;
- char getMonetaryDecimalPoint() const;
- char getMonetaryThousandsSeparator() const;
- std::string getMonetaryString( S32 input ) const;
- void getIntegerString( std::string& output, S32 input ) const;
+ char getMonetaryDecimalPoint() const;
+ char getMonetaryThousandsSeparator() const;
+ std::string getMonetaryString( S32 input ) const;
+ void getIntegerString( std::string& output, S32 input ) const;
private:
- LLLOCALE_ID mLocale;
+ LLLOCALE_ID mLocale;
};
class LLLocale
{
public:
- LLLocale(const std::string& locale_string);
- virtual ~LLLocale();
+ LLLocale(const std::string& locale_string);
+ virtual ~LLLocale();
- static const std::string USER_LOCALE;
- static const std::string SYSTEM_LOCALE;
+ static const std::string USER_LOCALE;
+ static const std::string SYSTEM_LOCALE;
private:
- std::string mPrevLocaleString;
+ std::string mPrevLocaleString;
};
#endif // LL_RESMGR_
diff --git a/indra/llui/llrngwriter.cpp b/indra/llui/llrngwriter.cpp
index 4bd1561425..1b4008cff2 100644
--- a/indra/llui/llrngwriter.cpp
+++ b/indra/llui/llrngwriter.cpp
@@ -1,25 +1,25 @@
-/**
+/**
* @file llrngwriter.cpp
* @brief Generates Relax NG schema from param blocks
*
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -33,9 +33,9 @@
#include "boost/bind.hpp"
-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;
+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;
//
// LLRNGWriter - writes Relax NG schema files based on a param block
@@ -43,276 +43,276 @@ static LLInitParam::Parser::parser_inspect_func_map_t sInspectFuncs;
LLRNGWriter::LLRNGWriter()
: Parser(sReadFuncs, sWriteFuncs, sInspectFuncs)
{
- // register various callbacks for inspecting the contents of a param block
- registerInspectFunc<bool>(boost::bind(&LLRNGWriter::writeAttribute, this, "boolean", _1, _2, _3, _4));
- registerInspectFunc<std::string>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
- registerInspectFunc<U8>(boost::bind(&LLRNGWriter::writeAttribute, this, "unsignedByte", _1, _2, _3, _4));
- registerInspectFunc<S8>(boost::bind(&LLRNGWriter::writeAttribute, this, "signedByte", _1, _2, _3, _4));
- registerInspectFunc<U16>(boost::bind(&LLRNGWriter::writeAttribute, this, "unsignedShort", _1, _2, _3, _4));
- registerInspectFunc<S16>(boost::bind(&LLRNGWriter::writeAttribute, this, "signedShort", _1, _2, _3, _4));
- registerInspectFunc<U32>(boost::bind(&LLRNGWriter::writeAttribute, this, "unsignedInt", _1, _2, _3, _4));
- registerInspectFunc<S32>(boost::bind(&LLRNGWriter::writeAttribute, this, "integer", _1, _2, _3, _4));
- registerInspectFunc<F32>(boost::bind(&LLRNGWriter::writeAttribute, this, "float", _1, _2, _3, _4));
- registerInspectFunc<F64>(boost::bind(&LLRNGWriter::writeAttribute, this, "double", _1, _2, _3, _4));
- registerInspectFunc<LLColor4>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
- registerInspectFunc<LLUIColor>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
- registerInspectFunc<LLUUID>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
- registerInspectFunc<LLSD>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
+ // register various callbacks for inspecting the contents of a param block
+ registerInspectFunc<bool>(boost::bind(&LLRNGWriter::writeAttribute, this, "boolean", _1, _2, _3, _4));
+ registerInspectFunc<std::string>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
+ registerInspectFunc<U8>(boost::bind(&LLRNGWriter::writeAttribute, this, "unsignedByte", _1, _2, _3, _4));
+ registerInspectFunc<S8>(boost::bind(&LLRNGWriter::writeAttribute, this, "signedByte", _1, _2, _3, _4));
+ registerInspectFunc<U16>(boost::bind(&LLRNGWriter::writeAttribute, this, "unsignedShort", _1, _2, _3, _4));
+ registerInspectFunc<S16>(boost::bind(&LLRNGWriter::writeAttribute, this, "signedShort", _1, _2, _3, _4));
+ registerInspectFunc<U32>(boost::bind(&LLRNGWriter::writeAttribute, this, "unsignedInt", _1, _2, _3, _4));
+ registerInspectFunc<S32>(boost::bind(&LLRNGWriter::writeAttribute, this, "integer", _1, _2, _3, _4));
+ registerInspectFunc<F32>(boost::bind(&LLRNGWriter::writeAttribute, this, "float", _1, _2, _3, _4));
+ registerInspectFunc<F64>(boost::bind(&LLRNGWriter::writeAttribute, this, "double", _1, _2, _3, _4));
+ registerInspectFunc<LLColor4>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
+ registerInspectFunc<LLUIColor>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
+ registerInspectFunc<LLUUID>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
+ registerInspectFunc<LLSD>(boost::bind(&LLRNGWriter::writeAttribute, this, "string", _1, _2, _3, _4));
}
void LLRNGWriter::writeRNG(const std::string& type_name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace)
{
- mGrammarNode = node;
- mGrammarNode->setName("grammar");
- mGrammarNode->createChild("xmlns", true)->setStringValue("http://relaxng.org/ns/structure/1.0");
- mGrammarNode->createChild("datatypeLibrary", true)->setStringValue("http://www.w3.org/2001/XMLSchema-datatypes");
- mGrammarNode->createChild("ns", true)->setStringValue(xml_namespace);
+ mGrammarNode = node;
+ mGrammarNode->setName("grammar");
+ mGrammarNode->createChild("xmlns", true)->setStringValue("http://relaxng.org/ns/structure/1.0");
+ mGrammarNode->createChild("datatypeLibrary", true)->setStringValue("http://www.w3.org/2001/XMLSchema-datatypes");
+ mGrammarNode->createChild("ns", true)->setStringValue(xml_namespace);
- node = mGrammarNode->createChild("start", false);
- node = node->createChild("ref", false);
- node->createChild("name", true)->setStringValue(type_name);
+ node = mGrammarNode->createChild("start", false);
+ node = node->createChild("ref", false);
+ node->createChild("name", true)->setStringValue(type_name);
- addDefinition(type_name, block);
+ addDefinition(type_name, block);
}
void LLRNGWriter::addDefinition(const std::string& type_name, const LLInitParam::BaseBlock& block)
{
- if (mDefinedElements.find(type_name) != mDefinedElements.end()) return;
- mDefinedElements.insert(type_name);
-
- LLXMLNodePtr node = mGrammarNode->createChild("define", false);
- node->createChild("name", true)->setStringValue(type_name);
-
- mElementNode = node->createChild("element", false);
- mElementNode->createChild("name", true)->setStringValue(type_name);
- mChildrenNode = mElementNode->createChild("zeroOrMore", false)->createChild("choice", false);
-
- mAttributesWritten.first = mElementNode;
- mAttributesWritten.second.clear();
- mElementsWritten.clear();
-
- block.inspectBlock(*this);
-
- // add includes for all possible children
- const std::type_info* type = *LLWidgetTypeRegistry::instance().getValue(type_name);
- const widget_registry_t* widget_registryp = LLChildRegistryRegistry::instance().getValue(type);
-
- // add include declarations for all valid children
- for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems();
- it != widget_registryp->currentRegistrar().endItems();
- ++it)
- {
- std::string child_name = it->first;
- if (child_name == type_name)
- {
- continue;
- }
-
- LLXMLNodePtr old_element_node = mElementNode;
- LLXMLNodePtr old_child_node = mChildrenNode;
- //FIXME: add LLDefaultParamBlockRegistry back when working on schema generation
- //addDefinition(child_name, (*LLDefaultParamBlockRegistry::instance().getValue(type))());
- mElementNode = old_element_node;
- mChildrenNode = old_child_node;
-
- mChildrenNode->createChild("ref", false)->createChild("name", true)->setStringValue(child_name);
- }
-
- if (mChildrenNode->mChildren.isNull())
- {
- // remove unused children node
- mChildrenNode->mParent->mParent->deleteChild(mChildrenNode->mParent);
- }
+ if (mDefinedElements.find(type_name) != mDefinedElements.end()) return;
+ mDefinedElements.insert(type_name);
+
+ LLXMLNodePtr node = mGrammarNode->createChild("define", false);
+ node->createChild("name", true)->setStringValue(type_name);
+
+ mElementNode = node->createChild("element", false);
+ mElementNode->createChild("name", true)->setStringValue(type_name);
+ mChildrenNode = mElementNode->createChild("zeroOrMore", false)->createChild("choice", false);
+
+ mAttributesWritten.first = mElementNode;
+ mAttributesWritten.second.clear();
+ mElementsWritten.clear();
+
+ block.inspectBlock(*this);
+
+ // add includes for all possible children
+ const std::type_info* type = *LLWidgetTypeRegistry::instance().getValue(type_name);
+ const widget_registry_t* widget_registryp = LLChildRegistryRegistry::instance().getValue(type);
+
+ // add include declarations for all valid children
+ for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems();
+ it != widget_registryp->currentRegistrar().endItems();
+ ++it)
+ {
+ std::string child_name = it->first;
+ if (child_name == type_name)
+ {
+ continue;
+ }
+
+ LLXMLNodePtr old_element_node = mElementNode;
+ LLXMLNodePtr old_child_node = mChildrenNode;
+ //FIXME: add LLDefaultParamBlockRegistry back when working on schema generation
+ //addDefinition(child_name, (*LLDefaultParamBlockRegistry::instance().getValue(type))());
+ mElementNode = old_element_node;
+ mChildrenNode = old_child_node;
+
+ mChildrenNode->createChild("ref", false)->createChild("name", true)->setStringValue(child_name);
+ }
+
+ if (mChildrenNode->mChildren.isNull())
+ {
+ // remove unused children node
+ mChildrenNode->mParent->mParent->deleteChild(mChildrenNode->mParent);
+ }
}
void LLRNGWriter::writeAttribute(const std::string& type, const Parser::name_stack_t& stack, S32 min_count, S32 max_count, const std::vector<std::string>* possible_values)
{
- if (max_count == 0) return;
-
- name_stack_t non_empty_names;
- std::string attribute_name;
- for (name_stack_t::const_iterator it = stack.begin();
- it != stack.end();
- ++it)
- {
- const std::string& name = it->first;
- if (!name.empty())
- {
- non_empty_names.push_back(*it);
- }
- }
-
- if (non_empty_names.empty()) return;
-
- for (name_stack_t::const_iterator it = non_empty_names.begin();
- it != non_empty_names.end();
- ++it)
- {
- if (!attribute_name.empty())
- {
- attribute_name += ".";
- }
- attribute_name += it->first;
- }
-
- // singular attribute, e.g. <foo bar="1"/>
- if (non_empty_names.size() == 1 && max_count == 1)
- {
- if (mAttributesWritten.second.find(attribute_name) == mAttributesWritten.second.end())
- {
- LLXMLNodePtr node = createCardinalityNode(mElementNode, min_count, max_count)->createChild("attribute", false);
- node->createChild("name", true)->setStringValue(attribute_name);
- node->createChild("data", false)->createChild("type", true)->setStringValue(type);
-
- mAttributesWritten.second.insert(attribute_name);
- }
- }
- // compound attribute
- else
- {
- std::string element_name;
-
- // traverse all but last element, leaving that as an attribute name
- name_stack_t::const_iterator end_it = non_empty_names.end();
- end_it--;
-
- for (name_stack_t::const_iterator it = non_empty_names.begin();
- it != end_it;
- ++it)
- {
- if (it != non_empty_names.begin())
- {
- element_name += ".";
- }
- element_name += it->first;
- }
-
- elements_map_t::iterator found_it = mElementsWritten.find(element_name);
- // <choice>
- // <group>
- // <optional>
- // <attribute name="foo.bar"><data type="string"/></attribute>
- // </optional>
- // <optional>
- // <attribute name="foo.baz"><data type="integer"/></attribute>
- // </optional>
- // </group>
- // <element name="foo">
- // <optional>
- // <attribute name="bar"><data type="string"/></attribute>
- // </optional>
- // <optional>
- // <attribute name="baz"><data type="string"/></attribute>
- // </optional>
- // </element>
- // <element name="outer.foo">
- // <ref name="foo"/>
- // </element>
- // </choice>
-
- if (found_it != mElementsWritten.end())
- {
- // reuse existing element
- LLXMLNodePtr choice_node = found_it->second.first;
-
- // attribute with this name not already written?
- if (found_it->second.second.find(attribute_name) == found_it->second.second.end())
- {
- // append to <group>
- LLXMLNodePtr node = choice_node->mChildren->head;
- node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
- node->createChild("name", true)->setStringValue(attribute_name);
- addTypeNode(node, type, possible_values);
-
- // append to <element>
- node = choice_node->mChildren->head->mNext->mChildren->head;
- node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
- node->createChild("name", true)->setStringValue(non_empty_names.back().first);
- addTypeNode(node, type, possible_values);
-
- // append to <element>
- //node = choice_node->mChildren->head->mNext->mNext->mChildren->head;
- //node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
- //node->createChild("name", true)->setStringValue(non_empty_names.back().first);
- //addTypeNode(node, type, possible_values);
-
- found_it->second.second.insert(attribute_name);
- }
- }
- else
- {
- LLXMLNodePtr choice_node = mElementNode->createChild("choice", false);
-
- LLXMLNodePtr node = choice_node->createChild("group", false);
- node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
- node->createChild("name", true)->setStringValue(attribute_name);
- addTypeNode(node, type, possible_values);
-
- node = choice_node->createChild("optional", false);
- node = node->createChild("element", false);
- node->createChild("name", true)->setStringValue(element_name);
- node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
- node->createChild("name", true)->setStringValue(non_empty_names.back().first);
- addTypeNode(node, type, possible_values);
-
- //node = choice_node->createChild("optional", false);
- //node = node->createChild("element", false);
- //node->createChild("name", true)->setStringValue(mDefinitionName + "." + element_name);
- //node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
- //node->createChild("name", true)->setStringValue(non_empty_names.back().first);
- //addTypeNode(node, type, possible_values);
-
- attribute_data_t& attribute_data = mElementsWritten[element_name];
- attribute_data.first = choice_node;
- attribute_data.second.insert(attribute_name);
- }
- }
+ if (max_count == 0) return;
+
+ name_stack_t non_empty_names;
+ std::string attribute_name;
+ for (name_stack_t::const_iterator it = stack.begin();
+ it != stack.end();
+ ++it)
+ {
+ const std::string& name = it->first;
+ if (!name.empty())
+ {
+ non_empty_names.push_back(*it);
+ }
+ }
+
+ if (non_empty_names.empty()) return;
+
+ for (name_stack_t::const_iterator it = non_empty_names.begin();
+ it != non_empty_names.end();
+ ++it)
+ {
+ if (!attribute_name.empty())
+ {
+ attribute_name += ".";
+ }
+ attribute_name += it->first;
+ }
+
+ // singular attribute, e.g. <foo bar="1"/>
+ if (non_empty_names.size() == 1 && max_count == 1)
+ {
+ if (mAttributesWritten.second.find(attribute_name) == mAttributesWritten.second.end())
+ {
+ LLXMLNodePtr node = createCardinalityNode(mElementNode, min_count, max_count)->createChild("attribute", false);
+ node->createChild("name", true)->setStringValue(attribute_name);
+ node->createChild("data", false)->createChild("type", true)->setStringValue(type);
+
+ mAttributesWritten.second.insert(attribute_name);
+ }
+ }
+ // compound attribute
+ else
+ {
+ std::string element_name;
+
+ // traverse all but last element, leaving that as an attribute name
+ name_stack_t::const_iterator end_it = non_empty_names.end();
+ end_it--;
+
+ for (name_stack_t::const_iterator it = non_empty_names.begin();
+ it != end_it;
+ ++it)
+ {
+ if (it != non_empty_names.begin())
+ {
+ element_name += ".";
+ }
+ element_name += it->first;
+ }
+
+ elements_map_t::iterator found_it = mElementsWritten.find(element_name);
+ // <choice>
+ // <group>
+ // <optional>
+ // <attribute name="foo.bar"><data type="string"/></attribute>
+ // </optional>
+ // <optional>
+ // <attribute name="foo.baz"><data type="integer"/></attribute>
+ // </optional>
+ // </group>
+ // <element name="foo">
+ // <optional>
+ // <attribute name="bar"><data type="string"/></attribute>
+ // </optional>
+ // <optional>
+ // <attribute name="baz"><data type="string"/></attribute>
+ // </optional>
+ // </element>
+ // <element name="outer.foo">
+ // <ref name="foo"/>
+ // </element>
+ // </choice>
+
+ if (found_it != mElementsWritten.end())
+ {
+ // reuse existing element
+ LLXMLNodePtr choice_node = found_it->second.first;
+
+ // attribute with this name not already written?
+ if (found_it->second.second.find(attribute_name) == found_it->second.second.end())
+ {
+ // append to <group>
+ LLXMLNodePtr node = choice_node->mChildren->head;
+ node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
+ node->createChild("name", true)->setStringValue(attribute_name);
+ addTypeNode(node, type, possible_values);
+
+ // append to <element>
+ node = choice_node->mChildren->head->mNext->mChildren->head;
+ node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
+ node->createChild("name", true)->setStringValue(non_empty_names.back().first);
+ addTypeNode(node, type, possible_values);
+
+ // append to <element>
+ //node = choice_node->mChildren->head->mNext->mNext->mChildren->head;
+ //node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
+ //node->createChild("name", true)->setStringValue(non_empty_names.back().first);
+ //addTypeNode(node, type, possible_values);
+
+ found_it->second.second.insert(attribute_name);
+ }
+ }
+ else
+ {
+ LLXMLNodePtr choice_node = mElementNode->createChild("choice", false);
+
+ LLXMLNodePtr node = choice_node->createChild("group", false);
+ node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
+ node->createChild("name", true)->setStringValue(attribute_name);
+ addTypeNode(node, type, possible_values);
+
+ node = choice_node->createChild("optional", false);
+ node = node->createChild("element", false);
+ node->createChild("name", true)->setStringValue(element_name);
+ node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
+ node->createChild("name", true)->setStringValue(non_empty_names.back().first);
+ addTypeNode(node, type, possible_values);
+
+ //node = choice_node->createChild("optional", false);
+ //node = node->createChild("element", false);
+ //node->createChild("name", true)->setStringValue(mDefinitionName + "." + element_name);
+ //node = createCardinalityNode(node, min_count, max_count)->createChild("attribute", false);
+ //node->createChild("name", true)->setStringValue(non_empty_names.back().first);
+ //addTypeNode(node, type, possible_values);
+
+ attribute_data_t& attribute_data = mElementsWritten[element_name];
+ attribute_data.first = choice_node;
+ attribute_data.second.insert(attribute_name);
+ }
+ }
}
void LLRNGWriter::addTypeNode(LLXMLNodePtr parent_node, const std::string& type, const std::vector<std::string>* possible_values)
{
- if (possible_values)
- {
- LLXMLNodePtr enum_node = parent_node->createChild("choice", false);
- for (std::vector<std::string>::const_iterator it = possible_values->begin();
- it != possible_values->end();
- ++it)
- {
- enum_node->createChild("value", false)->setStringValue(*it);
- }
- }
- else
- {
- parent_node->createChild("data", false)->createChild("type", true)->setStringValue(type);
- }
+ if (possible_values)
+ {
+ LLXMLNodePtr enum_node = parent_node->createChild("choice", false);
+ for (std::vector<std::string>::const_iterator it = possible_values->begin();
+ it != possible_values->end();
+ ++it)
+ {
+ enum_node->createChild("value", false)->setStringValue(*it);
+ }
+ }
+ else
+ {
+ parent_node->createChild("data", false)->createChild("type", true)->setStringValue(type);
+ }
}
LLXMLNodePtr LLRNGWriter::createCardinalityNode(LLXMLNodePtr parent_node, S32 min_count, S32 max_count)
{
- // unlinked by default, meaning this attribute is forbidden
- LLXMLNodePtr count_node = new LLXMLNode();
- if (min_count == 0)
- {
- if (max_count == 1)
- {
- count_node = parent_node->createChild("optional", false);
- }
- else if (max_count > 1)
- {
- count_node = parent_node->createChild("zeroOrMore", false);
- }
- }
- else if (min_count >= 1)
- {
- if (max_count == 1 && min_count == 1)
- {
- // just add raw element, will count as 1 and only 1
- count_node = parent_node;
- }
- else
- {
- count_node = parent_node->createChild("oneOrMore", false);
- }
- }
- return count_node;
+ // unlinked by default, meaning this attribute is forbidden
+ LLXMLNodePtr count_node = new LLXMLNode();
+ if (min_count == 0)
+ {
+ if (max_count == 1)
+ {
+ count_node = parent_node->createChild("optional", false);
+ }
+ else if (max_count > 1)
+ {
+ count_node = parent_node->createChild("zeroOrMore", false);
+ }
+ }
+ else if (min_count >= 1)
+ {
+ if (max_count == 1 && min_count == 1)
+ {
+ // just add raw element, will count as 1 and only 1
+ count_node = parent_node;
+ }
+ else
+ {
+ count_node = parent_node->createChild("oneOrMore", false);
+ }
+ }
+ return count_node;
}
diff --git a/indra/llui/llrngwriter.h b/indra/llui/llrngwriter.h
index c33aa28613..33ec049a1a 100644
--- a/indra/llui/llrngwriter.h
+++ b/indra/llui/llrngwriter.h
@@ -1,25 +1,25 @@
-/**
+/**
* @file llrngwriter.h
* @brief Generates Relax NG schema files from a param block
*
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -32,32 +32,32 @@
class LLRNGWriter : public LLInitParam::Parser
{
- LOG_CLASS(LLRNGWriter);
+ LOG_CLASS(LLRNGWriter);
public:
- void writeRNG(const std::string& name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace);
- void addDefinition(const std::string& type_name, const LLInitParam::BaseBlock& block);
+ void writeRNG(const std::string& name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace);
+ void addDefinition(const std::string& type_name, const LLInitParam::BaseBlock& block);
- /*virtual*/ std::string getCurrentElementName() { return LLStringUtil::null; }
+ /*virtual*/ std::string getCurrentElementName() { return LLStringUtil::null; }
- LLRNGWriter();
+ LLRNGWriter();
private:
- LLXMLNodePtr createCardinalityNode(LLXMLNodePtr parent_node, S32 min_count, S32 max_count);
- void addTypeNode(LLXMLNodePtr parent_node, const std::string& type, const std::vector<std::string>* possible_values);
-
- void writeAttribute(const std::string& type, const Parser::name_stack_t&, S32 min_count, S32 max_count, const std::vector<std::string>* possible_values);
- LLXMLNodePtr mElementNode;
- LLXMLNodePtr mChildrenNode;
- LLXMLNodePtr mGrammarNode;
- std::string mDefinitionName;
-
- typedef std::pair<LLXMLNodePtr, std::set<std::string> > attribute_data_t;
- typedef std::map<std::string, attribute_data_t> elements_map_t;
- typedef std::set<std::string> defined_elements_t;
-
- defined_elements_t mDefinedElements;
- attribute_data_t mAttributesWritten;
- elements_map_t mElementsWritten;
+ LLXMLNodePtr createCardinalityNode(LLXMLNodePtr parent_node, S32 min_count, S32 max_count);
+ void addTypeNode(LLXMLNodePtr parent_node, const std::string& type, const std::vector<std::string>* possible_values);
+
+ void writeAttribute(const std::string& type, const Parser::name_stack_t&, S32 min_count, S32 max_count, const std::vector<std::string>* possible_values);
+ LLXMLNodePtr mElementNode;
+ LLXMLNodePtr mChildrenNode;
+ LLXMLNodePtr mGrammarNode;
+ std::string mDefinitionName;
+
+ typedef std::pair<LLXMLNodePtr, std::set<std::string> > attribute_data_t;
+ typedef std::map<std::string, attribute_data_t> elements_map_t;
+ typedef std::set<std::string> defined_elements_t;
+
+ defined_elements_t mDefinedElements;
+ attribute_data_t mAttributesWritten;
+ elements_map_t mElementsWritten;
};
#endif //LLRNGWRITER_H
diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp
index 615df6977b..ab7d0bc38f 100644
--- a/indra/llui/llscrollbar.cpp
+++ b/indra/llui/llscrollbar.cpp
@@ -1,666 +1,666 @@
-/**
- * @file llscrollbar.cpp
- * @brief Scrollbar UI widget
- *
- * $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 "llscrollbar.h"
-
-#include "llmath.h"
-#include "lltimer.h"
-#include "v3color.h"
-
-#include "llbutton.h"
-#include "llcriticaldamp.h"
-#include "llkeyboard.h"
-#include "llui.h"
-#include "llfocusmgr.h"
-#include "llwindow.h"
-#include "llcontrol.h"
-#include "llrender.h"
-#include "lluictrlfactory.h"
-
-static LLDefaultChildRegistry::Register<LLScrollbar> register_scrollbar("scroll_bar");
-
-LLScrollbar::Params::Params()
-: orientation ("orientation", HORIZONTAL),
- doc_size ("doc_size", 0),
- doc_pos ("doc_pos", 0),
- page_size ("page_size", 0),
- step_size ("step_size", 1),
- thumb_image_vertical("thumb_image_vertical"),
- thumb_image_horizontal("thumb_image_horizontal"),
- track_image_vertical("track_image_vertical"),
- track_image_horizontal("track_image_horizontal"),
- track_color("track_color"),
- thumb_color("thumb_color"),
- thickness("thickness"),
- up_button("up_button"),
- down_button("down_button"),
- left_button("left_button"),
- right_button("right_button"),
- bg_visible("bg_visible", false),
- bg_color("bg_color", LLColor4::black)
-{}
-
-LLScrollbar::LLScrollbar(const Params & p)
-: LLUICtrl(p),
- mChangeCallback( p.change_callback() ),
- mOrientation( p.orientation ),
- mDocSize( p.doc_size ),
- mDocPos( p.doc_pos ),
- mPageSize( p.page_size ),
- mStepSize( p.step_size ),
- mDocChanged(false),
- mDragStartX( 0 ),
- mDragStartY( 0 ),
- mHoverGlowStrength(0.15f),
- mCurGlowStrength(0.f),
- mTrackColor( p.track_color() ),
- mThumbColor ( p.thumb_color() ),
- mThumbImageV(p.thumb_image_vertical),
- mThumbImageH(p.thumb_image_horizontal),
- mTrackImageV(p.track_image_vertical),
- mTrackImageH(p.track_image_horizontal),
- mThickness(p.thickness.isProvided() ? p.thickness : LLUI::getInstance()->mSettingGroups["config"]->getS32("UIScrollbarSize")),
- mBGVisible(p.bg_visible),
- mBGColor(p.bg_color)
-{
- updateThumbRect();
-
- // Page up and page down buttons
- LLRect line_up_rect;
- LLRect line_down_rect;
-
- if( VERTICAL == mOrientation )
- {
- line_up_rect.setLeftTopAndSize( 0, getRect().getHeight(), mThickness, mThickness );
- line_down_rect.setOriginAndSize( 0, 0, mThickness, mThickness );
- }
- else // HORIZONTAL
- {
- line_up_rect.setOriginAndSize( 0, 0, mThickness, mThickness );
- line_down_rect.setOriginAndSize( getRect().getWidth() - mThickness, 0, mThickness, mThickness );
- }
-
- LLButton::Params up_btn(mOrientation == VERTICAL ? p.up_button : p.left_button);
- up_btn.name(std::string("Line Up"));
- up_btn.rect(line_up_rect);
- up_btn.click_callback.function(boost::bind(&LLScrollbar::onLineUpBtnPressed, this, _2));
- up_btn.mouse_held_callback.function(boost::bind(&LLScrollbar::onLineUpBtnPressed, this, _2));
- up_btn.tab_stop(false);
- up_btn.follows.flags = (mOrientation == VERTICAL ? (FOLLOWS_RIGHT | FOLLOWS_TOP) : (FOLLOWS_LEFT | FOLLOWS_BOTTOM));
-
- addChild(LLUICtrlFactory::create<LLButton>(up_btn));
-
- LLButton::Params down_btn(mOrientation == VERTICAL ? p.down_button : p.right_button);
- down_btn.name(std::string("Line Down"));
- down_btn.rect(line_down_rect);
- down_btn.follows.flags(FOLLOWS_RIGHT|FOLLOWS_BOTTOM);
- down_btn.click_callback.function(boost::bind(&LLScrollbar::onLineDownBtnPressed, this, _2));
- down_btn.mouse_held_callback.function(boost::bind(&LLScrollbar::onLineDownBtnPressed, this, _2));
- down_btn.tab_stop(false);
-
- addChild(LLUICtrlFactory::create<LLButton>(down_btn));
-}
-
-
-LLScrollbar::~LLScrollbar()
-{
- // Children buttons killed by parent class
-}
-
-void LLScrollbar::setDocParams( S32 size, S32 pos )
-{
- mDocSize = size;
- setDocPos(pos);
- mDocChanged = true;
-
- updateThumbRect();
-}
-
-// returns true if document position really changed
-bool LLScrollbar::setDocPos(S32 pos, bool update_thumb)
-{
- pos = llclamp(pos, 0, getDocPosMax());
- if (pos != mDocPos)
- {
- mDocPos = pos;
- mDocChanged = true;
-
- if( mChangeCallback )
- {
- mChangeCallback( mDocPos, this );
- }
-
- if( update_thumb )
- {
- updateThumbRect();
- }
- return true;
- }
- return false;
-}
-
-void LLScrollbar::setDocSize(S32 size)
-{
- if (size != mDocSize)
- {
- mDocSize = size;
- setDocPos(mDocPos);
- mDocChanged = true;
-
- updateThumbRect();
- }
-}
-
-void LLScrollbar::setPageSize( S32 page_size )
-{
- if (page_size != mPageSize)
- {
- mPageSize = page_size;
- setDocPos(mDocPos);
- mDocChanged = true;
-
- updateThumbRect();
- }
-}
-
-bool LLScrollbar::isAtBeginning() const
-{
- return mDocPos == 0;
-}
-
-bool LLScrollbar::isAtEnd() const
-{
- return mDocPos == getDocPosMax();
-}
-
-
-void LLScrollbar::updateThumbRect()
-{
-// llassert( 0 <= mDocSize );
-// llassert( 0 <= mDocPos && mDocPos <= getDocPosMax() );
-
- const S32 THUMB_MIN_LENGTH = 16;
-
- S32 window_length = (mOrientation == LLScrollbar::HORIZONTAL) ? getRect().getWidth() : getRect().getHeight();
- S32 thumb_bg_length = llmax(0, window_length - 2 * mThickness);
- S32 visible_lines = llmin( mDocSize, mPageSize );
- S32 thumb_length = mDocSize ? llmin(llmax( visible_lines * thumb_bg_length / mDocSize, THUMB_MIN_LENGTH), thumb_bg_length) : thumb_bg_length;
-
- S32 variable_lines = mDocSize - visible_lines;
-
- if( mOrientation == LLScrollbar::VERTICAL )
- {
- S32 thumb_start_max = thumb_bg_length + mThickness;
- S32 thumb_start_min = mThickness + THUMB_MIN_LENGTH;
- S32 thumb_start = variable_lines ? llmin( llmax(thumb_start_max - (mDocPos * (thumb_bg_length - thumb_length)) / variable_lines, thumb_start_min), thumb_start_max ) : thumb_start_max;
-
- mThumbRect.mLeft = 0;
- mThumbRect.mTop = thumb_start;
- mThumbRect.mRight = mThickness;
- mThumbRect.mBottom = thumb_start - thumb_length;
- }
- else
- {
- // Horizontal
- S32 thumb_start_max = thumb_bg_length + mThickness - thumb_length;
- S32 thumb_start_min = mThickness;
- S32 thumb_start = variable_lines ? llmin(llmax( thumb_start_min + (mDocPos * (thumb_bg_length - thumb_length)) / variable_lines, thumb_start_min), thumb_start_max ) : thumb_start_min;
-
- mThumbRect.mLeft = thumb_start;
- mThumbRect.mTop = mThickness;
- mThumbRect.mRight = thumb_start + thumb_length;
- mThumbRect.mBottom = 0;
- }
-}
-
-bool LLScrollbar::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- // Check children first
- bool handled_by_child = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
- if( !handled_by_child )
- {
- if( mThumbRect.pointInRect(x,y) )
- {
- // Start dragging the thumb
- // No handler needed for focus lost since this clas has no state that depends on it.
- gFocusMgr.setMouseCapture( this );
- mDragStartX = x;
- mDragStartY = y;
- mOrigRect.mTop = mThumbRect.mTop;
- mOrigRect.mBottom = mThumbRect.mBottom;
- mOrigRect.mLeft = mThumbRect.mLeft;
- mOrigRect.mRight = mThumbRect.mRight;
- mLastDelta = 0;
- }
- else
- {
- if(
- ( (LLScrollbar::VERTICAL == mOrientation) && (mThumbRect.mTop < y) ) ||
- ( (LLScrollbar::HORIZONTAL == mOrientation) && (x < mThumbRect.mLeft) )
- )
- {
- // Page up
- pageUp(0);
- }
- else
- if(
- ( (LLScrollbar::VERTICAL == mOrientation) && (y < mThumbRect.mBottom) ) ||
- ( (LLScrollbar::HORIZONTAL == mOrientation) && (mThumbRect.mRight < x) )
- )
- {
- // Page down
- pageDown(0);
- }
- }
- }
-
- return true;
-}
-
-
-bool LLScrollbar::handleHover(S32 x, S32 y, MASK mask)
-{
- // Note: we don't bother sending the event to the children (the arrow buttons)
- // because they'll capture the mouse whenever they need hover events.
-
- bool handled = false;
- if( hasMouseCapture() )
- {
- S32 height = getRect().getHeight();
- S32 width = getRect().getWidth();
-
- if( VERTICAL == mOrientation )
- {
-// S32 old_pos = mThumbRect.mTop;
-
- S32 delta_pixels = y - mDragStartY;
- if( mOrigRect.mBottom + delta_pixels < mThickness )
- {
- delta_pixels = mThickness - mOrigRect.mBottom - 1;
- }
- else
- if( mOrigRect.mTop + delta_pixels > height - mThickness )
- {
- delta_pixels = height - mThickness - mOrigRect.mTop + 1;
- }
-
- mThumbRect.mTop = mOrigRect.mTop + delta_pixels;
- mThumbRect.mBottom = mOrigRect.mBottom + delta_pixels;
-
- S32 thumb_length = mThumbRect.getHeight();
- S32 thumb_track_length = height - 2 * mThickness;
-
-
- if( delta_pixels != mLastDelta || mDocChanged)
- {
- // Note: delta_pixels increases as you go up. mDocPos increases down (line 0 is at the top of the page).
- S32 usable_track_length = thumb_track_length - thumb_length;
- if( 0 < usable_track_length )
- {
- S32 variable_lines = getDocPosMax();
- S32 pos = mThumbRect.mTop;
- F32 ratio = F32(pos - mThickness - thumb_length) / usable_track_length;
-
- S32 new_pos = llclamp( S32(variable_lines - ratio * variable_lines + 0.5f), 0, variable_lines );
- // Note: we do not call updateThumbRect() here. Instead we let the thumb and the document go slightly
- // out of sync (less than a line's worth) to make the thumb feel responsive.
- changeLine( new_pos - mDocPos, false );
- }
- }
-
- mLastDelta = delta_pixels;
-
- }
- else
- {
- // Horizontal
-// S32 old_pos = mThumbRect.mLeft;
-
- S32 delta_pixels = x - mDragStartX;
-
- if( mOrigRect.mLeft + delta_pixels < mThickness )
- {
- delta_pixels = mThickness - mOrigRect.mLeft - 1;
- }
- else
- if( mOrigRect.mRight + delta_pixels > width - mThickness )
- {
- delta_pixels = width - mThickness - mOrigRect.mRight + 1;
- }
-
- mThumbRect.mLeft = mOrigRect.mLeft + delta_pixels;
- mThumbRect.mRight = mOrigRect.mRight + delta_pixels;
-
- S32 thumb_length = mThumbRect.getWidth();
- S32 thumb_track_length = width - 2 * mThickness;
-
- if( delta_pixels != mLastDelta || mDocChanged)
- {
- // Note: delta_pixels increases as you go up. mDocPos increases down (line 0 is at the top of the page).
- S32 usable_track_length = thumb_track_length - thumb_length;
- if( 0 < usable_track_length )
- {
- S32 variable_lines = getDocPosMax();
- S32 pos = mThumbRect.mLeft;
- F32 ratio = F32(pos - mThickness) / usable_track_length;
-
- S32 new_pos = llclamp( S32(ratio * variable_lines + 0.5f), 0, variable_lines);
-
- // Note: we do not call updateThumbRect() here. Instead we let the thumb and the document go slightly
- // out of sync (less than a line's worth) to make the thumb feel responsive.
- changeLine( new_pos - mDocPos, false );
- }
- }
-
- mLastDelta = delta_pixels;
- }
-
- getWindow()->setCursor(UI_CURSOR_ARROW);
- LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL;
- handled = true;
- }
- else
- {
- handled = childrenHandleHover( x, y, mask ) != NULL;
- }
-
- // Opaque
- if( !handled )
- {
- getWindow()->setCursor(UI_CURSOR_ARROW);
- LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL;
- handled = true;
- }
-
- mDocChanged = false;
- return handled;
-} // end handleHover
-
-
-bool LLScrollbar::handleScrollWheel(S32 x, S32 y, S32 clicks)
-{
- bool handled = changeLine( clicks * mStepSize, true );
- return handled;
-}
-
-bool LLScrollbar::handleScrollHWheel(S32 x, S32 y, S32 clicks)
-{
- bool handled = false;
- if (LLScrollbar::HORIZONTAL == mOrientation)
- {
- handled = changeLine(clicks * mStepSize, true);
- }
- return handled;
-}
-
-bool LLScrollbar::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
- EDragAndDropType cargo_type, void *cargo_data, EAcceptance *accept, std::string &tooltip_msg)
-{
- // enable this to get drag and drop to control scrollbars
- //if (!drop)
- //{
- // //TODO: refactor this
- // S32 variable_lines = getDocPosMax();
- // S32 pos = (VERTICAL == mOrientation) ? y : x;
- // S32 thumb_length = (VERTICAL == mOrientation) ? mThumbRect.getHeight() : mThumbRect.getWidth();
- // S32 thumb_track_length = (VERTICAL == mOrientation) ? (getRect().getHeight() - 2 * SCROLLBAR_SIZE) : (getRect().getWidth() - 2 * SCROLLBAR_SIZE);
- // S32 usable_track_length = thumb_track_length - thumb_length;
- // F32 ratio = (VERTICAL == mOrientation) ? F32(pos - SCROLLBAR_SIZE - thumb_length) / usable_track_length
- // : F32(pos - SCROLLBAR_SIZE) / usable_track_length;
- // S32 new_pos = (VERTICAL == mOrientation) ? llclamp( S32(variable_lines - ratio * variable_lines + 0.5f), 0, variable_lines )
- // : llclamp( S32(ratio * variable_lines + 0.5f), 0, variable_lines );
- // changeLine( new_pos - mDocPos, true );
- //}
- //return true;
- return false;
-}
-
-bool LLScrollbar::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
- if( hasMouseCapture() )
- {
- gFocusMgr.setMouseCapture( NULL );
- handled = true;
- }
- else
- {
- // Opaque, so don't just check children
- handled = LLView::handleMouseUp( x, y, mask );
- }
-
- return handled;
-}
-
-bool LLScrollbar::handleDoubleClick(S32 x, S32 y, MASK mask)
-{
- // just treat a double click as a second click
- return handleMouseDown(x, y, mask);
-}
-
-
-void LLScrollbar::reshape(S32 width, S32 height, bool called_from_parent)
-{
- if (width == getRect().getWidth() && height == getRect().getHeight()) return;
- LLView::reshape( width, height, called_from_parent );
- LLButton* up_button = getChild<LLButton>("Line Up");
- LLButton* down_button = getChild<LLButton>("Line Down");
-
- if (mOrientation == VERTICAL)
- {
- up_button->reshape(up_button->getRect().getWidth(), llmin(getRect().getHeight() / 2, mThickness));
- down_button->reshape(down_button->getRect().getWidth(), llmin(getRect().getHeight() / 2, mThickness));
- up_button->setOrigin(0, getRect().getHeight() - up_button->getRect().getHeight());
- down_button->setOrigin(0, 0);
- }
- else
- {
- up_button->reshape(llmin(getRect().getWidth() / 2, mThickness), up_button->getRect().getHeight());
- down_button->reshape(llmin(getRect().getWidth() / 2, mThickness), down_button->getRect().getHeight());
- up_button->setOrigin(0, 0);
- down_button->setOrigin(getRect().getWidth() - down_button->getRect().getWidth(), 0);
- }
- updateThumbRect();
-}
-
-
-void LLScrollbar::draw()
-{
- if (!getRect().isValid()) return;
-
- if(mBGVisible)
- {
- gl_rect_2d(getLocalRect(), mBGColor.get(), true);
- }
-
- S32 local_mouse_x;
- S32 local_mouse_y;
- LLUI::getInstance()->getMousePositionLocal(this, &local_mouse_x, &local_mouse_y);
- bool other_captor = gFocusMgr.getMouseCapture() && gFocusMgr.getMouseCapture() != this;
- bool hovered = getEnabled() && !other_captor && (hasMouseCapture() || mThumbRect.pointInRect(local_mouse_x, local_mouse_y));
- if (hovered)
- {
- mCurGlowStrength = lerp(mCurGlowStrength, mHoverGlowStrength, LLSmoothInterpolation::getInterpolant(0.05f));
- }
- else
- {
- mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLSmoothInterpolation::getInterpolant(0.05f));
- }
-
- // Draw background and thumb.
- if ( ( mOrientation == VERTICAL&&(mThumbImageV.isNull() || mThumbImageH.isNull()) )
- || (mOrientation == HORIZONTAL&&(mTrackImageH.isNull() || mTrackImageV.isNull()) ))
- {
- gl_rect_2d(mOrientation == HORIZONTAL ? mThickness : 0,
- mOrientation == VERTICAL ? getRect().getHeight() - 2 * mThickness : getRect().getHeight(),
- mOrientation == HORIZONTAL ? getRect().getWidth() - 2 * mThickness : getRect().getWidth(),
- mOrientation == VERTICAL ? mThickness : 0, mTrackColor.get(), true);
-
- gl_rect_2d(mThumbRect, mThumbColor.get(), true);
-
- }
- else
- {
- // Thumb
- LLRect outline_rect = mThumbRect;
- outline_rect.stretch(2);
- // Background
-
- if(mOrientation == HORIZONTAL)
- {
- mTrackImageH->drawSolid(mThickness //S32 x
- , 0 //S32 y
- , getRect().getWidth() - 2 * mThickness //S32 width
- , getRect().getHeight() //S32 height
- , mTrackColor.get()); //const LLColor4& color
-
- if (gFocusMgr.getKeyboardFocus() == this)
- {
- mTrackImageH->draw(outline_rect, gFocusMgr.getFocusColor());
- }
-
- mThumbImageH->draw(mThumbRect, mThumbColor.get());
- if (mCurGlowStrength > 0.01f)
- {
- gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA);
- mThumbImageH->drawSolid(mThumbRect, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength));
- gGL.setSceneBlendType(LLRender::BT_ALPHA);
- }
-
- }
- else if(mOrientation == VERTICAL)
- {
- mTrackImageV->drawSolid( 0 //S32 x
- , mThickness //S32 y
- , getRect().getWidth() //S32 width
- , getRect().getHeight() - 2 * mThickness //S32 height
- , mTrackColor.get()); //const LLColor4& color
- if (gFocusMgr.getKeyboardFocus() == this)
- {
- mTrackImageV->draw(outline_rect, gFocusMgr.getFocusColor());
- }
-
- mThumbImageV->draw(mThumbRect, mThumbColor.get());
- if (mCurGlowStrength > 0.01f)
- {
- gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA);
- mThumbImageV->drawSolid(mThumbRect, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength));
- gGL.setSceneBlendType(LLRender::BT_ALPHA);
- }
- }
- }
-
- // Draw children
- LLView::draw();
-} // end draw
-
-
-bool LLScrollbar::changeLine( S32 delta, bool update_thumb )
-{
- return setDocPos(mDocPos + delta, update_thumb);
-}
-
-void LLScrollbar::setValue(const LLSD& value)
-{
- setDocPos((S32) value.asInteger());
-}
-
-
-bool LLScrollbar::handleKeyHere(KEY key, MASK mask)
-{
- if (getDocPosMax() == 0 && !getVisible())
- {
- return false;
- }
-
- bool handled = false;
-
- switch( key )
- {
- case KEY_HOME:
- setDocPos( 0 );
- handled = true;
- break;
-
- case KEY_END:
- setDocPos( getDocPosMax() );
- handled = true;
- break;
-
- case KEY_DOWN:
- setDocPos( getDocPos() + mStepSize );
- handled = true;
- break;
-
- case KEY_UP:
- setDocPos( getDocPos() - mStepSize );
- handled = true;
- break;
-
- case KEY_PAGE_DOWN:
- pageDown(1);
- break;
-
- case KEY_PAGE_UP:
- pageUp(1);
- break;
- }
-
- return handled;
-}
-
-void LLScrollbar::pageUp(S32 overlap)
-{
- if (mDocSize > mPageSize)
- {
- changeLine( -(mPageSize - overlap), true );
- }
-}
-
-void LLScrollbar::pageDown(S32 overlap)
-{
- if (mDocSize > mPageSize)
- {
- changeLine( mPageSize - overlap, true );
- }
-}
-
-void LLScrollbar::onLineUpBtnPressed( const LLSD& data )
-{
- changeLine( -mStepSize, true );
-}
-
-void LLScrollbar::onLineDownBtnPressed( const LLSD& data )
-{
- changeLine( mStepSize, true );
-}
-
-void LLScrollbar::setThickness(S32 thickness)
-{
- mThickness = thickness < 0 ? LLUI::getInstance()->mSettingGroups["config"]->getS32("UIScrollbarSize") : thickness;
-}
+/**
+ * @file llscrollbar.cpp
+ * @brief Scrollbar UI widget
+ *
+ * $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 "llscrollbar.h"
+
+#include "llmath.h"
+#include "lltimer.h"
+#include "v3color.h"
+
+#include "llbutton.h"
+#include "llcriticaldamp.h"
+#include "llkeyboard.h"
+#include "llui.h"
+#include "llfocusmgr.h"
+#include "llwindow.h"
+#include "llcontrol.h"
+#include "llrender.h"
+#include "lluictrlfactory.h"
+
+static LLDefaultChildRegistry::Register<LLScrollbar> register_scrollbar("scroll_bar");
+
+LLScrollbar::Params::Params()
+: orientation ("orientation", HORIZONTAL),
+ doc_size ("doc_size", 0),
+ doc_pos ("doc_pos", 0),
+ page_size ("page_size", 0),
+ step_size ("step_size", 1),
+ thumb_image_vertical("thumb_image_vertical"),
+ thumb_image_horizontal("thumb_image_horizontal"),
+ track_image_vertical("track_image_vertical"),
+ track_image_horizontal("track_image_horizontal"),
+ track_color("track_color"),
+ thumb_color("thumb_color"),
+ thickness("thickness"),
+ up_button("up_button"),
+ down_button("down_button"),
+ left_button("left_button"),
+ right_button("right_button"),
+ bg_visible("bg_visible", false),
+ bg_color("bg_color", LLColor4::black)
+{}
+
+LLScrollbar::LLScrollbar(const Params & p)
+: LLUICtrl(p),
+ mChangeCallback( p.change_callback() ),
+ mOrientation( p.orientation ),
+ mDocSize( p.doc_size ),
+ mDocPos( p.doc_pos ),
+ mPageSize( p.page_size ),
+ mStepSize( p.step_size ),
+ mDocChanged(false),
+ mDragStartX( 0 ),
+ mDragStartY( 0 ),
+ mHoverGlowStrength(0.15f),
+ mCurGlowStrength(0.f),
+ mTrackColor( p.track_color() ),
+ mThumbColor ( p.thumb_color() ),
+ mThumbImageV(p.thumb_image_vertical),
+ mThumbImageH(p.thumb_image_horizontal),
+ mTrackImageV(p.track_image_vertical),
+ mTrackImageH(p.track_image_horizontal),
+ mThickness(p.thickness.isProvided() ? p.thickness : LLUI::getInstance()->mSettingGroups["config"]->getS32("UIScrollbarSize")),
+ mBGVisible(p.bg_visible),
+ mBGColor(p.bg_color)
+{
+ updateThumbRect();
+
+ // Page up and page down buttons
+ LLRect line_up_rect;
+ LLRect line_down_rect;
+
+ if( VERTICAL == mOrientation )
+ {
+ line_up_rect.setLeftTopAndSize( 0, getRect().getHeight(), mThickness, mThickness );
+ line_down_rect.setOriginAndSize( 0, 0, mThickness, mThickness );
+ }
+ else // HORIZONTAL
+ {
+ line_up_rect.setOriginAndSize( 0, 0, mThickness, mThickness );
+ line_down_rect.setOriginAndSize( getRect().getWidth() - mThickness, 0, mThickness, mThickness );
+ }
+
+ LLButton::Params up_btn(mOrientation == VERTICAL ? p.up_button : p.left_button);
+ up_btn.name(std::string("Line Up"));
+ up_btn.rect(line_up_rect);
+ up_btn.click_callback.function(boost::bind(&LLScrollbar::onLineUpBtnPressed, this, _2));
+ up_btn.mouse_held_callback.function(boost::bind(&LLScrollbar::onLineUpBtnPressed, this, _2));
+ up_btn.tab_stop(false);
+ up_btn.follows.flags = (mOrientation == VERTICAL ? (FOLLOWS_RIGHT | FOLLOWS_TOP) : (FOLLOWS_LEFT | FOLLOWS_BOTTOM));
+
+ addChild(LLUICtrlFactory::create<LLButton>(up_btn));
+
+ LLButton::Params down_btn(mOrientation == VERTICAL ? p.down_button : p.right_button);
+ down_btn.name(std::string("Line Down"));
+ down_btn.rect(line_down_rect);
+ down_btn.follows.flags(FOLLOWS_RIGHT|FOLLOWS_BOTTOM);
+ down_btn.click_callback.function(boost::bind(&LLScrollbar::onLineDownBtnPressed, this, _2));
+ down_btn.mouse_held_callback.function(boost::bind(&LLScrollbar::onLineDownBtnPressed, this, _2));
+ down_btn.tab_stop(false);
+
+ addChild(LLUICtrlFactory::create<LLButton>(down_btn));
+}
+
+
+LLScrollbar::~LLScrollbar()
+{
+ // Children buttons killed by parent class
+}
+
+void LLScrollbar::setDocParams( S32 size, S32 pos )
+{
+ mDocSize = size;
+ setDocPos(pos);
+ mDocChanged = true;
+
+ updateThumbRect();
+}
+
+// returns true if document position really changed
+bool LLScrollbar::setDocPos(S32 pos, bool update_thumb)
+{
+ pos = llclamp(pos, 0, getDocPosMax());
+ if (pos != mDocPos)
+ {
+ mDocPos = pos;
+ mDocChanged = true;
+
+ if( mChangeCallback )
+ {
+ mChangeCallback( mDocPos, this );
+ }
+
+ if( update_thumb )
+ {
+ updateThumbRect();
+ }
+ return true;
+ }
+ return false;
+}
+
+void LLScrollbar::setDocSize(S32 size)
+{
+ if (size != mDocSize)
+ {
+ mDocSize = size;
+ setDocPos(mDocPos);
+ mDocChanged = true;
+
+ updateThumbRect();
+ }
+}
+
+void LLScrollbar::setPageSize( S32 page_size )
+{
+ if (page_size != mPageSize)
+ {
+ mPageSize = page_size;
+ setDocPos(mDocPos);
+ mDocChanged = true;
+
+ updateThumbRect();
+ }
+}
+
+bool LLScrollbar::isAtBeginning() const
+{
+ return mDocPos == 0;
+}
+
+bool LLScrollbar::isAtEnd() const
+{
+ return mDocPos == getDocPosMax();
+}
+
+
+void LLScrollbar::updateThumbRect()
+{
+// llassert( 0 <= mDocSize );
+// llassert( 0 <= mDocPos && mDocPos <= getDocPosMax() );
+
+ const S32 THUMB_MIN_LENGTH = 16;
+
+ S32 window_length = (mOrientation == LLScrollbar::HORIZONTAL) ? getRect().getWidth() : getRect().getHeight();
+ S32 thumb_bg_length = llmax(0, window_length - 2 * mThickness);
+ S32 visible_lines = llmin( mDocSize, mPageSize );
+ S32 thumb_length = mDocSize ? llmin(llmax( visible_lines * thumb_bg_length / mDocSize, THUMB_MIN_LENGTH), thumb_bg_length) : thumb_bg_length;
+
+ S32 variable_lines = mDocSize - visible_lines;
+
+ if( mOrientation == LLScrollbar::VERTICAL )
+ {
+ S32 thumb_start_max = thumb_bg_length + mThickness;
+ S32 thumb_start_min = mThickness + THUMB_MIN_LENGTH;
+ S32 thumb_start = variable_lines ? llmin( llmax(thumb_start_max - (mDocPos * (thumb_bg_length - thumb_length)) / variable_lines, thumb_start_min), thumb_start_max ) : thumb_start_max;
+
+ mThumbRect.mLeft = 0;
+ mThumbRect.mTop = thumb_start;
+ mThumbRect.mRight = mThickness;
+ mThumbRect.mBottom = thumb_start - thumb_length;
+ }
+ else
+ {
+ // Horizontal
+ S32 thumb_start_max = thumb_bg_length + mThickness - thumb_length;
+ S32 thumb_start_min = mThickness;
+ S32 thumb_start = variable_lines ? llmin(llmax( thumb_start_min + (mDocPos * (thumb_bg_length - thumb_length)) / variable_lines, thumb_start_min), thumb_start_max ) : thumb_start_min;
+
+ mThumbRect.mLeft = thumb_start;
+ mThumbRect.mTop = mThickness;
+ mThumbRect.mRight = thumb_start + thumb_length;
+ mThumbRect.mBottom = 0;
+ }
+}
+
+bool LLScrollbar::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // Check children first
+ bool handled_by_child = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
+ if( !handled_by_child )
+ {
+ if( mThumbRect.pointInRect(x,y) )
+ {
+ // Start dragging the thumb
+ // No handler needed for focus lost since this clas has no state that depends on it.
+ gFocusMgr.setMouseCapture( this );
+ mDragStartX = x;
+ mDragStartY = y;
+ mOrigRect.mTop = mThumbRect.mTop;
+ mOrigRect.mBottom = mThumbRect.mBottom;
+ mOrigRect.mLeft = mThumbRect.mLeft;
+ mOrigRect.mRight = mThumbRect.mRight;
+ mLastDelta = 0;
+ }
+ else
+ {
+ if(
+ ( (LLScrollbar::VERTICAL == mOrientation) && (mThumbRect.mTop < y) ) ||
+ ( (LLScrollbar::HORIZONTAL == mOrientation) && (x < mThumbRect.mLeft) )
+ )
+ {
+ // Page up
+ pageUp(0);
+ }
+ else
+ if(
+ ( (LLScrollbar::VERTICAL == mOrientation) && (y < mThumbRect.mBottom) ) ||
+ ( (LLScrollbar::HORIZONTAL == mOrientation) && (mThumbRect.mRight < x) )
+ )
+ {
+ // Page down
+ pageDown(0);
+ }
+ }
+ }
+
+ return true;
+}
+
+
+bool LLScrollbar::handleHover(S32 x, S32 y, MASK mask)
+{
+ // Note: we don't bother sending the event to the children (the arrow buttons)
+ // because they'll capture the mouse whenever they need hover events.
+
+ bool handled = false;
+ if( hasMouseCapture() )
+ {
+ S32 height = getRect().getHeight();
+ S32 width = getRect().getWidth();
+
+ if( VERTICAL == mOrientation )
+ {
+// S32 old_pos = mThumbRect.mTop;
+
+ S32 delta_pixels = y - mDragStartY;
+ if( mOrigRect.mBottom + delta_pixels < mThickness )
+ {
+ delta_pixels = mThickness - mOrigRect.mBottom - 1;
+ }
+ else
+ if( mOrigRect.mTop + delta_pixels > height - mThickness )
+ {
+ delta_pixels = height - mThickness - mOrigRect.mTop + 1;
+ }
+
+ mThumbRect.mTop = mOrigRect.mTop + delta_pixels;
+ mThumbRect.mBottom = mOrigRect.mBottom + delta_pixels;
+
+ S32 thumb_length = mThumbRect.getHeight();
+ S32 thumb_track_length = height - 2 * mThickness;
+
+
+ if( delta_pixels != mLastDelta || mDocChanged)
+ {
+ // Note: delta_pixels increases as you go up. mDocPos increases down (line 0 is at the top of the page).
+ S32 usable_track_length = thumb_track_length - thumb_length;
+ if( 0 < usable_track_length )
+ {
+ S32 variable_lines = getDocPosMax();
+ S32 pos = mThumbRect.mTop;
+ F32 ratio = F32(pos - mThickness - thumb_length) / usable_track_length;
+
+ S32 new_pos = llclamp( S32(variable_lines - ratio * variable_lines + 0.5f), 0, variable_lines );
+ // Note: we do not call updateThumbRect() here. Instead we let the thumb and the document go slightly
+ // out of sync (less than a line's worth) to make the thumb feel responsive.
+ changeLine( new_pos - mDocPos, false );
+ }
+ }
+
+ mLastDelta = delta_pixels;
+
+ }
+ else
+ {
+ // Horizontal
+// S32 old_pos = mThumbRect.mLeft;
+
+ S32 delta_pixels = x - mDragStartX;
+
+ if( mOrigRect.mLeft + delta_pixels < mThickness )
+ {
+ delta_pixels = mThickness - mOrigRect.mLeft - 1;
+ }
+ else
+ if( mOrigRect.mRight + delta_pixels > width - mThickness )
+ {
+ delta_pixels = width - mThickness - mOrigRect.mRight + 1;
+ }
+
+ mThumbRect.mLeft = mOrigRect.mLeft + delta_pixels;
+ mThumbRect.mRight = mOrigRect.mRight + delta_pixels;
+
+ S32 thumb_length = mThumbRect.getWidth();
+ S32 thumb_track_length = width - 2 * mThickness;
+
+ if( delta_pixels != mLastDelta || mDocChanged)
+ {
+ // Note: delta_pixels increases as you go up. mDocPos increases down (line 0 is at the top of the page).
+ S32 usable_track_length = thumb_track_length - thumb_length;
+ if( 0 < usable_track_length )
+ {
+ S32 variable_lines = getDocPosMax();
+ S32 pos = mThumbRect.mLeft;
+ F32 ratio = F32(pos - mThickness) / usable_track_length;
+
+ S32 new_pos = llclamp( S32(ratio * variable_lines + 0.5f), 0, variable_lines);
+
+ // Note: we do not call updateThumbRect() here. Instead we let the thumb and the document go slightly
+ // out of sync (less than a line's worth) to make the thumb feel responsive.
+ changeLine( new_pos - mDocPos, false );
+ }
+ }
+
+ mLastDelta = delta_pixels;
+ }
+
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL;
+ handled = true;
+ }
+ else
+ {
+ handled = childrenHandleHover( x, y, mask ) != NULL;
+ }
+
+ // Opaque
+ if( !handled )
+ {
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL;
+ handled = true;
+ }
+
+ mDocChanged = false;
+ return handled;
+} // end handleHover
+
+
+bool LLScrollbar::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ bool handled = changeLine( clicks * mStepSize, true );
+ return handled;
+}
+
+bool LLScrollbar::handleScrollHWheel(S32 x, S32 y, S32 clicks)
+{
+ bool handled = false;
+ if (LLScrollbar::HORIZONTAL == mOrientation)
+ {
+ handled = changeLine(clicks * mStepSize, true);
+ }
+ return handled;
+}
+
+bool LLScrollbar::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
+ EDragAndDropType cargo_type, void *cargo_data, EAcceptance *accept, std::string &tooltip_msg)
+{
+ // enable this to get drag and drop to control scrollbars
+ //if (!drop)
+ //{
+ // //TODO: refactor this
+ // S32 variable_lines = getDocPosMax();
+ // S32 pos = (VERTICAL == mOrientation) ? y : x;
+ // S32 thumb_length = (VERTICAL == mOrientation) ? mThumbRect.getHeight() : mThumbRect.getWidth();
+ // S32 thumb_track_length = (VERTICAL == mOrientation) ? (getRect().getHeight() - 2 * SCROLLBAR_SIZE) : (getRect().getWidth() - 2 * SCROLLBAR_SIZE);
+ // S32 usable_track_length = thumb_track_length - thumb_length;
+ // F32 ratio = (VERTICAL == mOrientation) ? F32(pos - SCROLLBAR_SIZE - thumb_length) / usable_track_length
+ // : F32(pos - SCROLLBAR_SIZE) / usable_track_length;
+ // S32 new_pos = (VERTICAL == mOrientation) ? llclamp( S32(variable_lines - ratio * variable_lines + 0.5f), 0, variable_lines )
+ // : llclamp( S32(ratio * variable_lines + 0.5f), 0, variable_lines );
+ // changeLine( new_pos - mDocPos, true );
+ //}
+ //return true;
+ return false;
+}
+
+bool LLScrollbar::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ bool handled = false;
+ if( hasMouseCapture() )
+ {
+ gFocusMgr.setMouseCapture( NULL );
+ handled = true;
+ }
+ else
+ {
+ // Opaque, so don't just check children
+ handled = LLView::handleMouseUp( x, y, mask );
+ }
+
+ return handled;
+}
+
+bool LLScrollbar::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ // just treat a double click as a second click
+ return handleMouseDown(x, y, mask);
+}
+
+
+void LLScrollbar::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ if (width == getRect().getWidth() && height == getRect().getHeight()) return;
+ LLView::reshape( width, height, called_from_parent );
+ LLButton* up_button = getChild<LLButton>("Line Up");
+ LLButton* down_button = getChild<LLButton>("Line Down");
+
+ if (mOrientation == VERTICAL)
+ {
+ up_button->reshape(up_button->getRect().getWidth(), llmin(getRect().getHeight() / 2, mThickness));
+ down_button->reshape(down_button->getRect().getWidth(), llmin(getRect().getHeight() / 2, mThickness));
+ up_button->setOrigin(0, getRect().getHeight() - up_button->getRect().getHeight());
+ down_button->setOrigin(0, 0);
+ }
+ else
+ {
+ up_button->reshape(llmin(getRect().getWidth() / 2, mThickness), up_button->getRect().getHeight());
+ down_button->reshape(llmin(getRect().getWidth() / 2, mThickness), down_button->getRect().getHeight());
+ up_button->setOrigin(0, 0);
+ down_button->setOrigin(getRect().getWidth() - down_button->getRect().getWidth(), 0);
+ }
+ updateThumbRect();
+}
+
+
+void LLScrollbar::draw()
+{
+ if (!getRect().isValid()) return;
+
+ if(mBGVisible)
+ {
+ gl_rect_2d(getLocalRect(), mBGColor.get(), true);
+ }
+
+ S32 local_mouse_x;
+ S32 local_mouse_y;
+ LLUI::getInstance()->getMousePositionLocal(this, &local_mouse_x, &local_mouse_y);
+ bool other_captor = gFocusMgr.getMouseCapture() && gFocusMgr.getMouseCapture() != this;
+ bool hovered = getEnabled() && !other_captor && (hasMouseCapture() || mThumbRect.pointInRect(local_mouse_x, local_mouse_y));
+ if (hovered)
+ {
+ mCurGlowStrength = lerp(mCurGlowStrength, mHoverGlowStrength, LLSmoothInterpolation::getInterpolant(0.05f));
+ }
+ else
+ {
+ mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLSmoothInterpolation::getInterpolant(0.05f));
+ }
+
+ // Draw background and thumb.
+ if ( ( mOrientation == VERTICAL&&(mThumbImageV.isNull() || mThumbImageH.isNull()) )
+ || (mOrientation == HORIZONTAL&&(mTrackImageH.isNull() || mTrackImageV.isNull()) ))
+ {
+ gl_rect_2d(mOrientation == HORIZONTAL ? mThickness : 0,
+ mOrientation == VERTICAL ? getRect().getHeight() - 2 * mThickness : getRect().getHeight(),
+ mOrientation == HORIZONTAL ? getRect().getWidth() - 2 * mThickness : getRect().getWidth(),
+ mOrientation == VERTICAL ? mThickness : 0, mTrackColor.get(), true);
+
+ gl_rect_2d(mThumbRect, mThumbColor.get(), true);
+
+ }
+ else
+ {
+ // Thumb
+ LLRect outline_rect = mThumbRect;
+ outline_rect.stretch(2);
+ // Background
+
+ if(mOrientation == HORIZONTAL)
+ {
+ mTrackImageH->drawSolid(mThickness //S32 x
+ , 0 //S32 y
+ , getRect().getWidth() - 2 * mThickness //S32 width
+ , getRect().getHeight() //S32 height
+ , mTrackColor.get()); //const LLColor4& color
+
+ if (gFocusMgr.getKeyboardFocus() == this)
+ {
+ mTrackImageH->draw(outline_rect, gFocusMgr.getFocusColor());
+ }
+
+ mThumbImageH->draw(mThumbRect, mThumbColor.get());
+ if (mCurGlowStrength > 0.01f)
+ {
+ gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA);
+ mThumbImageH->drawSolid(mThumbRect, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength));
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+ }
+
+ }
+ else if(mOrientation == VERTICAL)
+ {
+ mTrackImageV->drawSolid( 0 //S32 x
+ , mThickness //S32 y
+ , getRect().getWidth() //S32 width
+ , getRect().getHeight() - 2 * mThickness //S32 height
+ , mTrackColor.get()); //const LLColor4& color
+ if (gFocusMgr.getKeyboardFocus() == this)
+ {
+ mTrackImageV->draw(outline_rect, gFocusMgr.getFocusColor());
+ }
+
+ mThumbImageV->draw(mThumbRect, mThumbColor.get());
+ if (mCurGlowStrength > 0.01f)
+ {
+ gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA);
+ mThumbImageV->drawSolid(mThumbRect, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength));
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
+ }
+ }
+ }
+
+ // Draw children
+ LLView::draw();
+} // end draw
+
+
+bool LLScrollbar::changeLine( S32 delta, bool update_thumb )
+{
+ return setDocPos(mDocPos + delta, update_thumb);
+}
+
+void LLScrollbar::setValue(const LLSD& value)
+{
+ setDocPos((S32) value.asInteger());
+}
+
+
+bool LLScrollbar::handleKeyHere(KEY key, MASK mask)
+{
+ if (getDocPosMax() == 0 && !getVisible())
+ {
+ return false;
+ }
+
+ bool handled = false;
+
+ switch( key )
+ {
+ case KEY_HOME:
+ setDocPos( 0 );
+ handled = true;
+ break;
+
+ case KEY_END:
+ setDocPos( getDocPosMax() );
+ handled = true;
+ break;
+
+ case KEY_DOWN:
+ setDocPos( getDocPos() + mStepSize );
+ handled = true;
+ break;
+
+ case KEY_UP:
+ setDocPos( getDocPos() - mStepSize );
+ handled = true;
+ break;
+
+ case KEY_PAGE_DOWN:
+ pageDown(1);
+ break;
+
+ case KEY_PAGE_UP:
+ pageUp(1);
+ break;
+ }
+
+ return handled;
+}
+
+void LLScrollbar::pageUp(S32 overlap)
+{
+ if (mDocSize > mPageSize)
+ {
+ changeLine( -(mPageSize - overlap), true );
+ }
+}
+
+void LLScrollbar::pageDown(S32 overlap)
+{
+ if (mDocSize > mPageSize)
+ {
+ changeLine( mPageSize - overlap, true );
+ }
+}
+
+void LLScrollbar::onLineUpBtnPressed( const LLSD& data )
+{
+ changeLine( -mStepSize, true );
+}
+
+void LLScrollbar::onLineDownBtnPressed( const LLSD& data )
+{
+ changeLine( mStepSize, true );
+}
+
+void LLScrollbar::setThickness(S32 thickness)
+{
+ mThickness = thickness < 0 ? LLUI::getInstance()->mSettingGroups["config"]->getS32("UIScrollbarSize") : thickness;
+}
diff --git a/indra/llui/llscrollbar.h b/indra/llui/llscrollbar.h
index b381c57283..b93c988b37 100644
--- a/indra/llui/llscrollbar.h
+++ b/indra/llui/llscrollbar.h
@@ -1,167 +1,167 @@
-/**
- * @file llscrollbar.h
- * @brief Scrollbar UI widget
- *
- * $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_SCROLLBAR_H
-#define LL_SCROLLBAR_H
-
-#include "stdtypes.h"
-#include "lluictrl.h"
-#include "v4color.h"
-#include "llbutton.h"
-
-//
-// Classes
-//
-class LLScrollbar
-: public LLUICtrl
-{
-public:
-
- typedef boost::function<void (S32, LLScrollbar*)> callback_t;
- struct Params
- : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Mandatory<EOrientation> orientation;
- Mandatory<S32> doc_size;
- Mandatory<S32> doc_pos;
- Mandatory<S32> page_size;
-
- Optional<callback_t> change_callback;
- Optional<S32> step_size;
- Optional<S32> thickness;
-
- Optional<LLUIImage*> thumb_image_vertical,
- thumb_image_horizontal,
- track_image_horizontal,
- track_image_vertical;
-
- Optional<bool> bg_visible;
-
- Optional<LLUIColor> track_color,
- thumb_color,
- bg_color;
-
- Optional<LLButton::Params> up_button;
- Optional<LLButton::Params> down_button;
- Optional<LLButton::Params> left_button;
- Optional<LLButton::Params> right_button;
-
- Params();
- };
-
-protected:
- LLScrollbar (const Params & p);
- friend class LLUICtrlFactory;
-
-public:
- virtual ~LLScrollbar();
-
- virtual void setValue(const LLSD& value);
-
- // Overrides from LLView
- virtual bool handleKeyHere(KEY key, MASK mask);
- virtual bool handleMouseDown(S32 x, S32 y, MASK mask);
- virtual bool handleMouseUp(S32 x, S32 y, MASK mask);
- virtual bool handleDoubleClick(S32 x, S32 y, MASK mask);
- virtual bool handleHover(S32 x, S32 y, MASK mask);
- virtual bool handleScrollWheel(S32 x, S32 y, S32 clicks);
- virtual bool handleScrollHWheel(S32 x, S32 y, S32 clicks);
- 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 draw();
-
- // How long the "document" is.
- void setDocSize( S32 size );
- S32 getDocSize() const { return mDocSize; }
-
- // How many "lines" the "document" has scrolled.
- // 0 <= DocPos <= DocSize - DocVisibile
- bool setDocPos( S32 pos, bool update_thumb = true );
- S32 getDocPos() const { return mDocPos; }
-
- bool isAtBeginning() const;
- bool isAtEnd() const;
-
- // Setting both at once.
- void setDocParams( S32 size, S32 pos );
-
- // How many "lines" of the "document" is can appear on a page.
- void setPageSize( S32 page_size );
- S32 getPageSize() const { return mPageSize; }
-
- // The farthest the document can be scrolled (top of the last page).
- S32 getDocPosMax() const { return llmax( 0, mDocSize - mPageSize); }
-
- void pageUp(S32 overlap);
- void pageDown(S32 overlap);
-
- 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 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.
- S32 mStepSize;
- bool mDocChanged;
-
- LLRect mThumbRect;
- S32 mDragStartX;
- S32 mDragStartY;
- F32 mHoverGlowStrength;
- F32 mCurGlowStrength;
-
- LLRect mOrigRect;
- S32 mLastDelta;
-
- LLUIColor mTrackColor;
- LLUIColor mThumbColor;
- LLUIColor mBGColor;
-
- bool mBGVisible;
-
- LLUIImagePtr mThumbImageV;
- LLUIImagePtr mThumbImageH;
- LLUIImagePtr mTrackImageV;
- LLUIImagePtr mTrackImageH;
-
- S32 mThickness;
-};
-
-
-#endif // LL_SCROLLBAR_H
+/**
+ * @file llscrollbar.h
+ * @brief Scrollbar UI widget
+ *
+ * $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_SCROLLBAR_H
+#define LL_SCROLLBAR_H
+
+#include "stdtypes.h"
+#include "lluictrl.h"
+#include "v4color.h"
+#include "llbutton.h"
+
+//
+// Classes
+//
+class LLScrollbar
+: public LLUICtrl
+{
+public:
+
+ typedef boost::function<void (S32, LLScrollbar*)> callback_t;
+ struct Params
+ : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Mandatory<EOrientation> orientation;
+ Mandatory<S32> doc_size;
+ Mandatory<S32> doc_pos;
+ Mandatory<S32> page_size;
+
+ Optional<callback_t> change_callback;
+ Optional<S32> step_size;
+ Optional<S32> thickness;
+
+ Optional<LLUIImage*> thumb_image_vertical,
+ thumb_image_horizontal,
+ track_image_horizontal,
+ track_image_vertical;
+
+ Optional<bool> bg_visible;
+
+ Optional<LLUIColor> track_color,
+ thumb_color,
+ bg_color;
+
+ Optional<LLButton::Params> up_button;
+ Optional<LLButton::Params> down_button;
+ Optional<LLButton::Params> left_button;
+ Optional<LLButton::Params> right_button;
+
+ Params();
+ };
+
+protected:
+ LLScrollbar (const Params & p);
+ friend class LLUICtrlFactory;
+
+public:
+ virtual ~LLScrollbar();
+
+ virtual void setValue(const LLSD& value);
+
+ // Overrides from LLView
+ virtual bool handleKeyHere(KEY key, MASK mask);
+ virtual bool handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual bool handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual bool handleDoubleClick(S32 x, S32 y, MASK mask);
+ virtual bool handleHover(S32 x, S32 y, MASK mask);
+ virtual bool handleScrollWheel(S32 x, S32 y, S32 clicks);
+ virtual bool handleScrollHWheel(S32 x, S32 y, S32 clicks);
+ 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 draw();
+
+ // How long the "document" is.
+ void setDocSize( S32 size );
+ S32 getDocSize() const { return mDocSize; }
+
+ // How many "lines" the "document" has scrolled.
+ // 0 <= DocPos <= DocSize - DocVisibile
+ bool setDocPos( S32 pos, bool update_thumb = true );
+ S32 getDocPos() const { return mDocPos; }
+
+ bool isAtBeginning() const;
+ bool isAtEnd() const;
+
+ // Setting both at once.
+ void setDocParams( S32 size, S32 pos );
+
+ // How many "lines" of the "document" is can appear on a page.
+ void setPageSize( S32 page_size );
+ S32 getPageSize() const { return mPageSize; }
+
+ // The farthest the document can be scrolled (top of the last page).
+ S32 getDocPosMax() const { return llmax( 0, mDocSize - mPageSize); }
+
+ void pageUp(S32 overlap);
+ void pageDown(S32 overlap);
+
+ 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 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.
+ S32 mStepSize;
+ bool mDocChanged;
+
+ LLRect mThumbRect;
+ S32 mDragStartX;
+ S32 mDragStartY;
+ F32 mHoverGlowStrength;
+ F32 mCurGlowStrength;
+
+ LLRect mOrigRect;
+ S32 mLastDelta;
+
+ LLUIColor mTrackColor;
+ LLUIColor mThumbColor;
+ LLUIColor mBGColor;
+
+ bool mBGVisible;
+
+ LLUIImagePtr mThumbImageV;
+ LLUIImagePtr mThumbImageH;
+ LLUIImagePtr mTrackImageV;
+ LLUIImagePtr mTrackImageH;
+
+ S32 mThickness;
+};
+
+
+#endif // LL_SCROLLBAR_H
diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp
index 5aba44c2b5..1e99d408cc 100644
--- a/indra/llui/llscrollcontainer.cpp
+++ b/indra/llui/llscrollcontainer.cpp
@@ -1,802 +1,802 @@
-/**
- * @file llscrollcontainer.cpp
- * @brief LLScrollContainer base class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-
-#include "linden_common.h"
-
-#include "llscrollcontainer.h"
-
-#include "llrender.h"
-#include "llcontainerview.h"
-#include "lllocalcliprect.h"
-// #include "llfolderview.h"
-#include "llscrollingpanellist.h"
-#include "llscrollbar.h"
-#include "llui.h"
-#include "llkeyboard.h"
-#include "llviewborder.h"
-#include "llfocusmgr.h"
-#include "llframetimer.h"
-#include "lluictrlfactory.h"
-#include "llpanel.h"
-#include "llfontgl.h"
-
-///----------------------------------------------------------------------------
-/// Local function declarations, constants, enums, and typedefs
-///----------------------------------------------------------------------------
-
-static const S32 VERTICAL_MULTIPLE = 16;
-static const F32 AUTO_SCROLL_RATE_ACCEL = 120.f;
-
-///----------------------------------------------------------------------------
-/// Class LLScrollContainer
-///----------------------------------------------------------------------------
-
-static LLDefaultChildRegistry::Register<LLScrollContainer> r("scroll_container");
-
-#include "llscrollingpanellist.h"
-#include "llcontainerview.h"
-#include "llpanel.h"
-
-static ScrollContainerRegistry::Register<LLScrollingPanelList> r1("scrolling_panel_list");
-static ScrollContainerRegistry::Register<LLContainerView> r2("container_view");
-static ScrollContainerRegistry::Register<LLPanel> r3("panel", &LLPanel::fromXML);
-
-LLScrollContainer::Params::Params()
-: is_opaque("opaque"),
- bg_color("color"),
- border_visible("border_visible"),
- hide_scrollbar("hide_scrollbar"),
- ignore_arrow_keys("ignore_arrow_keys"),
- min_auto_scroll_rate("min_auto_scroll_rate", 100),
- max_auto_scroll_rate("max_auto_scroll_rate", 1000),
- max_auto_scroll_zone("max_auto_scroll_zone", 16),
- reserve_scroll_corner("reserve_scroll_corner", false),
- size("size", -1)
-{}
-
-
-// Default constructor
-LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)
-: LLUICtrl(p),
- mAutoScrolling( false ),
- mAutoScrollRate( 0.f ),
- mBackgroundColor(p.bg_color()),
- mIsOpaque(p.is_opaque),
- mHideScrollbar(p.hide_scrollbar),
- mIgnoreArrowKeys(p.ignore_arrow_keys),
- mReserveScrollCorner(p.reserve_scroll_corner),
- mMinAutoScrollRate(p.min_auto_scroll_rate),
- mMaxAutoScrollRate(p.max_auto_scroll_rate),
- mMaxAutoScrollZone(p.max_auto_scroll_zone),
- mScrolledView(NULL),
- mSize(p.size)
-{
- 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");
- params.rect(border_rect);
- params.visible(p.border_visible);
- params.bevel_style(LLViewBorder::BEVEL_IN);
- mBorder = LLUICtrlFactory::create<LLViewBorder> (params);
- LLView::addChild( mBorder );
-
- mInnerRect = getLocalRect();
- mInnerRect.stretch( -getBorderWidth() );
-
- LLRect vertical_scroll_rect = mInnerRect;
- vertical_scroll_rect.mLeft = vertical_scroll_rect.mRight - scrollbar_size;
- LLScrollbar::Params sbparams;
- sbparams.name("scrollable vertical");
- sbparams.rect(vertical_scroll_rect);
- sbparams.orientation(LLScrollbar::VERTICAL);
- sbparams.doc_size(mInnerRect.getHeight());
- sbparams.doc_pos(0);
- sbparams.page_size(mInnerRect.getHeight());
- sbparams.step_size(VERTICAL_MULTIPLE);
- sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
- sbparams.visible(false);
- sbparams.change_callback(p.scroll_callback);
- mScrollbar[VERTICAL] = LLUICtrlFactory::create<LLScrollbar> (sbparams);
- LLView::addChild( mScrollbar[VERTICAL] );
-
- LLRect horizontal_scroll_rect;
- horizontal_scroll_rect.mTop = scrollbar_size;
- horizontal_scroll_rect.mRight = mInnerRect.getWidth();
- sbparams.name("scrollable horizontal");
- sbparams.rect(horizontal_scroll_rect);
- sbparams.orientation(LLScrollbar::HORIZONTAL);
- sbparams.doc_size(mInnerRect.getWidth());
- sbparams.doc_pos(0);
- sbparams.page_size(mInnerRect.getWidth());
- sbparams.step_size(VERTICAL_MULTIPLE);
- sbparams.visible(false);
- sbparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM);
- sbparams.change_callback(p.scroll_callback);
- mScrollbar[HORIZONTAL] = LLUICtrlFactory::create<LLScrollbar> (sbparams);
- LLView::addChild( mScrollbar[HORIZONTAL] );
-}
-
-// Destroys the object
-LLScrollContainer::~LLScrollContainer( void )
-{
- // mScrolledView and mScrollbar are child views, so the LLView
- // destructor takes care of memory deallocation.
- for( S32 i = 0; i < ORIENTATION_COUNT; i++ )
- {
- mScrollbar[i] = NULL;
- }
- mScrolledView = NULL;
-}
-
-// internal scrollbar handlers
-// virtual
-void LLScrollContainer::scrollHorizontal( S32 new_pos )
-{
- if( mScrolledView )
- {
- LLRect doc_rect = mScrolledView->getRect();
- S32 old_pos = -(doc_rect.mLeft - mInnerRect.mLeft);
- mScrolledView->translate( -(new_pos - old_pos), 0 );
- }
-}
-
-// virtual
-void LLScrollContainer::scrollVertical( S32 new_pos )
-{
- if( mScrolledView )
- {
- LLRect doc_rect = mScrolledView->getRect();
- S32 old_pos = doc_rect.mTop - mInnerRect.mTop;
- mScrolledView->translate( 0, new_pos - old_pos );
- }
-}
-
-// LLView functionality
-void LLScrollContainer::reshape(S32 width, S32 height,
- bool called_from_parent)
-{
- LLUICtrl::reshape( width, height, called_from_parent );
-
- mInnerRect = getLocalRect();
- mInnerRect.stretch( -getBorderWidth() );
-
- if (mScrolledView)
- {
- const LLRect& scrolled_rect = mScrolledView->getRect();
-
- 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 );
-
- mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() );
- mScrollbar[VERTICAL]->setPageSize( visible_height );
-
- mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() );
- mScrollbar[HORIZONTAL]->setPageSize( visible_width );
- updateScroll();
- }
-}
-
-// virtual
-bool LLScrollContainer::handleKeyHere(KEY key, MASK mask)
-{
- if (mIgnoreArrowKeys)
- {
- switch(key)
- {
- case KEY_LEFT:
- case KEY_RIGHT:
- case KEY_UP:
- case KEY_DOWN:
- case KEY_PAGE_UP:
- case KEY_PAGE_DOWN:
- case KEY_HOME:
- case KEY_END:
- return false;
- default:
- break;
- }
- }
-
- // allow scrolled view to handle keystrokes in case it delegated keyboard focus
- // to the scroll container.
- // NOTE: this should not recurse indefinitely as handleKeyHere
- // should not propagate to parent controls, so mScrolledView should *not*
- // call LLScrollContainer::handleKeyHere in turn
- if (mScrolledView && mScrolledView->handleKeyHere(key, mask))
- {
- return true;
- }
- for( S32 i = 0; i < ORIENTATION_COUNT; i++ )
- {
- if( mScrollbar[i]->handleKeyHere(key, mask) )
- {
- updateScroll();
- return true;
- }
- }
-
- 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
- // (Bad UI design, but technically possible.)
- if (LLUICtrl::handleScrollWheel(x,y,clicks))
- return true;
-
- // When the vertical scrollbar is visible, scroll wheel
- // only affects vertical scrolling. It's confusing to have
- // scroll wheel perform both vertical and horizontal in a
- // single container.
- LLScrollbar* vertical = mScrollbar[VERTICAL];
- if (vertical->getVisible()
- && vertical->getEnabled())
- {
- // Pretend the mouse is over the scrollbar
- if (vertical->handleScrollWheel( 0, 0, clicks ) )
- {
- updateScroll();
- }
- // Always eat the event
- return true;
- }
-
- LLScrollbar* horizontal = mScrollbar[HORIZONTAL];
- // Test enablement and visibility for consistency with
- // LLView::childrenHandleScrollWheel().
- if (horizontal->getVisible()
- && horizontal->getEnabled()
- && horizontal->handleScrollWheel( 0, 0, clicks ) )
- {
- updateScroll();
- return true;
- }
- return false;
-}
-
-bool LLScrollContainer::handleScrollHWheel(S32 x, S32 y, S32 clicks)
-{
- if (LLUICtrl::handleScrollHWheel(x,y,clicks))
- {
- return true;
- }
-
- LLScrollbar* horizontal = mScrollbar[HORIZONTAL];
- if (horizontal->getVisible()
- && horizontal->getEnabled()
- && horizontal->handleScrollHWheel( 0, 0, clicks ) )
- {
- updateScroll();
- return true;
- }
-
- return false;
-}
-
-bool LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask,
- bool drop,
- EDragAndDropType cargo_type,
- void* cargo_data,
- EAcceptance* accept,
- std::string& tooltip_msg)
-{
- // Scroll folder view if needed. Never accepts a drag or drop.
- *accept = ACCEPT_NO;
- bool handled = autoScroll(x, y);
-
- if( !handled )
- {
- handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type,
- cargo_data, accept, tooltip_msg) != NULL;
- }
-
- return true;
-}
-
-bool LLScrollContainer::canAutoScroll(S32 x, S32 y)
-{
- if (mAutoScrolling)
- {
- return true; // already scrolling
- }
- return autoScroll(x, y, false);
-}
-
-bool LLScrollContainer::autoScroll(S32 x, S32 y)
-{
- return autoScroll(x, y, true);
-}
-
-bool LLScrollContainer::autoScroll(S32 x, S32 y, bool do_scroll)
-{
- 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() )
- {
- LLRect screen_local_extents;
- screenRectToLocal(getRootView()->getLocalRect(), &screen_local_extents);
-
- LLRect inner_rect_local( 0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0 );
- // Note: Will also include scrollers as scroll zones, so opposite
- // scroll zones might have different size due to visible scrollers
- if( mScrollbar[HORIZONTAL]->getVisible() )
- {
- inner_rect_local.mBottom += scrollbar_size;
- }
- if( mScrollbar[VERTICAL]->getVisible() )
- {
- inner_rect_local.mRight -= scrollbar_size;
- }
-
- // clip rect against root view
- inner_rect_local.intersectWith(screen_local_extents);
-
- 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, (S32)mMaxAutoScrollZone);
- S32 auto_scroll_region_height = llmin(inner_rect_local.getHeight() / 3, (S32)mMaxAutoScrollZone);
-
- if( mScrollbar[HORIZONTAL]->getVisible() )
- {
- LLRect left_scroll_rect = screen_local_extents;
- left_scroll_rect.mRight = inner_rect_local.mLeft + auto_scroll_region_width;
- if( left_scroll_rect.pointInRect( x, y ) && (mScrollbar[HORIZONTAL]->getDocPos() > 0) )
- {
- if (do_scroll)
- {
- mScrollbar[HORIZONTAL]->setDocPos(mScrollbar[HORIZONTAL]->getDocPos() - auto_scroll_speed);
- mAutoScrolling = true;
- }
- scrolling = true;
- }
-
- LLRect right_scroll_rect = screen_local_extents;
- right_scroll_rect.mLeft = inner_rect_local.mRight - auto_scroll_region_width;
- if( right_scroll_rect.pointInRect( x, y ) && (mScrollbar[HORIZONTAL]->getDocPos() < mScrollbar[HORIZONTAL]->getDocPosMax()) )
- {
- if (do_scroll)
- {
- mScrollbar[HORIZONTAL]->setDocPos(mScrollbar[HORIZONTAL]->getDocPos() + auto_scroll_speed);
- mAutoScrolling = true;
- }
- scrolling = true;
- }
- }
- if( mScrollbar[VERTICAL]->getVisible() )
- {
- LLRect bottom_scroll_rect = screen_local_extents;
- bottom_scroll_rect.mTop = inner_rect_local.mBottom + auto_scroll_region_height;
- if( bottom_scroll_rect.pointInRect( x, y ) && (mScrollbar[VERTICAL]->getDocPos() < mScrollbar[VERTICAL]->getDocPosMax()) )
- {
- if (do_scroll)
- {
- mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocPos() + auto_scroll_speed);
- mAutoScrolling = true;
- }
- scrolling = true;
- }
-
- LLRect top_scroll_rect = screen_local_extents;
- top_scroll_rect.mBottom = inner_rect_local.mTop - auto_scroll_region_height;
- if( top_scroll_rect.pointInRect( x, y ) && (mScrollbar[VERTICAL]->getDocPos() > 0) )
- {
- if (do_scroll)
- {
- mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocPos() - auto_scroll_speed);
- mAutoScrolling = true;
- }
- scrolling = true;
- }
- }
- }
- return scrolling;
-}
-
-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_control ("UIScrollbarSize", 0);
- S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize);
-
- S32 doc_width = doc_rect.getWidth();
- S32 doc_height = doc_rect.getHeight();
-
- S32 border_width = getBorderWidth();
- *visible_width = getRect().getWidth() - 2 * border_width;
- *visible_height = getRect().getHeight() - 2 * border_width;
-
- *show_v_scrollbar = false;
- *show_h_scrollbar = false;
-
- if (!mHideScrollbar)
- {
- // 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 ((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.
-
- if( !*show_v_scrollbar && ((doc_height - *visible_height) > 1) )
- {
- *show_v_scrollbar = true;
- *visible_width -= scrollbar_size;
- }
- }
- }
-}
-
-
-void LLScrollContainer::draw()
-{
- static LLUICachedControl<S32> scrollbar_size_control ("UIScrollbarSize", 0);
- S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize);
-
- if (mAutoScrolling)
- {
- // add acceleration to autoscroll
- mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), mMaxAutoScrollRate);
- }
- else
- {
- // reset to minimum for next time
- mAutoScrollRate = mMinAutoScrollRate;
- }
- // clear this flag to be set on next call to autoScroll
- mAutoScrolling = false;
-
- // auto-focus when scrollbar active
- // this allows us to capture user intent (i.e. stop automatically scrolling the view/etc)
- if (!hasFocus()
- && (mScrollbar[VERTICAL]->hasMouseCapture() || mScrollbar[HORIZONTAL]->hasMouseCapture()))
- {
- focusFirstItem();
- }
-
- if (getRect().isValid())
- {
- // Draw background
- if( mIsOpaque )
- {
- F32 alpha = getCurrentTransparency();
-
- 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.
- {
- 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) );
- }
-
- // 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++;
- }
- if( (viewp != mScrolledView) && viewp->getVisible() )
- {
- drawChild(viewp);
- }
- if( sDebugRects )
- {
- sDepth--;
- }
- }
- }
-} // end draw
-
-bool LLScrollContainer::addChild(LLView* view, S32 tab_group)
-{
- if (!mScrolledView)
- {
- // Use the first panel or container as the scrollable view (bit of a hack)
- mScrolledView = view;
- }
-
- bool ret_val = LLView::addChild(view, tab_group);
-
- //bring the scrollbars to the front
- sendChildToFront( mScrollbar[HORIZONTAL] );
- sendChildToFront( mScrollbar[VERTICAL] );
-
- return ret_val;
-}
-
-void LLScrollContainer::updateScroll()
-{
- if (!getVisible() || !mScrolledView)
- {
- return;
- }
- 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();
- 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 );
-
- S32 border_width = getBorderWidth();
- if( show_v_scrollbar )
- {
- if( doc_rect.mTop < getRect().getHeight() - border_width )
- {
- mScrolledView->translate( 0, getRect().getHeight() - border_width - doc_rect.mTop );
- }
-
- scrollVertical( mScrollbar[VERTICAL]->getDocPos() );
- mScrollbar[VERTICAL]->setVisible( true );
-
- S32 v_scrollbar_height = visible_height;
- if( !show_h_scrollbar && mReserveScrollCorner )
- {
- v_scrollbar_height -= scrollbar_size;
- }
- mScrollbar[VERTICAL]->reshape( scrollbar_size, v_scrollbar_height, true );
-
- // Make room for the horizontal scrollbar (or not)
- S32 v_scrollbar_offset = 0;
- if( show_h_scrollbar || mReserveScrollCorner )
- {
- v_scrollbar_offset = scrollbar_size;
- }
- LLRect r = mScrollbar[VERTICAL]->getRect();
- r.translate( 0, mInnerRect.mBottom - r.mBottom + v_scrollbar_offset );
- mScrollbar[VERTICAL]->setRect( r );
- }
- else
- {
- mScrolledView->translate( 0, getRect().getHeight() - border_width - doc_rect.mTop );
-
- mScrollbar[VERTICAL]->setVisible( false );
- mScrollbar[VERTICAL]->setDocPos( 0 );
- }
-
- if( show_h_scrollbar )
- {
- if( doc_rect.mLeft > border_width )
- {
- mScrolledView->translate( border_width - doc_rect.mLeft, 0 );
- mScrollbar[HORIZONTAL]->setDocPos( 0 );
- }
- else
- {
- scrollHorizontal( mScrollbar[HORIZONTAL]->getDocPos() );
- }
-
- mScrollbar[HORIZONTAL]->setVisible( true );
- S32 h_scrollbar_width = visible_width;
- if( !show_v_scrollbar && mReserveScrollCorner )
- {
- h_scrollbar_width -= scrollbar_size;
- }
- mScrollbar[HORIZONTAL]->reshape( h_scrollbar_width, scrollbar_size, true );
- }
- else
- {
- mScrolledView->translate( border_width - doc_rect.mLeft, 0 );
-
- mScrollbar[HORIZONTAL]->setVisible( false );
- mScrollbar[HORIZONTAL]->setDocPos( 0 );
- }
-
- mScrollbar[HORIZONTAL]->setDocSize( doc_width );
- mScrollbar[HORIZONTAL]->setPageSize( visible_width );
-
- mScrollbar[VERTICAL]->setDocSize( doc_height );
- mScrollbar[VERTICAL]->setPageSize( visible_height );
-} // end updateScroll
-
-void LLScrollContainer::setBorderVisible(bool b)
-{
- mBorder->setVisible( b );
- // Recompute inner rect, as border visibility changes it
- mInnerRect = getLocalRect();
- mInnerRect.stretch( -getBorderWidth() );
-}
-
-LLRect LLScrollContainer::getVisibleContentRect()
-{
- updateScroll();
- LLRect visible_rect = getContentWindowRect();
- LLRect contents_rect = mScrolledView->getRect();
- visible_rect.translate(-contents_rect.mLeft, -contents_rect.mBottom);
- return visible_rect;
-}
-
-LLRect LLScrollContainer::getContentWindowRect()
-{
- updateScroll();
- LLRect scroller_view_rect;
- S32 visible_width = 0;
- S32 visible_height = 0;
- bool show_h_scrollbar = false;
- bool show_v_scrollbar = false;
- calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
- S32 border_width = getBorderWidth();
- scroller_view_rect.setOriginAndSize(border_width,
- show_h_scrollbar ? mScrollbar[HORIZONTAL]->getRect().mTop : border_width,
- visible_width,
- visible_height);
- return scroller_view_rect;
-}
-
-// rect is in document coordinates, constraint is in display coordinates relative to content window rect
-void LLScrollContainer::scrollToShowRect(const LLRect& rect, const LLRect& constraint)
-{
- if (!mScrolledView)
- {
- LL_WARNS() << "LLScrollContainer::scrollToShowRect with no view!" << LL_ENDL;
- return;
- }
-
- LLRect content_window_rect = getContentWindowRect();
- // get document rect
- LLRect scrolled_rect = mScrolledView->getRect();
-
- // shrink target rect to fit within constraint region, biasing towards top left
- LLRect rect_to_constrain = rect;
- rect_to_constrain.mBottom = llmax(rect_to_constrain.mBottom, rect_to_constrain.mTop - constraint.getHeight());
- rect_to_constrain.mRight = llmin(rect_to_constrain.mRight, rect_to_constrain.mLeft + constraint.getWidth());
-
- // calculate allowable positions for scroller window in document coordinates
- LLRect allowable_scroll_rect(rect_to_constrain.mRight - constraint.mRight,
- rect_to_constrain.mBottom - constraint.mBottom,
- rect_to_constrain.mLeft - constraint.mLeft,
- rect_to_constrain.mTop - constraint.mTop);
-
- // translate from allowable region for lower left corner to upper left corner
- allowable_scroll_rect.translate(0, content_window_rect.getHeight());
-
- S32 vert_pos = llclamp(mScrollbar[VERTICAL]->getDocPos(),
- mScrollbar[VERTICAL]->getDocSize() - allowable_scroll_rect.mTop, // min vertical scroll
- mScrollbar[VERTICAL]->getDocSize() - allowable_scroll_rect.mBottom); // max vertical scroll
-
- mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() );
- mScrollbar[VERTICAL]->setPageSize( content_window_rect.getHeight() );
- mScrollbar[VERTICAL]->setDocPos( vert_pos );
-
- S32 horizontal_pos = llclamp(mScrollbar[HORIZONTAL]->getDocPos(),
- allowable_scroll_rect.mLeft,
- allowable_scroll_rect.mRight);
-
- mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() );
- mScrollbar[HORIZONTAL]->setPageSize( content_window_rect.getWidth() );
- mScrollbar[HORIZONTAL]->setDocPos( horizontal_pos );
-
- // propagate scroll to document
- updateScroll();
-
- // In case we are in accordion tab notify parent to show selected rectangle
- LLRect screen_rc;
- localRectToScreen(rect_to_constrain, &screen_rc);
- notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue()));
-}
-
-void LLScrollContainer::pageUp(S32 overlap)
-{
- mScrollbar[VERTICAL]->pageUp(overlap);
- updateScroll();
-}
-
-void LLScrollContainer::pageDown(S32 overlap)
-{
- mScrollbar[VERTICAL]->pageDown(overlap);
- updateScroll();
-}
-
-void LLScrollContainer::goToTop()
-{
- mScrollbar[VERTICAL]->setDocPos(0);
- updateScroll();
-}
-
-void LLScrollContainer::goToBottom()
-{
- mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocSize());
- updateScroll();
-}
-
-S32 LLScrollContainer::getBorderWidth() const
-{
- if (mBorder->getVisible())
- {
- return mBorder->getBorderWidth();
- }
-
- return 0;
-}
-
-void LLScrollContainer::setSize(S32 size)
-{
- mSize = size;
- mScrollbar[VERTICAL]->setThickness(size);
- mScrollbar[HORIZONTAL]->setThickness(size);
-}
+/**
+ * @file llscrollcontainer.cpp
+ * @brief LLScrollContainer base class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+#include "linden_common.h"
+
+#include "llscrollcontainer.h"
+
+#include "llrender.h"
+#include "llcontainerview.h"
+#include "lllocalcliprect.h"
+// #include "llfolderview.h"
+#include "llscrollingpanellist.h"
+#include "llscrollbar.h"
+#include "llui.h"
+#include "llkeyboard.h"
+#include "llviewborder.h"
+#include "llfocusmgr.h"
+#include "llframetimer.h"
+#include "lluictrlfactory.h"
+#include "llpanel.h"
+#include "llfontgl.h"
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+static const S32 VERTICAL_MULTIPLE = 16;
+static const F32 AUTO_SCROLL_RATE_ACCEL = 120.f;
+
+///----------------------------------------------------------------------------
+/// Class LLScrollContainer
+///----------------------------------------------------------------------------
+
+static LLDefaultChildRegistry::Register<LLScrollContainer> r("scroll_container");
+
+#include "llscrollingpanellist.h"
+#include "llcontainerview.h"
+#include "llpanel.h"
+
+static ScrollContainerRegistry::Register<LLScrollingPanelList> r1("scrolling_panel_list");
+static ScrollContainerRegistry::Register<LLContainerView> r2("container_view");
+static ScrollContainerRegistry::Register<LLPanel> r3("panel", &LLPanel::fromXML);
+
+LLScrollContainer::Params::Params()
+: is_opaque("opaque"),
+ bg_color("color"),
+ border_visible("border_visible"),
+ hide_scrollbar("hide_scrollbar"),
+ ignore_arrow_keys("ignore_arrow_keys"),
+ min_auto_scroll_rate("min_auto_scroll_rate", 100),
+ max_auto_scroll_rate("max_auto_scroll_rate", 1000),
+ max_auto_scroll_zone("max_auto_scroll_zone", 16),
+ reserve_scroll_corner("reserve_scroll_corner", false),
+ size("size", -1)
+{}
+
+
+// Default constructor
+LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)
+: LLUICtrl(p),
+ mAutoScrolling( false ),
+ mAutoScrollRate( 0.f ),
+ mBackgroundColor(p.bg_color()),
+ mIsOpaque(p.is_opaque),
+ mHideScrollbar(p.hide_scrollbar),
+ mIgnoreArrowKeys(p.ignore_arrow_keys),
+ mReserveScrollCorner(p.reserve_scroll_corner),
+ mMinAutoScrollRate(p.min_auto_scroll_rate),
+ mMaxAutoScrollRate(p.max_auto_scroll_rate),
+ mMaxAutoScrollZone(p.max_auto_scroll_zone),
+ mScrolledView(NULL),
+ mSize(p.size)
+{
+ 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");
+ params.rect(border_rect);
+ params.visible(p.border_visible);
+ params.bevel_style(LLViewBorder::BEVEL_IN);
+ mBorder = LLUICtrlFactory::create<LLViewBorder> (params);
+ LLView::addChild( mBorder );
+
+ mInnerRect = getLocalRect();
+ mInnerRect.stretch( -getBorderWidth() );
+
+ LLRect vertical_scroll_rect = mInnerRect;
+ vertical_scroll_rect.mLeft = vertical_scroll_rect.mRight - scrollbar_size;
+ LLScrollbar::Params sbparams;
+ sbparams.name("scrollable vertical");
+ sbparams.rect(vertical_scroll_rect);
+ sbparams.orientation(LLScrollbar::VERTICAL);
+ sbparams.doc_size(mInnerRect.getHeight());
+ sbparams.doc_pos(0);
+ sbparams.page_size(mInnerRect.getHeight());
+ sbparams.step_size(VERTICAL_MULTIPLE);
+ sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
+ sbparams.visible(false);
+ sbparams.change_callback(p.scroll_callback);
+ mScrollbar[VERTICAL] = LLUICtrlFactory::create<LLScrollbar> (sbparams);
+ LLView::addChild( mScrollbar[VERTICAL] );
+
+ LLRect horizontal_scroll_rect;
+ horizontal_scroll_rect.mTop = scrollbar_size;
+ horizontal_scroll_rect.mRight = mInnerRect.getWidth();
+ sbparams.name("scrollable horizontal");
+ sbparams.rect(horizontal_scroll_rect);
+ sbparams.orientation(LLScrollbar::HORIZONTAL);
+ sbparams.doc_size(mInnerRect.getWidth());
+ sbparams.doc_pos(0);
+ sbparams.page_size(mInnerRect.getWidth());
+ sbparams.step_size(VERTICAL_MULTIPLE);
+ sbparams.visible(false);
+ sbparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM);
+ sbparams.change_callback(p.scroll_callback);
+ mScrollbar[HORIZONTAL] = LLUICtrlFactory::create<LLScrollbar> (sbparams);
+ LLView::addChild( mScrollbar[HORIZONTAL] );
+}
+
+// Destroys the object
+LLScrollContainer::~LLScrollContainer( void )
+{
+ // mScrolledView and mScrollbar are child views, so the LLView
+ // destructor takes care of memory deallocation.
+ for( S32 i = 0; i < ORIENTATION_COUNT; i++ )
+ {
+ mScrollbar[i] = NULL;
+ }
+ mScrolledView = NULL;
+}
+
+// internal scrollbar handlers
+// virtual
+void LLScrollContainer::scrollHorizontal( S32 new_pos )
+{
+ if( mScrolledView )
+ {
+ LLRect doc_rect = mScrolledView->getRect();
+ S32 old_pos = -(doc_rect.mLeft - mInnerRect.mLeft);
+ mScrolledView->translate( -(new_pos - old_pos), 0 );
+ }
+}
+
+// virtual
+void LLScrollContainer::scrollVertical( S32 new_pos )
+{
+ if( mScrolledView )
+ {
+ LLRect doc_rect = mScrolledView->getRect();
+ S32 old_pos = doc_rect.mTop - mInnerRect.mTop;
+ mScrolledView->translate( 0, new_pos - old_pos );
+ }
+}
+
+// LLView functionality
+void LLScrollContainer::reshape(S32 width, S32 height,
+ bool called_from_parent)
+{
+ LLUICtrl::reshape( width, height, called_from_parent );
+
+ mInnerRect = getLocalRect();
+ mInnerRect.stretch( -getBorderWidth() );
+
+ if (mScrolledView)
+ {
+ const LLRect& scrolled_rect = mScrolledView->getRect();
+
+ 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 );
+
+ mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() );
+ mScrollbar[VERTICAL]->setPageSize( visible_height );
+
+ mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() );
+ mScrollbar[HORIZONTAL]->setPageSize( visible_width );
+ updateScroll();
+ }
+}
+
+// virtual
+bool LLScrollContainer::handleKeyHere(KEY key, MASK mask)
+{
+ if (mIgnoreArrowKeys)
+ {
+ switch(key)
+ {
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ case KEY_UP:
+ case KEY_DOWN:
+ case KEY_PAGE_UP:
+ case KEY_PAGE_DOWN:
+ case KEY_HOME:
+ case KEY_END:
+ return false;
+ default:
+ break;
+ }
+ }
+
+ // allow scrolled view to handle keystrokes in case it delegated keyboard focus
+ // to the scroll container.
+ // NOTE: this should not recurse indefinitely as handleKeyHere
+ // should not propagate to parent controls, so mScrolledView should *not*
+ // call LLScrollContainer::handleKeyHere in turn
+ if (mScrolledView && mScrolledView->handleKeyHere(key, mask))
+ {
+ return true;
+ }
+ for( S32 i = 0; i < ORIENTATION_COUNT; i++ )
+ {
+ if( mScrollbar[i]->handleKeyHere(key, mask) )
+ {
+ updateScroll();
+ return true;
+ }
+ }
+
+ 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
+ // (Bad UI design, but technically possible.)
+ if (LLUICtrl::handleScrollWheel(x,y,clicks))
+ return true;
+
+ // When the vertical scrollbar is visible, scroll wheel
+ // only affects vertical scrolling. It's confusing to have
+ // scroll wheel perform both vertical and horizontal in a
+ // single container.
+ LLScrollbar* vertical = mScrollbar[VERTICAL];
+ if (vertical->getVisible()
+ && vertical->getEnabled())
+ {
+ // Pretend the mouse is over the scrollbar
+ if (vertical->handleScrollWheel( 0, 0, clicks ) )
+ {
+ updateScroll();
+ }
+ // Always eat the event
+ return true;
+ }
+
+ LLScrollbar* horizontal = mScrollbar[HORIZONTAL];
+ // Test enablement and visibility for consistency with
+ // LLView::childrenHandleScrollWheel().
+ if (horizontal->getVisible()
+ && horizontal->getEnabled()
+ && horizontal->handleScrollWheel( 0, 0, clicks ) )
+ {
+ updateScroll();
+ return true;
+ }
+ return false;
+}
+
+bool LLScrollContainer::handleScrollHWheel(S32 x, S32 y, S32 clicks)
+{
+ if (LLUICtrl::handleScrollHWheel(x,y,clicks))
+ {
+ return true;
+ }
+
+ LLScrollbar* horizontal = mScrollbar[HORIZONTAL];
+ if (horizontal->getVisible()
+ && horizontal->getEnabled()
+ && horizontal->handleScrollHWheel( 0, 0, clicks ) )
+ {
+ updateScroll();
+ return true;
+ }
+
+ return false;
+}
+
+bool LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask,
+ bool drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ // Scroll folder view if needed. Never accepts a drag or drop.
+ *accept = ACCEPT_NO;
+ bool handled = autoScroll(x, y);
+
+ if( !handled )
+ {
+ handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type,
+ cargo_data, accept, tooltip_msg) != NULL;
+ }
+
+ return true;
+}
+
+bool LLScrollContainer::canAutoScroll(S32 x, S32 y)
+{
+ if (mAutoScrolling)
+ {
+ return true; // already scrolling
+ }
+ return autoScroll(x, y, false);
+}
+
+bool LLScrollContainer::autoScroll(S32 x, S32 y)
+{
+ return autoScroll(x, y, true);
+}
+
+bool LLScrollContainer::autoScroll(S32 x, S32 y, bool do_scroll)
+{
+ 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() )
+ {
+ LLRect screen_local_extents;
+ screenRectToLocal(getRootView()->getLocalRect(), &screen_local_extents);
+
+ LLRect inner_rect_local( 0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0 );
+ // Note: Will also include scrollers as scroll zones, so opposite
+ // scroll zones might have different size due to visible scrollers
+ if( mScrollbar[HORIZONTAL]->getVisible() )
+ {
+ inner_rect_local.mBottom += scrollbar_size;
+ }
+ if( mScrollbar[VERTICAL]->getVisible() )
+ {
+ inner_rect_local.mRight -= scrollbar_size;
+ }
+
+ // clip rect against root view
+ inner_rect_local.intersectWith(screen_local_extents);
+
+ 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, (S32)mMaxAutoScrollZone);
+ S32 auto_scroll_region_height = llmin(inner_rect_local.getHeight() / 3, (S32)mMaxAutoScrollZone);
+
+ if( mScrollbar[HORIZONTAL]->getVisible() )
+ {
+ LLRect left_scroll_rect = screen_local_extents;
+ left_scroll_rect.mRight = inner_rect_local.mLeft + auto_scroll_region_width;
+ if( left_scroll_rect.pointInRect( x, y ) && (mScrollbar[HORIZONTAL]->getDocPos() > 0) )
+ {
+ if (do_scroll)
+ {
+ mScrollbar[HORIZONTAL]->setDocPos(mScrollbar[HORIZONTAL]->getDocPos() - auto_scroll_speed);
+ mAutoScrolling = true;
+ }
+ scrolling = true;
+ }
+
+ LLRect right_scroll_rect = screen_local_extents;
+ right_scroll_rect.mLeft = inner_rect_local.mRight - auto_scroll_region_width;
+ if( right_scroll_rect.pointInRect( x, y ) && (mScrollbar[HORIZONTAL]->getDocPos() < mScrollbar[HORIZONTAL]->getDocPosMax()) )
+ {
+ if (do_scroll)
+ {
+ mScrollbar[HORIZONTAL]->setDocPos(mScrollbar[HORIZONTAL]->getDocPos() + auto_scroll_speed);
+ mAutoScrolling = true;
+ }
+ scrolling = true;
+ }
+ }
+ if( mScrollbar[VERTICAL]->getVisible() )
+ {
+ LLRect bottom_scroll_rect = screen_local_extents;
+ bottom_scroll_rect.mTop = inner_rect_local.mBottom + auto_scroll_region_height;
+ if( bottom_scroll_rect.pointInRect( x, y ) && (mScrollbar[VERTICAL]->getDocPos() < mScrollbar[VERTICAL]->getDocPosMax()) )
+ {
+ if (do_scroll)
+ {
+ mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocPos() + auto_scroll_speed);
+ mAutoScrolling = true;
+ }
+ scrolling = true;
+ }
+
+ LLRect top_scroll_rect = screen_local_extents;
+ top_scroll_rect.mBottom = inner_rect_local.mTop - auto_scroll_region_height;
+ if( top_scroll_rect.pointInRect( x, y ) && (mScrollbar[VERTICAL]->getDocPos() > 0) )
+ {
+ if (do_scroll)
+ {
+ mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocPos() - auto_scroll_speed);
+ mAutoScrolling = true;
+ }
+ scrolling = true;
+ }
+ }
+ }
+ return scrolling;
+}
+
+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_control ("UIScrollbarSize", 0);
+ S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize);
+
+ S32 doc_width = doc_rect.getWidth();
+ S32 doc_height = doc_rect.getHeight();
+
+ S32 border_width = getBorderWidth();
+ *visible_width = getRect().getWidth() - 2 * border_width;
+ *visible_height = getRect().getHeight() - 2 * border_width;
+
+ *show_v_scrollbar = false;
+ *show_h_scrollbar = false;
+
+ if (!mHideScrollbar)
+ {
+ // 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 ((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.
+
+ if( !*show_v_scrollbar && ((doc_height - *visible_height) > 1) )
+ {
+ *show_v_scrollbar = true;
+ *visible_width -= scrollbar_size;
+ }
+ }
+ }
+}
+
+
+void LLScrollContainer::draw()
+{
+ static LLUICachedControl<S32> scrollbar_size_control ("UIScrollbarSize", 0);
+ S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize);
+
+ if (mAutoScrolling)
+ {
+ // add acceleration to autoscroll
+ mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), mMaxAutoScrollRate);
+ }
+ else
+ {
+ // reset to minimum for next time
+ mAutoScrollRate = mMinAutoScrollRate;
+ }
+ // clear this flag to be set on next call to autoScroll
+ mAutoScrolling = false;
+
+ // auto-focus when scrollbar active
+ // this allows us to capture user intent (i.e. stop automatically scrolling the view/etc)
+ if (!hasFocus()
+ && (mScrollbar[VERTICAL]->hasMouseCapture() || mScrollbar[HORIZONTAL]->hasMouseCapture()))
+ {
+ focusFirstItem();
+ }
+
+ if (getRect().isValid())
+ {
+ // Draw background
+ if( mIsOpaque )
+ {
+ F32 alpha = getCurrentTransparency();
+
+ 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.
+ {
+ 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) );
+ }
+
+ // 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++;
+ }
+ if( (viewp != mScrolledView) && viewp->getVisible() )
+ {
+ drawChild(viewp);
+ }
+ if( sDebugRects )
+ {
+ sDepth--;
+ }
+ }
+ }
+} // end draw
+
+bool LLScrollContainer::addChild(LLView* view, S32 tab_group)
+{
+ if (!mScrolledView)
+ {
+ // Use the first panel or container as the scrollable view (bit of a hack)
+ mScrolledView = view;
+ }
+
+ bool ret_val = LLView::addChild(view, tab_group);
+
+ //bring the scrollbars to the front
+ sendChildToFront( mScrollbar[HORIZONTAL] );
+ sendChildToFront( mScrollbar[VERTICAL] );
+
+ return ret_val;
+}
+
+void LLScrollContainer::updateScroll()
+{
+ if (!getVisible() || !mScrolledView)
+ {
+ return;
+ }
+ 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();
+ 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 );
+
+ S32 border_width = getBorderWidth();
+ if( show_v_scrollbar )
+ {
+ if( doc_rect.mTop < getRect().getHeight() - border_width )
+ {
+ mScrolledView->translate( 0, getRect().getHeight() - border_width - doc_rect.mTop );
+ }
+
+ scrollVertical( mScrollbar[VERTICAL]->getDocPos() );
+ mScrollbar[VERTICAL]->setVisible( true );
+
+ S32 v_scrollbar_height = visible_height;
+ if( !show_h_scrollbar && mReserveScrollCorner )
+ {
+ v_scrollbar_height -= scrollbar_size;
+ }
+ mScrollbar[VERTICAL]->reshape( scrollbar_size, v_scrollbar_height, true );
+
+ // Make room for the horizontal scrollbar (or not)
+ S32 v_scrollbar_offset = 0;
+ if( show_h_scrollbar || mReserveScrollCorner )
+ {
+ v_scrollbar_offset = scrollbar_size;
+ }
+ LLRect r = mScrollbar[VERTICAL]->getRect();
+ r.translate( 0, mInnerRect.mBottom - r.mBottom + v_scrollbar_offset );
+ mScrollbar[VERTICAL]->setRect( r );
+ }
+ else
+ {
+ mScrolledView->translate( 0, getRect().getHeight() - border_width - doc_rect.mTop );
+
+ mScrollbar[VERTICAL]->setVisible( false );
+ mScrollbar[VERTICAL]->setDocPos( 0 );
+ }
+
+ if( show_h_scrollbar )
+ {
+ if( doc_rect.mLeft > border_width )
+ {
+ mScrolledView->translate( border_width - doc_rect.mLeft, 0 );
+ mScrollbar[HORIZONTAL]->setDocPos( 0 );
+ }
+ else
+ {
+ scrollHorizontal( mScrollbar[HORIZONTAL]->getDocPos() );
+ }
+
+ mScrollbar[HORIZONTAL]->setVisible( true );
+ S32 h_scrollbar_width = visible_width;
+ if( !show_v_scrollbar && mReserveScrollCorner )
+ {
+ h_scrollbar_width -= scrollbar_size;
+ }
+ mScrollbar[HORIZONTAL]->reshape( h_scrollbar_width, scrollbar_size, true );
+ }
+ else
+ {
+ mScrolledView->translate( border_width - doc_rect.mLeft, 0 );
+
+ mScrollbar[HORIZONTAL]->setVisible( false );
+ mScrollbar[HORIZONTAL]->setDocPos( 0 );
+ }
+
+ mScrollbar[HORIZONTAL]->setDocSize( doc_width );
+ mScrollbar[HORIZONTAL]->setPageSize( visible_width );
+
+ mScrollbar[VERTICAL]->setDocSize( doc_height );
+ mScrollbar[VERTICAL]->setPageSize( visible_height );
+} // end updateScroll
+
+void LLScrollContainer::setBorderVisible(bool b)
+{
+ mBorder->setVisible( b );
+ // Recompute inner rect, as border visibility changes it
+ mInnerRect = getLocalRect();
+ mInnerRect.stretch( -getBorderWidth() );
+}
+
+LLRect LLScrollContainer::getVisibleContentRect()
+{
+ updateScroll();
+ LLRect visible_rect = getContentWindowRect();
+ LLRect contents_rect = mScrolledView->getRect();
+ visible_rect.translate(-contents_rect.mLeft, -contents_rect.mBottom);
+ return visible_rect;
+}
+
+LLRect LLScrollContainer::getContentWindowRect()
+{
+ updateScroll();
+ LLRect scroller_view_rect;
+ S32 visible_width = 0;
+ S32 visible_height = 0;
+ bool show_h_scrollbar = false;
+ bool show_v_scrollbar = false;
+ calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
+ S32 border_width = getBorderWidth();
+ scroller_view_rect.setOriginAndSize(border_width,
+ show_h_scrollbar ? mScrollbar[HORIZONTAL]->getRect().mTop : border_width,
+ visible_width,
+ visible_height);
+ return scroller_view_rect;
+}
+
+// rect is in document coordinates, constraint is in display coordinates relative to content window rect
+void LLScrollContainer::scrollToShowRect(const LLRect& rect, const LLRect& constraint)
+{
+ if (!mScrolledView)
+ {
+ LL_WARNS() << "LLScrollContainer::scrollToShowRect with no view!" << LL_ENDL;
+ return;
+ }
+
+ LLRect content_window_rect = getContentWindowRect();
+ // get document rect
+ LLRect scrolled_rect = mScrolledView->getRect();
+
+ // shrink target rect to fit within constraint region, biasing towards top left
+ LLRect rect_to_constrain = rect;
+ rect_to_constrain.mBottom = llmax(rect_to_constrain.mBottom, rect_to_constrain.mTop - constraint.getHeight());
+ rect_to_constrain.mRight = llmin(rect_to_constrain.mRight, rect_to_constrain.mLeft + constraint.getWidth());
+
+ // calculate allowable positions for scroller window in document coordinates
+ LLRect allowable_scroll_rect(rect_to_constrain.mRight - constraint.mRight,
+ rect_to_constrain.mBottom - constraint.mBottom,
+ rect_to_constrain.mLeft - constraint.mLeft,
+ rect_to_constrain.mTop - constraint.mTop);
+
+ // translate from allowable region for lower left corner to upper left corner
+ allowable_scroll_rect.translate(0, content_window_rect.getHeight());
+
+ S32 vert_pos = llclamp(mScrollbar[VERTICAL]->getDocPos(),
+ mScrollbar[VERTICAL]->getDocSize() - allowable_scroll_rect.mTop, // min vertical scroll
+ mScrollbar[VERTICAL]->getDocSize() - allowable_scroll_rect.mBottom); // max vertical scroll
+
+ mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() );
+ mScrollbar[VERTICAL]->setPageSize( content_window_rect.getHeight() );
+ mScrollbar[VERTICAL]->setDocPos( vert_pos );
+
+ S32 horizontal_pos = llclamp(mScrollbar[HORIZONTAL]->getDocPos(),
+ allowable_scroll_rect.mLeft,
+ allowable_scroll_rect.mRight);
+
+ mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() );
+ mScrollbar[HORIZONTAL]->setPageSize( content_window_rect.getWidth() );
+ mScrollbar[HORIZONTAL]->setDocPos( horizontal_pos );
+
+ // propagate scroll to document
+ updateScroll();
+
+ // In case we are in accordion tab notify parent to show selected rectangle
+ LLRect screen_rc;
+ localRectToScreen(rect_to_constrain, &screen_rc);
+ notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue()));
+}
+
+void LLScrollContainer::pageUp(S32 overlap)
+{
+ mScrollbar[VERTICAL]->pageUp(overlap);
+ updateScroll();
+}
+
+void LLScrollContainer::pageDown(S32 overlap)
+{
+ mScrollbar[VERTICAL]->pageDown(overlap);
+ updateScroll();
+}
+
+void LLScrollContainer::goToTop()
+{
+ mScrollbar[VERTICAL]->setDocPos(0);
+ updateScroll();
+}
+
+void LLScrollContainer::goToBottom()
+{
+ mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocSize());
+ updateScroll();
+}
+
+S32 LLScrollContainer::getBorderWidth() const
+{
+ if (mBorder->getVisible())
+ {
+ return mBorder->getBorderWidth();
+ }
+
+ 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 a9191714e8..7e9532822b 100644
--- a/indra/llui/llscrollcontainer.h
+++ b/indra/llui/llscrollcontainer.h
@@ -1,157 +1,157 @@
-/**
- * @file llscrollcontainer.h
- * @brief LLScrollContainer class header file.
- *
- * $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_LLSCROLLCONTAINER_H
-#define LL_LLSCROLLCONTAINER_H
-
-#include "lluictrl.h"
-#ifndef LL_V4COLOR_H
-#include "v4color.h"
-#endif
-#include "llcoord.h"
-#include "llscrollbar.h"
-
-
-class LLViewBorder;
-class LLUICtrlFactory;
-
-/*****************************************************************************
- *
- * A decorator view class meant to encapsulate a clipped region which is
- * scrollable. It automatically takes care of pixel perfect scrolling
- * and cliipping, as well as turning the scrollbars on or off based on
- * the width and height of the view you're scrolling.
- *
- *****************************************************************************/
-
-struct ScrollContainerRegistry : public LLChildRegistry<ScrollContainerRegistry>
-{
- LLSINGLETON_EMPTY_CTOR(ScrollContainerRegistry);
-};
-
-class LLScrollContainer : public LLUICtrl
-{
-public:
- // Note: vertical comes before horizontal because vertical
- // scrollbars have priority for mouse and keyboard events.
-
- struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Optional<bool> is_opaque,
- reserve_scroll_corner,
- border_visible,
- hide_scrollbar,
- ignore_arrow_keys;
- Optional<F32> min_auto_scroll_rate,
- max_auto_scroll_rate;
- Optional<U32> max_auto_scroll_zone;
- Optional<LLUIColor> bg_color;
- Optional<LLScrollbar::callback_t> scroll_callback;
- Optional<S32> size;
-
- Params();
- };
-
- // my valid children are stored in this registry
- typedef ScrollContainerRegistry child_registry_t;
-
-protected:
- LLScrollContainer(const Params&);
- friend class LLUICtrlFactory;
-public:
- virtual ~LLScrollContainer( void );
-
- virtual void setValue(const LLSD& value) { mInnerRect.setValue(value); }
-
- void setBorderVisible( bool b );
-
- void scrollToShowRect( const LLRect& rect, const LLRect& constraint);
- void scrollToShowRect( const LLRect& rect) { scrollToShowRect(rect, LLRect(0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0)); }
-
- void setReserveScrollCorner( bool b ) { mReserveScrollCorner = b; }
- LLRect getVisibleContentRect();
- LLRect getContentWindowRect();
- virtual const LLRect getScrolledViewRect() const { return mScrolledView ? mScrolledView->getRect() : LLRect::null; }
- void pageUp(S32 overlap = 0);
- void pageDown(S32 overlap = 0);
- void goToTop();
- void goToBottom();
- bool isAtTop() const { return mScrollbar[VERTICAL]->isAtBeginning(); }
- bool isAtBottom() const { return mScrollbar[VERTICAL]->isAtEnd(); }
- S32 getDocPosVertical() const { return mScrollbar[VERTICAL]->getDocPos(); }
- S32 getDocPosHorizontal() const { return mScrollbar[HORIZONTAL]->getDocPos(); }
- S32 getBorderWidth() const;
-
- // 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 handleScrollHWheel( S32 x, S32 y, S32 clicks );
- virtual bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
- EDragAndDropType cargo_type,
- void* cargo_data,
- EAcceptance* accept,
- std::string& tooltip_msg);
-
- virtual void draw();
- virtual bool addChild(LLView* view, S32 tab_group = 0);
-
- bool canAutoScroll(S32 x, S32 y);
- 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 );
- virtual void scrollVertical( S32 new_pos );
- void updateScroll();
- bool autoScroll(S32 x, S32 y, bool do_scroll);
- void calcVisibleSize( S32 *visible_width, S32 *visible_height, bool* show_h_scrollbar, bool* show_v_scrollbar ) const;
-
- LLScrollbar* mScrollbar[ORIENTATION_COUNT];
- S32 mSize;
- bool mIsOpaque;
- LLUIColor mBackgroundColor;
- LLRect mInnerRect;
- LLViewBorder* mBorder;
- bool mReserveScrollCorner;
- bool mAutoScrolling;
- F32 mAutoScrollRate;
- F32 mMinAutoScrollRate;
- F32 mMaxAutoScrollRate;
- U32 mMaxAutoScrollZone;
- bool mHideScrollbar;
- bool mIgnoreArrowKeys;
-};
-
-
-#endif // LL_LLSCROLLCONTAINER_H
+/**
+ * @file llscrollcontainer.h
+ * @brief LLScrollContainer class header file.
+ *
+ * $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_LLSCROLLCONTAINER_H
+#define LL_LLSCROLLCONTAINER_H
+
+#include "lluictrl.h"
+#ifndef LL_V4COLOR_H
+#include "v4color.h"
+#endif
+#include "llcoord.h"
+#include "llscrollbar.h"
+
+
+class LLViewBorder;
+class LLUICtrlFactory;
+
+/*****************************************************************************
+ *
+ * A decorator view class meant to encapsulate a clipped region which is
+ * scrollable. It automatically takes care of pixel perfect scrolling
+ * and cliipping, as well as turning the scrollbars on or off based on
+ * the width and height of the view you're scrolling.
+ *
+ *****************************************************************************/
+
+struct ScrollContainerRegistry : public LLChildRegistry<ScrollContainerRegistry>
+{
+ LLSINGLETON_EMPTY_CTOR(ScrollContainerRegistry);
+};
+
+class LLScrollContainer : public LLUICtrl
+{
+public:
+ // Note: vertical comes before horizontal because vertical
+ // scrollbars have priority for mouse and keyboard events.
+
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<bool> is_opaque,
+ reserve_scroll_corner,
+ border_visible,
+ hide_scrollbar,
+ ignore_arrow_keys;
+ Optional<F32> min_auto_scroll_rate,
+ max_auto_scroll_rate;
+ Optional<U32> max_auto_scroll_zone;
+ Optional<LLUIColor> bg_color;
+ Optional<LLScrollbar::callback_t> scroll_callback;
+ Optional<S32> size;
+
+ Params();
+ };
+
+ // my valid children are stored in this registry
+ typedef ScrollContainerRegistry child_registry_t;
+
+protected:
+ LLScrollContainer(const Params&);
+ friend class LLUICtrlFactory;
+public:
+ virtual ~LLScrollContainer( void );
+
+ virtual void setValue(const LLSD& value) { mInnerRect.setValue(value); }
+
+ void setBorderVisible( bool b );
+
+ void scrollToShowRect( const LLRect& rect, const LLRect& constraint);
+ void scrollToShowRect( const LLRect& rect) { scrollToShowRect(rect, LLRect(0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0)); }
+
+ void setReserveScrollCorner( bool b ) { mReserveScrollCorner = b; }
+ LLRect getVisibleContentRect();
+ LLRect getContentWindowRect();
+ virtual const LLRect getScrolledViewRect() const { return mScrolledView ? mScrolledView->getRect() : LLRect::null; }
+ void pageUp(S32 overlap = 0);
+ void pageDown(S32 overlap = 0);
+ void goToTop();
+ void goToBottom();
+ bool isAtTop() const { return mScrollbar[VERTICAL]->isAtBeginning(); }
+ bool isAtBottom() const { return mScrollbar[VERTICAL]->isAtEnd(); }
+ S32 getDocPosVertical() const { return mScrollbar[VERTICAL]->getDocPos(); }
+ S32 getDocPosHorizontal() const { return mScrollbar[HORIZONTAL]->getDocPos(); }
+ S32 getBorderWidth() const;
+
+ // 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 handleScrollHWheel( S32 x, S32 y, S32 clicks );
+ virtual bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg);
+
+ virtual void draw();
+ virtual bool addChild(LLView* view, S32 tab_group = 0);
+
+ bool canAutoScroll(S32 x, S32 y);
+ 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 );
+ virtual void scrollVertical( S32 new_pos );
+ void updateScroll();
+ bool autoScroll(S32 x, S32 y, bool do_scroll);
+ void calcVisibleSize( S32 *visible_width, S32 *visible_height, bool* show_h_scrollbar, bool* show_v_scrollbar ) const;
+
+ LLScrollbar* mScrollbar[ORIENTATION_COUNT];
+ S32 mSize;
+ bool mIsOpaque;
+ LLUIColor mBackgroundColor;
+ LLRect mInnerRect;
+ LLViewBorder* mBorder;
+ bool mReserveScrollCorner;
+ bool mAutoScrolling;
+ F32 mAutoScrollRate;
+ F32 mMinAutoScrollRate;
+ F32 mMaxAutoScrollRate;
+ U32 mMaxAutoScrollZone;
+ bool mHideScrollbar;
+ bool mIgnoreArrowKeys;
+};
+
+
+#endif // LL_LLSCROLLCONTAINER_H
diff --git a/indra/llui/llscrollingpanellist.cpp b/indra/llui/llscrollingpanellist.cpp
index b282378fe1..5c6b528afc 100644
--- a/indra/llui/llscrollingpanellist.cpp
+++ b/indra/llui/llscrollingpanellist.cpp
@@ -1,252 +1,252 @@
-/**
- * @file llscrollingpanellist.cpp
- * @brief
- *
- * $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$
- */
-
-#include "linden_common.h"
-#include "llstl.h"
-
-#include "llscrollingpanellist.h"
-
-static LLDefaultChildRegistry::Register<LLScrollingPanelList> r("scrolling_panel_list");
-
-
-/////////////////////////////////////////////////////////////////////
-// LLScrollingPanelList
-
-// This could probably be integrated with LLScrollContainer -SJB
-
-LLScrollingPanelList::Params::Params()
- : is_horizontal("is_horizontal")
- , padding("padding")
- , spacing("spacing")
-{
-}
-
-LLScrollingPanelList::LLScrollingPanelList(const Params& p)
- : LLUICtrl(p)
- , mIsHorizontal(p.is_horizontal)
- , mPadding(p.padding.isProvided() ? p.padding : DEFAULT_PADDING)
- , mSpacing(p.spacing.isProvided() ? p.spacing : DEFAULT_SPACING)
-{
-}
-
-void LLScrollingPanelList::clearPanels()
-{
- deleteAllChildren();
- mPanelList.clear();
- rearrange();
-}
-
-S32 LLScrollingPanelList::addPanel(LLScrollingPanel* panel, bool back)
-{
- if (back)
- {
- addChild(panel);
- mPanelList.push_back(panel);
- }
- else
- {
- addChildInBack(panel);
- mPanelList.push_front(panel);
- }
-
- rearrange();
-
- return mIsHorizontal ? getRect().getWidth() : getRect().getHeight();
-}
-
-void LLScrollingPanelList::removePanel(LLScrollingPanel* panel)
-{
- U32 index = 0;
- LLScrollingPanelList::panel_list_t::const_iterator iter;
-
- if (!mPanelList.empty())
- {
- for (iter = mPanelList.begin(); iter != mPanelList.end(); ++iter, ++index)
- {
- if (*iter == panel)
- {
- break;
- }
- }
- if (iter != mPanelList.end())
- {
- removePanel(index);
- }
- }
-}
-
-void LLScrollingPanelList::removePanel( U32 panel_index )
-{
- if ( mPanelList.empty() || panel_index >= mPanelList.size() )
- {
- LL_WARNS() << "Panel index " << panel_index << " is out of range!" << LL_ENDL;
- return;
- }
- else
- {
- removeChild( mPanelList.at(panel_index) );
- mPanelList.erase( mPanelList.begin() + panel_index );
- }
-
- rearrange();
-}
-
-void LLScrollingPanelList::updatePanels(bool allow_modify)
-{
- for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
- iter != mPanelList.end(); ++iter)
- {
- LLScrollingPanel *childp = *iter;
- childp->updatePanel(allow_modify);
- }
-}
-
-void LLScrollingPanelList::rearrange()
-{
- // Resize this view
- S32 new_width, new_height;
- if (!mPanelList.empty())
- {
- new_width = new_height = mPadding * 2;
- for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
- iter != mPanelList.end(); ++iter)
- {
- LLScrollingPanel* childp = *iter;
- const LLRect& rect = childp->getRect();
- if (mIsHorizontal)
- {
- new_width += rect.getWidth() + mSpacing;
- new_height = llmax(new_height, rect.getHeight());
- }
- else
- {
- new_height += rect.getHeight() + mSpacing;
- new_width = llmax(new_width, rect.getWidth());
- }
- }
-
- if (mIsHorizontal)
- {
- new_width -= mSpacing;
- }
- else
- {
- new_height -= mSpacing;
- }
- }
- else
- {
- new_width = new_height = 1;
- }
-
- LLRect rc = getRect();
- if (mIsHorizontal || !followsRight())
- {
- rc.mRight = rc.mLeft + new_width;
- }
- if (!mIsHorizontal || !followsBottom())
- {
- rc.mBottom = rc.mTop - new_height;
- }
-
- if (rc.mRight != getRect().mRight || rc.mBottom != getRect().mBottom)
- {
- setRect(rc);
- notifySizeChanged();
- }
-
- // Reposition each of the child views
- S32 pos = mIsHorizontal ? mPadding : rc.getHeight() - mPadding;
- for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
- iter != mPanelList.end(); ++iter)
- {
- LLScrollingPanel* childp = *iter;
- const LLRect& rect = childp->getRect();
- if (mIsHorizontal)
- {
- childp->translate(pos - rect.mLeft, rc.getHeight() - mPadding - rect.mTop);
- pos += rect.getWidth() + mSpacing;
- }
- else
- {
- childp->translate(mPadding - rect.mLeft, pos - rect.mTop);
- pos -= rect.getHeight() + mSpacing;
- }
- }
-}
-
-void LLScrollingPanelList::updatePanelVisiblilty()
-{
- // Determine visibility of children.
-
- LLRect parent_screen_rect;
- getParent()->localPointToScreen(
- mPadding, mPadding,
- &parent_screen_rect.mLeft, &parent_screen_rect.mBottom );
- getParent()->localPointToScreen(
- getParent()->getRect().getWidth() - mPadding,
- getParent()->getRect().getHeight() - mPadding,
- &parent_screen_rect.mRight, &parent_screen_rect.mTop );
-
- for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
- iter != mPanelList.end(); ++iter)
- {
- LLScrollingPanel *childp = *iter;
- const LLRect& local_rect = childp->getRect();
- LLRect screen_rect;
- childp->localPointToScreen(
- 0, 0,
- &screen_rect.mLeft, &screen_rect.mBottom );
- childp->localPointToScreen(
- local_rect.getWidth(), local_rect.getHeight(),
- &screen_rect.mRight, &screen_rect.mTop );
-
- bool intersects =
- ( (screen_rect.mRight > parent_screen_rect.mLeft) && (screen_rect.mLeft < parent_screen_rect.mRight) ) &&
- ( (screen_rect.mTop > parent_screen_rect.mBottom) && (screen_rect.mBottom < parent_screen_rect.mTop) );
-
- childp->setVisible( intersects );
- }
-}
-
-
-void LLScrollingPanelList::draw()
-{
- updatePanelVisiblilty();
-
- LLUICtrl::draw();
-}
-
-void LLScrollingPanelList::notifySizeChanged()
-{
- LLSD info;
- info["action"] = "size_changes";
- info["height"] = getRect().getHeight();
- info["width"] = getRect().getWidth();
- notifyParent(info);
-}
-
-// EOF
+/**
+ * @file llscrollingpanellist.cpp
+ * @brief
+ *
+ * $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$
+ */
+
+#include "linden_common.h"
+#include "llstl.h"
+
+#include "llscrollingpanellist.h"
+
+static LLDefaultChildRegistry::Register<LLScrollingPanelList> r("scrolling_panel_list");
+
+
+/////////////////////////////////////////////////////////////////////
+// LLScrollingPanelList
+
+// This could probably be integrated with LLScrollContainer -SJB
+
+LLScrollingPanelList::Params::Params()
+ : is_horizontal("is_horizontal")
+ , padding("padding")
+ , spacing("spacing")
+{
+}
+
+LLScrollingPanelList::LLScrollingPanelList(const Params& p)
+ : LLUICtrl(p)
+ , mIsHorizontal(p.is_horizontal)
+ , mPadding(p.padding.isProvided() ? p.padding : DEFAULT_PADDING)
+ , mSpacing(p.spacing.isProvided() ? p.spacing : DEFAULT_SPACING)
+{
+}
+
+void LLScrollingPanelList::clearPanels()
+{
+ deleteAllChildren();
+ mPanelList.clear();
+ rearrange();
+}
+
+S32 LLScrollingPanelList::addPanel(LLScrollingPanel* panel, bool back)
+{
+ if (back)
+ {
+ addChild(panel);
+ mPanelList.push_back(panel);
+ }
+ else
+ {
+ addChildInBack(panel);
+ mPanelList.push_front(panel);
+ }
+
+ rearrange();
+
+ return mIsHorizontal ? getRect().getWidth() : getRect().getHeight();
+}
+
+void LLScrollingPanelList::removePanel(LLScrollingPanel* panel)
+{
+ U32 index = 0;
+ LLScrollingPanelList::panel_list_t::const_iterator iter;
+
+ if (!mPanelList.empty())
+ {
+ for (iter = mPanelList.begin(); iter != mPanelList.end(); ++iter, ++index)
+ {
+ if (*iter == panel)
+ {
+ break;
+ }
+ }
+ if (iter != mPanelList.end())
+ {
+ removePanel(index);
+ }
+ }
+}
+
+void LLScrollingPanelList::removePanel( U32 panel_index )
+{
+ if ( mPanelList.empty() || panel_index >= mPanelList.size() )
+ {
+ LL_WARNS() << "Panel index " << panel_index << " is out of range!" << LL_ENDL;
+ return;
+ }
+ else
+ {
+ removeChild( mPanelList.at(panel_index) );
+ mPanelList.erase( mPanelList.begin() + panel_index );
+ }
+
+ rearrange();
+}
+
+void LLScrollingPanelList::updatePanels(bool allow_modify)
+{
+ for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
+ iter != mPanelList.end(); ++iter)
+ {
+ LLScrollingPanel *childp = *iter;
+ childp->updatePanel(allow_modify);
+ }
+}
+
+void LLScrollingPanelList::rearrange()
+{
+ // Resize this view
+ S32 new_width, new_height;
+ if (!mPanelList.empty())
+ {
+ new_width = new_height = mPadding * 2;
+ for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
+ iter != mPanelList.end(); ++iter)
+ {
+ LLScrollingPanel* childp = *iter;
+ const LLRect& rect = childp->getRect();
+ if (mIsHorizontal)
+ {
+ new_width += rect.getWidth() + mSpacing;
+ new_height = llmax(new_height, rect.getHeight());
+ }
+ else
+ {
+ new_height += rect.getHeight() + mSpacing;
+ new_width = llmax(new_width, rect.getWidth());
+ }
+ }
+
+ if (mIsHorizontal)
+ {
+ new_width -= mSpacing;
+ }
+ else
+ {
+ new_height -= mSpacing;
+ }
+ }
+ else
+ {
+ new_width = new_height = 1;
+ }
+
+ LLRect rc = getRect();
+ if (mIsHorizontal || !followsRight())
+ {
+ rc.mRight = rc.mLeft + new_width;
+ }
+ if (!mIsHorizontal || !followsBottom())
+ {
+ rc.mBottom = rc.mTop - new_height;
+ }
+
+ if (rc.mRight != getRect().mRight || rc.mBottom != getRect().mBottom)
+ {
+ setRect(rc);
+ notifySizeChanged();
+ }
+
+ // Reposition each of the child views
+ S32 pos = mIsHorizontal ? mPadding : rc.getHeight() - mPadding;
+ for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
+ iter != mPanelList.end(); ++iter)
+ {
+ LLScrollingPanel* childp = *iter;
+ const LLRect& rect = childp->getRect();
+ if (mIsHorizontal)
+ {
+ childp->translate(pos - rect.mLeft, rc.getHeight() - mPadding - rect.mTop);
+ pos += rect.getWidth() + mSpacing;
+ }
+ else
+ {
+ childp->translate(mPadding - rect.mLeft, pos - rect.mTop);
+ pos -= rect.getHeight() + mSpacing;
+ }
+ }
+}
+
+void LLScrollingPanelList::updatePanelVisiblilty()
+{
+ // Determine visibility of children.
+
+ LLRect parent_screen_rect;
+ getParent()->localPointToScreen(
+ mPadding, mPadding,
+ &parent_screen_rect.mLeft, &parent_screen_rect.mBottom );
+ getParent()->localPointToScreen(
+ getParent()->getRect().getWidth() - mPadding,
+ getParent()->getRect().getHeight() - mPadding,
+ &parent_screen_rect.mRight, &parent_screen_rect.mTop );
+
+ for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
+ iter != mPanelList.end(); ++iter)
+ {
+ LLScrollingPanel *childp = *iter;
+ const LLRect& local_rect = childp->getRect();
+ LLRect screen_rect;
+ childp->localPointToScreen(
+ 0, 0,
+ &screen_rect.mLeft, &screen_rect.mBottom );
+ childp->localPointToScreen(
+ local_rect.getWidth(), local_rect.getHeight(),
+ &screen_rect.mRight, &screen_rect.mTop );
+
+ bool intersects =
+ ( (screen_rect.mRight > parent_screen_rect.mLeft) && (screen_rect.mLeft < parent_screen_rect.mRight) ) &&
+ ( (screen_rect.mTop > parent_screen_rect.mBottom) && (screen_rect.mBottom < parent_screen_rect.mTop) );
+
+ childp->setVisible( intersects );
+ }
+}
+
+
+void LLScrollingPanelList::draw()
+{
+ updatePanelVisiblilty();
+
+ LLUICtrl::draw();
+}
+
+void LLScrollingPanelList::notifySizeChanged()
+{
+ LLSD info;
+ info["action"] = "size_changes";
+ info["height"] = getRect().getHeight();
+ info["width"] = getRect().getWidth();
+ notifyParent(info);
+}
+
+// EOF
diff --git a/indra/llui/llscrollingpanellist.h b/indra/llui/llscrollingpanellist.h
index c812d23bc7..8e92e3d6aa 100644
--- a/indra/llui/llscrollingpanellist.h
+++ b/indra/llui/llscrollingpanellist.h
@@ -1,102 +1,102 @@
-/**
- * @file llscrollingpanellist.h
- *
- * $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_LLSCROLLINGPANELLIST_H
-#define LL_LLSCROLLINGPANELLIST_H
-
-#include <vector>
-
-#include "llui.h"
-#include "lluictrlfactory.h"
-#include "llview.h"
-#include "llpanel.h"
-
-/*
- * Pure virtual class represents a scrolling panel.
- */
-class LLScrollingPanel : public LLPanel
-{
-public:
- LLScrollingPanel(const LLPanel::Params& params) : LLPanel(params) {}
- virtual void updatePanel(bool allow_modify) = 0;
-};
-
-
-/*
- * A set of panels that are displayed in a sequence inside a scroll container.
- */
-class LLScrollingPanelList : public LLUICtrl
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Optional<bool> is_horizontal;
- Optional<S32> padding;
- Optional<S32> spacing;
-
- Params();
- };
-
- LLScrollingPanelList(const Params& p);
-
- static const S32 DEFAULT_SPACING = 6;
- static const S32 DEFAULT_PADDING = 2;
-
- typedef std::deque<LLScrollingPanel*> panel_list_t;
-
- virtual void setValue(const LLSD& value) {};
-
- virtual void draw();
-
- void clearPanels();
- S32 addPanel(LLScrollingPanel* panel, bool back = false);
- void removePanel(LLScrollingPanel* panel);
- void removePanel(U32 panel_index);
- void updatePanels(bool allow_modify);
- void rearrange();
-
- const panel_list_t& getPanelList() const { return mPanelList; }
- bool getIsHorizontal() const { return mIsHorizontal; }
- void setPadding(S32 padding) { mPadding = padding; rearrange(); }
- void setSpacing(S32 spacing) { mSpacing = spacing; rearrange(); }
- S32 getPadding() const { return mPadding; }
- S32 getSpacing() const { return mSpacing; }
-
-private:
- void updatePanelVisiblilty();
-
- /**
- * Notify parent about size change, makes sense when used inside accordion
- */
- void notifySizeChanged();
-
- bool mIsHorizontal;
- S32 mPadding;
- S32 mSpacing;
-
- panel_list_t mPanelList;
-};
-
-#endif //LL_LLSCROLLINGPANELLIST_H
+/**
+ * @file llscrollingpanellist.h
+ *
+ * $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_LLSCROLLINGPANELLIST_H
+#define LL_LLSCROLLINGPANELLIST_H
+
+#include <vector>
+
+#include "llui.h"
+#include "lluictrlfactory.h"
+#include "llview.h"
+#include "llpanel.h"
+
+/*
+ * Pure virtual class represents a scrolling panel.
+ */
+class LLScrollingPanel : public LLPanel
+{
+public:
+ LLScrollingPanel(const LLPanel::Params& params) : LLPanel(params) {}
+ virtual void updatePanel(bool allow_modify) = 0;
+};
+
+
+/*
+ * A set of panels that are displayed in a sequence inside a scroll container.
+ */
+class LLScrollingPanelList : public LLUICtrl
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<bool> is_horizontal;
+ Optional<S32> padding;
+ Optional<S32> spacing;
+
+ Params();
+ };
+
+ LLScrollingPanelList(const Params& p);
+
+ static const S32 DEFAULT_SPACING = 6;
+ static const S32 DEFAULT_PADDING = 2;
+
+ typedef std::deque<LLScrollingPanel*> panel_list_t;
+
+ virtual void setValue(const LLSD& value) {};
+
+ virtual void draw();
+
+ void clearPanels();
+ S32 addPanel(LLScrollingPanel* panel, bool back = false);
+ void removePanel(LLScrollingPanel* panel);
+ void removePanel(U32 panel_index);
+ void updatePanels(bool allow_modify);
+ void rearrange();
+
+ const panel_list_t& getPanelList() const { return mPanelList; }
+ bool getIsHorizontal() const { return mIsHorizontal; }
+ void setPadding(S32 padding) { mPadding = padding; rearrange(); }
+ void setSpacing(S32 spacing) { mSpacing = spacing; rearrange(); }
+ S32 getPadding() const { return mPadding; }
+ S32 getSpacing() const { return mSpacing; }
+
+private:
+ void updatePanelVisiblilty();
+
+ /**
+ * Notify parent about size change, makes sense when used inside accordion
+ */
+ void notifySizeChanged();
+
+ bool mIsHorizontal;
+ S32 mPadding;
+ S32 mSpacing;
+
+ panel_list_t mPanelList;
+};
+
+#endif //LL_LLSCROLLINGPANELLIST_H
diff --git a/indra/llui/llscrolllistcell.cpp b/indra/llui/llscrolllistcell.cpp
index 89435b1874..d58696a5c9 100644
--- a/indra/llui/llscrolllistcell.cpp
+++ b/indra/llui/llscrolllistcell.cpp
@@ -1,671 +1,671 @@
-/**
- * @file llscrolllistcell.cpp
- * @brief Scroll lists are composed of rows (items), each of which
- * contains columns (cells).
- *
- * $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$
- */
-
-#include "linden_common.h"
-
-#include "llscrolllistcell.h"
-
-#include "llcheckboxctrl.h"
-#include "llui.h" // LLUIImage
-#include "lluictrlfactory.h"
-
-//static
-LLScrollListCell* LLScrollListCell::create(const LLScrollListCell::Params& cell_p)
-{
- LLScrollListCell* cell = NULL;
-
- if (cell_p.type() == "icon")
- {
- cell = new LLScrollListIcon(cell_p);
- }
- else if (cell_p.type() == "checkbox")
- {
- cell = new LLScrollListCheck(cell_p);
- }
- else if (cell_p.type() == "date")
- {
- cell = new LLScrollListDate(cell_p);
- }
- else if (cell_p.type() == "icontext")
- {
- cell = new LLScrollListIconText(cell_p);
- }
- else if (cell_p.type() == "bar")
- {
- cell = new LLScrollListBar(cell_p);
- }
- else // default is "text"
- {
- cell = new LLScrollListText(cell_p);
- }
-
- if (cell_p.value.isProvided())
- {
- cell->setValue(cell_p.value);
- }
-
- return cell;
-}
-
-
-LLScrollListCell::LLScrollListCell(const LLScrollListCell::Params& p)
-: mWidth(p.width),
- mToolTip(p.tool_tip)
-{}
-
-// virtual
-const LLSD LLScrollListCell::getValue() const
-{
- return LLStringUtil::null;
-}
-
-
-// virtual
-const LLSD LLScrollListCell::getAltValue() const
-{
- return LLStringUtil::null;
-}
-
-
-//
-// LLScrollListIcon
-//
-LLScrollListIcon::LLScrollListIcon(const LLScrollListCell::Params& p)
-: LLScrollListCell(p),
- mIcon(LLUI::getUIImage(p.value().asString())),
- mColor(p.color),
- mAlignment(p.font_halign)
-{}
-
-LLScrollListIcon::~LLScrollListIcon()
-{
-}
-
-/*virtual*/
-S32 LLScrollListIcon::getHeight() const
-{ return mIcon ? mIcon->getHeight() : 0; }
-
-/*virtual*/
-const LLSD LLScrollListIcon::getValue() const
-{ return mIcon.isNull() ? LLStringUtil::null : mIcon->getName(); }
-
-void LLScrollListIcon::setValue(const LLSD& value)
-{
- if (value.isUUID())
- {
- // don't use default image specified by LLUUID::null, use no image in that case
- LLUUID image_id = value.asUUID();
- mIcon = image_id.notNull() ? LLUI::getUIImageByID(image_id) : LLUIImagePtr(NULL);
- }
- else
- {
- std::string value_string = value.asString();
- if (LLUUID::validate(value_string))
- {
- setValue(LLUUID(value_string));
- }
- else if (!value_string.empty())
- {
- mIcon = LLUI::getUIImage(value.asString());
- }
- else
- {
- mIcon = NULL;
- }
- }
-}
-
-
-void LLScrollListIcon::setColor(const LLColor4& color)
-{
- mColor = color;
-}
-
-S32 LLScrollListIcon::getWidth() const
-{
- // if no specified fix width, use width of icon
- if (LLScrollListCell::getWidth() == 0 && mIcon.notNull())
- {
- return mIcon->getWidth();
- }
- return LLScrollListCell::getWidth();
-}
-
-
-void LLScrollListIcon::draw(const LLColor4& color, const LLColor4& highlight_color) const
-{
- if (mIcon)
- {
- switch(mAlignment)
- {
- case LLFontGL::LEFT:
- mIcon->draw(0, 0, mColor);
- break;
- case LLFontGL::RIGHT:
- mIcon->draw(getWidth() - mIcon->getWidth(), 0, mColor);
- break;
- case LLFontGL::HCENTER:
- mIcon->draw((getWidth() - mIcon->getWidth()) / 2, 0, mColor);
- break;
- default:
- break;
- }
- }
-}
-
-//
-// LLScrollListBar
-//
-LLScrollListBar::LLScrollListBar(const LLScrollListCell::Params& p)
- : LLScrollListCell(p),
- mRatio(0),
- mColor(p.color),
- mBottom(1),
- mLeftPad(1),
- mRightPad(1)
-{}
-
-LLScrollListBar::~LLScrollListBar()
-{
-}
-
-/*virtual*/
-S32 LLScrollListBar::getHeight() const
-{
- return LLScrollListCell::getHeight();
-}
-
-/*virtual*/
-const LLSD LLScrollListBar::getValue() const
-{
- return LLStringUtil::null;
-}
-
-void LLScrollListBar::setValue(const LLSD& value)
-{
- if (value.has("ratio"))
- {
- mRatio = value["ratio"].asReal();
- }
- if (value.has("bottom"))
- {
- mBottom = value["bottom"].asInteger();
- }
- if (value.has("left_pad"))
- {
- mLeftPad = value["left_pad"].asInteger();
- }
- if (value.has("right_pad"))
- {
- mRightPad = value["right_pad"].asInteger();
- }
-}
-
-void LLScrollListBar::setColor(const LLColor4& color)
-{
- mColor = color;
-}
-
-S32 LLScrollListBar::getWidth() const
-{
- return LLScrollListCell::getWidth();
-}
-
-
-void LLScrollListBar::draw(const LLColor4& color, const LLColor4& highlight_color) const
-{
- S32 bar_width = getWidth() - mLeftPad - mRightPad;
- S32 left = bar_width - bar_width * mRatio;
- left = llclamp(left, mLeftPad, getWidth() - mRightPad - 1);
-
- gl_rect_2d(left, mBottom, getWidth() - mRightPad, mBottom - 1, mColor);
-}
-
-//
-// LLScrollListText
-//
-U32 LLScrollListText::sCount = 0;
-
-LLScrollListText::LLScrollListText(const LLScrollListCell::Params& p)
-: LLScrollListCell(p),
- mText(p.label.isProvided() ? p.label() : p.value().asString()),
- mAltText(p.alt_value().asString()),
- mFont(p.font),
- mColor(p.color),
- mUseColor(p.color.isProvided()),
- mFontAlignment(p.font_halign),
- mVisible(p.visible),
- mHighlightCount( 0 ),
- mHighlightOffset( 0 )
-{
- sCount++;
-
- mTextWidth = getWidth();
-
- // initialize rounded rect image
- if (!mRoundedRectImage)
- {
- mRoundedRectImage = LLUI::getUIImage("Rounded_Square");
- }
-}
-
-//virtual
-void LLScrollListText::highlightText(S32 offset, S32 num_chars)
-{
- mHighlightOffset = offset;
- mHighlightCount = llmax(0, num_chars);
-}
-
-//virtual
-bool LLScrollListText::isText() const
-{
- return true;
-}
-
-// virtual
-const std::string &LLScrollListText::getToolTip() const
-{
- // If base class has a tooltip, return that
- if (! LLScrollListCell::getToolTip().empty())
- return LLScrollListCell::getToolTip();
-
- // ...otherwise, return the value itself as the tooltip
- return mText.getString();
-}
-
-// virtual
-bool LLScrollListText::needsToolTip() const
-{
- // If base class has a tooltip, return that
- if (LLScrollListCell::needsToolTip())
- return LLScrollListCell::needsToolTip();
-
- // ...otherwise, show tooltips for truncated text
- return mFont->getWidth(mText.getString()) > getWidth();
-}
-
-//virtual
-bool LLScrollListText::getVisible() const
-{
- return mVisible;
-}
-
-//virtual
-S32 LLScrollListText::getHeight() const
-{
- return mFont->getLineHeight();
-}
-
-
-LLScrollListText::~LLScrollListText()
-{
- sCount--;
-}
-
-S32 LLScrollListText::getContentWidth() const
-{
- return mFont->getWidth(mText.getString());
-}
-
-
-void LLScrollListText::setColor(const LLColor4& color)
-{
- mColor = color;
- mUseColor = true;
-}
-
-void LLScrollListText::setText(const LLStringExplicit& text)
-{
- mText = text;
-}
-
-void LLScrollListText::setFontStyle(const U8 font_style)
-{
- LLFontDescriptor new_desc(mFont->getFontDesc());
- new_desc.setStyle(font_style);
- mFont = LLFontGL::getFont(new_desc);
-}
-
-//virtual
-void LLScrollListText::setValue(const LLSD& text)
-{
- setText(text.asString());
-}
-
-//virtual
-void LLScrollListText::setAltValue(const LLSD& text)
-{
- mAltText = text.asString();
-}
-
-//virtual
-const LLSD LLScrollListText::getValue() const
-{
- return LLSD(mText.getString());
-}
-
-//virtual
-const LLSD LLScrollListText::getAltValue() const
-{
- return LLSD(mAltText.getString());
-}
-
-
-void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_color) const
-{
- LLColor4 display_color;
- if (mUseColor)
- {
- display_color = mColor;
- }
- else
- {
- display_color = color;
- }
-
- if (mHighlightCount > 0)
- {
- // Highlight text
- S32 left = 0;
- switch(mFontAlignment)
- {
- case LLFontGL::LEFT:
- left = mFont->getWidth(mText.getString(), 1, mHighlightOffset);
- break;
- case LLFontGL::RIGHT:
- left = getWidth() - mFont->getWidth(mText.getString(), mHighlightOffset, S32_MAX);
- break;
- case LLFontGL::HCENTER:
- left = (getWidth() - mFont->getWidth(mText.getString())) / 2;
- break;
- }
- LLRect highlight_rect(left - 2,
- mFont->getLineHeight() + 1,
- left + mFont->getWidth(mText.getString(), mHighlightOffset, mHighlightCount) + 1,
- 1);
- mRoundedRectImage->draw(highlight_rect, highlight_color);
- }
-
- // Try to draw the entire string
- F32 right_x;
- U32 string_chars = mText.length();
- F32 start_x = 0.f;
- switch(mFontAlignment)
- {
- case LLFontGL::LEFT:
- start_x = 1.f;
- break;
- case LLFontGL::RIGHT:
- start_x = (F32)getWidth();
- break;
- case LLFontGL::HCENTER:
- start_x = (F32)getWidth() * 0.5f;
- break;
- }
- mFont->render(mText.getWString(), 0,
- start_x, 0.f,
- display_color,
- mFontAlignment,
- LLFontGL::BOTTOM,
- 0,
- LLFontGL::NO_SHADOW,
- string_chars,
- getTextWidth(),
- &right_x,
- true);
-}
-
-//
-// LLScrollListCheck
-//
-LLScrollListCheck::LLScrollListCheck(const LLScrollListCell::Params& p)
-: LLScrollListCell(p)
-{
- LLCheckBoxCtrl::Params checkbox_p;
- checkbox_p.name("checkbox");
- checkbox_p.rect = LLRect(0, p.width, p.width, 0);
- checkbox_p.enabled(p.enabled);
- checkbox_p.initial_value(p.value());
-
- mCheckBox = LLUICtrlFactory::create<LLCheckBoxCtrl>(checkbox_p);
-
- LLRect rect(mCheckBox->getRect());
- if (p.width)
- {
- rect.mRight = rect.mLeft + p.width;
- mCheckBox->setRect(rect);
- setWidth(p.width);
- }
- else
- {
- setWidth(rect.getWidth()); //check_box->getWidth();
- }
-
- mCheckBox->setColor(p.color);
-}
-
-
-LLScrollListCheck::~LLScrollListCheck()
-{
- delete mCheckBox;
- mCheckBox = NULL;
-}
-
-void LLScrollListCheck::draw(const LLColor4& color, const LLColor4& highlight_color) const
-{
- mCheckBox->draw();
-}
-
-bool LLScrollListCheck::handleClick()
-{
- if (mCheckBox->getEnabled())
- {
- mCheckBox->toggle();
- }
- // don't change selection when clicking on embedded checkbox
- return true;
-}
-
-/*virtual*/
-const LLSD LLScrollListCheck::getValue() const
-{
- return mCheckBox->getValue();
-}
-
-/*virtual*/
-void LLScrollListCheck::setValue(const LLSD& value)
-{
- mCheckBox->setValue(value);
-}
-
-/*virtual*/
-void LLScrollListCheck::onCommit()
-{
- mCheckBox->onCommit();
-}
-
-/*virtual*/
-void LLScrollListCheck::setEnabled(bool enable)
-{
- mCheckBox->setEnabled(enable);
-}
-
-//
-// LLScrollListDate
-//
-
-LLScrollListDate::LLScrollListDate( const LLScrollListCell::Params& p)
-: LLScrollListText(p),
- mDate(p.value().asDate())
-{}
-
-void LLScrollListDate::setValue(const LLSD& value)
-{
- mDate = value.asDate();
- LLScrollListText::setValue(mDate.asRFC1123());
-}
-
-const LLSD LLScrollListDate::getValue() const
-{
- return mDate;
-}
-
-//
-// LLScrollListIconText
-//
-LLScrollListIconText::LLScrollListIconText(const LLScrollListCell::Params& p)
- : LLScrollListText(p),
- mIcon(p.value().isUUID() ? LLUI::getUIImageByID(p.value().asUUID()) : LLUI::getUIImage(p.value().asString())),
- mPad(4)
-{
- mTextWidth = getWidth() - mPad /*padding*/ - mFont->getLineHeight();
-}
-
-LLScrollListIconText::~LLScrollListIconText()
-{
-}
-
-const LLSD LLScrollListIconText::getValue() const
-{
- if (mIcon.isNull())
- {
- return LLStringUtil::null;
- }
- return mIcon->getName();
-}
-
-void LLScrollListIconText::setValue(const LLSD& value)
-{
- if (value.isUUID())
- {
- // don't use default image specified by LLUUID::null, use no image in that case
- LLUUID image_id = value.asUUID();
- mIcon = image_id.notNull() ? LLUI::getUIImageByID(image_id) : LLUIImagePtr(NULL);
- }
- else
- {
- std::string value_string = value.asString();
- if (LLUUID::validate(value_string))
- {
- setValue(LLUUID(value_string));
- }
- else if (!value_string.empty())
- {
- mIcon = LLUI::getUIImage(value.asString());
- }
- else
- {
- mIcon = NULL;
- }
- }
-}
-
-void LLScrollListIconText::setWidth(S32 width)
-{
- LLScrollListCell::setWidth(width);
- // Assume that iamge height and width is identical to font height and width
- mTextWidth = width - mPad /*padding*/ - mFont->getLineHeight();
-}
-
-
-void LLScrollListIconText::draw(const LLColor4& color, const LLColor4& highlight_color) const
-{
- LLColor4 display_color;
- if (mUseColor)
- {
- display_color = mColor;
- }
- else
- {
- display_color = color;
- }
-
- S32 icon_height = mFont->getLineHeight();
- S32 icon_space = mIcon ? (icon_height + mPad) : 0;
-
- if (mHighlightCount > 0)
- {
- S32 left = 0;
- switch (mFontAlignment)
- {
- case LLFontGL::LEFT:
- left = mFont->getWidth(mText.getString(), icon_space + 1, mHighlightOffset);
- break;
- case LLFontGL::RIGHT:
- left = getWidth() - mFont->getWidth(mText.getString(), mHighlightOffset, S32_MAX) - icon_space;
- break;
- case LLFontGL::HCENTER:
- left = (getWidth() - mFont->getWidth(mText.getString()) - icon_space) / 2;
- break;
- }
- LLRect highlight_rect(left - 2,
- mFont->getLineHeight() + 1,
- left + mFont->getWidth(mText.getString(), mHighlightOffset, mHighlightCount) + 1,
- 1);
- mRoundedRectImage->draw(highlight_rect, highlight_color);
- }
-
- // Try to draw the entire string
- F32 right_x;
- U32 string_chars = mText.length();
- F32 start_text_x = 0.f;
- S32 start_icon_x = 0;
- switch (mFontAlignment)
- {
- case LLFontGL::LEFT:
- start_text_x = icon_space + 1;
- start_icon_x = 1;
- break;
- case LLFontGL::RIGHT:
- start_text_x = (F32)getWidth();
- start_icon_x = getWidth() - mFont->getWidth(mText.getString()) - icon_space;
- break;
- case LLFontGL::HCENTER:
- F32 center = (F32)getWidth()* 0.5f;
- start_text_x = center + ((F32)icon_space * 0.5f);
- start_icon_x = center - (((F32)icon_space + mFont->getWidth(mText.getString())) * 0.5f);
- break;
- }
- mFont->render(mText.getWString(), 0,
- start_text_x, 0.f,
- display_color,
- mFontAlignment,
- LLFontGL::BOTTOM,
- 0,
- LLFontGL::NO_SHADOW,
- string_chars,
- getTextWidth(),
- &right_x,
- true);
-
- if (mIcon)
- {
- mIcon->draw(start_icon_x, 0, icon_height, icon_height, mColor);
- }
-}
-
-
+/**
+ * @file llscrolllistcell.cpp
+ * @brief Scroll lists are composed of rows (items), each of which
+ * contains columns (cells).
+ *
+ * $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$
+ */
+
+#include "linden_common.h"
+
+#include "llscrolllistcell.h"
+
+#include "llcheckboxctrl.h"
+#include "llui.h" // LLUIImage
+#include "lluictrlfactory.h"
+
+//static
+LLScrollListCell* LLScrollListCell::create(const LLScrollListCell::Params& cell_p)
+{
+ LLScrollListCell* cell = NULL;
+
+ if (cell_p.type() == "icon")
+ {
+ cell = new LLScrollListIcon(cell_p);
+ }
+ else if (cell_p.type() == "checkbox")
+ {
+ cell = new LLScrollListCheck(cell_p);
+ }
+ else if (cell_p.type() == "date")
+ {
+ cell = new LLScrollListDate(cell_p);
+ }
+ else if (cell_p.type() == "icontext")
+ {
+ cell = new LLScrollListIconText(cell_p);
+ }
+ else if (cell_p.type() == "bar")
+ {
+ cell = new LLScrollListBar(cell_p);
+ }
+ else // default is "text"
+ {
+ cell = new LLScrollListText(cell_p);
+ }
+
+ if (cell_p.value.isProvided())
+ {
+ cell->setValue(cell_p.value);
+ }
+
+ return cell;
+}
+
+
+LLScrollListCell::LLScrollListCell(const LLScrollListCell::Params& p)
+: mWidth(p.width),
+ mToolTip(p.tool_tip)
+{}
+
+// virtual
+const LLSD LLScrollListCell::getValue() const
+{
+ return LLStringUtil::null;
+}
+
+
+// virtual
+const LLSD LLScrollListCell::getAltValue() const
+{
+ return LLStringUtil::null;
+}
+
+
+//
+// LLScrollListIcon
+//
+LLScrollListIcon::LLScrollListIcon(const LLScrollListCell::Params& p)
+: LLScrollListCell(p),
+ mIcon(LLUI::getUIImage(p.value().asString())),
+ mColor(p.color),
+ mAlignment(p.font_halign)
+{}
+
+LLScrollListIcon::~LLScrollListIcon()
+{
+}
+
+/*virtual*/
+S32 LLScrollListIcon::getHeight() const
+{ return mIcon ? mIcon->getHeight() : 0; }
+
+/*virtual*/
+const LLSD LLScrollListIcon::getValue() const
+{ return mIcon.isNull() ? LLStringUtil::null : mIcon->getName(); }
+
+void LLScrollListIcon::setValue(const LLSD& value)
+{
+ if (value.isUUID())
+ {
+ // don't use default image specified by LLUUID::null, use no image in that case
+ LLUUID image_id = value.asUUID();
+ mIcon = image_id.notNull() ? LLUI::getUIImageByID(image_id) : LLUIImagePtr(NULL);
+ }
+ else
+ {
+ std::string value_string = value.asString();
+ if (LLUUID::validate(value_string))
+ {
+ setValue(LLUUID(value_string));
+ }
+ else if (!value_string.empty())
+ {
+ mIcon = LLUI::getUIImage(value.asString());
+ }
+ else
+ {
+ mIcon = NULL;
+ }
+ }
+}
+
+
+void LLScrollListIcon::setColor(const LLColor4& color)
+{
+ mColor = color;
+}
+
+S32 LLScrollListIcon::getWidth() const
+{
+ // if no specified fix width, use width of icon
+ if (LLScrollListCell::getWidth() == 0 && mIcon.notNull())
+ {
+ return mIcon->getWidth();
+ }
+ return LLScrollListCell::getWidth();
+}
+
+
+void LLScrollListIcon::draw(const LLColor4& color, const LLColor4& highlight_color) const
+{
+ if (mIcon)
+ {
+ switch(mAlignment)
+ {
+ case LLFontGL::LEFT:
+ mIcon->draw(0, 0, mColor);
+ break;
+ case LLFontGL::RIGHT:
+ mIcon->draw(getWidth() - mIcon->getWidth(), 0, mColor);
+ break;
+ case LLFontGL::HCENTER:
+ mIcon->draw((getWidth() - mIcon->getWidth()) / 2, 0, mColor);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+//
+// LLScrollListBar
+//
+LLScrollListBar::LLScrollListBar(const LLScrollListCell::Params& p)
+ : LLScrollListCell(p),
+ mRatio(0),
+ mColor(p.color),
+ mBottom(1),
+ mLeftPad(1),
+ mRightPad(1)
+{}
+
+LLScrollListBar::~LLScrollListBar()
+{
+}
+
+/*virtual*/
+S32 LLScrollListBar::getHeight() const
+{
+ return LLScrollListCell::getHeight();
+}
+
+/*virtual*/
+const LLSD LLScrollListBar::getValue() const
+{
+ return LLStringUtil::null;
+}
+
+void LLScrollListBar::setValue(const LLSD& value)
+{
+ if (value.has("ratio"))
+ {
+ mRatio = value["ratio"].asReal();
+ }
+ if (value.has("bottom"))
+ {
+ mBottom = value["bottom"].asInteger();
+ }
+ if (value.has("left_pad"))
+ {
+ mLeftPad = value["left_pad"].asInteger();
+ }
+ if (value.has("right_pad"))
+ {
+ mRightPad = value["right_pad"].asInteger();
+ }
+}
+
+void LLScrollListBar::setColor(const LLColor4& color)
+{
+ mColor = color;
+}
+
+S32 LLScrollListBar::getWidth() const
+{
+ return LLScrollListCell::getWidth();
+}
+
+
+void LLScrollListBar::draw(const LLColor4& color, const LLColor4& highlight_color) const
+{
+ S32 bar_width = getWidth() - mLeftPad - mRightPad;
+ S32 left = bar_width - bar_width * mRatio;
+ left = llclamp(left, mLeftPad, getWidth() - mRightPad - 1);
+
+ gl_rect_2d(left, mBottom, getWidth() - mRightPad, mBottom - 1, mColor);
+}
+
+//
+// LLScrollListText
+//
+U32 LLScrollListText::sCount = 0;
+
+LLScrollListText::LLScrollListText(const LLScrollListCell::Params& p)
+: LLScrollListCell(p),
+ mText(p.label.isProvided() ? p.label() : p.value().asString()),
+ mAltText(p.alt_value().asString()),
+ mFont(p.font),
+ mColor(p.color),
+ mUseColor(p.color.isProvided()),
+ mFontAlignment(p.font_halign),
+ mVisible(p.visible),
+ mHighlightCount( 0 ),
+ mHighlightOffset( 0 )
+{
+ sCount++;
+
+ mTextWidth = getWidth();
+
+ // initialize rounded rect image
+ if (!mRoundedRectImage)
+ {
+ mRoundedRectImage = LLUI::getUIImage("Rounded_Square");
+ }
+}
+
+//virtual
+void LLScrollListText::highlightText(S32 offset, S32 num_chars)
+{
+ mHighlightOffset = offset;
+ mHighlightCount = llmax(0, num_chars);
+}
+
+//virtual
+bool LLScrollListText::isText() const
+{
+ return true;
+}
+
+// virtual
+const std::string &LLScrollListText::getToolTip() const
+{
+ // If base class has a tooltip, return that
+ if (! LLScrollListCell::getToolTip().empty())
+ return LLScrollListCell::getToolTip();
+
+ // ...otherwise, return the value itself as the tooltip
+ return mText.getString();
+}
+
+// virtual
+bool LLScrollListText::needsToolTip() const
+{
+ // If base class has a tooltip, return that
+ if (LLScrollListCell::needsToolTip())
+ return LLScrollListCell::needsToolTip();
+
+ // ...otherwise, show tooltips for truncated text
+ return mFont->getWidth(mText.getString()) > getWidth();
+}
+
+//virtual
+bool LLScrollListText::getVisible() const
+{
+ return mVisible;
+}
+
+//virtual
+S32 LLScrollListText::getHeight() const
+{
+ return mFont->getLineHeight();
+}
+
+
+LLScrollListText::~LLScrollListText()
+{
+ sCount--;
+}
+
+S32 LLScrollListText::getContentWidth() const
+{
+ return mFont->getWidth(mText.getString());
+}
+
+
+void LLScrollListText::setColor(const LLColor4& color)
+{
+ mColor = color;
+ mUseColor = true;
+}
+
+void LLScrollListText::setText(const LLStringExplicit& text)
+{
+ mText = text;
+}
+
+void LLScrollListText::setFontStyle(const U8 font_style)
+{
+ LLFontDescriptor new_desc(mFont->getFontDesc());
+ new_desc.setStyle(font_style);
+ mFont = LLFontGL::getFont(new_desc);
+}
+
+//virtual
+void LLScrollListText::setValue(const LLSD& text)
+{
+ setText(text.asString());
+}
+
+//virtual
+void LLScrollListText::setAltValue(const LLSD& text)
+{
+ mAltText = text.asString();
+}
+
+//virtual
+const LLSD LLScrollListText::getValue() const
+{
+ return LLSD(mText.getString());
+}
+
+//virtual
+const LLSD LLScrollListText::getAltValue() const
+{
+ return LLSD(mAltText.getString());
+}
+
+
+void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_color) const
+{
+ LLColor4 display_color;
+ if (mUseColor)
+ {
+ display_color = mColor;
+ }
+ else
+ {
+ display_color = color;
+ }
+
+ if (mHighlightCount > 0)
+ {
+ // Highlight text
+ S32 left = 0;
+ switch(mFontAlignment)
+ {
+ case LLFontGL::LEFT:
+ left = mFont->getWidth(mText.getString(), 1, mHighlightOffset);
+ break;
+ case LLFontGL::RIGHT:
+ left = getWidth() - mFont->getWidth(mText.getString(), mHighlightOffset, S32_MAX);
+ break;
+ case LLFontGL::HCENTER:
+ left = (getWidth() - mFont->getWidth(mText.getString())) / 2;
+ break;
+ }
+ LLRect highlight_rect(left - 2,
+ mFont->getLineHeight() + 1,
+ left + mFont->getWidth(mText.getString(), mHighlightOffset, mHighlightCount) + 1,
+ 1);
+ mRoundedRectImage->draw(highlight_rect, highlight_color);
+ }
+
+ // Try to draw the entire string
+ F32 right_x;
+ U32 string_chars = mText.length();
+ F32 start_x = 0.f;
+ switch(mFontAlignment)
+ {
+ case LLFontGL::LEFT:
+ start_x = 1.f;
+ break;
+ case LLFontGL::RIGHT:
+ start_x = (F32)getWidth();
+ break;
+ case LLFontGL::HCENTER:
+ start_x = (F32)getWidth() * 0.5f;
+ break;
+ }
+ mFont->render(mText.getWString(), 0,
+ start_x, 0.f,
+ display_color,
+ mFontAlignment,
+ LLFontGL::BOTTOM,
+ 0,
+ LLFontGL::NO_SHADOW,
+ string_chars,
+ getTextWidth(),
+ &right_x,
+ true);
+}
+
+//
+// LLScrollListCheck
+//
+LLScrollListCheck::LLScrollListCheck(const LLScrollListCell::Params& p)
+: LLScrollListCell(p)
+{
+ LLCheckBoxCtrl::Params checkbox_p;
+ checkbox_p.name("checkbox");
+ checkbox_p.rect = LLRect(0, p.width, p.width, 0);
+ checkbox_p.enabled(p.enabled);
+ checkbox_p.initial_value(p.value());
+
+ mCheckBox = LLUICtrlFactory::create<LLCheckBoxCtrl>(checkbox_p);
+
+ LLRect rect(mCheckBox->getRect());
+ if (p.width)
+ {
+ rect.mRight = rect.mLeft + p.width;
+ mCheckBox->setRect(rect);
+ setWidth(p.width);
+ }
+ else
+ {
+ setWidth(rect.getWidth()); //check_box->getWidth();
+ }
+
+ mCheckBox->setColor(p.color);
+}
+
+
+LLScrollListCheck::~LLScrollListCheck()
+{
+ delete mCheckBox;
+ mCheckBox = NULL;
+}
+
+void LLScrollListCheck::draw(const LLColor4& color, const LLColor4& highlight_color) const
+{
+ mCheckBox->draw();
+}
+
+bool LLScrollListCheck::handleClick()
+{
+ if (mCheckBox->getEnabled())
+ {
+ mCheckBox->toggle();
+ }
+ // don't change selection when clicking on embedded checkbox
+ return true;
+}
+
+/*virtual*/
+const LLSD LLScrollListCheck::getValue() const
+{
+ return mCheckBox->getValue();
+}
+
+/*virtual*/
+void LLScrollListCheck::setValue(const LLSD& value)
+{
+ mCheckBox->setValue(value);
+}
+
+/*virtual*/
+void LLScrollListCheck::onCommit()
+{
+ mCheckBox->onCommit();
+}
+
+/*virtual*/
+void LLScrollListCheck::setEnabled(bool enable)
+{
+ mCheckBox->setEnabled(enable);
+}
+
+//
+// LLScrollListDate
+//
+
+LLScrollListDate::LLScrollListDate( const LLScrollListCell::Params& p)
+: LLScrollListText(p),
+ mDate(p.value().asDate())
+{}
+
+void LLScrollListDate::setValue(const LLSD& value)
+{
+ mDate = value.asDate();
+ LLScrollListText::setValue(mDate.asRFC1123());
+}
+
+const LLSD LLScrollListDate::getValue() const
+{
+ return mDate;
+}
+
+//
+// LLScrollListIconText
+//
+LLScrollListIconText::LLScrollListIconText(const LLScrollListCell::Params& p)
+ : LLScrollListText(p),
+ mIcon(p.value().isUUID() ? LLUI::getUIImageByID(p.value().asUUID()) : LLUI::getUIImage(p.value().asString())),
+ mPad(4)
+{
+ mTextWidth = getWidth() - mPad /*padding*/ - mFont->getLineHeight();
+}
+
+LLScrollListIconText::~LLScrollListIconText()
+{
+}
+
+const LLSD LLScrollListIconText::getValue() const
+{
+ if (mIcon.isNull())
+ {
+ return LLStringUtil::null;
+ }
+ return mIcon->getName();
+}
+
+void LLScrollListIconText::setValue(const LLSD& value)
+{
+ if (value.isUUID())
+ {
+ // don't use default image specified by LLUUID::null, use no image in that case
+ LLUUID image_id = value.asUUID();
+ mIcon = image_id.notNull() ? LLUI::getUIImageByID(image_id) : LLUIImagePtr(NULL);
+ }
+ else
+ {
+ std::string value_string = value.asString();
+ if (LLUUID::validate(value_string))
+ {
+ setValue(LLUUID(value_string));
+ }
+ else if (!value_string.empty())
+ {
+ mIcon = LLUI::getUIImage(value.asString());
+ }
+ else
+ {
+ mIcon = NULL;
+ }
+ }
+}
+
+void LLScrollListIconText::setWidth(S32 width)
+{
+ LLScrollListCell::setWidth(width);
+ // Assume that iamge height and width is identical to font height and width
+ mTextWidth = width - mPad /*padding*/ - mFont->getLineHeight();
+}
+
+
+void LLScrollListIconText::draw(const LLColor4& color, const LLColor4& highlight_color) const
+{
+ LLColor4 display_color;
+ if (mUseColor)
+ {
+ display_color = mColor;
+ }
+ else
+ {
+ display_color = color;
+ }
+
+ S32 icon_height = mFont->getLineHeight();
+ S32 icon_space = mIcon ? (icon_height + mPad) : 0;
+
+ if (mHighlightCount > 0)
+ {
+ S32 left = 0;
+ switch (mFontAlignment)
+ {
+ case LLFontGL::LEFT:
+ left = mFont->getWidth(mText.getString(), icon_space + 1, mHighlightOffset);
+ break;
+ case LLFontGL::RIGHT:
+ left = getWidth() - mFont->getWidth(mText.getString(), mHighlightOffset, S32_MAX) - icon_space;
+ break;
+ case LLFontGL::HCENTER:
+ left = (getWidth() - mFont->getWidth(mText.getString()) - icon_space) / 2;
+ break;
+ }
+ LLRect highlight_rect(left - 2,
+ mFont->getLineHeight() + 1,
+ left + mFont->getWidth(mText.getString(), mHighlightOffset, mHighlightCount) + 1,
+ 1);
+ mRoundedRectImage->draw(highlight_rect, highlight_color);
+ }
+
+ // Try to draw the entire string
+ F32 right_x;
+ U32 string_chars = mText.length();
+ F32 start_text_x = 0.f;
+ S32 start_icon_x = 0;
+ switch (mFontAlignment)
+ {
+ case LLFontGL::LEFT:
+ start_text_x = icon_space + 1;
+ start_icon_x = 1;
+ break;
+ case LLFontGL::RIGHT:
+ start_text_x = (F32)getWidth();
+ start_icon_x = getWidth() - mFont->getWidth(mText.getString()) - icon_space;
+ break;
+ case LLFontGL::HCENTER:
+ F32 center = (F32)getWidth()* 0.5f;
+ start_text_x = center + ((F32)icon_space * 0.5f);
+ start_icon_x = center - (((F32)icon_space + mFont->getWidth(mText.getString())) * 0.5f);
+ break;
+ }
+ mFont->render(mText.getWString(), 0,
+ start_text_x, 0.f,
+ display_color,
+ mFontAlignment,
+ LLFontGL::BOTTOM,
+ 0,
+ LLFontGL::NO_SHADOW,
+ string_chars,
+ getTextWidth(),
+ &right_x,
+ true);
+
+ if (mIcon)
+ {
+ mIcon->draw(start_icon_x, 0, icon_height, icon_height, mColor);
+ }
+}
+
+
diff --git a/indra/llui/llscrolllistcell.h b/indra/llui/llscrolllistcell.h
index 38184e7860..c6c43537c6 100644
--- a/indra/llui/llscrolllistcell.h
+++ b/indra/llui/llscrolllistcell.h
@@ -1,280 +1,280 @@
-/**
- * @file llscrolllistcell.h
- * @brief Scroll lists are composed of rows (items), each of which
- * contains columns (cells).
- *
- * $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 LLSCROLLLISTCELL_H
-#define LLSCROLLLISTCELL_H
-
-#include "llfontgl.h" // HAlign
-#include "llpointer.h" // LLPointer<>
-#include "lluistring.h"
-#include "v4color.h"
-#include "llui.h"
-#include "llgltexture.h"
-
-class LLCheckBoxCtrl;
-class LLSD;
-class LLUIImage;
-
-/*
- * Represents a cell in a scrollable table.
- *
- * Sub-classes must return height and other properties
- * though width accessors are implemented by the base class.
- * It is therefore important for sub-class constructors to call
- * setWidth() with realistic values.
- */
-class LLScrollListCell
-{
-public:
- struct Params : public LLInitParam::Block<Params>
- {
- Optional<std::string> type,
- column;
-
- Optional<S32> width;
- Optional<bool> enabled,
- visible;
-
- Optional<void*> userdata;
- Optional<LLSD> value; // state of checkbox, icon id/name, date
- Optional<LLSD> alt_value;
- Optional<std::string> label; // description or text
- Optional<std::string> tool_tip;
-
- Optional<const LLFontGL*> font;
- Optional<LLColor4> font_color;
- Optional<LLFontGL::HAlign> font_halign;
-
- Optional<LLColor4> color;
-
- Params()
- : type("type", "text"),
- column("column"),
- width("width"),
- enabled("enabled", true),
- visible("visible", true),
- value("value"),
- alt_value("alt_value", ""),
- label("label"),
- tool_tip("tool_tip", ""),
- font("font", LLFontGL::getFontSansSerifSmall()),
- font_color("font_color", LLColor4::black),
- color("color", LLColor4::white),
- font_halign("halign", LLFontGL::LEFT)
- {
- addSynonym(column, "name");
- addSynonym(font_color, "font-color");
- }
- };
-
- static LLScrollListCell* create(const Params&);
-
- LLScrollListCell(const LLScrollListCell::Params&);
- virtual ~LLScrollListCell() {};
-
- virtual void draw(const LLColor4& color, const LLColor4& highlight_color) const {}; // truncate to given width, if possible
- virtual S32 getWidth() const {return mWidth;}
- virtual S32 getContentWidth() const { return 0; }
- virtual S32 getHeight() const { return 0; }
- virtual const LLSD getValue() const;
- virtual const LLSD getAltValue() const;
- virtual void setValue(const LLSD& value) { }
- virtual void setAltValue(const LLSD& value) { }
- virtual const std::string &getToolTip() const { return mToolTip; }
- virtual void setToolTip(const std::string &str) { mToolTip = str; }
- virtual bool getVisible() const { return true; }
- virtual void setWidth(S32 width) { mWidth = width; }
- virtual void highlightText(S32 offset, S32 num_chars) {}
- virtual bool isText() const { return false; }
- virtual bool needsToolTip() const { return ! mToolTip.empty(); }
- virtual void setColor(const LLColor4&) {}
- virtual void onCommit() {};
-
- virtual bool handleClick() { return false; }
- virtual void setEnabled(bool enable) { }
-
-private:
- S32 mWidth;
- std::string mToolTip;
-};
-
-class LLScrollListSpacer : public LLScrollListCell
-{
-public:
- LLScrollListSpacer(const LLScrollListCell::Params& p) : LLScrollListCell(p) {}
- /*virtual*/ ~LLScrollListSpacer() {};
- /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const {}
-};
-
-/*
- * Cell displaying a text label.
- */
-class LLScrollListText : public LLScrollListCell
-{
-public:
- LLScrollListText(const LLScrollListCell::Params&);
- /*virtual*/ ~LLScrollListText();
-
- /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const;
- /*virtual*/ S32 getContentWidth() const;
- /*virtual*/ S32 getHeight() const;
- /*virtual*/ void setValue(const LLSD& value);
- /*virtual*/ void setAltValue(const LLSD& value);
- /*virtual*/ const LLSD getValue() const;
- /*virtual*/ const LLSD getAltValue() const;
- /*virtual*/ bool getVisible() const;
- /*virtual*/ void highlightText(S32 offset, S32 num_chars);
-
- /*virtual*/ void setColor(const LLColor4&);
- /*virtual*/ bool isText() const;
- /*virtual*/ const std::string & getToolTip() const;
- /*virtual*/ bool needsToolTip() const;
-
- S32 getTextWidth() const { return mTextWidth;}
- void setTextWidth(S32 value) { mTextWidth = value;}
- virtual void setWidth(S32 width) { LLScrollListCell::setWidth(width); mTextWidth = width; }
-
- void setText(const LLStringExplicit& text);
- void setFontStyle(const U8 font_style);
- void setAlignment(LLFontGL::HAlign align) { mFontAlignment = align; }
-
-protected:
- LLUIString mText;
- LLUIString mAltText;
- S32 mTextWidth;
- const LLFontGL* mFont;
- LLColor4 mColor;
- LLColor4 mHighlightColor;
- U8 mUseColor;
- LLFontGL::HAlign mFontAlignment;
- bool mVisible;
- S32 mHighlightCount;
- S32 mHighlightOffset;
-
- LLPointer<LLUIImage> mRoundedRectImage;
-
- static U32 sCount;
-};
-
-/*
- * Cell displaying an image. AT the moment, this is specifically UI image
- */
-class LLScrollListIcon : public LLScrollListCell
-{
-public:
- LLScrollListIcon(const LLScrollListCell::Params& p);
- /*virtual*/ ~LLScrollListIcon();
- /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const;
- /*virtual*/ S32 getWidth() const;
- /*virtual*/ S32 getHeight() const;
- /*virtual*/ const LLSD getValue() const;
- /*virtual*/ void setColor(const LLColor4&);
- /*virtual*/ void setValue(const LLSD& value);
-
-private:
- LLPointer<LLUIImage> mIcon;
- LLColor4 mColor;
- LLFontGL::HAlign mAlignment;
-};
-
-
-class LLScrollListBar : public LLScrollListCell
-{
-public:
- LLScrollListBar(const LLScrollListCell::Params& p);
- /*virtual*/ ~LLScrollListBar();
- /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const;
- /*virtual*/ S32 getWidth() const;
- /*virtual*/ S32 getHeight() const;
- /*virtual*/ const LLSD getValue() const;
- /*virtual*/ void setColor(const LLColor4&);
- /*virtual*/ void setValue(const LLSD& value);
-
-private:
- LLColor4 mColor;
- F32 mRatio;
- S32 mBottom;
- S32 mRightPad;
- S32 mLeftPad;
-};
-/*
- * An interactive cell containing a check box.
- */
-class LLScrollListCheck : public LLScrollListCell
-{
-public:
- LLScrollListCheck( const LLScrollListCell::Params&);
- /*virtual*/ ~LLScrollListCheck();
- /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const;
- /*virtual*/ S32 getHeight() const { return 0; }
- /*virtual*/ const LLSD getValue() const;
- /*virtual*/ void setValue(const LLSD& value);
- /*virtual*/ void onCommit();
-
- /*virtual*/ bool handleClick();
- /*virtual*/ void setEnabled(bool enable);
-
- LLCheckBoxCtrl* getCheckBox() { return mCheckBox; }
-
-private:
- LLCheckBoxCtrl* mCheckBox;
-};
-
-class LLScrollListDate : public LLScrollListText
-{
-public:
- LLScrollListDate( const LLScrollListCell::Params& p );
- virtual void setValue(const LLSD& value);
- virtual const LLSD getValue() const;
-
-private:
- LLDate mDate;
-};
-
-/*
-* Cell displaying icon and text.
-*/
-
-class LLScrollListIconText : public LLScrollListText
-{
-public:
- LLScrollListIconText(const LLScrollListCell::Params& p);
- /*virtual*/ ~LLScrollListIconText();
- /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const;
- /*virtual*/ const LLSD getValue() const;
- /*virtual*/ void setValue(const LLSD& value);
-
-
- S32 getIconWidth() const;
- /*virtual*/ void setWidth(S32 width);/* { LLScrollListCell::setWidth(width); mTextWidth = width - ; }*/
-
-private:
- LLPointer<LLUIImage> mIcon;
- S32 mPad;
-};
-
-#endif
+/**
+ * @file llscrolllistcell.h
+ * @brief Scroll lists are composed of rows (items), each of which
+ * contains columns (cells).
+ *
+ * $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 LLSCROLLLISTCELL_H
+#define LLSCROLLLISTCELL_H
+
+#include "llfontgl.h" // HAlign
+#include "llpointer.h" // LLPointer<>
+#include "lluistring.h"
+#include "v4color.h"
+#include "llui.h"
+#include "llgltexture.h"
+
+class LLCheckBoxCtrl;
+class LLSD;
+class LLUIImage;
+
+/*
+ * Represents a cell in a scrollable table.
+ *
+ * Sub-classes must return height and other properties
+ * though width accessors are implemented by the base class.
+ * It is therefore important for sub-class constructors to call
+ * setWidth() with realistic values.
+ */
+class LLScrollListCell
+{
+public:
+ struct Params : public LLInitParam::Block<Params>
+ {
+ Optional<std::string> type,
+ column;
+
+ Optional<S32> width;
+ Optional<bool> enabled,
+ visible;
+
+ Optional<void*> userdata;
+ Optional<LLSD> value; // state of checkbox, icon id/name, date
+ Optional<LLSD> alt_value;
+ Optional<std::string> label; // description or text
+ Optional<std::string> tool_tip;
+
+ Optional<const LLFontGL*> font;
+ Optional<LLColor4> font_color;
+ Optional<LLFontGL::HAlign> font_halign;
+
+ Optional<LLColor4> color;
+
+ Params()
+ : type("type", "text"),
+ column("column"),
+ width("width"),
+ enabled("enabled", true),
+ visible("visible", true),
+ value("value"),
+ alt_value("alt_value", ""),
+ label("label"),
+ tool_tip("tool_tip", ""),
+ font("font", LLFontGL::getFontEmojiSmall()),
+ font_color("font_color", LLColor4::black),
+ color("color", LLColor4::white),
+ font_halign("halign", LLFontGL::LEFT)
+ {
+ addSynonym(column, "name");
+ addSynonym(font_color, "font-color");
+ }
+ };
+
+ static LLScrollListCell* create(const Params&);
+
+ LLScrollListCell(const LLScrollListCell::Params&);
+ virtual ~LLScrollListCell() {};
+
+ virtual void draw(const LLColor4& color, const LLColor4& highlight_color) const {}; // truncate to given width, if possible
+ virtual S32 getWidth() const {return mWidth;}
+ virtual S32 getContentWidth() const { return 0; }
+ virtual S32 getHeight() const { return 0; }
+ virtual const LLSD getValue() const;
+ virtual const LLSD getAltValue() const;
+ virtual void setValue(const LLSD& value) { }
+ virtual void setAltValue(const LLSD& value) { }
+ virtual const std::string &getToolTip() const { return mToolTip; }
+ virtual void setToolTip(const std::string &str) { mToolTip = str; }
+ virtual bool getVisible() const { return true; }
+ virtual void setWidth(S32 width) { mWidth = width; }
+ virtual void highlightText(S32 offset, S32 num_chars) {}
+ virtual bool isText() const { return false; }
+ virtual bool needsToolTip() const { return ! mToolTip.empty(); }
+ virtual void setColor(const LLColor4&) {}
+ virtual void onCommit() {};
+
+ virtual bool handleClick() { return false; }
+ virtual void setEnabled(bool enable) { }
+
+private:
+ S32 mWidth;
+ std::string mToolTip;
+};
+
+class LLScrollListSpacer : public LLScrollListCell
+{
+public:
+ LLScrollListSpacer(const LLScrollListCell::Params& p) : LLScrollListCell(p) {}
+ /*virtual*/ ~LLScrollListSpacer() {};
+ /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const {}
+};
+
+/*
+ * Cell displaying a text label.
+ */
+class LLScrollListText : public LLScrollListCell
+{
+public:
+ LLScrollListText(const LLScrollListCell::Params&);
+ /*virtual*/ ~LLScrollListText();
+
+ /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const;
+ /*virtual*/ S32 getContentWidth() const;
+ /*virtual*/ S32 getHeight() const;
+ /*virtual*/ void setValue(const LLSD& value);
+ /*virtual*/ void setAltValue(const LLSD& value);
+ /*virtual*/ const LLSD getValue() const;
+ /*virtual*/ const LLSD getAltValue() const;
+ /*virtual*/ bool getVisible() const;
+ /*virtual*/ void highlightText(S32 offset, S32 num_chars);
+
+ /*virtual*/ void setColor(const LLColor4&);
+ /*virtual*/ bool isText() const;
+ /*virtual*/ const std::string & getToolTip() const;
+ /*virtual*/ bool needsToolTip() const;
+
+ S32 getTextWidth() const { return mTextWidth;}
+ void setTextWidth(S32 value) { mTextWidth = value;}
+ virtual void setWidth(S32 width) { LLScrollListCell::setWidth(width); mTextWidth = width; }
+
+ void setText(const LLStringExplicit& text);
+ void setFontStyle(const U8 font_style);
+ void setAlignment(LLFontGL::HAlign align) { mFontAlignment = align; }
+
+protected:
+ LLUIString mText;
+ LLUIString mAltText;
+ S32 mTextWidth;
+ const LLFontGL* mFont;
+ LLColor4 mColor;
+ LLColor4 mHighlightColor;
+ U8 mUseColor;
+ LLFontGL::HAlign mFontAlignment;
+ bool mVisible;
+ S32 mHighlightCount;
+ S32 mHighlightOffset;
+
+ LLPointer<LLUIImage> mRoundedRectImage;
+
+ static U32 sCount;
+};
+
+/*
+ * Cell displaying an image. AT the moment, this is specifically UI image
+ */
+class LLScrollListIcon : public LLScrollListCell
+{
+public:
+ LLScrollListIcon(const LLScrollListCell::Params& p);
+ /*virtual*/ ~LLScrollListIcon();
+ /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const;
+ /*virtual*/ S32 getWidth() const;
+ /*virtual*/ S32 getHeight() const;
+ /*virtual*/ const LLSD getValue() const;
+ /*virtual*/ void setColor(const LLColor4&);
+ /*virtual*/ void setValue(const LLSD& value);
+
+private:
+ LLPointer<LLUIImage> mIcon;
+ LLColor4 mColor;
+ LLFontGL::HAlign mAlignment;
+};
+
+
+class LLScrollListBar : public LLScrollListCell
+{
+public:
+ LLScrollListBar(const LLScrollListCell::Params& p);
+ /*virtual*/ ~LLScrollListBar();
+ /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const;
+ /*virtual*/ S32 getWidth() const;
+ /*virtual*/ S32 getHeight() const;
+ /*virtual*/ const LLSD getValue() const;
+ /*virtual*/ void setColor(const LLColor4&);
+ /*virtual*/ void setValue(const LLSD& value);
+
+private:
+ LLColor4 mColor;
+ F32 mRatio;
+ S32 mBottom;
+ S32 mRightPad;
+ S32 mLeftPad;
+};
+/*
+ * An interactive cell containing a check box.
+ */
+class LLScrollListCheck : public LLScrollListCell
+{
+public:
+ LLScrollListCheck( const LLScrollListCell::Params&);
+ /*virtual*/ ~LLScrollListCheck();
+ /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const;
+ /*virtual*/ S32 getHeight() const { return 0; }
+ /*virtual*/ const LLSD getValue() const;
+ /*virtual*/ void setValue(const LLSD& value);
+ /*virtual*/ void onCommit();
+
+ /*virtual*/ bool handleClick();
+ /*virtual*/ void setEnabled(bool enable);
+
+ LLCheckBoxCtrl* getCheckBox() { return mCheckBox; }
+
+private:
+ LLCheckBoxCtrl* mCheckBox;
+};
+
+class LLScrollListDate : public LLScrollListText
+{
+public:
+ LLScrollListDate( const LLScrollListCell::Params& p );
+ virtual void setValue(const LLSD& value);
+ virtual const LLSD getValue() const;
+
+private:
+ LLDate mDate;
+};
+
+/*
+* Cell displaying icon and text.
+*/
+
+class LLScrollListIconText : public LLScrollListText
+{
+public:
+ LLScrollListIconText(const LLScrollListCell::Params& p);
+ /*virtual*/ ~LLScrollListIconText();
+ /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const;
+ /*virtual*/ const LLSD getValue() const;
+ /*virtual*/ void setValue(const LLSD& value);
+
+
+ S32 getIconWidth() const;
+ /*virtual*/ void setWidth(S32 width);/* { LLScrollListCell::setWidth(width); mTextWidth = width - ; }*/
+
+private:
+ LLPointer<LLUIImage> mIcon;
+ S32 mPad;
+};
+
+#endif
diff --git a/indra/llui/llscrolllistcolumn.cpp b/indra/llui/llscrolllistcolumn.cpp
index a935ea2f11..c6be401676 100644
--- a/indra/llui/llscrolllistcolumn.cpp
+++ b/indra/llui/llscrolllistcolumn.cpp
@@ -1,340 +1,340 @@
-/**
- * @file llscrollcolumnheader.cpp
- * @brief Scroll lists are composed of rows (items), each of which
- * contains columns (cells).
- *
- * $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$
- */
-
-#include "linden_common.h"
-
-#include "llscrolllistcolumn.h"
-
-#include "llbutton.h"
-#include "llresizebar.h"
-#include "llscrolllistcell.h"
-#include "llscrolllistctrl.h"
-#include "llscrolllistitem.h"
-#include "lluictrlfactory.h"
-
-const S32 MIN_COLUMN_WIDTH = 20;
-
-// defaults for LLScrollColumnHeader param block pulled from widgets/scroll_column_header.xml
-static LLWidgetNameRegistry::StaticRegistrar sRegisterColumnHeaderParams(&typeid(LLScrollColumnHeader::Params), "scroll_column_header");
-
-//---------------------------------------------------------------------------
-// LLScrollColumnHeader
-//---------------------------------------------------------------------------
-LLScrollColumnHeader::Params::Params()
-: column("column")
-{}
-
-
-LLScrollColumnHeader::LLScrollColumnHeader(const LLScrollColumnHeader::Params& p)
-: LLButton(p), // use combobox params to steal images
- mColumn(p.column),
- mHasResizableElement(false)
-{
- setClickedCallback(boost::bind(&LLScrollColumnHeader::onClick, this, _2));
-
- // resize handles on left and right
- const S32 RESIZE_BAR_THICKNESS = 3;
- LLResizeBar::Params resize_bar_p;
- resize_bar_p.resizing_view(this);
- resize_bar_p.rect(LLRect(getRect().getWidth() - RESIZE_BAR_THICKNESS, getRect().getHeight(), getRect().getWidth(), 0));
- resize_bar_p.min_size(MIN_COLUMN_WIDTH);
- resize_bar_p.side(LLResizeBar::RIGHT);
- resize_bar_p.enabled(false);
- mResizeBar = LLUICtrlFactory::create<LLResizeBar>(resize_bar_p);
- addChild(mResizeBar);
-}
-
-LLScrollColumnHeader::~LLScrollColumnHeader()
-{}
-
-void LLScrollColumnHeader::draw()
-{
- std::string sort_column = mColumn->mParentCtrl->getSortColumnName();
- bool draw_arrow = !mColumn->mLabel.empty()
- && mColumn->mParentCtrl->isSorted()
- // check for indirect sorting column as well as column's sorting name
- && (sort_column == mColumn->mSortingColumn || sort_column == mColumn->mName);
-
- bool is_ascending = mColumn->mParentCtrl->getSortAscending();
- if (draw_arrow)
- {
- setImageOverlay(is_ascending ? "up_arrow.tga" : "down_arrow.tga", LLFontGL::RIGHT, LLColor4::white);
- }
- else
- {
- setImageOverlay(LLUUID::null);
- }
-
- // Draw children
- LLButton::draw();
-}
-
-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);
- }
- else
- {
- onClick(LLSD());
- }
- return true;
-}
-
-void LLScrollColumnHeader::onClick(const LLSD& data)
-{
- if (mColumn)
- {
- LLScrollListCtrl::onClickColumn(mColumn);
- }
-}
-
-LLView* LLScrollColumnHeader::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding)
-{
- // this logic assumes dragging on right
- llassert(snap_edge == SNAP_RIGHT);
-
- // use higher snap threshold for column headers
- threshold = llmin(threshold, 10);
-
- 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
- if (llabs(snap_delta) <= threshold && mouse_dir.mX * snap_delta > 0 )
- {
- new_edge_val = snap_rect.mRight + snap_delta;
- }
- else
- {
- LLScrollListColumn* next_column = mColumn->mParentCtrl->getColumn(mColumn->mIndex + 1);
- while (next_column)
- {
- if (next_column->mHeader)
- {
- snap_delta = (next_column->mHeader->getSnapRect().mRight - next_column->mMaxContentWidth) - snap_rect.mRight;
- if (llabs(snap_delta) <= threshold && mouse_dir.mX * snap_delta > 0 )
- {
- new_edge_val = snap_rect.mRight + snap_delta;
- }
- break;
- }
- next_column = mColumn->mParentCtrl->getColumn(next_column->mIndex + 1);
- }
- }
-
- return this;
-}
-
-void LLScrollColumnHeader::handleReshape(const LLRect& new_rect, bool by_user)
-{
- S32 new_width = new_rect.getWidth();
- S32 delta_width = new_width - (getRect().getWidth() /*+ mColumn->mParentCtrl->getColumnPadding()*/);
-
- if (delta_width != 0)
- {
- S32 remaining_width = -delta_width;
- S32 col;
- for (col = mColumn->mIndex + 1; col < mColumn->mParentCtrl->getNumColumns(); col++)
- {
- LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col);
- if (!columnp) continue;
-
- if (columnp->mHeader && columnp->mHeader->canResize())
- {
- // how many pixels in width can this column afford to give up?
- S32 resize_buffer_amt = llmax(0, columnp->getWidth() - MIN_COLUMN_WIDTH);
-
- // user shrinking column, need to add width to other columns
- if (delta_width < 0)
- {
- if (columnp->getWidth() > 0)
- {
- // statically sized column, give all remaining width to this column
- columnp->setWidth(columnp->getWidth() + remaining_width);
- if (columnp->mRelWidth > 0.f)
- {
- columnp->mRelWidth = (F32)columnp->getWidth() / (F32)mColumn->mParentCtrl->getItemListRect().getWidth();
- }
- // all padding went to this widget, we're done
- break;
- }
- }
- else
- {
- // user growing column, need to take width from other columns
- remaining_width += resize_buffer_amt;
-
- if (columnp->getWidth() > 0)
- {
- columnp->setWidth(columnp->getWidth() - llmin(columnp->getWidth() - MIN_COLUMN_WIDTH, delta_width));
- if (columnp->mRelWidth > 0.f)
- {
- columnp->mRelWidth = (F32)columnp->getWidth() / (F32)mColumn->mParentCtrl->getItemListRect().getWidth();
- }
- }
-
- if (remaining_width >= 0)
- {
- // width sucked up from neighboring columns, done
- break;
- }
- }
- }
- }
-
- // clamp resize amount to maximum that can be absorbed by other columns
- if (delta_width > 0)
- {
- delta_width += llmin(remaining_width, 0);
- }
-
- // propagate constrained delta_width to new width for this column
- new_width = getRect().getWidth() + delta_width - mColumn->mParentCtrl->getColumnPadding();
-
- // use requested width
- mColumn->setWidth(new_width);
-
- // update proportional spacing
- if (mColumn->mRelWidth > 0.f)
- {
- mColumn->mRelWidth = (F32)new_width / (F32)mColumn->mParentCtrl->getItemListRect().getWidth();
- }
-
- // 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
- const bool force_update = true;
- mColumn->mParentCtrl->updateColumns(force_update);
- }
-}
-
-void LLScrollColumnHeader::setHasResizableElement(bool resizable)
-{
- if (mHasResizableElement != resizable)
- {
- mColumn->mParentCtrl->dirtyColumns();
- mHasResizableElement = resizable;
- }
-}
-
-void LLScrollColumnHeader::updateResizeBars()
-{
- S32 num_resizable_columns = 0;
- S32 col;
- for (col = 0; col < mColumn->mParentCtrl->getNumColumns(); col++)
- {
- LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col);
- if (columnp && columnp->mHeader && columnp->mHeader->canResize())
- {
- num_resizable_columns++;
- }
- }
-
- S32 num_resizers_enabled = 0;
-
- // now enable/disable resize handles on resizable columns if we have at least two
- for (col = 0; col < mColumn->mParentCtrl->getNumColumns(); col++)
- {
- LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col);
- if (!columnp || !columnp->mHeader) continue;
- bool enable = num_resizable_columns >= 2 && num_resizers_enabled < (num_resizable_columns - 1) && columnp->mHeader->canResize();
- columnp->mHeader->enableResizeBar(enable);
- if (enable)
- {
- num_resizers_enabled++;
- }
- }
-}
-
-void LLScrollColumnHeader::enableResizeBar(bool enable)
-{
- mResizeBar->setEnabled(enable);
-}
-
-bool LLScrollColumnHeader::canResize()
-{
- return getVisible() && (mHasResizableElement || mColumn->mDynamicWidth);
-}
-
-void LLScrollListColumn::SortNames::declareValues()
-{
- declare("ascending", LLScrollListColumn::ASCENDING);
- declare("descending", LLScrollListColumn::DESCENDING);
-}
-
-//
-// LLScrollListColumn
-//
-//static
-const LLScrollListColumn::Params& LLScrollListColumn::getDefaultParams()
-{
- return LLUICtrlFactory::getDefaultParams<LLScrollListColumn>();
-}
-
-
-LLScrollListColumn::LLScrollListColumn(const Params& p, LLScrollListCtrl* parent)
-: mWidth(0),
- mIndex (-1),
- mParentCtrl(parent),
- mName(p.name),
- mLabel(p.header.label),
- mHeader(NULL),
- mMaxContentWidth(0),
- mDynamicWidth(p.width.dynamic_width),
- mRelWidth(p.width.relative_width),
- mFontAlignment(p.halign),
- mSortingColumn(p.sort_column)
-{
- if (p.sort_ascending.isProvided())
- {
- mSortDirection = p.sort_ascending() ? ASCENDING : DESCENDING;
- }
- else
- {
- mSortDirection = p.sort_direction;
- }
-
- setWidth(p.width.pixel_width);
-}
-
-void LLScrollListColumn::setWidth(S32 width)
-{
- if (!mDynamicWidth && mRelWidth <= 0.f)
- {
- mParentCtrl->updateStaticColumnWidth(this, width);
- }
- mWidth = width;
-}
+/**
+ * @file llscrollcolumnheader.cpp
+ * @brief Scroll lists are composed of rows (items), each of which
+ * contains columns (cells).
+ *
+ * $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$
+ */
+
+#include "linden_common.h"
+
+#include "llscrolllistcolumn.h"
+
+#include "llbutton.h"
+#include "llresizebar.h"
+#include "llscrolllistcell.h"
+#include "llscrolllistctrl.h"
+#include "llscrolllistitem.h"
+#include "lluictrlfactory.h"
+
+const S32 MIN_COLUMN_WIDTH = 20;
+
+// defaults for LLScrollColumnHeader param block pulled from widgets/scroll_column_header.xml
+static LLWidgetNameRegistry::StaticRegistrar sRegisterColumnHeaderParams(&typeid(LLScrollColumnHeader::Params), "scroll_column_header");
+
+//---------------------------------------------------------------------------
+// LLScrollColumnHeader
+//---------------------------------------------------------------------------
+LLScrollColumnHeader::Params::Params()
+: column("column")
+{}
+
+
+LLScrollColumnHeader::LLScrollColumnHeader(const LLScrollColumnHeader::Params& p)
+: LLButton(p), // use combobox params to steal images
+ mColumn(p.column),
+ mHasResizableElement(false)
+{
+ setClickedCallback(boost::bind(&LLScrollColumnHeader::onClick, this, _2));
+
+ // resize handles on left and right
+ const S32 RESIZE_BAR_THICKNESS = 3;
+ LLResizeBar::Params resize_bar_p;
+ resize_bar_p.resizing_view(this);
+ resize_bar_p.rect(LLRect(getRect().getWidth() - RESIZE_BAR_THICKNESS, getRect().getHeight(), getRect().getWidth(), 0));
+ resize_bar_p.min_size(MIN_COLUMN_WIDTH);
+ resize_bar_p.side(LLResizeBar::RIGHT);
+ resize_bar_p.enabled(false);
+ mResizeBar = LLUICtrlFactory::create<LLResizeBar>(resize_bar_p);
+ addChild(mResizeBar);
+}
+
+LLScrollColumnHeader::~LLScrollColumnHeader()
+{}
+
+void LLScrollColumnHeader::draw()
+{
+ std::string sort_column = mColumn->mParentCtrl->getSortColumnName();
+ bool draw_arrow = !mColumn->mLabel.empty()
+ && mColumn->mParentCtrl->isSorted()
+ // check for indirect sorting column as well as column's sorting name
+ && (sort_column == mColumn->mSortingColumn || sort_column == mColumn->mName);
+
+ bool is_ascending = mColumn->mParentCtrl->getSortAscending();
+ if (draw_arrow)
+ {
+ setImageOverlay(is_ascending ? "up_arrow.tga" : "down_arrow.tga", LLFontGL::RIGHT, LLColor4::white);
+ }
+ else
+ {
+ setImageOverlay(LLUUID::null);
+ }
+
+ // Draw children
+ LLButton::draw();
+}
+
+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);
+ }
+ else
+ {
+ onClick(LLSD());
+ }
+ return true;
+}
+
+void LLScrollColumnHeader::onClick(const LLSD& data)
+{
+ if (mColumn)
+ {
+ LLScrollListCtrl::onClickColumn(mColumn);
+ }
+}
+
+LLView* LLScrollColumnHeader::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding)
+{
+ // this logic assumes dragging on right
+ llassert(snap_edge == SNAP_RIGHT);
+
+ // use higher snap threshold for column headers
+ threshold = llmin(threshold, 10);
+
+ 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
+ if (llabs(snap_delta) <= threshold && mouse_dir.mX * snap_delta > 0 )
+ {
+ new_edge_val = snap_rect.mRight + snap_delta;
+ }
+ else
+ {
+ LLScrollListColumn* next_column = mColumn->mParentCtrl->getColumn(mColumn->mIndex + 1);
+ while (next_column)
+ {
+ if (next_column->mHeader)
+ {
+ snap_delta = (next_column->mHeader->getSnapRect().mRight - next_column->mMaxContentWidth) - snap_rect.mRight;
+ if (llabs(snap_delta) <= threshold && mouse_dir.mX * snap_delta > 0 )
+ {
+ new_edge_val = snap_rect.mRight + snap_delta;
+ }
+ break;
+ }
+ next_column = mColumn->mParentCtrl->getColumn(next_column->mIndex + 1);
+ }
+ }
+
+ return this;
+}
+
+void LLScrollColumnHeader::handleReshape(const LLRect& new_rect, bool by_user)
+{
+ S32 new_width = new_rect.getWidth();
+ S32 delta_width = new_width - (getRect().getWidth() /*+ mColumn->mParentCtrl->getColumnPadding()*/);
+
+ if (delta_width != 0)
+ {
+ S32 remaining_width = -delta_width;
+ S32 col;
+ for (col = mColumn->mIndex + 1; col < mColumn->mParentCtrl->getNumColumns(); col++)
+ {
+ LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col);
+ if (!columnp) continue;
+
+ if (columnp->mHeader && columnp->mHeader->canResize())
+ {
+ // how many pixels in width can this column afford to give up?
+ S32 resize_buffer_amt = llmax(0, columnp->getWidth() - MIN_COLUMN_WIDTH);
+
+ // user shrinking column, need to add width to other columns
+ if (delta_width < 0)
+ {
+ if (columnp->getWidth() > 0)
+ {
+ // statically sized column, give all remaining width to this column
+ columnp->setWidth(columnp->getWidth() + remaining_width);
+ if (columnp->mRelWidth > 0.f)
+ {
+ columnp->mRelWidth = (F32)columnp->getWidth() / (F32)mColumn->mParentCtrl->getItemListRect().getWidth();
+ }
+ // all padding went to this widget, we're done
+ break;
+ }
+ }
+ else
+ {
+ // user growing column, need to take width from other columns
+ remaining_width += resize_buffer_amt;
+
+ if (columnp->getWidth() > 0)
+ {
+ columnp->setWidth(columnp->getWidth() - llmin(columnp->getWidth() - MIN_COLUMN_WIDTH, delta_width));
+ if (columnp->mRelWidth > 0.f)
+ {
+ columnp->mRelWidth = (F32)columnp->getWidth() / (F32)mColumn->mParentCtrl->getItemListRect().getWidth();
+ }
+ }
+
+ if (remaining_width >= 0)
+ {
+ // width sucked up from neighboring columns, done
+ break;
+ }
+ }
+ }
+ }
+
+ // clamp resize amount to maximum that can be absorbed by other columns
+ if (delta_width > 0)
+ {
+ delta_width += llmin(remaining_width, 0);
+ }
+
+ // propagate constrained delta_width to new width for this column
+ new_width = getRect().getWidth() + delta_width - mColumn->mParentCtrl->getColumnPadding();
+
+ // use requested width
+ mColumn->setWidth(new_width);
+
+ // update proportional spacing
+ if (mColumn->mRelWidth > 0.f)
+ {
+ mColumn->mRelWidth = (F32)new_width / (F32)mColumn->mParentCtrl->getItemListRect().getWidth();
+ }
+
+ // 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
+ const bool force_update = true;
+ mColumn->mParentCtrl->updateColumns(force_update);
+ }
+}
+
+void LLScrollColumnHeader::setHasResizableElement(bool resizable)
+{
+ if (mHasResizableElement != resizable)
+ {
+ mColumn->mParentCtrl->dirtyColumns();
+ mHasResizableElement = resizable;
+ }
+}
+
+void LLScrollColumnHeader::updateResizeBars()
+{
+ S32 num_resizable_columns = 0;
+ S32 col;
+ for (col = 0; col < mColumn->mParentCtrl->getNumColumns(); col++)
+ {
+ LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col);
+ if (columnp && columnp->mHeader && columnp->mHeader->canResize())
+ {
+ num_resizable_columns++;
+ }
+ }
+
+ S32 num_resizers_enabled = 0;
+
+ // now enable/disable resize handles on resizable columns if we have at least two
+ for (col = 0; col < mColumn->mParentCtrl->getNumColumns(); col++)
+ {
+ LLScrollListColumn* columnp = mColumn->mParentCtrl->getColumn(col);
+ if (!columnp || !columnp->mHeader) continue;
+ bool enable = num_resizable_columns >= 2 && num_resizers_enabled < (num_resizable_columns - 1) && columnp->mHeader->canResize();
+ columnp->mHeader->enableResizeBar(enable);
+ if (enable)
+ {
+ num_resizers_enabled++;
+ }
+ }
+}
+
+void LLScrollColumnHeader::enableResizeBar(bool enable)
+{
+ mResizeBar->setEnabled(enable);
+}
+
+bool LLScrollColumnHeader::canResize()
+{
+ return getVisible() && (mHasResizableElement || mColumn->mDynamicWidth);
+}
+
+void LLScrollListColumn::SortNames::declareValues()
+{
+ declare("ascending", LLScrollListColumn::ASCENDING);
+ declare("descending", LLScrollListColumn::DESCENDING);
+}
+
+//
+// LLScrollListColumn
+//
+//static
+const LLScrollListColumn::Params& LLScrollListColumn::getDefaultParams()
+{
+ return LLUICtrlFactory::getDefaultParams<LLScrollListColumn>();
+}
+
+
+LLScrollListColumn::LLScrollListColumn(const Params& p, LLScrollListCtrl* parent)
+: mWidth(0),
+ mIndex (-1),
+ mParentCtrl(parent),
+ mName(p.name),
+ mLabel(p.header.label),
+ mHeader(NULL),
+ mMaxContentWidth(0),
+ mDynamicWidth(p.width.dynamic_width),
+ mRelWidth(p.width.relative_width),
+ mFontAlignment(p.halign),
+ mSortingColumn(p.sort_column)
+{
+ if (p.sort_ascending.isProvided())
+ {
+ mSortDirection = p.sort_ascending() ? ASCENDING : DESCENDING;
+ }
+ else
+ {
+ mSortDirection = p.sort_direction;
+ }
+
+ setWidth(p.width.pixel_width);
+}
+
+void LLScrollListColumn::setWidth(S32 width)
+{
+ if (!mDynamicWidth && mRelWidth <= 0.f)
+ {
+ mParentCtrl->updateStaticColumnWidth(this, width);
+ }
+ mWidth = width;
+}
diff --git a/indra/llui/llscrolllistcolumn.h b/indra/llui/llscrolllistcolumn.h
index 630e5ef529..042e9e5f02 100644
--- a/indra/llui/llscrolllistcolumn.h
+++ b/indra/llui/llscrolllistcolumn.h
@@ -1,172 +1,172 @@
-/**
- * @file llscrollcolumnheader.h
- * @brief Scroll lists are composed of rows (items), each of which
- * contains columns (cells).
- *
- * $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 LLSCROLLLISTCOLUMN_H
-#define LLSCROLLLISTCOLUMN_H
-
-#include "llrect.h"
-#include "lluistring.h"
-#include "llbutton.h"
-#include "llinitparam.h"
-
-class LLScrollListColumn;
-class LLResizeBar;
-class LLScrollListCtrl;
-
-class LLScrollColumnHeader : public LLButton
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLButton::Params>
- {
- Mandatory<LLScrollListColumn*> column;
-
- Params();
- };
- LLScrollColumnHeader(const Params&);
- ~LLScrollColumnHeader();
-
- /*virtual*/ void draw();
- /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask);
-
- /*virtual*/ LLView* findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding);
- /*virtual*/ void handleReshape(const LLRect& new_rect, bool by_user = false);
-
- LLScrollListColumn* getColumn() { return mColumn; }
- void setHasResizableElement(bool resizable);
- void updateResizeBars();
- bool canResize();
- void enableResizeBar(bool enable);
-
- void onClick(const LLSD& data);
-
-private:
- LLScrollListColumn* mColumn;
- LLResizeBar* mResizeBar;
- bool mHasResizableElement;
-};
-
-/*
- * A simple data class describing a column within a scroll list.
- */
-class LLScrollListColumn
-{
-public:
- typedef enum e_sort_direction
- {
- DESCENDING,
- ASCENDING
- } ESortDirection;
-
- struct SortNames
- : public LLInitParam::TypeValuesHelper<LLScrollListColumn::ESortDirection, SortNames>
- {
- static void declareValues();
- };
-
- struct Params : public LLInitParam::Block<Params>
- {
- Optional<std::string> name,
- tool_tip;
- Optional<std::string> sort_column;
- Optional<ESortDirection, SortNames> sort_direction;
- Optional<bool> sort_ascending;
-
- struct Width : public LLInitParam::ChoiceBlock<Width>
- {
- Alternative<bool> dynamic_width;
- Alternative<S32> pixel_width;
- Alternative<F32> relative_width;
-
- Width()
- : dynamic_width("dynamic_width", false),
- pixel_width("width"),
- relative_width("relative_width", -1.f)
- {
- addSynonym(relative_width, "relwidth");
- }
- };
- Optional<Width> width;
-
- // either an image or label is used in column header
- struct Header : public LLInitParam::ChoiceBlock<Header>
- {
- Alternative<std::string> label;
- Alternative<LLUIImage*> image;
-
- Header()
- : label("label"),
- image("image")
- {}
- };
- Optional<Header> header;
-
- Optional<LLFontGL::HAlign> halign;
-
- Params()
- : name("name"),
- tool_tip("tool_tip"),
- sort_column("sort_column"),
- sort_direction("sort_direction"),
- sort_ascending("sort_ascending", true),
- halign("halign", LLFontGL::LEFT)
- {
- // default choice to "dynamic_width"
- changeDefault(width.dynamic_width, true);
-
- addSynonym(sort_column, "sort");
- }
- };
-
- static const Params& getDefaultParams();
-
- //NOTE: this is default constructible so we can store it in a map.
- LLScrollListColumn(const Params& p = getDefaultParams(), LLScrollListCtrl* = NULL);
-
- void setWidth(S32 width);
- S32 getWidth() const { return mWidth; }
-
-public:
- // Public data is fine so long as this remains a simple struct-like data class.
- // If it ever gets any smarter than that, these should all become private
- // with protected or public accessor methods added as needed. -MG
- std::string mName;
- std::string mSortingColumn;
- ESortDirection mSortDirection;
- LLUIString mLabel;
- F32 mRelWidth;
- bool mDynamicWidth;
- S32 mMaxContentWidth;
- S32 mIndex;
- LLScrollListCtrl* mParentCtrl;
- LLScrollColumnHeader* mHeader;
- LLFontGL::HAlign mFontAlignment;
-
-private:
- S32 mWidth;
-};
-
-#endif
+/**
+ * @file llscrollcolumnheader.h
+ * @brief Scroll lists are composed of rows (items), each of which
+ * contains columns (cells).
+ *
+ * $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 LLSCROLLLISTCOLUMN_H
+#define LLSCROLLLISTCOLUMN_H
+
+#include "llrect.h"
+#include "lluistring.h"
+#include "llbutton.h"
+#include "llinitparam.h"
+
+class LLScrollListColumn;
+class LLResizeBar;
+class LLScrollListCtrl;
+
+class LLScrollColumnHeader : public LLButton
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLButton::Params>
+ {
+ Mandatory<LLScrollListColumn*> column;
+
+ Params();
+ };
+ LLScrollColumnHeader(const Params&);
+ ~LLScrollColumnHeader();
+
+ /*virtual*/ void draw();
+ /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask);
+
+ /*virtual*/ LLView* findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding);
+ /*virtual*/ void handleReshape(const LLRect& new_rect, bool by_user = false);
+
+ LLScrollListColumn* getColumn() { return mColumn; }
+ void setHasResizableElement(bool resizable);
+ void updateResizeBars();
+ bool canResize();
+ void enableResizeBar(bool enable);
+
+ void onClick(const LLSD& data);
+
+private:
+ LLScrollListColumn* mColumn;
+ LLResizeBar* mResizeBar;
+ bool mHasResizableElement;
+};
+
+/*
+ * A simple data class describing a column within a scroll list.
+ */
+class LLScrollListColumn
+{
+public:
+ typedef enum e_sort_direction
+ {
+ DESCENDING,
+ ASCENDING
+ } ESortDirection;
+
+ struct SortNames
+ : public LLInitParam::TypeValuesHelper<LLScrollListColumn::ESortDirection, SortNames>
+ {
+ static void declareValues();
+ };
+
+ struct Params : public LLInitParam::Block<Params>
+ {
+ Optional<std::string> name,
+ tool_tip;
+ Optional<std::string> sort_column;
+ Optional<ESortDirection, SortNames> sort_direction;
+ Optional<bool> sort_ascending;
+
+ struct Width : public LLInitParam::ChoiceBlock<Width>
+ {
+ Alternative<bool> dynamic_width;
+ Alternative<S32> pixel_width;
+ Alternative<F32> relative_width;
+
+ Width()
+ : dynamic_width("dynamic_width", false),
+ pixel_width("width"),
+ relative_width("relative_width", -1.f)
+ {
+ addSynonym(relative_width, "relwidth");
+ }
+ };
+ Optional<Width> width;
+
+ // either an image or label is used in column header
+ struct Header : public LLInitParam::ChoiceBlock<Header>
+ {
+ Alternative<std::string> label;
+ Alternative<LLUIImage*> image;
+
+ Header()
+ : label("label"),
+ image("image")
+ {}
+ };
+ Optional<Header> header;
+
+ Optional<LLFontGL::HAlign> halign;
+
+ Params()
+ : name("name"),
+ tool_tip("tool_tip"),
+ sort_column("sort_column"),
+ sort_direction("sort_direction"),
+ sort_ascending("sort_ascending", true),
+ halign("halign", LLFontGL::LEFT)
+ {
+ // default choice to "dynamic_width"
+ changeDefault(width.dynamic_width, true);
+
+ addSynonym(sort_column, "sort");
+ }
+ };
+
+ static const Params& getDefaultParams();
+
+ //NOTE: this is default constructible so we can store it in a map.
+ LLScrollListColumn(const Params& p = getDefaultParams(), LLScrollListCtrl* = NULL);
+
+ void setWidth(S32 width);
+ S32 getWidth() const { return mWidth; }
+
+public:
+ // Public data is fine so long as this remains a simple struct-like data class.
+ // If it ever gets any smarter than that, these should all become private
+ // with protected or public accessor methods added as needed. -MG
+ std::string mName;
+ std::string mSortingColumn;
+ ESortDirection mSortDirection;
+ LLUIString mLabel;
+ F32 mRelWidth;
+ bool mDynamicWidth;
+ S32 mMaxContentWidth;
+ S32 mIndex;
+ LLScrollListCtrl* mParentCtrl;
+ LLScrollColumnHeader* mHeader;
+ LLFontGL::HAlign mFontAlignment;
+
+private:
+ S32 mWidth;
+};
+
+#endif
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index 535a880120..1868ac9639 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -1,3448 +1,3448 @@
- /**
- * @file llscrolllistctrl.cpp
- * @brief Scroll lists are composed of rows (items), each of which
- * contains columns (cells).
- *
- * $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 "llscrolllistctrl.h"
-
-#include <algorithm>
-
-#include "llstl.h"
-#include "llboost.h"
-//#include "indra_constants.h"
-
-#include "llavatarnamecache.h"
-#include "llcheckboxctrl.h"
-#include "llclipboard.h"
-#include "llfocusmgr.h"
-#include "llgl.h" // LLGLSUIDefault()
-#include "lllocalcliprect.h"
-//#include "llrender.h"
-#include "llresmgr.h"
-#include "llscrollbar.h"
-#include "llscrolllistcell.h"
-#include "llstring.h"
-#include "llui.h"
-#include "lluictrlfactory.h"
-#include "llwindow.h"
-#include "llcontrol.h"
-#include "llkeyboard.h"
-#include "llviewborder.h"
-#include "lltextbox.h"
-#include "llsdparam.h"
-#include "llcachename.h"
-#include "llmenugl.h"
-#include "llurlaction.h"
-#include "lltooltip.h"
-
-#include <boost/bind.hpp>
-
-static LLDefaultChildRegistry::Register<LLScrollListCtrl> r("scroll_list");
-
-// local structures & classes.
-struct SortScrollListItem
-{
- SortScrollListItem(const std::vector<std::pair<S32, bool> >& sort_orders,const LLScrollListCtrl::sort_signal_t* sort_signal, bool alternate_sort)
- : mSortOrders(sort_orders)
- , mSortSignal(sort_signal)
- , mAltSort(alternate_sort)
- {}
-
- bool operator()(const LLScrollListItem* i1, const LLScrollListItem* i2)
- {
- // sort over all columns in order specified by mSortOrders
- S32 sort_result = 0;
- for (sort_order_t::const_reverse_iterator it = mSortOrders.rbegin();
- it != mSortOrders.rend(); ++it)
- {
- S32 col_idx = it->first;
- bool sort_ascending = it->second;
-
- S32 order = sort_ascending ? 1 : -1; // ascending or descending sort for this column?
-
- const LLScrollListCell *cell1 = i1->getColumn(col_idx);
- const LLScrollListCell *cell2 = i2->getColumn(col_idx);
- if (cell1 && cell2)
- {
- if(mSortSignal)
- {
- sort_result = order * (*mSortSignal)(col_idx,i1, i2);
- }
- else
- {
- if (mAltSort && !cell1->getAltValue().asString().empty() && !cell2->getAltValue().asString().empty())
- {
- sort_result = order * LLStringUtil::compareDict(cell1->getAltValue().asString(), cell2->getAltValue().asString());
- }
- else
- {
- sort_result = order * LLStringUtil::compareDict(cell1->getValue().asString(), cell2->getValue().asString());
- }
- }
- if (sort_result != 0)
- {
- break; // we have a sort order!
- }
- }
- }
-
- return sort_result < 0;
- }
-
-
- typedef std::vector<std::pair<S32, bool> > sort_order_t;
- const LLScrollListCtrl::sort_signal_t* mSortSignal;
- const sort_order_t& mSortOrders;
- const bool mAltSort;
-};
-
-//---------------------------------------------------------------------------
-// LLScrollListCtrl
-//---------------------------------------------------------------------------
-
-void LLScrollListCtrl::SelectionTypeNames::declareValues()
-{
- declare("row", LLScrollListCtrl::ROW);
- declare("cell", LLScrollListCtrl::CELL);
- declare("header", LLScrollListCtrl::HEADER);
-}
-
-LLScrollListCtrl::Contents::Contents()
-: columns("column"),
- rows("row")
-{
- addSynonym(columns, "columns");
- addSynonym(rows, "rows");
-}
-
-LLScrollListCtrl::Params::Params()
-: multi_select("multi_select", false),
- has_border("draw_border"),
- draw_heading("draw_heading"),
- search_column("search_column", 0),
- selection_type("selection_type", ROW),
- sort_column("sort_column", -1),
- sort_ascending("sort_ascending", true),
- can_sort("can_sort", true),
- mouse_wheel_opaque("mouse_wheel_opaque", false),
- commit_on_keyboard_movement("commit_on_keyboard_movement", true),
- commit_on_selection_change("commit_on_selection_change", false),
- heading_height("heading_height"),
- page_lines("page_lines", 0),
- background_visible("background_visible"),
- draw_stripes("draw_stripes"),
- column_padding("column_padding"),
- row_padding("row_padding", 2),
- fg_unselected_color("fg_unselected_color"),
- fg_selected_color("fg_selected_color"),
- bg_selected_color("bg_selected_color"),
- fg_disable_color("fg_disable_color"),
- bg_writeable_color("bg_writeable_color"),
- bg_readonly_color("bg_readonly_color"),
- bg_stripe_color("bg_stripe_color"),
- hovered_color("hovered_color"),
- highlighted_color("highlighted_color"),
- contents(""),
- scroll_bar_bg_visible("scroll_bar_bg_visible"),
- scroll_bar_bg_color("scroll_bar_bg_color"),
- border("border")
-{}
-
-LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p)
-: LLUICtrl(p),
- mLineHeight(0),
- mScrollLines(0),
- mMouseWheelOpaque(p.mouse_wheel_opaque),
- mPageLines(p.page_lines),
- mMaxSelectable(0),
- mAllowKeyboardMovement(true),
- mCommitOnKeyboardMovement(p.commit_on_keyboard_movement),
- mCommitOnSelectionChange(p.commit_on_selection_change),
- mSelectionChanged(false),
- mSelectionType(p.selection_type),
- mNeedsScroll(false),
- mCanSelect(true),
- mCanSort(p.can_sort),
- mColumnsDirty(false),
- mMaxItemCount(INT_MAX),
- mBorderThickness( 2 ),
- mOnDoubleClickCallback( NULL ),
- mOnMaximumSelectCallback( NULL ),
- mOnSortChangedCallback( NULL ),
- mHighlightedItem(-1),
- mBorder(NULL),
- mSortCallback(NULL),
- mCommentTextView(NULL),
- mNumDynamicWidthColumns(0),
- mTotalStaticColumnWidth(0),
- mTotalColumnPadding(0),
- mSorted(false),
- mDirty(false),
- mOriginalSelection(-1),
- mLastSelected(NULL),
- mHeadingHeight(p.heading_height),
- mAllowMultipleSelection(p.multi_select),
- mDisplayColumnHeaders(p.draw_heading),
- mBackgroundVisible(p.background_visible),
- mDrawStripes(p.draw_stripes),
- mBgWriteableColor(p.bg_writeable_color()),
- mBgReadOnlyColor(p.bg_readonly_color()),
- mBgSelectedColor(p.bg_selected_color()),
- mBgStripeColor(p.bg_stripe_color()),
- mFgSelectedColor(p.fg_selected_color()),
- mFgUnselectedColor(p.fg_unselected_color()),
- mFgDisabledColor(p.fg_disable_color()),
- mHighlightedColor(p.highlighted_color()),
- mHoveredColor(p.hovered_color()),
- mSearchColumn(p.search_column),
- mColumnPadding(p.column_padding),
- mRowPadding(p.row_padding),
- mAlternateSort(false),
- mContextMenuType(MENU_NONE),
- mIsFriendSignal(NULL)
-{
- mItemListRect.setOriginAndSize(
- mBorderThickness,
- mBorderThickness,
- getRect().getWidth() - 2 * mBorderThickness,
- getRect().getHeight() - 2 * mBorderThickness );
-
- updateLineHeight();
-
- // Init the scrollbar
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
-
- LLRect scroll_rect;
- scroll_rect.setOriginAndSize(
- getRect().getWidth() - mBorderThickness - scrollbar_size,
- mItemListRect.mBottom,
- scrollbar_size,
- mItemListRect.getHeight());
-
- LLScrollbar::Params sbparams;
- sbparams.name("Scrollbar");
- sbparams.rect(scroll_rect);
- sbparams.orientation(LLScrollbar::VERTICAL);
- sbparams.doc_size(getItemCount());
- sbparams.doc_pos(mScrollLines);
- sbparams.page_size( getLinesPerPage() );
- sbparams.change_callback(boost::bind(&LLScrollListCtrl::onScrollChange, this, _1, _2));
- sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
- sbparams.visible(false);
- sbparams.bg_visible(p.scroll_bar_bg_visible);
- sbparams.bg_color(p.scroll_bar_bg_color);
- mScrollbar = LLUICtrlFactory::create<LLScrollbar> (sbparams);
- addChild(mScrollbar);
-
- // Border
- if (p.has_border)
- {
- LLRect border_rect = getLocalRect();
- LLViewBorder::Params params = p.border;
- params.rect(border_rect);
- mBorder = LLUICtrlFactory::create<LLViewBorder> (params);
- addChild(mBorder);
- }
-
- // set border *after* rect is fully initialized
- if (mBorder)
- {
- mBorder->setRect(getLocalRect());
- mBorder->reshape(getRect().getWidth(), getRect().getHeight());
- }
-
- if (p.sort_column >= 0)
- {
- sortByColumnIndex(p.sort_column, p.sort_ascending);
- }
-
-
- for (LLInitParam::ParamIterator<LLScrollListColumn::Params>::const_iterator row_it = p.contents.columns.begin();
- row_it != p.contents.columns.end();
- ++row_it)
- {
- addColumn(*row_it);
- }
-
- for (LLInitParam::ParamIterator<LLScrollListItem::Params>::const_iterator row_it = p.contents.rows.begin();
- row_it != p.contents.rows.end();
- ++row_it)
- {
- addRow(*row_it);
- }
-
- LLTextBox::Params text_p;
- text_p.name("comment_text");
- text_p.border_visible(false);
- text_p.rect(mItemListRect);
- text_p.follows.flags(FOLLOWS_ALL);
- // word wrap was added accroding to the EXT-6841
- text_p.wrap(true);
- addChild(LLUICtrlFactory::create<LLTextBox>(text_p));
-}
-
-S32 LLScrollListCtrl::getSearchColumn()
-{
- // search for proper search column
- if (mSearchColumn < 0)
- {
- LLScrollListItem* itemp = getFirstData();
- if (itemp)
- {
- for(S32 column = 0; column < getNumColumns(); column++)
- {
- LLScrollListCell* cell = itemp->getColumn(column);
- if (cell && cell->isText())
- {
- mSearchColumn = column;
- break;
- }
- }
- }
- }
- return llclamp(mSearchColumn, 0, getNumColumns());
-}
-/*virtual*/
-bool LLScrollListCtrl::preProcessChildNode(LLXMLNodePtr child)
-{
- if (child->hasName("column") || child->hasName("row"))
- {
- return true; // skip
- }
- else
- {
- return false;
- }
-}
-
-LLScrollListCtrl::~LLScrollListCtrl()
-{
- delete mSortCallback;
-
- std::for_each(mItemList.begin(), mItemList.end(), DeletePointer());
- mItemList.clear();
- clearColumns(); //clears columns and deletes headers
- delete mIsFriendSignal;
-
- auto menu = mPopupMenuHandle.get();
- if (menu)
- {
- menu->die();
- mPopupMenuHandle.markDead();
- }
-}
-
-
-bool LLScrollListCtrl::setMaxItemCount(S32 max_count)
-{
- if (max_count >= getItemCount())
- {
- mMaxItemCount = max_count;
- }
- return (max_count == mMaxItemCount);
-}
-
-S32 LLScrollListCtrl::isEmpty() const
-{
- return mItemList.empty();
-}
-
-S32 LLScrollListCtrl::getItemCount() const
-{
- return mItemList.size();
-}
-
-bool LLScrollListCtrl::hasSelectedItem() const
-{
- item_list::iterator iter;
- for (iter = mItemList.begin(); iter < mItemList.end(); )
- {
- LLScrollListItem* itemp = *iter;
- if (itemp && itemp->getSelected())
- {
- return true;
- }
- iter++;
- }
- return false;
-}
-
-// virtual LLScrolListInterface function (was deleteAllItems)
-void LLScrollListCtrl::clearRows()
-{
- std::for_each(mItemList.begin(), mItemList.end(), DeletePointer());
- mItemList.clear();
- //mItemCount = 0;
-
- // Scroll the bar back up to the top.
- mScrollbar->setDocParams(0, 0);
-
- mScrollLines = 0;
- mLastSelected = NULL;
- updateLayout();
- mDirty = false;
-}
-
-
-LLScrollListItem* LLScrollListCtrl::getFirstSelected() const
-{
- item_list::const_iterator iter;
- for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem* item = *iter;
- if (item->getSelected())
- {
- return item;
- }
- }
- return NULL;
-}
-
-std::vector<LLScrollListItem*> LLScrollListCtrl::getAllSelected() const
-{
- std::vector<LLScrollListItem*> ret;
- item_list::const_iterator iter;
- for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem* item = *iter;
- if (item->getSelected())
- {
- ret.push_back(item);
- }
- }
- 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;
-
- // make sure sort is up to date before returning an index
- updateSort();
-
- item_list::const_iterator iter;
- for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem* item = *iter;
- if (item->getSelected())
- {
- return CurSelectedIndex;
- }
- CurSelectedIndex++;
- }
-
- return -1;
-}
-
-LLScrollListItem* LLScrollListCtrl::getFirstData() const
-{
- if (mItemList.size() == 0)
- {
- return NULL;
- }
- return mItemList[0];
-}
-
-LLScrollListItem* LLScrollListCtrl::getLastData() const
-{
- if (mItemList.size() == 0)
- {
- return NULL;
- }
- return mItemList[mItemList.size() - 1];
-}
-
-std::vector<LLScrollListItem*> LLScrollListCtrl::getAllData() const
-{
- std::vector<LLScrollListItem*> ret;
- item_list::const_iterator iter;
- for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem* item = *iter;
- ret.push_back(item);
- }
- return ret;
-}
-
-// returns first matching item
-LLScrollListItem* LLScrollListCtrl::getItem(const LLSD& sd) const
-{
- std::string string_val = sd.asString();
-
- item_list::const_iterator iter;
- for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem* item = *iter;
- // assumes string representation is good enough for comparison
- if (item->getValue().asString() == string_val)
- {
- return item;
- }
- }
- return NULL;
-}
-
-
-void LLScrollListCtrl::reshape( S32 width, S32 height, bool called_from_parent )
-{
- LLUICtrl::reshape( width, height, called_from_parent );
-
- updateLayout();
-}
-
-void LLScrollListCtrl::updateLayout()
-{
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
- // reserve room for column headers, if needed
- S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0);
- mItemListRect.setOriginAndSize(
- mBorderThickness,
- mBorderThickness,
- getRect().getWidth() - 2 * mBorderThickness,
- getRect().getHeight() - (2 * mBorderThickness ) - heading_size );
-
- if (mCommentTextView == NULL)
- {
- mCommentTextView = getChildView("comment_text");
- }
-
- mCommentTextView->setShape(mItemListRect);
-
- // how many lines of content in a single "page"
- S32 page_lines = getLinesPerPage();
-
- bool scrollbar_visible = mLineHeight * getItemCount() > mItemListRect.getHeight();
- if (scrollbar_visible)
- {
- // provide space on the right for scrollbar
- mItemListRect.mRight = getRect().getWidth() - mBorderThickness - scrollbar_size;
- }
-
- mScrollbar->setOrigin(getRect().getWidth() - mBorderThickness - scrollbar_size, mItemListRect.mBottom);
- mScrollbar->reshape(scrollbar_size, mItemListRect.getHeight() + (mDisplayColumnHeaders ? mHeadingHeight : 0));
- mScrollbar->setPageSize(page_lines);
- mScrollbar->setDocSize( getItemCount() );
- mScrollbar->setVisible(scrollbar_visible);
-
- dirtyColumns();
-}
-
-// Attempt to size the control to show all items.
-// Do not make larger than width or height.
-void LLScrollListCtrl::fitContents(S32 max_width, S32 max_height)
-{
- S32 height = llmin( getRequiredRect().getHeight(), max_height );
- if(mPageLines)
- height = llmin( mPageLines * mLineHeight + 2*mBorderThickness + (mDisplayColumnHeaders ? mHeadingHeight : 0), height );
-
- S32 width = getRect().getWidth();
-
- reshape( width, height );
-}
-
-
-LLRect LLScrollListCtrl::getRequiredRect()
-{
- S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0);
- S32 height = (mLineHeight * getItemCount())
- + (2 * mBorderThickness )
- + heading_size;
- S32 width = getRect().getWidth();
-
- return LLRect(0, height, width, 0);
-}
-
-
-bool LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, bool requires_column )
-{
- bool not_too_big = getItemCount() < mMaxItemCount;
- if (not_too_big)
- {
- switch( pos )
- {
- case ADD_TOP:
- mItemList.push_front(item);
- setNeedsSort();
- break;
-
- case ADD_DEFAULT:
- case ADD_BOTTOM:
- mItemList.push_back(item);
- setNeedsSort();
- break;
-
- default:
- llassert(0);
- mItemList.push_back(item);
- setNeedsSort();
- break;
- }
-
- // create new column on demand
- if (mColumns.empty() && requires_column)
- {
- LLScrollListColumn::Params col_params;
- col_params.name = "default_column";
- col_params.header.label = "";
- col_params.width.dynamic_width = true;
- 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();
- }
-
- return not_too_big;
-}
-
-// 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
-S32 LLScrollListCtrl::calcMaxContentWidth()
-{
- const S32 HEADING_TEXT_PADDING = 25;
- const S32 COLUMN_TEXT_PADDING = 10;
-
- S32 max_item_width = 0;
-
- ordered_columns_t::iterator column_itor;
- for (column_itor = mColumnsIndexed.begin(); column_itor != mColumnsIndexed.end(); ++column_itor)
- {
- 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 = 0;
- if (column->mRelWidth >= 0)
- {
- new_width = (S32)ll_round(column->mRelWidth*mItemListRect.getWidth());
- }
- else if (column->mDynamicWidth)
- {
- new_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth - mTotalColumnPadding) / mNumDynamicWidthColumns;
- }
- else
- {
- new_width = column->getWidth();
- }
-
- if (column->getWidth() != new_width)
- {
- column->setWidth(new_width);
- width_changed = true;
- }
- }
- return width_changed;
-}
-
-// Line height is the max height of all the cells in all the items.
-void LLScrollListCtrl::updateLineHeight()
-{
- mLineHeight = 0;
- item_list::iterator iter;
- for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem *itemp = *iter;
- S32 num_cols = itemp->getNumColumns();
- S32 i = 0;
- for (const LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i))
- {
- mLineHeight = llmax( mLineHeight, cell->getHeight() + mRowPadding );
- }
- }
-}
-
-// when the only change to line height is from an insert, we needn't scan the entire list
-void LLScrollListCtrl::updateLineHeightInsert(LLScrollListItem* itemp)
-{
- S32 num_cols = itemp->getNumColumns();
- S32 i = 0;
- for (const LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i))
- {
- mLineHeight = llmax( mLineHeight, cell->getHeight() + mRowPadding );
- }
-}
-
-
-void LLScrollListCtrl::updateColumns(bool force_update)
-{
- if (!mColumnsDirty && !force_update)
- return;
-
- mColumnsDirty = false;
-
- bool columns_changed_width = updateColumnWidths();
-
- // update column headers
- std::vector<LLScrollListColumn*>::iterator column_ordered_it;
- S32 left = mItemListRect.mLeft;
- LLScrollColumnHeader* last_header = NULL;
- for (column_ordered_it = mColumnsIndexed.begin(); column_ordered_it != mColumnsIndexed.end(); ++column_ordered_it)
- {
- LLScrollListColumn* column = *column_ordered_it;
- if (!column || column->getWidth() < 0)
- {
- // skip hidden columns
- continue;
- }
-
- if (column->mHeader)
- {
- column->mHeader->updateResizeBars();
-
- last_header = column->mHeader;
- S32 top = mItemListRect.mTop;
- S32 right = left + column->getWidth();
-
- if (column->mIndex != (S32)mColumnsIndexed.size()-1)
- {
- right += mColumnPadding;
- }
- right = llmax(left, llmin(mItemListRect.getWidth(), right));
- S32 header_width = right - left;
-
- last_header->reshape(header_width, mHeadingHeight);
- last_header->translate(
- left - last_header->getRect().mLeft,
- top - last_header->getRect().mBottom);
- last_header->setVisible(mDisplayColumnHeaders && header_width > 0);
- left = right;
- }
- }
-
- bool header_changed_width = false;
- // expand last column header we encountered to full list width
- if (last_header)
- {
- S32 old_width = last_header->getColumn()->getWidth();
- S32 new_width = llmax(0, mItemListRect.mRight - last_header->getRect().mLeft);
- last_header->reshape(new_width, last_header->getRect().getHeight());
- last_header->setVisible(mDisplayColumnHeaders && new_width > 0);
- if (old_width != new_width)
- {
- last_header->getColumn()->setWidth(new_width);
- header_changed_width = true;
- }
- }
-
- // propagate column widths to individual cells
- if (columns_changed_width || force_update)
- {
- item_list::iterator iter;
- for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- 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());
- }
- }
- }
- else if (header_changed_width)
- {
- item_list::iterator iter;
- S32 index = last_header->getColumn()->mIndex; // Not always identical to last column!
- for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem *itemp = *iter;
- LLScrollListCell* cell = itemp->getColumn(index);
- if (cell)
- {
- cell->setWidth(last_header->getColumn()->getWidth());
- }
- }
- }
-}
-
-void LLScrollListCtrl::setHeadingHeight(S32 heading_height)
-{
- mHeadingHeight = heading_height;
-
- updateLayout();
-
-}
-void LLScrollListCtrl::setPageLines(S32 new_page_lines)
-{
- mPageLines = new_page_lines;
-
- updateLayout();
-}
-
-bool LLScrollListCtrl::selectFirstItem()
-{
- bool success = false;
-
- // our $%&@#$()^%#$()*^ iterators don't let us check against the first item inside out iteration
- bool first_item = true;
-
- item_list::iterator iter;
- for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem *itemp = *iter;
- if( first_item && itemp->getEnabled() )
- {
- if (!itemp->getSelected())
- {
- switch (mSelectionType)
- {
- case CELL:
- selectItem(itemp, 0);
- break;
- case HEADER:
- case ROW:
- selectItem(itemp, -1);
- }
- }
- success = true;
- mOriginalSelection = 0;
- }
- else
- {
- deselectItem(itemp);
- }
- first_item = false;
- }
- if (mCommitOnSelectionChange)
- {
- commitIfChanged();
- }
- return success;
-}
-
-// Deselects all other items
-// virtual
-bool LLScrollListCtrl::selectNthItem( S32 target_index )
-{
- return selectItemRange(target_index, target_index);
-}
-
-// virtual
-bool LLScrollListCtrl::selectItemRange( S32 first_index, S32 last_index )
-{
- if (mItemList.empty())
- {
- return false;
- }
-
- // make sure sort is up to date
- updateSort();
-
- S32 listlen = (S32)mItemList.size();
- first_index = llclamp(first_index, 0, listlen-1);
-
- if (last_index < 0)
- last_index = listlen-1;
- else
- last_index = llclamp(last_index, first_index, listlen-1);
-
- bool success = false;
- S32 index = 0;
- for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); )
- {
- LLScrollListItem *itemp = *iter;
- if(!itemp)
- {
- iter = mItemList.erase(iter);
- continue ;
- }
-
- if( index >= first_index && index <= last_index )
- {
- if( itemp->getEnabled() )
- {
- // TODO: support range selection for cells
- selectItem(itemp, -1, false);
- success = true;
- }
- }
- else
- {
- deselectItem(itemp);
- }
- index++;
- iter++ ;
- }
-
- if (mCommitOnSelectionChange)
- {
- commitIfChanged();
- }
-
- mSearchString.clear();
-
- return success;
-}
-
-
-void LLScrollListCtrl::swapWithNext(S32 index)
-{
- if (index >= ((S32)mItemList.size() - 1))
- {
- // At end of list, doesn't do anything
- return;
- }
- updateSort();
- LLScrollListItem *cur_itemp = mItemList[index];
- mItemList[index] = mItemList[index + 1];
- mItemList[index + 1] = cur_itemp;
-}
-
-
-void LLScrollListCtrl::swapWithPrevious(S32 index)
-{
- if (index <= 0)
- {
- // At beginning of list, don't do anything
- }
-
- updateSort();
- LLScrollListItem *cur_itemp = mItemList[index];
- mItemList[index] = mItemList[index - 1];
- mItemList[index - 1] = cur_itemp;
-}
-
-
-void LLScrollListCtrl::deleteSingleItem(S32 target_index)
-{
- if (target_index < 0 || target_index >= (S32)mItemList.size())
- {
- return;
- }
-
- updateSort();
-
- LLScrollListItem *itemp;
- itemp = mItemList[target_index];
- if (itemp == mLastSelected)
- {
- mLastSelected = NULL;
- }
- delete itemp;
- mItemList.erase(mItemList.begin() + target_index);
- dirtyColumns();
-}
-
-//FIXME: refactor item deletion
-void LLScrollListCtrl::deleteItems(const LLSD& sd)
-{
- item_list::iterator iter;
- for (iter = mItemList.begin(); iter < mItemList.end(); )
- {
- LLScrollListItem* itemp = *iter;
- if (itemp->getValue().asString() == sd.asString())
- {
- if (itemp == mLastSelected)
- {
- mLastSelected = NULL;
- }
- delete itemp;
- iter = mItemList.erase(iter);
- }
- else
- {
- iter++;
- }
- }
-
- dirtyColumns();
-}
-
-void LLScrollListCtrl::deleteSelectedItems()
-{
- item_list::iterator iter;
- for (iter = mItemList.begin(); iter < mItemList.end(); )
- {
- LLScrollListItem* itemp = *iter;
- if (itemp->getSelected())
- {
- delete itemp;
- iter = mItemList.erase(iter);
- }
- else
- {
- iter++;
- }
- }
- mLastSelected = NULL;
- dirtyColumns();
-}
-
-void LLScrollListCtrl::clearHighlightedItems()
-{
- for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); ++iter)
- {
- (*iter)->setHighlighted(false);
- }
-}
-
-void LLScrollListCtrl::mouseOverHighlightNthItem(S32 target_index)
-{
- if (mHighlightedItem != target_index)
- {
- if (mHighlightedItem >= 0 && mHighlightedItem < mItemList.size())
- {
- mItemList[mHighlightedItem]->setHoverCell(-1);
- }
- mHighlightedItem = target_index;
- }
-}
-
-S32 LLScrollListCtrl::selectMultiple( uuid_vec_t ids )
-{
- item_list::iterator iter;
- S32 count = 0;
- for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem* item = *iter;
- uuid_vec_t::iterator iditr;
- for(iditr = ids.begin(); iditr != ids.end(); ++iditr)
- {
- if (item->getEnabled() && (item->getUUID() == (*iditr)))
- {
- // TODO: support multiple selection for cells
- selectItem(item, -1, false);
- ++count;
- break;
- }
- }
- if(ids.end() != iditr) ids.erase(iditr);
- }
-
- if (mCommitOnSelectionChange)
- {
- commitIfChanged();
- }
- return count;
-}
-
-S32 LLScrollListCtrl::getItemIndex( LLScrollListItem* target_item ) const
-{
- updateSort();
-
- S32 index = 0;
- item_list::const_iterator iter;
- for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem *itemp = *iter;
- if (target_item == itemp)
- {
- return index;
- }
- index++;
- }
- return -1;
-}
-
-S32 LLScrollListCtrl::getItemIndex( const LLUUID& target_id ) const
-{
- updateSort();
-
- S32 index = 0;
- item_list::const_iterator iter;
- for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem *itemp = *iter;
- if (target_id == itemp->getUUID())
- {
- return index;
- }
- index++;
- }
- return -1;
-}
-
-void LLScrollListCtrl::selectPrevItem( bool extend_selection)
-{
- LLScrollListItem* prev_item = NULL;
-
- if (!getFirstSelected())
- {
- // select last item
- selectNthItem(getItemCount() - 1);
- }
- else
- {
- updateSort();
-
- item_list::iterator iter;
- for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem* cur_item = *iter;
-
- if (cur_item->getSelected())
- {
- if (prev_item)
- {
- selectItem(prev_item, cur_item->getSelectedCell(), !extend_selection);
- }
- else
- {
- reportInvalidInput();
- }
- break;
- }
-
- // don't allow navigation to disabled elements
- prev_item = cur_item->getEnabled() ? cur_item : prev_item;
- }
- }
-
- if ((mCommitOnSelectionChange || mCommitOnKeyboardMovement))
- {
- commitIfChanged();
- }
-
- mSearchString.clear();
-}
-
-
-void LLScrollListCtrl::selectNextItem( bool extend_selection)
-{
- LLScrollListItem* next_item = NULL;
-
- if (!getFirstSelected())
- {
- selectFirstItem();
- }
- else
- {
- updateSort();
-
- item_list::reverse_iterator iter;
- for (iter = mItemList.rbegin(); iter != mItemList.rend(); iter++)
- {
- LLScrollListItem* cur_item = *iter;
-
- if (cur_item->getSelected())
- {
- if (next_item)
- {
- selectItem(next_item, cur_item->getSelectedCell(), !extend_selection);
- }
- else
- {
- reportInvalidInput();
- }
- break;
- }
-
- // don't allow navigation to disabled items
- next_item = cur_item->getEnabled() ? cur_item : next_item;
- }
- }
-
- if (mCommitOnKeyboardMovement)
- {
- onCommit();
- }
-
- mSearchString.clear();
-}
-
-
-
-void LLScrollListCtrl::deselectAllItems(bool no_commit_on_change)
-{
- item_list::iterator iter;
- for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem* item = *iter;
- deselectItem(item);
- }
-
- if (mCommitOnSelectionChange && !no_commit_on_change)
- {
- commitIfChanged();
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Use this to add comment text such as "Searching", which ignores column settings of list
-
-void LLScrollListCtrl::setCommentText(const std::string& comment_text)
-{
- getChild<LLTextBox>("comment_text")->setValue(comment_text);
-}
-
-LLScrollListItem* LLScrollListCtrl::addSeparator(EAddPosition pos)
-{
- LLScrollListItem::Params separator_params;
- separator_params.enabled(false);
- LLScrollListCell::Params column_params;
- column_params.type = "icon";
- column_params.value = "menu_separator";
- column_params.color = LLColor4(0.f, 0.f, 0.f, 0.7f);
- column_params.font_halign = LLFontGL::HCENTER;
- separator_params.columns.add(column_params);
- return addRow( separator_params, pos );
-}
-
-// 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, 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, column);
-
- bool found = NULL != item;
- if (found)
- {
- selectItem(item, -1);
- }
-
- if (mCommitOnSelectionChange)
- {
- commitIfChanged();
- }
-
- return found;
-}
-
-LLScrollListItem* LLScrollListCtrl::getItemByLabel(const std::string& label, bool case_sensitive, S32 column)
-{
- if (label.empty()) //RN: assume no empty items
- {
- return NULL;
- }
-
- std::string target_text = label;
- if (!case_sensitive)
- {
- LLStringUtil::toLower(target_text);
- }
-
- item_list::iterator iter;
- for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem* item = *iter;
- std::string item_text = item->getColumn(column)->getValue().asString(); // Only select enabled items with matching names
- if (!case_sensitive)
- {
- LLStringUtil::toLower(item_text);
- }
- if(item_text == target_text)
- {
- return item;
- }
- }
- return NULL;
-}
-
-
-bool LLScrollListCtrl::selectItemByPrefix(const std::string& target, bool case_sensitive, S32 column)
-{
- return selectItemByPrefix(utf8str_to_wstring(target), case_sensitive, column);
-}
-
-// Selects first enabled item that has a name where the name's first part matched the target string.
-// Returns false if item not found.
-bool LLScrollListCtrl::selectItemByPrefix(const LLWString& target, bool case_sensitive, S32 column)
-{
- bool found = false;
-
- LLWString target_trimmed( target );
- S32 target_len = target_trimmed.size();
-
- if( 0 == target_len )
- {
- // Is "" a valid choice?
- item_list::iterator iter;
- for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem* item = *iter;
- // Only select enabled items with matching names
- LLScrollListCell* cellp = item->getColumn(column == -1 ? getSearchColumn() : column);
- bool select = cellp ? item->getEnabled() && ('\0' == cellp->getValue().asString()[0]) : false;
- if (select)
- {
- selectItem(item, -1);
- found = true;
- break;
- }
- }
- }
- else
- {
- if (!case_sensitive)
- {
- // do comparisons in lower case
- LLWStringUtil::toLower(target_trimmed);
- }
-
- for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem* item = *iter;
-
- // Only select enabled items with matching names
- LLScrollListCell* cellp = item->getColumn(column == -1 ? getSearchColumn() : column);
- if (!cellp)
- {
- continue;
- }
- LLWString item_label = utf8str_to_wstring(cellp->getValue().asString());
- if (!case_sensitive)
- {
- LLWStringUtil::toLower(item_label);
- }
- // remove extraneous whitespace from searchable label
- LLWString trimmed_label = item_label;
- LLWStringUtil::trim(trimmed_label);
-
- bool select = item->getEnabled() && trimmed_label.compare(0, target_trimmed.size(), target_trimmed) == 0;
-
- if (select)
- {
- // find offset of matching text (might have leading whitespace)
- S32 offset = item_label.find(target_trimmed);
- cellp->highlightText(offset, target_trimmed.size());
- selectItem(item, -1);
- found = true;
- break;
- }
- }
- }
-
- if (mCommitOnSelectionChange)
- {
- commitIfChanged();
- }
-
- return found;
-}
-
-U32 LLScrollListCtrl::searchItems(const std::string& substring, bool case_sensitive, bool focus)
-{
- return searchItems(utf8str_to_wstring(substring), case_sensitive, focus);
-}
-
-U32 LLScrollListCtrl::searchItems(const LLWString& substring, bool case_sensitive, bool focus)
-{
- U32 found = 0;
-
- LLWString substring_trimmed(substring);
- S32 len = substring_trimmed.size();
-
- if (0 == len)
- {
- // at the moment search for empty element is not supported
- return 0;
- }
- else
- {
- deselectAllItems(true);
- if (!case_sensitive)
- {
- // do comparisons in lower case
- LLWStringUtil::toLower(substring_trimmed);
- }
-
- for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem* item = *iter;
- // Only select enabled items with matching names
- if (!item->getEnabled())
- {
- continue;
- }
- LLScrollListCell* cellp = item->getColumn(getSearchColumn());
- if (!cellp)
- {
- continue;
- }
- LLWString item_label = utf8str_to_wstring(cellp->getValue().asString());
- if (!case_sensitive)
- {
- LLWStringUtil::toLower(item_label);
- }
- // remove extraneous whitespace from searchable label
- LLWStringUtil::trim(item_label);
-
- size_t found_iter = item_label.find(substring_trimmed);
-
- if (found_iter != std::string::npos)
- {
- // find offset of matching text
- cellp->highlightText(found_iter, substring_trimmed.size());
- selectItem(item, -1, false);
-
- found++;
-
- if (!mAllowMultipleSelection)
- {
- break;
- }
- }
- }
- }
-
- if (focus && found != 0)
- {
- mNeedsScroll = true;
- }
-
- if (mCommitOnSelectionChange)
- {
- commitIfChanged();
- }
-
- return found;
-}
-
-const std::string LLScrollListCtrl::getSelectedItemLabel(S32 column) const
-{
- LLScrollListItem* item;
-
- item = getFirstSelected();
- if (item)
- {
- return item->getColumn(column)->getValue().asString();
- }
-
- return LLStringUtil::null;
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// "StringUUID" interface: use this when you're creating a list that contains non-unique strings each of which
-// has an associated, unique UUID, and only one of which can be selected at a time.
-
-LLScrollListItem* LLScrollListCtrl::addStringUUIDItem(const std::string& item_text, const LLUUID& id, EAddPosition pos, bool enabled)
-{
- if (getItemCount() < mMaxItemCount)
- {
- LLScrollListItem::Params item_p;
- item_p.enabled(enabled);
- item_p.value(id);
- item_p.columns.add().value(item_text).type("text");
-
- return addRow( item_p, pos );
- }
- return NULL;
-}
-
-// Select the line or lines that match this UUID
-bool LLScrollListCtrl::selectByID( const LLUUID& id )
-{
- return selectByValue( LLSD(id) );
-}
-
-bool LLScrollListCtrl::setSelectedByValue(const LLSD& value, bool selected)
-{
- bool found = false;
-
- if (selected && !mAllowMultipleSelection) deselectAllItems(true);
-
- item_list::iterator iter;
- for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem* item = *iter;
- if (item->getEnabled())
- {
- if (value.isBinary())
- {
- if (item->getValue().isBinary())
- {
- LLSD::Binary data1 = value.asBinary();
- LLSD::Binary data2 = item->getValue().asBinary();
- found = std::equal(data1.begin(), data1.end(), data2.begin());
- }
- }
- else
- {
- found = item->getValue().asString() == value.asString();
- }
-
- if (found)
- {
- if (selected)
- {
- selectItem(item, -1);
- }
- else
- {
- deselectItem(item);
- }
- break;
- }
- }
- }
-
- if (mCommitOnSelectionChange)
- {
- commitIfChanged();
- }
-
- return found;
-}
-
-bool LLScrollListCtrl::isSelected(const LLSD& value) const
-{
- item_list::const_iterator iter;
- for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem* item = *iter;
- if (item->getValue().asString() == value.asString())
- {
- return item->getSelected();
- }
- }
- return false;
-}
-
-LLUUID LLScrollListCtrl::getStringUUIDSelectedItem() const
-{
- LLScrollListItem* item = getFirstSelected();
-
- if (item)
- {
- return item->getUUID();
- }
-
- return LLUUID::null;
-}
-
-LLSD LLScrollListCtrl::getSelectedValue()
-{
- LLScrollListItem* item = getFirstSelected();
-
- if (item)
- {
- return item->getValue();
- }
- else
- {
- return LLSD();
- }
-}
-
-void LLScrollListCtrl::drawItems()
-{
- S32 x = mItemListRect.mLeft;
- S32 y = mItemListRect.mTop - mLineHeight;
-
- // allow for partial line at bottom
- S32 num_page_lines = getLinesPerPage();
-
- LLRect item_rect;
-
- LLGLSUIDefault gls_ui;
-
- F32 alpha = getDrawContext().mAlpha;
-
- {
- LLLocalClipRect clip(mItemListRect);
-
- S32 cur_y = y;
-
- S32 max_columns = 0;
-
- LLColor4 highlight_color = LLColor4::white; // ex: text inside cells
- 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);
-
- 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 (S32 line = first_line; line <= last_line; line++)
- {
- LLScrollListItem* item = mItemList[line];
-
- item_rect.setOriginAndSize(
- x,
- cur_y,
- mItemListRect.getWidth(),
- mLineHeight );
- item->setRect(item_rect);
-
- max_columns = llmax(max_columns, item->getNumColumns());
-
- LLColor4 fg_color;
- LLColor4 hover_color(LLColor4::transparent);
- LLColor4 select_color(LLColor4::transparent);
-
- if( mScrollLines <= line && line < mScrollLines + num_page_lines )
- {
- fg_color = (item->getEnabled() ? mFgUnselectedColor.get() : mFgDisabledColor.get());
- if( item->getSelected() && mCanSelect)
- {
- if(item->getHighlighted()) // if it's highlighted, average the colors
- {
- select_color = lerp(mBgSelectedColor.get(), mHighlightedColor.get(), 0.5f);
- }
- else // otherwise just select-highlight it
- {
- select_color = mBgSelectedColor.get();
- }
-
- fg_color = (item->getEnabled() ? mFgSelectedColor.get() : mFgDisabledColor.get());
- }
- if (mHighlightedItem == line && mCanSelect)
- {
- if(item->getHighlighted()) // if it's highlighted, average the colors
- {
- hover_color = lerp(mHoveredColor.get(), mHighlightedColor.get(), 0.5f);
- }
- else // otherwise just hover-highlight it
- {
- hover_color = mHoveredColor.get();
- }
- }
- else if (item->getHighlighted())
- {
- hover_color = mHighlightedColor.get();
- }
- else
- {
- if (mDrawStripes && (line % 2 == 0) && (max_columns > 1))
- {
- hover_color = mBgStripeColor.get();
- }
- }
-
- if (!item->getEnabled())
- {
- hover_color = mBgReadOnlyColor.get();
- }
-
- item->draw(item_rect, fg_color % alpha, hover_color% alpha, select_color% alpha, highlight_color % alpha, mColumnPadding);
-
- cur_y -= mLineHeight;
- }
- }
- }
-}
-
-
-void LLScrollListCtrl::draw()
-{
- LLLocalClipRect clip(getLocalRect());
-
- // if user specifies sort, make sure it is maintained
- updateSort();
-
- if (mNeedsScroll)
- {
- scrollToShowSelected();
- mNeedsScroll = false;
- }
- LLRect background(0, getRect().getHeight(), getRect().getWidth(), 0);
- // Draw background
- if (mBackgroundVisible)
- {
- F32 alpha = getCurrentTransparency();
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- gl_rect_2d(background, getEnabled() ? mBgWriteableColor.get() % alpha : mBgReadOnlyColor.get() % alpha );
- }
-
- updateColumns();
-
- getChildView("comment_text")->setVisible(mItemList.empty());
-
- drawItems();
-
- if (mBorder)
- {
- mBorder->setKeyboardFocusHighlight(hasFocus());
- }
-
- LLUICtrl::draw();
-}
-
-void LLScrollListCtrl::setEnabled(bool enabled)
-{
- mCanSelect = enabled;
- setTabStop(enabled);
- mScrollbar->setTabStop(!enabled && mScrollbar->getPageSize() < mScrollbar->getDocSize());
-}
-
-bool LLScrollListCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks)
-{
- bool handled = false;
- // Pretend the mouse is over the scrollbar
- handled = mScrollbar->handleScrollWheel( 0, 0, clicks );
-
- if (mMouseWheelOpaque)
- {
- return true;
- }
-
- return handled;
-}
-
-bool LLScrollListCtrl::handleScrollHWheel(S32 x, S32 y, S32 clicks)
-{
- bool handled = false;
- // Pretend the mouse is over the scrollbar
- handled = mScrollbar->handleScrollHWheel( 0, 0, clicks );
-
- if (mMouseWheelOpaque)
- {
- return true;
- }
-
- return handled;
-}
-
-// *NOTE: Requires a valid row_index and column_index
-LLRect LLScrollListCtrl::getCellRect(S32 row_index, S32 column_index)
-{
- LLRect cell_rect;
- S32 rect_left = getColumnOffsetFromIndex(column_index) + mItemListRect.mLeft;
- S32 rect_bottom = getRowOffsetFromIndex(row_index);
- LLScrollListColumn* columnp = getColumn(column_index);
- cell_rect.setOriginAndSize(rect_left, rect_bottom,
- /*rect_left + */columnp->getWidth(), mLineHeight);
- return cell_rect;
-}
-
-bool LLScrollListCtrl::handleToolTip(S32 x, S32 y, MASK mask)
-{
- S32 column_index = getColumnIndexFromOffset(x);
- LLScrollListColumn* columnp = getColumn(column_index);
-
- if (columnp == NULL) return false;
-
- bool handled = false;
- // show tooltip for full name of hovered item if it has been truncated
- LLScrollListItem* hit_item = hitItem(x, y);
- if (hit_item)
- {
- LLScrollListCell* hit_cell = hit_item->getColumn(column_index);
- if (!hit_cell) return false;
- if (hit_cell
- && hit_cell->isText()
- && hit_cell->needsToolTip())
- {
- S32 row_index = getItemIndex(hit_item);
- LLRect cell_rect = getCellRect(row_index, column_index);
- // Convert rect local to screen coordinates
- LLRect sticky_rect;
- localRectToScreen(cell_rect, &sticky_rect);
-
- // display tooltip exactly over original cell, in same font
- LLToolTipMgr::instance().show(LLToolTip::Params()
- .message(hit_cell->getToolTip())
- .font(LLFontGL::getFontSansSerifSmall())
- .pos(LLCoordGL(sticky_rect.mLeft - 5, sticky_rect.mTop + 6))
- .delay_time(0.2f)
- .sticky_rect(sticky_rect));
- }
- handled = true;
- }
-
- // otherwise, look for a tooltip associated with this column
- LLScrollColumnHeader* headerp = columnp->mHeader;
- if (headerp && !handled)
- {
- handled = headerp->handleToolTip(x, y, mask);
- }
-
- return handled;
-}
-
-bool LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask)
-{
- if (!mCanSelect) return false;
-
- bool selection_changed = false;
-
- LLScrollListItem* hit_item = hitItem(x, y);
-
- if( hit_item )
- {
- if( mAllowMultipleSelection )
- {
- if (mask & MASK_SHIFT)
- {
- if (mLastSelected == NULL)
- {
- selectItem(hit_item, getColumnIndexFromOffset(x));
- }
- else
- {
- // Select everthing between mLastSelected and hit_item
- bool selecting = false;
- item_list::iterator itor;
- // If we multiselect backwards, we'll stomp on mLastSelected,
- // meaning that we never stop selecting until hitting max or
- // the end of the list.
- LLScrollListItem* lastSelected = mLastSelected;
- for (itor = mItemList.begin(); itor != mItemList.end(); ++itor)
- {
- if(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable)
- {
- if(mOnMaximumSelectCallback)
- {
- mOnMaximumSelectCallback();
- }
- break;
- }
- LLScrollListItem *item = *itor;
- if (item == hit_item || item == lastSelected)
- {
- selectItem(item, getColumnIndexFromOffset(x), false);
- selecting = !selecting;
- if (hit_item == lastSelected)
- {
- // stop selecting now, since we just clicked on our last selected item
- selecting = false;
- }
- }
- if (selecting)
- {
- selectItem(item, getColumnIndexFromOffset(x), false);
- }
- }
- }
- }
- else if (mask & MASK_CONTROL)
- {
- if (hit_item->getSelected())
- {
- deselectItem(hit_item);
- }
- else
- {
- if(!(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable))
- {
- selectItem(hit_item, getColumnIndexFromOffset(x), false);
- }
- else
- {
- if(mOnMaximumSelectCallback)
- {
- mOnMaximumSelectCallback();
- }
- }
- }
- }
- else
- {
- deselectAllItems(true);
- selectItem(hit_item, getColumnIndexFromOffset(x));
- }
- }
- else
- {
- selectItem(hit_item, getColumnIndexFromOffset(x));
- }
-
- selection_changed = mSelectionChanged;
- if (mCommitOnSelectionChange)
- {
- commitIfChanged();
- }
-
- // clear search string on mouse operations
- mSearchString.clear();
- }
- else
- {
- //mLastSelected = NULL;
- //deselectAllItems(true);
- }
-
- return selection_changed;
-}
-
-
-bool LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- bool handled = childrenHandleMouseDown(x, y, mask) != NULL;
-
- if( !handled )
- {
- // set keyboard focus first, in case click action wants to move focus elsewhere
- setFocus(true);
-
- // clear selection changed flag because user is starting a selection operation
- mSelectionChanged = false;
-
- handleClick(x, y, mask);
- }
-
- return true;
-}
-
-bool LLScrollListCtrl::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- if (hasMouseCapture())
- {
- // release mouse capture immediately so
- // scroll to show selected logic will work
- gFocusMgr.setMouseCapture(NULL);
- if(mask == MASK_NONE)
- {
- selectItemAt(x, y, mask);
- mNeedsScroll = true;
- }
- }
-
- // always commit when mouse operation is completed inside list
- if (mItemListRect.pointInRect(x,y))
- {
- mDirty = mDirty || mSelectionChanged;
- mSelectionChanged = false;
- onCommit();
- }
-
- return LLUICtrl::handleMouseUp(x, y, mask);
-}
-
-// virtual
-bool LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- LLScrollListItem *item = hitItem(x, y);
- if (item)
- {
- // check to see if we have a UUID for this row
- std::string id = item->getValue().asString();
- LLUUID uuid(id);
- if (! uuid.isNull() && mContextMenuType != MENU_NONE)
- {
- // set up the callbacks for all of the avatar/group menu items
- // (N.B. callbacks don't take const refs as id is local scope)
- bool is_group = (mContextMenuType == MENU_GROUP);
- LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
- registrar.add("Url.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.RemoveFriend", boost::bind(&LLScrollListCtrl::removeFriend, id));
- registrar.add("Url.ReportAbuse", boost::bind(&LLScrollListCtrl::reportAbuse, id, is_group));
- registrar.add("Url.Execute", boost::bind(&LLScrollListCtrl::showNameDetails, id, is_group));
- registrar.add("Url.CopyLabel", boost::bind(&LLScrollListCtrl::copyNameToClipboard, id, is_group));
- registrar.add("Url.CopyUrl", boost::bind(&LLScrollListCtrl::copySLURLToClipboard, id, is_group));
-
- // create the context menu from the XUI file and display it
- std::string menu_name = is_group ? "menu_url_group.xml" : "menu_url_agent.xml";
- auto menu = mPopupMenuHandle.get();
- if (menu)
- {
- menu->die();
- mPopupMenuHandle.markDead();
- }
- llassert(LLMenuGL::sMenuContainer != NULL);
- menu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(
- menu_name, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
- if (menu)
- {
- mPopupMenuHandle = menu->getHandle();
- if (mIsFriendSignal)
- {
- bool isFriend = *(*mIsFriendSignal)(uuid);
- LLView* addFriendButton = menu->getChild<LLView>("add_friend");
- LLView* removeFriendButton = menu->getChild<LLView>("remove_friend");
-
- if (addFriendButton && removeFriendButton)
- {
- addFriendButton->setEnabled(!isFriend);
- removeFriendButton->setEnabled(isFriend);
- }
- }
-
- menu->show(x, y);
- LLMenuGL::showPopup(this, menu, x, y);
- return true;
- }
- }
- return LLUICtrl::handleRightMouseDown(x, y, mask);
- }
- return false;
-}
-
-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::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::removeFriend(std::string id)
-{
- std::string slurl = "secondlife:///app/agent/" + id + "/about";
- LLUrlAction::removeFriend(slurl);
-}
-
-void LLScrollListCtrl::reportAbuse(std::string id, bool is_group)
-{
- if (!is_group)
- {
- std::string slurl = "secondlife:///app/agent/" + id + "/about";
- LLUrlAction::reportAbuse(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)
-{
- // copy the name of the avatar or group to the clipboard
- std::string name;
- if (is_group)
- {
- gCacheName->getGroupName(LLUUID(id), name);
- }
- else
- {
- LLAvatarName av_name;
- LLAvatarNameCache::get(LLUUID(id), &av_name);
- name = av_name.getAccountName();
- }
- LLUrlAction::copyURLToClipboard(name);
-}
-
-void LLScrollListCtrl::copySLURLToClipboard(std::string id, bool is_group)
-{
- // copy a SLURL for the avatar or group to the clipboard
- std::string sltype = is_group ? "group" : "agent";
- std::string slurl = "secondlife:///app/" + sltype + "/" + id + "/about";
- LLUrlAction::copyURLToClipboard(slurl);
-}
-
-bool LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask)
-{
- //bool handled = false;
- bool handled = handleClick(x, y, mask);
-
- if (!handled)
- {
- // Offer the click to the children, even if we aren't enabled
- // so the scroll bars will work.
- if (NULL == LLView::childrenHandleDoubleClick(x, y, mask))
- {
- // Run the callback only if an item is being double-clicked.
- if( mCanSelect && hitItem(x, y) && mOnDoubleClickCallback )
- {
- mOnDoubleClickCallback();
- }
- }
- }
-
- return true;
-}
-
-bool LLScrollListCtrl::handleClick(S32 x, S32 y, MASK mask)
-{
- // which row was clicked on?
- LLScrollListItem* hit_item = hitItem(x, y);
- if (!hit_item) return false;
-
- // get appropriate cell from that row
- S32 column_index = getColumnIndexFromOffset(x);
- LLScrollListCell* hit_cell = hit_item->getColumn(column_index);
- if (!hit_cell) return false;
-
- // if cell handled click directly (i.e. clicked on an embedded checkbox)
- if (hit_cell->handleClick())
- {
- // if item not currently selected, select it
- if (!hit_item->getSelected())
- {
- selectItemAt(x, y, mask);
- gFocusMgr.setMouseCapture(this);
- mNeedsScroll = true;
- }
-
- // propagate state of cell to rest of selected column
- {
- // propagate value of this cell to other selected items
- // and commit the respective widgets
- LLSD item_value = hit_cell->getValue();
- for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem* item = *iter;
- if (item->getSelected())
- {
- LLScrollListCell* cellp = item->getColumn(column_index);
- cellp->setValue(item_value);
- cellp->onCommit();
- if (mLastSelected == NULL)
- {
- break;
- }
- }
- }
- //FIXME: find a better way to signal cell changes
- onCommit();
- }
- // eat click (e.g. do not trigger double click callback)
- return true;
- }
- else
- {
- // treat this as a normal single item selection
- selectItemAt(x, y, mask);
- gFocusMgr.setMouseCapture(this);
- mNeedsScroll = true;
- // do not eat click (allow double click callback)
- return false;
- }
-}
-
-LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y )
-{
- // Excludes disabled items.
- LLScrollListItem* hit_item = NULL;
-
- updateSort();
-
- LLRect item_rect;
- item_rect.setLeftTopAndSize(
- mItemListRect.mLeft,
- mItemListRect.mTop,
- mItemListRect.getWidth(),
- mLineHeight );
-
- // allow for partial line at bottom
- S32 num_page_lines = getLinesPerPage();
-
- S32 line = 0;
- item_list::iterator iter;
- for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem* item = *iter;
- if( mScrollLines <= line && line < mScrollLines + num_page_lines )
- {
- if( item->getEnabled() && item_rect.pointInRect( x, y ) )
- {
- hit_item = item;
- break;
- }
-
- item_rect.translate(0, -mLineHeight);
- }
- line++;
- }
-
- return hit_item;
-}
-
-S32 LLScrollListCtrl::getColumnIndexFromOffset(S32 x)
-{
- // which column did we hit?
- S32 left = 0;
- S32 right = 0;
- S32 width = 0;
- S32 column_index = 0;
-
- ordered_columns_t::const_iterator iter = mColumnsIndexed.begin();
- ordered_columns_t::const_iterator end = mColumnsIndexed.end();
- for ( ; iter != end; ++iter)
- {
- width = (*iter)->getWidth() + mColumnPadding;
- right += width;
- if (left <= x && x < right )
- {
- break;
- }
-
- // set left for next column as right of current column
- left = right;
- column_index++;
- }
-
- return llclamp(column_index, 0, getNumColumns() - 1);
-}
-
-
-S32 LLScrollListCtrl::getColumnOffsetFromIndex(S32 index)
-{
- S32 column_offset = 0;
- ordered_columns_t::const_iterator iter = mColumnsIndexed.begin();
- ordered_columns_t::const_iterator end = mColumnsIndexed.end();
- for ( ; iter != end; ++iter)
- {
- if (index-- <= 0)
- {
- return column_offset;
- }
- column_offset += (*iter)->getWidth() + mColumnPadding;
- }
-
- // when running off the end, return the rightmost pixel
- return mItemListRect.mRight;
-}
-
-S32 LLScrollListCtrl::getRowOffsetFromIndex(S32 index)
-{
- S32 row_bottom = (mItemListRect.mTop - ((index - mScrollLines + 1) * mLineHeight) );
- return row_bottom;
-}
-
-
-bool LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask)
-{
- bool handled = false;
-
- if (hasMouseCapture())
- {
- if(mask == MASK_NONE)
- {
- selectItemAt(x, y, mask);
- mNeedsScroll = true;
- }
- }
- else
- if (mCanSelect)
- {
- LLScrollListItem* item = hitItem(x, y);
- if (item)
- {
- mouseOverHighlightNthItem(getItemIndex(item));
- switch (mSelectionType)
- {
- case CELL:
- item->setHoverCell(getColumnIndexFromOffset(x));
- break;
- case HEADER:
- {
- S32 cell = getColumnIndexFromOffset(x);
- if (cell > 0)
- {
- item->setHoverCell(cell);
- }
- else
- {
- item->setHoverCell(-1);
- }
- break;
- }
- case ROW:
- break;
- }
- }
- else
- {
- mouseOverHighlightNthItem(-1);
- }
- }
-
- handled = LLUICtrl::handleHover( x, y, mask );
-
- return handled;
-}
-
-void LLScrollListCtrl::onMouseLeave(S32 x, S32 y, MASK mask)
-{
- // clear mouse highlight
- mouseOverHighlightNthItem(-1);
-}
-
-bool LLScrollListCtrl::handleKeyHere(KEY key,MASK mask )
-{
- bool handled = false;
-
- // not called from parent means we have keyboard focus or a child does
- if (mCanSelect)
- {
- if (mask == MASK_NONE)
- {
- switch(key)
- {
- case KEY_UP:
- if (mAllowKeyboardMovement || hasFocus())
- {
- // commit implicit in call
- selectPrevItem(false);
- mNeedsScroll = true;
- handled = true;
- }
- break;
- case KEY_DOWN:
- if (mAllowKeyboardMovement || hasFocus())
- {
- // commit implicit in call
- selectNextItem(false);
- mNeedsScroll = true;
- handled = true;
- }
- break;
- case KEY_LEFT:
- if (mAllowKeyboardMovement || hasFocus())
- {
- // TODO: support multi-select
- LLScrollListItem *item = getFirstSelected();
- if (item)
- {
- S32 cell = item->getSelectedCell();
- switch (mSelectionType)
- {
- case CELL:
- if (cell < mColumns.size()) cell++;
- break;
- case HEADER:
- if (cell == -1) cell = 1;
- else if (cell > 1 && cell < mColumns.size()) cell++; // skip header
- break;
- case ROW:
- cell = -1;
- break;
- }
- item->setSelectedCell(cell);
- handled = true;
- }
- }
- break;
- case KEY_RIGHT:
- if (mAllowKeyboardMovement || hasFocus())
- {
- // TODO: support multi-select
- LLScrollListItem *item = getFirstSelected();
- if (item)
- {
- S32 cell = item->getSelectedCell();
- switch (mSelectionType)
- {
- case CELL:
- if (cell >= 0) cell--;
- break;
- case HEADER:
- if (cell > 1) cell--;
- else if (cell == 1) cell = -1; // skip header
- break;
- case ROW:
- cell = -1;
- break;
- }
- item->setSelectedCell(cell);
- handled = true;
- }
- }
- break;
- case KEY_PAGE_UP:
- if (mAllowKeyboardMovement || hasFocus())
- {
- selectNthItem(getFirstSelectedIndex() - (mScrollbar->getPageSize() - 1));
- mNeedsScroll = true;
- if (mCommitOnKeyboardMovement
- && !mCommitOnSelectionChange)
- {
- onCommit();
- }
- handled = true;
- }
- break;
- case KEY_PAGE_DOWN:
- if (mAllowKeyboardMovement || hasFocus())
- {
- selectNthItem(getFirstSelectedIndex() + (mScrollbar->getPageSize() - 1));
- mNeedsScroll = true;
- if (mCommitOnKeyboardMovement
- && !mCommitOnSelectionChange)
- {
- onCommit();
- }
- handled = true;
- }
- break;
- case KEY_HOME:
- if (mAllowKeyboardMovement || hasFocus())
- {
- selectFirstItem();
- mNeedsScroll = true;
- if (mCommitOnKeyboardMovement
- && !mCommitOnSelectionChange)
- {
- onCommit();
- }
- handled = true;
- }
- break;
- case KEY_END:
- if (mAllowKeyboardMovement || hasFocus())
- {
- selectNthItem(getItemCount() - 1);
- mNeedsScroll = true;
- if (mCommitOnKeyboardMovement
- && !mCommitOnSelectionChange)
- {
- onCommit();
- }
- handled = true;
- }
- break;
- case KEY_RETURN:
- // JC - Special case: Only claim to have handled it
- // if we're the special non-commit-on-move
- // type. AND we are visible
- if (!mCommitOnKeyboardMovement && mask == MASK_NONE)
- {
- onCommit();
- mSearchString.clear();
- handled = true;
- }
- break;
- case KEY_BACKSPACE:
- mSearchTimer.reset();
- if (mSearchString.size())
- {
- mSearchString.erase(mSearchString.size() - 1, 1);
- }
- if (mSearchString.empty())
- {
- if (getFirstSelected())
- {
- LLScrollListCell* cellp = getFirstSelected()->getColumn(getSearchColumn());
- if (cellp)
- {
- cellp->highlightText(0, 0);
- }
- }
- }
- else if (selectItemByPrefix(wstring_to_utf8str(mSearchString), false))
- {
- mNeedsScroll = true;
- // update search string only on successful match
- mSearchTimer.reset();
-
- if (mCommitOnKeyboardMovement
- && !mCommitOnSelectionChange)
- {
- onCommit();
- }
- }
- break;
- default:
- break;
- }
- }
- // TODO: multiple: shift-up, shift-down, shift-home, shift-end, select all
- }
-
- return handled;
-}
-
-bool LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char)
-{
- if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL
- {
- return false;
- }
-
- // perform incremental search based on keyboard input
- static LLUICachedControl<F32> type_ahead_timeout ("TypeAheadTimeout", 0);
- if (mSearchTimer.getElapsedTimeF32() > type_ahead_timeout)
- {
- mSearchString.clear();
- }
-
- // type ahead search is case insensitive
- uni_char = LLStringOps::toLower((llwchar)uni_char);
-
- if (selectItemByPrefix(wstring_to_utf8str(mSearchString + (llwchar)uni_char), false))
- {
- // update search string only on successful match
- mNeedsScroll = true;
- mSearchString += uni_char;
- mSearchTimer.reset();
-
- if (mCommitOnKeyboardMovement
- && !mCommitOnSelectionChange)
- {
- onCommit();
- }
- }
- // handle iterating over same starting character
- else if (isRepeatedChars(mSearchString + (llwchar)uni_char) && !mItemList.empty())
- {
- // start from last selected item, in case we previously had a successful match against
- // duplicated characters ('AA' matches 'Aaron')
- item_list::iterator start_iter = mItemList.begin();
- S32 first_selected = getFirstSelectedIndex();
-
- // if we have a selection (> -1) then point iterator at the selected item
- if (first_selected > 0)
- {
- // point iterator to first selected item
- start_iter += first_selected;
- }
-
- // start search at first item after current selection
- item_list::iterator iter = start_iter;
- ++iter;
- if (iter == mItemList.end())
- {
- iter = mItemList.begin();
- }
-
- // loop around once, back to previous selection
- while(iter != start_iter)
- {
- LLScrollListItem* item = *iter;
-
- LLScrollListCell* cellp = item->getColumn(getSearchColumn());
- if (cellp)
- {
- // Only select enabled items with matching first characters
- LLWString item_label = utf8str_to_wstring(cellp->getValue().asString());
- if (item->getEnabled() && LLStringOps::toLower(item_label[0]) == uni_char)
- {
- selectItem(item, -1);
- mNeedsScroll = true;
- cellp->highlightText(0, 1);
- mSearchTimer.reset();
-
- if (mCommitOnKeyboardMovement
- && !mCommitOnSelectionChange)
- {
- onCommit();
- }
-
- break;
- }
- }
-
- ++iter;
- if (iter == mItemList.end())
- {
- iter = mItemList.begin();
- }
- }
- }
-
- return true;
-}
-
-
-void LLScrollListCtrl::reportInvalidInput()
-{
- make_ui_sound("UISndBadKeystroke");
-}
-
-bool LLScrollListCtrl::isRepeatedChars(const LLWString& string) const
-{
- if (string.empty())
- {
- return false;
- }
-
- llwchar first_char = string[0];
-
- for (U32 i = 0; i < string.size(); i++)
- {
- if (string[i] != first_char)
- {
- return false;
- }
- }
-
- return true;
-}
-
-void LLScrollListCtrl::selectItem(LLScrollListItem* itemp, S32 cell, bool select_single_item)
-{
- if (!itemp) return;
-
- if (!itemp->getSelected())
- {
- if (mLastSelected)
- {
- LLScrollListCell* cellp = mLastSelected->getColumn(getSearchColumn());
- if (cellp)
- {
- cellp->highlightText(0, 0);
- }
- }
- if (select_single_item)
- {
- deselectAllItems(true);
- }
- itemp->setSelected(true);
- switch (mSelectionType)
- {
- case CELL:
- itemp->setSelectedCell(cell);
- break;
- case HEADER:
- itemp->setSelectedCell(cell <= 0 ? -1 : cell);
- break;
- case ROW:
- itemp->setSelectedCell(-1);
- break;
- }
- mLastSelected = itemp;
- mSelectionChanged = true;
- }
-}
-
-void LLScrollListCtrl::deselectItem(LLScrollListItem* itemp)
-{
- if (!itemp) return;
-
- if (itemp->getSelected())
- {
- if (mLastSelected == itemp)
- {
- mLastSelected = NULL;
- }
-
- itemp->setSelected(false);
- LLScrollListCell* cellp = itemp->getColumn(getSearchColumn());
- if (cellp)
- {
- cellp->highlightText(0, 0);
- }
- mSelectionChanged = true;
- }
-}
-
-void LLScrollListCtrl::commitIfChanged()
-{
- if (mSelectionChanged)
- {
- mDirty = true;
- mSelectionChanged = false;
- onCommit();
- }
-}
-
-struct SameSortColumn
-{
- SameSortColumn(S32 column) : mColumn(column) {}
- S32 mColumn;
-
- bool operator()(std::pair<S32, bool> sort_column) { return sort_column.first == mColumn; }
-};
-
-bool LLScrollListCtrl::setSort(S32 column_idx, bool ascending)
-{
- LLScrollListColumn* sort_column = getColumn(column_idx);
- if (!sort_column) return false;
-
- sort_column->mSortDirection = ascending ? LLScrollListColumn::ASCENDING : LLScrollListColumn::DESCENDING;
-
- sort_column_t new_sort_column(column_idx, ascending);
-
- setNeedsSort();
-
- if (mSortColumns.empty())
- {
- mSortColumns.push_back(new_sort_column);
- return true;
- }
- else
- {
- // grab current sort column
- sort_column_t cur_sort_column = mSortColumns.back();
-
- // remove any existing sort criterion referencing this column
- // and add the new one
- mSortColumns.erase(remove_if(mSortColumns.begin(), mSortColumns.end(), SameSortColumn(column_idx)), mSortColumns.end());
- mSortColumns.push_back(new_sort_column);
-
- // did the sort criteria change?
- return (cur_sort_column != new_sort_column);
- }
-}
-
-S32 LLScrollListCtrl::getLinesPerPage()
-{
- //if mPageLines is NOT provided display all item
- if (mPageLines)
- {
- return mPageLines;
- }
- else
- {
- return mLineHeight ? mItemListRect.getHeight() / mLineHeight : getItemCount();
- }
-}
-
-
-// Called by scrollbar
-void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar )
-{
- mScrollLines = new_pos;
-}
-
-
-void LLScrollListCtrl::sortByColumn(const std::string& name, bool ascending)
-{
- column_map_t::iterator itor = mColumns.find(name);
- if (itor != mColumns.end())
- {
- sortByColumnIndex((*itor).second->mIndex, ascending);
- }
-}
-
-// First column is column 0
-void LLScrollListCtrl::sortByColumnIndex(U32 column, bool ascending)
-{
- setSort(column, ascending);
- updateSort();
-}
-
-void LLScrollListCtrl::updateSort() const
-{
- if (hasSortOrder() && !isSorted())
- {
- // do stable sort to preserve any previous sorts
- std::stable_sort(
- mItemList.begin(),
- mItemList.end(),
- SortScrollListItem(mSortColumns,mSortCallback, mAlternateSort));
-
- mSorted = true;
- }
-}
-
-// for one-shot sorts, does not save sort column/order
-void LLScrollListCtrl::sortOnce(S32 column, bool ascending)
-{
- std::vector<std::pair<S32, bool> > sort_column;
- sort_column.push_back(std::make_pair(column, ascending));
-
- // do stable sort to preserve any previous sorts
- std::stable_sort(
- mItemList.begin(),
- mItemList.end(),
- SortScrollListItem(sort_column,mSortCallback,mAlternateSort));
-}
-
-void LLScrollListCtrl::dirtyColumns()
-{
- mColumnsDirty = true;
- mColumnWidthsDirty = true;
-
- // need to keep mColumnsIndexed up to date
- // just in case someone indexes into it immediately
- mColumnsIndexed.resize(mColumns.size());
-
- column_map_t::iterator column_itor;
- for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor)
- {
- LLScrollListColumn *column = column_itor->second;
- mColumnsIndexed[column_itor->second->mIndex] = column;
- }
-}
-
-
-S32 LLScrollListCtrl::getScrollPos() const
-{
- return mScrollbar->getDocPos();
-}
-
-
-void LLScrollListCtrl::setScrollPos( S32 pos )
-{
- mScrollbar->setDocPos( pos );
-
- onScrollChange(mScrollbar->getDocPos(), mScrollbar);
-}
-
-
-void LLScrollListCtrl::scrollToShowSelected()
-{
- // don't scroll automatically when capturing mouse input
- // as that will change what is currently under the mouse cursor
- if (hasMouseCapture())
- {
- return;
- }
-
- updateSort();
-
- S32 index = getFirstSelectedIndex();
- if (index < 0)
- {
- return;
- }
-
- LLScrollListItem* item = mItemList[index];
- if (!item)
- {
- // I don't THINK this should ever happen.
- return;
- }
-
- S32 lowest = mScrollLines;
- S32 page_lines = getLinesPerPage();
- S32 highest = mScrollLines + page_lines;
-
- if (index < lowest)
- {
- // need to scroll to show item
- setScrollPos(index);
- }
- else if (highest <= index)
- {
- setScrollPos(index - page_lines + 1);
- }
-}
-
-void LLScrollListCtrl::updateStaticColumnWidth(LLScrollListColumn* col, S32 new_width)
-{
- mTotalStaticColumnWidth += llmax(0, new_width) - llmax(0, col->getWidth());
-}
-
-// LLEditMenuHandler functions
-
-// virtual
-void LLScrollListCtrl::copy()
-{
- std::string buffer;
-
- std::vector<LLScrollListItem*> items = getAllSelected();
- std::vector<LLScrollListItem*>::iterator itor;
- for (itor = items.begin(); itor != items.end(); ++itor)
- {
- buffer += (*itor)->getContentsCSV() + "\n";
- }
- LLClipboard::instance().copyToClipboard(utf8str_to_wstring(buffer), 0, buffer.length());
-}
-
-// virtual
-bool LLScrollListCtrl::canCopy() const
-{
- return (getFirstSelected() != NULL);
-}
-
-// virtual
-void LLScrollListCtrl::cut()
-{
- copy();
- doDelete();
-}
-
-// virtual
-bool LLScrollListCtrl::canCut() const
-{
- return canCopy() && canDoDelete();
-}
-
-// virtual
-void LLScrollListCtrl::selectAll()
-{
- // Deselects all other items
- item_list::iterator iter;
- for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
- {
- LLScrollListItem *itemp = *iter;
- if( itemp->getEnabled() )
- {
- selectItem(itemp, -1, false);
- }
- }
-
- if (mCommitOnSelectionChange)
- {
- commitIfChanged();
- }
-}
-
-// virtual
-bool LLScrollListCtrl::canSelectAll() const
-{
- return getCanSelect() && mAllowMultipleSelection && !(mMaxSelectable > 0 && mItemList.size() > mMaxSelectable);
-}
-
-// virtual
-void LLScrollListCtrl::deselect()
-{
- deselectAllItems();
-}
-
-// virtual
-bool LLScrollListCtrl::canDeselect() const
-{
- return getCanSelect();
-}
-
-void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos)
-{
- LLScrollListColumn::Params p;
- LLParamSDParser parser;
- parser.readSD(column, p);
- addColumn(p, pos);
-}
-
-void LLScrollListCtrl::addColumn(const LLScrollListColumn::Params& column_params, EAddPosition pos)
-{
- if (!column_params.validateBlock()) return;
-
- std::string name = column_params.name;
- // if no column name provided, just use ordinal as name
- if (name.empty())
- {
- name = llformat("%d", mColumnsIndexed.size());
- }
-
- if (mColumns.find(name) == mColumns.end())
- {
- // Add column
- mColumns[name] = new LLScrollListColumn(column_params, this);
- LLScrollListColumn* new_column = mColumns[name];
- new_column->mIndex = mColumns.size()-1;
-
- // Add button
- if (new_column->getWidth() > 0 || new_column->mRelWidth > 0 || new_column->mDynamicWidth)
- {
- if (getNumColumns() > 0)
- {
- mTotalColumnPadding += mColumnPadding;
- }
- if (new_column->mRelWidth >= 0)
- {
- new_column->setWidth((S32)ll_round(new_column->mRelWidth*mItemListRect.getWidth()));
- }
- else if(new_column->mDynamicWidth)
- {
- mNumDynamicWidthColumns++;
- new_column->setWidth((mItemListRect.getWidth() - mTotalStaticColumnWidth - mTotalColumnPadding) / mNumDynamicWidthColumns);
- }
- S32 top = mItemListRect.mTop;
-
- S32 left = mItemListRect.mLeft;
- for (column_map_t::iterator itor = mColumns.begin();
- itor != mColumns.end();
- ++itor)
- {
- if (itor->second->mIndex < new_column->mIndex &&
- itor->second->getWidth() > 0)
- {
- left += itor->second->getWidth() + mColumnPadding;
- }
- }
-
- S32 right = left+new_column->getWidth();
- if (new_column->mIndex != (S32)mColumns.size()-1)
- {
- right += mColumnPadding;
- }
-
- LLRect temp_rect = LLRect(left,top+mHeadingHeight,right,top);
-
- LLScrollColumnHeader::Params params(LLUICtrlFactory::getDefaultParams<LLScrollColumnHeader>());
- params.name = "btn_" + name;
- params.rect = temp_rect;
- params.column = new_column;
- params.tool_tip = column_params.tool_tip;
- params.tab_stop = false;
- params.visible = mDisplayColumnHeaders;
-
- if(column_params.header.image.isProvided())
- {
- params.image_selected = column_params.header.image;
- params.image_unselected = column_params.header.image;
- }
- else
- {
- params.label = column_params.header.label;
- }
-
- new_column->mHeader = LLUICtrlFactory::create<LLScrollColumnHeader>(params);
- addChild(new_column->mHeader);
-
- sendChildToFront(mScrollbar);
- }
- }
-
- dirtyColumns();
-}
-
-// static
-void LLScrollListCtrl::onClickColumn(void *userdata)
-{
- LLScrollListColumn *info = (LLScrollListColumn*)userdata;
- if (!info) return;
-
- LLScrollListCtrl *parent = info->mParentCtrl;
- if (!parent) return;
-
- if (!parent->mCanSort) return;
-
- S32 column_index = info->mIndex;
-
- LLScrollListColumn* column = parent->mColumnsIndexed[info->mIndex];
- bool ascending = column->mSortDirection == LLScrollListColumn::ASCENDING;
- if (column->mSortingColumn != column->mName
- && parent->mColumns.find(column->mSortingColumn) != parent->mColumns.end())
- {
- LLScrollListColumn* info_redir = parent->mColumns[column->mSortingColumn];
- column_index = info_redir->mIndex;
- }
-
- // if this column is the primary sort key, reverse the direction
- if (!parent->mSortColumns.empty() && parent->mSortColumns.back().first == column_index)
- {
- ascending = !parent->mSortColumns.back().second;
- }
-
- parent->sortByColumnIndex(column_index, ascending);
-
- if (parent->mOnSortChangedCallback)
- {
- parent->mOnSortChangedCallback();
- }
-}
-
-std::string LLScrollListCtrl::getSortColumnName()
-{
- LLScrollListColumn* column = mSortColumns.empty() ? NULL : mColumnsIndexed[mSortColumns.back().first];
-
- if (column) return column->mName;
- else return "";
-}
-
-bool LLScrollListCtrl::hasSortOrder() const
-{
- return !mSortColumns.empty();
-}
-
-void LLScrollListCtrl::clearSortOrder()
-{
- mSortColumns.clear();
-}
-
-void LLScrollListCtrl::clearColumns()
-{
- column_map_t::iterator itor;
- for (itor = mColumns.begin(); itor != mColumns.end(); ++itor)
- {
- LLScrollColumnHeader *header = itor->second->mHeader;
- if (header)
- {
- removeChild(header);
- delete header;
- }
- }
- std::for_each(mColumns.begin(), mColumns.end(), DeletePairedPointer());
- mColumns.clear();
- mSortColumns.clear();
- mTotalStaticColumnWidth = 0;
- mTotalColumnPadding = 0;
-
- dirtyColumns(); // Clears mColumnsIndexed
-}
-
-void LLScrollListCtrl::setColumnLabel(const std::string& column, const std::string& label)
-{
- LLScrollListColumn* columnp = getColumn(column);
- if (columnp)
- {
- columnp->mLabel = label;
- if (columnp->mHeader)
- {
- columnp->mHeader->setLabel(label);
- }
- }
-}
-
-LLScrollListColumn* LLScrollListCtrl::getColumn(S32 index)
-{
- if (index < 0 || index >= (S32)mColumnsIndexed.size())
- {
- return NULL;
- }
- return mColumnsIndexed[index];
-}
-
-LLScrollListColumn* LLScrollListCtrl::getColumn(const std::string& name)
-{
- column_map_t::iterator column_itor = mColumns.find(name);
- if (column_itor != mColumns.end())
- {
- return column_itor->second;
- }
- return NULL;
-}
-
-LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& element, EAddPosition pos, void* userdata)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
- LLScrollListItem::Params item_params;
- LLParamSDParser parser;
- parser.readSD(element, item_params);
- item_params.userdata = userdata;
- return addRow(item_params, pos);
-}
-
-LLScrollListItem* LLScrollListCtrl::addRow(const LLScrollListItem::Params& item_p, EAddPosition pos)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
- 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)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
- if (!item_p.validateBlock() || !new_item) return NULL;
- new_item->setNumColumns(mColumns.size());
-
- // Add any columns we don't already have
- S32 col_index = 0;
-
- for(LLInitParam::ParamIterator<LLScrollListCell::Params>::const_iterator itor = item_p.columns.begin();
- itor != item_p.columns.end();
- ++itor)
- {
- LLScrollListCell::Params cell_p = *itor;
- std::string column = cell_p.column;
-
- // empty columns strings index by ordinal
- if (column.empty())
- {
- column = llformat("%d", col_index);
- }
-
- LLScrollListColumn* columnp = getColumn(column);
-
- // create new column on demand
- if (!columnp)
- {
- LLScrollListColumn::Params new_column;
- new_column.name = column;
- new_column.header.label = column;
-
- // if width supplied for column, use it, otherwise
- // use adaptive width
- if (cell_p.width.isProvided())
- {
- new_column.width.pixel_width = cell_p.width;
- }
- addColumn(new_column);
- columnp = mColumns[column];
- new_item->setNumColumns(mColumns.size());
- }
-
- S32 index = columnp->mIndex;
- if (!cell_p.width.isProvided())
- {
- cell_p.width = columnp->getWidth();
- }
-
- LLScrollListCell* cell = LLScrollListCell::create(cell_p);
-
- if (cell)
- {
- new_item->setColumn(index, cell);
- if (columnp->mHeader
- && cell->isText()
- && !cell->getValue().asString().empty())
- {
- columnp->mHeader->setHasResizableElement(true);
- }
- }
-
- col_index++;
- }
-
- if (item_p.columns.empty())
- {
- if (mColumns.empty())
- {
- LLScrollListColumn::Params new_column;
- new_column.name = "0";
-
- addColumn(new_column);
- new_item->setNumColumns(mColumns.size());
- }
-
- LLScrollListCell* cell = LLScrollListCell::create(LLScrollListCell::Params().value(item_p.value));
- if (cell)
- {
- LLScrollListColumn* columnp = mColumns.begin()->second;
-
- new_item->setColumn(0, cell);
- if (columnp->mHeader
- && cell->isText()
- && !cell->getValue().asString().empty())
- {
- columnp->mHeader->setHasResizableElement(true);
- }
- }
- }
-
- // add dummy cells for missing columns
- for (column_map_t::iterator column_it = mColumns.begin(); column_it != mColumns.end(); ++column_it)
- {
- S32 column_idx = column_it->second->mIndex;
- if (new_item->getColumn(column_idx) == NULL)
- {
- LLScrollListColumn* column_ptr = column_it->second;
- LLScrollListCell::Params cell_p;
- cell_p.width = column_ptr->getWidth();
-
- new_item->setColumn(column_idx, new LLScrollListSpacer(cell_p));
- }
- }
-
- addItem(new_item, pos);
- return new_item;
-}
-
-LLScrollListItem* LLScrollListCtrl::addSimpleElement(const std::string& value, EAddPosition pos, const LLSD& id)
-{
- LLSD entry_id = id;
-
- if (id.isUndefined())
- {
- entry_id = value;
- }
-
- LLScrollListItem::Params item_params;
- item_params.value(entry_id);
- item_params.columns.add()
- .value(value)
- .font(LLFontGL::getFontSansSerifSmall());
-
- return addRow(item_params, pos);
-}
-
-void LLScrollListCtrl::setValue(const LLSD& value )
-{
- LLSD::array_const_iterator itor;
- for (itor = value.beginArray(); itor != value.endArray(); ++itor)
- {
- addElement(*itor);
- }
-}
-
-LLSD LLScrollListCtrl::getValue() const
-{
- LLScrollListItem *item = getFirstSelected();
- if (!item) return LLSD();
- return item->getValue();
-}
-
-bool LLScrollListCtrl::operateOnSelection(EOperation op)
-{
- if (op == OP_DELETE)
- {
- deleteSelectedItems();
- return true;
- }
- else if (op == OP_DESELECT)
- {
- deselectAllItems();
- }
- return false;
-}
-
-bool LLScrollListCtrl::operateOnAll(EOperation op)
-{
- if (op == OP_DELETE)
- {
- clearRows();
- return true;
- }
- else if (op == OP_DESELECT)
- {
- deselectAllItems();
- }
- else if (op == OP_SELECT)
- {
- selectAll();
- }
- return false;
-}
-//virtual
-void LLScrollListCtrl::setFocus(bool b)
-{
- // for tabbing into pristine scroll lists (Finder)
- if (!getFirstSelected())
- {
- selectFirstItem();
- //onCommit(); // SJB: selectFirstItem() will call onCommit() if appropriate
- }
- LLUICtrl::setFocus(b);
-}
-
-
-// virtual
-bool LLScrollListCtrl::isDirty() const
-{
- bool grubby = mDirty;
- if ( !mAllowMultipleSelection )
- {
- grubby = (mOriginalSelection != getFirstSelectedIndex());
- }
- return grubby;
-}
-
-// Clear dirty state
-void LLScrollListCtrl::resetDirty()
-{
- mDirty = false;
- mOriginalSelection = getFirstSelectedIndex();
-}
-
-
-//virtual
-void LLScrollListCtrl::onFocusReceived()
-{
- // forget latent selection changes when getting focus
- mSelectionChanged = false;
- LLUICtrl::onFocusReceived();
-}
-
-//virtual
-void LLScrollListCtrl::onFocusLost()
-{
- if (hasMouseCapture())
- {
- gFocusMgr.setMouseCapture(NULL);
- }
-
- mSearchString.clear();
-
- LLUICtrl::onFocusLost();
-}
-
-boost::signals2::connection LLScrollListCtrl::setIsFriendCallback(const is_friend_signal_t::slot_type& cb)
-{
- if (!mIsFriendSignal)
- {
- mIsFriendSignal = new is_friend_signal_t();
- }
- return mIsFriendSignal->connect(cb);
-}
-
-bool LLScrollListCtrl::highlightMatchingItems(const std::string& filter_str)
-{
- if (filter_str == "" || filter_str == " ")
- {
- clearHighlightedItems();
- return false;
- }
-
- bool res = false;
-
- setHighlightedColor(LLUIColorTable::instance().getColor("SearchableControlHighlightColor", LLColor4::red));
-
- std::string filter_str_lc(filter_str);
- LLStringUtil::toLower(filter_str_lc);
-
- std::vector<LLScrollListItem*> data = getAllData();
- std::vector<LLScrollListItem*>::iterator iter = data.begin();
- while (iter != data.end())
- {
- LLScrollListCell* cell = (*iter)->getColumn(0);
- if (cell)
- {
- std::string value = cell->getValue().asString();
- LLStringUtil::toLower(value);
- if (value.find(filter_str_lc) == std::string::npos)
- {
- (*iter)->setHighlighted(false);
- }
- else
- {
- (*iter)->setHighlighted(true);
- res = true;
- }
- }
- iter++;
- }
- return res;
-}
+ /**
+ * @file llscrolllistctrl.cpp
+ * @brief Scroll lists are composed of rows (items), each of which
+ * contains columns (cells).
+ *
+ * $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 "llscrolllistctrl.h"
+
+#include <algorithm>
+
+#include "llstl.h"
+#include "llboost.h"
+//#include "indra_constants.h"
+
+#include "llavatarnamecache.h"
+#include "llcheckboxctrl.h"
+#include "llclipboard.h"
+#include "llfocusmgr.h"
+#include "llgl.h" // LLGLSUIDefault()
+#include "lllocalcliprect.h"
+//#include "llrender.h"
+#include "llresmgr.h"
+#include "llscrollbar.h"
+#include "llscrolllistcell.h"
+#include "llstring.h"
+#include "llui.h"
+#include "lluictrlfactory.h"
+#include "llwindow.h"
+#include "llcontrol.h"
+#include "llkeyboard.h"
+#include "llviewborder.h"
+#include "lltextbox.h"
+#include "llsdparam.h"
+#include "llcachename.h"
+#include "llmenugl.h"
+#include "llurlaction.h"
+#include "lltooltip.h"
+
+#include <boost/bind.hpp>
+
+static LLDefaultChildRegistry::Register<LLScrollListCtrl> r("scroll_list");
+
+// local structures & classes.
+struct SortScrollListItem
+{
+ SortScrollListItem(const std::vector<std::pair<S32, bool> >& sort_orders,const LLScrollListCtrl::sort_signal_t* sort_signal, bool alternate_sort)
+ : mSortOrders(sort_orders)
+ , mSortSignal(sort_signal)
+ , mAltSort(alternate_sort)
+ {}
+
+ bool operator()(const LLScrollListItem* i1, const LLScrollListItem* i2)
+ {
+ // sort over all columns in order specified by mSortOrders
+ S32 sort_result = 0;
+ for (sort_order_t::const_reverse_iterator it = mSortOrders.rbegin();
+ it != mSortOrders.rend(); ++it)
+ {
+ S32 col_idx = it->first;
+ bool sort_ascending = it->second;
+
+ S32 order = sort_ascending ? 1 : -1; // ascending or descending sort for this column?
+
+ const LLScrollListCell *cell1 = i1->getColumn(col_idx);
+ const LLScrollListCell *cell2 = i2->getColumn(col_idx);
+ if (cell1 && cell2)
+ {
+ if(mSortSignal)
+ {
+ sort_result = order * (*mSortSignal)(col_idx,i1, i2);
+ }
+ else
+ {
+ if (mAltSort && !cell1->getAltValue().asString().empty() && !cell2->getAltValue().asString().empty())
+ {
+ sort_result = order * LLStringUtil::compareDict(cell1->getAltValue().asString(), cell2->getAltValue().asString());
+ }
+ else
+ {
+ sort_result = order * LLStringUtil::compareDict(cell1->getValue().asString(), cell2->getValue().asString());
+ }
+ }
+ if (sort_result != 0)
+ {
+ break; // we have a sort order!
+ }
+ }
+ }
+
+ return sort_result < 0;
+ }
+
+
+ typedef std::vector<std::pair<S32, bool> > sort_order_t;
+ const LLScrollListCtrl::sort_signal_t* mSortSignal;
+ const sort_order_t& mSortOrders;
+ const bool mAltSort;
+};
+
+//---------------------------------------------------------------------------
+// LLScrollListCtrl
+//---------------------------------------------------------------------------
+
+void LLScrollListCtrl::SelectionTypeNames::declareValues()
+{
+ declare("row", LLScrollListCtrl::ROW);
+ declare("cell", LLScrollListCtrl::CELL);
+ declare("header", LLScrollListCtrl::HEADER);
+}
+
+LLScrollListCtrl::Contents::Contents()
+: columns("column"),
+ rows("row")
+{
+ addSynonym(columns, "columns");
+ addSynonym(rows, "rows");
+}
+
+LLScrollListCtrl::Params::Params()
+: multi_select("multi_select", false),
+ has_border("draw_border"),
+ draw_heading("draw_heading"),
+ search_column("search_column", 0),
+ selection_type("selection_type", ROW),
+ sort_column("sort_column", -1),
+ sort_ascending("sort_ascending", true),
+ can_sort("can_sort", true),
+ mouse_wheel_opaque("mouse_wheel_opaque", false),
+ commit_on_keyboard_movement("commit_on_keyboard_movement", true),
+ commit_on_selection_change("commit_on_selection_change", false),
+ heading_height("heading_height"),
+ page_lines("page_lines", 0),
+ background_visible("background_visible"),
+ draw_stripes("draw_stripes"),
+ column_padding("column_padding"),
+ row_padding("row_padding", 2),
+ fg_unselected_color("fg_unselected_color"),
+ fg_selected_color("fg_selected_color"),
+ bg_selected_color("bg_selected_color"),
+ fg_disable_color("fg_disable_color"),
+ bg_writeable_color("bg_writeable_color"),
+ bg_readonly_color("bg_readonly_color"),
+ bg_stripe_color("bg_stripe_color"),
+ hovered_color("hovered_color"),
+ highlighted_color("highlighted_color"),
+ contents(""),
+ scroll_bar_bg_visible("scroll_bar_bg_visible"),
+ scroll_bar_bg_color("scroll_bar_bg_color"),
+ border("border")
+{}
+
+LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p)
+: LLUICtrl(p),
+ mLineHeight(0),
+ mScrollLines(0),
+ mMouseWheelOpaque(p.mouse_wheel_opaque),
+ mPageLines(p.page_lines),
+ mMaxSelectable(0),
+ mAllowKeyboardMovement(true),
+ mCommitOnKeyboardMovement(p.commit_on_keyboard_movement),
+ mCommitOnSelectionChange(p.commit_on_selection_change),
+ mSelectionChanged(false),
+ mSelectionType(p.selection_type),
+ mNeedsScroll(false),
+ mCanSelect(true),
+ mCanSort(p.can_sort),
+ mColumnsDirty(false),
+ mMaxItemCount(INT_MAX),
+ mBorderThickness( 2 ),
+ mOnDoubleClickCallback( NULL ),
+ mOnMaximumSelectCallback( NULL ),
+ mOnSortChangedCallback( NULL ),
+ mHighlightedItem(-1),
+ mBorder(NULL),
+ mSortCallback(NULL),
+ mCommentTextView(NULL),
+ mNumDynamicWidthColumns(0),
+ mTotalStaticColumnWidth(0),
+ mTotalColumnPadding(0),
+ mSorted(false),
+ mDirty(false),
+ mOriginalSelection(-1),
+ mLastSelected(NULL),
+ mHeadingHeight(p.heading_height),
+ mAllowMultipleSelection(p.multi_select),
+ mDisplayColumnHeaders(p.draw_heading),
+ mBackgroundVisible(p.background_visible),
+ mDrawStripes(p.draw_stripes),
+ mBgWriteableColor(p.bg_writeable_color()),
+ mBgReadOnlyColor(p.bg_readonly_color()),
+ mBgSelectedColor(p.bg_selected_color()),
+ mBgStripeColor(p.bg_stripe_color()),
+ mFgSelectedColor(p.fg_selected_color()),
+ mFgUnselectedColor(p.fg_unselected_color()),
+ mFgDisabledColor(p.fg_disable_color()),
+ mHighlightedColor(p.highlighted_color()),
+ mHoveredColor(p.hovered_color()),
+ mSearchColumn(p.search_column),
+ mColumnPadding(p.column_padding),
+ mRowPadding(p.row_padding),
+ mAlternateSort(false),
+ mContextMenuType(MENU_NONE),
+ mIsFriendSignal(NULL)
+{
+ mItemListRect.setOriginAndSize(
+ mBorderThickness,
+ mBorderThickness,
+ getRect().getWidth() - 2 * mBorderThickness,
+ getRect().getHeight() - 2 * mBorderThickness );
+
+ updateLineHeight();
+
+ // Init the scrollbar
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+
+ LLRect scroll_rect;
+ scroll_rect.setOriginAndSize(
+ getRect().getWidth() - mBorderThickness - scrollbar_size,
+ mItemListRect.mBottom,
+ scrollbar_size,
+ mItemListRect.getHeight());
+
+ LLScrollbar::Params sbparams;
+ sbparams.name("Scrollbar");
+ sbparams.rect(scroll_rect);
+ sbparams.orientation(LLScrollbar::VERTICAL);
+ sbparams.doc_size(getItemCount());
+ sbparams.doc_pos(mScrollLines);
+ sbparams.page_size( getLinesPerPage() );
+ sbparams.change_callback(boost::bind(&LLScrollListCtrl::onScrollChange, this, _1, _2));
+ sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
+ sbparams.visible(false);
+ sbparams.bg_visible(p.scroll_bar_bg_visible);
+ sbparams.bg_color(p.scroll_bar_bg_color);
+ mScrollbar = LLUICtrlFactory::create<LLScrollbar> (sbparams);
+ addChild(mScrollbar);
+
+ // Border
+ if (p.has_border)
+ {
+ LLRect border_rect = getLocalRect();
+ LLViewBorder::Params params = p.border;
+ params.rect(border_rect);
+ mBorder = LLUICtrlFactory::create<LLViewBorder> (params);
+ addChild(mBorder);
+ }
+
+ // set border *after* rect is fully initialized
+ if (mBorder)
+ {
+ mBorder->setRect(getLocalRect());
+ mBorder->reshape(getRect().getWidth(), getRect().getHeight());
+ }
+
+ if (p.sort_column >= 0)
+ {
+ sortByColumnIndex(p.sort_column, p.sort_ascending);
+ }
+
+
+ for (LLInitParam::ParamIterator<LLScrollListColumn::Params>::const_iterator row_it = p.contents.columns.begin();
+ row_it != p.contents.columns.end();
+ ++row_it)
+ {
+ addColumn(*row_it);
+ }
+
+ for (LLInitParam::ParamIterator<LLScrollListItem::Params>::const_iterator row_it = p.contents.rows.begin();
+ row_it != p.contents.rows.end();
+ ++row_it)
+ {
+ addRow(*row_it);
+ }
+
+ LLTextBox::Params text_p;
+ text_p.name("comment_text");
+ text_p.border_visible(false);
+ text_p.rect(mItemListRect);
+ text_p.follows.flags(FOLLOWS_ALL);
+ // word wrap was added accroding to the EXT-6841
+ text_p.wrap(true);
+ addChild(LLUICtrlFactory::create<LLTextBox>(text_p));
+}
+
+S32 LLScrollListCtrl::getSearchColumn()
+{
+ // search for proper search column
+ if (mSearchColumn < 0)
+ {
+ LLScrollListItem* itemp = getFirstData();
+ if (itemp)
+ {
+ for(S32 column = 0; column < getNumColumns(); column++)
+ {
+ LLScrollListCell* cell = itemp->getColumn(column);
+ if (cell && cell->isText())
+ {
+ mSearchColumn = column;
+ break;
+ }
+ }
+ }
+ }
+ return llclamp(mSearchColumn, 0, getNumColumns());
+}
+/*virtual*/
+bool LLScrollListCtrl::preProcessChildNode(LLXMLNodePtr child)
+{
+ if (child->hasName("column") || child->hasName("row"))
+ {
+ return true; // skip
+ }
+ else
+ {
+ return false;
+ }
+}
+
+LLScrollListCtrl::~LLScrollListCtrl()
+{
+ delete mSortCallback;
+
+ std::for_each(mItemList.begin(), mItemList.end(), DeletePointer());
+ mItemList.clear();
+ clearColumns(); //clears columns and deletes headers
+ delete mIsFriendSignal;
+
+ auto menu = mPopupMenuHandle.get();
+ if (menu)
+ {
+ menu->die();
+ mPopupMenuHandle.markDead();
+ }
+}
+
+
+bool LLScrollListCtrl::setMaxItemCount(S32 max_count)
+{
+ if (max_count >= getItemCount())
+ {
+ mMaxItemCount = max_count;
+ }
+ return (max_count == mMaxItemCount);
+}
+
+S32 LLScrollListCtrl::isEmpty() const
+{
+ return mItemList.empty();
+}
+
+S32 LLScrollListCtrl::getItemCount() const
+{
+ return mItemList.size();
+}
+
+bool LLScrollListCtrl::hasSelectedItem() const
+{
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter < mItemList.end(); )
+ {
+ LLScrollListItem* itemp = *iter;
+ if (itemp && itemp->getSelected())
+ {
+ return true;
+ }
+ iter++;
+ }
+ return false;
+}
+
+// virtual LLScrolListInterface function (was deleteAllItems)
+void LLScrollListCtrl::clearRows()
+{
+ std::for_each(mItemList.begin(), mItemList.end(), DeletePointer());
+ mItemList.clear();
+ //mItemCount = 0;
+
+ // Scroll the bar back up to the top.
+ mScrollbar->setDocParams(0, 0);
+
+ mScrollLines = 0;
+ mLastSelected = NULL;
+ updateLayout();
+ mDirty = false;
+}
+
+
+LLScrollListItem* LLScrollListCtrl::getFirstSelected() const
+{
+ item_list::const_iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ if (item->getSelected())
+ {
+ return item;
+ }
+ }
+ return NULL;
+}
+
+std::vector<LLScrollListItem*> LLScrollListCtrl::getAllSelected() const
+{
+ std::vector<LLScrollListItem*> ret;
+ item_list::const_iterator iter;
+ for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ if (item->getSelected())
+ {
+ ret.push_back(item);
+ }
+ }
+ 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;
+
+ // make sure sort is up to date before returning an index
+ updateSort();
+
+ item_list::const_iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ if (item->getSelected())
+ {
+ return CurSelectedIndex;
+ }
+ CurSelectedIndex++;
+ }
+
+ return -1;
+}
+
+LLScrollListItem* LLScrollListCtrl::getFirstData() const
+{
+ if (mItemList.size() == 0)
+ {
+ return NULL;
+ }
+ return mItemList[0];
+}
+
+LLScrollListItem* LLScrollListCtrl::getLastData() const
+{
+ if (mItemList.size() == 0)
+ {
+ return NULL;
+ }
+ return mItemList[mItemList.size() - 1];
+}
+
+std::vector<LLScrollListItem*> LLScrollListCtrl::getAllData() const
+{
+ std::vector<LLScrollListItem*> ret;
+ item_list::const_iterator iter;
+ for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ ret.push_back(item);
+ }
+ return ret;
+}
+
+// returns first matching item
+LLScrollListItem* LLScrollListCtrl::getItem(const LLSD& sd) const
+{
+ std::string string_val = sd.asString();
+
+ item_list::const_iterator iter;
+ for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ // assumes string representation is good enough for comparison
+ if (item->getValue().asString() == string_val)
+ {
+ return item;
+ }
+ }
+ return NULL;
+}
+
+
+void LLScrollListCtrl::reshape( S32 width, S32 height, bool called_from_parent )
+{
+ LLUICtrl::reshape( width, height, called_from_parent );
+
+ updateLayout();
+}
+
+void LLScrollListCtrl::updateLayout()
+{
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+ // reserve room for column headers, if needed
+ S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0);
+ mItemListRect.setOriginAndSize(
+ mBorderThickness,
+ mBorderThickness,
+ getRect().getWidth() - 2 * mBorderThickness,
+ getRect().getHeight() - (2 * mBorderThickness ) - heading_size );
+
+ if (mCommentTextView == NULL)
+ {
+ mCommentTextView = getChildView("comment_text");
+ }
+
+ mCommentTextView->setShape(mItemListRect);
+
+ // how many lines of content in a single "page"
+ S32 page_lines = getLinesPerPage();
+
+ bool scrollbar_visible = mLineHeight * getItemCount() > mItemListRect.getHeight();
+ if (scrollbar_visible)
+ {
+ // provide space on the right for scrollbar
+ mItemListRect.mRight = getRect().getWidth() - mBorderThickness - scrollbar_size;
+ }
+
+ mScrollbar->setOrigin(getRect().getWidth() - mBorderThickness - scrollbar_size, mItemListRect.mBottom);
+ mScrollbar->reshape(scrollbar_size, mItemListRect.getHeight() + (mDisplayColumnHeaders ? mHeadingHeight : 0));
+ mScrollbar->setPageSize(page_lines);
+ mScrollbar->setDocSize( getItemCount() );
+ mScrollbar->setVisible(scrollbar_visible);
+
+ dirtyColumns();
+}
+
+// Attempt to size the control to show all items.
+// Do not make larger than width or height.
+void LLScrollListCtrl::fitContents(S32 max_width, S32 max_height)
+{
+ S32 height = llmin( getRequiredRect().getHeight(), max_height );
+ if(mPageLines)
+ height = llmin( mPageLines * mLineHeight + 2*mBorderThickness + (mDisplayColumnHeaders ? mHeadingHeight : 0), height );
+
+ S32 width = getRect().getWidth();
+
+ reshape( width, height );
+}
+
+
+LLRect LLScrollListCtrl::getRequiredRect()
+{
+ S32 heading_size = (mDisplayColumnHeaders ? mHeadingHeight : 0);
+ S32 height = (mLineHeight * getItemCount())
+ + (2 * mBorderThickness )
+ + heading_size;
+ S32 width = getRect().getWidth();
+
+ return LLRect(0, height, width, 0);
+}
+
+
+bool LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, bool requires_column )
+{
+ bool not_too_big = getItemCount() < mMaxItemCount;
+ if (not_too_big)
+ {
+ switch( pos )
+ {
+ case ADD_TOP:
+ mItemList.push_front(item);
+ setNeedsSort();
+ break;
+
+ case ADD_DEFAULT:
+ case ADD_BOTTOM:
+ mItemList.push_back(item);
+ setNeedsSort();
+ break;
+
+ default:
+ llassert(0);
+ mItemList.push_back(item);
+ setNeedsSort();
+ break;
+ }
+
+ // create new column on demand
+ if (mColumns.empty() && requires_column)
+ {
+ LLScrollListColumn::Params col_params;
+ col_params.name = "default_column";
+ col_params.header.label = "";
+ col_params.width.dynamic_width = true;
+ 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();
+ }
+
+ return not_too_big;
+}
+
+// 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
+S32 LLScrollListCtrl::calcMaxContentWidth()
+{
+ const S32 HEADING_TEXT_PADDING = 25;
+ const S32 COLUMN_TEXT_PADDING = 10;
+
+ S32 max_item_width = 0;
+
+ ordered_columns_t::iterator column_itor;
+ for (column_itor = mColumnsIndexed.begin(); column_itor != mColumnsIndexed.end(); ++column_itor)
+ {
+ 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 = 0;
+ if (column->mRelWidth >= 0)
+ {
+ new_width = (S32)ll_round(column->mRelWidth*mItemListRect.getWidth());
+ }
+ else if (column->mDynamicWidth)
+ {
+ new_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth - mTotalColumnPadding) / mNumDynamicWidthColumns;
+ }
+ else
+ {
+ new_width = column->getWidth();
+ }
+
+ if (column->getWidth() != new_width)
+ {
+ column->setWidth(new_width);
+ width_changed = true;
+ }
+ }
+ return width_changed;
+}
+
+// Line height is the max height of all the cells in all the items.
+void LLScrollListCtrl::updateLineHeight()
+{
+ mLineHeight = 0;
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem *itemp = *iter;
+ S32 num_cols = itemp->getNumColumns();
+ S32 i = 0;
+ for (const LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i))
+ {
+ mLineHeight = llmax( mLineHeight, cell->getHeight() + mRowPadding );
+ }
+ }
+}
+
+// when the only change to line height is from an insert, we needn't scan the entire list
+void LLScrollListCtrl::updateLineHeightInsert(LLScrollListItem* itemp)
+{
+ S32 num_cols = itemp->getNumColumns();
+ S32 i = 0;
+ for (const LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i))
+ {
+ mLineHeight = llmax( mLineHeight, cell->getHeight() + mRowPadding );
+ }
+}
+
+
+void LLScrollListCtrl::updateColumns(bool force_update)
+{
+ if (!mColumnsDirty && !force_update)
+ return;
+
+ mColumnsDirty = false;
+
+ bool columns_changed_width = updateColumnWidths();
+
+ // update column headers
+ std::vector<LLScrollListColumn*>::iterator column_ordered_it;
+ S32 left = mItemListRect.mLeft;
+ LLScrollColumnHeader* last_header = NULL;
+ for (column_ordered_it = mColumnsIndexed.begin(); column_ordered_it != mColumnsIndexed.end(); ++column_ordered_it)
+ {
+ LLScrollListColumn* column = *column_ordered_it;
+ if (!column || column->getWidth() < 0)
+ {
+ // skip hidden columns
+ continue;
+ }
+
+ if (column->mHeader)
+ {
+ column->mHeader->updateResizeBars();
+
+ last_header = column->mHeader;
+ S32 top = mItemListRect.mTop;
+ S32 right = left + column->getWidth();
+
+ if (column->mIndex != (S32)mColumnsIndexed.size()-1)
+ {
+ right += mColumnPadding;
+ }
+ right = llmax(left, llmin(mItemListRect.getWidth(), right));
+ S32 header_width = right - left;
+
+ last_header->reshape(header_width, mHeadingHeight);
+ last_header->translate(
+ left - last_header->getRect().mLeft,
+ top - last_header->getRect().mBottom);
+ last_header->setVisible(mDisplayColumnHeaders && header_width > 0);
+ left = right;
+ }
+ }
+
+ bool header_changed_width = false;
+ // expand last column header we encountered to full list width
+ if (last_header)
+ {
+ S32 old_width = last_header->getColumn()->getWidth();
+ S32 new_width = llmax(0, mItemListRect.mRight - last_header->getRect().mLeft);
+ last_header->reshape(new_width, last_header->getRect().getHeight());
+ last_header->setVisible(mDisplayColumnHeaders && new_width > 0);
+ if (old_width != new_width)
+ {
+ last_header->getColumn()->setWidth(new_width);
+ header_changed_width = true;
+ }
+ }
+
+ // propagate column widths to individual cells
+ if (columns_changed_width || force_update)
+ {
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ 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());
+ }
+ }
+ }
+ else if (header_changed_width)
+ {
+ item_list::iterator iter;
+ S32 index = last_header->getColumn()->mIndex; // Not always identical to last column!
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem *itemp = *iter;
+ LLScrollListCell* cell = itemp->getColumn(index);
+ if (cell)
+ {
+ cell->setWidth(last_header->getColumn()->getWidth());
+ }
+ }
+ }
+}
+
+void LLScrollListCtrl::setHeadingHeight(S32 heading_height)
+{
+ mHeadingHeight = heading_height;
+
+ updateLayout();
+
+}
+void LLScrollListCtrl::setPageLines(S32 new_page_lines)
+{
+ mPageLines = new_page_lines;
+
+ updateLayout();
+}
+
+bool LLScrollListCtrl::selectFirstItem()
+{
+ bool success = false;
+
+ // our $%&@#$()^%#$()*^ iterators don't let us check against the first item inside out iteration
+ bool first_item = true;
+
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem *itemp = *iter;
+ if( first_item && itemp->getEnabled() )
+ {
+ if (!itemp->getSelected())
+ {
+ switch (mSelectionType)
+ {
+ case CELL:
+ selectItem(itemp, 0);
+ break;
+ case HEADER:
+ case ROW:
+ selectItem(itemp, -1);
+ }
+ }
+ success = true;
+ mOriginalSelection = 0;
+ }
+ else
+ {
+ deselectItem(itemp);
+ }
+ first_item = false;
+ }
+ if (mCommitOnSelectionChange)
+ {
+ commitIfChanged();
+ }
+ return success;
+}
+
+// Deselects all other items
+// virtual
+bool LLScrollListCtrl::selectNthItem( S32 target_index )
+{
+ return selectItemRange(target_index, target_index);
+}
+
+// virtual
+bool LLScrollListCtrl::selectItemRange( S32 first_index, S32 last_index )
+{
+ if (mItemList.empty())
+ {
+ return false;
+ }
+
+ // make sure sort is up to date
+ updateSort();
+
+ S32 listlen = (S32)mItemList.size();
+ first_index = llclamp(first_index, 0, listlen-1);
+
+ if (last_index < 0)
+ last_index = listlen-1;
+ else
+ last_index = llclamp(last_index, first_index, listlen-1);
+
+ bool success = false;
+ S32 index = 0;
+ for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); )
+ {
+ LLScrollListItem *itemp = *iter;
+ if(!itemp)
+ {
+ iter = mItemList.erase(iter);
+ continue ;
+ }
+
+ if( index >= first_index && index <= last_index )
+ {
+ if( itemp->getEnabled() )
+ {
+ // TODO: support range selection for cells
+ selectItem(itemp, -1, false);
+ success = true;
+ }
+ }
+ else
+ {
+ deselectItem(itemp);
+ }
+ index++;
+ iter++ ;
+ }
+
+ if (mCommitOnSelectionChange)
+ {
+ commitIfChanged();
+ }
+
+ mSearchString.clear();
+
+ return success;
+}
+
+
+void LLScrollListCtrl::swapWithNext(S32 index)
+{
+ if (index >= ((S32)mItemList.size() - 1))
+ {
+ // At end of list, doesn't do anything
+ return;
+ }
+ updateSort();
+ LLScrollListItem *cur_itemp = mItemList[index];
+ mItemList[index] = mItemList[index + 1];
+ mItemList[index + 1] = cur_itemp;
+}
+
+
+void LLScrollListCtrl::swapWithPrevious(S32 index)
+{
+ if (index <= 0)
+ {
+ // At beginning of list, don't do anything
+ }
+
+ updateSort();
+ LLScrollListItem *cur_itemp = mItemList[index];
+ mItemList[index] = mItemList[index - 1];
+ mItemList[index - 1] = cur_itemp;
+}
+
+
+void LLScrollListCtrl::deleteSingleItem(S32 target_index)
+{
+ if (target_index < 0 || target_index >= (S32)mItemList.size())
+ {
+ return;
+ }
+
+ updateSort();
+
+ LLScrollListItem *itemp;
+ itemp = mItemList[target_index];
+ if (itemp == mLastSelected)
+ {
+ mLastSelected = NULL;
+ }
+ delete itemp;
+ mItemList.erase(mItemList.begin() + target_index);
+ dirtyColumns();
+}
+
+//FIXME: refactor item deletion
+void LLScrollListCtrl::deleteItems(const LLSD& sd)
+{
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter < mItemList.end(); )
+ {
+ LLScrollListItem* itemp = *iter;
+ if (itemp->getValue().asString() == sd.asString())
+ {
+ if (itemp == mLastSelected)
+ {
+ mLastSelected = NULL;
+ }
+ delete itemp;
+ iter = mItemList.erase(iter);
+ }
+ else
+ {
+ iter++;
+ }
+ }
+
+ dirtyColumns();
+}
+
+void LLScrollListCtrl::deleteSelectedItems()
+{
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter < mItemList.end(); )
+ {
+ LLScrollListItem* itemp = *iter;
+ if (itemp->getSelected())
+ {
+ delete itemp;
+ iter = mItemList.erase(iter);
+ }
+ else
+ {
+ iter++;
+ }
+ }
+ mLastSelected = NULL;
+ dirtyColumns();
+}
+
+void LLScrollListCtrl::clearHighlightedItems()
+{
+ for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); ++iter)
+ {
+ (*iter)->setHighlighted(false);
+ }
+}
+
+void LLScrollListCtrl::mouseOverHighlightNthItem(S32 target_index)
+{
+ if (mHighlightedItem != target_index)
+ {
+ if (mHighlightedItem >= 0 && mHighlightedItem < mItemList.size())
+ {
+ mItemList[mHighlightedItem]->setHoverCell(-1);
+ }
+ mHighlightedItem = target_index;
+ }
+}
+
+S32 LLScrollListCtrl::selectMultiple( uuid_vec_t ids )
+{
+ item_list::iterator iter;
+ S32 count = 0;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ uuid_vec_t::iterator iditr;
+ for(iditr = ids.begin(); iditr != ids.end(); ++iditr)
+ {
+ if (item->getEnabled() && (item->getUUID() == (*iditr)))
+ {
+ // TODO: support multiple selection for cells
+ selectItem(item, -1, false);
+ ++count;
+ break;
+ }
+ }
+ if(ids.end() != iditr) ids.erase(iditr);
+ }
+
+ if (mCommitOnSelectionChange)
+ {
+ commitIfChanged();
+ }
+ return count;
+}
+
+S32 LLScrollListCtrl::getItemIndex( LLScrollListItem* target_item ) const
+{
+ updateSort();
+
+ S32 index = 0;
+ item_list::const_iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem *itemp = *iter;
+ if (target_item == itemp)
+ {
+ return index;
+ }
+ index++;
+ }
+ return -1;
+}
+
+S32 LLScrollListCtrl::getItemIndex( const LLUUID& target_id ) const
+{
+ updateSort();
+
+ S32 index = 0;
+ item_list::const_iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem *itemp = *iter;
+ if (target_id == itemp->getUUID())
+ {
+ return index;
+ }
+ index++;
+ }
+ return -1;
+}
+
+void LLScrollListCtrl::selectPrevItem( bool extend_selection)
+{
+ LLScrollListItem* prev_item = NULL;
+
+ if (!getFirstSelected())
+ {
+ // select last item
+ selectNthItem(getItemCount() - 1);
+ }
+ else
+ {
+ updateSort();
+
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* cur_item = *iter;
+
+ if (cur_item->getSelected())
+ {
+ if (prev_item)
+ {
+ selectItem(prev_item, cur_item->getSelectedCell(), !extend_selection);
+ }
+ else
+ {
+ reportInvalidInput();
+ }
+ break;
+ }
+
+ // don't allow navigation to disabled elements
+ prev_item = cur_item->getEnabled() ? cur_item : prev_item;
+ }
+ }
+
+ if ((mCommitOnSelectionChange || mCommitOnKeyboardMovement))
+ {
+ commitIfChanged();
+ }
+
+ mSearchString.clear();
+}
+
+
+void LLScrollListCtrl::selectNextItem( bool extend_selection)
+{
+ LLScrollListItem* next_item = NULL;
+
+ if (!getFirstSelected())
+ {
+ selectFirstItem();
+ }
+ else
+ {
+ updateSort();
+
+ item_list::reverse_iterator iter;
+ for (iter = mItemList.rbegin(); iter != mItemList.rend(); iter++)
+ {
+ LLScrollListItem* cur_item = *iter;
+
+ if (cur_item->getSelected())
+ {
+ if (next_item)
+ {
+ selectItem(next_item, cur_item->getSelectedCell(), !extend_selection);
+ }
+ else
+ {
+ reportInvalidInput();
+ }
+ break;
+ }
+
+ // don't allow navigation to disabled items
+ next_item = cur_item->getEnabled() ? cur_item : next_item;
+ }
+ }
+
+ if (mCommitOnKeyboardMovement)
+ {
+ onCommit();
+ }
+
+ mSearchString.clear();
+}
+
+
+
+void LLScrollListCtrl::deselectAllItems(bool no_commit_on_change)
+{
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ deselectItem(item);
+ }
+
+ if (mCommitOnSelectionChange && !no_commit_on_change)
+ {
+ commitIfChanged();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Use this to add comment text such as "Searching", which ignores column settings of list
+
+void LLScrollListCtrl::setCommentText(const std::string& comment_text)
+{
+ getChild<LLTextBox>("comment_text")->setValue(comment_text);
+}
+
+LLScrollListItem* LLScrollListCtrl::addSeparator(EAddPosition pos)
+{
+ LLScrollListItem::Params separator_params;
+ separator_params.enabled(false);
+ LLScrollListCell::Params column_params;
+ column_params.type = "icon";
+ column_params.value = "menu_separator";
+ column_params.color = LLColor4(0.f, 0.f, 0.f, 0.7f);
+ column_params.font_halign = LLFontGL::HCENTER;
+ separator_params.columns.add(column_params);
+ return addRow( separator_params, pos );
+}
+
+// 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, 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, column);
+
+ bool found = NULL != item;
+ if (found)
+ {
+ selectItem(item, -1);
+ }
+
+ if (mCommitOnSelectionChange)
+ {
+ commitIfChanged();
+ }
+
+ return found;
+}
+
+LLScrollListItem* LLScrollListCtrl::getItemByLabel(const std::string& label, bool case_sensitive, S32 column)
+{
+ if (label.empty()) //RN: assume no empty items
+ {
+ return NULL;
+ }
+
+ std::string target_text = label;
+ if (!case_sensitive)
+ {
+ LLStringUtil::toLower(target_text);
+ }
+
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ std::string item_text = item->getColumn(column)->getValue().asString(); // Only select enabled items with matching names
+ if (!case_sensitive)
+ {
+ LLStringUtil::toLower(item_text);
+ }
+ if(item_text == target_text)
+ {
+ return item;
+ }
+ }
+ return NULL;
+}
+
+
+bool LLScrollListCtrl::selectItemByPrefix(const std::string& target, bool case_sensitive, S32 column)
+{
+ return selectItemByPrefix(utf8str_to_wstring(target), case_sensitive, column);
+}
+
+// Selects first enabled item that has a name where the name's first part matched the target string.
+// Returns false if item not found.
+bool LLScrollListCtrl::selectItemByPrefix(const LLWString& target, bool case_sensitive, S32 column)
+{
+ bool found = false;
+
+ LLWString target_trimmed( target );
+ S32 target_len = target_trimmed.size();
+
+ if( 0 == target_len )
+ {
+ // Is "" a valid choice?
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ // Only select enabled items with matching names
+ LLScrollListCell* cellp = item->getColumn(column == -1 ? getSearchColumn() : column);
+ bool select = cellp ? item->getEnabled() && ('\0' == cellp->getValue().asString()[0]) : false;
+ if (select)
+ {
+ selectItem(item, -1);
+ found = true;
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (!case_sensitive)
+ {
+ // do comparisons in lower case
+ LLWStringUtil::toLower(target_trimmed);
+ }
+
+ for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+
+ // Only select enabled items with matching names
+ LLScrollListCell* cellp = item->getColumn(column == -1 ? getSearchColumn() : column);
+ if (!cellp)
+ {
+ continue;
+ }
+ LLWString item_label = utf8str_to_wstring(cellp->getValue().asString());
+ if (!case_sensitive)
+ {
+ LLWStringUtil::toLower(item_label);
+ }
+ // remove extraneous whitespace from searchable label
+ LLWString trimmed_label = item_label;
+ LLWStringUtil::trim(trimmed_label);
+
+ bool select = item->getEnabled() && trimmed_label.compare(0, target_trimmed.size(), target_trimmed) == 0;
+
+ if (select)
+ {
+ // find offset of matching text (might have leading whitespace)
+ S32 offset = item_label.find(target_trimmed);
+ cellp->highlightText(offset, target_trimmed.size());
+ selectItem(item, -1);
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (mCommitOnSelectionChange)
+ {
+ commitIfChanged();
+ }
+
+ return found;
+}
+
+U32 LLScrollListCtrl::searchItems(const std::string& substring, bool case_sensitive, bool focus)
+{
+ return searchItems(utf8str_to_wstring(substring), case_sensitive, focus);
+}
+
+U32 LLScrollListCtrl::searchItems(const LLWString& substring, bool case_sensitive, bool focus)
+{
+ U32 found = 0;
+
+ LLWString substring_trimmed(substring);
+ S32 len = substring_trimmed.size();
+
+ if (0 == len)
+ {
+ // at the moment search for empty element is not supported
+ return 0;
+ }
+ else
+ {
+ deselectAllItems(true);
+ if (!case_sensitive)
+ {
+ // do comparisons in lower case
+ LLWStringUtil::toLower(substring_trimmed);
+ }
+
+ for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ // Only select enabled items with matching names
+ if (!item->getEnabled())
+ {
+ continue;
+ }
+ LLScrollListCell* cellp = item->getColumn(getSearchColumn());
+ if (!cellp)
+ {
+ continue;
+ }
+ LLWString item_label = utf8str_to_wstring(cellp->getValue().asString());
+ if (!case_sensitive)
+ {
+ LLWStringUtil::toLower(item_label);
+ }
+ // remove extraneous whitespace from searchable label
+ LLWStringUtil::trim(item_label);
+
+ size_t found_iter = item_label.find(substring_trimmed);
+
+ if (found_iter != std::string::npos)
+ {
+ // find offset of matching text
+ cellp->highlightText(found_iter, substring_trimmed.size());
+ selectItem(item, -1, false);
+
+ found++;
+
+ if (!mAllowMultipleSelection)
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ if (focus && found != 0)
+ {
+ mNeedsScroll = true;
+ }
+
+ if (mCommitOnSelectionChange)
+ {
+ commitIfChanged();
+ }
+
+ return found;
+}
+
+const std::string LLScrollListCtrl::getSelectedItemLabel(S32 column) const
+{
+ LLScrollListItem* item;
+
+ item = getFirstSelected();
+ if (item)
+ {
+ return item->getColumn(column)->getValue().asString();
+ }
+
+ return LLStringUtil::null;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// "StringUUID" interface: use this when you're creating a list that contains non-unique strings each of which
+// has an associated, unique UUID, and only one of which can be selected at a time.
+
+LLScrollListItem* LLScrollListCtrl::addStringUUIDItem(const std::string& item_text, const LLUUID& id, EAddPosition pos, bool enabled)
+{
+ if (getItemCount() < mMaxItemCount)
+ {
+ LLScrollListItem::Params item_p;
+ item_p.enabled(enabled);
+ item_p.value(id);
+ item_p.columns.add().value(item_text).type("text");
+
+ return addRow( item_p, pos );
+ }
+ return NULL;
+}
+
+// Select the line or lines that match this UUID
+bool LLScrollListCtrl::selectByID( const LLUUID& id )
+{
+ return selectByValue( LLSD(id) );
+}
+
+bool LLScrollListCtrl::setSelectedByValue(const LLSD& value, bool selected)
+{
+ bool found = false;
+
+ if (selected && !mAllowMultipleSelection) deselectAllItems(true);
+
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ if (item->getEnabled())
+ {
+ if (value.isBinary())
+ {
+ if (item->getValue().isBinary())
+ {
+ LLSD::Binary data1 = value.asBinary();
+ LLSD::Binary data2 = item->getValue().asBinary();
+ found = std::equal(data1.begin(), data1.end(), data2.begin());
+ }
+ }
+ else
+ {
+ found = item->getValue().asString() == value.asString();
+ }
+
+ if (found)
+ {
+ if (selected)
+ {
+ selectItem(item, -1);
+ }
+ else
+ {
+ deselectItem(item);
+ }
+ break;
+ }
+ }
+ }
+
+ if (mCommitOnSelectionChange)
+ {
+ commitIfChanged();
+ }
+
+ return found;
+}
+
+bool LLScrollListCtrl::isSelected(const LLSD& value) const
+{
+ item_list::const_iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ if (item->getValue().asString() == value.asString())
+ {
+ return item->getSelected();
+ }
+ }
+ return false;
+}
+
+LLUUID LLScrollListCtrl::getStringUUIDSelectedItem() const
+{
+ LLScrollListItem* item = getFirstSelected();
+
+ if (item)
+ {
+ return item->getUUID();
+ }
+
+ return LLUUID::null;
+}
+
+LLSD LLScrollListCtrl::getSelectedValue()
+{
+ LLScrollListItem* item = getFirstSelected();
+
+ if (item)
+ {
+ return item->getValue();
+ }
+ else
+ {
+ return LLSD();
+ }
+}
+
+void LLScrollListCtrl::drawItems()
+{
+ S32 x = mItemListRect.mLeft;
+ S32 y = mItemListRect.mTop - mLineHeight;
+
+ // allow for partial line at bottom
+ S32 num_page_lines = getLinesPerPage();
+
+ LLRect item_rect;
+
+ LLGLSUIDefault gls_ui;
+
+ F32 alpha = getDrawContext().mAlpha;
+
+ {
+ LLLocalClipRect clip(mItemListRect);
+
+ S32 cur_y = y;
+
+ S32 max_columns = 0;
+
+ LLColor4 highlight_color = LLColor4::white; // ex: text inside cells
+ 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);
+
+ 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 (S32 line = first_line; line <= last_line; line++)
+ {
+ LLScrollListItem* item = mItemList[line];
+
+ item_rect.setOriginAndSize(
+ x,
+ cur_y,
+ mItemListRect.getWidth(),
+ mLineHeight );
+ item->setRect(item_rect);
+
+ max_columns = llmax(max_columns, item->getNumColumns());
+
+ LLColor4 fg_color;
+ LLColor4 hover_color(LLColor4::transparent);
+ LLColor4 select_color(LLColor4::transparent);
+
+ if( mScrollLines <= line && line < mScrollLines + num_page_lines )
+ {
+ fg_color = (item->getEnabled() ? mFgUnselectedColor.get() : mFgDisabledColor.get());
+ if( item->getSelected() && mCanSelect)
+ {
+ if(item->getHighlighted()) // if it's highlighted, average the colors
+ {
+ select_color = lerp(mBgSelectedColor.get(), mHighlightedColor.get(), 0.5f);
+ }
+ else // otherwise just select-highlight it
+ {
+ select_color = mBgSelectedColor.get();
+ }
+
+ fg_color = (item->getEnabled() ? mFgSelectedColor.get() : mFgDisabledColor.get());
+ }
+ if (mHighlightedItem == line && mCanSelect)
+ {
+ if(item->getHighlighted()) // if it's highlighted, average the colors
+ {
+ hover_color = lerp(mHoveredColor.get(), mHighlightedColor.get(), 0.5f);
+ }
+ else // otherwise just hover-highlight it
+ {
+ hover_color = mHoveredColor.get();
+ }
+ }
+ else if (item->getHighlighted())
+ {
+ hover_color = mHighlightedColor.get();
+ }
+ else
+ {
+ if (mDrawStripes && (line % 2 == 0) && (max_columns > 1))
+ {
+ hover_color = mBgStripeColor.get();
+ }
+ }
+
+ if (!item->getEnabled())
+ {
+ hover_color = mBgReadOnlyColor.get();
+ }
+
+ item->draw(item_rect, fg_color % alpha, hover_color% alpha, select_color% alpha, highlight_color % alpha, mColumnPadding);
+
+ cur_y -= mLineHeight;
+ }
+ }
+ }
+}
+
+
+void LLScrollListCtrl::draw()
+{
+ LLLocalClipRect clip(getLocalRect());
+
+ // if user specifies sort, make sure it is maintained
+ updateSort();
+
+ if (mNeedsScroll)
+ {
+ scrollToShowSelected();
+ mNeedsScroll = false;
+ }
+ LLRect background(0, getRect().getHeight(), getRect().getWidth(), 0);
+ // Draw background
+ if (mBackgroundVisible)
+ {
+ F32 alpha = getCurrentTransparency();
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gl_rect_2d(background, getEnabled() ? mBgWriteableColor.get() % alpha : mBgReadOnlyColor.get() % alpha );
+ }
+
+ updateColumns();
+
+ getChildView("comment_text")->setVisible(mItemList.empty());
+
+ drawItems();
+
+ if (mBorder)
+ {
+ mBorder->setKeyboardFocusHighlight(hasFocus());
+ }
+
+ LLUICtrl::draw();
+}
+
+void LLScrollListCtrl::setEnabled(bool enabled)
+{
+ mCanSelect = enabled;
+ setTabStop(enabled);
+ mScrollbar->setTabStop(!enabled && mScrollbar->getPageSize() < mScrollbar->getDocSize());
+}
+
+bool LLScrollListCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ bool handled = false;
+ // Pretend the mouse is over the scrollbar
+ handled = mScrollbar->handleScrollWheel( 0, 0, clicks );
+
+ if (mMouseWheelOpaque)
+ {
+ return true;
+ }
+
+ return handled;
+}
+
+bool LLScrollListCtrl::handleScrollHWheel(S32 x, S32 y, S32 clicks)
+{
+ bool handled = false;
+ // Pretend the mouse is over the scrollbar
+ handled = mScrollbar->handleScrollHWheel( 0, 0, clicks );
+
+ if (mMouseWheelOpaque)
+ {
+ return true;
+ }
+
+ return handled;
+}
+
+// *NOTE: Requires a valid row_index and column_index
+LLRect LLScrollListCtrl::getCellRect(S32 row_index, S32 column_index)
+{
+ LLRect cell_rect;
+ S32 rect_left = getColumnOffsetFromIndex(column_index) + mItemListRect.mLeft;
+ S32 rect_bottom = getRowOffsetFromIndex(row_index);
+ LLScrollListColumn* columnp = getColumn(column_index);
+ cell_rect.setOriginAndSize(rect_left, rect_bottom,
+ /*rect_left + */columnp->getWidth(), mLineHeight);
+ return cell_rect;
+}
+
+bool LLScrollListCtrl::handleToolTip(S32 x, S32 y, MASK mask)
+{
+ S32 column_index = getColumnIndexFromOffset(x);
+ LLScrollListColumn* columnp = getColumn(column_index);
+
+ if (columnp == NULL) return false;
+
+ bool handled = false;
+ // show tooltip for full name of hovered item if it has been truncated
+ LLScrollListItem* hit_item = hitItem(x, y);
+ if (hit_item)
+ {
+ LLScrollListCell* hit_cell = hit_item->getColumn(column_index);
+ if (!hit_cell) return false;
+ if (hit_cell
+ && hit_cell->isText()
+ && hit_cell->needsToolTip())
+ {
+ S32 row_index = getItemIndex(hit_item);
+ LLRect cell_rect = getCellRect(row_index, column_index);
+ // Convert rect local to screen coordinates
+ LLRect sticky_rect;
+ localRectToScreen(cell_rect, &sticky_rect);
+
+ // display tooltip exactly over original cell, in same font
+ LLToolTipMgr::instance().show(LLToolTip::Params()
+ .message(hit_cell->getToolTip())
+ .font(LLFontGL::getFontEmojiSmall())
+ .pos(LLCoordGL(sticky_rect.mLeft - 5, sticky_rect.mTop + 6))
+ .delay_time(0.2f)
+ .sticky_rect(sticky_rect));
+ }
+ handled = true;
+ }
+
+ // otherwise, look for a tooltip associated with this column
+ LLScrollColumnHeader* headerp = columnp->mHeader;
+ if (headerp && !handled)
+ {
+ handled = headerp->handleToolTip(x, y, mask);
+ }
+
+ return handled;
+}
+
+bool LLScrollListCtrl::selectItemAt(S32 x, S32 y, MASK mask)
+{
+ if (!mCanSelect) return false;
+
+ bool selection_changed = false;
+
+ LLScrollListItem* hit_item = hitItem(x, y);
+
+ if( hit_item )
+ {
+ if( mAllowMultipleSelection )
+ {
+ if (mask & MASK_SHIFT)
+ {
+ if (mLastSelected == NULL)
+ {
+ selectItem(hit_item, getColumnIndexFromOffset(x));
+ }
+ else
+ {
+ // Select everthing between mLastSelected and hit_item
+ bool selecting = false;
+ item_list::iterator itor;
+ // If we multiselect backwards, we'll stomp on mLastSelected,
+ // meaning that we never stop selecting until hitting max or
+ // the end of the list.
+ LLScrollListItem* lastSelected = mLastSelected;
+ for (itor = mItemList.begin(); itor != mItemList.end(); ++itor)
+ {
+ if(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable)
+ {
+ if(mOnMaximumSelectCallback)
+ {
+ mOnMaximumSelectCallback();
+ }
+ break;
+ }
+ LLScrollListItem *item = *itor;
+ if (item == hit_item || item == lastSelected)
+ {
+ selectItem(item, getColumnIndexFromOffset(x), false);
+ selecting = !selecting;
+ if (hit_item == lastSelected)
+ {
+ // stop selecting now, since we just clicked on our last selected item
+ selecting = false;
+ }
+ }
+ if (selecting)
+ {
+ selectItem(item, getColumnIndexFromOffset(x), false);
+ }
+ }
+ }
+ }
+ else if (mask & MASK_CONTROL)
+ {
+ if (hit_item->getSelected())
+ {
+ deselectItem(hit_item);
+ }
+ else
+ {
+ if(!(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable))
+ {
+ selectItem(hit_item, getColumnIndexFromOffset(x), false);
+ }
+ else
+ {
+ if(mOnMaximumSelectCallback)
+ {
+ mOnMaximumSelectCallback();
+ }
+ }
+ }
+ }
+ else
+ {
+ deselectAllItems(true);
+ selectItem(hit_item, getColumnIndexFromOffset(x));
+ }
+ }
+ else
+ {
+ selectItem(hit_item, getColumnIndexFromOffset(x));
+ }
+
+ selection_changed = mSelectionChanged;
+ if (mCommitOnSelectionChange)
+ {
+ commitIfChanged();
+ }
+
+ // clear search string on mouse operations
+ mSearchString.clear();
+ }
+ else
+ {
+ //mLastSelected = NULL;
+ //deselectAllItems(true);
+ }
+
+ return selection_changed;
+}
+
+
+bool LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ bool handled = childrenHandleMouseDown(x, y, mask) != NULL;
+
+ if( !handled )
+ {
+ // set keyboard focus first, in case click action wants to move focus elsewhere
+ setFocus(true);
+
+ // clear selection changed flag because user is starting a selection operation
+ mSelectionChanged = false;
+
+ handleClick(x, y, mask);
+ }
+
+ return true;
+}
+
+bool LLScrollListCtrl::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ if (hasMouseCapture())
+ {
+ // release mouse capture immediately so
+ // scroll to show selected logic will work
+ gFocusMgr.setMouseCapture(NULL);
+ if(mask == MASK_NONE)
+ {
+ selectItemAt(x, y, mask);
+ mNeedsScroll = true;
+ }
+ }
+
+ // always commit when mouse operation is completed inside list
+ if (mItemListRect.pointInRect(x,y))
+ {
+ mDirty = mDirty || mSelectionChanged;
+ mSelectionChanged = false;
+ onCommit();
+ }
+
+ return LLUICtrl::handleMouseUp(x, y, mask);
+}
+
+// virtual
+bool LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLScrollListItem *item = hitItem(x, y);
+ if (item)
+ {
+ // check to see if we have a UUID for this row
+ std::string id = item->getValue().asString();
+ LLUUID uuid(id);
+ if (! uuid.isNull() && mContextMenuType != MENU_NONE)
+ {
+ // set up the callbacks for all of the avatar/group menu items
+ // (N.B. callbacks don't take const refs as id is local scope)
+ bool is_group = (mContextMenuType == MENU_GROUP);
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+ registrar.add("Url.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.RemoveFriend", boost::bind(&LLScrollListCtrl::removeFriend, id));
+ registrar.add("Url.ReportAbuse", boost::bind(&LLScrollListCtrl::reportAbuse, id, is_group));
+ registrar.add("Url.Execute", boost::bind(&LLScrollListCtrl::showNameDetails, id, is_group));
+ registrar.add("Url.CopyLabel", boost::bind(&LLScrollListCtrl::copyNameToClipboard, id, is_group));
+ registrar.add("Url.CopyUrl", boost::bind(&LLScrollListCtrl::copySLURLToClipboard, id, is_group));
+
+ // create the context menu from the XUI file and display it
+ std::string menu_name = is_group ? "menu_url_group.xml" : "menu_url_agent.xml";
+ auto menu = mPopupMenuHandle.get();
+ if (menu)
+ {
+ menu->die();
+ mPopupMenuHandle.markDead();
+ }
+ llassert(LLMenuGL::sMenuContainer != NULL);
+ menu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(
+ menu_name, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
+ if (menu)
+ {
+ mPopupMenuHandle = menu->getHandle();
+ if (mIsFriendSignal)
+ {
+ bool isFriend = *(*mIsFriendSignal)(uuid);
+ LLView* addFriendButton = menu->getChild<LLView>("add_friend");
+ LLView* removeFriendButton = menu->getChild<LLView>("remove_friend");
+
+ if (addFriendButton && removeFriendButton)
+ {
+ addFriendButton->setEnabled(!isFriend);
+ removeFriendButton->setEnabled(isFriend);
+ }
+ }
+
+ menu->show(x, y);
+ LLMenuGL::showPopup(this, menu, x, y);
+ return true;
+ }
+ }
+ return LLUICtrl::handleRightMouseDown(x, y, mask);
+ }
+ return false;
+}
+
+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::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::removeFriend(std::string id)
+{
+ std::string slurl = "secondlife:///app/agent/" + id + "/about";
+ LLUrlAction::removeFriend(slurl);
+}
+
+void LLScrollListCtrl::reportAbuse(std::string id, bool is_group)
+{
+ if (!is_group)
+ {
+ std::string slurl = "secondlife:///app/agent/" + id + "/about";
+ LLUrlAction::reportAbuse(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)
+{
+ // copy the name of the avatar or group to the clipboard
+ std::string name;
+ if (is_group)
+ {
+ gCacheName->getGroupName(LLUUID(id), name);
+ }
+ else
+ {
+ LLAvatarName av_name;
+ LLAvatarNameCache::get(LLUUID(id), &av_name);
+ name = av_name.getAccountName();
+ }
+ LLUrlAction::copyURLToClipboard(name);
+}
+
+void LLScrollListCtrl::copySLURLToClipboard(std::string id, bool is_group)
+{
+ // copy a SLURL for the avatar or group to the clipboard
+ std::string sltype = is_group ? "group" : "agent";
+ std::string slurl = "secondlife:///app/" + sltype + "/" + id + "/about";
+ LLUrlAction::copyURLToClipboard(slurl);
+}
+
+bool LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ //bool handled = false;
+ bool handled = handleClick(x, y, mask);
+
+ if (!handled)
+ {
+ // Offer the click to the children, even if we aren't enabled
+ // so the scroll bars will work.
+ if (NULL == LLView::childrenHandleDoubleClick(x, y, mask))
+ {
+ // Run the callback only if an item is being double-clicked.
+ if( mCanSelect && hitItem(x, y) && mOnDoubleClickCallback )
+ {
+ mOnDoubleClickCallback();
+ }
+ }
+ }
+
+ return true;
+}
+
+bool LLScrollListCtrl::handleClick(S32 x, S32 y, MASK mask)
+{
+ // which row was clicked on?
+ LLScrollListItem* hit_item = hitItem(x, y);
+ if (!hit_item) return false;
+
+ // get appropriate cell from that row
+ S32 column_index = getColumnIndexFromOffset(x);
+ LLScrollListCell* hit_cell = hit_item->getColumn(column_index);
+ if (!hit_cell) return false;
+
+ // if cell handled click directly (i.e. clicked on an embedded checkbox)
+ if (hit_cell->handleClick())
+ {
+ // if item not currently selected, select it
+ if (!hit_item->getSelected())
+ {
+ selectItemAt(x, y, mask);
+ gFocusMgr.setMouseCapture(this);
+ mNeedsScroll = true;
+ }
+
+ // propagate state of cell to rest of selected column
+ {
+ // propagate value of this cell to other selected items
+ // and commit the respective widgets
+ LLSD item_value = hit_cell->getValue();
+ for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ if (item->getSelected())
+ {
+ LLScrollListCell* cellp = item->getColumn(column_index);
+ cellp->setValue(item_value);
+ cellp->onCommit();
+ if (mLastSelected == NULL)
+ {
+ break;
+ }
+ }
+ }
+ //FIXME: find a better way to signal cell changes
+ onCommit();
+ }
+ // eat click (e.g. do not trigger double click callback)
+ return true;
+ }
+ else
+ {
+ // treat this as a normal single item selection
+ selectItemAt(x, y, mask);
+ gFocusMgr.setMouseCapture(this);
+ mNeedsScroll = true;
+ // do not eat click (allow double click callback)
+ return false;
+ }
+}
+
+LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y )
+{
+ // Excludes disabled items.
+ LLScrollListItem* hit_item = NULL;
+
+ updateSort();
+
+ LLRect item_rect;
+ item_rect.setLeftTopAndSize(
+ mItemListRect.mLeft,
+ mItemListRect.mTop,
+ mItemListRect.getWidth(),
+ mLineHeight );
+
+ // allow for partial line at bottom
+ S32 num_page_lines = getLinesPerPage();
+
+ S32 line = 0;
+ item_list::iterator iter;
+ for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ if( mScrollLines <= line && line < mScrollLines + num_page_lines )
+ {
+ if( item->getEnabled() && item_rect.pointInRect( x, y ) )
+ {
+ hit_item = item;
+ break;
+ }
+
+ item_rect.translate(0, -mLineHeight);
+ }
+ line++;
+ }
+
+ return hit_item;
+}
+
+S32 LLScrollListCtrl::getColumnIndexFromOffset(S32 x)
+{
+ // which column did we hit?
+ S32 left = 0;
+ S32 right = 0;
+ S32 width = 0;
+ S32 column_index = 0;
+
+ ordered_columns_t::const_iterator iter = mColumnsIndexed.begin();
+ ordered_columns_t::const_iterator end = mColumnsIndexed.end();
+ for ( ; iter != end; ++iter)
+ {
+ width = (*iter)->getWidth() + mColumnPadding;
+ right += width;
+ if (left <= x && x < right )
+ {
+ break;
+ }
+
+ // set left for next column as right of current column
+ left = right;
+ column_index++;
+ }
+
+ return llclamp(column_index, 0, getNumColumns() - 1);
+}
+
+
+S32 LLScrollListCtrl::getColumnOffsetFromIndex(S32 index)
+{
+ S32 column_offset = 0;
+ ordered_columns_t::const_iterator iter = mColumnsIndexed.begin();
+ ordered_columns_t::const_iterator end = mColumnsIndexed.end();
+ for ( ; iter != end; ++iter)
+ {
+ if (index-- <= 0)
+ {
+ return column_offset;
+ }
+ column_offset += (*iter)->getWidth() + mColumnPadding;
+ }
+
+ // when running off the end, return the rightmost pixel
+ return mItemListRect.mRight;
+}
+
+S32 LLScrollListCtrl::getRowOffsetFromIndex(S32 index)
+{
+ S32 row_bottom = (mItemListRect.mTop - ((index - mScrollLines + 1) * mLineHeight) );
+ return row_bottom;
+}
+
+
+bool LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask)
+{
+ bool handled = false;
+
+ if (hasMouseCapture())
+ {
+ if(mask == MASK_NONE)
+ {
+ selectItemAt(x, y, mask);
+ mNeedsScroll = true;
+ }
+ }
+ else
+ if (mCanSelect)
+ {
+ LLScrollListItem* item = hitItem(x, y);
+ if (item)
+ {
+ mouseOverHighlightNthItem(getItemIndex(item));
+ switch (mSelectionType)
+ {
+ case CELL:
+ item->setHoverCell(getColumnIndexFromOffset(x));
+ break;
+ case HEADER:
+ {
+ S32 cell = getColumnIndexFromOffset(x);
+ if (cell > 0)
+ {
+ item->setHoverCell(cell);
+ }
+ else
+ {
+ item->setHoverCell(-1);
+ }
+ break;
+ }
+ case ROW:
+ break;
+ }
+ }
+ else
+ {
+ mouseOverHighlightNthItem(-1);
+ }
+ }
+
+ handled = LLUICtrl::handleHover( x, y, mask );
+
+ return handled;
+}
+
+void LLScrollListCtrl::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+ // clear mouse highlight
+ mouseOverHighlightNthItem(-1);
+}
+
+bool LLScrollListCtrl::handleKeyHere(KEY key,MASK mask )
+{
+ bool handled = false;
+
+ // not called from parent means we have keyboard focus or a child does
+ if (mCanSelect)
+ {
+ if (mask == MASK_NONE)
+ {
+ switch(key)
+ {
+ case KEY_UP:
+ if (mAllowKeyboardMovement || hasFocus())
+ {
+ // commit implicit in call
+ selectPrevItem(false);
+ mNeedsScroll = true;
+ handled = true;
+ }
+ break;
+ case KEY_DOWN:
+ if (mAllowKeyboardMovement || hasFocus())
+ {
+ // commit implicit in call
+ selectNextItem(false);
+ mNeedsScroll = true;
+ handled = true;
+ }
+ break;
+ case KEY_LEFT:
+ if (mAllowKeyboardMovement || hasFocus())
+ {
+ // TODO: support multi-select
+ LLScrollListItem *item = getFirstSelected();
+ if (item)
+ {
+ S32 cell = item->getSelectedCell();
+ switch (mSelectionType)
+ {
+ case CELL:
+ if (cell < mColumns.size()) cell++;
+ break;
+ case HEADER:
+ if (cell == -1) cell = 1;
+ else if (cell > 1 && cell < mColumns.size()) cell++; // skip header
+ break;
+ case ROW:
+ cell = -1;
+ break;
+ }
+ item->setSelectedCell(cell);
+ handled = true;
+ }
+ }
+ break;
+ case KEY_RIGHT:
+ if (mAllowKeyboardMovement || hasFocus())
+ {
+ // TODO: support multi-select
+ LLScrollListItem *item = getFirstSelected();
+ if (item)
+ {
+ S32 cell = item->getSelectedCell();
+ switch (mSelectionType)
+ {
+ case CELL:
+ if (cell >= 0) cell--;
+ break;
+ case HEADER:
+ if (cell > 1) cell--;
+ else if (cell == 1) cell = -1; // skip header
+ break;
+ case ROW:
+ cell = -1;
+ break;
+ }
+ item->setSelectedCell(cell);
+ handled = true;
+ }
+ }
+ break;
+ case KEY_PAGE_UP:
+ if (mAllowKeyboardMovement || hasFocus())
+ {
+ selectNthItem(getFirstSelectedIndex() - (mScrollbar->getPageSize() - 1));
+ mNeedsScroll = true;
+ if (mCommitOnKeyboardMovement
+ && !mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+ handled = true;
+ }
+ break;
+ case KEY_PAGE_DOWN:
+ if (mAllowKeyboardMovement || hasFocus())
+ {
+ selectNthItem(getFirstSelectedIndex() + (mScrollbar->getPageSize() - 1));
+ mNeedsScroll = true;
+ if (mCommitOnKeyboardMovement
+ && !mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+ handled = true;
+ }
+ break;
+ case KEY_HOME:
+ if (mAllowKeyboardMovement || hasFocus())
+ {
+ selectFirstItem();
+ mNeedsScroll = true;
+ if (mCommitOnKeyboardMovement
+ && !mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+ handled = true;
+ }
+ break;
+ case KEY_END:
+ if (mAllowKeyboardMovement || hasFocus())
+ {
+ selectNthItem(getItemCount() - 1);
+ mNeedsScroll = true;
+ if (mCommitOnKeyboardMovement
+ && !mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+ handled = true;
+ }
+ break;
+ case KEY_RETURN:
+ // JC - Special case: Only claim to have handled it
+ // if we're the special non-commit-on-move
+ // type. AND we are visible
+ if (!mCommitOnKeyboardMovement && mask == MASK_NONE)
+ {
+ onCommit();
+ mSearchString.clear();
+ handled = true;
+ }
+ break;
+ case KEY_BACKSPACE:
+ mSearchTimer.reset();
+ if (mSearchString.size())
+ {
+ mSearchString.erase(mSearchString.size() - 1, 1);
+ }
+ if (mSearchString.empty())
+ {
+ if (getFirstSelected())
+ {
+ LLScrollListCell* cellp = getFirstSelected()->getColumn(getSearchColumn());
+ if (cellp)
+ {
+ cellp->highlightText(0, 0);
+ }
+ }
+ }
+ else if (selectItemByPrefix(wstring_to_utf8str(mSearchString), false))
+ {
+ mNeedsScroll = true;
+ // update search string only on successful match
+ mSearchTimer.reset();
+
+ if (mCommitOnKeyboardMovement
+ && !mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ // TODO: multiple: shift-up, shift-down, shift-home, shift-end, select all
+ }
+
+ return handled;
+}
+
+bool LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char)
+{
+ if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL
+ {
+ return false;
+ }
+
+ // perform incremental search based on keyboard input
+ static LLUICachedControl<F32> type_ahead_timeout ("TypeAheadTimeout", 0);
+ if (mSearchTimer.getElapsedTimeF32() > type_ahead_timeout)
+ {
+ mSearchString.clear();
+ }
+
+ // type ahead search is case insensitive
+ uni_char = LLStringOps::toLower((llwchar)uni_char);
+
+ if (selectItemByPrefix(wstring_to_utf8str(mSearchString + (llwchar)uni_char), false))
+ {
+ // update search string only on successful match
+ mNeedsScroll = true;
+ mSearchString += uni_char;
+ mSearchTimer.reset();
+
+ if (mCommitOnKeyboardMovement
+ && !mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+ }
+ // handle iterating over same starting character
+ else if (isRepeatedChars(mSearchString + (llwchar)uni_char) && !mItemList.empty())
+ {
+ // start from last selected item, in case we previously had a successful match against
+ // duplicated characters ('AA' matches 'Aaron')
+ item_list::iterator start_iter = mItemList.begin();
+ S32 first_selected = getFirstSelectedIndex();
+
+ // if we have a selection (> -1) then point iterator at the selected item
+ if (first_selected > 0)
+ {
+ // point iterator to first selected item
+ start_iter += first_selected;
+ }
+
+ // start search at first item after current selection
+ item_list::iterator iter = start_iter;
+ ++iter;
+ if (iter == mItemList.end())
+ {
+ iter = mItemList.begin();
+ }
+
+ // loop around once, back to previous selection
+ while(iter != start_iter)
+ {
+ LLScrollListItem* item = *iter;
+
+ LLScrollListCell* cellp = item->getColumn(getSearchColumn());
+ if (cellp)
+ {
+ // Only select enabled items with matching first characters
+ LLWString item_label = utf8str_to_wstring(cellp->getValue().asString());
+ if (item->getEnabled() && LLStringOps::toLower(item_label[0]) == uni_char)
+ {
+ selectItem(item, -1);
+ mNeedsScroll = true;
+ cellp->highlightText(0, 1);
+ mSearchTimer.reset();
+
+ if (mCommitOnKeyboardMovement
+ && !mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+
+ break;
+ }
+ }
+
+ ++iter;
+ if (iter == mItemList.end())
+ {
+ iter = mItemList.begin();
+ }
+ }
+ }
+
+ return true;
+}
+
+
+void LLScrollListCtrl::reportInvalidInput()
+{
+ make_ui_sound("UISndBadKeystroke");
+}
+
+bool LLScrollListCtrl::isRepeatedChars(const LLWString& string) const
+{
+ if (string.empty())
+ {
+ return false;
+ }
+
+ llwchar first_char = string[0];
+
+ for (U32 i = 0; i < string.size(); i++)
+ {
+ if (string[i] != first_char)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void LLScrollListCtrl::selectItem(LLScrollListItem* itemp, S32 cell, bool select_single_item)
+{
+ if (!itemp) return;
+
+ if (!itemp->getSelected())
+ {
+ if (mLastSelected)
+ {
+ LLScrollListCell* cellp = mLastSelected->getColumn(getSearchColumn());
+ if (cellp)
+ {
+ cellp->highlightText(0, 0);
+ }
+ }
+ if (select_single_item)
+ {
+ deselectAllItems(true);
+ }
+ itemp->setSelected(true);
+ switch (mSelectionType)
+ {
+ case CELL:
+ itemp->setSelectedCell(cell);
+ break;
+ case HEADER:
+ itemp->setSelectedCell(cell <= 0 ? -1 : cell);
+ break;
+ case ROW:
+ itemp->setSelectedCell(-1);
+ break;
+ }
+ mLastSelected = itemp;
+ mSelectionChanged = true;
+ }
+}
+
+void LLScrollListCtrl::deselectItem(LLScrollListItem* itemp)
+{
+ if (!itemp) return;
+
+ if (itemp->getSelected())
+ {
+ if (mLastSelected == itemp)
+ {
+ mLastSelected = NULL;
+ }
+
+ itemp->setSelected(false);
+ LLScrollListCell* cellp = itemp->getColumn(getSearchColumn());
+ if (cellp)
+ {
+ cellp->highlightText(0, 0);
+ }
+ mSelectionChanged = true;
+ }
+}
+
+void LLScrollListCtrl::commitIfChanged()
+{
+ if (mSelectionChanged)
+ {
+ mDirty = true;
+ mSelectionChanged = false;
+ onCommit();
+ }
+}
+
+struct SameSortColumn
+{
+ SameSortColumn(S32 column) : mColumn(column) {}
+ S32 mColumn;
+
+ bool operator()(std::pair<S32, bool> sort_column) { return sort_column.first == mColumn; }
+};
+
+bool LLScrollListCtrl::setSort(S32 column_idx, bool ascending)
+{
+ LLScrollListColumn* sort_column = getColumn(column_idx);
+ if (!sort_column) return false;
+
+ sort_column->mSortDirection = ascending ? LLScrollListColumn::ASCENDING : LLScrollListColumn::DESCENDING;
+
+ sort_column_t new_sort_column(column_idx, ascending);
+
+ setNeedsSort();
+
+ if (mSortColumns.empty())
+ {
+ mSortColumns.push_back(new_sort_column);
+ return true;
+ }
+ else
+ {
+ // grab current sort column
+ sort_column_t cur_sort_column = mSortColumns.back();
+
+ // remove any existing sort criterion referencing this column
+ // and add the new one
+ mSortColumns.erase(remove_if(mSortColumns.begin(), mSortColumns.end(), SameSortColumn(column_idx)), mSortColumns.end());
+ mSortColumns.push_back(new_sort_column);
+
+ // did the sort criteria change?
+ return (cur_sort_column != new_sort_column);
+ }
+}
+
+S32 LLScrollListCtrl::getLinesPerPage()
+{
+ //if mPageLines is NOT provided display all item
+ if (mPageLines)
+ {
+ return mPageLines;
+ }
+ else
+ {
+ return mLineHeight ? mItemListRect.getHeight() / mLineHeight : getItemCount();
+ }
+}
+
+
+// Called by scrollbar
+void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar )
+{
+ mScrollLines = new_pos;
+}
+
+
+void LLScrollListCtrl::sortByColumn(const std::string& name, bool ascending)
+{
+ column_map_t::iterator itor = mColumns.find(name);
+ if (itor != mColumns.end())
+ {
+ sortByColumnIndex((*itor).second->mIndex, ascending);
+ }
+}
+
+// First column is column 0
+void LLScrollListCtrl::sortByColumnIndex(U32 column, bool ascending)
+{
+ setSort(column, ascending);
+ updateSort();
+}
+
+void LLScrollListCtrl::updateSort() const
+{
+ if (hasSortOrder() && !isSorted())
+ {
+ // do stable sort to preserve any previous sorts
+ std::stable_sort(
+ mItemList.begin(),
+ mItemList.end(),
+ SortScrollListItem(mSortColumns,mSortCallback, mAlternateSort));
+
+ mSorted = true;
+ }
+}
+
+// for one-shot sorts, does not save sort column/order
+void LLScrollListCtrl::sortOnce(S32 column, bool ascending)
+{
+ std::vector<std::pair<S32, bool> > sort_column;
+ sort_column.push_back(std::make_pair(column, ascending));
+
+ // do stable sort to preserve any previous sorts
+ std::stable_sort(
+ mItemList.begin(),
+ mItemList.end(),
+ SortScrollListItem(sort_column,mSortCallback,mAlternateSort));
+}
+
+void LLScrollListCtrl::dirtyColumns()
+{
+ mColumnsDirty = true;
+ mColumnWidthsDirty = true;
+
+ // need to keep mColumnsIndexed up to date
+ // just in case someone indexes into it immediately
+ mColumnsIndexed.resize(mColumns.size());
+
+ column_map_t::iterator column_itor;
+ for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor)
+ {
+ LLScrollListColumn *column = column_itor->second;
+ mColumnsIndexed[column_itor->second->mIndex] = column;
+ }
+}
+
+
+S32 LLScrollListCtrl::getScrollPos() const
+{
+ return mScrollbar->getDocPos();
+}
+
+
+void LLScrollListCtrl::setScrollPos( S32 pos )
+{
+ mScrollbar->setDocPos( pos );
+
+ onScrollChange(mScrollbar->getDocPos(), mScrollbar);
+}
+
+
+void LLScrollListCtrl::scrollToShowSelected()
+{
+ // don't scroll automatically when capturing mouse input
+ // as that will change what is currently under the mouse cursor
+ if (hasMouseCapture())
+ {
+ return;
+ }
+
+ updateSort();
+
+ S32 index = getFirstSelectedIndex();
+ if (index < 0)
+ {
+ return;
+ }
+
+ LLScrollListItem* item = mItemList[index];
+ if (!item)
+ {
+ // I don't THINK this should ever happen.
+ return;
+ }
+
+ S32 lowest = mScrollLines;
+ S32 page_lines = getLinesPerPage();
+ S32 highest = mScrollLines + page_lines;
+
+ if (index < lowest)
+ {
+ // need to scroll to show item
+ setScrollPos(index);
+ }
+ else if (highest <= index)
+ {
+ setScrollPos(index - page_lines + 1);
+ }
+}
+
+void LLScrollListCtrl::updateStaticColumnWidth(LLScrollListColumn* col, S32 new_width)
+{
+ mTotalStaticColumnWidth += llmax(0, new_width) - llmax(0, col->getWidth());
+}
+
+// LLEditMenuHandler functions
+
+// virtual
+void LLScrollListCtrl::copy()
+{
+ std::string buffer;
+
+ std::vector<LLScrollListItem*> items = getAllSelected();
+ std::vector<LLScrollListItem*>::iterator itor;
+ for (itor = items.begin(); itor != items.end(); ++itor)
+ {
+ buffer += (*itor)->getContentsCSV() + "\n";
+ }
+ LLClipboard::instance().copyToClipboard(utf8str_to_wstring(buffer), 0, buffer.length());
+}
+
+// virtual
+bool LLScrollListCtrl::canCopy() const
+{
+ return (getFirstSelected() != NULL);
+}
+
+// virtual
+void LLScrollListCtrl::cut()
+{
+ copy();
+ doDelete();
+}
+
+// virtual
+bool LLScrollListCtrl::canCut() const
+{
+ return canCopy() && canDoDelete();
+}
+
+// virtual
+void LLScrollListCtrl::selectAll()
+{
+ // Deselects all other items
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem *itemp = *iter;
+ if( itemp->getEnabled() )
+ {
+ selectItem(itemp, -1, false);
+ }
+ }
+
+ if (mCommitOnSelectionChange)
+ {
+ commitIfChanged();
+ }
+}
+
+// virtual
+bool LLScrollListCtrl::canSelectAll() const
+{
+ return getCanSelect() && mAllowMultipleSelection && !(mMaxSelectable > 0 && mItemList.size() > mMaxSelectable);
+}
+
+// virtual
+void LLScrollListCtrl::deselect()
+{
+ deselectAllItems();
+}
+
+// virtual
+bool LLScrollListCtrl::canDeselect() const
+{
+ return getCanSelect();
+}
+
+void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos)
+{
+ LLScrollListColumn::Params p;
+ LLParamSDParser parser;
+ parser.readSD(column, p);
+ addColumn(p, pos);
+}
+
+void LLScrollListCtrl::addColumn(const LLScrollListColumn::Params& column_params, EAddPosition pos)
+{
+ if (!column_params.validateBlock()) return;
+
+ std::string name = column_params.name;
+ // if no column name provided, just use ordinal as name
+ if (name.empty())
+ {
+ name = llformat("%d", mColumnsIndexed.size());
+ }
+
+ if (mColumns.find(name) == mColumns.end())
+ {
+ // Add column
+ mColumns[name] = new LLScrollListColumn(column_params, this);
+ LLScrollListColumn* new_column = mColumns[name];
+ new_column->mIndex = mColumns.size()-1;
+
+ // Add button
+ if (new_column->getWidth() > 0 || new_column->mRelWidth > 0 || new_column->mDynamicWidth)
+ {
+ if (getNumColumns() > 0)
+ {
+ mTotalColumnPadding += mColumnPadding;
+ }
+ if (new_column->mRelWidth >= 0)
+ {
+ new_column->setWidth((S32)ll_round(new_column->mRelWidth*mItemListRect.getWidth()));
+ }
+ else if(new_column->mDynamicWidth)
+ {
+ mNumDynamicWidthColumns++;
+ new_column->setWidth((mItemListRect.getWidth() - mTotalStaticColumnWidth - mTotalColumnPadding) / mNumDynamicWidthColumns);
+ }
+ S32 top = mItemListRect.mTop;
+
+ S32 left = mItemListRect.mLeft;
+ for (column_map_t::iterator itor = mColumns.begin();
+ itor != mColumns.end();
+ ++itor)
+ {
+ if (itor->second->mIndex < new_column->mIndex &&
+ itor->second->getWidth() > 0)
+ {
+ left += itor->second->getWidth() + mColumnPadding;
+ }
+ }
+
+ S32 right = left+new_column->getWidth();
+ if (new_column->mIndex != (S32)mColumns.size()-1)
+ {
+ right += mColumnPadding;
+ }
+
+ LLRect temp_rect = LLRect(left,top+mHeadingHeight,right,top);
+
+ LLScrollColumnHeader::Params params(LLUICtrlFactory::getDefaultParams<LLScrollColumnHeader>());
+ params.name = "btn_" + name;
+ params.rect = temp_rect;
+ params.column = new_column;
+ params.tool_tip = column_params.tool_tip;
+ params.tab_stop = false;
+ params.visible = mDisplayColumnHeaders;
+
+ if(column_params.header.image.isProvided())
+ {
+ params.image_selected = column_params.header.image;
+ params.image_unselected = column_params.header.image;
+ }
+ else
+ {
+ params.label = column_params.header.label;
+ }
+
+ new_column->mHeader = LLUICtrlFactory::create<LLScrollColumnHeader>(params);
+ addChild(new_column->mHeader);
+
+ sendChildToFront(mScrollbar);
+ }
+ }
+
+ dirtyColumns();
+}
+
+// static
+void LLScrollListCtrl::onClickColumn(void *userdata)
+{
+ LLScrollListColumn *info = (LLScrollListColumn*)userdata;
+ if (!info) return;
+
+ LLScrollListCtrl *parent = info->mParentCtrl;
+ if (!parent) return;
+
+ if (!parent->mCanSort) return;
+
+ S32 column_index = info->mIndex;
+
+ LLScrollListColumn* column = parent->mColumnsIndexed[info->mIndex];
+ bool ascending = column->mSortDirection == LLScrollListColumn::ASCENDING;
+ if (column->mSortingColumn != column->mName
+ && parent->mColumns.find(column->mSortingColumn) != parent->mColumns.end())
+ {
+ LLScrollListColumn* info_redir = parent->mColumns[column->mSortingColumn];
+ column_index = info_redir->mIndex;
+ }
+
+ // if this column is the primary sort key, reverse the direction
+ if (!parent->mSortColumns.empty() && parent->mSortColumns.back().first == column_index)
+ {
+ ascending = !parent->mSortColumns.back().second;
+ }
+
+ parent->sortByColumnIndex(column_index, ascending);
+
+ if (parent->mOnSortChangedCallback)
+ {
+ parent->mOnSortChangedCallback();
+ }
+}
+
+std::string LLScrollListCtrl::getSortColumnName()
+{
+ LLScrollListColumn* column = mSortColumns.empty() ? NULL : mColumnsIndexed[mSortColumns.back().first];
+
+ if (column) return column->mName;
+ else return "";
+}
+
+bool LLScrollListCtrl::hasSortOrder() const
+{
+ return !mSortColumns.empty();
+}
+
+void LLScrollListCtrl::clearSortOrder()
+{
+ mSortColumns.clear();
+}
+
+void LLScrollListCtrl::clearColumns()
+{
+ column_map_t::iterator itor;
+ for (itor = mColumns.begin(); itor != mColumns.end(); ++itor)
+ {
+ LLScrollColumnHeader *header = itor->second->mHeader;
+ if (header)
+ {
+ removeChild(header);
+ delete header;
+ }
+ }
+ std::for_each(mColumns.begin(), mColumns.end(), DeletePairedPointer());
+ mColumns.clear();
+ mSortColumns.clear();
+ mTotalStaticColumnWidth = 0;
+ mTotalColumnPadding = 0;
+
+ dirtyColumns(); // Clears mColumnsIndexed
+}
+
+void LLScrollListCtrl::setColumnLabel(const std::string& column, const std::string& label)
+{
+ LLScrollListColumn* columnp = getColumn(column);
+ if (columnp)
+ {
+ columnp->mLabel = label;
+ if (columnp->mHeader)
+ {
+ columnp->mHeader->setLabel(label);
+ }
+ }
+}
+
+LLScrollListColumn* LLScrollListCtrl::getColumn(S32 index)
+{
+ if (index < 0 || index >= (S32)mColumnsIndexed.size())
+ {
+ return NULL;
+ }
+ return mColumnsIndexed[index];
+}
+
+LLScrollListColumn* LLScrollListCtrl::getColumn(const std::string& name)
+{
+ column_map_t::iterator column_itor = mColumns.find(name);
+ if (column_itor != mColumns.end())
+ {
+ return column_itor->second;
+ }
+ return NULL;
+}
+
+LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& element, EAddPosition pos, void* userdata)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+ LLScrollListItem::Params item_params;
+ LLParamSDParser parser;
+ parser.readSD(element, item_params);
+ item_params.userdata = userdata;
+ return addRow(item_params, pos);
+}
+
+LLScrollListItem* LLScrollListCtrl::addRow(const LLScrollListItem::Params& item_p, EAddPosition pos)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+ 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)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+ if (!item_p.validateBlock() || !new_item) return NULL;
+ new_item->setNumColumns(mColumns.size());
+
+ // Add any columns we don't already have
+ S32 col_index = 0;
+
+ for(LLInitParam::ParamIterator<LLScrollListCell::Params>::const_iterator itor = item_p.columns.begin();
+ itor != item_p.columns.end();
+ ++itor)
+ {
+ LLScrollListCell::Params cell_p = *itor;
+ std::string column = cell_p.column;
+
+ // empty columns strings index by ordinal
+ if (column.empty())
+ {
+ column = llformat("%d", col_index);
+ }
+
+ LLScrollListColumn* columnp = getColumn(column);
+
+ // create new column on demand
+ if (!columnp)
+ {
+ LLScrollListColumn::Params new_column;
+ new_column.name = column;
+ new_column.header.label = column;
+
+ // if width supplied for column, use it, otherwise
+ // use adaptive width
+ if (cell_p.width.isProvided())
+ {
+ new_column.width.pixel_width = cell_p.width;
+ }
+ addColumn(new_column);
+ columnp = mColumns[column];
+ new_item->setNumColumns(mColumns.size());
+ }
+
+ S32 index = columnp->mIndex;
+ if (!cell_p.width.isProvided())
+ {
+ cell_p.width = columnp->getWidth();
+ }
+
+ LLScrollListCell* cell = LLScrollListCell::create(cell_p);
+
+ if (cell)
+ {
+ new_item->setColumn(index, cell);
+ if (columnp->mHeader
+ && cell->isText()
+ && !cell->getValue().asString().empty())
+ {
+ columnp->mHeader->setHasResizableElement(true);
+ }
+ }
+
+ col_index++;
+ }
+
+ if (item_p.columns.empty())
+ {
+ if (mColumns.empty())
+ {
+ LLScrollListColumn::Params new_column;
+ new_column.name = "0";
+
+ addColumn(new_column);
+ new_item->setNumColumns(mColumns.size());
+ }
+
+ LLScrollListCell* cell = LLScrollListCell::create(LLScrollListCell::Params().value(item_p.value));
+ if (cell)
+ {
+ LLScrollListColumn* columnp = mColumns.begin()->second;
+
+ new_item->setColumn(0, cell);
+ if (columnp->mHeader
+ && cell->isText()
+ && !cell->getValue().asString().empty())
+ {
+ columnp->mHeader->setHasResizableElement(true);
+ }
+ }
+ }
+
+ // add dummy cells for missing columns
+ for (column_map_t::iterator column_it = mColumns.begin(); column_it != mColumns.end(); ++column_it)
+ {
+ S32 column_idx = column_it->second->mIndex;
+ if (new_item->getColumn(column_idx) == NULL)
+ {
+ LLScrollListColumn* column_ptr = column_it->second;
+ LLScrollListCell::Params cell_p;
+ cell_p.width = column_ptr->getWidth();
+
+ new_item->setColumn(column_idx, new LLScrollListSpacer(cell_p));
+ }
+ }
+
+ addItem(new_item, pos);
+ return new_item;
+}
+
+LLScrollListItem* LLScrollListCtrl::addSimpleElement(const std::string& value, EAddPosition pos, const LLSD& id)
+{
+ LLSD entry_id = id;
+
+ if (id.isUndefined())
+ {
+ entry_id = value;
+ }
+
+ LLScrollListItem::Params item_params;
+ item_params.value(entry_id);
+ item_params.columns.add()
+ .value(value)
+ .font(LLFontGL::getFontEmojiSmall());
+
+ return addRow(item_params, pos);
+}
+
+void LLScrollListCtrl::setValue(const LLSD& value )
+{
+ LLSD::array_const_iterator itor;
+ for (itor = value.beginArray(); itor != value.endArray(); ++itor)
+ {
+ addElement(*itor);
+ }
+}
+
+LLSD LLScrollListCtrl::getValue() const
+{
+ LLScrollListItem *item = getFirstSelected();
+ if (!item) return LLSD();
+ return item->getValue();
+}
+
+bool LLScrollListCtrl::operateOnSelection(EOperation op)
+{
+ if (op == OP_DELETE)
+ {
+ deleteSelectedItems();
+ return true;
+ }
+ else if (op == OP_DESELECT)
+ {
+ deselectAllItems();
+ }
+ return false;
+}
+
+bool LLScrollListCtrl::operateOnAll(EOperation op)
+{
+ if (op == OP_DELETE)
+ {
+ clearRows();
+ return true;
+ }
+ else if (op == OP_DESELECT)
+ {
+ deselectAllItems();
+ }
+ else if (op == OP_SELECT)
+ {
+ selectAll();
+ }
+ return false;
+}
+//virtual
+void LLScrollListCtrl::setFocus(bool b)
+{
+ // for tabbing into pristine scroll lists (Finder)
+ if (!getFirstSelected())
+ {
+ selectFirstItem();
+ //onCommit(); // SJB: selectFirstItem() will call onCommit() if appropriate
+ }
+ LLUICtrl::setFocus(b);
+}
+
+
+// virtual
+bool LLScrollListCtrl::isDirty() const
+{
+ bool grubby = mDirty;
+ if ( !mAllowMultipleSelection )
+ {
+ grubby = (mOriginalSelection != getFirstSelectedIndex());
+ }
+ return grubby;
+}
+
+// Clear dirty state
+void LLScrollListCtrl::resetDirty()
+{
+ mDirty = false;
+ mOriginalSelection = getFirstSelectedIndex();
+}
+
+
+//virtual
+void LLScrollListCtrl::onFocusReceived()
+{
+ // forget latent selection changes when getting focus
+ mSelectionChanged = false;
+ LLUICtrl::onFocusReceived();
+}
+
+//virtual
+void LLScrollListCtrl::onFocusLost()
+{
+ if (hasMouseCapture())
+ {
+ gFocusMgr.setMouseCapture(NULL);
+ }
+
+ mSearchString.clear();
+
+ LLUICtrl::onFocusLost();
+}
+
+boost::signals2::connection LLScrollListCtrl::setIsFriendCallback(const is_friend_signal_t::slot_type& cb)
+{
+ if (!mIsFriendSignal)
+ {
+ mIsFriendSignal = new is_friend_signal_t();
+ }
+ return mIsFriendSignal->connect(cb);
+}
+
+bool LLScrollListCtrl::highlightMatchingItems(const std::string& filter_str)
+{
+ if (filter_str == "" || filter_str == " ")
+ {
+ clearHighlightedItems();
+ return false;
+ }
+
+ bool res = false;
+
+ setHighlightedColor(LLUIColorTable::instance().getColor("SearchableControlHighlightColor", LLColor4::red));
+
+ std::string filter_str_lc(filter_str);
+ LLStringUtil::toLower(filter_str_lc);
+
+ std::vector<LLScrollListItem*> data = getAllData();
+ std::vector<LLScrollListItem*>::iterator iter = data.begin();
+ while (iter != data.end())
+ {
+ LLScrollListCell* cell = (*iter)->getColumn(0);
+ if (cell)
+ {
+ std::string value = cell->getValue().asString();
+ LLStringUtil::toLower(value);
+ if (value.find(filter_str_lc) == std::string::npos)
+ {
+ (*iter)->setHighlighted(false);
+ }
+ else
+ {
+ (*iter)->setHighlighted(true);
+ res = true;
+ }
+ }
+ iter++;
+ }
+ return res;
+}
diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h
index b8ae33e541..d6ba9240b7 100644
--- a/indra/llui/llscrolllistctrl.h
+++ b/indra/llui/llscrolllistctrl.h
@@ -1,564 +1,564 @@
-/**
- * @file llscrolllistctrl.h
- * @brief A scrolling list of items. This is the one you want to use
- * in UI code. LLScrollListCell, LLScrollListItem, etc. are utility
- * classes.
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_SCROLLLISTCTRL_H
-#define LL_SCROLLLISTCTRL_H
-
-#include <vector>
-#include <deque>
-
-#include "lluictrl.h"
-#include "llctrlselectioninterface.h"
-#include "llfontgl.h"
-#include "llui.h"
-#include "llstring.h" // LLWString
-#include "lleditmenuhandler.h"
-#include "llframetimer.h"
-
-#include "llscrollbar.h"
-#include "lldate.h"
-#include "llscrolllistitem.h"
-#include "llscrolllistcolumn.h"
-#include "llviewborder.h"
-
-class LLScrollListCell;
-class LLTextBox;
-class LLContextMenu;
-
-class LLScrollListCtrl : public LLUICtrl, public LLEditMenuHandler,
- public LLCtrlListInterface, public LLCtrlScrollInterface
-{
-public:
- typedef enum e_selection_type
- {
- ROW, // default
- CELL, // does not support multi-selection
- HEADER, // when pointing to cells in column 0 will highlight whole row, otherwise cell, no multi-select
- } ESelectionType;
-
- struct SelectionTypeNames : public LLInitParam::TypeValuesHelper<LLScrollListCtrl::ESelectionType, SelectionTypeNames>
- {
- static void declareValues();
- };
-
- struct Contents : public LLInitParam::Block<Contents>
- {
- Multiple<LLScrollListColumn::Params> columns;
- Multiple<LLScrollListItem::Params> rows;
-
- //Multiple<Contents> groups;
-
- Contents();
- };
-
- // *TODO: Add callbacks to Params
- typedef boost::function<void (void)> callback_t;
-
- template<typename T> struct maximum
- {
- typedef T result_type;
-
- template<typename InputIterator>
- T operator()(InputIterator first, InputIterator last) const
- {
- // If there are no slots to call, just return the
- // default-constructed value
- if(first == last ) return T();
- T max_value = *first++;
- while (first != last) {
- if (max_value < *first)
- max_value = *first;
- ++first;
- }
-
- return max_value;
- }
- };
-
-
- typedef boost::signals2::signal<S32 (S32,const LLScrollListItem*,const LLScrollListItem*),maximum<S32> > sort_signal_t;
- typedef boost::signals2::signal<bool(const LLUUID& user_id)> is_friend_signal_t;
-
- struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- // behavioral flags
- Optional<bool> multi_select,
- commit_on_keyboard_movement,
- commit_on_selection_change,
- mouse_wheel_opaque;
-
- Optional<ESelectionType, SelectionTypeNames> selection_type;
-
- // display flags
- Optional<bool> has_border,
- draw_heading,
- draw_stripes,
- background_visible,
- scroll_bar_bg_visible;
-
- // layout
- Optional<S32> column_padding,
- row_padding,
- page_lines,
- heading_height;
-
- // sort and search behavior
- Optional<S32> search_column,
- sort_column;
- Optional<bool> sort_ascending,
- can_sort; // whether user is allowed to sort
-
- // colors
- Optional<LLUIColor> fg_unselected_color,
- fg_selected_color,
- bg_selected_color,
- fg_disable_color,
- bg_writeable_color,
- bg_readonly_color,
- bg_stripe_color,
- hovered_color,
- highlighted_color,
- scroll_bar_bg_color;
-
- Optional<Contents> contents;
-
- Optional<LLViewBorder::Params> border;
-
- Params();
- };
-
-protected:
- friend class LLUICtrlFactory;
-
- LLScrollListCtrl(const Params&);
-
-public:
- virtual ~LLScrollListCtrl();
-
- S32 isEmpty() const;
-
- void deleteAllItems() { clearRows(); }
-
- // Sets an array of column descriptors
- void setColumnHeadings(const LLSD& headings);
- void sortByColumnIndex(U32 column, bool ascending);
-
- // LLCtrlListInterface functions
- virtual S32 getItemCount() const;
- // Adds a single column descriptor: ["name" : string, "label" : string, "width" : integer, "relwidth" : integer ]
- virtual void addColumn(const LLScrollListColumn::Params& column, EAddPosition pos = ADD_BOTTOM);
- virtual void addColumn(const LLSD& column, EAddPosition pos = ADD_BOTTOM);
- virtual void clearColumns();
- virtual void setColumnLabel(const std::string& column, const std::string& label);
- virtual bool preProcessChildNode(LLXMLNodePtr child);
- virtual LLScrollListColumn* getColumn(S32 index);
- virtual LLScrollListColumn* getColumn(const std::string& name);
- virtual S32 getNumColumns() const { return mColumnsIndexed.size(); }
-
- // Adds a single element, from an array of:
- // "columns" => [ "column" => column name, "value" => value, "type" => type, "font" => font, "font-style" => style ], "id" => uuid
- // Creates missing columns automatically.
- virtual LLScrollListItem* addElement(const LLSD& element, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL);
- virtual LLScrollListItem* addRow(LLScrollListItem *new_item, const LLScrollListItem::Params& value, EAddPosition pos = ADD_BOTTOM);
- virtual LLScrollListItem* addRow(const LLScrollListItem::Params& value, EAddPosition pos = ADD_BOTTOM);
- // Simple add element. Takes a single array of:
- // [ "value" => value, "font" => font, "font-style" => style ]
- virtual void clearRows(); // clears all elements
- virtual void sortByColumn(const std::string& name, bool ascending);
-
- // These functions take and return an array of arrays of elements, as above
- virtual void setValue(const LLSD& value );
- virtual LLSD getValue() const;
-
- LLCtrlSelectionInterface* getSelectionInterface() { return (LLCtrlSelectionInterface*)this; }
- LLCtrlListInterface* getListInterface() { return (LLCtrlListInterface*)this; }
- LLCtrlScrollInterface* getScrollInterface() { return (LLCtrlScrollInterface*)this; }
-
- // DEPRECATED: Use setSelectedByValue() below.
- bool setCurrentByID( const LLUUID& id ) { return selectByID(id); }
- virtual LLUUID getCurrentID() const { return getStringUUIDSelectedItem(); }
-
- bool operateOnSelection(EOperation op);
- bool operateOnAll(EOperation op);
-
- // returns false if unable to set the max count so low
- bool setMaxItemCount(S32 max_count);
-
- bool selectByID( const LLUUID& id ); // false if item not found
-
- // Match item by value.asString(), which should work for string, integer, uuid.
- // Returns false if not found.
- bool setSelectedByValue(const LLSD& value, bool selected);
-
- bool isSorted() const { return mSorted; }
-
- virtual bool isSelected(const LLSD& value) const;
-
- bool hasSelectedItem() const;
-
- bool handleClick(S32 x, S32 y, MASK mask);
- bool selectFirstItem();
- bool selectNthItem( S32 index );
- bool selectItemRange( S32 first, S32 last );
- bool selectItemAt(S32 x, S32 y, MASK mask);
-
- void deleteSingleItem( S32 index );
- void deleteItems(const LLSD& sd);
- void deleteSelectedItems();
- void deselectAllItems(bool no_commit_on_change = false); // by default, go ahead and commit on selection change
-
- void clearHighlightedItems();
-
- virtual void mouseOverHighlightNthItem( S32 index );
-
- S32 getHighlightedItemInx() const { return mHighlightedItem; }
-
- void setDoubleClickCallback( callback_t cb ) { mOnDoubleClickCallback = cb; }
- void setMaximumSelectCallback( callback_t cb) { mOnMaximumSelectCallback = cb; }
- void setSortChangedCallback( callback_t cb) { mOnSortChangedCallback = cb; }
- // Convenience function; *TODO: replace with setter above + boost::bind() in calling code
- void setDoubleClickCallback( boost::function<void (void* userdata)> cb, void* userdata) { mOnDoubleClickCallback = boost::bind(cb, userdata); }
-
- void swapWithNext(S32 index);
- void swapWithPrevious(S32 index);
-
- void setCanSelect(bool can_select) { mCanSelect = can_select; }
- virtual bool getCanSelect() const { return mCanSelect; }
-
- S32 getItemIndex( LLScrollListItem* item ) const;
- S32 getItemIndex( const LLUUID& item_id ) const;
-
- void setCommentText( const std::string& comment_text);
- LLScrollListItem* addSeparator(EAddPosition pos);
-
- // "Simple" interface: use this when you're creating a list that contains only unique strings, only
- // 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, S32 column = 0 ); // false if item not found
- bool selectItemByPrefix(const std::string& target, bool case_sensitive = true, S32 column = -1);
- bool selectItemByPrefix(const LLWString& target, bool case_sensitive = true, S32 column = -1);
- LLScrollListItem* getItemByLabel( const std::string& item, bool case_sensitive = true, S32 column = 0 );
- const std::string getSelectedItemLabel(S32 column = 0) const;
- LLSD getSelectedValue();
-
- // If multi select is on, select all element that include substring,
- // otherwise select first match only.
- // If focus is true will scroll to selection.
- // Returns number of results.
- // Note: at the moment search happens in one go and is expensive
- U32 searchItems(const std::string& substring, bool case_sensitive = false, bool focus = true);
- U32 searchItems(const LLWString& substring, bool case_sensitive = false, bool focus = true);
-
- // DEPRECATED: Use LLSD versions of setCommentText() and getSelectedValue().
- // "StringUUID" interface: use this when you're creating a list that contains non-unique strings each of which
- // has an associated, unique UUID, and only one of which can be selected at a time.
- LLScrollListItem* addStringUUIDItem(const std::string& item_text, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, bool enabled = true);
- LLUUID getStringUUIDSelectedItem() const;
-
- LLScrollListItem* getFirstSelected() const;
- virtual S32 getFirstSelectedIndex() const;
- std::vector<LLScrollListItem*> getAllSelected() const;
- S32 getNumSelected() const;
- LLScrollListItem* getLastSelectedItem() const { return mLastSelected; }
-
- // iterate over all items
- LLScrollListItem* getFirstData() const;
- LLScrollListItem* getLastData() const;
- std::vector<LLScrollListItem*> getAllData() const;
-
- LLScrollListItem* getItem(const LLSD& sd) const;
-
- void setAllowMultipleSelection(bool mult ) { mAllowMultipleSelection = mult; }
-
- void setBgWriteableColor(const LLColor4 &c) { mBgWriteableColor = c; }
- void setReadOnlyBgColor(const LLColor4 &c) { mBgReadOnlyColor = c; }
- void setBgSelectedColor(const LLColor4 &c) { mBgSelectedColor = c; }
- void setBgStripeColor(const LLColor4& c) { mBgStripeColor = c; }
- void setFgSelectedColor(const LLColor4 &c) { mFgSelectedColor = c; }
- void setFgUnselectedColor(const LLColor4 &c){ mFgUnselectedColor = c; }
- void setHoveredColor(const LLColor4 &c) { mHoveredColor = c; }
- void setHighlightedColor(const LLColor4 &c) { mHighlightedColor = c; }
- void setFgDisableColor(const LLColor4 &c) { mFgDisabledColor = c; }
-
- void setBackgroundVisible(bool b) { mBackgroundVisible = b; }
- void setDrawStripes(bool b) { mDrawStripes = b; }
- void setColumnPadding(const S32 c) { mColumnPadding = c; }
- S32 getColumnPadding() const { return mColumnPadding; }
- void setRowPadding(const S32 c) { mColumnPadding = c; }
- S32 getRowPadding() const { return mColumnPadding; }
- void setCommitOnKeyboardMovement(bool b) { mCommitOnKeyboardMovement = b; }
- void setCommitOnSelectionChange(bool b) { mCommitOnSelectionChange = b; }
- void setAllowKeyboardMovement(bool b) { mAllowKeyboardMovement = b; }
-
- void setMaxSelectable(U32 max_selected) { mMaxSelectable = max_selected; }
- S32 getMaxSelectable() { return mMaxSelectable; }
-
-
- virtual S32 getScrollPos() const;
- virtual void setScrollPos( S32 pos );
- S32 getSearchColumn();
- void setSearchColumn(S32 column) { mSearchColumn = column; }
- S32 getColumnIndexFromOffset(S32 x);
- S32 getColumnOffsetFromIndex(S32 index);
- S32 getRowOffsetFromIndex(S32 index);
-
- void clearSearchString() { mSearchString.clear(); }
-
- // support right-click context menus for avatar/group lists
- enum ContextMenuType { MENU_NONE, MENU_AVATAR, MENU_GROUP };
- void setContextMenu(const ContextMenuType &menu) { mContextMenuType = menu; }
- ContextMenuType getContextMenuType() { return mContextMenuType; }
-
- // Overridden from LLView
- /*virtual*/ void draw();
- /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleKeyHere(KEY key, MASK mask);
- /*virtual*/ bool handleUnicodeCharHere(llwchar uni_char);
- /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks);
- /*virtual*/ bool handleScrollHWheel(S32 x, S32 y, S32 clicks);
- /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask);
- /*virtual*/ void setEnabled(bool enabled);
- /*virtual*/ void setFocus( bool b );
- /*virtual*/ void onFocusReceived();
- /*virtual*/ void onFocusLost();
- /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask);
- /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true);
-
- virtual bool isDirty() const;
- virtual void resetDirty(); // Clear dirty state
-
- virtual void updateLayout();
- virtual void fitContents(S32 max_width, S32 max_height);
-
- virtual LLRect getRequiredRect();
- static bool rowPreceeds(LLScrollListItem *new_row, LLScrollListItem *test_row);
-
- LLRect getItemListRect() { return mItemListRect; }
-
- /// Returns rect, in local coords, of a given row/column
- LLRect getCellRect(S32 row_index, S32 column_index);
-
- // Used "internally" by the scroll bar.
- void onScrollChange( S32 new_pos, LLScrollbar* src );
-
- static void onClickColumn(void *userdata);
-
- virtual void updateColumns(bool force_update = false);
- S32 calcMaxContentWidth();
- bool updateColumnWidths();
-
- void setHeadingHeight(S32 heading_height);
- /**
- * Sets max visible lines without scroolbar, if this value equals to 0,
- * then display all items.
- */
- void setPageLines(S32 page_lines );
- void setCollapseEmptyColumns(bool collapse);
-
- LLScrollListItem* hitItem(S32 x,S32 y);
- virtual void scrollToShowSelected();
-
- // LLEditMenuHandler functions
- virtual void copy();
- virtual bool canCopy() const;
- virtual void cut();
- virtual bool canCut() const;
- virtual void selectAll();
- virtual bool canSelectAll() const;
- virtual void deselect();
- virtual bool canDeselect() const;
-
- void setNumDynamicColumns(S32 num) { mNumDynamicWidthColumns = num; }
- void updateStaticColumnWidth(LLScrollListColumn* col, S32 new_width);
- S32 getTotalStaticColumnWidth() { return mTotalStaticColumnWidth; }
-
- std::string getSortColumnName();
- bool getSortAscending() { return mSortColumns.empty() ? true : mSortColumns.back().second; }
- bool hasSortOrder() const;
- void clearSortOrder();
-
- void setAlternateSort() { mAlternateSort = true; }
-
- void selectPrevItem(bool extend_selection = false);
- void selectNextItem(bool extend_selection = false);
- S32 selectMultiple(uuid_vec_t ids);
- // conceptually const, but mutates mItemList
- void updateSort() const;
- // sorts a list without affecting the permanent sort order (so further list insertions can be unsorted, for example)
- void sortOnce(S32 column, bool ascending);
-
- // manually call this whenever editing list items in place to flag need for resorting
- void setNeedsSort(bool val = true) { mSorted = !val; }
- void dirtyColumns(); // some operation has potentially affected column layout or ordering
-
- bool highlightMatchingItems(const std::string& filter_str);
-
- boost::signals2::connection setSortCallback(sort_signal_t::slot_type cb )
- {
- if (!mSortCallback) mSortCallback = new sort_signal_t();
- return mSortCallback->connect(cb);
- }
-
- boost::signals2::connection setIsFriendCallback(const is_friend_signal_t::slot_type& cb);
-
-
-protected:
- // "Full" interface: use this when you're creating a list that has one or more of the following:
- // * contains icons
- // * contains multiple columns
- // * allows multiple selection
- // * has items that are not guarenteed to have unique names
- // * has additional per-item data (e.g. a UUID or void* userdata)
- //
- // To add items using this approach, create new LLScrollListItems and LLScrollListCells. Add the
- // cells (column entries) to each item, and add the item to the LLScrollListCtrl.
- //
- // The LLScrollListCtrl owns its items and is responsible for deleting them
- // (except in the case that the addItem() call fails, in which case it is up
- // to the caller to delete the item)
- //
- // returns false if item faile to be added to list, does NOT delete 'item'
- bool addItem( LLScrollListItem* item, EAddPosition pos = ADD_BOTTOM, bool requires_column = true );
-
- typedef std::deque<LLScrollListItem *> item_list;
- item_list& getItemList() { return mItemList; }
-
- void updateLineHeight();
-
-private:
- void drawItems();
-
- void updateLineHeightInsert(LLScrollListItem* item);
- void reportInvalidInput();
- bool isRepeatedChars(const LLWString& string) const;
- void selectItem(LLScrollListItem* itemp, S32 cell, bool single_select = true);
- void deselectItem(LLScrollListItem* itemp);
- void commitIfChanged();
- 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 removeFriend(std::string id);
- static void reportAbuse(std::string id, bool is_group);
- static void showNameDetails(std::string id, bool is_group);
- static void copyNameToClipboard(std::string id, bool is_group);
- static void copySLURLToClipboard(std::string id, bool is_group);
-
- S32 mLineHeight; // the max height of a single line
- S32 mScrollLines; // how many lines we've scrolled down
- S32 mPageLines; // max number of lines is it possible to see on the screen given mRect and mLineHeight
- 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;
- ESelectionType mSelectionType;
- bool mNeedsScroll;
- bool mMouseWheelOpaque;
- bool mCanSelect;
- bool mCanSort; // Whether user is allowed to sort
- bool mDisplayColumnHeaders;
- bool mColumnsDirty;
- bool mColumnWidthsDirty;
-
- bool mAlternateSort;
-
- mutable item_list mItemList;
-
- LLScrollListItem *mLastSelected;
-
- S32 mMaxItemCount;
-
- LLRect mItemListRect;
- S32 mColumnPadding;
- S32 mRowPadding;
-
- bool mBackgroundVisible;
- bool mDrawStripes;
-
- LLUIColor mBgWriteableColor;
- LLUIColor mBgReadOnlyColor;
- LLUIColor mBgSelectedColor;
- LLUIColor mBgStripeColor;
- LLUIColor mFgSelectedColor;
- LLUIColor mFgUnselectedColor;
- LLUIColor mFgDisabledColor;
- LLUIColor mHoveredColor;
- LLUIColor mHighlightedColor;
-
- S32 mBorderThickness;
- callback_t mOnDoubleClickCallback;
- callback_t mOnMaximumSelectCallback;
- callback_t mOnSortChangedCallback;
-
- S32 mHighlightedItem;
- class LLViewBorder* mBorder;
- LLHandle<LLContextMenu> mPopupMenuHandle;
-
- LLView *mCommentTextView;
-
- LLWString mSearchString;
- LLFrameTimer mSearchTimer;
-
- S32 mSearchColumn;
- S32 mNumDynamicWidthColumns;
- S32 mTotalStaticColumnWidth;
- S32 mTotalColumnPadding;
-
- mutable bool mSorted;
-
- typedef std::map<std::string, LLScrollListColumn*> column_map_t;
- column_map_t mColumns;
-
- bool mDirty;
- S32 mOriginalSelection;
-
- ContextMenuType mContextMenuType;
-
- typedef std::vector<LLScrollListColumn*> ordered_columns_t;
- ordered_columns_t mColumnsIndexed;
-
- typedef std::pair<S32, bool> sort_column_t;
- std::vector<sort_column_t> mSortColumns;
-
- sort_signal_t* mSortCallback;
-
- is_friend_signal_t* mIsFriendSignal;
-}; // end class LLScrollListCtrl
-
-#endif // LL_SCROLLLISTCTRL_H
+/**
+ * @file llscrolllistctrl.h
+ * @brief A scrolling list of items. This is the one you want to use
+ * in UI code. LLScrollListCell, LLScrollListItem, etc. are utility
+ * classes.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_SCROLLLISTCTRL_H
+#define LL_SCROLLLISTCTRL_H
+
+#include <vector>
+#include <deque>
+
+#include "lluictrl.h"
+#include "llctrlselectioninterface.h"
+#include "llfontgl.h"
+#include "llui.h"
+#include "llstring.h" // LLWString
+#include "lleditmenuhandler.h"
+#include "llframetimer.h"
+
+#include "llscrollbar.h"
+#include "lldate.h"
+#include "llscrolllistitem.h"
+#include "llscrolllistcolumn.h"
+#include "llviewborder.h"
+
+class LLScrollListCell;
+class LLTextBox;
+class LLContextMenu;
+
+class LLScrollListCtrl : public LLUICtrl, public LLEditMenuHandler,
+ public LLCtrlListInterface, public LLCtrlScrollInterface
+{
+public:
+ typedef enum e_selection_type
+ {
+ ROW, // default
+ CELL, // does not support multi-selection
+ HEADER, // when pointing to cells in column 0 will highlight whole row, otherwise cell, no multi-select
+ } ESelectionType;
+
+ struct SelectionTypeNames : public LLInitParam::TypeValuesHelper<LLScrollListCtrl::ESelectionType, SelectionTypeNames>
+ {
+ static void declareValues();
+ };
+
+ struct Contents : public LLInitParam::Block<Contents>
+ {
+ Multiple<LLScrollListColumn::Params> columns;
+ Multiple<LLScrollListItem::Params> rows;
+
+ //Multiple<Contents> groups;
+
+ Contents();
+ };
+
+ // *TODO: Add callbacks to Params
+ typedef boost::function<void (void)> callback_t;
+
+ template<typename T> struct maximum
+ {
+ typedef T result_type;
+
+ template<typename InputIterator>
+ T operator()(InputIterator first, InputIterator last) const
+ {
+ // If there are no slots to call, just return the
+ // default-constructed value
+ if(first == last ) return T();
+ T max_value = *first++;
+ while (first != last) {
+ if (max_value < *first)
+ max_value = *first;
+ ++first;
+ }
+
+ return max_value;
+ }
+ };
+
+
+ typedef boost::signals2::signal<S32 (S32,const LLScrollListItem*,const LLScrollListItem*),maximum<S32> > sort_signal_t;
+ typedef boost::signals2::signal<bool(const LLUUID& user_id)> is_friend_signal_t;
+
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ // behavioral flags
+ Optional<bool> multi_select,
+ commit_on_keyboard_movement,
+ commit_on_selection_change,
+ mouse_wheel_opaque;
+
+ Optional<ESelectionType, SelectionTypeNames> selection_type;
+
+ // display flags
+ Optional<bool> has_border,
+ draw_heading,
+ draw_stripes,
+ background_visible,
+ scroll_bar_bg_visible;
+
+ // layout
+ Optional<S32> column_padding,
+ row_padding,
+ page_lines,
+ heading_height;
+
+ // sort and search behavior
+ Optional<S32> search_column,
+ sort_column;
+ Optional<bool> sort_ascending,
+ can_sort; // whether user is allowed to sort
+
+ // colors
+ Optional<LLUIColor> fg_unselected_color,
+ fg_selected_color,
+ bg_selected_color,
+ fg_disable_color,
+ bg_writeable_color,
+ bg_readonly_color,
+ bg_stripe_color,
+ hovered_color,
+ highlighted_color,
+ scroll_bar_bg_color;
+
+ Optional<Contents> contents;
+
+ Optional<LLViewBorder::Params> border;
+
+ Params();
+ };
+
+protected:
+ friend class LLUICtrlFactory;
+
+ LLScrollListCtrl(const Params&);
+
+public:
+ virtual ~LLScrollListCtrl();
+
+ S32 isEmpty() const;
+
+ void deleteAllItems() { clearRows(); }
+
+ // Sets an array of column descriptors
+ void setColumnHeadings(const LLSD& headings);
+ void sortByColumnIndex(U32 column, bool ascending);
+
+ // LLCtrlListInterface functions
+ virtual S32 getItemCount() const;
+ // Adds a single column descriptor: ["name" : string, "label" : string, "width" : integer, "relwidth" : integer ]
+ virtual void addColumn(const LLScrollListColumn::Params& column, EAddPosition pos = ADD_BOTTOM);
+ virtual void addColumn(const LLSD& column, EAddPosition pos = ADD_BOTTOM);
+ virtual void clearColumns();
+ virtual void setColumnLabel(const std::string& column, const std::string& label);
+ virtual bool preProcessChildNode(LLXMLNodePtr child);
+ virtual LLScrollListColumn* getColumn(S32 index);
+ virtual LLScrollListColumn* getColumn(const std::string& name);
+ virtual S32 getNumColumns() const { return mColumnsIndexed.size(); }
+
+ // Adds a single element, from an array of:
+ // "columns" => [ "column" => column name, "value" => value, "type" => type, "font" => font, "font-style" => style ], "id" => uuid
+ // Creates missing columns automatically.
+ virtual LLScrollListItem* addElement(const LLSD& element, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL);
+ virtual LLScrollListItem* addRow(LLScrollListItem *new_item, const LLScrollListItem::Params& value, EAddPosition pos = ADD_BOTTOM);
+ virtual LLScrollListItem* addRow(const LLScrollListItem::Params& value, EAddPosition pos = ADD_BOTTOM);
+ // Simple add element. Takes a single array of:
+ // [ "value" => value, "font" => font, "font-style" => style ]
+ virtual void clearRows(); // clears all elements
+ virtual void sortByColumn(const std::string& name, bool ascending);
+
+ // These functions take and return an array of arrays of elements, as above
+ virtual void setValue(const LLSD& value );
+ virtual LLSD getValue() const;
+
+ LLCtrlSelectionInterface* getSelectionInterface() { return (LLCtrlSelectionInterface*)this; }
+ LLCtrlListInterface* getListInterface() { return (LLCtrlListInterface*)this; }
+ LLCtrlScrollInterface* getScrollInterface() { return (LLCtrlScrollInterface*)this; }
+
+ // DEPRECATED: Use setSelectedByValue() below.
+ bool setCurrentByID( const LLUUID& id ) { return selectByID(id); }
+ virtual LLUUID getCurrentID() const { return getStringUUIDSelectedItem(); }
+
+ bool operateOnSelection(EOperation op);
+ bool operateOnAll(EOperation op);
+
+ // returns false if unable to set the max count so low
+ bool setMaxItemCount(S32 max_count);
+
+ bool selectByID( const LLUUID& id ); // false if item not found
+
+ // Match item by value.asString(), which should work for string, integer, uuid.
+ // Returns false if not found.
+ bool setSelectedByValue(const LLSD& value, bool selected);
+
+ bool isSorted() const { return mSorted; }
+
+ virtual bool isSelected(const LLSD& value) const;
+
+ bool hasSelectedItem() const;
+
+ bool handleClick(S32 x, S32 y, MASK mask);
+ bool selectFirstItem();
+ bool selectNthItem( S32 index );
+ bool selectItemRange( S32 first, S32 last );
+ bool selectItemAt(S32 x, S32 y, MASK mask);
+
+ void deleteSingleItem( S32 index );
+ void deleteItems(const LLSD& sd);
+ void deleteSelectedItems();
+ void deselectAllItems(bool no_commit_on_change = false); // by default, go ahead and commit on selection change
+
+ void clearHighlightedItems();
+
+ virtual void mouseOverHighlightNthItem( S32 index );
+
+ S32 getHighlightedItemInx() const { return mHighlightedItem; }
+
+ void setDoubleClickCallback( callback_t cb ) { mOnDoubleClickCallback = cb; }
+ void setMaximumSelectCallback( callback_t cb) { mOnMaximumSelectCallback = cb; }
+ void setSortChangedCallback( callback_t cb) { mOnSortChangedCallback = cb; }
+ // Convenience function; *TODO: replace with setter above + boost::bind() in calling code
+ void setDoubleClickCallback( boost::function<void (void* userdata)> cb, void* userdata) { mOnDoubleClickCallback = boost::bind(cb, userdata); }
+
+ void swapWithNext(S32 index);
+ void swapWithPrevious(S32 index);
+
+ void setCanSelect(bool can_select) { mCanSelect = can_select; }
+ virtual bool getCanSelect() const { return mCanSelect; }
+
+ S32 getItemIndex( LLScrollListItem* item ) const;
+ S32 getItemIndex( const LLUUID& item_id ) const;
+
+ void setCommentText( const std::string& comment_text);
+ LLScrollListItem* addSeparator(EAddPosition pos);
+
+ // "Simple" interface: use this when you're creating a list that contains only unique strings, only
+ // 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, S32 column = 0 ); // false if item not found
+ bool selectItemByPrefix(const std::string& target, bool case_sensitive = true, S32 column = -1);
+ bool selectItemByPrefix(const LLWString& target, bool case_sensitive = true, S32 column = -1);
+ LLScrollListItem* getItemByLabel( const std::string& item, bool case_sensitive = true, S32 column = 0 );
+ const std::string getSelectedItemLabel(S32 column = 0) const;
+ LLSD getSelectedValue();
+
+ // If multi select is on, select all element that include substring,
+ // otherwise select first match only.
+ // If focus is true will scroll to selection.
+ // Returns number of results.
+ // Note: at the moment search happens in one go and is expensive
+ U32 searchItems(const std::string& substring, bool case_sensitive = false, bool focus = true);
+ U32 searchItems(const LLWString& substring, bool case_sensitive = false, bool focus = true);
+
+ // DEPRECATED: Use LLSD versions of setCommentText() and getSelectedValue().
+ // "StringUUID" interface: use this when you're creating a list that contains non-unique strings each of which
+ // has an associated, unique UUID, and only one of which can be selected at a time.
+ LLScrollListItem* addStringUUIDItem(const std::string& item_text, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, bool enabled = true);
+ LLUUID getStringUUIDSelectedItem() const;
+
+ LLScrollListItem* getFirstSelected() const;
+ virtual S32 getFirstSelectedIndex() const;
+ std::vector<LLScrollListItem*> getAllSelected() const;
+ S32 getNumSelected() const;
+ LLScrollListItem* getLastSelectedItem() const { return mLastSelected; }
+
+ // iterate over all items
+ LLScrollListItem* getFirstData() const;
+ LLScrollListItem* getLastData() const;
+ std::vector<LLScrollListItem*> getAllData() const;
+
+ LLScrollListItem* getItem(const LLSD& sd) const;
+
+ void setAllowMultipleSelection(bool mult ) { mAllowMultipleSelection = mult; }
+
+ void setBgWriteableColor(const LLColor4 &c) { mBgWriteableColor = c; }
+ void setReadOnlyBgColor(const LLColor4 &c) { mBgReadOnlyColor = c; }
+ void setBgSelectedColor(const LLColor4 &c) { mBgSelectedColor = c; }
+ void setBgStripeColor(const LLColor4& c) { mBgStripeColor = c; }
+ void setFgSelectedColor(const LLColor4 &c) { mFgSelectedColor = c; }
+ void setFgUnselectedColor(const LLColor4 &c){ mFgUnselectedColor = c; }
+ void setHoveredColor(const LLColor4 &c) { mHoveredColor = c; }
+ void setHighlightedColor(const LLColor4 &c) { mHighlightedColor = c; }
+ void setFgDisableColor(const LLColor4 &c) { mFgDisabledColor = c; }
+
+ void setBackgroundVisible(bool b) { mBackgroundVisible = b; }
+ void setDrawStripes(bool b) { mDrawStripes = b; }
+ void setColumnPadding(const S32 c) { mColumnPadding = c; }
+ S32 getColumnPadding() const { return mColumnPadding; }
+ void setRowPadding(const S32 c) { mColumnPadding = c; }
+ S32 getRowPadding() const { return mColumnPadding; }
+ void setCommitOnKeyboardMovement(bool b) { mCommitOnKeyboardMovement = b; }
+ void setCommitOnSelectionChange(bool b) { mCommitOnSelectionChange = b; }
+ void setAllowKeyboardMovement(bool b) { mAllowKeyboardMovement = b; }
+
+ void setMaxSelectable(U32 max_selected) { mMaxSelectable = max_selected; }
+ S32 getMaxSelectable() { return mMaxSelectable; }
+
+
+ virtual S32 getScrollPos() const;
+ virtual void setScrollPos( S32 pos );
+ S32 getSearchColumn();
+ void setSearchColumn(S32 column) { mSearchColumn = column; }
+ S32 getColumnIndexFromOffset(S32 x);
+ S32 getColumnOffsetFromIndex(S32 index);
+ S32 getRowOffsetFromIndex(S32 index);
+
+ void clearSearchString() { mSearchString.clear(); }
+
+ // support right-click context menus for avatar/group lists
+ enum ContextMenuType { MENU_NONE, MENU_AVATAR, MENU_GROUP };
+ void setContextMenu(const ContextMenuType &menu) { mContextMenuType = menu; }
+ ContextMenuType getContextMenuType() { return mContextMenuType; }
+
+ // Overridden from LLView
+ /*virtual*/ void draw();
+ /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleKeyHere(KEY key, MASK mask);
+ /*virtual*/ bool handleUnicodeCharHere(llwchar uni_char);
+ /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks);
+ /*virtual*/ bool handleScrollHWheel(S32 x, S32 y, S32 clicks);
+ /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask);
+ /*virtual*/ void setEnabled(bool enabled);
+ /*virtual*/ void setFocus( bool b );
+ /*virtual*/ void onFocusReceived();
+ /*virtual*/ void onFocusLost();
+ /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask);
+ /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true);
+
+ virtual bool isDirty() const;
+ virtual void resetDirty(); // Clear dirty state
+
+ virtual void updateLayout();
+ virtual void fitContents(S32 max_width, S32 max_height);
+
+ virtual LLRect getRequiredRect();
+ static bool rowPreceeds(LLScrollListItem *new_row, LLScrollListItem *test_row);
+
+ LLRect getItemListRect() { return mItemListRect; }
+
+ /// Returns rect, in local coords, of a given row/column
+ LLRect getCellRect(S32 row_index, S32 column_index);
+
+ // Used "internally" by the scroll bar.
+ void onScrollChange( S32 new_pos, LLScrollbar* src );
+
+ static void onClickColumn(void *userdata);
+
+ virtual void updateColumns(bool force_update = false);
+ S32 calcMaxContentWidth();
+ bool updateColumnWidths();
+
+ void setHeadingHeight(S32 heading_height);
+ /**
+ * Sets max visible lines without scroolbar, if this value equals to 0,
+ * then display all items.
+ */
+ void setPageLines(S32 page_lines );
+ void setCollapseEmptyColumns(bool collapse);
+
+ LLScrollListItem* hitItem(S32 x,S32 y);
+ virtual void scrollToShowSelected();
+
+ // LLEditMenuHandler functions
+ virtual void copy();
+ virtual bool canCopy() const;
+ virtual void cut();
+ virtual bool canCut() const;
+ virtual void selectAll();
+ virtual bool canSelectAll() const;
+ virtual void deselect();
+ virtual bool canDeselect() const;
+
+ void setNumDynamicColumns(S32 num) { mNumDynamicWidthColumns = num; }
+ void updateStaticColumnWidth(LLScrollListColumn* col, S32 new_width);
+ S32 getTotalStaticColumnWidth() { return mTotalStaticColumnWidth; }
+
+ std::string getSortColumnName();
+ bool getSortAscending() { return mSortColumns.empty() ? true : mSortColumns.back().second; }
+ bool hasSortOrder() const;
+ void clearSortOrder();
+
+ void setAlternateSort() { mAlternateSort = true; }
+
+ void selectPrevItem(bool extend_selection = false);
+ void selectNextItem(bool extend_selection = false);
+ S32 selectMultiple(uuid_vec_t ids);
+ // conceptually const, but mutates mItemList
+ void updateSort() const;
+ // sorts a list without affecting the permanent sort order (so further list insertions can be unsorted, for example)
+ void sortOnce(S32 column, bool ascending);
+
+ // manually call this whenever editing list items in place to flag need for resorting
+ void setNeedsSort(bool val = true) { mSorted = !val; }
+ void dirtyColumns(); // some operation has potentially affected column layout or ordering
+
+ bool highlightMatchingItems(const std::string& filter_str);
+
+ boost::signals2::connection setSortCallback(sort_signal_t::slot_type cb )
+ {
+ if (!mSortCallback) mSortCallback = new sort_signal_t();
+ return mSortCallback->connect(cb);
+ }
+
+ boost::signals2::connection setIsFriendCallback(const is_friend_signal_t::slot_type& cb);
+
+
+protected:
+ // "Full" interface: use this when you're creating a list that has one or more of the following:
+ // * contains icons
+ // * contains multiple columns
+ // * allows multiple selection
+ // * has items that are not guarenteed to have unique names
+ // * has additional per-item data (e.g. a UUID or void* userdata)
+ //
+ // To add items using this approach, create new LLScrollListItems and LLScrollListCells. Add the
+ // cells (column entries) to each item, and add the item to the LLScrollListCtrl.
+ //
+ // The LLScrollListCtrl owns its items and is responsible for deleting them
+ // (except in the case that the addItem() call fails, in which case it is up
+ // to the caller to delete the item)
+ //
+ // returns false if item faile to be added to list, does NOT delete 'item'
+ bool addItem( LLScrollListItem* item, EAddPosition pos = ADD_BOTTOM, bool requires_column = true );
+
+ typedef std::deque<LLScrollListItem *> item_list;
+ item_list& getItemList() { return mItemList; }
+
+ void updateLineHeight();
+
+private:
+ void drawItems();
+
+ void updateLineHeightInsert(LLScrollListItem* item);
+ void reportInvalidInput();
+ bool isRepeatedChars(const LLWString& string) const;
+ void selectItem(LLScrollListItem* itemp, S32 cell, bool single_select = true);
+ void deselectItem(LLScrollListItem* itemp);
+ void commitIfChanged();
+ 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 removeFriend(std::string id);
+ static void reportAbuse(std::string id, bool is_group);
+ static void showNameDetails(std::string id, bool is_group);
+ static void copyNameToClipboard(std::string id, bool is_group);
+ static void copySLURLToClipboard(std::string id, bool is_group);
+
+ S32 mLineHeight; // the max height of a single line
+ S32 mScrollLines; // how many lines we've scrolled down
+ S32 mPageLines; // max number of lines is it possible to see on the screen given mRect and mLineHeight
+ 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;
+ ESelectionType mSelectionType;
+ bool mNeedsScroll;
+ bool mMouseWheelOpaque;
+ bool mCanSelect;
+ bool mCanSort; // Whether user is allowed to sort
+ bool mDisplayColumnHeaders;
+ bool mColumnsDirty;
+ bool mColumnWidthsDirty;
+
+ bool mAlternateSort;
+
+ mutable item_list mItemList;
+
+ LLScrollListItem *mLastSelected;
+
+ S32 mMaxItemCount;
+
+ LLRect mItemListRect;
+ S32 mColumnPadding;
+ S32 mRowPadding;
+
+ bool mBackgroundVisible;
+ bool mDrawStripes;
+
+ LLUIColor mBgWriteableColor;
+ LLUIColor mBgReadOnlyColor;
+ LLUIColor mBgSelectedColor;
+ LLUIColor mBgStripeColor;
+ LLUIColor mFgSelectedColor;
+ LLUIColor mFgUnselectedColor;
+ LLUIColor mFgDisabledColor;
+ LLUIColor mHoveredColor;
+ LLUIColor mHighlightedColor;
+
+ S32 mBorderThickness;
+ callback_t mOnDoubleClickCallback;
+ callback_t mOnMaximumSelectCallback;
+ callback_t mOnSortChangedCallback;
+
+ S32 mHighlightedItem;
+ class LLViewBorder* mBorder;
+ LLHandle<LLContextMenu> mPopupMenuHandle;
+
+ LLView *mCommentTextView;
+
+ LLWString mSearchString;
+ LLFrameTimer mSearchTimer;
+
+ S32 mSearchColumn;
+ S32 mNumDynamicWidthColumns;
+ S32 mTotalStaticColumnWidth;
+ S32 mTotalColumnPadding;
+
+ mutable bool mSorted;
+
+ typedef std::map<std::string, LLScrollListColumn*> column_map_t;
+ column_map_t mColumns;
+
+ bool mDirty;
+ S32 mOriginalSelection;
+
+ ContextMenuType mContextMenuType;
+
+ typedef std::vector<LLScrollListColumn*> ordered_columns_t;
+ ordered_columns_t mColumnsIndexed;
+
+ typedef std::pair<S32, bool> sort_column_t;
+ std::vector<sort_column_t> mSortColumns;
+
+ sort_signal_t* mSortCallback;
+
+ is_friend_signal_t* mIsFriendSignal;
+}; // end class LLScrollListCtrl
+
+#endif // LL_SCROLLLISTCTRL_H
diff --git a/indra/llui/llscrolllistitem.cpp b/indra/llui/llscrolllistitem.cpp
index 6d360a1bd9..8cfe971fa6 100644
--- a/indra/llui/llscrolllistitem.cpp
+++ b/indra/llui/llscrolllistitem.cpp
@@ -1,204 +1,204 @@
-/**
- * @file llscrolllistitem.cpp
- * @brief Scroll lists are composed of rows (items), each of which
- * contains columns (cells).
- *
- * $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 "llscrolllistitem.h"
-
-#include "llrect.h"
-#include "llui.h"
-
-
-//---------------------------------------------------------------------------
-// LLScrollListItem
-//---------------------------------------------------------------------------
-
-LLScrollListItem::LLScrollListItem( const Params& p )
-: mSelected(false),
- mHighlighted(false),
- mHoverIndex(-1),
- mSelectedIndex(-1),
- mEnabled(p.enabled),
- mUserdata(p.userdata),
- mItemValue(p.value),
- mItemAltValue(p.alt_value)
-{
-}
-
-
-LLScrollListItem::~LLScrollListItem()
-{
- std::for_each(mColumns.begin(), mColumns.end(), DeletePointer());
- mColumns.clear();
-}
-
-void LLScrollListItem::setSelected(bool b)
-{
- mSelected = b;
- mSelectedIndex = -1;
-}
-
-void LLScrollListItem::setHighlighted(bool b)
-{
- mHighlighted = b;
- mHoverIndex = -1;
-}
-
-void LLScrollListItem::setHoverCell(S32 cell)
-{
- mHoverIndex = cell;
-}
-
-void LLScrollListItem::setSelectedCell(S32 cell)
-{
- mSelectedIndex = cell;
-}
-
-void LLScrollListItem::addColumn(const LLScrollListCell::Params& p)
-{
- mColumns.push_back(LLScrollListCell::create(p));
-}
-
-void LLScrollListItem::setNumColumns(S32 columns)
-{
- S32 prev_columns = mColumns.size();
- if (columns < prev_columns)
- {
- std::for_each(mColumns.begin()+columns, mColumns.end(), DeletePointer());
- }
-
- mColumns.resize(columns);
-
- for (S32 col = prev_columns; col < columns; ++col)
- {
- mColumns[col] = NULL;
- }
-}
-
-void LLScrollListItem::setColumn( S32 column, LLScrollListCell *cell )
-{
- if (column < (S32)mColumns.size())
- {
- delete mColumns[column];
- mColumns[column] = cell;
- }
- else
- {
- LL_ERRS() << "LLScrollListItem::setColumn: bad column: " << column << LL_ENDL;
- }
-}
-
-
-S32 LLScrollListItem::getNumColumns() const
-{
- return mColumns.size();
-}
-
-LLScrollListCell* LLScrollListItem::getColumn(const S32 i) const
-{
- if (0 <= i && i < (S32)mColumns.size())
- {
- return mColumns[i];
- }
- return NULL;
-}
-
-std::string LLScrollListItem::getContentsCSV() const
-{
- std::string ret;
-
- S32 count = getNumColumns();
- for (S32 i=0; i<count; ++i)
- {
- ret += getColumn(i)->getValue().asString();
- if (i < count-1)
- {
- ret += ", ";
- }
- }
-
- return ret;
-}
-
-
-void LLScrollListItem::draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& hover_color, const LLColor4& select_color, const LLColor4& highlight_color, S32 column_padding)
-{
- // draw background rect
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- LLRect bg_rect = rect;
- if (mSelectedIndex < 0 && getSelected())
- {
- // Whole item is highlighted/selected
- gl_rect_2d(bg_rect, select_color);
- }
- else if (mHoverIndex < 0)
- {
- // Whole item is highlighted/selected
- gl_rect_2d(bg_rect, hover_color);
- }
-
- S32 cur_x = rect.mLeft;
- S32 num_cols = getNumColumns();
- S32 cur_col = 0;
-
- for (LLScrollListCell* cell = getColumn(0); cur_col < num_cols; cell = getColumn(++cur_col))
- {
- // Two ways a cell could be hidden
- if (cell->getWidth() < 0
- || !cell->getVisible()) continue;
-
- LLUI::pushMatrix();
- {
- LLUI::translate((F32) cur_x, (F32) rect.mBottom);
-
- if (mSelectedIndex == cur_col)
- {
- // select specific cell
- LLRect highlight_rect(0,
- cell->getHeight(),
- cell->getWidth(),
- 0);
- gl_rect_2d(highlight_rect, select_color);
- }
- else if (mHoverIndex == cur_col)
- {
- // highlight specific cell
- LLRect highlight_rect(0,
- cell->getHeight(),
- cell->getWidth() ,
- 0);
- gl_rect_2d(highlight_rect, hover_color);
- }
-
- cell->draw( fg_color, highlight_color );
- }
- LLUI::popMatrix();
-
- cur_x += cell->getWidth() + column_padding;
- }
-}
-
+/**
+ * @file llscrolllistitem.cpp
+ * @brief Scroll lists are composed of rows (items), each of which
+ * contains columns (cells).
+ *
+ * $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 "llscrolllistitem.h"
+
+#include "llrect.h"
+#include "llui.h"
+
+
+//---------------------------------------------------------------------------
+// LLScrollListItem
+//---------------------------------------------------------------------------
+
+LLScrollListItem::LLScrollListItem( const Params& p )
+: mSelected(false),
+ mHighlighted(false),
+ mHoverIndex(-1),
+ mSelectedIndex(-1),
+ mEnabled(p.enabled),
+ mUserdata(p.userdata),
+ mItemValue(p.value),
+ mItemAltValue(p.alt_value)
+{
+}
+
+
+LLScrollListItem::~LLScrollListItem()
+{
+ std::for_each(mColumns.begin(), mColumns.end(), DeletePointer());
+ mColumns.clear();
+}
+
+void LLScrollListItem::setSelected(bool b)
+{
+ mSelected = b;
+ mSelectedIndex = -1;
+}
+
+void LLScrollListItem::setHighlighted(bool b)
+{
+ mHighlighted = b;
+ mHoverIndex = -1;
+}
+
+void LLScrollListItem::setHoverCell(S32 cell)
+{
+ mHoverIndex = cell;
+}
+
+void LLScrollListItem::setSelectedCell(S32 cell)
+{
+ mSelectedIndex = cell;
+}
+
+void LLScrollListItem::addColumn(const LLScrollListCell::Params& p)
+{
+ mColumns.push_back(LLScrollListCell::create(p));
+}
+
+void LLScrollListItem::setNumColumns(S32 columns)
+{
+ S32 prev_columns = mColumns.size();
+ if (columns < prev_columns)
+ {
+ std::for_each(mColumns.begin()+columns, mColumns.end(), DeletePointer());
+ }
+
+ mColumns.resize(columns);
+
+ for (S32 col = prev_columns; col < columns; ++col)
+ {
+ mColumns[col] = NULL;
+ }
+}
+
+void LLScrollListItem::setColumn( S32 column, LLScrollListCell *cell )
+{
+ if (column < (S32)mColumns.size())
+ {
+ delete mColumns[column];
+ mColumns[column] = cell;
+ }
+ else
+ {
+ LL_ERRS() << "LLScrollListItem::setColumn: bad column: " << column << LL_ENDL;
+ }
+}
+
+
+S32 LLScrollListItem::getNumColumns() const
+{
+ return mColumns.size();
+}
+
+LLScrollListCell* LLScrollListItem::getColumn(const S32 i) const
+{
+ if (0 <= i && i < (S32)mColumns.size())
+ {
+ return mColumns[i];
+ }
+ return NULL;
+}
+
+std::string LLScrollListItem::getContentsCSV() const
+{
+ std::string ret;
+
+ S32 count = getNumColumns();
+ for (S32 i=0; i<count; ++i)
+ {
+ ret += getColumn(i)->getValue().asString();
+ if (i < count-1)
+ {
+ ret += ", ";
+ }
+ }
+
+ return ret;
+}
+
+
+void LLScrollListItem::draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& hover_color, const LLColor4& select_color, const LLColor4& highlight_color, S32 column_padding)
+{
+ // draw background rect
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ LLRect bg_rect = rect;
+ if (mSelectedIndex < 0 && getSelected())
+ {
+ // Whole item is highlighted/selected
+ gl_rect_2d(bg_rect, select_color);
+ }
+ else if (mHoverIndex < 0)
+ {
+ // Whole item is highlighted/selected
+ gl_rect_2d(bg_rect, hover_color);
+ }
+
+ S32 cur_x = rect.mLeft;
+ S32 num_cols = getNumColumns();
+ S32 cur_col = 0;
+
+ for (LLScrollListCell* cell = getColumn(0); cur_col < num_cols; cell = getColumn(++cur_col))
+ {
+ // Two ways a cell could be hidden
+ if (cell->getWidth() < 0
+ || !cell->getVisible()) continue;
+
+ LLUI::pushMatrix();
+ {
+ LLUI::translate((F32) cur_x, (F32) rect.mBottom);
+
+ if (mSelectedIndex == cur_col)
+ {
+ // select specific cell
+ LLRect highlight_rect(0,
+ cell->getHeight(),
+ cell->getWidth(),
+ 0);
+ gl_rect_2d(highlight_rect, select_color);
+ }
+ else if (mHoverIndex == cur_col)
+ {
+ // highlight specific cell
+ LLRect highlight_rect(0,
+ cell->getHeight(),
+ cell->getWidth() ,
+ 0);
+ gl_rect_2d(highlight_rect, hover_color);
+ }
+
+ cell->draw( fg_color, highlight_color );
+ }
+ LLUI::popMatrix();
+
+ cur_x += cell->getWidth() + column_padding;
+ }
+}
+
diff --git a/indra/llui/llscrolllistitem.h b/indra/llui/llscrolllistitem.h
index a6fa05cd44..3e9e6e084c 100644
--- a/indra/llui/llscrolllistitem.h
+++ b/indra/llui/llscrolllistitem.h
@@ -1,142 +1,142 @@
-/**
- * @file llscrolllistitem.h
- * @brief Scroll lists are composed of rows (items), each of which
- * contains columns (cells).
- *
- * $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 LLSCROLLLISTITEM_H
-#define LLSCROLLLISTITEM_H
-
-#include "llpointer.h" // LLPointer<>
-#include "llsd.h"
-#include "v4color.h"
-#include "llinitparam.h"
-#include "llscrolllistcell.h"
-#include "llcoord.h"
-
-#include <vector>
-
-class LLCheckBoxCtrl;
-class LLResizeBar;
-class LLScrollListCtrl;
-class LLScrollColumnHeader;
-class LLUIImage;
-
-//---------------------------------------------------------------------------
-// LLScrollListItem
-//---------------------------------------------------------------------------
-class LLScrollListItem
-{
- friend class LLScrollListCtrl;
-public:
- struct Params : public LLInitParam::Block<Params>
- {
- Optional<bool> enabled;
- Optional<void*> userdata;
- Optional<LLSD> value;
- Optional<LLSD> alt_value;
-
- Ignored name; // use for localization tools
- Ignored type;
- Ignored length;
-
- Multiple<LLScrollListCell::Params> columns;
-
- Params()
- : enabled("enabled", true),
- value("value"),
- alt_value("alt_value"),
- name("name"),
- type("type"),
- length("length"),
- columns("columns")
- {
- addSynonym(columns, "column");
- addSynonym(value, "id");
- }
- };
-
- virtual ~LLScrollListItem();
-
- void setSelected( bool b );
- bool getSelected() const { return mSelected; }
-
- void setEnabled( bool b ) { mEnabled = b; }
- bool getEnabled() const { return mEnabled; }
-
- void setHighlighted( bool b );
- bool getHighlighted() const { return mHighlighted; }
-
- void setSelectedCell( S32 cell );
- S32 getSelectedCell() const { return mSelectedIndex; }
-
- void setHoverCell( S32 cell );
- S32 getHoverCell() const { return mHoverIndex; }
-
- void setUserdata( void* userdata ) { mUserdata = userdata; }
- void* getUserdata() const { return mUserdata; }
-
- virtual LLUUID getUUID() const { return mItemValue.asUUID(); }
- LLSD getValue() const { return mItemValue; }
- LLSD getAltValue() const { return mItemAltValue; }
-
- void setRect(LLRect rect) { mRectangle = rect; }
- LLRect getRect() const { return mRectangle; }
-
- void addColumn( const LLScrollListCell::Params& p );
-
- void setNumColumns(S32 columns);
-
- void setColumn( S32 column, LLScrollListCell *cell );
-
- S32 getNumColumns() const;
-
- LLScrollListCell *getColumn(const S32 i) const;
-
- std::string getContentsCSV() const;
-
- virtual void draw(const LLRect& rect,
- const LLColor4& fg_color,
- const LLColor4& hover_color, // highlight/hover selection of whole item or cell
- const LLColor4& select_color, // highlight/hover selection of whole item or cell
- const LLColor4& highlight_color, // highlights contents of cells (ex: text)
- S32 column_padding);
-
-protected:
- LLScrollListItem( const Params& );
-
-private:
- bool mSelected;
- bool mHighlighted;
- S32 mHoverIndex;
- S32 mSelectedIndex;
- bool mEnabled;
- void* mUserdata;
- LLSD mItemValue;
- LLSD mItemAltValue;
- std::vector<LLScrollListCell *> mColumns;
- LLRect mRectangle;
-};
-
-#endif
+/**
+ * @file llscrolllistitem.h
+ * @brief Scroll lists are composed of rows (items), each of which
+ * contains columns (cells).
+ *
+ * $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 LLSCROLLLISTITEM_H
+#define LLSCROLLLISTITEM_H
+
+#include "llpointer.h" // LLPointer<>
+#include "llsd.h"
+#include "v4color.h"
+#include "llinitparam.h"
+#include "llscrolllistcell.h"
+#include "llcoord.h"
+
+#include <vector>
+
+class LLCheckBoxCtrl;
+class LLResizeBar;
+class LLScrollListCtrl;
+class LLScrollColumnHeader;
+class LLUIImage;
+
+//---------------------------------------------------------------------------
+// LLScrollListItem
+//---------------------------------------------------------------------------
+class LLScrollListItem
+{
+ friend class LLScrollListCtrl;
+public:
+ struct Params : public LLInitParam::Block<Params>
+ {
+ Optional<bool> enabled;
+ Optional<void*> userdata;
+ Optional<LLSD> value;
+ Optional<LLSD> alt_value;
+
+ Ignored name; // use for localization tools
+ Ignored type;
+ Ignored length;
+
+ Multiple<LLScrollListCell::Params> columns;
+
+ Params()
+ : enabled("enabled", true),
+ value("value"),
+ alt_value("alt_value"),
+ name("name"),
+ type("type"),
+ length("length"),
+ columns("columns")
+ {
+ addSynonym(columns, "column");
+ addSynonym(value, "id");
+ }
+ };
+
+ virtual ~LLScrollListItem();
+
+ void setSelected( bool b );
+ bool getSelected() const { return mSelected; }
+
+ void setEnabled( bool b ) { mEnabled = b; }
+ bool getEnabled() const { return mEnabled; }
+
+ void setHighlighted( bool b );
+ bool getHighlighted() const { return mHighlighted; }
+
+ void setSelectedCell( S32 cell );
+ S32 getSelectedCell() const { return mSelectedIndex; }
+
+ void setHoverCell( S32 cell );
+ S32 getHoverCell() const { return mHoverIndex; }
+
+ void setUserdata( void* userdata ) { mUserdata = userdata; }
+ void* getUserdata() const { return mUserdata; }
+
+ virtual LLUUID getUUID() const { return mItemValue.asUUID(); }
+ LLSD getValue() const { return mItemValue; }
+ LLSD getAltValue() const { return mItemAltValue; }
+
+ void setRect(LLRect rect) { mRectangle = rect; }
+ LLRect getRect() const { return mRectangle; }
+
+ void addColumn( const LLScrollListCell::Params& p );
+
+ void setNumColumns(S32 columns);
+
+ void setColumn( S32 column, LLScrollListCell *cell );
+
+ S32 getNumColumns() const;
+
+ LLScrollListCell *getColumn(const S32 i) const;
+
+ std::string getContentsCSV() const;
+
+ virtual void draw(const LLRect& rect,
+ const LLColor4& fg_color,
+ const LLColor4& hover_color, // highlight/hover selection of whole item or cell
+ const LLColor4& select_color, // highlight/hover selection of whole item or cell
+ const LLColor4& highlight_color, // highlights contents of cells (ex: text)
+ S32 column_padding);
+
+protected:
+ LLScrollListItem( const Params& );
+
+private:
+ bool mSelected;
+ bool mHighlighted;
+ S32 mHoverIndex;
+ S32 mSelectedIndex;
+ bool mEnabled;
+ void* mUserdata;
+ LLSD mItemValue;
+ LLSD mItemAltValue;
+ std::vector<LLScrollListCell *> mColumns;
+ LLRect mRectangle;
+};
+
+#endif
diff --git a/indra/llui/llsearchablecontrol.h b/indra/llui/llsearchablecontrol.h
index f7f1ffa0a5..bccb4858e1 100644
--- a/indra/llui/llsearchablecontrol.h
+++ b/indra/llui/llsearchablecontrol.h
@@ -31,40 +31,40 @@
namespace ll
{
- namespace ui
- {
- class SearchableControl
- {
- mutable bool mIsHighlighed;
- public:
- SearchableControl()
- : mIsHighlighed( false )
- { }
- virtual ~SearchableControl()
- { }
+ namespace ui
+ {
+ class SearchableControl
+ {
+ mutable bool mIsHighlighed;
+ public:
+ SearchableControl()
+ : mIsHighlighed( false )
+ { }
+ virtual ~SearchableControl()
+ { }
- LLColor4 getHighlightColor( ) const
- {
- static LLUIColor highlight_color = LLUIColorTable::instance().getColor("SearchableControlHighlightColor", LLColor4::red);
- return highlight_color.get();
- }
+ LLColor4 getHighlightColor( ) const
+ {
+ static LLUIColor highlight_color = LLUIColorTable::instance().getColor("SearchableControlHighlightColor", LLColor4::red);
+ return highlight_color.get();
+ }
- void setHighlighted( bool aVal ) const
- {
- mIsHighlighed = aVal;
- onSetHighlight( );
- }
- bool getHighlighted( ) const
- { return mIsHighlighed; }
+ void setHighlighted( bool aVal ) const
+ {
+ mIsHighlighed = aVal;
+ onSetHighlight( );
+ }
+ bool getHighlighted( ) const
+ { return mIsHighlighed; }
- std::string getSearchText() const
- { return _getSearchText(); }
- protected:
- virtual std::string _getSearchText() const = 0;
- virtual void onSetHighlight( ) const
- { }
- };
- }
+ std::string getSearchText() const
+ { return _getSearchText(); }
+ protected:
+ virtual std::string _getSearchText() const = 0;
+ virtual void onSetHighlight( ) const
+ { }
+ };
+ }
}
diff --git a/indra/llui/llsearcheditor.cpp b/indra/llui/llsearcheditor.cpp
index 46fb8c64b5..6229bf031a 100644
--- a/indra/llui/llsearcheditor.cpp
+++ b/indra/llui/llsearcheditor.cpp
@@ -1,218 +1,218 @@
-/**
- * @file llsearcheditor.cpp
- * @brief LLSearchEditor implementation
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-// Text editor widget to let users enter a single line.
-
-#include "linden_common.h"
-
-#include "llsearcheditor.h"
-#include "llkeyboard.h"
-
-LLSearchEditor::LLSearchEditor(const LLSearchEditor::Params& p)
-: LLUICtrl(p),
- mSearchButton(NULL),
- mClearButton(NULL),
- mEditorImage(p.background_image),
- mEditorImageFocused(p.background_image_focused),
- mEditorSearchImage(p.background_image_highlight),
- mHighlightTextField(p.highlight_text_field)
-{
- S32 srch_btn_top = p.search_button.top_pad + p.search_button.rect.height;
- S32 srch_btn_right = p.search_button.rect.width + p.search_button.left_pad;
- LLRect srch_btn_rect(p.search_button.left_pad, srch_btn_top, srch_btn_right, p.search_button.top_pad);
-
- S32 clr_btn_top = p.clear_button.rect.bottom + p.clear_button.rect.height;
- S32 clr_btn_right = getRect().getWidth() - p.clear_button.pad_right;
- S32 clr_btn_left = clr_btn_right - p.clear_button.rect.width;
- LLRect clear_btn_rect(clr_btn_left, clr_btn_top, clr_btn_right, p.clear_button.rect.bottom);
-
- S32 text_pad_left = p.text_pad_left;
- S32 text_pad_right = p.text_pad_right;
-
- if (p.search_button_visible)
- text_pad_left += srch_btn_rect.getWidth();
-
- if (p.clear_button_visible)
- text_pad_right = getRect().getWidth() - clr_btn_left + p.clear_button.pad_left;
-
- // Set up line editor.
- LLLineEditor::Params line_editor_params(p);
- line_editor_params.name("filter edit box");
- line_editor_params.background_image(p.background_image);
- line_editor_params.background_image_focused(p.background_image_focused);
- line_editor_params.rect(getLocalRect());
- line_editor_params.follows.flags(FOLLOWS_ALL);
- line_editor_params.text_pad_left(text_pad_left);
- line_editor_params.text_pad_right(text_pad_right);
- line_editor_params.revert_on_esc(false);
- line_editor_params.commit_callback.function(boost::bind(&LLUICtrl::onCommit, this));
- line_editor_params.keystroke_callback(boost::bind(&LLSearchEditor::handleKeystroke, this));
-
- mSearchEditor = LLUICtrlFactory::create<LLLineEditor>(line_editor_params);
- mSearchEditor->setPassDelete(true);
- addChild(mSearchEditor);
-
- if (p.search_button_visible)
- {
- // Set up search button.
- LLButton::Params srch_btn_params(p.search_button);
- srch_btn_params.name(std::string("search button"));
- srch_btn_params.rect(srch_btn_rect) ;
- srch_btn_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_TOP);
- srch_btn_params.tab_stop(false);
- srch_btn_params.click_callback.function(boost::bind(&LLUICtrl::onCommit, this));
-
- mSearchButton = LLUICtrlFactory::create<LLButton>(srch_btn_params);
- mSearchEditor->addChild(mSearchButton);
- }
-
- if (p.clear_button_visible)
- {
- // Set up clear button.
- LLButton::Params clr_btn_params(p.clear_button);
- clr_btn_params.name(std::string("clear button"));
- clr_btn_params.rect(clear_btn_rect) ;
- clr_btn_params.follows.flags(FOLLOWS_RIGHT|FOLLOWS_TOP);
- clr_btn_params.tab_stop(false);
- clr_btn_params.click_callback.function(boost::bind(&LLSearchEditor::onClearButtonClick, this, _2));
-
- mClearButton = LLUICtrlFactory::create<LLButton>(clr_btn_params);
- mSearchEditor->addChild(mClearButton);
- }
-}
-
-LLSearchEditor::~LLSearchEditor()
-{
- mKeystrokeCallback = NULL;
- mTextChangedCallback = NULL;
- setCommitOnFocusLost(false);
-
- mSearchButton = NULL;
- mClearButton = NULL;
- mSearchEditor->deleteAllChildren();
- deleteAllChildren();
-}
-
-//virtual
-void LLSearchEditor::draw()
-{
- if (mClearButton)
- mClearButton->setVisible(!mSearchEditor->getWText().empty());
-
- if (mHighlightTextField)
- {
- if (!mSearchEditor->getWText().empty())
- {
- mSearchEditor->setBgImage(mEditorSearchImage);
- mSearchEditor->setBgImageFocused(mEditorSearchImage);
- }
- else
- {
- mSearchEditor->setBgImage(mEditorImage);
- mSearchEditor->setBgImageFocused(mEditorImageFocused);
- }
- }
-
- LLUICtrl::draw();
-}
-
-//virtual
-void LLSearchEditor::setValue(const LLSD& value )
-{
- mSearchEditor->setValue(value);
-}
-
-//virtual
-LLSD LLSearchEditor::getValue() const
-{
- return mSearchEditor->getValue();
-}
-
-//virtual
-bool LLSearchEditor::setTextArg( const std::string& key, const LLStringExplicit& text )
-{
- return mSearchEditor->setTextArg(key, text);
-}
-
-//virtual
-bool LLSearchEditor::setLabelArg( const std::string& key, const LLStringExplicit& text )
-{
- return mSearchEditor->setLabelArg(key, text);
-}
-
-//virtual
-void LLSearchEditor::setLabel( const LLStringExplicit &new_label )
-{
- mSearchEditor->setLabel(new_label);
-}
-
-//virtual
-void LLSearchEditor::clear()
-{
- if (mSearchEditor)
- {
- mSearchEditor->clear();
- }
-}
-
-//virtual
-void LLSearchEditor::setFocus( bool b )
-{
- if (mSearchEditor)
- {
- mSearchEditor->setFocus(b);
- }
-}
-
-void LLSearchEditor::onClearButtonClick(const LLSD& data)
-{
- setText(LLStringUtil::null);
- if (mTextChangedCallback)
- {
- mTextChangedCallback(this, getValue());
- }
- mSearchEditor->onCommit(); // force keystroke callback
-}
-
-void LLSearchEditor::handleKeystroke()
-{
- if (mKeystrokeCallback)
- {
- mKeystrokeCallback(this, getValue());
- }
-
- KEY key = gKeyboard->currentKey();
- if (key == KEY_LEFT ||
- key == KEY_RIGHT)
- {
- return;
- }
-
- if (mTextChangedCallback)
- {
- mTextChangedCallback(this, getValue());
- }
-}
+/**
+ * @file llsearcheditor.cpp
+ * @brief LLSearchEditor implementation
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+// Text editor widget to let users enter a single line.
+
+#include "linden_common.h"
+
+#include "llsearcheditor.h"
+#include "llkeyboard.h"
+
+LLSearchEditor::LLSearchEditor(const LLSearchEditor::Params& p)
+: LLUICtrl(p),
+ mSearchButton(NULL),
+ mClearButton(NULL),
+ mEditorImage(p.background_image),
+ mEditorImageFocused(p.background_image_focused),
+ mEditorSearchImage(p.background_image_highlight),
+ mHighlightTextField(p.highlight_text_field)
+{
+ S32 srch_btn_top = p.search_button.top_pad + p.search_button.rect.height;
+ S32 srch_btn_right = p.search_button.rect.width + p.search_button.left_pad;
+ LLRect srch_btn_rect(p.search_button.left_pad, srch_btn_top, srch_btn_right, p.search_button.top_pad);
+
+ S32 clr_btn_top = p.clear_button.rect.bottom + p.clear_button.rect.height;
+ S32 clr_btn_right = getRect().getWidth() - p.clear_button.pad_right;
+ S32 clr_btn_left = clr_btn_right - p.clear_button.rect.width;
+ LLRect clear_btn_rect(clr_btn_left, clr_btn_top, clr_btn_right, p.clear_button.rect.bottom);
+
+ S32 text_pad_left = p.text_pad_left;
+ S32 text_pad_right = p.text_pad_right;
+
+ if (p.search_button_visible)
+ text_pad_left += srch_btn_rect.getWidth();
+
+ if (p.clear_button_visible)
+ text_pad_right = getRect().getWidth() - clr_btn_left + p.clear_button.pad_left;
+
+ // Set up line editor.
+ LLLineEditor::Params line_editor_params(p);
+ line_editor_params.name("filter edit box");
+ line_editor_params.background_image(p.background_image);
+ line_editor_params.background_image_focused(p.background_image_focused);
+ line_editor_params.rect(getLocalRect());
+ line_editor_params.follows.flags(FOLLOWS_ALL);
+ line_editor_params.text_pad_left(text_pad_left);
+ line_editor_params.text_pad_right(text_pad_right);
+ line_editor_params.revert_on_esc(false);
+ line_editor_params.commit_callback.function(boost::bind(&LLUICtrl::onCommit, this));
+ line_editor_params.keystroke_callback(boost::bind(&LLSearchEditor::handleKeystroke, this));
+
+ mSearchEditor = LLUICtrlFactory::create<LLLineEditor>(line_editor_params);
+ mSearchEditor->setPassDelete(true);
+ addChild(mSearchEditor);
+
+ if (p.search_button_visible)
+ {
+ // Set up search button.
+ LLButton::Params srch_btn_params(p.search_button);
+ srch_btn_params.name(std::string("search button"));
+ srch_btn_params.rect(srch_btn_rect) ;
+ srch_btn_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_TOP);
+ srch_btn_params.tab_stop(false);
+ srch_btn_params.click_callback.function(boost::bind(&LLUICtrl::onCommit, this));
+
+ mSearchButton = LLUICtrlFactory::create<LLButton>(srch_btn_params);
+ mSearchEditor->addChild(mSearchButton);
+ }
+
+ if (p.clear_button_visible)
+ {
+ // Set up clear button.
+ LLButton::Params clr_btn_params(p.clear_button);
+ clr_btn_params.name(std::string("clear button"));
+ clr_btn_params.rect(clear_btn_rect) ;
+ clr_btn_params.follows.flags(FOLLOWS_RIGHT|FOLLOWS_TOP);
+ clr_btn_params.tab_stop(false);
+ clr_btn_params.click_callback.function(boost::bind(&LLSearchEditor::onClearButtonClick, this, _2));
+
+ mClearButton = LLUICtrlFactory::create<LLButton>(clr_btn_params);
+ mSearchEditor->addChild(mClearButton);
+ }
+}
+
+LLSearchEditor::~LLSearchEditor()
+{
+ mKeystrokeCallback = NULL;
+ mTextChangedCallback = NULL;
+ setCommitOnFocusLost(false);
+
+ mSearchButton = NULL;
+ mClearButton = NULL;
+ mSearchEditor->deleteAllChildren();
+ deleteAllChildren();
+}
+
+//virtual
+void LLSearchEditor::draw()
+{
+ if (mClearButton)
+ mClearButton->setVisible(!mSearchEditor->getWText().empty());
+
+ if (mHighlightTextField)
+ {
+ if (!mSearchEditor->getWText().empty())
+ {
+ mSearchEditor->setBgImage(mEditorSearchImage);
+ mSearchEditor->setBgImageFocused(mEditorSearchImage);
+ }
+ else
+ {
+ mSearchEditor->setBgImage(mEditorImage);
+ mSearchEditor->setBgImageFocused(mEditorImageFocused);
+ }
+ }
+
+ LLUICtrl::draw();
+}
+
+//virtual
+void LLSearchEditor::setValue(const LLSD& value )
+{
+ mSearchEditor->setValue(value);
+}
+
+//virtual
+LLSD LLSearchEditor::getValue() const
+{
+ return mSearchEditor->getValue();
+}
+
+//virtual
+bool LLSearchEditor::setTextArg( const std::string& key, const LLStringExplicit& text )
+{
+ return mSearchEditor->setTextArg(key, text);
+}
+
+//virtual
+bool LLSearchEditor::setLabelArg( const std::string& key, const LLStringExplicit& text )
+{
+ return mSearchEditor->setLabelArg(key, text);
+}
+
+//virtual
+void LLSearchEditor::setLabel( const LLStringExplicit &new_label )
+{
+ mSearchEditor->setLabel(new_label);
+}
+
+//virtual
+void LLSearchEditor::clear()
+{
+ if (mSearchEditor)
+ {
+ mSearchEditor->clear();
+ }
+}
+
+//virtual
+void LLSearchEditor::setFocus( bool b )
+{
+ if (mSearchEditor)
+ {
+ mSearchEditor->setFocus(b);
+ }
+}
+
+void LLSearchEditor::onClearButtonClick(const LLSD& data)
+{
+ setText(LLStringUtil::null);
+ if (mTextChangedCallback)
+ {
+ mTextChangedCallback(this, getValue());
+ }
+ mSearchEditor->onCommit(); // force keystroke callback
+}
+
+void LLSearchEditor::handleKeystroke()
+{
+ if (mKeystrokeCallback)
+ {
+ mKeystrokeCallback(this, getValue());
+ }
+
+ KEY key = gKeyboard->currentKey();
+ if (key == KEY_LEFT ||
+ key == KEY_RIGHT)
+ {
+ return;
+ }
+
+ if (mTextChangedCallback)
+ {
+ mTextChangedCallback(this, getValue());
+ }
+}
diff --git a/indra/llui/llsearcheditor.h b/indra/llui/llsearcheditor.h
index 3f8c6323b0..7c6e5dc554 100644
--- a/indra/llui/llsearcheditor.h
+++ b/indra/llui/llsearcheditor.h
@@ -1,114 +1,114 @@
-/**
- * @file llsearcheditor.h
- * @brief Text editor widget that represents a search operation
- *
- * Features:
- * Text entry of a single line (text, delete, left and right arrow, insert, return).
- * Callbacks either on every keystroke or just on the return key.
- * Focus (allow multiple text entry widgets)
- * Clipboard (cut, copy, and paste)
- * Horizontal scrolling to allow strings longer than widget size allows
- * Pre-validation (limit which keys can be used)
- * Optional line history so previous entries can be recalled by CTRL UP/DOWN
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_SEARCHEDITOR_H
-#define LL_SEARCHEDITOR_H
-
-#include "lllineeditor.h"
-#include "llbutton.h"
-
-class LLSearchEditor : public LLUICtrl
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLLineEditor::Params>
- {
- Optional<LLButton::Params> search_button,
- clear_button;
- Optional<bool> search_button_visible,
- clear_button_visible,
- highlight_text_field;
- Optional<commit_callback_t> keystroke_callback;
-
- Optional<LLUIImage*> background_image,
- background_image_focused,
- background_image_highlight;
-
- Params()
- : search_button("search_button"),
- search_button_visible("search_button_visible"),
- clear_button("clear_button"),
- clear_button_visible("clear_button_visible"),
- highlight_text_field("highlight_text_field"),
- background_image("background_image"),
- background_image_focused("background_image_focused"),
- background_image_highlight("background_image_highlight")
- {}
- };
-
- void setCommitOnFocusLost(bool b) { if (mSearchEditor) mSearchEditor->setCommitOnFocusLost(b); }
-
-protected:
- LLSearchEditor(const Params&);
- friend class LLUICtrlFactory;
-
-public:
- virtual ~LLSearchEditor();
-
- /*virtual*/ void draw();
-
- void setText(const LLStringExplicit &new_text) { mSearchEditor->setText(new_text); }
- const std::string& getText() const { return mSearchEditor->getText(); }
-
- // LLUICtrl interface
- virtual void setValue(const LLSD& value );
- virtual LLSD getValue() const;
- virtual bool setTextArg( const std::string& key, const LLStringExplicit& text );
- virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text );
- virtual void setLabel( const LLStringExplicit &new_label );
- virtual void clear();
- virtual void setFocus( bool b );
-
- void setKeystrokeCallback( commit_callback_t cb ) { mKeystrokeCallback = cb; }
- void setTextChangedCallback( commit_callback_t cb ) { mTextChangedCallback = cb; }
-
-protected:
- void onClearButtonClick(const LLSD& data);
- virtual void handleKeystroke();
-
- commit_callback_t mKeystrokeCallback;
- commit_callback_t mTextChangedCallback;
- LLLineEditor* mSearchEditor;
- LLButton* mSearchButton;
- LLButton* mClearButton;
-
- LLPointer<LLUIImage> mEditorImage;
- LLPointer<LLUIImage> mEditorImageFocused;
- LLPointer<LLUIImage> mEditorSearchImage;
- LLPointer<LLUIImage> mEditorSearchImageFocused;
-
- bool mHighlightTextField;
-};
-
-#endif // LL_SEARCHEDITOR_H
+/**
+ * @file llsearcheditor.h
+ * @brief Text editor widget that represents a search operation
+ *
+ * Features:
+ * Text entry of a single line (text, delete, left and right arrow, insert, return).
+ * Callbacks either on every keystroke or just on the return key.
+ * Focus (allow multiple text entry widgets)
+ * Clipboard (cut, copy, and paste)
+ * Horizontal scrolling to allow strings longer than widget size allows
+ * Pre-validation (limit which keys can be used)
+ * Optional line history so previous entries can be recalled by CTRL UP/DOWN
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_SEARCHEDITOR_H
+#define LL_SEARCHEDITOR_H
+
+#include "lllineeditor.h"
+#include "llbutton.h"
+
+class LLSearchEditor : public LLUICtrl
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLLineEditor::Params>
+ {
+ Optional<LLButton::Params> search_button,
+ clear_button;
+ Optional<bool> search_button_visible,
+ clear_button_visible,
+ highlight_text_field;
+ Optional<commit_callback_t> keystroke_callback;
+
+ Optional<LLUIImage*> background_image,
+ background_image_focused,
+ background_image_highlight;
+
+ Params()
+ : search_button("search_button"),
+ search_button_visible("search_button_visible"),
+ clear_button("clear_button"),
+ clear_button_visible("clear_button_visible"),
+ highlight_text_field("highlight_text_field"),
+ background_image("background_image"),
+ background_image_focused("background_image_focused"),
+ background_image_highlight("background_image_highlight")
+ {}
+ };
+
+ void setCommitOnFocusLost(bool b) { if (mSearchEditor) mSearchEditor->setCommitOnFocusLost(b); }
+
+protected:
+ LLSearchEditor(const Params&);
+ friend class LLUICtrlFactory;
+
+public:
+ virtual ~LLSearchEditor();
+
+ /*virtual*/ void draw();
+
+ void setText(const LLStringExplicit &new_text) { mSearchEditor->setText(new_text); }
+ const std::string& getText() const { return mSearchEditor->getText(); }
+
+ // LLUICtrl interface
+ virtual void setValue(const LLSD& value );
+ virtual LLSD getValue() const;
+ virtual bool setTextArg( const std::string& key, const LLStringExplicit& text );
+ virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text );
+ virtual void setLabel( const LLStringExplicit &new_label );
+ virtual void clear();
+ virtual void setFocus( bool b );
+
+ void setKeystrokeCallback( commit_callback_t cb ) { mKeystrokeCallback = cb; }
+ void setTextChangedCallback( commit_callback_t cb ) { mTextChangedCallback = cb; }
+
+protected:
+ void onClearButtonClick(const LLSD& data);
+ virtual void handleKeystroke();
+
+ commit_callback_t mKeystrokeCallback;
+ commit_callback_t mTextChangedCallback;
+ LLLineEditor* mSearchEditor;
+ LLButton* mSearchButton;
+ LLButton* mClearButton;
+
+ LLPointer<LLUIImage> mEditorImage;
+ LLPointer<LLUIImage> mEditorImageFocused;
+ LLPointer<LLUIImage> mEditorSearchImage;
+ LLPointer<LLUIImage> mEditorSearchImageFocused;
+
+ bool mHighlightTextField;
+};
+
+#endif // LL_SEARCHEDITOR_H
diff --git a/indra/llui/llslider.cpp b/indra/llui/llslider.cpp
index c1c761c8fa..f892816116 100644
--- a/indra/llui/llslider.cpp
+++ b/indra/llui/llslider.cpp
@@ -1,383 +1,383 @@
-/**
- * @file llslider.cpp
- * @brief LLSlider base class
- *
- * $LicenseInfo:firstyear=2002&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 "llslider.h"
-#include "llui.h"
-
-#include "llgl.h"
-#include "llwindow.h"
-#include "llfocusmgr.h"
-#include "llkeyboard.h" // for the MASK constants
-#include "llcontrol.h"
-#include "lluictrlfactory.h"
-
-static LLDefaultChildRegistry::Register<LLSlider> r1("slider_bar");
-//FIXME: make this into an unregistered template so that code constructed sliders don't
-// have ambigious template lookup problem
-
-LLSlider::Params::Params()
-: orientation ("orientation", std::string ("horizontal")),
- thumb_outline_color("thumb_outline_color"),
- thumb_center_color("thumb_center_color"),
- thumb_image("thumb_image"),
- thumb_image_pressed("thumb_image_pressed"),
- thumb_image_disabled("thumb_image_disabled"),
- track_image_horizontal("track_image_horizontal"),
- track_image_vertical("track_image_vertical"),
- track_highlight_horizontal_image("track_highlight_horizontal_image"),
- track_highlight_vertical_image("track_highlight_vertical_image"),
- mouse_down_callback("mouse_down_callback"),
- mouse_up_callback("mouse_up_callback")
-{}
-
-LLSlider::LLSlider(const LLSlider::Params& p)
-: LLF32UICtrl(p),
- mMouseOffset( 0 ),
- mOrientation ((p.orientation() == "horizontal") ? HORIZONTAL : VERTICAL),
- mThumbOutlineColor(p.thumb_outline_color()),
- mThumbCenterColor(p.thumb_center_color()),
- mThumbImage(p.thumb_image),
- mThumbImagePressed(p.thumb_image_pressed),
- mThumbImageDisabled(p.thumb_image_disabled),
- mTrackImageHorizontal(p.track_image_horizontal),
- mTrackImageVertical(p.track_image_vertical),
- mTrackHighlightHorizontalImage(p.track_highlight_horizontal_image),
- mTrackHighlightVerticalImage(p.track_highlight_vertical_image),
- mMouseDownSignal(NULL),
- mMouseUpSignal(NULL)
-{
- mViewModel->setValue(p.initial_value);
- updateThumbRect();
- mDragStartThumbRect = mThumbRect;
- setControlName(p.control_name, NULL);
- setValue(getValueF32());
-
- if (p.mouse_down_callback.isProvided())
- {
- setMouseDownCallback(initCommitCallback(p.mouse_down_callback));
- }
- if (p.mouse_up_callback.isProvided())
- {
- setMouseUpCallback(initCommitCallback(p.mouse_up_callback));
- }
-}
-
-LLSlider::~LLSlider()
-{
- delete mMouseDownSignal;
- delete mMouseUpSignal;
-}
-
-void LLSlider::setValue(F32 value, bool from_event)
-{
- value = llclamp( value, mMinValue, mMaxValue );
-
- // Round to nearest increment (bias towards rounding down)
- value -= mMinValue;
- value += mIncrement/2.0001f;
- value -= fmod(value, mIncrement);
- value += mMinValue;
-
- if (!from_event && getValueF32() != value)
- {
- setControlValue(value);
- }
-
- LLF32UICtrl::setValue(value);
- updateThumbRect();
-}
-
-void LLSlider::updateThumbRect()
-{
- const S32 DEFAULT_THUMB_SIZE = 16;
- F32 t = (getValueF32() - mMinValue) / (mMaxValue - mMinValue);
-
- S32 thumb_width = mThumbImage ? mThumbImage->getWidth() : DEFAULT_THUMB_SIZE;
- S32 thumb_height = mThumbImage ? mThumbImage->getHeight() : DEFAULT_THUMB_SIZE;
-
- if ( mOrientation == HORIZONTAL )
- {
- S32 left_edge = (thumb_width / 2);
- S32 right_edge = getRect().getWidth() - (thumb_width / 2);
-
- S32 x = left_edge + S32( t * (right_edge - left_edge) );
- mThumbRect.mLeft = x - (thumb_width / 2);
- mThumbRect.mRight = mThumbRect.mLeft + thumb_width;
- mThumbRect.mBottom = getLocalRect().getCenterY() - (thumb_height / 2);
- mThumbRect.mTop = mThumbRect.mBottom + thumb_height;
- }
- else
- {
- S32 top_edge = (thumb_height / 2);
- S32 bottom_edge = getRect().getHeight() - (thumb_height / 2);
-
- S32 y = top_edge + S32( t * (bottom_edge - top_edge) );
- mThumbRect.mLeft = getLocalRect().getCenterX() - (thumb_width / 2);
- mThumbRect.mRight = mThumbRect.mLeft + thumb_width;
- mThumbRect.mBottom = y - (thumb_height / 2);
- mThumbRect.mTop = mThumbRect.mBottom + thumb_height;
- }
-}
-
-
-void LLSlider::setValueAndCommit(F32 value)
-{
- F32 old_value = getValueF32();
- setValue(value);
-
- if (getValueF32() != old_value)
- {
- onCommit();
- }
-}
-
-
-bool LLSlider::handleHover(S32 x, S32 y, MASK mask)
-{
- if( hasMouseCapture() )
- {
- if ( mOrientation == HORIZONTAL )
- {
- S32 thumb_half_width = mThumbImage->getWidth()/2;
- S32 left_edge = thumb_half_width;
- S32 right_edge = getRect().getWidth() - (thumb_half_width);
-
- x += mMouseOffset;
- x = llclamp( x, left_edge, right_edge );
-
- F32 t = F32(x - left_edge) / (right_edge - left_edge);
- setValueAndCommit(t * (mMaxValue - mMinValue) + mMinValue );
- }
- else // mOrientation == VERTICAL
- {
- S32 thumb_half_height = mThumbImage->getHeight()/2;
- S32 top_edge = thumb_half_height;
- S32 bottom_edge = getRect().getHeight() - (thumb_half_height);
-
- y += mMouseOffset;
- y = llclamp(y, top_edge, bottom_edge);
-
- F32 t = F32(y - top_edge) / (bottom_edge - top_edge);
- setValueAndCommit(t * (mMaxValue - mMinValue) + mMinValue );
- }
- getWindow()->setCursor(UI_CURSOR_ARROW);
- LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL;
- }
- else
- {
- getWindow()->setCursor(UI_CURSOR_ARROW);
- LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL;
- }
- return true;
-}
-
-bool LLSlider::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
-
- if( hasMouseCapture() )
- {
- gFocusMgr.setMouseCapture( NULL );
-
- if (mMouseUpSignal)
- (*mMouseUpSignal)( this, getValueF32() );
-
- handled = true;
- make_ui_sound("UISndClickRelease");
- }
- else
- {
- handled = true;
- }
-
- return handled;
-}
-
-bool LLSlider::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- // only do sticky-focus on non-chrome widgets
- if (!getIsChrome())
- {
- setFocus(true);
- }
- if (mMouseDownSignal)
- (*mMouseDownSignal)( this, getValueF32() );
-
- if (MASK_CONTROL & mask) // if CTRL is modifying
- {
- setValueAndCommit(mInitialValue);
- }
- else
- {
- // Find the offset of the actual mouse location from the center of the thumb.
- if (mThumbRect.pointInRect(x,y))
- {
- mMouseOffset = (mOrientation == HORIZONTAL)
- ? (mThumbRect.mLeft + mThumbImage->getWidth()/2) - x
- : (mThumbRect.mBottom + mThumbImage->getHeight()/2) - y;
- }
- else
- {
- mMouseOffset = 0;
- }
-
- // Start dragging the thumb
- // No handler needed for focus lost since this class has no state that depends on it.
- gFocusMgr.setMouseCapture( this );
- mDragStartThumbRect = mThumbRect;
- }
- make_ui_sound("UISndClick");
-
- return true;
-}
-
-bool LLSlider::handleKeyHere(KEY key, MASK mask)
-{
- bool handled = false;
- switch(key)
- {
- case KEY_DOWN:
- case KEY_LEFT:
- setValueAndCommit(getValueF32() - getIncrement());
- handled = true;
- break;
- case KEY_UP:
- case KEY_RIGHT:
- setValueAndCommit(getValueF32() + getIncrement());
- handled = true;
- break;
- default:
- break;
- }
- return handled;
-}
-
-bool LLSlider::handleScrollWheel(S32 x, S32 y, S32 clicks)
-{
- if ( mOrientation == VERTICAL )
- {
- F32 new_val = getValueF32() - clicks * getIncrement();
- setValueAndCommit(new_val);
- return true;
- }
- return LLF32UICtrl::handleScrollWheel(x,y,clicks);
-}
-
-void LLSlider::draw()
-{
- F32 alpha = getDrawContext().mAlpha;
-
- // since thumb image might still be decoding, need thumb to accomodate image size
- updateThumbRect();
-
- // Draw background and thumb.
-
- // drawing solids requires texturing be disabled
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- // Track
- LLPointer<LLUIImage>& trackImage = ( mOrientation == HORIZONTAL )
- ? mTrackImageHorizontal
- : mTrackImageVertical;
-
- LLPointer<LLUIImage>& trackHighlightImage = ( mOrientation == HORIZONTAL )
- ? mTrackHighlightHorizontalImage
- : mTrackHighlightVerticalImage;
-
- LLRect track_rect;
- LLRect highlight_rect;
-
- if ( mOrientation == HORIZONTAL )
- {
- track_rect.set(mThumbImage->getWidth() / 2,
- getLocalRect().getCenterY() + (trackImage->getHeight() / 2),
- getRect().getWidth() - mThumbImage->getWidth() / 2,
- getLocalRect().getCenterY() - (trackImage->getHeight() / 2) );
- highlight_rect.set(track_rect.mLeft, track_rect.mTop, mThumbRect.getCenterX(), track_rect.mBottom);
- }
- else
- {
- track_rect.set(getLocalRect().getCenterX() - (trackImage->getWidth() / 2),
- getRect().getHeight(),
- getLocalRect().getCenterX() + (trackImage->getWidth() / 2),
- 0);
- highlight_rect.set(track_rect.mLeft, track_rect.mTop, track_rect.mRight, track_rect.mBottom);
- }
-
- LLColor4 color = isInEnabledChain() ? LLColor4::white % alpha : LLColor4::white % (0.6f * alpha);
- trackImage->draw(track_rect, color);
- trackHighlightImage->draw(highlight_rect, color);
-
- // Thumb
- if (hasFocus())
- {
- // Draw focus highlighting.
- mThumbImage->drawBorder(mThumbRect, gFocusMgr.getFocusColor() % alpha, gFocusMgr.getFocusFlashWidth());
- }
-
- if( hasMouseCapture() ) // currently clicking on slider
- {
- // Show ghost where thumb was before dragging began.
- if (mThumbImage.notNull())
- {
- mThumbImage->draw(mDragStartThumbRect, mThumbCenterColor.get() % (0.3f * alpha));
- }
- if (mThumbImagePressed.notNull())
- {
- mThumbImagePressed->draw(mThumbRect, mThumbOutlineColor % alpha);
- }
- }
- else if (!isInEnabledChain())
- {
- if (mThumbImageDisabled.notNull())
- {
- mThumbImageDisabled->draw(mThumbRect, mThumbCenterColor % alpha);
- }
- }
- else
- {
- if (mThumbImage.notNull())
- {
- mThumbImage->draw(mThumbRect, mThumbCenterColor % alpha);
- }
- }
-
- LLUICtrl::draw();
-}
-
-boost::signals2::connection LLSlider::setMouseDownCallback( const commit_signal_t::slot_type& cb )
-{
- if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t();
- return mMouseDownSignal->connect(cb);
-}
-
-boost::signals2::connection LLSlider::setMouseUpCallback( const commit_signal_t::slot_type& cb )
-{
- if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t();
- return mMouseUpSignal->connect(cb);
-}
+/**
+ * @file llslider.cpp
+ * @brief LLSlider base class
+ *
+ * $LicenseInfo:firstyear=2002&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 "llslider.h"
+#include "llui.h"
+
+#include "llgl.h"
+#include "llwindow.h"
+#include "llfocusmgr.h"
+#include "llkeyboard.h" // for the MASK constants
+#include "llcontrol.h"
+#include "lluictrlfactory.h"
+
+static LLDefaultChildRegistry::Register<LLSlider> r1("slider_bar");
+//FIXME: make this into an unregistered template so that code constructed sliders don't
+// have ambigious template lookup problem
+
+LLSlider::Params::Params()
+: orientation ("orientation", std::string ("horizontal")),
+ thumb_outline_color("thumb_outline_color"),
+ thumb_center_color("thumb_center_color"),
+ thumb_image("thumb_image"),
+ thumb_image_pressed("thumb_image_pressed"),
+ thumb_image_disabled("thumb_image_disabled"),
+ track_image_horizontal("track_image_horizontal"),
+ track_image_vertical("track_image_vertical"),
+ track_highlight_horizontal_image("track_highlight_horizontal_image"),
+ track_highlight_vertical_image("track_highlight_vertical_image"),
+ mouse_down_callback("mouse_down_callback"),
+ mouse_up_callback("mouse_up_callback")
+{}
+
+LLSlider::LLSlider(const LLSlider::Params& p)
+: LLF32UICtrl(p),
+ mMouseOffset( 0 ),
+ mOrientation ((p.orientation() == "horizontal") ? HORIZONTAL : VERTICAL),
+ mThumbOutlineColor(p.thumb_outline_color()),
+ mThumbCenterColor(p.thumb_center_color()),
+ mThumbImage(p.thumb_image),
+ mThumbImagePressed(p.thumb_image_pressed),
+ mThumbImageDisabled(p.thumb_image_disabled),
+ mTrackImageHorizontal(p.track_image_horizontal),
+ mTrackImageVertical(p.track_image_vertical),
+ mTrackHighlightHorizontalImage(p.track_highlight_horizontal_image),
+ mTrackHighlightVerticalImage(p.track_highlight_vertical_image),
+ mMouseDownSignal(NULL),
+ mMouseUpSignal(NULL)
+{
+ mViewModel->setValue(p.initial_value);
+ updateThumbRect();
+ mDragStartThumbRect = mThumbRect;
+ setControlName(p.control_name, NULL);
+ setValue(getValueF32());
+
+ if (p.mouse_down_callback.isProvided())
+ {
+ setMouseDownCallback(initCommitCallback(p.mouse_down_callback));
+ }
+ if (p.mouse_up_callback.isProvided())
+ {
+ setMouseUpCallback(initCommitCallback(p.mouse_up_callback));
+ }
+}
+
+LLSlider::~LLSlider()
+{
+ delete mMouseDownSignal;
+ delete mMouseUpSignal;
+}
+
+void LLSlider::setValue(F32 value, bool from_event)
+{
+ value = llclamp( value, mMinValue, mMaxValue );
+
+ // Round to nearest increment (bias towards rounding down)
+ value -= mMinValue;
+ value += mIncrement/2.0001f;
+ value -= fmod(value, mIncrement);
+ value += mMinValue;
+
+ if (!from_event && getValueF32() != value)
+ {
+ setControlValue(value);
+ }
+
+ LLF32UICtrl::setValue(value);
+ updateThumbRect();
+}
+
+void LLSlider::updateThumbRect()
+{
+ const S32 DEFAULT_THUMB_SIZE = 16;
+ F32 t = (getValueF32() - mMinValue) / (mMaxValue - mMinValue);
+
+ S32 thumb_width = mThumbImage ? mThumbImage->getWidth() : DEFAULT_THUMB_SIZE;
+ S32 thumb_height = mThumbImage ? mThumbImage->getHeight() : DEFAULT_THUMB_SIZE;
+
+ if ( mOrientation == HORIZONTAL )
+ {
+ S32 left_edge = (thumb_width / 2);
+ S32 right_edge = getRect().getWidth() - (thumb_width / 2);
+
+ S32 x = left_edge + S32( t * (right_edge - left_edge) );
+ mThumbRect.mLeft = x - (thumb_width / 2);
+ mThumbRect.mRight = mThumbRect.mLeft + thumb_width;
+ mThumbRect.mBottom = getLocalRect().getCenterY() - (thumb_height / 2);
+ mThumbRect.mTop = mThumbRect.mBottom + thumb_height;
+ }
+ else
+ {
+ S32 top_edge = (thumb_height / 2);
+ S32 bottom_edge = getRect().getHeight() - (thumb_height / 2);
+
+ S32 y = top_edge + S32( t * (bottom_edge - top_edge) );
+ mThumbRect.mLeft = getLocalRect().getCenterX() - (thumb_width / 2);
+ mThumbRect.mRight = mThumbRect.mLeft + thumb_width;
+ mThumbRect.mBottom = y - (thumb_height / 2);
+ mThumbRect.mTop = mThumbRect.mBottom + thumb_height;
+ }
+}
+
+
+void LLSlider::setValueAndCommit(F32 value)
+{
+ F32 old_value = getValueF32();
+ setValue(value);
+
+ if (getValueF32() != old_value)
+ {
+ onCommit();
+ }
+}
+
+
+bool LLSlider::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( hasMouseCapture() )
+ {
+ if ( mOrientation == HORIZONTAL )
+ {
+ S32 thumb_half_width = mThumbImage->getWidth()/2;
+ S32 left_edge = thumb_half_width;
+ S32 right_edge = getRect().getWidth() - (thumb_half_width);
+
+ x += mMouseOffset;
+ x = llclamp( x, left_edge, right_edge );
+
+ F32 t = F32(x - left_edge) / (right_edge - left_edge);
+ setValueAndCommit(t * (mMaxValue - mMinValue) + mMinValue );
+ }
+ else // mOrientation == VERTICAL
+ {
+ S32 thumb_half_height = mThumbImage->getHeight()/2;
+ S32 top_edge = thumb_half_height;
+ S32 bottom_edge = getRect().getHeight() - (thumb_half_height);
+
+ y += mMouseOffset;
+ y = llclamp(y, top_edge, bottom_edge);
+
+ F32 t = F32(y - top_edge) / (bottom_edge - top_edge);
+ setValueAndCommit(t * (mMaxValue - mMinValue) + mMinValue );
+ }
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL;
+ }
+ else
+ {
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (inactive)" << LL_ENDL;
+ }
+ return true;
+}
+
+bool LLSlider::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ bool handled = false;
+
+ if( hasMouseCapture() )
+ {
+ gFocusMgr.setMouseCapture( NULL );
+
+ if (mMouseUpSignal)
+ (*mMouseUpSignal)( this, getValueF32() );
+
+ handled = true;
+ make_ui_sound("UISndClickRelease");
+ }
+ else
+ {
+ handled = true;
+ }
+
+ return handled;
+}
+
+bool LLSlider::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // only do sticky-focus on non-chrome widgets
+ if (!getIsChrome())
+ {
+ setFocus(true);
+ }
+ if (mMouseDownSignal)
+ (*mMouseDownSignal)( this, getValueF32() );
+
+ if (MASK_CONTROL & mask) // if CTRL is modifying
+ {
+ setValueAndCommit(mInitialValue);
+ }
+ else
+ {
+ // Find the offset of the actual mouse location from the center of the thumb.
+ if (mThumbRect.pointInRect(x,y))
+ {
+ mMouseOffset = (mOrientation == HORIZONTAL)
+ ? (mThumbRect.mLeft + mThumbImage->getWidth()/2) - x
+ : (mThumbRect.mBottom + mThumbImage->getHeight()/2) - y;
+ }
+ else
+ {
+ mMouseOffset = 0;
+ }
+
+ // Start dragging the thumb
+ // No handler needed for focus lost since this class has no state that depends on it.
+ gFocusMgr.setMouseCapture( this );
+ mDragStartThumbRect = mThumbRect;
+ }
+ make_ui_sound("UISndClick");
+
+ return true;
+}
+
+bool LLSlider::handleKeyHere(KEY key, MASK mask)
+{
+ bool handled = false;
+ switch(key)
+ {
+ case KEY_DOWN:
+ case KEY_LEFT:
+ setValueAndCommit(getValueF32() - getIncrement());
+ handled = true;
+ break;
+ case KEY_UP:
+ case KEY_RIGHT:
+ setValueAndCommit(getValueF32() + getIncrement());
+ handled = true;
+ break;
+ default:
+ break;
+ }
+ return handled;
+}
+
+bool LLSlider::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ if ( mOrientation == VERTICAL )
+ {
+ F32 new_val = getValueF32() - clicks * getIncrement();
+ setValueAndCommit(new_val);
+ return true;
+ }
+ return LLF32UICtrl::handleScrollWheel(x,y,clicks);
+}
+
+void LLSlider::draw()
+{
+ F32 alpha = getDrawContext().mAlpha;
+
+ // since thumb image might still be decoding, need thumb to accomodate image size
+ updateThumbRect();
+
+ // Draw background and thumb.
+
+ // drawing solids requires texturing be disabled
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ // Track
+ LLPointer<LLUIImage>& trackImage = ( mOrientation == HORIZONTAL )
+ ? mTrackImageHorizontal
+ : mTrackImageVertical;
+
+ LLPointer<LLUIImage>& trackHighlightImage = ( mOrientation == HORIZONTAL )
+ ? mTrackHighlightHorizontalImage
+ : mTrackHighlightVerticalImage;
+
+ LLRect track_rect;
+ LLRect highlight_rect;
+
+ if ( mOrientation == HORIZONTAL )
+ {
+ track_rect.set(mThumbImage->getWidth() / 2,
+ getLocalRect().getCenterY() + (trackImage->getHeight() / 2),
+ getRect().getWidth() - mThumbImage->getWidth() / 2,
+ getLocalRect().getCenterY() - (trackImage->getHeight() / 2) );
+ highlight_rect.set(track_rect.mLeft, track_rect.mTop, mThumbRect.getCenterX(), track_rect.mBottom);
+ }
+ else
+ {
+ track_rect.set(getLocalRect().getCenterX() - (trackImage->getWidth() / 2),
+ getRect().getHeight(),
+ getLocalRect().getCenterX() + (trackImage->getWidth() / 2),
+ 0);
+ highlight_rect.set(track_rect.mLeft, track_rect.mTop, track_rect.mRight, track_rect.mBottom);
+ }
+
+ LLColor4 color = isInEnabledChain() ? LLColor4::white % alpha : LLColor4::white % (0.6f * alpha);
+ trackImage->draw(track_rect, color);
+ trackHighlightImage->draw(highlight_rect, color);
+
+ // Thumb
+ if (hasFocus())
+ {
+ // Draw focus highlighting.
+ mThumbImage->drawBorder(mThumbRect, gFocusMgr.getFocusColor() % alpha, gFocusMgr.getFocusFlashWidth());
+ }
+
+ if( hasMouseCapture() ) // currently clicking on slider
+ {
+ // Show ghost where thumb was before dragging began.
+ if (mThumbImage.notNull())
+ {
+ mThumbImage->draw(mDragStartThumbRect, mThumbCenterColor.get() % (0.3f * alpha));
+ }
+ if (mThumbImagePressed.notNull())
+ {
+ mThumbImagePressed->draw(mThumbRect, mThumbOutlineColor % alpha);
+ }
+ }
+ else if (!isInEnabledChain())
+ {
+ if (mThumbImageDisabled.notNull())
+ {
+ mThumbImageDisabled->draw(mThumbRect, mThumbCenterColor % alpha);
+ }
+ }
+ else
+ {
+ if (mThumbImage.notNull())
+ {
+ mThumbImage->draw(mThumbRect, mThumbCenterColor % alpha);
+ }
+ }
+
+ LLUICtrl::draw();
+}
+
+boost::signals2::connection LLSlider::setMouseDownCallback( const commit_signal_t::slot_type& cb )
+{
+ if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t();
+ return mMouseDownSignal->connect(cb);
+}
+
+boost::signals2::connection LLSlider::setMouseUpCallback( const commit_signal_t::slot_type& cb )
+{
+ if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t();
+ return mMouseUpSignal->connect(cb);
+}
diff --git a/indra/llui/llslider.h b/indra/llui/llslider.h
index 767fcd3bbd..9cd1c9cee4 100644
--- a/indra/llui/llslider.h
+++ b/indra/llui/llslider.h
@@ -1,108 +1,108 @@
-/**
- * @file llslider.h
- * @brief A simple slider with no label.
- *
- * $LicenseInfo:firstyear=2002&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_LLSLIDER_H
-#define LL_LLSLIDER_H
-
-#include "llf32uictrl.h"
-#include "v4color.h"
-#include "lluiimage.h"
-
-class LLSlider : public LLF32UICtrl
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLF32UICtrl::Params>
- {
- Optional<std::string> orientation;
-
- Optional<LLUIColor> thumb_outline_color,
- thumb_center_color;
-
- Optional<LLUIImage*> thumb_image,
- thumb_image_pressed,
- thumb_image_disabled,
- track_image_horizontal,
- track_image_vertical,
- track_highlight_horizontal_image,
- track_highlight_vertical_image;
-
- Optional<CommitCallbackParam> mouse_down_callback,
- mouse_up_callback;
-
-
- Params();
- };
-protected:
- LLSlider(const Params&);
- friend class LLUICtrlFactory;
-public:
- virtual ~LLSlider();
- void setValue( F32 value, bool from_event = false );
- // overrides for LLF32UICtrl methods
- virtual void setValue(const LLSD& value ) { setValue((F32)value.asReal(), true); }
-
- virtual void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); }
- virtual void setMaxValue(const LLSD& max_value) { setMaxValue((F32)max_value.asReal()); }
- virtual void setMinValue(F32 min_value) { LLF32UICtrl::setMinValue(min_value); updateThumbRect(); }
- virtual void setMaxValue(F32 max_value) { LLF32UICtrl::setMaxValue(max_value); updateThumbRect(); }
-
- boost::signals2::connection setMouseDownCallback( const commit_signal_t::slot_type& cb );
- boost::signals2::connection setMouseUpCallback( const commit_signal_t::slot_type& cb );
-
- virtual bool handleHover(S32 x, S32 y, MASK mask);
- virtual bool handleMouseUp(S32 x, S32 y, MASK mask);
- virtual bool handleMouseDown(S32 x, S32 y, MASK mask);
- virtual bool handleKeyHere(KEY key, MASK mask);
- virtual bool handleScrollWheel(S32 x, S32 y, S32 clicks);
- virtual void draw();
-
-private:
- void setValueAndCommit(F32 value);
- void updateThumbRect();
-
- bool mVolumeSlider;
- S32 mMouseOffset;
- LLRect mDragStartThumbRect;
-
- LLPointer<LLUIImage> mThumbImage;
- LLPointer<LLUIImage> mThumbImagePressed;
- LLPointer<LLUIImage> mThumbImageDisabled;
- LLPointer<LLUIImage> mTrackImageHorizontal;
- LLPointer<LLUIImage> mTrackImageVertical;
- LLPointer<LLUIImage> mTrackHighlightHorizontalImage;
- LLPointer<LLUIImage> mTrackHighlightVerticalImage;
-
- const EOrientation mOrientation;
-
- LLRect mThumbRect;
- LLUIColor mThumbOutlineColor;
- LLUIColor mThumbCenterColor;
-
- commit_signal_t* mMouseDownSignal;
- commit_signal_t* mMouseUpSignal;
-};
-
-#endif // LL_LLSLIDER_H
+/**
+ * @file llslider.h
+ * @brief A simple slider with no label.
+ *
+ * $LicenseInfo:firstyear=2002&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_LLSLIDER_H
+#define LL_LLSLIDER_H
+
+#include "llf32uictrl.h"
+#include "v4color.h"
+#include "lluiimage.h"
+
+class LLSlider : public LLF32UICtrl
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLF32UICtrl::Params>
+ {
+ Optional<std::string> orientation;
+
+ Optional<LLUIColor> thumb_outline_color,
+ thumb_center_color;
+
+ Optional<LLUIImage*> thumb_image,
+ thumb_image_pressed,
+ thumb_image_disabled,
+ track_image_horizontal,
+ track_image_vertical,
+ track_highlight_horizontal_image,
+ track_highlight_vertical_image;
+
+ Optional<CommitCallbackParam> mouse_down_callback,
+ mouse_up_callback;
+
+
+ Params();
+ };
+protected:
+ LLSlider(const Params&);
+ friend class LLUICtrlFactory;
+public:
+ virtual ~LLSlider();
+ void setValue( F32 value, bool from_event = false );
+ // overrides for LLF32UICtrl methods
+ virtual void setValue(const LLSD& value ) { setValue((F32)value.asReal(), true); }
+
+ virtual void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); }
+ virtual void setMaxValue(const LLSD& max_value) { setMaxValue((F32)max_value.asReal()); }
+ virtual void setMinValue(F32 min_value) { LLF32UICtrl::setMinValue(min_value); updateThumbRect(); }
+ virtual void setMaxValue(F32 max_value) { LLF32UICtrl::setMaxValue(max_value); updateThumbRect(); }
+
+ boost::signals2::connection setMouseDownCallback( const commit_signal_t::slot_type& cb );
+ boost::signals2::connection setMouseUpCallback( const commit_signal_t::slot_type& cb );
+
+ virtual bool handleHover(S32 x, S32 y, MASK mask);
+ virtual bool handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual bool handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual bool handleKeyHere(KEY key, MASK mask);
+ virtual bool handleScrollWheel(S32 x, S32 y, S32 clicks);
+ virtual void draw();
+
+private:
+ void setValueAndCommit(F32 value);
+ void updateThumbRect();
+
+ bool mVolumeSlider;
+ S32 mMouseOffset;
+ LLRect mDragStartThumbRect;
+
+ LLPointer<LLUIImage> mThumbImage;
+ LLPointer<LLUIImage> mThumbImagePressed;
+ LLPointer<LLUIImage> mThumbImageDisabled;
+ LLPointer<LLUIImage> mTrackImageHorizontal;
+ LLPointer<LLUIImage> mTrackImageVertical;
+ LLPointer<LLUIImage> mTrackHighlightHorizontalImage;
+ LLPointer<LLUIImage> mTrackHighlightVerticalImage;
+
+ const EOrientation mOrientation;
+
+ LLRect mThumbRect;
+ LLUIColor mThumbOutlineColor;
+ LLUIColor mThumbCenterColor;
+
+ commit_signal_t* mMouseDownSignal;
+ commit_signal_t* mMouseUpSignal;
+};
+
+#endif // LL_LLSLIDER_H
diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp
index 2bfb3a624b..e7e25716ad 100644
--- a/indra/llui/llsliderctrl.cpp
+++ b/indra/llui/llsliderctrl.cpp
@@ -1,489 +1,489 @@
-/**
- * @file llsliderctrl.cpp
- * @brief LLSliderCtrl base class
- *
- * $LicenseInfo:firstyear=2002&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 "llsliderctrl.h"
-
-#include "llmath.h"
-#include "llfontgl.h"
-#include "llgl.h"
-#include "llkeyboard.h"
-#include "lllineeditor.h"
-#include "llslider.h"
-#include "llstring.h"
-#include "lltextbox.h"
-#include "llui.h"
-#include "lluiconstants.h"
-#include "llcontrol.h"
-#include "llfocusmgr.h"
-#include "llresmgr.h"
-#include "lluictrlfactory.h"
-
-static LLDefaultChildRegistry::Register<LLSliderCtrl> r("slider");
-
-LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p)
-: LLF32UICtrl(p),
- mLabelBox( NULL ),
- mEditor( NULL ),
- mTextBox( NULL ),
- mFont(p.font),
- mShowText(p.show_text),
- mCanEditText(p.can_edit_text),
- mPrecision(p.decimal_digits),
- mTextEnabledColor(p.text_color()),
- mTextDisabledColor(p.text_disabled_color()),
- mLabelWidth(p.label_width),
- mEditorCommitSignal(NULL)
-{
- S32 top = getRect().getHeight();
- S32 bottom = 0;
- S32 left = 0;
-
- S32 label_width = p.label_width;
- S32 text_width = p.text_width;
-
- // Label
- if( !p.label().empty() )
- {
- if (!p.label_width.isProvided())
- {
- label_width = p.font()->getWidth(p.label);
- }
- LLRect label_rect( left, top, label_width, bottom );
- LLTextBox::Params params(p.slider_label);
- 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);
- mLabelFont = params.font();
- }
-
- if (p.show_text && !p.text_width.isProvided())
- {
- // calculate the size of the text box (log max_value is number of digits - 1 so plus 1)
- if ( p.max_value )
- text_width = p.font()->getWidth(std::string("0")) * ( static_cast < S32 > ( log10 ( p.max_value ) ) + p.decimal_digits + 1 );
-
- if ( p.increment < 1.0f )
- text_width += p.font()->getWidth(std::string(".")); // (mostly) take account of decimal point in value
-
- if ( p.min_value < 0.0f || p.max_value < 0.0f )
- text_width += p.font()->getWidth(std::string("-")); // (mostly) take account of minus sign
-
- // padding to make things look nicer
- text_width += 8;
- }
-
-
- S32 text_left = getRect().getWidth() - text_width;
- static LLUICachedControl<S32> sliderctrl_spacing ("UISliderctrlSpacing", 0);
-
- S32 slider_right = getRect().getWidth();
- if( p.show_text )
- {
- slider_right = text_left - sliderctrl_spacing;
- }
-
- S32 slider_left = label_width ? label_width + sliderctrl_spacing : 0;
- LLSlider::Params slider_p(p.slider_bar);
- slider_p.name("slider_bar");
- 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.mouse_down_callback( p.mouse_down_callback );
- slider_p.mouse_up_callback( p.mouse_up_callback );
- mSlider = LLUICtrlFactory::create<LLSlider> (slider_p);
-
- addChild( mSlider );
-
- if( p.show_text() )
- {
- LLRect text_rect( text_left, top, getRect().getWidth(), bottom );
- if( p.can_edit_text() )
- {
- LLLineEditor::Params line_p(p.value_editor);
- 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);
-
- mEditor->setFocusReceivedCallback( boost::bind(&LLSliderCtrl::onEditorGainFocus, _1, this ));
- // don't do this, as selecting the entire text is single clicking in some cases
- // and double clicking in others
- //mEditor->setSelectAllonFocusReceived(true);
- addChild(mEditor);
- }
- else
- {
- LLTextBox::Params text_p(p.value_text);
- 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);
- }
- }
-
- updateText();
-}
-
-LLSliderCtrl::~LLSliderCtrl()
-{
- delete mEditorCommitSignal;
-}
-
-// static
-void LLSliderCtrl::onEditorGainFocus( LLFocusableElement* caller, void *userdata )
-{
- LLSliderCtrl* self = (LLSliderCtrl*) userdata;
- llassert( caller == self->mEditor );
-
- self->onFocusReceived();
-}
-
-
-void LLSliderCtrl::setValue(F32 v, bool from_event)
-{
- mSlider->setValue( v, from_event );
- mValue = mSlider->getValueF32();
- updateText();
-}
-
-bool LLSliderCtrl::setLabelArg( const std::string& key, const LLStringExplicit& text )
-{
- bool res = false;
- if (mLabelBox)
- {
- res = mLabelBox->setTextArg(key, text);
- if (res && mLabelFont && mLabelWidth == 0)
- {
- S32 label_width = mLabelFont->getWidth(mLabelBox->getText());
- LLRect rect = mLabelBox->getRect();
- S32 prev_right = rect.mRight;
- rect.mRight = rect.mLeft + label_width;
- mLabelBox->setRect(rect);
-
- S32 delta = rect.mRight - prev_right;
- rect = mSlider->getRect();
- S32 left = rect.mLeft + delta;
- static LLUICachedControl<S32> sliderctrl_spacing ("UISliderctrlSpacing", 0);
- left = llclamp(left, 0, rect.mRight - sliderctrl_spacing);
- rect.mLeft = left;
- mSlider->setRect(rect);
- }
- }
- return res;
-}
-
-void LLSliderCtrl::clear()
-{
- setValue(0.0f);
- if( mEditor )
- {
- mEditor->setText( LLStringUtil::null );
- }
- if( mTextBox )
- {
- mTextBox->setText( LLStringUtil::null );
- }
-
-}
-
-void LLSliderCtrl::updateText()
-{
- if( mEditor || mTextBox )
- {
- LLLocale locale(LLLocale::USER_LOCALE);
-
- // Don't display very small negative values as -0.000
- F32 displayed_value = (F32)(floor(getValueF32() * pow(10.0, (F64)mPrecision) + 0.5) / pow(10.0, (F64)mPrecision));
-
- std::string format = llformat("%%.%df", mPrecision);
- std::string text = llformat(format.c_str(), displayed_value);
- if( mEditor )
- {
- // Setting editor text here to "" before using actual text is here because if text which
- // is set is the same as the one which is actually typed into lineeditor, LLLineEditor::setText()
- // will exit at it's beginning, so text for revert on escape won't be saved. (EXT-8536)
- mEditor->setText( LLStringUtil::null );
- mEditor->setText( text );
- }
- else
- {
- mTextBox->setText( text );
- }
- }
-}
-
-void LLSliderCtrl::updateSliderRect()
-{
- S32 right = getRect().getWidth();
- S32 top = getRect().getHeight();
- S32 bottom = 0;
- S32 left = 0;
- static LLUICachedControl<S32> sliderctrl_spacing("UISliderctrlSpacing", 0);
- if (mEditor)
- {
- LLRect editor_rect = mEditor->getRect();
- S32 editor_width = editor_rect.getWidth();
- editor_rect.mRight = right;
- editor_rect.mLeft = right - editor_width;
- mEditor->setRect(editor_rect);
-
- right -= editor_width + sliderctrl_spacing;
- }
- if (mTextBox)
- {
- right -= mTextBox->getRect().getWidth() + sliderctrl_spacing;
- }
- if (mLabelBox)
- {
- left += mLabelBox->getRect().getWidth() + sliderctrl_spacing;
- }
-
- mSlider->setRect(LLRect(left, top,right,bottom));
-}
-
-// static
-void LLSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata )
-{
- LLSliderCtrl* self = dynamic_cast<LLSliderCtrl*>(ctrl->getParent());
- if (!self)
- return;
-
- bool success = false;
- F32 val = self->mValue;
- F32 saved_val = self->mValue;
-
- std::string text = self->mEditor->getText();
- if( LLLineEditor::postvalidateFloat( text ) )
- {
- LLLocale locale(LLLocale::USER_LOCALE);
- val = (F32) atof( text.c_str() );
- if( self->mSlider->getMinValue() <= val && val <= self->mSlider->getMaxValue() )
- {
- self->setValue( val ); // set the value temporarily so that the callback can retrieve it.
- if( !self->mValidateSignal || (*(self->mValidateSignal))( self, val ) )
- {
- success = true;
- }
- }
- }
-
- if( success )
- {
- self->onCommit();
- if (self->mEditorCommitSignal)
- (*(self->mEditorCommitSignal))(self, self->getValueF32());
- }
- else
- {
- if( self->getValueF32() != saved_val )
- {
- self->setValue( saved_val );
- }
- self->reportInvalidData();
- }
- self->updateText();
-}
-
-// static
-void LLSliderCtrl::onSliderCommit( LLUICtrl* ctrl, const LLSD& userdata )
-{
- LLSliderCtrl* self = dynamic_cast<LLSliderCtrl*>(ctrl->getParent());
- if (!self)
- return;
-
- bool success = false;
- F32 saved_val = self->mValue;
- F32 new_val = self->mSlider->getValueF32();
-
- self->mValue = new_val; // set the value temporarily so that the callback can retrieve it.
- if( !self->mValidateSignal || (*(self->mValidateSignal))( self, new_val ) )
- {
- success = true;
- }
-
- if( success )
- {
- self->onCommit();
- }
- else
- {
- if( self->mValue != saved_val )
- {
- self->setValue( saved_val );
- }
- self->reportInvalidData();
- }
- self->updateText();
-}
-
-void LLSliderCtrl::setEnabled(bool b)
-{
- LLView::setEnabled( b );
-
- if( mLabelBox )
- {
- mLabelBox->setColor( b ? mTextEnabledColor.get() : mTextDisabledColor.get() );
- }
-
- mSlider->setEnabled( b );
-
- if( mEditor )
- {
- mEditor->setEnabled( b );
- }
-
- if( mTextBox )
- {
- mTextBox->setColor( b ? mTextEnabledColor.get() : mTextDisabledColor.get() );
- }
-}
-
-
-void LLSliderCtrl::setTentative(bool b)
-{
- if( mEditor )
- {
- mEditor->setTentative(b);
- }
- LLF32UICtrl::setTentative(b);
-}
-
-
-void LLSliderCtrl::onCommit()
-{
- setTentative(false);
-
- if( mEditor )
- {
- mEditor->setTentative(false);
- }
-
- setControlValue(getValueF32());
- LLF32UICtrl::onCommit();
-}
-
-void LLSliderCtrl::setRect(const LLRect& rect)
-{
- LLF32UICtrl::setRect(rect);
- updateSliderRect();
-}
-
-//virtual
-void LLSliderCtrl::reshape(S32 width, S32 height, bool called_from_parent)
-{
- LLF32UICtrl::reshape(width, height, called_from_parent);
- updateSliderRect();
-}
-
-void LLSliderCtrl::setPrecision(S32 precision)
-{
- if (precision < 0 || precision > 10)
- {
- LL_ERRS() << "LLSliderCtrl::setPrecision - precision out of range" << LL_ENDL;
- return;
- }
-
- mPrecision = precision;
- updateText();
-}
-
-boost::signals2::connection LLSliderCtrl::setSliderMouseDownCallback( const commit_signal_t::slot_type& cb )
-{
- return mSlider->setMouseDownCallback( cb );
-}
-
-boost::signals2::connection LLSliderCtrl::setSliderMouseUpCallback( const commit_signal_t::slot_type& cb )
-{
- 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 )
- {
- mEditor->onTabInto();
- }
- LLF32UICtrl::onTabInto();
-}
-
-void LLSliderCtrl::reportInvalidData()
-{
- make_ui_sound("UISndBadKeystroke");
-}
-
+/**
+ * @file llsliderctrl.cpp
+ * @brief LLSliderCtrl base class
+ *
+ * $LicenseInfo:firstyear=2002&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 "llsliderctrl.h"
+
+#include "llmath.h"
+#include "llfontgl.h"
+#include "llgl.h"
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "llslider.h"
+#include "llstring.h"
+#include "lltextbox.h"
+#include "llui.h"
+#include "lluiconstants.h"
+#include "llcontrol.h"
+#include "llfocusmgr.h"
+#include "llresmgr.h"
+#include "lluictrlfactory.h"
+
+static LLDefaultChildRegistry::Register<LLSliderCtrl> r("slider");
+
+LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p)
+: LLF32UICtrl(p),
+ mLabelBox( NULL ),
+ mEditor( NULL ),
+ mTextBox( NULL ),
+ mFont(p.font),
+ mShowText(p.show_text),
+ mCanEditText(p.can_edit_text),
+ mPrecision(p.decimal_digits),
+ mTextEnabledColor(p.text_color()),
+ mTextDisabledColor(p.text_disabled_color()),
+ mLabelWidth(p.label_width),
+ mEditorCommitSignal(NULL)
+{
+ S32 top = getRect().getHeight();
+ S32 bottom = 0;
+ S32 left = 0;
+
+ S32 label_width = p.label_width;
+ S32 text_width = p.text_width;
+
+ // Label
+ if( !p.label().empty() )
+ {
+ if (!p.label_width.isProvided())
+ {
+ label_width = p.font()->getWidth(p.label);
+ }
+ LLRect label_rect( left, top, label_width, bottom );
+ LLTextBox::Params params(p.slider_label);
+ 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);
+ mLabelFont = params.font();
+ }
+
+ if (p.show_text && !p.text_width.isProvided())
+ {
+ // calculate the size of the text box (log max_value is number of digits - 1 so plus 1)
+ if ( p.max_value )
+ text_width = p.font()->getWidth(std::string("0")) * ( static_cast < S32 > ( log10 ( p.max_value ) ) + p.decimal_digits + 1 );
+
+ if ( p.increment < 1.0f )
+ text_width += p.font()->getWidth(std::string(".")); // (mostly) take account of decimal point in value
+
+ if ( p.min_value < 0.0f || p.max_value < 0.0f )
+ text_width += p.font()->getWidth(std::string("-")); // (mostly) take account of minus sign
+
+ // padding to make things look nicer
+ text_width += 8;
+ }
+
+
+ S32 text_left = getRect().getWidth() - text_width;
+ static LLUICachedControl<S32> sliderctrl_spacing ("UISliderctrlSpacing", 0);
+
+ S32 slider_right = getRect().getWidth();
+ if( p.show_text )
+ {
+ slider_right = text_left - sliderctrl_spacing;
+ }
+
+ S32 slider_left = label_width ? label_width + sliderctrl_spacing : 0;
+ LLSlider::Params slider_p(p.slider_bar);
+ slider_p.name("slider_bar");
+ 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.mouse_down_callback( p.mouse_down_callback );
+ slider_p.mouse_up_callback( p.mouse_up_callback );
+ mSlider = LLUICtrlFactory::create<LLSlider> (slider_p);
+
+ addChild( mSlider );
+
+ if( p.show_text() )
+ {
+ LLRect text_rect( text_left, top, getRect().getWidth(), bottom );
+ if( p.can_edit_text() )
+ {
+ LLLineEditor::Params line_p(p.value_editor);
+ 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.prevalidator(&LLTextValidate::validateFloat);
+ mEditor = LLUICtrlFactory::create<LLLineEditor>(line_p);
+
+ mEditor->setFocusReceivedCallback( boost::bind(&LLSliderCtrl::onEditorGainFocus, _1, this ));
+ // don't do this, as selecting the entire text is single clicking in some cases
+ // and double clicking in others
+ //mEditor->setSelectAllonFocusReceived(true);
+ addChild(mEditor);
+ }
+ else
+ {
+ LLTextBox::Params text_p(p.value_text);
+ 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);
+ }
+ }
+
+ updateText();
+}
+
+LLSliderCtrl::~LLSliderCtrl()
+{
+ delete mEditorCommitSignal;
+}
+
+// static
+void LLSliderCtrl::onEditorGainFocus( LLFocusableElement* caller, void *userdata )
+{
+ LLSliderCtrl* self = (LLSliderCtrl*) userdata;
+ llassert( caller == self->mEditor );
+
+ self->onFocusReceived();
+}
+
+
+void LLSliderCtrl::setValue(F32 v, bool from_event)
+{
+ mSlider->setValue( v, from_event );
+ mValue = mSlider->getValueF32();
+ updateText();
+}
+
+bool LLSliderCtrl::setLabelArg( const std::string& key, const LLStringExplicit& text )
+{
+ bool res = false;
+ if (mLabelBox)
+ {
+ res = mLabelBox->setTextArg(key, text);
+ if (res && mLabelFont && mLabelWidth == 0)
+ {
+ S32 label_width = mLabelFont->getWidth(mLabelBox->getText());
+ LLRect rect = mLabelBox->getRect();
+ S32 prev_right = rect.mRight;
+ rect.mRight = rect.mLeft + label_width;
+ mLabelBox->setRect(rect);
+
+ S32 delta = rect.mRight - prev_right;
+ rect = mSlider->getRect();
+ S32 left = rect.mLeft + delta;
+ static LLUICachedControl<S32> sliderctrl_spacing ("UISliderctrlSpacing", 0);
+ left = llclamp(left, 0, rect.mRight - sliderctrl_spacing);
+ rect.mLeft = left;
+ mSlider->setRect(rect);
+ }
+ }
+ return res;
+}
+
+void LLSliderCtrl::clear()
+{
+ setValue(0.0f);
+ if( mEditor )
+ {
+ mEditor->setText( LLStringUtil::null );
+ }
+ if( mTextBox )
+ {
+ mTextBox->setText( LLStringUtil::null );
+ }
+
+}
+
+void LLSliderCtrl::updateText()
+{
+ if( mEditor || mTextBox )
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ // Don't display very small negative values as -0.000
+ F32 displayed_value = (F32)(floor(getValueF32() * pow(10.0, (F64)mPrecision) + 0.5) / pow(10.0, (F64)mPrecision));
+
+ std::string format = llformat("%%.%df", mPrecision);
+ std::string text = llformat(format.c_str(), displayed_value);
+ if( mEditor )
+ {
+ // Setting editor text here to "" before using actual text is here because if text which
+ // is set is the same as the one which is actually typed into lineeditor, LLLineEditor::setText()
+ // will exit at it's beginning, so text for revert on escape won't be saved. (EXT-8536)
+ mEditor->setText( LLStringUtil::null );
+ mEditor->setText( text );
+ }
+ else
+ {
+ mTextBox->setText( text );
+ }
+ }
+}
+
+void LLSliderCtrl::updateSliderRect()
+{
+ S32 right = getRect().getWidth();
+ S32 top = getRect().getHeight();
+ S32 bottom = 0;
+ S32 left = 0;
+ static LLUICachedControl<S32> sliderctrl_spacing("UISliderctrlSpacing", 0);
+ if (mEditor)
+ {
+ LLRect editor_rect = mEditor->getRect();
+ S32 editor_width = editor_rect.getWidth();
+ editor_rect.mRight = right;
+ editor_rect.mLeft = right - editor_width;
+ mEditor->setRect(editor_rect);
+
+ right -= editor_width + sliderctrl_spacing;
+ }
+ if (mTextBox)
+ {
+ right -= mTextBox->getRect().getWidth() + sliderctrl_spacing;
+ }
+ if (mLabelBox)
+ {
+ left += mLabelBox->getRect().getWidth() + sliderctrl_spacing;
+ }
+
+ mSlider->setRect(LLRect(left, top,right,bottom));
+}
+
+// static
+void LLSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata )
+{
+ LLSliderCtrl* self = dynamic_cast<LLSliderCtrl*>(ctrl->getParent());
+ if (!self)
+ return;
+
+ bool success = false;
+ F32 val = self->mValue;
+ F32 saved_val = self->mValue;
+
+ std::string text = self->mEditor->getText();
+ if( LLLineEditor::postvalidateFloat( text ) )
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+ val = (F32) atof( text.c_str() );
+ if( self->mSlider->getMinValue() <= val && val <= self->mSlider->getMaxValue() )
+ {
+ self->setValue( val ); // set the value temporarily so that the callback can retrieve it.
+ if( !self->mValidateSignal || (*(self->mValidateSignal))( self, val ) )
+ {
+ success = true;
+ }
+ }
+ }
+
+ if( success )
+ {
+ self->onCommit();
+ if (self->mEditorCommitSignal)
+ (*(self->mEditorCommitSignal))(self, self->getValueF32());
+ }
+ else
+ {
+ if( self->getValueF32() != saved_val )
+ {
+ self->setValue( saved_val );
+ }
+ self->reportInvalidData();
+ }
+ self->updateText();
+}
+
+// static
+void LLSliderCtrl::onSliderCommit( LLUICtrl* ctrl, const LLSD& userdata )
+{
+ LLSliderCtrl* self = dynamic_cast<LLSliderCtrl*>(ctrl->getParent());
+ if (!self)
+ return;
+
+ bool success = false;
+ F32 saved_val = self->mValue;
+ F32 new_val = self->mSlider->getValueF32();
+
+ self->mValue = new_val; // set the value temporarily so that the callback can retrieve it.
+ if( !self->mValidateSignal || (*(self->mValidateSignal))( self, new_val ) )
+ {
+ success = true;
+ }
+
+ if( success )
+ {
+ self->onCommit();
+ }
+ else
+ {
+ if( self->mValue != saved_val )
+ {
+ self->setValue( saved_val );
+ }
+ self->reportInvalidData();
+ }
+ self->updateText();
+}
+
+void LLSliderCtrl::setEnabled(bool b)
+{
+ LLView::setEnabled( b );
+
+ if( mLabelBox )
+ {
+ mLabelBox->setColor( b ? mTextEnabledColor.get() : mTextDisabledColor.get() );
+ }
+
+ mSlider->setEnabled( b );
+
+ if( mEditor )
+ {
+ mEditor->setEnabled( b );
+ }
+
+ if( mTextBox )
+ {
+ mTextBox->setColor( b ? mTextEnabledColor.get() : mTextDisabledColor.get() );
+ }
+}
+
+
+void LLSliderCtrl::setTentative(bool b)
+{
+ if( mEditor )
+ {
+ mEditor->setTentative(b);
+ }
+ LLF32UICtrl::setTentative(b);
+}
+
+
+void LLSliderCtrl::onCommit()
+{
+ setTentative(false);
+
+ if( mEditor )
+ {
+ mEditor->setTentative(false);
+ }
+
+ setControlValue(getValueF32());
+ LLF32UICtrl::onCommit();
+}
+
+void LLSliderCtrl::setRect(const LLRect& rect)
+{
+ LLF32UICtrl::setRect(rect);
+ updateSliderRect();
+}
+
+//virtual
+void LLSliderCtrl::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ LLF32UICtrl::reshape(width, height, called_from_parent);
+ updateSliderRect();
+}
+
+void LLSliderCtrl::setPrecision(S32 precision)
+{
+ if (precision < 0 || precision > 10)
+ {
+ LL_ERRS() << "LLSliderCtrl::setPrecision - precision out of range" << LL_ENDL;
+ return;
+ }
+
+ mPrecision = precision;
+ updateText();
+}
+
+boost::signals2::connection LLSliderCtrl::setSliderMouseDownCallback( const commit_signal_t::slot_type& cb )
+{
+ return mSlider->setMouseDownCallback( cb );
+}
+
+boost::signals2::connection LLSliderCtrl::setSliderMouseUpCallback( const commit_signal_t::slot_type& cb )
+{
+ 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 )
+ {
+ mEditor->onTabInto();
+ }
+ LLF32UICtrl::onTabInto();
+}
+
+void LLSliderCtrl::reportInvalidData()
+{
+ make_ui_sound("UISndBadKeystroke");
+}
+
diff --git a/indra/llui/llsliderctrl.h b/indra/llui/llsliderctrl.h
index 810162df16..993cea4d2f 100644
--- a/indra/llui/llsliderctrl.h
+++ b/indra/llui/llsliderctrl.h
@@ -1,176 +1,176 @@
-/**
- * @file llsliderctrl.h
- * @brief Decorated wrapper for a LLSlider.
- *
- * $LicenseInfo:firstyear=2002&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_LLSLIDERCTRL_H
-#define LL_LLSLIDERCTRL_H
-
-#include "lluictrl.h"
-#include "v4color.h"
-#include "llslider.h"
-#include "lltextbox.h"
-#include "llrect.h"
-#include "lllineeditor.h"
-
-
-class LLSliderCtrl: public LLF32UICtrl, public ll::ui::SearchableControl
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLF32UICtrl::Params>
- {
- Optional<std::string> orientation;
- Optional<S32> label_width;
- Optional<S32> text_width;
- Optional<bool> show_text;
- Optional<bool> can_edit_text;
- Optional<bool> is_volume_slider;
- Optional<S32> decimal_digits;
-
- Optional<LLUIColor> text_color,
- text_disabled_color;
-
- Optional<CommitCallbackParam> mouse_down_callback,
- mouse_up_callback;
-
- Optional<LLSlider::Params> slider_bar;
- Optional<LLLineEditor::Params> value_editor;
- Optional<LLTextBox::Params> value_text;
- Optional<LLTextBox::Params> slider_label;
-
- Params()
- : text_width("text_width"),
- label_width("label_width"),
- show_text("show_text"),
- can_edit_text("can_edit_text"),
- is_volume_slider("volume"),
- decimal_digits("decimal_digits", 3),
- text_color("text_color"),
- text_disabled_color("text_disabled_color"),
- slider_bar("slider_bar"),
- value_editor("value_editor"),
- value_text("value_text"),
- slider_label("slider_label"),
- mouse_down_callback("mouse_down_callback"),
- mouse_up_callback("mouse_up_callback"),
- orientation("orientation", std::string ("horizontal"))
- {}
- };
-protected:
- LLSliderCtrl(const Params&);
- friend class LLUICtrlFactory;
-public:
- virtual ~LLSliderCtrl();
-
- /*virtual*/ F32 getValueF32() const { return mSlider->getValueF32(); }
- void setValue(F32 v, bool from_event = false);
-
- /*virtual*/ void setValue(const LLSD& value) { setValue((F32)value.asReal(), true); }
- /*virtual*/ LLSD getValue() const { return LLSD(getValueF32()); }
- /*virtual*/ bool setLabelArg( const std::string& key, const LLStringExplicit& text );
-
- bool isMouseHeldDown() const { return mSlider->hasMouseCapture(); }
-
- virtual void setPrecision(S32 precision);
-
- /*virtual*/ void setEnabled( bool b );
- /*virtual*/ void clear();
-
- /*virtual*/ void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); }
- /*virtual*/ void setMaxValue(const LLSD& max_value) { setMaxValue((F32)max_value.asReal()); }
- /*virtual*/ void setMinValue(F32 min_value) { mSlider->setMinValue(min_value); updateText(); }
- /*virtual*/ void setMaxValue(F32 max_value) { mSlider->setMaxValue(max_value); updateText(); }
- /*virtual*/ void setIncrement(F32 increment) { mSlider->setIncrement(increment);}
-
- F32 getMinValue() const { return mSlider->getMinValue(); }
- F32 getMaxValue() const { return mSlider->getMaxValue(); }
-
- void setLabel(const LLStringExplicit& label) { if (mLabelBox) mLabelBox->setText(label); }
- void setLabelColor(const LLColor4& c) { mTextEnabledColor = c; }
- void setDisabledLabelColor(const LLColor4& c) { mTextDisabledColor = c; }
-
- boost::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();
-
- /*virtual*/ void setTentative(bool b); // marks value as tentative
- /*virtual*/ void onCommit(); // mark not tentative, then commit
-
- /*virtual*/ void setControlName(const std::string& control_name, LLView* context)
- {
- LLUICtrl::setControlName(control_name, context);
- mSlider->setControlName(control_name, context);
- }
-
- /*virtual*/ void setRect(const LLRect& rect);
- /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true);
-
- static void onSliderCommit(LLUICtrl* caller, const LLSD& userdata);
-
- static void onEditorCommit(LLUICtrl* ctrl, const LLSD& userdata);
- static void onEditorGainFocus(LLFocusableElement* caller, void *userdata);
- static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata);
-
-protected:
- virtual std::string _getSearchText() const
- {
- std::string strLabel;
- if( mLabelBox )
- strLabel = mLabelBox->getLabel();
- return strLabel + getToolTip();
- }
- virtual void onSetHighlight() const // When highlight, really do highlight the label
- {
- if( mLabelBox )
- mLabelBox->ll::ui::SearchableControl::setHighlighted( ll::ui::SearchableControl::getHighlighted() );
- }
-private:
- void updateText();
- void updateSliderRect();
- void reportInvalidData();
-
- const LLFontGL* mFont;
- const LLFontGL* mLabelFont;
- bool mShowText;
- bool mCanEditText;
-
- S32 mPrecision;
- LLTextBox* mLabelBox;
- S32 mLabelWidth;
-
- F32 mValue;
- LLSlider* mSlider;
- class LLLineEditor* mEditor;
- LLTextBox* mTextBox;
-
- LLUIColor mTextEnabledColor;
- LLUIColor mTextDisabledColor;
-
- commit_signal_t* mEditorCommitSignal;
-};
-
-#endif // LL_LLSLIDERCTRL_H
-
+/**
+ * @file llsliderctrl.h
+ * @brief Decorated wrapper for a LLSlider.
+ *
+ * $LicenseInfo:firstyear=2002&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_LLSLIDERCTRL_H
+#define LL_LLSLIDERCTRL_H
+
+#include "lluictrl.h"
+#include "v4color.h"
+#include "llslider.h"
+#include "lltextbox.h"
+#include "llrect.h"
+#include "lllineeditor.h"
+
+
+class LLSliderCtrl: public LLF32UICtrl, public ll::ui::SearchableControl
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLF32UICtrl::Params>
+ {
+ Optional<std::string> orientation;
+ Optional<S32> label_width;
+ Optional<S32> text_width;
+ Optional<bool> show_text;
+ Optional<bool> can_edit_text;
+ Optional<bool> is_volume_slider;
+ Optional<S32> decimal_digits;
+
+ Optional<LLUIColor> text_color,
+ text_disabled_color;
+
+ Optional<CommitCallbackParam> mouse_down_callback,
+ mouse_up_callback;
+
+ Optional<LLSlider::Params> slider_bar;
+ Optional<LLLineEditor::Params> value_editor;
+ Optional<LLTextBox::Params> value_text;
+ Optional<LLTextBox::Params> slider_label;
+
+ Params()
+ : text_width("text_width"),
+ label_width("label_width"),
+ show_text("show_text"),
+ can_edit_text("can_edit_text"),
+ is_volume_slider("volume"),
+ decimal_digits("decimal_digits", 3),
+ text_color("text_color"),
+ text_disabled_color("text_disabled_color"),
+ slider_bar("slider_bar"),
+ value_editor("value_editor"),
+ value_text("value_text"),
+ slider_label("slider_label"),
+ mouse_down_callback("mouse_down_callback"),
+ mouse_up_callback("mouse_up_callback"),
+ orientation("orientation", std::string ("horizontal"))
+ {}
+ };
+protected:
+ LLSliderCtrl(const Params&);
+ friend class LLUICtrlFactory;
+public:
+ virtual ~LLSliderCtrl();
+
+ /*virtual*/ F32 getValueF32() const { return mSlider->getValueF32(); }
+ void setValue(F32 v, bool from_event = false);
+
+ /*virtual*/ void setValue(const LLSD& value) { setValue((F32)value.asReal(), true); }
+ /*virtual*/ LLSD getValue() const { return LLSD(getValueF32()); }
+ /*virtual*/ bool setLabelArg( const std::string& key, const LLStringExplicit& text );
+
+ bool isMouseHeldDown() const { return mSlider->hasMouseCapture(); }
+
+ virtual void setPrecision(S32 precision);
+
+ /*virtual*/ void setEnabled( bool b );
+ /*virtual*/ void clear();
+
+ /*virtual*/ void setMinValue(const LLSD& min_value) { setMinValue((F32)min_value.asReal()); }
+ /*virtual*/ void setMaxValue(const LLSD& max_value) { setMaxValue((F32)max_value.asReal()); }
+ /*virtual*/ void setMinValue(F32 min_value) { mSlider->setMinValue(min_value); updateText(); }
+ /*virtual*/ void setMaxValue(F32 max_value) { mSlider->setMaxValue(max_value); updateText(); }
+ /*virtual*/ void setIncrement(F32 increment) { mSlider->setIncrement(increment);}
+
+ F32 getMinValue() const { return mSlider->getMinValue(); }
+ F32 getMaxValue() const { return mSlider->getMaxValue(); }
+
+ void setLabel(const LLStringExplicit& label) { if (mLabelBox) mLabelBox->setText(label); }
+ void setLabelColor(const LLColor4& c) { mTextEnabledColor = c; }
+ void setDisabledLabelColor(const LLColor4& c) { mTextDisabledColor = c; }
+
+ boost::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();
+
+ /*virtual*/ void setTentative(bool b); // marks value as tentative
+ /*virtual*/ void onCommit(); // mark not tentative, then commit
+
+ /*virtual*/ void setControlName(const std::string& control_name, LLView* context)
+ {
+ LLUICtrl::setControlName(control_name, context);
+ mSlider->setControlName(control_name, context);
+ }
+
+ /*virtual*/ void setRect(const LLRect& rect);
+ /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true);
+
+ static void onSliderCommit(LLUICtrl* caller, const LLSD& userdata);
+
+ static void onEditorCommit(LLUICtrl* ctrl, const LLSD& userdata);
+ static void onEditorGainFocus(LLFocusableElement* caller, void *userdata);
+ static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata);
+
+protected:
+ virtual std::string _getSearchText() const
+ {
+ std::string strLabel;
+ if( mLabelBox )
+ strLabel = mLabelBox->getLabel();
+ return strLabel + getToolTip();
+ }
+ virtual void onSetHighlight() const // When highlight, really do highlight the label
+ {
+ if( mLabelBox )
+ mLabelBox->ll::ui::SearchableControl::setHighlighted( ll::ui::SearchableControl::getHighlighted() );
+ }
+private:
+ void updateText();
+ void updateSliderRect();
+ void reportInvalidData();
+
+ const LLFontGL* mFont;
+ const LLFontGL* mLabelFont;
+ bool mShowText;
+ bool mCanEditText;
+
+ S32 mPrecision;
+ LLTextBox* mLabelBox;
+ S32 mLabelWidth;
+
+ F32 mValue;
+ LLSlider* mSlider;
+ class LLLineEditor* mEditor;
+ LLTextBox* mTextBox;
+
+ LLUIColor mTextEnabledColor;
+ LLUIColor mTextDisabledColor;
+
+ commit_signal_t* mEditorCommitSignal;
+};
+
+#endif // LL_LLSLIDERCTRL_H
+
diff --git a/indra/llui/llspellcheck.cpp b/indra/llui/llspellcheck.cpp
index ebd8ca0923..b8aeb3b91f 100644
--- a/indra/llui/llspellcheck.cpp
+++ b/indra/llui/llspellcheck.cpp
@@ -1,25 +1,25 @@
-/**
+/**
* @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$
*/
@@ -31,10 +31,10 @@
#include "llspellcheck.h"
#if LL_WINDOWS
- #include <hunspell/hunspelldll.h>
- #pragma comment(lib, "libhunspell.lib")
+ #include <hunspell/hunspelldll.h>
+ #pragma comment(lib, "libhunspell.lib")
#else
- #include <hunspell/hunspell.hxx>
+ #include <hunspell/hunspell.hxx>
#endif
static const std::string DICT_DIR = "dictionaries";
@@ -47,453 +47,453 @@ static const std::string DICT_FILE_USER = "user_dictionaries.xml";
LLSpellChecker::settings_change_signal_t LLSpellChecker::sSettingsChangeSignal;
LLSpellChecker::LLSpellChecker()
- : mHunspell(NULL)
+ : mHunspell(NULL)
{
}
LLSpellChecker::~LLSpellChecker()
{
- delete mHunspell;
+ delete mHunspell;
}
void LLSpellChecker::initSingleton()
{
- // Load initial dictionary information
- refreshDictionaryMap();
+ // Load initial dictionary information
+ refreshDictionaryMap();
}
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;
+ 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();
+ 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();
}
const LLSD LLSpellChecker::getDictionaryData(const std::string& dict_language)
{
- for (LLSD::array_const_iterator it = mDictMap.beginArray(); it != mDictMap.endArray(); ++it)
- {
- const LLSD& dict_entry = *it;
- if (dict_language == dict_entry["language"].asString())
- {
- return dict_entry;
- }
- }
- return LLSD();
+ for (LLSD::array_const_iterator it = mDictMap.beginArray(); it != mDictMap.endArray(); ++it)
+ {
+ const LLSD& dict_entry = *it;
+ if (dict_language == dict_entry["language"].asString())
+ {
+ return dict_entry;
+ }
+ }
+ return LLSD();
}
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()) );
+ const LLSD dict_info = getDictionaryData(dict_language);
+ return dict_info.has("language") && ( (!check_installed) || (dict_info["installed"].asBoolean()) );
}
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 = mDictMap.beginArray(); it != mDictMap.endArray(); ++it)
- {
- LLSD& dict_entry = *it;
- if (dict_language == dict_entry["language"].asString())
- {
- dict_entry = dict_info;
- return;
- }
- }
- mDictMap.append(dict_info);
- return;
+ const std::string dict_language = dict_info["language"].asString();
+ if (dict_language.empty())
+ {
+ return;
+ }
+
+ for (LLSD::array_iterator it = mDictMap.beginArray(); it != mDictMap.endArray(); ++it)
+ {
+ LLSD& dict_entry = *it;
+ if (dict_language == dict_entry["language"].asString())
+ {
+ dict_entry = dict_info;
+ return;
+ }
+ }
+ mDictMap.append(dict_info);
+ return;
}
// static
void LLSpellChecker::refreshDictionaryMap()
{
- const std::string app_path = getDictionaryAppPath();
- const std::string user_path = getDictionaryUserPath();
+ const std::string app_path = getDictionaryAppPath();
+ const std::string user_path = getDictionaryUserPath();
- // Load dictionary information (file name, friendly name, ...)
+ // 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(mDictMap, user_file))
- || (0 == mDictMap.size()) )
- {
+ llifstream user_file(user_filename.c_str(), std::ios::binary);
+ if ( (!user_file.is_open())
+ || (LLSDParser::PARSE_FAILURE == LLSDSerialize::fromXMLDocument(mDictMap, user_file))
+ || (0 == mDictMap.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(mDictMap, app_file))
- || (0 == mDictMap.size()) )
- {
- return;
- }
- }
-
- // Load user installed dictionary information
- user_filename = user_path + DICT_FILE_USER;
- 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 = mDictMap.beginArray(); it != mDictMap.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();
+ llifstream app_file(app_filename.c_str(), std::ios::binary);
+ if ( (!app_file.is_open())
+ || (LLSDParser::PARSE_FAILURE == LLSDSerialize::fromXMLDocument(mDictMap, app_file))
+ || (0 == mDictMap.size()) )
+ {
+ return;
+ }
+ }
+
+ // Load user installed dictionary information
+ user_filename = user_path + DICT_FILE_USER;
+ 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 = mDictMap.beginArray(); it != mDictMap.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();
+ 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();
- }
+ 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();
- }
+ 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));
+ 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();
- }
+ 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();
+ 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;
+ 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, "");
- LLFile::mkdir(dict_path);
- return dict_path;
+ std::string dict_path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, DICT_DIR, "");
+ LLFile::mkdir(dict_path);
+ return dict_path;
}
// static
bool LLSpellChecker::getUseSpellCheck()
{
- return (LLSpellChecker::instanceExists()) && (LLSpellChecker::instance().mHunspell);
+ return (LLSpellChecker::instanceExists()) && (LLSpellChecker::instance().mHunspell);
}
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)) );
+ // 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)) );
}
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();
+ 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;
+ 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;
+ 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();
- }
+ 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);
+ 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);
- }
+ if ( (((dict_language.empty()) && (getUseSpellCheck())) || (!dict_language.empty())) &&
+ (LLSpellChecker::instance().mDictLanguage != dict_language) )
+ {
+ LLSpellChecker::instance().initHunspell(dict_language);
+ }
}
diff --git a/indra/llui/llspellcheck.h b/indra/llui/llspellcheck.h
index 14f9b44fe4..e4d8a12ef1 100644
--- a/indra/llui/llspellcheck.h
+++ b/indra/llui/llspellcheck.h
@@ -1,25 +1,25 @@
-/**
+/**
* @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$
*/
@@ -36,55 +36,55 @@ class Hunspell;
class LLSpellChecker : public LLSingleton<LLSpellChecker>
{
- LLSINGLETON(LLSpellChecker);
- ~LLSpellChecker();
+ LLSINGLETON(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;
+ 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);
- void initSingleton() override;
+ void addToDictFile(const std::string& dict_path, const std::string& word);
+ void initHunspell(const std::string& dict_language);
+ void initSingleton() override;
public:
- typedef std::list<std::string> dict_list_t;
+ 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);
+ 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);
- bool canRemoveDictionary(const std::string& dict_language);
- static const std::string getDictionaryAppPath();
- static const std::string getDictionaryUserPath();
- const LLSD getDictionaryData(const std::string& dict_language);
- const LLSD& getDictionaryMap() { return mDictMap; }
- static bool getUseSpellCheck();
- bool hasDictionary(const std::string& dict_language, bool check_installed = false);
- void refreshDictionaryMap();
- void removeDictionary(const std::string& dict_language);
- static void setUseSpellCheck(const std::string& dict_language);
+ bool canRemoveDictionary(const std::string& dict_language);
+ static const std::string getDictionaryAppPath();
+ static const std::string getDictionaryUserPath();
+ const LLSD getDictionaryData(const std::string& dict_language);
+ const LLSD& getDictionaryMap() { return mDictMap; }
+ static bool getUseSpellCheck();
+ bool hasDictionary(const std::string& dict_language, bool check_installed = false);
+ void refreshDictionaryMap();
+ void removeDictionary(const std::string& dict_language);
+ static void setUseSpellCheck(const std::string& dict_language);
protected:
- static LLSD loadUserDictionaryMap();
- void setDictionaryData(const LLSD& dict_info);
- static void saveUserDictionaryMap(const LLSD& dict_map);
+ static LLSD loadUserDictionaryMap();
+ 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);
+ typedef boost::signals2::signal<void()> settings_change_signal_t;
+ static boost::signals2::connection setSettingsChangeCallback(const settings_change_signal_t::slot_type& cb);
protected:
- Hunspell* mHunspell;
- std::string mDictLanguage;
- std::string mDictFile;
- dict_list_t mDictSecondary;
- std::vector<std::string> mIgnoreList;
- LLSD mDictMap;
+ Hunspell* mHunspell;
+ std::string mDictLanguage;
+ std::string mDictFile;
+ dict_list_t mDictSecondary;
+ std::vector<std::string> mIgnoreList;
+ LLSD mDictMap;
- static settings_change_signal_t sSettingsChangeSignal;
+ static settings_change_signal_t sSettingsChangeSignal;
};
#endif // LLSPELLCHECK_H
diff --git a/indra/llui/llspellcheckmenuhandler.h b/indra/llui/llspellcheckmenuhandler.h
index d5c95bad39..e1fec5cbea 100644
--- a/indra/llui/llspellcheckmenuhandler.h
+++ b/indra/llui/llspellcheckmenuhandler.h
@@ -1,25 +1,25 @@
-/**
+/**
* @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$
*/
@@ -30,17 +30,17 @@
class LLSpellCheckMenuHandler
{
public:
- virtual bool getSpellCheck() const { return false; }
+ 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 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 addToDictionary() {}
+ virtual bool canAddToDictionary() const { return false; }
- virtual void addToIgnore() {}
- virtual bool canAddToIgnore() 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 84ac3a8c0f..f361877251 100644
--- a/indra/llui/llspinctrl.cpp
+++ b/indra/llui/llspinctrl.cpp
@@ -1,504 +1,504 @@
-/**
- * @file llspinctrl.cpp
- * @brief LLSpinCtrl base class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llspinctrl.h"
-
-#include "llgl.h"
-#include "llui.h"
-#include "lluiconstants.h"
-
-#include "llstring.h"
-#include "llfontgl.h"
-#include "lllineeditor.h"
-#include "llbutton.h"
-#include "lltextbox.h"
-#include "llkeyboard.h"
-#include "llmath.h"
-#include "llcontrol.h"
-#include "llfocusmgr.h"
-#include "llresmgr.h"
-#include "lluictrlfactory.h"
-
-const U32 MAX_STRING_LENGTH = 255;
-
-static LLDefaultChildRegistry::Register<LLSpinCtrl> r2("spinner");
-
-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"),
- down_button("down_button")
-{}
-
-LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
-: LLF32UICtrl(p),
- mLabelBox(NULL),
- mbHasBeenSet( false ),
- mPrecision(p.decimal_digits),
- mTextEnabledColor(p.text_enabled_color()),
- mTextDisabledColor(p.text_disabled_color())
-{
- 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 btn_left = 0;
- // reserve space for spinner
- S32 label_width = llclamp(p.label_width(), 0, llmax(0, getRect().getWidth() - 40));
-
- // Label
- if( !p.label().empty() )
- {
- 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());
- if (p.font.isProvided())
- {
- params.font(p.font);
- }
- mLabelBox = LLUICtrlFactory::create<LLTextBox> (params);
- addChild(mLabelBox);
-
- btn_left += label_rect.mRight + spinctrl_spacing;
- }
-
- S32 btn_right = btn_left + spinctrl_btn_width;
-
- // Spin buttons
- LLButton::Params up_button_params(p.up_button);
- up_button_params.rect = LLRect(btn_left, getRect().getHeight(), btn_right, getRect().getHeight() - spinctrl_btn_height);
- // Click callback starts within the button and ends within the button,
- // but LLSpinCtrl handles the action continuosly so subsribers needs to
- // be informed about click ending even if outside view, use 'up' instead
- up_button_params.mouse_up_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2));
- up_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2));
- up_button_params.commit_on_capture_lost = true;
-
- mUpBtn = LLUICtrlFactory::create<LLButton>(up_button_params);
- addChild(mUpBtn);
-
- LLButton::Params down_button_params(p.down_button);
- down_button_params.rect = LLRect(btn_left, getRect().getHeight() - spinctrl_btn_height, btn_right, getRect().getHeight() - 2 * spinctrl_btn_height);
- down_button_params.mouse_up_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2));
- down_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2));
- down_button_params.commit_on_capture_lost = true;
- mDownBtn = LLUICtrlFactory::create<LLButton>(down_button_params);
- addChild(mDownBtn);
-
- LLRect editor_rect( btn_right + 1, centered_top, getRect().getWidth(), centered_bottom );
- LLLineEditor::Params params;
- params.name("SpinCtrl Editor");
- params.rect(editor_rect);
- if (p.font.isProvided())
- {
- params.font(p.font);
- }
- params.max_length.bytes(MAX_STRING_LENGTH);
- params.commit_callback.function((boost::bind(&LLSpinCtrl::onEditorCommit, this, _2)));
-
- //*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 ));
- mEditor->setFocusLostCallback( boost::bind(&LLSpinCtrl::onEditorLostFocus, _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();
- setUseBoundingRect( true );
-}
-
-F32 clamp_precision(F32 value, S32 decimal_precision)
-{
- // pow() isn't perfect
-
- F64 clamped_value = value;
- for (S32 i = 0; i < decimal_precision; i++)
- clamped_value *= 10.0;
-
- clamped_value = ll_round(clamped_value);
-
- for (S32 i = 0; i < decimal_precision; i++)
- clamped_value /= 10.0;
-
- return (F32)clamped_value;
-}
-
-
-void LLSpinCtrl::onUpBtn( const LLSD& data )
-{
- if( getEnabled() )
- {
- std::string text = mEditor->getText();
- if( LLLineEditor::postvalidateFloat( text ) )
- {
-
- LLLocale locale(LLLocale::USER_LOCALE);
- F32 cur_val = (F32) atof(text.c_str());
-
- // use getValue()/setValue() to force reload from/to control
- F32 val = cur_val + mIncrement;
- val = clamp_precision(val, mPrecision);
- val = llmin( val, mMaxValue );
- if (val < mMinValue) val = mMinValue;
- if (val > mMaxValue) val = mMaxValue;
-
- F32 saved_val = (F32)getValue().asReal();
- setValue(val);
- if( mValidateSignal && !(*mValidateSignal)( this, val ) )
- {
- setValue( saved_val );
- reportInvalidData();
- updateEditor();
- return;
- }
-
- updateEditor();
- onCommit();
- }
- }
-}
-
-void LLSpinCtrl::onDownBtn( const LLSD& data )
-{
- if( getEnabled() )
- {
- std::string text = mEditor->getText();
- if( LLLineEditor::postvalidateFloat( text ) )
- {
-
- LLLocale locale(LLLocale::USER_LOCALE);
- F32 cur_val = (F32) atof(text.c_str());
-
- F32 val = cur_val - mIncrement;
- val = clamp_precision(val, mPrecision);
- val = llmax( val, mMinValue );
-
- if (val < mMinValue) val = mMinValue;
- if (val > mMaxValue) val = mMaxValue;
-
- F32 saved_val = (F32)getValue().asReal();
- setValue(val);
- if( mValidateSignal && !(*mValidateSignal)( this, val ) )
- {
- setValue( saved_val );
- reportInvalidData();
- updateEditor();
- return;
- }
-
- updateEditor();
- onCommit();
- }
- }
-}
-
-// static
-void LLSpinCtrl::onEditorGainFocus( LLFocusableElement* caller, void *userdata )
-{
- LLSpinCtrl* self = (LLSpinCtrl*) userdata;
- llassert( caller == self->mEditor );
-
- self->onFocusReceived();
-}
-
-// static
-void LLSpinCtrl::onEditorLostFocus( LLFocusableElement* caller, void *userdata )
-{
- LLSpinCtrl* self = (LLSpinCtrl*) userdata;
- llassert( caller == self->mEditor );
-
- self->onFocusLost();
-
- std::string text = self->mEditor->getText();
-
- LLLocale locale(LLLocale::USER_LOCALE);
- F32 val = (F32)atof(text.c_str());
-
- F32 saved_val = self->getValueF32();
- if (saved_val != val && !self->mEditor->isDirty())
- {
- // Editor was focused when value update arrived, string
- // in editor is different from one in spin control.
- // Since editor is not dirty, it won't commit, so either
- // attempt to commit value from editor or revert to a more
- // recent value from spin control
- self->updateEditor();
- }
-}
-
-void LLSpinCtrl::setValue(const LLSD& value )
-{
- F32 v = (F32)value.asReal();
- if (getValueF32() != v || !mbHasBeenSet)
- {
- mbHasBeenSet = true;
- LLF32UICtrl::setValue(value);
-
- if (!mEditor->hasFocus())
- {
- updateEditor();
- }
- }
-}
-
-//no matter if Editor has the focus, update the value
-void LLSpinCtrl::forceSetValue(const LLSD& value )
-{
- F32 v = (F32)value.asReal();
- if (getValueF32() != v || !mbHasBeenSet)
- {
- mbHasBeenSet = true;
- LLF32UICtrl::setValue(value);
-
- updateEditor();
- mEditor->resetScrollPosition();
- }
-}
-
-void LLSpinCtrl::clear()
-{
- setValue(mMinValue);
- mEditor->clear();
- mbHasBeenSet = false;
-}
-
-void LLSpinCtrl::updateLabelColor()
-{
- if( mLabelBox )
- {
- mLabelBox->setColor( getEnabled() ? mTextEnabledColor.get() : mTextDisabledColor.get() );
- }
-}
-
-void LLSpinCtrl::updateEditor()
-{
- LLLocale locale(LLLocale::USER_LOCALE);
-
- // Don't display very small negative valu es as -0.000
- F32 displayed_value = clamp_precision((F32)getValue().asReal(), mPrecision);
-
-// if( S32( displayed_value * pow( 10, mPrecision ) ) == 0 )
-// {
-// displayed_value = 0.f;
-// }
-
- std::string format = llformat("%%.%df", mPrecision);
- std::string text = llformat(format.c_str(), displayed_value);
- mEditor->setText( text );
-}
-
-void LLSpinCtrl::onEditorCommit( const LLSD& data )
-{
- bool success = false;
-
- if( mEditor->evaluateFloat() )
- {
- std::string text = mEditor->getText();
-
- LLLocale locale(LLLocale::USER_LOCALE);
- F32 val = (F32) atof(text.c_str());
-
- if (val < mMinValue) val = mMinValue;
- if (val > mMaxValue) val = mMaxValue;
-
- F32 saved_val = getValueF32();
- setValue(val);
- if( !mValidateSignal || (*mValidateSignal)( this, val ) )
- {
- success = true;
- onCommit();
- }
- else
- {
- setValue(saved_val);
- }
- }
- updateEditor();
-
- if( success )
- {
- // We commited and clamped value
- // try to display as much as possible
- mEditor->resetScrollPosition();
- }
- else
- {
- reportInvalidData();
- }
-}
-
-
-void LLSpinCtrl::forceEditorCommit()
-{
- onEditorCommit( LLSD() );
-}
-
-
-void LLSpinCtrl::setFocus(bool b)
-{
- LLUICtrl::setFocus( b );
- mEditor->setFocus( b );
-}
-
-void LLSpinCtrl::setEnabled(bool b)
-{
- LLView::setEnabled( b );
- mEditor->setEnabled( b );
- updateLabelColor();
-}
-
-
-void LLSpinCtrl::setTentative(bool b)
-{
- mEditor->setTentative(b);
- LLUICtrl::setTentative(b);
-}
-
-
-bool LLSpinCtrl::isMouseHeldDown() const
-{
- return
- mDownBtn->hasMouseCapture()
- || mUpBtn->hasMouseCapture();
-}
-
-void LLSpinCtrl::onCommit()
-{
- setTentative(false);
- setControlValue(getValueF32());
- LLF32UICtrl::onCommit();
-}
-
-
-void LLSpinCtrl::setPrecision(S32 precision)
-{
- if (precision < 0 || precision > 10)
- {
- LL_ERRS() << "LLSpinCtrl::setPrecision - precision out of range" << LL_ENDL;
- return;
- }
-
- mPrecision = precision;
- updateEditor();
-}
-
-void LLSpinCtrl::setLabel(const LLStringExplicit& label)
-{
- if (mLabelBox)
- {
- mLabelBox->setText(label);
- }
- else
- {
- LL_WARNS() << "Attempting to set label on LLSpinCtrl constructed without one " << getName() << LL_ENDL;
- }
- updateLabelColor();
-}
-
-void LLSpinCtrl::setAllowEdit(bool allow_edit)
-{
- mEditor->setEnabled(allow_edit);
- mAllowEdit = allow_edit;
-}
-
-void LLSpinCtrl::onTabInto()
-{
- mEditor->onTabInto();
- LLF32UICtrl::onTabInto();
-}
-
-
-void LLSpinCtrl::reportInvalidData()
-{
- make_ui_sound("UISndBadKeystroke");
-}
-
-bool LLSpinCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks)
-{
- if( clicks > 0 )
- {
- while( clicks-- )
- {
- onDownBtn(getValue());
- }
- }
- else
- while( clicks++ )
- {
- onUpBtn(getValue());
- }
-
- return true;
-}
-
-bool LLSpinCtrl::handleKeyHere(KEY key, MASK mask)
-{
- if (mEditor->hasFocus())
- {
- if(key == KEY_ESCAPE)
- {
- // text editors don't support revert normally (due to user confusion)
- // but not allowing revert on a spinner seems dangerous
- updateEditor();
- mEditor->resetScrollPosition();
- mEditor->setFocus(false);
- return true;
- }
- if(key == KEY_UP)
- {
- onUpBtn(getValue());
- return true;
- }
- if(key == KEY_DOWN)
- {
- onDownBtn(getValue());
- return true;
- }
- }
- return false;
-}
-
+/**
+ * @file llspinctrl.cpp
+ * @brief LLSpinCtrl base class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llspinctrl.h"
+
+#include "llgl.h"
+#include "llui.h"
+#include "lluiconstants.h"
+
+#include "llstring.h"
+#include "llfontgl.h"
+#include "lllineeditor.h"
+#include "llbutton.h"
+#include "lltextbox.h"
+#include "llkeyboard.h"
+#include "llmath.h"
+#include "llcontrol.h"
+#include "llfocusmgr.h"
+#include "llresmgr.h"
+#include "lluictrlfactory.h"
+
+const U32 MAX_STRING_LENGTH = 255;
+
+static LLDefaultChildRegistry::Register<LLSpinCtrl> r2("spinner");
+
+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"),
+ down_button("down_button")
+{}
+
+LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
+: LLF32UICtrl(p),
+ mLabelBox(NULL),
+ mbHasBeenSet( false ),
+ mPrecision(p.decimal_digits),
+ mTextEnabledColor(p.text_enabled_color()),
+ mTextDisabledColor(p.text_disabled_color())
+{
+ 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 btn_left = 0;
+ // reserve space for spinner
+ S32 label_width = llclamp(p.label_width(), 0, llmax(0, getRect().getWidth() - 40));
+
+ // Label
+ if( !p.label().empty() )
+ {
+ 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());
+ if (p.font.isProvided())
+ {
+ params.font(p.font);
+ }
+ mLabelBox = LLUICtrlFactory::create<LLTextBox> (params);
+ addChild(mLabelBox);
+
+ btn_left += label_rect.mRight + spinctrl_spacing;
+ }
+
+ S32 btn_right = btn_left + spinctrl_btn_width;
+
+ // Spin buttons
+ LLButton::Params up_button_params(p.up_button);
+ up_button_params.rect = LLRect(btn_left, getRect().getHeight(), btn_right, getRect().getHeight() - spinctrl_btn_height);
+ // Click callback starts within the button and ends within the button,
+ // but LLSpinCtrl handles the action continuosly so subsribers needs to
+ // be informed about click ending even if outside view, use 'up' instead
+ up_button_params.mouse_up_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2));
+ up_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2));
+ up_button_params.commit_on_capture_lost = true;
+
+ mUpBtn = LLUICtrlFactory::create<LLButton>(up_button_params);
+ addChild(mUpBtn);
+
+ LLButton::Params down_button_params(p.down_button);
+ down_button_params.rect = LLRect(btn_left, getRect().getHeight() - spinctrl_btn_height, btn_right, getRect().getHeight() - 2 * spinctrl_btn_height);
+ down_button_params.mouse_up_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2));
+ down_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2));
+ down_button_params.commit_on_capture_lost = true;
+ mDownBtn = LLUICtrlFactory::create<LLButton>(down_button_params);
+ addChild(mDownBtn);
+
+ LLRect editor_rect( btn_right + 1, centered_top, getRect().getWidth(), centered_bottom );
+ LLLineEditor::Params params;
+ params.name("SpinCtrl Editor");
+ params.rect(editor_rect);
+ if (p.font.isProvided())
+ {
+ params.font(p.font);
+ }
+ params.max_length.bytes(MAX_STRING_LENGTH);
+ params.commit_callback.function((boost::bind(&LLSpinCtrl::onEditorCommit, this, _2)));
+
+ //*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 ));
+ mEditor->setFocusLostCallback( boost::bind(&LLSpinCtrl::onEditorLostFocus, _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();
+ setUseBoundingRect( true );
+}
+
+F32 clamp_precision(F32 value, S32 decimal_precision)
+{
+ // pow() isn't perfect
+
+ F64 clamped_value = value;
+ for (S32 i = 0; i < decimal_precision; i++)
+ clamped_value *= 10.0;
+
+ clamped_value = ll_round(clamped_value);
+
+ for (S32 i = 0; i < decimal_precision; i++)
+ clamped_value /= 10.0;
+
+ return (F32)clamped_value;
+}
+
+
+void LLSpinCtrl::onUpBtn( const LLSD& data )
+{
+ if( getEnabled() )
+ {
+ std::string text = mEditor->getText();
+ if( LLLineEditor::postvalidateFloat( text ) )
+ {
+
+ LLLocale locale(LLLocale::USER_LOCALE);
+ F32 cur_val = (F32) atof(text.c_str());
+
+ // use getValue()/setValue() to force reload from/to control
+ F32 val = cur_val + mIncrement;
+ val = clamp_precision(val, mPrecision);
+ val = llmin( val, mMaxValue );
+ if (val < mMinValue) val = mMinValue;
+ if (val > mMaxValue) val = mMaxValue;
+
+ F32 saved_val = (F32)getValue().asReal();
+ setValue(val);
+ if( mValidateSignal && !(*mValidateSignal)( this, val ) )
+ {
+ setValue( saved_val );
+ reportInvalidData();
+ updateEditor();
+ return;
+ }
+
+ updateEditor();
+ onCommit();
+ }
+ }
+}
+
+void LLSpinCtrl::onDownBtn( const LLSD& data )
+{
+ if( getEnabled() )
+ {
+ std::string text = mEditor->getText();
+ if( LLLineEditor::postvalidateFloat( text ) )
+ {
+
+ LLLocale locale(LLLocale::USER_LOCALE);
+ F32 cur_val = (F32) atof(text.c_str());
+
+ F32 val = cur_val - mIncrement;
+ val = clamp_precision(val, mPrecision);
+ val = llmax( val, mMinValue );
+
+ if (val < mMinValue) val = mMinValue;
+ if (val > mMaxValue) val = mMaxValue;
+
+ F32 saved_val = (F32)getValue().asReal();
+ setValue(val);
+ if( mValidateSignal && !(*mValidateSignal)( this, val ) )
+ {
+ setValue( saved_val );
+ reportInvalidData();
+ updateEditor();
+ return;
+ }
+
+ updateEditor();
+ onCommit();
+ }
+ }
+}
+
+// static
+void LLSpinCtrl::onEditorGainFocus( LLFocusableElement* caller, void *userdata )
+{
+ LLSpinCtrl* self = (LLSpinCtrl*) userdata;
+ llassert( caller == self->mEditor );
+
+ self->onFocusReceived();
+}
+
+// static
+void LLSpinCtrl::onEditorLostFocus( LLFocusableElement* caller, void *userdata )
+{
+ LLSpinCtrl* self = (LLSpinCtrl*) userdata;
+ llassert( caller == self->mEditor );
+
+ self->onFocusLost();
+
+ std::string text = self->mEditor->getText();
+
+ LLLocale locale(LLLocale::USER_LOCALE);
+ F32 val = (F32)atof(text.c_str());
+
+ F32 saved_val = self->getValueF32();
+ if (saved_val != val && !self->mEditor->isDirty())
+ {
+ // Editor was focused when value update arrived, string
+ // in editor is different from one in spin control.
+ // Since editor is not dirty, it won't commit, so either
+ // attempt to commit value from editor or revert to a more
+ // recent value from spin control
+ self->updateEditor();
+ }
+}
+
+void LLSpinCtrl::setValue(const LLSD& value )
+{
+ F32 v = (F32)value.asReal();
+ if (getValueF32() != v || !mbHasBeenSet)
+ {
+ mbHasBeenSet = true;
+ LLF32UICtrl::setValue(value);
+
+ if (!mEditor->hasFocus())
+ {
+ updateEditor();
+ }
+ }
+}
+
+//no matter if Editor has the focus, update the value
+void LLSpinCtrl::forceSetValue(const LLSD& value )
+{
+ F32 v = (F32)value.asReal();
+ if (getValueF32() != v || !mbHasBeenSet)
+ {
+ mbHasBeenSet = true;
+ LLF32UICtrl::setValue(value);
+
+ updateEditor();
+ mEditor->resetScrollPosition();
+ }
+}
+
+void LLSpinCtrl::clear()
+{
+ setValue(mMinValue);
+ mEditor->clear();
+ mbHasBeenSet = false;
+}
+
+void LLSpinCtrl::updateLabelColor()
+{
+ if( mLabelBox )
+ {
+ mLabelBox->setColor( getEnabled() ? mTextEnabledColor.get() : mTextDisabledColor.get() );
+ }
+}
+
+void LLSpinCtrl::updateEditor()
+{
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ // Don't display very small negative valu es as -0.000
+ F32 displayed_value = clamp_precision((F32)getValue().asReal(), mPrecision);
+
+// if( S32( displayed_value * pow( 10, mPrecision ) ) == 0 )
+// {
+// displayed_value = 0.f;
+// }
+
+ std::string format = llformat("%%.%df", mPrecision);
+ std::string text = llformat(format.c_str(), displayed_value);
+ mEditor->setText( text );
+}
+
+void LLSpinCtrl::onEditorCommit( const LLSD& data )
+{
+ bool success = false;
+
+ if( mEditor->evaluateFloat() )
+ {
+ std::string text = mEditor->getText();
+
+ LLLocale locale(LLLocale::USER_LOCALE);
+ F32 val = (F32) atof(text.c_str());
+
+ if (val < mMinValue) val = mMinValue;
+ if (val > mMaxValue) val = mMaxValue;
+
+ F32 saved_val = getValueF32();
+ setValue(val);
+ if( !mValidateSignal || (*mValidateSignal)( this, val ) )
+ {
+ success = true;
+ onCommit();
+ }
+ else
+ {
+ setValue(saved_val);
+ }
+ }
+ updateEditor();
+
+ if( success )
+ {
+ // We commited and clamped value
+ // try to display as much as possible
+ mEditor->resetScrollPosition();
+ }
+ else
+ {
+ reportInvalidData();
+ }
+}
+
+
+void LLSpinCtrl::forceEditorCommit()
+{
+ onEditorCommit( LLSD() );
+}
+
+
+void LLSpinCtrl::setFocus(bool b)
+{
+ LLUICtrl::setFocus( b );
+ mEditor->setFocus( b );
+}
+
+void LLSpinCtrl::setEnabled(bool b)
+{
+ LLView::setEnabled( b );
+ mEditor->setEnabled( b );
+ updateLabelColor();
+}
+
+
+void LLSpinCtrl::setTentative(bool b)
+{
+ mEditor->setTentative(b);
+ LLUICtrl::setTentative(b);
+}
+
+
+bool LLSpinCtrl::isMouseHeldDown() const
+{
+ return
+ mDownBtn->hasMouseCapture()
+ || mUpBtn->hasMouseCapture();
+}
+
+void LLSpinCtrl::onCommit()
+{
+ setTentative(false);
+ setControlValue(getValueF32());
+ LLF32UICtrl::onCommit();
+}
+
+
+void LLSpinCtrl::setPrecision(S32 precision)
+{
+ if (precision < 0 || precision > 10)
+ {
+ LL_ERRS() << "LLSpinCtrl::setPrecision - precision out of range" << LL_ENDL;
+ return;
+ }
+
+ mPrecision = precision;
+ updateEditor();
+}
+
+void LLSpinCtrl::setLabel(const LLStringExplicit& label)
+{
+ if (mLabelBox)
+ {
+ mLabelBox->setText(label);
+ }
+ else
+ {
+ LL_WARNS() << "Attempting to set label on LLSpinCtrl constructed without one " << getName() << LL_ENDL;
+ }
+ updateLabelColor();
+}
+
+void LLSpinCtrl::setAllowEdit(bool allow_edit)
+{
+ mEditor->setEnabled(allow_edit);
+ mAllowEdit = allow_edit;
+}
+
+void LLSpinCtrl::onTabInto()
+{
+ mEditor->onTabInto();
+ LLF32UICtrl::onTabInto();
+}
+
+
+void LLSpinCtrl::reportInvalidData()
+{
+ make_ui_sound("UISndBadKeystroke");
+}
+
+bool LLSpinCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ if( clicks > 0 )
+ {
+ while( clicks-- )
+ {
+ onDownBtn(getValue());
+ }
+ }
+ else
+ while( clicks++ )
+ {
+ onUpBtn(getValue());
+ }
+
+ return true;
+}
+
+bool LLSpinCtrl::handleKeyHere(KEY key, MASK mask)
+{
+ if (mEditor->hasFocus())
+ {
+ if(key == KEY_ESCAPE)
+ {
+ // text editors don't support revert normally (due to user confusion)
+ // but not allowing revert on a spinner seems dangerous
+ updateEditor();
+ mEditor->resetScrollPosition();
+ mEditor->setFocus(false);
+ return true;
+ }
+ if(key == KEY_UP)
+ {
+ onUpBtn(getValue());
+ return true;
+ }
+ if(key == KEY_DOWN)
+ {
+ onDownBtn(getValue());
+ return true;
+ }
+ }
+ return false;
+}
+
diff --git a/indra/llui/llspinctrl.h b/indra/llui/llspinctrl.h
index 046d15eaf7..471747f404 100644
--- a/indra/llui/llspinctrl.h
+++ b/indra/llui/llspinctrl.h
@@ -1,124 +1,124 @@
-/**
- * @file llspinctrl.h
- * @brief Typical spinner with "up" and "down" arrow buttons.
- *
- * $LicenseInfo:firstyear=2002&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_LLSPINCTRL_H
-#define LL_LLSPINCTRL_H
-
-
-#include "stdtypes.h"
-#include "llbutton.h"
-#include "llf32uictrl.h"
-#include "v4color.h"
-#include "llrect.h"
-
-
-class LLSpinCtrl
-: public LLF32UICtrl
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLF32UICtrl::Params>
- {
- 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;
-
- Optional<LLButton::Params> up_button;
- Optional<LLButton::Params> down_button;
-
- Params();
- };
-protected:
- LLSpinCtrl(const Params&);
- friend class LLUICtrlFactory;
-public:
- virtual ~LLSpinCtrl() {} // Children all cleaned up by default view destructor.
-
- virtual void forceSetValue(const LLSD& value ) ;
- virtual void setValue(const LLSD& value );
- F32 get() const { return getValueF32(); }
- void set(F32 value) { setValue(value); mInitialValue = value; }
-
- bool isMouseHeldDown() const;
-
- virtual void setEnabled( bool b );
- virtual void setFocus( bool b );
- virtual void clear();
- virtual bool isDirty() const { return( getValueF32() != mInitialValue ); }
- virtual void resetDirty() { mInitialValue = getValueF32(); }
-
- virtual void setPrecision(S32 precision);
-
- void setLabel(const LLStringExplicit& label);
- void setLabelColor(const LLColor4& c) { mTextEnabledColor = c; updateLabelColor(); }
- void setDisabledLabelColor(const LLColor4& c) { mTextDisabledColor = c; updateLabelColor();}
- void setAllowEdit(bool allow_edit);
-
- virtual void onTabInto();
-
- virtual void setTentative(bool b); // marks value as tentative
- virtual void onCommit(); // mark not tentative, then commit
-
- void forceEditorCommit(); // for commit on external button
-
- virtual bool handleScrollWheel(S32 x,S32 y,S32 clicks);
- virtual bool handleKeyHere(KEY key, MASK mask);
-
- void onEditorCommit(const LLSD& data);
- static void onEditorGainFocus(LLFocusableElement* caller, void *userdata);
- static void onEditorLostFocus(LLFocusableElement* caller, void *userdata);
- static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata);
-
- 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();
- void updateEditor();
- void reportInvalidData();
-
- S32 mPrecision;
- class LLTextBox* mLabelBox;
-
- class LLLineEditor* mEditor;
- LLUIColor mTextEnabledColor;
- LLUIColor mTextDisabledColor;
-
- class LLButton* mUpBtn;
- class LLButton* mDownBtn;
-
- bool mbHasBeenSet;
- bool mAllowEdit;
-};
-
-#endif // LL_LLSPINCTRL_H
+/**
+ * @file llspinctrl.h
+ * @brief Typical spinner with "up" and "down" arrow buttons.
+ *
+ * $LicenseInfo:firstyear=2002&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_LLSPINCTRL_H
+#define LL_LLSPINCTRL_H
+
+
+#include "stdtypes.h"
+#include "llbutton.h"
+#include "llf32uictrl.h"
+#include "v4color.h"
+#include "llrect.h"
+
+
+class LLSpinCtrl
+: public LLF32UICtrl
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLF32UICtrl::Params>
+ {
+ 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;
+
+ Optional<LLButton::Params> up_button;
+ Optional<LLButton::Params> down_button;
+
+ Params();
+ };
+protected:
+ LLSpinCtrl(const Params&);
+ friend class LLUICtrlFactory;
+public:
+ virtual ~LLSpinCtrl() {} // Children all cleaned up by default view destructor.
+
+ virtual void forceSetValue(const LLSD& value ) ;
+ virtual void setValue(const LLSD& value );
+ F32 get() const { return getValueF32(); }
+ void set(F32 value) { setValue(value); mInitialValue = value; }
+
+ bool isMouseHeldDown() const;
+
+ virtual void setEnabled( bool b );
+ virtual void setFocus( bool b );
+ virtual void clear();
+ virtual bool isDirty() const { return( getValueF32() != mInitialValue ); }
+ virtual void resetDirty() { mInitialValue = getValueF32(); }
+
+ virtual void setPrecision(S32 precision);
+
+ void setLabel(const LLStringExplicit& label);
+ void setLabelColor(const LLColor4& c) { mTextEnabledColor = c; updateLabelColor(); }
+ void setDisabledLabelColor(const LLColor4& c) { mTextDisabledColor = c; updateLabelColor();}
+ void setAllowEdit(bool allow_edit);
+
+ virtual void onTabInto();
+
+ virtual void setTentative(bool b); // marks value as tentative
+ virtual void onCommit(); // mark not tentative, then commit
+
+ void forceEditorCommit(); // for commit on external button
+
+ virtual bool handleScrollWheel(S32 x,S32 y,S32 clicks);
+ virtual bool handleKeyHere(KEY key, MASK mask);
+
+ void onEditorCommit(const LLSD& data);
+ static void onEditorGainFocus(LLFocusableElement* caller, void *userdata);
+ static void onEditorLostFocus(LLFocusableElement* caller, void *userdata);
+ static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata);
+
+ 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();
+ void updateEditor();
+ void reportInvalidData();
+
+ S32 mPrecision;
+ class LLTextBox* mLabelBox;
+
+ class LLLineEditor* mEditor;
+ LLUIColor mTextEnabledColor;
+ LLUIColor mTextDisabledColor;
+
+ class LLButton* mUpBtn;
+ class LLButton* mDownBtn;
+
+ bool mbHasBeenSet;
+ bool mAllowEdit;
+};
+
+#endif // LL_LLSPINCTRL_H
diff --git a/indra/llui/llstatbar.cpp b/indra/llui/llstatbar.cpp
index cba9eca9d2..6e9b19a4de 100644
--- a/indra/llui/llstatbar.cpp
+++ b/indra/llui/llstatbar.cpp
@@ -1,716 +1,716 @@
-/**
- * @file llstatbar.cpp
- * @brief A little map of the world with network information
- *
- * $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 "llviewerprecompiledheaders.h"
-#include "linden_common.h"
-
-#include "llstatbar.h"
-
-#include "llmath.h"
-#include "llui.h"
-#include "llgl.h"
-#include "llfontgl.h"
-
-#include "lluictrlfactory.h"
-#include "lltracerecording.h"
-#include "llcriticaldamp.h"
-#include "lltooltip.h"
-#include "lllocalcliprect.h"
-#include <iostream>
-#include "lltrans.h"
-
-// 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;
- }
- }
- }
-
- return is_approx_equal(range, 0.f) ? 0.f : range / best_divisor;
-}
-
-void calc_auto_scale_range(F32& min, F32& max, F32& tick)
-{
- 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++)
- {
- 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];
- }
- }
-
- cur_max = starting_max;
- cur_min = starting_min;
- for (S32 range_idx = LL_ARRAY_SIZE(RANGES) - 1; range_idx >= 0; range_idx--)
- {
- 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];
- }
- }
-
- tick = power_of_10 * llmax(cur_tick_min, cur_tick_max);
- min = out_min;
- max = out_max;
-}
-
-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_median("show_median", 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);
-}
-
-///////////////////////////////////////////////////////////////////////////////////
-
-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),
- mShowMedian(p.show_median),
- 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)
-{
- mFloatingTargetMinBar = mTargetMinBar;
- mFloatingTargetMaxBar = mTargetMaxBar;
-
- mStat.valid = NULL;
- // tick value will be automatically calculated later
- if (!p.tick_spacing.isProvided() && p.bar_min.isProvided() && p.bar_max.isProvided())
- {
- mTickSpacing = calc_tick_value(mTargetMinBar, mTargetMaxBar);
- }
-
- setStat(p.stat);
-}
-
-bool LLStatBar::handleHover(S32 x, S32 y, MASK mask)
-{
- switch(mStatType)
- {
- 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;
- default:
- break;
- }
- return true;
-}
-
-bool LLStatBar::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- bool handled = LLView::handleMouseDown(x, y, mask);
- if (!handled)
- {
- if (mDisplayBar)
- {
- if (mDisplayHistory || mOrientation == HORIZONTAL)
- {
- mDisplayBar = false;
- mDisplayHistory = false;
- }
- else
- {
- mDisplayHistory = true;
- }
- }
- else
- {
- mDisplayBar = true;
- if (mOrientation == HORIZONTAL)
- {
- mDisplayHistory = true;
- }
- }
- LLView* parent = getParent();
- parent->reshape(parent->getRect().getWidth(), parent->getRect().getHeight(), false);
- }
- return true;
-}
-
-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);
-
- for (S32 i = 2; i < periodic_recording.getNumRecordedPeriods(); i++)
- {
- LLTrace::Recording& recording = periodic_recording.getPrevRecording(i);
- F64 cur_value = recording.getLastValue(stat);
-
- 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;
- }
-
- 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)
- {
- 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);
- if (mShowMedian)
- {
- display_value = frame_recording.getPeriodMedianPerSec(count_stat, num_frames);
- }
- else
- {
- 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 (mShowMedian)
- {
- display_value = frame_recording.getPeriodMedian(sample_stat, num_frames);
- }
- else 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;
- default:
- break;
- }
-
- LLRect bar_rect;
- if (mOrientation == HORIZONTAL)
- {
- 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);
- }
-
- mCurMaxBar = LLSmoothInterpolation::lerp(mCurMaxBar, mTargetMaxBar, 0.05f);
- mCurMinBar = LLSmoothInterpolation::lerp(mCurMinBar, mTargetMinBar, 0.05f);
-
- // rate limited updates
- if (mLastDisplayValueTimer.getElapsedTimeF32() < MEAN_VALUE_UPDATE_TIME)
- {
- 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.
- LLGLSUIDefault gls_ui;
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- F32 value_scale;
- if (mCurMaxBar == mCurMinBar)
- {
- value_scale = 0.f;
- }
- else
- {
- value_scale = (mOrientation == HORIZONTAL)
- ? (bar_rect.getHeight())/(mCurMaxBar - mCurMinBar)
- : (bar_rect.getWidth())/(mCurMaxBar - mCurMinBar);
- }
-
- drawTicks(min, max, value_scale, bar_rect);
-
- // draw background bar.
- gl_rect_2d(bar_rect.mLeft, bar_rect.mTop, bar_rect.mRight, bar_rect.mBottom, LLColor4(0.f, 0.f, 0.f, 0.25f));
-
- // draw values
- if (!llisnan(display_value) && frame_recording.getNumRecordedPeriods() != 0)
- {
- // draw min and max
- S32 begin = (S32) ((min - mCurMinBar) * value_scale);
-
- if (begin < 0)
- {
- begin = 0;
- }
-
- 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));
- }
-
- F32 span = (mOrientation == HORIZONTAL)
- ? (bar_rect.getWidth())
- : (bar_rect.getHeight());
-
- if (mDisplayHistory && mStat.valid)
- {
- 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++)
- {
- 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;
- 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 );
- }
- }
- gGL.end();
- }
- else
- {
- S32 begin = (S32) ((current - mCurMinBar) * value_scale) - 1;
- S32 end = (S32) ((current - mCurMinBar) * value_scale) + 1;
- // draw current
- if (mOrientation == HORIZONTAL)
- {
- gl_rect_2d(bar_rect.mLeft, end, bar_rect.mRight, begin, LLColor4(1.f, 0.f, 0.f, 1.f));
- }
- else
- {
- gl_rect_2d(begin, bar_rect.mTop, end, bar_rect.mBottom, LLColor4(1.f, 0.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::setStat(const std::string& stat_name)
-{
- using namespace LLTrace;
-
- if (auto count_stat = StatType<CountAccumulator>::getInstance(stat_name))
- {
- mStat.countStatp = count_stat.get();
- mStatType = STAT_COUNT;
- }
- else if (auto event_stat = StatType<EventAccumulator>::getInstance(stat_name))
- {
- mStat.eventStatp = event_stat.get();
- mStatType = STAT_EVENT;
- }
- else if (auto sample_stat = StatType<SampleAccumulator>::getInstance(stat_name))
- {
- mStat.sampleStatp = sample_stat.get();
- mStatType = STAT_SAMPLE;
- }
-}
-
-void LLStatBar::setRange(F32 bar_min, F32 bar_max)
-{
- 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()
-{
- LLRect rect;
-
- if (mDisplayBar)
- {
- if (mDisplayHistory)
- {
- rect.mTop = mMaxHeight;
- }
- else
- {
- rect.mTop = 40;
- }
- }
- else
- {
- rect.mTop = 14;
- }
- 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())
- : LLTrans::getString("na");
-
- // 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, 1.f),
- LLFontGL::RIGHT, LLFontGL::TOP);
- }
- else
- {
- LLFontGL::getFontMonospace()->renderUTF8(value_str, 0, bar_rect.mRight, getRect().getHeight(),
- LLColor4(1.f, 1.f, 1.f, 1.f),
- 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;
- }
- }
- }
-}
+/**
+ * @file llstatbar.cpp
+ * @brief A little map of the world with network information
+ *
+ * $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 "llviewerprecompiledheaders.h"
+#include "linden_common.h"
+
+#include "llstatbar.h"
+
+#include "llmath.h"
+#include "llui.h"
+#include "llgl.h"
+#include "llfontgl.h"
+
+#include "lluictrlfactory.h"
+#include "lltracerecording.h"
+#include "llcriticaldamp.h"
+#include "lltooltip.h"
+#include "lllocalcliprect.h"
+#include <iostream>
+#include "lltrans.h"
+
+// 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;
+ }
+ }
+ }
+
+ return is_approx_equal(range, 0.f) ? 0.f : range / best_divisor;
+}
+
+void calc_auto_scale_range(F32& min, F32& max, F32& tick)
+{
+ 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++)
+ {
+ 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];
+ }
+ }
+
+ cur_max = starting_max;
+ cur_min = starting_min;
+ for (S32 range_idx = LL_ARRAY_SIZE(RANGES) - 1; range_idx >= 0; range_idx--)
+ {
+ 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];
+ }
+ }
+
+ tick = power_of_10 * llmax(cur_tick_min, cur_tick_max);
+ min = out_min;
+ max = out_max;
+}
+
+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_median("show_median", 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);
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+
+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),
+ mShowMedian(p.show_median),
+ 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)
+{
+ mFloatingTargetMinBar = mTargetMinBar;
+ mFloatingTargetMaxBar = mTargetMaxBar;
+
+ mStat.valid = NULL;
+ // tick value will be automatically calculated later
+ if (!p.tick_spacing.isProvided() && p.bar_min.isProvided() && p.bar_max.isProvided())
+ {
+ mTickSpacing = calc_tick_value(mTargetMinBar, mTargetMaxBar);
+ }
+
+ setStat(p.stat);
+}
+
+bool LLStatBar::handleHover(S32 x, S32 y, MASK mask)
+{
+ switch(mStatType)
+ {
+ 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;
+ default:
+ break;
+ }
+ return true;
+}
+
+bool LLStatBar::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ bool handled = LLView::handleMouseDown(x, y, mask);
+ if (!handled)
+ {
+ if (mDisplayBar)
+ {
+ if (mDisplayHistory || mOrientation == HORIZONTAL)
+ {
+ mDisplayBar = false;
+ mDisplayHistory = false;
+ }
+ else
+ {
+ mDisplayHistory = true;
+ }
+ }
+ else
+ {
+ mDisplayBar = true;
+ if (mOrientation == HORIZONTAL)
+ {
+ mDisplayHistory = true;
+ }
+ }
+ LLView* parent = getParent();
+ parent->reshape(parent->getRect().getWidth(), parent->getRect().getHeight(), false);
+ }
+ return true;
+}
+
+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);
+
+ for (S32 i = 2; i < periodic_recording.getNumRecordedPeriods(); i++)
+ {
+ LLTrace::Recording& recording = periodic_recording.getPrevRecording(i);
+ F64 cur_value = recording.getLastValue(stat);
+
+ 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;
+ }
+
+ 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)
+ {
+ 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);
+ if (mShowMedian)
+ {
+ display_value = frame_recording.getPeriodMedianPerSec(count_stat, num_frames);
+ }
+ else
+ {
+ 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 (mShowMedian)
+ {
+ display_value = frame_recording.getPeriodMedian(sample_stat, num_frames);
+ }
+ else 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;
+ default:
+ break;
+ }
+
+ LLRect bar_rect;
+ if (mOrientation == HORIZONTAL)
+ {
+ 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);
+ }
+
+ mCurMaxBar = LLSmoothInterpolation::lerp(mCurMaxBar, mTargetMaxBar, 0.05f);
+ mCurMinBar = LLSmoothInterpolation::lerp(mCurMinBar, mTargetMinBar, 0.05f);
+
+ // rate limited updates
+ if (mLastDisplayValueTimer.getElapsedTimeF32() < MEAN_VALUE_UPDATE_TIME)
+ {
+ 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.
+ LLGLSUIDefault gls_ui;
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ F32 value_scale;
+ if (mCurMaxBar == mCurMinBar)
+ {
+ value_scale = 0.f;
+ }
+ else
+ {
+ value_scale = (mOrientation == HORIZONTAL)
+ ? (bar_rect.getHeight())/(mCurMaxBar - mCurMinBar)
+ : (bar_rect.getWidth())/(mCurMaxBar - mCurMinBar);
+ }
+
+ drawTicks(min, max, value_scale, bar_rect);
+
+ // draw background bar.
+ gl_rect_2d(bar_rect.mLeft, bar_rect.mTop, bar_rect.mRight, bar_rect.mBottom, LLColor4(0.f, 0.f, 0.f, 0.25f));
+
+ // draw values
+ if (!llisnan(display_value) && frame_recording.getNumRecordedPeriods() != 0)
+ {
+ // draw min and max
+ S32 begin = (S32) ((min - mCurMinBar) * value_scale);
+
+ if (begin < 0)
+ {
+ begin = 0;
+ }
+
+ 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));
+ }
+
+ F32 span = (mOrientation == HORIZONTAL)
+ ? (bar_rect.getWidth())
+ : (bar_rect.getHeight());
+
+ if (mDisplayHistory && mStat.valid)
+ {
+ 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++)
+ {
+ 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;
+ 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 );
+ }
+ }
+ gGL.end();
+ }
+ else
+ {
+ S32 begin = (S32) ((current - mCurMinBar) * value_scale) - 1;
+ S32 end = (S32) ((current - mCurMinBar) * value_scale) + 1;
+ // draw current
+ if (mOrientation == HORIZONTAL)
+ {
+ gl_rect_2d(bar_rect.mLeft, end, bar_rect.mRight, begin, LLColor4(1.f, 0.f, 0.f, 1.f));
+ }
+ else
+ {
+ gl_rect_2d(begin, bar_rect.mTop, end, bar_rect.mBottom, LLColor4(1.f, 0.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::setStat(const std::string& stat_name)
+{
+ using namespace LLTrace;
+
+ if (auto count_stat = StatType<CountAccumulator>::getInstance(stat_name))
+ {
+ mStat.countStatp = count_stat.get();
+ mStatType = STAT_COUNT;
+ }
+ else if (auto event_stat = StatType<EventAccumulator>::getInstance(stat_name))
+ {
+ mStat.eventStatp = event_stat.get();
+ mStatType = STAT_EVENT;
+ }
+ else if (auto sample_stat = StatType<SampleAccumulator>::getInstance(stat_name))
+ {
+ mStat.sampleStatp = sample_stat.get();
+ mStatType = STAT_SAMPLE;
+ }
+}
+
+void LLStatBar::setRange(F32 bar_min, F32 bar_max)
+{
+ 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()
+{
+ LLRect rect;
+
+ if (mDisplayBar)
+ {
+ if (mDisplayHistory)
+ {
+ rect.mTop = mMaxHeight;
+ }
+ else
+ {
+ rect.mTop = 40;
+ }
+ }
+ else
+ {
+ rect.mTop = 14;
+ }
+ 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())
+ : LLTrans::getString("na");
+
+ // 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, 1.f),
+ LLFontGL::RIGHT, LLFontGL::TOP);
+ }
+ else
+ {
+ LLFontGL::getFontMonospace()->renderUTF8(value_str, 0, bar_rect.mRight, getRect().getHeight(),
+ LLColor4(1.f, 1.f, 1.f, 1.f),
+ 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 8e78337c0d..84e9ac6ecb 100644
--- a/indra/llui/llstatbar.h
+++ b/indra/llui/llstatbar.h
@@ -1,119 +1,119 @@
-/**
- * @file llstatbar.h
- * @brief A little map of the world with network information
- *
- * $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_LLSTATBAR_H
-#define LL_LLSTATBAR_H
-
-#include "llview.h"
-#include "llframetimer.h"
-#include "lltracerecording.h"
-
-class LLStatBar : public LLView
-{
-public:
-
- struct Params : public LLInitParam::Block<Params, LLView::Params>
- {
- Optional<std::string> label,
- unit_label;
-
- Optional<F32> bar_min,
- bar_max,
- tick_spacing;
-
- Optional<bool> show_bar,
- show_history,
- scale_range,
- show_median; // default is mean
-
- 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 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:
- 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
- } mStatType;
-
- union
- {
- void* valid;
- const LLTrace::StatType<LLTrace::CountAccumulator>* countStatp;
- const LLTrace::StatType<LLTrace::EventAccumulator>* eventStatp;
- const LLTrace::StatType<LLTrace::SampleAccumulator>* sampleStatp;
- } mStat;
-
- LLUIString mLabel;
- std::string mUnitLabel;
-
- bool mDisplayBar, // Display the bar graph.
- mDisplayHistory,
- mShowMedian,
- mAutoScaleMax,
- mAutoScaleMin;
-};
-
-#endif
+/**
+ * @file llstatbar.h
+ * @brief A little map of the world with network information
+ *
+ * $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_LLSTATBAR_H
+#define LL_LLSTATBAR_H
+
+#include "llview.h"
+#include "llframetimer.h"
+#include "lltracerecording.h"
+
+class LLStatBar : public LLView
+{
+public:
+
+ struct Params : public LLInitParam::Block<Params, LLView::Params>
+ {
+ Optional<std::string> label,
+ unit_label;
+
+ Optional<F32> bar_min,
+ bar_max,
+ tick_spacing;
+
+ Optional<bool> show_bar,
+ show_history,
+ scale_range,
+ show_median; // default is mean
+
+ 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 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:
+ 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
+ } mStatType;
+
+ union
+ {
+ void* valid;
+ const LLTrace::StatType<LLTrace::CountAccumulator>* countStatp;
+ const LLTrace::StatType<LLTrace::EventAccumulator>* eventStatp;
+ const LLTrace::StatType<LLTrace::SampleAccumulator>* sampleStatp;
+ } mStat;
+
+ LLUIString mLabel;
+ std::string mUnitLabel;
+
+ bool mDisplayBar, // Display the bar graph.
+ mDisplayHistory,
+ mShowMedian,
+ mAutoScaleMax,
+ mAutoScaleMin;
+};
+
+#endif
diff --git a/indra/llui/llstatgraph.cpp b/indra/llui/llstatgraph.cpp
index ec014a7419..58532fb22d 100644
--- a/indra/llui/llstatgraph.cpp
+++ b/indra/llui/llstatgraph.cpp
@@ -1,126 +1,126 @@
-/**
- * @file llstatgraph.cpp
- * @brief Simpler compact stat graph with tooltip
- *
- * $LicenseInfo:firstyear=2002&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 "llviewerprecompiledheaders.h"
-#include "linden_common.h"
-
-#include "llstatgraph.h"
-#include "llrender.h"
-
-#include "llmath.h"
-#include "llui.h"
-#include "llgl.h"
-#include "llglheaders.h"
-#include "lltracerecording.h"
-#include "lltracethreadrecorder.h"
-//#include "llviewercontrol.h"
-
-///////////////////////////////////////////////////////////////////////////////////
-
-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)
-{
- setToolTip(p.name());
-
- 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 (mNewStatFloatp)
- {
- LLTrace::Recording& recording = LLTrace::get_frame_recording().getLastRecording();
-
- if (mPerSec)
- {
- mValue = recording.getPerSec(*mNewStatFloatp);
- }
- else
- {
- mValue = recording.getSum(*mNewStatFloatp);
- }
- }
-
- frac = (mValue - mMin) / range;
- frac = llmax(0.f, frac);
- frac = llmin(1.f, frac);
-
- if (mUpdateTimer.getElapsedTimeF32() > 0.5f)
- {
- std::string format_str;
- std::string tmp_str;
- format_str = llformat("%%s%%.%df%%s", mPrecision);
- tmp_str = llformat(format_str.c_str(), mLabel.c_str(), mValue, mUnits.c_str());
- setToolTip(tmp_str);
-
- mUpdateTimer.reset();
- }
-
- LLColor4 color;
-
- threshold_vec_t::iterator it = std::lower_bound(mThresholds.begin(), mThresholds.end(), Threshold(mValue / mMax, LLUIColor()));
-
- if (it != mThresholds.begin())
- {
- it--;
- }
-
- color = LLUIColorTable::instance().getColor( "MenuDefaultBgColor" );
- gGL.color4fv(color.mV);
- gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, true);
-
- gGL.color4fv(LLColor4::black.mV);
- gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, false);
-
- color = it->mColor;
- gGL.color4fv(color.mV);
- gl_rect_2d(1, ll_round(frac*getRect().getHeight()), getRect().getWidth() - 1, 0, true);
-}
-
-void LLStatGraph::setMin(const F32 min)
-{
- mMin = min;
-}
-
-void LLStatGraph::setMax(const F32 max)
-{
- mMax = max;
-}
-
+/**
+ * @file llstatgraph.cpp
+ * @brief Simpler compact stat graph with tooltip
+ *
+ * $LicenseInfo:firstyear=2002&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 "llviewerprecompiledheaders.h"
+#include "linden_common.h"
+
+#include "llstatgraph.h"
+#include "llrender.h"
+
+#include "llmath.h"
+#include "llui.h"
+#include "llgl.h"
+#include "llglheaders.h"
+#include "lltracerecording.h"
+#include "lltracethreadrecorder.h"
+//#include "llviewercontrol.h"
+
+///////////////////////////////////////////////////////////////////////////////////
+
+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)
+{
+ setToolTip(p.name());
+
+ 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 (mNewStatFloatp)
+ {
+ LLTrace::Recording& recording = LLTrace::get_frame_recording().getLastRecording();
+
+ if (mPerSec)
+ {
+ mValue = recording.getPerSec(*mNewStatFloatp);
+ }
+ else
+ {
+ mValue = recording.getSum(*mNewStatFloatp);
+ }
+ }
+
+ frac = (mValue - mMin) / range;
+ frac = llmax(0.f, frac);
+ frac = llmin(1.f, frac);
+
+ if (mUpdateTimer.getElapsedTimeF32() > 0.5f)
+ {
+ std::string format_str;
+ std::string tmp_str;
+ format_str = llformat("%%s%%.%df%%s", mPrecision);
+ tmp_str = llformat(format_str.c_str(), mLabel.c_str(), mValue, mUnits.c_str());
+ setToolTip(tmp_str);
+
+ mUpdateTimer.reset();
+ }
+
+ LLColor4 color;
+
+ threshold_vec_t::iterator it = std::lower_bound(mThresholds.begin(), mThresholds.end(), Threshold(mValue / mMax, LLUIColor()));
+
+ if (it != mThresholds.begin())
+ {
+ it--;
+ }
+
+ color = LLUIColorTable::instance().getColor( "MenuDefaultBgColor" );
+ gGL.color4fv(color.mV);
+ gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, true);
+
+ gGL.color4fv(LLColor4::black.mV);
+ gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, false);
+
+ color = it->mColor;
+ gGL.color4fv(color.mV);
+ gl_rect_2d(1, ll_round(frac*getRect().getHeight()), getRect().getWidth() - 1, 0, true);
+}
+
+void LLStatGraph::setMin(const F32 min)
+{
+ mMin = min;
+}
+
+void LLStatGraph::setMax(const F32 max)
+{
+ mMax = max;
+}
+
diff --git a/indra/llui/llstatgraph.h b/indra/llui/llstatgraph.h
index e70c745b3b..1c1b707688 100644
--- a/indra/llui/llstatgraph.h
+++ b/indra/llui/llstatgraph.h
@@ -1,141 +1,141 @@
-/**
- * @file llstatgraph.h
- * @brief Simpler compact stat graph with tooltip
- *
- * $LicenseInfo:firstyear=2002&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_LLSTATGRAPH_H
-#define LL_LLSTATGRAPH_H
-
-#include "llview.h"
-#include "llframetimer.h"
-#include "v4color.h"
-#include "lltrace.h"
-
-class LLStatGraph : public LLView
-{
-public:
- struct ThresholdParams : public LLInitParam::Block<ThresholdParams>
- {
- Mandatory<F32> value;
- Optional<LLUIColor> color;
-
- 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 setMin(const F32 min);
- void setMax(const F32 max);
-
- virtual void draw();
-
- /*virtual*/ void setValue(const LLSD& value);
-
-private:
- LLTrace::StatType<LLTrace::CountAccumulator>* mNewStatFloatp;
-
- bool mPerSec;
-
- F32 mValue;
-
- F32 mMin;
- F32 mMax;
- LLFrameTimer mUpdateTimer;
- std::string mLabel;
- std::string mUnits;
- S32 mPrecision; // Num of digits of precision after dot
-
- struct Threshold
- {
- Threshold(F32 value, const LLUIColor& color)
- : mValue(value),
- mColor(color)
- {}
-
- F32 mValue;
- LLUIColor mColor;
- bool operator <(const Threshold& other) const
- {
- 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
+/**
+ * @file llstatgraph.h
+ * @brief Simpler compact stat graph with tooltip
+ *
+ * $LicenseInfo:firstyear=2002&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_LLSTATGRAPH_H
+#define LL_LLSTATGRAPH_H
+
+#include "llview.h"
+#include "llframetimer.h"
+#include "v4color.h"
+#include "lltrace.h"
+
+class LLStatGraph : public LLView
+{
+public:
+ struct ThresholdParams : public LLInitParam::Block<ThresholdParams>
+ {
+ Mandatory<F32> value;
+ Optional<LLUIColor> color;
+
+ 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 setMin(const F32 min);
+ void setMax(const F32 max);
+
+ virtual void draw();
+
+ /*virtual*/ void setValue(const LLSD& value);
+
+private:
+ LLTrace::StatType<LLTrace::CountAccumulator>* mNewStatFloatp;
+
+ bool mPerSec;
+
+ F32 mValue;
+
+ F32 mMin;
+ F32 mMax;
+ LLFrameTimer mUpdateTimer;
+ std::string mLabel;
+ std::string mUnits;
+ S32 mPrecision; // Num of digits of precision after dot
+
+ struct Threshold
+ {
+ Threshold(F32 value, const LLUIColor& color)
+ : mValue(value),
+ mColor(color)
+ {}
+
+ F32 mValue;
+ LLUIColor mColor;
+ bool operator <(const Threshold& other) const
+ {
+ 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 bcc22431d0..c2da7b5053 100644
--- a/indra/llui/llstatview.cpp
+++ b/indra/llui/llstatview.cpp
@@ -1,64 +1,64 @@
-/**
- * @file llstatview.cpp
- * @brief Container for all statistics info.
- *
- * $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 "llstatview.h"
-
-#include "llerror.h"
-#include "llstatbar.h"
-#include "llfontgl.h"
-#include "llgl.h"
-#include "llui.h"
-
-#include "llstatbar.h"
-
-LLStatView::LLStatView(const LLStatView::Params& p)
-: LLContainerView(p),
- mSetting(p.setting)
-{
- bool isopen = getDisplayChildren();
- if (mSetting.length() > 0)
- {
- isopen = LLUI::getInstance()->mSettingGroups["config"]->getBOOL(mSetting);
- }
- setDisplayChildren(isopen);
-}
-
-LLStatView::~LLStatView()
-{
- // Children all cleaned up by default view destructor.
- if (mSetting.length() > 0)
- {
- bool isopen = getDisplayChildren();
- LLUI::getInstance()->mSettingGroups["config"]->setBOOL(mSetting, isopen);
- }
-}
-
-static StatViewRegistry::Register<LLStatBar> r1("stat_bar");
-static StatViewRegistry::Register<LLStatView> r2("stat_view");
-// stat_view can be a child of panels/etc.
-static LLDefaultChildRegistry::Register<LLStatView> r3("stat_view");
+/**
+ * @file llstatview.cpp
+ * @brief Container for all statistics info.
+ *
+ * $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 "llstatview.h"
+
+#include "llerror.h"
+#include "llstatbar.h"
+#include "llfontgl.h"
+#include "llgl.h"
+#include "llui.h"
+
+#include "llstatbar.h"
+
+LLStatView::LLStatView(const LLStatView::Params& p)
+: LLContainerView(p),
+ mSetting(p.setting)
+{
+ bool isopen = getDisplayChildren();
+ if (mSetting.length() > 0)
+ {
+ isopen = LLUI::getInstance()->mSettingGroups["config"]->getBOOL(mSetting);
+ }
+ setDisplayChildren(isopen);
+}
+
+LLStatView::~LLStatView()
+{
+ // Children all cleaned up by default view destructor.
+ if (mSetting.length() > 0)
+ {
+ bool isopen = getDisplayChildren();
+ LLUI::getInstance()->mSettingGroups["config"]->setBOOL(mSetting, isopen);
+ }
+}
+
+static StatViewRegistry::Register<LLStatBar> r1("stat_bar");
+static StatViewRegistry::Register<LLStatView> r2("stat_view");
+// stat_view can be a child of panels/etc.
+static LLDefaultChildRegistry::Register<LLStatView> r3("stat_view");
diff --git a/indra/llui/llstatview.h b/indra/llui/llstatview.h
index 044f0a8679..b5187f886d 100644
--- a/indra/llui/llstatview.h
+++ b/indra/llui/llstatview.h
@@ -1,25 +1,25 @@
-/**
+/**
* @file llstatview.h
* @brief Container for all statistics info.
*
* $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$
*/
@@ -36,34 +36,34 @@ class LLStatBar;
// widget registrars
struct StatViewRegistry : public LLChildRegistry<StatViewRegistry>
{
- LLSINGLETON_EMPTY_CTOR(StatViewRegistry);
+ LLSINGLETON_EMPTY_CTOR(StatViewRegistry);
};
class LLStatView : public LLContainerView
{
public:
- struct Params : public LLInitParam::Block<Params, LLContainerView::Params>
- {
- Optional<std::string> setting;
- Params()
- : setting("setting")
- {
- changeDefault(follows.flags, FOLLOWS_TOP | FOLLOWS_LEFT | FOLLOWS_RIGHT);
- changeDefault(show_label, true);
- }
- };
+ struct Params : public LLInitParam::Block<Params, LLContainerView::Params>
+ {
+ Optional<std::string> setting;
+ Params()
+ : setting("setting")
+ {
+ changeDefault(follows.flags, FOLLOWS_TOP | FOLLOWS_LEFT | FOLLOWS_RIGHT);
+ changeDefault(show_label, true);
+ }
+ };
- // my valid children are stored in this registry
- typedef StatViewRegistry child_registry_t;
+ // my valid children are stored in this registry
+ typedef StatViewRegistry child_registry_t;
- ~LLStatView();
+ ~LLStatView();
protected:
- LLStatView(const Params&);
- friend class LLUICtrlFactory;
+ LLStatView(const Params&);
+ friend class LLUICtrlFactory;
protected:
- const std::string mSetting;
+ const std::string mSetting;
};
#endif // LL_STATVIEW_
diff --git a/indra/llui/llstyle.cpp b/indra/llui/llstyle.cpp
index 5b8b9a4e35..19d98824c1 100644
--- a/indra/llui/llstyle.cpp
+++ b/indra/llui/llstyle.cpp
@@ -1,104 +1,104 @@
-/**
- * @file llstyle.cpp
- * @brief Text style class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llstyle.h"
-
-#include "llfontgl.h"
-#include "llstring.h"
-#include "llui.h"
-
-LLStyle::Params::Params()
-: visible("visible", true),
- drop_shadow("drop_shadow", LLFontGL::NO_SHADOW),
- color("color", LLColor4::black),
- readonly_color("readonly_color", LLColor4::black),
- selected_color("selected_color", LLColor4::black),
- font("font", LLFontGL::getFontMonospace()),
- image("image"),
- link_href("href"),
- is_link("is_link")
-{}
-
-
-LLStyle::LLStyle(const LLStyle::Params& p)
-: mVisible(p.visible),
- mColor(p.color),
- mReadOnlyColor(p.readonly_color),
- mSelectedColor(p.selected_color),
- mFont(p.font()),
- mLink(p.link_href),
- mIsLink(p.is_link.isProvided() ? p.is_link : !p.link_href().empty()),
- mDropShadow(p.drop_shadow),
- mImagep(p.image())
-{}
-
-void LLStyle::setFont(const LLFontGL* font)
-{
- mFont = font;
-}
-
-
-const LLFontGL* LLStyle::getFont() const
-{
- return mFont;
-}
-
-void LLStyle::setLinkHREF(const std::string& href)
-{
- mLink = href;
-}
-
-bool LLStyle::isLink() const
-{
- return mIsLink;
-}
-
-bool LLStyle::isVisible() const
-{
- return mVisible;
-}
-
-void LLStyle::setVisible(bool is_visible)
-{
- mVisible = is_visible;
-}
-
-LLPointer<LLUIImage> LLStyle::getImage() const
-{
- return mImagep;
-}
-
-void LLStyle::setImage(const LLUUID& src)
-{
- mImagep = LLUI::getUIImageByID(src);
-}
-
-void LLStyle::setImage(const std::string& name)
-{
- mImagep = LLUI::getUIImage(name);
-}
+/**
+ * @file llstyle.cpp
+ * @brief Text style class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llstyle.h"
+
+#include "llfontgl.h"
+#include "llstring.h"
+#include "llui.h"
+
+LLStyle::Params::Params()
+: visible("visible", true),
+ drop_shadow("drop_shadow", LLFontGL::NO_SHADOW),
+ color("color", LLColor4::black),
+ readonly_color("readonly_color", LLColor4::black),
+ selected_color("selected_color", LLColor4::black),
+ font("font", LLFontGL::getFontMonospace()),
+ image("image"),
+ link_href("href"),
+ is_link("is_link")
+{}
+
+
+LLStyle::LLStyle(const LLStyle::Params& p)
+: mVisible(p.visible),
+ mColor(p.color),
+ mReadOnlyColor(p.readonly_color),
+ mSelectedColor(p.selected_color),
+ mFont(p.font()),
+ mLink(p.link_href),
+ mIsLink(p.is_link.isProvided() ? p.is_link : !p.link_href().empty()),
+ mDropShadow(p.drop_shadow),
+ mImagep(p.image())
+{}
+
+void LLStyle::setFont(const LLFontGL* font)
+{
+ mFont = font;
+}
+
+
+const LLFontGL* LLStyle::getFont() const
+{
+ return mFont;
+}
+
+void LLStyle::setLinkHREF(const std::string& href)
+{
+ mLink = href;
+}
+
+bool LLStyle::isLink() const
+{
+ return mIsLink;
+}
+
+bool LLStyle::isVisible() const
+{
+ return mVisible;
+}
+
+void LLStyle::setVisible(bool is_visible)
+{
+ mVisible = is_visible;
+}
+
+LLPointer<LLUIImage> LLStyle::getImage() const
+{
+ return mImagep;
+}
+
+void LLStyle::setImage(const LLUUID& src)
+{
+ mImagep = LLUI::getUIImageByID(src);
+}
+
+void LLStyle::setImage(const std::string& name)
+{
+ mImagep = LLUI::getUIImage(name);
+}
diff --git a/indra/llui/llstyle.h b/indra/llui/llstyle.h
index 906df1762a..57b839e38d 100644
--- a/indra/llui/llstyle.h
+++ b/indra/llui/llstyle.h
@@ -1,118 +1,118 @@
-/**
- * @file llstyle.h
- * @brief Text style 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$
- */
-
-#ifndef LL_LLSTYLE_H
-#define LL_LLSTYLE_H
-
-#include "v4color.h"
-#include "llui.h"
-#include "llinitparam.h"
-#include "lluiimage.h"
-
-class LLFontGL;
-
-class LLStyle : public LLRefCount
-{
-public:
- struct Params : public LLInitParam::Block<Params>
- {
- Optional<bool> visible;
- Optional<LLFontGL::ShadowType> drop_shadow;
- Optional<LLUIColor> color,
- readonly_color,
- selected_color;
- Optional<const LLFontGL*> font;
- Optional<LLUIImage*> image;
- Optional<std::string> link_href;
- Optional<bool> is_link;
- Params();
- };
- LLStyle(const Params& p = Params());
-public:
- const LLUIColor& getColor() const { return mColor; }
- void setColor(const LLUIColor &color) { mColor = color; }
-
- const LLUIColor& getReadOnlyColor() const { return mReadOnlyColor; }
- void setReadOnlyColor(const LLUIColor& color) { mReadOnlyColor = color; }
-
- const LLUIColor& getSelectedColor() const { return mSelectedColor; }
- void setSelectedColor(const LLUIColor& color) { mSelectedColor = color; }
-
- bool isVisible() const;
- void setVisible(bool is_visible);
-
- LLFontGL::ShadowType getShadowType() const { return mDropShadow; }
-
- void setFont(const LLFontGL* font);
- const LLFontGL* getFont() const;
-
- const std::string& getLinkHREF() const { return mLink; }
- void setLinkHREF(const std::string& href);
- bool isLink() const;
-
- LLPointer<LLUIImage> getImage() const;
- void setImage(const LLUUID& src);
- void setImage(const std::string& name);
-
- bool isImage() const { return mImagep.notNull(); }
-
- bool operator==(const LLStyle &rhs) const
- {
- return
- mVisible == rhs.mVisible
- && mColor == rhs.mColor
- && mReadOnlyColor == rhs.mReadOnlyColor
- && mSelectedColor == rhs.mSelectedColor
- && mFont == rhs.mFont
- && mLink == rhs.mLink
- && mImagep == rhs.mImagep
- && mDropShadow == rhs.mDropShadow;
- }
-
- bool operator!=(const LLStyle& rhs) const { return !(*this == rhs); }
-
-public:
- LLFontGL::ShadowType mDropShadow;
-
-protected:
- ~LLStyle() { }
-
-private:
- bool mVisible;
- LLUIColor mColor;
- LLUIColor mReadOnlyColor;
- LLUIColor mSelectedColor;
- std::string mFontName;
- const LLFontGL* mFont;
- std::string mLink;
- bool mIsLink;
- LLPointer<LLUIImage> mImagep;
-};
-
-typedef LLPointer<LLStyle> LLStyleSP;
-typedef LLPointer<const LLStyle> LLStyleConstSP;
-
-#endif // LL_LLSTYLE_H
+/**
+ * @file llstyle.h
+ * @brief Text style 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$
+ */
+
+#ifndef LL_LLSTYLE_H
+#define LL_LLSTYLE_H
+
+#include "v4color.h"
+#include "llui.h"
+#include "llinitparam.h"
+#include "lluiimage.h"
+
+class LLFontGL;
+
+class LLStyle : public LLRefCount
+{
+public:
+ struct Params : public LLInitParam::Block<Params>
+ {
+ Optional<bool> visible;
+ Optional<LLFontGL::ShadowType> drop_shadow;
+ Optional<LLUIColor> color,
+ readonly_color,
+ selected_color;
+ Optional<const LLFontGL*> font;
+ Optional<LLUIImage*> image;
+ Optional<std::string> link_href;
+ Optional<bool> is_link;
+ Params();
+ };
+ LLStyle(const Params& p = Params());
+public:
+ const LLUIColor& getColor() const { return mColor; }
+ void setColor(const LLUIColor &color) { mColor = color; }
+
+ const LLUIColor& getReadOnlyColor() const { return mReadOnlyColor; }
+ void setReadOnlyColor(const LLUIColor& color) { mReadOnlyColor = color; }
+
+ const LLUIColor& getSelectedColor() const { return mSelectedColor; }
+ void setSelectedColor(const LLUIColor& color) { mSelectedColor = color; }
+
+ bool isVisible() const;
+ void setVisible(bool is_visible);
+
+ LLFontGL::ShadowType getShadowType() const { return mDropShadow; }
+
+ void setFont(const LLFontGL* font);
+ const LLFontGL* getFont() const;
+
+ const std::string& getLinkHREF() const { return mLink; }
+ void setLinkHREF(const std::string& href);
+ bool isLink() const;
+
+ LLPointer<LLUIImage> getImage() const;
+ void setImage(const LLUUID& src);
+ void setImage(const std::string& name);
+
+ bool isImage() const { return mImagep.notNull(); }
+
+ bool operator==(const LLStyle &rhs) const
+ {
+ return
+ mVisible == rhs.mVisible
+ && mColor == rhs.mColor
+ && mReadOnlyColor == rhs.mReadOnlyColor
+ && mSelectedColor == rhs.mSelectedColor
+ && mFont == rhs.mFont
+ && mLink == rhs.mLink
+ && mImagep == rhs.mImagep
+ && mDropShadow == rhs.mDropShadow;
+ }
+
+ bool operator!=(const LLStyle& rhs) const { return !(*this == rhs); }
+
+public:
+ LLFontGL::ShadowType mDropShadow;
+
+protected:
+ ~LLStyle() { }
+
+private:
+ bool mVisible;
+ LLUIColor mColor;
+ LLUIColor mReadOnlyColor;
+ LLUIColor mSelectedColor;
+ std::string mFontName;
+ const LLFontGL* mFont;
+ std::string mLink;
+ bool mIsLink;
+ LLPointer<LLUIImage> mImagep;
+};
+
+typedef LLPointer<LLStyle> LLStyleSP;
+typedef LLPointer<const LLStyle> LLStyleConstSP;
+
+#endif // LL_LLSTYLE_H
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index 772dcdf119..a55e244711 100644
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -1,2203 +1,2204 @@
-/**
- * @file lltabcontainer.cpp
- * @brief LLTabContainer class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "lltabcontainer.h"
-#include "llviewereventrecorder.h"
-#include "llfocusmgr.h"
-#include "lllocalcliprect.h"
-#include "llrect.h"
-#include "llresizehandle.h"
-#include "lltextbox.h"
-#include "llcriticaldamp.h"
-#include "lluictrlfactory.h"
-#include "llrender.h"
-#include "llfloater.h"
-#include "lltrans.h"
-#include "lluiusage.h"
-
-//----------------------------------------------------------------------------
-
-// Implementation Notes:
-// - Each tab points to a LLPanel (see LLTabTuple below)
-// - When a tab is selected, the validation callback
-// (LLUICtrl::mValidateSignal) is called
-// - If the validation callback returns true (or none is provided),
-// the tab is changed and the commit callback
-// (LLUICtrl::mCommitSignal) is called
-// - Callbacks pass the LLTabContainer as the control,
-// and the NAME of the selected PANEL as the LLSD data
-
-//----------------------------------------------------------------------------
-
-const F32 SCROLL_STEP_TIME = 0.4f;
-const F32 SCROLL_DELAY_TIME = 0.5f;
-
-void LLTabContainer::TabPositions::declareValues()
-{
- declare("top", LLTabContainer::TOP);
- declare("bottom", LLTabContainer::BOTTOM);
- declare("left", LLTabContainer::LEFT);
-}
-
-//----------------------------------------------------------------------------
-
-// Structure used to map tab buttons to and from tab panels
-class LLTabTuple
-{
-public:
- LLTabTuple( LLTabContainer* c, LLPanel* p, LLButton* b, LLTextBox* placeholder = NULL)
- :
- mTabContainer(c),
- mTabPanel(p),
- mButton(b),
- mOldState(false),
- mPlaceholderText(placeholder),
- mPadding(0),
- mVisible(true)
- {}
-
- LLTabContainer* mTabContainer;
- LLPanel* mTabPanel;
- LLButton* mButton;
- bool mOldState;
- LLTextBox* mPlaceholderText;
- S32 mPadding;
-
- mutable bool mVisible;
-};
-
-//----------------------------------------------------------------------------
-
-//============================================================================
-/*
- * @file lltabcontainer.cpp
- * @brief class implements LLButton with LLIconCtrl on it
- */
-class LLCustomButtonIconCtrl : public LLButton
-{
-public:
- struct Params
- : public LLInitParam::Block<Params, LLButton::Params>
- {
- // LEFT, RIGHT, TOP, BOTTOM paddings of LLIconCtrl in this class has same value
- Optional<S32> icon_ctrl_pad;
-
- Params()
- : icon_ctrl_pad("icon_ctrl_pad", 1)
- {}
- };
-
-protected:
- friend class LLUICtrlFactory;
-
- LLCustomButtonIconCtrl(const Params& p)
- : LLButton(p),
- mIcon(NULL),
- mIconAlignment(LLFontGL::HCENTER),
- mIconCtrlPad(p.icon_ctrl_pad)
- {}
-
-public:
-
- void updateLayout()
- {
- LLRect button_rect = getRect();
- LLRect icon_rect = mIcon->getRect();
-
- S32 icon_size = button_rect.getHeight() - 2*mIconCtrlPad;
-
- switch(mIconAlignment)
- {
- case LLFontGL::LEFT:
- icon_rect.setLeftTopAndSize(button_rect.mLeft + mIconCtrlPad, button_rect.mTop - mIconCtrlPad,
- icon_size, icon_size);
- setLeftHPad(icon_size + mIconCtrlPad * 2);
- break;
- case LLFontGL::HCENTER:
- icon_rect.setLeftTopAndSize(button_rect.mRight - (button_rect.getWidth() + mIconCtrlPad - icon_size)/2, button_rect.mTop - mIconCtrlPad,
- icon_size, icon_size);
- setRightHPad(icon_size + mIconCtrlPad * 2);
- break;
- case LLFontGL::RIGHT:
- icon_rect.setLeftTopAndSize(button_rect.mRight - mIconCtrlPad - icon_size, button_rect.mTop - mIconCtrlPad,
- icon_size, icon_size);
- setRightHPad(icon_size + mIconCtrlPad * 2);
- break;
- default:
- break;
- }
- mIcon->setRect(icon_rect);
- }
-
- void setIcon(LLIconCtrl* icon, LLFontGL::HAlign alignment = LLFontGL::LEFT)
- {
- if(icon)
- {
- if(mIcon)
- {
- removeChild(mIcon);
- mIcon->die();
- }
- mIcon = icon;
- mIconAlignment = alignment;
-
- addChild(mIcon);
- updateLayout();
- }
- }
-
- LLIconCtrl* getIconCtrl() const
- {
- return mIcon;
- }
-
-private:
- LLIconCtrl* mIcon;
- LLFontGL::HAlign mIconAlignment;
- S32 mIconCtrlPad;
-};
-//============================================================================
-
-struct LLPlaceHolderPanel : public LLPanel
-{
- // create dummy param block to register with "placeholder" nane
- struct Params : public LLPanel::Params{};
- LLPlaceHolderPanel(const Params& p) : LLPanel(p)
- {}
-};
-static LLDefaultChildRegistry::Register<LLPlaceHolderPanel> r1("placeholder");
-static LLDefaultChildRegistry::Register<LLTabContainer> r2("tab_container");
-
-LLTabContainer::TabParams::TabParams()
-: tab_top_image_unselected("tab_top_image_unselected"),
- tab_top_image_selected("tab_top_image_selected"),
- tab_top_image_flash("tab_top_image_flash"),
- tab_bottom_image_unselected("tab_bottom_image_unselected"),
- tab_bottom_image_selected("tab_bottom_image_selected"),
- tab_bottom_image_flash("tab_bottom_image_flash"),
- tab_left_image_unselected("tab_left_image_unselected"),
- tab_left_image_selected("tab_left_image_selected"),
- tab_left_image_flash("tab_left_image_flash")
-{}
-
-LLTabContainer::Params::Params()
-: tab_width("tab_width"),
- tab_min_width("tab_min_width"),
- tab_max_width("tab_max_width"),
- tab_height("tab_height"),
- label_pad_bottom("label_pad_bottom"),
- label_pad_left("label_pad_left"),
- tab_position("tab_position"),
- hide_tabs("hide_tabs", false),
- hide_scroll_arrows("hide_scroll_arrows", false),
- tab_padding_right("tab_padding_right"),
- first_tab("first_tab"),
- middle_tab("middle_tab"),
- last_tab("last_tab"),
- use_custom_icon_ctrl("use_custom_icon_ctrl", false),
- open_tabs_on_drag_and_drop("open_tabs_on_drag_and_drop", false),
- enable_tabs_flashing("enable_tabs_flashing", false),
- tabs_flashing_color("tabs_flashing_color"),
- tab_icon_ctrl_pad("tab_icon_ctrl_pad", 0),
- use_ellipses("use_ellipses"),
- font_halign("halign")
-{}
-
-LLTabContainer::LLTabContainer(const LLTabContainer::Params& p)
-: LLPanel(p),
- mCurrentTabIdx(-1),
- mTabsHidden(p.hide_tabs),
- mScrolled(false),
- mScrollPos(0),
- mScrollPosPixels(0),
- mMaxScrollPos(0),
- mTitleBox(NULL),
- mTopBorderHeight(LLPANEL_BORDER_WIDTH),
- mLockedTabCount(0),
- mMinTabWidth(0),
- mMaxTabWidth(p.tab_max_width),
- mTabHeight(p.tab_height),
- mLabelPadBottom(p.label_pad_bottom),
- mLabelPadLeft(p.label_pad_left),
- mPrevArrowBtn(NULL),
- mNextArrowBtn(NULL),
- mIsVertical( p.tab_position == LEFT ),
- mHideScrollArrows(p.hide_scroll_arrows),
- // Horizontal Specific
- mJumpPrevArrowBtn(NULL),
- mJumpNextArrowBtn(NULL),
- mRightTabBtnOffset(p.tab_padding_right),
- mTotalTabWidth(0),
- mTabPosition(p.tab_position),
- mFontHalign(p.font_halign),
- mFont(p.font),
- mFirstTabParams(p.first_tab),
- mMiddleTabParams(p.middle_tab),
- mLastTabParams(p.last_tab),
- mCustomIconCtrlUsed(p.use_custom_icon_ctrl),
- mOpenTabsOnDragAndDrop(p.open_tabs_on_drag_and_drop),
- mTabIconCtrlPad(p.tab_icon_ctrl_pad),
- mEnableTabsFlashing(p.enable_tabs_flashing),
- mTabsFlashingColor(p.tabs_flashing_color),
- mUseTabEllipses(p.use_ellipses)
-{
- static LLUICachedControl<S32> tabcntr_vert_tab_min_width ("UITabCntrVertTabMinWidth", 0);
-
- mDragAndDropDelayTimer.stop();
-
- if (p.tab_width.isProvided())
- {
- mMinTabWidth = p.tab_width;
- }
- else if (!mIsVertical)
- {
- mMinTabWidth = p.tab_min_width;
- }
- else
- {
- // *HACK: support default min width for legacy vertical
- // tab containers
- mMinTabWidth = tabcntr_vert_tab_min_width;
- }
-
- if (p.tabs_flashing_color.isProvided())
- {
- mEnableTabsFlashing = true;
- }
-
- initButtons( );
-}
-
-LLTabContainer::~LLTabContainer()
-{
- std::for_each(mTabList.begin(), mTabList.end(), DeletePointer());
- mTabList.clear();
-}
-
-//virtual
-void LLTabContainer::setValue(const LLSD& value)
-{
- selectTab((S32) value.asInteger());
-}
-
-//virtual
-void LLTabContainer::reshape(S32 width, S32 height, bool called_from_parent)
-{
- LLPanel::reshape( width, height, called_from_parent );
- updateMaxScrollPos();
-}
-
-//virtual
-LLView* LLTabContainer::getChildView(const std::string& name, bool recurse) const
-{
- tuple_list_t::const_iterator itor;
- for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
- {
- LLPanel *panel = (*itor)->mTabPanel;
- if (panel->getName() == name)
- {
- return panel;
- }
- }
-
- if (recurse)
- {
- for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
- {
- LLPanel *panel = (*itor)->mTabPanel;
- LLView *child = panel->getChildView(name, recurse);
- if (child)
- {
- return child;
- }
- }
- }
- return LLView::getChildView(name, recurse);
-}
-
-//virtual
-LLView* LLTabContainer::findChildView(const std::string& name, bool recurse) const
-{
- tuple_list_t::const_iterator itor;
- for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
- {
- LLPanel *panel = (*itor)->mTabPanel;
- if (panel->getName() == name)
- {
- return panel;
- }
- }
-
- if (recurse)
- {
- for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
- {
- LLPanel *panel = (*itor)->mTabPanel;
- LLView *child = panel->findChildView(name, recurse);
- if (child)
- {
- return child;
- }
- }
- }
- return LLView::findChildView(name, recurse);
-}
-
-bool LLTabContainer::addChild(LLView* view, S32 tab_group)
-{
- LLPanel* panelp = dynamic_cast<LLPanel*>(view);
-
- if (panelp)
- {
- addTabPanel(TabPanelParams().panel(panelp).label(panelp->getLabel()).is_placeholder(dynamic_cast<LLPlaceHolderPanel*>(view) != NULL));
- return true;
- }
- else
- {
- return LLUICtrl::addChild(view, tab_group);
- }
-}
-
-bool LLTabContainer::postBuild()
-{
- selectFirstTab();
-
- return true;
-}
-
-// virtual
-void LLTabContainer::draw()
-{
- static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
- static LLUICachedControl<S32> tabcntrv_arrow_btn_size ("UITabCntrvArrowBtnSize", 0);
- static LLUICachedControl<S32> tabcntr_tab_h_pad ("UITabCntrTabHPad", 0);
- static LLUICachedControl<S32> tabcntr_arrow_btn_size ("UITabCntrArrowBtnSize", 0);
- static LLUICachedControl<S32> tabcntr_tab_partial_width ("UITabCntrTabPartialWidth", 0);
- S32 target_pixel_scroll = 0;
- S32 cur_scroll_pos = getScrollPos();
- if (cur_scroll_pos > 0)
- {
- if (mIsVertical)
- {
- target_pixel_scroll = cur_scroll_pos * (BTN_HEIGHT + tabcntrv_pad);
- }
- else
- {
- S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_arrow_btn_size + tabcntr_arrow_btn_size + 1);
- for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
- {
- if (cur_scroll_pos == 0)
- {
- break;
- }
-
- if( (*iter)->mVisible )
- target_pixel_scroll += (*iter)->mButton->getRect().getWidth();
-
- cur_scroll_pos--;
- }
-
- // Show part of the tab to the left of what is fully visible
- target_pixel_scroll -= tabcntr_tab_partial_width;
- // clamp so that rightmost tab never leaves right side of screen
- target_pixel_scroll = llmin(mTotalTabWidth - available_width_with_arrows, target_pixel_scroll);
- }
- }
-
- setScrollPosPixels((S32)lerp((F32)getScrollPosPixels(), (F32)target_pixel_scroll, LLSmoothInterpolation::getInterpolant(0.08f)));
-
- bool has_scroll_arrows = !mHideScrollArrows && !getTabsHidden() && ((mMaxScrollPos > 0) || (mScrollPosPixels > 0));
- if (!mIsVertical)
- {
- mJumpPrevArrowBtn->setVisible( has_scroll_arrows );
- mJumpNextArrowBtn->setVisible( has_scroll_arrows );
- }
- mPrevArrowBtn->setVisible( has_scroll_arrows );
- mNextArrowBtn->setVisible( has_scroll_arrows );
-
- S32 left = 0, top = 0;
- if (mIsVertical)
- {
- top = getRect().getHeight() - getTopBorderHeight() - LLPANEL_BORDER_WIDTH - 1 - (has_scroll_arrows ? tabcntrv_arrow_btn_size : 0);
- top += getScrollPosPixels();
- }
- else
- {
- // Set the leftmost position of the tab buttons.
- left = LLPANEL_BORDER_WIDTH + (has_scroll_arrows ? (tabcntr_arrow_btn_size * 2) : tabcntr_tab_h_pad);
- left -= getScrollPosPixels();
- }
-
- // Hide all the buttons
- if (getTabsHidden())
- {
- for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
- {
- LLTabTuple* tuple = *iter;
- tuple->mButton->setVisible( false );
- }
- }
-
- {
- LLRect clip_rect = getLocalRect();
- clip_rect.mLeft+=(LLPANEL_BORDER_WIDTH + 2);
- clip_rect.mRight-=(LLPANEL_BORDER_WIDTH + 2);
- LLLocalClipRect clip(clip_rect);
- LLPanel::draw();
- }
-
- // if tabs are hidden, don't draw them and leave them in the invisible state
- if (!getTabsHidden())
- {
- // Show all the buttons
- for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
- {
- LLTabTuple* tuple = *iter;
- tuple->mButton->setVisible( true );
- }
-
- S32 max_scroll_visible = getTabCount() - getMaxScrollPos() + getScrollPos();
- S32 idx = 0;
- for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
- {
- LLTabTuple* tuple = *iter;
-
- if( !tuple->mVisible )
- {
- tuple->mButton->setVisible( false );
- continue;
- }
-
- tuple->mButton->translate( left ? left - tuple->mButton->getRect().mLeft : 0,
- top ? top - tuple->mButton->getRect().mTop : 0 );
- if (top) top -= BTN_HEIGHT + tabcntrv_pad;
- if (left) left += tuple->mButton->getRect().getWidth();
-
- if (!mIsVertical)
- {
- if( idx < getScrollPos() )
- {
- if( tuple->mButton->getFlashing() )
- {
- mPrevArrowBtn->setFlashing( true );
- }
- }
- else if( max_scroll_visible < idx )
- {
- if( tuple->mButton->getFlashing() )
- {
- mNextArrowBtn->setFlashing( true );
- }
- }
- }
-
- idx++;
- }
-
-
- if( mIsVertical && has_scroll_arrows )
- {
- // Redraw the arrows so that they appears on top.
- gGL.pushUIMatrix();
- gGL.translateUI((F32)mPrevArrowBtn->getRect().mLeft, (F32)mPrevArrowBtn->getRect().mBottom, 0.f);
- mPrevArrowBtn->draw();
- gGL.popUIMatrix();
-
- gGL.pushUIMatrix();
- gGL.translateUI((F32)mNextArrowBtn->getRect().mLeft, (F32)mNextArrowBtn->getRect().mBottom, 0.f);
- mNextArrowBtn->draw();
- gGL.popUIMatrix();
- }
- }
-
- mPrevArrowBtn->setFlashing(false);
- mNextArrowBtn->setFlashing(false);
-}
-
-
-// virtual
-bool LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
-{
- static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
- bool handled = false;
- bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0) && !getTabsHidden();
-
- 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;
- handled = mJumpPrevArrowBtn->handleMouseDown(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;
- handled = mJumpNextArrowBtn->handleMouseDown(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;
- handled = mPrevArrowBtn->handleMouseDown(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;
- handled = mNextArrowBtn->handleMouseDown(local_x, local_y, mask);
- }
- }
- if (!handled)
- {
- handled = LLPanel::handleMouseDown( x, y, mask );
- }
-
- S32 tab_count = getTabCount();
- 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 );
- }
- 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 );
- }
- if( tab_rect.pointInRect( x, y ) )
- {
- S32 index = getCurrentPanelIndex();
- index = llclamp(index, 0, tab_count-1);
- LLButton* tab_button = getTab(index)->mButton;
- gFocusMgr.setMouseCapture(this);
- tab_button->setFocus(true);
- mMouseDownTimer.start();
- }
- }
- if (handled) {
- // Note: May need to also capture local coords right here ?
- LLViewerEventRecorder::instance().update_xui(getPathname( ));
- }
-
- return handled;
-}
-
-// virtual
-bool LLTabContainer::handleHover( S32 x, S32 y, MASK mask )
-{
- bool handled = false;
- bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0) && !getTabsHidden();
-
- 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;
- handled = mJumpPrevArrowBtn->handleHover(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;
- handled = mJumpNextArrowBtn->handleHover(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;
- handled = mPrevArrowBtn->handleHover(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;
- handled = mNextArrowBtn->handleHover(local_x, local_y, mask);
- }
- }
- if (!handled)
- {
- handled = LLPanel::handleHover(x, y, mask);
- }
-
- F32 drag_delay = 0.25f; // filter out clicks from dragging
- if (mMouseDownTimer.getElapsedTimeF32() > drag_delay)
- {
- commitHoveredButton(x, y);
- }
- return handled;
-}
-
-// virtual
-bool LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
-{
- bool handled = false;
- bool has_scroll_arrows = !mHideScrollArrows && (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))
- {
- 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))
- {
- 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))
- {
- 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))
- {
- local_x = x - mNextArrowBtn->getRect().mLeft;
- local_y = y - mNextArrowBtn->getRect().mBottom;
- handled = mNextArrowBtn->handleMouseUp(local_x, local_y, mask);
- }
- }
- if (!handled)
- {
- handled = LLPanel::handleMouseUp( x, y, mask );
- }
-
- commitHoveredButton(x, y);
- mMouseDownTimer.stop();
- LLPanel* cur_panel = getCurrentPanel();
- if (hasMouseCapture())
- {
- if (cur_panel)
- {
- if (!cur_panel->focusFirstItem(false))
- {
- // if nothing in the panel gets focus, make sure the new tab does
- // otherwise the last tab might keep focus
- getTab(getCurrentPanelIndex())->mButton->setFocus(true);
- }
- }
- gFocusMgr.setMouseCapture(NULL);
- }
- if (handled) {
- // Note: may need to capture local coords here
- LLViewerEventRecorder::instance().update_xui(getPathname( ));
- }
- return handled;
-}
-
-// virtual
-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 && !getTabsHidden())
- {
- LLTabTuple* firsttuple = getTab(0);
-
- bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0);
- LLRect clip;
- if (mIsVertical)
- {
- clip = 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 );
- }
- else
- {
- clip = 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 );
- }
-
- if( clip.pointInRect( x, y ) )
- {
- for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
- {
- LLButton* tab_button = (*iter)->mButton;
- if (!tab_button->getVisible()) continue;
- S32 local_x = x - tab_button->getRect().mLeft;
- S32 local_y = y - tab_button->getRect().mBottom;
- handled = tab_button->handleToolTip(local_x, local_y, mask);
- if( handled )
- {
- break;
- }
- }
- }
- }
- return handled;
-}
-
-// virtual
-bool LLTabContainer::handleKeyHere(KEY key, MASK mask)
-{
- bool handled = false;
- if (key == KEY_LEFT && mask == MASK_ALT)
- {
- selectPrevTab();
- handled = true;
- }
- else if (key == KEY_RIGHT && mask == MASK_ALT)
- {
- selectNextTab();
- handled = true;
- }
-
- if (handled)
- {
- if (getCurrentPanel())
- {
- getCurrentPanel()->setFocus(true);
- }
- }
-
- if (!gFocusMgr.childHasKeyboardFocus(getCurrentPanel()))
- {
- // if child has focus, but not the current panel, focus is on a button
- if (mIsVertical)
- {
- switch(key)
- {
- case KEY_UP:
- selectPrevTab();
- handled = true;
- break;
- case KEY_DOWN:
- selectNextTab();
- handled = true;
- break;
- case KEY_LEFT:
- handled = true;
- break;
- case KEY_RIGHT:
- if (getTabPosition() == LEFT && getCurrentPanel())
- {
- getCurrentPanel()->setFocus(true);
- }
- handled = true;
- break;
- default:
- break;
- }
- }
- else
- {
- switch(key)
- {
- case KEY_UP:
- if (getTabPosition() == BOTTOM && getCurrentPanel())
- {
- getCurrentPanel()->setFocus(true);
- }
- handled = true;
- break;
- case KEY_DOWN:
- if (getTabPosition() == TOP && getCurrentPanel())
- {
- getCurrentPanel()->setFocus(true);
- }
- handled = true;
- break;
- case KEY_LEFT:
- selectPrevTab();
- handled = true;
- break;
- case KEY_RIGHT:
- selectNextTab();
- handled = true;
- break;
- default:
- break;
- }
- }
- }
- return handled;
-}
-
-// virtual
-bool LLTabContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType type, void* cargo_data, EAcceptance *accept, std::string &tooltip)
-{
- bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0);
-
- if(mOpenTabsOnDragAndDrop && !getTabsHidden())
- {
- // In that case, we'll open the hovered tab while dragging and dropping items.
- // This allows for drilling through tabs.
- if (mDragAndDropDelayTimer.getStarted())
- {
- if (mDragAndDropDelayTimer.getElapsedTimeF32() > SCROLL_DELAY_TIME)
- {
- 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();
- }
- }
- // 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);
-}
-
-void LLTabContainer::addTabPanel(LLPanel* panelp)
-{
- addTabPanel(TabPanelParams().panel(panelp));
-}
-
-// function to update images
-void LLTabContainer::update_images(LLTabTuple* tuple, TabParams params, LLTabContainer::TabPosition pos)
-{
- if (tuple && tuple->mButton)
- {
- if (pos == LLTabContainer::TOP)
- {
- tuple->mButton->setImageUnselected(static_cast<LLUIImage*>(params.tab_top_image_unselected));
- tuple->mButton->setImageSelected(static_cast<LLUIImage*>(params.tab_top_image_selected));
- tuple->mButton->setImageFlash(static_cast<LLUIImage*>(params.tab_top_image_flash));
- }
- else if (pos == LLTabContainer::BOTTOM)
- {
- tuple->mButton->setImageUnselected(static_cast<LLUIImage*>(params.tab_bottom_image_unselected));
- tuple->mButton->setImageSelected(static_cast<LLUIImage*>(params.tab_bottom_image_selected));
- tuple->mButton->setImageFlash(static_cast<LLUIImage*>(params.tab_bottom_image_flash));
- }
- else if (pos == LLTabContainer::LEFT)
- {
- tuple->mButton->setImageUnselected(static_cast<LLUIImage*>(params.tab_left_image_unselected));
- tuple->mButton->setImageSelected(static_cast<LLUIImage*>(params.tab_left_image_selected));
- tuple->mButton->setImageFlash(static_cast<LLUIImage*>(params.tab_left_image_flash));
- }
- }
-}
-
-void LLTabContainer::addTabPanel(const TabPanelParams& panel)
-{
- LLPanel* child = panel.panel();
-
- llassert(child);
- if (!child) return;
-
- const std::string& label = panel.label.isProvided()
- ? panel.label()
- : panel.panel()->getLabel();
- bool select = panel.select_tab();
- S32 indent = panel.indent();
- bool placeholder = panel.is_placeholder;
- eInsertionPoint insertion_point = panel.insert_at();
-
- static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
- static LLUICachedControl<S32> tabcntr_button_panel_overlap ("UITabCntrButtonPanelOverlap", 0);
- static LLUICachedControl<S32> tab_padding ("UITabPadding", 0);
- if (child->getParent() == this)
- {
- // already a child of mine
- return;
- }
-
- // Store the original label for possible xml export.
- child->setLabel(label);
- std::string trimmed_label = label;
- LLStringUtil::trim(trimmed_label);
-
- S32 button_width = mMinTabWidth;
- if (!mIsVertical)
- {
- button_width = llclamp(mFont->getWidth(trimmed_label) + tab_padding, mMinTabWidth, mMaxTabWidth);
- }
-
- // Tab panel
- S32 tab_panel_top;
- S32 tab_panel_bottom;
- if (!getTabsHidden())
- {
- if( getTabPosition() == LLTabContainer::TOP )
- {
- S32 tab_height = mIsVertical ? BTN_HEIGHT : mTabHeight;
- tab_panel_top = getRect().getHeight() - getTopBorderHeight() - (tab_height - tabcntr_button_panel_overlap);
- tab_panel_bottom = LLPANEL_BORDER_WIDTH;
- }
- else
- {
- tab_panel_top = getRect().getHeight() - getTopBorderHeight();
- tab_panel_bottom = (mTabHeight - tabcntr_button_panel_overlap); // Run to the edge, covering up the border
- }
- }
- else
- {
- // Skip tab button space if tabs are invisible (EXT-576)
- tab_panel_top = getRect().getHeight();
- tab_panel_bottom = LLPANEL_BORDER_WIDTH;
- }
-
- LLRect tab_panel_rect;
- if (!getTabsHidden() && mIsVertical)
- {
- tab_panel_rect = LLRect(mMinTabWidth + mRightTabBtnOffset + (LLPANEL_BORDER_WIDTH * 2) + tabcntrv_pad,
- getRect().getHeight() - LLPANEL_BORDER_WIDTH,
- getRect().getWidth() - LLPANEL_BORDER_WIDTH,
- LLPANEL_BORDER_WIDTH);
- }
- else
- {
- tab_panel_rect = LLRect(LLPANEL_BORDER_WIDTH * 3,
- tab_panel_top,
- getRect().getWidth() - LLPANEL_BORDER_WIDTH * 2,
- tab_panel_bottom );
- }
- child->setFollowsAll();
- child->translate( tab_panel_rect.mLeft - child->getRect().mLeft, tab_panel_rect.mBottom - child->getRect().mBottom);
- child->reshape( tab_panel_rect.getWidth(), tab_panel_rect.getHeight(), true );
- // add this child later
-
- child->setVisible( false ); // Will be made visible when selected
-
- mTotalTabWidth += button_width;
-
- // Tab button
- LLRect btn_rect; // Note: btn_rect.mLeft is just a dummy. Will be updated in draw().
- LLUIImage* tab_img = NULL;
- LLUIImage* tab_selected_img = NULL;
- S32 tab_fudge = 1; // To make new tab art look better, nudge buttons up 1 pel
-
- if (mIsVertical)
- {
- btn_rect.setLeftTopAndSize(tabcntrv_pad + LLPANEL_BORDER_WIDTH + 2, // JC - Fudge factor
- (getRect().getHeight() - getTopBorderHeight() - LLPANEL_BORDER_WIDTH - 1) - ((BTN_HEIGHT + tabcntrv_pad) * getTabCount()),
- mMinTabWidth,
- BTN_HEIGHT);
- }
- else if( getTabPosition() == LLTabContainer::TOP )
- {
- btn_rect.setLeftTopAndSize( 0, getRect().getHeight() - getTopBorderHeight() + tab_fudge, button_width, mTabHeight);
- tab_img = mMiddleTabParams.tab_top_image_unselected;
- tab_selected_img = mMiddleTabParams.tab_top_image_selected;
- }
- else
- {
- btn_rect.setOriginAndSize( 0, 0 + tab_fudge, button_width, mTabHeight);
- tab_img = mMiddleTabParams.tab_bottom_image_unselected;
- tab_selected_img = mMiddleTabParams.tab_bottom_image_selected;
- }
-
- LLTextBox* textbox = NULL;
- LLButton* btn = NULL;
- LLCustomButtonIconCtrl::Params custom_btn_params;
- {
- custom_btn_params.icon_ctrl_pad(mTabIconCtrlPad);
- }
- LLButton::Params normal_btn_params;
-
- if (placeholder)
- {
- btn_rect.translate(0, -6); // *TODO: make configurable
- LLTextBox::Params params;
- params.name(trimmed_label);
- params.rect(btn_rect);
- params.initial_value(trimmed_label);
- params.font(mFont);
- textbox = LLUICtrlFactory::create<LLTextBox> (params);
-
- LLButton::Params p;
- p.name("placeholder");
- btn = LLUICtrlFactory::create<LLButton>(p);
- }
- 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)
- {
- 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);
- }
-
- // inits flash timer
- p.button_flash_enable = mEnableTabsFlashing;
- p.flash_color = mTabsFlashingColor;
-
- // *TODO : It seems wrong not to use p in both cases considering the way p is initialized
- if (mCustomIconCtrlUsed)
- {
- btn = LLUICtrlFactory::create<LLCustomButtonIconCtrl>(custom_btn_params);
- }
- else
- {
- btn = LLUICtrlFactory::create<LLButton>(p);
- }
- }
-
- LLTabTuple* tuple = new LLTabTuple( this, child, btn, textbox );
- insertTuple( tuple, insertion_point );
-
- // if new tab was added as a first or last tab, update button image
- // and update button image of any tab it may have affected
- if (tuple == mTabList.front())
- {
- update_images(tuple, mFirstTabParams, getTabPosition());
-
- if (mTabList.size() == 2)
- {
- update_images(mTabList[1], mLastTabParams, getTabPosition());
- }
- else if (mTabList.size() > 2)
- {
- update_images(mTabList[1], mMiddleTabParams, getTabPosition());
- }
- }
- else if (tuple == mTabList.back())
- {
- update_images(tuple, mLastTabParams, getTabPosition());
-
- if (mTabList.size() > 2)
- {
- update_images(mTabList[mTabList.size()-2], mMiddleTabParams, getTabPosition());
- }
- }
-
- //Don't add button and textbox if tab buttons are invisible(EXT - 576)
- if (!getTabsHidden())
- {
- if (textbox)
- {
- addChild( textbox, 0 );
- }
- if (btn)
- {
- addChild( btn, 0 );
- }
- }
- else
- {
- if (textbox)
- {
- LLUICtrl::addChild(textbox, 0);
- }
- if (btn)
- {
- LLUICtrl::addChild(btn, 0);
- }
- }
-
- if (child)
- {
- LLUICtrl::addChild(child, 1);
- }
-
- sendChildToFront(mPrevArrowBtn);
- sendChildToFront(mNextArrowBtn);
- sendChildToFront(mJumpPrevArrowBtn);
- sendChildToFront(mJumpNextArrowBtn);
-
- updateMaxScrollPos();
-
- if( select )
- {
- selectLastTab();
- mScrollPos = mMaxScrollPos;
- }
-
-}
-
-void LLTabContainer::addPlaceholder(LLPanel* child, const std::string& label)
-{
- addTabPanel(TabPanelParams().panel(child).label(label).is_placeholder(true));
-}
-
-void LLTabContainer::removeTabPanel(LLPanel* child)
-{
- static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
- if (mIsVertical)
- {
- // Fix-up button sizes
- S32 tab_count = 0;
- for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
- {
- LLTabTuple* tuple = *iter;
- LLRect rect;
- rect.setLeftTopAndSize(tabcntrv_pad + LLPANEL_BORDER_WIDTH + 2, // JC - Fudge factor
- (getRect().getHeight() - LLPANEL_BORDER_WIDTH - 1) - ((BTN_HEIGHT + tabcntrv_pad) * (tab_count)),
- mMinTabWidth,
- BTN_HEIGHT);
- if (tuple->mPlaceholderText)
- {
- tuple->mPlaceholderText->setRect(rect);
- }
- else
- {
- tuple->mButton->setRect(rect);
- }
- tab_count++;
- }
- }
- else
- {
- // Adjust the total tab width.
- for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
- {
- LLTabTuple* tuple = *iter;
- if( tuple->mTabPanel == child )
- {
- mTotalTabWidth -= tuple->mButton->getRect().getWidth();
- break;
- }
- }
- }
-
- bool has_focus = gFocusMgr.childHasKeyboardFocus(this);
-
- // If the tab being deleted is the selected one, select a different tab.
- for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
- {
- LLTabTuple* tuple = *iter;
- if( tuple->mTabPanel == child )
- {
- // update tab button images if removing the first or last tab
- if ((tuple == mTabList.front()) && (mTabList.size() > 1))
- {
- update_images(mTabList[1], mFirstTabParams, getTabPosition());
- }
- else if ((tuple == mTabList.back()) && (mTabList.size() > 2))
- {
- update_images(mTabList[mTabList.size()-2], mLastTabParams, getTabPosition());
- }
-
- 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;
-
- break;
- }
- }
-
- // make sure we don't have more locked tabs than we have tabs
- mLockedTabCount = llmin(getTabCount(), mLockedTabCount);
-
- if (mCurrentTabIdx >= (S32)mTabList.size())
- {
- mCurrentTabIdx = mTabList.size()-1;
- }
- selectTab(mCurrentTabIdx);
- if (has_focus)
- {
- LLPanel* panelp = getPanelByIndex(mCurrentTabIdx);
- if (panelp)
- {
- panelp->setFocus(true);
- }
- }
-
- updateMaxScrollPos();
-}
-
-void LLTabContainer::lockTabs(S32 num_tabs)
-{
- // count current tabs or use supplied value and ensure no new tabs get
- // inserted between them
- mLockedTabCount = num_tabs > 0 ? llmin(getTabCount(), num_tabs) : getTabCount();
-}
-
-void LLTabContainer::unlockTabs()
-{
- mLockedTabCount = 0;
-}
-
-void LLTabContainer::enableTabButton(S32 which, bool enable)
-{
- if (which >= 0 && which < (S32)mTabList.size())
- {
- 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()
-{
- // Remove all the tab buttons and delete them. Also, unlink all the child panels.
- for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
- {
- LLTabTuple* tuple = *iter;
-
- removeChild( tuple->mButton );
- delete tuple->mButton;
- tuple->mButton = NULL;
-
- removeChild( tuple->mTabPanel );
-// delete tuple->mTabPanel;
- tuple->mTabPanel = NULL;
- }
-
- // Actually delete the tuples themselves
- std::for_each(mTabList.begin(), mTabList.end(), DeletePointer());
- mTabList.clear();
-
- // And there isn't a current tab any more
- mCurrentTabIdx = -1;
-}
-
-LLPanel* LLTabContainer::getCurrentPanel()
-{
- if (mCurrentTabIdx >= 0 && mCurrentTabIdx < (S32) mTabList.size())
- {
- return mTabList[mCurrentTabIdx]->mTabPanel;
- }
- return NULL;
-}
-
-S32 LLTabContainer::getCurrentPanelIndex()
-{
- return mCurrentTabIdx;
-}
-
-S32 LLTabContainer::getTabCount()
-{
- return mTabList.size();
-}
-
-LLPanel* LLTabContainer::getPanelByIndex(S32 index)
-{
- if (index >= 0 && index < (S32)mTabList.size())
- {
- return mTabList[index]->mTabPanel;
- }
- return NULL;
-}
-
-S32 LLTabContainer::getIndexForPanel(LLPanel* panel)
-{
- for (S32 index = 0; index < (S32)mTabList.size(); index++)
- {
- if (mTabList[index]->mTabPanel == panel)
- {
- return index;
- }
- }
- return -1;
-}
-
-S32 LLTabContainer::getPanelIndexByTitle(const std::string& title)
-{
- for (S32 index = 0 ; index < (S32)mTabList.size(); index++)
- {
- if (title == mTabList[index]->mButton->getLabelSelected())
- {
- return index;
- }
- }
- return -1;
-}
-
-LLPanel* LLTabContainer::getPanelByName(const std::string& name)
-{
- for (S32 index = 0 ; index < (S32)mTabList.size(); index++)
- {
- LLPanel *panel = mTabList[index]->mTabPanel;
- if (name == panel->getName())
- {
- return panel;
- }
- }
- return NULL;
-}
-
-// Change the name of the button for the current tab.
-void LLTabContainer::setCurrentTabName(const std::string& name)
-{
- // Might not have a tab selected
- if (mCurrentTabIdx < 0) return;
-
- mTabList[mCurrentTabIdx]->mButton->setLabelSelected(name);
- mTabList[mCurrentTabIdx]->mButton->setLabelUnselected(name);
-}
-
-void LLTabContainer::selectFirstTab()
-{
- selectTab( 0 );
-}
-
-
-void LLTabContainer::selectLastTab()
-{
- selectTab( mTabList.size()-1 );
-}
-
-void LLTabContainer::selectNextTab()
-{
- if (mTabList.size() == 0)
- {
- return;
- }
-
- bool tab_has_focus = false;
- if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus())
- {
- tab_has_focus = true;
- }
- S32 idx = mCurrentTabIdx+1;
- if (idx >= (S32)mTabList.size())
- idx = 0;
- while (!selectTab(idx) && idx != mCurrentTabIdx)
- {
- idx = (idx + 1 ) % (S32)mTabList.size();
- }
-
- if (tab_has_focus)
- {
- mTabList[idx]->mButton->setFocus(true);
- }
-}
-
-void LLTabContainer::selectPrevTab()
-{
- bool tab_has_focus = false;
- if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus())
- {
- tab_has_focus = true;
- }
- S32 idx = mCurrentTabIdx-1;
- if (idx < 0)
- idx = mTabList.size()-1;
- while (!selectTab(idx) && idx != mCurrentTabIdx)
- {
- idx = idx - 1;
- if (idx < 0)
- idx = mTabList.size()-1;
- }
- if (tab_has_focus)
- {
- mTabList[idx]->mButton->setFocus(true);
- }
-}
-
-bool LLTabContainer::selectTabPanel(LLPanel* child)
-{
- S32 idx = 0;
- for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
- {
- LLTabTuple* tuple = *iter;
- if( tuple->mTabPanel == child )
- {
- return selectTab( idx );
- }
- idx++;
- }
- return false;
-}
-
-bool LLTabContainer::selectTab(S32 which)
-{
- if (which >= getTabCount() || which < 0)
- return false;
-
- LLTabTuple* selected_tuple = getTab(which);
- if (!selected_tuple)
- return false;
-
- LLSD cbdata;
- if (selected_tuple->mTabPanel)
- cbdata = selected_tuple->mTabPanel->getName();
-
- bool result = false;
- if (!mValidateSignal || (*mValidateSignal)(this, cbdata))
- {
- result = setTab(which);
- if (result && mCommitSignal)
- {
- (*mCommitSignal)(this, cbdata);
- }
- }
-
- return result;
-}
-
-// private
-bool LLTabContainer::setTab(S32 which)
-{
- static LLUICachedControl<S32> tabcntr_arrow_btn_size ("UITabCntrArrowBtnSize", 0);
- LLTabTuple* selected_tuple = getTab(which);
- if (!selected_tuple)
- {
- return false;
- }
-
- bool is_visible = false;
- if( selected_tuple->mButton->getEnabled() && selected_tuple->mVisible )
- {
- setCurrentPanelIndex(which);
-
- S32 i = 0;
- for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
- {
- LLTabTuple* tuple = *iter;
- bool is_selected = ( tuple == selected_tuple );
- // 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)
- {
- LLUIUsage::instance().logPanel(tuple->mTabPanel->getName());
-
- // Make sure selected tab is within scroll region
- if (mIsVertical)
- {
- S32 num_visible = getTabCount() - getMaxScrollPos();
- if( i >= getScrollPos() && i <= getScrollPos() + num_visible)
- {
- setCurrentPanelIndex(which);
- is_visible = true;
- }
- else
- {
- is_visible = false;
- }
- }
- else if (!mHideScrollArrows && getMaxScrollPos() > 0)
- {
- if( i < getScrollPos() )
- {
- setScrollPos(i);
- }
- 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 && tuple->mButton ? tuple->mButton->getRect().getWidth() : 0);
- S32 j = i - 1;
- S32 min_scroll_pos = i;
- if (running_tab_width < available_width_with_arrows)
- {
- while (j >= 0)
- {
- LLTabTuple* other_tuple = getTab(j);
- running_tab_width += (other_tuple && other_tuple->mButton ? other_tuple->mButton->getRect().getWidth() : 0);
- if (running_tab_width > available_width_with_arrows)
- {
- break;
- }
- j--;
- }
- min_scroll_pos = j + 1;
- }
- setScrollPos(llclamp(getScrollPos(), min_scroll_pos, i));
- setScrollPos(llmin(getScrollPos(), getMaxScrollPos()));
- }
- is_visible = true;
- }
- else
- {
- is_visible = true;
- }
- }
- i++;
- }
- }
- if (mIsVertical && getCurrentPanelIndex() >= 0)
- {
- LLTabTuple* tuple = getTab(getCurrentPanelIndex());
- tuple->mTabPanel->setVisible( true );
- tuple->mButton->setToggleState( true );
- }
- return is_visible;
-}
-
-bool LLTabContainer::selectTabByName(const std::string& name)
-{
- LLPanel* panel = getPanelByName(name);
- if (!panel)
- {
- LL_WARNS() << "LLTabContainer::selectTabByName(" << name << ") failed" << LL_ENDL;
- return false;
- }
-
- bool result = selectTabPanel(panel);
- return result;
-}
-
-bool LLTabContainer::getTabPanelFlashing(LLPanel *child)
-{
- LLTabTuple* tuple = getTabByPanel(child);
- if( tuple )
- {
- return tuple->mButton->getFlashing();
- }
- return false;
-}
-
-void LLTabContainer::setTabPanelFlashing(LLPanel* child, bool state )
-{
- LLTabTuple* tuple = getTabByPanel(child);
- if( tuple )
- {
- tuple->mButton->setFlashing( state );
- }
-}
-
-void LLTabContainer::setTabImage(LLPanel* child, std::string image_name, const LLColor4& color)
-{
- LLTabTuple* tuple = getTabByPanel(child);
- if( tuple )
- {
- tuple->mButton->setImageOverlay(image_name, LLFontGL::LEFT, color);
- reshapeTuple(tuple);
- }
-}
-
-void LLTabContainer::setTabImage(LLPanel* child, const LLUUID& image_id, const LLColor4& color)
-{
- LLTabTuple* tuple = getTabByPanel(child);
- if( tuple )
- {
- tuple->mButton->setImageOverlay(image_id, LLFontGL::LEFT, color);
- reshapeTuple(tuple);
- }
-}
-
-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)
-{
- static LLUICachedControl<S32> tab_padding ("UITabPadding", 0);
-
- if (!mIsVertical)
- {
- S32 image_overlay_width = 0;
-
- if(mCustomIconCtrlUsed)
- {
- LLCustomButtonIconCtrl* button = dynamic_cast<LLCustomButtonIconCtrl*>(tuple->mButton);
- LLIconCtrl* icon_ctrl = button ? button->getIconCtrl() : NULL;
- image_overlay_width = icon_ctrl ? icon_ctrl->getRect().getWidth() : 0;
- }
- else
- {
- image_overlay_width = tuple->mButton->getImageOverlay().notNull() ?
- tuple->mButton->getImageOverlay()->getImage()->getWidth(0) : 0;
- }
- // remove current width from total tab strip width
- mTotalTabWidth -= tuple->mButton->getRect().getWidth();
-
- tuple->mPadding = image_overlay_width;
-
- tuple->mButton->reshape(llclamp(mFont->getWidth(tuple->mButton->getLabelSelected()) + tab_padding + tuple->mPadding, mMinTabWidth, mMaxTabWidth),
- tuple->mButton->getRect().getHeight());
- // add back in button width to total tab strip width
- mTotalTabWidth += tuple->mButton->getRect().getWidth();
-
- // tabs have changed size, might need to scroll to see current tab
- updateMaxScrollPos();
- }
-}
-
-void LLTabContainer::setTitle(const std::string& title)
-{
- if (mTitleBox)
- {
- mTitleBox->setText( title );
- }
-}
-
-const std::string LLTabContainer::getPanelTitle(S32 index)
-{
- if (index >= 0 && index < (S32)mTabList.size())
- {
- LLButton* tab_button = mTabList[index]->mButton;
- return tab_button->getLabelSelected();
- }
- return LLStringUtil::null;
-}
-
-void LLTabContainer::setTopBorderHeight(S32 height)
-{
- mTopBorderHeight = height;
-}
-
-S32 LLTabContainer::getTopBorderHeight() const
-{
- return mTopBorderHeight;
-}
-
-void LLTabContainer::setRightTabBtnOffset(S32 offset)
-{
- mNextArrowBtn->translate( -offset - mRightTabBtnOffset, 0 );
- mRightTabBtnOffset = offset;
- updateMaxScrollPos();
-}
-
-void LLTabContainer::setPanelTitle(S32 index, const std::string& title)
-{
- static LLUICachedControl<S32> tab_padding ("UITabPadding", 0);
-
- if (index >= 0 && index < getTabCount())
- {
- LLTabTuple* tuple = getTab(index);
- LLButton* tab_button = tuple->mButton;
- const LLFontGL* fontp = LLFontGL::getFontSansSerifSmall();
- mTotalTabWidth -= tab_button->getRect().getWidth();
- tab_button->reshape(llclamp(fontp->getWidth(title) + tab_padding + tuple->mPadding, mMinTabWidth, mMaxTabWidth), tab_button->getRect().getHeight());
- mTotalTabWidth += tab_button->getRect().getWidth();
- tab_button->setLabelSelected(title);
- tab_button->setLabelUnselected(title);
- }
- updateMaxScrollPos();
-}
-
-
-void LLTabContainer::onTabBtn( const LLSD& data, LLPanel* panel )
-{
- LLTabTuple* tuple = getTabByPanel(panel);
- selectTabPanel( panel );
-
- if (tuple)
- {
- tuple->mTabPanel->setFocus(true);
- }
-}
-
-void LLTabContainer::onNextBtn( const LLSD& data )
-{
- if (!mScrolled)
- {
- scrollNext();
- }
- mScrolled = false;
-
- if(mCurrentTabIdx < mTabList.size()-1)
- {
- selectNextTab();
- }
-}
-
-void LLTabContainer::onNextBtnHeld( const LLSD& data )
-{
- if (mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME)
- {
- mScrollTimer.reset();
- scrollNext();
-
- if(mCurrentTabIdx < mTabList.size()-1)
- {
- selectNextTab();
- }
- mScrolled = true;
- }
-}
-
-void LLTabContainer::onPrevBtn( const LLSD& data )
-{
- if (!mScrolled)
- {
- scrollPrev();
- }
- mScrolled = false;
-
- if(mCurrentTabIdx > 0)
- {
- selectPrevTab();
- }
-}
-
-void LLTabContainer::onJumpFirstBtn( const LLSD& data )
-{
- mScrollPos = 0;
-}
-
-void LLTabContainer::onJumpLastBtn( const LLSD& data )
-{
- mScrollPos = mMaxScrollPos;
-}
-
-void LLTabContainer::onPrevBtnHeld( const LLSD& data )
-{
- if (mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME)
- {
- mScrollTimer.reset();
- scrollPrev();
-
- if(mCurrentTabIdx > 0)
- {
- selectPrevTab();
- }
- mScrolled = true;
- }
-}
-
-// private
-
-void LLTabContainer::initButtons()
-{
- // Hack:
- if (getRect().getHeight() == 0 || mPrevArrowBtn)
- {
- return; // Don't have a rect yet or already got called
- }
-
- if (mIsVertical)
- {
- static LLUICachedControl<S32> tabcntrv_arrow_btn_size ("UITabCntrvArrowBtnSize", 0);
- // Left and right scroll arrows (for when there are too many tabs to show all at once).
- S32 btn_top = getRect().getHeight();
- S32 btn_top_lower = getRect().mBottom+tabcntrv_arrow_btn_size;
-
- LLRect up_arrow_btn_rect;
- up_arrow_btn_rect.setLeftTopAndSize( mMinTabWidth/2 , btn_top, tabcntrv_arrow_btn_size, tabcntrv_arrow_btn_size );
-
- LLRect down_arrow_btn_rect;
- down_arrow_btn_rect.setLeftTopAndSize( mMinTabWidth/2 , btn_top_lower, tabcntrv_arrow_btn_size, tabcntrv_arrow_btn_size );
-
- LLButton::Params prev_btn_params;
- prev_btn_params.name(std::string("Up Arrow"));
- prev_btn_params.rect(up_arrow_btn_rect);
- prev_btn_params.follows.flags(FOLLOWS_TOP | FOLLOWS_LEFT);
- prev_btn_params.image_unselected.name("scrollbutton_up_out_blue.tga");
- prev_btn_params.image_selected.name("scrollbutton_up_in_blue.tga");
- prev_btn_params.click_callback.function(boost::bind(&LLTabContainer::onPrevBtn, this, _2));
- mPrevArrowBtn = LLUICtrlFactory::create<LLButton>(prev_btn_params);
-
- LLButton::Params next_btn_params;
- next_btn_params.name(std::string("Down Arrow"));
- next_btn_params.rect(down_arrow_btn_rect);
- next_btn_params.follows.flags(FOLLOWS_BOTTOM | FOLLOWS_LEFT);
- next_btn_params.image_unselected.name("scrollbutton_down_out_blue.tga");
- next_btn_params.image_selected.name("scrollbutton_down_in_blue.tga");
- next_btn_params.click_callback.function(boost::bind(&LLTabContainer::onNextBtn, this, _2));
- mNextArrowBtn = LLUICtrlFactory::create<LLButton>(next_btn_params);
- }
- else // Horizontal
- {
- static LLUICachedControl<S32> tabcntr_arrow_btn_size ("UITabCntrArrowBtnSize", 0);
- S32 arrow_fudge = 1; // match new art better
-
- // Left and right scroll arrows (for when there are too many tabs to show all at once).
- S32 btn_top = (getTabPosition() == TOP ) ? getRect().getHeight() - getTopBorderHeight() : tabcntr_arrow_btn_size + 1;
-
- LLRect left_arrow_btn_rect;
- left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1+tabcntr_arrow_btn_size, btn_top + arrow_fudge, tabcntr_arrow_btn_size, mTabHeight );
-
- LLRect jump_left_arrow_btn_rect;
- jump_left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1, btn_top + arrow_fudge, tabcntr_arrow_btn_size, mTabHeight );
-
- S32 right_pad = tabcntr_arrow_btn_size + LLPANEL_BORDER_WIDTH + 1;
-
- LLRect right_arrow_btn_rect;
- right_arrow_btn_rect.setLeftTopAndSize( getRect().getWidth() - mRightTabBtnOffset - right_pad - tabcntr_arrow_btn_size,
- btn_top + arrow_fudge,
- tabcntr_arrow_btn_size, mTabHeight );
-
-
- LLRect jump_right_arrow_btn_rect;
- jump_right_arrow_btn_rect.setLeftTopAndSize( getRect().getWidth() - mRightTabBtnOffset - right_pad,
- btn_top + arrow_fudge,
- tabcntr_arrow_btn_size, mTabHeight );
-
- LLButton::Params p;
- p.name(std::string("Jump Left Arrow"));
- p.image_unselected.name("jump_left_out.tga");
- p.image_selected.name("jump_left_in.tga");
- p.click_callback.function(boost::bind(&LLTabContainer::onJumpFirstBtn, this, _2));
- p.rect(jump_left_arrow_btn_rect);
- p.follows.flags(FOLLOWS_LEFT);
-
- mJumpPrevArrowBtn = LLUICtrlFactory::create<LLButton>(p);
-
- p = LLButton::Params();
- p.name(std::string("Left Arrow"));
- p.rect(left_arrow_btn_rect);
- p.follows.flags(FOLLOWS_LEFT);
- p.image_unselected.name("scrollbutton_left_out_blue.tga");
- p.image_selected.name("scrollbutton_left_in_blue.tga");
- p.click_callback.function(boost::bind(&LLTabContainer::onPrevBtn, this, _2));
- p.mouse_held_callback.function(boost::bind(&LLTabContainer::onPrevBtnHeld, this, _2));
-
- mPrevArrowBtn = LLUICtrlFactory::create<LLButton>(p);
-
- p = LLButton::Params();
- p.name(std::string("Jump Right Arrow"));
- p.rect(jump_right_arrow_btn_rect);
- p.follows.flags(FOLLOWS_RIGHT);
- p.image_unselected.name("jump_right_out.tga");
- p.image_selected.name("jump_right_in.tga");
- p.click_callback.function(boost::bind(&LLTabContainer::onJumpLastBtn, this, _2));
-
- mJumpNextArrowBtn = LLUICtrlFactory::create<LLButton>(p);
-
- p = LLButton::Params();
- p.name(std::string("Right Arrow"));
- p.rect(right_arrow_btn_rect);
- p.follows.flags(FOLLOWS_RIGHT);
- p.image_unselected.name("scrollbutton_right_out_blue.tga");
- p.image_selected.name("scrollbutton_right_in_blue.tga");
- p.click_callback.function(boost::bind(&LLTabContainer::onNextBtn, this, _2));
- p.mouse_held_callback.function(boost::bind(&LLTabContainer::onNextBtnHeld, this, _2));
-
- mNextArrowBtn = LLUICtrlFactory::create<LLButton>(p);
-
- if( getTabPosition() == TOP )
- {
- mNextArrowBtn->setFollowsTop();
- mPrevArrowBtn->setFollowsTop();
- mJumpPrevArrowBtn->setFollowsTop();
- mJumpNextArrowBtn->setFollowsTop();
- }
- else
- {
- mNextArrowBtn->setFollowsBottom();
- mPrevArrowBtn->setFollowsBottom();
- mJumpPrevArrowBtn->setFollowsBottom();
- mJumpNextArrowBtn->setFollowsBottom();
- }
- }
-
- mPrevArrowBtn->setTabStop(false);
- addChild(mPrevArrowBtn);
-
- mNextArrowBtn->setTabStop(false);
- addChild(mNextArrowBtn);
-
- if (mJumpPrevArrowBtn)
- {
- mJumpPrevArrowBtn->setTabStop(false);
- addChild(mJumpPrevArrowBtn);
- }
-
- if (mJumpNextArrowBtn)
- {
- mJumpNextArrowBtn->setTabStop(false);
- addChild(mJumpNextArrowBtn);
- }
-
- // set default tab group to be panel contents
- setDefaultTabGroup(1);
-}
-
-//this is a work around for the current LLPanel::initFromParams hack
-//so that it doesn't overwrite the default tab group.
-//will be removed when LLPanel is fixed soon.
-void LLTabContainer::initFromParams(const LLPanel::Params& p)
-{
- LLPanel::initFromParams(p);
-
- setDefaultTabGroup(1);
-}
-
-LLTabTuple* LLTabContainer::getTabByPanel(LLPanel* child)
-{
- for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
- {
- LLTabTuple* tuple = *iter;
- if( tuple->mTabPanel == child )
- {
- return tuple;
- }
- }
- return NULL;
-}
-
-void LLTabContainer::insertTuple(LLTabTuple * tuple, eInsertionPoint insertion_point)
-{
- switch(insertion_point)
- {
- case START:
- // insert the new tab in the front of the list
- mTabList.insert(mTabList.begin() + mLockedTabCount, tuple);
- break;
- case LEFT_OF_CURRENT:
- // insert the new tab before the current tab (but not before mLockedTabCount)
- {
- tuple_list_t::iterator current_iter = mTabList.begin() + llmax(mLockedTabCount, mCurrentTabIdx);
- mTabList.insert(current_iter, tuple);
- }
- break;
-
- case RIGHT_OF_CURRENT:
- // insert the new tab after the current tab (but not before mLockedTabCount)
- {
- tuple_list_t::iterator current_iter = mTabList.begin() + llmax(mLockedTabCount, mCurrentTabIdx + 1);
- mTabList.insert(current_iter, tuple);
- }
- break;
- case END:
- default:
- mTabList.push_back( tuple );
- }
-}
-
-
-
-void LLTabContainer::updateMaxScrollPos()
-{
- static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
- bool no_scroll = true;
- if (mIsVertical)
- {
- S32 tab_total_height = (BTN_HEIGHT + tabcntrv_pad) * getTabCount();
- S32 available_height = getRect().getHeight() - getTopBorderHeight();
- if( tab_total_height > available_height )
- {
- static LLUICachedControl<S32> tabcntrv_arrow_btn_size ("UITabCntrvArrowBtnSize", 0);
- S32 available_height_with_arrows = getRect().getHeight() - 2*(tabcntrv_arrow_btn_size + 3*tabcntrv_pad) - mNextArrowBtn->getRect().mBottom;
- S32 additional_needed = tab_total_height - available_height_with_arrows;
- setMaxScrollPos((S32) ceil(additional_needed / float(BTN_HEIGHT + tabcntrv_pad) ) );
- no_scroll = false;
- }
- }
- else
- {
- static LLUICachedControl<S32> tabcntr_tab_h_pad ("UITabCntrTabHPad", 0);
- static LLUICachedControl<S32> tabcntr_arrow_btn_size ("UITabCntrArrowBtnSize", 0);
- static LLUICachedControl<S32> tabcntr_tab_partial_width ("UITabCntrTabPartialWidth", 0);
- S32 tab_space = 0;
- S32 available_space = 0;
- tab_space = mTotalTabWidth;
- available_space = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_tab_h_pad);
-
- if( tab_space > available_space )
- {
- S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_arrow_btn_size + tabcntr_arrow_btn_size + 1);
- // subtract off reserved portion on left
- available_width_with_arrows -= tabcntr_tab_partial_width;
-
- S32 running_tab_width = 0;
- setMaxScrollPos(getTabCount());
- for(tuple_list_t::reverse_iterator tab_it = mTabList.rbegin(); tab_it != mTabList.rend(); ++tab_it)
- {
- running_tab_width += (*tab_it)->mButton->getRect().getWidth();
- if (running_tab_width > available_width_with_arrows)
- {
- break;
- }
- setMaxScrollPos(getMaxScrollPos()-1);
- }
- // in case last tab doesn't actually fit on screen, make it the last scrolling position
- setMaxScrollPos(llmin(getMaxScrollPos(), getTabCount() - 1));
- no_scroll = false;
- }
- }
- if (no_scroll)
- {
- setMaxScrollPos(0);
- setScrollPos(0);
- }
- if (getScrollPos() > getMaxScrollPos())
- {
- setScrollPos(getMaxScrollPos()); // maybe just enforce this via limits in setScrollPos instead?
- }
-}
-
-void LLTabContainer::commitHoveredButton(S32 x, S32 y)
-{
- if (!getTabsHidden() && hasMouseCapture())
- {
- for (tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
- {
- LLButton* button = (*iter)->mButton;
- LLPanel* panel = (*iter)->mTabPanel;
- if (button->getEnabled() && button->getVisible() && !panel->getVisible())
- {
- S32 local_x = x - button->getRect().mLeft;
- S32 local_y = y - button->getRect().mBottom;
- if (button->pointInView(local_x, local_y))
- {
- button->onCommit();
- break;
- }
- }
- }
- }
-}
-
-S32 LLTabContainer::getTotalTabWidth() const
-{
- return mTotalTabWidth;
-}
-
-void LLTabContainer::setTabVisibility( LLPanel const *aPanel, bool aVisible )
-{
- for( tuple_list_t::const_iterator itr = mTabList.begin(); itr != mTabList.end(); ++itr )
- {
- LLTabTuple const *pTT = *itr;
- if( pTT->mTabPanel == aPanel )
- {
- pTT->mVisible = aVisible;
- break;
- }
- }
-
- bool foundTab( false );
- for( tuple_list_t::const_iterator itr = mTabList.begin(); itr != mTabList.end(); ++itr )
- {
- LLTabTuple const *pTT = *itr;
- if( pTT->mVisible )
- {
- this->selectTab( itr - mTabList.begin() );
- foundTab = true;
- break;
- }
- }
-
- if( foundTab )
- this->setVisible( true );
- else
- this->setVisible( false );
-
- updateMaxScrollPos();
-}
+/**
+ * @file lltabcontainer.cpp
+ * @brief LLTabContainer class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltabcontainer.h"
+#include "llviewereventrecorder.h"
+#include "llfocusmgr.h"
+#include "lllocalcliprect.h"
+#include "llrect.h"
+#include "llresizehandle.h"
+#include "lltextbox.h"
+#include "llcriticaldamp.h"
+#include "lluictrlfactory.h"
+#include "llrender.h"
+#include "llfloater.h"
+#include "lltrans.h"
+#include "lluiusage.h"
+
+//----------------------------------------------------------------------------
+
+// Implementation Notes:
+// - Each tab points to a LLPanel (see LLTabTuple below)
+// - When a tab is selected, the validation callback
+// (LLUICtrl::mValidateSignal) is called
+// - If the validation callback returns true (or none is provided),
+// the tab is changed and the commit callback
+// (LLUICtrl::mCommitSignal) is called
+// - Callbacks pass the LLTabContainer as the control,
+// and the NAME of the selected PANEL as the LLSD data
+
+//----------------------------------------------------------------------------
+
+const F32 SCROLL_STEP_TIME = 0.4f;
+const F32 SCROLL_DELAY_TIME = 0.5f;
+
+void LLTabContainer::TabPositions::declareValues()
+{
+ declare("top", LLTabContainer::TOP);
+ declare("bottom", LLTabContainer::BOTTOM);
+ declare("left", LLTabContainer::LEFT);
+}
+
+//----------------------------------------------------------------------------
+
+// Structure used to map tab buttons to and from tab panels
+class LLTabTuple
+{
+public:
+ LLTabTuple( LLTabContainer* c, LLPanel* p, LLButton* b, LLTextBox* placeholder = NULL)
+ :
+ mTabContainer(c),
+ mTabPanel(p),
+ mButton(b),
+ mOldState(false),
+ mPlaceholderText(placeholder),
+ mPadding(0),
+ mVisible(true)
+ {}
+
+ LLTabContainer* mTabContainer;
+ LLPanel* mTabPanel;
+ LLButton* mButton;
+ bool mOldState;
+ LLTextBox* mPlaceholderText;
+ S32 mPadding;
+
+ mutable bool mVisible;
+};
+
+//----------------------------------------------------------------------------
+
+//============================================================================
+/*
+ * @file lltabcontainer.cpp
+ * @brief class implements LLButton with LLIconCtrl on it
+ */
+class LLCustomButtonIconCtrl : public LLButton
+{
+public:
+ struct Params
+ : public LLInitParam::Block<Params, LLButton::Params>
+ {
+ // LEFT, RIGHT, TOP, BOTTOM paddings of LLIconCtrl in this class has same value
+ Optional<S32> icon_ctrl_pad;
+
+ Params()
+ : icon_ctrl_pad("icon_ctrl_pad", 1)
+ {}
+ };
+
+protected:
+ friend class LLUICtrlFactory;
+
+ LLCustomButtonIconCtrl(const Params& p)
+ : LLButton(p),
+ mIcon(NULL),
+ mIconAlignment(LLFontGL::HCENTER),
+ mIconCtrlPad(p.icon_ctrl_pad)
+ {}
+
+public:
+
+ void updateLayout()
+ {
+ LLRect button_rect = getRect();
+ LLRect icon_rect = mIcon->getRect();
+
+ S32 icon_size = button_rect.getHeight() - 2*mIconCtrlPad;
+
+ switch(mIconAlignment)
+ {
+ case LLFontGL::LEFT:
+ icon_rect.setLeftTopAndSize(button_rect.mLeft + mIconCtrlPad, button_rect.mTop - mIconCtrlPad,
+ icon_size, icon_size);
+ setLeftHPad(icon_size + mIconCtrlPad * 2);
+ break;
+ case LLFontGL::HCENTER:
+ icon_rect.setLeftTopAndSize(button_rect.mRight - (button_rect.getWidth() + mIconCtrlPad - icon_size)/2, button_rect.mTop - mIconCtrlPad,
+ icon_size, icon_size);
+ setRightHPad(icon_size + mIconCtrlPad * 2);
+ break;
+ case LLFontGL::RIGHT:
+ icon_rect.setLeftTopAndSize(button_rect.mRight - mIconCtrlPad - icon_size, button_rect.mTop - mIconCtrlPad,
+ icon_size, icon_size);
+ setRightHPad(icon_size + mIconCtrlPad * 2);
+ break;
+ default:
+ break;
+ }
+ mIcon->setRect(icon_rect);
+ }
+
+ void setIcon(LLIconCtrl* icon, LLFontGL::HAlign alignment = LLFontGL::LEFT)
+ {
+ if(icon)
+ {
+ if(mIcon)
+ {
+ removeChild(mIcon);
+ mIcon->die();
+ }
+ mIcon = icon;
+ mIconAlignment = alignment;
+
+ addChild(mIcon);
+ updateLayout();
+ }
+ }
+
+ LLIconCtrl* getIconCtrl() const
+ {
+ return mIcon;
+ }
+
+private:
+ LLIconCtrl* mIcon;
+ LLFontGL::HAlign mIconAlignment;
+ S32 mIconCtrlPad;
+};
+//============================================================================
+
+struct LLPlaceHolderPanel : public LLPanel
+{
+ // create dummy param block to register with "placeholder" nane
+ struct Params : public LLPanel::Params{};
+ LLPlaceHolderPanel(const Params& p) : LLPanel(p)
+ {}
+};
+static LLDefaultChildRegistry::Register<LLPlaceHolderPanel> r1("placeholder");
+static LLDefaultChildRegistry::Register<LLTabContainer> r2("tab_container");
+
+LLTabContainer::TabParams::TabParams()
+: tab_top_image_unselected("tab_top_image_unselected"),
+ tab_top_image_selected("tab_top_image_selected"),
+ tab_top_image_flash("tab_top_image_flash"),
+ tab_bottom_image_unselected("tab_bottom_image_unselected"),
+ tab_bottom_image_selected("tab_bottom_image_selected"),
+ tab_bottom_image_flash("tab_bottom_image_flash"),
+ tab_left_image_unselected("tab_left_image_unselected"),
+ tab_left_image_selected("tab_left_image_selected"),
+ tab_left_image_flash("tab_left_image_flash")
+{}
+
+LLTabContainer::Params::Params()
+: tab_width("tab_width"),
+ tab_min_width("tab_min_width"),
+ tab_max_width("tab_max_width"),
+ tab_height("tab_height"),
+ label_pad_bottom("label_pad_bottom"),
+ label_pad_left("label_pad_left"),
+ tab_position("tab_position"),
+ hide_tabs("hide_tabs", false),
+ hide_scroll_arrows("hide_scroll_arrows", false),
+ tab_padding_right("tab_padding_right"),
+ first_tab("first_tab"),
+ middle_tab("middle_tab"),
+ last_tab("last_tab"),
+ use_custom_icon_ctrl("use_custom_icon_ctrl", false),
+ open_tabs_on_drag_and_drop("open_tabs_on_drag_and_drop", false),
+ enable_tabs_flashing("enable_tabs_flashing", false),
+ tabs_flashing_color("tabs_flashing_color"),
+ tab_icon_ctrl_pad("tab_icon_ctrl_pad", 0),
+ use_ellipses("use_ellipses"),
+ font_halign("halign"),
+ use_tab_offset("use_tab_offset", false)
+{}
+
+LLTabContainer::LLTabContainer(const LLTabContainer::Params& p)
+: LLPanel(p),
+ mCurrentTabIdx(-1),
+ mTabsHidden(p.hide_tabs),
+ mScrolled(false),
+ mScrollPos(0),
+ mScrollPosPixels(0),
+ mMaxScrollPos(0),
+ mTitleBox(NULL),
+ mTopBorderHeight(LLPANEL_BORDER_WIDTH),
+ mLockedTabCount(0),
+ mMinTabWidth(0),
+ mMaxTabWidth(p.tab_max_width),
+ mTabHeight(p.tab_height),
+ mLabelPadBottom(p.label_pad_bottom),
+ mLabelPadLeft(p.label_pad_left),
+ mPrevArrowBtn(NULL),
+ mNextArrowBtn(NULL),
+ mIsVertical( p.tab_position == LEFT ),
+ mHideScrollArrows(p.hide_scroll_arrows),
+ // Horizontal Specific
+ mJumpPrevArrowBtn(NULL),
+ mJumpNextArrowBtn(NULL),
+ mRightTabBtnOffset(p.tab_padding_right),
+ mTotalTabWidth(0),
+ mTabPosition(p.tab_position),
+ mFontHalign(p.font_halign),
+ mFont(p.font),
+ mFirstTabParams(p.first_tab),
+ mMiddleTabParams(p.middle_tab),
+ mLastTabParams(p.last_tab),
+ mCustomIconCtrlUsed(p.use_custom_icon_ctrl),
+ mOpenTabsOnDragAndDrop(p.open_tabs_on_drag_and_drop),
+ mTabIconCtrlPad(p.tab_icon_ctrl_pad),
+ mEnableTabsFlashing(p.enable_tabs_flashing),
+ mTabsFlashingColor(p.tabs_flashing_color),
+ mUseTabEllipses(p.use_ellipses),
+ mUseTabOffset(p.use_tab_offset)
+{
+ static LLUICachedControl<S32> tabcntr_vert_tab_min_width ("UITabCntrVertTabMinWidth", 0);
+
+ mDragAndDropDelayTimer.stop();
+
+ if (p.tab_width.isProvided())
+ {
+ mMinTabWidth = p.tab_width;
+ }
+ else if (!mIsVertical)
+ {
+ mMinTabWidth = p.tab_min_width;
+ }
+ else
+ {
+ // *HACK: support default min width for legacy vertical
+ // tab containers
+ mMinTabWidth = tabcntr_vert_tab_min_width;
+ }
+
+ if (p.tabs_flashing_color.isProvided())
+ {
+ mEnableTabsFlashing = true;
+ }
+
+ initButtons( );
+}
+
+LLTabContainer::~LLTabContainer()
+{
+ std::for_each(mTabList.begin(), mTabList.end(), DeletePointer());
+ mTabList.clear();
+}
+
+//virtual
+void LLTabContainer::setValue(const LLSD& value)
+{
+ selectTab((S32) value.asInteger());
+}
+
+//virtual
+void LLTabContainer::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ LLPanel::reshape( width, height, called_from_parent );
+ updateMaxScrollPos();
+}
+
+//virtual
+LLView* LLTabContainer::getChildView(const std::string& name, bool recurse) const
+{
+ tuple_list_t::const_iterator itor;
+ for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
+ {
+ LLPanel *panel = (*itor)->mTabPanel;
+ if (panel->getName() == name)
+ {
+ return panel;
+ }
+ }
+
+ if (recurse)
+ {
+ for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
+ {
+ LLPanel *panel = (*itor)->mTabPanel;
+ LLView *child = panel->getChildView(name, recurse);
+ if (child)
+ {
+ return child;
+ }
+ }
+ }
+ return LLView::getChildView(name, recurse);
+}
+
+//virtual
+LLView* LLTabContainer::findChildView(const std::string& name, bool recurse) const
+{
+ tuple_list_t::const_iterator itor;
+ for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
+ {
+ LLPanel *panel = (*itor)->mTabPanel;
+ if (panel->getName() == name)
+ {
+ return panel;
+ }
+ }
+
+ if (recurse)
+ {
+ for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
+ {
+ LLPanel *panel = (*itor)->mTabPanel;
+ LLView *child = panel->findChildView(name, recurse);
+ if (child)
+ {
+ return child;
+ }
+ }
+ }
+ return LLView::findChildView(name, recurse);
+}
+
+bool LLTabContainer::addChild(LLView* view, S32 tab_group)
+{
+ LLPanel* panelp = dynamic_cast<LLPanel*>(view);
+
+ if (panelp)
+ {
+ addTabPanel(TabPanelParams().panel(panelp).label(panelp->getLabel()).is_placeholder(dynamic_cast<LLPlaceHolderPanel*>(view) != NULL));
+ return true;
+ }
+ else
+ {
+ return LLUICtrl::addChild(view, tab_group);
+ }
+}
+
+bool LLTabContainer::postBuild()
+{
+ selectFirstTab();
+
+ return true;
+}
+
+// virtual
+void LLTabContainer::draw()
+{
+ static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
+ static LLUICachedControl<S32> tabcntrv_arrow_btn_size ("UITabCntrvArrowBtnSize", 0);
+ static LLUICachedControl<S32> tabcntr_tab_h_pad ("UITabCntrTabHPad", 0);
+ static LLUICachedControl<S32> tabcntr_arrow_btn_size ("UITabCntrArrowBtnSize", 0);
+ static LLUICachedControl<S32> tabcntr_tab_partial_width ("UITabCntrTabPartialWidth", 0);
+ S32 target_pixel_scroll = 0;
+ S32 cur_scroll_pos = getScrollPos();
+ if (cur_scroll_pos > 0)
+ {
+ if (mIsVertical)
+ {
+ target_pixel_scroll = cur_scroll_pos * (BTN_HEIGHT + tabcntrv_pad);
+ }
+ else
+ {
+ S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_arrow_btn_size + tabcntr_arrow_btn_size + 1);
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ if (cur_scroll_pos == 0)
+ {
+ break;
+ }
+
+ if( (*iter)->mVisible )
+ target_pixel_scroll += (*iter)->mButton->getRect().getWidth();
+
+ cur_scroll_pos--;
+ }
+
+ // Show part of the tab to the left of what is fully visible
+ target_pixel_scroll -= tabcntr_tab_partial_width;
+ // clamp so that rightmost tab never leaves right side of screen
+ target_pixel_scroll = llmin(mTotalTabWidth - available_width_with_arrows, target_pixel_scroll);
+ }
+ }
+
+ setScrollPosPixels((S32)lerp((F32)getScrollPosPixels(), (F32)target_pixel_scroll, LLSmoothInterpolation::getInterpolant(0.08f)));
+
+ bool has_scroll_arrows = !mHideScrollArrows && !getTabsHidden() && ((mMaxScrollPos > 0) || (mScrollPosPixels > 0));
+ if (!mIsVertical)
+ {
+ mJumpPrevArrowBtn->setVisible( has_scroll_arrows );
+ mJumpNextArrowBtn->setVisible( has_scroll_arrows );
+ }
+ mPrevArrowBtn->setVisible( has_scroll_arrows );
+ mNextArrowBtn->setVisible( has_scroll_arrows );
+
+ S32 left = 0, top = 0;
+ if (mIsVertical)
+ {
+ top = getRect().getHeight() - getTopBorderHeight() - LLPANEL_BORDER_WIDTH - 1 - (has_scroll_arrows ? tabcntrv_arrow_btn_size : 0);
+ top += getScrollPosPixels();
+ }
+ else
+ {
+ // Set the leftmost position of the tab buttons.
+ left = LLPANEL_BORDER_WIDTH + (has_scroll_arrows ? (tabcntr_arrow_btn_size * 2) : tabcntr_tab_h_pad);
+ left -= getScrollPosPixels();
+ }
+
+ // Hide all the buttons
+ if (getTabsHidden())
+ {
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ tuple->mButton->setVisible( false );
+ }
+ }
+
+ {
+ LLRect clip_rect = getLocalRect();
+ clip_rect.mLeft+=(LLPANEL_BORDER_WIDTH + 2);
+ clip_rect.mRight-=(LLPANEL_BORDER_WIDTH + 2);
+ LLLocalClipRect clip(clip_rect);
+ LLPanel::draw();
+ }
+
+ // if tabs are hidden, don't draw them and leave them in the invisible state
+ if (!getTabsHidden())
+ {
+ // Show all the buttons
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ tuple->mButton->setVisible( true );
+ }
+
+ S32 max_scroll_visible = getTabCount() - getMaxScrollPos() + getScrollPos();
+ S32 idx = 0;
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+
+ if( !tuple->mVisible )
+ {
+ tuple->mButton->setVisible( false );
+ continue;
+ }
+
+ tuple->mButton->translate( left ? left - tuple->mButton->getRect().mLeft : 0,
+ top ? top - tuple->mButton->getRect().mTop : 0 );
+ if (top) top -= BTN_HEIGHT + tabcntrv_pad;
+ if (left) left += tuple->mButton->getRect().getWidth();
+
+ if (!mIsVertical)
+ {
+ if( idx < getScrollPos() )
+ {
+ if( tuple->mButton->getFlashing() )
+ {
+ mPrevArrowBtn->setFlashing( true );
+ }
+ }
+ else if( max_scroll_visible < idx )
+ {
+ if( tuple->mButton->getFlashing() )
+ {
+ mNextArrowBtn->setFlashing( true );
+ }
+ }
+ }
+
+ idx++;
+ }
+
+
+ if( mIsVertical && has_scroll_arrows )
+ {
+ // Redraw the arrows so that they appears on top.
+ gGL.pushUIMatrix();
+ gGL.translateUI((F32)mPrevArrowBtn->getRect().mLeft, (F32)mPrevArrowBtn->getRect().mBottom, 0.f);
+ mPrevArrowBtn->draw();
+ gGL.popUIMatrix();
+
+ gGL.pushUIMatrix();
+ gGL.translateUI((F32)mNextArrowBtn->getRect().mLeft, (F32)mNextArrowBtn->getRect().mBottom, 0.f);
+ mNextArrowBtn->draw();
+ gGL.popUIMatrix();
+ }
+ }
+
+ mPrevArrowBtn->setFlashing(false);
+ mNextArrowBtn->setFlashing(false);
+}
+
+
+// virtual
+bool LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
+ bool handled = false;
+ bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0) && !getTabsHidden();
+
+ 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;
+ handled = mJumpPrevArrowBtn->handleMouseDown(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;
+ handled = mJumpNextArrowBtn->handleMouseDown(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;
+ handled = mPrevArrowBtn->handleMouseDown(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;
+ handled = mNextArrowBtn->handleMouseDown(local_x, local_y, mask);
+ }
+ }
+ if (!handled)
+ {
+ handled = LLPanel::handleMouseDown( x, y, mask );
+ }
+
+ S32 tab_count = getTabCount();
+ 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 );
+ }
+ 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 );
+ }
+ if( tab_rect.pointInRect( x, y ) )
+ {
+ S32 index = getCurrentPanelIndex();
+ index = llclamp(index, 0, tab_count-1);
+ LLButton* tab_button = getTab(index)->mButton;
+ gFocusMgr.setMouseCapture(this);
+ tab_button->setFocus(true);
+ mMouseDownTimer.start();
+ }
+ }
+ if (handled) {
+ // Note: May need to also capture local coords right here ?
+ LLViewerEventRecorder::instance().update_xui(getPathname( ));
+ }
+
+ return handled;
+}
+
+// virtual
+bool LLTabContainer::handleHover( S32 x, S32 y, MASK mask )
+{
+ bool handled = false;
+ bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0) && !getTabsHidden();
+
+ 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;
+ handled = mJumpPrevArrowBtn->handleHover(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;
+ handled = mJumpNextArrowBtn->handleHover(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;
+ handled = mPrevArrowBtn->handleHover(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;
+ handled = mNextArrowBtn->handleHover(local_x, local_y, mask);
+ }
+ }
+ if (!handled)
+ {
+ handled = LLPanel::handleHover(x, y, mask);
+ }
+
+ F32 drag_delay = 0.25f; // filter out clicks from dragging
+ if (mMouseDownTimer.getElapsedTimeF32() > drag_delay)
+ {
+ commitHoveredButton(x, y);
+ }
+ return handled;
+}
+
+// virtual
+bool LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
+{
+ bool handled = false;
+ bool has_scroll_arrows = !mHideScrollArrows && (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))
+ {
+ 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))
+ {
+ 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))
+ {
+ 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))
+ {
+ local_x = x - mNextArrowBtn->getRect().mLeft;
+ local_y = y - mNextArrowBtn->getRect().mBottom;
+ handled = mNextArrowBtn->handleMouseUp(local_x, local_y, mask);
+ }
+ }
+ if (!handled)
+ {
+ handled = LLPanel::handleMouseUp( x, y, mask );
+ }
+
+ commitHoveredButton(x, y);
+ mMouseDownTimer.stop();
+ LLPanel* cur_panel = getCurrentPanel();
+ if (hasMouseCapture())
+ {
+ if (cur_panel)
+ {
+ if (!cur_panel->focusFirstItem(false))
+ {
+ // if nothing in the panel gets focus, make sure the new tab does
+ // otherwise the last tab might keep focus
+ getTab(getCurrentPanelIndex())->mButton->setFocus(true);
+ }
+ }
+ gFocusMgr.setMouseCapture(NULL);
+ }
+ if (handled) {
+ // Note: may need to capture local coords here
+ LLViewerEventRecorder::instance().update_xui(getPathname( ));
+ }
+ return handled;
+}
+
+// virtual
+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 && !getTabsHidden())
+ {
+ LLTabTuple* firsttuple = getTab(0);
+
+ bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0);
+ LLRect clip;
+ if (mIsVertical)
+ {
+ clip = 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 );
+ }
+ else
+ {
+ clip = 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 );
+ }
+
+ if( clip.pointInRect( x, y ) )
+ {
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLButton* tab_button = (*iter)->mButton;
+ if (!tab_button->getVisible()) continue;
+ S32 local_x = x - tab_button->getRect().mLeft;
+ S32 local_y = y - tab_button->getRect().mBottom;
+ handled = tab_button->handleToolTip(local_x, local_y, mask);
+ if( handled )
+ {
+ break;
+ }
+ }
+ }
+ }
+ return handled;
+}
+
+// virtual
+bool LLTabContainer::handleKeyHere(KEY key, MASK mask)
+{
+ bool handled = false;
+ if (key == KEY_LEFT && mask == MASK_ALT)
+ {
+ selectPrevTab();
+ handled = true;
+ }
+ else if (key == KEY_RIGHT && mask == MASK_ALT)
+ {
+ selectNextTab();
+ handled = true;
+ }
+
+ if (handled)
+ {
+ if (getCurrentPanel())
+ {
+ getCurrentPanel()->setFocus(true);
+ }
+ }
+
+ if (!gFocusMgr.childHasKeyboardFocus(getCurrentPanel()))
+ {
+ // if child has focus, but not the current panel, focus is on a button
+ if (mIsVertical)
+ {
+ switch(key)
+ {
+ case KEY_UP:
+ selectPrevTab();
+ handled = true;
+ break;
+ case KEY_DOWN:
+ selectNextTab();
+ handled = true;
+ break;
+ case KEY_LEFT:
+ handled = true;
+ break;
+ case KEY_RIGHT:
+ if (getTabPosition() == LEFT && getCurrentPanel())
+ {
+ getCurrentPanel()->setFocus(true);
+ }
+ handled = true;
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ switch(key)
+ {
+ case KEY_UP:
+ if (getTabPosition() == BOTTOM && getCurrentPanel())
+ {
+ getCurrentPanel()->setFocus(true);
+ }
+ handled = true;
+ break;
+ case KEY_DOWN:
+ if (getTabPosition() == TOP && getCurrentPanel())
+ {
+ getCurrentPanel()->setFocus(true);
+ }
+ handled = true;
+ break;
+ case KEY_LEFT:
+ selectPrevTab();
+ handled = true;
+ break;
+ case KEY_RIGHT:
+ selectNextTab();
+ handled = true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return handled;
+}
+
+// virtual
+bool LLTabContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType type, void* cargo_data, EAcceptance *accept, std::string &tooltip)
+{
+ bool has_scroll_arrows = !mHideScrollArrows && (getMaxScrollPos() > 0);
+
+ if(mOpenTabsOnDragAndDrop && !getTabsHidden())
+ {
+ // In that case, we'll open the hovered tab while dragging and dropping items.
+ // This allows for drilling through tabs.
+ if (mDragAndDropDelayTimer.getStarted())
+ {
+ if (mDragAndDropDelayTimer.getElapsedTimeF32() > SCROLL_DELAY_TIME)
+ {
+ 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();
+ }
+ }
+ // 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);
+}
+
+void LLTabContainer::addTabPanel(LLPanel* panelp)
+{
+ addTabPanel(TabPanelParams().panel(panelp));
+}
+
+// function to update images
+void LLTabContainer::update_images(LLTabTuple* tuple, TabParams params, LLTabContainer::TabPosition pos)
+{
+ if (tuple && tuple->mButton)
+ {
+ if (pos == LLTabContainer::TOP)
+ {
+ tuple->mButton->setImageUnselected(static_cast<LLUIImage*>(params.tab_top_image_unselected));
+ tuple->mButton->setImageSelected(static_cast<LLUIImage*>(params.tab_top_image_selected));
+ tuple->mButton->setImageFlash(static_cast<LLUIImage*>(params.tab_top_image_flash));
+ }
+ else if (pos == LLTabContainer::BOTTOM)
+ {
+ tuple->mButton->setImageUnselected(static_cast<LLUIImage*>(params.tab_bottom_image_unselected));
+ tuple->mButton->setImageSelected(static_cast<LLUIImage*>(params.tab_bottom_image_selected));
+ tuple->mButton->setImageFlash(static_cast<LLUIImage*>(params.tab_bottom_image_flash));
+ }
+ else if (pos == LLTabContainer::LEFT)
+ {
+ tuple->mButton->setImageUnselected(static_cast<LLUIImage*>(params.tab_left_image_unselected));
+ tuple->mButton->setImageSelected(static_cast<LLUIImage*>(params.tab_left_image_selected));
+ tuple->mButton->setImageFlash(static_cast<LLUIImage*>(params.tab_left_image_flash));
+ }
+ }
+}
+
+void LLTabContainer::addTabPanel(const TabPanelParams& panel)
+{
+ LLPanel* child = panel.panel();
+
+ llassert(child);
+ if (!child) return;
+
+ const std::string& label = panel.label.isProvided()
+ ? panel.label()
+ : panel.panel()->getLabel();
+ bool select = panel.select_tab();
+ S32 indent = panel.indent();
+ bool placeholder = panel.is_placeholder;
+ eInsertionPoint insertion_point = panel.insert_at();
+
+ static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
+ static LLUICachedControl<S32> tabcntr_button_panel_overlap ("UITabCntrButtonPanelOverlap", 0);
+ static LLUICachedControl<S32> tab_padding ("UITabPadding", 0);
+ if (child->getParent() == this)
+ {
+ // already a child of mine
+ return;
+ }
+
+ // Store the original label for possible xml export.
+ child->setLabel(label);
+ std::string trimmed_label = label;
+ LLStringUtil::trim(trimmed_label);
+
+ S32 button_width = mMinTabWidth;
+ if (!mIsVertical)
+ {
+ button_width = llclamp(mFont->getWidth(trimmed_label) + tab_padding, mMinTabWidth, mMaxTabWidth);
+ }
+
+ // Tab panel
+ S32 tab_panel_top;
+ S32 tab_panel_bottom;
+ if (!getTabsHidden())
+ {
+ if( getTabPosition() == LLTabContainer::TOP )
+ {
+ S32 tab_height = mIsVertical ? BTN_HEIGHT : mTabHeight;
+ tab_panel_top = getRect().getHeight() - getTopBorderHeight() - (tab_height - tabcntr_button_panel_overlap);
+ tab_panel_bottom = LLPANEL_BORDER_WIDTH;
+ }
+ else
+ {
+ tab_panel_top = getRect().getHeight() - getTopBorderHeight();
+ tab_panel_bottom = (mTabHeight - tabcntr_button_panel_overlap); // Run to the edge, covering up the border
+ }
+ }
+ else
+ {
+ // Skip tab button space if tabs are invisible (EXT-576)
+ tab_panel_top = getRect().getHeight();
+ tab_panel_bottom = LLPANEL_BORDER_WIDTH;
+ }
+
+ LLRect tab_panel_rect;
+ if (!getTabsHidden() && mIsVertical)
+ {
+ tab_panel_rect = LLRect(mMinTabWidth + mRightTabBtnOffset + (LLPANEL_BORDER_WIDTH * 2) + tabcntrv_pad,
+ getRect().getHeight() - LLPANEL_BORDER_WIDTH,
+ getRect().getWidth() - LLPANEL_BORDER_WIDTH,
+ LLPANEL_BORDER_WIDTH);
+ }
+ else
+ {
+ S32 left_offset = mUseTabOffset ? LLPANEL_BORDER_WIDTH * 3 : LLPANEL_BORDER_WIDTH;
+ S32 right_offset = mUseTabOffset ? LLPANEL_BORDER_WIDTH * 2 : LLPANEL_BORDER_WIDTH;
+ tab_panel_rect = LLRect(left_offset, tab_panel_top, getRect().getWidth() - right_offset, tab_panel_bottom);
+ }
+ child->setFollowsAll();
+ child->translate( tab_panel_rect.mLeft - child->getRect().mLeft, tab_panel_rect.mBottom - child->getRect().mBottom);
+ child->reshape( tab_panel_rect.getWidth(), tab_panel_rect.getHeight(), true );
+ // add this child later
+
+ child->setVisible( false ); // Will be made visible when selected
+
+ mTotalTabWidth += button_width;
+
+ // Tab button
+ LLRect btn_rect; // Note: btn_rect.mLeft is just a dummy. Will be updated in draw().
+ LLUIImage* tab_img = NULL;
+ LLUIImage* tab_selected_img = NULL;
+ S32 tab_fudge = 1; // To make new tab art look better, nudge buttons up 1 pel
+
+ if (mIsVertical)
+ {
+ btn_rect.setLeftTopAndSize(tabcntrv_pad + LLPANEL_BORDER_WIDTH + 2, // JC - Fudge factor
+ (getRect().getHeight() - getTopBorderHeight() - LLPANEL_BORDER_WIDTH - 1) - ((BTN_HEIGHT + tabcntrv_pad) * getTabCount()),
+ mMinTabWidth,
+ BTN_HEIGHT);
+ }
+ else if( getTabPosition() == LLTabContainer::TOP )
+ {
+ btn_rect.setLeftTopAndSize( 0, getRect().getHeight() - getTopBorderHeight() + tab_fudge, button_width, mTabHeight);
+ tab_img = mMiddleTabParams.tab_top_image_unselected;
+ tab_selected_img = mMiddleTabParams.tab_top_image_selected;
+ }
+ else
+ {
+ btn_rect.setOriginAndSize( 0, 0 + tab_fudge, button_width, mTabHeight);
+ tab_img = mMiddleTabParams.tab_bottom_image_unselected;
+ tab_selected_img = mMiddleTabParams.tab_bottom_image_selected;
+ }
+
+ LLTextBox* textbox = NULL;
+ LLButton* btn = NULL;
+ LLCustomButtonIconCtrl::Params custom_btn_params;
+ {
+ custom_btn_params.icon_ctrl_pad(mTabIconCtrlPad);
+ }
+ LLButton::Params normal_btn_params;
+
+ if (placeholder)
+ {
+ btn_rect.translate(0, -6); // *TODO: make configurable
+ LLTextBox::Params params;
+ params.name(trimmed_label);
+ params.rect(btn_rect);
+ params.initial_value(trimmed_label);
+ params.font(mFont);
+ textbox = LLUICtrlFactory::create<LLTextBox> (params);
+
+ LLButton::Params p;
+ p.name("placeholder");
+ btn = LLUICtrlFactory::create<LLButton>(p);
+ }
+ 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)
+ {
+ 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);
+ }
+
+ // inits flash timer
+ p.button_flash_enable = mEnableTabsFlashing;
+ p.flash_color = mTabsFlashingColor;
+
+ // *TODO : It seems wrong not to use p in both cases considering the way p is initialized
+ if (mCustomIconCtrlUsed)
+ {
+ btn = LLUICtrlFactory::create<LLCustomButtonIconCtrl>(custom_btn_params);
+ }
+ else
+ {
+ btn = LLUICtrlFactory::create<LLButton>(p);
+ }
+ }
+
+ LLTabTuple* tuple = new LLTabTuple( this, child, btn, textbox );
+ insertTuple( tuple, insertion_point );
+
+ // if new tab was added as a first or last tab, update button image
+ // and update button image of any tab it may have affected
+ if (tuple == mTabList.front())
+ {
+ update_images(tuple, mFirstTabParams, getTabPosition());
+
+ if (mTabList.size() == 2)
+ {
+ update_images(mTabList[1], mLastTabParams, getTabPosition());
+ }
+ else if (mTabList.size() > 2)
+ {
+ update_images(mTabList[1], mMiddleTabParams, getTabPosition());
+ }
+ }
+ else if (tuple == mTabList.back())
+ {
+ update_images(tuple, mLastTabParams, getTabPosition());
+
+ if (mTabList.size() > 2)
+ {
+ update_images(mTabList[mTabList.size()-2], mMiddleTabParams, getTabPosition());
+ }
+ }
+
+ //Don't add button and textbox if tab buttons are invisible(EXT - 576)
+ if (!getTabsHidden())
+ {
+ if (textbox)
+ {
+ addChild( textbox, 0 );
+ }
+ if (btn)
+ {
+ addChild( btn, 0 );
+ }
+ }
+ else
+ {
+ if (textbox)
+ {
+ LLUICtrl::addChild(textbox, 0);
+ }
+ if (btn)
+ {
+ LLUICtrl::addChild(btn, 0);
+ }
+ }
+
+ if (child)
+ {
+ LLUICtrl::addChild(child, 1);
+ }
+
+ sendChildToFront(mPrevArrowBtn);
+ sendChildToFront(mNextArrowBtn);
+ sendChildToFront(mJumpPrevArrowBtn);
+ sendChildToFront(mJumpNextArrowBtn);
+
+ updateMaxScrollPos();
+
+ if( select )
+ {
+ selectLastTab();
+ mScrollPos = mMaxScrollPos;
+ }
+
+}
+
+void LLTabContainer::addPlaceholder(LLPanel* child, const std::string& label)
+{
+ addTabPanel(TabPanelParams().panel(child).label(label).is_placeholder(true));
+}
+
+void LLTabContainer::removeTabPanel(LLPanel* child)
+{
+ static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
+ if (mIsVertical)
+ {
+ // Fix-up button sizes
+ S32 tab_count = 0;
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ LLRect rect;
+ rect.setLeftTopAndSize(tabcntrv_pad + LLPANEL_BORDER_WIDTH + 2, // JC - Fudge factor
+ (getRect().getHeight() - LLPANEL_BORDER_WIDTH - 1) - ((BTN_HEIGHT + tabcntrv_pad) * (tab_count)),
+ mMinTabWidth,
+ BTN_HEIGHT);
+ if (tuple->mPlaceholderText)
+ {
+ tuple->mPlaceholderText->setRect(rect);
+ }
+ else
+ {
+ tuple->mButton->setRect(rect);
+ }
+ tab_count++;
+ }
+ }
+ else
+ {
+ // Adjust the total tab width.
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ if( tuple->mTabPanel == child )
+ {
+ mTotalTabWidth -= tuple->mButton->getRect().getWidth();
+ break;
+ }
+ }
+ }
+
+ bool has_focus = gFocusMgr.childHasKeyboardFocus(this);
+
+ // If the tab being deleted is the selected one, select a different tab.
+ for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ if( tuple->mTabPanel == child )
+ {
+ // update tab button images if removing the first or last tab
+ if ((tuple == mTabList.front()) && (mTabList.size() > 1))
+ {
+ update_images(mTabList[1], mFirstTabParams, getTabPosition());
+ }
+ else if ((tuple == mTabList.back()) && (mTabList.size() > 2))
+ {
+ update_images(mTabList[mTabList.size()-2], mLastTabParams, getTabPosition());
+ }
+
+ 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;
+
+ break;
+ }
+ }
+
+ // make sure we don't have more locked tabs than we have tabs
+ mLockedTabCount = llmin(getTabCount(), mLockedTabCount);
+
+ if (mCurrentTabIdx >= (S32)mTabList.size())
+ {
+ mCurrentTabIdx = mTabList.size()-1;
+ }
+ selectTab(mCurrentTabIdx);
+ if (has_focus)
+ {
+ LLPanel* panelp = getPanelByIndex(mCurrentTabIdx);
+ if (panelp)
+ {
+ panelp->setFocus(true);
+ }
+ }
+
+ updateMaxScrollPos();
+}
+
+void LLTabContainer::lockTabs(S32 num_tabs)
+{
+ // count current tabs or use supplied value and ensure no new tabs get
+ // inserted between them
+ mLockedTabCount = num_tabs > 0 ? llmin(getTabCount(), num_tabs) : getTabCount();
+}
+
+void LLTabContainer::unlockTabs()
+{
+ mLockedTabCount = 0;
+}
+
+void LLTabContainer::enableTabButton(S32 which, bool enable)
+{
+ if (which >= 0 && which < (S32)mTabList.size())
+ {
+ 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()
+{
+ // Remove all the tab buttons and delete them. Also, unlink all the child panels.
+ for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+
+ removeChild( tuple->mButton );
+ delete tuple->mButton;
+ tuple->mButton = NULL;
+
+ removeChild( tuple->mTabPanel );
+// delete tuple->mTabPanel;
+ tuple->mTabPanel = NULL;
+ }
+
+ // Actually delete the tuples themselves
+ std::for_each(mTabList.begin(), mTabList.end(), DeletePointer());
+ mTabList.clear();
+
+ // And there isn't a current tab any more
+ mCurrentTabIdx = -1;
+}
+
+LLPanel* LLTabContainer::getCurrentPanel()
+{
+ if (mCurrentTabIdx >= 0 && mCurrentTabIdx < (S32) mTabList.size())
+ {
+ return mTabList[mCurrentTabIdx]->mTabPanel;
+ }
+ return NULL;
+}
+
+S32 LLTabContainer::getCurrentPanelIndex()
+{
+ return mCurrentTabIdx;
+}
+
+S32 LLTabContainer::getTabCount()
+{
+ return mTabList.size();
+}
+
+LLPanel* LLTabContainer::getPanelByIndex(S32 index)
+{
+ if (index >= 0 && index < (S32)mTabList.size())
+ {
+ return mTabList[index]->mTabPanel;
+ }
+ return NULL;
+}
+
+S32 LLTabContainer::getIndexForPanel(LLPanel* panel)
+{
+ for (S32 index = 0; index < (S32)mTabList.size(); index++)
+ {
+ if (mTabList[index]->mTabPanel == panel)
+ {
+ return index;
+ }
+ }
+ return -1;
+}
+
+S32 LLTabContainer::getPanelIndexByTitle(const std::string& title)
+{
+ for (S32 index = 0 ; index < (S32)mTabList.size(); index++)
+ {
+ if (title == mTabList[index]->mButton->getLabelSelected())
+ {
+ return index;
+ }
+ }
+ return -1;
+}
+
+LLPanel* LLTabContainer::getPanelByName(const std::string& name)
+{
+ for (S32 index = 0 ; index < (S32)mTabList.size(); index++)
+ {
+ LLPanel *panel = mTabList[index]->mTabPanel;
+ if (name == panel->getName())
+ {
+ return panel;
+ }
+ }
+ return NULL;
+}
+
+// Change the name of the button for the current tab.
+void LLTabContainer::setCurrentTabName(const std::string& name)
+{
+ // Might not have a tab selected
+ if (mCurrentTabIdx < 0) return;
+
+ mTabList[mCurrentTabIdx]->mButton->setLabelSelected(name);
+ mTabList[mCurrentTabIdx]->mButton->setLabelUnselected(name);
+}
+
+void LLTabContainer::selectFirstTab()
+{
+ selectTab( 0 );
+}
+
+
+void LLTabContainer::selectLastTab()
+{
+ selectTab( mTabList.size()-1 );
+}
+
+void LLTabContainer::selectNextTab()
+{
+ if (mTabList.size() == 0)
+ {
+ return;
+ }
+
+ bool tab_has_focus = false;
+ if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus())
+ {
+ tab_has_focus = true;
+ }
+ S32 idx = mCurrentTabIdx+1;
+ if (idx >= (S32)mTabList.size())
+ idx = 0;
+ while (!selectTab(idx) && idx != mCurrentTabIdx)
+ {
+ idx = (idx + 1 ) % (S32)mTabList.size();
+ }
+
+ if (tab_has_focus)
+ {
+ mTabList[idx]->mButton->setFocus(true);
+ }
+}
+
+void LLTabContainer::selectPrevTab()
+{
+ bool tab_has_focus = false;
+ if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus())
+ {
+ tab_has_focus = true;
+ }
+ S32 idx = mCurrentTabIdx-1;
+ if (idx < 0)
+ idx = mTabList.size()-1;
+ while (!selectTab(idx) && idx != mCurrentTabIdx)
+ {
+ idx = idx - 1;
+ if (idx < 0)
+ idx = mTabList.size()-1;
+ }
+ if (tab_has_focus)
+ {
+ mTabList[idx]->mButton->setFocus(true);
+ }
+}
+
+bool LLTabContainer::selectTabPanel(LLPanel* child)
+{
+ S32 idx = 0;
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ if( tuple->mTabPanel == child )
+ {
+ return selectTab( idx );
+ }
+ idx++;
+ }
+ return false;
+}
+
+bool LLTabContainer::selectTab(S32 which)
+{
+ if (which >= getTabCount() || which < 0)
+ return false;
+
+ LLTabTuple* selected_tuple = getTab(which);
+ if (!selected_tuple)
+ return false;
+
+ LLSD cbdata;
+ if (selected_tuple->mTabPanel)
+ cbdata = selected_tuple->mTabPanel->getName();
+
+ bool result = false;
+ if (!mValidateSignal || (*mValidateSignal)(this, cbdata))
+ {
+ result = setTab(which);
+ if (result && mCommitSignal)
+ {
+ (*mCommitSignal)(this, cbdata);
+ }
+ }
+
+ return result;
+}
+
+// private
+bool LLTabContainer::setTab(S32 which)
+{
+ static LLUICachedControl<S32> tabcntr_arrow_btn_size ("UITabCntrArrowBtnSize", 0);
+ LLTabTuple* selected_tuple = getTab(which);
+ if (!selected_tuple)
+ {
+ return false;
+ }
+
+ bool is_visible = false;
+ if( selected_tuple->mButton->getEnabled() && selected_tuple->mVisible )
+ {
+ setCurrentPanelIndex(which);
+
+ S32 i = 0;
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ bool is_selected = ( tuple == selected_tuple );
+ // 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)
+ {
+ LLUIUsage::instance().logPanel(tuple->mTabPanel->getName());
+
+ // Make sure selected tab is within scroll region
+ if (mIsVertical)
+ {
+ S32 num_visible = getTabCount() - getMaxScrollPos();
+ if( i >= getScrollPos() && i <= getScrollPos() + num_visible)
+ {
+ setCurrentPanelIndex(which);
+ is_visible = true;
+ }
+ else
+ {
+ is_visible = false;
+ }
+ }
+ else if (!mHideScrollArrows && getMaxScrollPos() > 0)
+ {
+ if( i < getScrollPos() )
+ {
+ setScrollPos(i);
+ }
+ 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 && tuple->mButton ? tuple->mButton->getRect().getWidth() : 0);
+ S32 j = i - 1;
+ S32 min_scroll_pos = i;
+ if (running_tab_width < available_width_with_arrows)
+ {
+ while (j >= 0)
+ {
+ LLTabTuple* other_tuple = getTab(j);
+ running_tab_width += (other_tuple && other_tuple->mButton ? other_tuple->mButton->getRect().getWidth() : 0);
+ if (running_tab_width > available_width_with_arrows)
+ {
+ break;
+ }
+ j--;
+ }
+ min_scroll_pos = j + 1;
+ }
+ setScrollPos(llclamp(getScrollPos(), min_scroll_pos, i));
+ setScrollPos(llmin(getScrollPos(), getMaxScrollPos()));
+ }
+ is_visible = true;
+ }
+ else
+ {
+ is_visible = true;
+ }
+ }
+ i++;
+ }
+ }
+ if (mIsVertical && getCurrentPanelIndex() >= 0)
+ {
+ LLTabTuple* tuple = getTab(getCurrentPanelIndex());
+ tuple->mTabPanel->setVisible( true );
+ tuple->mButton->setToggleState( true );
+ }
+ return is_visible;
+}
+
+bool LLTabContainer::selectTabByName(const std::string& name)
+{
+ LLPanel* panel = getPanelByName(name);
+ if (!panel)
+ {
+ LL_WARNS() << "LLTabContainer::selectTabByName(" << name << ") failed" << LL_ENDL;
+ return false;
+ }
+
+ bool result = selectTabPanel(panel);
+ return result;
+}
+
+bool LLTabContainer::getTabPanelFlashing(LLPanel *child)
+{
+ LLTabTuple* tuple = getTabByPanel(child);
+ if( tuple )
+ {
+ return tuple->mButton->getFlashing();
+ }
+ return false;
+}
+
+void LLTabContainer::setTabPanelFlashing(LLPanel* child, bool state )
+{
+ LLTabTuple* tuple = getTabByPanel(child);
+ if( tuple )
+ {
+ tuple->mButton->setFlashing( state );
+ }
+}
+
+void LLTabContainer::setTabImage(LLPanel* child, std::string image_name, const LLColor4& color)
+{
+ LLTabTuple* tuple = getTabByPanel(child);
+ if( tuple )
+ {
+ tuple->mButton->setImageOverlay(image_name, LLFontGL::LEFT, color);
+ reshapeTuple(tuple);
+ }
+}
+
+void LLTabContainer::setTabImage(LLPanel* child, const LLUUID& image_id, const LLColor4& color)
+{
+ LLTabTuple* tuple = getTabByPanel(child);
+ if( tuple )
+ {
+ tuple->mButton->setImageOverlay(image_id, LLFontGL::LEFT, color);
+ reshapeTuple(tuple);
+ }
+}
+
+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)
+{
+ static LLUICachedControl<S32> tab_padding ("UITabPadding", 0);
+
+ if (!mIsVertical)
+ {
+ S32 image_overlay_width = 0;
+
+ if(mCustomIconCtrlUsed)
+ {
+ LLCustomButtonIconCtrl* button = dynamic_cast<LLCustomButtonIconCtrl*>(tuple->mButton);
+ LLIconCtrl* icon_ctrl = button ? button->getIconCtrl() : NULL;
+ image_overlay_width = icon_ctrl ? icon_ctrl->getRect().getWidth() : 0;
+ }
+ else
+ {
+ image_overlay_width = tuple->mButton->getImageOverlay().notNull() ?
+ tuple->mButton->getImageOverlay()->getImage()->getWidth(0) : 0;
+ }
+ // remove current width from total tab strip width
+ mTotalTabWidth -= tuple->mButton->getRect().getWidth();
+
+ tuple->mPadding = image_overlay_width;
+
+ tuple->mButton->reshape(llclamp(mFont->getWidth(tuple->mButton->getLabelSelected()) + tab_padding + tuple->mPadding, mMinTabWidth, mMaxTabWidth),
+ tuple->mButton->getRect().getHeight());
+ // add back in button width to total tab strip width
+ mTotalTabWidth += tuple->mButton->getRect().getWidth();
+
+ // tabs have changed size, might need to scroll to see current tab
+ updateMaxScrollPos();
+ }
+}
+
+void LLTabContainer::setTitle(const std::string& title)
+{
+ if (mTitleBox)
+ {
+ mTitleBox->setText( title );
+ }
+}
+
+const std::string LLTabContainer::getPanelTitle(S32 index)
+{
+ if (index >= 0 && index < (S32)mTabList.size())
+ {
+ LLButton* tab_button = mTabList[index]->mButton;
+ return tab_button->getLabelSelected();
+ }
+ return LLStringUtil::null;
+}
+
+void LLTabContainer::setTopBorderHeight(S32 height)
+{
+ mTopBorderHeight = height;
+}
+
+S32 LLTabContainer::getTopBorderHeight() const
+{
+ return mTopBorderHeight;
+}
+
+void LLTabContainer::setRightTabBtnOffset(S32 offset)
+{
+ mNextArrowBtn->translate( -offset - mRightTabBtnOffset, 0 );
+ mRightTabBtnOffset = offset;
+ updateMaxScrollPos();
+}
+
+void LLTabContainer::setPanelTitle(S32 index, const std::string& title)
+{
+ static LLUICachedControl<S32> tab_padding ("UITabPadding", 0);
+
+ if (index >= 0 && index < getTabCount())
+ {
+ LLTabTuple* tuple = getTab(index);
+ LLButton* tab_button = tuple->mButton;
+ const LLFontGL* fontp = LLFontGL::getFontSansSerifSmall();
+ mTotalTabWidth -= tab_button->getRect().getWidth();
+ tab_button->reshape(llclamp(fontp->getWidth(title) + tab_padding + tuple->mPadding, mMinTabWidth, mMaxTabWidth), tab_button->getRect().getHeight());
+ mTotalTabWidth += tab_button->getRect().getWidth();
+ tab_button->setLabelSelected(title);
+ tab_button->setLabelUnselected(title);
+ }
+ updateMaxScrollPos();
+}
+
+
+void LLTabContainer::onTabBtn( const LLSD& data, LLPanel* panel )
+{
+ LLTabTuple* tuple = getTabByPanel(panel);
+ selectTabPanel( panel );
+
+ if (tuple)
+ {
+ tuple->mTabPanel->setFocus(true);
+ }
+}
+
+void LLTabContainer::onNextBtn( const LLSD& data )
+{
+ if (!mScrolled)
+ {
+ scrollNext();
+ }
+ mScrolled = false;
+
+ if(mCurrentTabIdx < mTabList.size()-1)
+ {
+ selectNextTab();
+ }
+}
+
+void LLTabContainer::onNextBtnHeld( const LLSD& data )
+{
+ if (mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME)
+ {
+ mScrollTimer.reset();
+ scrollNext();
+
+ if(mCurrentTabIdx < mTabList.size()-1)
+ {
+ selectNextTab();
+ }
+ mScrolled = true;
+ }
+}
+
+void LLTabContainer::onPrevBtn( const LLSD& data )
+{
+ if (!mScrolled)
+ {
+ scrollPrev();
+ }
+ mScrolled = false;
+
+ if(mCurrentTabIdx > 0)
+ {
+ selectPrevTab();
+ }
+}
+
+void LLTabContainer::onJumpFirstBtn( const LLSD& data )
+{
+ mScrollPos = 0;
+}
+
+void LLTabContainer::onJumpLastBtn( const LLSD& data )
+{
+ mScrollPos = mMaxScrollPos;
+}
+
+void LLTabContainer::onPrevBtnHeld( const LLSD& data )
+{
+ if (mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME)
+ {
+ mScrollTimer.reset();
+ scrollPrev();
+
+ if(mCurrentTabIdx > 0)
+ {
+ selectPrevTab();
+ }
+ mScrolled = true;
+ }
+}
+
+// private
+
+void LLTabContainer::initButtons()
+{
+ // Hack:
+ if (getRect().getHeight() == 0 || mPrevArrowBtn)
+ {
+ return; // Don't have a rect yet or already got called
+ }
+
+ if (mIsVertical)
+ {
+ static LLUICachedControl<S32> tabcntrv_arrow_btn_size ("UITabCntrvArrowBtnSize", 0);
+ // Left and right scroll arrows (for when there are too many tabs to show all at once).
+ S32 btn_top = getRect().getHeight();
+ S32 btn_top_lower = getRect().mBottom+tabcntrv_arrow_btn_size;
+
+ LLRect up_arrow_btn_rect;
+ up_arrow_btn_rect.setLeftTopAndSize( mMinTabWidth/2 , btn_top, tabcntrv_arrow_btn_size, tabcntrv_arrow_btn_size );
+
+ LLRect down_arrow_btn_rect;
+ down_arrow_btn_rect.setLeftTopAndSize( mMinTabWidth/2 , btn_top_lower, tabcntrv_arrow_btn_size, tabcntrv_arrow_btn_size );
+
+ LLButton::Params prev_btn_params;
+ prev_btn_params.name(std::string("Up Arrow"));
+ prev_btn_params.rect(up_arrow_btn_rect);
+ prev_btn_params.follows.flags(FOLLOWS_TOP | FOLLOWS_LEFT);
+ prev_btn_params.image_unselected.name("scrollbutton_up_out_blue.tga");
+ prev_btn_params.image_selected.name("scrollbutton_up_in_blue.tga");
+ prev_btn_params.click_callback.function(boost::bind(&LLTabContainer::onPrevBtn, this, _2));
+ mPrevArrowBtn = LLUICtrlFactory::create<LLButton>(prev_btn_params);
+
+ LLButton::Params next_btn_params;
+ next_btn_params.name(std::string("Down Arrow"));
+ next_btn_params.rect(down_arrow_btn_rect);
+ next_btn_params.follows.flags(FOLLOWS_BOTTOM | FOLLOWS_LEFT);
+ next_btn_params.image_unselected.name("scrollbutton_down_out_blue.tga");
+ next_btn_params.image_selected.name("scrollbutton_down_in_blue.tga");
+ next_btn_params.click_callback.function(boost::bind(&LLTabContainer::onNextBtn, this, _2));
+ mNextArrowBtn = LLUICtrlFactory::create<LLButton>(next_btn_params);
+ }
+ else // Horizontal
+ {
+ static LLUICachedControl<S32> tabcntr_arrow_btn_size ("UITabCntrArrowBtnSize", 0);
+ S32 arrow_fudge = 1; // match new art better
+
+ // Left and right scroll arrows (for when there are too many tabs to show all at once).
+ S32 btn_top = (getTabPosition() == TOP ) ? getRect().getHeight() - getTopBorderHeight() : tabcntr_arrow_btn_size + 1;
+
+ LLRect left_arrow_btn_rect;
+ left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1+tabcntr_arrow_btn_size, btn_top + arrow_fudge, tabcntr_arrow_btn_size, mTabHeight );
+
+ LLRect jump_left_arrow_btn_rect;
+ jump_left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1, btn_top + arrow_fudge, tabcntr_arrow_btn_size, mTabHeight );
+
+ S32 right_pad = tabcntr_arrow_btn_size + LLPANEL_BORDER_WIDTH + 1;
+
+ LLRect right_arrow_btn_rect;
+ right_arrow_btn_rect.setLeftTopAndSize( getRect().getWidth() - mRightTabBtnOffset - right_pad - tabcntr_arrow_btn_size,
+ btn_top + arrow_fudge,
+ tabcntr_arrow_btn_size, mTabHeight );
+
+
+ LLRect jump_right_arrow_btn_rect;
+ jump_right_arrow_btn_rect.setLeftTopAndSize( getRect().getWidth() - mRightTabBtnOffset - right_pad,
+ btn_top + arrow_fudge,
+ tabcntr_arrow_btn_size, mTabHeight );
+
+ LLButton::Params p;
+ p.name(std::string("Jump Left Arrow"));
+ p.image_unselected.name("jump_left_out.tga");
+ p.image_selected.name("jump_left_in.tga");
+ p.click_callback.function(boost::bind(&LLTabContainer::onJumpFirstBtn, this, _2));
+ p.rect(jump_left_arrow_btn_rect);
+ p.follows.flags(FOLLOWS_LEFT);
+
+ mJumpPrevArrowBtn = LLUICtrlFactory::create<LLButton>(p);
+
+ p = LLButton::Params();
+ p.name(std::string("Left Arrow"));
+ p.rect(left_arrow_btn_rect);
+ p.follows.flags(FOLLOWS_LEFT);
+ p.image_unselected.name("scrollbutton_left_out_blue.tga");
+ p.image_selected.name("scrollbutton_left_in_blue.tga");
+ p.click_callback.function(boost::bind(&LLTabContainer::onPrevBtn, this, _2));
+ p.mouse_held_callback.function(boost::bind(&LLTabContainer::onPrevBtnHeld, this, _2));
+
+ mPrevArrowBtn = LLUICtrlFactory::create<LLButton>(p);
+
+ p = LLButton::Params();
+ p.name(std::string("Jump Right Arrow"));
+ p.rect(jump_right_arrow_btn_rect);
+ p.follows.flags(FOLLOWS_RIGHT);
+ p.image_unselected.name("jump_right_out.tga");
+ p.image_selected.name("jump_right_in.tga");
+ p.click_callback.function(boost::bind(&LLTabContainer::onJumpLastBtn, this, _2));
+
+ mJumpNextArrowBtn = LLUICtrlFactory::create<LLButton>(p);
+
+ p = LLButton::Params();
+ p.name(std::string("Right Arrow"));
+ p.rect(right_arrow_btn_rect);
+ p.follows.flags(FOLLOWS_RIGHT);
+ p.image_unselected.name("scrollbutton_right_out_blue.tga");
+ p.image_selected.name("scrollbutton_right_in_blue.tga");
+ p.click_callback.function(boost::bind(&LLTabContainer::onNextBtn, this, _2));
+ p.mouse_held_callback.function(boost::bind(&LLTabContainer::onNextBtnHeld, this, _2));
+
+ mNextArrowBtn = LLUICtrlFactory::create<LLButton>(p);
+
+ if( getTabPosition() == TOP )
+ {
+ mNextArrowBtn->setFollowsTop();
+ mPrevArrowBtn->setFollowsTop();
+ mJumpPrevArrowBtn->setFollowsTop();
+ mJumpNextArrowBtn->setFollowsTop();
+ }
+ else
+ {
+ mNextArrowBtn->setFollowsBottom();
+ mPrevArrowBtn->setFollowsBottom();
+ mJumpPrevArrowBtn->setFollowsBottom();
+ mJumpNextArrowBtn->setFollowsBottom();
+ }
+ }
+
+ mPrevArrowBtn->setTabStop(false);
+ addChild(mPrevArrowBtn);
+
+ mNextArrowBtn->setTabStop(false);
+ addChild(mNextArrowBtn);
+
+ if (mJumpPrevArrowBtn)
+ {
+ mJumpPrevArrowBtn->setTabStop(false);
+ addChild(mJumpPrevArrowBtn);
+ }
+
+ if (mJumpNextArrowBtn)
+ {
+ mJumpNextArrowBtn->setTabStop(false);
+ addChild(mJumpNextArrowBtn);
+ }
+
+ // set default tab group to be panel contents
+ setDefaultTabGroup(1);
+}
+
+//this is a work around for the current LLPanel::initFromParams hack
+//so that it doesn't overwrite the default tab group.
+//will be removed when LLPanel is fixed soon.
+void LLTabContainer::initFromParams(const LLPanel::Params& p)
+{
+ LLPanel::initFromParams(p);
+
+ setDefaultTabGroup(1);
+}
+
+LLTabTuple* LLTabContainer::getTabByPanel(LLPanel* child)
+{
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ if( tuple->mTabPanel == child )
+ {
+ return tuple;
+ }
+ }
+ return NULL;
+}
+
+void LLTabContainer::insertTuple(LLTabTuple * tuple, eInsertionPoint insertion_point)
+{
+ switch(insertion_point)
+ {
+ case START:
+ // insert the new tab in the front of the list
+ mTabList.insert(mTabList.begin() + mLockedTabCount, tuple);
+ break;
+ case LEFT_OF_CURRENT:
+ // insert the new tab before the current tab (but not before mLockedTabCount)
+ {
+ tuple_list_t::iterator current_iter = mTabList.begin() + llmax(mLockedTabCount, mCurrentTabIdx);
+ mTabList.insert(current_iter, tuple);
+ }
+ break;
+
+ case RIGHT_OF_CURRENT:
+ // insert the new tab after the current tab (but not before mLockedTabCount)
+ {
+ tuple_list_t::iterator current_iter = mTabList.begin() + llmax(mLockedTabCount, mCurrentTabIdx + 1);
+ mTabList.insert(current_iter, tuple);
+ }
+ break;
+ case END:
+ default:
+ mTabList.push_back( tuple );
+ }
+}
+
+
+
+void LLTabContainer::updateMaxScrollPos()
+{
+ static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
+ bool no_scroll = true;
+ if (mIsVertical)
+ {
+ S32 tab_total_height = (BTN_HEIGHT + tabcntrv_pad) * getTabCount();
+ S32 available_height = getRect().getHeight() - getTopBorderHeight();
+ if( tab_total_height > available_height )
+ {
+ static LLUICachedControl<S32> tabcntrv_arrow_btn_size ("UITabCntrvArrowBtnSize", 0);
+ S32 available_height_with_arrows = getRect().getHeight() - 2*(tabcntrv_arrow_btn_size + 3*tabcntrv_pad) - mNextArrowBtn->getRect().mBottom;
+ S32 additional_needed = tab_total_height - available_height_with_arrows;
+ setMaxScrollPos((S32) ceil(additional_needed / float(BTN_HEIGHT + tabcntrv_pad) ) );
+ no_scroll = false;
+ }
+ }
+ else
+ {
+ static LLUICachedControl<S32> tabcntr_tab_h_pad ("UITabCntrTabHPad", 0);
+ static LLUICachedControl<S32> tabcntr_arrow_btn_size ("UITabCntrArrowBtnSize", 0);
+ static LLUICachedControl<S32> tabcntr_tab_partial_width ("UITabCntrTabPartialWidth", 0);
+ S32 tab_space = 0;
+ S32 available_space = 0;
+ tab_space = mTotalTabWidth;
+ available_space = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_tab_h_pad);
+
+ if( tab_space > available_space )
+ {
+ S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_arrow_btn_size + tabcntr_arrow_btn_size + 1);
+ // subtract off reserved portion on left
+ available_width_with_arrows -= tabcntr_tab_partial_width;
+
+ S32 running_tab_width = 0;
+ setMaxScrollPos(getTabCount());
+ for(tuple_list_t::reverse_iterator tab_it = mTabList.rbegin(); tab_it != mTabList.rend(); ++tab_it)
+ {
+ running_tab_width += (*tab_it)->mButton->getRect().getWidth();
+ if (running_tab_width > available_width_with_arrows)
+ {
+ break;
+ }
+ setMaxScrollPos(getMaxScrollPos()-1);
+ }
+ // in case last tab doesn't actually fit on screen, make it the last scrolling position
+ setMaxScrollPos(llmin(getMaxScrollPos(), getTabCount() - 1));
+ no_scroll = false;
+ }
+ }
+ if (no_scroll)
+ {
+ setMaxScrollPos(0);
+ setScrollPos(0);
+ }
+ if (getScrollPos() > getMaxScrollPos())
+ {
+ setScrollPos(getMaxScrollPos()); // maybe just enforce this via limits in setScrollPos instead?
+ }
+}
+
+void LLTabContainer::commitHoveredButton(S32 x, S32 y)
+{
+ if (!getTabsHidden() && hasMouseCapture())
+ {
+ for (tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLButton* button = (*iter)->mButton;
+ LLPanel* panel = (*iter)->mTabPanel;
+ if (button->getEnabled() && button->getVisible() && !panel->getVisible())
+ {
+ S32 local_x = x - button->getRect().mLeft;
+ S32 local_y = y - button->getRect().mBottom;
+ if (button->pointInView(local_x, local_y))
+ {
+ button->onCommit();
+ break;
+ }
+ }
+ }
+ }
+}
+
+S32 LLTabContainer::getTotalTabWidth() const
+{
+ return mTotalTabWidth;
+}
+
+void LLTabContainer::setTabVisibility( LLPanel const *aPanel, bool aVisible )
+{
+ for( tuple_list_t::const_iterator itr = mTabList.begin(); itr != mTabList.end(); ++itr )
+ {
+ LLTabTuple const *pTT = *itr;
+ if( pTT->mTabPanel == aPanel )
+ {
+ pTT->mVisible = aVisible;
+ break;
+ }
+ }
+
+ bool foundTab( false );
+ for( tuple_list_t::const_iterator itr = mTabList.begin(); itr != mTabList.end(); ++itr )
+ {
+ LLTabTuple const *pTT = *itr;
+ if( pTT->mVisible )
+ {
+ this->selectTab( itr - mTabList.begin() );
+ foundTab = true;
+ break;
+ }
+ }
+
+ if( foundTab )
+ this->setVisible( true );
+ else
+ this->setVisible( false );
+
+ updateMaxScrollPos();
+}
diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h
index d5fee27406..1684b9570d 100644
--- a/indra/llui/lltabcontainer.h
+++ b/indra/llui/lltabcontainer.h
@@ -1,326 +1,330 @@
-/**
- * @file lltabcontainer.h
- * @brief LLTabContainer 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$
- */
-
-#ifndef LL_TABCONTAINER_H
-#define LL_TABCONTAINER_H
-
-#include "llpanel.h"
-#include "lltextbox.h"
-#include "llframetimer.h"
-#include "lliconctrl.h"
-#include "llbutton.h"
-
-class LLTabTuple;
-
-class LLTabContainer : public LLPanel
-{
-public:
- enum TabPosition
- {
- TOP,
- BOTTOM,
- LEFT
- };
- typedef enum e_insertion_point
- {
- START,
- END,
- LEFT_OF_CURRENT,
- RIGHT_OF_CURRENT
- } eInsertionPoint;
-
- struct TabPositions : public LLInitParam::TypeValuesHelper<LLTabContainer::TabPosition, TabPositions>
- {
- static void declareValues();
- };
-
- struct TabParams : public LLInitParam::Block<TabParams>
- {
- Optional<LLUIImage*> tab_top_image_unselected,
- tab_top_image_selected,
- tab_top_image_flash,
- tab_bottom_image_unselected,
- tab_bottom_image_selected,
- tab_bottom_image_flash,
- tab_left_image_unselected,
- tab_left_image_selected,
- tab_left_image_flash;
- TabParams();
- };
-
- struct Params
- : public LLInitParam::Block<Params, LLPanel::Params>
- {
- Optional<TabPosition, TabPositions> tab_position;
- Optional<S32> tab_width,
- tab_min_width,
- tab_max_width,
- tab_height,
- label_pad_bottom,
- label_pad_left;
-
- Optional<bool> hide_tabs;
- Optional<bool> hide_scroll_arrows;
- Optional<S32> tab_padding_right;
-
- Optional<TabParams> first_tab,
- middle_tab,
- last_tab;
-
- /**
- * Tab label horizontal alignment
- */
- Optional<LLFontGL::HAlign> font_halign;
-
- /**
- * Tab label ellipses
- */
- Optional<bool> use_ellipses;
-
- /**
- * Use LLCustomButtonIconCtrl or LLButton in LLTabTuple
- */
- Optional<bool> use_custom_icon_ctrl;
-
- /**
- * Open tabs on hover in drag and drop situations
- */
- Optional<bool> open_tabs_on_drag_and_drop;
-
- /**
- * Enable tab flashing
- */
- Optional<bool> enable_tabs_flashing;
- Optional<LLUIColor> tabs_flashing_color;
-
- /**
- * Paddings for LLIconCtrl in case of LLCustomButtonIconCtrl usage(use_custom_icon_ctrl = true)
- */
- Optional<S32> tab_icon_ctrl_pad;
-
- Params();
- };
-
-protected:
- LLTabContainer(const Params&);
- friend class LLUICtrlFactory;
-
-public:
- //LLTabContainer( const std::string& name, const LLRect& rect, TabPosition pos,
- // bool bordered, bool is_vertical);
-
- /*virtual*/ ~LLTabContainer();
-
- // from LLView
- /*virtual*/ void setValue(const LLSD& value);
-
- /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true);
- /*virtual*/ void draw();
- /*virtual*/ bool handleMouseDown( S32 x, S32 y, MASK mask );
- /*virtual*/ bool handleHover( S32 x, S32 y, MASK mask );
- /*virtual*/ bool handleMouseUp( S32 x, S32 y, MASK mask );
- /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleKeyHere(KEY key, MASK mask);
- /*virtual*/ bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
- EDragAndDropType type, void* cargo_data,
- EAcceptance* accept, std::string& tooltip);
- /*virtual*/ LLView* getChildView(const std::string& name, bool recurse = true) const;
- /*virtual*/ LLView* findChildView(const std::string& name, bool recurse = true) const;
- /*virtual*/ void initFromParams(const LLPanel::Params& p);
- /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0);
- /*virtual*/ bool postBuild();
-
- struct TabPanelParams : public LLInitParam::Block<TabPanelParams>
- {
- Mandatory<LLPanel*> panel;
-
- Optional<std::string> label;
- Optional<bool> select_tab,
- is_placeholder;
- Optional<S32> indent;
- Optional<eInsertionPoint> insert_at;
- Optional<void*> user_data;
-
- TabPanelParams()
- : panel("panel", NULL),
- label("label"),
- select_tab("select_tab"),
- is_placeholder("is_placeholder"),
- indent("indent"),
- insert_at("insert_at", END)
- {}
- };
-
- void addTabPanel(LLPanel* panel);
- void addTabPanel(const TabPanelParams& panel);
- void addPlaceholder(LLPanel* child, const std::string& label);
- void removeTabPanel( LLPanel* child );
- void lockTabs(S32 num_tabs = 0);
- void unlockTabs();
- S32 getNumLockedTabs() { return mLockedTabCount; }
- void enableTabButton(S32 which, bool enable);
- void deleteAllTabs();
- LLPanel* getCurrentPanel();
- S32 getCurrentPanelIndex();
- S32 getTabCount();
- LLPanel* getPanelByIndex(S32 index);
- S32 getIndexForPanel(LLPanel* panel);
- S32 getPanelIndexByTitle(const std::string& title);
- LLPanel* getPanelByName(const std::string& name);
- S32 getTotalTabWidth() const;
- void setCurrentTabName(const std::string& name);
-
- void selectFirstTab();
- void selectLastTab();
- void selectNextTab();
- 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);
- void setTabImage(LLPanel* child, std::string img_name, const LLColor4& color = LLColor4::white);
- void setTabImage(LLPanel* child, const LLUUID& img_id, const LLColor4& color = LLColor4::white);
- void setTabImage(LLPanel* child, LLIconCtrl* icon);
- void setTitle( const std::string& title );
- const std::string getPanelTitle(S32 index);
-
- void setTopBorderHeight(S32 height);
- S32 getTopBorderHeight() const;
-
- void setRightTabBtnOffset( S32 offset );
- void setPanelTitle(S32 index, const std::string& title);
-
- TabPosition getTabPosition() const { return mTabPosition; }
- void setMinTabWidth(S32 width) { mMinTabWidth = width; }
- void setMaxTabWidth(S32 width) { mMaxTabWidth = width; }
- S32 getMinTabWidth() const { return mMinTabWidth; }
- S32 getMaxTabWidth() const { return mMaxTabWidth; }
-
- void setTabVisibility( LLPanel const *aPanel, bool );
-
- void startDragAndDropDelayTimer() { mDragAndDropDelayTimer.start(); }
-
- void onTabBtn( const LLSD& data, LLPanel* panel );
- void onNextBtn(const LLSD& data);
- void onNextBtnHeld(const LLSD& data);
- void onPrevBtn(const LLSD& data);
- void onPrevBtnHeld(const LLSD& data);
- void onJumpFirstBtn( const LLSD& data );
- void onJumpLastBtn( const LLSD& data );
-
-private:
-
- void initButtons();
-
- bool setTab(S32 which);
-
- LLTabTuple* getTab(S32 index) { return mTabList[index]; }
- LLTabTuple* getTabByPanel(LLPanel* child);
- void insertTuple(LLTabTuple * tuple, eInsertionPoint insertion_point);
-
- S32 getScrollPos() const { return mScrollPos; }
- void setScrollPos(S32 pos) { mScrollPos = pos; }
- S32 getMaxScrollPos() const { return mMaxScrollPos; }
- void setMaxScrollPos(S32 pos) { mMaxScrollPos = pos; }
- S32 getScrollPosPixels() const { return mScrollPosPixels; }
- void setScrollPosPixels(S32 pixels) { mScrollPosPixels = pixels; }
-
- void setTabsHidden(bool hidden) { mTabsHidden = hidden; }
- bool getTabsHidden() const { return mTabsHidden; }
-
- void scrollPrev() { mScrollPos = llmax(0, mScrollPos-1); } // No wrap
- void scrollNext() { mScrollPos = llmin(mScrollPos+1, mMaxScrollPos); } // No wrap
-
- void updateMaxScrollPos();
- void commitHoveredButton(S32 x, S32 y);
-
- // updates tab button images given the tuple, tab position and the corresponding params
- void update_images(LLTabTuple* tuple, TabParams params, LLTabContainer::TabPosition pos);
- void reshapeTuple(LLTabTuple* tuple);
-
- // Variables
-
- typedef std::vector<LLTabTuple*> tuple_list_t;
- tuple_list_t mTabList;
-
- S32 mCurrentTabIdx;
- bool mTabsHidden;
- bool mHideScrollArrows;
-
- bool mScrolled;
- LLFrameTimer mScrollTimer;
- S32 mScrollPos;
- S32 mScrollPosPixels;
- S32 mMaxScrollPos;
-
- LLTextBox* mTitleBox;
-
- S32 mTopBorderHeight;
- TabPosition mTabPosition;
- S32 mLockedTabCount;
- S32 mMinTabWidth;
- LLButton* mPrevArrowBtn;
- LLButton* mNextArrowBtn;
-
- bool mIsVertical;
-
- // Horizontal specific
- LLButton* mJumpPrevArrowBtn;
- LLButton* mJumpNextArrowBtn;
-
- S32 mRightTabBtnOffset; // Extra room to the right of the tab buttons.
-
- S32 mMaxTabWidth;
- S32 mTotalTabWidth;
- S32 mTabHeight;
-
- // Padding under the text labels of tab buttons
- S32 mLabelPadBottom;
- // Padding to the left of text labels of tab buttons
- S32 mLabelPadLeft;
-
- LLFrameTimer mDragAndDropDelayTimer;
-
- LLFontGL::HAlign mFontHalign;
- const LLFontGL* mFont;
-
- TabParams mFirstTabParams;
- TabParams mMiddleTabParams;
- TabParams mLastTabParams;
-
- bool mCustomIconCtrlUsed;
- bool mOpenTabsOnDragAndDrop;
- bool mEnableTabsFlashing;
- LLUIColor mTabsFlashingColor;
- S32 mTabIconCtrlPad;
- bool mUseTabEllipses;
- LLFrameTimer mMouseDownTimer;
-};
-
-#endif // LL_TABCONTAINER_H
+/**
+ * @file lltabcontainer.h
+ * @brief LLTabContainer 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$
+ */
+
+#ifndef LL_TABCONTAINER_H
+#define LL_TABCONTAINER_H
+
+#include "llpanel.h"
+#include "lltextbox.h"
+#include "llframetimer.h"
+#include "lliconctrl.h"
+#include "llbutton.h"
+
+class LLTabTuple;
+
+class LLTabContainer : public LLPanel
+{
+public:
+ enum TabPosition
+ {
+ TOP,
+ BOTTOM,
+ LEFT
+ };
+ typedef enum e_insertion_point
+ {
+ START,
+ END,
+ LEFT_OF_CURRENT,
+ RIGHT_OF_CURRENT
+ } eInsertionPoint;
+
+ struct TabPositions : public LLInitParam::TypeValuesHelper<LLTabContainer::TabPosition, TabPositions>
+ {
+ static void declareValues();
+ };
+
+ struct TabParams : public LLInitParam::Block<TabParams>
+ {
+ Optional<LLUIImage*> tab_top_image_unselected,
+ tab_top_image_selected,
+ tab_top_image_flash,
+ tab_bottom_image_unselected,
+ tab_bottom_image_selected,
+ tab_bottom_image_flash,
+ tab_left_image_unselected,
+ tab_left_image_selected,
+ tab_left_image_flash;
+ TabParams();
+ };
+
+ struct Params
+ : public LLInitParam::Block<Params, LLPanel::Params>
+ {
+ Optional<TabPosition, TabPositions> tab_position;
+ Optional<S32> tab_width,
+ tab_min_width,
+ tab_max_width,
+ tab_height,
+ label_pad_bottom,
+ label_pad_left;
+
+ Optional<bool> hide_tabs;
+ Optional<bool> hide_scroll_arrows;
+ Optional<S32> tab_padding_right;
+
+ Optional<TabParams> first_tab,
+ middle_tab,
+ last_tab;
+
+ /**
+ * Tab label horizontal alignment
+ */
+ Optional<LLFontGL::HAlign> font_halign;
+
+ /**
+ * Tab label ellipses
+ */
+ Optional<bool> use_ellipses;
+
+ /**
+ * Use LLCustomButtonIconCtrl or LLButton in LLTabTuple
+ */
+ Optional<bool> use_custom_icon_ctrl;
+
+ /**
+ * Open tabs on hover in drag and drop situations
+ */
+ Optional<bool> open_tabs_on_drag_and_drop;
+
+ /**
+ * Enable tab flashing
+ */
+ Optional<bool> enable_tabs_flashing;
+ Optional<LLUIColor> tabs_flashing_color;
+
+ /**
+ * Paddings for LLIconCtrl in case of LLCustomButtonIconCtrl usage(use_custom_icon_ctrl = true)
+ */
+ Optional<S32> tab_icon_ctrl_pad;
+
+ Optional<bool> use_tab_offset;
+
+ Params();
+ };
+
+protected:
+ LLTabContainer(const Params&);
+ friend class LLUICtrlFactory;
+
+public:
+ //LLTabContainer( const std::string& name, const LLRect& rect, TabPosition pos,
+ // bool bordered, bool is_vertical);
+
+ /*virtual*/ ~LLTabContainer();
+
+ // from LLView
+ /*virtual*/ void setValue(const LLSD& value);
+
+ /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true);
+ /*virtual*/ void draw();
+ /*virtual*/ bool handleMouseDown( S32 x, S32 y, MASK mask );
+ /*virtual*/ bool handleHover( S32 x, S32 y, MASK mask );
+ /*virtual*/ bool handleMouseUp( S32 x, S32 y, MASK mask );
+ /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleKeyHere(KEY key, MASK mask);
+ /*virtual*/ bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
+ EDragAndDropType type, void* cargo_data,
+ EAcceptance* accept, std::string& tooltip);
+ /*virtual*/ LLView* getChildView(const std::string& name, bool recurse = true) const;
+ /*virtual*/ LLView* findChildView(const std::string& name, bool recurse = true) const;
+ /*virtual*/ void initFromParams(const LLPanel::Params& p);
+ /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0);
+ /*virtual*/ bool postBuild();
+
+ struct TabPanelParams : public LLInitParam::Block<TabPanelParams>
+ {
+ Mandatory<LLPanel*> panel;
+
+ Optional<std::string> label;
+ Optional<bool> select_tab,
+ is_placeholder;
+ Optional<S32> indent;
+ Optional<eInsertionPoint> insert_at;
+ Optional<void*> user_data;
+
+ TabPanelParams()
+ : panel("panel", NULL),
+ label("label"),
+ select_tab("select_tab"),
+ is_placeholder("is_placeholder"),
+ indent("indent"),
+ insert_at("insert_at", END)
+ {}
+ };
+
+ void addTabPanel(LLPanel* panel);
+ void addTabPanel(const TabPanelParams& panel);
+ void addPlaceholder(LLPanel* child, const std::string& label);
+ void removeTabPanel( LLPanel* child );
+ void lockTabs(S32 num_tabs = 0);
+ void unlockTabs();
+ S32 getNumLockedTabs() { return mLockedTabCount; }
+ void enableTabButton(S32 which, bool enable);
+ void deleteAllTabs();
+ LLPanel* getCurrentPanel();
+ S32 getCurrentPanelIndex();
+ S32 getTabCount();
+ LLPanel* getPanelByIndex(S32 index);
+ S32 getIndexForPanel(LLPanel* panel);
+ S32 getPanelIndexByTitle(const std::string& title);
+ LLPanel* getPanelByName(const std::string& name);
+ S32 getTotalTabWidth() const;
+ void setCurrentTabName(const std::string& name);
+
+ void selectFirstTab();
+ void selectLastTab();
+ void selectNextTab();
+ 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);
+ void setTabImage(LLPanel* child, std::string img_name, const LLColor4& color = LLColor4::white);
+ void setTabImage(LLPanel* child, const LLUUID& img_id, const LLColor4& color = LLColor4::white);
+ void setTabImage(LLPanel* child, LLIconCtrl* icon);
+ void setTitle( const std::string& title );
+ const std::string getPanelTitle(S32 index);
+
+ void setTopBorderHeight(S32 height);
+ S32 getTopBorderHeight() const;
+
+ void setRightTabBtnOffset( S32 offset );
+ void setPanelTitle(S32 index, const std::string& title);
+
+ TabPosition getTabPosition() const { return mTabPosition; }
+ void setMinTabWidth(S32 width) { mMinTabWidth = width; }
+ void setMaxTabWidth(S32 width) { mMaxTabWidth = width; }
+ S32 getMinTabWidth() const { return mMinTabWidth; }
+ S32 getMaxTabWidth() const { return mMaxTabWidth; }
+
+ void setTabVisibility( LLPanel const *aPanel, bool );
+
+ void startDragAndDropDelayTimer() { mDragAndDropDelayTimer.start(); }
+
+ void onTabBtn( const LLSD& data, LLPanel* panel );
+ void onNextBtn(const LLSD& data);
+ void onNextBtnHeld(const LLSD& data);
+ void onPrevBtn(const LLSD& data);
+ void onPrevBtnHeld(const LLSD& data);
+ void onJumpFirstBtn( const LLSD& data );
+ void onJumpLastBtn( const LLSD& data );
+
+private:
+
+ void initButtons();
+
+ bool setTab(S32 which);
+
+ LLTabTuple* getTab(S32 index) { return mTabList[index]; }
+ LLTabTuple* getTabByPanel(LLPanel* child);
+ void insertTuple(LLTabTuple * tuple, eInsertionPoint insertion_point);
+
+ S32 getScrollPos() const { return mScrollPos; }
+ void setScrollPos(S32 pos) { mScrollPos = pos; }
+ S32 getMaxScrollPos() const { return mMaxScrollPos; }
+ void setMaxScrollPos(S32 pos) { mMaxScrollPos = pos; }
+ S32 getScrollPosPixels() const { return mScrollPosPixels; }
+ void setScrollPosPixels(S32 pixels) { mScrollPosPixels = pixels; }
+
+ void setTabsHidden(bool hidden) { mTabsHidden = hidden; }
+ bool getTabsHidden() const { return mTabsHidden; }
+
+ void scrollPrev() { mScrollPos = llmax(0, mScrollPos-1); } // No wrap
+ void scrollNext() { mScrollPos = llmin(mScrollPos+1, mMaxScrollPos); } // No wrap
+
+ void updateMaxScrollPos();
+ void commitHoveredButton(S32 x, S32 y);
+
+ // updates tab button images given the tuple, tab position and the corresponding params
+ void update_images(LLTabTuple* tuple, TabParams params, LLTabContainer::TabPosition pos);
+ void reshapeTuple(LLTabTuple* tuple);
+
+ // Variables
+
+ typedef std::vector<LLTabTuple*> tuple_list_t;
+ tuple_list_t mTabList;
+
+ S32 mCurrentTabIdx;
+ bool mTabsHidden;
+ bool mHideScrollArrows;
+
+ bool mScrolled;
+ LLFrameTimer mScrollTimer;
+ S32 mScrollPos;
+ S32 mScrollPosPixels;
+ S32 mMaxScrollPos;
+
+ LLTextBox* mTitleBox;
+
+ S32 mTopBorderHeight;
+ TabPosition mTabPosition;
+ S32 mLockedTabCount;
+ S32 mMinTabWidth;
+ LLButton* mPrevArrowBtn;
+ LLButton* mNextArrowBtn;
+
+ bool mIsVertical;
+
+ // Horizontal specific
+ LLButton* mJumpPrevArrowBtn;
+ LLButton* mJumpNextArrowBtn;
+
+ S32 mRightTabBtnOffset; // Extra room to the right of the tab buttons.
+
+ S32 mMaxTabWidth;
+ S32 mTotalTabWidth;
+ S32 mTabHeight;
+
+ // Padding under the text labels of tab buttons
+ S32 mLabelPadBottom;
+ // Padding to the left of text labels of tab buttons
+ S32 mLabelPadLeft;
+
+ LLFrameTimer mDragAndDropDelayTimer;
+
+ LLFontGL::HAlign mFontHalign;
+ const LLFontGL* mFont;
+
+ TabParams mFirstTabParams;
+ TabParams mMiddleTabParams;
+ TabParams mLastTabParams;
+
+ bool mCustomIconCtrlUsed;
+ bool mOpenTabsOnDragAndDrop;
+ bool mEnableTabsFlashing;
+ LLUIColor mTabsFlashingColor;
+ S32 mTabIconCtrlPad;
+ bool mUseTabEllipses;
+ LLFrameTimer mMouseDownTimer;
+
+ bool mUseTabOffset;
+};
+
+#endif // LL_TABCONTAINER_H
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index 349a001335..f6dd38bce5 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -1,3900 +1,3890 @@
-/**
- * @file lltextbase.cpp
- * @author Martin Reddy
- * @brief The base class of text box/editor, providing Url handling support
- *
- * $LicenseInfo:firstyear=2009&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2009-2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "lltextbase.h"
-
-#include "llemojidictionary.h"
-#include "llemojihelper.h"
-#include "lllocalcliprect.h"
-#include "llmenugl.h"
-#include "llscrollcontainer.h"
-#include "llspellcheck.h"
-#include "llstl.h"
-#include "lltextparser.h"
-#include "lltextutil.h"
-#include "lltooltip.h"
-#include "lltrans.h"
-#include "lluictrl.h"
-#include "llurlaction.h"
-#include "llurlregistry.h"
-#include "llview.h"
-#include "llwindow.h"
-#include <boost/bind.hpp>
-
-const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds
-const S32 CURSOR_THICKNESS = 2;
-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),
- mDocIndexEnd(index_end),
- mRect(rect),
- mLineNum(line_num)
-{}
-
-bool LLTextBase::compare_segment_end::operator()(const LLTextSegmentPtr& a, const LLTextSegmentPtr& b) const
-{
- // sort empty spans (e.g. 11-11) after previous non-empty spans (e.g. 5-11)
- if (a->getEnd() == b->getEnd())
- {
- return a->getStart() < b->getStart();
- }
- else
- {
- return a->getEnd() < b->getEnd();
- }
-}
-
-
-// helper functors
-bool LLTextBase::compare_bottom::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 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
-bool LLTextBase::compare_top::operator()(const S32& a, const LLTextBase::line_info& b) const
-{
- return a > b.mRect.mTop; // top of a is higher than top of b
-}
-
-bool 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 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
-{
- bool operator()(const S32& pos, const LLTextBase::line_info& info) const
- {
- return (pos < info.mDocIndexEnd);
- }
-
- bool operator()(const LLTextBase::line_info& info, const S32& pos) const
- {
- return (info.mDocIndexEnd < pos);
- }
-
- bool operator()(const LLTextBase::line_info& a, const LLTextBase::line_info& b) const
- {
- return (a.mDocIndexEnd < b.mDocIndexEnd);
- }
-
-};
-
-//////////////////////////////////////////////////////////////////////////
-//
-// LLTextBase
-//
-
-// register LLTextBase::Params under name "textbase"
-static LLWidgetNameRegistry::StaticRegistrar sRegisterTextBaseParams(&typeid(LLTextBase::Params), "textbase");
-
-LLTextBase::LineSpacingParams::LineSpacingParams()
-: multiple("multiple", 1.f),
- pixels("pixels", 0)
-{
-}
-
-
-LLTextBase::Params::Params()
-: cursor_color("cursor_color"),
- text_color("text_color"),
- text_readonly_color("text_readonly_color"),
- text_tentative_color("text_tentative_color"),
- bg_visible("bg_visible", false),
- border_visible("border_visible", false),
- bg_readonly_color("bg_readonly_color"),
- bg_writeable_color("bg_writeable_color"),
- bg_focus_color("bg_focus_color"),
- text_selected_color("text_selected_color"),
- bg_selected_color("bg_selected_color"),
- allow_scroll("allow_scroll", true),
- plain_text("plain_text",false),
- track_end("track_end", false),
- read_only("read_only", false),
- skip_link_underline("skip_link_underline", 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"),
- text_valign("text_valign"),
- wrap("wrap"),
- trusted_content("trusted_content", true),
- always_show_icons("always_show_icons", false),
- use_ellipses("use_ellipses", false),
- use_color("use_color", true),
- parse_urls("parse_urls", false),
- force_urls_external("force_urls_external", false),
- parse_highlights("parse_highlights", false)
-{
- addSynonym(track_end, "track_bottom");
- addSynonym(wrap, "word_wrap");
- addSynonym(parse_urls, "allow_html");
-}
-
-
-LLTextBase::LLTextBase(const LLTextBase::Params &p)
-: LLUICtrl(p, LLTextViewModelPtr(new LLTextViewModel)),
- mURLClickSignal(NULL),
- mIsFriendSignal(NULL),
- mIsObjectBlockedSignal(NULL),
- mMaxTextByteLength( p.max_text_length ),
- mFont(p.font),
- mFontShadow(p.font_shadow),
- mPopupMenuHandle(),
- mReadOnly(p.read_only),
- mSkipTripleClick(false),
- mSkipLinkUnderline(p.skip_link_underline),
- 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),
- mTextSelectedColor(p.text_selected_color),
- mSelectedBGColor(p.bg_selected_color),
- mReflowIndex(S32_MAX),
- mCursorPos( 0 ),
- mScrollNeeded(false),
- mDesiredXPixel(-1),
- mHPad(p.h_pad),
- mVPad(p.v_pad),
- mHAlign(p.font_halign),
- mVAlign(p.font_valign),
- mTextVAlign(p.text_valign.isProvided() ? p.text_valign.getValue() : p.font_valign.getValue()),
- mLineSpacingMult(p.line_spacing.multiple),
- mLineSpacingPixels(p.line_spacing.pixels),
- mClip(p.clip),
- mClipPartial(p.clip_partial && !p.allow_scroll),
- mTrustedContent(p.trusted_content),
- mAlwaysShowIcons(p.always_show_icons),
- mTrackEnd( p.track_end ),
- mScrollIndex(-1),
- mSelectionStart( 0 ),
- mSelectionEnd( 0 ),
- mIsSelecting( false ),
- mPlainText ( p.plain_text ),
- mWordWrap(p.wrap),
- mUseEllipses( p.use_ellipses ),
- mUseColor(p.use_color),
- mParseHTML(p.parse_urls),
- mForceUrlsExternal(p.force_urls_external),
- mParseHighlights(p.parse_highlights),
- mBGVisible(p.bg_visible),
- mScroller(NULL),
- mStyleDirty(true)
-{
- if(p.allow_scroll)
- {
- LLScrollContainer::Params scroll_params;
- scroll_params.name = "text scroller";
- scroll_params.rect = getLocalRect();
- scroll_params.follows.flags = FOLLOWS_ALL;
- scroll_params.is_opaque = false;
- scroll_params.mouse_opaque = false;
- scroll_params.min_auto_scroll_rate = 200;
- scroll_params.max_auto_scroll_rate = 800;
- scroll_params.border_visible = p.border_visible;
- mScroller = LLUICtrlFactory::create<LLScrollContainer>(scroll_params);
- addChild(mScroller);
- }
-
- LLView::Params view_params;
- view_params.name = "text_contents";
- view_params.rect = LLRect(0, 500, 500, 0);
- view_params.mouse_opaque = false;
-
- mDocumentView = LLUICtrlFactory::create<LLView>(view_params);
- if (mScroller)
- {
- mScroller->addChild(mDocumentView);
- }
- else
- {
- addChild(mDocumentView);
- }
-
- if (mSpellCheck)
- {
- LLSpellChecker::setSettingsChangeCallback(boost::bind(&LLTextBase::onSpellCheckSettingsChange, this));
- }
- mSpellCheckTimer.reset();
-
- createDefaultSegment();
-
- updateRects();
-}
-
-LLTextBase::~LLTextBase()
-{
- mSegments.clear();
- LLContextMenu* menu = static_cast<LLContextMenu*>(mPopupMenuHandle.get());
- if (menu)
- {
- menu->die();
- mPopupMenuHandle.markDead();
- }
- delete mURLClickSignal;
- delete mIsFriendSignal;
- delete mIsObjectBlockedSignal;
-}
-
-void LLTextBase::initFromParams(const LLTextBase::Params& p)
-{
- LLUICtrl::initFromParams(p);
- resetDirty(); // Update saved text state
- updateSegments();
-
- // HACK: work around enabled == readonly design bug -- RN
- // setEnabled will modify our read only status, so do this after
- // LLTextBase::initFromParams
- if (p.read_only.isProvided())
- {
- mReadOnly = p.read_only;
- }
-}
-
-bool LLTextBase::truncate()
-{
- bool did_truncate = false;
-
- // First rough check - if we're less than 1/4th the size, we're OK
- if (getLength() >= S32(mMaxTextByteLength / 4))
- {
- // Have to check actual byte size
- 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 = 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.
- removeStringNoUndo(text.size(), getWText().size() - text.size());
- did_truncate = true;
- }
- }
-
- return did_truncate;
-}
-
-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)
- {
- mStyle
- .color(LLUIColor(&mFgColor)) // pass linked color instead of copy of mFGColor
- .readonly_color(LLUIColor(&mReadOnlyFgColor))
- .selected_color(LLUIColor(&mTextSelectedColor))
- .font(mFont)
- .drop_shadow(mFontShadow);
- mStyleDirty = false;
- }
- return mStyle;
-}
-
-void LLTextBase::beforeValueChange()
-{
-
-}
-
-void LLTextBase::onValueChange(S32 start, S32 end)
-{
-}
-
-std::vector<LLRect> LLTextBase::getSelectionRects()
-{
- // Nor supposed to be called without selection
- llassert(hasSelection());
- llassert(!mLineInfoList.empty());
-
- std::vector<LLRect> selection_rects;
-
- S32 selection_left = llmin(mSelectionStart, mSelectionEnd);
- S32 selection_right = llmax(mSelectionStart, mSelectionEnd);
-
- // 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::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mBottom, compare_top());
-
- bool done = false;
-
- // Find the coordinates of the selected area
- for (; line_iter != end_iter && !done; ++line_iter)
- {
- // is selection visible on this line?
- if (line_iter->mDocIndexEnd > selection_left && line_iter->mDocIndexStart < selection_right)
- {
- segment_set_t::iterator segment_iter;
- S32 segment_offset;
- getSegmentAndOffset(line_iter->mDocIndexStart, &segment_iter, &segment_offset);
-
- // Use F32 otherwise a string of multiple segments
- // will accumulate a large error
- F32 left_precise = line_iter->mRect.mLeft;
- F32 right_precise = line_iter->mRect.mLeft;
-
- for (; segment_iter != mSegments.end(); ++segment_iter, segment_offset = 0)
- {
- LLTextSegmentPtr segmentp = *segment_iter;
-
- S32 segment_line_start = segmentp->getStart() + segment_offset;
- S32 segment_line_end = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd);
-
- if (segment_line_start > segment_line_end) break;
-
- F32 segment_width = 0;
- S32 segment_height = 0;
-
- // if selection after beginning of segment
- if (selection_left >= segment_line_start)
- {
- S32 num_chars = llmin(selection_left, segment_line_end) - segment_line_start;
- segmentp->getDimensionsF32(segment_offset, num_chars, segment_width, segment_height);
- left_precise += segment_width;
- }
-
- // if selection_right == segment_line_end then that means we are the first character of the next segment
- // or first character of the next line, in either case we want to add the length of the current segment
- // to the selection rectangle and continue.
- // if selection right > segment_line_end then selection spans end of current segment...
- if (selection_right >= segment_line_end)
- {
- // extend selection slightly beyond end of line
- // to indicate selection of newline character (use "n" character to determine width)
- S32 num_chars = segment_line_end - segment_line_start;
- segmentp->getDimensionsF32(segment_offset, num_chars, segment_width, segment_height);
- right_precise += segment_width;
- }
- // else if selection ends on current segment...
- else
- {
- S32 num_chars = selection_right - segment_line_start;
- segmentp->getDimensionsF32(segment_offset, num_chars, segment_width, segment_height);
- right_precise += segment_width;
-
- break;
- }
- }
-
- LLRect selection_rect;
- selection_rect.mLeft = left_precise;
- selection_rect.mRight = right_precise;
- selection_rect.mBottom = line_iter->mRect.mBottom;
- selection_rect.mTop = line_iter->mRect.mTop;
-
- selection_rects.push_back(selection_rect);
- }
- }
-
- return selection_rects;
-}
-
-// Draws the black box behind the selected text
-void LLTextBase::drawSelectionBackground()
-{
- // Draw selection even if we don't have keyboard focus for search/replace
- if (hasSelection() && !mLineInfoList.empty())
- {
- std::vector<LLRect> selection_rects = getSelectionRects();
-
- // Draw the selection box (we're using a box instead of reversing the colors on the selected text).
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- const LLColor4& color = mSelectedBGColor;
- F32 alpha = hasFocus() ? 0.7f : 0.3f;
- alpha *= getDrawContext().mAlpha;
-
- LLColor4 selection_color(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], alpha);
- LLRect content_display_rect = getVisibleDocumentRect();
-
- for (std::vector<LLRect>::iterator rect_it = selection_rects.begin();
- rect_it != selection_rects.end();
- ++rect_it)
- {
- LLRect selection_rect = *rect_it;
- if (mScroller)
- {
- // If scroller is On content_display_rect has correct rect and safe to use as is
- // Note: we might need to account for border
- selection_rect.translate(mVisibleTextRect.mLeft - content_display_rect.mLeft, mVisibleTextRect.mBottom - content_display_rect.mBottom);
- }
- else
- {
- // If scroller is Off content_display_rect will have rect from document, adjusted to text width, heigh and position
- // and we have to acount for offset depending on position
- S32 v_delta = 0;
- S32 h_delta = 0;
- switch (mVAlign)
- {
- case LLFontGL::TOP:
- v_delta = mVisibleTextRect.mTop - content_display_rect.mTop - mVPad;
- break;
- case LLFontGL::VCENTER:
- v_delta = (llmax(mVisibleTextRect.getHeight() - content_display_rect.mTop, -content_display_rect.mBottom) + (mVisibleTextRect.mBottom - content_display_rect.mBottom)) / 2;
- break;
- case LLFontGL::BOTTOM:
- v_delta = mVisibleTextRect.mBottom - content_display_rect.mBottom;
- break;
- default:
- break;
- }
- switch (mHAlign)
- {
- case LLFontGL::LEFT:
- h_delta = mVisibleTextRect.mLeft - content_display_rect.mLeft + mHPad;
- break;
- case LLFontGL::HCENTER:
- h_delta = (llmax(mVisibleTextRect.getWidth() - content_display_rect.mLeft, -content_display_rect.mRight) + (mVisibleTextRect.mRight - content_display_rect.mRight)) / 2;
- break;
- case LLFontGL::RIGHT:
- h_delta = mVisibleTextRect.mRight - content_display_rect.mRight;
- break;
- default:
- break;
- }
- selection_rect.translate(h_delta, v_delta);
- }
- gl_rect_2d(selection_rect, selection_color);
- }
- }
-}
-
-void LLTextBase::drawCursor()
-{
- F32 alpha = getDrawContext().mAlpha;
-
- if( hasFocus()
- && gFocusMgr.getAppHasFocus()
- && !mReadOnly)
- {
- const LLWString &wtext = getWText();
- const llwchar* text = wtext.c_str();
-
- LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos);
- cursor_rect.translate(-1, 0);
- segment_set_t::iterator seg_it = getSegIterContaining(mCursorPos);
-
- // take style from last segment
- LLTextSegmentPtr segmentp;
-
- if (seg_it != mSegments.end())
- {
- segmentp = *seg_it;
- }
- else
- {
- return;
- }
-
- // Draw the cursor
- // (Flash the cursor every half second starting a fixed time after the last keystroke)
- F32 elapsed = mCursorBlinkTimer.getElapsedTimeF32();
- if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) )
- {
-
- if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
- {
- S32 segment_width = 0;
- S32 segment_height = 0;
- segmentp->getDimensions(mCursorPos - segmentp->getStart(), 1, segment_width, segment_height);
- S32 width = llmax(CURSOR_THICKNESS, segment_width);
- cursor_rect.mRight = cursor_rect.mLeft + width;
- }
- else
- {
- cursor_rect.mRight = cursor_rect.mLeft + CURSOR_THICKNESS;
- }
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- LLColor4 cursor_color = mCursorColor.get() % alpha;
- gGL.color4fv( cursor_color.mV );
-
- gl_rect_2d(cursor_rect);
-
- if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection() && text[mCursorPos] != '\n')
- {
- LLColor4 text_color;
- const LLFontGL* fontp;
- text_color = segmentp->getColor();
- fontp = segmentp->getStyle()->getFont();
- fontp->render(text, mCursorPos, cursor_rect,
- LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], alpha),
- LLFontGL::LEFT, mTextVAlign,
- LLFontGL::NORMAL,
- LLFontGL::NO_SHADOW,
- 1);
- }
-
- // Make sure the IME is in the right place
- LLRect screen_pos = calcScreenRect();
- LLCoordGL ime_pos( screen_pos.mLeft + llfloor(cursor_rect.mLeft), screen_pos.mBottom + llfloor(cursor_rect.mTop) );
-
- ime_pos.mX = (S32) (ime_pos.mX * LLUI::getScaleFactor().mV[VX]);
- ime_pos.mY = (S32) (ime_pos.mY * LLUI::getScaleFactor().mV[VY]);
- getWindow()->setLanguageTextInput( ime_pos );
- }
- }
-}
-
-void LLTextBase::drawText()
-{
- 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
- if( hasSelection())
- {
- selection_left = llmin( mSelectionStart, mSelectionEnd );
- selection_right = llmax( mSelectionStart, mSelectionEnd );
- }
-
- std::pair<S32, S32> line_range = getVisibleLines(mClipPartial);
- S32 first_line = line_range.first;
- S32 last_line = line_range.second;
- if (first_line >= last_line)
- {
- return;
- }
-
- S32 line_start = getLineStart(first_line);
- // find first text segment that spans top of visible portion of text buffer
- segment_set_t::iterator seg_iter = getSegIterContaining(line_start);
- if (seg_iter == mSegments.end())
- {
- return;
- }
-
- // 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;
- line_info& line = mLineInfoList[cur_line];
-
- S32 next_start = -1;
- S32 line_end = text_len;
-
- if (next_line < getLineCount())
- {
- next_start = getLineStart(next_line);
- line_end = next_start;
- }
-
- LLRectf text_rect(line.mRect.mLeft, line.mRect.mTop, line.mRect.mRight, line.mRect.mBottom);
- 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;
- while( seg_start < line_end )
- {
- while( cur_segment->getEnd() <= seg_start )
- {
- seg_iter++;
- if (seg_iter == mSegments.end())
- {
- LL_WARNS() << "Ran off the segmentation end!" << LL_ENDL;
-
- return;
- }
- cur_segment = *seg_iter;
- }
-
- 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
- && next_line == last_line // this is the last visible line
- && last_line < (S32)mLineInfoList.size()) // and there is more text to display
- {
- // more lines of text to go, but we can't fit them
- // so shrink text rect to force ellipses
- text_rect.mRight -= 2;
- }
-
- // 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 = cur_segment->draw(seg_start - cur_segment->getStart(), clipped_end, selection_left, selection_right, text_rect);
-
- seg_start = clipped_end + cur_segment->getStart();
- }
-
- line_start = next_start;
- }
-}
-
-///////////////////////////////////////////////////////////////////
-// Returns change in number of characters in mWText
-
-S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::segment_vec_t* segments )
-{
- beforeValueChange();
-
- S32 old_len = getLength(); // length() returns character length
- S32 insert_len = wstr.length();
-
- pos = getEditableIndex(pos, true);
- if (pos > old_len)
- {
- pos = old_len;
- // Should not happen,
- // if you encounter this, check where wrong position comes from
- llassert(false);
- }
-
- segment_set_t::iterator seg_iter = getEditableSegIterContaining(pos);
-
- LLTextSegmentPtr default_segment;
-
- LLTextSegmentPtr segmentp;
- if (seg_iter != mSegments.end())
- {
- segmentp = *seg_iter;
- }
- else
- {
- //segmentp = mSegments.back();
- return pos;
- }
-
- if (segmentp->canEdit())
- {
- segmentp->setEnd(segmentp->getEnd() + insert_len);
- if (seg_iter != mSegments.end())
- {
- ++seg_iter;
- }
- }
- else
- {
- // create default editable segment to hold new text
- LLStyleConstSP sp(new LLStyle(getStyleParams()));
- default_segment = new LLNormalTextSegment( sp, pos, pos + insert_len, *this);
- }
-
- // shift remaining segments to right
- for(;seg_iter != mSegments.end(); ++seg_iter)
- {
- LLTextSegmentPtr segmentp = *seg_iter;
- segmentp->setStart(segmentp->getStart() + insert_len);
- segmentp->setEnd(segmentp->getEnd() + insert_len);
- }
-
- // insert new segments
- if (segments)
- {
- if (default_segment.notNull())
- {
- // potentially overwritten by segments passed in
- insertSegment(default_segment);
- }
- for (segment_vec_t::iterator seg_iter = segments->begin();
- seg_iter != segments->end();
- ++seg_iter)
- {
- LLTextSegment* segmentp = *seg_iter;
- insertSegment(segmentp);
- }
- }
-
- // Insert special segments where necessary (insertSegment takes care of splitting normal text segments around them for us)
- {
- LLStyleSP emoji_style;
- LLEmojiDictionary* ed = LLEmojiDictionary::instanceExists() ? LLEmojiDictionary::getInstance() : NULL;
- for (S32 text_kitty = 0, text_len = wstr.size(); text_kitty < text_len; text_kitty++)
- {
- llwchar code = wstr[text_kitty];
- bool isEmoji = ed ? ed->isEmoji(code) : LLStringOps::isEmoji(code);
- if (isEmoji)
- {
- if (!emoji_style)
- {
- emoji_style = new LLStyle(getStyleParams());
- emoji_style->setFont(LLFontGL::getFontEmoji());
- }
-
- S32 new_seg_start = pos + text_kitty;
- insertSegment(new LLEmojiTextSegment(emoji_style, new_seg_start, new_seg_start + 1, *this));
- }
- }
- }
-
- getViewModel()->getEditableDisplay().insert(pos, wstr);
-
- if ( truncate() )
- {
- insert_len = getLength() - old_len;
- }
-
- onValueChange(pos, pos + insert_len);
- needsReflow(pos);
-
- return insert_len;
-}
-
-S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length)
-{
-
- beforeValueChange();
- segment_set_t::iterator seg_iter = getSegIterContaining(pos);
- while(seg_iter != mSegments.end())
- {
- LLTextSegmentPtr segmentp = *seg_iter;
- S32 end = pos + length;
- if (segmentp->getStart() < pos)
- {
- // deleting from middle of segment
- if (segmentp->getEnd() > end)
- {
- segmentp->setEnd(segmentp->getEnd() - length);
- }
- // truncating segment
- else
- {
- segmentp->setEnd(pos);
- }
- }
- else if (segmentp->getStart() < end)
- {
- // deleting entire segment
- if (segmentp->getEnd() <= end)
- {
- // remove segment
- segmentp->unlinkFromDocument(this);
- segment_set_t::iterator seg_to_erase(seg_iter++);
- mSegments.erase(seg_to_erase);
- continue;
- }
- // deleting head of segment
- else
- {
- segmentp->setStart(pos);
- segmentp->setEnd(segmentp->getEnd() - length);
- }
- }
- else
- {
- // shifting segments backward to fill deleted portion
- segmentp->setStart(segmentp->getStart() - length);
- segmentp->setEnd(segmentp->getEnd() - length);
- }
- ++seg_iter;
- }
-
- getViewModel()->getEditableDisplay().erase(pos, length);
-
- // recreate default segment in case we erased everything
- createDefaultSegment();
-
- onValueChange(pos, pos);
- needsReflow(pos);
-
- return -length; // This will be wrong if someone calls removeStringNoUndo with an excessive length
-}
-
-S32 LLTextBase::overwriteCharNoUndo(S32 pos, llwchar wc)
-{
- beforeValueChange();
-
- if (pos > (S32)getLength())
- {
- return 0;
- }
- getViewModel()->getEditableDisplay()[pos] = wc;
-
- onValueChange(pos, pos + 1);
- needsReflow(pos);
-
- return 1;
-}
-
-
-void LLTextBase::createDefaultSegment()
-{
- // ensures that there is always at least one segment
- if (mSegments.empty())
- {
- LLStyleConstSP sp(new LLStyle(getStyleParams()));
- LLTextSegmentPtr default_segment = new LLNormalTextSegment( sp, 0, getLength() + 1, *this);
- mSegments.insert(default_segment);
- default_segment->linkToDocument(this);
- }
-}
-
-void LLTextBase::insertSegment(LLTextSegmentPtr segment_to_insert)
-{
- if (segment_to_insert.isNull())
- {
- return;
- }
-
- segment_set_t::iterator cur_seg_iter = getSegIterContaining(segment_to_insert->getStart());
- S32 reflow_start_index = 0;
-
- if (cur_seg_iter == mSegments.end())
- {
- mSegments.insert(segment_to_insert);
- segment_to_insert->linkToDocument(this);
- reflow_start_index = segment_to_insert->getStart();
- }
- else
- {
- LLTextSegmentPtr cur_segmentp = *cur_seg_iter;
- reflow_start_index = cur_segmentp->getStart();
- if (cur_segmentp->getStart() < segment_to_insert->getStart())
- {
- S32 old_segment_end = cur_segmentp->getEnd();
- // split old at start point for new segment
- cur_segmentp->setEnd(segment_to_insert->getStart());
- // advance to next segment
- // insert remainder of old segment
- LLStyleConstSP sp = cur_segmentp->getStyle();
- LLTextSegmentPtr remainder_segment = new LLNormalTextSegment( sp, segment_to_insert->getStart(), old_segment_end, *this);
- mSegments.insert(cur_seg_iter, remainder_segment);
- remainder_segment->linkToDocument(this);
- // insert new segment before remainder of old segment
- mSegments.insert(cur_seg_iter, segment_to_insert);
-
- segment_to_insert->linkToDocument(this);
- // at this point, there will be two overlapping segments owning the text
- // associated with the incoming segment
- }
- else
- {
- mSegments.insert(cur_seg_iter, segment_to_insert);
- segment_to_insert->linkToDocument(this);
- }
-
- // now delete/truncate remaining segments as necessary
- // cur_seg_iter points to segment before incoming segment
- while(cur_seg_iter != mSegments.end())
- {
- cur_segmentp = *cur_seg_iter;
- if (cur_segmentp == segment_to_insert)
- {
- ++cur_seg_iter;
- continue;
- }
-
- if (cur_segmentp->getStart() >= segment_to_insert->getStart())
- {
- if(cur_segmentp->getEnd() <= segment_to_insert->getEnd())
- {
- cur_segmentp->unlinkFromDocument(this);
- // grab copy of iterator to erase, and bump it
- segment_set_t::iterator seg_to_erase(cur_seg_iter++);
- mSegments.erase(seg_to_erase);
- continue;
- }
- else
- {
- // last overlapping segment, clip to end of incoming segment
- // and stop traversal
- cur_segmentp->setStart(segment_to_insert->getEnd());
- break;
- }
- }
- ++cur_seg_iter;
- }
- }
-
- // layout potentially changed
- needsReflow(reflow_start_index);
-}
-
-//virtual
-bool LLTextBase::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- // handle triple click
- if (!mTripleClickTimer.hasExpired())
- {
- if (mSkipTripleClick)
- {
- return true;
- }
-
- S32 real_line = getLineNumFromDocIndex(mCursorPos, false);
- S32 line_start = -1;
- S32 line_end = -1;
- for (line_list_t::const_iterator it = mLineInfoList.begin(), end_it = mLineInfoList.end();
- it != end_it;
- ++it)
- {
- if (it->mLineNum < real_line)
- {
- continue;
- }
- if (it->mLineNum > real_line)
- {
- break;
- }
- if (line_start == -1)
- {
- line_start = it->mDocIndexStart;
- }
- line_end = it->mDocIndexEnd;
- line_end = llclamp(line_end, 0, getLength());
- }
-
- if (line_start == -1)
- {
- return true;
- }
-
- mSelectionEnd = line_start;
- mSelectionStart = line_end;
- setCursorPos(line_start);
-
- return true;
- }
-
- LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
- if (cur_segment && cur_segment->handleMouseDown(x, y, mask))
- {
- return true;
- }
-
- return LLUICtrl::handleMouseDown(x, y, mask);
-}
-
-//virtual
-bool LLTextBase::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
- 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())
- {
- // *TODO: send URL here?
- (*mURLClickSignal)(this, LLSD() );
- }
- return true;
- }
-
- return LLUICtrl::handleMouseUp(x, y, mask);
-}
-
-//virtual
-bool LLTextBase::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
-{
- LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
- if (cur_segment && cur_segment->handleMiddleMouseDown(x, y, mask))
- {
- return true;
- }
-
- return LLUICtrl::handleMiddleMouseDown(x, y, mask);
-}
-
-//virtual
-bool LLTextBase::handleMiddleMouseUp(S32 x, S32 y, MASK mask)
-{
- LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
- if (cur_segment && cur_segment->handleMiddleMouseUp(x, y, mask))
- {
- return true;
- }
-
- return LLUICtrl::handleMiddleMouseUp(x, y, mask);
-}
-
-//virtual
-bool LLTextBase::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
- if (cur_segment && cur_segment->handleRightMouseDown(x, y, mask))
- {
- return true;
- }
-
- return LLUICtrl::handleRightMouseDown(x, y, mask);
-}
-
-//virtual
-bool LLTextBase::handleRightMouseUp(S32 x, S32 y, MASK mask)
-{
- LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
- if (cur_segment && cur_segment->handleRightMouseUp(x, y, mask))
- {
- return true;
- }
-
- return LLUICtrl::handleRightMouseUp(x, y, mask);
-}
-
-//virtual
-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))
- {
- return true;
- }
-
- return LLUICtrl::handleDoubleClick(x, y, mask);
-}
-
-//virtual
-bool LLTextBase::handleHover(S32 x, S32 y, MASK mask)
-{
- LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
- if (cur_segment && cur_segment->handleHover(x, y, mask))
- {
- return true;
- }
-
- return LLUICtrl::handleHover(x, y, mask);
-}
-
-//virtual
-bool LLTextBase::handleScrollWheel(S32 x, S32 y, S32 clicks)
-{
- LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
- if (cur_segment && cur_segment->handleScrollWheel(x, y, clicks))
- {
- return true;
- }
-
- return LLUICtrl::handleScrollWheel(x, y, clicks);
-}
-
-//virtual
-bool LLTextBase::handleToolTip(S32 x, S32 y, MASK mask)
-{
- LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
- if (cur_segment && cur_segment->handleToolTip(x, y, mask))
- {
- return true;
- }
-
- return LLUICtrl::handleToolTip(x, y, mask);
-}
-
-//virtual
-const std::string LLTextBase::getToolTip() const
-{
- if (sDebugUnicode)
- {
- std::string text = getText();
- std::string tooltip = utf8str_showBytesUTF8(text);
- return tooltip;
- }
-
- return LLUICtrl::getToolTip();
-}
-
-//virtual
-void LLTextBase::reshape(S32 width, S32 height, bool called_from_parent)
-{
- if (width != getRect().getWidth() || height != getRect().getHeight() || LLView::sForceReshape)
- {
- bool scrolled_to_bottom = mScroller ? mScroller->isAtBottom() : false;
-
- LLUICtrl::reshape( width, height, called_from_parent );
-
- if (mScroller && scrolled_to_bottom && mTrackEnd)
- {
- // keep bottom of text buffer visible
- // do this here as well as in reflow to handle case
- // where shrinking from top, which causes buffer to temporarily
- // not be scrolled to the bottom, since the scroll index
- // specified the _top_ of the visible document region
- mScroller->goToBottom();
- }
-
- // do this first after reshape, because other things depend on
- // up-to-date mVisibleTextRect
- updateRects();
-
- needsReflow();
- }
-}
-
-//virtual
-void LLTextBase::draw()
-{
- // reflow if needed, on demand
- reflow();
-
- // then update scroll position, as cursor may have moved
- if (!mReadOnly)
- {
- updateScrollFromCursor();
- }
-
- LLRect text_rect;
- if (mScroller)
- {
- mScroller->localRectToOtherView(mScroller->getContentWindowRect(), &text_rect, this);
- }
- else
- {
- 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)
- {
- F32 alpha = getCurrentTransparency();
- // clip background rect against extents, if we support scrolling
- LLRect bg_rect = mVisibleTextRect;
- if (mScroller)
- {
- bg_rect.intersectWith(text_rect);
- }
- LLColor4 bg_color = mReadOnly
- ? mReadOnlyBgColor.get()
- : hasFocus()
- ? mFocusBgColor.get()
- : mWriteableBgColor.get();
- gl_rect_2d(text_rect, bg_color % alpha, true);
- }
-
- // Draw highlighted if needed
- if( ll::ui::SearchableControl::getHighlighted() )
- {
- LLColor4 bg_color = ll::ui::SearchableControl::getHighlightColor();
- LLRect bg_rect = mVisibleTextRect;
- if( mScroller )
- bg_rect.intersectWith( text_rect );
-
- gl_rect_2d( text_rect, bg_color, true );
- }
-
- 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->setVisibleDirect(false);
- LLUICtrl::draw();
- mDocumentView->setVisibleDirect(true);
-}
-
-
-//virtual
-void LLTextBase::setColor( const LLColor4& c )
-{
- mFgColor = c;
- mStyleDirty = true;
-}
-
-//virtual
-void LLTextBase::setReadOnlyColor(const LLColor4 &c)
-{
- mReadOnlyFgColor = c;
- mStyleDirty = true;
-}
-
-//virtual
-void LLTextBase::onVisibilityChange( bool new_visibility )
-{
- LLContextMenu* menu = static_cast<LLContextMenu*>(mPopupMenuHandle.get());
- if(!new_visibility && menu)
- {
- menu->hide();
- }
- LLUICtrl::onVisibilityChange(new_visibility);
-}
-
-//virtual
-void LLTextBase::setValue(const LLSD& value )
-{
- setText(value.asString());
-}
-
-//virtual
-bool LLTextBase::canDeselect() const
-{
- return hasSelection();
-}
-
-
-//virtual
-void LLTextBase::deselect()
-{
- mSelectionStart = 0;
- mSelectionEnd = 0;
- mIsSelecting = false;
-}
-
-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());
- onSpellCheckPerformed();
-
- 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()
-{
- // Update scroll position even in read-only mode (when there's no cursor displayed)
- // because startOfDoc()/endOfDoc() modify cursor position. See EXT-736.
-
- if (!mScrollNeeded || !mScroller)
- {
- return;
- }
- mScrollNeeded = false;
-
- // scroll so that the cursor is at the top of the page
- LLRect scroller_doc_window = getVisibleDocumentRect();
- LLRect cursor_rect_doc = getDocRectFromDocIndex(mCursorPos);
- mScroller->scrollToShowRect(cursor_rect_doc, LLRect(0, scroller_doc_window.getHeight() - 5, scroller_doc_window.getWidth(), 5));
-}
-
-S32 LLTextBase::getLeftOffset(S32 width)
-{
- switch (mHAlign)
- {
- case LLFontGL::LEFT:
- return mHPad;
- case LLFontGL::HCENTER:
- return mHPad + llmax(0, (mVisibleTextRect.getWidth() - width - mHPad) / 2);
- case LLFontGL::RIGHT:
- {
- // Font's rendering rounds string size, if value gets rounded
- // down last symbol might not have enough space to render,
- // compensate by adding an extra pixel as padding
- const S32 right_padding = 1;
- return llmax(mHPad, mVisibleTextRect.getWidth() - width - right_padding);
- }
- default:
- return mHPad;
- }
-}
-
-void LLTextBase::reflow()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
-
- updateSegments();
-
- if (mReflowIndex == S32_MAX)
- {
- return;
- }
-
- bool scrolled_to_bottom = mScroller ? mScroller->isAtBottom() : false;
-
- LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos);
- bool follow_selection = getLocalRect().overlaps(cursor_rect); // cursor is (potentially) visible
-
- // store in top-left relative coordinates to avoid issues with horizontal scrollbar appearing and disappearing
- cursor_rect.mTop = mVisibleTextRect.mTop - cursor_rect.mTop;
- cursor_rect.mBottom = mVisibleTextRect.mTop - cursor_rect.mBottom;
-
- S32 first_line = getFirstVisibleLine();
-
- // if scroll anchor not on first line, update it to first character of first line
- if ((first_line < mLineInfoList.size())
- && (mScrollIndex < mLineInfoList[first_line].mDocIndexStart
- || mScrollIndex >= mLineInfoList[first_line].mDocIndexEnd))
- {
- mScrollIndex = mLineInfoList[first_line].mDocIndexStart;
- }
- LLRect first_char_rect = getLocalRectFromDocIndex(mScrollIndex);
- // store in top-left relative coordinates to avoid issues with horizontal scrollbar appearing and disappearing
- first_char_rect.mTop = mVisibleTextRect.mTop - first_char_rect.mTop;
- first_char_rect.mBottom = mVisibleTextRect.mTop - first_char_rect.mBottom;
-
- S32 reflow_count = 0;
- while(mReflowIndex < S32_MAX)
- {
- // we can get into an infinite loop if the document height does not monotonically increase
- // with decreasing width (embedded ui elements with alternate layouts). In that case,
- // we want to stop reflowing after 2 iterations. We use 2, since we need to handle the case
- // of introducing a vertical scrollbar causing a reflow with less width. We should also always
- // use an even number of iterations to avoid user visible oscillation of the layout
- if(++reflow_count > 2)
- {
- LL_DEBUGS() << "Breaking out of reflow due to possible infinite loop in " << getName() << LL_ENDL;
- break;
- }
-
- S32 start_index = mReflowIndex;
- mReflowIndex = S32_MAX;
-
- // shrink document to minimum size (visible portion of text widget)
- // to force inlined widgets with follows set to shrink
- if (mWordWrap)
- {
- mDocumentView->reshape(mVisibleTextRect.getWidth(), mDocumentView->getRect().getHeight());
- }
-
- S32 cur_top = 0;
-
- segment_set_t::iterator seg_iter = mSegments.begin();
- S32 seg_offset = 0;
- S32 line_start_index = 0;
- const F32 text_available_width = mVisibleTextRect.getWidth() - mHPad; // reserve room for margin
- F32 remaining_pixels = text_available_width;
- S32 line_count = 0;
-
- // find and erase line info structs starting at start_index and going to end of document
- if (!mLineInfoList.empty())
- {
- // find first element whose end comes after start_index
- line_list_t::iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), start_index, line_end_compare());
- if (iter != mLineInfoList.end())
- {
- line_start_index = iter->mDocIndexStart;
- line_count = iter->mLineNum;
- cur_top = iter->mRect.mTop;
- getSegmentAndOffset(iter->mDocIndexStart, &seg_iter, &seg_offset);
- mLineInfoList.erase(iter, mLineInfoList.end());
- }
- }
-
- S32 line_height = 0;
- S32 seg_line_offset = line_count + 1;
-
- while(seg_iter != mSegments.end())
- {
- LLTextSegmentPtr segment = *seg_iter;
-
- // track maximum height of any segment on this line
- S32 cur_index = segment->getStart() + seg_offset;
-
- // ask segment how many character fit in remaining space
- S32 character_count = segment->getNumChars(getWordWrap() ? llmax(0, ll_round(remaining_pixels)) : S32_MAX,
- seg_offset,
- cur_index - line_start_index,
- S32_MAX,
- line_count - seg_line_offset);
-
- F32 segment_width;
- S32 segment_height;
- bool force_newline = segment->getDimensionsF32(seg_offset, character_count, segment_width, segment_height);
- // grow line height as necessary based on reported height of this segment
- line_height = llmax(line_height, segment_height);
- remaining_pixels -= segment_width;
-
- seg_offset += character_count;
-
- S32 last_segment_char_on_line = segment->getStart() + seg_offset;
-
- // Note: make sure text will fit in width - use ceil, but also make sure
- // ceil is used only once per line
- S32 text_actual_width = llceil(text_available_width - remaining_pixels);
- S32 text_left = getLeftOffset(text_actual_width);
- LLRect line_rect(text_left,
- cur_top,
- text_left + text_actual_width,
- cur_top - line_height);
-
- // if we didn't finish the current segment...
- if (last_segment_char_on_line < segment->getEnd())
- {
- // add line info and keep going
- mLineInfoList.push_back(line_info(
- line_start_index,
- last_segment_char_on_line,
- line_rect,
- line_count));
-
- line_start_index = segment->getStart() + seg_offset;
- cur_top -= ll_round((F32)line_height * mLineSpacingMult) + mLineSpacingPixels;
- remaining_pixels = text_available_width;
- line_height = 0;
- }
- // ...just consumed last segment..
- else if (++segment_set_t::iterator(seg_iter) == mSegments.end())
- {
- mLineInfoList.push_back(line_info(
- line_start_index,
- last_segment_char_on_line,
- line_rect,
- line_count));
- cur_top -= ll_round((F32)line_height * mLineSpacingMult) + mLineSpacingPixels;
- break;
- }
- // ...or finished a segment and there are segments remaining on this line
- else
- {
- // subtract pixels used and increment segment
- if (force_newline)
- {
- mLineInfoList.push_back(line_info(
- line_start_index,
- last_segment_char_on_line,
- line_rect,
- line_count));
- line_start_index = segment->getStart() + seg_offset;
- cur_top -= ll_round((F32)line_height * mLineSpacingMult) + mLineSpacingPixels;
- line_height = 0;
- remaining_pixels = text_available_width;
- }
- ++seg_iter;
- seg_offset = 0;
- seg_line_offset = force_newline ? line_count + 1 : line_count;
- }
- if (force_newline)
- {
- line_count++;
- }
- }
-
- // calculate visible region for diplaying text
- updateRects();
-
- for (segment_set_t::iterator segment_it = mSegments.begin();
- segment_it != mSegments.end();
- ++segment_it)
- {
- LLTextSegmentPtr segmentp = *segment_it;
- segmentp->updateLayout(*this);
-
- }
- }
-
- // apply scroll constraints after reflowing text
- if (!hasMouseCapture() && mScroller)
- {
- if (scrolled_to_bottom && mTrackEnd)
- {
- // keep bottom of text buffer visible
- endOfDoc();
- }
- else if (hasSelection() && follow_selection)
- {
- // keep cursor in same vertical position on screen when selecting text
- LLRect new_cursor_rect_doc = getDocRectFromDocIndex(mCursorPos);
- LLRect old_cursor_rect = cursor_rect;
- old_cursor_rect.mTop = mVisibleTextRect.mTop - cursor_rect.mTop;
- old_cursor_rect.mBottom = mVisibleTextRect.mTop - cursor_rect.mBottom;
-
- mScroller->scrollToShowRect(new_cursor_rect_doc, old_cursor_rect);
- }
- else
- {
- // keep first line of text visible
- LLRect new_first_char_rect = getDocRectFromDocIndex(mScrollIndex);
-
- // pass in desired rect in the coordinate frame of the document viewport
- LLRect old_first_char_rect = first_char_rect;
- old_first_char_rect.mTop = mVisibleTextRect.mTop - first_char_rect.mTop;
- old_first_char_rect.mBottom = mVisibleTextRect.mTop - first_char_rect.mBottom;
-
- mScroller->scrollToShowRect(new_first_char_rect, old_first_char_rect);
- }
- }
-
- // reset desired x cursor position
- updateCursorXPos();
-}
-
-LLRect LLTextBase::getTextBoundingRect()
-{
- reflow();
- return mTextBoundingRect;
-}
-
-
-void LLTextBase::clearSegments()
-{
- mSegments.clear();
- createDefaultSegment();
-}
-
-S32 LLTextBase::getLineStart( S32 line ) const
-{
- S32 num_lines = getLineCount();
- if (num_lines == 0)
- {
- return 0;
- }
-
- line = llclamp(line, 0, num_lines-1);
- return mLineInfoList[line].mDocIndexStart;
-}
-
-S32 LLTextBase::getLineEnd( S32 line ) const
-{
- S32 num_lines = getLineCount();
- if (num_lines == 0)
- {
- return 0;
- }
-
- line = llclamp(line, 0, num_lines-1);
- return mLineInfoList[line].mDocIndexEnd;
-}
-
-
-
-S32 LLTextBase::getLineNumFromDocIndex( S32 doc_index, bool include_wordwrap) const
-{
- if (mLineInfoList.empty())
- {
- return 0;
- }
- else
- {
- line_list_t::const_iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), doc_index, line_end_compare());
- if (include_wordwrap)
- {
- return iter - mLineInfoList.begin();
- }
- else
- {
- if (iter == mLineInfoList.end())
- {
- return mLineInfoList.back().mLineNum;
- }
- else
- {
- return iter->mLineNum;
- }
- }
- }
-}
-
-// Given an offset into text (pos), find the corresponding line (from the start of the doc) and an offset into the line.
-S32 LLTextBase::getLineOffsetFromDocIndex( S32 startpos, bool include_wordwrap) const
-{
- if (mLineInfoList.empty())
- {
- return startpos;
- }
- else
- {
- line_list_t::const_iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), startpos, line_end_compare());
- return startpos - iter->mDocIndexStart;
- }
-}
-
-S32 LLTextBase::getFirstVisibleLine() const
-{
- LLRect visible_region = getVisibleDocumentRect();
-
- // binary search for line that starts before top of visible buffer
- line_list_t::const_iterator iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_bottom());
-
- return iter - mLineInfoList.begin();
-}
-
-std::pair<S32, S32> LLTextBase::getVisibleLines(bool require_fully_visible)
-{
- LLRect visible_region = getVisibleDocumentRect();
- line_list_t::const_iterator first_iter;
- line_list_t::const_iterator last_iter;
-
- // make sure we have an up-to-date mLineInfoList
- reflow();
-
- if (require_fully_visible)
- {
- first_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_top());
- last_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mBottom, compare_bottom());
- }
- else
- {
- 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());
-}
-
-
-
-LLTextViewModel* LLTextBase::getViewModel() const
-{
- return (LLTextViewModel*)mViewModel.get();
-}
-
-void LLTextBase::addDocumentChild(LLView* view)
-{
- mDocumentView->addChild(view);
-}
-
-void LLTextBase::removeDocumentChild(LLView* view)
-{
- mDocumentView->removeChild(view);
-}
-
-
-void LLTextBase::updateSegments()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
- createDefaultSegment();
-}
-
-void LLTextBase::getSegmentAndOffset( S32 startpos, segment_set_t::const_iterator* seg_iter, S32* offsetp ) const
-{
- *seg_iter = getSegIterContaining(startpos);
- if (*seg_iter == mSegments.end())
- {
- *offsetp = 0;
- }
- else
- {
- *offsetp = startpos - (**seg_iter)->getStart();
- }
-}
-
-void LLTextBase::getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp )
-{
- *seg_iter = getSegIterContaining(startpos);
- if (*seg_iter == mSegments.end())
- {
- *offsetp = 0;
- }
- else
- {
- *offsetp = startpos - (**seg_iter)->getStart();
- }
-}
-
-LLTextBase::segment_set_t::iterator LLTextBase::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();
-
- // when there are no segments, we return the end iterator, which must be checked by caller
- if (mSegments.size() <= 1) { return mSegments.begin(); }
-
- index_segment->setStart(index);
- index_segment->setEnd(index);
- segment_set_t::iterator it = mSegments.upper_bound(index_segment);
- return it;
-}
-
-LLTextBase::segment_set_t::const_iterator LLTextBase::getSegIterContaining(S32 index) const
-{
- static LLPointer<LLIndexSegment> index_segment = new LLIndexSegment();
-
- // when there are no segments, we return the end iterator, which must be checked by caller
- if (mSegments.size() <= 1) { return mSegments.begin(); }
-
- index_segment->setStart(index);
- index_segment->setEnd(index);
- LLTextBase::segment_set_t::const_iterator it = mSegments.upper_bound(index_segment);
- return it;
-}
-
-// Finds the text segment (if any) at the give local screen position
-LLTextSegmentPtr LLTextBase::getSegmentAtLocalPos( S32 x, S32 y, bool hit_past_end_of_line)
-{
- // Find the cursor position at the requested local screen position
- S32 offset = getDocIndexFromLocalCoord( x, y, false, hit_past_end_of_line);
- segment_set_t::iterator seg_iter = getSegIterContaining(offset);
- if (seg_iter != mSegments.end())
- {
- return *seg_iter;
- }
- else
- {
- return LLTextSegmentPtr();
- }
-}
-
-void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url)
-{
- // work out the XUI menu file to use for this url
- LLUrlMatch match;
- std::string url = in_url;
- if (! LLUrlRegistry::instance().findUrl(url, match))
- {
- return;
- }
-
- std::string xui_file = match.getMenuName();
- if (xui_file.empty())
- {
- return;
- }
-
- // set up the callbacks for all of the potential menu items, N.B. we
- // don't use const ref strings in callbacks in case url goes out of scope
- LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
- registrar.add("Url.Open", boost::bind(&LLUrlAction::openURL, url));
- registrar.add("Url.OpenInternal", boost::bind(&LLUrlAction::openURLInternal, url));
- registrar.add("Url.OpenExternal", boost::bind(&LLUrlAction::openURLExternal, url));
- registrar.add("Url.Execute", boost::bind(&LLUrlAction::executeSLURL, url, true));
- registrar.add("Url.Block", boost::bind(&LLUrlAction::blockObject, url));
- registrar.add("Url.Unblock", boost::bind(&LLUrlAction::unblockObject, 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.ReportAbuse", boost::bind(&LLUrlAction::reportAbuse, 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
-
- LLContextMenu* menu = static_cast<LLContextMenu*>(mPopupMenuHandle.get());
- if (menu)
- {
- menu->die();
- mPopupMenuHandle.markDead();
- }
- llassert(LLMenuGL::sMenuContainer != NULL);
- menu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(xui_file, LLMenuGL::sMenuContainer,
- LLMenuHolderGL::child_registry_t::instance());
- if (menu)
- {
- mPopupMenuHandle = menu->getHandle();
-
- if (mIsFriendSignal)
- {
- bool isFriend = *(*mIsFriendSignal)(LLUUID(LLUrlAction::getUserID(url)));
- LLView* addFriendButton = menu->getChild<LLView>("add_friend");
- LLView* removeFriendButton = menu->getChild<LLView>("remove_friend");
-
- if (addFriendButton && removeFriendButton)
- {
- addFriendButton->setEnabled(!isFriend);
- removeFriendButton->setEnabled(isFriend);
- }
- }
-
- if (mIsObjectBlockedSignal)
- {
- bool is_blocked = *(*mIsObjectBlockedSignal)(LLUUID(LLUrlAction::getObjectId(url)), LLUrlAction::getObjectName(url));
- LLView* blockButton = menu->getChild<LLView>("block_object");
- LLView* unblockButton = menu->getChild<LLView>("unblock_object");
-
- if (blockButton && unblockButton)
- {
- blockButton->setVisible(!is_blocked);
- unblockButton->setVisible(is_blocked);
- }
- }
- menu->show(x, y);
- LLMenuGL::showPopup(this, menu, x, y);
- }
-}
-
-void LLTextBase::setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params)
-{
- // clear out the existing text and segments
- getViewModel()->setDisplay(LLWStringUtil::null);
-
- clearSegments();
-// createDefaultSegment();
-
- deselect();
-
- // append the new text (supports Url linking)
- std::string text(utf8str);
- LLStringUtil::removeCRLF(text);
-
- // appendText modifies mCursorPos...
- appendText(text, false, input_params);
- // ...so move cursor to top after appending text
- if (!mTrackEnd)
- {
- startOfDoc();
- }
-
- onValueChange(0, getLength());
-}
-
-//virtual
-std::string LLTextBase::getText() const
-{
- return getViewModel()->getValue().asString();
-}
-
-// IDEVO - icons can be UI image names or UUID sent from
-// server with avatar display name
-static LLUIImagePtr image_from_icon_name(const std::string& icon_name)
-{
- if (LLUUID::validate(icon_name))
- {
- return LLUI::getUIImageByID( LLUUID(icon_name) );
- }
- else
- {
- return LLUI::getUIImage(icon_name);
- }
-}
-
-
-void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
- LLStyle::Params style_params(input_params);
- 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).
- {
- 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), isContentTrusted() || mAlwaysShowIcons))
- {
- start = match.getStart();
- end = match.getEnd()+1;
-
- LLStyle::Params link_params(style_params);
- link_params.overwriteFrom(match.getStyle());
-
- // output the text before the Url
- if (start > 0)
- {
- if (part == (S32)LLTextParser::WHOLE ||
- part == (S32)LLTextParser::START)
- {
- part = (S32)LLTextParser::START;
- }
- else
- {
- part = (S32)LLTextParser::MIDDLE;
- }
- std::string subtext=text.substr(0,start);
- appendAndHighlightText(subtext, part, style_params);
- }
-
- // add icon before url if need
- LLTextUtil::processUrlMatch(&match, this, isContentTrusted() || match.isTrusted() || mAlwaysShowIcons);
- if ((isContentTrusted() || match.isTrusted()) && !match.getIcon().empty() )
- {
- setLastSegmentToolTip(LLTrans::getString("TooltipSLIcon"));
- }
-
- // output the styled Url
- appendAndHighlightTextImpl(match.getLabel(), part, link_params, match.underlineOnHoverOnly());
- bool tooltip_required = !match.getTooltip().empty();
-
- // set the tooltip for the Url label
- if (tooltip_required)
- {
- setLastSegmentToolTip(match.getTooltip());
- }
-
- // show query part of url with gray color only for LLUrlEntryHTTP 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 query part of url
- if (tooltip_required)
- {
- setLastSegmentToolTip(match.getTooltip());
- }
- }
-
- // move on to the rest of the text after the Url
- if (end < (S32)text.length())
- {
- text = text.substr(end,text.length() - end);
- end=0;
- part=(S32)LLTextParser::END;
- }
- else
- {
- break;
- }
- }
- if (part != (S32)LLTextParser::WHOLE)
- part=(S32)LLTextParser::END;
- if (end < (S32)text.length())
- appendAndHighlightText(text, part, style_params);
- }
- else
- {
- appendAndHighlightText(new_text, part, style_params);
- }
-}
-
-void LLTextBase::setLastSegmentToolTip(const std::string &tooltip)
-{
- segment_set_t::iterator it = getSegIterContaining(getLength()-1);
- if (it != mSegments.end())
- {
- LLTextSegmentPtr segment = *it;
- segment->setToolTip(tooltip);
- }
-}
-
-void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
- if (new_text.empty())
- return;
-
- if(prepend_newline)
- appendLineBreakSegment(input_params);
- 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)
-{
- LL_DEBUGS() << "reflow on object " << (void*)this << " index = " << mReflowIndex << ", new index = " << index << LL_ENDL;
- mReflowIndex = llmin(mReflowIndex, index);
-}
-
-S32 LLTextBase::removeFirstLine()
-{
- if (!mLineInfoList.empty())
- {
- S32 length = getLineEnd(0);
- deselect();
- removeStringNoUndo(0, length);
- return length;
- }
- return 0;
-}
-
-void LLTextBase::appendLineBreakSegment(const LLStyle::Params& style_params)
-{
- segment_vec_t segments;
- LLStyleConstSP sp(new LLStyle(style_params));
- segments.push_back(new LLLineBreakTextSegment(sp, getLength()));
-
- insertStringNoUndo(getLength(), utf8str_to_wstring("\n"), &segments);
-}
-
-void LLTextBase::appendImageSegment(const LLStyle::Params& style_params)
-{
- if(getPlainText())
- {
- return;
- }
- segment_vec_t segments;
- LLStyleConstSP sp(new LLStyle(style_params));
- segments.push_back(new LLImageTextSegment(sp, getLength(),*this));
-
- insertStringNoUndo(getLength(), utf8str_to_wstring(" "), &segments);
-}
-
-void LLTextBase::appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo)
-{
- segment_vec_t segments;
- LLWString widget_wide_text = utf8str_to_wstring(text);
- segments.push_back(new LLInlineViewSegment(params, getLength(), getLength() + widget_wide_text.size()));
-
- insertStringNoUndo(getLength(), widget_wide_text, &segments);
-}
-
-void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only)
-{
- // Save old state
- S32 selection_start = mSelectionStart;
- S32 selection_end = mSelectionEnd;
- bool was_selecting = mIsSelecting;
- S32 cursor_pos = mCursorPos;
- S32 old_length = getLength();
- bool cursor_was_at_end = (mCursorPos == old_length);
-
- deselect();
-
- setCursorPos(old_length);
-
- if (mParseHighlights)
- {
- LLStyle::Params highlight_params(style_params);
-
- LLSD pieces = LLTextParser::instance().parsePartialLineHighlights(new_text, highlight_params.color(), (LLTextParser::EHighlightPosition)highlight_part);
- for (S32 i = 0; i < pieces.size(); i++)
- {
- LLSD color_llsd = pieces[i]["color"];
- LLColor4 lcolor;
- lcolor.setValue(color_llsd);
- highlight_params.color = lcolor;
-
- LLWString wide_text;
- wide_text = utf8str_to_wstring(pieces[i]["text"].asString());
-
- S32 cur_length = getLength();
- LLStyleConstSP sp(new LLStyle(highlight_params));
- LLTextSegmentPtr segmentp;
- if (underline_on_hover_only || mSkipLinkUnderline)
- {
- highlight_params.font.style("NORMAL");
- LLStyleConstSP normal_sp(new LLStyle(highlight_params));
- segmentp = new LLOnHoverChangeableTextSegment(sp, normal_sp, cur_length, cur_length + wide_text.size(), *this);
- }
- else
- {
- segmentp = new LLNormalTextSegment(sp, cur_length, cur_length + wide_text.size(), *this);
- }
- segment_vec_t segments;
- segments.push_back(segmentp);
- insertStringNoUndo(cur_length, wide_text, &segments);
- }
- }
- else
- {
- LLWString wide_text;
- wide_text = utf8str_to_wstring(new_text);
-
- segment_vec_t segments;
- S32 segment_start = old_length;
- S32 segment_end = old_length + wide_text.size();
- LLStyleConstSP sp(new LLStyle(style_params));
- if (underline_on_hover_only || mSkipLinkUnderline)
- {
- LLStyle::Params normal_style_params(style_params);
- normal_style_params.font.style("NORMAL");
- LLStyleConstSP normal_sp(new LLStyle(normal_style_params));
- segments.push_back(new LLOnHoverChangeableTextSegment(sp, normal_sp, segment_start, segment_end, *this));
- }
- else
- {
- segments.push_back(new LLNormalTextSegment(sp, segment_start, segment_end, *this));
- }
-
- insertStringNoUndo(getLength(), wide_text, &segments);
- }
-
- // Set the cursor and scroll position
- if (selection_start != selection_end)
- {
- mSelectionStart = selection_start;
- mSelectionEnd = selection_end;
-
- mIsSelecting = was_selecting;
- setCursorPos(cursor_pos);
- }
- else if (cursor_was_at_end)
- {
- setCursorPos(getLength());
- }
- else
- {
- setCursorPos(cursor_pos);
- }
-}
-
-void LLTextBase::appendAndHighlightText(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only)
-{
- if (new_text.empty())
- {
- return;
- }
-
- std::string::size_type start = 0;
- std::string::size_type pos = new_text.find("\n", start);
-
- while (pos != std::string::npos)
- {
- if (pos != start)
- {
- std::string str = std::string(new_text,start,pos-start);
- appendAndHighlightTextImpl(str, highlight_part, style_params, underline_on_hover_only);
- }
- appendLineBreakSegment(style_params);
- start = pos+1;
- pos = new_text.find("\n", start);
- }
-
- std::string str = std::string(new_text, start, new_text.length() - start);
- appendAndHighlightTextImpl(str, highlight_part, style_params, underline_on_hover_only);
-}
-
-
-void LLTextBase::replaceUrl(const std::string &url,
- const std::string &label,
- const std::string &icon)
-{
- // get the full (wide) text for the editor so we can change it
- LLWString text = getWText();
- LLWString wlabel = utf8str_to_wstring(label);
- bool modified = false;
- S32 seg_start = 0;
-
- // iterate through each segment looking for ones styled as links
- segment_set_t::iterator it;
- for (it = mSegments.begin(); it != mSegments.end(); ++it)
- {
- LLTextSegment *seg = *it;
- LLStyleConstSP style = seg->getStyle();
-
- // update segment start/end length in case we replaced text earlier
- S32 seg_length = seg->getEnd() - seg->getStart();
- seg->setStart(seg_start);
- seg->setEnd(seg_start + seg_length);
-
- // if we find a link with our Url, then replace the label
- if (style->getLinkHREF() == url)
- {
- S32 start = seg->getStart();
- S32 end = seg->getEnd();
- text = text.substr(0, start) + wlabel + text.substr(end, text.size() - end + 1);
- seg->setEnd(start + wlabel.size());
- modified = true;
- }
-
- // Icon might be updated when more avatar or group info
- // becomes available
- if (style->isImage() && style->getLinkHREF() == url)
- {
- LLUIImagePtr image = image_from_icon_name( icon );
- if (image)
- {
- LLStyle::Params icon_params;
- icon_params.image = image;
- LLStyleConstSP new_style(new LLStyle(icon_params));
- seg->setStyle(new_style);
- modified = true;
- }
- }
-
- // work out the character offset for the next segment
- seg_start = seg->getEnd();
- }
-
- // update the editor with the new (wide) text string
- if (modified)
- {
- getViewModel()->setDisplay(text);
- deselect();
- setCursorPos(mCursorPos);
- needsReflow();
- }
-}
-
-
-void LLTextBase::setWText(const LLWString& text)
-{
- setText(wstring_to_utf8str(text));
-}
-
-const LLWString& LLTextBase::getWText() const
-{
- return getViewModel()->getDisplay();
-}
-
-// If round is true, if the position is on the right half of a character, the cursor
-// will be put to its right. If round is false, the cursor will always be put to the
-// character's left.
-
-S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, bool round, bool hit_past_end_of_line) const
-{
- // Figure out which line we're nearest to.
- LLRect 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(), doc_y, compare_bottom());
-
- if (!mLineInfoList.size() || line_iter == mLineInfoList.end())
- {
- return getLength(); // past the end
- }
-
- S32 pos = getLength();
- F32 start_x = line_iter->mRect.mLeft + doc_rect.mLeft;
-
- segment_set_t::iterator line_seg_iter;
- S32 line_seg_offset;
- for(getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset);
- line_seg_iter != mSegments.end();
- ++line_seg_iter, line_seg_offset = 0)
- {
- const LLTextSegmentPtr segmentp = *line_seg_iter;
-
- S32 segment_line_start = segmentp->getStart() + line_seg_offset;
- S32 segment_line_length = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd) - segment_line_start;
- F32 text_width;
- S32 text_height;
- bool newline = segmentp->getDimensionsF32(line_seg_offset, segment_line_length, text_width, text_height);
-
- if(newline)
- {
- pos = segment_line_start + segmentp->getOffset(local_x - start_x, line_seg_offset, segment_line_length, round);
- break;
- }
-
- // if we've reached a line of text *below* the mouse cursor, doc index is first character on that line
- if (hit_past_end_of_line && doc_y > line_iter->mRect.mTop)
- {
- pos = segment_line_start;
- break;
- }
- if (local_x < start_x + text_width) // cursor to left of right edge of text
- {
- // Figure out which character we're nearest to.
- S32 offset;
- if (!segmentp->canEdit())
- {
- F32 segment_width;
- S32 segment_height;
- segmentp->getDimensionsF32(0, segmentp->getEnd() - segmentp->getStart(), segment_width, segment_height);
- if (round && local_x - start_x > segment_width / 2)
- {
- offset = segment_line_length;
- }
- else
- {
- offset = 0;
- }
- }
- else
- {
- offset = segmentp->getOffset(local_x - start_x, line_seg_offset, segment_line_length, round);
- }
- pos = segment_line_start + offset;
- break;
- }
- else if (hit_past_end_of_line && segmentp->getEnd() >= line_iter->mDocIndexEnd)
- {
- 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;
- }
-
- return pos;
-}
-
-// returns rectangle of insertion caret
-// in document coordinate frame from given index into text
-LLRect LLTextBase::getDocRectFromDocIndex(S32 pos) const
-{
- if (mLineInfoList.empty())
- {
- return LLRect();
- }
-
- // clamp pos to valid values
- pos = llclamp(pos, 0, mLineInfoList.back().mDocIndexEnd - 1);
-
- line_list_t::const_iterator line_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), pos, line_end_compare());
-
- segment_set_t::iterator line_seg_iter;
- S32 line_seg_offset;
- segment_set_t::iterator cursor_seg_iter;
- S32 cursor_seg_offset;
- getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset);
- getSegmentAndOffset(pos, &cursor_seg_iter, &cursor_seg_offset);
-
- F32 doc_left_precise = line_iter->mRect.mLeft;
-
- while(line_seg_iter != mSegments.end())
- {
- const LLTextSegmentPtr segmentp = *line_seg_iter;
-
- if (line_seg_iter == cursor_seg_iter)
- {
- // cursor advanced to right based on difference in offset of cursor to start of line
- F32 segment_width;
- S32 segment_height;
- segmentp->getDimensionsF32(line_seg_offset, cursor_seg_offset - line_seg_offset, segment_width, segment_height);
- doc_left_precise += segment_width;
-
- break;
- }
- else
- {
- // add remainder of current text segment to cursor position
- F32 segment_width;
- S32 segment_height;
- segmentp->getDimensionsF32(line_seg_offset, (segmentp->getEnd() - segmentp->getStart()) - line_seg_offset, segment_width, segment_height);
- doc_left_precise += segment_width;
- // offset will be 0 for all segments after the first
- line_seg_offset = 0;
- // go to next text segment on this line
- ++line_seg_iter;
- }
- }
-
- LLRect doc_rect;
- doc_rect.mLeft = doc_left_precise;
- doc_rect.mBottom = line_iter->mRect.mBottom;
- doc_rect.mTop = line_iter->mRect.mTop;
-
- // set rect to 0 width
- doc_rect.mRight = doc_rect.mLeft;
-
- return doc_rect;
-}
-
-LLRect LLTextBase::getLocalRectFromDocIndex(S32 pos) const
-{
- LLRect content_window_rect = mScroller ? mScroller->getContentWindowRect() : getLocalRect();
- if (mBorderVisible)
- {
- content_window_rect.stretch(-1);
- }
-
- LLRect local_rect;
-
- if (mLineInfoList.empty())
- {
- // return default height rect in upper left
- local_rect = content_window_rect;
- local_rect.mBottom = local_rect.mTop - mFont->getLineHeight();
- return local_rect;
- }
-
- // get the rect in document coordinates
- LLRect doc_rect = getDocRectFromDocIndex(pos);
-
- // compensate for scrolled, inset view of doc
- LLRect scrolled_view_rect = getVisibleDocumentRect();
- local_rect = doc_rect;
- local_rect.translate(content_window_rect.mLeft - scrolled_view_rect.mLeft,
- content_window_rect.mBottom - scrolled_view_rect.mBottom);
-
- return local_rect;
-}
-
-void LLTextBase::updateCursorXPos()
-{
- // reset desired x cursor position
- mDesiredXPixel = getLocalRectFromDocIndex(mCursorPos).mLeft;
-}
-
-
-void LLTextBase::startOfLine()
-{
- S32 offset = getLineOffsetFromDocIndex(mCursorPos);
- setCursorPos(mCursorPos - offset);
-}
-
-void LLTextBase::endOfLine()
-{
- S32 line = getLineNumFromDocIndex(mCursorPos);
- S32 num_lines = getLineCount();
- if (line + 1 >= num_lines)
- {
- setCursorPos(getLength());
- }
- else
- {
- setCursorPos( getLineStart(line + 1) - 1 );
- }
-}
-
-void LLTextBase::startOfDoc()
-{
- setCursorPos(0);
- if (mScroller)
- {
- mScroller->goToTop();
- }
-}
-
-void LLTextBase::endOfDoc()
-{
- setCursorPos(getLength());
- if (mScroller)
- {
- mScroller->goToBottom();
- }
-}
-
-void LLTextBase::changePage( S32 delta )
-{
- const S32 PIXEL_OVERLAP_ON_PAGE_CHANGE = 10;
- if (delta == 0 || !mScroller) return;
-
- LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos);
-
- if( delta == -1 )
- {
- mScroller->pageUp(PIXEL_OVERLAP_ON_PAGE_CHANGE);
- }
- else
- if( delta == 1 )
- {
- mScroller->pageDown(PIXEL_OVERLAP_ON_PAGE_CHANGE);
- }
-
- if (getLocalRectFromDocIndex(mCursorPos) == cursor_rect)
- {
- // cursor didn't change apparent position, so move to top or bottom of document, respectively
- if (delta < 0)
- {
- startOfDoc();
- }
- else
- {
- endOfDoc();
- }
- }
- else
- {
- setCursorAtLocalPos(cursor_rect.getCenterX(), cursor_rect.getCenterY(), true, false);
- }
-}
-
-// Picks a new cursor position based on the screen size of text being drawn.
-void LLTextBase::setCursorAtLocalPos( S32 local_x, S32 local_y, bool round, bool keep_cursor_offset )
-{
- setCursorPos(getDocIndexFromLocalCoord(local_x, local_y, round), keep_cursor_offset);
-}
-
-
-void LLTextBase::changeLine( S32 delta )
-{
- S32 line = getLineNumFromDocIndex(mCursorPos);
- S32 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);
-
- 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()
-{
- return mScroller->isAtTop();
-}
-
-bool LLTextBase::scrolledToEnd()
-{
- return mScroller->isAtBottom();
-}
-
-bool LLTextBase::setCursor(S32 row, S32 column)
-{
- if (row < 0 || column < 0) return false;
-
- S32 n_lines = mLineInfoList.size();
- for (S32 line = row; line < n_lines; ++line)
- {
- const line_info& li = mLineInfoList[line];
-
- if (li.mLineNum < row)
- {
- continue;
- }
- else if (li.mLineNum > row)
- {
- break; // invalid column specified
- }
-
- // Found the given row.
- S32 line_length = li.mDocIndexEnd - li.mDocIndexStart;;
- if (column >= line_length)
- {
- column -= line_length;
- continue;
- }
-
- // Found the given column.
- updateCursorXPos();
- S32 doc_pos = li.mDocIndexStart + column;
- return setCursorPos(doc_pos);
- }
-
- return false; // invalid row or column specified
-}
-
-
-bool LLTextBase::setCursorPos(S32 cursor_pos, bool keep_cursor_offset)
-{
- S32 new_cursor_pos = cursor_pos;
- if (new_cursor_pos != mCursorPos)
- {
- new_cursor_pos = getEditableIndex(new_cursor_pos, new_cursor_pos >= mCursorPos);
- }
-
- mCursorPos = llclamp(new_cursor_pos, 0, (S32)getLength());
- needsScroll();
- if (!keep_cursor_offset)
- updateCursorXPos();
- // did we get requested position?
- return new_cursor_pos == cursor_pos;
-}
-
-// constraint cursor to editable segments of document
-S32 LLTextBase::getEditableIndex(S32 index, bool increasing_direction)
-{
- segment_set_t::iterator segment_iter;
- S32 offset;
- getSegmentAndOffset(index, &segment_iter, &offset);
- if (segment_iter == mSegments.end())
- {
- return 0;
- }
-
- LLTextSegmentPtr segmentp = *segment_iter;
-
- if (segmentp->canEdit())
- {
- return segmentp->getStart() + offset;
- }
- else if (segmentp->getStart() < index && index < segmentp->getEnd())
- {
- // bias towards document end
- if (increasing_direction)
- {
- return segmentp->getEnd();
- }
- // bias towards document start
- else
- {
- return segmentp->getStart();
- }
- }
- else
- {
- return index;
- }
-}
-
-void LLTextBase::updateRects()
-{
- LLRect old_text_rect = mVisibleTextRect;
- mVisibleTextRect = mScroller ? mScroller->getContentWindowRect() : getLocalRect();
-
- if (mLineInfoList.empty())
- {
- mTextBoundingRect = LLRect(0, mVPad, mHPad, 0);
- }
- else
- {
- mTextBoundingRect = mLineInfoList.begin()->mRect;
- for (line_list_t::const_iterator line_iter = ++mLineInfoList.begin();
- line_iter != mLineInfoList.end();
- ++line_iter)
- {
- mTextBoundingRect.unionWith(line_iter->mRect);
- }
-
- mTextBoundingRect.mTop += mVPad;
-
- 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)
- {
- it->mRect.translate(0, delta_pos);
- }
- mTextBoundingRect.translate(0, delta_pos);
- }
-
- // update document container dimensions according to text contents
- 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?
- // if so, use entire width of text contents
- // otherwise, stop at width of mVisibleTextRect
- //FIXME: consider use of getWordWrap() instead
- doc_rect.mRight = mScroller
- ? llmax(mVisibleTextRect.getWidth(), mTextBoundingRect.mRight)
- : mVisibleTextRect.getWidth();
- doc_rect.mTop = llmax(mVisibleTextRect.mTop, mTextBoundingRect.mTop);
-
- 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);
-
- //update mVisibleTextRect *after* mDocumentView has been resized
- // so that scrollbars are added if document needs to scroll
- // since mVisibleTextRect does not include scrollbars
- mVisibleTextRect = mScroller ? mScroller->getContentWindowRect() : getLocalRect();
- //FIXME: replace border with image?
- if (mBorderVisible)
- {
- mVisibleTextRect.stretch(-1);
- }
- if (mVisibleTextRect != old_text_rect)
- {
- 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);
-}
-
-
-void LLTextBase::startSelection()
-{
- if( !mIsSelecting )
- {
- mIsSelecting = true;
- mSelectionStart = mCursorPos;
- mSelectionEnd = mCursorPos;
- }
-}
-
-void LLTextBase::endSelection()
-{
- if( mIsSelecting )
- {
- mIsSelecting = false;
- mSelectionEnd = mCursorPos;
- }
-}
-
-// get portion of document that is visible in text editor
-LLRect LLTextBase::getVisibleDocumentRect() const
-{
- if (mScroller)
- {
- return mScroller->getVisibleContentRect();
- }
- else if (mClip)
- {
- 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
- doc_rect.mBottom = doc_rect.getHeight() - mVisibleTextRect.getHeight();
- return doc_rect;
- }
-}
-
-boost::signals2::connection LLTextBase::setURLClickedCallback(const commit_signal_t::slot_type& cb)
-{
- if (!mURLClickSignal)
- {
- mURLClickSignal = new commit_signal_t();
- }
- return mURLClickSignal->connect(cb);
-}
-
-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);
-}
-
-boost::signals2::connection LLTextBase::setIsObjectBlockedCallback(const is_blocked_signal_t::slot_type& cb)
-{
- if (!mIsObjectBlockedSignal)
- {
- mIsObjectBlockedSignal = new is_blocked_signal_t();
- }
- return mIsObjectBlockedSignal->connect(cb);
-}
-
-//
-// LLTextSegment
-//
-
-LLTextSegment::~LLTextSegment()
-{}
-
-bool LLTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const { width = 0; height = 0; return false; }
-bool LLTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const
-{
- F32 fwidth = 0;
- bool result = getDimensionsF32(first_char, num_chars, fwidth, height);
- width = ll_round(fwidth);
- return result;
-}
-
-S32 LLTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const { return 0; }
-S32 LLTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const { return 0; }
-void LLTextSegment::updateLayout(const LLTextBase& editor) {}
-F32 LLTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) { return draw_rect.mLeft; }
-bool LLTextSegment::canEdit() const { return false; }
-void LLTextSegment::unlinkFromDocument(LLTextBase*) {}
-void LLTextSegment::linkToDocument(LLTextBase*) {}
-const LLColor4& LLTextSegment::getColor() const { return LLColor4::white; }
-//void LLTextSegment::setColor(const LLColor4 &color) {}
-LLStyleConstSP LLTextSegment::getStyle() const {static LLStyleConstSP sp(new LLStyle()); return sp; }
-void LLTextSegment::setStyle(LLStyleConstSP style) {}
-void LLTextSegment::setToken( LLKeywordToken* token ) {}
-LLKeywordToken* LLTextSegment::getToken() const { return NULL; }
-void LLTextSegment::setToolTip( const std::string &msg ) {}
-void LLTextSegment::dump() const {}
-bool LLTextSegment::handleMouseDown(S32 x, S32 y, MASK mask) { return false; }
-bool LLTextSegment::handleMouseUp(S32 x, S32 y, MASK mask) { return false; }
-bool LLTextSegment::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { return false; }
-bool LLTextSegment::handleMiddleMouseUp(S32 x, S32 y, MASK mask) { return false; }
-bool LLTextSegment::handleRightMouseDown(S32 x, S32 y, MASK mask) { return false; }
-bool LLTextSegment::handleRightMouseUp(S32 x, S32 y, MASK mask) { return false; }
-bool LLTextSegment::handleDoubleClick(S32 x, S32 y, MASK mask) { return false; }
-bool LLTextSegment::handleHover(S32 x, S32 y, MASK mask) { return false; }
-bool LLTextSegment::handleScrollWheel(S32 x, S32 y, S32 clicks) { return false; }
-bool LLTextSegment::handleScrollHWheel(S32 x, S32 y, S32 clicks) { return false; }
-bool LLTextSegment::handleToolTip(S32 x, S32 y, MASK mask) { return false; }
-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 {}
-bool LLTextSegment::hasMouseCapture() { return false; }
-
-//
-// LLNormalTextSegment
-//
-
-LLNormalTextSegment::LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor )
-: LLTextSegment(start, end),
- mStyle( style ),
- mToken(NULL),
- mEditor(editor)
-{
- mFontHeight = mStyle->getFont()->getLineHeight();
-
- LLUIImagePtr image = mStyle->getImage();
- if (image.notNull())
- {
- mImageLoadedConnection = image->addLoadedCallback(boost::bind(&LLTextBase::needsReflow, &mEditor, start));
- }
-}
-
-LLNormalTextSegment::LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible)
-: LLTextSegment(start, end),
- mToken(NULL),
- mEditor(editor)
-{
- mStyle = new LLStyle(LLStyle::Params().visible(is_visible).color(color));
-
- mFontHeight = mStyle->getFont()->getLineHeight();
-}
-
-LLNormalTextSegment::~LLNormalTextSegment()
-{
- mImageLoadedConnection.disconnect();
-}
-
-
-F32 LLNormalTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect)
-{
- if( end - start > 0 )
- {
- return drawClippedSegment( getStart() + start, getStart() + end, selection_start, selection_end, draw_rect);
- }
- return draw_rect.mLeft;
-}
-
-// Draws a single text segment, reversing the color for selection if needed.
-F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRectf rect)
-{
- F32 alpha = LLViewDrawContext::getCurrentContext().mAlpha;
-
- const LLWString &text = getWText();
-
- F32 right_x = rect.mLeft;
- if (!mStyle->isVisible())
- {
- return right_x;
- }
-
- const LLFontGL* font = mStyle->getFont();
-
- LLColor4 color = (mEditor.getReadOnly() ? mStyle->getReadOnlyColor() : mStyle->getColor()) % alpha;
-
- if( selection_start > seg_start )
- {
- // Draw normally
- S32 start = seg_start;
- S32 end = llmin( selection_start, seg_end );
- S32 length = end - start;
- font->render(text, start,
- rect,
- color,
- LLFontGL::LEFT, mEditor.mTextVAlign,
- LLFontGL::NORMAL,
- mStyle->getShadowType(),
- length,
- &right_x,
- mEditor.getUseEllipses(),
- mEditor.getUseColor());
- }
- rect.mLeft = right_x;
-
- if( (selection_start < seg_end) && (selection_end > seg_start) )
- {
- // Draw reversed
- S32 start = llmax( selection_start, seg_start );
- S32 end = llmin( selection_end, seg_end );
- S32 length = end - start;
-
- font->render(text, start,
- rect,
- mStyle->getSelectedColor().get(),
- LLFontGL::LEFT, mEditor.mTextVAlign,
- LLFontGL::NORMAL,
- LLFontGL::NO_SHADOW,
- length,
- &right_x,
- mEditor.getUseEllipses(),
- mEditor.getUseColor());
- }
- rect.mLeft = right_x;
- if( selection_end < seg_end )
- {
- // Draw normally
- S32 start = llmax( selection_end, seg_start );
- S32 end = seg_end;
- S32 length = end - start;
- font->render(text, start,
- rect,
- color,
- LLFontGL::LEFT, mEditor.mTextVAlign,
- LLFontGL::NORMAL,
- mStyle->getShadowType(),
- length,
- &right_x,
- mEditor.getUseEllipses(),
- mEditor.getUseColor());
- }
- return right_x;
-}
-
-bool LLNormalTextSegment::handleHover(S32 x, S32 y, MASK mask)
-{
- if (getStyle() && getStyle()->isLink())
- {
- // Only process the click if it's actually in this segment, not to the right of the end-of-line.
- if(mEditor.getSegmentAtLocalPos(x, y, false) == this)
- {
- LLUI::getInstance()->getWindow()->setCursor(UI_CURSOR_HAND);
- return true;
- }
- }
- return false;
-}
-
-bool LLNormalTextSegment::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- if (getStyle() && getStyle()->isLink())
- {
- // Only process the click if it's actually in this segment, not to the right of the end-of-line.
- if(mEditor.getSegmentAtLocalPos(x, y, false) == this)
- {
- mEditor.createUrlContextMenu(x, y, getStyle()->getLinkHREF());
- return true;
- }
- }
- return false;
-}
-
-bool LLNormalTextSegment::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- if (getStyle() && getStyle()->isLink())
- {
- // Only process the click if it's actually in this segment, not to the right of the end-of-line.
- if(mEditor.getSegmentAtLocalPos(x, y, false) == this)
- {
- // eat mouse down event on hyperlinks, so we get the mouse up
- return true;
- }
- }
-
- return false;
-}
-
-bool LLNormalTextSegment::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- if (getStyle() && getStyle()->isLink())
- {
- // Only process the click if it's actually in this segment, not to the right of the end-of-line.
- if(mEditor.getSegmentAtLocalPos(x, y, false) == this)
- {
- std::string url = getStyle()->getLinkHREF();
- if (!mEditor.mForceUrlsExternal)
- {
- LLUrlAction::clickAction(url, mEditor.isContentTrusted());
- }
- else if (!LLUrlAction::executeSLURL(url, mEditor.isContentTrusted()))
- {
- LLUrlAction::openURLExternal(url);
- }
- return true;
- }
- }
-
- return false;
-}
-
-bool LLNormalTextSegment::handleToolTip(S32 x, S32 y, MASK mask)
-{
- std::string msg;
- // do we have a tooltip for a loaded keyword (for script editor)?
- if (mToken && !mToken->getToolTip().empty())
- {
- const LLWString& wmsg = mToken->getToolTip();
- LLToolTipMgr::instance().show(wstring_to_utf8str(wmsg), (mToken->getType() == LLKeywordToken::TT_FUNCTION));
- return true;
- }
- // or do we have an explicitly set tooltip (e.g., for Urls)
- if (!mTooltip.empty())
- {
- LLToolTipMgr::instance().show(mTooltip);
- return true;
- }
-
- return false;
-}
-
-void LLNormalTextSegment::setToolTip(const std::string& tooltip)
-{
- // we cannot replace a keyword tooltip that's loaded from a file
- if (mToken)
- {
- LL_WARNS() << "LLTextSegment::setToolTip: cannot replace keyword tooltip." << LL_ENDL;
- return;
- }
- mTooltip = tooltip;
-}
-
-bool LLNormalTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const
-{
- height = 0;
- width = 0;
- if (num_chars > 0)
- {
- height = mFontHeight;
- const LLWString &text = getWText();
- // if last character is a newline, then return true, forcing line break
- width = mStyle->getFont()->getWidthF32(text.c_str(), mStart + first_char, num_chars, true);
- }
- return false;
-}
-
-S32 LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const
-{
- const LLWString &text = getWText();
- return mStyle->getFont()->charFromPixelOffset(text.c_str(), mStart + start_offset,
- (F32)segment_local_x_coord,
- F32_MAX,
- num_chars,
- round);
-}
-
-S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const
-{
- const LLWString &text = getWText();
-
- LLUIImagePtr image = mStyle->getImage();
- if( image.notNull())
- {
- num_pixels = llmax(0, num_pixels - image->getWidth());
- }
-
- S32 last_char = mEnd;
-
- // set max characters to length of segment, or to first newline
- max_chars = llmin(max_chars, last_char - (mStart + segment_offset));
-
- // if no character yet displayed on this line, don't require word wrapping since
- // we can just move to the next line, otherwise insist on it so we make forward progress
- LLFontGL::EWordWrapStyle word_wrap_style = (line_offset == 0)
- ? LLFontGL::WORD_BOUNDARY_IF_POSSIBLE
- : LLFontGL::ONLY_WORD_BOUNDARIES;
-
-
- S32 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);
-
- if (num_chars == 0
- && line_offset == 0
- && max_chars > 0)
- {
- // If at the beginning of a line, and a single character won't fit, draw it anyway
- num_chars = 1;
- }
-
- // include *either* the EOF or newline character in this run of text
- // but not both
- S32 last_char_in_run = mStart + segment_offset + num_chars;
- // check length first to avoid indexing off end of string
- if (last_char_in_run < mEnd
- && (last_char_in_run >= getLength()))
- {
- num_chars++;
- }
- return num_chars;
-}
-
-void LLNormalTextSegment::dump() const
-{
- LL_INFOS() << "Segment [" <<
-// mColor.mV[VX] << ", " <<
-// mColor.mV[VY] << ", " <<
-// mColor.mV[VZ] << "]\t[" <<
- mStart << ", " <<
- getEnd() << "]" <<
- 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();
-}
-
-//
-// LLEmojiTextSegment
-//
-LLEmojiTextSegment::LLEmojiTextSegment(LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor)
- : LLNormalTextSegment(style, start, end, editor)
-{
-}
-
-LLEmojiTextSegment::LLEmojiTextSegment(const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible)
- : LLNormalTextSegment(color, start, end, editor, is_visible)
-{
-}
-
-bool LLEmojiTextSegment::handleToolTip(S32 x, S32 y, MASK mask)
-{
- if (mTooltip.empty())
- {
- LLWString emoji = getWText().substr(getStart(), getEnd() - getStart());
- if (!emoji.empty())
- {
- mTooltip = LLEmojiHelper::instance().getToolTip(emoji[0]);
- }
- }
-
- return LLNormalTextSegment::handleToolTip(x, y, mask);
-}
-
-//
-// LLOnHoverChangeableTextSegment
-//
-
-LLOnHoverChangeableTextSegment::LLOnHoverChangeableTextSegment( LLStyleConstSP style, LLStyleConstSP normal_style, S32 start, S32 end, LLTextBase& editor ):
- LLNormalTextSegment(normal_style, start, end, editor),
- mHoveredStyle(style),
- mNormalStyle(normal_style){}
-
-/*virtual*/
-F32 LLOnHoverChangeableTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect)
-{
- F32 result = LLNormalTextSegment::draw(start, end, selection_start, selection_end, draw_rect);
- if (end == mEnd - mStart)
- {
- mStyle = mNormalStyle;
- }
- return result;
-}
-
-/*virtual*/
-bool LLOnHoverChangeableTextSegment::handleHover(S32 x, S32 y, MASK mask)
-{
- mStyle = mEditor.getSkipLinkUnderline() ? mNormalStyle : mHoveredStyle;
- return LLNormalTextSegment::handleHover(x, y, mask);
-}
-
-
-//
-// LLInlineViewSegment
-//
-
-LLInlineViewSegment::LLInlineViewSegment(const Params& p, S32 start, S32 end)
-: LLTextSegment(start, end),
- mView(p.view),
- mForceNewLine(p.force_newline),
- mLeftPad(p.left_pad),
- mRightPad(p.right_pad),
- mTopPad(p.top_pad),
- mBottomPad(p.bottom_pad)
-{
-}
-
-LLInlineViewSegment::~LLInlineViewSegment()
-{
- mView->die();
-}
-
-bool LLInlineViewSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const
-{
- if (first_char == 0 && num_chars == 0)
- {
- // We didn't fit on a line or were forced to new string
- // the widget will fall on the next line, so width here is 0
- width = 0;
-
- if (mForceNewLine)
- {
- // Chat, string can't be smaller then font height even if it is empty
- LLStyleSP s(new LLStyle(LLStyle::Params().visible(true)));
- height = s->getFont()->getLineHeight();
-
- return true; // new line
- }
- else
- {
- // height from previous segment in same string will be used, word-wrap
- height = 0;
- }
-
- }
- else
- {
- width = mLeftPad + mRightPad + mView->getRect().getWidth();
- height = mBottomPad + mTopPad + mView->getRect().getHeight();
- }
-
- return false;
-}
-
-S32 LLInlineViewSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const
-{
- // if putting a widget anywhere but at the beginning of a line
- // and the widget doesn't fit or mForceNewLine is true
- // then return 0 chars for that line, and all characters for the next
- if (mForceNewLine && line_ind == 0)
- {
- return 0;
- }
- else if (line_offset != 0 && num_pixels < mView->getRect().getWidth())
- {
- return 0;
- }
- else
- {
- return mEnd - mStart;
- }
-}
-
-void LLInlineViewSegment::updateLayout(const LLTextBase& editor)
-{
- LLRect start_rect = editor.getDocRectFromDocIndex(mStart);
- mView->setOrigin(start_rect.mLeft + mLeftPad, start_rect.mBottom + mBottomPad);
-}
-
-F32 LLInlineViewSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect)
-{
- // return padded width of widget
- // widget is actually drawn during mDocumentView's draw()
- return (F32)(draw_rect.mLeft + mView->getRect().getWidth() + mLeftPad + mRightPad);
-}
-
-void LLInlineViewSegment::unlinkFromDocument(LLTextBase* editor)
-{
- editor->removeDocumentChild(mView);
-}
-
-void LLInlineViewSegment::linkToDocument(LLTextBase* editor)
-{
- editor->addDocumentChild(mView);
-}
-
-LLLineBreakTextSegment::LLLineBreakTextSegment(S32 pos):LLTextSegment(pos,pos+1)
-{
- LLStyleSP s( new LLStyle(LLStyle::Params().visible(true)));
-
- mFontHeight = s->getFont()->getLineHeight();
-}
-LLLineBreakTextSegment::LLLineBreakTextSegment(LLStyleConstSP style,S32 pos):LLTextSegment(pos,pos+1)
-{
- mFontHeight = style->getFont()->getLineHeight();
-}
-LLLineBreakTextSegment::~LLLineBreakTextSegment()
-{
-}
-bool LLLineBreakTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const
-{
- width = 0;
- height = mFontHeight;
-
- return true;
-}
-S32 LLLineBreakTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const
-{
- return 1;
-}
-F32 LLLineBreakTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect)
-{
- return draw_rect.mLeft;
-}
-
-LLImageTextSegment::LLImageTextSegment(LLStyleConstSP style,S32 pos,class LLTextBase& editor)
-: LLTextSegment(pos,pos+1),
- mStyle( style ),
- mEditor(editor)
-{
-}
-
-LLImageTextSegment::~LLImageTextSegment()
-{
-}
-
-static const S32 IMAGE_HPAD = 3;
-
-bool LLImageTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const
-{
- width = 0;
- height = mStyle->getFont()->getLineHeight();
-
- LLUIImagePtr image = mStyle->getImage();
- if( num_chars>0 && image.notNull())
- {
- width += image->getWidth() + IMAGE_HPAD;
- height = llmax(height, image->getHeight() + IMAGE_HPAD );
- }
- return false;
-}
-
-S32 LLImageTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) 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;
-}
-
-bool LLImageTextSegment::handleToolTip(S32 x, S32 y, MASK mask)
-{
- if (!mTooltip.empty())
- {
- LLToolTipMgr::instance().show(mTooltip);
- return true;
- }
-
- return false;
-}
-
-void LLImageTextSegment::setToolTip(const std::string& tooltip)
-{
- mTooltip = tooltip;
-}
-
-F32 LLImageTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect)
-{
- if ( (start >= 0) && (end <= mEnd - mStart))
- {
- LLColor4 color = LLColor4::white % mEditor.getDrawContext().mAlpha;
- LLUIImagePtr image = mStyle->getImage();
- 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;
-}
+/**
+ * @file lltextbase.cpp
+ * @author Martin Reddy
+ * @brief The base class of text box/editor, providing Url handling support
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2009-2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltextbase.h"
+
+#include "llemojidictionary.h"
+#include "llemojihelper.h"
+#include "lllocalcliprect.h"
+#include "llmenugl.h"
+#include "llscrollcontainer.h"
+#include "llspellcheck.h"
+#include "llstl.h"
+#include "lltextparser.h"
+#include "lltextutil.h"
+#include "lltooltip.h"
+#include "lltrans.h"
+#include "lluictrl.h"
+#include "llurlaction.h"
+#include "llurlregistry.h"
+#include "llview.h"
+#include "llwindow.h"
+#include <boost/bind.hpp>
+
+const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds
+const S32 CURSOR_THICKNESS = 2;
+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),
+ mDocIndexEnd(index_end),
+ mRect(rect),
+ mLineNum(line_num)
+{}
+
+bool LLTextBase::compare_segment_end::operator()(const LLTextSegmentPtr& a, const LLTextSegmentPtr& b) const
+{
+ // sort empty spans (e.g. 11-11) after previous non-empty spans (e.g. 5-11)
+ if (a->getEnd() == b->getEnd())
+ {
+ return a->getStart() < b->getStart();
+ }
+ else
+ {
+ return a->getEnd() < b->getEnd();
+ }
+}
+
+
+// helper functors
+bool LLTextBase::compare_bottom::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 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
+bool LLTextBase::compare_top::operator()(const S32& a, const LLTextBase::line_info& b) const
+{
+ return a > b.mRect.mTop; // top of a is higher than top of b
+}
+
+bool 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 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
+{
+ bool operator()(const S32& pos, const LLTextBase::line_info& info) const
+ {
+ return (pos < info.mDocIndexEnd);
+ }
+
+ bool operator()(const LLTextBase::line_info& info, const S32& pos) const
+ {
+ return (info.mDocIndexEnd < pos);
+ }
+
+ bool operator()(const LLTextBase::line_info& a, const LLTextBase::line_info& b) const
+ {
+ return (a.mDocIndexEnd < b.mDocIndexEnd);
+ }
+
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+// LLTextBase
+//
+
+// register LLTextBase::Params under name "textbase"
+static LLWidgetNameRegistry::StaticRegistrar sRegisterTextBaseParams(&typeid(LLTextBase::Params), "textbase");
+
+LLTextBase::LineSpacingParams::LineSpacingParams()
+: multiple("multiple", 1.f),
+ pixels("pixels", 0)
+{
+}
+
+
+LLTextBase::Params::Params()
+: cursor_color("cursor_color"),
+ text_color("text_color"),
+ text_readonly_color("text_readonly_color"),
+ text_tentative_color("text_tentative_color"),
+ bg_visible("bg_visible", false),
+ border_visible("border_visible", false),
+ bg_readonly_color("bg_readonly_color"),
+ bg_writeable_color("bg_writeable_color"),
+ bg_focus_color("bg_focus_color"),
+ text_selected_color("text_selected_color"),
+ bg_selected_color("bg_selected_color"),
+ allow_scroll("allow_scroll", true),
+ plain_text("plain_text",false),
+ track_end("track_end", false),
+ read_only("read_only", false),
+ skip_link_underline("skip_link_underline", 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"),
+ text_valign("text_valign"),
+ wrap("wrap"),
+ trusted_content("trusted_content", true),
+ always_show_icons("always_show_icons", false),
+ use_ellipses("use_ellipses", false),
+ use_emoji("use_emoji", true),
+ use_color("use_color", true),
+ parse_urls("parse_urls", false),
+ force_urls_external("force_urls_external", false),
+ parse_highlights("parse_highlights", false)
+{
+ addSynonym(track_end, "track_bottom");
+ addSynonym(wrap, "word_wrap");
+ addSynonym(parse_urls, "allow_html");
+}
+
+
+LLTextBase::LLTextBase(const LLTextBase::Params &p)
+: LLUICtrl(p, LLTextViewModelPtr(new LLTextViewModel)),
+ mURLClickSignal(NULL),
+ mIsFriendSignal(NULL),
+ mIsObjectBlockedSignal(NULL),
+ mMaxTextByteLength( p.max_text_length ),
+ mFont(p.font),
+ mFontShadow(p.font_shadow),
+ mPopupMenuHandle(),
+ mReadOnly(p.read_only),
+ mSkipTripleClick(false),
+ mSkipLinkUnderline(p.skip_link_underline),
+ 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),
+ mTextSelectedColor(p.text_selected_color),
+ mSelectedBGColor(p.bg_selected_color),
+ mReflowIndex(S32_MAX),
+ mCursorPos( 0 ),
+ mScrollNeeded(false),
+ mDesiredXPixel(-1),
+ mHPad(p.h_pad),
+ mVPad(p.v_pad),
+ mHAlign(p.font_halign),
+ mVAlign(p.font_valign),
+ mTextVAlign(p.text_valign.isProvided() ? p.text_valign.getValue() : p.font_valign.getValue()),
+ mLineSpacingMult(p.line_spacing.multiple),
+ mLineSpacingPixels(p.line_spacing.pixels),
+ mClip(p.clip),
+ mClipPartial(p.clip_partial && !p.allow_scroll),
+ mTrustedContent(p.trusted_content),
+ mAlwaysShowIcons(p.always_show_icons),
+ mTrackEnd( p.track_end ),
+ mScrollIndex(-1),
+ mSelectionStart( 0 ),
+ mSelectionEnd( 0 ),
+ mIsSelecting( false ),
+ mPlainText ( p.plain_text ),
+ mWordWrap(p.wrap),
+ mUseEllipses( p.use_ellipses ),
+ mUseEmoji(p.use_emoji),
+ mUseColor(p.use_color),
+ mParseHTML(p.parse_urls),
+ mForceUrlsExternal(p.force_urls_external),
+ mParseHighlights(p.parse_highlights),
+ mBGVisible(p.bg_visible),
+ mScroller(NULL),
+ mStyleDirty(true)
+{
+ if(p.allow_scroll)
+ {
+ LLScrollContainer::Params scroll_params;
+ scroll_params.name = "text scroller";
+ scroll_params.rect = getLocalRect();
+ scroll_params.follows.flags = FOLLOWS_ALL;
+ scroll_params.is_opaque = false;
+ scroll_params.mouse_opaque = false;
+ scroll_params.min_auto_scroll_rate = 200;
+ scroll_params.max_auto_scroll_rate = 800;
+ scroll_params.border_visible = p.border_visible;
+ mScroller = LLUICtrlFactory::create<LLScrollContainer>(scroll_params);
+ addChild(mScroller);
+ }
+
+ LLView::Params view_params;
+ view_params.name = "text_contents";
+ view_params.rect = LLRect(0, 500, 500, 0);
+ view_params.mouse_opaque = false;
+
+ mDocumentView = LLUICtrlFactory::create<LLView>(view_params);
+ if (mScroller)
+ {
+ mScroller->addChild(mDocumentView);
+ }
+ else
+ {
+ addChild(mDocumentView);
+ }
+
+ if (mSpellCheck)
+ {
+ LLSpellChecker::setSettingsChangeCallback(boost::bind(&LLTextBase::onSpellCheckSettingsChange, this));
+ }
+ mSpellCheckTimer.reset();
+
+ createDefaultSegment();
+
+ updateRects();
+}
+
+LLTextBase::~LLTextBase()
+{
+ mSegments.clear();
+ LLContextMenu* menu = static_cast<LLContextMenu*>(mPopupMenuHandle.get());
+ if (menu)
+ {
+ menu->die();
+ mPopupMenuHandle.markDead();
+ }
+ delete mURLClickSignal;
+ delete mIsFriendSignal;
+ delete mIsObjectBlockedSignal;
+}
+
+void LLTextBase::initFromParams(const LLTextBase::Params& p)
+{
+ LLUICtrl::initFromParams(p);
+ resetDirty(); // Update saved text state
+ updateSegments();
+
+ // HACK: work around enabled == readonly design bug -- RN
+ // setEnabled will modify our read only status, so do this after
+ // LLTextBase::initFromParams
+ if (p.read_only.isProvided())
+ {
+ mReadOnly = p.read_only;
+ }
+}
+
+bool LLTextBase::truncate()
+{
+ bool did_truncate = false;
+
+ // First rough check - if we're less than 1/4th the size, we're OK
+ if (getLength() >= S32(mMaxTextByteLength / 4))
+ {
+ // Have to check actual byte size
+ 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 = 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.
+ removeStringNoUndo(text.size(), getWText().size() - text.size());
+ did_truncate = true;
+ }
+ }
+
+ return did_truncate;
+}
+
+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)
+ {
+ mStyle
+ .color(LLUIColor(&mFgColor)) // pass linked color instead of copy of mFGColor
+ .readonly_color(LLUIColor(&mReadOnlyFgColor))
+ .selected_color(LLUIColor(&mTextSelectedColor))
+ .font(mFont)
+ .drop_shadow(mFontShadow);
+ mStyleDirty = false;
+ }
+ return mStyle;
+}
+
+void LLTextBase::beforeValueChange()
+{
+
+}
+
+void LLTextBase::onValueChange(S32 start, S32 end)
+{
+}
+
+std::vector<LLRect> LLTextBase::getSelectionRects()
+{
+ // Nor supposed to be called without selection
+ llassert(hasSelection());
+ llassert(!mLineInfoList.empty());
+
+ std::vector<LLRect> selection_rects;
+
+ S32 selection_left = llmin(mSelectionStart, mSelectionEnd);
+ S32 selection_right = llmax(mSelectionStart, mSelectionEnd);
+
+ // 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::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mBottom, compare_top());
+
+ bool done = false;
+
+ // Find the coordinates of the selected area
+ for (; line_iter != end_iter && !done; ++line_iter)
+ {
+ // is selection visible on this line?
+ if (line_iter->mDocIndexEnd > selection_left && line_iter->mDocIndexStart < selection_right)
+ {
+ segment_set_t::iterator segment_iter;
+ S32 segment_offset;
+ getSegmentAndOffset(line_iter->mDocIndexStart, &segment_iter, &segment_offset);
+
+ // Use F32 otherwise a string of multiple segments
+ // will accumulate a large error
+ F32 left_precise = line_iter->mRect.mLeft;
+ F32 right_precise = line_iter->mRect.mLeft;
+
+ for (; segment_iter != mSegments.end(); ++segment_iter, segment_offset = 0)
+ {
+ LLTextSegmentPtr segmentp = *segment_iter;
+
+ S32 segment_line_start = segmentp->getStart() + segment_offset;
+ S32 segment_line_end = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd);
+
+ if (segment_line_start > segment_line_end) break;
+
+ F32 segment_width = 0;
+ S32 segment_height = 0;
+
+ // if selection after beginning of segment
+ if (selection_left >= segment_line_start)
+ {
+ S32 num_chars = llmin(selection_left, segment_line_end) - segment_line_start;
+ segmentp->getDimensionsF32(segment_offset, num_chars, segment_width, segment_height);
+ left_precise += segment_width;
+ }
+
+ // if selection_right == segment_line_end then that means we are the first character of the next segment
+ // or first character of the next line, in either case we want to add the length of the current segment
+ // to the selection rectangle and continue.
+ // if selection right > segment_line_end then selection spans end of current segment...
+ if (selection_right >= segment_line_end)
+ {
+ // extend selection slightly beyond end of line
+ // to indicate selection of newline character (use "n" character to determine width)
+ S32 num_chars = segment_line_end - segment_line_start;
+ segmentp->getDimensionsF32(segment_offset, num_chars, segment_width, segment_height);
+ right_precise += segment_width;
+ }
+ // else if selection ends on current segment...
+ else
+ {
+ S32 num_chars = selection_right - segment_line_start;
+ segmentp->getDimensionsF32(segment_offset, num_chars, segment_width, segment_height);
+ right_precise += segment_width;
+
+ break;
+ }
+ }
+
+ LLRect selection_rect;
+ selection_rect.mLeft = left_precise;
+ selection_rect.mRight = right_precise;
+ selection_rect.mBottom = line_iter->mRect.mBottom;
+ selection_rect.mTop = line_iter->mRect.mTop;
+
+ selection_rects.push_back(selection_rect);
+ }
+ }
+
+ return selection_rects;
+}
+
+// Draws the black box behind the selected text
+void LLTextBase::drawSelectionBackground()
+{
+ // Draw selection even if we don't have keyboard focus for search/replace
+ if (hasSelection() && !mLineInfoList.empty())
+ {
+ std::vector<LLRect> selection_rects = getSelectionRects();
+
+ // Draw the selection box (we're using a box instead of reversing the colors on the selected text).
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ const LLColor4& color = mSelectedBGColor;
+ F32 alpha = hasFocus() ? 0.7f : 0.3f;
+ alpha *= getDrawContext().mAlpha;
+
+ LLColor4 selection_color(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], alpha);
+ LLRect content_display_rect = getVisibleDocumentRect();
+
+ for (std::vector<LLRect>::iterator rect_it = selection_rects.begin();
+ rect_it != selection_rects.end();
+ ++rect_it)
+ {
+ LLRect selection_rect = *rect_it;
+ if (mScroller)
+ {
+ // If scroller is On content_display_rect has correct rect and safe to use as is
+ // Note: we might need to account for border
+ selection_rect.translate(mVisibleTextRect.mLeft - content_display_rect.mLeft, mVisibleTextRect.mBottom - content_display_rect.mBottom);
+ }
+ else
+ {
+ // If scroller is Off content_display_rect will have rect from document, adjusted to text width, heigh and position
+ // and we have to acount for offset depending on position
+ S32 v_delta = 0;
+ S32 h_delta = 0;
+ switch (mVAlign)
+ {
+ case LLFontGL::TOP:
+ v_delta = mVisibleTextRect.mTop - content_display_rect.mTop - mVPad;
+ break;
+ case LLFontGL::VCENTER:
+ v_delta = (llmax(mVisibleTextRect.getHeight() - content_display_rect.mTop, -content_display_rect.mBottom) + (mVisibleTextRect.mBottom - content_display_rect.mBottom)) / 2;
+ break;
+ case LLFontGL::BOTTOM:
+ v_delta = mVisibleTextRect.mBottom - content_display_rect.mBottom;
+ break;
+ default:
+ break;
+ }
+ switch (mHAlign)
+ {
+ case LLFontGL::LEFT:
+ h_delta = mVisibleTextRect.mLeft - content_display_rect.mLeft + mHPad;
+ break;
+ case LLFontGL::HCENTER:
+ h_delta = (llmax(mVisibleTextRect.getWidth() - content_display_rect.mLeft, -content_display_rect.mRight) + (mVisibleTextRect.mRight - content_display_rect.mRight)) / 2;
+ break;
+ case LLFontGL::RIGHT:
+ h_delta = mVisibleTextRect.mRight - content_display_rect.mRight;
+ break;
+ default:
+ break;
+ }
+ selection_rect.translate(h_delta, v_delta);
+ }
+ gl_rect_2d(selection_rect, selection_color);
+ }
+ }
+}
+
+void LLTextBase::drawCursor()
+{
+ F32 alpha = getDrawContext().mAlpha;
+
+ if( hasFocus()
+ && gFocusMgr.getAppHasFocus()
+ && !mReadOnly)
+ {
+ const LLWString &wtext = getWText();
+ const llwchar* text = wtext.c_str();
+
+ LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos);
+ cursor_rect.translate(-1, 0);
+ segment_set_t::iterator seg_it = getSegIterContaining(mCursorPos);
+
+ // take style from last segment
+ LLTextSegmentPtr segmentp;
+
+ if (seg_it != mSegments.end())
+ {
+ segmentp = *seg_it;
+ }
+ else
+ {
+ return;
+ }
+
+ // Draw the cursor
+ // (Flash the cursor every half second starting a fixed time after the last keystroke)
+ F32 elapsed = mCursorBlinkTimer.getElapsedTimeF32();
+ if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) )
+ {
+
+ if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
+ {
+ S32 segment_width = 0;
+ S32 segment_height = 0;
+ segmentp->getDimensions(mCursorPos - segmentp->getStart(), 1, segment_width, segment_height);
+ S32 width = llmax(CURSOR_THICKNESS, segment_width);
+ cursor_rect.mRight = cursor_rect.mLeft + width;
+ }
+ else
+ {
+ cursor_rect.mRight = cursor_rect.mLeft + CURSOR_THICKNESS;
+ }
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ LLColor4 cursor_color = mCursorColor.get() % alpha;
+ gGL.color4fv( cursor_color.mV );
+
+ gl_rect_2d(cursor_rect);
+
+ if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection() && text[mCursorPos] != '\n')
+ {
+ LLColor4 text_color;
+ const LLFontGL* fontp;
+ text_color = segmentp->getColor();
+ fontp = segmentp->getStyle()->getFont();
+ fontp->render(text, mCursorPos, cursor_rect,
+ LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], alpha),
+ LLFontGL::LEFT, mTextVAlign,
+ LLFontGL::NORMAL,
+ LLFontGL::NO_SHADOW,
+ 1);
+ }
+
+ // Make sure the IME is in the right place
+ LLRect screen_pos = calcScreenRect();
+ LLCoordGL ime_pos( screen_pos.mLeft + llfloor(cursor_rect.mLeft), screen_pos.mBottom + llfloor(cursor_rect.mTop) );
+
+ ime_pos.mX = (S32) (ime_pos.mX * LLUI::getScaleFactor().mV[VX]);
+ ime_pos.mY = (S32) (ime_pos.mY * LLUI::getScaleFactor().mV[VY]);
+ getWindow()->setLanguageTextInput( ime_pos );
+ }
+ }
+}
+
+void LLTextBase::drawText()
+{
+ 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
+ if( hasSelection())
+ {
+ selection_left = llmin( mSelectionStart, mSelectionEnd );
+ selection_right = llmax( mSelectionStart, mSelectionEnd );
+ }
+
+ std::pair<S32, S32> line_range = getVisibleLines(mClipPartial);
+ S32 first_line = line_range.first;
+ S32 last_line = line_range.second;
+ if (first_line >= last_line)
+ {
+ return;
+ }
+
+ S32 line_start = getLineStart(first_line);
+ // find first text segment that spans top of visible portion of text buffer
+ segment_set_t::iterator seg_iter = getSegIterContaining(line_start);
+ if (seg_iter == mSegments.end())
+ {
+ return;
+ }
+
+ // 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;
+ line_info& line = mLineInfoList[cur_line];
+
+ S32 next_start = -1;
+ S32 line_end = text_len;
+
+ if (next_line < getLineCount())
+ {
+ next_start = getLineStart(next_line);
+ line_end = next_start;
+ }
+
+ LLRectf text_rect(line.mRect.mLeft, line.mRect.mTop, line.mRect.mRight, line.mRect.mBottom);
+ 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;
+ while( seg_start < line_end )
+ {
+ while( cur_segment->getEnd() <= seg_start )
+ {
+ seg_iter++;
+ if (seg_iter == mSegments.end())
+ {
+ LL_WARNS() << "Ran off the segmentation end!" << LL_ENDL;
+
+ return;
+ }
+ cur_segment = *seg_iter;
+ }
+
+ 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
+ && next_line == last_line // this is the last visible line
+ && last_line < (S32)mLineInfoList.size()) // and there is more text to display
+ {
+ // more lines of text to go, but we can't fit them
+ // so shrink text rect to force ellipses
+ text_rect.mRight -= 2;
+ }
+
+ // 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 = cur_segment->draw(seg_start - cur_segment->getStart(), clipped_end, selection_left, selection_right, text_rect);
+
+ seg_start = clipped_end + cur_segment->getStart();
+ }
+
+ line_start = next_start;
+ }
+}
+
+///////////////////////////////////////////////////////////////////
+// Returns change in number of characters in mWText
+
+S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::segment_vec_t* segments )
+{
+ beforeValueChange();
+
+ S32 old_len = getLength(); // length() returns character length
+ S32 insert_len = wstr.length();
+
+ pos = getEditableIndex(pos, true);
+ if (pos > old_len)
+ {
+ pos = old_len;
+ // Should not happen,
+ // if you encounter this, check where wrong position comes from
+ llassert(false);
+ }
+
+ segment_set_t::iterator seg_iter = getEditableSegIterContaining(pos);
+
+ LLTextSegmentPtr default_segment;
+
+ LLTextSegmentPtr segmentp;
+ if (seg_iter != mSegments.end())
+ {
+ segmentp = *seg_iter;
+ }
+ else
+ {
+ //segmentp = mSegments.back();
+ return pos;
+ }
+
+ if (segmentp->canEdit())
+ {
+ segmentp->setEnd(segmentp->getEnd() + insert_len);
+ if (seg_iter != mSegments.end())
+ {
+ ++seg_iter;
+ }
+ }
+ else
+ {
+ // create default editable segment to hold new text
+ LLStyleConstSP sp(new LLStyle(getStyleParams()));
+ default_segment = new LLNormalTextSegment( sp, pos, pos + insert_len, *this);
+ }
+
+ // shift remaining segments to right
+ for(;seg_iter != mSegments.end(); ++seg_iter)
+ {
+ LLTextSegmentPtr segmentp = *seg_iter;
+ segmentp->setStart(segmentp->getStart() + insert_len);
+ segmentp->setEnd(segmentp->getEnd() + insert_len);
+ }
+
+ // insert new segments
+ if (segments)
+ {
+ if (default_segment.notNull())
+ {
+ // potentially overwritten by segments passed in
+ insertSegment(default_segment);
+ }
+ for (segment_vec_t::iterator seg_iter = segments->begin();
+ seg_iter != segments->end();
+ ++seg_iter)
+ {
+ LLTextSegment* segmentp = *seg_iter;
+ insertSegment(segmentp);
+ }
+ }
+
+ // Insert special segments where necessary (insertSegment takes care of splitting normal text segments around them for us)
+ if (mUseEmoji)
+ {
+ LLStyleSP emoji_style;
+ LLEmojiDictionary* ed = LLEmojiDictionary::instanceExists() ? LLEmojiDictionary::getInstance() : NULL;
+ for (S32 text_kitty = 0, text_len = wstr.size(); text_kitty < text_len; text_kitty++)
+ {
+ llwchar code = wstr[text_kitty];
+ bool isEmoji = ed ? ed->isEmoji(code) : LLStringOps::isEmoji(code);
+ if (isEmoji)
+ {
+ if (!emoji_style)
+ {
+ emoji_style = new LLStyle(getStyleParams());
+ emoji_style->setFont(LLFontGL::getFontEmojiLarge());
+ }
+
+ S32 new_seg_start = pos + text_kitty;
+ insertSegment(new LLEmojiTextSegment(emoji_style, new_seg_start, new_seg_start + 1, *this));
+ }
+ }
+ }
+
+ getViewModel()->getEditableDisplay().insert(pos, wstr);
+
+ if ( truncate() )
+ {
+ insert_len = getLength() - old_len;
+ }
+
+ onValueChange(pos, pos + insert_len);
+ needsReflow(pos);
+
+ return insert_len;
+}
+
+S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length)
+{
+
+ beforeValueChange();
+ segment_set_t::iterator seg_iter = getSegIterContaining(pos);
+ while(seg_iter != mSegments.end())
+ {
+ LLTextSegmentPtr segmentp = *seg_iter;
+ S32 end = pos + length;
+ if (segmentp->getStart() < pos)
+ {
+ // deleting from middle of segment
+ if (segmentp->getEnd() > end)
+ {
+ segmentp->setEnd(segmentp->getEnd() - length);
+ }
+ // truncating segment
+ else
+ {
+ segmentp->setEnd(pos);
+ }
+ }
+ else if (segmentp->getStart() < end)
+ {
+ // deleting entire segment
+ if (segmentp->getEnd() <= end)
+ {
+ // remove segment
+ segmentp->unlinkFromDocument(this);
+ segment_set_t::iterator seg_to_erase(seg_iter++);
+ mSegments.erase(seg_to_erase);
+ continue;
+ }
+ // deleting head of segment
+ else
+ {
+ segmentp->setStart(pos);
+ segmentp->setEnd(segmentp->getEnd() - length);
+ }
+ }
+ else
+ {
+ // shifting segments backward to fill deleted portion
+ segmentp->setStart(segmentp->getStart() - length);
+ segmentp->setEnd(segmentp->getEnd() - length);
+ }
+ ++seg_iter;
+ }
+
+ getViewModel()->getEditableDisplay().erase(pos, length);
+
+ // recreate default segment in case we erased everything
+ createDefaultSegment();
+
+ onValueChange(pos, pos);
+ needsReflow(pos);
+
+ return -length; // This will be wrong if someone calls removeStringNoUndo with an excessive length
+}
+
+S32 LLTextBase::overwriteCharNoUndo(S32 pos, llwchar wc)
+{
+ beforeValueChange();
+
+ if (pos > (S32)getLength())
+ {
+ return 0;
+ }
+ getViewModel()->getEditableDisplay()[pos] = wc;
+
+ onValueChange(pos, pos + 1);
+ needsReflow(pos);
+
+ return 1;
+}
+
+
+void LLTextBase::createDefaultSegment()
+{
+ // ensures that there is always at least one segment
+ if (mSegments.empty())
+ {
+ LLStyleConstSP sp(new LLStyle(getStyleParams()));
+ LLTextSegmentPtr default_segment = new LLNormalTextSegment( sp, 0, getLength() + 1, *this);
+ mSegments.insert(default_segment);
+ default_segment->linkToDocument(this);
+ }
+}
+
+void LLTextBase::insertSegment(LLTextSegmentPtr segment_to_insert)
+{
+ if (segment_to_insert.isNull())
+ {
+ return;
+ }
+
+ segment_set_t::iterator cur_seg_iter = getSegIterContaining(segment_to_insert->getStart());
+ S32 reflow_start_index = 0;
+
+ if (cur_seg_iter == mSegments.end())
+ {
+ mSegments.insert(segment_to_insert);
+ segment_to_insert->linkToDocument(this);
+ reflow_start_index = segment_to_insert->getStart();
+ }
+ else
+ {
+ LLTextSegmentPtr cur_segmentp = *cur_seg_iter;
+ reflow_start_index = cur_segmentp->getStart();
+ if (cur_segmentp->getStart() < segment_to_insert->getStart())
+ {
+ S32 old_segment_end = cur_segmentp->getEnd();
+ // split old at start point for new segment
+ cur_segmentp->setEnd(segment_to_insert->getStart());
+ // advance to next segment
+ // insert remainder of old segment
+ LLStyleConstSP sp = cur_segmentp->getStyle();
+ LLTextSegmentPtr remainder_segment = new LLNormalTextSegment( sp, segment_to_insert->getStart(), old_segment_end, *this);
+ mSegments.insert(cur_seg_iter, remainder_segment);
+ remainder_segment->linkToDocument(this);
+ // insert new segment before remainder of old segment
+ mSegments.insert(cur_seg_iter, segment_to_insert);
+
+ segment_to_insert->linkToDocument(this);
+ // at this point, there will be two overlapping segments owning the text
+ // associated with the incoming segment
+ }
+ else
+ {
+ mSegments.insert(cur_seg_iter, segment_to_insert);
+ segment_to_insert->linkToDocument(this);
+ }
+
+ // now delete/truncate remaining segments as necessary
+ // cur_seg_iter points to segment before incoming segment
+ while(cur_seg_iter != mSegments.end())
+ {
+ cur_segmentp = *cur_seg_iter;
+ if (cur_segmentp == segment_to_insert)
+ {
+ ++cur_seg_iter;
+ continue;
+ }
+
+ if (cur_segmentp->getStart() >= segment_to_insert->getStart())
+ {
+ if(cur_segmentp->getEnd() <= segment_to_insert->getEnd())
+ {
+ cur_segmentp->unlinkFromDocument(this);
+ // grab copy of iterator to erase, and bump it
+ segment_set_t::iterator seg_to_erase(cur_seg_iter++);
+ mSegments.erase(seg_to_erase);
+ continue;
+ }
+ else
+ {
+ // last overlapping segment, clip to end of incoming segment
+ // and stop traversal
+ cur_segmentp->setStart(segment_to_insert->getEnd());
+ break;
+ }
+ }
+ ++cur_seg_iter;
+ }
+ }
+
+ // layout potentially changed
+ needsReflow(reflow_start_index);
+}
+
+//virtual
+bool LLTextBase::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // handle triple click
+ if (!mTripleClickTimer.hasExpired())
+ {
+ if (mSkipTripleClick)
+ {
+ return true;
+ }
+
+ S32 real_line = getLineNumFromDocIndex(mCursorPos, false);
+ S32 line_start = -1;
+ S32 line_end = -1;
+ for (line_list_t::const_iterator it = mLineInfoList.begin(), end_it = mLineInfoList.end();
+ it != end_it;
+ ++it)
+ {
+ if (it->mLineNum < real_line)
+ {
+ continue;
+ }
+ if (it->mLineNum > real_line)
+ {
+ break;
+ }
+ if (line_start == -1)
+ {
+ line_start = it->mDocIndexStart;
+ }
+ line_end = it->mDocIndexEnd;
+ line_end = llclamp(line_end, 0, getLength());
+ }
+
+ if (line_start == -1)
+ {
+ return true;
+ }
+
+ mSelectionEnd = line_start;
+ mSelectionStart = line_end;
+ setCursorPos(line_start);
+
+ return true;
+ }
+
+ LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleMouseDown(x, y, mask))
+ {
+ return true;
+ }
+
+ return LLUICtrl::handleMouseDown(x, y, mask);
+}
+
+//virtual
+bool LLTextBase::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
+ 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())
+ {
+ // *TODO: send URL here?
+ (*mURLClickSignal)(this, LLSD() );
+ }
+ return true;
+ }
+
+ return LLUICtrl::handleMouseUp(x, y, mask);
+}
+
+//virtual
+bool LLTextBase::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleMiddleMouseDown(x, y, mask))
+ {
+ return true;
+ }
+
+ return LLUICtrl::handleMiddleMouseDown(x, y, mask);
+}
+
+//virtual
+bool LLTextBase::handleMiddleMouseUp(S32 x, S32 y, MASK mask)
+{
+ LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleMiddleMouseUp(x, y, mask))
+ {
+ return true;
+ }
+
+ return LLUICtrl::handleMiddleMouseUp(x, y, mask);
+}
+
+//virtual
+bool LLTextBase::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleRightMouseDown(x, y, mask))
+ {
+ return true;
+ }
+
+ return LLUICtrl::handleRightMouseDown(x, y, mask);
+}
+
+//virtual
+bool LLTextBase::handleRightMouseUp(S32 x, S32 y, MASK mask)
+{
+ LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleRightMouseUp(x, y, mask))
+ {
+ return true;
+ }
+
+ return LLUICtrl::handleRightMouseUp(x, y, mask);
+}
+
+//virtual
+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))
+ {
+ return true;
+ }
+
+ return LLUICtrl::handleDoubleClick(x, y, mask);
+}
+
+//virtual
+bool LLTextBase::handleHover(S32 x, S32 y, MASK mask)
+{
+ LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleHover(x, y, mask))
+ {
+ return true;
+ }
+
+ return LLUICtrl::handleHover(x, y, mask);
+}
+
+//virtual
+bool LLTextBase::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleScrollWheel(x, y, clicks))
+ {
+ return true;
+ }
+
+ return LLUICtrl::handleScrollWheel(x, y, clicks);
+}
+
+//virtual
+bool LLTextBase::handleToolTip(S32 x, S32 y, MASK mask)
+{
+ LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleToolTip(x, y, mask))
+ {
+ return true;
+ }
+
+ return LLUICtrl::handleToolTip(x, y, mask);
+}
+
+//virtual
+void LLTextBase::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ if (width != getRect().getWidth() || height != getRect().getHeight() || LLView::sForceReshape)
+ {
+ bool scrolled_to_bottom = mScroller ? mScroller->isAtBottom() : false;
+
+ LLUICtrl::reshape( width, height, called_from_parent );
+
+ if (mScroller && scrolled_to_bottom && mTrackEnd)
+ {
+ // keep bottom of text buffer visible
+ // do this here as well as in reflow to handle case
+ // where shrinking from top, which causes buffer to temporarily
+ // not be scrolled to the bottom, since the scroll index
+ // specified the _top_ of the visible document region
+ mScroller->goToBottom();
+ }
+
+ // do this first after reshape, because other things depend on
+ // up-to-date mVisibleTextRect
+ updateRects();
+
+ needsReflow();
+ }
+}
+
+//virtual
+void LLTextBase::draw()
+{
+ // reflow if needed, on demand
+ reflow();
+
+ // then update scroll position, as cursor may have moved
+ if (!mReadOnly)
+ {
+ updateScrollFromCursor();
+ }
+
+ LLRect text_rect;
+ if (mScroller)
+ {
+ mScroller->localRectToOtherView(mScroller->getContentWindowRect(), &text_rect, this);
+ }
+ else
+ {
+ 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)
+ {
+ F32 alpha = getCurrentTransparency();
+ // clip background rect against extents, if we support scrolling
+ LLRect bg_rect = mVisibleTextRect;
+ if (mScroller)
+ {
+ bg_rect.intersectWith(text_rect);
+ }
+ LLColor4 bg_color = mReadOnly
+ ? mReadOnlyBgColor.get()
+ : hasFocus()
+ ? mFocusBgColor.get()
+ : mWriteableBgColor.get();
+ gl_rect_2d(text_rect, bg_color % alpha, true);
+ }
+
+ // Draw highlighted if needed
+ if( ll::ui::SearchableControl::getHighlighted() )
+ {
+ LLColor4 bg_color = ll::ui::SearchableControl::getHighlightColor();
+ LLRect bg_rect = mVisibleTextRect;
+ if( mScroller )
+ bg_rect.intersectWith( text_rect );
+
+ gl_rect_2d( text_rect, bg_color, true );
+ }
+
+ 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->setVisibleDirect(false);
+ LLUICtrl::draw();
+ mDocumentView->setVisibleDirect(true);
+}
+
+
+//virtual
+void LLTextBase::setColor( const LLColor4& c )
+{
+ mFgColor = c;
+ mStyleDirty = true;
+}
+
+//virtual
+void LLTextBase::setReadOnlyColor(const LLColor4 &c)
+{
+ mReadOnlyFgColor = c;
+ mStyleDirty = true;
+}
+
+//virtual
+void LLTextBase::onVisibilityChange( bool new_visibility )
+{
+ LLContextMenu* menu = static_cast<LLContextMenu*>(mPopupMenuHandle.get());
+ if(!new_visibility && menu)
+ {
+ menu->hide();
+ }
+ LLUICtrl::onVisibilityChange(new_visibility);
+}
+
+//virtual
+void LLTextBase::setValue(const LLSD& value )
+{
+ setText(value.asString());
+}
+
+//virtual
+bool LLTextBase::canDeselect() const
+{
+ return hasSelection();
+}
+
+
+//virtual
+void LLTextBase::deselect()
+{
+ mSelectionStart = 0;
+ mSelectionEnd = 0;
+ mIsSelecting = false;
+}
+
+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());
+ onSpellCheckPerformed();
+
+ 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()
+{
+ // Update scroll position even in read-only mode (when there's no cursor displayed)
+ // because startOfDoc()/endOfDoc() modify cursor position. See EXT-736.
+
+ if (!mScrollNeeded || !mScroller)
+ {
+ return;
+ }
+ mScrollNeeded = false;
+
+ // scroll so that the cursor is at the top of the page
+ LLRect scroller_doc_window = getVisibleDocumentRect();
+ LLRect cursor_rect_doc = getDocRectFromDocIndex(mCursorPos);
+ mScroller->scrollToShowRect(cursor_rect_doc, LLRect(0, scroller_doc_window.getHeight() - 5, scroller_doc_window.getWidth(), 5));
+}
+
+S32 LLTextBase::getLeftOffset(S32 width)
+{
+ switch (mHAlign)
+ {
+ case LLFontGL::LEFT:
+ return mHPad;
+ case LLFontGL::HCENTER:
+ return mHPad + llmax(0, (mVisibleTextRect.getWidth() - width - mHPad) / 2);
+ case LLFontGL::RIGHT:
+ {
+ // Font's rendering rounds string size, if value gets rounded
+ // down last symbol might not have enough space to render,
+ // compensate by adding an extra pixel as padding
+ const S32 right_padding = 1;
+ return llmax(mHPad, mVisibleTextRect.getWidth() - width - right_padding);
+ }
+ default:
+ return mHPad;
+ }
+}
+
+void LLTextBase::reflow()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+
+ updateSegments();
+
+ if (mReflowIndex == S32_MAX)
+ {
+ return;
+ }
+
+ bool scrolled_to_bottom = mScroller ? mScroller->isAtBottom() : false;
+
+ LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos);
+ bool follow_selection = getLocalRect().overlaps(cursor_rect); // cursor is (potentially) visible
+
+ // store in top-left relative coordinates to avoid issues with horizontal scrollbar appearing and disappearing
+ cursor_rect.mTop = mVisibleTextRect.mTop - cursor_rect.mTop;
+ cursor_rect.mBottom = mVisibleTextRect.mTop - cursor_rect.mBottom;
+
+ S32 first_line = getFirstVisibleLine();
+
+ // if scroll anchor not on first line, update it to first character of first line
+ if ((first_line < mLineInfoList.size())
+ && (mScrollIndex < mLineInfoList[first_line].mDocIndexStart
+ || mScrollIndex >= mLineInfoList[first_line].mDocIndexEnd))
+ {
+ mScrollIndex = mLineInfoList[first_line].mDocIndexStart;
+ }
+ LLRect first_char_rect = getLocalRectFromDocIndex(mScrollIndex);
+ // store in top-left relative coordinates to avoid issues with horizontal scrollbar appearing and disappearing
+ first_char_rect.mTop = mVisibleTextRect.mTop - first_char_rect.mTop;
+ first_char_rect.mBottom = mVisibleTextRect.mTop - first_char_rect.mBottom;
+
+ S32 reflow_count = 0;
+ while(mReflowIndex < S32_MAX)
+ {
+ // we can get into an infinite loop if the document height does not monotonically increase
+ // with decreasing width (embedded ui elements with alternate layouts). In that case,
+ // we want to stop reflowing after 2 iterations. We use 2, since we need to handle the case
+ // of introducing a vertical scrollbar causing a reflow with less width. We should also always
+ // use an even number of iterations to avoid user visible oscillation of the layout
+ if(++reflow_count > 2)
+ {
+ LL_DEBUGS() << "Breaking out of reflow due to possible infinite loop in " << getName() << LL_ENDL;
+ break;
+ }
+
+ S32 start_index = mReflowIndex;
+ mReflowIndex = S32_MAX;
+
+ // shrink document to minimum size (visible portion of text widget)
+ // to force inlined widgets with follows set to shrink
+ if (mWordWrap)
+ {
+ mDocumentView->reshape(mVisibleTextRect.getWidth(), mDocumentView->getRect().getHeight());
+ }
+
+ S32 cur_top = 0;
+
+ segment_set_t::iterator seg_iter = mSegments.begin();
+ S32 seg_offset = 0;
+ S32 line_start_index = 0;
+ const F32 text_available_width = mVisibleTextRect.getWidth() - mHPad; // reserve room for margin
+ F32 remaining_pixels = text_available_width;
+ S32 line_count = 0;
+
+ // find and erase line info structs starting at start_index and going to end of document
+ if (!mLineInfoList.empty())
+ {
+ // find first element whose end comes after start_index
+ line_list_t::iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), start_index, line_end_compare());
+ if (iter != mLineInfoList.end())
+ {
+ line_start_index = iter->mDocIndexStart;
+ line_count = iter->mLineNum;
+ cur_top = iter->mRect.mTop;
+ getSegmentAndOffset(iter->mDocIndexStart, &seg_iter, &seg_offset);
+ mLineInfoList.erase(iter, mLineInfoList.end());
+ }
+ }
+
+ S32 line_height = 0;
+ S32 seg_line_offset = line_count + 1;
+
+ while(seg_iter != mSegments.end())
+ {
+ LLTextSegmentPtr segment = *seg_iter;
+
+ // track maximum height of any segment on this line
+ S32 cur_index = segment->getStart() + seg_offset;
+
+ // ask segment how many character fit in remaining space
+ S32 character_count = segment->getNumChars(getWordWrap() ? llmax(0, ll_round(remaining_pixels)) : S32_MAX,
+ seg_offset,
+ cur_index - line_start_index,
+ S32_MAX,
+ line_count - seg_line_offset);
+
+ F32 segment_width;
+ S32 segment_height;
+ bool force_newline = segment->getDimensionsF32(seg_offset, character_count, segment_width, segment_height);
+ // grow line height as necessary based on reported height of this segment
+ line_height = llmax(line_height, segment_height);
+ remaining_pixels -= segment_width;
+
+ seg_offset += character_count;
+
+ S32 last_segment_char_on_line = segment->getStart() + seg_offset;
+
+ // Note: make sure text will fit in width - use ceil, but also make sure
+ // ceil is used only once per line
+ S32 text_actual_width = llceil(text_available_width - remaining_pixels);
+ S32 text_left = getLeftOffset(text_actual_width);
+ LLRect line_rect(text_left,
+ cur_top,
+ text_left + text_actual_width,
+ cur_top - line_height);
+
+ // if we didn't finish the current segment...
+ if (last_segment_char_on_line < segment->getEnd())
+ {
+ // add line info and keep going
+ mLineInfoList.push_back(line_info(
+ line_start_index,
+ last_segment_char_on_line,
+ line_rect,
+ line_count));
+
+ line_start_index = segment->getStart() + seg_offset;
+ cur_top -= ll_round((F32)line_height * mLineSpacingMult) + mLineSpacingPixels;
+ remaining_pixels = text_available_width;
+ line_height = 0;
+ }
+ // ...just consumed last segment..
+ else if (++segment_set_t::iterator(seg_iter) == mSegments.end())
+ {
+ mLineInfoList.push_back(line_info(
+ line_start_index,
+ last_segment_char_on_line,
+ line_rect,
+ line_count));
+ cur_top -= ll_round((F32)line_height * mLineSpacingMult) + mLineSpacingPixels;
+ break;
+ }
+ // ...or finished a segment and there are segments remaining on this line
+ else
+ {
+ // subtract pixels used and increment segment
+ if (force_newline)
+ {
+ mLineInfoList.push_back(line_info(
+ line_start_index,
+ last_segment_char_on_line,
+ line_rect,
+ line_count));
+ line_start_index = segment->getStart() + seg_offset;
+ cur_top -= ll_round((F32)line_height * mLineSpacingMult) + mLineSpacingPixels;
+ line_height = 0;
+ remaining_pixels = text_available_width;
+ }
+ ++seg_iter;
+ seg_offset = 0;
+ seg_line_offset = force_newline ? line_count + 1 : line_count;
+ }
+ if (force_newline)
+ {
+ line_count++;
+ }
+ }
+
+ // calculate visible region for diplaying text
+ updateRects();
+
+ for (segment_set_t::iterator segment_it = mSegments.begin();
+ segment_it != mSegments.end();
+ ++segment_it)
+ {
+ LLTextSegmentPtr segmentp = *segment_it;
+ segmentp->updateLayout(*this);
+
+ }
+ }
+
+ // apply scroll constraints after reflowing text
+ if (!hasMouseCapture() && mScroller)
+ {
+ if (scrolled_to_bottom && mTrackEnd)
+ {
+ // keep bottom of text buffer visible
+ endOfDoc();
+ }
+ else if (hasSelection() && follow_selection)
+ {
+ // keep cursor in same vertical position on screen when selecting text
+ LLRect new_cursor_rect_doc = getDocRectFromDocIndex(mCursorPos);
+ LLRect old_cursor_rect = cursor_rect;
+ old_cursor_rect.mTop = mVisibleTextRect.mTop - cursor_rect.mTop;
+ old_cursor_rect.mBottom = mVisibleTextRect.mTop - cursor_rect.mBottom;
+
+ mScroller->scrollToShowRect(new_cursor_rect_doc, old_cursor_rect);
+ }
+ else
+ {
+ // keep first line of text visible
+ LLRect new_first_char_rect = getDocRectFromDocIndex(mScrollIndex);
+
+ // pass in desired rect in the coordinate frame of the document viewport
+ LLRect old_first_char_rect = first_char_rect;
+ old_first_char_rect.mTop = mVisibleTextRect.mTop - first_char_rect.mTop;
+ old_first_char_rect.mBottom = mVisibleTextRect.mTop - first_char_rect.mBottom;
+
+ mScroller->scrollToShowRect(new_first_char_rect, old_first_char_rect);
+ }
+ }
+
+ // reset desired x cursor position
+ updateCursorXPos();
+}
+
+LLRect LLTextBase::getTextBoundingRect()
+{
+ reflow();
+ return mTextBoundingRect;
+}
+
+
+void LLTextBase::clearSegments()
+{
+ mSegments.clear();
+ createDefaultSegment();
+}
+
+S32 LLTextBase::getLineStart( S32 line ) const
+{
+ S32 num_lines = getLineCount();
+ if (num_lines == 0)
+ {
+ return 0;
+ }
+
+ line = llclamp(line, 0, num_lines-1);
+ return mLineInfoList[line].mDocIndexStart;
+}
+
+S32 LLTextBase::getLineEnd( S32 line ) const
+{
+ S32 num_lines = getLineCount();
+ if (num_lines == 0)
+ {
+ return 0;
+ }
+
+ line = llclamp(line, 0, num_lines-1);
+ return mLineInfoList[line].mDocIndexEnd;
+}
+
+
+
+S32 LLTextBase::getLineNumFromDocIndex( S32 doc_index, bool include_wordwrap) const
+{
+ if (mLineInfoList.empty())
+ {
+ return 0;
+ }
+ else
+ {
+ line_list_t::const_iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), doc_index, line_end_compare());
+ if (include_wordwrap)
+ {
+ return iter - mLineInfoList.begin();
+ }
+ else
+ {
+ if (iter == mLineInfoList.end())
+ {
+ return mLineInfoList.back().mLineNum;
+ }
+ else
+ {
+ return iter->mLineNum;
+ }
+ }
+ }
+}
+
+// Given an offset into text (pos), find the corresponding line (from the start of the doc) and an offset into the line.
+S32 LLTextBase::getLineOffsetFromDocIndex( S32 startpos, bool include_wordwrap) const
+{
+ if (mLineInfoList.empty())
+ {
+ return startpos;
+ }
+ else
+ {
+ line_list_t::const_iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), startpos, line_end_compare());
+ return startpos - iter->mDocIndexStart;
+ }
+}
+
+S32 LLTextBase::getFirstVisibleLine() const
+{
+ LLRect visible_region = getVisibleDocumentRect();
+
+ // binary search for line that starts before top of visible buffer
+ line_list_t::const_iterator iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_bottom());
+
+ return iter - mLineInfoList.begin();
+}
+
+std::pair<S32, S32> LLTextBase::getVisibleLines(bool require_fully_visible)
+{
+ LLRect visible_region = getVisibleDocumentRect();
+ line_list_t::const_iterator first_iter;
+ line_list_t::const_iterator last_iter;
+
+ // make sure we have an up-to-date mLineInfoList
+ reflow();
+
+ if (require_fully_visible)
+ {
+ first_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_top());
+ last_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mBottom, compare_bottom());
+ }
+ else
+ {
+ 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());
+}
+
+
+
+LLTextViewModel* LLTextBase::getViewModel() const
+{
+ return (LLTextViewModel*)mViewModel.get();
+}
+
+void LLTextBase::addDocumentChild(LLView* view)
+{
+ mDocumentView->addChild(view);
+}
+
+void LLTextBase::removeDocumentChild(LLView* view)
+{
+ mDocumentView->removeChild(view);
+}
+
+
+void LLTextBase::updateSegments()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+ createDefaultSegment();
+}
+
+void LLTextBase::getSegmentAndOffset( S32 startpos, segment_set_t::const_iterator* seg_iter, S32* offsetp ) const
+{
+ *seg_iter = getSegIterContaining(startpos);
+ if (*seg_iter == mSegments.end())
+ {
+ *offsetp = 0;
+ }
+ else
+ {
+ *offsetp = startpos - (**seg_iter)->getStart();
+ }
+}
+
+void LLTextBase::getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp )
+{
+ *seg_iter = getSegIterContaining(startpos);
+ if (*seg_iter == mSegments.end())
+ {
+ *offsetp = 0;
+ }
+ else
+ {
+ *offsetp = startpos - (**seg_iter)->getStart();
+ }
+}
+
+LLTextBase::segment_set_t::iterator LLTextBase::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();
+
+ // when there are no segments, we return the end iterator, which must be checked by caller
+ if (mSegments.size() <= 1) { return mSegments.begin(); }
+
+ index_segment->setStart(index);
+ index_segment->setEnd(index);
+ segment_set_t::iterator it = mSegments.upper_bound(index_segment);
+ return it;
+}
+
+LLTextBase::segment_set_t::const_iterator LLTextBase::getSegIterContaining(S32 index) const
+{
+ static LLPointer<LLIndexSegment> index_segment = new LLIndexSegment();
+
+ // when there are no segments, we return the end iterator, which must be checked by caller
+ if (mSegments.size() <= 1) { return mSegments.begin(); }
+
+ index_segment->setStart(index);
+ index_segment->setEnd(index);
+ LLTextBase::segment_set_t::const_iterator it = mSegments.upper_bound(index_segment);
+ return it;
+}
+
+// Finds the text segment (if any) at the give local screen position
+LLTextSegmentPtr LLTextBase::getSegmentAtLocalPos( S32 x, S32 y, bool hit_past_end_of_line)
+{
+ // Find the cursor position at the requested local screen position
+ S32 offset = getDocIndexFromLocalCoord( x, y, false, hit_past_end_of_line);
+ segment_set_t::iterator seg_iter = getSegIterContaining(offset);
+ if (seg_iter != mSegments.end())
+ {
+ return *seg_iter;
+ }
+ else
+ {
+ return LLTextSegmentPtr();
+ }
+}
+
+void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url)
+{
+ // work out the XUI menu file to use for this url
+ LLUrlMatch match;
+ std::string url = in_url;
+ if (! LLUrlRegistry::instance().findUrl(url, match))
+ {
+ return;
+ }
+
+ std::string xui_file = match.getMenuName();
+ if (xui_file.empty())
+ {
+ return;
+ }
+
+ // set up the callbacks for all of the potential menu items, N.B. we
+ // don't use const ref strings in callbacks in case url goes out of scope
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+ registrar.add("Url.Open", boost::bind(&LLUrlAction::openURL, url));
+ registrar.add("Url.OpenInternal", boost::bind(&LLUrlAction::openURLInternal, url));
+ registrar.add("Url.OpenExternal", boost::bind(&LLUrlAction::openURLExternal, url));
+ registrar.add("Url.Execute", boost::bind(&LLUrlAction::executeSLURL, url, true));
+ registrar.add("Url.Block", boost::bind(&LLUrlAction::blockObject, url));
+ registrar.add("Url.Unblock", boost::bind(&LLUrlAction::unblockObject, 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.ReportAbuse", boost::bind(&LLUrlAction::reportAbuse, 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
+
+ LLContextMenu* menu = static_cast<LLContextMenu*>(mPopupMenuHandle.get());
+ if (menu)
+ {
+ menu->die();
+ mPopupMenuHandle.markDead();
+ }
+ llassert(LLMenuGL::sMenuContainer != NULL);
+ menu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(xui_file, LLMenuGL::sMenuContainer,
+ LLMenuHolderGL::child_registry_t::instance());
+ if (menu)
+ {
+ mPopupMenuHandle = menu->getHandle();
+
+ if (mIsFriendSignal)
+ {
+ bool isFriend = *(*mIsFriendSignal)(LLUUID(LLUrlAction::getUserID(url)));
+ LLView* addFriendButton = menu->getChild<LLView>("add_friend");
+ LLView* removeFriendButton = menu->getChild<LLView>("remove_friend");
+
+ if (addFriendButton && removeFriendButton)
+ {
+ addFriendButton->setEnabled(!isFriend);
+ removeFriendButton->setEnabled(isFriend);
+ }
+ }
+
+ if (mIsObjectBlockedSignal)
+ {
+ bool is_blocked = *(*mIsObjectBlockedSignal)(LLUUID(LLUrlAction::getObjectId(url)), LLUrlAction::getObjectName(url));
+ LLView* blockButton = menu->getChild<LLView>("block_object");
+ LLView* unblockButton = menu->getChild<LLView>("unblock_object");
+
+ if (blockButton && unblockButton)
+ {
+ blockButton->setVisible(!is_blocked);
+ unblockButton->setVisible(is_blocked);
+ }
+ }
+ menu->show(x, y);
+ LLMenuGL::showPopup(this, menu, x, y);
+ }
+}
+
+void LLTextBase::setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params)
+{
+ // clear out the existing text and segments
+ getViewModel()->setDisplay(LLWStringUtil::null);
+
+ clearSegments();
+// createDefaultSegment();
+
+ deselect();
+
+ // append the new text (supports Url linking)
+ std::string text(utf8str);
+ LLStringUtil::removeCRLF(text);
+
+ // appendText modifies mCursorPos...
+ appendText(text, false, input_params);
+ // ...so move cursor to top after appending text
+ if (!mTrackEnd)
+ {
+ startOfDoc();
+ }
+
+ onValueChange(0, getLength());
+}
+
+// virtual
+const std::string& LLTextBase::getText() const
+{
+ return getViewModel()->getStringValue();
+}
+
+// IDEVO - icons can be UI image names or UUID sent from
+// server with avatar display name
+static LLUIImagePtr image_from_icon_name(const std::string& icon_name)
+{
+ if (LLUUID::validate(icon_name))
+ {
+ return LLUI::getUIImageByID( LLUUID(icon_name) );
+ }
+ else
+ {
+ return LLUI::getUIImage(icon_name);
+ }
+}
+
+
+void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+ LLStyle::Params style_params(input_params);
+ 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).
+ {
+ 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), isContentTrusted() || mAlwaysShowIcons))
+ {
+ start = match.getStart();
+ end = match.getEnd()+1;
+
+ LLStyle::Params link_params(style_params);
+ link_params.overwriteFrom(match.getStyle());
+
+ // output the text before the Url
+ if (start > 0)
+ {
+ if (part == (S32)LLTextParser::WHOLE ||
+ part == (S32)LLTextParser::START)
+ {
+ part = (S32)LLTextParser::START;
+ }
+ else
+ {
+ part = (S32)LLTextParser::MIDDLE;
+ }
+ std::string subtext=text.substr(0,start);
+ appendAndHighlightText(subtext, part, style_params);
+ }
+
+ // add icon before url if need
+ LLTextUtil::processUrlMatch(&match, this, isContentTrusted() || match.isTrusted() || mAlwaysShowIcons);
+ if ((isContentTrusted() || match.isTrusted()) && !match.getIcon().empty() )
+ {
+ setLastSegmentToolTip(LLTrans::getString("TooltipSLIcon"));
+ }
+
+ // output the styled Url
+ appendAndHighlightTextImpl(match.getLabel(), part, link_params, match.underlineOnHoverOnly());
+ bool tooltip_required = !match.getTooltip().empty();
+
+ // set the tooltip for the Url label
+ if (tooltip_required)
+ {
+ setLastSegmentToolTip(match.getTooltip());
+ }
+
+ // show query part of url with gray color only for LLUrlEntryHTTP 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 query part of url
+ if (tooltip_required)
+ {
+ setLastSegmentToolTip(match.getTooltip());
+ }
+ }
+
+ // move on to the rest of the text after the Url
+ if (end < (S32)text.length())
+ {
+ text = text.substr(end,text.length() - end);
+ end=0;
+ part=(S32)LLTextParser::END;
+ }
+ else
+ {
+ break;
+ }
+ }
+ if (part != (S32)LLTextParser::WHOLE)
+ part=(S32)LLTextParser::END;
+ if (end < (S32)text.length())
+ appendAndHighlightText(text, part, style_params);
+ }
+ else
+ {
+ appendAndHighlightText(new_text, part, style_params);
+ }
+}
+
+void LLTextBase::setLastSegmentToolTip(const std::string &tooltip)
+{
+ segment_set_t::iterator it = getSegIterContaining(getLength()-1);
+ if (it != mSegments.end())
+ {
+ LLTextSegmentPtr segment = *it;
+ segment->setToolTip(tooltip);
+ }
+}
+
+void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+ if (new_text.empty())
+ return;
+
+ if(prepend_newline)
+ appendLineBreakSegment(input_params);
+ 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)
+{
+ LL_DEBUGS() << "reflow on object " << (void*)this << " index = " << mReflowIndex << ", new index = " << index << LL_ENDL;
+ mReflowIndex = llmin(mReflowIndex, index);
+}
+
+S32 LLTextBase::removeFirstLine()
+{
+ if (!mLineInfoList.empty())
+ {
+ S32 length = getLineEnd(0);
+ deselect();
+ removeStringNoUndo(0, length);
+ return length;
+ }
+ return 0;
+}
+
+void LLTextBase::appendLineBreakSegment(const LLStyle::Params& style_params)
+{
+ segment_vec_t segments;
+ LLStyleConstSP sp(new LLStyle(style_params));
+ segments.push_back(new LLLineBreakTextSegment(sp, getLength()));
+
+ insertStringNoUndo(getLength(), utf8str_to_wstring("\n"), &segments);
+}
+
+void LLTextBase::appendImageSegment(const LLStyle::Params& style_params)
+{
+ if(getPlainText())
+ {
+ return;
+ }
+ segment_vec_t segments;
+ LLStyleConstSP sp(new LLStyle(style_params));
+ segments.push_back(new LLImageTextSegment(sp, getLength(),*this));
+
+ insertStringNoUndo(getLength(), utf8str_to_wstring(" "), &segments);
+}
+
+void LLTextBase::appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo)
+{
+ segment_vec_t segments;
+ LLWString widget_wide_text = utf8str_to_wstring(text);
+ segments.push_back(new LLInlineViewSegment(params, getLength(), getLength() + widget_wide_text.size()));
+
+ insertStringNoUndo(getLength(), widget_wide_text, &segments);
+}
+
+void LLTextBase::appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only)
+{
+ // Save old state
+ S32 selection_start = mSelectionStart;
+ S32 selection_end = mSelectionEnd;
+ bool was_selecting = mIsSelecting;
+ S32 cursor_pos = mCursorPos;
+ S32 old_length = getLength();
+ bool cursor_was_at_end = (mCursorPos == old_length);
+
+ deselect();
+
+ setCursorPos(old_length);
+
+ if (mParseHighlights)
+ {
+ LLStyle::Params highlight_params(style_params);
+
+ LLSD pieces = LLTextParser::instance().parsePartialLineHighlights(new_text, highlight_params.color(), (LLTextParser::EHighlightPosition)highlight_part);
+ for (S32 i = 0; i < pieces.size(); i++)
+ {
+ LLSD color_llsd = pieces[i]["color"];
+ LLColor4 lcolor;
+ lcolor.setValue(color_llsd);
+ highlight_params.color = lcolor;
+
+ LLWString wide_text;
+ wide_text = utf8str_to_wstring(pieces[i]["text"].asString());
+
+ S32 cur_length = getLength();
+ LLStyleConstSP sp(new LLStyle(highlight_params));
+ LLTextSegmentPtr segmentp;
+ if (underline_on_hover_only || mSkipLinkUnderline)
+ {
+ highlight_params.font.style("NORMAL");
+ LLStyleConstSP normal_sp(new LLStyle(highlight_params));
+ segmentp = new LLOnHoverChangeableTextSegment(sp, normal_sp, cur_length, cur_length + wide_text.size(), *this);
+ }
+ else
+ {
+ segmentp = new LLNormalTextSegment(sp, cur_length, cur_length + wide_text.size(), *this);
+ }
+ segment_vec_t segments;
+ segments.push_back(segmentp);
+ insertStringNoUndo(cur_length, wide_text, &segments);
+ }
+ }
+ else
+ {
+ LLWString wide_text;
+ wide_text = utf8str_to_wstring(new_text);
+
+ segment_vec_t segments;
+ S32 segment_start = old_length;
+ S32 segment_end = old_length + wide_text.size();
+ LLStyleConstSP sp(new LLStyle(style_params));
+ if (underline_on_hover_only || mSkipLinkUnderline)
+ {
+ LLStyle::Params normal_style_params(style_params);
+ normal_style_params.font.style("NORMAL");
+ LLStyleConstSP normal_sp(new LLStyle(normal_style_params));
+ segments.push_back(new LLOnHoverChangeableTextSegment(sp, normal_sp, segment_start, segment_end, *this));
+ }
+ else
+ {
+ segments.push_back(new LLNormalTextSegment(sp, segment_start, segment_end, *this));
+ }
+
+ insertStringNoUndo(getLength(), wide_text, &segments);
+ }
+
+ // Set the cursor and scroll position
+ if (selection_start != selection_end)
+ {
+ mSelectionStart = selection_start;
+ mSelectionEnd = selection_end;
+
+ mIsSelecting = was_selecting;
+ setCursorPos(cursor_pos);
+ }
+ else if (cursor_was_at_end)
+ {
+ setCursorPos(getLength());
+ }
+ else
+ {
+ setCursorPos(cursor_pos);
+ }
+}
+
+void LLTextBase::appendAndHighlightText(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, bool underline_on_hover_only)
+{
+ if (new_text.empty())
+ {
+ return;
+ }
+
+ std::string::size_type start = 0;
+ std::string::size_type pos = new_text.find("\n", start);
+
+ while (pos != std::string::npos)
+ {
+ if (pos != start)
+ {
+ std::string str = std::string(new_text,start,pos-start);
+ appendAndHighlightTextImpl(str, highlight_part, style_params, underline_on_hover_only);
+ }
+ appendLineBreakSegment(style_params);
+ start = pos+1;
+ pos = new_text.find("\n", start);
+ }
+
+ std::string str = std::string(new_text, start, new_text.length() - start);
+ appendAndHighlightTextImpl(str, highlight_part, style_params, underline_on_hover_only);
+}
+
+
+void LLTextBase::replaceUrl(const std::string &url,
+ const std::string &label,
+ const std::string &icon)
+{
+ // get the full (wide) text for the editor so we can change it
+ LLWString text = getWText();
+ LLWString wlabel = utf8str_to_wstring(label);
+ bool modified = false;
+ S32 seg_start = 0;
+
+ // iterate through each segment looking for ones styled as links
+ segment_set_t::iterator it;
+ for (it = mSegments.begin(); it != mSegments.end(); ++it)
+ {
+ LLTextSegment *seg = *it;
+ LLStyleConstSP style = seg->getStyle();
+
+ // update segment start/end length in case we replaced text earlier
+ S32 seg_length = seg->getEnd() - seg->getStart();
+ seg->setStart(seg_start);
+ seg->setEnd(seg_start + seg_length);
+
+ // if we find a link with our Url, then replace the label
+ if (style->getLinkHREF() == url)
+ {
+ S32 start = seg->getStart();
+ S32 end = seg->getEnd();
+ text = text.substr(0, start) + wlabel + text.substr(end, text.size() - end + 1);
+ seg->setEnd(start + wlabel.size());
+ modified = true;
+ }
+
+ // Icon might be updated when more avatar or group info
+ // becomes available
+ if (style->isImage() && style->getLinkHREF() == url)
+ {
+ LLUIImagePtr image = image_from_icon_name( icon );
+ if (image)
+ {
+ LLStyle::Params icon_params;
+ icon_params.image = image;
+ LLStyleConstSP new_style(new LLStyle(icon_params));
+ seg->setStyle(new_style);
+ modified = true;
+ }
+ }
+
+ // work out the character offset for the next segment
+ seg_start = seg->getEnd();
+ }
+
+ // update the editor with the new (wide) text string
+ if (modified)
+ {
+ getViewModel()->setDisplay(text);
+ deselect();
+ setCursorPos(mCursorPos);
+ needsReflow();
+ }
+}
+
+
+void LLTextBase::setWText(const LLWString& text)
+{
+ setText(wstring_to_utf8str(text));
+}
+
+const LLWString& LLTextBase::getWText() const
+{
+ return getViewModel()->getDisplay();
+}
+
+// If round is true, if the position is on the right half of a character, the cursor
+// will be put to its right. If round is false, the cursor will always be put to the
+// character's left.
+
+S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, bool round, bool hit_past_end_of_line) const
+{
+ // Figure out which line we're nearest to.
+ LLRect 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(), doc_y, compare_bottom());
+
+ if (!mLineInfoList.size() || line_iter == mLineInfoList.end())
+ {
+ return getLength(); // past the end
+ }
+
+ S32 pos = getLength();
+ F32 start_x = line_iter->mRect.mLeft + doc_rect.mLeft;
+
+ segment_set_t::iterator line_seg_iter;
+ S32 line_seg_offset;
+ for(getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset);
+ line_seg_iter != mSegments.end();
+ ++line_seg_iter, line_seg_offset = 0)
+ {
+ const LLTextSegmentPtr segmentp = *line_seg_iter;
+
+ S32 segment_line_start = segmentp->getStart() + line_seg_offset;
+ S32 segment_line_length = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd) - segment_line_start;
+ F32 text_width;
+ S32 text_height;
+ bool newline = segmentp->getDimensionsF32(line_seg_offset, segment_line_length, text_width, text_height);
+
+ if(newline)
+ {
+ pos = segment_line_start + segmentp->getOffset(local_x - start_x, line_seg_offset, segment_line_length, round);
+ break;
+ }
+
+ // if we've reached a line of text *below* the mouse cursor, doc index is first character on that line
+ if (hit_past_end_of_line && doc_y > line_iter->mRect.mTop)
+ {
+ pos = segment_line_start;
+ break;
+ }
+ if (local_x < start_x + text_width) // cursor to left of right edge of text
+ {
+ // Figure out which character we're nearest to.
+ S32 offset;
+ if (!segmentp->canEdit())
+ {
+ F32 segment_width;
+ S32 segment_height;
+ segmentp->getDimensionsF32(0, segmentp->getEnd() - segmentp->getStart(), segment_width, segment_height);
+ if (round && local_x - start_x > segment_width / 2)
+ {
+ offset = segment_line_length;
+ }
+ else
+ {
+ offset = 0;
+ }
+ }
+ else
+ {
+ offset = segmentp->getOffset(local_x - start_x, line_seg_offset, segment_line_length, round);
+ }
+ pos = segment_line_start + offset;
+ break;
+ }
+ else if (hit_past_end_of_line && segmentp->getEnd() >= line_iter->mDocIndexEnd)
+ {
+ 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;
+ }
+
+ return pos;
+}
+
+// returns rectangle of insertion caret
+// in document coordinate frame from given index into text
+LLRect LLTextBase::getDocRectFromDocIndex(S32 pos) const
+{
+ if (mLineInfoList.empty())
+ {
+ return LLRect();
+ }
+
+ // clamp pos to valid values
+ pos = llclamp(pos, 0, mLineInfoList.back().mDocIndexEnd - 1);
+
+ line_list_t::const_iterator line_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), pos, line_end_compare());
+
+ segment_set_t::iterator line_seg_iter;
+ S32 line_seg_offset;
+ segment_set_t::iterator cursor_seg_iter;
+ S32 cursor_seg_offset;
+ getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset);
+ getSegmentAndOffset(pos, &cursor_seg_iter, &cursor_seg_offset);
+
+ F32 doc_left_precise = line_iter->mRect.mLeft;
+
+ while(line_seg_iter != mSegments.end())
+ {
+ const LLTextSegmentPtr segmentp = *line_seg_iter;
+
+ if (line_seg_iter == cursor_seg_iter)
+ {
+ // cursor advanced to right based on difference in offset of cursor to start of line
+ F32 segment_width;
+ S32 segment_height;
+ segmentp->getDimensionsF32(line_seg_offset, cursor_seg_offset - line_seg_offset, segment_width, segment_height);
+ doc_left_precise += segment_width;
+
+ break;
+ }
+ else
+ {
+ // add remainder of current text segment to cursor position
+ F32 segment_width;
+ S32 segment_height;
+ segmentp->getDimensionsF32(line_seg_offset, (segmentp->getEnd() - segmentp->getStart()) - line_seg_offset, segment_width, segment_height);
+ doc_left_precise += segment_width;
+ // offset will be 0 for all segments after the first
+ line_seg_offset = 0;
+ // go to next text segment on this line
+ ++line_seg_iter;
+ }
+ }
+
+ LLRect doc_rect;
+ doc_rect.mLeft = doc_left_precise;
+ doc_rect.mBottom = line_iter->mRect.mBottom;
+ doc_rect.mTop = line_iter->mRect.mTop;
+
+ // set rect to 0 width
+ doc_rect.mRight = doc_rect.mLeft;
+
+ return doc_rect;
+}
+
+LLRect LLTextBase::getLocalRectFromDocIndex(S32 pos) const
+{
+ LLRect content_window_rect = mScroller ? mScroller->getContentWindowRect() : getLocalRect();
+ if (mBorderVisible)
+ {
+ content_window_rect.stretch(-1);
+ }
+
+ LLRect local_rect;
+
+ if (mLineInfoList.empty())
+ {
+ // return default height rect in upper left
+ local_rect = content_window_rect;
+ local_rect.mBottom = local_rect.mTop - mFont->getLineHeight();
+ return local_rect;
+ }
+
+ // get the rect in document coordinates
+ LLRect doc_rect = getDocRectFromDocIndex(pos);
+
+ // compensate for scrolled, inset view of doc
+ LLRect scrolled_view_rect = getVisibleDocumentRect();
+ local_rect = doc_rect;
+ local_rect.translate(content_window_rect.mLeft - scrolled_view_rect.mLeft,
+ content_window_rect.mBottom - scrolled_view_rect.mBottom);
+
+ return local_rect;
+}
+
+void LLTextBase::updateCursorXPos()
+{
+ // reset desired x cursor position
+ mDesiredXPixel = getLocalRectFromDocIndex(mCursorPos).mLeft;
+}
+
+
+void LLTextBase::startOfLine()
+{
+ S32 offset = getLineOffsetFromDocIndex(mCursorPos);
+ setCursorPos(mCursorPos - offset);
+}
+
+void LLTextBase::endOfLine()
+{
+ S32 line = getLineNumFromDocIndex(mCursorPos);
+ S32 num_lines = getLineCount();
+ if (line + 1 >= num_lines)
+ {
+ setCursorPos(getLength());
+ }
+ else
+ {
+ setCursorPos( getLineStart(line + 1) - 1 );
+ }
+}
+
+void LLTextBase::startOfDoc()
+{
+ setCursorPos(0);
+ if (mScroller)
+ {
+ mScroller->goToTop();
+ }
+}
+
+void LLTextBase::endOfDoc()
+{
+ setCursorPos(getLength());
+ if (mScroller)
+ {
+ mScroller->goToBottom();
+ }
+}
+
+void LLTextBase::changePage( S32 delta )
+{
+ const S32 PIXEL_OVERLAP_ON_PAGE_CHANGE = 10;
+ if (delta == 0 || !mScroller) return;
+
+ LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos);
+
+ if( delta == -1 )
+ {
+ mScroller->pageUp(PIXEL_OVERLAP_ON_PAGE_CHANGE);
+ }
+ else
+ if( delta == 1 )
+ {
+ mScroller->pageDown(PIXEL_OVERLAP_ON_PAGE_CHANGE);
+ }
+
+ if (getLocalRectFromDocIndex(mCursorPos) == cursor_rect)
+ {
+ // cursor didn't change apparent position, so move to top or bottom of document, respectively
+ if (delta < 0)
+ {
+ startOfDoc();
+ }
+ else
+ {
+ endOfDoc();
+ }
+ }
+ else
+ {
+ setCursorAtLocalPos(cursor_rect.getCenterX(), cursor_rect.getCenterY(), true, false);
+ }
+}
+
+// Picks a new cursor position based on the screen size of text being drawn.
+void LLTextBase::setCursorAtLocalPos( S32 local_x, S32 local_y, bool round, bool keep_cursor_offset )
+{
+ setCursorPos(getDocIndexFromLocalCoord(local_x, local_y, round), keep_cursor_offset);
+}
+
+
+void LLTextBase::changeLine( S32 delta )
+{
+ S32 line = getLineNumFromDocIndex(mCursorPos);
+ S32 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);
+
+ 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()
+{
+ return mScroller->isAtTop();
+}
+
+bool LLTextBase::scrolledToEnd()
+{
+ return mScroller->isAtBottom();
+}
+
+bool LLTextBase::setCursor(S32 row, S32 column)
+{
+ if (row < 0 || column < 0) return false;
+
+ S32 n_lines = mLineInfoList.size();
+ for (S32 line = row; line < n_lines; ++line)
+ {
+ const line_info& li = mLineInfoList[line];
+
+ if (li.mLineNum < row)
+ {
+ continue;
+ }
+ else if (li.mLineNum > row)
+ {
+ break; // invalid column specified
+ }
+
+ // Found the given row.
+ S32 line_length = li.mDocIndexEnd - li.mDocIndexStart;;
+ if (column >= line_length)
+ {
+ column -= line_length;
+ continue;
+ }
+
+ // Found the given column.
+ updateCursorXPos();
+ S32 doc_pos = li.mDocIndexStart + column;
+ return setCursorPos(doc_pos);
+ }
+
+ return false; // invalid row or column specified
+}
+
+
+bool LLTextBase::setCursorPos(S32 cursor_pos, bool keep_cursor_offset)
+{
+ S32 new_cursor_pos = cursor_pos;
+ if (new_cursor_pos != mCursorPos)
+ {
+ new_cursor_pos = getEditableIndex(new_cursor_pos, new_cursor_pos >= mCursorPos);
+ }
+
+ mCursorPos = llclamp(new_cursor_pos, 0, (S32)getLength());
+ needsScroll();
+ if (!keep_cursor_offset)
+ updateCursorXPos();
+ // did we get requested position?
+ return new_cursor_pos == cursor_pos;
+}
+
+// constraint cursor to editable segments of document
+S32 LLTextBase::getEditableIndex(S32 index, bool increasing_direction)
+{
+ segment_set_t::iterator segment_iter;
+ S32 offset;
+ getSegmentAndOffset(index, &segment_iter, &offset);
+ if (segment_iter == mSegments.end())
+ {
+ return 0;
+ }
+
+ LLTextSegmentPtr segmentp = *segment_iter;
+
+ if (segmentp->canEdit())
+ {
+ return segmentp->getStart() + offset;
+ }
+ else if (segmentp->getStart() < index && index < segmentp->getEnd())
+ {
+ // bias towards document end
+ if (increasing_direction)
+ {
+ return segmentp->getEnd();
+ }
+ // bias towards document start
+ else
+ {
+ return segmentp->getStart();
+ }
+ }
+ else
+ {
+ return index;
+ }
+}
+
+void LLTextBase::updateRects()
+{
+ LLRect old_text_rect = mVisibleTextRect;
+ mVisibleTextRect = mScroller ? mScroller->getContentWindowRect() : getLocalRect();
+
+ if (mLineInfoList.empty())
+ {
+ mTextBoundingRect = LLRect(0, mVPad, mHPad, 0);
+ }
+ else
+ {
+ mTextBoundingRect = mLineInfoList.begin()->mRect;
+ for (line_list_t::const_iterator line_iter = ++mLineInfoList.begin();
+ line_iter != mLineInfoList.end();
+ ++line_iter)
+ {
+ mTextBoundingRect.unionWith(line_iter->mRect);
+ }
+
+ mTextBoundingRect.mTop += mVPad;
+
+ 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)
+ {
+ it->mRect.translate(0, delta_pos);
+ }
+ mTextBoundingRect.translate(0, delta_pos);
+ }
+
+ // update document container dimensions according to text contents
+ 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?
+ // if so, use entire width of text contents
+ // otherwise, stop at width of mVisibleTextRect
+ //FIXME: consider use of getWordWrap() instead
+ doc_rect.mRight = mScroller
+ ? llmax(mVisibleTextRect.getWidth(), mTextBoundingRect.mRight)
+ : mVisibleTextRect.getWidth();
+ doc_rect.mTop = llmax(mVisibleTextRect.mTop, mTextBoundingRect.mTop);
+
+ 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);
+
+ //update mVisibleTextRect *after* mDocumentView has been resized
+ // so that scrollbars are added if document needs to scroll
+ // since mVisibleTextRect does not include scrollbars
+ mVisibleTextRect = mScroller ? mScroller->getContentWindowRect() : getLocalRect();
+ //FIXME: replace border with image?
+ if (mBorderVisible)
+ {
+ mVisibleTextRect.stretch(-1);
+ }
+ if (mVisibleTextRect != old_text_rect)
+ {
+ 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);
+}
+
+
+void LLTextBase::startSelection()
+{
+ if( !mIsSelecting )
+ {
+ mIsSelecting = true;
+ mSelectionStart = mCursorPos;
+ mSelectionEnd = mCursorPos;
+ }
+}
+
+void LLTextBase::endSelection()
+{
+ if( mIsSelecting )
+ {
+ mIsSelecting = false;
+ mSelectionEnd = mCursorPos;
+ }
+}
+
+// get portion of document that is visible in text editor
+LLRect LLTextBase::getVisibleDocumentRect() const
+{
+ if (mScroller)
+ {
+ return mScroller->getVisibleContentRect();
+ }
+ else if (mClip)
+ {
+ 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
+ doc_rect.mBottom = doc_rect.getHeight() - mVisibleTextRect.getHeight();
+ return doc_rect;
+ }
+}
+
+boost::signals2::connection LLTextBase::setURLClickedCallback(const commit_signal_t::slot_type& cb)
+{
+ if (!mURLClickSignal)
+ {
+ mURLClickSignal = new commit_signal_t();
+ }
+ return mURLClickSignal->connect(cb);
+}
+
+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);
+}
+
+boost::signals2::connection LLTextBase::setIsObjectBlockedCallback(const is_blocked_signal_t::slot_type& cb)
+{
+ if (!mIsObjectBlockedSignal)
+ {
+ mIsObjectBlockedSignal = new is_blocked_signal_t();
+ }
+ return mIsObjectBlockedSignal->connect(cb);
+}
+
+//
+// LLTextSegment
+//
+
+LLTextSegment::~LLTextSegment()
+{}
+
+bool LLTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const { width = 0; height = 0; return false; }
+bool LLTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const
+{
+ F32 fwidth = 0;
+ bool result = getDimensionsF32(first_char, num_chars, fwidth, height);
+ width = ll_round(fwidth);
+ return result;
+}
+
+S32 LLTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const { return 0; }
+S32 LLTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const { return 0; }
+void LLTextSegment::updateLayout(const LLTextBase& editor) {}
+F32 LLTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) { return draw_rect.mLeft; }
+bool LLTextSegment::canEdit() const { return false; }
+void LLTextSegment::unlinkFromDocument(LLTextBase*) {}
+void LLTextSegment::linkToDocument(LLTextBase*) {}
+const LLColor4& LLTextSegment::getColor() const { return LLColor4::white; }
+//void LLTextSegment::setColor(const LLColor4 &color) {}
+LLStyleConstSP LLTextSegment::getStyle() const {static LLStyleConstSP sp(new LLStyle()); return sp; }
+void LLTextSegment::setStyle(LLStyleConstSP style) {}
+void LLTextSegment::setToken( LLKeywordToken* token ) {}
+LLKeywordToken* LLTextSegment::getToken() const { return NULL; }
+void LLTextSegment::setToolTip( const std::string &msg ) {}
+void LLTextSegment::dump() const {}
+bool LLTextSegment::handleMouseDown(S32 x, S32 y, MASK mask) { return false; }
+bool LLTextSegment::handleMouseUp(S32 x, S32 y, MASK mask) { return false; }
+bool LLTextSegment::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { return false; }
+bool LLTextSegment::handleMiddleMouseUp(S32 x, S32 y, MASK mask) { return false; }
+bool LLTextSegment::handleRightMouseDown(S32 x, S32 y, MASK mask) { return false; }
+bool LLTextSegment::handleRightMouseUp(S32 x, S32 y, MASK mask) { return false; }
+bool LLTextSegment::handleDoubleClick(S32 x, S32 y, MASK mask) { return false; }
+bool LLTextSegment::handleHover(S32 x, S32 y, MASK mask) { return false; }
+bool LLTextSegment::handleScrollWheel(S32 x, S32 y, S32 clicks) { return false; }
+bool LLTextSegment::handleScrollHWheel(S32 x, S32 y, S32 clicks) { return false; }
+bool LLTextSegment::handleToolTip(S32 x, S32 y, MASK mask) { return false; }
+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 {}
+bool LLTextSegment::hasMouseCapture() { return false; }
+
+//
+// LLNormalTextSegment
+//
+
+LLNormalTextSegment::LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor )
+: LLTextSegment(start, end),
+ mStyle( style ),
+ mToken(NULL),
+ mEditor(editor)
+{
+ mFontHeight = mStyle->getFont()->getLineHeight();
+
+ LLUIImagePtr image = mStyle->getImage();
+ if (image.notNull())
+ {
+ mImageLoadedConnection = image->addLoadedCallback(boost::bind(&LLTextBase::needsReflow, &mEditor, start));
+ }
+}
+
+LLNormalTextSegment::LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible)
+: LLTextSegment(start, end),
+ mToken(NULL),
+ mEditor(editor)
+{
+ mStyle = new LLStyle(LLStyle::Params().visible(is_visible).color(color));
+
+ mFontHeight = mStyle->getFont()->getLineHeight();
+}
+
+LLNormalTextSegment::~LLNormalTextSegment()
+{
+ mImageLoadedConnection.disconnect();
+}
+
+
+F32 LLNormalTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect)
+{
+ if( end - start > 0 )
+ {
+ return drawClippedSegment( getStart() + start, getStart() + end, selection_start, selection_end, draw_rect);
+ }
+ return draw_rect.mLeft;
+}
+
+// Draws a single text segment, reversing the color for selection if needed.
+F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRectf rect)
+{
+ F32 alpha = LLViewDrawContext::getCurrentContext().mAlpha;
+
+ const LLWString &text = getWText();
+
+ F32 right_x = rect.mLeft;
+ if (!mStyle->isVisible())
+ {
+ return right_x;
+ }
+
+ const LLFontGL* font = mStyle->getFont();
+
+ LLColor4 color = (mEditor.getReadOnly() ? mStyle->getReadOnlyColor() : mStyle->getColor()) % alpha;
+
+ if( selection_start > seg_start )
+ {
+ // Draw normally
+ S32 start = seg_start;
+ S32 end = llmin( selection_start, seg_end );
+ S32 length = end - start;
+ font->render(text, start,
+ rect,
+ color,
+ LLFontGL::LEFT, mEditor.mTextVAlign,
+ LLFontGL::NORMAL,
+ mStyle->getShadowType(),
+ length,
+ &right_x,
+ mEditor.getUseEllipses(),
+ mEditor.getUseColor());
+ }
+ rect.mLeft = right_x;
+
+ if( (selection_start < seg_end) && (selection_end > seg_start) )
+ {
+ // Draw reversed
+ S32 start = llmax( selection_start, seg_start );
+ S32 end = llmin( selection_end, seg_end );
+ S32 length = end - start;
+
+ font->render(text, start,
+ rect,
+ mStyle->getSelectedColor().get(),
+ LLFontGL::LEFT, mEditor.mTextVAlign,
+ LLFontGL::NORMAL,
+ LLFontGL::NO_SHADOW,
+ length,
+ &right_x,
+ mEditor.getUseEllipses(),
+ mEditor.getUseColor());
+ }
+ rect.mLeft = right_x;
+ if( selection_end < seg_end )
+ {
+ // Draw normally
+ S32 start = llmax( selection_end, seg_start );
+ S32 end = seg_end;
+ S32 length = end - start;
+ font->render(text, start,
+ rect,
+ color,
+ LLFontGL::LEFT, mEditor.mTextVAlign,
+ LLFontGL::NORMAL,
+ mStyle->getShadowType(),
+ length,
+ &right_x,
+ mEditor.getUseEllipses(),
+ mEditor.getUseColor());
+ }
+ return right_x;
+}
+
+bool LLNormalTextSegment::handleHover(S32 x, S32 y, MASK mask)
+{
+ if (getStyle() && getStyle()->isLink())
+ {
+ // Only process the click if it's actually in this segment, not to the right of the end-of-line.
+ if(mEditor.getSegmentAtLocalPos(x, y, false) == this)
+ {
+ LLUI::getInstance()->getWindow()->setCursor(UI_CURSOR_HAND);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool LLNormalTextSegment::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (getStyle() && getStyle()->isLink())
+ {
+ // Only process the click if it's actually in this segment, not to the right of the end-of-line.
+ if(mEditor.getSegmentAtLocalPos(x, y, false) == this)
+ {
+ mEditor.createUrlContextMenu(x, y, getStyle()->getLinkHREF());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool LLNormalTextSegment::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (getStyle() && getStyle()->isLink())
+ {
+ // Only process the click if it's actually in this segment, not to the right of the end-of-line.
+ if(mEditor.getSegmentAtLocalPos(x, y, false) == this)
+ {
+ // eat mouse down event on hyperlinks, so we get the mouse up
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool LLNormalTextSegment::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ if (getStyle() && getStyle()->isLink())
+ {
+ // Only process the click if it's actually in this segment, not to the right of the end-of-line.
+ if(mEditor.getSegmentAtLocalPos(x, y, false) == this)
+ {
+ std::string url = getStyle()->getLinkHREF();
+ if (!mEditor.mForceUrlsExternal)
+ {
+ LLUrlAction::clickAction(url, mEditor.isContentTrusted());
+ }
+ else if (!LLUrlAction::executeSLURL(url, mEditor.isContentTrusted()))
+ {
+ LLUrlAction::openURLExternal(url);
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool LLNormalTextSegment::handleToolTip(S32 x, S32 y, MASK mask)
+{
+ std::string msg;
+ // do we have a tooltip for a loaded keyword (for script editor)?
+ if (mToken && !mToken->getToolTip().empty())
+ {
+ const LLWString& wmsg = mToken->getToolTip();
+ LLToolTipMgr::instance().show(wstring_to_utf8str(wmsg), (mToken->getType() == LLKeywordToken::TT_FUNCTION));
+ return true;
+ }
+ // or do we have an explicitly set tooltip (e.g., for Urls)
+ if (!mTooltip.empty())
+ {
+ LLToolTipMgr::instance().show(mTooltip);
+ return true;
+ }
+
+ return false;
+}
+
+void LLNormalTextSegment::setToolTip(const std::string& tooltip)
+{
+ // we cannot replace a keyword tooltip that's loaded from a file
+ if (mToken)
+ {
+ LL_WARNS() << "LLTextSegment::setToolTip: cannot replace keyword tooltip." << LL_ENDL;
+ return;
+ }
+ mTooltip = tooltip;
+}
+
+bool LLNormalTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const
+{
+ height = 0;
+ width = 0;
+ if (num_chars > 0)
+ {
+ height = mFontHeight;
+ const LLWString &text = getWText();
+ // if last character is a newline, then return true, forcing line break
+ width = mStyle->getFont()->getWidthF32(text.c_str(), mStart + first_char, num_chars, true);
+ }
+ return false;
+}
+
+S32 LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const
+{
+ const LLWString &text = getWText();
+ return mStyle->getFont()->charFromPixelOffset(text.c_str(), mStart + start_offset,
+ (F32)segment_local_x_coord,
+ F32_MAX,
+ num_chars,
+ round);
+}
+
+S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const
+{
+ const LLWString &text = getWText();
+
+ LLUIImagePtr image = mStyle->getImage();
+ if( image.notNull())
+ {
+ num_pixels = llmax(0, num_pixels - image->getWidth());
+ }
+
+ S32 last_char = mEnd;
+
+ // set max characters to length of segment, or to first newline
+ max_chars = llmin(max_chars, last_char - (mStart + segment_offset));
+
+ // if no character yet displayed on this line, don't require word wrapping since
+ // we can just move to the next line, otherwise insist on it so we make forward progress
+ LLFontGL::EWordWrapStyle word_wrap_style = (line_offset == 0)
+ ? LLFontGL::WORD_BOUNDARY_IF_POSSIBLE
+ : LLFontGL::ONLY_WORD_BOUNDARIES;
+
+
+ S32 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);
+
+ if (num_chars == 0
+ && line_offset == 0
+ && max_chars > 0)
+ {
+ // If at the beginning of a line, and a single character won't fit, draw it anyway
+ num_chars = 1;
+ }
+
+ // include *either* the EOF or newline character in this run of text
+ // but not both
+ S32 last_char_in_run = mStart + segment_offset + num_chars;
+ // check length first to avoid indexing off end of string
+ if (last_char_in_run < mEnd
+ && (last_char_in_run >= getLength()))
+ {
+ num_chars++;
+ }
+ return num_chars;
+}
+
+void LLNormalTextSegment::dump() const
+{
+ LL_INFOS() << "Segment [" <<
+// mColor.mV[VX] << ", " <<
+// mColor.mV[VY] << ", " <<
+// mColor.mV[VZ] << "]\t[" <<
+ mStart << ", " <<
+ getEnd() << "]" <<
+ 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();
+}
+
+//
+// LLEmojiTextSegment
+//
+LLEmojiTextSegment::LLEmojiTextSegment(LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor)
+ : LLNormalTextSegment(style, start, end, editor)
+{
+}
+
+LLEmojiTextSegment::LLEmojiTextSegment(const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible)
+ : LLNormalTextSegment(color, start, end, editor, is_visible)
+{
+}
+
+bool LLEmojiTextSegment::handleToolTip(S32 x, S32 y, MASK mask)
+{
+ if (mTooltip.empty())
+ {
+ LLWString emoji = getWText().substr(getStart(), getEnd() - getStart());
+ if (!emoji.empty())
+ {
+ mTooltip = LLEmojiHelper::instance().getToolTip(emoji[0]);
+ }
+ }
+
+ return LLNormalTextSegment::handleToolTip(x, y, mask);
+}
+
+//
+// LLOnHoverChangeableTextSegment
+//
+
+LLOnHoverChangeableTextSegment::LLOnHoverChangeableTextSegment( LLStyleConstSP style, LLStyleConstSP normal_style, S32 start, S32 end, LLTextBase& editor ):
+ LLNormalTextSegment(normal_style, start, end, editor),
+ mHoveredStyle(style),
+ mNormalStyle(normal_style){}
+
+/*virtual*/
+F32 LLOnHoverChangeableTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect)
+{
+ F32 result = LLNormalTextSegment::draw(start, end, selection_start, selection_end, draw_rect);
+ if (end == mEnd - mStart)
+ {
+ mStyle = mNormalStyle;
+ }
+ return result;
+}
+
+/*virtual*/
+bool LLOnHoverChangeableTextSegment::handleHover(S32 x, S32 y, MASK mask)
+{
+ mStyle = mEditor.getSkipLinkUnderline() ? mNormalStyle : mHoveredStyle;
+ return LLNormalTextSegment::handleHover(x, y, mask);
+}
+
+
+//
+// LLInlineViewSegment
+//
+
+LLInlineViewSegment::LLInlineViewSegment(const Params& p, S32 start, S32 end)
+: LLTextSegment(start, end),
+ mView(p.view),
+ mForceNewLine(p.force_newline),
+ mLeftPad(p.left_pad),
+ mRightPad(p.right_pad),
+ mTopPad(p.top_pad),
+ mBottomPad(p.bottom_pad)
+{
+}
+
+LLInlineViewSegment::~LLInlineViewSegment()
+{
+ mView->die();
+}
+
+bool LLInlineViewSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const
+{
+ if (first_char == 0 && num_chars == 0)
+ {
+ // We didn't fit on a line or were forced to new string
+ // the widget will fall on the next line, so width here is 0
+ width = 0;
+
+ if (mForceNewLine)
+ {
+ // Chat, string can't be smaller then font height even if it is empty
+ LLStyleSP s(new LLStyle(LLStyle::Params().visible(true)));
+ height = s->getFont()->getLineHeight();
+
+ return true; // new line
+ }
+ else
+ {
+ // height from previous segment in same string will be used, word-wrap
+ height = 0;
+ }
+
+ }
+ else
+ {
+ width = mLeftPad + mRightPad + mView->getRect().getWidth();
+ height = mBottomPad + mTopPad + mView->getRect().getHeight();
+ }
+
+ return false;
+}
+
+S32 LLInlineViewSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const
+{
+ // if putting a widget anywhere but at the beginning of a line
+ // and the widget doesn't fit or mForceNewLine is true
+ // then return 0 chars for that line, and all characters for the next
+ if (mForceNewLine && line_ind == 0)
+ {
+ return 0;
+ }
+ else if (line_offset != 0 && num_pixels < mView->getRect().getWidth())
+ {
+ return 0;
+ }
+ else
+ {
+ return mEnd - mStart;
+ }
+}
+
+void LLInlineViewSegment::updateLayout(const LLTextBase& editor)
+{
+ LLRect start_rect = editor.getDocRectFromDocIndex(mStart);
+ mView->setOrigin(start_rect.mLeft + mLeftPad, start_rect.mBottom + mBottomPad);
+}
+
+F32 LLInlineViewSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect)
+{
+ // return padded width of widget
+ // widget is actually drawn during mDocumentView's draw()
+ return (F32)(draw_rect.mLeft + mView->getRect().getWidth() + mLeftPad + mRightPad);
+}
+
+void LLInlineViewSegment::unlinkFromDocument(LLTextBase* editor)
+{
+ editor->removeDocumentChild(mView);
+}
+
+void LLInlineViewSegment::linkToDocument(LLTextBase* editor)
+{
+ editor->addDocumentChild(mView);
+}
+
+LLLineBreakTextSegment::LLLineBreakTextSegment(S32 pos):LLTextSegment(pos,pos+1)
+{
+ LLStyleSP s( new LLStyle(LLStyle::Params().visible(true)));
+
+ mFontHeight = s->getFont()->getLineHeight();
+}
+LLLineBreakTextSegment::LLLineBreakTextSegment(LLStyleConstSP style,S32 pos):LLTextSegment(pos,pos+1)
+{
+ mFontHeight = style->getFont()->getLineHeight();
+}
+LLLineBreakTextSegment::~LLLineBreakTextSegment()
+{
+}
+bool LLLineBreakTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const
+{
+ width = 0;
+ height = mFontHeight;
+
+ return true;
+}
+S32 LLLineBreakTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const
+{
+ return 1;
+}
+F32 LLLineBreakTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect)
+{
+ return draw_rect.mLeft;
+}
+
+LLImageTextSegment::LLImageTextSegment(LLStyleConstSP style,S32 pos,class LLTextBase& editor)
+: LLTextSegment(pos,pos+1),
+ mStyle( style ),
+ mEditor(editor)
+{
+}
+
+LLImageTextSegment::~LLImageTextSegment()
+{
+}
+
+static const S32 IMAGE_HPAD = 3;
+
+bool LLImageTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const
+{
+ width = 0;
+ height = mStyle->getFont()->getLineHeight();
+
+ LLUIImagePtr image = mStyle->getImage();
+ if( num_chars>0 && image.notNull())
+ {
+ width += image->getWidth() + IMAGE_HPAD;
+ height = llmax(height, image->getHeight() + IMAGE_HPAD );
+ }
+ return false;
+}
+
+S32 LLImageTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) 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;
+}
+
+bool LLImageTextSegment::handleToolTip(S32 x, S32 y, MASK mask)
+{
+ if (!mTooltip.empty())
+ {
+ LLToolTipMgr::instance().show(mTooltip);
+ return true;
+ }
+
+ return false;
+}
+
+void LLImageTextSegment::setToolTip(const std::string& tooltip)
+{
+ mTooltip = tooltip;
+}
+
+F32 LLImageTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect)
+{
+ if ( (start >= 0) && (end <= mEnd - mStart))
+ {
+ LLColor4 color = LLColor4::white % mEditor.getDrawContext().mAlpha;
+ LLUIImagePtr image = mStyle->getImage();
+ 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 f5adb0dd86..d8e9027bae 100644
--- a/indra/llui/lltextbase.h
+++ b/indra/llui/lltextbase.h
@@ -1,754 +1,758 @@
-/**
- * @file lltextbase.h
- * @author Martin Reddy
- * @brief The base class of text box/editor, providing Url handling support
- *
- * $LicenseInfo:firstyear=2009&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLTEXTBASE_H
-#define LL_LLTEXTBASE_H
-
-#include "v4color.h"
-#include "lleditmenuhandler.h"
-#include "llspellcheckmenuhandler.h"
-#include "llstyle.h"
-#include "llkeywords.h"
-#include "llpanel.h"
-
-#include <string>
-#include <vector>
-#include <set>
-
-#include <boost/signals2.hpp>
-
-class LLScrollContainer;
-class LLContextMenu;
-class LLUrlMatch;
-
-///
-/// A text segment is used to specify a subsection of a text string
-/// that should be formatted differently, such as a hyperlink. It
-/// includes a start/end offset from the start of the string, a
-/// style to render with, an optional tooltip, etc.
-///
-class LLTextSegment
-: public LLRefCount,
- public LLMouseHandler
-{
-public:
- LLTextSegment(S32 start, S32 end)
- : mStart(start),
- mEnd(end)
- {}
- virtual ~LLTextSegment();
- bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const;
-
- virtual bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const;
- virtual S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const;
-
- /**
- * Get number of chars that fit into free part of current line.
- *
- * @param num_pixels - maximum width of rect
- * @param segment_offset - symbol in segment we start processing line from
- * @param line_offset - symbol in line after which segment starts
- * @param max_chars - limit of symbols that will fit in current line
- * @param line_ind - index of not word-wrapped string inside segment for multi-line segments.
- * Two string separated by word-wrap will have same index.
- * @return number of chars that will fit into current line
- */
- virtual S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const;
- virtual void updateLayout(const class LLTextBase& editor);
- virtual F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect);
- virtual bool canEdit() const;
- virtual void unlinkFromDocument(class LLTextBase* editor);
- virtual void linkToDocument(class LLTextBase* editor);
-
- virtual const LLColor4& getColor() const;
- //virtual void setColor(const LLColor4 &color);
- virtual LLStyleConstSP getStyle() const;
- virtual void setStyle(LLStyleConstSP style);
- virtual void setToken( LLKeywordToken* token );
- virtual LLKeywordToken* getToken() const;
- virtual void setToolTip(const std::string& tooltip);
- virtual void dump() const;
-
- // LLMouseHandler interface
- /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleMiddleMouseDown(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleMiddleMouseUp(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleRightMouseUp(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks);
- /*virtual*/ bool handleScrollHWheel(S32 x, S32 y, S32 clicks);
- /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask);
- /*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; }
-
-protected:
- S32 mStart;
- S32 mEnd;
-};
-
-class LLNormalTextSegment : public LLTextSegment
-{
-public:
- LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor );
- LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible = true);
- virtual ~LLNormalTextSegment();
-
- /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const;
- /*virtual*/ S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const;
- /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const;
- /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect);
- /*virtual*/ bool canEdit() const { return true; }
- /*virtual*/ const LLColor4& getColor() const { return mStyle->getColor(); }
- /*virtual*/ LLStyleConstSP getStyle() const { return mStyle; }
- /*virtual*/ void setStyle(LLStyleConstSP style) { mStyle = style; }
- /*virtual*/ void setToken( LLKeywordToken* token ) { mToken = token; }
- /*virtual*/ LLKeywordToken* getToken() const { return mToken; }
- /*virtual*/ bool getToolTip( std::string& msg ) const;
- /*virtual*/ void setToolTip(const std::string& tooltip);
- /*virtual*/ void dump() const;
-
- /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask);
-
-protected:
- F32 drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRectf rect);
-
- virtual const LLWString& getWText() const;
- virtual const S32 getLength() const;
-
-protected:
- class LLTextBase& mEditor;
- LLStyleConstSP mStyle;
- S32 mFontHeight;
- LLKeywordToken* mToken;
- std::string mTooltip;
- 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 represents a single emoji character that has a different style (=font size) than the rest of
-// the document it belongs to
-class LLEmojiTextSegment : public LLNormalTextSegment
-{
-public:
- LLEmojiTextSegment(LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor);
- LLEmojiTextSegment(const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible = true);
-
- bool canEdit() const override { return false; }
- bool handleToolTip(S32 x, S32 y, MASK mask) override;
-};
-
-// Text segment that changes it's style depending of mouse pointer position ( is it inside or outside segment)
-class LLOnHoverChangeableTextSegment : public LLNormalTextSegment
-{
-public:
- LLOnHoverChangeableTextSegment( LLStyleConstSP style, LLStyleConstSP normal_style, S32 start, S32 end, LLTextBase& editor );
- /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect);
- /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
-protected:
- // Style used for text when mouse pointer is over segment
- LLStyleConstSP mHoveredStyle;
- // Style used for text when mouse pointer is outside segment
- LLStyleConstSP mNormalStyle;
-
-};
-
-class LLIndexSegment : public LLTextSegment
-{
-public:
- LLIndexSegment() : LLTextSegment(0, 0) {}
-};
-
-class LLInlineViewSegment : public LLTextSegment
-{
-public:
- struct Params : public LLInitParam::Block<Params>
- {
- Mandatory<LLView*> view;
- Optional<bool> force_newline;
- Optional<S32> left_pad,
- right_pad,
- bottom_pad,
- top_pad;
- };
-
- LLInlineViewSegment(const Params& p, S32 start, S32 end);
- ~LLInlineViewSegment();
- /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const;
- /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const;
- /*virtual*/ void updateLayout(const class LLTextBase& editor);
- /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect);
- /*virtual*/ bool canEdit() const { return false; }
- /*virtual*/ void unlinkFromDocument(class LLTextBase* editor);
- /*virtual*/ void linkToDocument(class LLTextBase* editor);
-
-private:
- S32 mLeftPad;
- S32 mRightPad;
- S32 mTopPad;
- S32 mBottomPad;
- LLView* mView;
- bool mForceNewLine;
-};
-
-class LLLineBreakTextSegment : public LLTextSegment
-{
-public:
-
- LLLineBreakTextSegment(LLStyleConstSP style,S32 pos);
- LLLineBreakTextSegment(S32 pos);
- ~LLLineBreakTextSegment();
- /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const;
- S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const;
- F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect);
-
-private:
- S32 mFontHeight;
-};
-
-class LLImageTextSegment : public LLTextSegment
-{
-public:
- LLImageTextSegment(LLStyleConstSP style,S32 pos,class LLTextBase& editor);
- ~LLImageTextSegment();
- /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const;
- S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 char_offset, S32 max_chars, S32 line_ind) const;
- F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect);
-
- /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask);
- /*virtual*/ void setToolTip(const std::string& tooltip);
-
-private:
- class LLTextBase& mEditor;
- LLStyleConstSP mStyle;
-
-protected:
- std::string mTooltip;
-};
-
-typedef LLPointer<LLTextSegment> LLTextSegmentPtr;
-
-///
-/// The LLTextBase class provides a base class for all text fields, such
-/// as LLTextEditor and LLTextBox. It implements shared functionality
-/// such as Url highlighting and opening.
-///
-class LLTextBase
-: public LLUICtrl,
- protected LLEditMenuHandler,
- public LLSpellCheckMenuHandler,
- public ll::ui::SearchableControl
-{
-public:
- friend class LLTextSegment;
- friend class LLNormalTextSegment;
- friend class LLUICtrlFactory;
-
- typedef boost::signals2::signal<bool (const LLUUID& user_id)> is_friend_signal_t;
- typedef boost::signals2::signal<bool (const LLUUID& blocked_id, const std::string from)> is_blocked_signal_t;
-
- struct LineSpacingParams : public LLInitParam::ChoiceBlock<LineSpacingParams>
- {
- Alternative<F32> multiple;
- Alternative<S32> pixels;
- LineSpacingParams();
- };
-
- struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Optional<LLUIColor> cursor_color,
- text_color,
- text_readonly_color,
- text_tentative_color,
- bg_readonly_color,
- bg_writeable_color,
- bg_focus_color,
- text_selected_color,
- bg_selected_color;
-
- Optional<bool> bg_visible,
- border_visible,
- track_end,
- read_only,
- skip_link_underline,
- spellcheck,
- allow_scroll,
- plain_text,
- wrap,
- use_ellipses,
- use_color,
- parse_urls,
- force_urls_external,
- parse_highlights,
- clip,
- clip_partial,
- trusted_content,
- always_show_icons;
-
- Optional<S32> v_pad,
- h_pad;
-
-
- Optional<LineSpacingParams>
- line_spacing;
-
- Optional<S32> max_text_length;
-
- Optional<LLFontGL::ShadowType> font_shadow;
-
- Optional<LLFontGL::VAlign> text_valign;
-
- Params();
- };
-
- // LLMouseHandler interface
- /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask) override;
- /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask) override;
- /*virtual*/ bool handleMiddleMouseDown(S32 x, S32 y, MASK mask) override;
- /*virtual*/ bool handleMiddleMouseUp(S32 x, S32 y, MASK mask) override;
- /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask) override;
- /*virtual*/ bool handleRightMouseUp(S32 x, S32 y, MASK mask) override;
- /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask) override;
- /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask) override;
- /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks) override;
- /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask) override;
-
- // LLView interface
- /*virtual*/ const std::string getToolTip() const override;
- /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true) override;
- /*virtual*/ void draw() override;
-
- // LLUICtrl interface
- /*virtual*/ bool acceptsTextInput() const override { return !mReadOnly; }
- /*virtual*/ void setColor(const LLColor4& c) override;
- virtual void setReadOnlyColor(const LLColor4 &c);
- /*virtual*/ void onVisibilityChange(bool new_visibility) override;
-
- /*virtual*/ void setValue(const LLSD& value) override;
- /*virtual*/ LLTextViewModel* getViewModel() const override;
-
- // LLEditMenuHandler interface
- /*virtual*/ bool canDeselect() const override;
- /*virtual*/ void deselect() override;
-
- virtual void onFocusReceived() override;
- virtual void onFocusLost() override;
-
- void setParseHTML(bool parse_html) { mParseHTML = parse_html; }
-
- // LLSpellCheckMenuHandler overrides
- /*virtual*/ bool getSpellCheck() const override;
-
- /*virtual*/ const std::string& getSuggestion(U32 index) const override;
- /*virtual*/ U32 getSuggestionCount() const override;
- /*virtual*/ void replaceWithSuggestion(U32 index) override;
-
- /*virtual*/ void addToDictionary() override;
- /*virtual*/ bool canAddToDictionary() const override;
-
- /*virtual*/ void addToIgnore() override;
- /*virtual*/ bool canAddToIgnore() const override;
-
- // Spell checking helper functions
- std::string getMisspelledWord(U32 pos) const;
- bool isMisspelledWord(U32 pos) const;
- void onSpellCheckSettingsChange();
- virtual void onSpellCheckPerformed(){}
-
- // used by LLTextSegment layout code
- bool getWordWrap() { return mWordWrap; }
- bool getUseEllipses() { return mUseEllipses; }
- bool getUseColor() { return mUseColor; }
- 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
-
- // Text accessors
- // TODO: add optional style parameter
- virtual void setText(const LLStringExplicit &utf8str , const LLStyle::Params& input_params = LLStyle::Params()); // uses default style
- virtual std::string getText() const;
- void setMaxTextLength(S32 length) { mMaxTextByteLength = length; }
- S32 getMaxTextLength() { return mMaxTextByteLength; }
-
- // wide-char versions
- void setWText(const LLWString& text);
- const LLWString& getWText() const;
-
- void appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params = LLStyle::Params());
-
- void setLabel(const LLStringExplicit& label);
- /*virtual*/ bool setLabelArg(const std::string& key, const LLStringExplicit& text) override;
-
- const std::string& getLabel() { return mLabel.getString(); }
- const LLWString& getWlabel() { return mLabel.getWString();}
-
- void setLastSegmentToolTip(const std::string &tooltip);
-
- /**
- * 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);
-
- S32 getLength() const { return getWText().length(); }
- S32 getLineCount() const { return mLineInfoList.size(); }
- S32 removeFirstLine(); // returns removed length
-
- void addDocumentChild(LLView* view);
- void removeDocumentChild(LLView* view);
- const LLView* getDocumentView() const { return mDocumentView; }
- LLRect getVisibleTextRect() const { return mVisibleTextRect; }
- LLRect getTextBoundingRect();
- LLRect getVisibleDocumentRect() const;
-
- S32 getVPad() { return mVPad; }
- S32 getHPad() { return mHPad; }
- F32 getLineSpacingMult() { return mLineSpacingMult; }
- S32 getLineSpacingPixels() { return mLineSpacingPixels; } // only for multiline
-
- S32 getDocIndexFromLocalCoord( S32 local_x, S32 local_y, bool round, bool hit_past_end_of_line = true) const;
- LLRect getLocalRectFromDocIndex(S32 pos) const;
- LLRect getDocRectFromDocIndex(S32 pos) const;
-
- void setReadOnly(bool read_only) { mReadOnly = read_only; }
- bool getReadOnly() { return mReadOnly; }
-
- void setSkipLinkUnderline(bool skip_link_underline) { mSkipLinkUnderline = skip_link_underline; }
- bool getSkipLinkUnderline() { return mSkipLinkUnderline; }
-
- void setParseURLs(bool parse_urls) { mParseHTML = parse_urls; }
-
- void setPlainText(bool value) { mPlainText = value;}
- bool getPlainText() const { return mPlainText; }
-
- // cursor manipulation
- bool setCursor(S32 row, S32 column);
- bool setCursorPos(S32 cursor_pos, bool keep_cursor_offset = false);
- void startOfLine();
- void endOfLine();
- void startOfDoc();
- void endOfDoc();
- void changePage( S32 delta );
- void changeLine( S32 delta );
-
- bool scrolledToStart();
- bool scrolledToEnd();
-
- const LLFontGL* 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);
- boost::signals2::connection setIsObjectBlockedCallback(const is_blocked_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
- {
- 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;
-
- // Abstract inner base class representing an undoable editor command.
- // Concrete sub-classes can be defined for operations such as insert, remove, etc.
- // Used as arguments to the execute() method below.
- class TextCmd
- {
- public:
- TextCmd( S32 pos, bool group_with_next, LLTextSegmentPtr segment = LLTextSegmentPtr() )
- : mPos(pos),
- mGroupWithNext(group_with_next)
- {
- if (segment.notNull())
- {
- mSegments.push_back(segment);
- }
- }
- virtual ~TextCmd() {}
- virtual bool execute(LLTextBase* editor, S32* delta) = 0;
- virtual S32 undo(LLTextBase* editor) = 0;
- virtual S32 redo(LLTextBase* editor) = 0;
- virtual bool canExtend(S32 pos) const { return false; }
- virtual void blockExtensions() {}
- virtual bool extendAndExecute( LLTextBase* editor, S32 pos, llwchar c, S32* delta ) { llassert(0); return 0; }
- virtual bool hasExtCharValue( llwchar value ) const { return false; }
-
- // Defined here so they can access protected LLTextEditor editing methods
- S32 insert(LLTextBase* editor, S32 pos, const LLWString &wstr) { return editor->insertStringNoUndo( pos, wstr, &mSegments ); }
- S32 remove(LLTextBase* editor, S32 pos, S32 length) { return editor->removeStringNoUndo( pos, length ); }
- S32 overwrite(LLTextBase* editor, S32 pos, llwchar wc) { return editor->overwriteCharNoUndo(pos, wc); }
-
- S32 getPosition() const { return mPos; }
- bool groupWithNext() const { return mGroupWithNext; }
-
- protected:
- const S32 mPos;
- bool mGroupWithNext;
- segment_vec_t mSegments;
- };
-
- struct compare_segment_end
- {
- bool operator()(const LLTextSegmentPtr& a, const LLTextSegmentPtr& b) const;
- };
- typedef std::multiset<LLTextSegmentPtr, compare_segment_end> segment_set_t;
-
- // 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
- virtual void drawSelectionBackground(); // draws the black box behind the selected text
- void drawCursor();
- void drawText();
-
- // modify contents
- S32 insertStringNoUndo(S32 pos, const LLWString &wstr, segment_vec_t* segments = NULL); // returns num of chars actually inserted
- S32 removeStringNoUndo(S32 pos, S32 length);
- S32 overwriteCharNoUndo(S32 pos, llwchar wc);
- void appendAndHighlightText(const std::string &new_text, S32 highlight_part, const LLStyle::Params& stylep, bool underline_on_hover_only = false);
-
-
- // manage segments
- void getSegmentAndOffset( S32 startpos, segment_set_t::const_iterator* seg_iter, S32* offsetp ) const;
- void getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp );
- LLTextSegmentPtr getSegmentAtLocalPos( S32 x, S32 y, bool hit_past_end_of_line = true);
- segment_set_t::iterator 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& getStyleParams();
-
- // manage lines
- S32 getLineStart( S32 line ) const;
- S32 getLineEnd( S32 line ) const;
- S32 getLineNumFromDocIndex( S32 doc_index, bool include_wordwrap = true) const;
- S32 getLineOffsetFromDocIndex( S32 doc_index, bool include_wordwrap = true) const;
- S32 getFirstVisibleLine() const;
- std::pair<S32, S32> getVisibleLines(bool fully_visible = false);
- S32 getLeftOffset(S32 width);
- void reflow();
-
- // cursor
- void updateCursorXPos();
- void setCursorAtLocalPos( S32 local_x, S32 local_y, bool round, bool keep_cursor_offset=false );
- S32 getEditableIndex(S32 index, bool increasing_direction); // constraint cursor to editable segments of document
- void resetCursorBlink() { mCursorBlinkTimer.reset(); }
- void updateScrollFromCursor();
-
- // text selection
- bool hasSelection() const { return (mSelectionStart !=mSelectionEnd); }
- void startSelection();
- void endSelection();
-
- // misc
- void updateRects();
- void needsScroll() { mScrollNeeded = true; }
-
- struct URLLabelCallback;
- // Replace a URL with a new icon and label, for example, when
- // avatar names are looked up.
- void replaceUrl(const std::string &url, const std::string &label, const std::string& icon);
-
- 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:
- // virtual
- std::string _getSearchText() const override
- {
- return mLabel.getString() + getToolTip();
- }
-
- std::vector<LLRect> getSelectionRects();
-
-protected:
- // text segmentation and flow
- segment_set_t mSegments;
- line_list_t mLineInfoList;
- LLRect mVisibleTextRect; // The rect in which text is drawn. Excludes borders.
- LLRect mTextBoundingRect;
-
- // default text style
- LLStyle::Params mStyle;
- bool mStyleDirty;
- const LLFontGL* mFont;
- const LLFontGL::ShadowType mFontShadow;
-
- // colors
- LLUIColor mCursorColor;
- LLUIColor mFgColor;
- LLUIColor mReadOnlyFgColor;
- LLUIColor mTentativeFgColor;
- LLUIColor mWriteableBgColor;
- LLUIColor mReadOnlyBgColor;
- LLUIColor mFocusBgColor;
- LLUIColor mTextSelectedColor;
- LLUIColor mSelectedBGColor;
-
- // cursor
- S32 mCursorPos; // I-beam is just after the mCursorPos-th character.
- S32 mDesiredXPixel; // X pixel position where the user wants the cursor to be
- LLFrameTimer mCursorBlinkTimer; // timer that controls cursor blinking
-
- // selection
- S32 mSelectionStart;
- S32 mSelectionEnd;
- 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
- LLFontGL::HAlign mHAlign; // horizontal alignment of the document in its entirety
- LLFontGL::VAlign mVAlign; // vertical alignment of the document in its entirety
- LLFontGL::VAlign mTextVAlign; // vertical alignment of a text segment within a single line of text
- F32 mLineSpacingMult; // multiple of line height used as space for a single line of text (e.g. 1.5 to get 50% padding)
- S32 mLineSpacingPixels; // padding between lines
- bool mBorderVisible;
- bool mParseHTML; // make URLs interactive
- bool mForceUrlsExternal; // URLs from this textbox will be opened in external browser
- bool mParseHighlights; // highlight user-defined keywords
- bool mWordWrap;
- bool mUseEllipses;
- bool mUseColor;
- 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
- bool mSkipTripleClick;
- bool mAlwaysShowIcons;
-
- bool mSkipLinkUnderline;
-
- // support widgets
- LLHandle<LLContextMenu> mPopupMenuHandle;
- LLView* mDocumentView;
- LLScrollContainer* mScroller;
-
- // transient state
- S32 mReflowIndex; // index at which to start reflow. S32_MAX indicates no reflow needed.
- bool mScrollNeeded; // need to change scroll region because of change to cursor position
- S32 mScrollIndex; // index of first character to keep visible in scroll region
-
- // Fired when a URL link is clicked
- commit_signal_t* mURLClickSignal;
-
- // Used to check if user with given ID is avatar's friend
- is_friend_signal_t* mIsFriendSignal;
- is_blocked_signal_t* mIsObjectBlockedSignal;
-
- LLUIString mLabel; // text label that is visible when no user text provided
-};
-
-#endif
+/**
+ * @file lltextbase.h
+ * @author Martin Reddy
+ * @brief The base class of text box/editor, providing Url handling support
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTEXTBASE_H
+#define LL_LLTEXTBASE_H
+
+#include "v4color.h"
+#include "lleditmenuhandler.h"
+#include "llspellcheckmenuhandler.h"
+#include "llstyle.h"
+#include "llkeywords.h"
+#include "llpanel.h"
+
+#include <string>
+#include <vector>
+#include <set>
+
+#include <boost/signals2.hpp>
+
+class LLScrollContainer;
+class LLContextMenu;
+class LLUrlMatch;
+
+///
+/// A text segment is used to specify a subsection of a text string
+/// that should be formatted differently, such as a hyperlink. It
+/// includes a start/end offset from the start of the string, a
+/// style to render with, an optional tooltip, etc.
+///
+class LLTextSegment
+: public LLRefCount,
+ public LLMouseHandler
+{
+public:
+ LLTextSegment(S32 start, S32 end)
+ : mStart(start),
+ mEnd(end)
+ {}
+ virtual ~LLTextSegment();
+ bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const;
+
+ virtual bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const;
+ virtual S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const;
+
+ /**
+ * Get number of chars that fit into free part of current line.
+ *
+ * @param num_pixels - maximum width of rect
+ * @param segment_offset - symbol in segment we start processing line from
+ * @param line_offset - symbol in line after which segment starts
+ * @param max_chars - limit of symbols that will fit in current line
+ * @param line_ind - index of not word-wrapped string inside segment for multi-line segments.
+ * Two string separated by word-wrap will have same index.
+ * @return number of chars that will fit into current line
+ */
+ virtual S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const;
+ virtual void updateLayout(const class LLTextBase& editor);
+ virtual F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect);
+ virtual bool canEdit() const;
+ virtual void unlinkFromDocument(class LLTextBase* editor);
+ virtual void linkToDocument(class LLTextBase* editor);
+
+ virtual const LLColor4& getColor() const;
+ //virtual void setColor(const LLColor4 &color);
+ virtual LLStyleConstSP getStyle() const;
+ virtual void setStyle(LLStyleConstSP style);
+ virtual void setToken( LLKeywordToken* token );
+ virtual LLKeywordToken* getToken() const;
+ virtual void setToolTip(const std::string& tooltip);
+ virtual void dump() const;
+
+ // LLMouseHandler interface
+ /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleMiddleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleMiddleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleRightMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks);
+ /*virtual*/ bool handleScrollHWheel(S32 x, S32 y, S32 clicks);
+ /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask);
+ /*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; }
+
+protected:
+ S32 mStart;
+ S32 mEnd;
+};
+
+class LLNormalTextSegment : public LLTextSegment
+{
+public:
+ LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor );
+ LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible = true);
+ virtual ~LLNormalTextSegment();
+
+ /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const;
+ /*virtual*/ S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const;
+ /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const;
+ /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect);
+ /*virtual*/ bool canEdit() const { return true; }
+ /*virtual*/ const LLColor4& getColor() const { return mStyle->getColor(); }
+ /*virtual*/ LLStyleConstSP getStyle() const { return mStyle; }
+ /*virtual*/ void setStyle(LLStyleConstSP style) { mStyle = style; }
+ /*virtual*/ void setToken( LLKeywordToken* token ) { mToken = token; }
+ /*virtual*/ LLKeywordToken* getToken() const { return mToken; }
+ /*virtual*/ bool getToolTip( std::string& msg ) const;
+ /*virtual*/ void setToolTip(const std::string& tooltip);
+ /*virtual*/ void dump() const;
+
+ /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask);
+
+protected:
+ F32 drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRectf rect);
+
+ virtual const LLWString& getWText() const;
+ virtual const S32 getLength() const;
+
+protected:
+ class LLTextBase& mEditor;
+ LLStyleConstSP mStyle;
+ S32 mFontHeight;
+ LLKeywordToken* mToken;
+ std::string mTooltip;
+ 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 represents a single emoji character that has a different style (=font size) than the rest of
+// the document it belongs to
+class LLEmojiTextSegment : public LLNormalTextSegment
+{
+public:
+ LLEmojiTextSegment(LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor);
+ LLEmojiTextSegment(const LLColor4& color, S32 start, S32 end, LLTextBase& editor, bool is_visible = true);
+
+ bool canEdit() const override { return false; }
+ bool handleToolTip(S32 x, S32 y, MASK mask) override;
+};
+
+// Text segment that changes it's style depending of mouse pointer position ( is it inside or outside segment)
+class LLOnHoverChangeableTextSegment : public LLNormalTextSegment
+{
+public:
+ LLOnHoverChangeableTextSegment( LLStyleConstSP style, LLStyleConstSP normal_style, S32 start, S32 end, LLTextBase& editor );
+ /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect);
+ /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
+protected:
+ // Style used for text when mouse pointer is over segment
+ LLStyleConstSP mHoveredStyle;
+ // Style used for text when mouse pointer is outside segment
+ LLStyleConstSP mNormalStyle;
+
+};
+
+class LLIndexSegment : public LLTextSegment
+{
+public:
+ LLIndexSegment() : LLTextSegment(0, 0) {}
+};
+
+class LLInlineViewSegment : public LLTextSegment
+{
+public:
+ struct Params : public LLInitParam::Block<Params>
+ {
+ Mandatory<LLView*> view;
+ Optional<bool> force_newline;
+ Optional<S32> left_pad,
+ right_pad,
+ bottom_pad,
+ top_pad;
+ };
+
+ LLInlineViewSegment(const Params& p, S32 start, S32 end);
+ ~LLInlineViewSegment();
+ /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const;
+ /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const;
+ /*virtual*/ void updateLayout(const class LLTextBase& editor);
+ /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect);
+ /*virtual*/ bool canEdit() const { return false; }
+ /*virtual*/ void unlinkFromDocument(class LLTextBase* editor);
+ /*virtual*/ void linkToDocument(class LLTextBase* editor);
+
+private:
+ S32 mLeftPad;
+ S32 mRightPad;
+ S32 mTopPad;
+ S32 mBottomPad;
+ LLView* mView;
+ bool mForceNewLine;
+};
+
+class LLLineBreakTextSegment : public LLTextSegment
+{
+public:
+
+ LLLineBreakTextSegment(LLStyleConstSP style,S32 pos);
+ LLLineBreakTextSegment(S32 pos);
+ ~LLLineBreakTextSegment();
+ /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const;
+ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const;
+ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect);
+
+private:
+ S32 mFontHeight;
+};
+
+class LLImageTextSegment : public LLTextSegment
+{
+public:
+ LLImageTextSegment(LLStyleConstSP style,S32 pos,class LLTextBase& editor);
+ ~LLImageTextSegment();
+ /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const;
+ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 char_offset, S32 max_chars, S32 line_ind) const;
+ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect);
+
+ /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask);
+ /*virtual*/ void setToolTip(const std::string& tooltip);
+
+private:
+ class LLTextBase& mEditor;
+ LLStyleConstSP mStyle;
+
+protected:
+ std::string mTooltip;
+};
+
+typedef LLPointer<LLTextSegment> LLTextSegmentPtr;
+
+///
+/// The LLTextBase class provides a base class for all text fields, such
+/// as LLTextEditor and LLTextBox. It implements shared functionality
+/// such as Url highlighting and opening.
+///
+class LLTextBase
+: public LLUICtrl,
+ protected LLEditMenuHandler,
+ public LLSpellCheckMenuHandler,
+ public ll::ui::SearchableControl
+{
+public:
+ friend class LLTextSegment;
+ friend class LLNormalTextSegment;
+ friend class LLUICtrlFactory;
+
+ typedef boost::signals2::signal<bool (const LLUUID& user_id)> is_friend_signal_t;
+ typedef boost::signals2::signal<bool (const LLUUID& blocked_id, const std::string from)> is_blocked_signal_t;
+
+ struct LineSpacingParams : public LLInitParam::ChoiceBlock<LineSpacingParams>
+ {
+ Alternative<F32> multiple;
+ Alternative<S32> pixels;
+ LineSpacingParams();
+ };
+
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<LLUIColor> cursor_color,
+ text_color,
+ text_readonly_color,
+ text_tentative_color,
+ bg_readonly_color,
+ bg_writeable_color,
+ bg_focus_color,
+ text_selected_color,
+ bg_selected_color;
+
+ Optional<bool> bg_visible,
+ border_visible,
+ track_end,
+ read_only,
+ skip_link_underline,
+ spellcheck,
+ allow_scroll,
+ plain_text,
+ wrap,
+ use_ellipses,
+ use_emoji,
+ use_color,
+ parse_urls,
+ force_urls_external,
+ parse_highlights,
+ clip,
+ clip_partial,
+ trusted_content,
+ always_show_icons;
+
+ Optional<S32> v_pad,
+ h_pad;
+
+
+ Optional<LineSpacingParams>
+ line_spacing;
+
+ Optional<S32> max_text_length;
+
+ Optional<LLFontGL::ShadowType> font_shadow;
+
+ Optional<LLFontGL::VAlign> text_valign;
+
+ Params();
+ };
+
+ // LLMouseHandler interface
+ /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ bool handleMiddleMouseDown(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ bool handleMiddleMouseUp(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ bool handleRightMouseUp(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks) override;
+ /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask) override;
+
+ // LLView interface
+ /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true) override;
+ /*virtual*/ void draw() override;
+
+ // LLUICtrl interface
+ /*virtual*/ bool acceptsTextInput() const override { return !mReadOnly; }
+ /*virtual*/ void setColor(const LLColor4& c) override;
+ virtual void setReadOnlyColor(const LLColor4 &c);
+ /*virtual*/ void onVisibilityChange(bool new_visibility) override;
+
+ /*virtual*/ void setValue(const LLSD& value) override;
+ /*virtual*/ LLTextViewModel* getViewModel() const override;
+
+ // LLEditMenuHandler interface
+ /*virtual*/ bool canDeselect() const override;
+ /*virtual*/ void deselect() override;
+
+ virtual void onFocusReceived() override;
+ virtual void onFocusLost() override;
+
+ void setParseHTML(bool parse_html) { mParseHTML = parse_html; }
+
+ // LLSpellCheckMenuHandler overrides
+ /*virtual*/ bool getSpellCheck() const override;
+
+ /*virtual*/ const std::string& getSuggestion(U32 index) const override;
+ /*virtual*/ U32 getSuggestionCount() const override;
+ /*virtual*/ void replaceWithSuggestion(U32 index) override;
+
+ /*virtual*/ void addToDictionary() override;
+ /*virtual*/ bool canAddToDictionary() const override;
+
+ /*virtual*/ void addToIgnore() override;
+ /*virtual*/ bool canAddToIgnore() const override;
+
+ // Spell checking helper functions
+ std::string getMisspelledWord(U32 pos) const;
+ bool isMisspelledWord(U32 pos) const;
+ void onSpellCheckSettingsChange();
+ virtual void onSpellCheckPerformed(){}
+
+ // used by LLTextSegment layout code
+ bool getWordWrap() const { return mWordWrap; }
+ bool getUseEllipses() const { return mUseEllipses; }
+ bool getUseEmoji() const { return mUseEmoji; }
+ void setUseEmoji(bool value) { mUseEmoji = value; }
+ bool getUseColor() const { return mUseColor; }
+ void setUseColor(bool value) { mUseColor = value; }
+ bool truncate(); // returns true of truncation occurred
+
+ bool isContentTrusted() const { 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
+
+ // Text accessors
+ // TODO: add optional style parameter
+ virtual void setText(const LLStringExplicit &utf8str , const LLStyle::Params& input_params = LLStyle::Params()); // uses default style
+ /*virtual*/ const std::string& getText() const override;
+ void setMaxTextLength(S32 length) { mMaxTextByteLength = length; }
+ S32 getMaxTextLength() { return mMaxTextByteLength; }
+
+ // wide-char versions
+ void setWText(const LLWString& text);
+ const LLWString& getWText() const;
+
+ void appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params = LLStyle::Params());
+
+ void setLabel(const LLStringExplicit& label);
+ /*virtual*/ bool setLabelArg(const std::string& key, const LLStringExplicit& text) override;
+
+ const std::string& getLabel() { return mLabel.getString(); }
+ const LLWString& getWlabel() { return mLabel.getWString();}
+
+ void setLastSegmentToolTip(const std::string &tooltip);
+
+ /**
+ * 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);
+
+ S32 getLength() const { return getWText().length(); }
+ S32 getLineCount() const { return mLineInfoList.size(); }
+ S32 removeFirstLine(); // returns removed length
+
+ void addDocumentChild(LLView* view);
+ void removeDocumentChild(LLView* view);
+ const LLView* getDocumentView() const { return mDocumentView; }
+ LLRect getVisibleTextRect() const { return mVisibleTextRect; }
+ LLRect getTextBoundingRect();
+ LLRect getVisibleDocumentRect() const;
+
+ S32 getVPad() { return mVPad; }
+ S32 getHPad() { return mHPad; }
+ F32 getLineSpacingMult() { return mLineSpacingMult; }
+ S32 getLineSpacingPixels() { return mLineSpacingPixels; } // only for multiline
+
+ S32 getDocIndexFromLocalCoord( S32 local_x, S32 local_y, bool round, bool hit_past_end_of_line = true) const;
+ LLRect getLocalRectFromDocIndex(S32 pos) const;
+ LLRect getDocRectFromDocIndex(S32 pos) const;
+
+ void setReadOnly(bool read_only) { mReadOnly = read_only; }
+ bool getReadOnly() { return mReadOnly; }
+
+ void setSkipLinkUnderline(bool skip_link_underline) { mSkipLinkUnderline = skip_link_underline; }
+ bool getSkipLinkUnderline() { return mSkipLinkUnderline; }
+
+ void setParseURLs(bool parse_urls) { mParseHTML = parse_urls; }
+
+ void setPlainText(bool value) { mPlainText = value;}
+ bool getPlainText() const { return mPlainText; }
+
+ // cursor manipulation
+ bool setCursor(S32 row, S32 column);
+ bool setCursorPos(S32 cursor_pos, bool keep_cursor_offset = false);
+ void startOfLine();
+ void endOfLine();
+ void startOfDoc();
+ void endOfDoc();
+ void changePage( S32 delta );
+ void changeLine( S32 delta );
+
+ bool scrolledToStart();
+ bool scrolledToEnd();
+
+ const LLFontGL* getFont() const override { 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);
+ boost::signals2::connection setIsObjectBlockedCallback(const is_blocked_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
+ {
+ 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;
+
+ // Abstract inner base class representing an undoable editor command.
+ // Concrete sub-classes can be defined for operations such as insert, remove, etc.
+ // Used as arguments to the execute() method below.
+ class TextCmd
+ {
+ public:
+ TextCmd( S32 pos, bool group_with_next, LLTextSegmentPtr segment = LLTextSegmentPtr() )
+ : mPos(pos),
+ mGroupWithNext(group_with_next)
+ {
+ if (segment.notNull())
+ {
+ mSegments.push_back(segment);
+ }
+ }
+ virtual ~TextCmd() {}
+ virtual bool execute(LLTextBase* editor, S32* delta) = 0;
+ virtual S32 undo(LLTextBase* editor) = 0;
+ virtual S32 redo(LLTextBase* editor) = 0;
+ virtual bool canExtend(S32 pos) const { return false; }
+ virtual void blockExtensions() {}
+ virtual bool extendAndExecute( LLTextBase* editor, S32 pos, llwchar c, S32* delta ) { llassert(0); return 0; }
+ virtual bool hasExtCharValue( llwchar value ) const { return false; }
+
+ // Defined here so they can access protected LLTextEditor editing methods
+ S32 insert(LLTextBase* editor, S32 pos, const LLWString &wstr) { return editor->insertStringNoUndo( pos, wstr, &mSegments ); }
+ S32 remove(LLTextBase* editor, S32 pos, S32 length) { return editor->removeStringNoUndo( pos, length ); }
+ S32 overwrite(LLTextBase* editor, S32 pos, llwchar wc) { return editor->overwriteCharNoUndo(pos, wc); }
+
+ S32 getPosition() const { return mPos; }
+ bool groupWithNext() const { return mGroupWithNext; }
+
+ protected:
+ const S32 mPos;
+ bool mGroupWithNext;
+ segment_vec_t mSegments;
+ };
+
+ struct compare_segment_end
+ {
+ bool operator()(const LLTextSegmentPtr& a, const LLTextSegmentPtr& b) const;
+ };
+ typedef std::multiset<LLTextSegmentPtr, compare_segment_end> segment_set_t;
+
+ // 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
+ virtual void drawSelectionBackground(); // draws the black box behind the selected text
+ void drawCursor();
+ void drawText();
+
+ // modify contents
+ S32 insertStringNoUndo(S32 pos, const LLWString &wstr, segment_vec_t* segments = NULL); // returns num of chars actually inserted
+ S32 removeStringNoUndo(S32 pos, S32 length);
+ S32 overwriteCharNoUndo(S32 pos, llwchar wc);
+ void appendAndHighlightText(const std::string &new_text, S32 highlight_part, const LLStyle::Params& stylep, bool underline_on_hover_only = false);
+
+
+ // manage segments
+ void getSegmentAndOffset( S32 startpos, segment_set_t::const_iterator* seg_iter, S32* offsetp ) const;
+ void getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp );
+ LLTextSegmentPtr getSegmentAtLocalPos( S32 x, S32 y, bool hit_past_end_of_line = true);
+ segment_set_t::iterator 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& getStyleParams();
+
+ // manage lines
+ S32 getLineStart( S32 line ) const;
+ S32 getLineEnd( S32 line ) const;
+ S32 getLineNumFromDocIndex( S32 doc_index, bool include_wordwrap = true) const;
+ S32 getLineOffsetFromDocIndex( S32 doc_index, bool include_wordwrap = true) const;
+ S32 getFirstVisibleLine() const;
+ std::pair<S32, S32> getVisibleLines(bool fully_visible = false);
+ S32 getLeftOffset(S32 width);
+ void reflow();
+
+ // cursor
+ void updateCursorXPos();
+ void setCursorAtLocalPos( S32 local_x, S32 local_y, bool round, bool keep_cursor_offset=false );
+ S32 getEditableIndex(S32 index, bool increasing_direction); // constraint cursor to editable segments of document
+ void resetCursorBlink() { mCursorBlinkTimer.reset(); }
+ void updateScrollFromCursor();
+
+ // text selection
+ bool hasSelection() const { return (mSelectionStart !=mSelectionEnd); }
+ void startSelection();
+ void endSelection();
+
+ // misc
+ void updateRects();
+ void needsScroll() { mScrollNeeded = true; }
+
+ struct URLLabelCallback;
+ // Replace a URL with a new icon and label, for example, when
+ // avatar names are looked up.
+ void replaceUrl(const std::string &url, const std::string &label, const std::string& icon);
+
+ 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:
+ // virtual
+ std::string _getSearchText() const override
+ {
+ return mLabel.getString() + getToolTip();
+ }
+
+ std::vector<LLRect> getSelectionRects();
+
+protected:
+ // text segmentation and flow
+ segment_set_t mSegments;
+ line_list_t mLineInfoList;
+ LLRect mVisibleTextRect; // The rect in which text is drawn. Excludes borders.
+ LLRect mTextBoundingRect;
+
+ // default text style
+ LLStyle::Params mStyle;
+ bool mStyleDirty;
+ const LLFontGL* mFont;
+ const LLFontGL::ShadowType mFontShadow;
+
+ // colors
+ LLUIColor mCursorColor;
+ LLUIColor mFgColor;
+ LLUIColor mReadOnlyFgColor;
+ LLUIColor mTentativeFgColor;
+ LLUIColor mWriteableBgColor;
+ LLUIColor mReadOnlyBgColor;
+ LLUIColor mFocusBgColor;
+ LLUIColor mTextSelectedColor;
+ LLUIColor mSelectedBGColor;
+
+ // cursor
+ S32 mCursorPos; // I-beam is just after the mCursorPos-th character.
+ S32 mDesiredXPixel; // X pixel position where the user wants the cursor to be
+ LLFrameTimer mCursorBlinkTimer; // timer that controls cursor blinking
+
+ // selection
+ S32 mSelectionStart;
+ S32 mSelectionEnd;
+ 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
+ LLFontGL::HAlign mHAlign; // horizontal alignment of the document in its entirety
+ LLFontGL::VAlign mVAlign; // vertical alignment of the document in its entirety
+ LLFontGL::VAlign mTextVAlign; // vertical alignment of a text segment within a single line of text
+ F32 mLineSpacingMult; // multiple of line height used as space for a single line of text (e.g. 1.5 to get 50% padding)
+ S32 mLineSpacingPixels; // padding between lines
+ bool mBorderVisible;
+ bool mParseHTML; // make URLs interactive
+ bool mForceUrlsExternal; // URLs from this textbox will be opened in external browser
+ bool mParseHighlights; // highlight user-defined keywords
+ bool mWordWrap;
+ bool mUseEllipses;
+ bool mUseEmoji;
+ bool mUseColor;
+ 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
+ bool mSkipTripleClick;
+ bool mAlwaysShowIcons;
+
+ bool mSkipLinkUnderline;
+
+ // support widgets
+ LLHandle<LLContextMenu> mPopupMenuHandle;
+ LLView* mDocumentView;
+ LLScrollContainer* mScroller;
+
+ // transient state
+ S32 mReflowIndex; // index at which to start reflow. S32_MAX indicates no reflow needed.
+ bool mScrollNeeded; // need to change scroll region because of change to cursor position
+ S32 mScrollIndex; // index of first character to keep visible in scroll region
+
+ // Fired when a URL link is clicked
+ commit_signal_t* mURLClickSignal;
+
+ // Used to check if user with given ID is avatar's friend
+ is_friend_signal_t* mIsFriendSignal;
+ is_blocked_signal_t* mIsObjectBlockedSignal;
+
+ 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 f143d619c5..b3b97c00b4 100644
--- a/indra/llui/lltextbox.cpp
+++ b/indra/llui/lltextbox.cpp
@@ -1,183 +1,183 @@
-/**
- * @file lltextbox.cpp
- * @brief A text display widget
- *
- * $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"
-
-#define LLTEXTBOX_CPP
-#include "lltextbox.h"
-
-#include "lluictrlfactory.h"
-#include "llfocusmgr.h"
-#include "llwindow.h"
-#include "llurlregistry.h"
-#include "llstyle.h"
-
-static LLDefaultChildRegistry::Register<LLTextBox> r("text");
-
-// Compiler optimization, generate extern template
-template class LLTextBox* LLView::getChild<class LLTextBox>(
- const std::string& name, bool recurse) const;
-
-LLTextBox::LLTextBox(const LLTextBox::Params& p)
-: LLTextBase(p),
- mClickedCallback(NULL),
- mShowCursorHand(true)
-{
- mSkipTripleClick = true;
-}
-
-LLTextBox::~LLTextBox()
-{}
-
-bool LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- bool handled = LLTextBase::handleMouseDown(x, y, mask);
-
- if (getSoundFlags() & MOUSE_DOWN)
- {
- make_ui_sound("UISndClick");
- }
-
- if (!handled && mClickedCallback)
- {
- handled = true;
- }
-
- if (handled)
- {
- // Route future Mouse messages here preemptively. (Release on mouse up.)
- gFocusMgr.setMouseCapture( this );
- }
-
- return handled;
-}
-
-bool LLTextBox::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- bool handled = LLTextBase::handleMouseUp(x, y, mask);
-
- if (getSoundFlags() & MOUSE_UP)
- {
- make_ui_sound("UISndClickRelease");
- }
-
- // We only handle the click if the click both started and ended within us
- if (hasMouseCapture())
- {
- // Release the mouse
- gFocusMgr.setMouseCapture( NULL );
-
- // DO THIS AT THE VERY END to allow the button to be destroyed
- // as a result of being clicked. If mouseup in the widget,
- // it's been clicked
- if (mClickedCallback && !handled)
- {
- mClickedCallback();
- handled = true;
- }
- }
-
- return handled;
-}
-
-bool LLTextBox::handleHover(S32 x, S32 y, MASK mask)
-{
- bool handled = LLTextBase::handleHover(x, y, mask);
- if (!handled && mClickedCallback && mShowCursorHand)
- {
- // Clickable text boxes change the cursor to a hand
- LLUI::getInstance()->getWindow()->setCursor(UI_CURSOR_HAND);
- return true;
- }
- return handled;
-}
-
-void LLTextBox::setEnabled(bool enabled)
-{
- // just treat enabled as read-only flag
- bool read_only = !enabled;
- if (read_only != mReadOnly)
- {
- LLTextBase::setReadOnly(read_only);
- updateSegments();
- }
- LLTextBase::setEnabled(enabled);
-}
-
-void LLTextBox::setText(const LLStringExplicit& text , const LLStyle::Params& input_params )
-{
- // does string argument insertion
- mText.assign(text);
-
- LLTextBase::setText(mText.getString(), input_params );
-}
-
-void LLTextBox::setClickedCallback( boost::function<void (void*)> cb, void* userdata /*= NULL */ )
-{
- mClickedCallback = boost::bind(cb, userdata);
-}
-
-S32 LLTextBox::getTextPixelWidth()
-{
- return getTextBoundingRect().getWidth();
-}
-
-S32 LLTextBox::getTextPixelHeight()
-{
- return getTextBoundingRect().getHeight();
-}
-
-
-LLSD LLTextBox::getValue() const
-{
- return getViewModel()->getValue();
-}
-
-bool LLTextBox::setTextArg( const std::string& key, const LLStringExplicit& text )
-{
- mText.setArg(key, text);
- LLTextBase::setText(mText.getString());
-
- return true;
-}
-
-
-void LLTextBox::reshapeToFitText(bool called_from_parent)
-{
- reflow();
-
- S32 width = getTextPixelWidth();
- S32 height = getTextPixelHeight();
- //consider investigating reflow() to find missing width pixel (see SL-17045 changes)
- reshape( width + 2 * mHPad + 1, height + 2 * mVPad, called_from_parent );
-}
-
-
-void LLTextBox::onUrlLabelUpdated(const std::string &url, const std::string &label)
-{
- needsReflow();
-}
-
+/**
+ * @file lltextbox.cpp
+ * @brief A text display widget
+ *
+ * $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"
+
+#define LLTEXTBOX_CPP
+#include "lltextbox.h"
+
+#include "lluictrlfactory.h"
+#include "llfocusmgr.h"
+#include "llwindow.h"
+#include "llurlregistry.h"
+#include "llstyle.h"
+
+static LLDefaultChildRegistry::Register<LLTextBox> r("text");
+
+// Compiler optimization, generate extern template
+template class LLTextBox* LLView::getChild<class LLTextBox>(
+ const std::string& name, bool recurse) const;
+
+LLTextBox::LLTextBox(const LLTextBox::Params& p)
+: LLTextBase(p),
+ mClickedCallback(NULL),
+ mShowCursorHand(true)
+{
+ mSkipTripleClick = true;
+}
+
+LLTextBox::~LLTextBox()
+{}
+
+bool LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ bool handled = LLTextBase::handleMouseDown(x, y, mask);
+
+ if (getSoundFlags() & MOUSE_DOWN)
+ {
+ make_ui_sound("UISndClick");
+ }
+
+ if (!handled && mClickedCallback)
+ {
+ handled = true;
+ }
+
+ if (handled)
+ {
+ // Route future Mouse messages here preemptively. (Release on mouse up.)
+ gFocusMgr.setMouseCapture( this );
+ }
+
+ return handled;
+}
+
+bool LLTextBox::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ bool handled = LLTextBase::handleMouseUp(x, y, mask);
+
+ if (getSoundFlags() & MOUSE_UP)
+ {
+ make_ui_sound("UISndClickRelease");
+ }
+
+ // We only handle the click if the click both started and ended within us
+ if (hasMouseCapture())
+ {
+ // Release the mouse
+ gFocusMgr.setMouseCapture( NULL );
+
+ // DO THIS AT THE VERY END to allow the button to be destroyed
+ // as a result of being clicked. If mouseup in the widget,
+ // it's been clicked
+ if (mClickedCallback && !handled)
+ {
+ mClickedCallback();
+ handled = true;
+ }
+ }
+
+ return handled;
+}
+
+bool LLTextBox::handleHover(S32 x, S32 y, MASK mask)
+{
+ bool handled = LLTextBase::handleHover(x, y, mask);
+ if (!handled && mClickedCallback && mShowCursorHand)
+ {
+ // Clickable text boxes change the cursor to a hand
+ LLUI::getInstance()->getWindow()->setCursor(UI_CURSOR_HAND);
+ return true;
+ }
+ return handled;
+}
+
+void LLTextBox::setEnabled(bool enabled)
+{
+ // just treat enabled as read-only flag
+ bool read_only = !enabled;
+ if (read_only != mReadOnly)
+ {
+ LLTextBase::setReadOnly(read_only);
+ updateSegments();
+ }
+ LLTextBase::setEnabled(enabled);
+}
+
+void LLTextBox::setText(const LLStringExplicit& text , const LLStyle::Params& input_params )
+{
+ // does string argument insertion
+ mText.assign(text);
+
+ LLTextBase::setText(mText.getString(), input_params );
+}
+
+void LLTextBox::setClickedCallback( boost::function<void (void*)> cb, void* userdata /*= NULL */ )
+{
+ mClickedCallback = boost::bind(cb, userdata);
+}
+
+S32 LLTextBox::getTextPixelWidth()
+{
+ return getTextBoundingRect().getWidth();
+}
+
+S32 LLTextBox::getTextPixelHeight()
+{
+ return getTextBoundingRect().getHeight();
+}
+
+
+LLSD LLTextBox::getValue() const
+{
+ return getViewModel()->getValue();
+}
+
+bool LLTextBox::setTextArg( const std::string& key, const LLStringExplicit& text )
+{
+ mText.setArg(key, text);
+ LLTextBase::setText(mText.getString());
+
+ return true;
+}
+
+
+void LLTextBox::reshapeToFitText(bool called_from_parent)
+{
+ reflow();
+
+ S32 width = getTextPixelWidth();
+ S32 height = getTextPixelHeight();
+ //consider investigating reflow() to find missing width pixel (see SL-17045 changes)
+ reshape( width + 2 * mHPad + 1, height + 2 * mVPad, called_from_parent );
+}
+
+
+void LLTextBox::onUrlLabelUpdated(const std::string &url, const std::string &label)
+{
+ needsReflow();
+}
+
diff --git a/indra/llui/lltextbox.h b/indra/llui/lltextbox.h
index 39016e2f4e..1e58fe954c 100644
--- a/indra/llui/lltextbox.h
+++ b/indra/llui/lltextbox.h
@@ -1,87 +1,87 @@
-/**
- * @file lltextbox.h
- * @brief A single text item display
- *
- * $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_LLTEXTBOX_H
-#define LL_LLTEXTBOX_H
-
-#include "lluistring.h"
-#include "lltextbase.h"
-
-class LLTextBox :
- public LLTextBase
-{
-public:
-
- // *TODO: Add callback to Params
- typedef boost::function<void (void)> callback_t;
-
- struct Params : public LLInitParam::Block<Params, LLTextBase::Params>
- {};
-
-protected:
- LLTextBox(const Params&);
- friend class LLUICtrlFactory;
-
-public:
- virtual ~LLTextBox();
-
- /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
-
- /*virtual*/ void setEnabled(bool enabled);
-
- /*virtual*/ void setText( const LLStringExplicit& text, const LLStyle::Params& input_params = LLStyle::Params() );
-
- void setRightAlign() { mHAlign = LLFontGL::RIGHT; }
- void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; }
- void setClickedCallback( boost::function<void (void*)> cb, void* userdata = NULL );
-
- void reshapeToFitText(bool called_from_parent = false);
-
- S32 getTextPixelWidth();
- S32 getTextPixelHeight();
-
- /*virtual*/ LLSD getValue() const;
- /*virtual*/ bool setTextArg( const std::string& key, const LLStringExplicit& text );
-
- void setShowCursorHand(bool show_cursor) { mShowCursorHand = show_cursor; }
-
-protected:
- void onUrlLabelUpdated(const std::string &url, const std::string &label);
-
- LLUIString mText;
- callback_t mClickedCallback;
- bool mShowCursorHand;
-};
-
-// Build time optimization, generate once in .cpp file
-#ifndef LLTEXTBOX_CPP
-extern template class LLTextBox* LLView::getChild<class LLTextBox>(
- const std::string& name, bool recurse) const;
-#endif
-
-#endif
+/**
+ * @file lltextbox.h
+ * @brief A single text item display
+ *
+ * $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_LLTEXTBOX_H
+#define LL_LLTEXTBOX_H
+
+#include "lluistring.h"
+#include "lltextbase.h"
+
+class LLTextBox :
+ public LLTextBase
+{
+public:
+
+ // *TODO: Add callback to Params
+ typedef boost::function<void (void)> callback_t;
+
+ struct Params : public LLInitParam::Block<Params, LLTextBase::Params>
+ {};
+
+protected:
+ LLTextBox(const Params&);
+ friend class LLUICtrlFactory;
+
+public:
+ virtual ~LLTextBox();
+
+ /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
+
+ /*virtual*/ void setEnabled(bool enabled);
+
+ /*virtual*/ void setText( const LLStringExplicit& text, const LLStyle::Params& input_params = LLStyle::Params() );
+
+ void setRightAlign() { mHAlign = LLFontGL::RIGHT; }
+ void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; }
+ void setClickedCallback( boost::function<void (void*)> cb, void* userdata = NULL );
+
+ void reshapeToFitText(bool called_from_parent = false);
+
+ S32 getTextPixelWidth();
+ S32 getTextPixelHeight();
+
+ /*virtual*/ LLSD getValue() const;
+ /*virtual*/ bool setTextArg( const std::string& key, const LLStringExplicit& text );
+
+ void setShowCursorHand(bool show_cursor) { mShowCursorHand = show_cursor; }
+
+protected:
+ void onUrlLabelUpdated(const std::string &url, const std::string &label);
+
+ LLUIString mText;
+ callback_t mClickedCallback;
+ bool mShowCursorHand;
+};
+
+// Build time optimization, generate once in .cpp file
+#ifndef LLTEXTBOX_CPP
+extern template class LLTextBox* LLView::getChild<class LLTextBox>(
+ const std::string& name, bool recurse) const;
+#endif
+
+#endif
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index 438b3d96b1..daffd58dfe 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -1,3057 +1,3070 @@
-/**
- * @file lltexteditor.cpp
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-// Text editor widget to let users enter a a multi-line ASCII document.
-
-#include "linden_common.h"
-
-#define LLTEXTEDITOR_CPP
-#include "lltexteditor.h"
-
-#include "llfontfreetype.h" // for LLFontFreetype::FIRST_CHAR
-#include "llfontgl.h"
-#include "llgl.h" // LLGLSUIDefault()
-#include "lllocalcliprect.h"
-#include "llrender.h"
-#include "llui.h"
-#include "lluictrlfactory.h"
-#include "llrect.h"
-#include "llfocusmgr.h"
-#include "lltimer.h"
-#include "llmath.h"
-
-#include "llclipboard.h"
-#include "llemojihelper.h"
-#include "llscrollbar.h"
-#include "llstl.h"
-#include "llstring.h"
-#include "llkeyboard.h"
-#include "llkeywords.h"
-#include "llundo.h"
-#include "llviewborder.h"
-#include "llcontrol.h"
-#include "llwindow.h"
-#include "lltextparser.h"
-#include "llscrollcontainer.h"
-#include "llspellcheck.h"
-#include "llpanel.h"
-#include "llurlregistry.h"
-#include "lltooltip.h"
-#include "llmenugl.h"
-
-#include <queue>
-#include "llcombobox.h"
-
-//
-// Globals
-//
-static LLDefaultChildRegistry::Register<LLTextEditor> r("simple_text_editor");
-
-// Compiler optimization, generate extern template
-template class LLTextEditor* LLView::getChild<class LLTextEditor>(
- const std::string& name, bool recurse) const;
-
-//
-// Constants
-//
-const S32 SPACES_PER_TAB = 4;
-const F32 SPELLCHECK_DELAY = 0.5f; // delay between the last keypress and spell checking the word the cursor is on
-
-///////////////////////////////////////////////////////////////////
-
-class LLTextEditor::TextCmdInsert : public LLTextBase::TextCmd
-{
-public:
- TextCmdInsert(S32 pos, bool group_with_next, const LLWString &ws, LLTextSegmentPtr segment)
- : TextCmd(pos, group_with_next, segment), mWString(ws)
- {
- }
- virtual ~TextCmdInsert() {}
- virtual bool execute( LLTextBase* editor, S32* delta )
- {
- *delta = insert(editor, getPosition(), mWString );
- LLWStringUtil::truncate(mWString, *delta);
- //mWString = wstring_truncate(mWString, *delta);
- return (*delta != 0);
- }
- virtual S32 undo( LLTextBase* editor )
- {
- remove(editor, getPosition(), mWString.length() );
- return getPosition();
- }
- virtual S32 redo( LLTextBase* editor )
- {
- insert(editor, getPosition(), mWString );
- return getPosition() + mWString.length();
- }
-
-private:
- LLWString mWString;
-};
-
-///////////////////////////////////////////////////////////////////
-class LLTextEditor::TextCmdAddChar : public LLTextBase::TextCmd
-{
-public:
- TextCmdAddChar( S32 pos, bool group_with_next, llwchar wc, LLTextSegmentPtr segment)
- : TextCmd(pos, group_with_next, segment), mWString(1, wc), mBlockExtensions(false)
- {
- }
- virtual void blockExtensions()
- {
- mBlockExtensions = true;
- }
- virtual bool canExtend(S32 pos) const
- {
- // cannot extend text with custom segments
- if (!mSegments.empty()) return false;
-
- return !mBlockExtensions && (pos == getPosition() + (S32)mWString.length());
- }
- virtual bool execute( LLTextBase* editor, S32* delta )
- {
- *delta = insert(editor, getPosition(), mWString);
- LLWStringUtil::truncate(mWString, *delta);
- //mWString = wstring_truncate(mWString, *delta);
- return (*delta != 0);
- }
- virtual bool extendAndExecute( LLTextBase* editor, S32 pos, llwchar wc, S32* delta )
- {
- LLWString ws;
- ws += wc;
-
- *delta = insert(editor, pos, ws);
- if( *delta > 0 )
- {
- mWString += wc;
- }
- return (*delta != 0);
- }
- virtual S32 undo( LLTextBase* editor )
- {
- remove(editor, getPosition(), mWString.length() );
- return getPosition();
- }
- virtual S32 redo( LLTextBase* editor )
- {
- insert(editor, getPosition(), mWString );
- return getPosition() + mWString.length();
- }
-
-private:
- LLWString mWString;
- bool mBlockExtensions;
-
-};
-
-///////////////////////////////////////////////////////////////////
-
-class LLTextEditor::TextCmdOverwriteChar : public LLTextBase::TextCmd
-{
-public:
- TextCmdOverwriteChar( S32 pos, bool group_with_next, llwchar wc)
- : TextCmd(pos, group_with_next), mChar(wc), mOldChar(0) {}
-
- virtual bool execute( LLTextBase* editor, S32* delta )
- {
- mOldChar = editor->getWText()[getPosition()];
- overwrite(editor, getPosition(), mChar);
- *delta = 0;
- return true;
- }
- virtual S32 undo( LLTextBase* editor )
- {
- overwrite(editor, getPosition(), mOldChar);
- return getPosition();
- }
- virtual S32 redo( LLTextBase* editor )
- {
- overwrite(editor, getPosition(), mChar);
- return getPosition()+1;
- }
-
-private:
- llwchar mChar;
- llwchar mOldChar;
-};
-
-///////////////////////////////////////////////////////////////////
-
-class LLTextEditor::TextCmdRemove : public LLTextBase::TextCmd
-{
-public:
- TextCmdRemove( S32 pos, bool group_with_next, S32 len, segment_vec_t& segments ) :
- TextCmd(pos, group_with_next), mLen(len)
- {
- std::swap(mSegments, segments);
- }
- virtual bool execute( LLTextBase* editor, S32* delta )
- {
- mWString = editor->getWText().substr(getPosition(), mLen);
- *delta = remove(editor, getPosition(), mLen );
- return (*delta != 0);
- }
- virtual S32 undo( LLTextBase* editor )
- {
- insert(editor, getPosition(), mWString);
- return getPosition() + mWString.length();
- }
- virtual S32 redo( LLTextBase* editor )
- {
- remove(editor, getPosition(), mLen );
- return getPosition();
- }
-private:
- LLWString mWString;
- S32 mLen;
-};
-
-
-///////////////////////////////////////////////////////////////////
-LLTextEditor::Params::Params()
-: default_text("default_text"),
- prevalidate_callback("prevalidate_callback"),
- embedded_items("embedded_items", false),
- ignore_tab("ignore_tab", true),
- 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_emoji_helper("show_emoji_helper"),
- 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() ),
- mAutoIndent(p.auto_indent),
- mCommitOnFocusLost( p.commit_on_focus_lost),
- mAllowEmbeddedItems( p.embedded_items ),
- mMouseDownX(0),
- mMouseDownY(0),
- mTabsToNextField(p.ignore_tab),
- mPrevalidateFunc(p.prevalidate_callback()),
- mShowContextMenu(p.show_context_menu),
- mShowEmojiHelper(p.show_emoji_helper),
- mEnableTooltipPaste(p.enable_tooltip_paste),
- mPassDelete(false),
- mKeepSelectionOnReturn(false)
-{
- mSourceID.generate();
-
- //FIXME: use image?
- LLViewBorder::Params params;
- params.name = "text ed border";
- params.rect = getLocalRect();
- params.bevel_style = LLViewBorder::BEVEL_IN;
- params.border_thickness = 1;
- params.visible = p.border_visible;
- mBorder = LLUICtrlFactory::create<LLViewBorder> (params);
- addChild( mBorder );
- setText(p.default_text());
-
- mParseOnTheFly = true;
-}
-
-void LLTextEditor::initFromParams( const LLTextEditor::Params& p)
-{
- LLTextBase::initFromParams(p);
-
- // HACK: text editors always need to be enabled so that we can scroll
- LLView::setEnabled(true);
-
- if (p.commit_on_focus_lost.isProvided())
- {
- mCommitOnFocusLost = p.commit_on_focus_lost;
- }
-
- updateAllowingLanguageInput();
-}
-
-LLTextEditor::~LLTextEditor()
-{
- gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() while LLTextEditor still valid
-
- // Scrollbar is deleted by LLView
- std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer());
- mUndoStack.clear();
- // Mark the menu as dead or its retained in memory till shutdown.
- LLContextMenu* menu = static_cast<LLContextMenu*>(mContextMenuHandle.get());
- if(menu)
- {
- menu->die();
- mContextMenuHandle.markDead();
- }
-}
-
-////////////////////////////////////////////////////////////
-// LLTextEditor
-// Public methods
-
-void LLTextEditor::setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params)
-{
- // validate incoming text if necessary
- if (mPrevalidateFunc)
- {
- LLWString test_text = utf8str_to_wstring(utf8str);
- if (!mPrevalidateFunc(test_text))
- {
- // not valid text, nothing to do
- return;
- }
- }
-
- blockUndo();
- deselect();
-
- mParseOnTheFly = false;
- LLTextBase::setText(utf8str, input_params);
- mParseOnTheFly = true;
-
- resetDirty();
-}
-
-void LLTextEditor::selectNext(const std::string& search_text_in, bool case_insensitive, bool wrap)
-{
- if (search_text_in.empty())
- {
- return;
- }
-
- LLWString text = getWText();
- LLWString search_text = utf8str_to_wstring(search_text_in);
- if (case_insensitive)
- {
- LLWStringUtil::toLower(text);
- LLWStringUtil::toLower(search_text);
- }
-
- if (mIsSelecting)
- {
- LLWString selected_text = text.substr(mSelectionEnd, mSelectionStart - mSelectionEnd);
-
- if (selected_text == search_text)
- {
- // We already have this word selected, we are searching for the next.
- setCursorPos(mCursorPos + search_text.size());
- }
- }
-
- S32 loc = text.find(search_text,mCursorPos);
-
- // If Maybe we wrapped, search again
- if (wrap && (-1 == loc))
- {
- loc = text.find(search_text);
- }
-
- // If still -1, then search_text just isn't found.
- if (-1 == loc)
- {
- mIsSelecting = false;
- mSelectionEnd = 0;
- mSelectionStart = 0;
- return;
- }
-
- setCursorPos(loc);
-
- mIsSelecting = true;
- mSelectionEnd = mCursorPos;
- mSelectionStart = llmin((S32)getLength(), (S32)(mCursorPos + search_text.size()));
-}
-
-bool LLTextEditor::replaceText(const std::string& search_text_in, const std::string& replace_text,
- bool case_insensitive, bool wrap)
-{
- bool replaced = false;
-
- if (search_text_in.empty())
- {
- return replaced;
- }
-
- LLWString search_text = utf8str_to_wstring(search_text_in);
- if (mIsSelecting)
- {
- LLWString text = getWText();
- LLWString selected_text = text.substr(mSelectionEnd, mSelectionStart - mSelectionEnd);
-
- if (case_insensitive)
- {
- LLWStringUtil::toLower(selected_text);
- LLWStringUtil::toLower(search_text);
- }
-
- if (selected_text == search_text)
- {
- insertText(replace_text);
- replaced = true;
- }
- }
-
- selectNext(search_text_in, case_insensitive, wrap);
- return replaced;
-}
-
-void LLTextEditor::replaceTextAll(const std::string& search_text, const std::string& replace_text, bool case_insensitive)
-{
- startOfDoc();
- selectNext(search_text, case_insensitive, false);
-
- bool replaced = true;
- while ( replaced )
- {
- replaced = replaceText(search_text,replace_text, case_insensitive, false);
- }
-}
-
-S32 LLTextEditor::prevWordPos(S32 cursorPos) const
-{
- LLWString wtext(getWText());
- while( (cursorPos > 0) && (wtext[cursorPos-1] == ' ') )
- {
- cursorPos--;
- }
- while( (cursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[cursorPos-1] ) )
- {
- cursorPos--;
- }
- return cursorPos;
-}
-
-S32 LLTextEditor::nextWordPos(S32 cursorPos) const
-{
- LLWString wtext(getWText());
- while( (cursorPos < getLength()) && LLWStringUtil::isPartOfWord( wtext[cursorPos] ) )
- {
- cursorPos++;
- }
- while( (cursorPos < getLength()) && (wtext[cursorPos] == ' ') )
- {
- cursorPos++;
- }
- return cursorPos;
-}
-
-const LLTextSegmentPtr LLTextEditor::getPreviousSegment() const
-{
- static LLPointer<LLIndexSegment> index_segment = new LLIndexSegment;
-
- index_segment->setStart(mCursorPos);
- index_segment->setEnd(mCursorPos);
-
- // find segment index at character to left of cursor (or rightmost edge of selection)
- segment_set_t::const_iterator it = mSegments.lower_bound(index_segment);
-
- if (it != mSegments.end())
- {
- return *it;
- }
- else
- {
- return LLTextSegmentPtr();
- }
-}
-
-void LLTextEditor::getSelectedSegments(LLTextEditor::segment_vec_t& segments) const
-{
- S32 left = hasSelection() ? llmin(mSelectionStart, mSelectionEnd) : mCursorPos;
- S32 right = hasSelection() ? llmax(mSelectionStart, mSelectionEnd) : mCursorPos;
-
- return getSegmentsInRange(segments, left, right, true);
-}
-
-void LLTextEditor::getSegmentsInRange(LLTextEditor::segment_vec_t& segments_out, S32 start, S32 end, bool include_partial) const
-{
- segment_set_t::const_iterator first_it = getSegIterContaining(start);
- segment_set_t::const_iterator end_it = getSegIterContaining(end - 1);
- if (end_it != mSegments.end()) ++end_it;
-
- for (segment_set_t::const_iterator it = first_it; it != end_it; ++it)
- {
- LLTextSegmentPtr segment = *it;
- if (include_partial
- || (segment->getStart() >= start
- && segment->getEnd() <= end))
- {
- segments_out.push_back(segment);
- }
- }
-}
-
-void LLTextEditor::setShowEmojiHelper(bool show)
-{
- if (!mShowEmojiHelper)
- {
- LLEmojiHelper::instance().hideHelper(this);
- }
-
- mShowEmojiHelper = show;
-}
-
-bool LLTextEditor::selectionContainsLineBreaks()
-{
- if (hasSelection())
- {
- S32 left = llmin(mSelectionStart, mSelectionEnd);
- S32 right = left + llabs(mSelectionStart - mSelectionEnd);
-
- LLWString wtext = getWText();
- for( S32 i = left; i < right; i++ )
- {
- if (wtext[i] == '\n')
- {
- return true;
- }
- }
- }
- return false;
-}
-
-
-S32 LLTextEditor::indentLine( S32 pos, S32 spaces )
-{
- // Assumes that pos is at the start of the line
- // spaces may be positive (indent) or negative (unindent).
- // Returns the actual number of characters added or removed.
-
- llassert(pos >= 0);
- llassert(pos <= getLength() );
-
- S32 delta_spaces = 0;
-
- if (spaces >= 0)
- {
- // Indent
- for(S32 i=0; i < spaces; i++)
- {
- delta_spaces += addChar(pos, ' ');
- }
- }
- else
- {
- // Unindent
- for(S32 i=0; i < -spaces; i++)
- {
- LLWString wtext = getWText();
- if (wtext[pos] == ' ')
- {
- delta_spaces += remove( pos, 1, false );
- }
- }
- }
-
- return delta_spaces;
-}
-
-void LLTextEditor::indentSelectedLines( S32 spaces )
-{
- if( hasSelection() )
- {
- LLWString text = getWText();
- S32 left = llmin( mSelectionStart, mSelectionEnd );
- S32 right = left + llabs( mSelectionStart - mSelectionEnd );
- bool cursor_on_right = (mSelectionEnd > mSelectionStart);
- S32 cur = left;
-
- // Expand left to start of line
- while( (cur > 0) && (text[cur] != '\n') )
- {
- cur--;
- }
- left = cur;
- if( cur > 0 )
- {
- left++;
- }
-
- // Expand right to end of line
- if( text[right - 1] == '\n' )
- {
- right--;
- }
- else
- {
- while( (text[right] != '\n') && (right <= getLength() ) )
- {
- right++;
- }
- }
-
- // 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
- {
- if( text[cur] == '\n' )
- {
- cur++;
- }
-
- S32 delta_spaces = indentLine( cur, spaces );
- if( delta_spaces > 0 )
- {
- cur += delta_spaces;
- }
- right += delta_spaces;
-
- text = getWText();
-
- // Find the next new line
- while( (cur < right) && (text[cur] != '\n') )
- {
- cur++;
- }
- }
- while( cur < right );
-
- mParseOnTheFly = true;
-
- if( (right < getLength()) && (text[right] == '\n') )
- {
- right++;
- }
-
- // Set the selection and cursor
- if( cursor_on_right )
- {
- mSelectionStart = left;
- mSelectionEnd = right;
- }
- else
- {
- mSelectionStart = right;
- mSelectionEnd = left;
- }
- setCursorPos(mSelectionEnd);
- }
-}
-
-//virtual
-bool LLTextEditor::canSelectAll() const
-{
- return true;
-}
-
-// virtual
-void LLTextEditor::selectAll()
-{
- mSelectionStart = getLength();
- mSelectionEnd = 0;
- setCursorPos(mSelectionEnd);
- updatePrimary();
-}
-
-void LLTextEditor::selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_pos)
-{
- setCursorPos(prev_cursor_pos);
- startSelection();
- setCursorPos(next_cursor_pos);
- endSelection();
-}
-
-void LLTextEditor::insertEmoji(llwchar emoji)
-{
- LL_INFOS() << "LLTextEditor::insertEmoji(" << wchar_utf8_preview(emoji) << ")" << LL_ENDL;
- auto styleParams = LLStyle::Params();
- styleParams.font = LLFontGL::getFontEmoji();
- auto segment = new LLEmojiTextSegment(new LLStyle(styleParams), mCursorPos, mCursorPos + 1, *this);
- insert(mCursorPos, LLWString(1, emoji), false, segment);
- setCursorPos(mCursorPos + 1);
-}
-
-void LLTextEditor::handleEmojiCommit(llwchar emoji)
-{
- S32 shortCodePos;
- if (LLEmojiHelper::isCursorInEmojiCode(getWText(), mCursorPos, &shortCodePos))
- {
- remove(shortCodePos, mCursorPos - shortCodePos, true);
- setCursorPos(shortCodePos);
-
- insertEmoji(emoji);
- }
-}
-
-bool LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
-
- // set focus first, in case click callbacks want to change it
- // RN: do we really need to have a tab stop?
- if (hasTabStop())
- {
- setFocus( true );
- }
-
- // Let scrollbar have first dibs
- handled = LLTextBase::handleMouseDown(x, y, mask);
-
- if( !handled )
- {
- if (!(mask & MASK_SHIFT))
- {
- deselect();
- }
-
- bool start_select = true;
- if( start_select )
- {
- // If we're not scrolling (handled by child), then we're selecting
- if (mask & MASK_SHIFT)
- {
- S32 old_cursor_pos = mCursorPos;
- setCursorAtLocalPos( x, y, true );
-
- if (hasSelection())
- {
- mSelectionEnd = mCursorPos;
- }
- else
- {
- mSelectionStart = old_cursor_pos;
- mSelectionEnd = mCursorPos;
- }
- // assume we're starting a drag select
- mIsSelecting = true;
- }
- else
- {
- setCursorAtLocalPos( x, y, true );
- startSelection();
- }
- }
-
- handled = true;
- }
-
- // Delay cursor flashing
- resetCursorBlink();
-
- if (handled && !gFocusMgr.getMouseCapture())
- {
- gFocusMgr.setMouseCapture( this );
- }
- return handled;
-}
-
-bool LLTextEditor::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- if (hasTabStop())
- {
- setFocus(true);
- }
-
- bool show_menu = false;
-
- // Prefer editor menu if it has selection. See EXT-6806.
- if (hasSelection())
- {
- S32 click_pos = getDocIndexFromLocalCoord(x, y, false);
- if (click_pos > mSelectionStart && click_pos < mSelectionEnd)
- {
- show_menu = true;
- }
- }
-
- // Let segments handle the click, if nothing does, show editor menu
- if (!show_menu && !LLTextBase::handleRightMouseDown(x, y, mask))
- {
- show_menu = true;
- }
-
- if (show_menu && getShowContextMenu())
- {
- showContextMenu(x, y);
- }
-
- return true;
-}
-
-
-
-bool LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
-{
- if (hasTabStop())
- {
- setFocus(true);
- }
-
- if (!LLTextBase::handleMouseDown(x, y, mask))
- {
- if( canPastePrimary() )
- {
- setCursorAtLocalPos( x, y, true );
- // does not rely on focus being set
- pastePrimary();
- }
- }
- return true;
-}
-
-
-bool LLTextEditor::handleHover(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
-
- if(hasMouseCapture() )
- {
- if( mIsSelecting )
- {
- if(mScroller)
- {
- mScroller->autoScroll(x, y);
- }
- S32 clamped_x = llclamp(x, mVisibleTextRect.mLeft, mVisibleTextRect.mRight);
- S32 clamped_y = llclamp(y, mVisibleTextRect.mBottom, mVisibleTextRect.mTop);
- setCursorAtLocalPos( clamped_x, clamped_y, true );
- mSelectionEnd = mCursorPos;
- }
- LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL;
- getWindow()->setCursor(UI_CURSOR_IBEAM);
- handled = true;
- }
-
- if( !handled )
- {
- // Pass to children
- handled = LLTextBase::handleHover(x, y, mask);
- }
-
- if( handled )
- {
- // Delay cursor flashing
- resetCursorBlink();
- }
-
- if( !handled )
- {
- getWindow()->setCursor(UI_CURSOR_IBEAM);
- handled = true;
- }
-
- return handled;
-}
-
-
-bool LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
-
- // if I'm not currently selecting text
- if (!(mIsSelecting && hasMouseCapture()))
- {
- // let text segments handle mouse event
- handled = LLTextBase::handleMouseUp(x, y, mask);
- }
-
- if( !handled )
- {
- if( mIsSelecting )
- {
- if(mScroller)
- {
- mScroller->autoScroll(x, y);
- }
- S32 clamped_x = llclamp(x, mVisibleTextRect.mLeft, mVisibleTextRect.mRight);
- S32 clamped_y = llclamp(y, mVisibleTextRect.mBottom, mVisibleTextRect.mTop);
- setCursorAtLocalPos( clamped_x, clamped_y, true );
- endSelection();
- }
-
- // take selection to 'primary' clipboard
- updatePrimary();
-
- handled = true;
- }
-
- // Delay cursor flashing
- resetCursorBlink();
-
- if( hasMouseCapture() )
- {
- gFocusMgr.setMouseCapture( NULL );
-
- handled = true;
- }
-
- return handled;
-}
-
-
-bool LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
-
- // let scrollbar and text segments have first dibs
- handled = LLTextBase::handleDoubleClick(x, y, mask);
-
- if( !handled )
- {
- setCursorAtLocalPos( x, y, false );
- deselect();
-
- LLWString text = getWText();
-
- if( LLWStringUtil::isPartOfWord( text[mCursorPos] ) )
- {
- // Select word the cursor is over
- while ((mCursorPos > 0) && LLWStringUtil::isPartOfWord(text[mCursorPos-1]))
- {
- if (!setCursorPos(mCursorPos - 1)) break;
- }
- startSelection();
-
- while ((mCursorPos < (S32)text.length()) && LLWStringUtil::isPartOfWord( text[mCursorPos] ) )
- {
- if (!setCursorPos(mCursorPos + 1)) break;
- }
-
- mSelectionEnd = mCursorPos;
- }
- else if ((mCursorPos < (S32)text.length()) && !iswspace( text[mCursorPos]) )
- {
- // Select the character the cursor is over
- startSelection();
- setCursorPos(mCursorPos + 1);
- mSelectionEnd = mCursorPos;
- }
-
- // We don't want handleMouseUp() to "finish" the selection (and thereby
- // set mSelectionEnd to where the mouse is), so we finish the selection here.
- mIsSelecting = false;
-
- // delay cursor flashing
- resetCursorBlink();
-
- // take selection to 'primary' clipboard
- updatePrimary();
-
- handled = true;
- }
-
- return handled;
-}
-
-
-//----------------------------------------------------------------------------
-// Returns change in number of characters in mText
-
-S32 LLTextEditor::execute( TextCmd* cmd )
-{
- if (!mReadOnly && mShowEmojiHelper)
- {
- // Any change to our contents should always hide the helper
- LLEmojiHelper::instance().hideHelper(this);
- }
-
- S32 delta = 0;
- if( cmd->execute(this, &delta) )
- {
- // Delete top of undo stack
- undo_stack_t::iterator enditer = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd);
- std::for_each(mUndoStack.begin(), enditer, DeletePointer());
- mUndoStack.erase(mUndoStack.begin(), enditer);
- // Push the new command is now on the top (front) of the undo stack.
- mUndoStack.push_front(cmd);
- mLastCmd = cmd;
-
- bool need_to_rollback = mPrevalidateFunc
- && !mPrevalidateFunc(getViewModel()->getDisplay());
- if (need_to_rollback)
- {
- // get rid of this last command and clean up undo stack
- undo();
-
- // remove any evidence of this command from redo history
- mUndoStack.pop_front();
- delete cmd;
-
- // failure, nothing changed
- delta = 0;
- }
- }
- else
- {
- // Operation failed, so don't put it on the undo stack.
- delete cmd;
- }
-
- return delta;
-}
-
-S32 LLTextEditor::insert(S32 pos, const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment)
-{
- return execute( new TextCmdInsert( pos, group_with_next_op, wstr, segment ) );
-}
-
-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 removedChar;
-}
-
-S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc)
-{
- if ((S32)getLength() == pos)
- {
- return addChar(pos, wc);
- }
- else
- {
- return execute(new TextCmdOverwriteChar(pos, false, wc));
- }
-}
-
-// Remove a single character from the text. Tries to remove
-// a pseudo-tab (up to for spaces in a row)
-void LLTextEditor::removeCharOrTab()
-{
- if (!getEnabled())
- {
- return;
- }
-
- if (mCursorPos > 0)
- {
- S32 chars_to_remove = 1;
-
- LLWString text = getWText();
- if (text[mCursorPos - 1] == ' ')
- {
- // Try to remove a "tab"
- S32 offset = getLineOffsetFromDocIndex(mCursorPos);
- if (offset > 0)
- {
- chars_to_remove = offset % SPACES_PER_TAB;
- if (chars_to_remove == 0)
- {
- chars_to_remove = SPACES_PER_TAB;
- }
-
- for (S32 i = 0; i < chars_to_remove; i++)
- {
- if (text[mCursorPos - i - 1] != ' ')
- {
- // Fewer than a full tab's worth of spaces, so
- // just delete a single character.
- chars_to_remove = 1;
- break;
- }
- }
- }
- }
-
- for (S32 i = 0; i < chars_to_remove; i++)
- {
- setCursorPos(mCursorPos - 1);
- remove(mCursorPos, 1, false);
- }
-
- tryToShowEmojiHelper();
- }
- else
- {
- LLUI::getInstance()->reportBadKeystroke();
- }
-}
-
-// Remove a single character from the text
-S32 LLTextEditor::removeChar(S32 pos)
-{
- return remove(pos, 1, false);
-}
-
-void LLTextEditor::removeChar()
-{
- if (!getEnabled())
- {
- return;
- }
-
- if (mCursorPos > 0)
- {
- setCursorPos(mCursorPos - 1);
- removeChar(mCursorPos);
- tryToShowEmojiHelper();
- }
- else
- {
- LLUI::getInstance()->reportBadKeystroke();
- }
-}
-
-// Add a single character to the text
-S32 LLTextEditor::addChar(S32 pos, llwchar wc)
-{
- if ( (wstring_utf8_length( getWText() ) + wchar_utf8_length( wc )) > mMaxTextByteLength)
- {
- make_ui_sound("UISndBadKeystroke");
- return 0;
- }
-
- if (mLastCmd && mLastCmd->canExtend(pos))
- {
- S32 delta = 0;
- if (mPrevalidateFunc)
- {
- // get a copy of current text contents
- LLWString test_string(getViewModel()->getDisplay());
-
- // modify text contents as if this addChar succeeded
- llassert(pos <= (S32)test_string.size());
- test_string.insert(pos, 1, wc);
- if (!mPrevalidateFunc( test_string))
- {
- return 0;
- }
- }
- mLastCmd->extendAndExecute(this, pos, wc, &delta);
-
- return delta;
- }
- else
- {
- return execute(new TextCmdAddChar(pos, false, wc, LLTextSegmentPtr()));
- }
-}
-
-void LLTextEditor::addChar(llwchar wc)
-{
- if( !getEnabled() )
- {
- return;
- }
- if( hasSelection() )
- {
- deleteSelection(true);
- }
- else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
- {
- removeChar(mCursorPos);
- }
-
- setCursorPos(mCursorPos + addChar( mCursorPos, wc ));
- tryToShowEmojiHelper();
-
- 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::showEmojiHelper()
-{
- if (mReadOnly || !mShowEmojiHelper)
- return;
-
- const LLRect cursorRect(getLocalRectFromDocIndex(mCursorPos));
- auto cb = [this](llwchar emoji) { insertEmoji(emoji); };
- LLEmojiHelper::instance().showHelper(this, cursorRect.mLeft, cursorRect.mTop, LLStringUtil::null, cb);
-}
-
-void LLTextEditor::tryToShowEmojiHelper()
-{
- if (mReadOnly || !mShowEmojiHelper)
- return;
-
- S32 shortCodePos;
- LLWString wtext(getWText());
- if (LLEmojiHelper::isCursorInEmojiCode(wtext, mCursorPos, &shortCodePos))
- {
- const LLRect cursorRect(getLocalRectFromDocIndex(shortCodePos));
- const LLWString wpart(wtext.substr(shortCodePos, mCursorPos - shortCodePos));
- const std::string part(wstring_to_utf8str(wpart));
- auto cb = [this](llwchar emoji) { handleEmojiCommit(emoji); };
- LLEmojiHelper::instance().showHelper(this, cursorRect.mLeft, cursorRect.mTop, part, cb);
- }
- else
- {
- LLEmojiHelper::instance().hideHelper();
- }
-}
-
-void LLTextEditor::addLineBreakChar(bool group_together)
-{
- if( !getEnabled() )
- {
- return;
- }
- if( hasSelection() )
- {
- deleteSelection(true);
- }
- else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
- {
- removeChar(mCursorPos);
- }
-
- LLStyleConstSP sp(new LLStyle(LLStyle::Params()));
- LLTextSegmentPtr segment = new LLLineBreakTextSegment(sp, mCursorPos);
-
- S32 pos = execute(new TextCmdAddChar(mCursorPos, group_together, '\n', segment));
-
- setCursorPos(mCursorPos + pos);
-}
-
-
-bool LLTextEditor::handleSelectionKey(const KEY key, const MASK mask)
-{
- bool handled = false;
-
- if( mask & MASK_SHIFT )
- {
- handled = true;
-
- switch( key )
- {
- case KEY_LEFT:
- if( 0 < mCursorPos )
- {
- startSelection();
- setCursorPos(mCursorPos - 1);
- if( mask & MASK_CONTROL )
- {
- setCursorPos(prevWordPos(mCursorPos));
- }
- mSelectionEnd = mCursorPos;
- }
- break;
-
- case KEY_RIGHT:
- if( mCursorPos < getLength() )
- {
- startSelection();
- setCursorPos(mCursorPos + 1);
- if( mask & MASK_CONTROL )
- {
- setCursorPos(nextWordPos(mCursorPos));
- }
- mSelectionEnd = mCursorPos;
- }
- break;
-
- case KEY_UP:
- startSelection();
- changeLine( -1 );
- mSelectionEnd = mCursorPos;
- break;
-
- case KEY_PAGE_UP:
- startSelection();
- changePage( -1 );
- mSelectionEnd = mCursorPos;
- break;
-
- case KEY_HOME:
- startSelection();
- if( mask & MASK_CONTROL )
- {
- setCursorPos(0);
- }
- else
- {
- startOfLine();
- }
- mSelectionEnd = mCursorPos;
- break;
-
- case KEY_DOWN:
- startSelection();
- changeLine( 1 );
- mSelectionEnd = mCursorPos;
- break;
-
- case KEY_PAGE_DOWN:
- startSelection();
- changePage( 1 );
- mSelectionEnd = mCursorPos;
- break;
-
- case KEY_END:
- startSelection();
- if( mask & MASK_CONTROL )
- {
- setCursorPos(getLength());
- }
- else
- {
- endOfLine();
- }
- mSelectionEnd = mCursorPos;
- break;
-
- default:
- handled = false;
- break;
- }
- }
-
- if( handled )
- {
- // take selection to 'primary' clipboard
- updatePrimary();
- }
-
- return handled;
-}
-
-bool LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
-{
- bool handled = false;
-
- // Ignore capslock key
- if( MASK_NONE == mask )
- {
- handled = true;
- switch( key )
- {
- case KEY_UP:
- changeLine( -1 );
- break;
-
- case KEY_PAGE_UP:
- changePage( -1 );
- break;
-
- case KEY_HOME:
- startOfLine();
- break;
-
- case KEY_DOWN:
- changeLine( 1 );
- deselect();
- break;
-
- case KEY_PAGE_DOWN:
- changePage( 1 );
- break;
-
- case KEY_END:
- endOfLine();
- break;
-
- case KEY_LEFT:
- if( hasSelection() )
- {
- setCursorPos(llmin( mSelectionStart, mSelectionEnd ));
- }
- else
- {
- if( 0 < mCursorPos )
- {
- setCursorPos(mCursorPos - 1);
- }
- else
- {
- LLUI::getInstance()->reportBadKeystroke();
- }
- }
- break;
-
- case KEY_RIGHT:
- if( hasSelection() )
- {
- setCursorPos(llmax( mSelectionStart, mSelectionEnd ));
- }
- else
- {
- if( mCursorPos < getLength() )
- {
- setCursorPos(mCursorPos + 1);
- }
- else
- {
- LLUI::getInstance()->reportBadKeystroke();
- }
- }
- break;
-
- default:
- handled = false;
- break;
- }
- }
-
- if (handled)
- {
- deselect();
- }
-
- return handled;
-}
-
-void LLTextEditor::deleteSelection(bool group_with_next_op )
-{
- if( getEnabled() && hasSelection() )
- {
- S32 pos = llmin( mSelectionStart, mSelectionEnd );
- S32 length = llabs( mSelectionStart - mSelectionEnd );
-
- remove( pos, length, group_with_next_op );
-
- deselect();
- setCursorPos(pos);
- }
-}
-
-// virtual
-bool LLTextEditor::canCut() const
-{
- return !mReadOnly && hasSelection();
-}
-
-// cut selection to clipboard
-void LLTextEditor::cut()
-{
- if( !canCut() )
- {
- return;
- }
- S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
- S32 length = llabs( mSelectionStart - mSelectionEnd );
- LLClipboard::instance().copyToClipboard( getWText(), left_pos, length);
- deleteSelection( false );
-
- onKeyStroke();
-}
-
-bool LLTextEditor::canCopy() const
-{
- return hasSelection();
-}
-
-// copy selection to clipboard
-void LLTextEditor::copy()
-{
- if( !canCopy() )
- {
- return;
- }
- S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
- S32 length = llabs( mSelectionStart - mSelectionEnd );
- LLClipboard::instance().copyToClipboard(getWText(), left_pos, length);
-}
-
-bool LLTextEditor::canPaste() const
-{
- return !mReadOnly && LLClipboard::instance().isTextAvailable();
-}
-
-// paste from clipboard
-void LLTextEditor::paste()
-{
- bool is_primary = false;
- pasteHelper(is_primary);
-}
-
-// paste from primary
-void LLTextEditor::pastePrimary()
-{
- bool is_primary = true;
- pasteHelper(is_primary);
-}
-
-// paste from primary (itsprimary==true) or clipboard (itsprimary==false)
-void LLTextEditor::pasteHelper(bool is_primary)
-{
- mParseOnTheFly = false;
- bool can_paste_it;
- if (is_primary)
- {
- can_paste_it = canPastePrimary();
- }
- else
- {
- can_paste_it = canPaste();
- }
-
- if (!can_paste_it)
- {
- return;
- }
-
- LLWString paste;
- LLClipboard::instance().pasteFromClipboard(paste, is_primary);
-
- if (paste.empty())
- {
- return;
- }
-
- // Delete any selected characters (the paste replaces them)
- if( (!is_primary) && hasSelection() )
- {
- deleteSelection(true);
- }
-
- // 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)
-{
- std::string clean_string_utf = wstring_to_utf8str(clean_string);
- std::replace( clean_string_utf.begin(), clean_string_utf.end(), '\r', '\n');
- clean_string = utf8str_to_wstring(clean_string_utf);
-
- LLWStringUtil::replaceTabsWithSpaces(clean_string, SPACES_PER_TAB);
- if( mAllowEmbeddedItems )
- {
- const llwchar LF = 10;
- S32 len = clean_string.length();
- for( S32 i = 0; i < len; i++ )
- {
- llwchar wc = clean_string[i];
- if( (wc < LLFontFreetype::FIRST_CHAR) && (wc != LF) )
- {
- clean_string[i] = LL_UNKNOWN_CHAR;
- }
- else if (wc >= FIRST_EMBEDDED_CHAR && wc <= LAST_EMBEDDED_CHAR)
- {
- clean_string[i] = pasteEmbeddedItem(wc);
- }
- }
- }
-}
-
-
-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) && (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, true, LLTextSegmentPtr()));
- }
- addLineBreakChar(true); // Add a line break and group with the next addition.
-
- start = pos+1;
- pos = clean_string.find('\n',start);
- }
-
- 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()
-{
- if( !canCopy() )
- {
- return;
- }
- S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
- S32 length = llabs( mSelectionStart - mSelectionEnd );
- LLClipboard::instance().copyToClipboard(getWText(), left_pos, length, true);
-}
-
-bool LLTextEditor::canPastePrimary() const
-{
- return !mReadOnly && LLClipboard::instance().isTextAvailable(true);
-}
-
-void LLTextEditor::updatePrimary()
-{
- if (canCopy())
- {
- copyPrimary();
- }
-}
-
-bool LLTextEditor::handleControlKey(const KEY key, const MASK mask)
-{
- bool handled = false;
-
- if( mask & MASK_CONTROL )
- {
- handled = true;
-
- switch( key )
- {
- case KEY_HOME:
- if( mask & MASK_SHIFT )
- {
- startSelection();
- setCursorPos(0);
- mSelectionEnd = mCursorPos;
- }
- else
- {
- // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down
- // all move the cursor as if clicking, so should deselect.
- deselect();
- startOfDoc();
- }
- break;
-
- case KEY_END:
- {
- if( mask & MASK_SHIFT )
- {
- startSelection();
- }
- else
- {
- // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down
- // all move the cursor as if clicking, so should deselect.
- deselect();
- }
- endOfDoc();
- if( mask & MASK_SHIFT )
- {
- mSelectionEnd = mCursorPos;
- }
- break;
- }
-
- case KEY_RIGHT:
- if( mCursorPos < getLength() )
- {
- // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down
- // all move the cursor as if clicking, so should deselect.
- deselect();
-
- setCursorPos(nextWordPos(mCursorPos + 1));
- }
- break;
-
-
- case KEY_LEFT:
- if( mCursorPos > 0 )
- {
- // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down
- // all move the cursor as if clicking, so should deselect.
- deselect();
-
- setCursorPos(prevWordPos(mCursorPos - 1));
- }
- break;
-
- default:
- handled = false;
- break;
- }
- }
-
- if (handled && !gFocusMgr.getMouseCapture())
- {
- updatePrimary();
- }
-
- return handled;
-}
-
-
-bool LLTextEditor::handleSpecialKey(const KEY key, const MASK mask)
- {
- bool handled = true;
-
- if (mReadOnly) return false;
-
- switch( key )
- {
- case KEY_INSERT:
- if (mask == MASK_NONE)
- {
- gKeyboard->toggleInsertMode();
- }
- break;
-
- case KEY_BACKSPACE:
- if( hasSelection() )
- {
- deleteSelection(false);
- }
- else
- if( 0 < mCursorPos )
- {
- removeCharOrTab();
- }
- else
- {
- LLUI::getInstance()->reportBadKeystroke();
- }
- break;
-
-
- case KEY_RETURN:
- if (mask == MASK_NONE)
- {
- if( hasSelection() && !mKeepSelectionOnReturn )
- {
- deleteSelection(false);
- }
- if (mAutoIndent)
- {
- autoIndent();
- }
- }
- else
- {
- handled = false;
- break;
- }
- break;
-
- case KEY_TAB:
- if (mask & MASK_CONTROL)
- {
- handled = false;
- break;
- }
- if( hasSelection() && selectionContainsLineBreaks() )
- {
- indentSelectedLines( (mask & MASK_SHIFT) ? -SPACES_PER_TAB : SPACES_PER_TAB );
- }
- else
- {
- if( hasSelection() )
- {
- deleteSelection(false);
- }
-
- S32 offset = getLineOffsetFromDocIndex(mCursorPos);
-
- S32 spaces_needed = SPACES_PER_TAB - (offset % SPACES_PER_TAB);
- for( S32 i=0; i < spaces_needed; i++ )
- {
- addChar( ' ' );
- }
- }
- break;
-
- default:
- handled = false;
- break;
- }
-
- if (handled)
- {
- onKeyStroke();
- }
- return handled;
-}
-
-
-void LLTextEditor::unindentLineBeforeCloseBrace()
-{
- if( mCursorPos >= 1 )
- {
- LLWString text = getWText();
- if( ' ' == text[ mCursorPos - 1 ] )
- {
- S32 line = getLineNumFromDocIndex(mCursorPos, false);
- S32 line_start = getLineStart(line);
-
- // Jump over spaces in the current line
- while ((' ' == text[line_start]) && (line_start < mCursorPos))
- {
- line_start++;
- }
-
- // Make sure there is nothing but ' ' before the Brace we are unindenting
- if (line_start == mCursorPos)
- {
- removeCharOrTab();
- }
- }
- }
-}
-
-
-bool LLTextEditor::handleKeyHere(KEY key, MASK mask )
-{
- bool handled = false;
-
- // Special case for TAB. If want to move to next field, report
- // not handled and let the parent take care of field movement.
- if (KEY_TAB == key && mTabsToNextField)
- {
- return false;
- }
-
- if (mReadOnly && mScroller)
- {
- handled = (mScroller && mScroller->handleKeyHere( key, mask ))
- || handleSelectionKey(key, mask)
- || handleControlKey(key, mask);
- }
- else
- {
- if (!mReadOnly && mShowEmojiHelper && LLEmojiHelper::instance().handleKey(this, key, mask))
- {
- return true;
- }
-
- if (mEnableTooltipPaste &&
- LLToolTipMgr::instance().toolTipVisible() &&
- LLToolTipMgr::instance().isTooltipPastable() &&
- 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);
- }
- }
-
- if( handled )
- {
- resetCursorBlink();
- needsScroll();
-
- if (mShowEmojiHelper)
- {
- // Dismiss the helper whenever we handled a key that it didn't
- LLEmojiHelper::instance().hideHelper(this);
- }
- }
-
- return handled;
-}
-
-
-bool LLTextEditor::handleUnicodeCharHere(llwchar uni_char)
-{
- if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL
- {
- return false;
- }
-
- bool handled = false;
-
- // Handle most keys only if the text editor is writeable.
- if( !mReadOnly )
- {
- if (mShowEmojiHelper && uni_char < 0x80 && LLEmojiHelper::instance().handleKey(this, (KEY)uni_char, MASK_NONE))
- {
- return true;
- }
-
- if( mAutoIndent && '}' == uni_char )
- {
- unindentLineBeforeCloseBrace();
- }
-
- // TODO: KLW Add auto show of tool tip on (
- addChar( uni_char );
-
- // Keys that add characters temporarily hide the cursor
- getWindow()->hideCursorUntilMouseMove();
-
- handled = true;
- }
-
- if( handled )
- {
- resetCursorBlink();
-
- // Most keystrokes will make the selection box go away, but not all will.
- deselect();
-
- onKeyStroke();
- }
-
- return handled;
-}
-
-
-// virtual
-bool LLTextEditor::canDoDelete() const
-{
- return !mReadOnly && ( !mPassDelete || ( hasSelection() || (mCursorPos < getLength())) );
-}
-
-void LLTextEditor::doDelete()
-{
- if( !canDoDelete() )
- {
- return;
- }
- if( hasSelection() )
- {
- deleteSelection(false);
- }
- else
- if( mCursorPos < getLength() )
- {
- S32 i;
- S32 chars_to_remove = 1;
- LLWString text = getWText();
- if( (text[ mCursorPos ] == ' ') && (mCursorPos + SPACES_PER_TAB < getLength()) )
- {
- // Try to remove a full tab's worth of spaces
- S32 offset = getLineOffsetFromDocIndex(mCursorPos);
- chars_to_remove = SPACES_PER_TAB - (offset % SPACES_PER_TAB);
- if( chars_to_remove == 0 )
- {
- chars_to_remove = SPACES_PER_TAB;
- }
-
- for( i = 0; i < chars_to_remove; i++ )
- {
- if( text[mCursorPos + i] != ' ' )
- {
- chars_to_remove = 1;
- break;
- }
- }
- }
-
- for( i = 0; i < chars_to_remove; i++ )
- {
- setCursorPos(mCursorPos + 1);
- removeChar();
- }
-
- }
-
- onKeyStroke();
-}
-
-//----------------------------------------------------------------------------
-
-
-void LLTextEditor::blockUndo()
-{
- mBaseDocIsPristine = false;
- mLastCmd = NULL;
- std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer());
- mUndoStack.clear();
-}
-
-// virtual
-bool LLTextEditor::canUndo() const
-{
- return !mReadOnly && mLastCmd != NULL;
-}
-
-void LLTextEditor::undo()
-{
- if( !canUndo() )
- {
- return;
- }
- deselect();
- S32 pos = 0;
- do
- {
- pos = mLastCmd->undo(this);
- undo_stack_t::iterator iter = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd);
- if (iter != mUndoStack.end())
- ++iter;
- if (iter != mUndoStack.end())
- mLastCmd = *iter;
- else
- mLastCmd = NULL;
-
- } while( mLastCmd && mLastCmd->groupWithNext() );
-
- setCursorPos(pos);
-
- onKeyStroke();
-}
-
-bool LLTextEditor::canRedo() const
-{
- return !mReadOnly && (mUndoStack.size() > 0) && (mLastCmd != mUndoStack.front());
-}
-
-void LLTextEditor::redo()
-{
- if( !canRedo() )
- {
- return;
- }
- deselect();
- S32 pos = 0;
- do
- {
- if( !mLastCmd )
- {
- mLastCmd = mUndoStack.back();
- }
- else
- {
- undo_stack_t::iterator iter = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd);
- if (iter != mUndoStack.begin())
- mLastCmd = *(--iter);
- else
- mLastCmd = NULL;
- }
-
- if( mLastCmd )
- {
- pos = mLastCmd->redo(this);
- }
- } while(
- mLastCmd &&
- mLastCmd->groupWithNext() &&
- (mLastCmd != mUndoStack.front()) );
-
- setCursorPos(pos);
-
- onKeyStroke();
-}
-
-void LLTextEditor::onFocusReceived()
-{
- LLTextBase::onFocusReceived();
- updateAllowingLanguageInput();
-}
-
-void LLTextEditor::focusLostHelper()
-{
- updateAllowingLanguageInput();
-
- // Route menu back to the default
- if( gEditMenuHandler == this )
- {
- gEditMenuHandler = NULL;
- }
-
- if (mCommitOnFocusLost)
- {
- onCommit();
- }
-
- // Make sure cursor is shown again
- getWindow()->showCursorFromMouseMove();
-}
-
-void LLTextEditor::onFocusLost()
-{
- focusLostHelper();
- LLTextBase::onFocusLost();
-}
-
-void LLTextEditor::onCommit()
-{
- setControlValue(getValue());
- LLTextBase::onCommit();
-}
-
-void LLTextEditor::setEnabled(bool enabled)
-{
- // just treat enabled as read-only flag
- bool read_only = !enabled;
- if (read_only != mReadOnly)
- {
- //mReadOnly = read_only;
- LLTextBase::setReadOnly(read_only);
- updateSegments();
- updateAllowingLanguageInput();
- }
-}
-
-void LLTextEditor::showContextMenu(S32 x, S32 y)
-{
- LLContextMenu* menu = static_cast<LLContextMenu*>(mContextMenuHandle.get());
- if (!menu)
- {
- llassert(LLMenuGL::sMenuContainer != NULL);
- menu = LLUICtrlFactory::createFromFile<LLContextMenu>("menu_text_editor.xml",
- LLMenuGL::sMenuContainer,
- LLMenuHolderGL::child_registry_t::instance());
- if(!menu)
- {
- LL_WARNS() << "Failed to create menu for LLTextEditor: " << getName() << LL_ENDL;
- return;
- }
- mContextMenuHandle = menu->getHandle();
- }
-
- // Route menu to this class
- // previously this was done in ::handleRightMoseDown:
- //if(hasTabStop())
- // setFocus(true) - why? weird...
- // and then inside setFocus
- // ....
- // gEditMenuHandler = this;
- // ....
- // but this didn't work in all cases and just weird...
- //why not here?
- // (all this was done for EXT-4443)
-
- gEditMenuHandler = this;
-
- S32 screen_x, screen_y;
- localPointToScreen(x, y, &screen_x, &screen_y);
-
- 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()))
- {
- 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);
-}
-
-
-void LLTextEditor::drawPreeditMarker()
-{
- static LLUICachedControl<F32> preedit_marker_brightness ("UIPreeditMarkerBrightness", 0);
- static LLUICachedControl<S32> preedit_marker_gap ("UIPreeditMarkerGap", 0);
- static LLUICachedControl<S32> preedit_marker_position ("UIPreeditMarkerPosition", 0);
- static LLUICachedControl<S32> preedit_marker_thickness ("UIPreeditMarkerThickness", 0);
- static LLUICachedControl<F32> preedit_standout_brightness ("UIPreeditStandoutBrightness", 0);
- static LLUICachedControl<S32> preedit_standout_gap ("UIPreeditStandoutGap", 0);
- static LLUICachedControl<S32> preedit_standout_position ("UIPreeditStandoutPosition", 0);
- static LLUICachedControl<S32> preedit_standout_thickness ("UIPreeditStandoutThickness", 0);
-
- if (!hasPreeditString())
- {
- return;
- }
-
- const LLWString textString(getWText());
- const llwchar *text = textString.c_str();
- const S32 text_len = getLength();
- const S32 num_lines = getLineCount();
-
- S32 cur_line = getFirstVisibleLine();
- if (cur_line >= num_lines)
- {
- return;
- }
-
- const S32 line_height = mFont->getLineHeight();
-
- S32 line_start = getLineStart(cur_line);
- S32 line_y = mVisibleTextRect.mTop - line_height;
- while((mVisibleTextRect.mBottom <= line_y) && (num_lines > cur_line))
- {
- S32 next_start = -1;
- S32 line_end = text_len;
-
- if ((cur_line + 1) < num_lines)
- {
- next_start = getLineStart(cur_line + 1);
- line_end = next_start;
- }
- if ( text[line_end-1] == '\n' )
- {
- --line_end;
- }
-
- // Does this line contain preedits?
- if (line_start >= mPreeditPositions.back())
- {
- // We have passed the preedits.
- break;
- }
- if (line_end > mPreeditPositions.front())
- {
- for (U32 i = 0; i < mPreeditStandouts.size(); i++)
- {
- S32 left = mPreeditPositions[i];
- S32 right = mPreeditPositions[i + 1];
- if (right <= line_start || left >= line_end)
- {
- continue;
- }
-
- 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 += mFont->getWidth(text, line_start, left - line_start);
- }
- S32 preedit_right = text_rect.mLeft;
- if (right < line_end)
- {
- preedit_right += mFont->getWidth(text, line_start, right - line_start);
- }
- else
- {
- preedit_right += mFont->getWidth(text, line_start, line_end - line_start);
- }
-
- if (mPreeditStandouts[i])
- {
- gl_rect_2d(preedit_left + preedit_standout_gap,
- 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,
- 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));
- }
- }
- }
-
- // move down one line
- line_y -= line_height;
- line_start = next_start;
- cur_line++;
- }
-}
-
-void LLTextEditor::draw()
-{
- {
- // pad clipping rectangle so that cursor can draw at full width
- // when at left edge of mVisibleTextRect
- LLRect clip_rect(mVisibleTextRect);
- clip_rect.stretch(1);
- LLLocalClipRect clip(clip_rect);
- }
-
- LLTextBase::draw();
-
- 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
- mBorder->setKeyboardFocusHighlight( hasFocus() );// && !mReadOnly);
-}
-
-// Start or stop the editor from accepting text-editing keystrokes
-// see also LLLineEditor
-void LLTextEditor::setFocus( bool new_state )
-{
- bool old_state = hasFocus();
-
- // Don't change anything if the focus state didn't change
- if (new_state == old_state) return;
-
- // Notify early if we are losing focus.
- if (!new_state)
- {
- getWindow()->allowLanguageTextInput(this, false);
- }
-
- LLTextBase::setFocus( new_state );
-
- if( new_state )
- {
- // Route menu to this class
- gEditMenuHandler = this;
-
- // Don't start the cursor flashing right away
- resetCursorBlink();
- }
- else
- {
- // Route menu back to the default
- if( gEditMenuHandler == this )
- {
- gEditMenuHandler = NULL;
- }
-
- endSelection();
- }
-}
-
-// public
-void LLTextEditor::setCursorAndScrollToEnd()
-{
- deselect();
- endOfDoc();
-}
-
-void LLTextEditor::getCurrentLineAndColumn( S32* line, S32* col, bool include_wordwrap )
-{
- *line = getLineNumFromDocIndex(mCursorPos, include_wordwrap);
- *col = getLineOffsetFromDocIndex(mCursorPos, include_wordwrap);
-}
-
-void LLTextEditor::autoIndent()
-{
- // Count the number of spaces in the current line
- S32 line = getLineNumFromDocIndex(mCursorPos, false);
- S32 line_start = getLineStart(line);
- S32 space_count = 0;
- S32 i;
-
- LLWString text = getWText();
- S32 offset = getLineOffsetFromDocIndex(mCursorPos);
- while(( ' ' == text[line_start] ) && (space_count < offset))
- {
- space_count++;
- line_start++;
- }
-
- // If we're starting a braced section, indent one level.
- if( (mCursorPos > 0) && (text[mCursorPos -1] == '{') )
- {
- space_count += SPACES_PER_TAB;
- }
-
- // Insert that number of spaces on the new line
-
- //appendLineBreakSegment(LLStyle::Params());//addChar( '\n' );
- addLineBreakChar();
-
- for( i = 0; i < space_count; i++ )
- {
- addChar( ' ' );
- }
-}
-
-// Inserts new text at the cursor position
-void LLTextEditor::insertText(const std::string &new_text)
-{
- bool enabled = getEnabled();
- setEnabled( true );
-
- // Delete any selected characters (the insertion replaces them)
- if( hasSelection() )
- {
- deleteSelection(true);
- }
-
- setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), false, LLTextSegmentPtr() ));
-
- 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
- S32 selection_start = mSelectionStart;
- S32 selection_end = mSelectionEnd;
- bool was_selecting = mIsSelecting;
- S32 cursor_pos = mCursorPos;
- S32 old_length = getLength();
- bool cursor_was_at_end = (mCursorPos == old_length);
-
- deselect();
-
- setCursorPos(old_length);
-
- LLWString widget_wide_text = utf8str_to_wstring(text);
-
- LLTextSegmentPtr segment = new LLInlineViewSegment(params, old_length, old_length + widget_wide_text.size());
- insert(getLength(), widget_wide_text, false, segment);
-
- // Set the cursor and scroll position
- if( selection_start != selection_end )
- {
- mSelectionStart = selection_start;
- mSelectionEnd = selection_end;
-
- mIsSelecting = was_selecting;
- setCursorPos(cursor_pos);
- }
- else if( cursor_was_at_end )
- {
- setCursorPos(getLength());
- }
- else
- {
- setCursorPos(cursor_pos);
- }
-
- if (!allow_undo)
- {
- blockUndo();
- }
-}
-
-void LLTextEditor::removeTextFromEnd(S32 num_chars)
-{
- if (num_chars <= 0) return;
-
- remove(getLength() - num_chars, num_chars, false);
-
- S32 len = getLength();
- setCursorPos (llclamp(mCursorPos, 0, len));
- mSelectionStart = llclamp(mSelectionStart, 0, len);
- mSelectionEnd = llclamp(mSelectionEnd, 0, len);
-
- needsScroll();
-}
-
-//----------------------------------------------------------------------------
-
-void LLTextEditor::onSpellCheckPerformed()
-{
- if (isPristine())
- {
- mBaseDocIsPristine = false;
- }
-}
-
-void LLTextEditor::makePristine()
-{
- mPristineCmd = mLastCmd;
- mBaseDocIsPristine = !mLastCmd;
-
- // Create a clean partition in the undo stack. We don't want a single command to extend from
- // the "pre-pristine" state to the "post-pristine" state.
- if( mLastCmd )
- {
- mLastCmd->blockExtensions();
- }
-}
-
-bool LLTextEditor::isPristine() const
-{
- if( mPristineCmd )
- {
- return (mPristineCmd == mLastCmd);
- }
- else
- {
- // No undo stack, so check if the version before and commands were done was the original version
- return !mLastCmd && mBaseDocIsPristine;
- }
-}
-
-bool LLTextEditor::tryToRevertToPristineState()
-{
- if( !isPristine() )
- {
- deselect();
- S32 i = 0;
- while( !isPristine() && canUndo() )
- {
- undo();
- i--;
- }
-
- while( !isPristine() && canRedo() )
- {
- redo();
- i++;
- }
-
- if( !isPristine() )
- {
- // failed, so go back to where we started
- while( i > 0 )
- {
- undo();
- i--;
- }
- }
- }
-
- return isPristine(); // true => success
-}
-
-void LLTextEditor::updateLinkSegments()
-{
- LLWString wtext = getWText();
-
- // update any segments that contain a link
- for (segment_set_t::iterator it = mSegments.begin(); it != mSegments.end(); ++it)
- {
- LLTextSegment *segment = *it;
- if (segment && segment->getStyle() && segment->getStyle()->isLink())
- {
- 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 (acceptsTextInput() && LLUrlRegistry::instance().hasUrl(url_label))
- {
- std::string new_url = wstring_to_utf8str(url_label);
- LLStringUtil::trim(new_url);
- new_style->setLinkHREF(new_url);
- LLStyleConstSP sp(new_style);
- segment->setStyle(sp);
- }
- }
- }
-}
-
-
-
-void LLTextEditor::onMouseCaptureLost()
-{
- endSelection();
-}
-
-///////////////////////////////////////////////////////////////////
-// Hack for Notecards
-
-bool LLTextEditor::importBuffer(const char* buffer, S32 length )
-{
- std::istringstream instream(buffer);
-
- // Version 1 format:
- // Linden text version 1\n
- // {\n
- // <EmbeddedItemList chunk>
- // Text length <bytes without \0>\n
- // <text without \0> (text may contain ext_char_values)
- // }\n
-
- char tbuf[MAX_STRING]; /* Flawfinder: ignore */
-
- S32 version = 0;
- instream.getline(tbuf, MAX_STRING);
- if( 1 != sscanf(tbuf, "Linden text version %d", &version) )
- {
- LL_WARNS() << "Invalid Linden text file header " << LL_ENDL;
- return false;
- }
-
- if( 1 != version )
- {
- LL_WARNS() << "Invalid Linden text file version: " << version << LL_ENDL;
- return false;
- }
-
- instream.getline(tbuf, MAX_STRING);
- if( 0 != sscanf(tbuf, "{") )
- {
- LL_WARNS() << "Invalid Linden text file format" << LL_ENDL;
- return false;
- }
-
- S32 text_len = 0;
- instream.getline(tbuf, MAX_STRING);
- if( 1 != sscanf(tbuf, "Text length %d", &text_len) )
- {
- LL_WARNS() << "Invalid Linden text length field" << LL_ENDL;
- return false;
- }
-
- if( text_len > mMaxTextByteLength )
- {
- LL_WARNS() << "Invalid Linden text length: " << text_len << LL_ENDL;
- return false;
- }
-
- bool success = true;
-
- char* text = new char[ text_len + 1];
- if (text == NULL)
- {
- LLError::LLUserWarningMsg::showOutOfMemory();
- 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 */
- {
- 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, "}")) )
- {
- LL_WARNS() << "Invalid Linden text file format: missing terminal }" << LL_ENDL;
- success = false;
- }
-
- if( success )
- {
- // Actually set the text
- setText( LLStringExplicit(text) );
- }
-
- delete[] text;
-
- startOfDoc();
- deselect();
-
- return success;
-}
-
-bool LLTextEditor::exportBuffer(std::string &buffer )
-{
- std::ostringstream outstream(buffer);
-
- outstream << "Linden text version 1\n";
- outstream << "{\n";
-
- outstream << llformat("Text length %d\n", getLength() );
- outstream << getText();
- outstream << "}\n";
-
- return true;
-}
-
-void LLTextEditor::updateAllowingLanguageInput()
-{
- LLWindow* window = getWindow();
- if (!window)
- {
- // test app, no window available
- return;
- }
- if (hasFocus() && !mReadOnly)
- {
- window->allowLanguageTextInput(this, true);
- }
- else
- {
- window->allowLanguageTextInput(this, false);
- }
-}
-
-// Preedit is managed off the undo/redo command stack.
-
-bool LLTextEditor::hasPreeditString() const
-{
- return (mPreeditPositions.size() > 1);
-}
-
-void LLTextEditor::resetPreedit()
-{
- if (hasSelection())
- {
- if (hasPreeditString())
- {
- LL_WARNS() << "Preedit and selection!" << LL_ENDL;
- deselect();
- }
- else
- {
- deleteSelection(true);
- }
- }
- if (hasPreeditString())
- {
- if (hasSelection())
- {
- LL_WARNS() << "Preedit and selection!" << LL_ENDL;
- deselect();
- }
-
- setCursorPos(mPreeditPositions.front());
- removeStringNoUndo(mCursorPos, mPreeditPositions.back() - mCursorPos);
- insertStringNoUndo(mCursorPos, mPreeditOverwrittenWString);
-
- mPreeditWString.clear();
- mPreeditOverwrittenWString.clear();
- mPreeditPositions.clear();
-
- // A call to updatePreedit should soon follow under a
- // normal course of operation, so we don't need to
- // maintain internal variables such as line start
- // positions now.
- }
-}
-
-void LLTextEditor::updatePreedit(const LLWString &preedit_string,
- const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position)
-{
- // Just in case.
- if (mReadOnly)
- {
- return;
- }
-
- getWindow()->hideCursorUntilMouseMove();
-
- S32 insert_preedit_at = mCursorPos;
-
- mPreeditWString = preedit_string;
- mPreeditPositions.resize(preedit_segment_lengths.size() + 1);
- S32 position = insert_preedit_at;
- for (segment_lengths_t::size_type i = 0; i < preedit_segment_lengths.size(); i++)
- {
- mPreeditPositions[i] = position;
- position += preedit_segment_lengths[i];
- }
- mPreeditPositions.back() = position;
-
- if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
- {
- mPreeditOverwrittenWString = getWText().substr(insert_preedit_at, mPreeditWString.length());
- removeStringNoUndo(insert_preedit_at, mPreeditWString.length());
- }
- else
- {
- mPreeditOverwrittenWString.clear();
- }
-
- 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;
-
- setCursorPos(insert_preedit_at + caret_position);
-
- // Update of the preedit should be caused by some key strokes.
- resetCursorBlink();
-
- onKeyStroke();
-}
-
-bool LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const
-{
- if (control)
- {
- LLRect control_rect_screen;
- localRectToScreen(mVisibleTextRect, &control_rect_screen);
- LLUI::getInstance()->screenRectToGL(control_rect_screen, control);
- }
-
- S32 preedit_left_position, preedit_right_position;
- if (hasPreeditString())
- {
- preedit_left_position = mPreeditPositions.front();
- preedit_right_position = mPreeditPositions.back();
- }
- else
- {
- preedit_left_position = preedit_right_position = mCursorPos;
- }
-
- const S32 query = (query_offset >= 0 ? preedit_left_position + query_offset : mCursorPos);
- if (query < preedit_left_position || query > preedit_right_position)
- {
- return false;
- }
-
- const S32 first_visible_line = getFirstVisibleLine();
- if (query < getLineStart(first_visible_line))
- {
- return false;
- }
-
- S32 current_line = first_visible_line;
- S32 current_line_start, current_line_end;
- for (;;)
- {
- current_line_start = getLineStart(current_line);
- current_line_end = getLineStart(current_line + 1);
- if (query >= current_line_start && query < current_line_end)
- {
- break;
- }
- if (current_line_start == current_line_end)
- {
- // We have reached on the last line. The query position must be here.
- break;
- }
- current_line++;
- }
-
- const LLWString textString(getWText());
- const llwchar * const text = textString.c_str();
- const S32 line_height = mFont->getLineHeight();
-
- if (coord)
- {
- 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);
- LLUI::getInstance()->screenPointToGL(query_screen_x, query_screen_y, &coord->mX, &coord->mY);
- }
-
- if (bounds)
- {
- S32 preedit_left = mVisibleTextRect.mLeft;
- if (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 += mFont->getWidth(text, current_line_start, preedit_right_position - current_line_start);
- }
- else
- {
- 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;
- const S32 preedit_bottom = preedit_top - line_height;
-
- const LLRect preedit_rect_local(preedit_left, preedit_top, preedit_right, preedit_bottom);
- LLRect preedit_rect_screen;
- localRectToScreen(preedit_rect_local, &preedit_rect_screen);
- LLUI::getInstance()->screenRectToGL(preedit_rect_screen, bounds);
- }
-
- return true;
-}
-
-void LLTextEditor::getSelectionRange(S32 *position, S32 *length) const
-{
- if (hasSelection())
- {
- *position = llmin(mSelectionStart, mSelectionEnd);
- *length = llabs(mSelectionStart - mSelectionEnd);
- }
- else
- {
- *position = mCursorPos;
- *length = 0;
- }
-}
-
-void LLTextEditor::getPreeditRange(S32 *position, S32 *length) const
-{
- if (hasPreeditString())
- {
- *position = mPreeditPositions.front();
- *length = mPreeditPositions.back() - mPreeditPositions.front();
- }
- else
- {
- *position = mCursorPos;
- *length = 0;
- }
-}
-
-void LLTextEditor::markAsPreedit(S32 position, S32 length)
-{
- deselect();
- setCursorPos(position);
- if (hasPreeditString())
- {
- LL_WARNS() << "markAsPreedit invoked when hasPreeditString is true." << LL_ENDL;
- }
- mPreeditWString = LLWString( getWText(), position, length );
- if (length > 0)
- {
- mPreeditPositions.resize(2);
- mPreeditPositions[0] = position;
- mPreeditPositions[1] = position + length;
- mPreeditStandouts.resize(1);
- mPreeditStandouts[0] = false;
- }
- else
- {
- mPreeditPositions.clear();
- mPreeditStandouts.clear();
- }
- if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
- {
- mPreeditOverwrittenWString = mPreeditWString;
- }
- else
- {
- mPreeditOverwrittenWString.clear();
- }
-}
-
-S32 LLTextEditor::getPreeditFontSize() const
-{
- return ll_round((F32)mFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]);
-}
-
-bool LLTextEditor::isDirty() const
-{
- if(mReadOnly)
- {
- return false;
- }
-
- if( mPristineCmd )
- {
- return ( mPristineCmd == mLastCmd );
- }
- else
- {
- return ( NULL != mLastCmd );
- }
-}
-
-void LLTextEditor::setKeystrokeCallback(const keystroke_signal_t::slot_type& callback)
-{
- mKeystrokeSignal.connect(callback);
-}
-
-void LLTextEditor::onKeyStroke()
-{
- mKeystrokeSignal(this);
-
- mSpellCheckStart = mSpellCheckEnd = -1;
- mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
-}
-
-//virtual
-void LLTextEditor::clear()
-{
- getViewModel()->setDisplay(LLWStringUtil::null);
- clearSegments();
-}
-
-bool LLTextEditor::canLoadOrSaveToFile()
-{
- return !mReadOnly;
-}
-
-S32 LLTextEditor::spacesPerTab()
-{
- return SPACES_PER_TAB;
-}
+/**
+ * @file lltexteditor.cpp
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+// Text editor widget to let users enter a a multi-line ASCII document.
+
+#include "linden_common.h"
+
+#define LLTEXTEDITOR_CPP
+#include "lltexteditor.h"
+
+#include "llfontfreetype.h" // for LLFontFreetype::FIRST_CHAR
+#include "llfontgl.h"
+#include "llgl.h" // LLGLSUIDefault()
+#include "lllocalcliprect.h"
+#include "llrender.h"
+#include "llui.h"
+#include "lluictrlfactory.h"
+#include "llrect.h"
+#include "llfocusmgr.h"
+#include "lltimer.h"
+#include "llmath.h"
+
+#include "llclipboard.h"
+#include "llemojihelper.h"
+#include "llscrollbar.h"
+#include "llstl.h"
+#include "llstring.h"
+#include "llkeyboard.h"
+#include "llkeywords.h"
+#include "llundo.h"
+#include "llviewborder.h"
+#include "llcontrol.h"
+#include "llwindow.h"
+#include "lltextparser.h"
+#include "llscrollcontainer.h"
+#include "llspellcheck.h"
+#include "llpanel.h"
+#include "llurlregistry.h"
+#include "lltooltip.h"
+#include "llmenugl.h"
+
+#include <queue>
+#include "llcombobox.h"
+
+//
+// Globals
+//
+static LLDefaultChildRegistry::Register<LLTextEditor> r("simple_text_editor");
+
+// Compiler optimization, generate extern template
+template class LLTextEditor* LLView::getChild<class LLTextEditor>(
+ const std::string& name, bool recurse) const;
+
+//
+// Constants
+//
+const S32 SPACES_PER_TAB = 4;
+const F32 SPELLCHECK_DELAY = 0.5f; // delay between the last keypress and spell checking the word the cursor is on
+
+///////////////////////////////////////////////////////////////////
+
+class LLTextEditor::TextCmdInsert : public LLTextBase::TextCmd
+{
+public:
+ TextCmdInsert(S32 pos, bool group_with_next, const LLWString &ws, LLTextSegmentPtr segment)
+ : TextCmd(pos, group_with_next, segment), mWString(ws)
+ {
+ }
+ virtual ~TextCmdInsert() {}
+ virtual bool execute( LLTextBase* editor, S32* delta )
+ {
+ *delta = insert(editor, getPosition(), mWString );
+ LLWStringUtil::truncate(mWString, *delta);
+ //mWString = wstring_truncate(mWString, *delta);
+ return (*delta != 0);
+ }
+ virtual S32 undo( LLTextBase* editor )
+ {
+ remove(editor, getPosition(), mWString.length() );
+ return getPosition();
+ }
+ virtual S32 redo( LLTextBase* editor )
+ {
+ insert(editor, getPosition(), mWString );
+ return getPosition() + mWString.length();
+ }
+
+private:
+ LLWString mWString;
+};
+
+///////////////////////////////////////////////////////////////////
+class LLTextEditor::TextCmdAddChar : public LLTextBase::TextCmd
+{
+public:
+ TextCmdAddChar( S32 pos, bool group_with_next, llwchar wc, LLTextSegmentPtr segment)
+ : TextCmd(pos, group_with_next, segment), mWString(1, wc), mBlockExtensions(false)
+ {
+ }
+ virtual void blockExtensions()
+ {
+ mBlockExtensions = true;
+ }
+ virtual bool canExtend(S32 pos) const
+ {
+ // cannot extend text with custom segments
+ if (!mSegments.empty()) return false;
+
+ return !mBlockExtensions && (pos == getPosition() + (S32)mWString.length());
+ }
+ virtual bool execute( LLTextBase* editor, S32* delta )
+ {
+ *delta = insert(editor, getPosition(), mWString);
+ LLWStringUtil::truncate(mWString, *delta);
+ //mWString = wstring_truncate(mWString, *delta);
+ return (*delta != 0);
+ }
+ virtual bool extendAndExecute( LLTextBase* editor, S32 pos, llwchar wc, S32* delta )
+ {
+ LLWString ws;
+ ws += wc;
+
+ *delta = insert(editor, pos, ws);
+ if( *delta > 0 )
+ {
+ mWString += wc;
+ }
+ return (*delta != 0);
+ }
+ virtual S32 undo( LLTextBase* editor )
+ {
+ remove(editor, getPosition(), mWString.length() );
+ return getPosition();
+ }
+ virtual S32 redo( LLTextBase* editor )
+ {
+ insert(editor, getPosition(), mWString );
+ return getPosition() + mWString.length();
+ }
+
+private:
+ LLWString mWString;
+ bool mBlockExtensions;
+
+};
+
+///////////////////////////////////////////////////////////////////
+
+class LLTextEditor::TextCmdOverwriteChar : public LLTextBase::TextCmd
+{
+public:
+ TextCmdOverwriteChar( S32 pos, bool group_with_next, llwchar wc)
+ : TextCmd(pos, group_with_next), mChar(wc), mOldChar(0) {}
+
+ virtual bool execute( LLTextBase* editor, S32* delta )
+ {
+ mOldChar = editor->getWText()[getPosition()];
+ overwrite(editor, getPosition(), mChar);
+ *delta = 0;
+ return true;
+ }
+ virtual S32 undo( LLTextBase* editor )
+ {
+ overwrite(editor, getPosition(), mOldChar);
+ return getPosition();
+ }
+ virtual S32 redo( LLTextBase* editor )
+ {
+ overwrite(editor, getPosition(), mChar);
+ return getPosition()+1;
+ }
+
+private:
+ llwchar mChar;
+ llwchar mOldChar;
+};
+
+///////////////////////////////////////////////////////////////////
+
+class LLTextEditor::TextCmdRemove : public LLTextBase::TextCmd
+{
+public:
+ TextCmdRemove( S32 pos, bool group_with_next, S32 len, segment_vec_t& segments ) :
+ TextCmd(pos, group_with_next), mLen(len)
+ {
+ std::swap(mSegments, segments);
+ }
+ virtual bool execute( LLTextBase* editor, S32* delta )
+ {
+ mWString = editor->getWText().substr(getPosition(), mLen);
+ *delta = remove(editor, getPosition(), mLen );
+ return (*delta != 0);
+ }
+ virtual S32 undo( LLTextBase* editor )
+ {
+ insert(editor, getPosition(), mWString);
+ return getPosition() + mWString.length();
+ }
+ virtual S32 redo( LLTextBase* editor )
+ {
+ remove(editor, getPosition(), mLen );
+ return getPosition();
+ }
+private:
+ LLWString mWString;
+ S32 mLen;
+};
+
+
+///////////////////////////////////////////////////////////////////
+LLTextEditor::Params::Params()
+: default_text("default_text"),
+ prevalidator("prevalidator"),
+ embedded_items("embedded_items", false),
+ ignore_tab("ignore_tab", true),
+ 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_emoji_helper("show_emoji_helper"),
+ enable_tooltip_paste("enable_tooltip_paste")
+{
+ addSynonym(prevalidator, "prevalidate_callback");
+ addSynonym(prevalidator, "text_type");
+}
+
+LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
+ LLTextBase(p),
+ mAutoreplaceCallback(),
+ mBaseDocIsPristine(true),
+ mPristineCmd( NULL ),
+ mLastCmd( NULL ),
+ mDefaultColor( p.default_color() ),
+ mAutoIndent(p.auto_indent),
+ mParseOnTheFly(false),
+ mCommitOnFocusLost( p.commit_on_focus_lost),
+ mAllowEmbeddedItems( p.embedded_items ),
+ mMouseDownX(0),
+ mMouseDownY(0),
+ mTabsToNextField(p.ignore_tab),
+ mPrevalidator(p.prevalidator()),
+ mShowContextMenu(p.show_context_menu),
+ mShowEmojiHelper(p.show_emoji_helper),
+ mEnableTooltipPaste(p.enable_tooltip_paste),
+ mPassDelete(false),
+ mKeepSelectionOnReturn(false)
+{
+ mSourceID.generate();
+
+ //FIXME: use image?
+ LLViewBorder::Params params;
+ params.name = "text ed border";
+ params.rect = getLocalRect();
+ params.bevel_style = LLViewBorder::BEVEL_IN;
+ params.border_thickness = 1;
+ params.visible = p.border_visible;
+ mBorder = LLUICtrlFactory::create<LLViewBorder> (params);
+ addChild( mBorder );
+ setText(p.default_text());
+
+ mParseOnTheFly = true;
+}
+
+void LLTextEditor::initFromParams( const LLTextEditor::Params& p)
+{
+ LLTextBase::initFromParams(p);
+
+ // HACK: text editors always need to be enabled so that we can scroll
+ LLView::setEnabled(true);
+
+ if (p.commit_on_focus_lost.isProvided())
+ {
+ mCommitOnFocusLost = p.commit_on_focus_lost;
+ }
+
+ updateAllowingLanguageInput();
+}
+
+LLTextEditor::~LLTextEditor()
+{
+ gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() while LLTextEditor still valid
+
+ // Scrollbar is deleted by LLView
+ std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer());
+ mUndoStack.clear();
+ // Mark the menu as dead or its retained in memory till shutdown.
+ LLContextMenu* menu = static_cast<LLContextMenu*>(mContextMenuHandle.get());
+ if(menu)
+ {
+ menu->die();
+ mContextMenuHandle.markDead();
+ }
+}
+
+////////////////////////////////////////////////////////////
+// LLTextEditor
+// Public methods
+
+void LLTextEditor::setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params)
+{
+ // validate incoming text if necessary
+ if (mPrevalidator)
+ {
+ if (!mPrevalidator.validate(utf8str))
+ {
+ LLUI::getInstance()->reportBadKeystroke();
+ mPrevalidator.showLastErrorUsingTimeout();
+
+ // not valid text, nothing to do
+ return;
+ }
+ }
+
+ blockUndo();
+ deselect();
+
+ mParseOnTheFly = false;
+ LLTextBase::setText(utf8str, input_params);
+ mParseOnTheFly = true;
+
+ resetDirty();
+}
+
+void LLTextEditor::selectNext(const std::string& search_text_in, bool case_insensitive, bool wrap)
+{
+ if (search_text_in.empty())
+ {
+ return;
+ }
+
+ LLWString text = getWText();
+ LLWString search_text = utf8str_to_wstring(search_text_in);
+ if (case_insensitive)
+ {
+ LLWStringUtil::toLower(text);
+ LLWStringUtil::toLower(search_text);
+ }
+
+ if (mIsSelecting)
+ {
+ LLWString selected_text = text.substr(mSelectionEnd, mSelectionStart - mSelectionEnd);
+
+ if (selected_text == search_text)
+ {
+ // We already have this word selected, we are searching for the next.
+ setCursorPos(mCursorPos + search_text.size());
+ }
+ }
+
+ S32 loc = text.find(search_text,mCursorPos);
+
+ // If Maybe we wrapped, search again
+ if (wrap && (-1 == loc))
+ {
+ loc = text.find(search_text);
+ }
+
+ // If still -1, then search_text just isn't found.
+ if (-1 == loc)
+ {
+ mIsSelecting = false;
+ mSelectionEnd = 0;
+ mSelectionStart = 0;
+ return;
+ }
+
+ setCursorPos(loc);
+
+ mIsSelecting = true;
+ mSelectionEnd = mCursorPos;
+ mSelectionStart = llmin((S32)getLength(), (S32)(mCursorPos + search_text.size()));
+}
+
+bool LLTextEditor::replaceText(const std::string& search_text_in, const std::string& replace_text,
+ bool case_insensitive, bool wrap)
+{
+ bool replaced = false;
+
+ if (search_text_in.empty())
+ {
+ return replaced;
+ }
+
+ LLWString search_text = utf8str_to_wstring(search_text_in);
+ if (mIsSelecting)
+ {
+ LLWString text = getWText();
+ LLWString selected_text = text.substr(mSelectionEnd, mSelectionStart - mSelectionEnd);
+
+ if (case_insensitive)
+ {
+ LLWStringUtil::toLower(selected_text);
+ LLWStringUtil::toLower(search_text);
+ }
+
+ if (selected_text == search_text)
+ {
+ insertText(replace_text);
+ replaced = true;
+ }
+ }
+
+ selectNext(search_text_in, case_insensitive, wrap);
+ return replaced;
+}
+
+void LLTextEditor::replaceTextAll(const std::string& search_text, const std::string& replace_text, bool case_insensitive)
+{
+ startOfDoc();
+ selectNext(search_text, case_insensitive, false);
+
+ bool replaced = true;
+ while ( replaced )
+ {
+ replaced = replaceText(search_text,replace_text, case_insensitive, false);
+ }
+}
+
+S32 LLTextEditor::prevWordPos(S32 cursorPos) const
+{
+ LLWString wtext(getWText());
+ while( (cursorPos > 0) && (wtext[cursorPos-1] == ' ') )
+ {
+ cursorPos--;
+ }
+ while( (cursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[cursorPos-1] ) )
+ {
+ cursorPos--;
+ }
+ return cursorPos;
+}
+
+S32 LLTextEditor::nextWordPos(S32 cursorPos) const
+{
+ LLWString wtext(getWText());
+ while( (cursorPos < getLength()) && LLWStringUtil::isPartOfWord( wtext[cursorPos] ) )
+ {
+ cursorPos++;
+ }
+ while( (cursorPos < getLength()) && (wtext[cursorPos] == ' ') )
+ {
+ cursorPos++;
+ }
+ return cursorPos;
+}
+
+const LLTextSegmentPtr LLTextEditor::getPreviousSegment() const
+{
+ static LLPointer<LLIndexSegment> index_segment = new LLIndexSegment;
+
+ index_segment->setStart(mCursorPos);
+ index_segment->setEnd(mCursorPos);
+
+ // find segment index at character to left of cursor (or rightmost edge of selection)
+ segment_set_t::const_iterator it = mSegments.lower_bound(index_segment);
+
+ if (it != mSegments.end())
+ {
+ return *it;
+ }
+ else
+ {
+ return LLTextSegmentPtr();
+ }
+}
+
+void LLTextEditor::getSelectedSegments(LLTextEditor::segment_vec_t& segments) const
+{
+ S32 left = hasSelection() ? llmin(mSelectionStart, mSelectionEnd) : mCursorPos;
+ S32 right = hasSelection() ? llmax(mSelectionStart, mSelectionEnd) : mCursorPos;
+
+ return getSegmentsInRange(segments, left, right, true);
+}
+
+void LLTextEditor::getSegmentsInRange(LLTextEditor::segment_vec_t& segments_out, S32 start, S32 end, bool include_partial) const
+{
+ segment_set_t::const_iterator first_it = getSegIterContaining(start);
+ segment_set_t::const_iterator end_it = getSegIterContaining(end - 1);
+ if (end_it != mSegments.end()) ++end_it;
+
+ for (segment_set_t::const_iterator it = first_it; it != end_it; ++it)
+ {
+ LLTextSegmentPtr segment = *it;
+ if (include_partial
+ || (segment->getStart() >= start
+ && segment->getEnd() <= end))
+ {
+ segments_out.push_back(segment);
+ }
+ }
+}
+
+void LLTextEditor::setShowEmojiHelper(bool show)
+{
+ if (!mShowEmojiHelper)
+ {
+ LLEmojiHelper::instance().hideHelper(this);
+ }
+
+ mShowEmojiHelper = show;
+}
+
+bool LLTextEditor::selectionContainsLineBreaks()
+{
+ if (hasSelection())
+ {
+ S32 left = llmin(mSelectionStart, mSelectionEnd);
+ S32 right = left + llabs(mSelectionStart - mSelectionEnd);
+
+ LLWString wtext = getWText();
+ for( S32 i = left; i < right; i++ )
+ {
+ if (wtext[i] == '\n')
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+S32 LLTextEditor::indentLine( S32 pos, S32 spaces )
+{
+ // Assumes that pos is at the start of the line
+ // spaces may be positive (indent) or negative (unindent).
+ // Returns the actual number of characters added or removed.
+
+ llassert(pos >= 0);
+ llassert(pos <= getLength() );
+
+ S32 delta_spaces = 0;
+
+ if (spaces >= 0)
+ {
+ // Indent
+ for(S32 i=0; i < spaces; i++)
+ {
+ delta_spaces += addChar(pos, ' ');
+ }
+ }
+ else
+ {
+ // Unindent
+ for(S32 i=0; i < -spaces; i++)
+ {
+ LLWString wtext = getWText();
+ if (wtext[pos] == ' ')
+ {
+ delta_spaces += remove( pos, 1, false );
+ }
+ }
+ }
+
+ return delta_spaces;
+}
+
+void LLTextEditor::indentSelectedLines( S32 spaces )
+{
+ if( hasSelection() )
+ {
+ LLWString text = getWText();
+ S32 left = llmin( mSelectionStart, mSelectionEnd );
+ S32 right = left + llabs( mSelectionStart - mSelectionEnd );
+ bool cursor_on_right = (mSelectionEnd > mSelectionStart);
+ S32 cur = left;
+
+ // Expand left to start of line
+ while( (cur > 0) && (text[cur] != '\n') )
+ {
+ cur--;
+ }
+ left = cur;
+ if( cur > 0 )
+ {
+ left++;
+ }
+
+ // Expand right to end of line
+ if( text[right - 1] == '\n' )
+ {
+ right--;
+ }
+ else
+ {
+ while( (text[right] != '\n') && (right <= getLength() ) )
+ {
+ right++;
+ }
+ }
+
+ // 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
+ {
+ if( text[cur] == '\n' )
+ {
+ cur++;
+ }
+
+ S32 delta_spaces = indentLine( cur, spaces );
+ if( delta_spaces > 0 )
+ {
+ cur += delta_spaces;
+ }
+ right += delta_spaces;
+
+ text = getWText();
+
+ // Find the next new line
+ while( (cur < right) && (text[cur] != '\n') )
+ {
+ cur++;
+ }
+ }
+ while( cur < right );
+
+ mParseOnTheFly = true;
+
+ if( (right < getLength()) && (text[right] == '\n') )
+ {
+ right++;
+ }
+
+ // Set the selection and cursor
+ if( cursor_on_right )
+ {
+ mSelectionStart = left;
+ mSelectionEnd = right;
+ }
+ else
+ {
+ mSelectionStart = right;
+ mSelectionEnd = left;
+ }
+ setCursorPos(mSelectionEnd);
+ }
+}
+
+//virtual
+bool LLTextEditor::canSelectAll() const
+{
+ return true;
+}
+
+// virtual
+void LLTextEditor::selectAll()
+{
+ mSelectionStart = getLength();
+ mSelectionEnd = 0;
+ setCursorPos(mSelectionEnd);
+ updatePrimary();
+}
+
+void LLTextEditor::selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_pos)
+{
+ setCursorPos(prev_cursor_pos);
+ startSelection();
+ setCursorPos(next_cursor_pos);
+ endSelection();
+}
+
+void LLTextEditor::insertEmoji(llwchar emoji)
+{
+ LL_INFOS() << "LLTextEditor::insertEmoji(" << wchar_utf8_preview(emoji) << ")" << LL_ENDL;
+ auto styleParams = LLStyle::Params();
+ styleParams.font = LLFontGL::getFontEmojiLarge();
+ auto segment = new LLEmojiTextSegment(new LLStyle(styleParams), mCursorPos, mCursorPos + 1, *this);
+ insert(mCursorPos, LLWString(1, emoji), false, segment);
+ setCursorPos(mCursorPos + 1);
+}
+
+void LLTextEditor::handleEmojiCommit(llwchar emoji)
+{
+ S32 shortCodePos;
+ if (LLEmojiHelper::isCursorInEmojiCode(getWText(), mCursorPos, &shortCodePos))
+ {
+ remove(shortCodePos, mCursorPos - shortCodePos, true);
+ setCursorPos(shortCodePos);
+
+ insertEmoji(emoji);
+ }
+}
+
+bool LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ bool handled = false;
+
+ // set focus first, in case click callbacks want to change it
+ // RN: do we really need to have a tab stop?
+ if (hasTabStop())
+ {
+ setFocus( true );
+ }
+
+ // Let scrollbar have first dibs
+ handled = LLTextBase::handleMouseDown(x, y, mask);
+
+ if( !handled )
+ {
+ if (!(mask & MASK_SHIFT))
+ {
+ deselect();
+ }
+
+ bool start_select = true;
+ if( start_select )
+ {
+ // If we're not scrolling (handled by child), then we're selecting
+ if (mask & MASK_SHIFT)
+ {
+ S32 old_cursor_pos = mCursorPos;
+ setCursorAtLocalPos( x, y, true );
+
+ if (hasSelection())
+ {
+ mSelectionEnd = mCursorPos;
+ }
+ else
+ {
+ mSelectionStart = old_cursor_pos;
+ mSelectionEnd = mCursorPos;
+ }
+ // assume we're starting a drag select
+ mIsSelecting = true;
+ }
+ else
+ {
+ setCursorAtLocalPos( x, y, true );
+ startSelection();
+ }
+ }
+
+ handled = true;
+ }
+
+ // Delay cursor flashing
+ resetCursorBlink();
+
+ if (handled && !gFocusMgr.getMouseCapture())
+ {
+ gFocusMgr.setMouseCapture( this );
+ }
+ return handled;
+}
+
+bool LLTextEditor::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (hasTabStop())
+ {
+ setFocus(true);
+ }
+
+ bool show_menu = false;
+
+ // Prefer editor menu if it has selection. See EXT-6806.
+ if (hasSelection())
+ {
+ S32 click_pos = getDocIndexFromLocalCoord(x, y, false);
+ if (click_pos > mSelectionStart && click_pos < mSelectionEnd)
+ {
+ show_menu = true;
+ }
+ }
+
+ // Let segments handle the click, if nothing does, show editor menu
+ if (!show_menu && !LLTextBase::handleRightMouseDown(x, y, mask))
+ {
+ show_menu = true;
+ }
+
+ if (show_menu && getShowContextMenu())
+ {
+ showContextMenu(x, y);
+ }
+
+ return true;
+}
+
+
+
+bool LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (hasTabStop())
+ {
+ setFocus(true);
+ }
+
+ if (!LLTextBase::handleMouseDown(x, y, mask))
+ {
+ if( canPastePrimary() )
+ {
+ setCursorAtLocalPos( x, y, true );
+ // does not rely on focus being set
+ pastePrimary();
+ }
+ }
+ return true;
+}
+
+
+bool LLTextEditor::handleHover(S32 x, S32 y, MASK mask)
+{
+ bool handled = false;
+
+ if(hasMouseCapture() )
+ {
+ if( mIsSelecting )
+ {
+ if(mScroller)
+ {
+ mScroller->autoScroll(x, y);
+ }
+ S32 clamped_x = llclamp(x, mVisibleTextRect.mLeft, mVisibleTextRect.mRight);
+ S32 clamped_y = llclamp(y, mVisibleTextRect.mBottom, mVisibleTextRect.mTop);
+ setCursorAtLocalPos( clamped_x, clamped_y, true );
+ mSelectionEnd = mCursorPos;
+ }
+ LL_DEBUGS("UserInput") << "hover handled by " << getName() << " (active)" << LL_ENDL;
+ getWindow()->setCursor(UI_CURSOR_IBEAM);
+ handled = true;
+ }
+
+ if( !handled )
+ {
+ // Pass to children
+ handled = LLTextBase::handleHover(x, y, mask);
+ }
+
+ if( handled )
+ {
+ // Delay cursor flashing
+ resetCursorBlink();
+ }
+
+ if( !handled )
+ {
+ getWindow()->setCursor(UI_CURSOR_IBEAM);
+ handled = true;
+ }
+
+ return handled;
+}
+
+
+bool LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ bool handled = false;
+
+ // if I'm not currently selecting text
+ if (!(mIsSelecting && hasMouseCapture()))
+ {
+ // let text segments handle mouse event
+ handled = LLTextBase::handleMouseUp(x, y, mask);
+ }
+
+ if( !handled )
+ {
+ if( mIsSelecting )
+ {
+ if(mScroller)
+ {
+ mScroller->autoScroll(x, y);
+ }
+ S32 clamped_x = llclamp(x, mVisibleTextRect.mLeft, mVisibleTextRect.mRight);
+ S32 clamped_y = llclamp(y, mVisibleTextRect.mBottom, mVisibleTextRect.mTop);
+ setCursorAtLocalPos( clamped_x, clamped_y, true );
+ endSelection();
+ }
+
+ // take selection to 'primary' clipboard
+ updatePrimary();
+
+ handled = true;
+ }
+
+ // Delay cursor flashing
+ resetCursorBlink();
+
+ if( hasMouseCapture() )
+ {
+ gFocusMgr.setMouseCapture( NULL );
+
+ handled = true;
+ }
+
+ return handled;
+}
+
+
+bool LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ bool handled = false;
+
+ // let scrollbar and text segments have first dibs
+ handled = LLTextBase::handleDoubleClick(x, y, mask);
+
+ if( !handled )
+ {
+ setCursorAtLocalPos( x, y, false );
+ deselect();
+
+ LLWString text = getWText();
+
+ if( LLWStringUtil::isPartOfWord( text[mCursorPos] ) )
+ {
+ // Select word the cursor is over
+ while ((mCursorPos > 0) && LLWStringUtil::isPartOfWord(text[mCursorPos-1]))
+ {
+ if (!setCursorPos(mCursorPos - 1)) break;
+ }
+ startSelection();
+
+ while ((mCursorPos < (S32)text.length()) && LLWStringUtil::isPartOfWord( text[mCursorPos] ) )
+ {
+ if (!setCursorPos(mCursorPos + 1)) break;
+ }
+
+ mSelectionEnd = mCursorPos;
+ }
+ else if ((mCursorPos < (S32)text.length()) && !iswspace( text[mCursorPos]) )
+ {
+ // Select the character the cursor is over
+ startSelection();
+ setCursorPos(mCursorPos + 1);
+ mSelectionEnd = mCursorPos;
+ }
+
+ // We don't want handleMouseUp() to "finish" the selection (and thereby
+ // set mSelectionEnd to where the mouse is), so we finish the selection here.
+ mIsSelecting = false;
+
+ // delay cursor flashing
+ resetCursorBlink();
+
+ // take selection to 'primary' clipboard
+ updatePrimary();
+
+ handled = true;
+ }
+
+ return handled;
+}
+
+
+//----------------------------------------------------------------------------
+// Returns change in number of characters in mText
+
+S32 LLTextEditor::execute( TextCmd* cmd )
+{
+ if (!mReadOnly && mShowEmojiHelper)
+ {
+ // Any change to our contents should always hide the helper
+ LLEmojiHelper::instance().hideHelper(this);
+ }
+
+ S32 delta = 0;
+ if( cmd->execute(this, &delta) )
+ {
+ // Delete top of undo stack
+ undo_stack_t::iterator enditer = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd);
+ std::for_each(mUndoStack.begin(), enditer, DeletePointer());
+ mUndoStack.erase(mUndoStack.begin(), enditer);
+ // Push the new command is now on the top (front) of the undo stack.
+ mUndoStack.push_front(cmd);
+ mLastCmd = cmd;
+
+ bool need_to_rollback = mPrevalidator && !mPrevalidator.validate(getViewModel()->getDisplay());
+ if (need_to_rollback)
+ {
+ LLUI::getInstance()->reportBadKeystroke();
+ mPrevalidator.showLastErrorUsingTimeout();
+
+ // get rid of this last command and clean up undo stack
+ undo();
+
+ // remove any evidence of this command from redo history
+ mUndoStack.pop_front();
+ delete cmd;
+
+ // failure, nothing changed
+ delta = 0;
+ }
+ }
+ else
+ {
+ // Operation failed, so don't put it on the undo stack.
+ delete cmd;
+ }
+
+ return delta;
+}
+
+S32 LLTextEditor::insert(S32 pos, const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment)
+{
+ return execute( new TextCmdInsert( pos, group_with_next_op, wstr, segment ) );
+}
+
+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 removedChar;
+}
+
+S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc)
+{
+ if ((S32)getLength() == pos)
+ {
+ return addChar(pos, wc);
+ }
+ else
+ {
+ return execute(new TextCmdOverwriteChar(pos, false, wc));
+ }
+}
+
+// Remove a single character from the text. Tries to remove
+// a pseudo-tab (up to for spaces in a row)
+void LLTextEditor::removeCharOrTab()
+{
+ if (!getEnabled())
+ {
+ return;
+ }
+
+ if (mCursorPos > 0)
+ {
+ S32 chars_to_remove = 1;
+
+ LLWString text = getWText();
+ if (text[mCursorPos - 1] == ' ')
+ {
+ // Try to remove a "tab"
+ S32 offset = getLineOffsetFromDocIndex(mCursorPos);
+ if (offset > 0)
+ {
+ chars_to_remove = offset % SPACES_PER_TAB;
+ if (chars_to_remove == 0)
+ {
+ chars_to_remove = SPACES_PER_TAB;
+ }
+
+ for (S32 i = 0; i < chars_to_remove; i++)
+ {
+ if (text[mCursorPos - i - 1] != ' ')
+ {
+ // Fewer than a full tab's worth of spaces, so
+ // just delete a single character.
+ chars_to_remove = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ for (S32 i = 0; i < chars_to_remove; i++)
+ {
+ setCursorPos(mCursorPos - 1);
+ remove(mCursorPos, 1, false);
+ }
+
+ tryToShowEmojiHelper();
+ }
+ else
+ {
+ LLUI::getInstance()->reportBadKeystroke();
+ }
+}
+
+// Remove a single character from the text
+S32 LLTextEditor::removeChar(S32 pos)
+{
+ return remove(pos, 1, false);
+}
+
+void LLTextEditor::removeChar()
+{
+ if (!getEnabled())
+ {
+ return;
+ }
+
+ if (mCursorPos > 0)
+ {
+ setCursorPos(mCursorPos - 1);
+ removeChar(mCursorPos);
+ tryToShowEmojiHelper();
+ }
+ else
+ {
+ LLUI::getInstance()->reportBadKeystroke();
+ }
+}
+
+// Add a single character to the text
+S32 LLTextEditor::addChar(S32 pos, llwchar wc)
+{
+ if ((wstring_utf8_length(getWText()) + wchar_utf8_length(wc)) > mMaxTextByteLength)
+ {
+ LLUI::getInstance()->reportBadKeystroke();
+ return 0;
+ }
+
+ if (mLastCmd && mLastCmd->canExtend(pos))
+ {
+ if (mPrevalidator)
+ {
+ // get a copy of current text contents
+ LLWString test_string(getViewModel()->getDisplay());
+
+ // modify text contents as if this addChar succeeded
+ llassert(pos <= (S32)test_string.size());
+ test_string.insert(pos, 1, wc);
+ if (!mPrevalidator.validate(test_string))
+ {
+ LLUI::getInstance()->reportBadKeystroke();
+ mPrevalidator.showLastErrorUsingTimeout();
+ return 0;
+ }
+ }
+
+ S32 delta = 0;
+ mLastCmd->extendAndExecute(this, pos, wc, &delta);
+
+ return delta;
+ }
+
+ return execute(new TextCmdAddChar(pos, false, wc, LLTextSegmentPtr()));
+}
+
+void LLTextEditor::addChar(llwchar wc)
+{
+ if (!getEnabled())
+ {
+ return;
+ }
+
+ if (hasSelection())
+ {
+ deleteSelection(true);
+ }
+ else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
+ {
+ removeChar(mCursorPos);
+ }
+
+ setCursorPos(mCursorPos + addChar( mCursorPos, wc ));
+ tryToShowEmojiHelper();
+
+ 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::showEmojiHelper()
+{
+ if (mReadOnly || !mShowEmojiHelper)
+ return;
+
+ const LLRect cursorRect(getLocalRectFromDocIndex(mCursorPos));
+ auto cb = [this](llwchar emoji) { insertEmoji(emoji); };
+ LLEmojiHelper::instance().showHelper(this, cursorRect.mLeft, cursorRect.mTop, LLStringUtil::null, cb);
+}
+
+void LLTextEditor::tryToShowEmojiHelper()
+{
+ if (mReadOnly || !mShowEmojiHelper)
+ return;
+
+ S32 shortCodePos;
+ LLWString wtext(getWText());
+ if (LLEmojiHelper::isCursorInEmojiCode(wtext, mCursorPos, &shortCodePos))
+ {
+ const LLRect cursorRect(getLocalRectFromDocIndex(shortCodePos));
+ const LLWString wpart(wtext.substr(shortCodePos, mCursorPos - shortCodePos));
+ const std::string part(wstring_to_utf8str(wpart));
+ auto cb = [this](llwchar emoji) { handleEmojiCommit(emoji); };
+ LLEmojiHelper::instance().showHelper(this, cursorRect.mLeft, cursorRect.mTop, part, cb);
+ }
+ else
+ {
+ LLEmojiHelper::instance().hideHelper();
+ }
+}
+
+void LLTextEditor::addLineBreakChar(bool group_together)
+{
+ if( !getEnabled() )
+ {
+ return;
+ }
+ if( hasSelection() )
+ {
+ deleteSelection(true);
+ }
+ else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
+ {
+ removeChar(mCursorPos);
+ }
+
+ LLStyleConstSP sp(new LLStyle(LLStyle::Params()));
+ LLTextSegmentPtr segment = new LLLineBreakTextSegment(sp, mCursorPos);
+
+ S32 pos = execute(new TextCmdAddChar(mCursorPos, group_together, '\n', segment));
+
+ setCursorPos(mCursorPos + pos);
+}
+
+
+bool LLTextEditor::handleSelectionKey(const KEY key, const MASK mask)
+{
+ bool handled = false;
+
+ if( mask & MASK_SHIFT )
+ {
+ handled = true;
+
+ switch( key )
+ {
+ case KEY_LEFT:
+ if( 0 < mCursorPos )
+ {
+ startSelection();
+ setCursorPos(mCursorPos - 1);
+ if( mask & MASK_CONTROL )
+ {
+ setCursorPos(prevWordPos(mCursorPos));
+ }
+ mSelectionEnd = mCursorPos;
+ }
+ break;
+
+ case KEY_RIGHT:
+ if( mCursorPos < getLength() )
+ {
+ startSelection();
+ setCursorPos(mCursorPos + 1);
+ if( mask & MASK_CONTROL )
+ {
+ setCursorPos(nextWordPos(mCursorPos));
+ }
+ mSelectionEnd = mCursorPos;
+ }
+ break;
+
+ case KEY_UP:
+ startSelection();
+ changeLine( -1 );
+ mSelectionEnd = mCursorPos;
+ break;
+
+ case KEY_PAGE_UP:
+ startSelection();
+ changePage( -1 );
+ mSelectionEnd = mCursorPos;
+ break;
+
+ case KEY_HOME:
+ startSelection();
+ if( mask & MASK_CONTROL )
+ {
+ setCursorPos(0);
+ }
+ else
+ {
+ startOfLine();
+ }
+ mSelectionEnd = mCursorPos;
+ break;
+
+ case KEY_DOWN:
+ startSelection();
+ changeLine( 1 );
+ mSelectionEnd = mCursorPos;
+ break;
+
+ case KEY_PAGE_DOWN:
+ startSelection();
+ changePage( 1 );
+ mSelectionEnd = mCursorPos;
+ break;
+
+ case KEY_END:
+ startSelection();
+ if( mask & MASK_CONTROL )
+ {
+ setCursorPos(getLength());
+ }
+ else
+ {
+ endOfLine();
+ }
+ mSelectionEnd = mCursorPos;
+ break;
+
+ default:
+ handled = false;
+ break;
+ }
+ }
+
+ if( handled )
+ {
+ // take selection to 'primary' clipboard
+ updatePrimary();
+ }
+
+ return handled;
+}
+
+bool LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
+{
+ bool handled = false;
+
+ // Ignore capslock key
+ if( MASK_NONE == mask )
+ {
+ handled = true;
+ switch( key )
+ {
+ case KEY_UP:
+ changeLine( -1 );
+ break;
+
+ case KEY_PAGE_UP:
+ changePage( -1 );
+ break;
+
+ case KEY_HOME:
+ startOfLine();
+ break;
+
+ case KEY_DOWN:
+ changeLine( 1 );
+ deselect();
+ break;
+
+ case KEY_PAGE_DOWN:
+ changePage( 1 );
+ break;
+
+ case KEY_END:
+ endOfLine();
+ break;
+
+ case KEY_LEFT:
+ if( hasSelection() )
+ {
+ setCursorPos(llmin( mSelectionStart, mSelectionEnd ));
+ }
+ else
+ {
+ if( 0 < mCursorPos )
+ {
+ setCursorPos(mCursorPos - 1);
+ }
+ else
+ {
+ LLUI::getInstance()->reportBadKeystroke();
+ }
+ }
+ break;
+
+ case KEY_RIGHT:
+ if( hasSelection() )
+ {
+ setCursorPos(llmax( mSelectionStart, mSelectionEnd ));
+ }
+ else
+ {
+ if( mCursorPos < getLength() )
+ {
+ setCursorPos(mCursorPos + 1);
+ }
+ else
+ {
+ LLUI::getInstance()->reportBadKeystroke();
+ }
+ }
+ break;
+
+ default:
+ handled = false;
+ break;
+ }
+ }
+
+ if (handled)
+ {
+ deselect();
+ }
+
+ return handled;
+}
+
+void LLTextEditor::deleteSelection(bool group_with_next_op )
+{
+ if( getEnabled() && hasSelection() )
+ {
+ S32 pos = llmin( mSelectionStart, mSelectionEnd );
+ S32 length = llabs( mSelectionStart - mSelectionEnd );
+
+ remove( pos, length, group_with_next_op );
+
+ deselect();
+ setCursorPos(pos);
+ }
+}
+
+// virtual
+bool LLTextEditor::canCut() const
+{
+ return !mReadOnly && hasSelection();
+}
+
+// cut selection to clipboard
+void LLTextEditor::cut()
+{
+ if( !canCut() )
+ {
+ return;
+ }
+ S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
+ S32 length = llabs( mSelectionStart - mSelectionEnd );
+ LLClipboard::instance().copyToClipboard( getWText(), left_pos, length);
+ deleteSelection( false );
+
+ onKeyStroke();
+}
+
+bool LLTextEditor::canCopy() const
+{
+ return hasSelection();
+}
+
+// copy selection to clipboard
+void LLTextEditor::copy()
+{
+ if( !canCopy() )
+ {
+ return;
+ }
+ S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
+ S32 length = llabs( mSelectionStart - mSelectionEnd );
+ LLClipboard::instance().copyToClipboard(getWText(), left_pos, length);
+}
+
+bool LLTextEditor::canPaste() const
+{
+ return !mReadOnly && LLClipboard::instance().isTextAvailable();
+}
+
+// paste from clipboard
+void LLTextEditor::paste()
+{
+ bool is_primary = false;
+ pasteHelper(is_primary);
+}
+
+// paste from primary
+void LLTextEditor::pastePrimary()
+{
+ bool is_primary = true;
+ pasteHelper(is_primary);
+}
+
+// paste from primary (itsprimary==true) or clipboard (itsprimary==false)
+void LLTextEditor::pasteHelper(bool is_primary)
+{
+ struct BoolReset
+ {
+ BoolReset(bool& value) : mValuePtr(&value) { *mValuePtr = false; }
+ ~BoolReset() { *mValuePtr = true; }
+ bool* mValuePtr;
+ } reset(mParseOnTheFly);
+
+ bool can_paste_it;
+ if (is_primary)
+ {
+ can_paste_it = canPastePrimary();
+ }
+ else
+ {
+ can_paste_it = canPaste();
+ }
+
+ if (!can_paste_it)
+ {
+ return;
+ }
+
+ LLWString paste;
+ LLClipboard::instance().pasteFromClipboard(paste, is_primary);
+
+ if (paste.empty())
+ {
+ return;
+ }
+
+ // Delete any selected characters (the paste replaces them)
+ if( (!is_primary) && hasSelection() )
+ {
+ deleteSelection(true);
+ }
+
+ // 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();
+}
+
+
+// Clean up string (replace tabs and remove characters that our fonts don't support).
+void LLTextEditor::cleanStringForPaste(LLWString & clean_string)
+{
+ std::string clean_string_utf = wstring_to_utf8str(clean_string);
+ std::replace( clean_string_utf.begin(), clean_string_utf.end(), '\r', '\n');
+ clean_string = utf8str_to_wstring(clean_string_utf);
+
+ LLWStringUtil::replaceTabsWithSpaces(clean_string, SPACES_PER_TAB);
+ if( mAllowEmbeddedItems )
+ {
+ const llwchar LF = 10;
+ S32 len = clean_string.length();
+ for( S32 i = 0; i < len; i++ )
+ {
+ llwchar wc = clean_string[i];
+ if( (wc < LLFontFreetype::FIRST_CHAR) && (wc != LF) )
+ {
+ clean_string[i] = LL_UNKNOWN_CHAR;
+ }
+ else if (wc >= FIRST_EMBEDDED_CHAR && wc <= LAST_EMBEDDED_CHAR)
+ {
+ clean_string[i] = pasteEmbeddedItem(wc);
+ }
+ }
+ }
+}
+
+
+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) && (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, true, LLTextSegmentPtr()));
+ }
+ addLineBreakChar(true); // Add a line break and group with the next addition.
+
+ start = pos+1;
+ pos = clean_string.find('\n',start);
+ }
+
+ 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()
+{
+ if( !canCopy() )
+ {
+ return;
+ }
+ S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
+ S32 length = llabs( mSelectionStart - mSelectionEnd );
+ LLClipboard::instance().copyToClipboard(getWText(), left_pos, length, true);
+}
+
+bool LLTextEditor::canPastePrimary() const
+{
+ return !mReadOnly && LLClipboard::instance().isTextAvailable(true);
+}
+
+void LLTextEditor::updatePrimary()
+{
+ if (canCopy())
+ {
+ copyPrimary();
+ }
+}
+
+bool LLTextEditor::handleControlKey(const KEY key, const MASK mask)
+{
+ bool handled = false;
+
+ if( mask & MASK_CONTROL )
+ {
+ handled = true;
+
+ switch( key )
+ {
+ case KEY_HOME:
+ if( mask & MASK_SHIFT )
+ {
+ startSelection();
+ setCursorPos(0);
+ mSelectionEnd = mCursorPos;
+ }
+ else
+ {
+ // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down
+ // all move the cursor as if clicking, so should deselect.
+ deselect();
+ startOfDoc();
+ }
+ break;
+
+ case KEY_END:
+ {
+ if( mask & MASK_SHIFT )
+ {
+ startSelection();
+ }
+ else
+ {
+ // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down
+ // all move the cursor as if clicking, so should deselect.
+ deselect();
+ }
+ endOfDoc();
+ if( mask & MASK_SHIFT )
+ {
+ mSelectionEnd = mCursorPos;
+ }
+ break;
+ }
+
+ case KEY_RIGHT:
+ if( mCursorPos < getLength() )
+ {
+ // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down
+ // all move the cursor as if clicking, so should deselect.
+ deselect();
+
+ setCursorPos(nextWordPos(mCursorPos + 1));
+ }
+ break;
+
+
+ case KEY_LEFT:
+ if( mCursorPos > 0 )
+ {
+ // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down
+ // all move the cursor as if clicking, so should deselect.
+ deselect();
+
+ setCursorPos(prevWordPos(mCursorPos - 1));
+ }
+ break;
+
+ default:
+ handled = false;
+ break;
+ }
+ }
+
+ if (handled && !gFocusMgr.getMouseCapture())
+ {
+ updatePrimary();
+ }
+
+ return handled;
+}
+
+
+bool LLTextEditor::handleSpecialKey(const KEY key, const MASK mask)
+ {
+ bool handled = true;
+
+ if (mReadOnly) return false;
+
+ switch( key )
+ {
+ case KEY_INSERT:
+ if (mask == MASK_NONE)
+ {
+ gKeyboard->toggleInsertMode();
+ }
+ break;
+
+ case KEY_BACKSPACE:
+ if( hasSelection() )
+ {
+ deleteSelection(false);
+ }
+ else
+ if( 0 < mCursorPos )
+ {
+ removeCharOrTab();
+ }
+ else
+ {
+ LLUI::getInstance()->reportBadKeystroke();
+ }
+ break;
+
+
+ case KEY_RETURN:
+ if (mask == MASK_NONE)
+ {
+ if( hasSelection() && !mKeepSelectionOnReturn )
+ {
+ deleteSelection(false);
+ }
+ if (mAutoIndent)
+ {
+ autoIndent();
+ }
+ }
+ else
+ {
+ handled = false;
+ break;
+ }
+ break;
+
+ case KEY_TAB:
+ if (mask & MASK_CONTROL)
+ {
+ handled = false;
+ break;
+ }
+ if( hasSelection() && selectionContainsLineBreaks() )
+ {
+ indentSelectedLines( (mask & MASK_SHIFT) ? -SPACES_PER_TAB : SPACES_PER_TAB );
+ }
+ else
+ {
+ if( hasSelection() )
+ {
+ deleteSelection(false);
+ }
+
+ S32 offset = getLineOffsetFromDocIndex(mCursorPos);
+
+ S32 spaces_needed = SPACES_PER_TAB - (offset % SPACES_PER_TAB);
+ for( S32 i=0; i < spaces_needed; i++ )
+ {
+ addChar( ' ' );
+ }
+ }
+ break;
+
+ default:
+ handled = false;
+ break;
+ }
+
+ if (handled)
+ {
+ onKeyStroke();
+ }
+ return handled;
+}
+
+
+void LLTextEditor::unindentLineBeforeCloseBrace()
+{
+ if( mCursorPos >= 1 )
+ {
+ LLWString text = getWText();
+ if( ' ' == text[ mCursorPos - 1 ] )
+ {
+ S32 line = getLineNumFromDocIndex(mCursorPos, false);
+ S32 line_start = getLineStart(line);
+
+ // Jump over spaces in the current line
+ while ((' ' == text[line_start]) && (line_start < mCursorPos))
+ {
+ line_start++;
+ }
+
+ // Make sure there is nothing but ' ' before the Brace we are unindenting
+ if (line_start == mCursorPos)
+ {
+ removeCharOrTab();
+ }
+ }
+ }
+}
+
+
+bool LLTextEditor::handleKeyHere(KEY key, MASK mask )
+{
+ bool handled = false;
+
+ // Special case for TAB. If want to move to next field, report
+ // not handled and let the parent take care of field movement.
+ if (KEY_TAB == key && mTabsToNextField)
+ {
+ return false;
+ }
+
+ if (mReadOnly && mScroller)
+ {
+ handled = (mScroller && mScroller->handleKeyHere( key, mask ))
+ || handleSelectionKey(key, mask)
+ || handleControlKey(key, mask);
+ }
+ else
+ {
+ if (!mReadOnly && mShowEmojiHelper && LLEmojiHelper::instance().handleKey(this, key, mask))
+ {
+ return true;
+ }
+
+ if (mEnableTooltipPaste &&
+ LLToolTipMgr::instance().toolTipVisible() &&
+ LLToolTipMgr::instance().isTooltipPastable() &&
+ 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);
+ }
+ }
+
+ if( handled )
+ {
+ resetCursorBlink();
+ needsScroll();
+
+ if (mShowEmojiHelper)
+ {
+ // Dismiss the helper whenever we handled a key that it didn't
+ LLEmojiHelper::instance().hideHelper(this);
+ }
+ }
+
+ return handled;
+}
+
+
+bool LLTextEditor::handleUnicodeCharHere(llwchar uni_char)
+{
+ if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL
+ {
+ return false;
+ }
+
+ bool handled = false;
+
+ // Handle most keys only if the text editor is writeable.
+ if( !mReadOnly )
+ {
+ if (mShowEmojiHelper && uni_char < 0x80 && LLEmojiHelper::instance().handleKey(this, (KEY)uni_char, MASK_NONE))
+ {
+ return true;
+ }
+
+ if( mAutoIndent && '}' == uni_char )
+ {
+ unindentLineBeforeCloseBrace();
+ }
+
+ // TODO: KLW Add auto show of tool tip on (
+ addChar( uni_char );
+
+ // Keys that add characters temporarily hide the cursor
+ getWindow()->hideCursorUntilMouseMove();
+
+ handled = true;
+ }
+
+ if( handled )
+ {
+ resetCursorBlink();
+
+ // Most keystrokes will make the selection box go away, but not all will.
+ deselect();
+
+ onKeyStroke();
+ }
+
+ return handled;
+}
+
+
+// virtual
+bool LLTextEditor::canDoDelete() const
+{
+ return !mReadOnly && ( !mPassDelete || ( hasSelection() || (mCursorPos < getLength())) );
+}
+
+void LLTextEditor::doDelete()
+{
+ if( !canDoDelete() )
+ {
+ return;
+ }
+ if( hasSelection() )
+ {
+ deleteSelection(false);
+ }
+ else
+ if( mCursorPos < getLength() )
+ {
+ S32 i;
+ S32 chars_to_remove = 1;
+ LLWString text = getWText();
+ if( (text[ mCursorPos ] == ' ') && (mCursorPos + SPACES_PER_TAB < getLength()) )
+ {
+ // Try to remove a full tab's worth of spaces
+ S32 offset = getLineOffsetFromDocIndex(mCursorPos);
+ chars_to_remove = SPACES_PER_TAB - (offset % SPACES_PER_TAB);
+ if( chars_to_remove == 0 )
+ {
+ chars_to_remove = SPACES_PER_TAB;
+ }
+
+ for( i = 0; i < chars_to_remove; i++ )
+ {
+ if( text[mCursorPos + i] != ' ' )
+ {
+ chars_to_remove = 1;
+ break;
+ }
+ }
+ }
+
+ for( i = 0; i < chars_to_remove; i++ )
+ {
+ setCursorPos(mCursorPos + 1);
+ removeChar();
+ }
+
+ }
+
+ onKeyStroke();
+}
+
+//----------------------------------------------------------------------------
+
+
+void LLTextEditor::blockUndo()
+{
+ mBaseDocIsPristine = false;
+ mLastCmd = NULL;
+ std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer());
+ mUndoStack.clear();
+}
+
+// virtual
+bool LLTextEditor::canUndo() const
+{
+ return !mReadOnly && mLastCmd != NULL;
+}
+
+void LLTextEditor::undo()
+{
+ if( !canUndo() )
+ {
+ return;
+ }
+ deselect();
+ S32 pos = 0;
+ do
+ {
+ pos = mLastCmd->undo(this);
+ undo_stack_t::iterator iter = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd);
+ if (iter != mUndoStack.end())
+ ++iter;
+ if (iter != mUndoStack.end())
+ mLastCmd = *iter;
+ else
+ mLastCmd = NULL;
+
+ } while( mLastCmd && mLastCmd->groupWithNext() );
+
+ setCursorPos(pos);
+
+ onKeyStroke();
+}
+
+bool LLTextEditor::canRedo() const
+{
+ return !mReadOnly && (mUndoStack.size() > 0) && (mLastCmd != mUndoStack.front());
+}
+
+void LLTextEditor::redo()
+{
+ if( !canRedo() )
+ {
+ return;
+ }
+ deselect();
+ S32 pos = 0;
+ do
+ {
+ if( !mLastCmd )
+ {
+ mLastCmd = mUndoStack.back();
+ }
+ else
+ {
+ undo_stack_t::iterator iter = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd);
+ if (iter != mUndoStack.begin())
+ mLastCmd = *(--iter);
+ else
+ mLastCmd = NULL;
+ }
+
+ if( mLastCmd )
+ {
+ pos = mLastCmd->redo(this);
+ }
+ } while(
+ mLastCmd &&
+ mLastCmd->groupWithNext() &&
+ (mLastCmd != mUndoStack.front()) );
+
+ setCursorPos(pos);
+
+ onKeyStroke();
+}
+
+void LLTextEditor::onFocusReceived()
+{
+ LLTextBase::onFocusReceived();
+ updateAllowingLanguageInput();
+}
+
+void LLTextEditor::focusLostHelper()
+{
+ updateAllowingLanguageInput();
+
+ // Route menu back to the default
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+
+ if (mCommitOnFocusLost)
+ {
+ onCommit();
+ }
+
+ // Make sure cursor is shown again
+ getWindow()->showCursorFromMouseMove();
+}
+
+void LLTextEditor::onFocusLost()
+{
+ focusLostHelper();
+ LLTextBase::onFocusLost();
+}
+
+void LLTextEditor::onCommit()
+{
+ setControlValue(getValue());
+ LLTextBase::onCommit();
+}
+
+void LLTextEditor::setEnabled(bool enabled)
+{
+ // just treat enabled as read-only flag
+ bool read_only = !enabled;
+ if (read_only != mReadOnly)
+ {
+ //mReadOnly = read_only;
+ LLTextBase::setReadOnly(read_only);
+ updateSegments();
+ updateAllowingLanguageInput();
+ }
+}
+
+void LLTextEditor::showContextMenu(S32 x, S32 y)
+{
+ LLContextMenu* menu = static_cast<LLContextMenu*>(mContextMenuHandle.get());
+ if (!menu)
+ {
+ llassert(LLMenuGL::sMenuContainer != NULL);
+ menu = LLUICtrlFactory::createFromFile<LLContextMenu>("menu_text_editor.xml",
+ LLMenuGL::sMenuContainer,
+ LLMenuHolderGL::child_registry_t::instance());
+ if(!menu)
+ {
+ LL_WARNS() << "Failed to create menu for LLTextEditor: " << getName() << LL_ENDL;
+ return;
+ }
+ mContextMenuHandle = menu->getHandle();
+ }
+
+ // Route menu to this class
+ // previously this was done in ::handleRightMoseDown:
+ //if(hasTabStop())
+ // setFocus(true) - why? weird...
+ // and then inside setFocus
+ // ....
+ // gEditMenuHandler = this;
+ // ....
+ // but this didn't work in all cases and just weird...
+ //why not here?
+ // (all this was done for EXT-4443)
+
+ gEditMenuHandler = this;
+
+ S32 screen_x, screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y);
+
+ 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()))
+ {
+ 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);
+}
+
+
+void LLTextEditor::drawPreeditMarker()
+{
+ static LLUICachedControl<F32> preedit_marker_brightness ("UIPreeditMarkerBrightness", 0);
+ static LLUICachedControl<S32> preedit_marker_gap ("UIPreeditMarkerGap", 0);
+ static LLUICachedControl<S32> preedit_marker_position ("UIPreeditMarkerPosition", 0);
+ static LLUICachedControl<S32> preedit_marker_thickness ("UIPreeditMarkerThickness", 0);
+ static LLUICachedControl<F32> preedit_standout_brightness ("UIPreeditStandoutBrightness", 0);
+ static LLUICachedControl<S32> preedit_standout_gap ("UIPreeditStandoutGap", 0);
+ static LLUICachedControl<S32> preedit_standout_position ("UIPreeditStandoutPosition", 0);
+ static LLUICachedControl<S32> preedit_standout_thickness ("UIPreeditStandoutThickness", 0);
+
+ if (!hasPreeditString())
+ {
+ return;
+ }
+
+ const LLWString textString(getWText());
+ const llwchar *text = textString.c_str();
+ const S32 text_len = getLength();
+ const S32 num_lines = getLineCount();
+
+ S32 cur_line = getFirstVisibleLine();
+ if (cur_line >= num_lines)
+ {
+ return;
+ }
+
+ const S32 line_height = mFont->getLineHeight();
+
+ S32 line_start = getLineStart(cur_line);
+ S32 line_y = mVisibleTextRect.mTop - line_height;
+ while((mVisibleTextRect.mBottom <= line_y) && (num_lines > cur_line))
+ {
+ S32 next_start = -1;
+ S32 line_end = text_len;
+
+ if ((cur_line + 1) < num_lines)
+ {
+ next_start = getLineStart(cur_line + 1);
+ line_end = next_start;
+ }
+ if ( text[line_end-1] == '\n' )
+ {
+ --line_end;
+ }
+
+ // Does this line contain preedits?
+ if (line_start >= mPreeditPositions.back())
+ {
+ // We have passed the preedits.
+ break;
+ }
+ if (line_end > mPreeditPositions.front())
+ {
+ for (U32 i = 0; i < mPreeditStandouts.size(); i++)
+ {
+ S32 left = mPreeditPositions[i];
+ S32 right = mPreeditPositions[i + 1];
+ if (right <= line_start || left >= line_end)
+ {
+ continue;
+ }
+
+ 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 += mFont->getWidth(text, line_start, left - line_start);
+ }
+ S32 preedit_right = text_rect.mLeft;
+ if (right < line_end)
+ {
+ preedit_right += mFont->getWidth(text, line_start, right - line_start);
+ }
+ else
+ {
+ preedit_right += mFont->getWidth(text, line_start, line_end - line_start);
+ }
+
+ if (mPreeditStandouts[i])
+ {
+ gl_rect_2d(preedit_left + preedit_standout_gap,
+ 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,
+ 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));
+ }
+ }
+ }
+
+ // move down one line
+ line_y -= line_height;
+ line_start = next_start;
+ cur_line++;
+ }
+}
+
+void LLTextEditor::draw()
+{
+ {
+ // pad clipping rectangle so that cursor can draw at full width
+ // when at left edge of mVisibleTextRect
+ LLRect clip_rect(mVisibleTextRect);
+ clip_rect.stretch(1);
+ LLLocalClipRect clip(clip_rect);
+ }
+
+ LLTextBase::draw();
+
+ 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
+ mBorder->setKeyboardFocusHighlight( hasFocus() );// && !mReadOnly);
+}
+
+// Start or stop the editor from accepting text-editing keystrokes
+// see also LLLineEditor
+void LLTextEditor::setFocus( bool new_state )
+{
+ bool old_state = hasFocus();
+
+ // Don't change anything if the focus state didn't change
+ if (new_state == old_state) return;
+
+ // Notify early if we are losing focus.
+ if (!new_state)
+ {
+ getWindow()->allowLanguageTextInput(this, false);
+ }
+
+ LLTextBase::setFocus( new_state );
+
+ if( new_state )
+ {
+ // Route menu to this class
+ gEditMenuHandler = this;
+
+ // Don't start the cursor flashing right away
+ resetCursorBlink();
+ }
+ else
+ {
+ // Route menu back to the default
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+
+ endSelection();
+ }
+}
+
+// public
+void LLTextEditor::setCursorAndScrollToEnd()
+{
+ deselect();
+ endOfDoc();
+}
+
+void LLTextEditor::getCurrentLineAndColumn( S32* line, S32* col, bool include_wordwrap )
+{
+ *line = getLineNumFromDocIndex(mCursorPos, include_wordwrap);
+ *col = getLineOffsetFromDocIndex(mCursorPos, include_wordwrap);
+}
+
+void LLTextEditor::autoIndent()
+{
+ // Count the number of spaces in the current line
+ S32 line = getLineNumFromDocIndex(mCursorPos, false);
+ S32 line_start = getLineStart(line);
+ S32 space_count = 0;
+ S32 i;
+
+ LLWString text = getWText();
+ S32 offset = getLineOffsetFromDocIndex(mCursorPos);
+ while(( ' ' == text[line_start] ) && (space_count < offset))
+ {
+ space_count++;
+ line_start++;
+ }
+
+ // If we're starting a braced section, indent one level.
+ if( (mCursorPos > 0) && (text[mCursorPos -1] == '{') )
+ {
+ space_count += SPACES_PER_TAB;
+ }
+
+ // Insert that number of spaces on the new line
+
+ //appendLineBreakSegment(LLStyle::Params());//addChar( '\n' );
+ addLineBreakChar();
+
+ for( i = 0; i < space_count; i++ )
+ {
+ addChar( ' ' );
+ }
+}
+
+// Inserts new text at the cursor position
+void LLTextEditor::insertText(const std::string &new_text)
+{
+ bool enabled = getEnabled();
+ setEnabled( true );
+
+ // Delete any selected characters (the insertion replaces them)
+ if( hasSelection() )
+ {
+ deleteSelection(true);
+ }
+
+ setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), false, LLTextSegmentPtr() ));
+
+ 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
+ S32 selection_start = mSelectionStart;
+ S32 selection_end = mSelectionEnd;
+ bool was_selecting = mIsSelecting;
+ S32 cursor_pos = mCursorPos;
+ S32 old_length = getLength();
+ bool cursor_was_at_end = (mCursorPos == old_length);
+
+ deselect();
+
+ setCursorPos(old_length);
+
+ LLWString widget_wide_text = utf8str_to_wstring(text);
+
+ LLTextSegmentPtr segment = new LLInlineViewSegment(params, old_length, old_length + widget_wide_text.size());
+ insert(getLength(), widget_wide_text, false, segment);
+
+ // Set the cursor and scroll position
+ if( selection_start != selection_end )
+ {
+ mSelectionStart = selection_start;
+ mSelectionEnd = selection_end;
+
+ mIsSelecting = was_selecting;
+ setCursorPos(cursor_pos);
+ }
+ else if( cursor_was_at_end )
+ {
+ setCursorPos(getLength());
+ }
+ else
+ {
+ setCursorPos(cursor_pos);
+ }
+
+ if (!allow_undo)
+ {
+ blockUndo();
+ }
+}
+
+void LLTextEditor::removeTextFromEnd(S32 num_chars)
+{
+ if (num_chars <= 0) return;
+
+ remove(getLength() - num_chars, num_chars, false);
+
+ S32 len = getLength();
+ setCursorPos (llclamp(mCursorPos, 0, len));
+ mSelectionStart = llclamp(mSelectionStart, 0, len);
+ mSelectionEnd = llclamp(mSelectionEnd, 0, len);
+
+ needsScroll();
+}
+
+//----------------------------------------------------------------------------
+
+void LLTextEditor::onSpellCheckPerformed()
+{
+ if (isPristine())
+ {
+ mBaseDocIsPristine = false;
+ }
+}
+
+void LLTextEditor::makePristine()
+{
+ mPristineCmd = mLastCmd;
+ mBaseDocIsPristine = !mLastCmd;
+
+ // Create a clean partition in the undo stack. We don't want a single command to extend from
+ // the "pre-pristine" state to the "post-pristine" state.
+ if( mLastCmd )
+ {
+ mLastCmd->blockExtensions();
+ }
+}
+
+bool LLTextEditor::isPristine() const
+{
+ if( mPristineCmd )
+ {
+ return (mPristineCmd == mLastCmd);
+ }
+ else
+ {
+ // No undo stack, so check if the version before and commands were done was the original version
+ return !mLastCmd && mBaseDocIsPristine;
+ }
+}
+
+bool LLTextEditor::tryToRevertToPristineState()
+{
+ if( !isPristine() )
+ {
+ deselect();
+ S32 i = 0;
+ while( !isPristine() && canUndo() )
+ {
+ undo();
+ i--;
+ }
+
+ while( !isPristine() && canRedo() )
+ {
+ redo();
+ i++;
+ }
+
+ if( !isPristine() )
+ {
+ // failed, so go back to where we started
+ while( i > 0 )
+ {
+ undo();
+ i--;
+ }
+ }
+ }
+
+ return isPristine(); // true => success
+}
+
+void LLTextEditor::updateLinkSegments()
+{
+ LLWString wtext = getWText();
+
+ // update any segments that contain a link
+ for (segment_set_t::iterator it = mSegments.begin(); it != mSegments.end(); ++it)
+ {
+ LLTextSegment *segment = *it;
+ if (segment && segment->getStyle() && segment->getStyle()->isLink())
+ {
+ 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 (acceptsTextInput() && LLUrlRegistry::instance().hasUrl(url_label))
+ {
+ std::string new_url = wstring_to_utf8str(url_label);
+ LLStringUtil::trim(new_url);
+ new_style->setLinkHREF(new_url);
+ LLStyleConstSP sp(new_style);
+ segment->setStyle(sp);
+ }
+ }
+ }
+}
+
+
+
+void LLTextEditor::onMouseCaptureLost()
+{
+ endSelection();
+}
+
+///////////////////////////////////////////////////////////////////
+// Hack for Notecards
+
+bool LLTextEditor::importBuffer(const char* buffer, S32 length )
+{
+ std::istringstream instream(buffer);
+
+ // Version 1 format:
+ // Linden text version 1\n
+ // {\n
+ // <EmbeddedItemList chunk>
+ // Text length <bytes without \0>\n
+ // <text without \0> (text may contain ext_char_values)
+ // }\n
+
+ char tbuf[MAX_STRING]; /* Flawfinder: ignore */
+
+ S32 version = 0;
+ instream.getline(tbuf, MAX_STRING);
+ if( 1 != sscanf(tbuf, "Linden text version %d", &version) )
+ {
+ LL_WARNS() << "Invalid Linden text file header " << LL_ENDL;
+ return false;
+ }
+
+ if( 1 != version )
+ {
+ LL_WARNS() << "Invalid Linden text file version: " << version << LL_ENDL;
+ return false;
+ }
+
+ instream.getline(tbuf, MAX_STRING);
+ if( 0 != sscanf(tbuf, "{") )
+ {
+ LL_WARNS() << "Invalid Linden text file format" << LL_ENDL;
+ return false;
+ }
+
+ S32 text_len = 0;
+ instream.getline(tbuf, MAX_STRING);
+ if( 1 != sscanf(tbuf, "Text length %d", &text_len) )
+ {
+ LL_WARNS() << "Invalid Linden text length field" << LL_ENDL;
+ return false;
+ }
+
+ if( text_len > mMaxTextByteLength )
+ {
+ LL_WARNS() << "Invalid Linden text length: " << text_len << LL_ENDL;
+ return false;
+ }
+
+ bool success = true;
+
+ char* text = new char[ text_len + 1];
+ if (text == NULL)
+ {
+ LLError::LLUserWarningMsg::showOutOfMemory();
+ 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 */
+ {
+ 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, "}")) )
+ {
+ LL_WARNS() << "Invalid Linden text file format: missing terminal }" << LL_ENDL;
+ success = false;
+ }
+
+ if( success )
+ {
+ // Actually set the text
+ setText( LLStringExplicit(text) );
+ }
+
+ delete[] text;
+
+ startOfDoc();
+ deselect();
+
+ return success;
+}
+
+bool LLTextEditor::exportBuffer(std::string &buffer )
+{
+ std::ostringstream outstream(buffer);
+
+ outstream << "Linden text version 1\n";
+ outstream << "{\n";
+
+ outstream << llformat("Text length %d\n", getLength() );
+ outstream << getText();
+ outstream << "}\n";
+
+ return true;
+}
+
+void LLTextEditor::updateAllowingLanguageInput()
+{
+ LLWindow* window = getWindow();
+ if (!window)
+ {
+ // test app, no window available
+ return;
+ }
+ if (hasFocus() && !mReadOnly)
+ {
+ window->allowLanguageTextInput(this, true);
+ }
+ else
+ {
+ window->allowLanguageTextInput(this, false);
+ }
+}
+
+// Preedit is managed off the undo/redo command stack.
+
+bool LLTextEditor::hasPreeditString() const
+{
+ return (mPreeditPositions.size() > 1);
+}
+
+void LLTextEditor::resetPreedit()
+{
+ if (hasSelection())
+ {
+ if (hasPreeditString())
+ {
+ LL_WARNS() << "Preedit and selection!" << LL_ENDL;
+ deselect();
+ }
+ else
+ {
+ deleteSelection(true);
+ }
+ }
+ if (hasPreeditString())
+ {
+ if (hasSelection())
+ {
+ LL_WARNS() << "Preedit and selection!" << LL_ENDL;
+ deselect();
+ }
+
+ setCursorPos(mPreeditPositions.front());
+ removeStringNoUndo(mCursorPos, mPreeditPositions.back() - mCursorPos);
+ insertStringNoUndo(mCursorPos, mPreeditOverwrittenWString);
+
+ mPreeditWString.clear();
+ mPreeditOverwrittenWString.clear();
+ mPreeditPositions.clear();
+
+ // A call to updatePreedit should soon follow under a
+ // normal course of operation, so we don't need to
+ // maintain internal variables such as line start
+ // positions now.
+ }
+}
+
+void LLTextEditor::updatePreedit(const LLWString &preedit_string,
+ const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position)
+{
+ // Just in case.
+ if (mReadOnly)
+ {
+ return;
+ }
+
+ getWindow()->hideCursorUntilMouseMove();
+
+ S32 insert_preedit_at = mCursorPos;
+
+ mPreeditWString = preedit_string;
+ mPreeditPositions.resize(preedit_segment_lengths.size() + 1);
+ S32 position = insert_preedit_at;
+ for (segment_lengths_t::size_type i = 0; i < preedit_segment_lengths.size(); i++)
+ {
+ mPreeditPositions[i] = position;
+ position += preedit_segment_lengths[i];
+ }
+ mPreeditPositions.back() = position;
+
+ if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
+ {
+ mPreeditOverwrittenWString = getWText().substr(insert_preedit_at, mPreeditWString.length());
+ removeStringNoUndo(insert_preedit_at, mPreeditWString.length());
+ }
+ else
+ {
+ mPreeditOverwrittenWString.clear();
+ }
+
+ 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;
+
+ setCursorPos(insert_preedit_at + caret_position);
+
+ // Update of the preedit should be caused by some key strokes.
+ resetCursorBlink();
+
+ onKeyStroke();
+}
+
+bool LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const
+{
+ if (control)
+ {
+ LLRect control_rect_screen;
+ localRectToScreen(mVisibleTextRect, &control_rect_screen);
+ LLUI::getInstance()->screenRectToGL(control_rect_screen, control);
+ }
+
+ S32 preedit_left_position, preedit_right_position;
+ if (hasPreeditString())
+ {
+ preedit_left_position = mPreeditPositions.front();
+ preedit_right_position = mPreeditPositions.back();
+ }
+ else
+ {
+ preedit_left_position = preedit_right_position = mCursorPos;
+ }
+
+ const S32 query = (query_offset >= 0 ? preedit_left_position + query_offset : mCursorPos);
+ if (query < preedit_left_position || query > preedit_right_position)
+ {
+ return false;
+ }
+
+ const S32 first_visible_line = getFirstVisibleLine();
+ if (query < getLineStart(first_visible_line))
+ {
+ return false;
+ }
+
+ S32 current_line = first_visible_line;
+ S32 current_line_start, current_line_end;
+ for (;;)
+ {
+ current_line_start = getLineStart(current_line);
+ current_line_end = getLineStart(current_line + 1);
+ if (query >= current_line_start && query < current_line_end)
+ {
+ break;
+ }
+ if (current_line_start == current_line_end)
+ {
+ // We have reached on the last line. The query position must be here.
+ break;
+ }
+ current_line++;
+ }
+
+ const LLWString textString(getWText());
+ const llwchar * const text = textString.c_str();
+ const S32 line_height = mFont->getLineHeight();
+
+ if (coord)
+ {
+ 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);
+ LLUI::getInstance()->screenPointToGL(query_screen_x, query_screen_y, &coord->mX, &coord->mY);
+ }
+
+ if (bounds)
+ {
+ S32 preedit_left = mVisibleTextRect.mLeft;
+ if (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 += mFont->getWidth(text, current_line_start, preedit_right_position - current_line_start);
+ }
+ else
+ {
+ 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;
+ const S32 preedit_bottom = preedit_top - line_height;
+
+ const LLRect preedit_rect_local(preedit_left, preedit_top, preedit_right, preedit_bottom);
+ LLRect preedit_rect_screen;
+ localRectToScreen(preedit_rect_local, &preedit_rect_screen);
+ LLUI::getInstance()->screenRectToGL(preedit_rect_screen, bounds);
+ }
+
+ return true;
+}
+
+void LLTextEditor::getSelectionRange(S32 *position, S32 *length) const
+{
+ if (hasSelection())
+ {
+ *position = llmin(mSelectionStart, mSelectionEnd);
+ *length = llabs(mSelectionStart - mSelectionEnd);
+ }
+ else
+ {
+ *position = mCursorPos;
+ *length = 0;
+ }
+}
+
+void LLTextEditor::getPreeditRange(S32 *position, S32 *length) const
+{
+ if (hasPreeditString())
+ {
+ *position = mPreeditPositions.front();
+ *length = mPreeditPositions.back() - mPreeditPositions.front();
+ }
+ else
+ {
+ *position = mCursorPos;
+ *length = 0;
+ }
+}
+
+void LLTextEditor::markAsPreedit(S32 position, S32 length)
+{
+ deselect();
+ setCursorPos(position);
+ if (hasPreeditString())
+ {
+ LL_WARNS() << "markAsPreedit invoked when hasPreeditString is true." << LL_ENDL;
+ }
+ mPreeditWString = LLWString( getWText(), position, length );
+ if (length > 0)
+ {
+ mPreeditPositions.resize(2);
+ mPreeditPositions[0] = position;
+ mPreeditPositions[1] = position + length;
+ mPreeditStandouts.resize(1);
+ mPreeditStandouts[0] = false;
+ }
+ else
+ {
+ mPreeditPositions.clear();
+ mPreeditStandouts.clear();
+ }
+ if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
+ {
+ mPreeditOverwrittenWString = mPreeditWString;
+ }
+ else
+ {
+ mPreeditOverwrittenWString.clear();
+ }
+}
+
+S32 LLTextEditor::getPreeditFontSize() const
+{
+ return ll_round((F32)mFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]);
+}
+
+bool LLTextEditor::isDirty() const
+{
+ if(mReadOnly)
+ {
+ return false;
+ }
+
+ if( mPristineCmd )
+ {
+ return ( mPristineCmd == mLastCmd );
+ }
+ else
+ {
+ return ( NULL != mLastCmd );
+ }
+}
+
+void LLTextEditor::setKeystrokeCallback(const keystroke_signal_t::slot_type& callback)
+{
+ mKeystrokeSignal.connect(callback);
+}
+
+void LLTextEditor::onKeyStroke()
+{
+ mKeystrokeSignal(this);
+
+ mSpellCheckStart = mSpellCheckEnd = -1;
+ mSpellCheckTimer.setTimerExpirySec(SPELLCHECK_DELAY);
+}
+
+//virtual
+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 873a028702..2bffc813a5 100644
--- a/indra/llui/lltexteditor.h
+++ b/indra/llui/lltexteditor.h
@@ -1,351 +1,351 @@
-/**
- * @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$
- */
-
-// Text editor widget to let users enter a a multi-line ASCII document//
-
-#ifndef LL_LLTEXTEDITOR_H
-#define LL_LLTEXTEDITOR_H
-
-#include "llrect.h"
-#include "llframetimer.h"
-#include "llstyle.h"
-#include "lleditmenuhandler.h"
-#include "llviewborder.h" // for params
-#include "lltextbase.h"
-#include "lltextvalidate.h"
-
-#include "llpreeditor.h"
-#include "llcontrol.h"
-
-class LLFontGL;
-class LLScrollbar;
-class TextCmd;
-class LLUICtrlFactory;
-class LLScrollContainer;
-
-class LLTextEditor :
- public LLTextBase,
- protected LLPreeditor
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLTextBase::Params>
- {
- Optional<std::string> default_text;
- Optional<LLTextValidate::validate_func_t, LLTextValidate::ValidateTextNamedFuncs> prevalidate_callback;
-
- Optional<bool> embedded_items,
- ignore_tab,
- commit_on_focus_lost,
- show_context_menu,
- show_emoji_helper,
- enable_tooltip_paste,
- auto_indent;
-
- //colors
- Optional<LLUIColor> default_color;
-
- Params();
- };
-
- void initFromParams(const Params&);
-protected:
- LLTextEditor(const Params&);
- friend class LLUICtrlFactory;
-public:
- //
- // Constants
- //
- static const llwchar FIRST_EMBEDDED_CHAR = 0x100000;
- static const llwchar LAST_EMBEDDED_CHAR = 0x10ffff;
- static const S32 MAX_EMBEDDED_ITEMS = LAST_EMBEDDED_CHAR - FIRST_EMBEDDED_CHAR + 1;
-
- virtual ~LLTextEditor();
-
- typedef boost::signals2::signal<void (LLTextEditor* caller)> keystroke_signal_t;
-
- void setKeystrokeCallback(const keystroke_signal_t::slot_type& callback);
-
- void setParseHighlights(bool parsing) {mParseHighlights=parsing;}
-
- static S32 spacesPerTab();
-
- void insertEmoji(llwchar emoji);
- void handleEmojiCommit(llwchar emoji);
-
- // mousehandler overrides
- virtual bool handleMouseDown(S32 x, S32 y, MASK mask);
- virtual bool handleMouseUp(S32 x, S32 y, MASK mask);
- virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask);
- virtual bool handleHover(S32 x, S32 y, MASK mask);
- virtual bool handleDoubleClick(S32 x, S32 y, MASK mask );
- virtual bool handleMiddleMouseDown(S32 x,S32 y,MASK mask);
-
- virtual bool handleKeyHere(KEY key, MASK mask );
- virtual bool handleUnicodeCharHere(llwchar uni_char);
-
- virtual void onMouseCaptureLost();
-
- // view overrides
- virtual void draw();
- virtual void onFocusReceived();
- virtual void onFocusLost();
- virtual void onCommit();
- virtual void setEnabled(bool enabled);
-
- // uictrl overrides
- virtual void clear();
- virtual void setFocus( bool b );
- virtual bool isDirty() const;
-
- // LLEditMenuHandler interface
- virtual void undo();
- virtual bool canUndo() const;
- virtual void redo();
- virtual bool canRedo() const;
-
- virtual void cut();
- virtual bool canCut() const;
- virtual void copy();
- virtual bool canCopy() const;
- virtual void paste();
- virtual bool canPaste() const;
-
- virtual void updatePrimary();
- virtual void copyPrimary();
- virtual void pastePrimary();
- virtual bool canPastePrimary() const;
-
- virtual void doDelete();
- virtual bool canDoDelete() const;
- 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();
-
- // Text editing
- virtual void makePristine();
- 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; }
-
- /*virtual*/ void onSpellCheckPerformed();
-
- //
- // 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
- void setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params = LLStyle::Params());
-
-
- // Removes text from the end of document
- // Does not change highlight or cursor position.
- void removeTextFromEnd(S32 num_chars);
-
- bool tryToRevertToPristineState();
-
- void setCursorAndScrollToEnd();
-
- void getCurrentLineAndColumn( S32* line, S32* col, bool include_wordwrap );
-
- // Hacky methods to make it into a word-wrapping, potentially scrolling,
- // read-only text box.
- void setCommitOnFocusLost(bool b) { mCommitOnFocusLost = b; }
-
- // Hack to handle Notecards
- virtual bool importBuffer(const char* buffer, S32 length );
- virtual bool exportBuffer(std::string& buffer );
-
- const LLUUID& getSourceID() const { return mSourceID; }
-
- const LLTextSegmentPtr getPreviousSegment() const;
- const LLTextSegmentPtr getLastSegment() const;
- void getSelectedSegments(segment_vec_t& segments) const;
-
- void setShowContextMenu(bool show) { mShowContextMenu = show; }
- bool getShowContextMenu() const { return mShowContextMenu; }
-
- void showEmojiHelper();
- void setShowEmojiHelper(bool show);
- bool getShowEmojiHelper() const { return mShowEmojiHelper; }
-
- 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 handleSelectionKey(const KEY key, const MASK mask);
- bool handleControlKey(const KEY key, const MASK mask);
-
- bool selectionContainsLineBreaks();
- void deleteSelection(bool transient_operation);
-
- S32 prevWordPos(S32 cursorPos) const;
- 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);
-
- // Undoable operations
- void addChar(llwchar c); // at mCursorPos
- S32 addChar(S32 pos, llwchar wc);
- 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 tryToShowEmojiHelper();
- void focusLostHelper();
- void updateAllowingLanguageInput();
- bool hasPreeditString() const;
-
- // Overrides LLPreeditor
- virtual void resetPreedit();
- virtual void updatePreedit(const LLWString &preedit_string,
- const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position);
- virtual void markAsPreedit(S32 position, S32 length);
- virtual void getPreeditRange(S32 *position, S32 *length) const;
- virtual void getSelectionRange(S32 *position, S32 *length) const;
- virtual bool getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const;
- virtual S32 getPreeditFontSize() const;
- virtual LLWString getPreeditString() const { return getWText(); }
- //
- // Protected data
- //
- // 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;
- LLPreeditor::standouts_t mPreeditStandouts;
-
-protected:
- LLUIColor mDefaultColor;
-
- bool mAutoIndent;
- bool mParseOnTheFly;
-
- void updateLinkSegments();
- void keepSelectionOnReturn(bool keep) { mKeepSelectionOnReturn = keep; }
- class LLViewBorder* mBorder;
-
-private:
- //
- // Methods
- //
- void pasteHelper(bool is_primary);
- void cleanStringForPaste(LLWString & clean_string);
- void pasteTextWithLinebreaks(LLWString & clean_string);
-
- void onKeyStroke();
-
- // Concrete TextCmd sub-classes used by the LLTextEditor base class
- class TextCmdInsert;
- class TextCmdAddChar;
- class TextCmdOverwriteChar;
- class TextCmdRemove;
-
- bool mBaseDocIsPristine;
- TextCmd* mPristineCmd;
-
- TextCmd* mLastCmd;
-
- typedef std::deque<TextCmd*> undo_stack_t;
- undo_stack_t mUndoStack;
-
- bool mTabsToNextField; // if true, tab moves focus to next field, else inserts spaces
- bool mCommitOnFocusLost;
- bool mTakesFocus;
-
- bool mAllowEmbeddedItems;
- bool mShowContextMenu;
- bool mShowEmojiHelper;
- bool mEnableTooltipPaste;
- bool mPassDelete;
- bool mKeepSelectionOnReturn; // disabling of removing selected text after pressing of Enter
-
- LLUUID mSourceID;
-
- LLCoordGL mLastIMEPosition; // Last position of the IME editor
-
- keystroke_signal_t mKeystrokeSignal;
- LLTextValidate::validate_func_t mPrevalidateFunc;
-
- LLHandle<LLContextMenu> mContextMenuHandle;
-}; // end class LLTextEditor
-
-// Build time optimization, generate once in .cpp file
-#ifndef LLTEXTEDITOR_CPP
-extern template class LLTextEditor* LLView::getChild<class LLTextEditor>(
- const std::string& name, bool recurse) const;
-#endif
-
-#endif // LL_TEXTEDITOR_H
+/**
+ * @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$
+ */
+
+// Text editor widget to let users enter a a multi-line ASCII document//
+
+#ifndef LL_LLTEXTEDITOR_H
+#define LL_LLTEXTEDITOR_H
+
+#include "llrect.h"
+#include "llframetimer.h"
+#include "llstyle.h"
+#include "lleditmenuhandler.h"
+#include "llviewborder.h" // for params
+#include "lltextbase.h"
+#include "lltextvalidate.h"
+
+#include "llpreeditor.h"
+#include "llcontrol.h"
+
+class LLFontGL;
+class LLScrollbar;
+class TextCmd;
+class LLUICtrlFactory;
+class LLScrollContainer;
+
+class LLTextEditor :
+ public LLTextBase,
+ protected LLPreeditor
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLTextBase::Params>
+ {
+ Optional<std::string> default_text;
+ Optional<LLTextValidate::Validator, LLTextValidate::Validators> prevalidator;
+
+ Optional<bool> embedded_items,
+ ignore_tab,
+ commit_on_focus_lost,
+ show_context_menu,
+ show_emoji_helper,
+ enable_tooltip_paste,
+ auto_indent;
+
+ //colors
+ Optional<LLUIColor> default_color;
+
+ Params();
+ };
+
+ void initFromParams(const Params&);
+protected:
+ LLTextEditor(const Params&);
+ friend class LLUICtrlFactory;
+public:
+ //
+ // Constants
+ //
+ static const llwchar FIRST_EMBEDDED_CHAR = 0x100000;
+ static const llwchar LAST_EMBEDDED_CHAR = 0x10ffff;
+ static const S32 MAX_EMBEDDED_ITEMS = LAST_EMBEDDED_CHAR - FIRST_EMBEDDED_CHAR + 1;
+
+ virtual ~LLTextEditor();
+
+ typedef boost::signals2::signal<void (LLTextEditor* caller)> keystroke_signal_t;
+
+ void setKeystrokeCallback(const keystroke_signal_t::slot_type& callback);
+
+ void setParseHighlights(bool parsing) {mParseHighlights=parsing;}
+
+ static S32 spacesPerTab();
+
+ void insertEmoji(llwchar emoji);
+ void handleEmojiCommit(llwchar emoji);
+
+ // mousehandler overrides
+ virtual bool handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual bool handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask);
+ virtual bool handleHover(S32 x, S32 y, MASK mask);
+ virtual bool handleDoubleClick(S32 x, S32 y, MASK mask );
+ virtual bool handleMiddleMouseDown(S32 x,S32 y,MASK mask);
+
+ virtual bool handleKeyHere(KEY key, MASK mask );
+ virtual bool handleUnicodeCharHere(llwchar uni_char);
+
+ virtual void onMouseCaptureLost();
+
+ // view overrides
+ virtual void draw();
+ virtual void onFocusReceived();
+ virtual void onFocusLost();
+ virtual void onCommit();
+ virtual void setEnabled(bool enabled);
+
+ // uictrl overrides
+ virtual void clear();
+ virtual void setFocus( bool b );
+ virtual bool isDirty() const;
+
+ // LLEditMenuHandler interface
+ virtual void undo();
+ virtual bool canUndo() const;
+ virtual void redo();
+ virtual bool canRedo() const;
+
+ virtual void cut();
+ virtual bool canCut() const;
+ virtual void copy();
+ virtual bool canCopy() const;
+ virtual void paste();
+ virtual bool canPaste() const;
+
+ virtual void updatePrimary();
+ virtual void copyPrimary();
+ virtual void pastePrimary();
+ virtual bool canPastePrimary() const;
+
+ virtual void doDelete();
+ virtual bool canDoDelete() const;
+ 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();
+
+ // Text editing
+ virtual void makePristine();
+ 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; }
+
+ /*virtual*/ void onSpellCheckPerformed();
+
+ //
+ // 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
+ void setText(const LLStringExplicit &utf8str, const LLStyle::Params& input_params = LLStyle::Params());
+
+
+ // Removes text from the end of document
+ // Does not change highlight or cursor position.
+ void removeTextFromEnd(S32 num_chars);
+
+ bool tryToRevertToPristineState();
+
+ void setCursorAndScrollToEnd();
+
+ void getCurrentLineAndColumn( S32* line, S32* col, bool include_wordwrap );
+
+ // Hacky methods to make it into a word-wrapping, potentially scrolling,
+ // read-only text box.
+ void setCommitOnFocusLost(bool b) { mCommitOnFocusLost = b; }
+
+ // Hack to handle Notecards
+ virtual bool importBuffer(const char* buffer, S32 length );
+ virtual bool exportBuffer(std::string& buffer );
+
+ const LLUUID& getSourceID() const { return mSourceID; }
+
+ const LLTextSegmentPtr getPreviousSegment() const;
+ const LLTextSegmentPtr getLastSegment() const;
+ void getSelectedSegments(segment_vec_t& segments) const;
+
+ void setShowContextMenu(bool show) { mShowContextMenu = show; }
+ bool getShowContextMenu() const { return mShowContextMenu; }
+
+ void showEmojiHelper();
+ void setShowEmojiHelper(bool show);
+ bool getShowEmojiHelper() const { return mShowEmojiHelper; }
+
+ 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 handleSelectionKey(const KEY key, const MASK mask);
+ bool handleControlKey(const KEY key, const MASK mask);
+
+ bool selectionContainsLineBreaks();
+ void deleteSelection(bool transient_operation);
+
+ S32 prevWordPos(S32 cursorPos) const;
+ 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);
+
+ // Undoable operations
+ void addChar(llwchar c); // at mCursorPos
+ S32 addChar(S32 pos, llwchar wc);
+ 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 tryToShowEmojiHelper();
+ void focusLostHelper();
+ void updateAllowingLanguageInput();
+ bool hasPreeditString() const;
+
+ // Overrides LLPreeditor
+ virtual void resetPreedit();
+ virtual void updatePreedit(const LLWString &preedit_string,
+ const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position);
+ virtual void markAsPreedit(S32 position, S32 length);
+ virtual void getPreeditRange(S32 *position, S32 *length) const;
+ virtual void getSelectionRange(S32 *position, S32 *length) const;
+ virtual bool getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const;
+ virtual S32 getPreeditFontSize() const;
+ virtual LLWString getPreeditString() const { return getWText(); }
+ //
+ // Protected data
+ //
+ // 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;
+ LLPreeditor::standouts_t mPreeditStandouts;
+
+protected:
+ LLUIColor mDefaultColor;
+
+ bool mAutoIndent;
+ bool mParseOnTheFly;
+
+ void updateLinkSegments();
+ void keepSelectionOnReturn(bool keep) { mKeepSelectionOnReturn = keep; }
+ class LLViewBorder* mBorder;
+
+private:
+ //
+ // Methods
+ //
+ void pasteHelper(bool is_primary);
+ void cleanStringForPaste(LLWString & clean_string);
+ void pasteTextWithLinebreaks(LLWString & clean_string);
+
+ void onKeyStroke();
+
+ // Concrete TextCmd sub-classes used by the LLTextEditor base class
+ class TextCmdInsert;
+ class TextCmdAddChar;
+ class TextCmdOverwriteChar;
+ class TextCmdRemove;
+
+ bool mBaseDocIsPristine;
+ TextCmd* mPristineCmd;
+
+ TextCmd* mLastCmd;
+
+ typedef std::deque<TextCmd*> undo_stack_t;
+ undo_stack_t mUndoStack;
+
+ bool mTabsToNextField; // if true, tab moves focus to next field, else inserts spaces
+ bool mCommitOnFocusLost;
+ bool mTakesFocus;
+
+ bool mAllowEmbeddedItems;
+ bool mShowContextMenu;
+ bool mShowEmojiHelper;
+ bool mEnableTooltipPaste;
+ bool mPassDelete;
+ bool mKeepSelectionOnReturn; // disabling of removing selected text after pressing of Enter
+
+ LLUUID mSourceID;
+
+ LLCoordGL mLastIMEPosition; // Last position of the IME editor
+
+ keystroke_signal_t mKeystrokeSignal;
+ LLTextValidate::Validator mPrevalidator;
+
+ LLHandle<LLContextMenu> mContextMenuHandle;
+}; // end class LLTextEditor
+
+// Build time optimization, generate once in .cpp file
+#ifndef LLTEXTEDITOR_CPP
+extern template class LLTextEditor* LLView::getChild<class LLTextEditor>(
+ const std::string& name, bool recurse) const;
+#endif
+
+#endif // LL_TEXTEDITOR_H
diff --git a/indra/llui/lltextparser.cpp b/indra/llui/lltextparser.cpp
index e9fc6592d0..be195e1146 100644
--- a/indra/llui/lltextparser.cpp
+++ b/indra/llui/lltextparser.cpp
@@ -1,239 +1,239 @@
-/**
- * @file lltextparser.cpp
- *
- * $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 "lltextparser.h"
-
-#include "llsd.h"
-#include "llsdserialize.h"
-#include "llerror.h"
-#include "lluuid.h"
-#include "llstring.h"
-#include "message.h"
-#include "llmath.h"
-#include "v4color.h"
-#include "lldir.h"
-
-//
-// Member Functions
-//
-
-LLTextParser::LLTextParser()
-: mLoaded(false)
-{}
-
-
-S32 LLTextParser::findPattern(const std::string &text, LLSD highlight)
-{
- if (!highlight.has("pattern")) return -1;
-
- std::string pattern=std::string(highlight["pattern"]);
- std::string ltext=text;
-
- if (!(bool)highlight["case_sensitive"])
- {
- ltext = utf8str_tolower(text);
- pattern= utf8str_tolower(pattern);
- }
-
- size_t found=std::string::npos;
-
- switch ((S32)highlight["condition"])
- {
- case CONTAINS:
- found = ltext.find(pattern);
- break;
- case MATCHES:
- found = (! ltext.compare(pattern) ? 0 : std::string::npos);
- break;
- case STARTS_WITH:
- found = (! ltext.find(pattern) ? 0 : std::string::npos);
- break;
- case ENDS_WITH:
- S32 pos = ltext.rfind(pattern);
- if (pos >= 0 && (ltext.length()-pattern.length()==pos)) found = pos;
- break;
- }
- return found;
-}
-
-LLSD LLTextParser::parsePartialLineHighlights(const std::string &text, const LLColor4 &color, EHighlightPosition part, S32 index)
-{
- loadKeywords();
-
- //evil recursive string atomizer.
- LLSD ret_llsd, start_llsd, middle_llsd, end_llsd;
-
- for (S32 i=index;i<mHighlights.size();i++)
- {
- S32 condition = mHighlights[i]["condition"];
- if ((S32)mHighlights[i]["highlight"]==PART && condition!=MATCHES)
- {
- if ( (condition==STARTS_WITH && part==START) ||
- (condition==ENDS_WITH && part==END) ||
- condition==CONTAINS || part==WHOLE )
- {
- S32 start = findPattern(text,mHighlights[i]);
- if (start >= 0 )
- {
- S32 end = std::string(mHighlights[i]["pattern"]).length();
- S32 len = text.length();
- EHighlightPosition newpart;
- if (start==0)
- {
- start_llsd[0]["text"] =text.substr(0,end);
- start_llsd[0]["color"]=mHighlights[i]["color"];
-
- if (end < len)
- {
- if (part==END || part==WHOLE) newpart=END; else newpart=MIDDLE;
- end_llsd=parsePartialLineHighlights(text.substr( end ),color,newpart,i);
- }
- }
- else
- {
- if (part==START || part==WHOLE) newpart=START; else newpart=MIDDLE;
-
- start_llsd=parsePartialLineHighlights(text.substr(0,start),color,newpart,i+1);
-
- if (end < len)
- {
- middle_llsd[0]["text"] =text.substr(start,end);
- middle_llsd[0]["color"]=mHighlights[i]["color"];
-
- if (part==END || part==WHOLE) newpart=END; else newpart=MIDDLE;
-
- end_llsd=parsePartialLineHighlights(text.substr( (start+end) ),color,newpart,i);
- }
- else
- {
- end_llsd[0]["text"] =text.substr(start,end);
- end_llsd[0]["color"]=mHighlights[i]["color"];
- }
- }
-
- S32 retcount=0;
-
- //FIXME These loops should be wrapped into a subroutine.
- for (LLSD::array_iterator iter = start_llsd.beginArray();
- iter != start_llsd.endArray();++iter)
- {
- LLSD highlight = *iter;
- ret_llsd[retcount++]=highlight;
- }
-
- for (LLSD::array_iterator iter = middle_llsd.beginArray();
- iter != middle_llsd.endArray();++iter)
- {
- LLSD highlight = *iter;
- ret_llsd[retcount++]=highlight;
- }
-
- for (LLSD::array_iterator iter = end_llsd.beginArray();
- iter != end_llsd.endArray();++iter)
- {
- LLSD highlight = *iter;
- ret_llsd[retcount++]=highlight;
- }
-
- return ret_llsd;
- }
- }
- }
- }
-
- //No patterns found. Just send back what was passed in.
- ret_llsd[0]["text"] =text;
- LLSD color_sd = color.getValue();
- ret_llsd[0]["color"]=color_sd;
- return ret_llsd;
-}
-
-bool LLTextParser::parseFullLineHighlights(const std::string &text, LLColor4 *color)
-{
- loadKeywords();
-
- for (S32 i=0;i<mHighlights.size();i++)
- {
- if ((S32)mHighlights[i]["highlight"]==ALL || (S32)mHighlights[i]["condition"]==MATCHES)
- {
- if (findPattern(text,mHighlights[i]) >= 0 )
- {
- LLSD color_llsd = mHighlights[i]["color"];
- color->setValue(color_llsd);
- return true;
- }
- }
- }
- return false; //No matches found.
-}
-
-std::string LLTextParser::getFileName()
-{
- std::string path=gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "");
-
- if (!path.empty())
- {
- path = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "highlights.xml");
- }
- return path;
-}
-
-void LLTextParser::loadKeywords()
-{
- if (mLoaded)
- {// keywords already loaded
- return;
- }
- std::string filename=getFileName();
- if (!filename.empty())
- {
- llifstream file;
- file.open(filename.c_str());
- if (file.is_open())
- {
- LLSDSerialize::fromXML(mHighlights, file);
- }
- file.close();
- mLoaded = true;
- }
-}
-
-bool LLTextParser::saveToDisk(LLSD highlights)
-{
- mHighlights=highlights;
- std::string filename=getFileName();
- if (filename.empty())
- {
- LL_WARNS() << "LLTextParser::saveToDisk() no valid user directory." << LL_ENDL;
- return false;
- }
- llofstream file;
- file.open(filename.c_str());
- LLSDSerialize::toPrettyXML(mHighlights, file);
- file.close();
- return true;
-}
+/**
+ * @file lltextparser.cpp
+ *
+ * $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 "lltextparser.h"
+
+#include "llsd.h"
+#include "llsdserialize.h"
+#include "llerror.h"
+#include "lluuid.h"
+#include "llstring.h"
+#include "message.h"
+#include "llmath.h"
+#include "v4color.h"
+#include "lldir.h"
+
+//
+// Member Functions
+//
+
+LLTextParser::LLTextParser()
+: mLoaded(false)
+{}
+
+
+S32 LLTextParser::findPattern(const std::string &text, LLSD highlight)
+{
+ if (!highlight.has("pattern")) return -1;
+
+ std::string pattern=std::string(highlight["pattern"]);
+ std::string ltext=text;
+
+ if (!(bool)highlight["case_sensitive"])
+ {
+ ltext = utf8str_tolower(text);
+ pattern= utf8str_tolower(pattern);
+ }
+
+ size_t found=std::string::npos;
+
+ switch ((S32)highlight["condition"])
+ {
+ case CONTAINS:
+ found = ltext.find(pattern);
+ break;
+ case MATCHES:
+ found = (! ltext.compare(pattern) ? 0 : std::string::npos);
+ break;
+ case STARTS_WITH:
+ found = (! ltext.find(pattern) ? 0 : std::string::npos);
+ break;
+ case ENDS_WITH:
+ S32 pos = ltext.rfind(pattern);
+ if (pos >= 0 && (ltext.length()-pattern.length()==pos)) found = pos;
+ break;
+ }
+ return found;
+}
+
+LLSD LLTextParser::parsePartialLineHighlights(const std::string &text, const LLColor4 &color, EHighlightPosition part, S32 index)
+{
+ loadKeywords();
+
+ //evil recursive string atomizer.
+ LLSD ret_llsd, start_llsd, middle_llsd, end_llsd;
+
+ for (S32 i=index;i<mHighlights.size();i++)
+ {
+ S32 condition = mHighlights[i]["condition"];
+ if ((S32)mHighlights[i]["highlight"]==PART && condition!=MATCHES)
+ {
+ if ( (condition==STARTS_WITH && part==START) ||
+ (condition==ENDS_WITH && part==END) ||
+ condition==CONTAINS || part==WHOLE )
+ {
+ S32 start = findPattern(text,mHighlights[i]);
+ if (start >= 0 )
+ {
+ S32 end = std::string(mHighlights[i]["pattern"]).length();
+ S32 len = text.length();
+ EHighlightPosition newpart;
+ if (start==0)
+ {
+ start_llsd[0]["text"] =text.substr(0,end);
+ start_llsd[0]["color"]=mHighlights[i]["color"];
+
+ if (end < len)
+ {
+ if (part==END || part==WHOLE) newpart=END; else newpart=MIDDLE;
+ end_llsd=parsePartialLineHighlights(text.substr( end ),color,newpart,i);
+ }
+ }
+ else
+ {
+ if (part==START || part==WHOLE) newpart=START; else newpart=MIDDLE;
+
+ start_llsd=parsePartialLineHighlights(text.substr(0,start),color,newpart,i+1);
+
+ if (end < len)
+ {
+ middle_llsd[0]["text"] =text.substr(start,end);
+ middle_llsd[0]["color"]=mHighlights[i]["color"];
+
+ if (part==END || part==WHOLE) newpart=END; else newpart=MIDDLE;
+
+ end_llsd=parsePartialLineHighlights(text.substr( (start+end) ),color,newpart,i);
+ }
+ else
+ {
+ end_llsd[0]["text"] =text.substr(start,end);
+ end_llsd[0]["color"]=mHighlights[i]["color"];
+ }
+ }
+
+ S32 retcount=0;
+
+ //FIXME These loops should be wrapped into a subroutine.
+ for (LLSD::array_iterator iter = start_llsd.beginArray();
+ iter != start_llsd.endArray();++iter)
+ {
+ LLSD highlight = *iter;
+ ret_llsd[retcount++]=highlight;
+ }
+
+ for (LLSD::array_iterator iter = middle_llsd.beginArray();
+ iter != middle_llsd.endArray();++iter)
+ {
+ LLSD highlight = *iter;
+ ret_llsd[retcount++]=highlight;
+ }
+
+ for (LLSD::array_iterator iter = end_llsd.beginArray();
+ iter != end_llsd.endArray();++iter)
+ {
+ LLSD highlight = *iter;
+ ret_llsd[retcount++]=highlight;
+ }
+
+ return ret_llsd;
+ }
+ }
+ }
+ }
+
+ //No patterns found. Just send back what was passed in.
+ ret_llsd[0]["text"] =text;
+ LLSD color_sd = color.getValue();
+ ret_llsd[0]["color"]=color_sd;
+ return ret_llsd;
+}
+
+bool LLTextParser::parseFullLineHighlights(const std::string &text, LLColor4 *color)
+{
+ loadKeywords();
+
+ for (S32 i=0;i<mHighlights.size();i++)
+ {
+ if ((S32)mHighlights[i]["highlight"]==ALL || (S32)mHighlights[i]["condition"]==MATCHES)
+ {
+ if (findPattern(text,mHighlights[i]) >= 0 )
+ {
+ LLSD color_llsd = mHighlights[i]["color"];
+ color->setValue(color_llsd);
+ return true;
+ }
+ }
+ }
+ return false; //No matches found.
+}
+
+std::string LLTextParser::getFileName()
+{
+ std::string path=gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "");
+
+ if (!path.empty())
+ {
+ path = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "highlights.xml");
+ }
+ return path;
+}
+
+void LLTextParser::loadKeywords()
+{
+ if (mLoaded)
+ {// keywords already loaded
+ return;
+ }
+ std::string filename=getFileName();
+ if (!filename.empty())
+ {
+ llifstream file;
+ file.open(filename.c_str());
+ if (file.is_open())
+ {
+ LLSDSerialize::fromXML(mHighlights, file);
+ }
+ file.close();
+ mLoaded = true;
+ }
+}
+
+bool LLTextParser::saveToDisk(LLSD highlights)
+{
+ mHighlights=highlights;
+ std::string filename=getFileName();
+ if (filename.empty())
+ {
+ LL_WARNS() << "LLTextParser::saveToDisk() no valid user directory." << LL_ENDL;
+ return false;
+ }
+ llofstream file;
+ file.open(filename.c_str());
+ LLSDSerialize::toPrettyXML(mHighlights, file);
+ file.close();
+ return true;
+}
diff --git a/indra/llui/lltextparser.h b/indra/llui/lltextparser.h
index 3d71e40452..20fcc07e4c 100644
--- a/indra/llui/lltextparser.h
+++ b/indra/llui/lltextparser.h
@@ -1,25 +1,25 @@
-/**
+/**
* @file llTextParser.h
* @brief GUI for user-defined highlights
*
* $LicenseInfo:firstyear=2002&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$
*
@@ -37,26 +37,26 @@ class LLColor4;
class LLTextParser : public LLSingleton<LLTextParser>
{
- LLSINGLETON(LLTextParser);
+ LLSINGLETON(LLTextParser);
public:
- typedef enum e_condition_type { CONTAINS, MATCHES, STARTS_WITH, ENDS_WITH } EConditionType;
- typedef enum e_highlight_type { PART, ALL } EHighlightType;
- typedef enum e_highlight_position { WHOLE, START, MIDDLE, END } EHighlightPosition;
- typedef enum e_dialog_action { ACTION_NONE, ACTION_CLOSE, ACTION_ADD, ACTION_COPY, ACTION_UPDATE } EDialogAction;
+ typedef enum e_condition_type { CONTAINS, MATCHES, STARTS_WITH, ENDS_WITH } EConditionType;
+ typedef enum e_highlight_type { PART, ALL } EHighlightType;
+ typedef enum e_highlight_position { WHOLE, START, MIDDLE, END } EHighlightPosition;
+ typedef enum e_dialog_action { ACTION_NONE, ACTION_CLOSE, ACTION_ADD, ACTION_COPY, ACTION_UPDATE } EDialogAction;
- LLSD parsePartialLineHighlights(const std::string &text,const LLColor4 &color, EHighlightPosition part=WHOLE, S32 index=0);
- bool parseFullLineHighlights(const std::string &text, LLColor4 *color);
+ LLSD parsePartialLineHighlights(const std::string &text,const LLColor4 &color, EHighlightPosition part=WHOLE, S32 index=0);
+ bool parseFullLineHighlights(const std::string &text, LLColor4 *color);
private:
- S32 findPattern(const std::string &text, LLSD highlight);
- std::string getFileName();
- void loadKeywords();
- bool saveToDisk(LLSD highlights);
+ S32 findPattern(const std::string &text, LLSD highlight);
+ std::string getFileName();
+ void loadKeywords();
+ bool saveToDisk(LLSD highlights);
public:
- LLSD mHighlights;
- bool mLoaded;
+ LLSD mHighlights;
+ bool mLoaded;
};
#endif
diff --git a/indra/llui/lltextutil.cpp b/indra/llui/lltextutil.cpp
index 78049319bc..8ffce1b8b4 100644
--- a/indra/llui/lltextutil.cpp
+++ b/indra/llui/lltextutil.cpp
@@ -1,25 +1,25 @@
-/**
+/**
* @file lltextutil.cpp
* @brief Misc text-related auxiliary methods
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -30,30 +30,30 @@
#include "lltextbox.h"
#include "llurlmatch.h"
-boost::function<bool(LLUrlMatch*,LLTextBase*)> LLTextUtil::TextHelpers::iconCallbackCreationFunction = 0;
+boost::function<bool(LLUrlMatch*,LLTextBase*)> LLTextUtil::TextHelpers::iconCallbackCreationFunction = 0;
void LLTextUtil::textboxSetHighlightedVal(LLTextBox *txtbox, const LLStyle::Params& normal_style, const std::string& text, const std::string& hl)
{
- static LLUIColor sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", LLColor4::green);
+ static LLUIColor sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", LLColor4::green);
- std::string text_uc = text;
- LLStringUtil::toUpper(text_uc);
+ std::string text_uc = text;
+ LLStringUtil::toUpper(text_uc);
- size_t hl_begin = 0, hl_len = hl.size();
+ size_t hl_begin = 0, hl_len = hl.size();
- if (hl_len == 0 || (hl_begin = text_uc.find(hl)) == std::string::npos)
- {
- txtbox->setText(text, normal_style);
- return;
- }
+ if (hl_len == 0 || (hl_begin = text_uc.find(hl)) == std::string::npos)
+ {
+ txtbox->setText(text, normal_style);
+ return;
+ }
- LLStyle::Params hl_style = normal_style;
- hl_style.color = sFilterTextColor;
+ LLStyle::Params hl_style = normal_style;
+ hl_style.color = sFilterTextColor;
- txtbox->setText(LLStringUtil::null); // clear text
- txtbox->appendText(text.substr(0, hl_begin), false, normal_style);
- txtbox->appendText(text.substr(hl_begin, hl_len), false, hl_style);
- txtbox->appendText(text.substr(hl_begin + hl_len), false, normal_style);
+ txtbox->setText(LLStringUtil::null); // clear text
+ txtbox->appendText(text.substr(0, hl_begin), false, normal_style);
+ txtbox->appendText(text.substr(hl_begin, hl_len), false, hl_style);
+ txtbox->appendText(text.substr(hl_begin + hl_len), false, normal_style);
}
void LLTextUtil::textboxSetGreyedVal(LLTextBox *txtbox, const LLStyle::Params& normal_style, const std::string& text, const std::string& greyed)
@@ -78,34 +78,34 @@ void LLTextUtil::textboxSetGreyedVal(LLTextBox *txtbox, const LLStyle::Params& n
bool LLTextUtil::processUrlMatch(LLUrlMatch* match,LLTextBase* text_base, bool is_content_trusted)
{
- if (match == 0 || text_base == 0)
- return false;
-
- if(match->getID() != LLUUID::null && TextHelpers::iconCallbackCreationFunction)
- {
- bool segment_created = TextHelpers::iconCallbackCreationFunction(match,text_base);
- if(segment_created)
- return true;
- }
-
- // output an optional icon before the Url
- if (is_content_trusted && !match->getIcon().empty() )
- {
- LLUIImagePtr image = LLUI::getUIImage(match->getIcon());
- if (image)
- {
- LLStyle::Params icon;
- icon.image = image;
- // Text will be replaced during rendering with the icon,
- // but string cannot be empty or the segment won't be
- // added (or drawn).
- text_base->appendImageSegment(icon);
-
- return true;
- }
- }
-
- return false;
+ if (match == 0 || text_base == 0)
+ return false;
+
+ if(match->getID() != LLUUID::null && TextHelpers::iconCallbackCreationFunction)
+ {
+ bool segment_created = TextHelpers::iconCallbackCreationFunction(match,text_base);
+ if(segment_created)
+ return true;
+ }
+
+ // output an optional icon before the Url
+ if (is_content_trusted && !match->getIcon().empty() )
+ {
+ LLUIImagePtr image = LLUI::getUIImage(match->getIcon());
+ if (image)
+ {
+ LLStyle::Params icon;
+ icon.image = image;
+ // Text will be replaced during rendering with the icon,
+ // but string cannot be empty or the segment won't be
+ // added (or drawn).
+ text_base->appendImageSegment(icon);
+
+ return true;
+ }
+ }
+
+ return false;
}
// EOF
diff --git a/indra/llui/lltextutil.h b/indra/llui/lltextutil.h
index 1adc3516f7..f3838e59fa 100644
--- a/indra/llui/lltextutil.h
+++ b/indra/llui/lltextutil.h
@@ -1,25 +1,25 @@
-/**
+/**
* @file lltextutil.h
* @brief Misc text-related auxiliary methods
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
- *
+ *
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
- *
+ *
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
+ *
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -36,48 +36,48 @@ class LLTextBase;
namespace LLTextUtil
{
- /**
- * Set value for text box, highlighting substring hl_uc.
- *
- * Used to highlight filter matches.
- *
- * @param txtbox Text box to set value for
- * @param normal_style Style to use for non-highlighted text
- * @param text Text to set
- * @param hl Upper-cased string to highlight
- */
- void textboxSetHighlightedVal(
- LLTextBox *txtbox,
- const LLStyle::Params& normal_style,
- const std::string& text,
- const std::string& hl);
+ /**
+ * Set value for text box, highlighting substring hl_uc.
+ *
+ * Used to highlight filter matches.
+ *
+ * @param txtbox Text box to set value for
+ * @param normal_style Style to use for non-highlighted text
+ * @param text Text to set
+ * @param hl Upper-cased string to highlight
+ */
+ void textboxSetHighlightedVal(
+ LLTextBox *txtbox,
+ const LLStyle::Params& normal_style,
+ const std::string& text,
+ const std::string& hl);
+
+ void textboxSetGreyedVal(
+ LLTextBox *txtbox,
+ const LLStyle::Params& normal_style,
+ const std::string& text,
+ const std::string& greyed);
- void textboxSetGreyedVal(
- LLTextBox *txtbox,
- const LLStyle::Params& normal_style,
- const std::string& text,
- const std::string& greyed);
+ /**
+ * 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);
- /**
- * 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
+ {
- class TextHelpers
- {
+ //we need this special callback since we need to create LLAvataIconCtrls while parsing
+ //avatar/group url but can't create LLAvataIconCtrl from LLUI
+ public:
+ static boost::function<bool(LLUrlMatch*,LLTextBase*)> iconCallbackCreationFunction;
+ };
- //we need this special callback since we need to create LLAvataIconCtrls while parsing
- //avatar/group url but can't create LLAvataIconCtrl from LLUI
- public:
- static boost::function<bool(LLUrlMatch*,LLTextBase*)> iconCallbackCreationFunction;
- };
-
}
#endif // LL_LLTEXTUTIL_H
diff --git a/indra/llui/lltextvalidate.cpp b/indra/llui/lltextvalidate.cpp
index bd3cfbacde..9e27ed6232 100644
--- a/indra/llui/lltextvalidate.cpp
+++ b/indra/llui/lltextvalidate.cpp
@@ -27,330 +27,452 @@
// Text editor widget to let users enter a single line.
#include "linden_common.h"
-
+
#include "lltextvalidate.h"
+
+#include "llnotificationsutil.h"
+#include "lltrans.h"
+
#include "llresmgr.h" // for LLLocale
namespace LLTextValidate
{
- void ValidateTextNamedFuncs::declareValues()
- {
- declare("ascii", validateASCII);
- declare("float", validateFloat);
- declare("int", validateInt);
- declare("positive_s32", validatePositiveS32);
- declare("non_negative_s32", validateNonNegativeS32);
- declare("alpha_num", validateAlphaNum);
- declare("alpha_num_space", validateAlphaNumSpace);
- declare("ascii_printable_no_pipe", validateASCIIPrintableNoPipe);
- declare("ascii_printable_no_space", validateASCIIPrintableNoSpace);
- declare("ascii_with_newline", validateASCIIWithNewLine);
- }
-
- // Limits what characters can be used to [1234567890.-] with [-] only valid in the first position.
- // Does NOT ensure that the string is a well-formed number--that's the job of post-validation--for
- // the simple reasons that intermediate states may be invalid even if the final result is valid.
- //
- bool validateFloat(const LLWString &str)
- {
- LLLocale locale(LLLocale::USER_LOCALE);
-
- bool success = true;
- LLWString trimmed = str;
- LLWStringUtil::trim(trimmed);
- S32 len = trimmed.length();
- if( 0 < len )
- {
- // May be a comma or period, depending on the locale
- llwchar decimal_point = (llwchar)LLResMgr::getInstance()->getDecimalPoint();
-
- S32 i = 0;
-
- // First character can be a negative sign
- if( '-' == trimmed[0] )
- {
- i++;
- }
-
- for( ; i < len; i++ )
- {
- if( (decimal_point != trimmed[i] ) && !LLStringOps::isDigit( trimmed[i] ) )
- {
- success = false;
- break;
- }
- }
- }
-
- return success;
- }
-
- // Limits what characters can be used to [1234567890-] with [-] only valid in the first position.
- // Does NOT ensure that the string is a well-formed number--that's the job of post-validation--for
- // the simple reasons that intermediate states may be invalid even if the final result is valid.
- //
- bool validateInt(const LLWString &str)
- {
- LLLocale locale(LLLocale::USER_LOCALE);
-
- bool success = true;
- LLWString trimmed = str;
- LLWStringUtil::trim(trimmed);
- S32 len = trimmed.length();
- if( 0 < len )
- {
- S32 i = 0;
-
- // First character can be a negative sign
- if( '-' == trimmed[0] )
- {
- i++;
- }
-
- for( ; i < len; i++ )
- {
- if( !LLStringOps::isDigit( trimmed[i] ) )
- {
- success = false;
- break;
- }
- }
- }
-
- return success;
- }
-
- bool validatePositiveS32(const LLWString &str)
- {
- LLLocale locale(LLLocale::USER_LOCALE);
-
- LLWString trimmed = str;
- LLWStringUtil::trim(trimmed);
- S32 len = trimmed.length();
- bool success = true;
- if(0 < len)
- {
- if(('-' == trimmed[0]) || ('0' == trimmed[0]))
- {
- success = false;
- }
- S32 i = 0;
- while(success && (i < len))
- {
- if(!LLStringOps::isDigit(trimmed[i++]))
- {
- success = false;
- }
- }
- }
- if (success)
- {
- S32 val = strtol(wstring_to_utf8str(trimmed).c_str(), NULL, 10);
- if (val <= 0)
- {
- success = false;
- }
- }
- return success;
- }
-
- bool validateNonNegativeS32(const LLWString &str)
- {
- LLLocale locale(LLLocale::USER_LOCALE);
-
- LLWString trimmed = str;
- LLWStringUtil::trim(trimmed);
- S32 len = trimmed.length();
- bool success = true;
- if(0 < len)
- {
- if('-' == trimmed[0])
- {
- success = false;
- }
- S32 i = 0;
- while(success && (i < len))
- {
- if(!LLStringOps::isDigit(trimmed[i++]))
- {
- success = false;
- }
- }
- }
- if (success)
- {
- S32 val = strtol(wstring_to_utf8str(trimmed).c_str(), NULL, 10);
- if (val < 0)
- {
- success = false;
- }
- }
- return success;
- }
-
- bool 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);
-
- bool rv = true;
- S32 len = str.length();
- if(len == 0) return rv;
- while(len--)
- {
- if( !LLStringOps::isAlnum((char)str[len]) )
- {
- rv = false;
- break;
- }
- }
- return rv;
- }
-
- bool validateAlphaNumSpace(const LLWString &str)
- {
- LLLocale locale(LLLocale::USER_LOCALE);
-
- bool rv = true;
- S32 len = str.length();
- if(len == 0) return rv;
- while(len--)
- {
- if(!(LLStringOps::isAlnum((char)str[len]) || (' ' == str[len])))
- {
- rv = false;
- break;
- }
- }
- return rv;
- }
-
- // Used for most names of things stored on the server, due to old file-formats
- // that used the pipe (|) for multiline text storage. Examples include
- // inventory item names, parcel names, object names, etc.
- bool validateASCIIPrintableNoPipe(const LLWString &str)
- {
- bool rv = true;
- S32 len = str.length();
- if(len == 0) return rv;
- while(len--)
- {
- llwchar wc = str[len];
- if (wc < 0x20
- || wc > 0x7f
- || wc == '|')
- {
- rv = false;
- break;
- }
- if(!(wc == ' '
- || LLStringOps::isAlnum((char)wc)
- || LLStringOps::isPunct((char)wc) ) )
- {
- rv = false;
- break;
- }
- }
- return rv;
- }
-
-
- // Used for avatar names
- bool validateASCIIPrintableNoSpace(const LLWString &str)
- {
- bool rv = true;
- S32 len = str.length();
- if(len == 0) return rv;
- while(len--)
- {
- llwchar wc = str[len];
- if (wc < 0x20
- || wc > 0x7f
- || LLStringOps::isSpace(wc))
- {
- rv = false;
- break;
- }
- if( !(LLStringOps::isAlnum((char)str[len]) ||
- LLStringOps::isPunct((char)str[len]) ) )
- {
- rv = false;
- break;
- }
- }
- return rv;
- }
-
- bool validateASCII(const LLWString &str)
- {
- bool rv = true;
- S32 len = str.length();
- while(len--)
- {
- if (str[len] < 0x20 || str[len] > 0x7f)
- {
- rv = false;
- break;
- }
- }
- return rv;
- }
-
- bool validateASCIINoLeadingSpace(const LLWString &str)
- {
- if (LLStringOps::isSpace(str[0]))
- {
- return false;
- }
- return validateASCII(str);
- }
-
- // Used for multiline text stored on the server.
- // Example is landmark description in Places SP.
- bool validateASCIIWithNewLine(const LLWString &str)
- {
- bool rv = true;
- S32 len = str.length();
- while(len--)
- {
- if ((str[len] < 0x20 && str[len] != 0xA) || str[len] > 0x7f)
- {
- rv = false;
- break;
- }
- }
- return rv;
- }
+
+static S32 strtol(const std::string& str) { return ::strtol(str.c_str(), NULL, 10); }
+static S32 strtol(const LLWString& str) { return ::strtol(wstring_to_utf8str(str).c_str(), NULL, 10); }
+
+static LLSD llsd(const std::string& str) { return LLSD(str); }
+static LLSD llsd(const LLWString& str) { return LLSD(wstring_to_utf8str(str)); }
+template <class CHAR>
+LLSD llsd(CHAR ch) { return llsd(std::basic_string<CHAR>(1, ch)); }
+
+void ValidatorImpl::setLastErrorShowTime()
+{
+ mLastErrorShowTime = (U32Seconds)LLTimer::getTotalTime();
+}
+
+void Validator::showLastErrorUsingTimeout(U32 timeout)
+{
+ if (mImpl && (U32Seconds)LLTimer::getTotalTime() >= mImpl->getLastErrorShowTime() + timeout)
+ {
+ mImpl->setLastErrorShowTime();
+ std::string reason = LLTrans::getString(mImpl->getLastErrorName(), mImpl->getLastErrorValues());
+ LLNotificationsUtil::add("InvalidKeystroke", LLSD().with("REASON", reason));
+ }
}
+
+// Limits what characters can be used to [1234567890.-] with [-] only valid in the first position.
+// Does NOT ensure that the string is a well-formed number--that's the job of post-validation--for
+// the simple reasons that intermediate states may be invalid even if the final result is valid.
+class ValidatorFloat : public ValidatorImpl
+{
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR> &str)
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ std::basic_string<CHAR> trimmed = str;
+ LLStringUtilBase<CHAR>::trim(trimmed);
+ S32 len = trimmed.length();
+ if (0 < len)
+ {
+ // May be a comma or period, depending on the locale
+ CHAR decimal_point = LLResMgr::getInstance()->getDecimalPoint();
+
+ S32 i = 0;
+
+ // First character can be a negative sign
+ if ('-' == trimmed.front())
+ {
+ i++;
+ }
+
+ for (; i < len; i++)
+ {
+ CHAR ch = trimmed[i];
+ if ((decimal_point != ch) && !LLStringOps::isDigit(ch))
+ {
+ return setError("Validator_ShouldBeDigitOrDot", LLSD().with("NR", i + 1).with("CH", llsd(ch)));
+ }
+ }
+ }
+
+ return resetError();
+ }
+
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorFloatImpl;
+Validator validateFloat(validatorFloatImpl);
+
+// Limits what characters can be used to [1234567890-] with [-] only valid in the first position.
+// Does NOT ensure that the string is a well-formed number--that's the job of post-validation--for
+// the simple reasons that intermediate states may be invalid even if the final result is valid.
+class ValidatorInt : public ValidatorImpl
+{
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR> &str)
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ std::basic_string<CHAR> trimmed = str;
+ LLStringUtilBase<CHAR>::trim(trimmed);
+ S32 len = trimmed.length();
+ if (0 < len)
+ {
+ S32 i = 0;
+
+ // First character can be a negative sign
+ if ('-' == trimmed.front())
+ {
+ i++;
+ }
+
+ for (; i < len; i++)
+ {
+ CHAR ch = trimmed[i];
+ if (!LLStringOps::isDigit(ch))
+ {
+ return setError("Validator_ShouldBeDigit", LLSD().with("NR", i + 1).with("CH", llsd(ch)));
+ }
+ }
+ }
+
+ return resetError();
+ }
+
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorIntImpl;
+Validator validateInt(validatorIntImpl);
+
+class ValidatorPositiveS32 : public ValidatorImpl
+{
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR>& str)
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ std::basic_string<CHAR> trimmed = str;
+ LLStringUtilBase<CHAR>::trim(trimmed);
+ S32 len = trimmed.length();
+ if (0 < len)
+ {
+ CHAR ch = trimmed.front();
+
+ if (('-' == ch) || ('0' == ch))
+ {
+ return setError("Validator_ShouldNotBeMinusOrZero", LLSD().with("CH", llsd(ch)));
+ }
+
+ for (S32 i = 0; i < len; ++i)
+ {
+ ch = trimmed[i];
+ if (!LLStringOps::isDigit(ch))
+ {
+ return setError("Validator_ShouldBeDigit", LLSD().with("NR", i + 1).with("CH", llsd(ch)));
+ }
+ }
+ }
+
+ S32 val = strtol(trimmed);
+ if (val <= 0)
+ {
+ return setError("Validator_InvalidNumericString", LLSD().with("STR", llsd(trimmed)));
+ }
+
+ return resetError();
+ }
+
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorPositiveS32Impl;
+Validator validatePositiveS32(validatorPositiveS32Impl);
+
+class ValidatorNonNegativeS32 : public ValidatorImpl
+{
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR>& str)
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ std::basic_string<CHAR> trimmed = str;
+ LLStringUtilBase<CHAR>::trim(trimmed);
+ S32 len = trimmed.length();
+ if (0 < len)
+ {
+ CHAR ch = trimmed.front();
+
+ if ('-' == ch)
+ {
+ return setError("Validator_ShouldNotBeMinus", LLSD().with("CH", llsd(ch)));
+ }
+
+ for (S32 i = 0; i < len; ++i)
+ {
+ ch = trimmed[i];
+ if (!LLStringOps::isDigit(ch))
+ {
+ return setError("Validator_ShouldBeDigit", LLSD().with("NR", i + 1).with("CH", llsd(ch)));
+ }
+ }
+ }
+
+ S32 val = strtol(trimmed);
+ if (val < 0)
+ {
+ return setError("Validator_InvalidNumericString", LLSD().with("STR", llsd(trimmed)));
+ }
+
+ return resetError();
+ }
+
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorNonNegativeS32Impl;
+Validator validateNonNegativeS32(validatorNonNegativeS32Impl);
+
+class ValidatorNonNegativeS32NoSpace : public ValidatorImpl
+{
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR>& str)
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ std::basic_string<CHAR> test_str = str;
+ S32 len = test_str.length();
+ if (0 < len)
+ {
+ CHAR ch = test_str.front();
+
+ if ('-' == ch)
+ {
+ return setError("Validator_ShouldNotBeMinus", LLSD().with("CH", llsd(ch)));
+ }
+
+ for (S32 i = 0; i < len; ++i)
+ {
+ ch = test_str[i];
+ if (!LLStringOps::isDigit(ch) || LLStringOps::isSpace(ch))
+ {
+ return setError("Validator_ShouldBeDigitNotSpace", LLSD().with("NR", i + 1).with("CH", llsd(ch)));
+ }
+ }
+ }
+
+ S32 val = strtol(test_str);
+ if (val < 0)
+ {
+ return setError("Validator_InvalidNumericString", LLSD().with("STR", llsd(test_str)));
+ }
+
+ return resetError();
+ }
+
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorNonNegativeS32NoSpaceImpl;
+Validator validateNonNegativeS32NoSpace(validatorNonNegativeS32NoSpaceImpl);
+
+class ValidatorAlphaNum : public ValidatorImpl
+{
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR>& str)
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ S32 len = str.length();
+ while (len--)
+ {
+ CHAR ch = str[len];
+
+ if (!LLStringOps::isAlnum(ch))
+ {
+ return setError("Validator_ShouldBeDigitOrAlpha", LLSD().with("NR", len + 1).with("CH", llsd(ch)));
+ }
+ }
+
+ return resetError();
+ }
+
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorAlphaNumImpl;
+Validator validateAlphaNum(validatorAlphaNumImpl);
+
+class ValidatorAlphaNumSpace : public ValidatorImpl
+{
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR>& str)
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ S32 len = str.length();
+ while (len--)
+ {
+ CHAR ch = str[len];
+
+ if (!LLStringOps::isAlnum(ch) && (' ' != ch))
+ {
+ return setError("Validator_ShouldBeDigitOrAlphaOrSpace", LLSD().with("NR", len + 1).with("CH", llsd(ch)));
+ }
+ }
+
+ return resetError();
+ }
+
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorAlphaNumSpaceImpl;
+Validator validateAlphaNumSpace(validatorAlphaNumSpaceImpl);
+
+// Used for most names of things stored on the server, due to old file-formats
+// that used the pipe (|) for multiline text storage. Examples include
+// inventory item names, parcel names, object names, etc.
+class ValidatorASCIIPrintableNoPipe : public ValidatorImpl
+{
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR>& str)
+ {
+ S32 len = str.length();
+ while (len--)
+ {
+ CHAR ch = str[len];
+
+ if (ch < 0x20 || ch > 0x7f || ch == '|' ||
+ (ch != ' ' && !LLStringOps::isAlnum(ch) && !LLStringOps::isPunct(ch)))
+ {
+ return setError("Validator_ShouldBeDigitOrAlphaOrPunct", LLSD().with("NR", len + 1).with("CH", llsd(ch)));
+ }
+ }
+
+ return resetError();
+ }
+
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorASCIIPrintableNoPipeImpl;
+Validator validateASCIIPrintableNoPipe(validatorASCIIPrintableNoPipeImpl);
+
+// Used for avatar names
+class ValidatorASCIIPrintableNoSpace : public ValidatorImpl
+{
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR>& str)
+ {
+ S32 len = str.length();
+ while (len--)
+ {
+ CHAR ch = str[len];
+
+ if (ch <= 0x20 || ch > 0x7f || LLStringOps::isSpace(ch) ||
+ (!LLStringOps::isAlnum(ch) && !LLStringOps::isPunct(ch)))
+ {
+ return setError("Validator_ShouldBeDigitOrAlphaOrPunctNotSpace", LLSD().with("NR", len + 1).with("CH", llsd(ch)));
+ }
+ }
+
+ return resetError();
+ }
+
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorASCIIPrintableNoSpaceImpl;
+Validator validateASCIIPrintableNoSpace(validatorASCIIPrintableNoSpaceImpl);
+
+class ValidatorASCII : public ValidatorImpl
+{
+protected:
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR>& str)
+ {
+ S32 len = str.length();
+ while (len--)
+ {
+ CHAR ch = str[len];
+
+ if (ch < 0x20 || ch > 0x7f)
+ {
+ return setError("Validator_ShouldBeASCII", LLSD().with("NR", len + 1).with("CH", llsd(ch)));
+ }
+ }
+
+ return resetError();
+ }
+
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorASCIIImpl;
+Validator validateASCII(validatorASCIIImpl);
+
+class ValidatorASCIINoLeadingSpace : public ValidatorASCII
+{
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR>& str)
+ {
+ if (LLStringOps::isSpace(str.front()))
+ {
+ return false;
+ }
+
+ return ValidatorASCII::validate<CHAR>(str);
+ }
+
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorASCIINoLeadingSpaceImpl;
+Validator validateASCIINoLeadingSpace(validatorASCIINoLeadingSpaceImpl);
+
+class ValidatorASCIIWithNewLine : public ValidatorImpl
+{
+ // Used for multiline text stored on the server.
+ // Example is landmark description in Places SP.
+ template <class CHAR>
+ bool validate(const std::basic_string<CHAR>& str)
+ {
+ S32 len = str.length();
+ while (len--)
+ {
+ CHAR ch = str[len];
+
+ if ((ch < 0x20 && ch != 0xA) || ch > 0x7f)
+ {
+ return setError("Validator_ShouldBeNewLineOrASCII", LLSD().with("NR", len + 1).with("CH", llsd(ch)));
+ }
+ }
+
+ return resetError();
+ }
+
+public:
+ /*virtual*/ bool validate(const std::string& str) override { return validate<char>(str); }
+ /*virtual*/ bool validate(const LLWString& str) override { return validate<llwchar>(str); }
+} validatorASCIIWithNewLineImpl;
+Validator validateASCIIWithNewLine(validatorASCIIWithNewLineImpl);
+
+void Validators::declareValues()
+{
+ declare("ascii", validateASCII);
+ declare("float", validateFloat);
+ declare("int", validateInt);
+ declare("positive_s32", validatePositiveS32);
+ declare("non_negative_s32", validateNonNegativeS32);
+ declare("alpha_num", validateAlphaNum);
+ declare("alpha_num_space", validateAlphaNumSpace);
+ declare("ascii_printable_no_pipe", validateASCIIPrintableNoPipe);
+ declare("ascii_printable_no_space", validateASCIIPrintableNoSpace);
+ declare("ascii_with_newline", validateASCIIWithNewLine);
+}
+
+} // namespace LLTextValidate
diff --git a/indra/llui/lltextvalidate.h b/indra/llui/lltextvalidate.h
index 57d7419c8c..2c2941de7f 100644
--- a/indra/llui/lltextvalidate.h
+++ b/indra/llui/lltextvalidate.h
@@ -34,27 +34,68 @@
namespace LLTextValidate
{
- typedef boost::function<bool (const LLWString &wstr)> validate_func_t;
-
- struct ValidateTextNamedFuncs
- : public LLInitParam::TypeValuesHelper<validate_func_t, ValidateTextNamedFuncs>
- {
- static void declareValues();
- };
-
- bool validateFloat(const LLWString &str );
- bool validateInt(const LLWString &str );
- bool validatePositiveS32(const LLWString &str);
- bool validateNonNegativeS32(const LLWString &str);
- bool validateNonNegativeS32NoSpace(const LLWString &str);
- bool validateAlphaNum(const LLWString &str );
- bool validateAlphaNumSpace(const LLWString &str );
- bool validateASCIIPrintableNoPipe(const LLWString &str);
- bool validateASCIIPrintableNoSpace(const LLWString &str);
- bool validateASCII(const LLWString &str);
- bool validateASCIINoLeadingSpace(const LLWString &str);
- bool validateASCIIWithNewLine(const LLWString &str);
-}
+ class ValidatorImpl
+ {
+ public:
+ ValidatorImpl() {}
+ virtual ~ValidatorImpl() {}
+ virtual bool validate(const std::string& str) = 0;
+ virtual bool validate(const LLWString& str) = 0;
+
+ bool setError(std::string name, LLSD values = LLSD()) { return mLastErrorName = name, mLastErrorValues = values, false; }
+ bool resetError() { return mLastErrorName.clear(), mLastErrorValues.clear(), true; }
+ const std::string& getLastErrorName() const { return mLastErrorName; }
+ const LLSD& getLastErrorValues() const { return mLastErrorValues; }
+
+ void setLastErrorShowTime();
+ U32 getLastErrorShowTime() const { return mLastErrorShowTime; }
+
+ protected:
+ std::string mLastErrorName;
+ LLSD mLastErrorValues;
+ U32 mLastErrorShowTime { 0 };
+ };
+
+ class Validator
+ {
+ public:
+ Validator() : mImpl(nullptr) {}
+ Validator(ValidatorImpl& impl) : mImpl(&impl) {}
+ Validator(const Validator& validator) : mImpl(validator.mImpl) {}
+ Validator(const Validator* validator) : mImpl(validator->mImpl) {}
+
+ bool validate(const std::string& str) const { return !mImpl || mImpl->validate(str); }
+ bool validate(const LLWString& str) const { return !mImpl || mImpl->validate(str); }
+
+ operator bool() const { return mImpl; }
+
+ static const U32 SHOW_LAST_ERROR_TIMEOUT_SEC = 30;
+ void showLastErrorUsingTimeout(U32 timeout = SHOW_LAST_ERROR_TIMEOUT_SEC);
+
+ private:
+ ValidatorImpl* mImpl;
+ };
+
+ // Available validators
+ extern Validator validateFloat;
+ extern Validator validateInt;
+ extern Validator validatePositiveS32;
+ extern Validator validateNonNegativeS32;
+ extern Validator validateNonNegativeS32NoSpace;
+ extern Validator validateAlphaNum;
+ extern Validator validateAlphaNumSpace;
+ extern Validator validateASCIIPrintableNoPipe;
+ extern Validator validateASCIIPrintableNoSpace;
+ extern Validator validateASCII;
+ extern Validator validateASCIINoLeadingSpace;
+ extern Validator validateASCIIWithNewLine;
+
+ // Add available validators to the internal map
+ struct Validators : public LLInitParam::TypeValuesHelper<Validator, Validators>
+ {
+ static void declareValues();
+ };
+};
#endif
diff --git a/indra/llui/lltimectrl.cpp b/indra/llui/lltimectrl.cpp
index 37cc52f967..5c298c9080 100644
--- a/indra/llui/lltimectrl.cpp
+++ b/indra/llui/lltimectrl.cpp
@@ -1,433 +1,454 @@
-/**
- * @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";
-}
+/**
+ * @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;
+
+class LLTimeValidatorImpl : public LLTextValidate::ValidatorImpl
+{
+public:
+ // virtual
+ bool validate(const std::string& str) override
+ {
+ std::string hours = LLTimeCtrl::getHoursString(str);
+ if (!LLTimeCtrl::isHoursStringValid(hours))
+ return setError("ValidatorInvalidHours", LLSD().with("STR", hours));
+
+ std::string minutes = LLTimeCtrl::getMinutesString(str);
+ if (!LLTimeCtrl::isMinutesStringValid(minutes))
+ return setError("ValidatorInvalidMinutes", LLSD().with("STR", minutes));
+
+ std::string ampm = LLTimeCtrl::getAMPMString(str);
+ if (!LLTimeCtrl::isPMAMStringValid(ampm))
+ return setError("ValidatorInvalidAMPM", LLSD().with("STR", ampm));
+
+ return resetError();
+ }
+
+ // virtual
+ bool validate(const LLWString& wstr) override
+ {
+ std::string str = wstring_to_utf8str(wstr);
+
+ return validate(str);
+ }
+} validateTimeImpl;
+LLTextValidate::Validator validateTime(validateTimeImpl);
+
+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(validateTime);
+ 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;
+}
+
+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
index 3fac65039a..b5f2008f4b 100644
--- a/indra/llui/lltimectrl.h
+++ b/indra/llui/lltimectrl.h
@@ -1,131 +1,129 @@
-/**
- * @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_ */
+/**
+ * @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)
+
+ 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);
+
+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);
+
+ void increaseMinutes();
+ void increaseHours();
+
+ void decreaseMinutes();
+ void decreaseHours();
+
+ bool isPM() const;
+ void switchDayPeriod();
+
+ void updateText();
+
+ EEditingPart getEditingPart();
+
+ 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 b324eff132..09bd25b1a9 100644
--- a/indra/llui/lltoggleablemenu.cpp
+++ b/indra/llui/lltoggleablemenu.cpp
@@ -1,108 +1,108 @@
-/**
- * @file lltoggleablemenu.cpp
- * @brief Menu toggled by a button press
- *
- * $LicenseInfo:firstyear=2009&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-
-#include "linden_common.h"
-
-#include "lltoggleablemenu.h"
-#include "lluictrlfactory.h"
-
-static LLDefaultChildRegistry::Register<LLToggleableMenu> r("toggleable_menu");
-
-LLToggleableMenu::LLToggleableMenu(const LLToggleableMenu::Params& p)
-: LLMenuGL(p),
- mButtonRect(),
- mVisibilityChangeSignal(NULL),
- mClosedByButtonClick(false)
-{
-}
-
-LLToggleableMenu::~LLToggleableMenu()
-{
- delete mVisibilityChangeSignal;
-}
-
-boost::signals2::connection LLToggleableMenu::setVisibilityChangeCallback(const commit_signal_t::slot_type& cb)
-{
- if (!mVisibilityChangeSignal) mVisibilityChangeSignal = new commit_signal_t();
- return mVisibilityChangeSignal->connect(cb);
-}
-
-// virtual
-void LLToggleableMenu::onVisibilityChange (bool curVisibilityIn)
-{
- S32 x,y;
- LLUI::getInstance()->getMousePositionLocal(LLUI::getInstance()->getRootView(), &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;
- }
-
- if (mVisibilityChangeSignal)
- {
- (*mVisibilityChangeSignal)(this,
- LLSD().with("visibility", curVisibilityIn).with("closed_by_button_click", mClosedByButtonClick));
- }
-}
-
-void LLToggleableMenu::setButtonRect(const LLRect& rect, LLView* current_view)
-{
- LLRect screen;
- current_view->localRectToScreen(rect, &screen);
- mButtonRect = screen;
-}
-
-void LLToggleableMenu::setButtonRect(LLView* current_view)
-{
- LLRect rect = current_view->getLocalRect();
- setButtonRect(rect, current_view);
-}
-
-bool LLToggleableMenu::toggleVisibility()
-{
- if (mClosedByButtonClick)
- {
- mClosedByButtonClick = false;
- return false;
- }
-
- if (getVisible())
- {
- setVisible(false);
- mClosedByButtonClick = false;
- return false;
- }
-
- return true;
-}
-
-bool LLToggleableMenu::addChild(LLView* view, S32 tab_group)
-{
- return addContextChild(view, tab_group);
-}
+/**
+ * @file lltoggleablemenu.cpp
+ * @brief Menu toggled by a button press
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+
+#include "linden_common.h"
+
+#include "lltoggleablemenu.h"
+#include "lluictrlfactory.h"
+
+static LLDefaultChildRegistry::Register<LLToggleableMenu> r("toggleable_menu");
+
+LLToggleableMenu::LLToggleableMenu(const LLToggleableMenu::Params& p)
+: LLMenuGL(p),
+ mButtonRect(),
+ mVisibilityChangeSignal(NULL),
+ mClosedByButtonClick(false)
+{
+}
+
+LLToggleableMenu::~LLToggleableMenu()
+{
+ delete mVisibilityChangeSignal;
+}
+
+boost::signals2::connection LLToggleableMenu::setVisibilityChangeCallback(const commit_signal_t::slot_type& cb)
+{
+ if (!mVisibilityChangeSignal) mVisibilityChangeSignal = new commit_signal_t();
+ return mVisibilityChangeSignal->connect(cb);
+}
+
+// virtual
+void LLToggleableMenu::onVisibilityChange (bool curVisibilityIn)
+{
+ S32 x,y;
+ LLUI::getInstance()->getMousePositionLocal(LLUI::getInstance()->getRootView(), &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;
+ }
+
+ if (mVisibilityChangeSignal)
+ {
+ (*mVisibilityChangeSignal)(this,
+ LLSD().with("visibility", curVisibilityIn).with("closed_by_button_click", mClosedByButtonClick));
+ }
+}
+
+void LLToggleableMenu::setButtonRect(const LLRect& rect, LLView* current_view)
+{
+ LLRect screen;
+ current_view->localRectToScreen(rect, &screen);
+ mButtonRect = screen;
+}
+
+void LLToggleableMenu::setButtonRect(LLView* current_view)
+{
+ LLRect rect = current_view->getLocalRect();
+ setButtonRect(rect, current_view);
+}
+
+bool LLToggleableMenu::toggleVisibility()
+{
+ if (mClosedByButtonClick)
+ {
+ mClosedByButtonClick = false;
+ return false;
+ }
+
+ if (getVisible())
+ {
+ setVisible(false);
+ mClosedByButtonClick = false;
+ return false;
+ }
+
+ return true;
+}
+
+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 b1260f050f..665d81b4fa 100644
--- a/indra/llui/lltoggleablemenu.h
+++ b/indra/llui/lltoggleablemenu.h
@@ -1,71 +1,71 @@
-/**
- * @file lltoggleablemenu.h
- * @brief Menu toggled by a button press
- *
- * $LicenseInfo:firstyear=2009&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLTOGGLEABLEMENU_H
-#define LL_LLTOGGLEABLEMENU_H
-
-#include "llmenugl.h"
-
-class LLToggleableMenu : public LLMenuGL
-{
-public:
- //adding blank params to work around registration issue
- //where LLToggleableMenu was owning the LLMenuGL param
- //and menu.xml was never loaded
- struct Params : public LLInitParam::Block<Params, LLMenuGL::Params>
- {};
-protected:
- LLToggleableMenu(const Params&);
- friend class LLUICtrlFactory;
-public:
- ~LLToggleableMenu();
-
- boost::signals2::connection setVisibilityChangeCallback( const commit_signal_t::slot_type& cb );
-
- virtual void onVisibilityChange (bool curVisibilityIn);
-
- virtual bool addChild (LLView* view, S32 tab_group = 0);
-
- const LLRect& getButtonRect() const { return mButtonRect; }
-
- // Converts the given local button rect to a screen rect
- void setButtonRect(const LLRect& rect, LLView* current_view);
- void setButtonRect(LLView* current_view);
-
- // Returns "true" if menu was not closed by button click
- // and is not still visible. If menu is visible toggles
- // its visibility off.
- bool toggleVisibility();
-
- LLHandle<LLToggleableMenu> getHandle() { return getDerivedHandle<LLToggleableMenu>(); }
-
-protected:
- bool mClosedByButtonClick;
- LLRect mButtonRect;
- commit_signal_t* mVisibilityChangeSignal;
-};
-
-#endif // LL_LLTOGGLEABLEMENU_H
+/**
+ * @file lltoggleablemenu.h
+ * @brief Menu toggled by a button press
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTOGGLEABLEMENU_H
+#define LL_LLTOGGLEABLEMENU_H
+
+#include "llmenugl.h"
+
+class LLToggleableMenu : public LLMenuGL
+{
+public:
+ //adding blank params to work around registration issue
+ //where LLToggleableMenu was owning the LLMenuGL param
+ //and menu.xml was never loaded
+ struct Params : public LLInitParam::Block<Params, LLMenuGL::Params>
+ {};
+protected:
+ LLToggleableMenu(const Params&);
+ friend class LLUICtrlFactory;
+public:
+ ~LLToggleableMenu();
+
+ boost::signals2::connection setVisibilityChangeCallback( const commit_signal_t::slot_type& cb );
+
+ virtual void onVisibilityChange (bool curVisibilityIn);
+
+ virtual bool addChild (LLView* view, S32 tab_group = 0);
+
+ const LLRect& getButtonRect() const { return mButtonRect; }
+
+ // Converts the given local button rect to a screen rect
+ void setButtonRect(const LLRect& rect, LLView* current_view);
+ void setButtonRect(LLView* current_view);
+
+ // Returns "true" if menu was not closed by button click
+ // and is not still visible. If menu is visible toggles
+ // its visibility off.
+ bool toggleVisibility();
+
+ LLHandle<LLToggleableMenu> getHandle() { return getDerivedHandle<LLToggleableMenu>(); }
+
+protected:
+ bool mClosedByButtonClick;
+ LLRect mButtonRect;
+ commit_signal_t* mVisibilityChangeSignal;
+};
+
+#endif // LL_LLTOGGLEABLEMENU_H
diff --git a/indra/llui/lltoolbar.cpp b/indra/llui/lltoolbar.cpp
index 1c1414dd74..9753881dfc 100644
--- a/indra/llui/lltoolbar.cpp
+++ b/indra/llui/lltoolbar.cpp
@@ -1,1267 +1,1266 @@
-/**
- * @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 "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()
-{
- auto menu = mPopupMenuHandle.get();
- if (menu)
- {
- menu->die();
- mPopupMenuHandle.markDead();
- }
- 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));
-
- for (const auto& 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;
- for (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
- for (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;
-
- for (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;
-
- for (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;
-
- for (const 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);
- button->setFunctionName(commandp->executeFunctionName());
- LL_DEBUGS("UIUsage") << "button function name a -> " << commandp->executeFunctionName() << LL_ENDL;
- 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->setFunctionName(commandp->executeFunctionName());
- LL_DEBUGS("UIUsage") << "button function name b -> " << commandp->executeFunctionName() << LL_ENDL;
- 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 != nullptr;
-
- // 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)));
- //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);
- static LLCachedControl<S32> drag_threshold(*LLUI::getInstance()->mSettingGroups["config"], "DragAndDropDistanceThreshold", 3);
- 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);
- }
-}
+/**
+ * @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 "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()
+{
+ auto menu = mPopupMenuHandle.get();
+ if (menu)
+ {
+ menu->die();
+ mPopupMenuHandle.markDead();
+ }
+ 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));
+
+ for (const auto& 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;
+ for (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
+ for (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;
+
+ for (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;
+
+ for (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;
+
+ for (const 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);
+ button->setFunctionName(commandp->executeFunctionName());
+ LL_DEBUGS("UIUsage") << "button function name a -> " << commandp->executeFunctionName() << LL_ENDL;
+ 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->setFunctionName(commandp->executeFunctionName());
+ LL_DEBUGS("UIUsage") << "button function name b -> " << commandp->executeFunctionName() << LL_ENDL;
+ 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 != nullptr;
+
+ // 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)));
+ //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);
+ if (mouse_distance_squared > DRAG_N_DROP_DISTANCE_THRESHOLD * DRAG_N_DROP_DISTANCE_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
index 0f5f7b16b7..4dee33f4ab 100644
--- a/indra/llui/lltoolbar.h
+++ b/indra/llui/lltoolbar.h
@@ -1,331 +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
+/**
+ * @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 4c138eff65..5664a1a153 100644
--- a/indra/llui/lltooltip.cpp
+++ b/indra/llui/lltooltip.cpp
@@ -1,648 +1,648 @@
-/**
- * @file lltooltip.cpp
- * @brief LLToolTipMgr class implementation and related classes
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-// self include
-#include "lltooltip.h"
-
-// Library includes
-#include "lltextbox.h"
-#include "lliconctrl.h"
-#include "llbutton.h"
-#include "llmenugl.h" // hideMenus()
-#include "llui.h" // positionViewNearMouse()
-#include "llwindow.h"
-#include "lltrans.h"
-//
-// Constants
-//
-
-//
-// Local globals
-//
-
-LLToolTipView *gToolTipView = NULL;
-
-//
-// Member functions
-//
-
-static LLDefaultChildRegistry::Register<LLToolTipView> register_tooltip_view("tooltip_view");
-
-LLToolTipView::Params::Params()
-{
- changeDefault(mouse_opaque, false);
-}
-
-LLToolTipView::LLToolTipView(const LLToolTipView::Params& p)
-: LLView(p)
-{
-}
-
-void LLToolTipView::draw()
-{
- LLToolTipMgr::instance().updateToolTipVisibility();
-
- // do the usual thing
- LLView::draw();
-}
-
-bool LLToolTipView::handleHover(S32 x, S32 y, MASK mask)
-{
- static S32 last_x = x;
- static S32 last_y = y;
-
- LLToolTipMgr& tooltip_mgr = LLToolTipMgr::instance();
-
- if (x != last_x && y != last_y && !tooltip_mgr.getMouseNearRect().pointInRect(x, y))
- {
- // allow new tooltips because mouse moved outside of mouse near rect
- tooltip_mgr.unblockToolTips();
- }
-
- last_x = x;
- last_y = y;
- return LLView::handleHover(x, y, mask);
-}
-
-bool LLToolTipView::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- LLToolTipMgr::instance().blockToolTips();
-
- if (LLView::handleMouseDown(x, y, mask))
- {
- // If we are handling the mouse event menu holder
- // won't get a chance to close menus so do this here
- LLMenuGL::sMenuContainer->hideMenus();
- return true;
- }
-
- return false;
-}
-
-bool LLToolTipView::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
-{
- LLToolTipMgr::instance().blockToolTips();
- return LLView::handleMiddleMouseDown(x, y, mask);
-}
-
-bool LLToolTipView::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- LLToolTipMgr::instance().blockToolTips();
- return LLView::handleRightMouseDown(x, y, mask);
-}
-
-
-bool LLToolTipView::handleScrollWheel( S32 x, S32 y, S32 clicks )
-{
- LLToolTipMgr::instance().blockToolTips();
- return false;
-}
-
-void LLToolTipView::drawStickyRect()
-{
- gl_rect_2d(LLToolTipMgr::instance().getMouseNearRect(), LLColor4::white, false);
-}
-
-// defaults for floater param block pulled from widgets/floater.xml
-static LLWidgetNameRegistry::StaticRegistrar sRegisterInspectorParams(&typeid(LLInspector::Params), "inspector");
-
-//
-// LLToolTip
-//
-
-
-static LLDefaultChildRegistry::Register<LLToolTip> register_tooltip("tool_tip");
-
-
-LLToolTip::Params::Params()
-: max_width("max_width", 200),
- padding("padding", 4),
- wrap("wrap", true),
- pos("pos"),
- message("message"),
- delay_time("delay_time", LLUI::getInstance()->mSettingGroups["config"]->getF32( "ToolTipDelay" )),
- visible_time_over("visible_time_over", LLUI::getInstance()->mSettingGroups["config"]->getF32( "ToolTipVisibleTimeOver" )),
- visible_time_near("visible_time_near", LLUI::getInstance()->mSettingGroups["config"]->getF32( "ToolTipVisibleTimeNear" )),
- visible_time_far("visible_time_far", LLUI::getInstance()->mSettingGroups["config"]->getF32( "ToolTipVisibleTimeFar" )),
- sticky_rect("sticky_rect"),
- image("image"),
- text_color("text_color"),
- time_based_media("time_based_media", false),
- web_based_media("web_based_media", false),
- media_playing("media_playing", false),
- allow_paste_tooltip("allow_paste_tooltip", false)
-{
- changeDefault(chrome, true);
-}
-
-LLToolTip::LLToolTip(const LLToolTip::Params& p)
-: LLPanel(p),
- mHasClickCallback(p.click_callback.isProvided()),
- mPadding(p.padding),
- mMaxWidth(p.max_width),
- mTextBox(NULL),
- mInfoButton(NULL),
- mPlayMediaButton(NULL),
- mHomePageButton(NULL),
- mIsTooltipPastable(p.allow_paste_tooltip)
-{
- LLTextBox::Params params;
- params.name = params.initial_value().asString();
- // bake textbox padding into initial rect
- params.rect = LLRect (mPadding, mPadding + 1, mPadding + 1, mPadding);
- params.h_pad = 0;
- params.v_pad = 0;
- params.mouse_opaque = false;
- params.text_color = p.text_color;
- params.bg_visible = false;
- params.font = p.font;
- params.use_ellipses = true;
- params.wrap = p.wrap;
- params.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);
-
- S32 TOOLTIP_ICON_SIZE = 0;
- S32 TOOLTIP_PLAYBUTTON_SIZE = 0;
- if (p.image.isProvided())
- {
- LLButton::Params icon_params;
- icon_params.name = "tooltip_info";
- LLRect icon_rect;
- LLUIImage* imagep = p.image;
- TOOLTIP_ICON_SIZE = (imagep ? imagep->getWidth() : 16);
- icon_rect.setOriginAndSize(mPadding, mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE);
- icon_params.rect = icon_rect;
- icon_params.image_unselected(imagep);
- icon_params.image_selected(imagep);
-
- icon_params.scale_image(true);
- icon_params.flash_color.control = "ButtonUnselectedFgColor";
- mInfoButton = LLUICtrlFactory::create<LLButton>(icon_params);
- if (p.click_callback.isProvided())
- {
- mInfoButton->setCommitCallback(boost::bind(p.click_callback()));
- }
- addChild(mInfoButton);
-
- // move text over to fit image in
- mTextBox->translate(TOOLTIP_ICON_SIZE + mPadding, 0);
- }
-
- if (p.time_based_media)
- {
- LLButton::Params p_button;
- p_button.name(std::string("play_media"));
- p_button.label(""); // provide label but set to empty so name does not overwrite it -angela
- TOOLTIP_PLAYBUTTON_SIZE = 16;
- LLRect button_rect;
- button_rect.setOriginAndSize((mPadding +TOOLTIP_ICON_SIZE+ mPadding ), mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE);
- p_button.rect = button_rect;
- p_button.image_selected.name("button_anim_pause.tga");
- p_button.image_unselected.name("button_anim_play.tga");
- p_button.scale_image(true);
-
- mPlayMediaButton = LLUICtrlFactory::create<LLButton>(p_button);
- if(p.click_playmedia_callback.isProvided())
- {
- mPlayMediaButton->setCommitCallback(boost::bind(p.click_playmedia_callback()));
- }
- mPlayMediaButton->setToggleState(p.media_playing);
- addChild(mPlayMediaButton);
-
- // move text over to fit image in
- mTextBox->translate(TOOLTIP_PLAYBUTTON_SIZE + mPadding, 0);
- }
-
- if (p.web_based_media)
- {
- LLButton::Params p_w_button;
- p_w_button.name(std::string("home_page"));
- p_w_button.label(""); // provid label but set to empty so name does not overwrite it -angela
- TOOLTIP_PLAYBUTTON_SIZE = 16;
- LLRect button_rect;
- button_rect.setOriginAndSize((mPadding +TOOLTIP_ICON_SIZE+ mPadding ), mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE);
- p_w_button.rect = button_rect;
- p_w_button.image_unselected.name("map_home.tga");
- p_w_button.scale_image(true);
-
- mHomePageButton = LLUICtrlFactory::create<LLButton>(p_w_button);
- if(p.click_homepage_callback.isProvided())
- {
- mHomePageButton->setCommitCallback(boost::bind(p.click_homepage_callback()));
- }
- addChild(mHomePageButton);
-
- // move text over to fit image in
- mTextBox->translate(TOOLTIP_PLAYBUTTON_SIZE + mPadding, 0);
- }
-
- if (p.click_callback.isProvided())
- {
- setMouseUpCallback(boost::bind(p.click_callback()));
- }
-}
-
-void LLToolTip::initFromParams(const LLToolTip::Params& p)
-{
- LLPanel::initFromParams(p);
-
- // do this *after* we've had our size set in LLPanel::initFromParams();
- const S32 REALLY_LARGE_HEIGHT = 10000;
- mTextBox->reshape(mMaxWidth, REALLY_LARGE_HEIGHT);
-
- if (p.styled_message.isProvided())
- {
- for (LLInitParam::ParamIterator<LLToolTip::StyledText>::const_iterator text_it = p.styled_message.begin();
- text_it != p.styled_message.end();
- ++text_it)
- {
- mTextBox->appendText(text_it->text(), false, text_it->style);
- }
- }
- else
- {
- mTextBox->setText(p.message());
- }
-
- mIsTooltipPastable = p.allow_paste_tooltip;
-
- updateTextBox();
- snapToChildren();
-}
-
-void LLToolTip::updateTextBox()
-{
- S32 text_width = llmin(mMaxWidth, mTextBox->getTextPixelWidth() + 1);
- S32 text_height = mTextBox->getTextPixelHeight();
- mTextBox->reshape(text_width, text_height);
-}
-
-void LLToolTip::snapToChildren()
-{
- // reshape tooltip panel to fit text box
- LLRect tooltip_rect = calcBoundingRect();
- tooltip_rect.mTop += mPadding;
- tooltip_rect.mRight += mPadding;
- tooltip_rect.mBottom = 0;
- tooltip_rect.mLeft = 0;
-
- if (mInfoButton)
- {
- mTextBox->reshape(mTextBox->getRect().getWidth(), llmax(mTextBox->getRect().getHeight(), tooltip_rect.getHeight() - 2 * mPadding));
-
- LLRect text_rect = mTextBox->getRect();
- LLRect icon_rect = mInfoButton->getRect();
- mInfoButton->translate(0, text_rect.getCenterY() - icon_rect.getCenterY());
- }
-
- setShape(tooltip_rect);
-}
-
-void LLToolTip::setVisible(bool visible)
-{
- // fade out tooltip over time
- if (visible)
- {
- mVisibleTimer.start();
- mFadeTimer.stop();
- LLPanel::setVisible(true);
- }
- else
- {
- mVisibleTimer.stop();
- // don't actually change mVisible state, start fade out transition instead
- if (!mFadeTimer.getStarted())
- {
- mFadeTimer.start();
- }
- }
-}
-
-bool LLToolTip::handleHover(S32 x, S32 y, MASK mask)
-{
- //mInfoButton->setFlashing(true);
- if(mInfoButton)
- mInfoButton->setHighlight(true);
-
- LLPanel::handleHover(x, y, mask);
- if (mHasClickCallback)
- {
- getWindow()->setCursor(UI_CURSOR_HAND);
- }
- return true;
-}
-
-void LLToolTip::onMouseLeave(S32 x, S32 y, MASK mask)
-{
- //mInfoButton->setFlashing(true);
- if(mInfoButton)
- mInfoButton->setHighlight(false);
- LLUICtrl::onMouseLeave(x, y, mask);
-}
-
-void LLToolTip::draw()
-{
- F32 alpha = 1.f;
-
- if (mFadeTimer.getStarted())
- {
- static LLCachedControl<F32> tool_tip_fade_time(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipFadeTime", 0.2f);
- alpha = clamp_rescale(mFadeTimer.getElapsedTimeF32(), 0.f, tool_tip_fade_time(), 1.f, 0.f);
- if (alpha == 0.f)
- {
- // finished fading out, so hide ourselves
- mFadeTimer.stop();
- LLPanel::setVisible(false);
- }
- }
-
- // draw tooltip contents with appropriate alpha
- {
- LLViewDrawContext context(alpha);
- LLPanel::draw();
- }
-}
-
-bool LLToolTip::isFading()
-{
- return mFadeTimer.getStarted();
-}
-
-F32 LLToolTip::getVisibleTime()
-{
- return mVisibleTimer.getStarted() ? mVisibleTimer.getElapsedTimeF32() : 0.f;
-}
-
-bool LLToolTip::hasClickCallback()
-{
- return mHasClickCallback;
-}
-
-void LLToolTip::getToolTipMessage(std::string & message)
-{
- if (mTextBox)
- {
- message = mTextBox->getText();
- }
-}
-
-
-
-//
-// LLToolTipMgr
-//
-
-LLToolTipMgr::LLToolTipMgr()
-: mToolTipsBlocked(false),
- mToolTip(NULL),
- mNeedsToolTip(false)
-{}
-
-void LLToolTipMgr::createToolTip(const LLToolTip::Params& params)
-{
- // block all other tooltips until tooltips re-enabled (e.g. mouse moved)
- blockToolTips();
-
- delete mToolTip;
-
- LLToolTip::Params tooltip_params(params);
- // block mouse events if there is a click handler registered (specifically, hover)
- if (params.click_callback.isProvided() && !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 = true;
- }
- tooltip_params.rect = LLRect (0, 1, 1, 0);
-
- if (tooltip_params.create_callback.isProvided())
- {
- mToolTip = tooltip_params.create_callback()(tooltip_params);
- if (mToolTip == NULL)
- {
- return;
- }
- }
- else
- mToolTip = LLUICtrlFactory::create<LLToolTip> (tooltip_params);
-
- gToolTipView->addChild(mToolTip);
-
- if (params.pos.isProvided())
- {
- LLCoordGL pos = params.pos;
- // try to spawn at requested position
- LLUI::getInstance()->positionViewNearMouse(mToolTip, pos.mX, pos.mY);
- }
- else
- {
- // just spawn at mouse location
- LLUI::getInstance()->positionViewNearMouse(mToolTip);
- }
-
- //...update "sticky" rect and tooltip position
- if (params.sticky_rect.isProvided())
- {
- mMouseNearRect = params.sticky_rect;
- }
- else
- {
- S32 mouse_x;
- S32 mouse_y;
- LLUI::getInstance()->getMousePositionLocal(gToolTipView->getParent(), &mouse_x, &mouse_y);
-
- // allow mouse a little bit of slop before changing tooltips
- mMouseNearRect.setCenterAndSize(mouse_x, mouse_y, 3, 3);
- }
-
- // allow mouse to move all the way to the tooltip without changing tooltips
- // (tooltip can still time out)
- if (mToolTip->hasClickCallback())
- {
- // keep tooltip up when we mouse over it
- mMouseNearRect.unionWith(mToolTip->getRect());
- }
-}
-
-
-void LLToolTipMgr::show(const std::string& msg, bool allow_paste_tooltip)
-{
- show(LLToolTip::Params().message(msg).allow_paste_tooltip(allow_paste_tooltip));
-}
-
-void LLToolTipMgr::show(const LLToolTip::Params& params)
-{
- if (!params.styled_message.isProvided()
- && (!params.message.isProvided() || params.message().empty())
- && !params.image.isProvided() && !params.create_callback.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())
- {
- LL_WARNS() << "Could not display tooltip!" << LL_ENDL;
- return;
- }
-
- // are we ready to show the tooltip?
- if (!mToolTipsBlocked // we haven't hit a key, moved the mouse, etc.
- && LLUI::getInstance()->getMouseIdleTime() > params_with_defaults.delay_time) // the mouse has been still long enough
- {
- bool tooltip_changed = mLastToolTipParams.message() != params_with_defaults.message()
- || mLastToolTipParams.pos() != params_with_defaults.pos()
- || mLastToolTipParams.time_based_media() != params_with_defaults.time_based_media()
- || mLastToolTipParams.web_based_media() != params_with_defaults.web_based_media();
-
- bool tooltip_shown = mToolTip
- && mToolTip->getVisible()
- && !mToolTip->isFading();
-
- mNeedsToolTip = tooltip_changed || !tooltip_shown;
- // store description of tooltip for later creation
- mNextToolTipParams = params_with_defaults;
- }
-}
-
-// allow new tooltips to be created, e.g. after mouse has moved
-void LLToolTipMgr::unblockToolTips()
-{
- mToolTipsBlocked = false;
-}
-
-// disallow new tooltips until unblockTooltips called
-void LLToolTipMgr::blockToolTips()
-{
- hideToolTips();
- mToolTipsBlocked = true;
-}
-
-void LLToolTipMgr::hideToolTips()
-{
- if (mToolTip)
- {
- mToolTip->setVisible(false);
- }
-}
-
-bool LLToolTipMgr::toolTipVisible()
-{
- return mToolTip ? mToolTip->isInVisibleChain() : false;
-}
-
-LLRect LLToolTipMgr::getToolTipRect()
-{
- if (mToolTip && mToolTip->getVisible())
- {
- return mToolTip->getRect();
- }
- return LLRect();
-}
-
-
-LLRect LLToolTipMgr::getMouseNearRect()
-{
- return toolTipVisible() ? mMouseNearRect : LLRect();
-}
-
-// every frame, determine if current tooltip should be hidden
-void LLToolTipMgr::updateToolTipVisibility()
-{
- // create new tooltip if we have one ready to go
- if (mNeedsToolTip)
- {
- mNeedsToolTip = false;
- createToolTip(mNextToolTipParams);
- mLastToolTipParams = mNextToolTipParams;
-
- return;
- }
-
- // hide tooltips when mouse cursor is hidden
- if (LLUI::getInstance()->getWindow()->isCursorHidden())
- {
- blockToolTips();
- return;
- }
-
- // hide existing tooltips if they have timed out
- F32 tooltip_timeout = 0.f;
- if (toolTipVisible())
- {
- S32 mouse_x, mouse_y;
- LLUI::getInstance()->getMousePositionLocal(gToolTipView, &mouse_x, &mouse_y);
-
- // mouse far away from tooltip
- tooltip_timeout = mLastToolTipParams.visible_time_far;
- // mouse near rect will only include the tooltip if the
- // tooltip is clickable
- if (mMouseNearRect.pointInRect(mouse_x, mouse_y))
- {
- // mouse "close" to tooltip
- tooltip_timeout = mLastToolTipParams.visible_time_near;
-
- // if tooltip is clickable (has large mMouseNearRect)
- // than having cursor over tooltip keeps it up indefinitely
- if (mToolTip->parentPointInView(mouse_x, mouse_y))
- {
- // mouse over tooltip itself, don't time out
- tooltip_timeout = mLastToolTipParams.visible_time_over;
- }
- }
-
- if (mToolTip->getVisibleTime() > tooltip_timeout)
- {
- hideToolTips();
- unblockToolTips();
- }
- }
-}
-
-
-// Return the current tooltip text
-void LLToolTipMgr::getToolTipMessage(std::string & message)
-{
- if (toolTipVisible())
- {
- mToolTip->getToolTipMessage(message);
- }
-}
-
-bool LLToolTipMgr::isTooltipPastable()
-{
- if (toolTipVisible())
- {
- return mToolTip->isTooltipPastable();
- }
- return false;
- }
-
-// EOF
+/**
+ * @file lltooltip.cpp
+ * @brief LLToolTipMgr class implementation and related classes
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+// self include
+#include "lltooltip.h"
+
+// Library includes
+#include "lltextbox.h"
+#include "lliconctrl.h"
+#include "llbutton.h"
+#include "llmenugl.h" // hideMenus()
+#include "llui.h" // positionViewNearMouse()
+#include "llwindow.h"
+#include "lltrans.h"
+//
+// Constants
+//
+
+//
+// Local globals
+//
+
+LLToolTipView *gToolTipView = NULL;
+
+//
+// Member functions
+//
+
+static LLDefaultChildRegistry::Register<LLToolTipView> register_tooltip_view("tooltip_view");
+
+LLToolTipView::Params::Params()
+{
+ changeDefault(mouse_opaque, false);
+}
+
+LLToolTipView::LLToolTipView(const LLToolTipView::Params& p)
+: LLView(p)
+{
+}
+
+void LLToolTipView::draw()
+{
+ LLToolTipMgr::instance().updateToolTipVisibility();
+
+ // do the usual thing
+ LLView::draw();
+}
+
+bool LLToolTipView::handleHover(S32 x, S32 y, MASK mask)
+{
+ static S32 last_x = x;
+ static S32 last_y = y;
+
+ LLToolTipMgr& tooltip_mgr = LLToolTipMgr::instance();
+
+ if (x != last_x && y != last_y && !tooltip_mgr.getMouseNearRect().pointInRect(x, y))
+ {
+ // allow new tooltips because mouse moved outside of mouse near rect
+ tooltip_mgr.unblockToolTips();
+ }
+
+ last_x = x;
+ last_y = y;
+ return LLView::handleHover(x, y, mask);
+}
+
+bool LLToolTipView::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLToolTipMgr::instance().blockToolTips();
+
+ if (LLView::handleMouseDown(x, y, mask))
+ {
+ // If we are handling the mouse event menu holder
+ // won't get a chance to close menus so do this here
+ LLMenuGL::sMenuContainer->hideMenus();
+ return true;
+ }
+
+ return false;
+}
+
+bool LLToolTipView::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLToolTipMgr::instance().blockToolTips();
+ return LLView::handleMiddleMouseDown(x, y, mask);
+}
+
+bool LLToolTipView::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLToolTipMgr::instance().blockToolTips();
+ return LLView::handleRightMouseDown(x, y, mask);
+}
+
+
+bool LLToolTipView::handleScrollWheel( S32 x, S32 y, S32 clicks )
+{
+ LLToolTipMgr::instance().blockToolTips();
+ return false;
+}
+
+void LLToolTipView::drawStickyRect()
+{
+ gl_rect_2d(LLToolTipMgr::instance().getMouseNearRect(), LLColor4::white, false);
+}
+
+// defaults for floater param block pulled from widgets/floater.xml
+static LLWidgetNameRegistry::StaticRegistrar sRegisterInspectorParams(&typeid(LLInspector::Params), "inspector");
+
+//
+// LLToolTip
+//
+
+
+static LLDefaultChildRegistry::Register<LLToolTip> register_tooltip("tool_tip");
+
+
+LLToolTip::Params::Params()
+: max_width("max_width", 200),
+ padding("padding", 4),
+ wrap("wrap", true),
+ pos("pos"),
+ message("message"),
+ delay_time("delay_time", LLUI::getInstance()->mSettingGroups["config"]->getF32( "ToolTipDelay" )),
+ visible_time_over("visible_time_over", LLUI::getInstance()->mSettingGroups["config"]->getF32( "ToolTipVisibleTimeOver" )),
+ visible_time_near("visible_time_near", LLUI::getInstance()->mSettingGroups["config"]->getF32( "ToolTipVisibleTimeNear" )),
+ visible_time_far("visible_time_far", LLUI::getInstance()->mSettingGroups["config"]->getF32( "ToolTipVisibleTimeFar" )),
+ sticky_rect("sticky_rect"),
+ image("image"),
+ text_color("text_color"),
+ time_based_media("time_based_media", false),
+ web_based_media("web_based_media", false),
+ media_playing("media_playing", false),
+ allow_paste_tooltip("allow_paste_tooltip", false)
+{
+ changeDefault(chrome, true);
+}
+
+LLToolTip::LLToolTip(const LLToolTip::Params& p)
+: LLPanel(p),
+ mHasClickCallback(p.click_callback.isProvided()),
+ mPadding(p.padding),
+ mMaxWidth(p.max_width),
+ mTextBox(NULL),
+ mInfoButton(NULL),
+ mPlayMediaButton(NULL),
+ mHomePageButton(NULL),
+ mIsTooltipPastable(p.allow_paste_tooltip)
+{
+ LLTextBox::Params params;
+ params.name = params.initial_value().asString();
+ // bake textbox padding into initial rect
+ params.rect = LLRect (mPadding, mPadding + 1, mPadding + 1, mPadding);
+ params.h_pad = 0;
+ params.v_pad = 0;
+ params.mouse_opaque = false;
+ params.text_color = p.text_color;
+ params.bg_visible = false;
+ params.font = p.font;
+ params.use_ellipses = true;
+ params.wrap = p.wrap;
+ params.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);
+
+ S32 TOOLTIP_ICON_SIZE = 0;
+ S32 TOOLTIP_PLAYBUTTON_SIZE = 0;
+ if (p.image.isProvided())
+ {
+ LLButton::Params icon_params;
+ icon_params.name = "tooltip_info";
+ LLRect icon_rect;
+ LLUIImage* imagep = p.image;
+ TOOLTIP_ICON_SIZE = (imagep ? imagep->getWidth() : 16);
+ icon_rect.setOriginAndSize(mPadding, mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE);
+ icon_params.rect = icon_rect;
+ icon_params.image_unselected(imagep);
+ icon_params.image_selected(imagep);
+
+ icon_params.scale_image(true);
+ icon_params.flash_color.control = "ButtonUnselectedFgColor";
+ mInfoButton = LLUICtrlFactory::create<LLButton>(icon_params);
+ if (p.click_callback.isProvided())
+ {
+ mInfoButton->setCommitCallback(boost::bind(p.click_callback()));
+ }
+ addChild(mInfoButton);
+
+ // move text over to fit image in
+ mTextBox->translate(TOOLTIP_ICON_SIZE + mPadding, 0);
+ }
+
+ if (p.time_based_media)
+ {
+ LLButton::Params p_button;
+ p_button.name(std::string("play_media"));
+ p_button.label(""); // provide label but set to empty so name does not overwrite it -angela
+ TOOLTIP_PLAYBUTTON_SIZE = 16;
+ LLRect button_rect;
+ button_rect.setOriginAndSize((mPadding +TOOLTIP_ICON_SIZE+ mPadding ), mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE);
+ p_button.rect = button_rect;
+ p_button.image_selected.name("button_anim_pause.tga");
+ p_button.image_unselected.name("button_anim_play.tga");
+ p_button.scale_image(true);
+
+ mPlayMediaButton = LLUICtrlFactory::create<LLButton>(p_button);
+ if(p.click_playmedia_callback.isProvided())
+ {
+ mPlayMediaButton->setCommitCallback(boost::bind(p.click_playmedia_callback()));
+ }
+ mPlayMediaButton->setToggleState(p.media_playing);
+ addChild(mPlayMediaButton);
+
+ // move text over to fit image in
+ mTextBox->translate(TOOLTIP_PLAYBUTTON_SIZE + mPadding, 0);
+ }
+
+ if (p.web_based_media)
+ {
+ LLButton::Params p_w_button;
+ p_w_button.name(std::string("home_page"));
+ p_w_button.label(""); // provid label but set to empty so name does not overwrite it -angela
+ TOOLTIP_PLAYBUTTON_SIZE = 16;
+ LLRect button_rect;
+ button_rect.setOriginAndSize((mPadding +TOOLTIP_ICON_SIZE+ mPadding ), mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE);
+ p_w_button.rect = button_rect;
+ p_w_button.image_unselected.name("map_home.tga");
+ p_w_button.scale_image(true);
+
+ mHomePageButton = LLUICtrlFactory::create<LLButton>(p_w_button);
+ if(p.click_homepage_callback.isProvided())
+ {
+ mHomePageButton->setCommitCallback(boost::bind(p.click_homepage_callback()));
+ }
+ addChild(mHomePageButton);
+
+ // move text over to fit image in
+ mTextBox->translate(TOOLTIP_PLAYBUTTON_SIZE + mPadding, 0);
+ }
+
+ if (p.click_callback.isProvided())
+ {
+ setMouseUpCallback(boost::bind(p.click_callback()));
+ }
+}
+
+void LLToolTip::initFromParams(const LLToolTip::Params& p)
+{
+ LLPanel::initFromParams(p);
+
+ // do this *after* we've had our size set in LLPanel::initFromParams();
+ const S32 REALLY_LARGE_HEIGHT = 10000;
+ mTextBox->reshape(mMaxWidth, REALLY_LARGE_HEIGHT);
+
+ if (p.styled_message.isProvided())
+ {
+ for (LLInitParam::ParamIterator<LLToolTip::StyledText>::const_iterator text_it = p.styled_message.begin();
+ text_it != p.styled_message.end();
+ ++text_it)
+ {
+ mTextBox->appendText(text_it->text(), false, text_it->style);
+ }
+ }
+ else
+ {
+ mTextBox->setText(p.message());
+ }
+
+ mIsTooltipPastable = p.allow_paste_tooltip;
+
+ updateTextBox();
+ snapToChildren();
+}
+
+void LLToolTip::updateTextBox()
+{
+ S32 text_width = llmin(mMaxWidth, mTextBox->getTextPixelWidth() + 1);
+ S32 text_height = mTextBox->getTextPixelHeight();
+ mTextBox->reshape(text_width, text_height);
+}
+
+void LLToolTip::snapToChildren()
+{
+ // reshape tooltip panel to fit text box
+ LLRect tooltip_rect = calcBoundingRect();
+ tooltip_rect.mTop += mPadding;
+ tooltip_rect.mRight += mPadding;
+ tooltip_rect.mBottom = 0;
+ tooltip_rect.mLeft = 0;
+
+ if (mInfoButton)
+ {
+ mTextBox->reshape(mTextBox->getRect().getWidth(), llmax(mTextBox->getRect().getHeight(), tooltip_rect.getHeight() - 2 * mPadding));
+
+ LLRect text_rect = mTextBox->getRect();
+ LLRect icon_rect = mInfoButton->getRect();
+ mInfoButton->translate(0, text_rect.getCenterY() - icon_rect.getCenterY());
+ }
+
+ setShape(tooltip_rect);
+}
+
+void LLToolTip::setVisible(bool visible)
+{
+ // fade out tooltip over time
+ if (visible)
+ {
+ mVisibleTimer.start();
+ mFadeTimer.stop();
+ LLPanel::setVisible(true);
+ }
+ else
+ {
+ mVisibleTimer.stop();
+ // don't actually change mVisible state, start fade out transition instead
+ if (!mFadeTimer.getStarted())
+ {
+ mFadeTimer.start();
+ }
+ }
+}
+
+bool LLToolTip::handleHover(S32 x, S32 y, MASK mask)
+{
+ //mInfoButton->setFlashing(true);
+ if(mInfoButton)
+ mInfoButton->setHighlight(true);
+
+ LLPanel::handleHover(x, y, mask);
+ if (mHasClickCallback)
+ {
+ getWindow()->setCursor(UI_CURSOR_HAND);
+ }
+ return true;
+}
+
+void LLToolTip::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+ //mInfoButton->setFlashing(true);
+ if(mInfoButton)
+ mInfoButton->setHighlight(false);
+ LLUICtrl::onMouseLeave(x, y, mask);
+}
+
+void LLToolTip::draw()
+{
+ F32 alpha = 1.f;
+
+ if (mFadeTimer.getStarted())
+ {
+ static LLCachedControl<F32> tool_tip_fade_time(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipFadeTime", 0.2f);
+ alpha = clamp_rescale(mFadeTimer.getElapsedTimeF32(), 0.f, tool_tip_fade_time(), 1.f, 0.f);
+ if (alpha == 0.f)
+ {
+ // finished fading out, so hide ourselves
+ mFadeTimer.stop();
+ LLPanel::setVisible(false);
+ }
+ }
+
+ // draw tooltip contents with appropriate alpha
+ {
+ LLViewDrawContext context(alpha);
+ LLPanel::draw();
+ }
+}
+
+bool LLToolTip::isFading()
+{
+ return mFadeTimer.getStarted();
+}
+
+F32 LLToolTip::getVisibleTime()
+{
+ return mVisibleTimer.getStarted() ? mVisibleTimer.getElapsedTimeF32() : 0.f;
+}
+
+bool LLToolTip::hasClickCallback()
+{
+ return mHasClickCallback;
+}
+
+void LLToolTip::getToolTipMessage(std::string & message)
+{
+ if (mTextBox)
+ {
+ message = mTextBox->getText();
+ }
+}
+
+
+
+//
+// LLToolTipMgr
+//
+
+LLToolTipMgr::LLToolTipMgr()
+: mToolTipsBlocked(false),
+ mToolTip(NULL),
+ mNeedsToolTip(false)
+{}
+
+void LLToolTipMgr::createToolTip(const LLToolTip::Params& params)
+{
+ // block all other tooltips until tooltips re-enabled (e.g. mouse moved)
+ blockToolTips();
+
+ delete mToolTip;
+
+ LLToolTip::Params tooltip_params(params);
+ // block mouse events if there is a click handler registered (specifically, hover)
+ if (params.click_callback.isProvided() && !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 = true;
+ }
+ tooltip_params.rect = LLRect (0, 1, 1, 0);
+
+ if (tooltip_params.create_callback.isProvided())
+ {
+ mToolTip = tooltip_params.create_callback()(tooltip_params);
+ if (mToolTip == NULL)
+ {
+ return;
+ }
+ }
+ else
+ mToolTip = LLUICtrlFactory::create<LLToolTip> (tooltip_params);
+
+ gToolTipView->addChild(mToolTip);
+
+ if (params.pos.isProvided())
+ {
+ LLCoordGL pos = params.pos;
+ // try to spawn at requested position
+ LLUI::getInstance()->positionViewNearMouse(mToolTip, pos.mX, pos.mY);
+ }
+ else
+ {
+ // just spawn at mouse location
+ LLUI::getInstance()->positionViewNearMouse(mToolTip);
+ }
+
+ //...update "sticky" rect and tooltip position
+ if (params.sticky_rect.isProvided())
+ {
+ mMouseNearRect = params.sticky_rect;
+ }
+ else
+ {
+ S32 mouse_x;
+ S32 mouse_y;
+ LLUI::getInstance()->getMousePositionLocal(gToolTipView->getParent(), &mouse_x, &mouse_y);
+
+ // allow mouse a little bit of slop before changing tooltips
+ mMouseNearRect.setCenterAndSize(mouse_x, mouse_y, 3, 3);
+ }
+
+ // allow mouse to move all the way to the tooltip without changing tooltips
+ // (tooltip can still time out)
+ if (mToolTip->hasClickCallback())
+ {
+ // keep tooltip up when we mouse over it
+ mMouseNearRect.unionWith(mToolTip->getRect());
+ }
+}
+
+
+void LLToolTipMgr::show(const std::string& msg, bool allow_paste_tooltip)
+{
+ show(LLToolTip::Params().message(msg).allow_paste_tooltip(allow_paste_tooltip));
+}
+
+void LLToolTipMgr::show(const LLToolTip::Params& params)
+{
+ if (!params.styled_message.isProvided()
+ && (!params.message.isProvided() || params.message().empty())
+ && !params.image.isProvided() && !params.create_callback.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())
+ {
+ LL_WARNS() << "Could not display tooltip!" << LL_ENDL;
+ return;
+ }
+
+ // are we ready to show the tooltip?
+ if (!mToolTipsBlocked // we haven't hit a key, moved the mouse, etc.
+ && LLUI::getInstance()->getMouseIdleTime() > params_with_defaults.delay_time) // the mouse has been still long enough
+ {
+ bool tooltip_changed = mLastToolTipParams.message() != params_with_defaults.message()
+ || mLastToolTipParams.pos() != params_with_defaults.pos()
+ || mLastToolTipParams.time_based_media() != params_with_defaults.time_based_media()
+ || mLastToolTipParams.web_based_media() != params_with_defaults.web_based_media();
+
+ bool tooltip_shown = mToolTip
+ && mToolTip->getVisible()
+ && !mToolTip->isFading();
+
+ mNeedsToolTip = tooltip_changed || !tooltip_shown;
+ // store description of tooltip for later creation
+ mNextToolTipParams = params_with_defaults;
+ }
+}
+
+// allow new tooltips to be created, e.g. after mouse has moved
+void LLToolTipMgr::unblockToolTips()
+{
+ mToolTipsBlocked = false;
+}
+
+// disallow new tooltips until unblockTooltips called
+void LLToolTipMgr::blockToolTips()
+{
+ hideToolTips();
+ mToolTipsBlocked = true;
+}
+
+void LLToolTipMgr::hideToolTips()
+{
+ if (mToolTip)
+ {
+ mToolTip->setVisible(false);
+ }
+}
+
+bool LLToolTipMgr::toolTipVisible()
+{
+ return mToolTip ? mToolTip->isInVisibleChain() : false;
+}
+
+LLRect LLToolTipMgr::getToolTipRect()
+{
+ if (mToolTip && mToolTip->getVisible())
+ {
+ return mToolTip->getRect();
+ }
+ return LLRect();
+}
+
+
+LLRect LLToolTipMgr::getMouseNearRect()
+{
+ return toolTipVisible() ? mMouseNearRect : LLRect();
+}
+
+// every frame, determine if current tooltip should be hidden
+void LLToolTipMgr::updateToolTipVisibility()
+{
+ // create new tooltip if we have one ready to go
+ if (mNeedsToolTip)
+ {
+ mNeedsToolTip = false;
+ createToolTip(mNextToolTipParams);
+ mLastToolTipParams = mNextToolTipParams;
+
+ return;
+ }
+
+ // hide tooltips when mouse cursor is hidden
+ if (LLUI::getInstance()->getWindow()->isCursorHidden())
+ {
+ blockToolTips();
+ return;
+ }
+
+ // hide existing tooltips if they have timed out
+ F32 tooltip_timeout = 0.f;
+ if (toolTipVisible())
+ {
+ S32 mouse_x, mouse_y;
+ LLUI::getInstance()->getMousePositionLocal(gToolTipView, &mouse_x, &mouse_y);
+
+ // mouse far away from tooltip
+ tooltip_timeout = mLastToolTipParams.visible_time_far;
+ // mouse near rect will only include the tooltip if the
+ // tooltip is clickable
+ if (mMouseNearRect.pointInRect(mouse_x, mouse_y))
+ {
+ // mouse "close" to tooltip
+ tooltip_timeout = mLastToolTipParams.visible_time_near;
+
+ // if tooltip is clickable (has large mMouseNearRect)
+ // than having cursor over tooltip keeps it up indefinitely
+ if (mToolTip->parentPointInView(mouse_x, mouse_y))
+ {
+ // mouse over tooltip itself, don't time out
+ tooltip_timeout = mLastToolTipParams.visible_time_over;
+ }
+ }
+
+ if (mToolTip->getVisibleTime() > tooltip_timeout)
+ {
+ hideToolTips();
+ unblockToolTips();
+ }
+ }
+}
+
+
+// Return the current tooltip text
+void LLToolTipMgr::getToolTipMessage(std::string & message)
+{
+ if (toolTipVisible())
+ {
+ mToolTip->getToolTipMessage(message);
+ }
+}
+
+bool LLToolTipMgr::isTooltipPastable()
+{
+ if (toolTipVisible())
+ {
+ return mToolTip->isTooltipPastable();
+ }
+ return false;
+ }
+
+// EOF
diff --git a/indra/llui/lltooltip.h b/indra/llui/lltooltip.h
index 0794159957..74ae5a219d 100644
--- a/indra/llui/lltooltip.h
+++ b/indra/llui/lltooltip.h
@@ -1,185 +1,185 @@
-/**
- * @file lltooltip.h
- * @brief LLToolTipMgr class definition and related classes
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLTOOLTIP_H
-#define LL_LLTOOLTIP_H
-
-// Library includes
-#include "llsingleton.h"
-#include "llinitparam.h"
-#include "llpanel.h"
-#include "llstyle.h"
-
-//
-// Classes
-//
-class LLToolTipView : public LLView
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLView::Params>
- {
- Params();
- };
- LLToolTipView(const LLToolTipView::Params&);
- /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleMiddleMouseDown(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleScrollWheel( S32 x, S32 y, S32 clicks );
-
- void drawStickyRect();
-
- /*virtual*/ void draw();
-};
-
-class LLToolTip : public LLPanel
-{
-public:
-
- struct StyledText : public LLInitParam::Block<StyledText>
- {
- Mandatory<std::string> text;
- Optional<LLStyle::Params> style;
- };
-
- struct Params : public LLInitParam::Block<Params, LLPanel::Params>
- {
- typedef boost::function<void(void)> click_callback_t;
- typedef boost::function<LLToolTip*(LLToolTip::Params)> create_callback_t;
-
- Optional<std::string> message;
- Multiple<StyledText> styled_message;
-
- Optional<LLCoordGL> pos;
- Optional<F32> delay_time,
- visible_time_over, // time for which tooltip is visible while mouse on it
- visible_time_near, // time for which tooltip is visible while mouse near it
- visible_time_far; // time for which tooltip is visible while mouse moved away
- Optional<LLRect> sticky_rect;
- Optional<const LLFontGL*> font;
- Optional<LLUIImage*> image;
- Optional<LLUIColor> text_color;
- Optional<bool> time_based_media,
- web_based_media,
- media_playing;
- Optional<create_callback_t> create_callback;
- Optional<LLSD> create_params;
- Optional<click_callback_t> click_callback,
- click_playmedia_callback,
- click_homepage_callback;
- Optional<S32> max_width,
- padding;
- Optional<bool> wrap;
-
- Optional<bool> allow_paste_tooltip;
-
- Params();
- };
- /*virtual*/ void draw();
- /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
- /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask);
- /*virtual*/ void setVisible(bool visible);
-
- bool isFading();
- F32 getVisibleTime();
- bool hasClickCallback();
-
- LLToolTip(const Params& p);
- virtual void initFromParams(const LLToolTip::Params& params);
-
- void getToolTipMessage(std::string & message);
- bool isTooltipPastable() { return mIsTooltipPastable; }
-
-protected:
- void updateTextBox();
- void snapToChildren();
-
-protected:
- class LLTextBox* mTextBox;
- class LLButton* mInfoButton;
- class LLButton* mPlayMediaButton;
- class LLButton* mHomePageButton;
-
- LLFrameTimer mFadeTimer;
- LLFrameTimer mVisibleTimer;
- bool mHasClickCallback;
- S32 mPadding; // pixels
- S32 mMaxWidth;
-
- bool mIsTooltipPastable;
-};
-
-// used for the inspector tooltips which need different background images etc.
-class LLInspector : public LLToolTip
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLToolTip::Params>
- {};
-};
-
-class LLToolTipMgr : public LLSingleton<LLToolTipMgr>
-{
- LLSINGLETON(LLToolTipMgr);
- LOG_CLASS(LLToolTipMgr);
-
-public:
- void show(const LLToolTip::Params& params);
- void show(const std::string& message, bool allow_paste_tooltip = false);
-
- void unblockToolTips();
- void blockToolTips();
-
- void hideToolTips();
- bool toolTipVisible();
- LLRect getToolTipRect();
- LLRect getMouseNearRect();
- void updateToolTipVisibility();
-
- void getToolTipMessage(std::string & message);
- bool isTooltipPastable();
-
-private:
- void createToolTip(const LLToolTip::Params& params);
-
- bool mToolTipsBlocked;
- class LLToolTip* mToolTip;
-
- // tooltip creation is deferred until the UI is drawn every frame
- // so the last tooltip to be created in a given frame will win
- LLToolTip::Params mLastToolTipParams; // description of last tooltip we showed
- LLToolTip::Params mNextToolTipParams; // description of next tooltip we want to show
- bool mNeedsToolTip; // do we want to show a tooltip
-
- LLRect mMouseNearRect;
-};
-
-//
-// Globals
-//
-
-extern LLToolTipView *gToolTipView;
-
-#endif
+/**
+ * @file lltooltip.h
+ * @brief LLToolTipMgr class definition and related classes
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTOOLTIP_H
+#define LL_LLTOOLTIP_H
+
+// Library includes
+#include "llsingleton.h"
+#include "llinitparam.h"
+#include "llpanel.h"
+#include "llstyle.h"
+
+//
+// Classes
+//
+class LLToolTipView : public LLView
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLView::Params>
+ {
+ Params();
+ };
+ LLToolTipView(const LLToolTipView::Params&);
+ /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleMiddleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleScrollWheel( S32 x, S32 y, S32 clicks );
+
+ void drawStickyRect();
+
+ /*virtual*/ void draw();
+};
+
+class LLToolTip : public LLPanel
+{
+public:
+
+ struct StyledText : public LLInitParam::Block<StyledText>
+ {
+ Mandatory<std::string> text;
+ Optional<LLStyle::Params> style;
+ };
+
+ struct Params : public LLInitParam::Block<Params, LLPanel::Params>
+ {
+ typedef boost::function<void(void)> click_callback_t;
+ typedef boost::function<LLToolTip*(LLToolTip::Params)> create_callback_t;
+
+ Optional<std::string> message;
+ Multiple<StyledText> styled_message;
+
+ Optional<LLCoordGL> pos;
+ Optional<F32> delay_time,
+ visible_time_over, // time for which tooltip is visible while mouse on it
+ visible_time_near, // time for which tooltip is visible while mouse near it
+ visible_time_far; // time for which tooltip is visible while mouse moved away
+ Optional<LLRect> sticky_rect;
+ Optional<const LLFontGL*> font;
+ Optional<LLUIImage*> image;
+ Optional<LLUIColor> text_color;
+ Optional<bool> time_based_media,
+ web_based_media,
+ media_playing;
+ Optional<create_callback_t> create_callback;
+ Optional<LLSD> create_params;
+ Optional<click_callback_t> click_callback,
+ click_playmedia_callback,
+ click_homepage_callback;
+ Optional<S32> max_width,
+ padding;
+ Optional<bool> wrap;
+
+ Optional<bool> allow_paste_tooltip;
+
+ Params();
+ };
+ /*virtual*/ void draw();
+ /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask);
+ /*virtual*/ void setVisible(bool visible);
+
+ bool isFading();
+ F32 getVisibleTime();
+ bool hasClickCallback();
+
+ LLToolTip(const Params& p);
+ virtual void initFromParams(const LLToolTip::Params& params);
+
+ void getToolTipMessage(std::string & message);
+ bool isTooltipPastable() { return mIsTooltipPastable; }
+
+protected:
+ void updateTextBox();
+ void snapToChildren();
+
+protected:
+ class LLTextBox* mTextBox;
+ class LLButton* mInfoButton;
+ class LLButton* mPlayMediaButton;
+ class LLButton* mHomePageButton;
+
+ LLFrameTimer mFadeTimer;
+ LLFrameTimer mVisibleTimer;
+ bool mHasClickCallback;
+ S32 mPadding; // pixels
+ S32 mMaxWidth;
+
+ bool mIsTooltipPastable;
+};
+
+// used for the inspector tooltips which need different background images etc.
+class LLInspector : public LLToolTip
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLToolTip::Params>
+ {};
+};
+
+class LLToolTipMgr : public LLSingleton<LLToolTipMgr>
+{
+ LLSINGLETON(LLToolTipMgr);
+ LOG_CLASS(LLToolTipMgr);
+
+public:
+ void show(const LLToolTip::Params& params);
+ void show(const std::string& message, bool allow_paste_tooltip = false);
+
+ void unblockToolTips();
+ void blockToolTips();
+
+ void hideToolTips();
+ bool toolTipVisible();
+ LLRect getToolTipRect();
+ LLRect getMouseNearRect();
+ void updateToolTipVisibility();
+
+ void getToolTipMessage(std::string & message);
+ bool isTooltipPastable();
+
+private:
+ void createToolTip(const LLToolTip::Params& params);
+
+ bool mToolTipsBlocked;
+ class LLToolTip* mToolTip;
+
+ // tooltip creation is deferred until the UI is drawn every frame
+ // so the last tooltip to be created in a given frame will win
+ LLToolTip::Params mLastToolTipParams; // description of last tooltip we showed
+ LLToolTip::Params mNextToolTipParams; // description of next tooltip we want to show
+ bool mNeedsToolTip; // do we want to show a tooltip
+
+ LLRect mMouseNearRect;
+};
+
+//
+// Globals
+//
+
+extern LLToolTipView *gToolTipView;
+
+#endif
diff --git a/indra/llui/lltrans.cpp b/indra/llui/lltrans.cpp
index a1ef34159d..6c7e472a87 100644
--- a/indra/llui/lltrans.cpp
+++ b/indra/llui/lltrans.cpp
@@ -5,21 +5,21 @@
* $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$
*/
@@ -28,7 +28,7 @@
#include "lltrans.h"
-#include "llfasttimer.h" // for call count statistics
+#include "llfasttimer.h" // for call count statistics
#include "llxuiparser.h"
#include "llsd.h"
#include "llxmlnode.h"
@@ -41,307 +41,307 @@ LLStringUtil::format_map_t LLTrans::sDefaultArgs;
struct StringDef : public LLInitParam::Block<StringDef>
{
- Mandatory<std::string> name;
- Mandatory<std::string> value;
+ Mandatory<std::string> name;
+ Mandatory<std::string> value;
- StringDef()
- : name("name"),
- value("value")
- {}
+ StringDef()
+ : name("name"),
+ value("value")
+ {}
};
struct StringTable : public LLInitParam::Block<StringTable>
{
- Multiple<StringDef> strings;
- StringTable()
- : strings("string")
- {}
+ Multiple<StringDef> strings;
+ StringTable()
+ : strings("string")
+ {}
};
-//static
+//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;
- }
- static bool default_strings_init = 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;
- if (!default_strings_init)
- {
- sDefaultStringTemplates[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;
- }
- }
- default_strings_init = true;
-
- return true;
+ 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;
+ }
+ static bool default_strings_init = 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;
+ if (!default_strings_init)
+ {
+ sDefaultStringTemplates[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;
+ }
+ }
+ default_strings_init = true;
+
+ 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;
+ 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
+//static
std::string LLTrans::getString(const std::string &xml_desc, const LLStringUtil::format_map_t& msg_args, bool def_string)
{
- // Don't care about time as much as call count. Make sure we're not
- // calling LLTrans::getString() in an inner loop. JC
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
-
- if (def_string)
- {
- return getDefString(xml_desc, msg_args);
- }
-
- 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+")";
- }
+ // Don't care about time as much as call count. Make sure we're not
+ // calling LLTrans::getString() in an inner loop. JC
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+
+ if (def_string)
+ {
+ return getDefString(xml_desc, msg_args);
+ }
+
+ 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
+//static
std::string LLTrans::getDefString(const std::string &xml_desc, const LLStringUtil::format_map_t& msg_args)
{
- template_map_t::iterator iter = sDefaultStringTemplates.find(xml_desc);
- if (iter != sDefaultStringTemplates.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 + ")";
- }
+ template_map_t::iterator iter = sDefaultStringTemplates.find(xml_desc);
+ if (iter != sDefaultStringTemplates.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, bool def_string)
{
- // Don't care about time as much as call count. Make sure we're not
- // calling LLTrans::getString() in an inner loop. JC
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
-
- if (def_string)
- {
- return getDefString(xml_desc, msg_args);
- }
-
- 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+")";
- }
+ // Don't care about time as much as call count. Make sure we're not
+ // calling LLTrans::getString() in an inner loop. JC
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+
+ if (def_string)
+ {
+ return getDefString(xml_desc, msg_args);
+ }
+
+ 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
std::string LLTrans::getDefString(const std::string &xml_desc, const LLSD& msg_args)
{
- template_map_t::iterator iter = sDefaultStringTemplates.find(xml_desc);
- if (iter != sDefaultStringTemplates.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 + ")";
- }
+ template_map_t::iterator iter = sDefaultStringTemplates.find(xml_desc);
+ if (iter != sDefaultStringTemplates.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
+//static
bool LLTrans::findString(std::string &result, const std::string &xml_desc, const LLStringUtil::format_map_t& msg_args)
{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
-
- 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;
- }
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+
+ 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_PROFILE_ZONE_SCOPED_CATEGORY_UI;
-
- 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;
- }
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+
+ 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);
+ // 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;
+ sDefaultArgs[name] = value;
}
diff --git a/indra/llui/lltrans.h b/indra/llui/lltrans.h
index 9bd751fc78..4f38ef9067 100644
--- a/indra/llui/lltrans.h
+++ b/indra/llui/lltrans.h
@@ -5,21 +5,21 @@
* $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$
*/
@@ -43,10 +43,10 @@ class LLSD;
class LLTransTemplate
{
public:
- LLTransTemplate(const std::string& name = LLStringUtil::null, const std::string& text = LLStringUtil::null) : mName(name), mText(text) {}
+ LLTransTemplate(const std::string& name = LLStringUtil::null, const std::string& text = LLStringUtil::null) : mName(name), mText(text) {}
- std::string mName;
- std::string mText;
+ std::string mName;
+ std::string mText;
};
/**
@@ -58,80 +58,80 @@ public:
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, bool def_string = false);
- static std::string getDefString(const std::string &xml_desc, const LLStringUtil::format_map_t& args);
- static std::string getString(const std::string &xml_desc, const LLSD& args, bool def_string = false);
- static std::string getDefString(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, bool def_string = false)
- {
- 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());
- }
-
+ 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, bool def_string = false);
+ static std::string getDefString(const std::string &xml_desc, const LLStringUtil::format_map_t& args);
+ static std::string getString(const std::string &xml_desc, const LLSD& args, bool def_string = false);
+ static std::string getDefString(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, bool def_string = false)
+ {
+ 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 template_map_t sDefaultStringTemplates;
- static LLStringUtil::format_map_t sDefaultArgs;
+ typedef std::map<std::string, LLTransTemplate > template_map_t;
+ static template_map_t sStringTemplates;
+ static template_map_t sDefaultStringTemplates;
+ static LLStringUtil::format_map_t sDefaultArgs;
};
#endif
diff --git a/indra/llui/lltransutil.cpp b/indra/llui/lltransutil.cpp
index a783fdd013..7f44f05f2b 100644
--- a/indra/llui/lltransutil.cpp
+++ b/indra/llui/lltransutil.cpp
@@ -1,74 +1,74 @@
-/**
- * @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 "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;
- // 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)
- {
- const std::string error_string =
- "Second Life viewer couldn't access some of the files it needs and will be closed."
- "\n\nPlease reinstall viewer from https://secondlife.com/support/downloads/ and "
- "contact https://support.secondlife.com if issue persists after reinstall.";
- LLError::LLUserWarningMsg::show(error_string);
- gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN);
- LL_ERRS() << "Couldn't load string table " << xml_filename << " " << errno << LL_ENDL;
- return false;
- }
-
- return LLTrans::parseStrings(root, default_args);
-}
-
-
-bool LLTransUtil::parseLanguageStrings(const std::string& xml_filename)
-{
- LLXMLNodePtr root;
- bool success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root);
-
- if (!success)
- {
- LLError::LLUserWarningMsg::showMissingFiles();
- LL_ERRS() << "Couldn't load localization table " << xml_filename << LL_ENDL;
- return false;
- }
-
- return LLTrans::parseLanguageStrings(root);
-}
+/**
+ * @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 "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;
+ // 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)
+ {
+ const std::string error_string =
+ "Second Life viewer couldn't access some of the files it needs and will be closed."
+ "\n\nPlease reinstall viewer from https://secondlife.com/support/downloads/ and "
+ "contact https://support.secondlife.com if issue persists after reinstall.";
+ LLError::LLUserWarningMsg::show(error_string);
+ gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN);
+ LL_ERRS() << "Couldn't load string table " << xml_filename << " " << errno << LL_ENDL;
+ return false;
+ }
+
+ return LLTrans::parseStrings(root, default_args);
+}
+
+
+bool LLTransUtil::parseLanguageStrings(const std::string& xml_filename)
+{
+ LLXMLNodePtr root;
+ bool success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root);
+
+ if (!success)
+ {
+ LLError::LLUserWarningMsg::showMissingFiles();
+ LL_ERRS() << "Couldn't load localization table " << xml_filename << LL_ENDL;
+ return false;
+ }
+
+ return LLTrans::parseLanguageStrings(root);
+}
diff --git a/indra/llui/lltransutil.h b/indra/llui/lltransutil.h
index 9c7cee3f6f..96ef14ce1d 100644
--- a/indra/llui/lltransutil.h
+++ b/indra/llui/lltransutil.h
@@ -5,21 +5,21 @@
* $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$
*/
@@ -31,15 +31,15 @@
namespace LLTransUtil
{
- /**
- * @brief Parses the xml file that holds the strings. Used once on startup
- * @param xml_filename Filename to parse
- * @param default_args Set of strings (expected to be in the file) to use as default replacement args, e.g. "SECOND_LIFE"
- * @returns true if the file was parsed successfully, true if something went wrong
- */
- bool parseStrings(const std::string& xml_filename, const std::set<std::string>& default_args);
+ /**
+ * @brief Parses the xml file that holds the strings. Used once on startup
+ * @param xml_filename Filename to parse
+ * @param default_args Set of strings (expected to be in the file) to use as default replacement args, e.g. "SECOND_LIFE"
+ * @returns true if the file was parsed successfully, true if something went wrong
+ */
+ bool parseStrings(const std::string& xml_filename, const std::set<std::string>& default_args);
- bool parseLanguageStrings(const std::string& xml_filename);
+ bool parseLanguageStrings(const std::string& xml_filename);
};
#endif
diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp
index cc11d53809..325dccf941 100644
--- a/indra/llui/llui.cpp
+++ b/indra/llui/llui.cpp
@@ -1,738 +1,738 @@
-/**
- * @file llui.cpp
- * @brief UI implementation
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-// Utilities functions the user interface needs
-
-#include "linden_common.h"
-
-#include <string>
-#include <map>
-
-// Linden library includes
-#include "v2math.h"
-#include "m3math.h"
-#include "v4color.h"
-#include "llrender.h"
-#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"
-#include "llview.h"
-#include "lllineeditor.h"
-#include "llfloater.h"
-#include "llfloaterreg.h"
-#include "llmenugl.h"
-#include "llmenubutton.h"
-#include "llloadingindicator.h"
-#include "llwindow.h"
-
-// for registration
-#include "llfiltereditor.h"
-#include "llflyoutbutton.h"
-#include "llsearcheditor.h"
-#include "lltoolbar.h"
-#include "llcleanup.h"
-
-// for XUIParse
-#include "llquaternion.h"
-#include <boost/tokenizer.hpp>
-#include <boost/algorithm/string/find_iterator.hpp>
-#include <boost/algorithm/string/finder.hpp>
-
-//
-// Globals
-//
-
-// Language for UI construction
-std::map<std::string, std::string> gTranslation;
-std::list<std::string> gUntranslated;
-
-// 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
-//
-
-LLUUID find_ui_sound(const char * namep)
-{
- std::string name = ll_safe_string(namep);
- LLUUID uuid = LLUUID(NULL);
- LLUI *ui_inst = LLUI::getInstance();
- if (!ui_inst->mSettingGroups["config"]->controlExists(name))
- {
- LL_WARNS() << "tried to make UI sound for unknown sound name: " << name << LL_ENDL;
- }
- else
- {
- uuid = LLUUID(ui_inst->mSettingGroups["config"]->getString(name));
- if (uuid.isNull())
- {
- if (ui_inst->mSettingGroups["config"]->getString(name) == LLUUID::null.asString())
- {
- if (ui_inst->mSettingGroups["config"]->getBOOL("UISndDebugSpamToggle"))
- {
- LL_INFOS() << "UI sound name: " << name << " triggered but silent (null uuid)" << LL_ENDL;
- }
- }
- else
- {
- LL_WARNS() << "UI sound named: " << name << " does not translate to a valid uuid" << LL_ENDL;
- }
- }
- else if (ui_inst->mAudioCallback != NULL)
- {
- if (ui_inst->mSettingGroups["config"]->getBOOL("UISndDebugSpamToggle"))
- {
- LL_INFOS() << "UI sound name: " << name << LL_ENDL;
- }
- }
- }
-
- return uuid;
-}
-
-void make_ui_sound(const char* namep)
-{
- LLUUID soundUUID = find_ui_sound(namep);
- if(soundUUID.notNull())
- {
- LLUI::getInstance()->mAudioCallback(soundUUID);
- }
-}
-
-void make_ui_sound_deferred(const char* namep)
-{
- LLUUID soundUUID = find_ui_sound(namep);
- if(soundUUID.notNull())
- {
- LLUI::getInstance()->mDeferredAudioCallback(soundUUID);
- }
-}
-
-LLUI::LLUI(const settings_map_t& settings,
- LLImageProviderInterface* image_provider,
- LLUIAudioCallback audio_callback,
- LLUIAudioCallback deferred_audio_callback)
-: mSettingGroups(settings),
-mAudioCallback(audio_callback),
-mDeferredAudioCallback(deferred_audio_callback),
-mWindow(NULL), // set later in startup
-mRootView(NULL),
-mHelpImpl(NULL)
-{
- LLRender2D::initParamSingleton(image_provider);
-
- if ((get_ptr_in_map(mSettingGroups, std::string("config")) == NULL) ||
- (get_ptr_in_map(mSettingGroups, std::string("floater")) == NULL) ||
- (get_ptr_in_map(mSettingGroups, std::string("ignores")) == NULL))
- {
- LL_ERRS() << "Failure to initialize configuration groups" << LL_ENDL;
- }
-
- LLFontGL::sShadowColor = LLUIColorTable::instance().getColor("ColorDropShadow");
-
- LLUICtrl::CommitCallbackRegistry::Registrar& reg = LLUICtrl::CommitCallbackRegistry::defaultRegistrar();
-
- // 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.ShowOrBringToFront", boost::bind(&LLFloaterReg::showInstanceOrBringToFront, _2, LLSD()));
- 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 dockable floaters
- reg.add("Button.SetDockableFloaterToggle", boost::bind(&LLButton::setDockableFloaterToggle, _1, _2));
-
- // Display the help topic for the current context
- reg.add("Button.ShowHelp", boost::bind(&LLButton::showHelp, _1, _2));
-
- // 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 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::setPopupFuncs(const add_popup_t& add_popup, const remove_popup_t& remove_popup, const clear_popups_t& clear_popups)
-{
- mAddPopupFunc = add_popup;
- mRemovePopupFunc = remove_popup;
- mClearPopupsFunc = clear_popups;
-}
-
-void LLUI::setMousePositionScreen(S32 x, S32 y)
-{
-#if defined(LL_DARWIN)
- S32 screen_x = ll_round(((F32)x * getScaleFactor().mV[VX]) / LLView::getWindow()->getSystemUISize());
- S32 screen_y = ll_round(((F32)y * getScaleFactor().mV[VY]) / LLView::getWindow()->getSystemUISize());
-#else
- S32 screen_x = ll_round((F32)x * getScaleFactor().mV[VX]);
- S32 screen_y = ll_round((F32)y * getScaleFactor().mV[VY]);
-#endif
-
- LLView::getWindow()->setCursorPosition(LLCoordGL(screen_x, screen_y).convert());
-}
-
-void LLUI::getMousePositionScreen(S32 *x, S32 *y)
-{
- LLCoordWindow cursor_pos_window;
- getWindow()->getCursorPosition(&cursor_pos_window);
- 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[VY]);
-}
-
-void LLUI::setMousePositionLocal(const LLView* viewp, S32 x, S32 y)
-{
- S32 screen_x, screen_y;
- viewp->localPointToScreen(x, y, &screen_x, &screen_y);
-
- setMousePositionScreen(screen_x, screen_y);
-}
-
-void LLUI::getMousePositionLocal(const LLView* viewp, S32 *x, S32 *y)
-{
- S32 screen_x, screen_y;
- getMousePositionScreen(&screen_x, &screen_y);
- viewp->screenPointToLocal(screen_x, screen_y, x, y);
-}
-
-
-// On Windows, the user typically sets the language when they install the
-// app (by running it with a shortcut that sets InstallLanguage). On Mac,
-// or on Windows if the SecondLife.exe executable is run directly, the
-// language follows the OS language. In all cases the user can override
-// the language manually in preferences. JC
-std::string LLUI::getUILanguage()
-{
- std::string language = "en";
- if (mSettingGroups["config"])
- {
- language = mSettingGroups["config"]->getString("Language");
- if (language.empty() || language == "default")
- {
- language = mSettingGroups["config"]->getString("InstallLanguage");
- }
- if (language.empty() || language == "default")
- {
- language = mSettingGroups["config"]->getString("SystemLanguage");
- }
- if (language.empty() || language == "default")
- {
- language = "en";
- }
- }
- return language;
-}
-
-// static
-std::string LLUI::getLanguage()
-{
- // Note: lldateutil_test redefines this function
- return LLUI::getInstance()->getUILanguage();
-}
-
-struct SubDir : public LLInitParam::Block<SubDir>
-{
- Mandatory<std::string> value;
-
- SubDir()
- : value("value")
- {}
-};
-
-struct Directory : public LLInitParam::Block<Directory>
-{
- Multiple<SubDir, AtLeast<1> > subdirs;
-
- Directory()
- : subdirs("subdir")
- {}
-};
-
-struct Paths : public LLInitParam::Block<Paths>
-{
- Multiple<Directory, AtLeast<1> > directories;
-
- Paths()
- : directories("directory")
- {}
-};
-
-
-//static
-std::string LLUI::locateSkin(const std::string& filename)
-{
- std::string found_file = filename;
- if (gDirUtilp->fileExists(found_file))
- {
- return found_file;
- }
-
- found_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); // Should be CUSTOM_SKINS?
- if (gDirUtilp->fileExists(found_file))
- {
- return found_file;
- }
-
- found_file = gDirUtilp->findSkinnedFilename(LLDir::XUI, filename);
- if (! found_file.empty())
- {
- return found_file;
- }
-
- found_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename);
- if (gDirUtilp->fileExists(found_file))
- {
- return found_file;
- }
- LL_WARNS("LLUI") << "Can't find '" << filename
- << "' in user settings, any skin directory or app_settings" << LL_ENDL;
- return "";
-}
-
-LLVector2 LLUI::getWindowSize()
-{
- LLCoordWindow window_rect;
- mWindow->getSize(&window_rect);
-
- return LLVector2(window_rect.mX / getScaleFactor().mV[VX], window_rect.mY / getScaleFactor().mV[VY]);
-}
-
-void LLUI::screenPointToGL(S32 screen_x, S32 screen_y, S32 *gl_x, S32 *gl_y)
-{
- *gl_x = ll_round((F32)screen_x * getScaleFactor().mV[VX]);
- *gl_y = ll_round((F32)screen_y * getScaleFactor().mV[VY]);
-}
-
-void LLUI::glPointToScreen(S32 gl_x, S32 gl_y, S32 *screen_x, S32 *screen_y)
-{
- *screen_x = ll_round((F32)gl_x / getScaleFactor().mV[VX]);
- *screen_y = ll_round((F32)gl_y / getScaleFactor().mV[VY]);
-}
-
-void LLUI::screenRectToGL(const LLRect& screen, LLRect *gl)
-{
- screenPointToGL(screen.mLeft, screen.mTop, &gl->mLeft, &gl->mTop);
- screenPointToGL(screen.mRight, screen.mBottom, &gl->mRight, &gl->mBottom);
-}
-
-void LLUI::glRectToScreen(const LLRect& gl, LLRect *screen)
-{
- glPointToScreen(gl.mLeft, gl.mTop, &screen->mLeft, &screen->mTop);
- glPointToScreen(gl.mRight, gl.mBottom, &screen->mRight, &screen->mBottom);
-}
-
-
-LLControlGroup& LLUI::getControlControlGroup (const std::string& controlname)
-{
- for (settings_map_t::iterator itor = mSettingGroups.begin();
- itor != mSettingGroups.end(); ++itor)
- {
- LLControlGroup* control_group = itor->second;
- if(control_group != NULL)
- {
- if (control_group->controlExists(controlname))
- return *control_group;
- }
- }
-
- return *mSettingGroups["config"]; // default group
-}
-
-void LLUI::addPopup(LLView* viewp)
-{
- if (mAddPopupFunc)
- {
- mAddPopupFunc(viewp);
- }
-}
-
-void LLUI::removePopup(LLView* viewp)
-{
- if (mRemovePopupFunc)
- {
- mRemovePopupFunc(viewp);
- }
-}
-
-void LLUI::clearPopups()
-{
- if (mClearPopupsFunc)
- {
- mClearPopupsFunc();
- }
-}
-
-void LLUI::reportBadKeystroke()
-{
- make_ui_sound("UISndBadKeystroke");
-}
-
-// spawn_x and spawn_y are top left corner of view in screen GL coordinates
-void LLUI::positionViewNearMouse(LLView* view, S32 spawn_x, S32 spawn_y)
-{
- const S32 CURSOR_HEIGHT = 16; // Approximate "normal" cursor size
- const S32 CURSOR_WIDTH = 8;
-
- LLView* parent = view->getParent();
-
- S32 mouse_x;
- S32 mouse_y;
- getMousePositionScreen(&mouse_x, &mouse_y);
-
- // If no spawn location provided, use mouse position
- if (spawn_x == S32_MAX || spawn_y == S32_MAX)
- {
- spawn_x = mouse_x + CURSOR_WIDTH;
- spawn_y = mouse_y - CURSOR_HEIGHT;
- }
-
- LLRect virtual_window_rect = parent->getLocalRect();
-
- LLRect mouse_rect;
- const S32 MOUSE_CURSOR_PADDING = 1;
- mouse_rect.setLeftTopAndSize(mouse_x - MOUSE_CURSOR_PADDING,
- mouse_y + MOUSE_CURSOR_PADDING,
- CURSOR_WIDTH + MOUSE_CURSOR_PADDING * 2,
- CURSOR_HEIGHT + MOUSE_CURSOR_PADDING * 2);
-
- S32 local_x, local_y;
- // convert screen coordinates to 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 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(LLURI::unescape(part), recurse);
- recurse = false;
- }
- }
-
- return context;
-}
-
-//static
-LLVector2& LLUI::getScaleFactor()
-{
- return LLRender::sUIGLScaleFactor;
-}
-
-//static
-void LLUI::setScaleFactor(const LLVector2& scale_factor)
-{
- LLRender::sUIGLScaleFactor = scale_factor;
-}
-
-
-// LLLocalClipRect and LLScreenClipRect moved to lllocalcliprect.h/cpp
-
-namespace LLInitParam
-{
- ParamValue<LLUIColor>::ParamValue(const LLUIColor& color)
- : super_t(color),
- red("red"),
- green("green"),
- blue("blue"),
- alpha("alpha"),
- control("")
- {
- updateBlockFromValue(false);
- }
-
- void ParamValue<LLUIColor>::updateValueFromBlock()
- {
- if (control.isProvided() && !control().empty())
- {
- updateValue(LLUIColorTable::instance().getColor(control));
- }
- else
- {
- updateValue(LLColor4(red, green, blue, alpha));
- }
- }
-
- void ParamValue<LLUIColor>::updateBlockFromValue(bool make_block_authoritative)
- {
- 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)
- {
- return !(a->getFontDesc() < b->getFontDesc())
- && !(b->getFontDesc() < a->getFontDesc());
- }
-
- ParamValue<const LLFontGL*>::ParamValue(const LLFontGL* fontp)
- : super_t(fontp),
- name("name"),
- size("size"),
- style("style")
- {
- if (!fontp)
- {
- updateValue(LLFontGL::getFontDefault());
- }
- addSynonym(name, "");
- updateBlockFromValue(false);
- }
-
- void ParamValue<const LLFontGL*>::updateValueFromBlock()
- {
- const LLFontGL* res_fontp = LLFontGL::getFontByName(name);
- if (res_fontp)
- {
- updateValue(res_fontp);
- return;
- }
-
- U8 fontstyle = 0;
- fontstyle = LLFontGL::getStyleFromString(style());
- LLFontDescriptor desc(name(), size(), fontstyle);
- const LLFontGL* fontp = LLFontGL::getFont(desc);
- if (fontp)
- {
- updateValue(fontp);
- }
- else
- {
- updateValue(LLFontGL::getFontDefault());
- }
- }
-
- void ParamValue<const LLFontGL*>::updateBlockFromValue(bool make_block_authoritative)
- {
- if (getValue())
- {
- 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);
- }
- }
-
- ParamValue<LLRect>::ParamValue(const LLRect& rect)
- : super_t(rect),
- left("left"),
- top("top"),
- right("right"),
- bottom("bottom"),
- width("width"),
- height("height")
- {
- updateBlockFromValue(false);
- }
-
- void ParamValue<LLRect>::updateValueFromBlock()
- {
- LLRect rect;
-
- //calculate from params
- // prefer explicit left and right
- if (left.isProvided() && right.isProvided())
- {
- rect.mLeft = left;
- rect.mRight = right;
- }
- // otherwise use width along with specified side, if any
- else if (width.isProvided())
- {
- // only right + width provided
- if (right.isProvided())
- {
- rect.mRight = right;
- rect.mLeft = right - width;
- }
- else // left + width, or just width
- {
- rect.mLeft = left;
- rect.mRight = left + width;
- }
- }
- // just left, just right, or none
- else
- {
- rect.mLeft = left;
- rect.mRight = right;
- }
-
- // prefer explicit bottom and top
- if (bottom.isProvided() && top.isProvided())
- {
- rect.mBottom = bottom;
- rect.mTop = top;
- }
- // otherwise height along with specified side, if any
- else if (height.isProvided())
- {
- // top + height provided
- if (top.isProvided())
- {
- rect.mTop = top;
- rect.mBottom = top - height;
- }
- // bottom + height or just height
- else
- {
- rect.mBottom = bottom;
- rect.mTop = bottom + height;
- }
- }
- // just bottom, just top, or none
- else
- {
- rect.mBottom = bottom;
- rect.mTop = top;
- }
- updateValue(rect);
- }
-
- void ParamValue<LLRect>::updateBlockFromValue(bool make_block_authoritative)
- {
- // because of the ambiguity in specifying a rect by position and/or dimensions
- // we use the lowest priority pairing so that any valid pairing in xui
- // 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);
- }
-
- ParamValue<LLCoordGL>::ParamValue(const LLCoordGL& coord)
- : super_t(coord),
- x("x"),
- y("y")
- {
- updateBlockFromValue(false);
- }
-
- void ParamValue<LLCoordGL>::updateValueFromBlock()
- {
- updateValue(LLCoordGL(x, y));
- }
-
- void ParamValue<LLCoordGL>::updateBlockFromValue(bool make_block_authoritative)
- {
- x.set(getValue().mX, make_block_authoritative);
- y.set(getValue().mY, make_block_authoritative);
- }
-
-
- void TypeValues<LLFontGL::HAlign>::declareValues()
- {
- declare("left", LLFontGL::LEFT);
- declare("right", LLFontGL::RIGHT);
- declare("center", LLFontGL::HCENTER);
- }
-
- void TypeValues<LLFontGL::VAlign>::declareValues()
- {
- declare("top", LLFontGL::TOP);
- declare("center", LLFontGL::VCENTER);
- declare("baseline", LLFontGL::BASELINE);
- declare("bottom", LLFontGL::BOTTOM);
- }
-
- void TypeValues<LLFontGL::ShadowType>::declareValues()
- {
- declare("none", LLFontGL::NO_SHADOW);
- declare("hard", LLFontGL::DROP_SHADOW);
- declare("soft", LLFontGL::DROP_SHADOW_SOFT);
- }
-}
-
+/**
+ * @file llui.cpp
+ * @brief UI implementation
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+// Utilities functions the user interface needs
+
+#include "linden_common.h"
+
+#include <string>
+#include <map>
+
+// Linden library includes
+#include "v2math.h"
+#include "m3math.h"
+#include "v4color.h"
+#include "llrender.h"
+#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"
+#include "llview.h"
+#include "lllineeditor.h"
+#include "llfloater.h"
+#include "llfloaterreg.h"
+#include "llmenugl.h"
+#include "llmenubutton.h"
+#include "llloadingindicator.h"
+#include "llwindow.h"
+
+// for registration
+#include "llfiltereditor.h"
+#include "llflyoutbutton.h"
+#include "llsearcheditor.h"
+#include "lltoolbar.h"
+#include "llcleanup.h"
+
+// for XUIParse
+#include "llquaternion.h"
+#include <boost/tokenizer.hpp>
+#include <boost/algorithm/string/find_iterator.hpp>
+#include <boost/algorithm/string/finder.hpp>
+
+//
+// Globals
+//
+
+// Language for UI construction
+std::map<std::string, std::string> gTranslation;
+std::list<std::string> gUntranslated;
+
+// 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
+//
+
+LLUUID find_ui_sound(const char * namep)
+{
+ std::string name = ll_safe_string(namep);
+ LLUUID uuid = LLUUID(NULL);
+ LLUI *ui_inst = LLUI::getInstance();
+ if (!ui_inst->mSettingGroups["config"]->controlExists(name))
+ {
+ LL_WARNS() << "tried to make UI sound for unknown sound name: " << name << LL_ENDL;
+ }
+ else
+ {
+ uuid = LLUUID(ui_inst->mSettingGroups["config"]->getString(name));
+ if (uuid.isNull())
+ {
+ if (ui_inst->mSettingGroups["config"]->getString(name) == LLUUID::null.asString())
+ {
+ if (ui_inst->mSettingGroups["config"]->getBOOL("UISndDebugSpamToggle"))
+ {
+ LL_INFOS() << "UI sound name: " << name << " triggered but silent (null uuid)" << LL_ENDL;
+ }
+ }
+ else
+ {
+ LL_WARNS() << "UI sound named: " << name << " does not translate to a valid uuid" << LL_ENDL;
+ }
+ }
+ else if (ui_inst->mAudioCallback != NULL)
+ {
+ if (ui_inst->mSettingGroups["config"]->getBOOL("UISndDebugSpamToggle"))
+ {
+ LL_INFOS() << "UI sound name: " << name << LL_ENDL;
+ }
+ }
+ }
+
+ return uuid;
+}
+
+void make_ui_sound(const char* namep)
+{
+ LLUUID soundUUID = find_ui_sound(namep);
+ if(soundUUID.notNull())
+ {
+ LLUI::getInstance()->mAudioCallback(soundUUID);
+ }
+}
+
+void make_ui_sound_deferred(const char* namep)
+{
+ LLUUID soundUUID = find_ui_sound(namep);
+ if(soundUUID.notNull())
+ {
+ LLUI::getInstance()->mDeferredAudioCallback(soundUUID);
+ }
+}
+
+LLUI::LLUI(const settings_map_t& settings,
+ LLImageProviderInterface* image_provider,
+ LLUIAudioCallback audio_callback,
+ LLUIAudioCallback deferred_audio_callback)
+: mSettingGroups(settings),
+mAudioCallback(audio_callback),
+mDeferredAudioCallback(deferred_audio_callback),
+mWindow(NULL), // set later in startup
+mRootView(NULL),
+mHelpImpl(NULL)
+{
+ LLRender2D::initParamSingleton(image_provider);
+
+ if ((get_ptr_in_map(mSettingGroups, std::string("config")) == NULL) ||
+ (get_ptr_in_map(mSettingGroups, std::string("floater")) == NULL) ||
+ (get_ptr_in_map(mSettingGroups, std::string("ignores")) == NULL))
+ {
+ LL_ERRS() << "Failure to initialize configuration groups" << LL_ENDL;
+ }
+
+ LLFontGL::sShadowColor = LLUIColorTable::instance().getColor("ColorDropShadow");
+
+ LLUICtrl::CommitCallbackRegistry::Registrar& reg = LLUICtrl::CommitCallbackRegistry::defaultRegistrar();
+
+ // 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.ShowOrBringToFront", boost::bind(&LLFloaterReg::showInstanceOrBringToFront, _2, LLSD()));
+ 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 dockable floaters
+ reg.add("Button.SetDockableFloaterToggle", boost::bind(&LLButton::setDockableFloaterToggle, _1, _2));
+
+ // Display the help topic for the current context
+ reg.add("Button.ShowHelp", boost::bind(&LLButton::showHelp, _1, _2));
+
+ // 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 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::setPopupFuncs(const add_popup_t& add_popup, const remove_popup_t& remove_popup, const clear_popups_t& clear_popups)
+{
+ mAddPopupFunc = add_popup;
+ mRemovePopupFunc = remove_popup;
+ mClearPopupsFunc = clear_popups;
+}
+
+void LLUI::setMousePositionScreen(S32 x, S32 y)
+{
+#if defined(LL_DARWIN)
+ S32 screen_x = ll_round(((F32)x * getScaleFactor().mV[VX]) / LLView::getWindow()->getSystemUISize());
+ S32 screen_y = ll_round(((F32)y * getScaleFactor().mV[VY]) / LLView::getWindow()->getSystemUISize());
+#else
+ S32 screen_x = ll_round((F32)x * getScaleFactor().mV[VX]);
+ S32 screen_y = ll_round((F32)y * getScaleFactor().mV[VY]);
+#endif
+
+ LLView::getWindow()->setCursorPosition(LLCoordGL(screen_x, screen_y).convert());
+}
+
+void LLUI::getMousePositionScreen(S32 *x, S32 *y)
+{
+ LLCoordWindow cursor_pos_window;
+ getWindow()->getCursorPosition(&cursor_pos_window);
+ 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[VY]);
+}
+
+void LLUI::setMousePositionLocal(const LLView* viewp, S32 x, S32 y)
+{
+ S32 screen_x, screen_y;
+ viewp->localPointToScreen(x, y, &screen_x, &screen_y);
+
+ setMousePositionScreen(screen_x, screen_y);
+}
+
+void LLUI::getMousePositionLocal(const LLView* viewp, S32 *x, S32 *y)
+{
+ S32 screen_x, screen_y;
+ getMousePositionScreen(&screen_x, &screen_y);
+ viewp->screenPointToLocal(screen_x, screen_y, x, y);
+}
+
+
+// On Windows, the user typically sets the language when they install the
+// app (by running it with a shortcut that sets InstallLanguage). On Mac,
+// or on Windows if the SecondLife.exe executable is run directly, the
+// language follows the OS language. In all cases the user can override
+// the language manually in preferences. JC
+std::string LLUI::getUILanguage()
+{
+ std::string language = "en";
+ if (mSettingGroups["config"])
+ {
+ language = mSettingGroups["config"]->getString("Language");
+ if (language.empty() || language == "default")
+ {
+ language = mSettingGroups["config"]->getString("InstallLanguage");
+ }
+ if (language.empty() || language == "default")
+ {
+ language = mSettingGroups["config"]->getString("SystemLanguage");
+ }
+ if (language.empty() || language == "default")
+ {
+ language = "en";
+ }
+ }
+ return language;
+}
+
+// static
+std::string LLUI::getLanguage()
+{
+ // Note: lldateutil_test redefines this function
+ return LLUI::getInstance()->getUILanguage();
+}
+
+struct SubDir : public LLInitParam::Block<SubDir>
+{
+ Mandatory<std::string> value;
+
+ SubDir()
+ : value("value")
+ {}
+};
+
+struct Directory : public LLInitParam::Block<Directory>
+{
+ Multiple<SubDir, AtLeast<1> > subdirs;
+
+ Directory()
+ : subdirs("subdir")
+ {}
+};
+
+struct Paths : public LLInitParam::Block<Paths>
+{
+ Multiple<Directory, AtLeast<1> > directories;
+
+ Paths()
+ : directories("directory")
+ {}
+};
+
+
+//static
+std::string LLUI::locateSkin(const std::string& filename)
+{
+ std::string found_file = filename;
+ if (gDirUtilp->fileExists(found_file))
+ {
+ return found_file;
+ }
+
+ found_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); // Should be CUSTOM_SKINS?
+ if (gDirUtilp->fileExists(found_file))
+ {
+ return found_file;
+ }
+
+ found_file = gDirUtilp->findSkinnedFilename(LLDir::XUI, filename);
+ if (! found_file.empty())
+ {
+ return found_file;
+ }
+
+ found_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename);
+ if (gDirUtilp->fileExists(found_file))
+ {
+ return found_file;
+ }
+ LL_WARNS("LLUI") << "Can't find '" << filename
+ << "' in user settings, any skin directory or app_settings" << LL_ENDL;
+ return "";
+}
+
+LLVector2 LLUI::getWindowSize()
+{
+ LLCoordWindow window_rect;
+ mWindow->getSize(&window_rect);
+
+ return LLVector2(window_rect.mX / getScaleFactor().mV[VX], window_rect.mY / getScaleFactor().mV[VY]);
+}
+
+void LLUI::screenPointToGL(S32 screen_x, S32 screen_y, S32 *gl_x, S32 *gl_y)
+{
+ *gl_x = ll_round((F32)screen_x * getScaleFactor().mV[VX]);
+ *gl_y = ll_round((F32)screen_y * getScaleFactor().mV[VY]);
+}
+
+void LLUI::glPointToScreen(S32 gl_x, S32 gl_y, S32 *screen_x, S32 *screen_y)
+{
+ *screen_x = ll_round((F32)gl_x / getScaleFactor().mV[VX]);
+ *screen_y = ll_round((F32)gl_y / getScaleFactor().mV[VY]);
+}
+
+void LLUI::screenRectToGL(const LLRect& screen, LLRect *gl)
+{
+ screenPointToGL(screen.mLeft, screen.mTop, &gl->mLeft, &gl->mTop);
+ screenPointToGL(screen.mRight, screen.mBottom, &gl->mRight, &gl->mBottom);
+}
+
+void LLUI::glRectToScreen(const LLRect& gl, LLRect *screen)
+{
+ glPointToScreen(gl.mLeft, gl.mTop, &screen->mLeft, &screen->mTop);
+ glPointToScreen(gl.mRight, gl.mBottom, &screen->mRight, &screen->mBottom);
+}
+
+
+LLControlGroup& LLUI::getControlControlGroup (const std::string& controlname)
+{
+ for (settings_map_t::iterator itor = mSettingGroups.begin();
+ itor != mSettingGroups.end(); ++itor)
+ {
+ LLControlGroup* control_group = itor->second;
+ if(control_group != NULL)
+ {
+ if (control_group->controlExists(controlname))
+ return *control_group;
+ }
+ }
+
+ return *mSettingGroups["config"]; // default group
+}
+
+void LLUI::addPopup(LLView* viewp)
+{
+ if (mAddPopupFunc)
+ {
+ mAddPopupFunc(viewp);
+ }
+}
+
+void LLUI::removePopup(LLView* viewp)
+{
+ if (mRemovePopupFunc)
+ {
+ mRemovePopupFunc(viewp);
+ }
+}
+
+void LLUI::clearPopups()
+{
+ if (mClearPopupsFunc)
+ {
+ mClearPopupsFunc();
+ }
+}
+
+void LLUI::reportBadKeystroke()
+{
+ make_ui_sound("UISndBadKeystroke");
+}
+
+// spawn_x and spawn_y are top left corner of view in screen GL coordinates
+void LLUI::positionViewNearMouse(LLView* view, S32 spawn_x, S32 spawn_y)
+{
+ const S32 CURSOR_HEIGHT = 16; // Approximate "normal" cursor size
+ const S32 CURSOR_WIDTH = 8;
+
+ LLView* parent = view->getParent();
+
+ S32 mouse_x;
+ S32 mouse_y;
+ getMousePositionScreen(&mouse_x, &mouse_y);
+
+ // If no spawn location provided, use mouse position
+ if (spawn_x == S32_MAX || spawn_y == S32_MAX)
+ {
+ spawn_x = mouse_x + CURSOR_WIDTH;
+ spawn_y = mouse_y - CURSOR_HEIGHT;
+ }
+
+ LLRect virtual_window_rect = parent->getLocalRect();
+
+ LLRect mouse_rect;
+ const S32 MOUSE_CURSOR_PADDING = 1;
+ mouse_rect.setLeftTopAndSize(mouse_x - MOUSE_CURSOR_PADDING,
+ mouse_y + MOUSE_CURSOR_PADDING,
+ CURSOR_WIDTH + MOUSE_CURSOR_PADDING * 2,
+ CURSOR_HEIGHT + MOUSE_CURSOR_PADDING * 2);
+
+ S32 local_x, local_y;
+ // convert screen coordinates to 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 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(LLURI::unescape(part), recurse);
+ recurse = false;
+ }
+ }
+
+ return context;
+}
+
+//static
+LLVector2& LLUI::getScaleFactor()
+{
+ return LLRender::sUIGLScaleFactor;
+}
+
+//static
+void LLUI::setScaleFactor(const LLVector2& scale_factor)
+{
+ LLRender::sUIGLScaleFactor = scale_factor;
+}
+
+
+// LLLocalClipRect and LLScreenClipRect moved to lllocalcliprect.h/cpp
+
+namespace LLInitParam
+{
+ ParamValue<LLUIColor>::ParamValue(const LLUIColor& color)
+ : super_t(color),
+ red("red"),
+ green("green"),
+ blue("blue"),
+ alpha("alpha"),
+ control("")
+ {
+ updateBlockFromValue(false);
+ }
+
+ void ParamValue<LLUIColor>::updateValueFromBlock()
+ {
+ if (control.isProvided() && !control().empty())
+ {
+ updateValue(LLUIColorTable::instance().getColor(control));
+ }
+ else
+ {
+ updateValue(LLColor4(red, green, blue, alpha));
+ }
+ }
+
+ void ParamValue<LLUIColor>::updateBlockFromValue(bool make_block_authoritative)
+ {
+ 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)
+ {
+ return !(a->getFontDesc() < b->getFontDesc())
+ && !(b->getFontDesc() < a->getFontDesc());
+ }
+
+ ParamValue<const LLFontGL*>::ParamValue(const LLFontGL* fontp)
+ : super_t(fontp),
+ name("name"),
+ size("size"),
+ style("style")
+ {
+ if (!fontp)
+ {
+ updateValue(LLFontGL::getFontDefault());
+ }
+ addSynonym(name, "");
+ updateBlockFromValue(false);
+ }
+
+ void ParamValue<const LLFontGL*>::updateValueFromBlock()
+ {
+ const LLFontGL* res_fontp = LLFontGL::getFontByName(name);
+ if (res_fontp)
+ {
+ updateValue(res_fontp);
+ return;
+ }
+
+ U8 fontstyle = 0;
+ fontstyle = LLFontGL::getStyleFromString(style());
+ LLFontDescriptor desc(name(), size(), fontstyle);
+ const LLFontGL* fontp = LLFontGL::getFont(desc);
+ if (fontp)
+ {
+ updateValue(fontp);
+ }
+ else
+ {
+ updateValue(LLFontGL::getFontDefault());
+ }
+ }
+
+ void ParamValue<const LLFontGL*>::updateBlockFromValue(bool make_block_authoritative)
+ {
+ if (getValue())
+ {
+ 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);
+ }
+ }
+
+ ParamValue<LLRect>::ParamValue(const LLRect& rect)
+ : super_t(rect),
+ left("left"),
+ top("top"),
+ right("right"),
+ bottom("bottom"),
+ width("width"),
+ height("height")
+ {
+ updateBlockFromValue(false);
+ }
+
+ void ParamValue<LLRect>::updateValueFromBlock()
+ {
+ LLRect rect;
+
+ //calculate from params
+ // prefer explicit left and right
+ if (left.isProvided() && right.isProvided())
+ {
+ rect.mLeft = left;
+ rect.mRight = right;
+ }
+ // otherwise use width along with specified side, if any
+ else if (width.isProvided())
+ {
+ // only right + width provided
+ if (right.isProvided())
+ {
+ rect.mRight = right;
+ rect.mLeft = right - width;
+ }
+ else // left + width, or just width
+ {
+ rect.mLeft = left;
+ rect.mRight = left + width;
+ }
+ }
+ // just left, just right, or none
+ else
+ {
+ rect.mLeft = left;
+ rect.mRight = right;
+ }
+
+ // prefer explicit bottom and top
+ if (bottom.isProvided() && top.isProvided())
+ {
+ rect.mBottom = bottom;
+ rect.mTop = top;
+ }
+ // otherwise height along with specified side, if any
+ else if (height.isProvided())
+ {
+ // top + height provided
+ if (top.isProvided())
+ {
+ rect.mTop = top;
+ rect.mBottom = top - height;
+ }
+ // bottom + height or just height
+ else
+ {
+ rect.mBottom = bottom;
+ rect.mTop = bottom + height;
+ }
+ }
+ // just bottom, just top, or none
+ else
+ {
+ rect.mBottom = bottom;
+ rect.mTop = top;
+ }
+ updateValue(rect);
+ }
+
+ void ParamValue<LLRect>::updateBlockFromValue(bool make_block_authoritative)
+ {
+ // because of the ambiguity in specifying a rect by position and/or dimensions
+ // we use the lowest priority pairing so that any valid pairing in xui
+ // 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);
+ }
+
+ ParamValue<LLCoordGL>::ParamValue(const LLCoordGL& coord)
+ : super_t(coord),
+ x("x"),
+ y("y")
+ {
+ updateBlockFromValue(false);
+ }
+
+ void ParamValue<LLCoordGL>::updateValueFromBlock()
+ {
+ updateValue(LLCoordGL(x, y));
+ }
+
+ void ParamValue<LLCoordGL>::updateBlockFromValue(bool make_block_authoritative)
+ {
+ x.set(getValue().mX, make_block_authoritative);
+ y.set(getValue().mY, make_block_authoritative);
+ }
+
+
+ void TypeValues<LLFontGL::HAlign>::declareValues()
+ {
+ declare("left", LLFontGL::LEFT);
+ declare("right", LLFontGL::RIGHT);
+ declare("center", LLFontGL::HCENTER);
+ }
+
+ void TypeValues<LLFontGL::VAlign>::declareValues()
+ {
+ declare("top", LLFontGL::TOP);
+ declare("center", LLFontGL::VCENTER);
+ declare("baseline", LLFontGL::BASELINE);
+ declare("bottom", LLFontGL::BOTTOM);
+ }
+
+ void TypeValues<LLFontGL::ShadowType>::declareValues()
+ {
+ declare("none", LLFontGL::NO_SHADOW);
+ declare("hard", LLFontGL::DROP_SHADOW);
+ declare("soft", LLFontGL::DROP_SHADOW_SOFT);
+ }
+}
+
diff --git a/indra/llui/llui.h b/indra/llui/llui.h
index 86b23c8c93..471602515d 100644
--- a/indra/llui/llui.h
+++ b/indra/llui/llui.h
@@ -1,25 +1,25 @@
-/**
+/**
* @file llui.h
* @brief General static UI services.
*
* $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$
*/
@@ -53,28 +53,28 @@ class LLWindow;
class LLView;
class LLHelp;
-
-// this enum is used by the llview.h (viewer) and the llassetstorage.h (viewer and sim)
+const S32 DRAG_N_DROP_DISTANCE_THRESHOLD = 3;
+// this enum is used by the llview.h (viewer) and the llassetstorage.h (viewer and sim)
enum EDragAndDropType
{
- 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_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_SETTINGS = 18,
DAD_MATERIAL = 19,
DAD_COUNT = 20, // number of types in this enum
@@ -84,21 +84,21 @@ enum EDragAndDropType
// ordered by priority for multi-drag
enum EAcceptance
{
- 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.
+ 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.
};
enum EAddPosition
{
- ADD_TOP,
- ADD_BOTTOM,
- ADD_DEFAULT
+ ADD_TOP,
+ ADD_BOTTOM,
+ ADD_DEFAULT
};
@@ -107,237 +107,237 @@ void make_ui_sound_deferred(const char * name);
class LLImageProviderInterface;
-typedef void (*LLUIAudioCallback)(const LLUUID& uuid);
+typedef void (*LLUIAudioCallback)(const LLUUID& uuid);
class LLUI : public LLParamSingleton<LLUI>
{
public:
- typedef std::map<std::string, LLControlGroup*> settings_map_t;
+ typedef std::map<std::string, LLControlGroup*> settings_map_t;
private:
- LLSINGLETON(LLUI , const settings_map_t &settings,
- LLImageProviderInterface* image_provider,
- LLUIAudioCallback audio_callback,
- LLUIAudioCallback deferred_audio_callback);
- LOG_CLASS(LLUI);
+ LLSINGLETON(LLUI , const settings_map_t &settings,
+ LLImageProviderInterface* image_provider,
+ LLUIAudioCallback audio_callback,
+ LLUIAudioCallback deferred_audio_callback);
+ 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 boost::function<void(LLView*)> add_popup_t;
- typedef boost::function<void(LLView*)> remove_popup_t;
- typedef boost::function<void(void)> clear_popups_t;
-
- void setPopupFuncs(const add_popup_t& add_popup, const remove_popup_t&, const clear_popups_t& );
-
- // Return the ISO639 language name ("en", "ko", etc.) for the viewer UI.
- // http://www.loc.gov/standards/iso639-2/php/code_list.php
- std::string getUILanguage();
- static std::string getLanguage(); // static for lldateutil_test compatibility
-
- //helper functions (should probably move free standing rendering helper functions here)
- LLView* getRootView() { return mRootView; }
- void setRootView(LLView* view) { mRootView = 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
- */
- const LLView* resolvePath(const LLView* context, const std::string& path);
- LLView* resolvePath(LLView* context, const std::string& path);
- static std::string locateSkin(const std::string& filename);
- void setMousePositionScreen(S32 x, S32 y);
- void getMousePositionScreen(S32 *x, S32 *y);
- void setMousePositionLocal(const LLView* viewp, S32 x, S32 y);
- void getMousePositionLocal(const LLView* viewp, S32 *x, S32 *y);
- LLVector2 getWindowSize();
- void screenPointToGL(S32 screen_x, S32 screen_y, S32 *gl_x, S32 *gl_y);
- void glPointToScreen(S32 gl_x, S32 gl_y, S32 *screen_x, S32 *screen_y);
- void screenRectToGL(const LLRect& screen, LLRect *gl);
- void glRectToScreen(const LLRect& gl, LLRect *screen);
- // Returns the control group containing the control name, or the default group
- LLControlGroup& getControlControlGroup (const std::string& controlname);
- F32 getMouseIdleTime() { return mMouseIdleTimer.getElapsedTimeF32(); }
- void resetMouseIdleTimer() { mMouseIdleTimer.reset(); }
- LLWindow* getWindow() { return mWindow; }
-
- void addPopup(LLView*);
- void removePopup(LLView*);
- void clearPopups();
-
- void reportBadKeystroke();
-
- // Ensures view does not overlap mouse cursor, but is inside
- // the view's parent rectangle. Used for tooltips, inspectors.
- // Optionally override the view's default X/Y, which are relative to the
- // view's parent.
- void positionViewNearMouse(LLView* view, S32 spawn_x = S32_MAX, S32 spawn_y = S32_MAX);
-
- // LLRender2D wrappers
- 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); }
+ //
+ // 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 boost::function<void(LLView*)> add_popup_t;
+ typedef boost::function<void(LLView*)> remove_popup_t;
+ typedef boost::function<void(void)> clear_popups_t;
+
+ void setPopupFuncs(const add_popup_t& add_popup, const remove_popup_t&, const clear_popups_t& );
+
+ // Return the ISO639 language name ("en", "ko", etc.) for the viewer UI.
+ // http://www.loc.gov/standards/iso639-2/php/code_list.php
+ std::string getUILanguage();
+ static std::string getLanguage(); // static for lldateutil_test compatibility
+
+ //helper functions (should probably move free standing rendering helper functions here)
+ LLView* getRootView() { return mRootView; }
+ void setRootView(LLView* view) { mRootView = 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
+ */
+ const LLView* resolvePath(const LLView* context, const std::string& path);
+ LLView* resolvePath(LLView* context, const std::string& path);
+ static std::string locateSkin(const std::string& filename);
+ void setMousePositionScreen(S32 x, S32 y);
+ void getMousePositionScreen(S32 *x, S32 *y);
+ void setMousePositionLocal(const LLView* viewp, S32 x, S32 y);
+ void getMousePositionLocal(const LLView* viewp, S32 *x, S32 *y);
+ LLVector2 getWindowSize();
+ void screenPointToGL(S32 screen_x, S32 screen_y, S32 *gl_x, S32 *gl_y);
+ void glPointToScreen(S32 gl_x, S32 gl_y, S32 *screen_x, S32 *screen_y);
+ void screenRectToGL(const LLRect& screen, LLRect *gl);
+ void glRectToScreen(const LLRect& gl, LLRect *screen);
+ // Returns the control group containing the control name, or the default group
+ LLControlGroup& getControlControlGroup (const std::string& controlname);
+ F32 getMouseIdleTime() { return mMouseIdleTimer.getElapsedTimeF32(); }
+ void resetMouseIdleTimer() { mMouseIdleTimer.reset(); }
+ LLWindow* getWindow() { return mWindow; }
+
+ void addPopup(LLView*);
+ void removePopup(LLView*);
+ void clearPopups();
+
+ void reportBadKeystroke();
+
+ // Ensures view does not overlap mouse cursor, but is inside
+ // the view's parent rectangle. Used for tooltips, inspectors.
+ // Optionally override the view's default X/Y, which are relative to the
+ // view's parent.
+ void positionViewNearMouse(LLView* view, S32 spawn_x = S32_MAX, S32 spawn_y = S32_MAX);
+
+ // LLRender2D wrappers
+ 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 LLVector2& getScaleFactor();
static void setScaleFactor(const LLVector2& scale_factor);
- static void setLineWidth(F32 width) { LLRender2D::setLineWidth(width); }
- static LLPointer<LLUIImage> getUIImageByID(const LLUUID& image_id, S32 priority = 0)
- { return LLRender2D::getInstance()->getUIImageByID(image_id, priority); }
- static LLPointer<LLUIImage> getUIImage(const std::string& name, S32 priority = 0)
- { return LLRender2D::getInstance()->getUIImage(name, priority); }
-
- //
- // Data
- //
- settings_map_t mSettingGroups;
- LLUIAudioCallback mAudioCallback;
- LLUIAudioCallback mDeferredAudioCallback;
- LLWindow* mWindow;
- LLView* mRootView;
- LLHelp* mHelpImpl;
+ static void setLineWidth(F32 width) { LLRender2D::setLineWidth(width); }
+ static LLPointer<LLUIImage> getUIImageByID(const LLUUID& image_id, S32 priority = 0)
+ { return LLRender2D::getInstance()->getUIImageByID(image_id, priority); }
+ static LLPointer<LLUIImage> getUIImage(const std::string& name, S32 priority = 0)
+ { return LLRender2D::getInstance()->getUIImage(name, priority); }
+
+ //
+ // Data
+ //
+ settings_map_t mSettingGroups;
+ LLUIAudioCallback mAudioCallback;
+ LLUIAudioCallback mDeferredAudioCallback;
+ LLWindow* mWindow;
+ LLView* mRootView;
+ LLHelp* mHelpImpl;
private:
- std::vector<std::string> mXUIPaths;
- LLFrameTimer mMouseIdleTimer;
- add_popup_t mAddPopupFunc;
- remove_popup_t mRemovePopupFunc;
- clear_popups_t mClearPopupsFunc;
+ std::vector<std::string> mXUIPaths;
+ LLFrameTimer mMouseIdleTimer;
+ add_popup_t mAddPopupFunc;
+ remove_popup_t mRemovePopupFunc;
+ clear_popups_t mClearPopupsFunc;
};
@@ -346,118 +346,118 @@ private:
// useful parameter blocks
struct TimeIntervalParam : public LLInitParam::ChoiceBlock<TimeIntervalParam>
{
- Alternative<F32> seconds;
- Alternative<S32> frames;
- TimeIntervalParam()
- : seconds("seconds"),
- frames("frames")
- {}
+ Alternative<F32> seconds;
+ Alternative<S32> frames;
+ TimeIntervalParam()
+ : seconds("seconds"),
+ frames("frames")
+ {}
};
template <class T>
class LLUICachedControl : public LLCachedControl<T>
{
public:
- // This constructor will declare a control if it doesn't exist in the contol group
- LLUICachedControl(const std::string& name,
- const T& default_value,
- const std::string& comment = "Declared In Code")
- : LLCachedControl<T>(LLUI::getInstance()->getControlControlGroup(name), name, default_value, comment)
- {}
+ // This constructor will declare a control if it doesn't exist in the contol group
+ LLUICachedControl(const std::string& name,
+ const T& default_value,
+ const std::string& comment = "Declared In Code")
+ : LLCachedControl<T>(LLUI::getInstance()->getControlControlGroup(name), name, default_value, comment)
+ {}
};
namespace LLInitParam
{
- template<>
- class ParamValue<LLRect>
- : public CustomParamValue<LLRect>
- {
+ template<>
+ class ParamValue<LLRect>
+ : public CustomParamValue<LLRect>
+ {
typedef CustomParamValue<LLRect> super_t;
- public:
- Optional<S32> left,
- top,
- right,
- bottom,
- width,
- height;
-
- ParamValue(const LLRect& value);
-
- void updateValueFromBlock();
- void updateBlockFromValue(bool make_block_authoritative);
- };
-
- template<>
- class ParamValue<LLUIColor>
- : public CustomParamValue<LLUIColor>
- {
+ public:
+ Optional<S32> left,
+ top,
+ right,
+ bottom,
+ width,
+ height;
+
+ ParamValue(const LLRect& value);
+
+ void updateValueFromBlock();
+ void updateBlockFromValue(bool make_block_authoritative);
+ };
+
+ template<>
+ class ParamValue<LLUIColor>
+ : public CustomParamValue<LLUIColor>
+ {
typedef CustomParamValue<LLUIColor> super_t;
- public:
- Optional<F32> red,
- green,
- blue,
- alpha;
- Optional<std::string> control;
-
- ParamValue(const LLUIColor& color);
- void updateValueFromBlock();
- void updateBlockFromValue(bool make_block_authoritative);
- };
-
- template<>
- class ParamValue<const LLFontGL*>
- : public CustomParamValue<const LLFontGL* >
- {
+ public:
+ Optional<F32> red,
+ green,
+ blue,
+ alpha;
+ Optional<std::string> control;
+
+ ParamValue(const LLUIColor& color);
+ void updateValueFromBlock();
+ void updateBlockFromValue(bool make_block_authoritative);
+ };
+
+ template<>
+ class ParamValue<const LLFontGL*>
+ : public CustomParamValue<const LLFontGL* >
+ {
typedef CustomParamValue<const LLFontGL*> super_t;
- public:
- Optional<std::string> name,
- size,
- style;
-
- ParamValue(const LLFontGL* value);
- void updateValueFromBlock();
- void updateBlockFromValue(bool make_block_authoritative);
- };
-
- template<>
- struct TypeValues<LLFontGL::HAlign> : public TypeValuesHelper<LLFontGL::HAlign>
- {
- static void declareValues();
- };
-
- template<>
- struct TypeValues<LLFontGL::VAlign> : public TypeValuesHelper<LLFontGL::VAlign>
- {
- static void declareValues();
- };
-
- template<>
- struct TypeValues<LLFontGL::ShadowType> : public TypeValuesHelper<LLFontGL::ShadowType>
- {
- static void declareValues();
- };
-
- template<>
- struct ParamCompare<const LLFontGL*, false>
- {
- static bool equals(const LLFontGL* a, const LLFontGL* b);
- };
-
-
- template<>
- class ParamValue<LLCoordGL>
- : public CustomParamValue<LLCoordGL>
- {
- typedef CustomParamValue<LLCoordGL> super_t;
- public:
- Optional<S32> x,
- y;
-
- ParamValue(const LLCoordGL& val);
- void updateValueFromBlock();
- void updateBlockFromValue(bool make_block_authoritative);
- };
+ public:
+ Optional<std::string> name,
+ size,
+ style;
+
+ ParamValue(const LLFontGL* value);
+ void updateValueFromBlock();
+ void updateBlockFromValue(bool make_block_authoritative);
+ };
+
+ template<>
+ struct TypeValues<LLFontGL::HAlign> : public TypeValuesHelper<LLFontGL::HAlign>
+ {
+ static void declareValues();
+ };
+
+ template<>
+ struct TypeValues<LLFontGL::VAlign> : public TypeValuesHelper<LLFontGL::VAlign>
+ {
+ static void declareValues();
+ };
+
+ template<>
+ struct TypeValues<LLFontGL::ShadowType> : public TypeValuesHelper<LLFontGL::ShadowType>
+ {
+ static void declareValues();
+ };
+
+ template<>
+ struct ParamCompare<const LLFontGL*, false>
+ {
+ static bool equals(const LLFontGL* a, const LLFontGL* b);
+ };
+
+
+ template<>
+ class ParamValue<LLCoordGL>
+ : public CustomParamValue<LLCoordGL>
+ {
+ typedef CustomParamValue<LLCoordGL> super_t;
+ public:
+ Optional<S32> x,
+ y;
+
+ ParamValue(const LLCoordGL& val);
+ void updateValueFromBlock();
+ void updateBlockFromValue(bool make_block_authoritative);
+ };
}
#endif
diff --git a/indra/llui/lluicolor.cpp b/indra/llui/lluicolor.cpp
index f9bb80f8c5..71db556fad 100644
--- a/indra/llui/lluicolor.cpp
+++ b/indra/llui/lluicolor.cpp
@@ -1,25 +1,25 @@
-/**
+/**
* @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$
*/
@@ -29,59 +29,59 @@
#include "lluicolor.h"
LLUIColor::LLUIColor()
- :mColorPtr(NULL)
+ :mColorPtr(NULL)
{
}
LLUIColor::LLUIColor(const LLColor4& color)
-: mColor(color),
- mColorPtr(NULL)
+: mColor(color),
+ mColorPtr(NULL)
{
}
LLUIColor::LLUIColor(const LLUIColor* color)
-: mColorPtr(color)
+: mColorPtr(color)
{
}
void LLUIColor::set(const LLColor4& color)
{
- mColor = color;
- mColorPtr = NULL;
+ mColor = color;
+ mColorPtr = NULL;
}
void LLUIColor::set(const LLUIColor* color)
{
- mColorPtr = color;
+ mColorPtr = color;
}
const LLColor4& LLUIColor::get() const
{
- return (mColorPtr == NULL ? mColor : mColorPtr->get());
+ return (mColorPtr == NULL ? mColor : mColorPtr->get());
}
LLUIColor::operator const LLColor4& () const
{
- return get();
+ return get();
}
const LLColor4& LLUIColor::operator()() const
{
- return get();
+ return get();
}
bool LLUIColor::isReference() const
{
- return mColorPtr != NULL;
+ 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);
- }
+ // 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
index 97ebea854a..f688cc95af 100644
--- a/indra/llui/lluicolor.h
+++ b/indra/llui/lluicolor.h
@@ -1,25 +1,25 @@
-/**
+/**
* @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$
*/
@@ -31,41 +31,41 @@
namespace LLInitParam
{
- template<typename T, bool>
- struct ParamCompare;
+ template<typename T, bool>
+ struct ParamCompare;
}
class LLUIColor
{
public:
- LLUIColor();
- LLUIColor(const LLColor4& color);
- LLUIColor(const LLUIColor* color);
+ LLUIColor();
+ LLUIColor(const LLColor4& color);
+ LLUIColor(const LLUIColor* color);
- void set(const LLColor4& color);
- void set(const LLUIColor* color);
+ void set(const LLColor4& color);
+ void set(const LLUIColor* color);
- const LLColor4& get() const;
+ const LLColor4& get() const;
- operator const LLColor4& () const;
- const LLColor4& operator()() const;
+ operator const LLColor4& () const;
+ const LLColor4& operator()() const;
- bool isReference() const;
+ bool isReference() const;
private:
- friend struct LLInitParam::ParamCompare<LLUIColor, false>;
+ friend struct LLInitParam::ParamCompare<LLUIColor, false>;
- const LLUIColor* mColorPtr;
- LLColor4 mColor;
+ const LLUIColor* mColorPtr;
+ LLColor4 mColor;
};
namespace LLInitParam
{
- template<>
- struct ParamCompare<LLUIColor, false>
- {
- static bool equals(const LLUIColor& a, const LLUIColor& b);
- };
+ 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 f43bdf1fdc..54f8727fa5 100644
--- a/indra/llui/lluicolortable.cpp
+++ b/indra/llui/lluicolortable.cpp
@@ -1,25 +1,25 @@
-/**
+/**
* @file lluicolortable.cpp
* @brief brief LLUIColorTable 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$
*/
@@ -34,298 +34,298 @@
#include "lluictrlfactory.h"
LLUIColorTable::ColorParams::ColorParams()
-: value("value"),
- reference("reference")
+: value("value"),
+ reference("reference")
{
}
LLUIColorTable::ColorEntryParams::ColorEntryParams()
-: name("name"),
- color("")
+: name("name"),
+ color("")
{
}
LLUIColorTable::Params::Params()
-: color_entries("color")
+: color_entries("color")
{
}
void LLUIColorTable::insertFromParams(const Params& p, string_color_map_t& table)
{
- // this map will contain all color references after the following loop
- typedef std::map<std::string, std::string> string_string_map_t;
- string_string_map_t unresolved_refs;
-
- for(LLInitParam::ParamIterator<ColorEntryParams>::const_iterator it = p.color_entries.begin();
- it != p.color_entries.end();
- ++it)
- {
- ColorEntryParams color_entry = *it;
- if(color_entry.color.value.isChosen())
- {
- setColor(color_entry.name, color_entry.color.value, table);
- }
- else
- {
- unresolved_refs.insert(string_string_map_t::value_type(color_entry.name, color_entry.color.reference));
- }
- }
-
- // maintain an in order queue of visited references for better debugging of cycles
- typedef std::queue<std::string> string_queue_t;
- string_queue_t ref_chain;
-
- // maintain a map of the previously visited references in the reference chain for detecting cycles
- typedef std::map<std::string, string_string_map_t::iterator> string_color_ref_iter_map_t;
- string_color_ref_iter_map_t visited_refs;
-
- // loop through the unresolved color references until there are none left
- while(!unresolved_refs.empty())
- {
- // we haven't visited any references yet
- visited_refs.clear();
-
- string_string_map_t::iterator current = unresolved_refs.begin();
- string_string_map_t::iterator previous;
-
- while(true)
- {
- if(current != unresolved_refs.end())
- {
- // locate the current reference in the previously visited references...
- string_color_ref_iter_map_t::iterator visited = visited_refs.lower_bound(current->first);
- if(visited != visited_refs.end()
- && !(visited_refs.key_comp()(current->first, visited->first)))
- {
- // ...if we find the current reference in the previously visited references
- // we know that there is a cycle
- std::string ending_ref = current->first;
- std::string warning("The following colors form a cycle: ");
-
- // warn about the references in the chain and remove them from
- // the unresolved references map because they cannot be resolved
- for(string_color_ref_iter_map_t::iterator iter = visited_refs.begin();
- iter != visited_refs.end();
- ++iter)
- {
- if(!ref_chain.empty())
- {
- warning += ref_chain.front() + "->";
- ref_chain.pop();
- }
- unresolved_refs.erase(iter->second);
- }
-
- LL_WARNS() << warning + ending_ref << LL_ENDL;
-
- break;
- }
- else
- {
- // ...continue along the reference chain
- ref_chain.push(current->first);
- visited_refs.insert(visited, string_color_ref_iter_map_t::value_type(current->first, current));
- }
- }
- else
- {
- // since this reference does not refer to another reference it must refer to an
- // actual color, lets find it...
- string_color_map_t::iterator color_value = mLoadedColors.find(previous->second);
-
- if(color_value != mLoadedColors.end())
- {
- // ...we found the color, and we now add every reference in the reference chain
- // to the color map
- for(string_color_ref_iter_map_t::iterator iter = visited_refs.begin();
- iter != visited_refs.end();
- ++iter)
- {
- setColor(iter->first, color_value->second, mLoadedColors);
- unresolved_refs.erase(iter->second);
- }
-
- break;
- }
- else
- {
- // ... we did not find the color which imples that the current reference
- // references a non-existant color
- for(string_color_ref_iter_map_t::iterator iter = visited_refs.begin();
- iter != visited_refs.end();
- ++iter)
- {
- LL_WARNS() << iter->first << " references a non-existent color" << LL_ENDL;
- unresolved_refs.erase(iter->second);
- }
-
- break;
- }
- }
-
- // find the next color reference in the reference chain
- previous = current;
- current = unresolved_refs.find(current->second);
- }
- }
+ // this map will contain all color references after the following loop
+ typedef std::map<std::string, std::string> string_string_map_t;
+ string_string_map_t unresolved_refs;
+
+ for(LLInitParam::ParamIterator<ColorEntryParams>::const_iterator it = p.color_entries.begin();
+ it != p.color_entries.end();
+ ++it)
+ {
+ ColorEntryParams color_entry = *it;
+ if(color_entry.color.value.isChosen())
+ {
+ setColor(color_entry.name, color_entry.color.value, table);
+ }
+ else
+ {
+ unresolved_refs.insert(string_string_map_t::value_type(color_entry.name, color_entry.color.reference));
+ }
+ }
+
+ // maintain an in order queue of visited references for better debugging of cycles
+ typedef std::queue<std::string> string_queue_t;
+ string_queue_t ref_chain;
+
+ // maintain a map of the previously visited references in the reference chain for detecting cycles
+ typedef std::map<std::string, string_string_map_t::iterator> string_color_ref_iter_map_t;
+ string_color_ref_iter_map_t visited_refs;
+
+ // loop through the unresolved color references until there are none left
+ while(!unresolved_refs.empty())
+ {
+ // we haven't visited any references yet
+ visited_refs.clear();
+
+ string_string_map_t::iterator current = unresolved_refs.begin();
+ string_string_map_t::iterator previous;
+
+ while(true)
+ {
+ if(current != unresolved_refs.end())
+ {
+ // locate the current reference in the previously visited references...
+ string_color_ref_iter_map_t::iterator visited = visited_refs.lower_bound(current->first);
+ if(visited != visited_refs.end()
+ && !(visited_refs.key_comp()(current->first, visited->first)))
+ {
+ // ...if we find the current reference in the previously visited references
+ // we know that there is a cycle
+ std::string ending_ref = current->first;
+ std::string warning("The following colors form a cycle: ");
+
+ // warn about the references in the chain and remove them from
+ // the unresolved references map because they cannot be resolved
+ for(string_color_ref_iter_map_t::iterator iter = visited_refs.begin();
+ iter != visited_refs.end();
+ ++iter)
+ {
+ if(!ref_chain.empty())
+ {
+ warning += ref_chain.front() + "->";
+ ref_chain.pop();
+ }
+ unresolved_refs.erase(iter->second);
+ }
+
+ LL_WARNS() << warning + ending_ref << LL_ENDL;
+
+ break;
+ }
+ else
+ {
+ // ...continue along the reference chain
+ ref_chain.push(current->first);
+ visited_refs.insert(visited, string_color_ref_iter_map_t::value_type(current->first, current));
+ }
+ }
+ else
+ {
+ // since this reference does not refer to another reference it must refer to an
+ // actual color, lets find it...
+ string_color_map_t::iterator color_value = mLoadedColors.find(previous->second);
+
+ if(color_value != mLoadedColors.end())
+ {
+ // ...we found the color, and we now add every reference in the reference chain
+ // to the color map
+ for(string_color_ref_iter_map_t::iterator iter = visited_refs.begin();
+ iter != visited_refs.end();
+ ++iter)
+ {
+ setColor(iter->first, color_value->second, mLoadedColors);
+ unresolved_refs.erase(iter->second);
+ }
+
+ break;
+ }
+ else
+ {
+ // ... we did not find the color which imples that the current reference
+ // references a non-existant color
+ for(string_color_ref_iter_map_t::iterator iter = visited_refs.begin();
+ iter != visited_refs.end();
+ ++iter)
+ {
+ LL_WARNS() << iter->first << " references a non-existent color" << LL_ENDL;
+ unresolved_refs.erase(iter->second);
+ }
+
+ break;
+ }
+ }
+
+ // find the next color reference in the reference chain
+ previous = current;
+ current = unresolved_refs.find(current->second);
+ }
+ }
}
void LLUIColorTable::clear()
{
- clearTable(mLoadedColors);
- clearTable(mUserSetColors);
+ clearTable(mLoadedColors);
+ clearTable(mUserSetColors);
}
LLUIColor LLUIColorTable::getColor(const std::string& name, const LLColor4& default_color) const
{
- string_color_map_t::const_iterator iter = mUserSetColors.find(name);
-
- if(iter != mUserSetColors.end())
- {
- return LLUIColor(&iter->second);
- }
-
- iter = mLoadedColors.find(name);
-
- if(iter != mLoadedColors.end())
- {
- return LLUIColor(&iter->second);
- }
-
- return LLUIColor(default_color);
+ string_color_map_t::const_iterator iter = mUserSetColors.find(name);
+
+ if(iter != mUserSetColors.end())
+ {
+ return LLUIColor(&iter->second);
+ }
+
+ iter = mLoadedColors.find(name);
+
+ if(iter != mLoadedColors.end())
+ {
+ return LLUIColor(&iter->second);
+ }
+
+ return LLUIColor(default_color);
}
// update user color, loaded colors are parsed on initialization
void LLUIColorTable::setColor(const std::string& name, const LLColor4& color)
{
- setColor(name, color, mUserSetColors);
+ setColor(name, color, mUserSetColors);
}
bool LLUIColorTable::loadFromSettings()
{
- bool result = false;
+ bool result = false;
- // pass constraint=LLDir::ALL_SKINS because we want colors.xml from every
- // skin dir
- for (const std::string& colors_path :
- gDirUtilp->findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS))
- {
- result |= loadFromFilename(colors_path, mLoadedColors);
- }
+ // pass constraint=LLDir::ALL_SKINS because we want colors.xml from every
+ // skin dir
+ for (const std::string& colors_path :
+ gDirUtilp->findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS))
+ {
+ result |= loadFromFilename(colors_path, mLoadedColors);
+ }
- std::string user_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "colors.xml");
- loadFromFilename(user_filename, mUserSetColors);
+ std::string user_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "colors.xml");
+ loadFromFilename(user_filename, mUserSetColors);
- return result;
+ return result;
}
void LLUIColorTable::saveUserSettings() const
{
- Params params;
-
- for(string_color_map_t::const_iterator it = mUserSetColors.begin();
- it != mUserSetColors.end();
- ++it)
- {
- // Compare user color value with the default value, skip if equal
- string_color_map_t::const_iterator itd = mLoadedColors.find(it->first);
- if(itd != mLoadedColors.end() && itd->second == it->second)
- continue;
-
- ColorEntryParams color_entry;
- color_entry.name = it->first;
- color_entry.color.value = it->second;
-
- params.color_entries.add(color_entry);
- }
-
- LLXMLNodePtr output_node = new LLXMLNode("colors", false);
- LLXUIParser parser;
- parser.writeXUI(output_node, params);
-
- if(!output_node->isNull())
- {
- const std::string& filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "colors.xml");
- LLFILE *fp = LLFile::fopen(filename, "w");
-
- if(fp != NULL)
- {
- LLXMLNode::writeHeaderToFile(fp);
- output_node->writeToFile(fp);
-
- fclose(fp);
- }
- }
+ Params params;
+
+ for(string_color_map_t::const_iterator it = mUserSetColors.begin();
+ it != mUserSetColors.end();
+ ++it)
+ {
+ // Compare user color value with the default value, skip if equal
+ string_color_map_t::const_iterator itd = mLoadedColors.find(it->first);
+ if(itd != mLoadedColors.end() && itd->second == it->second)
+ continue;
+
+ ColorEntryParams color_entry;
+ color_entry.name = it->first;
+ color_entry.color.value = it->second;
+
+ params.color_entries.add(color_entry);
+ }
+
+ LLXMLNodePtr output_node = new LLXMLNode("colors", false);
+ LLXUIParser parser;
+ parser.writeXUI(output_node, params);
+
+ if(!output_node->isNull())
+ {
+ const std::string& filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "colors.xml");
+ LLFILE *fp = LLFile::fopen(filename, "w");
+
+ if(fp != NULL)
+ {
+ LLXMLNode::writeHeaderToFile(fp);
+ output_node->writeToFile(fp);
+
+ fclose(fp);
+ }
+ }
}
bool LLUIColorTable::colorExists(const std::string& color_name) const
{
- return ((mLoadedColors.find(color_name) != mLoadedColors.end())
- || (mUserSetColors.find(color_name) != mUserSetColors.end()));
+ return ((mLoadedColors.find(color_name) != mLoadedColors.end())
+ || (mUserSetColors.find(color_name) != mUserSetColors.end()));
}
void LLUIColorTable::clearTable(string_color_map_t& table)
{
- for(string_color_map_t::iterator it = table.begin();
- it != table.end();
- ++it)
- {
- it->second = LLColor4::magenta;
- }
+ for(string_color_map_t::iterator it = table.begin();
+ it != table.end();
+ ++it)
+ {
+ it->second = LLColor4::magenta;
+ }
}
// this method inserts a color into the table if it does not exist
// if the color already exists it changes the color
void LLUIColorTable::setColor(const std::string& name, const LLColor4& color, string_color_map_t& table)
{
- string_color_map_t::iterator it = table.lower_bound(name);
- if(it != table.end()
- && !(table.key_comp()(name, it->first)))
- {
- it->second = color;
- }
- else
- {
- table.insert(it, string_color_map_t::value_type(name, color));
- }
+ string_color_map_t::iterator it = table.lower_bound(name);
+ if(it != table.end()
+ && !(table.key_comp()(name, it->first)))
+ {
+ it->second = color;
+ }
+ else
+ {
+ table.insert(it, string_color_map_t::value_type(name, color));
+ }
}
bool LLUIColorTable::loadFromFilename(const std::string& filename, string_color_map_t& table)
{
- LLXMLNodePtr root;
-
- if(!LLXMLNode::parseFile(filename, root, NULL))
- {
- LL_WARNS() << "Unable to parse color file " << filename << LL_ENDL;
- return false;
- }
-
- if(!root->hasName("colors"))
- {
- LL_WARNS() << filename << " is not a valid color definition file" << LL_ENDL;
- return false;
- }
-
- Params params;
- LLXUIParser parser;
- parser.readXUI(root, params, filename);
-
- if(params.validateBlock())
- {
- insertFromParams(params, table);
- }
- else
- {
- LL_WARNS() << filename << " failed to load" << LL_ENDL;
- return false;
- }
-
- return true;
+ LLXMLNodePtr root;
+
+ if(!LLXMLNode::parseFile(filename, root, NULL))
+ {
+ LL_WARNS() << "Unable to parse color file " << filename << LL_ENDL;
+ return false;
+ }
+
+ if(!root->hasName("colors"))
+ {
+ LL_WARNS() << filename << " is not a valid color definition file" << LL_ENDL;
+ return false;
+ }
+
+ Params params;
+ LLXUIParser parser;
+ parser.readXUI(root, params, filename);
+
+ if(params.validateBlock())
+ {
+ insertFromParams(params, table);
+ }
+ else
+ {
+ LL_WARNS() << filename << " failed to load" << LL_ENDL;
+ return false;
+ }
+
+ return true;
}
void LLUIColorTable::insertFromParams(const Params& p)
{
- insertFromParams(p, mUserSetColors);
+ insertFromParams(p, mUserSetColors);
}
// EOF
diff --git a/indra/llui/lluicolortable.h b/indra/llui/lluicolortable.h
index 44472070cc..7232077cab 100644
--- a/indra/llui/lluicolortable.h
+++ b/indra/llui/lluicolortable.h
@@ -1,25 +1,25 @@
-/**
+/**
* @file lluicolortable.h
* @brief brief LLUIColorTable 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$
*/
@@ -38,67 +38,67 @@ class LLUIColor;
class LLUIColorTable : public LLSingleton<LLUIColorTable>
{
- LLSINGLETON_EMPTY_CTOR(LLUIColorTable);
- LOG_CLASS(LLUIColorTable);
+ LLSINGLETON_EMPTY_CTOR(LLUIColorTable);
+ LOG_CLASS(LLUIColorTable);
- // consider using sorted vector, can be much faster
- typedef std::map<std::string, LLUIColor> string_color_map_t;
+ // consider using sorted vector, can be much faster
+ typedef std::map<std::string, LLUIColor> string_color_map_t;
public:
- struct ColorParams : LLInitParam::ChoiceBlock<ColorParams>
- {
- Alternative<LLColor4> value;
- Alternative<std::string> reference;
+ struct ColorParams : LLInitParam::ChoiceBlock<ColorParams>
+ {
+ Alternative<LLColor4> value;
+ Alternative<std::string> reference;
- ColorParams();
- };
+ ColorParams();
+ };
- struct ColorEntryParams : LLInitParam::Block<ColorEntryParams>
- {
- Mandatory<std::string> name;
- Mandatory<ColorParams> color;
+ struct ColorEntryParams : LLInitParam::Block<ColorEntryParams>
+ {
+ Mandatory<std::string> name;
+ Mandatory<ColorParams> color;
- ColorEntryParams();
- };
+ ColorEntryParams();
+ };
- struct Params : LLInitParam::Block<Params>
- {
- Multiple<ColorEntryParams> color_entries;
+ struct Params : LLInitParam::Block<Params>
+ {
+ Multiple<ColorEntryParams> color_entries;
- Params();
- };
+ Params();
+ };
- // define colors by passing in a param block that can be generated via XUI file or manually
- void insertFromParams(const Params& p);
+ // define colors by passing in a param block that can be generated via XUI file or manually
+ void insertFromParams(const Params& p);
- // reset all colors to default magenta color
- void clear();
+ // reset all colors to default magenta color
+ void clear();
- // color lookup
- LLUIColor getColor(const std::string& name, const LLColor4& default_color = LLColor4::magenta) const;
+ // color lookup
+ LLUIColor getColor(const std::string& name, const LLColor4& default_color = LLColor4::magenta) const;
- // if the color is in the table, it's value is changed, otherwise it is added
- void setColor(const std::string& name, const LLColor4& color);
+ // if the color is in the table, it's value is changed, otherwise it is added
+ void setColor(const std::string& name, const LLColor4& color);
- // returns true if color_name exists in the table
- bool colorExists(const std::string& color_name) const;
+ // returns true if color_name exists in the table
+ bool colorExists(const std::string& color_name) const;
- // loads colors from settings files
- bool loadFromSettings();
+ // loads colors from settings files
+ bool loadFromSettings();
- // saves colors specified by the user to the users skin directory
- void saveUserSettings() const;
+ // saves colors specified by the user to the users skin directory
+ void saveUserSettings() const;
private:
- bool loadFromFilename(const std::string& filename, string_color_map_t& table);
+ bool loadFromFilename(const std::string& filename, string_color_map_t& table);
+
+ void insertFromParams(const Params& p, string_color_map_t& table);
- void insertFromParams(const Params& p, string_color_map_t& table);
-
- void clearTable(string_color_map_t& table);
- void setColor(const std::string& name, const LLColor4& color, string_color_map_t& table);
+ void clearTable(string_color_map_t& table);
+ void setColor(const std::string& name, const LLColor4& color, string_color_map_t& table);
- string_color_map_t mLoadedColors;
- string_color_map_t mUserSetColors;
+ string_color_map_t mLoadedColors;
+ string_color_map_t mUserSetColors;
};
#endif // LL_LLUICOLORTABLE_H
diff --git a/indra/llui/lluiconstants.h b/indra/llui/lluiconstants.h
index 1479e58c43..5fdfd37c6e 100644
--- a/indra/llui/lluiconstants.h
+++ b/indra/llui/lluiconstants.h
@@ -1,25 +1,25 @@
-/**
+/**
* @file lluiconstants.h
* @brief Compile-time configuration for UI
*
* $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$
*/
@@ -40,11 +40,11 @@ const S32 VPAD = 4;
const S32 HPAD = 4;
// Account History, how far to look into past
-const S32 SUMMARY_INTERVAL = 7; // one week
-const S32 SUMMARY_MAX = 8; //
-const S32 DETAILS_INTERVAL = 1; // one day
-const S32 DETAILS_MAX = 30; // one month
+const S32 SUMMARY_INTERVAL = 7; // one week
+const S32 SUMMARY_MAX = 8; //
+const S32 DETAILS_INTERVAL = 1; // one day
+const S32 DETAILS_MAX = 30; // one month
const S32 TRANSACTIONS_INTERVAL = 1;// one day
-const S32 TRANSACTIONS_MAX = 30; // one month
+const S32 TRANSACTIONS_MAX = 30; // one month
#endif
diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp
index 8d57a69c6e..4c2ad9d92b 100644
--- a/indra/llui/lluictrl.cpp
+++ b/indra/llui/lluictrl.cpp
@@ -1,1137 +1,1137 @@
-/**
- * @file lluictrl.cpp
- * @author James Cook, Richard Nelson, Tom Yedwab
- * @brief Abstract base class for UI controls
- *
- * $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"
-
-#define LLUICTRL_CPP
-#include "lluictrl.h"
-#include "llviewereventrecorder.h"
-#include "llfocusmgr.h"
-#include "llpanel.h"
-#include "lluictrlfactory.h"
-#include "lltabcontainer.h"
-#include "llaccordionctrltab.h"
-#include "lluiusage.h"
-
-static LLDefaultChildRegistry::Register<LLUICtrl> r("ui_ctrl");
-
-F32 LLUICtrl::sActiveControlTransparency = 1.0f;
-F32 LLUICtrl::sInactiveControlTransparency = 1.0f;
-
-// Compiler optimization, generate extern template
-template class LLUICtrl* LLView::getChild<class LLUICtrl>(
- const std::string& name, bool recurse) const;
-
-LLUICtrl::CallbackParam::CallbackParam()
-: name("name"),
- function_name("function"),
- parameter("parameter"),
- control_name("control") // Shortcut to control -> "control_name" for backwards compatability
-{
- addSynonym(parameter, "userdata");
-}
-
-LLUICtrl::EnableControls::EnableControls()
-: enabled("enabled_control"),
- disabled("disabled_control")
-{}
-
-LLUICtrl::ControlVisibility::ControlVisibility()
-: visible("visibility_control"),
- invisible("invisibility_control")
-{
- addSynonym(visible, "visiblity_control");
- addSynonym(invisible, "invisiblity_control");
-}
-
-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"),
- commit_callback("commit_callback"),
- validate_callback("validate_callback"),
- mouseenter_callback("mouseenter_callback"),
- mouseleave_callback("mouseleave_callback"),
- control_name("control_name"),
- font("font", LLFontGL::getFontSansSerif()),
- font_halign("halign"),
- font_valign("valign"),
- length("length"), // ignore LLXMLNode cruft
- type("type") // ignore LLXMLNode cruft
-{
- addSynonym(initial_value, "initial_value");
-}
-
-// NOTE: the LLFocusableElement implementation has been moved from here to llfocusmgr.cpp.
-
-//static
-const LLUICtrl::Params& LLUICtrl::getDefaultParams()
-{
- return LLUICtrlFactory::getDefaultParams<LLUICtrl>();
-}
-
-
-LLUICtrl::LLUICtrl(const LLUICtrl::Params& p, const LLViewModelPtr& viewmodel)
-: LLView(p),
- mIsChrome(false),
- mRequestsFront(p.requests_front),
- mTabStop(false),
- mTentative(false),
- mViewModel(viewmodel),
- mControlVariable(NULL),
- mEnabledControlVariable(NULL),
- mDisabledControlVariable(NULL),
- mMakeVisibleControlVariable(NULL),
- mMakeInvisibleControlVariable(NULL),
- mCommitSignal(NULL),
- mValidateSignal(NULL),
- mMouseEnterSignal(NULL),
- mMouseLeaveSignal(NULL),
- mMouseDownSignal(NULL),
- mMouseUpSignal(NULL),
- mRightMouseDownSignal(NULL),
- mRightMouseUpSignal(NULL),
- mDoubleClickSignal(NULL),
- mTransparencyType(TT_DEFAULT)
-{
-}
-
-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())
- {
- if (p.enabled_controls.enabled.isChosen())
- {
- LLControlVariable* control = findControl(p.enabled_controls.enabled);
- if (control)
- {
- setEnabledControlVariable(control);
- }
- else
- {
- LL_WARNS() << "Failed to assign 'enabled' control variable to " << getName()
- << ": control " << p.enabled_controls.enabled()
- << " does not exist." << LL_ENDL;
- }
- }
- else if(p.enabled_controls.disabled.isChosen())
- {
- LLControlVariable* control = findControl(p.enabled_controls.disabled);
- if (control)
- {
- setDisabledControlVariable(control);
- }
- else
- {
- LL_WARNS() << "Failed to assign 'disabled' control variable to " << getName()
- << ": control " << p.enabled_controls.disabled()
- << " does not exist." << LL_ENDL;
- }
- }
- }
- if(p.controls_visibility.isProvided())
- {
- if (p.controls_visibility.visible.isChosen())
- {
- LLControlVariable* control = findControl(p.controls_visibility.visible);
- if (control)
- {
- setMakeVisibleControlVariable(control);
- }
- else
- {
- LL_WARNS() << "Failed to assign visibility control variable to " << getName()
- << ": control " << p.controls_visibility.visible()
- << " does not exist." << LL_ENDL;
- }
- }
- else if (p.controls_visibility.invisible.isChosen())
- {
- LLControlVariable* control = findControl(p.controls_visibility.invisible);
- if (control)
- {
- setMakeInvisibleControlVariable(control);
- }
- else
- {
- LL_WARNS() << "Failed to assign invisibility control variable to " << getName()
- << ": control " << p.controls_visibility.invisible()
- << " does not exist." << LL_ENDL;
- }
- }
- }
-
- setTabStop(p.tab_stop);
-
- if (p.initial_value.isProvided()
- && !p.control_name.isProvided())
- {
- setValue(p.initial_value);
- }
-
- if (p.commit_callback.isProvided())
- {
- setCommitCallback(initCommitCallback(p.commit_callback));
- }
-
- if (p.validate_callback.isProvided())
- {
- setValidateCallback(initEnableCallback(p.validate_callback));
- }
-
- if (p.init_callback.isProvided())
- {
- if (p.init_callback.function.isProvided())
- {
- p.init_callback.function()(this, p.init_callback.parameter);
- }
- else
- {
- commit_callback_t* initfunc = (CommitCallbackRegistry::getValue(p.init_callback.function_name));
- if (initfunc)
- {
- (*initfunc)(this, p.init_callback.parameter);
- }
- }
- }
-
- if(p.mouseenter_callback.isProvided())
- {
- setMouseEnterCallback(initCommitCallback(p.mouseenter_callback));
- }
-
- if(p.mouseleave_callback.isProvided())
- {
- setMouseLeaveCallback(initCommitCallback(p.mouseleave_callback));
- }
-}
-
-
-LLUICtrl::~LLUICtrl()
-{
- gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit()
-
- if( gFocusMgr.getTopCtrl() == this )
- {
- LL_WARNS() << "UI Control holding top ctrl deleted: " << getName() << ". Top view removed." << LL_ENDL;
- gFocusMgr.removeTopCtrlWithoutCallback( this );
- }
-
- delete mCommitSignal;
- delete mValidateSignal;
- delete mMouseEnterSignal;
- delete mMouseLeaveSignal;
- delete mMouseDownSignal;
- delete mMouseUpSignal;
- delete mRightMouseDownSignal;
- delete mRightMouseUpSignal;
- delete mDoubleClickSignal;
-}
-
-void default_commit_handler(LLUICtrl* ctrl, const LLSD& param)
-{}
-
-bool default_enable_handler(LLUICtrl* ctrl, const LLSD& param)
-{
- return true;
-}
-
-
-LLUICtrl::commit_signal_t::slot_type LLUICtrl::initCommitCallback(const CommitCallbackParam& cb)
-{
- if (cb.function.isProvided())
- {
- if (cb.parameter.isProvided())
- return boost::bind(cb.function(), _1, cb.parameter);
- else
- return cb.function();
- }
- else
- {
- std::string function_name = cb.function_name;
- setFunctionName(function_name);
- commit_callback_t* func = (CommitCallbackRegistry::getValue(function_name));
- if (func)
- {
- if (cb.parameter.isProvided())
- return boost::bind((*func), _1, cb.parameter);
- else
- return commit_signal_t::slot_type(*func);
- }
- else if (!function_name.empty())
- {
- LL_WARNS() << "No callback found for: '" << function_name << "' in control: " << getName() << LL_ENDL;
- }
- }
- return default_commit_handler;
-}
-
-LLUICtrl::enable_signal_t::slot_type LLUICtrl::initEnableCallback(const EnableCallbackParam& cb)
-{
- // Set the callback function
- if (cb.function.isProvided())
- {
- if (cb.parameter.isProvided())
- return boost::bind(cb.function(), this, cb.parameter);
- else
- return cb.function();
- }
- else
- {
- enable_callback_t* func = (EnableCallbackRegistry::getValue(cb.function_name));
- if (func)
- {
- if (cb.parameter.isProvided())
- return boost::bind((*func), this, cb.parameter);
- else
- return enable_signal_t::slot_type(*func);
- }
- }
- return default_enable_handler;
-}
-
-// virtual
-void LLUICtrl::onMouseEnter(S32 x, S32 y, MASK mask)
-{
- if (mMouseEnterSignal)
- {
- (*mMouseEnterSignal)(this, getValue());
- }
-}
-
-// virtual
-void LLUICtrl::onMouseLeave(S32 x, S32 y, MASK mask)
-{
- if(mMouseLeaveSignal)
- {
- (*mMouseLeaveSignal)(this, getValue());
- }
-}
-
-//virtual
-bool LLUICtrl::handleMouseDown(S32 x, S32 y, MASK mask)
-{
-
- 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;
-}
-
-//virtual
-bool LLUICtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- bool handled = LLView::handleRightMouseDown(x,y,mask);
- if (mRightMouseDownSignal)
- {
- (*mRightMouseDownSignal)(this,x,y,mask);
- }
- return handled;
-}
-
-//virtual
-bool LLUICtrl::handleRightMouseUp(S32 x, S32 y, MASK mask)
-{
- bool handled = LLView::handleRightMouseUp(x,y,mask);
- if(mRightMouseUpSignal)
- {
- (*mRightMouseUpSignal)(this,x,y,mask);
- }
- return handled;
-}
-
-bool LLUICtrl::handleDoubleClick(S32 x, S32 y, MASK mask)
-{
- bool handled = LLView::handleDoubleClick(x, y, mask);
- if (mDoubleClickSignal)
- {
- (*mDoubleClickSignal)(this, x, y, mask);
- }
- return handled;
-}
-
-// can't tab to children of a non-tab-stop widget
-bool LLUICtrl::canFocusChildren() const
-{
- return hasTabStop();
-}
-
-
-void LLUICtrl::onCommit()
-{
- if (mCommitSignal)
- {
- if (!mFunctionName.empty())
- {
- LL_DEBUGS("UIUsage") << "calling commit function " << mFunctionName << LL_ENDL;
- LLUIUsage::instance().logCommand(mFunctionName);
- LLUIUsage::instance().logControl(getPathname());
- }
- else
- {
- //LL_DEBUGS("UIUsage") << "calling commit function " << "UNKNOWN" << LL_ENDL;
- }
- (*mCommitSignal)(this, getValue());
- }
-}
-
-//virtual
-bool LLUICtrl::isCtrl() const
-{
- return true;
-}
-
-//virtual
-void LLUICtrl::setValue(const LLSD& value)
-{
- mViewModel->setValue(value);
-}
-
-//virtual
-LLSD LLUICtrl::getValue() const
-{
- return mViewModel->getValue();
-}
-
-/// When two widgets are displaying the same data (e.g. during a skin
-/// change), share their ViewModel.
-void LLUICtrl::shareViewModelFrom(const LLUICtrl& other)
-{
- // Because mViewModel is an LLViewModelPtr, this assignment will quietly
- // dispose of the previous LLViewModel -- unless it's already shared by
- // somebody else.
- mViewModel = other.mViewModel;
-}
-
-//virtual
-LLViewModel* LLUICtrl::getViewModel() const
-{
- return mViewModel;
-}
-
-//virtual
-bool LLUICtrl::postBuild()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
- //
- // 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)
- {
- mControlVariable->set(value);
- return true;
- }
- return false;
-}
-
-void LLUICtrl::setControlVariable(LLControlVariable* control)
-{
- if (mControlVariable)
- {
- //RN: this will happen in practice, should we try to avoid it?
- //LL_WARNS() << "setControlName called twice on same control!" << LL_ENDL;
- mControlConnection.disconnect(); // disconnect current signal
- mControlVariable = NULL;
- }
-
- if (control)
- {
- mControlVariable = control;
- mControlConnection = mControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("value")));
- setValue(mControlVariable->getValue());
- }
-}
-
-void LLUICtrl::removeControlVariable()
-{
- if (mControlVariable)
- {
- mControlConnection.disconnect();
- mControlVariable = NULL;
- }
-}
-
-//virtual
-void LLUICtrl::setControlName(const std::string& control_name, LLView *context)
-{
- if (context == NULL)
- {
- context = this;
- }
-
- // Register new listener
- if (!control_name.empty())
- {
- LLControlVariable* control = context->findControl(control_name);
- if (!control)
- {
- LL_WARNS() << "Failed to assign control variable to " << getName()
- << ": control "<< control_name << " does not exist." << LL_ENDL;
- }
- setControlVariable(control);
- }
-}
-
-void LLUICtrl::setEnabledControlVariable(LLControlVariable* control)
-{
- if (mEnabledControlVariable)
- {
- mEnabledControlConnection.disconnect(); // disconnect current signal
- mEnabledControlVariable = NULL;
- }
- if (control)
- {
- mEnabledControlVariable = control;
- mEnabledControlConnection = mEnabledControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("enabled")));
- setEnabled(mEnabledControlVariable->getValue().asBoolean());
- }
-}
-
-void LLUICtrl::setDisabledControlVariable(LLControlVariable* control)
-{
- if (mDisabledControlVariable)
- {
- mDisabledControlConnection.disconnect(); // disconnect current signal
- mDisabledControlVariable = NULL;
- }
- if (control)
- {
- mDisabledControlVariable = control;
- mDisabledControlConnection = mDisabledControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("disabled")));
- setEnabled(!(mDisabledControlVariable->getValue().asBoolean()));
- }
-}
-
-void LLUICtrl::setMakeVisibleControlVariable(LLControlVariable* control)
-{
- if (mMakeVisibleControlVariable)
- {
- mMakeVisibleControlConnection.disconnect(); // disconnect current signal
- mMakeVisibleControlVariable = NULL;
- }
- if (control)
- {
- mMakeVisibleControlVariable = control;
- mMakeVisibleControlConnection = mMakeVisibleControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("visible")));
- setVisible(mMakeVisibleControlVariable->getValue().asBoolean());
- }
-}
-
-void LLUICtrl::setMakeInvisibleControlVariable(LLControlVariable* control)
-{
- if (mMakeInvisibleControlVariable)
- {
- mMakeInvisibleControlConnection.disconnect(); // disconnect current signal
- mMakeInvisibleControlVariable = NULL;
- }
- if (control)
- {
- mMakeInvisibleControlVariable = control;
- mMakeInvisibleControlConnection = mMakeInvisibleControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("invisible")));
- setVisible(!(mMakeInvisibleControlVariable->getValue().asBoolean()));
- }
-}
-
-void LLUICtrl::setFunctionName(const std::string& function_name)
-{
- mFunctionName = function_name;
-}
-
-// static
-bool LLUICtrl::controlListener(const LLSD& newvalue, LLHandle<LLUICtrl> handle, std::string type)
-{
- LLUICtrl* ctrl = handle.get();
- if (ctrl)
- {
- if (type == "value")
- {
- ctrl->setValue(newvalue);
- return true;
- }
- else if (type == "enabled")
- {
- ctrl->setEnabled(newvalue.asBoolean());
- return true;
- }
- else if(type =="disabled")
- {
- ctrl->setEnabled(!newvalue.asBoolean());
- return true;
- }
- else if (type == "visible")
- {
- ctrl->setVisible(newvalue.asBoolean());
- return true;
- }
- else if (type == "invisible")
- {
- ctrl->setVisible(!newvalue.asBoolean());
- return true;
- }
- }
- return false;
-}
-
-// virtual
-bool LLUICtrl::setTextArg( const std::string& key, const LLStringExplicit& text )
-{
- return false;
-}
-
-// virtual
-bool LLUICtrl::setLabelArg( const std::string& key, const LLStringExplicit& text )
-{
- return false;
-}
-
-// virtual
-LLCtrlSelectionInterface* LLUICtrl::getSelectionInterface()
-{
- return NULL;
-}
-
-// virtual
-LLCtrlListInterface* LLUICtrl::getListInterface()
-{
- return NULL;
-}
-
-// virtual
-LLCtrlScrollInterface* LLUICtrl::getScrollInterface()
-{
- return NULL;
-}
-
-bool LLUICtrl::hasFocus() const
-{
- return (gFocusMgr.childHasKeyboardFocus(this));
-}
-
-void LLUICtrl::setFocus(bool b)
-{
- // focus NEVER goes to ui ctrls that are disabled!
- if (!getEnabled())
- {
- return;
- }
- if( b )
- {
- if (!hasFocus())
- {
- gFocusMgr.setKeyboardFocus( this );
- }
- }
- else
- {
- if( gFocusMgr.childHasKeyboardFocus(this))
- {
- gFocusMgr.setKeyboardFocus( NULL );
- }
- }
-}
-
-// virtual
-void LLUICtrl::setTabStop( bool b )
-{
- mTabStop = b;
-}
-
-// virtual
-bool LLUICtrl::hasTabStop() const
-{
- return mTabStop;
-}
-
-// virtual
-bool LLUICtrl::acceptsTextInput() const
-{
- return false;
-}
-
-//virtual
-bool LLUICtrl::isDirty() const
-{
- return mViewModel->isDirty();
-};
-
-//virtual
-void LLUICtrl::resetDirty()
-{
- mViewModel->resetDirty();
-}
-
-// virtual
-void LLUICtrl::onTabInto()
-{
- onUpdateScrollToChild(this);
-}
-
-// virtual
-void LLUICtrl::clear()
-{
-}
-
-// virtual
-void LLUICtrl::setIsChrome(bool is_chrome)
-{
- mIsChrome = is_chrome;
-}
-
-// virtual
-bool LLUICtrl::getIsChrome() const
-{
- if (mIsChrome)
- return true;
-
- LLView* parent_ctrl = getParent();
- while (parent_ctrl)
- {
- if (parent_ctrl->isCtrl())
- return ((LLUICtrl*)parent_ctrl)->getIsChrome();
-
- parent_ctrl = parent_ctrl->getParent();
- }
-
- return false;
-}
-
-
-bool LLUICtrl::focusFirstItem(bool prefer_text_fields, bool focus_flash)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
- // try to select default tab group child
- LLViewQuery query = getTabOrderQuery();
- child_list_t result = query(this);
- if(result.size() > 0)
- {
- LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.back());
- if(!ctrl->hasFocus())
- {
- ctrl->setFocus(true);
- ctrl->onTabInto();
- if(focus_flash)
- {
- gFocusMgr.triggerFocusFlash();
- }
- }
- return true;
- }
- // search for text field first
- if(prefer_text_fields)
- {
- LLViewQuery 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())
- {
- ctrl->setFocus(true);
- ctrl->onTabInto();
- if(focus_flash)
- {
- gFocusMgr.triggerFocusFlash();
- }
- }
- return true;
- }
- }
- // no text field found, or we don't care about text fields
- result = getTabOrderQuery().run(this);
- if(result.size() > 0)
- {
- LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.back());
- if(!ctrl->hasFocus())
- {
- ctrl->setFocus(true);
- ctrl->onTabInto();
- if(focus_flash)
- {
- gFocusMgr.triggerFocusFlash();
- }
- }
- return true;
- }
- return false;
-}
-
-
-bool LLUICtrl::focusNextItem(bool text_fields_only)
-{
- // this assumes that this method is called on the focus root.
- LLViewQuery query = getTabOrderQuery();
- static LLUICachedControl<bool> tab_to_text_fields_only ("TabToTextFieldsOnly", false);
- if(text_fields_only || tab_to_text_fields_only)
- {
- query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance());
- }
- child_list_t result = query(this);
- return focusNext(result);
-}
-
-bool LLUICtrl::focusPrevItem(bool text_fields_only)
-{
- // this assumes that this method is called on the focus root.
- LLViewQuery query = getTabOrderQuery();
- static LLUICachedControl<bool> tab_to_text_fields_only ("TabToTextFieldsOnly", false);
- if(text_fields_only || tab_to_text_fields_only)
- {
- query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance());
- }
- child_list_t result = query(this);
- return focusPrev(result);
-}
-
-LLUICtrl* LLUICtrl::findRootMostFocusRoot()
-{
- LLUICtrl* focus_root = NULL;
- LLUICtrl* next_view = this;
- while(next_view && next_view->hasTabStop())
- {
- if (next_view->isFocusRoot())
- {
- focus_root = next_view;
- }
- next_view = next_view->getParentUICtrl();
- }
-
- return focus_root;
-}
-
-// Skip over any parents that are not LLUICtrl's
-// Used in focus logic since only LLUICtrl elements can have focus
-LLUICtrl* LLUICtrl::getParentUICtrl() const
-{
- LLView* parent = getParent();
- while (parent)
- {
- if (parent->isCtrl())
- {
- return (LLUICtrl*)(parent);
- }
- else
- {
- parent = parent->getParent();
- }
- }
- return NULL;
-}
-
-bool LLUICtrl::findHelpTopic(std::string& help_topic_out)
-{
- LLUICtrl* ctrl = this;
-
- // search back through the control's parents for a panel
- // or tab with a help_topic string defined
- while (ctrl)
- {
- LLPanel *panel = dynamic_cast<LLPanel *>(ctrl);
-
- if (panel)
- {
-
- LLView *child;
- LLPanel *subpanel = NULL;
-
- // does the panel have a sub-panel with a help topic?
- 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();
- return true; // success (subpanel)
- }
-
- // does the panel have an active tab with a help topic?
- LLPanel *tab_panel = NULL;
-
- 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())
- {
- tab_panel = curTabPanel;
- break;
- }
- }
-
- if (tab_panel)
- {
- help_topic_out = tab_panel->getHelpTopic();
- return true; // success (tab)
- }
-
- // otherwise, does the panel have a help topic itself?
- if (!panel->getHelpTopic().empty())
- {
- help_topic_out = panel->getHelpTopic();
- return true; // success (panel)
- }
- }
-
- ctrl = ctrl->getParentUICtrl();
- }
-
- return false; // no help topic found
-}
-
-// *TODO: Deprecate; for backwards compatability only:
-boost::signals2::connection LLUICtrl::setCommitCallback( boost::function<void (LLUICtrl*,void*)> cb, void* data)
-{
- return setCommitCallback( boost::bind(cb, _1, data));
-}
-boost::signals2::connection LLUICtrl::setValidateBeforeCommit( boost::function<bool (const LLSD& data)> cb )
-{
- if (!mValidateSignal) mValidateSignal = new enable_signal_t();
-
- return mValidateSignal->connect(boost::bind(cb, _2));
-}
-
-// virtual
-void LLUICtrl::setTentative(bool b)
-{
- mTentative = b;
-}
-
-// virtual
-bool LLUICtrl::getTentative() const
-{
- return mTentative;
-}
-
-// virtual
-void LLUICtrl::setColor(const LLColor4& color)
-{ }
-
-F32 LLUICtrl::getCurrentTransparency()
-{
- F32 alpha = 0;
-
- switch(mTransparencyType)
- {
- case TT_DEFAULT:
- alpha = getDrawContext().mAlpha;
- break;
-
- case TT_ACTIVE:
- alpha = sActiveControlTransparency;
- break;
-
- case TT_INACTIVE:
- alpha = sInactiveControlTransparency;
- break;
-
- case TT_FADING:
- alpha = sInactiveControlTransparency / 2;
- break;
- }
-
- return alpha;
-}
-
-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();
-
- return mCommitSignal->connect(cb);
-}
-
-boost::signals2::connection LLUICtrl::setValidateCallback( const enable_signal_t::slot_type& cb )
-{
- if (!mValidateSignal) mValidateSignal = new enable_signal_t();
-
- return mValidateSignal->connect(cb);
-}
-
-boost::signals2::connection LLUICtrl::setMouseEnterCallback( const commit_signal_t::slot_type& cb )
-{
- if (!mMouseEnterSignal) mMouseEnterSignal = new commit_signal_t();
-
- return mMouseEnterSignal->connect(cb);
-}
-
-boost::signals2::connection LLUICtrl::setMouseLeaveCallback( const commit_signal_t::slot_type& cb )
-{
- if (!mMouseLeaveSignal) mMouseLeaveSignal = new commit_signal_t();
-
- return mMouseLeaveSignal->connect(cb);
-}
-
-boost::signals2::connection LLUICtrl::setMouseDownCallback( const mouse_signal_t::slot_type& cb )
-{
- if (!mMouseDownSignal) mMouseDownSignal = new mouse_signal_t();
-
- return mMouseDownSignal->connect(cb);
-}
-
-boost::signals2::connection LLUICtrl::setMouseUpCallback( const mouse_signal_t::slot_type& cb )
-{
- if (!mMouseUpSignal) mMouseUpSignal = new mouse_signal_t();
-
- return mMouseUpSignal->connect(cb);
-}
-
-boost::signals2::connection LLUICtrl::setRightMouseDownCallback( const mouse_signal_t::slot_type& cb )
-{
- if (!mRightMouseDownSignal) mRightMouseDownSignal = new mouse_signal_t();
-
- return mRightMouseDownSignal->connect(cb);
-}
-
-boost::signals2::connection LLUICtrl::setRightMouseUpCallback( const mouse_signal_t::slot_type& cb )
-{
- if (!mRightMouseUpSignal) mRightMouseUpSignal = new mouse_signal_t();
-
- return mRightMouseUpSignal->connect(cb);
-}
-
-boost::signals2::connection LLUICtrl::setDoubleClickCallback( const mouse_signal_t::slot_type& cb )
-{
- if (!mDoubleClickSignal) mDoubleClickSignal = new mouse_signal_t();
-
- return mDoubleClickSignal->connect(cb);
-}
-
-void LLUICtrl::addInfo(LLSD & info)
-{
- LLView::addInfo(info);
- info["value"] = getValue();
-}
+/**
+ * @file lluictrl.cpp
+ * @author James Cook, Richard Nelson, Tom Yedwab
+ * @brief Abstract base class for UI controls
+ *
+ * $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"
+
+#define LLUICTRL_CPP
+#include "lluictrl.h"
+#include "llviewereventrecorder.h"
+#include "llfocusmgr.h"
+#include "llpanel.h"
+#include "lluictrlfactory.h"
+#include "lltabcontainer.h"
+#include "llaccordionctrltab.h"
+#include "lluiusage.h"
+
+static LLDefaultChildRegistry::Register<LLUICtrl> r("ui_ctrl");
+
+F32 LLUICtrl::sActiveControlTransparency = 1.0f;
+F32 LLUICtrl::sInactiveControlTransparency = 1.0f;
+
+// Compiler optimization, generate extern template
+template class LLUICtrl* LLView::getChild<class LLUICtrl>(
+ const std::string& name, bool recurse) const;
+
+LLUICtrl::CallbackParam::CallbackParam()
+: name("name"),
+ function_name("function"),
+ parameter("parameter"),
+ control_name("control") // Shortcut to control -> "control_name" for backwards compatability
+{
+ addSynonym(parameter, "userdata");
+}
+
+LLUICtrl::EnableControls::EnableControls()
+: enabled("enabled_control"),
+ disabled("disabled_control")
+{}
+
+LLUICtrl::ControlVisibility::ControlVisibility()
+: visible("visibility_control"),
+ invisible("invisibility_control")
+{
+ addSynonym(visible, "visiblity_control");
+ addSynonym(invisible, "invisiblity_control");
+}
+
+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"),
+ commit_callback("commit_callback"),
+ validate_callback("validate_callback"),
+ mouseenter_callback("mouseenter_callback"),
+ mouseleave_callback("mouseleave_callback"),
+ control_name("control_name"),
+ font("font", LLFontGL::getFontEmojiMedium()),
+ font_halign("halign"),
+ font_valign("valign"),
+ length("length"), // ignore LLXMLNode cruft
+ type("type") // ignore LLXMLNode cruft
+{
+ addSynonym(initial_value, "initial_value");
+}
+
+// NOTE: the LLFocusableElement implementation has been moved from here to llfocusmgr.cpp.
+
+//static
+const LLUICtrl::Params& LLUICtrl::getDefaultParams()
+{
+ return LLUICtrlFactory::getDefaultParams<LLUICtrl>();
+}
+
+
+LLUICtrl::LLUICtrl(const LLUICtrl::Params& p, const LLViewModelPtr& viewmodel)
+: LLView(p),
+ mIsChrome(false),
+ mRequestsFront(p.requests_front),
+ mTabStop(false),
+ mTentative(false),
+ mViewModel(viewmodel),
+ mControlVariable(NULL),
+ mEnabledControlVariable(NULL),
+ mDisabledControlVariable(NULL),
+ mMakeVisibleControlVariable(NULL),
+ mMakeInvisibleControlVariable(NULL),
+ mCommitSignal(NULL),
+ mValidateSignal(NULL),
+ mMouseEnterSignal(NULL),
+ mMouseLeaveSignal(NULL),
+ mMouseDownSignal(NULL),
+ mMouseUpSignal(NULL),
+ mRightMouseDownSignal(NULL),
+ mRightMouseUpSignal(NULL),
+ mDoubleClickSignal(NULL),
+ mTransparencyType(TT_DEFAULT)
+{
+}
+
+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())
+ {
+ if (p.enabled_controls.enabled.isChosen())
+ {
+ LLControlVariable* control = findControl(p.enabled_controls.enabled);
+ if (control)
+ {
+ setEnabledControlVariable(control);
+ }
+ else
+ {
+ LL_WARNS() << "Failed to assign 'enabled' control variable to " << getName()
+ << ": control " << p.enabled_controls.enabled()
+ << " does not exist." << LL_ENDL;
+ }
+ }
+ else if(p.enabled_controls.disabled.isChosen())
+ {
+ LLControlVariable* control = findControl(p.enabled_controls.disabled);
+ if (control)
+ {
+ setDisabledControlVariable(control);
+ }
+ else
+ {
+ LL_WARNS() << "Failed to assign 'disabled' control variable to " << getName()
+ << ": control " << p.enabled_controls.disabled()
+ << " does not exist." << LL_ENDL;
+ }
+ }
+ }
+ if(p.controls_visibility.isProvided())
+ {
+ if (p.controls_visibility.visible.isChosen())
+ {
+ LLControlVariable* control = findControl(p.controls_visibility.visible);
+ if (control)
+ {
+ setMakeVisibleControlVariable(control);
+ }
+ else
+ {
+ LL_WARNS() << "Failed to assign visibility control variable to " << getName()
+ << ": control " << p.controls_visibility.visible()
+ << " does not exist." << LL_ENDL;
+ }
+ }
+ else if (p.controls_visibility.invisible.isChosen())
+ {
+ LLControlVariable* control = findControl(p.controls_visibility.invisible);
+ if (control)
+ {
+ setMakeInvisibleControlVariable(control);
+ }
+ else
+ {
+ LL_WARNS() << "Failed to assign invisibility control variable to " << getName()
+ << ": control " << p.controls_visibility.invisible()
+ << " does not exist." << LL_ENDL;
+ }
+ }
+ }
+
+ setTabStop(p.tab_stop);
+
+ if (p.initial_value.isProvided()
+ && !p.control_name.isProvided())
+ {
+ setValue(p.initial_value);
+ }
+
+ if (p.commit_callback.isProvided())
+ {
+ setCommitCallback(initCommitCallback(p.commit_callback));
+ }
+
+ if (p.validate_callback.isProvided())
+ {
+ setValidateCallback(initEnableCallback(p.validate_callback));
+ }
+
+ if (p.init_callback.isProvided())
+ {
+ if (p.init_callback.function.isProvided())
+ {
+ p.init_callback.function()(this, p.init_callback.parameter);
+ }
+ else
+ {
+ commit_callback_t* initfunc = (CommitCallbackRegistry::getValue(p.init_callback.function_name));
+ if (initfunc)
+ {
+ (*initfunc)(this, p.init_callback.parameter);
+ }
+ }
+ }
+
+ if(p.mouseenter_callback.isProvided())
+ {
+ setMouseEnterCallback(initCommitCallback(p.mouseenter_callback));
+ }
+
+ if(p.mouseleave_callback.isProvided())
+ {
+ setMouseLeaveCallback(initCommitCallback(p.mouseleave_callback));
+ }
+}
+
+
+LLUICtrl::~LLUICtrl()
+{
+ gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit()
+
+ if( gFocusMgr.getTopCtrl() == this )
+ {
+ LL_WARNS() << "UI Control holding top ctrl deleted: " << getName() << ". Top view removed." << LL_ENDL;
+ gFocusMgr.removeTopCtrlWithoutCallback( this );
+ }
+
+ delete mCommitSignal;
+ delete mValidateSignal;
+ delete mMouseEnterSignal;
+ delete mMouseLeaveSignal;
+ delete mMouseDownSignal;
+ delete mMouseUpSignal;
+ delete mRightMouseDownSignal;
+ delete mRightMouseUpSignal;
+ delete mDoubleClickSignal;
+}
+
+void default_commit_handler(LLUICtrl* ctrl, const LLSD& param)
+{}
+
+bool default_enable_handler(LLUICtrl* ctrl, const LLSD& param)
+{
+ return true;
+}
+
+
+LLUICtrl::commit_signal_t::slot_type LLUICtrl::initCommitCallback(const CommitCallbackParam& cb)
+{
+ if (cb.function.isProvided())
+ {
+ if (cb.parameter.isProvided())
+ return boost::bind(cb.function(), _1, cb.parameter);
+ else
+ return cb.function();
+ }
+ else
+ {
+ std::string function_name = cb.function_name;
+ setFunctionName(function_name);
+ commit_callback_t* func = (CommitCallbackRegistry::getValue(function_name));
+ if (func)
+ {
+ if (cb.parameter.isProvided())
+ return boost::bind((*func), _1, cb.parameter);
+ else
+ return commit_signal_t::slot_type(*func);
+ }
+ else if (!function_name.empty())
+ {
+ LL_WARNS() << "No callback found for: '" << function_name << "' in control: " << getName() << LL_ENDL;
+ }
+ }
+ return default_commit_handler;
+}
+
+LLUICtrl::enable_signal_t::slot_type LLUICtrl::initEnableCallback(const EnableCallbackParam& cb)
+{
+ // Set the callback function
+ if (cb.function.isProvided())
+ {
+ if (cb.parameter.isProvided())
+ return boost::bind(cb.function(), this, cb.parameter);
+ else
+ return cb.function();
+ }
+ else
+ {
+ enable_callback_t* func = (EnableCallbackRegistry::getValue(cb.function_name));
+ if (func)
+ {
+ if (cb.parameter.isProvided())
+ return boost::bind((*func), this, cb.parameter);
+ else
+ return enable_signal_t::slot_type(*func);
+ }
+ }
+ return default_enable_handler;
+}
+
+// virtual
+void LLUICtrl::onMouseEnter(S32 x, S32 y, MASK mask)
+{
+ if (mMouseEnterSignal)
+ {
+ (*mMouseEnterSignal)(this, getValue());
+ }
+}
+
+// virtual
+void LLUICtrl::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+ if(mMouseLeaveSignal)
+ {
+ (*mMouseLeaveSignal)(this, getValue());
+ }
+}
+
+//virtual
+bool LLUICtrl::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+
+ 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;
+}
+
+//virtual
+bool LLUICtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ bool handled = LLView::handleRightMouseDown(x,y,mask);
+ if (mRightMouseDownSignal)
+ {
+ (*mRightMouseDownSignal)(this,x,y,mask);
+ }
+ return handled;
+}
+
+//virtual
+bool LLUICtrl::handleRightMouseUp(S32 x, S32 y, MASK mask)
+{
+ bool handled = LLView::handleRightMouseUp(x,y,mask);
+ if(mRightMouseUpSignal)
+ {
+ (*mRightMouseUpSignal)(this,x,y,mask);
+ }
+ return handled;
+}
+
+bool LLUICtrl::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ bool handled = LLView::handleDoubleClick(x, y, mask);
+ if (mDoubleClickSignal)
+ {
+ (*mDoubleClickSignal)(this, x, y, mask);
+ }
+ return handled;
+}
+
+// can't tab to children of a non-tab-stop widget
+bool LLUICtrl::canFocusChildren() const
+{
+ return hasTabStop();
+}
+
+
+void LLUICtrl::onCommit()
+{
+ if (mCommitSignal)
+ {
+ if (!mFunctionName.empty())
+ {
+ LL_DEBUGS("UIUsage") << "calling commit function " << mFunctionName << LL_ENDL;
+ LLUIUsage::instance().logCommand(mFunctionName);
+ LLUIUsage::instance().logControl(getPathname());
+ }
+ else
+ {
+ //LL_DEBUGS("UIUsage") << "calling commit function " << "UNKNOWN" << LL_ENDL;
+ }
+ (*mCommitSignal)(this, getValue());
+ }
+}
+
+//virtual
+bool LLUICtrl::isCtrl() const
+{
+ return true;
+}
+
+//virtual
+void LLUICtrl::setValue(const LLSD& value)
+{
+ mViewModel->setValue(value);
+}
+
+//virtual
+LLSD LLUICtrl::getValue() const
+{
+ return mViewModel->getValue();
+}
+
+/// When two widgets are displaying the same data (e.g. during a skin
+/// change), share their ViewModel.
+void LLUICtrl::shareViewModelFrom(const LLUICtrl& other)
+{
+ // Because mViewModel is an LLViewModelPtr, this assignment will quietly
+ // dispose of the previous LLViewModel -- unless it's already shared by
+ // somebody else.
+ mViewModel = other.mViewModel;
+}
+
+//virtual
+LLViewModel* LLUICtrl::getViewModel() const
+{
+ return mViewModel;
+}
+
+//virtual
+bool LLUICtrl::postBuild()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+ //
+ // 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)
+ {
+ mControlVariable->set(value);
+ return true;
+ }
+ return false;
+}
+
+void LLUICtrl::setControlVariable(LLControlVariable* control)
+{
+ if (mControlVariable)
+ {
+ //RN: this will happen in practice, should we try to avoid it?
+ //LL_WARNS() << "setControlName called twice on same control!" << LL_ENDL;
+ mControlConnection.disconnect(); // disconnect current signal
+ mControlVariable = NULL;
+ }
+
+ if (control)
+ {
+ mControlVariable = control;
+ mControlConnection = mControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("value")));
+ setValue(mControlVariable->getValue());
+ }
+}
+
+void LLUICtrl::removeControlVariable()
+{
+ if (mControlVariable)
+ {
+ mControlConnection.disconnect();
+ mControlVariable = NULL;
+ }
+}
+
+//virtual
+void LLUICtrl::setControlName(const std::string& control_name, LLView *context)
+{
+ if (context == NULL)
+ {
+ context = this;
+ }
+
+ // Register new listener
+ if (!control_name.empty())
+ {
+ LLControlVariable* control = context->findControl(control_name);
+ if (!control)
+ {
+ LL_WARNS() << "Failed to assign control variable to " << getName()
+ << ": control "<< control_name << " does not exist." << LL_ENDL;
+ }
+ setControlVariable(control);
+ }
+}
+
+void LLUICtrl::setEnabledControlVariable(LLControlVariable* control)
+{
+ if (mEnabledControlVariable)
+ {
+ mEnabledControlConnection.disconnect(); // disconnect current signal
+ mEnabledControlVariable = NULL;
+ }
+ if (control)
+ {
+ mEnabledControlVariable = control;
+ mEnabledControlConnection = mEnabledControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("enabled")));
+ setEnabled(mEnabledControlVariable->getValue().asBoolean());
+ }
+}
+
+void LLUICtrl::setDisabledControlVariable(LLControlVariable* control)
+{
+ if (mDisabledControlVariable)
+ {
+ mDisabledControlConnection.disconnect(); // disconnect current signal
+ mDisabledControlVariable = NULL;
+ }
+ if (control)
+ {
+ mDisabledControlVariable = control;
+ mDisabledControlConnection = mDisabledControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("disabled")));
+ setEnabled(!(mDisabledControlVariable->getValue().asBoolean()));
+ }
+}
+
+void LLUICtrl::setMakeVisibleControlVariable(LLControlVariable* control)
+{
+ if (mMakeVisibleControlVariable)
+ {
+ mMakeVisibleControlConnection.disconnect(); // disconnect current signal
+ mMakeVisibleControlVariable = NULL;
+ }
+ if (control)
+ {
+ mMakeVisibleControlVariable = control;
+ mMakeVisibleControlConnection = mMakeVisibleControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("visible")));
+ setVisible(mMakeVisibleControlVariable->getValue().asBoolean());
+ }
+}
+
+void LLUICtrl::setMakeInvisibleControlVariable(LLControlVariable* control)
+{
+ if (mMakeInvisibleControlVariable)
+ {
+ mMakeInvisibleControlConnection.disconnect(); // disconnect current signal
+ mMakeInvisibleControlVariable = NULL;
+ }
+ if (control)
+ {
+ mMakeInvisibleControlVariable = control;
+ mMakeInvisibleControlConnection = mMakeInvisibleControlVariable->getSignal()->connect(boost::bind(&controlListener, _2, getHandle(), std::string("invisible")));
+ setVisible(!(mMakeInvisibleControlVariable->getValue().asBoolean()));
+ }
+}
+
+void LLUICtrl::setFunctionName(const std::string& function_name)
+{
+ mFunctionName = function_name;
+}
+
+// static
+bool LLUICtrl::controlListener(const LLSD& newvalue, LLHandle<LLUICtrl> handle, std::string type)
+{
+ LLUICtrl* ctrl = handle.get();
+ if (ctrl)
+ {
+ if (type == "value")
+ {
+ ctrl->setValue(newvalue);
+ return true;
+ }
+ else if (type == "enabled")
+ {
+ ctrl->setEnabled(newvalue.asBoolean());
+ return true;
+ }
+ else if(type =="disabled")
+ {
+ ctrl->setEnabled(!newvalue.asBoolean());
+ return true;
+ }
+ else if (type == "visible")
+ {
+ ctrl->setVisible(newvalue.asBoolean());
+ return true;
+ }
+ else if (type == "invisible")
+ {
+ ctrl->setVisible(!newvalue.asBoolean());
+ return true;
+ }
+ }
+ return false;
+}
+
+// virtual
+bool LLUICtrl::setTextArg( const std::string& key, const LLStringExplicit& text )
+{
+ return false;
+}
+
+// virtual
+bool LLUICtrl::setLabelArg( const std::string& key, const LLStringExplicit& text )
+{
+ return false;
+}
+
+// virtual
+LLCtrlSelectionInterface* LLUICtrl::getSelectionInterface()
+{
+ return NULL;
+}
+
+// virtual
+LLCtrlListInterface* LLUICtrl::getListInterface()
+{
+ return NULL;
+}
+
+// virtual
+LLCtrlScrollInterface* LLUICtrl::getScrollInterface()
+{
+ return NULL;
+}
+
+bool LLUICtrl::hasFocus() const
+{
+ return (gFocusMgr.childHasKeyboardFocus(this));
+}
+
+void LLUICtrl::setFocus(bool b)
+{
+ // focus NEVER goes to ui ctrls that are disabled!
+ if (!getEnabled())
+ {
+ return;
+ }
+ if( b )
+ {
+ if (!hasFocus())
+ {
+ gFocusMgr.setKeyboardFocus( this );
+ }
+ }
+ else
+ {
+ if( gFocusMgr.childHasKeyboardFocus(this))
+ {
+ gFocusMgr.setKeyboardFocus( NULL );
+ }
+ }
+}
+
+// virtual
+void LLUICtrl::setTabStop( bool b )
+{
+ mTabStop = b;
+}
+
+// virtual
+bool LLUICtrl::hasTabStop() const
+{
+ return mTabStop;
+}
+
+// virtual
+bool LLUICtrl::acceptsTextInput() const
+{
+ return false;
+}
+
+//virtual
+bool LLUICtrl::isDirty() const
+{
+ return mViewModel->isDirty();
+};
+
+//virtual
+void LLUICtrl::resetDirty()
+{
+ mViewModel->resetDirty();
+}
+
+// virtual
+void LLUICtrl::onTabInto()
+{
+ onUpdateScrollToChild(this);
+}
+
+// virtual
+void LLUICtrl::clear()
+{
+}
+
+// virtual
+void LLUICtrl::setIsChrome(bool is_chrome)
+{
+ mIsChrome = is_chrome;
+}
+
+// virtual
+bool LLUICtrl::getIsChrome() const
+{
+ if (mIsChrome)
+ return true;
+
+ LLView* parent_ctrl = getParent();
+ while (parent_ctrl)
+ {
+ if (parent_ctrl->isCtrl())
+ return ((LLUICtrl*)parent_ctrl)->getIsChrome();
+
+ parent_ctrl = parent_ctrl->getParent();
+ }
+
+ return false;
+}
+
+
+bool LLUICtrl::focusFirstItem(bool prefer_text_fields, bool focus_flash)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+ // try to select default tab group child
+ LLViewQuery query = getTabOrderQuery();
+ child_list_t result = query(this);
+ if(result.size() > 0)
+ {
+ LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.back());
+ if(!ctrl->hasFocus())
+ {
+ ctrl->setFocus(true);
+ ctrl->onTabInto();
+ if(focus_flash)
+ {
+ gFocusMgr.triggerFocusFlash();
+ }
+ }
+ return true;
+ }
+ // search for text field first
+ if(prefer_text_fields)
+ {
+ LLViewQuery 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())
+ {
+ ctrl->setFocus(true);
+ ctrl->onTabInto();
+ if(focus_flash)
+ {
+ gFocusMgr.triggerFocusFlash();
+ }
+ }
+ return true;
+ }
+ }
+ // no text field found, or we don't care about text fields
+ result = getTabOrderQuery().run(this);
+ if(result.size() > 0)
+ {
+ LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.back());
+ if(!ctrl->hasFocus())
+ {
+ ctrl->setFocus(true);
+ ctrl->onTabInto();
+ if(focus_flash)
+ {
+ gFocusMgr.triggerFocusFlash();
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+
+bool LLUICtrl::focusNextItem(bool text_fields_only)
+{
+ // this assumes that this method is called on the focus root.
+ LLViewQuery query = getTabOrderQuery();
+ static LLUICachedControl<bool> tab_to_text_fields_only ("TabToTextFieldsOnly", false);
+ if(text_fields_only || tab_to_text_fields_only)
+ {
+ query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance());
+ }
+ child_list_t result = query(this);
+ return focusNext(result);
+}
+
+bool LLUICtrl::focusPrevItem(bool text_fields_only)
+{
+ // this assumes that this method is called on the focus root.
+ LLViewQuery query = getTabOrderQuery();
+ static LLUICachedControl<bool> tab_to_text_fields_only ("TabToTextFieldsOnly", false);
+ if(text_fields_only || tab_to_text_fields_only)
+ {
+ query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance());
+ }
+ child_list_t result = query(this);
+ return focusPrev(result);
+}
+
+LLUICtrl* LLUICtrl::findRootMostFocusRoot()
+{
+ LLUICtrl* focus_root = NULL;
+ LLUICtrl* next_view = this;
+ while(next_view && next_view->hasTabStop())
+ {
+ if (next_view->isFocusRoot())
+ {
+ focus_root = next_view;
+ }
+ next_view = next_view->getParentUICtrl();
+ }
+
+ return focus_root;
+}
+
+// Skip over any parents that are not LLUICtrl's
+// Used in focus logic since only LLUICtrl elements can have focus
+LLUICtrl* LLUICtrl::getParentUICtrl() const
+{
+ LLView* parent = getParent();
+ while (parent)
+ {
+ if (parent->isCtrl())
+ {
+ return (LLUICtrl*)(parent);
+ }
+ else
+ {
+ parent = parent->getParent();
+ }
+ }
+ return NULL;
+}
+
+bool LLUICtrl::findHelpTopic(std::string& help_topic_out)
+{
+ LLUICtrl* ctrl = this;
+
+ // search back through the control's parents for a panel
+ // or tab with a help_topic string defined
+ while (ctrl)
+ {
+ LLPanel *panel = dynamic_cast<LLPanel *>(ctrl);
+
+ if (panel)
+ {
+
+ LLView *child;
+ LLPanel *subpanel = NULL;
+
+ // does the panel have a sub-panel with a help topic?
+ 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();
+ return true; // success (subpanel)
+ }
+
+ // does the panel have an active tab with a help topic?
+ LLPanel *tab_panel = NULL;
+
+ 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())
+ {
+ tab_panel = curTabPanel;
+ break;
+ }
+ }
+
+ if (tab_panel)
+ {
+ help_topic_out = tab_panel->getHelpTopic();
+ return true; // success (tab)
+ }
+
+ // otherwise, does the panel have a help topic itself?
+ if (!panel->getHelpTopic().empty())
+ {
+ help_topic_out = panel->getHelpTopic();
+ return true; // success (panel)
+ }
+ }
+
+ ctrl = ctrl->getParentUICtrl();
+ }
+
+ return false; // no help topic found
+}
+
+// *TODO: Deprecate; for backwards compatability only:
+boost::signals2::connection LLUICtrl::setCommitCallback( boost::function<void (LLUICtrl*,void*)> cb, void* data)
+{
+ return setCommitCallback( boost::bind(cb, _1, data));
+}
+boost::signals2::connection LLUICtrl::setValidateBeforeCommit( boost::function<bool (const LLSD& data)> cb )
+{
+ if (!mValidateSignal) mValidateSignal = new enable_signal_t();
+
+ return mValidateSignal->connect(boost::bind(cb, _2));
+}
+
+// virtual
+void LLUICtrl::setTentative(bool b)
+{
+ mTentative = b;
+}
+
+// virtual
+bool LLUICtrl::getTentative() const
+{
+ return mTentative;
+}
+
+// virtual
+void LLUICtrl::setColor(const LLColor4& color)
+{ }
+
+F32 LLUICtrl::getCurrentTransparency()
+{
+ F32 alpha = 0;
+
+ switch(mTransparencyType)
+ {
+ case TT_DEFAULT:
+ alpha = getDrawContext().mAlpha;
+ break;
+
+ case TT_ACTIVE:
+ alpha = sActiveControlTransparency;
+ break;
+
+ case TT_INACTIVE:
+ alpha = sInactiveControlTransparency;
+ break;
+
+ case TT_FADING:
+ alpha = sInactiveControlTransparency / 2;
+ break;
+ }
+
+ return alpha;
+}
+
+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();
+
+ return mCommitSignal->connect(cb);
+}
+
+boost::signals2::connection LLUICtrl::setValidateCallback( const enable_signal_t::slot_type& cb )
+{
+ if (!mValidateSignal) mValidateSignal = new enable_signal_t();
+
+ return mValidateSignal->connect(cb);
+}
+
+boost::signals2::connection LLUICtrl::setMouseEnterCallback( const commit_signal_t::slot_type& cb )
+{
+ if (!mMouseEnterSignal) mMouseEnterSignal = new commit_signal_t();
+
+ return mMouseEnterSignal->connect(cb);
+}
+
+boost::signals2::connection LLUICtrl::setMouseLeaveCallback( const commit_signal_t::slot_type& cb )
+{
+ if (!mMouseLeaveSignal) mMouseLeaveSignal = new commit_signal_t();
+
+ return mMouseLeaveSignal->connect(cb);
+}
+
+boost::signals2::connection LLUICtrl::setMouseDownCallback( const mouse_signal_t::slot_type& cb )
+{
+ if (!mMouseDownSignal) mMouseDownSignal = new mouse_signal_t();
+
+ return mMouseDownSignal->connect(cb);
+}
+
+boost::signals2::connection LLUICtrl::setMouseUpCallback( const mouse_signal_t::slot_type& cb )
+{
+ if (!mMouseUpSignal) mMouseUpSignal = new mouse_signal_t();
+
+ return mMouseUpSignal->connect(cb);
+}
+
+boost::signals2::connection LLUICtrl::setRightMouseDownCallback( const mouse_signal_t::slot_type& cb )
+{
+ if (!mRightMouseDownSignal) mRightMouseDownSignal = new mouse_signal_t();
+
+ return mRightMouseDownSignal->connect(cb);
+}
+
+boost::signals2::connection LLUICtrl::setRightMouseUpCallback( const mouse_signal_t::slot_type& cb )
+{
+ if (!mRightMouseUpSignal) mRightMouseUpSignal = new mouse_signal_t();
+
+ return mRightMouseUpSignal->connect(cb);
+}
+
+boost::signals2::connection LLUICtrl::setDoubleClickCallback( const mouse_signal_t::slot_type& cb )
+{
+ if (!mDoubleClickSignal) mDoubleClickSignal = new mouse_signal_t();
+
+ return mDoubleClickSignal->connect(cb);
+}
+
+void LLUICtrl::addInfo(LLSD & info)
+{
+ LLView::addInfo(info);
+ info["value"] = getValue();
+}
diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h
index 4f67a40cfb..8dac6bb38c 100644
--- a/indra/llui/lluictrl.h
+++ b/indra/llui/lluictrl.h
@@ -1,339 +1,341 @@
-/**
- * @file lluictrl.h
- * @author James Cook, Richard Nelson, Tom Yedwab
- * @brief Abstract base class for UI controls
- *
- * $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_LLUICTRL_H
-#define LL_LLUICTRL_H
-
-//#include "llboost.h"
-#include "llrect.h"
-#include "llsd.h"
-#include <boost/function.hpp>
-#include <boost/signals2.hpp>
-
-#include "llinitparam.h"
-#include "llview.h"
-#include "llviewmodel.h" // *TODO move dependency to .cpp file
-#include "llsearchablecontrol.h"
-
-const bool TAKE_FOCUS_YES = true;
-const bool TAKE_FOCUS_NO = false;
-
-class LLUICtrl
- : public LLView, public boost::signals2::trackable
-{
-public:
- typedef boost::function<void (LLUICtrl* ctrl, const LLSD& param)> commit_callback_t;
- typedef boost::signals2::signal<void (LLUICtrl* ctrl, const LLSD& param)> commit_signal_t;
- // *TODO: add xml support for this type of signal in the future
- typedef boost::signals2::signal<void (LLUICtrl* ctrl, S32 x, S32 y, MASK mask)> mouse_signal_t;
-
- typedef boost::function<bool (LLUICtrl* ctrl, const LLSD& param)> enable_callback_t;
- typedef boost::signals2::signal<bool (LLUICtrl* ctrl, const LLSD& param), boost_boolean_combiner> enable_signal_t;
-
- struct CallbackParam : public LLInitParam::Block<CallbackParam>
- {
- Ignored name;
-
- Optional<std::string> function_name;
- Optional<LLSD> parameter;
-
- Optional<std::string> control_name;
-
- CallbackParam();
- };
-
- struct CommitCallbackParam : public LLInitParam::Block<CommitCallbackParam, CallbackParam >
- {
- Optional<commit_callback_t> function;
- };
-
- // also used for visible callbacks
- struct EnableCallbackParam : public LLInitParam::Block<EnableCallbackParam, CallbackParam >
- {
- Optional<enable_callback_t> function;
- };
-
- struct EnableControls : public LLInitParam::ChoiceBlock<EnableControls>
- {
- Alternative<std::string> enabled;
- Alternative<std::string> disabled;
-
- EnableControls();
- };
- struct ControlVisibility : public LLInitParam::ChoiceBlock<ControlVisibility>
- {
- Alternative<std::string> visible;
- Alternative<std::string> invisible;
-
- ControlVisibility();
- };
- struct Params : public LLInitParam::Block<Params, LLView::Params>
- {
- Optional<std::string> label;
- Optional<bool> tab_stop,
- chrome,
- requests_front;
- Optional<LLSD> initial_value;
-
- Optional<CommitCallbackParam> init_callback,
- commit_callback;
- Optional<EnableCallbackParam> validate_callback;
-
- Optional<CommitCallbackParam> mouseenter_callback,
- mouseleave_callback;
-
- Optional<std::string> control_name;
- Optional<EnableControls> enabled_controls;
- Optional<ControlVisibility> controls_visibility;
-
- // font params
- Optional<const LLFontGL*> font;
- Optional<LLFontGL::HAlign> font_halign;
- Optional<LLFontGL::VAlign> font_valign;
-
- // cruft from LLXMLNode implementation
- Ignored type,
- length;
-
- Params();
- };
-
- enum ETypeTransparency
- {
- TT_DEFAULT,
- TT_ACTIVE, // focused floater
- TT_INACTIVE, // other floaters
- TT_FADING, // fading toast
- };
- /*virtual*/ ~LLUICtrl();
-
- void initFromParams(const Params& p);
-protected:
- friend class LLUICtrlFactory;
- static const Params& getDefaultParams();
- LLUICtrl(const Params& p = getDefaultParams(),
- const LLViewModelPtr& viewmodel=LLViewModelPtr(new LLViewModel));
-
- commit_signal_t::slot_type initCommitCallback(const CommitCallbackParam& cb);
- enable_signal_t::slot_type initEnableCallback(const EnableCallbackParam& cb);
-
- // We need this virtual so we can override it with derived versions
- virtual LLViewModel* getViewModel() const;
- // We shouldn't ever need to set this directly
- //virtual void setViewModel(const LLViewModelPtr&);
-
- /*virtual*/ bool postBuild() override;
-
-public:
- // LLView interface
- /*virtual*/ bool setLabelArg( const std::string& key, const LLStringExplicit& text ) override;
- /*virtual*/ bool isCtrl() const override;
- /*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask) override;
- /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) override;
- /*virtual*/ bool canFocusChildren() const override;
- /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask) override;
- /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask) override;
- /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask) override;
- /*virtual*/ bool handleRightMouseUp(S32 x, S32 y, MASK mask) override;
- /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask) override;
-
- // From LLFocusableElement
- /*virtual*/ void setFocus( bool b ) override;
- /*virtual*/ bool hasFocus() const override;
-
- // New virtuals
-
- // Return NULL by default (overrride if the class has the appropriate interface)
- virtual class LLCtrlSelectionInterface* getSelectionInterface();
- virtual class LLCtrlListInterface* getListInterface();
- virtual class LLCtrlScrollInterface* getScrollInterface();
-
- bool setControlValue(const LLSD& value);
- void setControlVariable(LLControlVariable* control);
- virtual void setControlName(const std::string& control, LLView *context = NULL);
- void removeControlVariable();
-
- LLControlVariable* getControlVariable() { return mControlVariable; }
-
- void setEnabledControlVariable(LLControlVariable* control);
- void setDisabledControlVariable(LLControlVariable* control);
- void setMakeVisibleControlVariable(LLControlVariable* control);
- void setMakeInvisibleControlVariable(LLControlVariable* control);
-
- void setFunctionName(const std::string& function_name);
-
- virtual void setTentative(bool b);
- virtual bool getTentative() const;
- virtual void setValue(const LLSD& value);
- virtual LLSD getValue() const;
- /// When two widgets are displaying the same data (e.g. during a skin
- /// change), share their ViewModel.
- virtual void shareViewModelFrom(const LLUICtrl& other);
-
- virtual bool setTextArg( const std::string& key, const LLStringExplicit& text );
- virtual void setIsChrome(bool is_chrome);
-
- virtual bool acceptsTextInput() const; // Defaults to false
-
- // A control is dirty if the user has modified its value.
- // Editable controls should override this.
- virtual bool isDirty() const; // Defauls to false
- virtual void resetDirty(); //Defaults to no-op
-
- // Call appropriate callback
- virtual void onCommit();
-
- // Default to no-op:
- virtual void onTabInto();
-
- // Clear any user-provided input (text in a text editor, checked checkbox,
- // selected radio button, etc.). Defaults to no-op.
- virtual void clear();
-
- virtual void setColor(const LLColor4& color);
-
- // Ansariel: Changed to virtual. We might want to change the transparency ourself!
- virtual F32 getCurrentTransparency();
-
- void setTransparencyType(ETypeTransparency type);
- ETypeTransparency getTransparencyType() const {return mTransparencyType;}
-
- bool focusNextItem(bool text_entry_only);
- bool focusPrevItem(bool text_entry_only);
- bool focusFirstItem(bool prefer_text_fields = false, bool focus_flash = true );
-
- // Non Virtuals
- LLHandle<LLUICtrl> getHandle() const { return getDerivedHandle<LLUICtrl>(); }
- bool getIsChrome() const;
-
- void setTabStop( bool b );
- bool hasTabStop() const;
-
- LLUICtrl* getParentUICtrl() const;
-
- // return true if help topic found by crawling through parents -
- // topic then put in help_topic_out
- bool findHelpTopic(std::string& help_topic_out);
-
- boost::signals2::connection setCommitCallback(const 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 );
-
- boost::signals2::connection setMouseEnterCallback( const commit_signal_t::slot_type& cb );
- boost::signals2::connection setMouseLeaveCallback( const commit_signal_t::slot_type& cb );
-
- boost::signals2::connection setMouseDownCallback( const mouse_signal_t::slot_type& cb );
- boost::signals2::connection setMouseUpCallback( const mouse_signal_t::slot_type& cb );
- boost::signals2::connection setRightMouseDownCallback( const mouse_signal_t::slot_type& cb );
- boost::signals2::connection setRightMouseUpCallback( const mouse_signal_t::slot_type& cb );
-
- boost::signals2::connection setDoubleClickCallback( const mouse_signal_t::slot_type& cb );
-
- // *TODO: Deprecate; for backwards compatability only:
- boost::signals2::connection setCommitCallback( boost::function<void (LLUICtrl*,void*)> cb, void* data);
- boost::signals2::connection setValidateBeforeCommit( boost::function<bool (const LLSD& data)> cb );
-
- LLUICtrl* findRootMostFocusRoot();
-
- class LLTextInputFilter : public LLQueryFilter, public LLSingleton<LLTextInputFilter>
- {
- LLSINGLETON_EMPTY_CTOR(LLTextInputFilter);
- /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override
- {
- return filterResult_t(view->isCtrl() && static_cast<const LLUICtrl *>(view)->acceptsTextInput(), true);
- }
- };
-
- template <typename F, typename DERIVED> class CallbackRegistry : public LLRegistrySingleton<std::string, F, DERIVED >
- {};
-
- class CommitCallbackRegistry : public CallbackRegistry<commit_callback_t, CommitCallbackRegistry>
- {
- LLSINGLETON_EMPTY_CTOR(CommitCallbackRegistry);
- };
- // the enable callback registry is also used for visiblity callbacks
- class EnableCallbackRegistry : public CallbackRegistry<enable_callback_t, EnableCallbackRegistry>
- {
- LLSINGLETON_EMPTY_CTOR(EnableCallbackRegistry);
- };
-
-protected:
-
- static bool controlListener(const LLSD& newvalue, LLHandle<LLUICtrl> handle, std::string type);
-
- commit_signal_t* mCommitSignal;
- enable_signal_t* mValidateSignal;
-
- commit_signal_t* mMouseEnterSignal;
- commit_signal_t* mMouseLeaveSignal;
-
- mouse_signal_t* mMouseDownSignal;
- mouse_signal_t* mMouseUpSignal;
- mouse_signal_t* mRightMouseDownSignal;
- mouse_signal_t* mRightMouseUpSignal;
-
- mouse_signal_t* mDoubleClickSignal;
-
- LLViewModelPtr mViewModel;
-
- LLControlVariable* mControlVariable;
- boost::signals2::connection mControlConnection;
- LLControlVariable* mEnabledControlVariable;
- boost::signals2::connection mEnabledControlConnection;
- LLControlVariable* mDisabledControlVariable;
- boost::signals2::connection mDisabledControlConnection;
- LLControlVariable* mMakeVisibleControlVariable;
- boost::signals2::connection mMakeVisibleControlConnection;
- LLControlVariable* mMakeInvisibleControlVariable;
- boost::signals2::connection mMakeInvisibleControlConnection;
-
- std::string mFunctionName;
-
- static F32 sActiveControlTransparency;
- static F32 sInactiveControlTransparency;
-
- /*virtual*/ void addInfo(LLSD & info) override;
-
-private:
-
- bool mIsChrome;
- bool mRequestsFront;
- bool mTabStop;
- bool mTentative;
-
- ETypeTransparency mTransparencyType;
-};
-
-// Build time optimization, generate once in .cpp file
-#ifndef LLUICTRL_CPP
-extern template class LLUICtrl* LLView::getChild<class LLUICtrl>(
- const std::string& name, bool recurse) const;
-#endif
-
-#endif // LL_LLUICTRL_H
+/**
+ * @file lluictrl.h
+ * @author James Cook, Richard Nelson, Tom Yedwab
+ * @brief Abstract base class for UI controls
+ *
+ * $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_LLUICTRL_H
+#define LL_LLUICTRL_H
+
+//#include "llboost.h"
+#include "llrect.h"
+#include "llsd.h"
+#include <boost/function.hpp>
+#include <boost/signals2.hpp>
+
+#include "llinitparam.h"
+#include "llview.h"
+#include "llviewmodel.h" // *TODO move dependency to .cpp file
+#include "llsearchablecontrol.h"
+
+const bool TAKE_FOCUS_YES = true;
+const bool TAKE_FOCUS_NO = false;
+const S32 DROP_SHADOW_FLOATER = 5;
+
+class LLUICtrl
+ : public LLView, public boost::signals2::trackable
+{
+public:
+ typedef boost::function<void (LLUICtrl* ctrl, const LLSD& param)> commit_callback_t;
+ typedef boost::signals2::signal<void (LLUICtrl* ctrl, const LLSD& param)> commit_signal_t;
+ // *TODO: add xml support for this type of signal in the future
+ typedef boost::signals2::signal<void (LLUICtrl* ctrl, S32 x, S32 y, MASK mask)> mouse_signal_t;
+
+ typedef boost::function<bool (LLUICtrl* ctrl, const LLSD& param)> enable_callback_t;
+ typedef boost::signals2::signal<bool (LLUICtrl* ctrl, const LLSD& param), boost_boolean_combiner> enable_signal_t;
+
+ struct CallbackParam : public LLInitParam::Block<CallbackParam>
+ {
+ Ignored name;
+
+ Optional<std::string> function_name;
+ Optional<LLSD> parameter;
+
+ Optional<std::string> control_name;
+
+ CallbackParam();
+ };
+
+ struct CommitCallbackParam : public LLInitParam::Block<CommitCallbackParam, CallbackParam >
+ {
+ Optional<commit_callback_t> function;
+ };
+
+ // also used for visible callbacks
+ struct EnableCallbackParam : public LLInitParam::Block<EnableCallbackParam, CallbackParam >
+ {
+ Optional<enable_callback_t> function;
+ };
+
+ struct EnableControls : public LLInitParam::ChoiceBlock<EnableControls>
+ {
+ Alternative<std::string> enabled;
+ Alternative<std::string> disabled;
+
+ EnableControls();
+ };
+ struct ControlVisibility : public LLInitParam::ChoiceBlock<ControlVisibility>
+ {
+ Alternative<std::string> visible;
+ Alternative<std::string> invisible;
+
+ ControlVisibility();
+ };
+ struct Params : public LLInitParam::Block<Params, LLView::Params>
+ {
+ Optional<std::string> label;
+ Optional<bool> tab_stop,
+ chrome,
+ requests_front;
+ Optional<LLSD> initial_value;
+
+ Optional<CommitCallbackParam> init_callback,
+ commit_callback;
+ Optional<EnableCallbackParam> validate_callback;
+
+ Optional<CommitCallbackParam> mouseenter_callback,
+ mouseleave_callback;
+
+ Optional<std::string> control_name;
+ Optional<EnableControls> enabled_controls;
+ Optional<ControlVisibility> controls_visibility;
+
+ // font params
+ Optional<const LLFontGL*> font;
+ Optional<LLFontGL::HAlign> font_halign;
+ Optional<LLFontGL::VAlign> font_valign;
+
+ // cruft from LLXMLNode implementation
+ Ignored type,
+ length;
+
+ Params();
+ };
+
+ enum ETypeTransparency
+ {
+ TT_DEFAULT,
+ TT_ACTIVE, // focused floater
+ TT_INACTIVE, // other floaters
+ TT_FADING, // fading toast
+ };
+ /*virtual*/ ~LLUICtrl();
+
+ void initFromParams(const Params& p);
+protected:
+ friend class LLUICtrlFactory;
+ static const Params& getDefaultParams();
+ LLUICtrl(const Params& p = getDefaultParams(),
+ const LLViewModelPtr& viewmodel=LLViewModelPtr(new LLViewModel));
+
+ commit_signal_t::slot_type initCommitCallback(const CommitCallbackParam& cb);
+ enable_signal_t::slot_type initEnableCallback(const EnableCallbackParam& cb);
+
+ // We need this virtual so we can override it with derived versions
+ virtual LLViewModel* getViewModel() const;
+ // We shouldn't ever need to set this directly
+ //virtual void setViewModel(const LLViewModelPtr&);
+
+ /*virtual*/ bool postBuild() override;
+
+public:
+ // LLView interface
+ /*virtual*/ bool setLabelArg( const std::string& key, const LLStringExplicit& text ) override;
+ /*virtual*/ bool isCtrl() const override;
+ /*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ bool canFocusChildren() const override;
+ /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ bool handleRightMouseUp(S32 x, S32 y, MASK mask) override;
+ /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask) override;
+
+ // From LLFocusableElement
+ /*virtual*/ void setFocus( bool b ) override;
+ /*virtual*/ bool hasFocus() const override;
+
+ // New virtuals
+
+
+ // Return NULL by default (overrride if the class has the appropriate interface)
+ virtual class LLCtrlSelectionInterface* getSelectionInterface();
+ virtual class LLCtrlListInterface* getListInterface();
+ virtual class LLCtrlScrollInterface* getScrollInterface();
+
+ bool setControlValue(const LLSD& value);
+ void setControlVariable(LLControlVariable* control);
+ virtual void setControlName(const std::string& control, LLView *context = NULL);
+ void removeControlVariable();
+
+ LLControlVariable* getControlVariable() { return mControlVariable; }
+
+ void setEnabledControlVariable(LLControlVariable* control);
+ void setDisabledControlVariable(LLControlVariable* control);
+ void setMakeVisibleControlVariable(LLControlVariable* control);
+ void setMakeInvisibleControlVariable(LLControlVariable* control);
+
+ void setFunctionName(const std::string& function_name);
+
+ virtual void setTentative(bool b);
+ virtual bool getTentative() const;
+ virtual void setValue(const LLSD& value);
+ virtual LLSD getValue() const;
+ /// When two widgets are displaying the same data (e.g. during a skin
+ /// change), share their ViewModel.
+ virtual void shareViewModelFrom(const LLUICtrl& other);
+
+ virtual bool setTextArg( const std::string& key, const LLStringExplicit& text );
+ virtual void setIsChrome(bool is_chrome);
+
+ virtual bool acceptsTextInput() const; // Defaults to false
+
+ // A control is dirty if the user has modified its value.
+ // Editable controls should override this.
+ virtual bool isDirty() const; // Defauls to false
+ virtual void resetDirty(); //Defaults to no-op
+
+ // Call appropriate callback
+ virtual void onCommit();
+
+ // Default to no-op:
+ virtual void onTabInto();
+
+ // Clear any user-provided input (text in a text editor, checked checkbox,
+ // selected radio button, etc.). Defaults to no-op.
+ virtual void clear();
+
+ virtual void setColor(const LLColor4& color);
+
+ // Ansariel: Changed to virtual. We might want to change the transparency ourself!
+ virtual F32 getCurrentTransparency();
+
+ void setTransparencyType(ETypeTransparency type);
+ ETypeTransparency getTransparencyType() const {return mTransparencyType;}
+
+ bool focusNextItem(bool text_entry_only);
+ bool focusPrevItem(bool text_entry_only);
+ bool focusFirstItem(bool prefer_text_fields = false, bool focus_flash = true );
+
+ // Non Virtuals
+ LLHandle<LLUICtrl> getHandle() const { return getDerivedHandle<LLUICtrl>(); }
+ bool getIsChrome() const;
+
+ void setTabStop( bool b );
+ bool hasTabStop() const;
+
+ LLUICtrl* getParentUICtrl() const;
+
+ // return true if help topic found by crawling through parents -
+ // topic then put in help_topic_out
+ bool findHelpTopic(std::string& help_topic_out);
+
+ boost::signals2::connection setCommitCallback(const 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 );
+
+ boost::signals2::connection setMouseEnterCallback( const commit_signal_t::slot_type& cb );
+ boost::signals2::connection setMouseLeaveCallback( const commit_signal_t::slot_type& cb );
+
+ boost::signals2::connection setMouseDownCallback( const mouse_signal_t::slot_type& cb );
+ boost::signals2::connection setMouseUpCallback( const mouse_signal_t::slot_type& cb );
+ boost::signals2::connection setRightMouseDownCallback( const mouse_signal_t::slot_type& cb );
+ boost::signals2::connection setRightMouseUpCallback( const mouse_signal_t::slot_type& cb );
+
+ boost::signals2::connection setDoubleClickCallback( const mouse_signal_t::slot_type& cb );
+
+ // *TODO: Deprecate; for backwards compatability only:
+ boost::signals2::connection setCommitCallback( boost::function<void (LLUICtrl*,void*)> cb, void* data);
+ boost::signals2::connection setValidateBeforeCommit( boost::function<bool (const LLSD& data)> cb );
+
+ LLUICtrl* findRootMostFocusRoot();
+
+ class LLTextInputFilter : public LLQueryFilter, public LLSingleton<LLTextInputFilter>
+ {
+ LLSINGLETON_EMPTY_CTOR(LLTextInputFilter);
+ /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override
+ {
+ return filterResult_t(view->isCtrl() && static_cast<const LLUICtrl *>(view)->acceptsTextInput(), true);
+ }
+ };
+
+ template <typename F, typename DERIVED> class CallbackRegistry : public LLRegistrySingleton<std::string, F, DERIVED >
+ {};
+
+ class CommitCallbackRegistry : public CallbackRegistry<commit_callback_t, CommitCallbackRegistry>
+ {
+ LLSINGLETON_EMPTY_CTOR(CommitCallbackRegistry);
+ };
+ // the enable callback registry is also used for visiblity callbacks
+ class EnableCallbackRegistry : public CallbackRegistry<enable_callback_t, EnableCallbackRegistry>
+ {
+ LLSINGLETON_EMPTY_CTOR(EnableCallbackRegistry);
+ };
+
+protected:
+
+ static bool controlListener(const LLSD& newvalue, LLHandle<LLUICtrl> handle, std::string type);
+
+ commit_signal_t* mCommitSignal;
+ enable_signal_t* mValidateSignal;
+
+ commit_signal_t* mMouseEnterSignal;
+ commit_signal_t* mMouseLeaveSignal;
+
+ mouse_signal_t* mMouseDownSignal;
+ mouse_signal_t* mMouseUpSignal;
+ mouse_signal_t* mRightMouseDownSignal;
+ mouse_signal_t* mRightMouseUpSignal;
+
+ mouse_signal_t* mDoubleClickSignal;
+
+ LLViewModelPtr mViewModel;
+
+ LLControlVariable* mControlVariable;
+ boost::signals2::connection mControlConnection;
+ LLControlVariable* mEnabledControlVariable;
+ boost::signals2::connection mEnabledControlConnection;
+ LLControlVariable* mDisabledControlVariable;
+ boost::signals2::connection mDisabledControlConnection;
+ LLControlVariable* mMakeVisibleControlVariable;
+ boost::signals2::connection mMakeVisibleControlConnection;
+ LLControlVariable* mMakeInvisibleControlVariable;
+ boost::signals2::connection mMakeInvisibleControlConnection;
+
+ std::string mFunctionName;
+
+ static F32 sActiveControlTransparency;
+ static F32 sInactiveControlTransparency;
+
+ /*virtual*/ void addInfo(LLSD & info) override;
+
+private:
+
+ bool mIsChrome;
+ bool mRequestsFront;
+ bool mTabStop;
+ bool mTentative;
+
+ ETypeTransparency mTransparencyType;
+};
+
+// Build time optimization, generate once in .cpp file
+#ifndef LLUICTRL_CPP
+extern template class LLUICtrl* LLView::getChild<class LLUICtrl>(
+ const std::string& name, bool recurse) const;
+#endif
+
+#endif // LL_LLUICTRL_H
diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp
index dca777cc1f..e7b98629d2 100644
--- a/indra/llui/lluictrlfactory.cpp
+++ b/indra/llui/lluictrlfactory.cpp
@@ -1,279 +1,279 @@
-/**
- * @file lluictrlfactory.cpp
- * @brief Factory class for creating UI controls
- *
- * $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"
-
-#define LLUICTRLFACTORY_CPP
-#include "lluictrlfactory.h"
-
-#include "llxmlnode.h"
-
-#include <fstream>
-#include <boost/tokenizer.hpp>
-
-// other library includes
-#include "llcontrol.h"
-#include "lldir.h"
-#include "v4color.h"
-#include "v3dmath.h"
-#include "llquaternion.h"
-
-// this library includes
-#include "llpanel.h"
-
-//-----------------------------------------------------------------------------
-
-// UI Ctrl class for padding
-class LLUICtrlLocate : public LLUICtrl
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Params()
- {
- name = "locate";
- tab_stop = false;
- }
- };
-
- LLUICtrlLocate(const Params& p) : LLUICtrl(p) {}
- virtual void draw() { }
-
-};
-
-static LLDefaultChildRegistry::Register<LLUICtrlLocate> r1("locate");
-
-// Build time optimization, generate this once in .cpp file
-template class LLUICtrlFactory* LLSingleton<class LLUICtrlFactory>::getInstance();
-
-//-----------------------------------------------------------------------------
-// LLUICtrlFactory()
-//-----------------------------------------------------------------------------
-LLUICtrlFactory::LLUICtrlFactory()
- : mDummyPanel(NULL) // instantiated when first needed
-{
-}
-
-LLUICtrlFactory::~LLUICtrlFactory()
-{
- // go ahead and leak mDummyPanel since this is static destructor time
- //delete mDummyPanel;
- //mDummyPanel = NULL;
-}
-
-void LLUICtrlFactory::loadWidgetTemplate(const std::string& widget_tag, LLInitParam::BaseBlock& block)
-{
- std::string filename = gDirUtilp->add("widgets", widget_tag + ".xml");
- LLXMLNodePtr root_node;
- std::vector<std::string> search_paths =
- gDirUtilp->findSkinnedFilenames(LLDir::XUI, filename);
-
- if (search_paths.empty())
- {
- return;
- }
-
- // "en" version, the default-language version of the file.
- std::string base_filename = search_paths.front();
- if (!base_filename.empty())
- {
- LLUICtrlFactory::instance().pushFileName(base_filename);
-
- if (!LLXMLNode::getLayeredXMLNode(root_node, search_paths))
- {
- LL_WARNS() << "Couldn't parse widget from: " << base_filename << LL_ENDL;
- return;
- }
- LLXUIParser parser;
- parser.readXUI(root_node, block, base_filename);
- LLUICtrlFactory::instance().popFileName();
- }
-}
-
-//static
-void LLUICtrlFactory::createChildren(LLView* viewp, LLXMLNodePtr node, const widget_registry_t& registry, LLXMLNodePtr output_node)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
- if (node.isNull()) return;
-
- for (LLXMLNodePtr child_node = node->getFirstChild(); child_node.notNull(); child_node = child_node->getNextSibling())
- {
- LLXMLNodePtr outputChild;
- if (output_node)
- {
- outputChild = output_node->createChild("", false);
- }
-
- if (!instance().createFromXML(child_node, viewp, LLStringUtil::null, registry, outputChild))
- {
- // child_node is not a valid child for the current parent
- std::string child_name = std::string(child_node->getName()->mString);
- if (LLDefaultChildRegistry::instance().getValue(child_name))
- {
- // This means that the registry assocaited with the parent widget does not have an entry
- // for the child widget
- // You might need to add something like:
- // static ParentWidgetRegistry::Register<ChildWidgetType> register("child_widget_name");
- LL_WARNS() << child_name << " is not a valid child of " << node->getName()->mString << LL_ENDL;
- }
- else
- {
- LL_WARNS() << "Could not create widget named " << child_node->getName()->mString << LL_ENDL;
- }
- }
-
- if (outputChild && !outputChild->mChildren && outputChild->mAttributes.empty() && outputChild->getValue().empty())
- {
- output_node->deleteChild(outputChild);
- }
- }
-
-}
-
-//-----------------------------------------------------------------------------
-// getLayeredXMLNode()
-//-----------------------------------------------------------------------------
-bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNodePtr& root,
- LLDir::ESkinConstraint constraint)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
- std::vector<std::string> paths =
- gDirUtilp->findSkinnedFilenames(LLDir::XUI, xui_filename, constraint);
-
- if (paths.empty())
- {
- // sometimes whole path is passed in as filename
- paths.push_back(xui_filename);
- }
-
- return LLXMLNode::getLayeredXMLNode(root, paths);
-}
-
-
-//-----------------------------------------------------------------------------
-// saveToXML()
-//-----------------------------------------------------------------------------
-S32 LLUICtrlFactory::saveToXML(LLView* viewp, const std::string& filename)
-{
- return 0;
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-
-LLView *LLUICtrlFactory::createFromXML(LLXMLNodePtr node, LLView* parent, const std::string& filename, const widget_registry_t& registry, LLXMLNodePtr output_node)
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
- std::string ctrl_type = node->getName()->mString;
- LLStringUtil::toLower(ctrl_type);
-
- const LLWidgetCreatorFunc* funcp = registry.getValue(ctrl_type);
- if (funcp == NULL)
- {
- return NULL;
- }
-
- if (parent == NULL)
- {
- if (mDummyPanel == NULL)
- {
- LLPanel::Params p;
- mDummyPanel = create<LLPanel>(p);
- }
- parent = mDummyPanel;
- }
- LLView *view = (*funcp)(node, parent, output_node);
-
- return view;
-}
-
-std::string LLUICtrlFactory::getCurFileName()
-{
- return mFileNames.empty() ? "" : mFileNames.back();
-}
-
-
-void LLUICtrlFactory::pushFileName(const std::string& 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()
-{
- mFileNames.pop_back();
-}
-
-//static
-void LLUICtrlFactory::setCtrlParent(LLView* view, LLView* parent, S32 tab_group)
-{
- if (tab_group == S32_MAX) tab_group = parent->getLastTabGroup();
- parent->addChild(view, tab_group);
-}
-
-//static
-void LLUICtrlFactory::copyName(LLXMLNodePtr src, LLXMLNodePtr dest)
-{
- dest->setName(src->getName()->mString);
-}
-
-template<typename T>
-const LLInitParam::BaseBlock& get_empty_param_block()
-{
- static typename T::Params params;
- return params;
-}
-
-// 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& name)
-{
- // associate parameter block type with template .xml file
- std::string* existing_name = LLWidgetNameRegistry::instance().getValue(param_block_type);
- if (existing_name != NULL)
- {
- 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
- char* foo = 0;
- *foo = 1;
- }
- else
- {
- // widget already registered this name
- return;
- }
- }
-
- 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>);
-}
-
-
+/**
+ * @file lluictrlfactory.cpp
+ * @brief Factory class for creating UI controls
+ *
+ * $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"
+
+#define LLUICTRLFACTORY_CPP
+#include "lluictrlfactory.h"
+
+#include "llxmlnode.h"
+
+#include <fstream>
+#include <boost/tokenizer.hpp>
+
+// other library includes
+#include "llcontrol.h"
+#include "lldir.h"
+#include "v4color.h"
+#include "v3dmath.h"
+#include "llquaternion.h"
+
+// this library includes
+#include "llpanel.h"
+
+//-----------------------------------------------------------------------------
+
+// UI Ctrl class for padding
+class LLUICtrlLocate : public LLUICtrl
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Params()
+ {
+ name = "locate";
+ tab_stop = false;
+ }
+ };
+
+ LLUICtrlLocate(const Params& p) : LLUICtrl(p) {}
+ virtual void draw() { }
+
+};
+
+static LLDefaultChildRegistry::Register<LLUICtrlLocate> r1("locate");
+
+// Build time optimization, generate this once in .cpp file
+template class LLUICtrlFactory* LLSingleton<class LLUICtrlFactory>::getInstance();
+
+//-----------------------------------------------------------------------------
+// LLUICtrlFactory()
+//-----------------------------------------------------------------------------
+LLUICtrlFactory::LLUICtrlFactory()
+ : mDummyPanel(NULL) // instantiated when first needed
+{
+}
+
+LLUICtrlFactory::~LLUICtrlFactory()
+{
+ // go ahead and leak mDummyPanel since this is static destructor time
+ //delete mDummyPanel;
+ //mDummyPanel = NULL;
+}
+
+void LLUICtrlFactory::loadWidgetTemplate(const std::string& widget_tag, LLInitParam::BaseBlock& block)
+{
+ std::string filename = gDirUtilp->add("widgets", widget_tag + ".xml");
+ LLXMLNodePtr root_node;
+ std::vector<std::string> search_paths =
+ gDirUtilp->findSkinnedFilenames(LLDir::XUI, filename);
+
+ if (search_paths.empty())
+ {
+ return;
+ }
+
+ // "en" version, the default-language version of the file.
+ std::string base_filename = search_paths.front();
+ if (!base_filename.empty())
+ {
+ LLUICtrlFactory::instance().pushFileName(base_filename);
+
+ if (!LLXMLNode::getLayeredXMLNode(root_node, search_paths))
+ {
+ LL_WARNS() << "Couldn't parse widget from: " << base_filename << LL_ENDL;
+ return;
+ }
+ LLXUIParser parser;
+ parser.readXUI(root_node, block, base_filename);
+ LLUICtrlFactory::instance().popFileName();
+ }
+}
+
+//static
+void LLUICtrlFactory::createChildren(LLView* viewp, LLXMLNodePtr node, const widget_registry_t& registry, LLXMLNodePtr output_node)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+ if (node.isNull()) return;
+
+ for (LLXMLNodePtr child_node = node->getFirstChild(); child_node.notNull(); child_node = child_node->getNextSibling())
+ {
+ LLXMLNodePtr outputChild;
+ if (output_node)
+ {
+ outputChild = output_node->createChild("", false);
+ }
+
+ if (!instance().createFromXML(child_node, viewp, LLStringUtil::null, registry, outputChild))
+ {
+ // child_node is not a valid child for the current parent
+ std::string child_name = std::string(child_node->getName()->mString);
+ if (LLDefaultChildRegistry::instance().getValue(child_name))
+ {
+ // This means that the registry assocaited with the parent widget does not have an entry
+ // for the child widget
+ // You might need to add something like:
+ // static ParentWidgetRegistry::Register<ChildWidgetType> register("child_widget_name");
+ LL_WARNS() << child_name << " is not a valid child of " << node->getName()->mString << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS() << "Could not create widget named " << child_node->getName()->mString << LL_ENDL;
+ }
+ }
+
+ if (outputChild && !outputChild->mChildren && outputChild->mAttributes.empty() && outputChild->getValue().empty())
+ {
+ output_node->deleteChild(outputChild);
+ }
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// getLayeredXMLNode()
+//-----------------------------------------------------------------------------
+bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNodePtr& root,
+ LLDir::ESkinConstraint constraint)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+ std::vector<std::string> paths =
+ gDirUtilp->findSkinnedFilenames(LLDir::XUI, xui_filename, constraint);
+
+ if (paths.empty())
+ {
+ // sometimes whole path is passed in as filename
+ paths.push_back(xui_filename);
+ }
+
+ return LLXMLNode::getLayeredXMLNode(root, paths);
+}
+
+
+//-----------------------------------------------------------------------------
+// saveToXML()
+//-----------------------------------------------------------------------------
+S32 LLUICtrlFactory::saveToXML(LLView* viewp, const std::string& filename)
+{
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+LLView *LLUICtrlFactory::createFromXML(LLXMLNodePtr node, LLView* parent, const std::string& filename, const widget_registry_t& registry, LLXMLNodePtr output_node)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+ std::string ctrl_type = node->getName()->mString;
+ LLStringUtil::toLower(ctrl_type);
+
+ const LLWidgetCreatorFunc* funcp = registry.getValue(ctrl_type);
+ if (funcp == NULL)
+ {
+ return NULL;
+ }
+
+ if (parent == NULL)
+ {
+ if (mDummyPanel == NULL)
+ {
+ LLPanel::Params p;
+ mDummyPanel = create<LLPanel>(p);
+ }
+ parent = mDummyPanel;
+ }
+ LLView *view = (*funcp)(node, parent, output_node);
+
+ return view;
+}
+
+std::string LLUICtrlFactory::getCurFileName()
+{
+ return mFileNames.empty() ? "" : mFileNames.back();
+}
+
+
+void LLUICtrlFactory::pushFileName(const std::string& 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()
+{
+ mFileNames.pop_back();
+}
+
+//static
+void LLUICtrlFactory::setCtrlParent(LLView* view, LLView* parent, S32 tab_group)
+{
+ if (tab_group == S32_MAX) tab_group = parent->getLastTabGroup();
+ parent->addChild(view, tab_group);
+}
+
+//static
+void LLUICtrlFactory::copyName(LLXMLNodePtr src, LLXMLNodePtr dest)
+{
+ dest->setName(src->getName()->mString);
+}
+
+template<typename T>
+const LLInitParam::BaseBlock& get_empty_param_block()
+{
+ static typename T::Params params;
+ return params;
+}
+
+// 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& name)
+{
+ // associate parameter block type with template .xml file
+ std::string* existing_name = LLWidgetNameRegistry::instance().getValue(param_block_type);
+ if (existing_name != NULL)
+ {
+ 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
+ char* foo = 0;
+ *foo = 1;
+ }
+ else
+ {
+ // widget already registered this name
+ return;
+ }
+ }
+
+ 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>);
+}
+
+
diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h
index 6e585abfc0..a07f9b7dae 100644
--- a/indra/llui/lluictrlfactory.h
+++ b/indra/llui/lluictrlfactory.h
@@ -1,25 +1,25 @@
-/**
+/**
* @file lluictrlfactory.h
* @brief Factory class for creating UI controls
*
* $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$
*/
@@ -44,39 +44,39 @@ template <typename DERIVED_TYPE>
class LLChildRegistry : public LLRegistrySingleton<std::string, LLWidgetCreatorFunc, DERIVED_TYPE>
{
public:
- typedef LLRegistrySingleton<std::string, LLWidgetCreatorFunc, DERIVED_TYPE> super_t;
- // local static instance for registering a particular widget
- template<typename T>
- class Register : public super_t::StaticRegistrar
- {
- public:
- // register with either the provided builder, or the generic templated builder
- Register(const char* tag, LLWidgetCreatorFunc func = NULL);
- };
+ typedef LLRegistrySingleton<std::string, LLWidgetCreatorFunc, DERIVED_TYPE> super_t;
+ // local static instance for registering a particular widget
+ template<typename T>
+ class Register : public super_t::StaticRegistrar
+ {
+ public:
+ // register with either the provided builder, or the generic templated builder
+ Register(const char* tag, LLWidgetCreatorFunc func = NULL);
+ };
protected:
- LLChildRegistry() {}
+ LLChildRegistry() {}
};
class LLDefaultChildRegistry : public LLChildRegistry<LLDefaultChildRegistry>
{
- LLSINGLETON_EMPTY_CTOR(LLDefaultChildRegistry);
+ LLSINGLETON_EMPTY_CTOR(LLDefaultChildRegistry);
};
// lookup widget name by type
-class LLWidgetNameRegistry
-: public LLRegistrySingleton<const std::type_info*, std::string, LLWidgetNameRegistry>
+class LLWidgetNameRegistry
+: public LLRegistrySingleton<const std::type_info*, std::string, LLWidgetNameRegistry>
{
- LLSINGLETON_EMPTY_CTOR(LLWidgetNameRegistry);
+ LLSINGLETON_EMPTY_CTOR(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>
+//: public LLRegistrySingleton<const std::type_info*, empty_param_block_func_t, LLDefaultParamBlockRegistry>
//{
-// LLSINGLETON(LLDefaultParamBlockRegistry);
+// LLSINGLETON(LLDefaultParamBlockRegistry);
//};
// Build time optimization, generate this once in .cpp file
@@ -86,221 +86,221 @@ extern template class LLUICtrlFactory* LLSingleton<class LLUICtrlFactory>::getIn
class LLUICtrlFactory : public LLSingleton<LLUICtrlFactory>
{
- LLSINGLETON(LLUICtrlFactory);
- ~LLUICtrlFactory();
-
- // only partial specialization allowed in inner classes, so use extra dummy parameter
- template <typename PARAM_BLOCK, int DUMMY>
- class ParamDefaults
- {
- public:
- ParamDefaults();
- const PARAM_BLOCK& get() { return mPrototype; }
-
- private:
- PARAM_BLOCK mPrototype;
- };
-
- // base case for recursion, there are NO base classes of LLInitParam::BaseBlock
- template<int DUMMY>
- class ParamDefaults<LLInitParam::BaseBlock, DUMMY>
- {
- public:
- ParamDefaults();
- const LLInitParam::BaseBlock& get() { return mBaseBlock; }
- private:
- LLInitParam::BaseBlock mBaseBlock;
- };
+ LLSINGLETON(LLUICtrlFactory);
+ ~LLUICtrlFactory();
+
+ // only partial specialization allowed in inner classes, so use extra dummy parameter
+ template <typename PARAM_BLOCK, int DUMMY>
+ class ParamDefaults
+ {
+ public:
+ ParamDefaults();
+ const PARAM_BLOCK& get() { return mPrototype; }
+
+ private:
+ PARAM_BLOCK mPrototype;
+ };
+
+ // base case for recursion, there are NO base classes of LLInitParam::BaseBlock
+ template<int DUMMY>
+ class ParamDefaults<LLInitParam::BaseBlock, DUMMY>
+ {
+ public:
+ ParamDefaults();
+ const LLInitParam::BaseBlock& get() { return mBaseBlock; }
+ private:
+ LLInitParam::BaseBlock mBaseBlock;
+ };
public:
- // get default parameter block for widget of a specific type
- template<typename T>
- static const typename T::Params& getDefaultParams()
- {
- return instance().mParamDefaultsMap.obtain< ParamDefaults<typename T::Params, 0> >().get();
- }
-
- // Does what you want for LLFloaters and LLPanels
- // Returns 0 on success
- S32 saveToXML(LLView* viewp, const std::string& filename);
-
- // filename tracking for debugging info
- std::string getCurFileName();
- void pushFileName(const std::string& name);
- void popFileName();
-
- template<typename T>
- static T* create(typename T::Params& params, LLView* parent = NULL)
- {
- params.fillFrom(instance().mParamDefaultsMap.obtain<
- ParamDefaults<typename T::Params, 0> >().get());
-
- T* widget = createWidgetImpl<T>(params, parent);
- if (widget)
- {
- widget->postBuild();
- }
-
- return widget;
- }
-
- LLView* createFromXML(LLXMLNodePtr node, LLView* parent, const std::string& filename, const widget_registry_t&, LLXMLNodePtr output_node );
-
- template<typename T>
- static T* createFromFile(const std::string &filename, LLView *parent, const widget_registry_t& registry)
- {
- T* widget = NULL;
-
- instance().pushFileName(filename);
- {
- LLXMLNodePtr root_node;
-
- if (!LLUICtrlFactory::getLayeredXMLNode(filename, root_node))
- {
+ // get default parameter block for widget of a specific type
+ template<typename T>
+ static const typename T::Params& getDefaultParams()
+ {
+ return instance().mParamDefaultsMap.obtain< ParamDefaults<typename T::Params, 0> >().get();
+ }
+
+ // Does what you want for LLFloaters and LLPanels
+ // Returns 0 on success
+ S32 saveToXML(LLView* viewp, const std::string& filename);
+
+ // filename tracking for debugging info
+ std::string getCurFileName();
+ void pushFileName(const std::string& name);
+ void popFileName();
+
+ template<typename T>
+ static T* create(typename T::Params& params, LLView* parent = NULL)
+ {
+ params.fillFrom(instance().mParamDefaultsMap.obtain<
+ ParamDefaults<typename T::Params, 0> >().get());
+
+ T* widget = createWidgetImpl<T>(params, parent);
+ if (widget)
+ {
+ widget->postBuild();
+ }
+
+ return widget;
+ }
+
+ LLView* createFromXML(LLXMLNodePtr node, LLView* parent, const std::string& filename, const widget_registry_t&, LLXMLNodePtr output_node );
+
+ template<typename T>
+ static T* createFromFile(const std::string &filename, LLView *parent, const widget_registry_t& registry)
+ {
+ T* widget = NULL;
+
+ instance().pushFileName(filename);
+ {
+ LLXMLNodePtr root_node;
+
+ if (!LLUICtrlFactory::getLayeredXMLNode(filename, root_node))
+ {
LL_WARNS() << "Couldn't parse XUI from path: " << instance().getCurFileName() << ", from filename: " << filename << LL_ENDL;
- goto fail;
- }
-
- 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;
-
- deleteView(view);
- view = NULL;
- }
- }
- }
+ goto fail;
+ }
+
+ 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;
+
+ deleteView(view);
+ view = NULL;
+ }
+ }
+ }
fail:
- instance().popFileName();
- return widget;
- }
+ instance().popFileName();
+ return widget;
+ }
- template<class T>
- static T* getDefaultWidget(const std::string& name)
- {
- typename T::Params widget_params;
- widget_params.name = name;
- return create<T>(widget_params);
- }
+ template<class T>
+ static T* getDefaultWidget(const std::string& name)
+ {
+ typename T::Params widget_params;
+ widget_params.name = name;
+ return create<T>(widget_params);
+ }
- static void createChildren(LLView* viewp, LLXMLNodePtr node, const widget_registry_t&, LLXMLNodePtr output_node = NULL);
+ static void createChildren(LLView* viewp, LLXMLNodePtr node, const widget_registry_t&, LLXMLNodePtr output_node = NULL);
- static bool getLayeredXMLNode(const std::string &filename, LLXMLNodePtr& root,
- LLDir::ESkinConstraint constraint=LLDir::CURRENT_SKIN);
+ 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
- template <typename T> friend class LLChildRegistry;
- template <typename T> template <typename U> friend class LLChildRegistry<T>::Register;
+ //NOTE: both friend declarations are necessary to keep both gcc and msvc happy
+ template <typename T> friend class LLChildRegistry;
+ template <typename T> template <typename U> friend class LLChildRegistry<T>::Register;
- static void copyName(LLXMLNodePtr src, LLXMLNodePtr dest);
+ static void copyName(LLXMLNodePtr src, LLXMLNodePtr dest);
- // helper function for adding widget type info to various registries
- static void registerWidget(const std::type_info* widget_type, const std::type_info* param_block_type, const std::string& tag);
+ // helper function for adding widget type info to various registries
+ static void registerWidget(const std::type_info* widget_type, const std::type_info* param_block_type, const std::string& tag);
- static void loadWidgetTemplate(const std::string& widget_tag, LLInitParam::BaseBlock& block);
+ static void loadWidgetTemplate(const std::string& widget_tag, LLInitParam::BaseBlock& block);
- template<typename T>
- static T* createWidgetImpl(const typename T::Params& params, LLView* parent = NULL)
- {
+ template<typename T>
+ static T* createWidgetImpl(const typename T::Params& params, LLView* parent = NULL)
+ {
LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
- T* widget = NULL;
-
- if (!params.validateBlock())
- {
- LL_WARNS() << getInstance()->getCurFileName() << ": Invalid parameter block for " << typeid(T).name() << LL_ENDL;
- //return NULL;
- }
-
- widget = new T(params);
-
- widget->initFromParams(params);
-
- if (parent)
- {
- S32 tab_group = params.tab_group.isProvided() ? params.tab_group() : S32_MAX;
- setCtrlParent(widget, parent, tab_group);
- }
- return widget;
- }
-
- template<typename T>
- static T* defaultBuilder(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node)
- {
+ T* widget = NULL;
+
+ if (!params.validateBlock())
+ {
+ LL_WARNS() << getInstance()->getCurFileName() << ": Invalid parameter block for " << typeid(T).name() << LL_ENDL;
+ //return NULL;
+ }
+
+ widget = new T(params);
+
+ widget->initFromParams(params);
+
+ if (parent)
+ {
+ S32 tab_group = params.tab_group.isProvided() ? params.tab_group() : S32_MAX;
+ setCtrlParent(widget, parent, tab_group);
+ }
+ return widget;
+ }
+
+ template<typename T>
+ static T* defaultBuilder(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node)
+ {
LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
- typename T::Params params(getDefaultParams<T>());
+ typename T::Params params(getDefaultParams<T>());
- LLXUIParser parser;
- parser.readXUI(node, params, LLUICtrlFactory::getInstance()->getCurFileName());
+ LLXUIParser parser;
+ parser.readXUI(node, params, LLUICtrlFactory::getInstance()->getCurFileName());
- if (output_node)
- {
- // We always want to output top-left coordinates
- typename T::Params output_params(params);
- T::setupParamsForExport(output_params, parent);
- copyName(node, output_node);
- parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &getDefaultParams<T>());
- }
+ if (output_node)
+ {
+ // We always want to output top-left coordinates
+ typename T::Params output_params(params);
+ T::setupParamsForExport(output_params, parent);
+ copyName(node, output_node);
+ parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &getDefaultParams<T>());
+ }
- // Apply layout transformations, usually munging rect
- params.from_xui = true;
- T::applyXUILayout(params, parent);
- T* widget = createWidgetImpl<T>(params, parent);
+ // Apply layout transformations, usually munging rect
+ params.from_xui = true;
+ T::applyXUILayout(params, parent);
+ T* widget = createWidgetImpl<T>(params, parent);
- typedef typename T::child_registry_t registry_t;
+ typedef typename T::child_registry_t registry_t;
- createChildren(widget, node, registry_t::instance(), output_node);
+ createChildren(widget, node, registry_t::instance(), output_node);
- if (widget && !widget->postBuild())
- {
- delete widget;
- widget = NULL;
- }
+ if (widget && !widget->postBuild())
+ {
+ delete widget;
+ widget = NULL;
+ }
- return widget;
- }
+ return widget;
+ }
- // this exists to get around dependency on llview
- static void setCtrlParent(LLView* view, LLView* parent, S32 tab_group);
+ // this exists to get around dependency on llview
+ static void setCtrlParent(LLView* view, LLView* parent, S32 tab_group);
- class LLPanel* mDummyPanel;
- std::vector<std::string> mFileNames;
+ class LLPanel* mDummyPanel;
+ std::vector<std::string> mFileNames;
- // store ParamDefaults specializations
- // Each ParamDefaults specialization used to be an LLSingleton in its own
- // right. But the 2016 changes to the LLSingleton mechanism, making
- // LLSingleton instances polymorphic, are incompatible with current
- // LLInitParam::BaseBlock functionality. (Thanks NickyD for spotting
- // that!) Moreover, instances of the private nested ParamDefaults template
- // aren't global resources -- which is what LLSingleton is designed for.
- // This is simply a cache looked up by type. Its lifespan is tied to
- // LLUICtrlFactory. Use LLHeteroMap for this cache.
- LLHeteroMap mParamDefaultsMap;
+ // store ParamDefaults specializations
+ // Each ParamDefaults specialization used to be an LLSingleton in its own
+ // right. But the 2016 changes to the LLSingleton mechanism, making
+ // LLSingleton instances polymorphic, are incompatible with current
+ // LLInitParam::BaseBlock functionality. (Thanks NickyD for spotting
+ // that!) Moreover, instances of the private nested ParamDefaults template
+ // aren't global resources -- which is what LLSingleton is designed for.
+ // This is simply a cache looked up by type. Its lifespan is tied to
+ // LLUICtrlFactory. Use LLHeteroMap for this cache.
+ LLHeteroMap mParamDefaultsMap;
};
template <typename PARAM_BLOCK, int DUMMY>
LLUICtrlFactory::ParamDefaults<PARAM_BLOCK, DUMMY>::ParamDefaults()
{
- // look up template file for this param block...
- const std::string* param_block_tag = LLWidgetNameRegistry::instance().getValue(&typeid(PARAM_BLOCK));
- if (param_block_tag)
- { // ...and if it exists, back fill values using the most specific template first
- PARAM_BLOCK params;
- LLUICtrlFactory::loadWidgetTemplate(*param_block_tag, params);
- mPrototype.fillFrom(params);
- }
- // recursively fill from base class param block
- ((typename PARAM_BLOCK::base_block_t&)mPrototype).fillFrom(
- LLUICtrlFactory::instance().mParamDefaultsMap.obtain<
- ParamDefaults<typename PARAM_BLOCK::base_block_t, DUMMY> >().get());
+ // look up template file for this param block...
+ const std::string* param_block_tag = LLWidgetNameRegistry::instance().getValue(&typeid(PARAM_BLOCK));
+ if (param_block_tag)
+ { // ...and if it exists, back fill values using the most specific template first
+ PARAM_BLOCK params;
+ LLUICtrlFactory::loadWidgetTemplate(*param_block_tag, params);
+ mPrototype.fillFrom(params);
+ }
+ // recursively fill from base class param block
+ ((typename PARAM_BLOCK::base_block_t&)mPrototype).fillFrom(
+ LLUICtrlFactory::instance().mParamDefaultsMap.obtain<
+ ParamDefaults<typename PARAM_BLOCK::base_block_t, DUMMY> >().get());
}
@@ -309,17 +309,17 @@ LLUICtrlFactory::ParamDefaults<LLInitParam::BaseBlock, DUMMY>::ParamDefaults() {
// this is here to make gcc happy with reference to LLUICtrlFactory
template<typename DERIVED>
-template<typename T>
+template<typename T>
LLChildRegistry<DERIVED>::Register<T>::Register(const char* tag, LLWidgetCreatorFunc func)
-: LLChildRegistry<DERIVED>::StaticRegistrar(tag, func.empty() ? (LLWidgetCreatorFunc)&LLUICtrlFactory::defaultBuilder<T> : func)
+: LLChildRegistry<DERIVED>::StaticRegistrar(tag, func.empty() ? (LLWidgetCreatorFunc)&LLUICtrlFactory::defaultBuilder<T> : func)
{
- // add this widget to various registries
- LLUICtrlFactory::instance().registerWidget(&typeid(T), &typeid(typename T::Params), tag);
-
- // since registry_t depends on T, do this in line here
- // TODO: uncomment this for schema generation
- //typedef typename T::child_registry_t registry_t;
- //LLChildRegistryRegistry::instance().defaultRegistrar().add(&typeid(T), registry_t::instance());
+ // add this widget to various registries
+ LLUICtrlFactory::instance().registerWidget(&typeid(T), &typeid(typename T::Params), tag);
+
+ // since registry_t depends on T, do this in line here
+ // TODO: uncomment this for schema generation
+ //typedef typename T::child_registry_t registry_t;
+ //LLChildRegistryRegistry::instance().defaultRegistrar().add(&typeid(T), registry_t::instance());
}
#endif //LLUICTRLFACTORY_H
diff --git a/indra/llui/lluifwd.h b/indra/llui/lluifwd.h
index a68629a091..940774cf46 100644
--- a/indra/llui/lluifwd.h
+++ b/indra/llui/lluifwd.h
@@ -5,21 +5,21 @@
* $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$
*/
diff --git a/indra/llui/lluistring.cpp b/indra/llui/lluistring.cpp
index 98d0c215e6..bfadeb8428 100644
--- a/indra/llui/lluistring.cpp
+++ b/indra/llui/lluistring.cpp
@@ -1,25 +1,25 @@
-/**
+/**
* @file lluistring.cpp
* @brief LLUIString implementation.
*
* $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$
*/
@@ -35,122 +35,122 @@ LLTrace::BlockTimerStatHandle FTM_UI_STRING("UI String");
LLUIString::LLUIString(const std::string& instring, const LLStringUtil::format_map_t& args)
-: mOrig(instring),
- mArgs(new LLStringUtil::format_map_t(args))
+: mOrig(instring),
+ mArgs(new LLStringUtil::format_map_t(args))
{
- dirty();
+ dirty();
}
void LLUIString::assign(const std::string& s)
{
- mOrig = s;
- dirty();
+ mOrig = s;
+ dirty();
}
void LLUIString::setArgList(const LLStringUtil::format_map_t& args)
{
- getArgs() = args;
- dirty();
+ getArgs() = args;
+ dirty();
}
void LLUIString::setArgs(const LLSD& sd)
{
- LL_RECORD_BLOCK_TIME(FTM_UI_STRING);
-
- if (!sd.isMap()) return;
- for(LLSD::map_const_iterator sd_it = sd.beginMap();
- sd_it != sd.endMap();
- ++sd_it)
- {
- setArg(sd_it->first, sd_it->second.asString());
- }
- dirty();
+ LL_RECORD_BLOCK_TIME(FTM_UI_STRING);
+
+ if (!sd.isMap()) return;
+ for(LLSD::map_const_iterator sd_it = sd.beginMap();
+ sd_it != sd.endMap();
+ ++sd_it)
+ {
+ setArg(sd_it->first, sd_it->second.asString());
+ }
+ dirty();
}
void LLUIString::setArg(const std::string& key, const std::string& replacement)
{
- getArgs()[key] = replacement;
- dirty();
+ getArgs()[key] = replacement;
+ dirty();
}
void LLUIString::truncate(S32 maxchars)
{
- if (getUpdatedWResult().size() > (size_t)maxchars)
- {
- LLWStringUtil::truncate(getUpdatedWResult(), maxchars);
- mResult = wstring_to_utf8str(getUpdatedWResult());
- }
+ if (getUpdatedWResult().size() > (size_t)maxchars)
+ {
+ LLWStringUtil::truncate(getUpdatedWResult(), maxchars);
+ mResult = wstring_to_utf8str(getUpdatedWResult());
+ }
}
void LLUIString::erase(S32 charidx, S32 len)
{
- getUpdatedWResult().erase(charidx, len);
- mResult = wstring_to_utf8str(getUpdatedWResult());
+ getUpdatedWResult().erase(charidx, len);
+ mResult = wstring_to_utf8str(getUpdatedWResult());
}
void LLUIString::insert(S32 charidx, const LLWString& wchars)
{
- getUpdatedWResult().insert(charidx, wchars);
- mResult = wstring_to_utf8str(getUpdatedWResult());
+ getUpdatedWResult().insert(charidx, wchars);
+ mResult = wstring_to_utf8str(getUpdatedWResult());
}
void LLUIString::replace(S32 charidx, llwchar wc)
{
- getUpdatedWResult()[charidx] = wc;
- mResult = wstring_to_utf8str(getUpdatedWResult());
+ getUpdatedWResult()[charidx] = wc;
+ mResult = wstring_to_utf8str(getUpdatedWResult());
}
void LLUIString::clear()
{
- // Keep Args
- mOrig.clear();
- mResult.clear();
- mWResult.clear();
+ // Keep Args
+ mOrig.clear();
+ mResult.clear();
+ mWResult.clear();
}
void LLUIString::dirty()
{
- mNeedsResult = true;
- mNeedsWResult = true;
+ mNeedsResult = true;
+ mNeedsWResult = true;
}
void LLUIString::updateResult() const
{
- mNeedsResult = false;
-
- LL_RECORD_BLOCK_TIME(FTM_UI_STRING);
-
- // optimize for empty strings (don't attempt string replacement)
- if (mOrig.empty())
- {
- mResult.clear();
- mWResult.clear();
- return;
- }
- mResult = mOrig;
-
- // get the default args + local args
- LLStringUtil::format_map_t combined_args = LLTrans::getDefaultArgs();
- if (mArgs && !mArgs->empty())
- {
- combined_args.insert(mArgs->begin(), mArgs->end());
- }
- LLStringUtil::format(mResult, combined_args);
+ mNeedsResult = false;
+
+ LL_RECORD_BLOCK_TIME(FTM_UI_STRING);
+
+ // optimize for empty strings (don't attempt string replacement)
+ if (mOrig.empty())
+ {
+ mResult.clear();
+ mWResult.clear();
+ return;
+ }
+ mResult = mOrig;
+
+ // get the default args + local args
+ LLStringUtil::format_map_t combined_args = LLTrans::getDefaultArgs();
+ if (mArgs && !mArgs->empty())
+ {
+ combined_args.insert(mArgs->begin(), mArgs->end());
+ }
+ LLStringUtil::format(mResult, combined_args);
}
void LLUIString::updateWResult() const
{
- mNeedsWResult = false;
+ mNeedsWResult = false;
- mWResult = utf8str_to_wstring(getUpdatedResult());
+ mWResult = utf8str_to_wstring(getUpdatedResult());
}
LLStringUtil::format_map_t& LLUIString::getArgs()
{
- if (!mArgs)
- {
- mArgs = new LLStringUtil::format_map_t;
- }
- return *mArgs;
+ if (!mArgs)
+ {
+ mArgs = new LLStringUtil::format_map_t;
+ }
+ return *mArgs;
}
diff --git a/indra/llui/lluistring.h b/indra/llui/lluistring.h
index b1089a3903..0cc699f59c 100644
--- a/indra/llui/lluistring.h
+++ b/indra/llui/lluistring.h
@@ -1,4 +1,4 @@
-/**
+/**
* @file lluistring.h
* @author: Steve Bennetts
* @brief A fancy wrapper for std::string supporting argument substitutions.
@@ -6,21 +6,21 @@
* $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$
*/
@@ -56,61 +56,61 @@
class LLUIString
{
public:
- // These methods all perform appropriate argument substitution
- // and modify mOrig where appropriate
- LLUIString() : mArgs(NULL), mNeedsResult(false), mNeedsWResult(false) {}
- LLUIString(const std::string& instring, const LLStringUtil::format_map_t& args);
- LLUIString(const std::string& instring) : mArgs(NULL) { assign(instring); }
- LLUIString(const LLWString& instring) : mArgs(NULL) { insert(0, instring); }
- ~LLUIString() { delete mArgs; }
+ // These methods all perform appropriate argument substitution
+ // and modify mOrig where appropriate
+ LLUIString() : mArgs(NULL), mNeedsResult(false), mNeedsWResult(false) {}
+ LLUIString(const std::string& instring, const LLStringUtil::format_map_t& args);
+ LLUIString(const std::string& instring) : mArgs(NULL) { assign(instring); }
+ LLUIString(const LLWString& instring) : mArgs(NULL) { insert(0, instring); }
+ ~LLUIString() { delete mArgs; }
- void assign(const std::string& instring);
- LLUIString& operator=(const std::string& s) { assign(s); return *this; }
+ void assign(const std::string& instring);
+ LLUIString& operator=(const std::string& s) { assign(s); return *this; }
- void setArgList(const LLStringUtil::format_map_t& args);
- void setArgs(const LLStringUtil::format_map_t& args) { setArgList(args); }
- void setArgs(const class LLSD& sd);
- void setArg(const std::string& key, const std::string& replacement);
+ void setArgList(const LLStringUtil::format_map_t& args);
+ void setArgs(const LLStringUtil::format_map_t& args) { setArgList(args); }
+ void setArgs(const class LLSD& sd);
+ void setArg(const std::string& key, const std::string& replacement);
- const std::string& getString() const { return getUpdatedResult(); }
- operator std::string() const { return getUpdatedResult(); }
+ const std::string& getString() const { return getUpdatedResult(); }
+ operator std::string() const { return getUpdatedResult(); }
- const LLWString& getWString() const { return getUpdatedWResult(); }
- operator LLWString() const { return getUpdatedWResult(); }
+ const LLWString& getWString() const { return getUpdatedWResult(); }
+ operator LLWString() const { return getUpdatedWResult(); }
- bool empty() const { return getUpdatedResult().empty(); }
- S32 length() const { return getUpdatedWResult().size(); }
+ bool empty() const { return getUpdatedResult().empty(); }
+ S32 length() const { return getUpdatedWResult().size(); }
- void clear();
- void clearArgs() { if (mArgs) mArgs->clear(); }
+ void clear();
+ void clearArgs() { if (mArgs) mArgs->clear(); }
- // These utility functions are included for text editing.
- // They do not affect mOrig and do not perform argument substitution
- void truncate(S32 maxchars);
- void erase(S32 charidx, S32 len);
- void insert(S32 charidx, const LLWString& wchars);
- void replace(S32 charidx, llwchar wc);
+ // These utility functions are included for text editing.
+ // They do not affect mOrig and do not perform argument substitution
+ void truncate(S32 maxchars);
+ void erase(S32 charidx, S32 len);
+ void insert(S32 charidx, const LLWString& wchars);
+ void replace(S32 charidx, llwchar wc);
private:
- // something changed, requiring reformatting of strings
- void dirty();
+ // something changed, requiring reformatting of strings
+ void dirty();
- std::string& getUpdatedResult() const { if (mNeedsResult) { updateResult(); } return mResult; }
- LLWString& getUpdatedWResult() const{ if (mNeedsWResult) { updateWResult(); } return mWResult; }
+ std::string& getUpdatedResult() const { if (mNeedsResult) { updateResult(); } return mResult; }
+ LLWString& getUpdatedWResult() const{ if (mNeedsWResult) { updateWResult(); } return mWResult; }
- // do actual work of updating strings (non-inlined)
- void updateResult() const;
- void updateWResult() const;
- LLStringUtil::format_map_t& getArgs();
+ // do actual work of updating strings (non-inlined)
+ void updateResult() const;
+ void updateWResult() const;
+ LLStringUtil::format_map_t& getArgs();
- std::string mOrig;
- mutable std::string mResult;
- mutable LLWString mWResult; // for displaying
- LLStringUtil::format_map_t* mArgs;
+ std::string mOrig;
+ mutable std::string mResult;
+ mutable LLWString mWResult; // for displaying
+ LLStringUtil::format_map_t* mArgs;
- // controls lazy evaluation
- mutable bool mNeedsResult;
- mutable bool mNeedsWResult;
+ // controls lazy evaluation
+ mutable bool mNeedsResult;
+ mutable bool mNeedsWResult;
};
#endif // LL_LLUISTRING_H
diff --git a/indra/llui/lluiusage.cpp b/indra/llui/lluiusage.cpp
index ccae6643b9..e617f60aba 100644
--- a/indra/llui/lluiusage.cpp
+++ b/indra/llui/lluiusage.cpp
@@ -39,108 +39,108 @@ LLUIUsage::~LLUIUsage()
// static
std::string LLUIUsage::sanitized(const std::string& s)
{
- // Remove characters that make the ViewerStats db unhappy
- std::string result(s);
- std::replace(result.begin(), result.end(), '.', '_');
- std::replace(result.begin(), result.end(), ' ', '_');
- return result;
+ // Remove characters that make the ViewerStats db unhappy
+ std::string result(s);
+ std::replace(result.begin(), result.end(), '.', '_');
+ std::replace(result.begin(), result.end(), ' ', '_');
+ return result;
}
// static
void LLUIUsage::setLLSDPath(LLSD& sd, const std::string& path, S32 max_elts, const LLSD& val)
{
- // Keep the last max_elts components of the specified path
- std::vector<std::string> fields;
- boost::split(fields, path, boost::is_any_of("/"));
- auto first_pos = std::max(fields.begin(), fields.end() - max_elts);
- auto end_pos = fields.end();
- std::vector<std::string> last_fields(first_pos,end_pos);
-
- setLLSDNested(sd, last_fields, val);
+ // Keep the last max_elts components of the specified path
+ std::vector<std::string> fields;
+ boost::split(fields, path, boost::is_any_of("/"));
+ auto first_pos = std::max(fields.begin(), fields.end() - max_elts);
+ auto end_pos = fields.end();
+ std::vector<std::string> last_fields(first_pos,end_pos);
+
+ setLLSDNested(sd, last_fields, val);
}
// setLLSDNested()
-// Accomplish the equivalent of
+// Accomplish the equivalent of
// sd[fields[0]][fields[1]]... = val;
// for an arbitrary number of fields.
// This might be useful as an LLSD utility function; is not specific to LLUIUsage
-//
+//
// static
void LLUIUsage::setLLSDNested(LLSD& sd, const std::vector<std::string>& fields, const LLSD& val)
{
- LLSD* fsd = &sd;
- for (auto it=fields.begin(); it!=fields.end(); ++it)
- {
- if (it == fields.end()-1)
- {
- (*fsd)[*it] = val;
- }
- else
- {
- if (!(*fsd)[*it].isMap())
- {
- (*fsd)[*it] = LLSD::emptyMap();
- }
- fsd = &(*fsd)[*it];
- }
- }
+ LLSD* fsd = &sd;
+ for (auto it=fields.begin(); it!=fields.end(); ++it)
+ {
+ if (it == fields.end()-1)
+ {
+ (*fsd)[*it] = val;
+ }
+ else
+ {
+ if (!(*fsd)[*it].isMap())
+ {
+ (*fsd)[*it] = LLSD::emptyMap();
+ }
+ fsd = &(*fsd)[*it];
+ }
+ }
}
void LLUIUsage::logCommand(const std::string& command)
{
- mCommandCounts[sanitized(command)]++;
- LL_DEBUGS("UIUsage") << "command " << command << LL_ENDL;
+ mCommandCounts[sanitized(command)]++;
+ LL_DEBUGS("UIUsage") << "command " << command << LL_ENDL;
}
void LLUIUsage::logControl(const std::string& control)
{
- mControlCounts[sanitized(control)]++;
- LL_DEBUGS("UIUsage") << "control " << control << LL_ENDL;
+ mControlCounts[sanitized(control)]++;
+ LL_DEBUGS("UIUsage") << "control " << control << LL_ENDL;
}
void LLUIUsage::logFloater(const std::string& floater)
{
- mFloaterCounts[sanitized(floater)]++;
- LL_DEBUGS("UIUsage") << "floater " << floater << LL_ENDL;
+ mFloaterCounts[sanitized(floater)]++;
+ LL_DEBUGS("UIUsage") << "floater " << floater << LL_ENDL;
}
void LLUIUsage::logPanel(const std::string& p)
{
- mPanelCounts[sanitized(p)]++;
- LL_DEBUGS("UIUsage") << "panel " << p << LL_ENDL;
+ mPanelCounts[sanitized(p)]++;
+ LL_DEBUGS("UIUsage") << "panel " << p << LL_ENDL;
}
LLSD LLUIUsage::asLLSD() const
{
- LLSD result;
- for (auto const& it : mCommandCounts)
- {
- result["commands"][it.first] = LLSD::Integer(it.second);
- }
- for (auto const& it : mControlCounts)
- {
- setLLSDPath(result["controls"], it.first, 2, LLSD::Integer(it.second));
- }
- for (auto const& it : mFloaterCounts)
- {
- result["floaters"][it.first] = LLSD::Integer(it.second);
- }
- for (auto const& it : mPanelCounts)
- {
- result["panels"][it.first] = LLSD::Integer(it.second);
- }
- return result;
+ LLSD result;
+ for (auto const& it : mCommandCounts)
+ {
+ result["commands"][it.first] = LLSD::Integer(it.second);
+ }
+ for (auto const& it : mControlCounts)
+ {
+ setLLSDPath(result["controls"], it.first, 2, LLSD::Integer(it.second));
+ }
+ for (auto const& it : mFloaterCounts)
+ {
+ result["floaters"][it.first] = LLSD::Integer(it.second);
+ }
+ for (auto const& it : mPanelCounts)
+ {
+ result["panels"][it.first] = LLSD::Integer(it.second);
+ }
+ return result;
}
// Clear up some junk content generated during initial login/UI initialization
void LLUIUsage::clear()
{
- LL_DEBUGS("UIUsage") << "clear" << LL_ENDL;
- mCommandCounts.clear();
- mControlCounts.clear();
- mFloaterCounts.clear();
- mPanelCounts.clear();
+ LL_DEBUGS("UIUsage") << "clear" << LL_ENDL;
+ mCommandCounts.clear();
+ mControlCounts.clear();
+ mFloaterCounts.clear();
+ mPanelCounts.clear();
}
diff --git a/indra/llui/lluiusage.h b/indra/llui/lluiusage.h
index a30cd80db3..13f1e44a33 100644
--- a/indra/llui/lluiusage.h
+++ b/indra/llui/lluiusage.h
@@ -35,23 +35,23 @@
class LLUIUsage : public LLSingleton<LLUIUsage>
{
public:
- LLSINGLETON(LLUIUsage);
- ~LLUIUsage();
+ LLSINGLETON(LLUIUsage);
+ ~LLUIUsage();
public:
- static std::string sanitized(const std::string& s);
- static void setLLSDPath(LLSD& sd, const std::string& path, S32 max_elts, const LLSD& val);
- static void setLLSDNested(LLSD& sd, const std::vector<std::string>& fields, const LLSD& val);
- void logCommand(const std::string& command);
- void logControl(const std::string& control);
- void logFloater(const std::string& floater);
- void logPanel(const std::string& p);
- LLSD asLLSD() const;
- void clear();
+ static std::string sanitized(const std::string& s);
+ static void setLLSDPath(LLSD& sd, const std::string& path, S32 max_elts, const LLSD& val);
+ static void setLLSDNested(LLSD& sd, const std::vector<std::string>& fields, const LLSD& val);
+ void logCommand(const std::string& command);
+ void logControl(const std::string& control);
+ void logFloater(const std::string& floater);
+ void logPanel(const std::string& p);
+ LLSD asLLSD() const;
+ void clear();
private:
- std::map<std::string,U32> mCommandCounts;
- std::map<std::string,U32> mControlCounts;
- std::map<std::string,U32> mFloaterCounts;
- std::map<std::string,U32> mPanelCounts;
+ std::map<std::string,U32> mCommandCounts;
+ std::map<std::string,U32> mControlCounts;
+ std::map<std::string,U32> mFloaterCounts;
+ std::map<std::string,U32> mPanelCounts;
};
#endif // LLUIUIUSAGE.h
diff --git a/indra/llui/llundo.cpp b/indra/llui/llundo.cpp
index 7a867357f8..94b250d51e 100644
--- a/indra/llui/llundo.cpp
+++ b/indra/llui/llundo.cpp
@@ -1,174 +1,174 @@
-/**
- * @file llundo.cpp
- *
- * $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 "llundo.h"
-
-
-// TODO:
-// implement doubly linked circular list for ring buffer
-// this will allow us to easily change the size of an undo buffer on the fly
-
-//-----------------------------------------------------------------------------
-// LLUndoBuffer()
-//-----------------------------------------------------------------------------
-LLUndoBuffer::LLUndoBuffer( LLUndoAction (*create_func()), S32 initial_count )
-{
- mNextAction = 0;
- mLastAction = 0;
- mFirstAction = 0;
- mOperationID = 0;
-
- mNumActions = initial_count;
-
- mActions = new LLUndoAction *[initial_count];
-
- //initialize buffer with actions
- for (S32 i = 0; i < initial_count; i++)
- {
- mActions[i] = create_func();
- if (!mActions[i])
- {
- LL_ERRS() << "Unable to create action for undo buffer" << LL_ENDL;
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// ~LLUndoBuffer()
-//-----------------------------------------------------------------------------
-LLUndoBuffer::~LLUndoBuffer()
-{
- for (S32 i = 0; i < mNumActions; i++)
- {
- delete mActions[i];
- }
-
- delete [] mActions;
-}
-
-//-----------------------------------------------------------------------------
-// getNextAction()
-//-----------------------------------------------------------------------------
-LLUndoBuffer::LLUndoAction* LLUndoBuffer::getNextAction(bool setClusterBegin)
-{
- LLUndoAction *nextAction = mActions[mNextAction];
-
- if (setClusterBegin)
- {
- mOperationID++;
- }
- mActions[mNextAction]->mClusterID = mOperationID;
-
- mNextAction = (mNextAction + 1) % mNumActions;
- mLastAction = mNextAction;
-
- if (mNextAction == mFirstAction)
- {
- mActions[mFirstAction]->cleanup();
- mFirstAction = (mFirstAction + 1) % mNumActions;
- }
-
- return nextAction;
-}
-
-//-----------------------------------------------------------------------------
-// undoAction()
-//-----------------------------------------------------------------------------
-bool LLUndoBuffer::undoAction()
-{
- if (!canUndo())
- {
- return false;
- }
-
- S32 prevAction = (mNextAction + mNumActions - 1) % mNumActions;
-
- while(mActions[prevAction]->mClusterID == mOperationID)
- {
- // go ahead and decrement action index
- mNextAction = prevAction;
-
- // undo this action
- mActions[mNextAction]->undo();
-
- // we're at the first action, so we don't know if we've actually undid everything
- if (mNextAction == mFirstAction)
- {
- mOperationID--;
- return false;
- }
-
- // do wrap-around of index, but avoid negative numbers for modulo operator
- prevAction = (mNextAction + mNumActions - 1) % mNumActions;
- }
-
- mOperationID--;
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// redoAction()
-//-----------------------------------------------------------------------------
-bool LLUndoBuffer::redoAction()
-{
- if (!canRedo())
- {
- return false;
- }
-
- mOperationID++;
-
- while(mActions[mNextAction]->mClusterID == mOperationID)
- {
- if (mNextAction == mLastAction)
- {
- return false;
- }
-
- mActions[mNextAction]->redo();
-
- // do wrap-around of index
- mNextAction = (mNextAction + 1) % mNumActions;
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// flushActions()
-//-----------------------------------------------------------------------------
-void LLUndoBuffer::flushActions()
-{
- for (S32 i = 0; i < mNumActions; i++)
- {
- mActions[i]->cleanup();
- }
- mNextAction = 0;
- mLastAction = 0;
- mFirstAction = 0;
- mOperationID = 0;
-}
+/**
+ * @file llundo.cpp
+ *
+ * $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 "llundo.h"
+
+
+// TODO:
+// implement doubly linked circular list for ring buffer
+// this will allow us to easily change the size of an undo buffer on the fly
+
+//-----------------------------------------------------------------------------
+// LLUndoBuffer()
+//-----------------------------------------------------------------------------
+LLUndoBuffer::LLUndoBuffer( LLUndoAction (*create_func()), S32 initial_count )
+{
+ mNextAction = 0;
+ mLastAction = 0;
+ mFirstAction = 0;
+ mOperationID = 0;
+
+ mNumActions = initial_count;
+
+ mActions = new LLUndoAction *[initial_count];
+
+ //initialize buffer with actions
+ for (S32 i = 0; i < initial_count; i++)
+ {
+ mActions[i] = create_func();
+ if (!mActions[i])
+ {
+ LL_ERRS() << "Unable to create action for undo buffer" << LL_ENDL;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// ~LLUndoBuffer()
+//-----------------------------------------------------------------------------
+LLUndoBuffer::~LLUndoBuffer()
+{
+ for (S32 i = 0; i < mNumActions; i++)
+ {
+ delete mActions[i];
+ }
+
+ delete [] mActions;
+}
+
+//-----------------------------------------------------------------------------
+// getNextAction()
+//-----------------------------------------------------------------------------
+LLUndoBuffer::LLUndoAction* LLUndoBuffer::getNextAction(bool setClusterBegin)
+{
+ LLUndoAction *nextAction = mActions[mNextAction];
+
+ if (setClusterBegin)
+ {
+ mOperationID++;
+ }
+ mActions[mNextAction]->mClusterID = mOperationID;
+
+ mNextAction = (mNextAction + 1) % mNumActions;
+ mLastAction = mNextAction;
+
+ if (mNextAction == mFirstAction)
+ {
+ mActions[mFirstAction]->cleanup();
+ mFirstAction = (mFirstAction + 1) % mNumActions;
+ }
+
+ return nextAction;
+}
+
+//-----------------------------------------------------------------------------
+// undoAction()
+//-----------------------------------------------------------------------------
+bool LLUndoBuffer::undoAction()
+{
+ if (!canUndo())
+ {
+ return false;
+ }
+
+ S32 prevAction = (mNextAction + mNumActions - 1) % mNumActions;
+
+ while(mActions[prevAction]->mClusterID == mOperationID)
+ {
+ // go ahead and decrement action index
+ mNextAction = prevAction;
+
+ // undo this action
+ mActions[mNextAction]->undo();
+
+ // we're at the first action, so we don't know if we've actually undid everything
+ if (mNextAction == mFirstAction)
+ {
+ mOperationID--;
+ return false;
+ }
+
+ // do wrap-around of index, but avoid negative numbers for modulo operator
+ prevAction = (mNextAction + mNumActions - 1) % mNumActions;
+ }
+
+ mOperationID--;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// redoAction()
+//-----------------------------------------------------------------------------
+bool LLUndoBuffer::redoAction()
+{
+ if (!canRedo())
+ {
+ return false;
+ }
+
+ mOperationID++;
+
+ while(mActions[mNextAction]->mClusterID == mOperationID)
+ {
+ if (mNextAction == mLastAction)
+ {
+ return false;
+ }
+
+ mActions[mNextAction]->redo();
+
+ // do wrap-around of index
+ mNextAction = (mNextAction + 1) % mNumActions;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// flushActions()
+//-----------------------------------------------------------------------------
+void LLUndoBuffer::flushActions()
+{
+ for (S32 i = 0; i < mNumActions; i++)
+ {
+ mActions[i]->cleanup();
+ }
+ mNextAction = 0;
+ mLastAction = 0;
+ mFirstAction = 0;
+ mOperationID = 0;
+}
diff --git a/indra/llui/llundo.h b/indra/llui/llundo.h
index f7ca6f66d2..41e8a2b2f4 100644
--- a/indra/llui/llundo.h
+++ b/indra/llui/llundo.h
@@ -1,68 +1,68 @@
-/**
- * @file llundo.h
- * @brief Generic interface for undo/redo circular buffer.
- *
- * $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_LLUNDO_H
-#define LL_LLUNDO_H
-
-
-class LLUndoBuffer
-{
-public:
- class LLUndoAction
- {
- friend class LLUndoBuffer;
- public:
- virtual void undo() = 0;
- virtual void redo() = 0;
- virtual void cleanup() {};
- protected:
- LLUndoAction(): mClusterID(0) {};
- virtual ~LLUndoAction(){};
- private:
- S32 mClusterID;
- };
-
- LLUndoBuffer( LLUndoAction (*create_func()), S32 initial_count );
- virtual ~LLUndoBuffer();
-
- LLUndoAction *getNextAction(bool setClusterBegin = true);
- bool undoAction();
- bool redoAction();
- bool canUndo() { return (mNextAction != mFirstAction); }
- bool canRedo() { return (mNextAction != mLastAction); }
-
- void flushActions();
-
-private:
- LLUndoAction **mActions; // array of pointers to undoactions
- S32 mNumActions; // total number of actions in ring buffer
- S32 mNextAction; // next action to perform undo/redo on
- S32 mLastAction; // last action actually added to undo buffer
- S32 mFirstAction; // beginning of ring buffer (don't undo any further)
- S32 mOperationID; // current operation id, for undoing and redoing in clusters
-};
-
-#endif //LL_LLUNDO_H
+/**
+ * @file llundo.h
+ * @brief Generic interface for undo/redo circular buffer.
+ *
+ * $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_LLUNDO_H
+#define LL_LLUNDO_H
+
+
+class LLUndoBuffer
+{
+public:
+ class LLUndoAction
+ {
+ friend class LLUndoBuffer;
+ public:
+ virtual void undo() = 0;
+ virtual void redo() = 0;
+ virtual void cleanup() {};
+ protected:
+ LLUndoAction(): mClusterID(0) {};
+ virtual ~LLUndoAction(){};
+ private:
+ S32 mClusterID;
+ };
+
+ LLUndoBuffer( LLUndoAction (*create_func()), S32 initial_count );
+ virtual ~LLUndoBuffer();
+
+ LLUndoAction *getNextAction(bool setClusterBegin = true);
+ bool undoAction();
+ bool redoAction();
+ bool canUndo() { return (mNextAction != mFirstAction); }
+ bool canRedo() { return (mNextAction != mLastAction); }
+
+ void flushActions();
+
+private:
+ LLUndoAction **mActions; // array of pointers to undoactions
+ S32 mNumActions; // total number of actions in ring buffer
+ S32 mNextAction; // next action to perform undo/redo on
+ S32 mLastAction; // last action actually added to undo buffer
+ S32 mFirstAction; // beginning of ring buffer (don't undo any further)
+ S32 mOperationID; // current operation id, for undoing and redoing in clusters
+};
+
+#endif //LL_LLUNDO_H
diff --git a/indra/llui/llurlaction.cpp b/indra/llui/llurlaction.cpp
index 8216046174..b6b450c2a1 100644
--- a/indra/llui/llurlaction.cpp
+++ b/indra/llui/llurlaction.cpp
@@ -1,4 +1,4 @@
-/**
+/**
* @file llurlaction.cpp
* @author Martin Reddy
* @brief A set of actions that can performed on Urls
@@ -6,21 +6,21 @@
* $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$
*/
@@ -33,193 +33,193 @@
// global state for the callback functions
-LLUrlAction::url_callback_t LLUrlAction::sOpenURLCallback;
-LLUrlAction::url_callback_t LLUrlAction::sOpenURLInternalCallback;
-LLUrlAction::url_callback_t LLUrlAction::sOpenURLExternalCallback;
+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(url_callback_t cb)
{
- sOpenURLCallback = cb;
+ sOpenURLCallback = cb;
}
void LLUrlAction::setOpenURLInternalCallback(url_callback_t cb)
{
- sOpenURLInternalCallback = cb;
+ sOpenURLInternalCallback = cb;
}
void LLUrlAction::setOpenURLExternalCallback(url_callback_t cb)
{
- sOpenURLExternalCallback = cb;
+ sOpenURLExternalCallback = cb;
}
void LLUrlAction::setExecuteSLURLCallback(execute_url_callback_t cb)
{
- sExecuteSLURLCallback = cb;
+ sExecuteSLURLCallback = cb;
}
void LLUrlAction::openURL(std::string url)
{
- if (sOpenURLCallback)
- {
- sOpenURLCallback(url);
- }
+ if (sOpenURLCallback)
+ {
+ sOpenURLCallback(url);
+ }
}
void LLUrlAction::openURLInternal(std::string url)
{
- if (sOpenURLInternalCallback)
- {
- sOpenURLInternalCallback(url);
- }
+ if (sOpenURLInternalCallback)
+ {
+ sOpenURLInternalCallback(url);
+ }
}
void LLUrlAction::openURLExternal(std::string url)
{
- if (sOpenURLExternalCallback)
- {
- sOpenURLExternalCallback(url);
- }
+ if (sOpenURLExternalCallback)
+ {
+ sOpenURLExternalCallback(url);
+ }
}
bool LLUrlAction::executeSLURL(std::string url, bool trusted_content)
{
- if (sExecuteSLURLCallback)
- {
- return sExecuteSLURLCallback(url, trusted_content);
- }
- return false;
+ if (sExecuteSLURLCallback)
+ {
+ return sExecuteSLURLCallback(url, trusted_content);
+ }
+ return false;
}
void LLUrlAction::clickAction(std::string url, bool trusted_content)
{
- // Try to handle as SLURL first, then http Url
- if ( (sExecuteSLURLCallback) && !sExecuteSLURLCallback(url, trusted_content) )
- {
- if (sOpenURLCallback)
- {
- sOpenURLCallback(url);
- }
- }
+ // Try to handle as SLURL first, then http Url
+ if ( (sExecuteSLURLCallback) && !sExecuteSLURLCallback(url, trusted_content) )
+ {
+ if (sOpenURLCallback)
+ {
+ sOpenURLCallback(url);
+ }
+ }
}
void LLUrlAction::teleportToLocation(std::string url)
{
- LLUrlMatch match;
- if (LLUrlRegistry::instance().findUrl(url, match))
- {
- if (! match.getLocation().empty())
- {
- executeSLURL("secondlife:///app/teleport/" + match.getLocation());
- }
- }
+ LLUrlMatch match;
+ if (LLUrlRegistry::instance().findUrl(url, match))
+ {
+ if (! match.getLocation().empty())
+ {
+ executeSLURL("secondlife:///app/teleport/" + match.getLocation());
+ }
+ }
}
void LLUrlAction::showLocationOnMap(std::string url)
{
- LLUrlMatch match;
- if (LLUrlRegistry::instance().findUrl(url, match))
- {
- if (! match.getLocation().empty())
- {
- executeSLURL("secondlife:///app/worldmap/" + match.getLocation());
- }
- }
+ LLUrlMatch match;
+ if (LLUrlRegistry::instance().findUrl(url, match))
+ {
+ if (! match.getLocation().empty())
+ {
+ executeSLURL("secondlife:///app/worldmap/" + match.getLocation());
+ }
+ }
}
void LLUrlAction::copyURLToClipboard(std::string url)
{
- LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(url));
+ LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(url));
}
void LLUrlAction::copyLabelToClipboard(std::string url)
{
- LLUrlMatch match;
- if (LLUrlRegistry::instance().findUrl(url, match))
- {
- LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(match.getLabel()));
- }
+ LLUrlMatch match;
+ if (LLUrlRegistry::instance().findUrl(url, match))
+ {
+ LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(match.getLabel()));
+ }
}
void LLUrlAction::showProfile(std::string url)
{
- // Get id from 'secondlife:///app/{cmd}/{id}/{action}'
- // and show its profile
- LLURI uri(url);
- LLSD path_array = uri.pathArray();
- if (path_array.size() == 4)
- {
- std::string id_str = path_array.get(2).asString();
- if (LLUUID::validate(id_str))
- {
- std::string cmd_str = path_array.get(1).asString();
- executeSLURL("secondlife:///app/" + cmd_str + "/" + id_str + "/about");
- }
- }
+ // Get id from 'secondlife:///app/{cmd}/{id}/{action}'
+ // and show its profile
+ LLURI uri(url);
+ LLSD path_array = uri.pathArray();
+ if (path_array.size() == 4)
+ {
+ std::string id_str = path_array.get(2).asString();
+ if (LLUUID::validate(id_str))
+ {
+ std::string cmd_str = path_array.get(1).asString();
+ executeSLURL("secondlife:///app/" + cmd_str + "/" + id_str + "/about");
+ }
+ }
}
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;
+ 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;
+ 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;
+ 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");
- }
+ 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");
- }
+ 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");
- }
+ std::string id_str = getUserID(url);
+ if (LLUUID::validate(id_str))
+ {
+ executeSLURL("secondlife:///app/agent/" + id_str + "/removefriend");
+ }
}
void LLUrlAction::reportAbuse(std::string url)
@@ -233,12 +233,12 @@ void LLUrlAction::reportAbuse(std::string url)
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/" + LLURI::escape(object_name));
- }
+ std::string object_id = getObjectId(url);
+ std::string object_name = getObjectName(url);
+ if (LLUUID::validate(object_id))
+ {
+ executeSLURL("secondlife:///app/agent/" + object_id + "/block/" + LLURI::escape(object_name));
+ }
}
void LLUrlAction::unblockObject(std::string url)
diff --git a/indra/llui/llurlaction.h b/indra/llui/llurlaction.h
index c2c576254d..0f54b66299 100644
--- a/indra/llui/llurlaction.h
+++ b/indra/llui/llurlaction.h
@@ -1,4 +1,4 @@
-/**
+/**
* @file llurlaction.h
* @author Martin Reddy
* @brief A set of actions that can performed on Urls
@@ -6,21 +6,21 @@
* $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$
*/
@@ -45,63 +45,63 @@
class LLUrlAction
{
public:
- LLUrlAction();
+ LLUrlAction();
- /// load a Url in the user's preferred web browser
- static void openURL(std::string url);
+ /// load a Url in the user's preferred web browser
+ static void openURL(std::string url);
- /// load a Url in the internal Second Life web browser
- static void openURLInternal(std::string url);
+ /// load a Url in the internal Second Life web browser
+ static void openURLInternal(std::string url);
- /// load a Url in the operating system's default web browser
- static void openURLExternal(std::string url);
+ /// load a Url in the operating system's default web browser
+ static void openURLExternal(std::string url);
- /// execute the given secondlife: SLURL
- static bool executeSLURL(std::string url, bool trusted_content = true);
+ /// execute the given secondlife: SLURL
+ static bool executeSLURL(std::string url, bool trusted_content = true);
- /// if the Url specifies an SL location, teleport there
- static void teleportToLocation(std::string url);
+ /// if the Url specifies an SL location, teleport there
+ static void teleportToLocation(std::string url);
- /// if the Url specifies an SL location, show it on a map
- static void showLocationOnMap(std::string url);
+ /// if the Url specifies an SL location, show it on a map
+ static void showLocationOnMap(std::string url);
- /// perform the appropriate action for left-clicking on a Url
- static void clickAction(std::string url, bool trusted_content);
+ /// perform the appropriate action for left-clicking on a 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);
+ /// copy the label for a Url to the clipboard
+ static void copyLabelToClipboard(std::string url);
- /// copy a Url to the clipboard
- static void copyURLToClipboard(std::string url);
+ /// copy a Url to the clipboard
+ static void copyURLToClipboard(std::string url);
- /// if the Url specifies an SL command in the form like 'app/{cmd}/{id}/*', show its profile
- static void showProfile(std::string url);
- 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);
+ /// 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 reportAbuse(std::string url);
- static void blockObject(std::string url);
- static void unblockObject(std::string url);
+ static void blockObject(std::string url);
+ static void unblockObject(std::string url);
- /// specify the callbacks to enable this class's functionality
- 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;
+ /// specify the callbacks to enable this class's functionality
+ 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);
+ 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 url_callback_t sOpenURLCallback;
- static url_callback_t sOpenURLInternalCallback;
- static url_callback_t sOpenURLExternalCallback;
+ // callbacks for operations we can perform on Urls
+ static url_callback_t sOpenURLCallback;
+ static url_callback_t sOpenURLInternalCallback;
+ static url_callback_t sOpenURLExternalCallback;
- static execute_url_callback_t sExecuteSLURLCallback;
+ static execute_url_callback_t sExecuteSLURLCallback;
};
#endif
diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp
index 45668e4a87..485dfd0abb 100644
--- a/indra/llui/llurlentry.cpp
+++ b/indra/llui/llurlentry.cpp
@@ -1,1738 +1,1738 @@
-/**
- * @file llurlentry.cpp
- * @author Martin Reddy
- * @brief Describes the Url types that can be registered in LLUrlRegistry
- *
- * $LicenseInfo:firstyear=2009&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "llurlentry.h"
-#include "lluictrl.h"
-#include "lluri.h"
-#include "llurlmatch.h"
-#include "llurlregistry.h"
-#include "lluriparser.h"
-
-#include "llavatarnamecache.h"
-#include "llcachename.h"
-#include "llkeyboard.h"
-#include "llregex.h"
-#include "llscrolllistctrl.h" // for LLUrlEntryKeybinding file parsing
-#include "lltrans.h"
-#include "lluicolortable.h"
-#include "message.h"
-#include "llexperiencecache.h"
-
-#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))"
-
-// Utility functions
-std::string localize_slapp_label(const std::string& url, const std::string& full_name);
-
-
-LLUrlEntryBase::LLUrlEntryBase()
-{
-}
-
-LLUrlEntryBase::~LLUrlEntryBase()
-{
-}
-
-std::string LLUrlEntryBase::getUrl(const std::string &string) const
-{
- return escapeUrl(string);
-}
-
-//virtual
-std::string LLUrlEntryBase::getIcon(const std::string &url)
-{
- return mIcon;
-}
-
-LLStyle::Params LLUrlEntryBase::getStyle() const
-{
- LLStyle::Params style_params;
- style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor");
- style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor");
- style_params.font.style = "UNDERLINE";
- return style_params;
-}
-
-
-std::string LLUrlEntryBase::getIDStringFromUrl(const std::string &url) const
-{
- // return the id from a SLURL in the format /app/{cmd}/{id}/about
- LLURI uri(url);
- LLSD path_array = uri.pathArray();
- if (path_array.size() == 4)
- {
- return path_array.get(2).asString();
- }
- return "";
-}
-
-std::string LLUrlEntryBase::unescapeUrl(const std::string &url) const
-{
- return LLURI::unescape(url);
-}
-
-std::string LLUrlEntryBase::escapeUrl(const std::string &url) const
-{
- static std::string no_escape_chars;
- static bool initialized = false;
- if (!initialized)
- {
- no_escape_chars =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz"
- "0123456789"
- "-._~!$?&()*+,@:;=/%#";
-
- std::sort(no_escape_chars.begin(), no_escape_chars.end());
- initialized = true;
- }
- return LLURI::escape(url, no_escape_chars, true);
-}
-
-std::string LLUrlEntryBase::getLabelFromWikiLink(const std::string &url) const
-{
- // return the label part from [http://www.example.org Label]
- const char *text = url.c_str();
- S32 start = 0;
- while (! isspace(text[start]))
- {
- start++;
- }
- while (text[start] == ' ' || text[start] == '\t')
- {
- start++;
- }
- return unescapeUrl(url.substr(start, url.size()-start-1));
-}
-
-std::string LLUrlEntryBase::getUrlFromWikiLink(const std::string &string) const
-{
- // return the url part from [http://www.example.org Label]
- const char *text = string.c_str();
- S32 end = 0;
- while (! isspace(text[end]))
- {
- end++;
- }
- return escapeUrl(string.substr(1, end-1));
-}
-
-void LLUrlEntryBase::addObserver(const std::string &id,
- const std::string &url,
- const LLUrlLabelCallback &cb)
-{
- // add a callback to be notified when we have a label for the uuid
- LLUrlEntryObserver observer;
- observer.url = url;
- observer.signal = new LLUrlLabelSignal();
- if (observer.signal)
- {
- observer.signal->connect(cb);
- mObservers.insert(std::pair<std::string, LLUrlEntryObserver>(id, observer));
- }
-}
-
-// *NOTE: See also LLUrlEntryAgent::callObservers()
-void LLUrlEntryBase::callObservers(const std::string &id,
- const std::string &label,
- const std::string &icon)
-{
- // notify all callbacks waiting on the given uuid
- typedef std::multimap<std::string, LLUrlEntryObserver>::iterator observer_it;
- std::pair<observer_it, observer_it> matching_range = mObservers.equal_range(id);
- for (observer_it it = matching_range.first; it != matching_range.second;)
- {
- // call the callback - give it the new label
- LLUrlEntryObserver &observer = it->second;
- (*observer.signal)(it->second.url, label, icon);
- // then remove the signal - we only need to call it once
- delete observer.signal;
- mObservers.erase(it++);
- }
-}
-
-/// is this a match for a URL that should not be hyperlinked?
-bool LLUrlEntryBase::isLinkDisabled() const
-{
- // this allows us to have a global setting to turn off text hyperlink highlighting/action
- static LLCachedControl<bool> globally_disabled(*LLUI::getInstance()->mSettingGroups["config"], "DisableTextHyperlinkActions", false);
-
- return globally_disabled;
-}
-
-bool LLUrlEntryBase::isWikiLinkCorrect(const std::string &labeled_url) const
-{
- LLWString wlabel = utf8str_to_wstring(getLabelFromWikiLink(labeled_url));
- wlabel.erase(std::remove(wlabel.begin(), wlabel.end(), L'\u200B'), wlabel.end());
-
- // Unicode URL validation, see SL-15243
- std::replace_if(wlabel.begin(),
- wlabel.end(),
- [](const llwchar &chr)
- {
- return (chr == L'\u2024') // "One Dot Leader"
- || (chr == L'\uFE52') // "Small Full Stop"
- || (chr == L'\uFF0E') // "Fullwidth Full Stop"
- // Not a decomposition, but suficiently similar
- || (chr == L'\u05C5'); // "Hebrew Mark Lower Dot"
- },
- L'\u002E'); // Dot "Full Stop"
-
- std::replace_if(wlabel.begin(),
- wlabel.end(),
- [](const llwchar &chr)
- {
- return (chr == L'\u02D0') // "Modifier Letter Colon"
- || (chr == L'\uFF1A') // "Fullwidth Colon"
- || (chr == L'\u2236') // "Ratio"
- || (chr == L'\uFE55'); // "Small Colon"
- },
- L'\u003A'); // Colon
-
- std::replace_if(wlabel.begin(),
- wlabel.end(),
- [](const llwchar &chr)
- {
- return (chr == L'\uFF0F'); // "Fullwidth Solidus"
- },
- L'\u002F'); // Solidus
-
- std::string label = wstring_to_utf8str(wlabel);
- if ((label.find(".com") != std::string::npos
- || label.find("www.") != std::string::npos)
- && label.find("://") == std::string::npos)
- {
- label = "http://" + label;
- }
-
- return !LLUrlRegistry::instance().hasUrl(label);
-}
-
-std::string LLUrlEntryBase::urlToLabelWithGreyQuery(const std::string &url) const
-{
- if (url.empty())
- {
- return url;
- }
- LLUriParser up(escapeUrl(url));
- if (up.normalize() == 0)
- {
- std::string label;
- up.extractParts();
- up.glueFirst(label);
-
- return unescapeUrl(label);
- }
- return std::string();
-}
-
-std::string LLUrlEntryBase::urlToGreyQuery(const std::string &url) const
-{
- std::string escaped_url = escapeUrl(url);
- LLUriParser up(escaped_url);
-
- std::string label;
- up.extractParts();
- up.glueFirst(label, false);
-
- size_t pos = escaped_url.find(label);
- if (pos == std::string::npos)
- {
- return "";
- }
- pos += label.size();
- return unescapeUrl(escaped_url.substr(pos));
-}
-
-
-static std::string getStringAfterToken(const std::string str, const std::string token)
-{
- size_t pos = str.find(token);
- if (pos == std::string::npos)
- {
- return "";
- }
-
- pos += token.size();
- return str.substr(pos, str.size() - pos);
-}
-
-//
-// LLUrlEntryHTTP Describes generic http: and https: Urls
-//
-LLUrlEntryHTTP::LLUrlEntryHTTP()
- : LLUrlEntryBase()
-{
- mPattern = boost::regex("https?://([^\\s/?\\.#]+\\.?)+\\.\\w+(:\\d+)?(/\\S*)?",
- boost::regex::perl|boost::regex::icase);
- mMenuName = "menu_url_http.xml";
- mTooltip = LLTrans::getString("TooltipHttpUrl");
-}
-
-std::string LLUrlEntryHTTP::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
-{
- return 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);
-}
-
-//
-// LLUrlEntryHTTP Describes generic http: and https: Urls with custom label
-// We use the wikipedia syntax of [http://www.example.org Text]
-//
-LLUrlEntryHTTPLabel::LLUrlEntryHTTPLabel()
-{
- mPattern = boost::regex("\\[https?://\\S+[ \t]+[^\\]]+\\]",
- boost::regex::perl|boost::regex::icase);
- mMenuName = "menu_url_http.xml";
- mTooltip = LLTrans::getString("TooltipHttpUrl");
-}
-
-std::string LLUrlEntryHTTPLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
-{
- std::string label = getLabelFromWikiLink(url);
- return (!LLUrlRegistry::instance().hasUrl(label)) ? label : getUrl(url);
-}
-
-std::string LLUrlEntryHTTPLabel::getTooltip(const std::string &string) const
-{
- return getUrl(string);
-}
-
-std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) const
-{
- return getUrlFromWikiLink(string);
-}
-
-LLUrlEntryInvalidSLURL::LLUrlEntryInvalidSLURL()
- : LLUrlEntryBase()
-{
- mPattern = boost::regex("(https?://(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
-//
-LLUrlEntrySLURL::LLUrlEntrySLURL()
-{
- // see http://slurl.com/about.php for details on the SLURL format
- mPattern = boost::regex("https?://(maps.secondlife.com|slurl.com)/secondlife/[^ /]+(/\\d+){0,3}(/?(\\?title|\\?img|\\?msg)=\\S*)?/?",
- boost::regex::perl|boost::regex::icase);
- mIcon = "Hand";
- mMenuName = "menu_url_slurl.xml";
- mTooltip = LLTrans::getString("TooltipSLURL");
-}
-
-std::string LLUrlEntrySLURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
-{
- //
- // we handle SLURLs in the following formats:
- // - http://slurl.com/secondlife/Place/X/Y/Z
- // - http://slurl.com/secondlife/Place/X/Y
- // - http://slurl.com/secondlife/Place/X
- // - http://slurl.com/secondlife/Place
- //
-
- LLURI uri(url);
- LLSD path_array = uri.pathArray();
- S32 path_parts = path_array.size();
- if (path_parts == 5)
- {
- // handle slurl with (X,Y,Z) coordinates
- std::string location = unescapeUrl(path_array[path_parts-4]);
- std::string x = path_array[path_parts-3];
- std::string y = path_array[path_parts-2];
- std::string z = path_array[path_parts-1];
- return location + " (" + x + "," + y + "," + z + ")";
- }
- else if (path_parts == 4)
- {
- // handle slurl with (X,Y) coordinates
- std::string location = unescapeUrl(path_array[path_parts-3]);
- std::string x = path_array[path_parts-2];
- std::string y = path_array[path_parts-1];
- return location + " (" + x + "," + y + ")";
- }
- else if (path_parts == 3)
- {
- // handle slurl with (X) coordinate
- std::string location = unescapeUrl(path_array[path_parts-2]);
- std::string x = path_array[path_parts-1];
- return location + " (" + x + ")";
- }
- else if (path_parts == 2)
- {
- // handle slurl with no coordinates
- std::string location = unescapeUrl(path_array[path_parts-1]);
- return location;
- }
-
- return url;
-}
-
-std::string LLUrlEntrySLURL::getLocation(const std::string &url) const
-{
- // return the part of the Url after slurl.com/secondlife/
- const std::string search_string = "/secondlife";
- size_t pos = url.find(search_string);
- if (pos == std::string::npos)
- {
- return "";
- }
-
- pos += search_string.size() + 1;
- return url.substr(pos, url.size() - pos);
-}
-
-//
-// LLUrlEntrySeconlifeURL Describes *secondlife.com/ *lindenlab.com/ *secondlifegrid.net/ and *tilia-inc.com/ urls to substitute icon 'hand.png' before link
-//
-LLUrlEntrySecondlifeURL::LLUrlEntrySecondlifeURL()
-{
- mPattern = boost::regex("((http://([-\\w\\.]*\\.)?(secondlife|lindenlab|tilia-inc)\\.com)"
- "|"
- "(http://([-\\w\\.]*\\.)?secondlifegrid\\.net)"
- "|"
- "(https://([-\\w\\.]*\\.)?(secondlife|lindenlab|tilia-inc)\\.com(:\\d{1,5})?)"
- "|"
- "(https://([-\\w\\.]*\\.)?secondlifegrid\\.net(:\\d{1,5})?)"
- "|"
- "(https?://([-\\w\\.]*\\.)?secondlife\\.io(:\\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 *lindenlab.com *secondlifegrid.net and *tilia-inc.com urls to substitute icon 'hand.png' before link
-//
-LLUrlEntrySimpleSecondlifeURL::LLUrlEntrySimpleSecondlifeURL()
- {
- mPattern = boost::regex("https?://([-\\w\\.]*\\.)?(secondlife|lindenlab|tilia-inc)\\.com(?!\\S)"
- "|"
- "https?://([-\\w\\.]*\\.)?secondlifegrid\\.net(?!\\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()
-{
- mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/\\w+",
- boost::regex::perl|boost::regex::icase);
- mMenuName = "menu_url_agent.xml";
- mIcon = "Generic_Person";
-}
-
-// virtual
-void LLUrlEntryAgent::callObservers(const std::string &id,
- const std::string &label,
- const std::string &icon)
-{
- // notify all callbacks waiting on the given uuid
- typedef std::multimap<std::string, LLUrlEntryObserver>::iterator observer_it;
- std::pair<observer_it, observer_it> matching_range = mObservers.equal_range(id);
- for (observer_it it = matching_range.first; it != matching_range.second;)
- {
- // call the callback - give it the new label
- LLUrlEntryObserver &observer = it->second;
- std::string final_label = localize_slapp_label(observer.url, label);
- (*observer.signal)(observer.url, final_label, icon);
- // then remove the signal - we only need to call it once
- delete observer.signal;
- mObservers.erase(it++);
- }
-}
-
-void LLUrlEntryAgent::onAvatarNameCache(const LLUUID& id,
- const LLAvatarName& av_name)
-{
- avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(id);
- if (it != mAvatarNameCacheConnections.end())
- {
- if (it->second.connected())
- {
- it->second.disconnect();
- }
- mAvatarNameCacheConnections.erase(it);
- }
-
- std::string label = av_name.getCompleteName();
-
- // received the agent name from the server - tell our observers
- callObservers(id.asString(), label, mIcon);
-}
-
-LLUUID LLUrlEntryAgent::getID(const std::string &string) const
-{
- return LLUUID(getIDStringFromUrl(string));
-}
-
-std::string LLUrlEntryAgent::getTooltip(const std::string &string) const
-{
- // return a tooltip corresponding to the URL type instead of the generic one
- std::string url = getUrl(string);
-
- if (LLStringUtil::endsWith(url, "/inspect"))
- {
- return LLTrans::getString("TooltipAgentInspect");
- }
- if (LLStringUtil::endsWith(url, "/mute"))
- {
- return LLTrans::getString("TooltipAgentMute");
- }
- if (LLStringUtil::endsWith(url, "/unmute"))
- {
- return LLTrans::getString("TooltipAgentUnmute");
- }
- if (LLStringUtil::endsWith(url, "/im"))
- {
- return LLTrans::getString("TooltipAgentIM");
- }
- if (LLStringUtil::endsWith(url, "/pay"))
- {
- return LLTrans::getString("TooltipAgentPay");
- }
- if (LLStringUtil::endsWith(url, "/offerteleport"))
- {
- return LLTrans::getString("TooltipAgentOfferTeleport");
- }
- if (LLStringUtil::endsWith(url, "/requestfriend"))
- {
- return LLTrans::getString("TooltipAgentRequestFriend");
- }
- return LLTrans::getString("TooltipAgentUrl");
-}
-
-bool LLUrlEntryAgent::underlineOnHoverOnly(const std::string &string) const
-{
- std::string url = getUrl(string);
- return LLStringUtil::endsWith(url, "/about") || LLStringUtil::endsWith(url, "/inspect");
-}
-
-std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
-{
- if (!gCacheName)
- {
- // probably at the login screen, use short string for layout
- return LLTrans::getString("LoadingData");
- }
-
- std::string agent_id_string = getIDStringFromUrl(url);
- if (agent_id_string.empty())
- {
- // something went wrong, just give raw url
- return unescapeUrl(url);
- }
-
- LLUUID agent_id(agent_id_string);
- if (agent_id.isNull())
- {
- return LLTrans::getString("AvatarNameNobody");
- }
-
- LLAvatarName av_name;
- if (LLAvatarNameCache::get(agent_id, &av_name))
- {
- std::string label = av_name.getCompleteName();
-
- // handle suffixes like /mute or /offerteleport
- label = localize_slapp_label(url, label);
- return label;
- }
- else
- {
- avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(agent_id);
- if (it != mAvatarNameCacheConnections.end())
- {
- if (it->second.connected())
- {
- it->second.disconnect();
- }
- mAvatarNameCacheConnections.erase(it);
- }
- mAvatarNameCacheConnections[agent_id] = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgent::onAvatarNameCache, this, _1, _2));
-
- addObserver(agent_id_string, url, cb);
- return LLTrans::getString("LoadingData");
- }
-}
-
-LLStyle::Params LLUrlEntryAgent::getStyle() const
-{
- LLStyle::Params style_params = LLUrlEntryBase::getStyle();
- style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor");
- style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor");
- return style_params;
-}
-
-std::string localize_slapp_label(const std::string& url, const std::string& full_name)
-{
- // customize label string based on agent SLapp suffix
- if (LLStringUtil::endsWith(url, "/mute"))
- {
- return LLTrans::getString("SLappAgentMute") + " " + full_name;
- }
- if (LLStringUtil::endsWith(url, "/unmute"))
- {
- return LLTrans::getString("SLappAgentUnmute") + " " + full_name;
- }
- if (LLStringUtil::endsWith(url, "/im"))
- {
- return LLTrans::getString("SLappAgentIM") + " " + full_name;
- }
- if (LLStringUtil::endsWith(url, "/pay"))
- {
- return LLTrans::getString("SLappAgentPay") + " " + full_name;
- }
- if (LLStringUtil::endsWith(url, "/offerteleport"))
- {
- return LLTrans::getString("SLappAgentOfferTeleport") + " " + full_name;
- }
- if (LLStringUtil::endsWith(url, "/requestfriend"))
- {
- return LLTrans::getString("SLappAgentRequestFriend") + " " + full_name;
- }
- if (LLStringUtil::endsWith(url, "/removefriend"))
- {
- return LLTrans::getString("SLappAgentRemoveFriend") + " " + full_name;
- }
- return full_name;
-}
-
-
-std::string LLUrlEntryAgent::getIcon(const std::string &url)
-{
- // *NOTE: Could look up a badge here by calling getIDStringFromUrl()
- // and looking up the badge for the agent.
- return mIcon;
-}
-
-//
-// LLUrlEntryAgentName describes a Second Life agent name Url, e.g.,
-// 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()
-{}
-
-void LLUrlEntryAgentName::onAvatarNameCache(const LLUUID& id,
- const LLAvatarName& av_name)
-{
- avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(id);
- if (it != mAvatarNameCacheConnections.end())
- {
- if (it->second.connected())
- {
- it->second.disconnect();
- }
- mAvatarNameCacheConnections.erase(it);
- }
-
- std::string label = getName(av_name);
- // received the agent name from the server - tell our observers
- callObservers(id.asString(), label, mIcon);
-}
-
-std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
-{
- if (!gCacheName)
- {
- // probably at the login screen, use short string for layout
- return LLTrans::getString("LoadingData");
- }
-
- std::string agent_id_string = getIDStringFromUrl(url);
- if (agent_id_string.empty())
- {
- // something went wrong, just give raw url
- return unescapeUrl(url);
- }
-
- LLUUID agent_id(agent_id_string);
- if (agent_id.isNull())
- {
- return LLTrans::getString("AvatarNameNobody");
- }
-
- LLAvatarName av_name;
- if (LLAvatarNameCache::get(agent_id, &av_name))
- {
- return getName(av_name);
- }
- else
- {
- avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(agent_id);
- if (it != mAvatarNameCacheConnections.end())
- {
- if (it->second.connected())
- {
- it->second.disconnect();
- }
- mAvatarNameCacheConnections.erase(it);
- }
- mAvatarNameCacheConnections[agent_id] = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgentName::onAvatarNameCache, this, _1, _2));
-
- addObserver(agent_id_string, url, cb);
- return LLTrans::getString("LoadingData");
- }
-}
-
-LLStyle::Params LLUrlEntryAgentName::getStyle() const
-{
- // don't override default colors
- return LLStyle::Params().is_link(false);
-}
-
-//
-// LLUrlEntryAgentCompleteName describes a Second Life agent complete name Url, e.g.,
-// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename
-// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename
-//
-LLUrlEntryAgentCompleteName::LLUrlEntryAgentCompleteName()
-{
- mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/completename",
- boost::regex::perl|boost::regex::icase);
-}
-
-std::string LLUrlEntryAgentCompleteName::getName(const LLAvatarName& avatar_name)
-{
- return avatar_name.getCompleteName(true, true);
-}
-
-//
-// LLUrlEntryAgentLegacyName describes a Second Life agent legacy name Url, e.g.,
-// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/legacyname
-// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/legacyname
-//
-LLUrlEntryAgentLegacyName::LLUrlEntryAgentLegacyName()
-{
- mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/legacyname",
- boost::regex::perl|boost::regex::icase);
-}
-
-std::string LLUrlEntryAgentLegacyName::getName(const LLAvatarName& avatar_name)
-{
- return avatar_name.getLegacyName();
-}
-
-//
-// LLUrlEntryAgentDisplayName describes a Second Life agent display name Url, e.g.,
-// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname
-// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname
-//
-LLUrlEntryAgentDisplayName::LLUrlEntryAgentDisplayName()
-{
- mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/displayname",
- boost::regex::perl|boost::regex::icase);
-}
-
-std::string LLUrlEntryAgentDisplayName::getName(const LLAvatarName& avatar_name)
-{
- return avatar_name.getDisplayName(true);
-}
-
-//
-// LLUrlEntryAgentUserName describes a Second Life agent user name Url, e.g.,
-// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username
-// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username
-//
-LLUrlEntryAgentUserName::LLUrlEntryAgentUserName()
-{
- mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/username",
- boost::regex::perl|boost::regex::icase);
-}
-
-std::string LLUrlEntryAgentUserName::getName(const LLAvatarName& avatar_name)
-{
- return avatar_name.getAccountName();
-}
-
-//
-// LLUrlEntryGroup Describes a Second Life group Url, e.g.,
-// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about
-// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect
-// x-grid-location-info://lincoln.lindenlab.com/app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect
-//
-LLUrlEntryGroup::LLUrlEntryGroup()
-{
- mPattern = boost::regex(APP_HEADER_REGEX "/group/[\\da-f-]+/\\w+",
- boost::regex::perl|boost::regex::icase);
- mMenuName = "menu_url_group.xml";
- mIcon = "Generic_Group";
- mTooltip = LLTrans::getString("TooltipGroupUrl");
-}
-
-
-
-void LLUrlEntryGroup::onGroupNameReceived(const LLUUID& id,
- const std::string& name,
- bool is_group)
-{
- // received the group name from the server - tell our observers
- callObservers(id.asString(), name, mIcon);
-}
-
-LLUUID LLUrlEntryGroup::getID(const std::string &string) const
-{
- return LLUUID(getIDStringFromUrl(string));
-}
-
-
-std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
-{
- if (!gCacheName)
- {
- // probably at login screen, give something short for layout
- return LLTrans::getString("LoadingData");
- }
-
- std::string group_id_string = getIDStringFromUrl(url);
- if (group_id_string.empty())
- {
- // something went wrong, give raw url
- return unescapeUrl(url);
- }
-
- LLUUID group_id(group_id_string);
- std::string group_name;
- if (group_id.isNull())
- {
- return LLTrans::getString("GroupNameNone");
- }
- else if (gCacheName->getGroupName(group_id, group_name))
- {
- return group_name;
- }
- else
- {
- gCacheName->getGroup(group_id,
- boost::bind(&LLUrlEntryGroup::onGroupNameReceived,
- this, _1, _2, _3));
- addObserver(group_id_string, url, cb);
- return LLTrans::getString("LoadingData");
- }
-}
-
-LLStyle::Params LLUrlEntryGroup::getStyle() const
-{
- LLStyle::Params style_params = LLUrlEntryBase::getStyle();
- style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor");
- style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor");
- return style_params;
-}
-
-
-//
-// LLUrlEntryInventory Describes a Second Life inventory Url, e.g.,
-// secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select
-//
-LLUrlEntryInventory::LLUrlEntryInventory()
-{
- //*TODO: add supporting of inventory item names with whitespaces
- //this pattern cann't parse for example
- //secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces&param2=value
- //x-grid-location-info://lincoln.lindenlab.com/app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces&param2=value
- mPattern = boost::regex(APP_HEADER_REGEX "/inventory/[\\da-f-]+/\\w+\\S*",
- boost::regex::perl|boost::regex::icase);
- mMenuName = "menu_url_inventory.xml";
-}
-
-std::string LLUrlEntryInventory::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
-{
- std::string label = getStringAfterToken(url, "name=");
- return LLURI::unescape(label.empty() ? url : label);
-}
-
-//
-// LLUrlEntryObjectIM Describes a Second Life inspector for the object Url, e.g.,
-// secondlife:///app/objectim/7bcd7864-da6b-e43f-4486-91d28a28d95b?name=Object&owner=3de548e1-57be-cfea-2b78-83ae3ad95998&slurl=Danger!%20Danger!/200/200/30/&groupowned=1
-//
-LLUrlEntryObjectIM::LLUrlEntryObjectIM()
-{
- mPattern = boost::regex("secondlife:///app/objectim/[\\da-f-]+\?\\S*\\w",
- boost::regex::perl|boost::regex::icase);
- mMenuName = "menu_url_objectim.xml";
-}
-
-std::string LLUrlEntryObjectIM::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
-{
- LLURI uri(url);
- LLSD query_map = uri.queryMap();
- if (query_map.has("name"))
- return query_map["name"];
- return unescapeUrl(url);
-}
-
-std::string LLUrlEntryObjectIM::getLocation(const std::string &url) const
-{
- LLURI uri(url);
- LLSD query_map = uri.queryMap();
- if (query_map.has("slurl"))
- return query_map["slurl"];
- return LLUrlEntryBase::getLocation(url);
-}
-
-//
-// LLUrlEntryChat Describes a Second Life chat Url, e.g.,
-// secondlife:///app/chat/42/This%20Is%20a%20test
-//
-
-LLUrlEntryChat::LLUrlEntryChat()
-{
- mPattern = boost::regex("secondlife:///app/chat/\\d+/\\S+",
- boost::regex::perl|boost::regex::icase);
- mMenuName = "menu_url_slapp.xml";
- mTooltip = LLTrans::getString("TooltipSLAPP");
-}
-
-std::string LLUrlEntryChat::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
-{
- return unescapeUrl(url);
-}
-
-// LLUrlEntryParcel statics.
-LLUUID LLUrlEntryParcel::sAgentID(LLUUID::null);
-LLUUID LLUrlEntryParcel::sSessionID(LLUUID::null);
-LLHost LLUrlEntryParcel::sRegionHost;
-bool LLUrlEntryParcel::sDisconnected(false);
-std::set<LLUrlEntryParcel*> LLUrlEntryParcel::sParcelInfoObservers;
-
-///
-/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
-/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
-/// x-grid-location-info://lincoln.lindenlab.com/app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
-///
-LLUrlEntryParcel::LLUrlEntryParcel()
-{
- mPattern = boost::regex(APP_HEADER_REGEX "/parcel/[\\da-f-]+/about",
- boost::regex::perl|boost::regex::icase);
- mMenuName = "menu_url_parcel.xml";
- mTooltip = LLTrans::getString("TooltipParcelUrl");
-
- sParcelInfoObservers.insert(this);
-}
-
-LLUrlEntryParcel::~LLUrlEntryParcel()
-{
- sParcelInfoObservers.erase(this);
-}
-
-std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
-{
- LLSD path_array = LLURI(url).pathArray();
- S32 path_parts = path_array.size();
-
- if (path_parts < 3) // no parcel id
- {
- LL_WARNS() << "Failed to parse url [" << url << "]" << LL_ENDL;
- return url;
- }
-
- std::string parcel_id_string = unescapeUrl(path_array[2]); // parcel id
-
- // Add an observer to call LLUrlLabelCallback when we have parcel name.
- addObserver(parcel_id_string, url, cb);
-
- LLUUID parcel_id(parcel_id_string);
-
- sendParcelInfoRequest(parcel_id);
-
- return unescapeUrl(url);
-}
-
-void LLUrlEntryParcel::sendParcelInfoRequest(const LLUUID& parcel_id)
-{
- if (sRegionHost.isInvalid() || sDisconnected) return;
-
- LLMessageSystem *msg = gMessageSystem;
- msg->newMessage("ParcelInfoRequest");
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, sAgentID );
- msg->addUUID("SessionID", sSessionID);
- msg->nextBlock("Data");
- msg->addUUID("ParcelID", parcel_id);
- msg->sendReliable(sRegionHost);
-}
-
-void LLUrlEntryParcel::onParcelInfoReceived(const std::string &id, const std::string &label)
-{
- callObservers(id, label.empty() ? LLTrans::getString("RegionInfoError") : label, mIcon);
-}
-
-// static
-void LLUrlEntryParcel::processParcelInfo(const LLParcelData& parcel_data)
-{
- std::string label(LLStringUtil::null);
- if (!parcel_data.name.empty())
- {
- label = parcel_data.name;
- }
- // If parcel name is empty use Sim_name (x, y, z) for parcel label.
- else if (!parcel_data.sim_name.empty())
- {
- 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);
- }
-
- for (std::set<LLUrlEntryParcel*>::iterator iter = sParcelInfoObservers.begin();
- iter != sParcelInfoObservers.end();
- ++iter)
- {
- LLUrlEntryParcel* url_entry = *iter;
- if (url_entry)
- {
- url_entry->onParcelInfoReceived(parcel_data.parcel_id.asString(), label);
- }
- }
-}
-
-//
-// LLUrlEntryPlace Describes secondlife://<location> URLs
-//
-LLUrlEntryPlace::LLUrlEntryPlace()
-{
- mPattern = boost::regex("((x-grid-location-info://[-\\w\\.]+/region/)|(secondlife://))\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?",
- boost::regex::perl|boost::regex::icase);
- mMenuName = "menu_url_slurl.xml";
- mTooltip = LLTrans::getString("TooltipSLURL");
-}
-
-std::string LLUrlEntryPlace::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
-{
- //
- // we handle SLURLs in the following formats:
- // - secondlife://Place/X/Y/Z
- // - secondlife://Place/X/Y
- //
- LLURI uri(url);
- std::string location = unescapeUrl(uri.hostName());
- LLSD path_array = uri.pathArray();
- S32 path_parts = path_array.size();
- if (path_parts == 3)
- {
- // handle slurl with (X,Y,Z) coordinates
- std::string x = path_array[0];
- std::string y = path_array[1];
- std::string z = path_array[2];
- return location + " (" + x + "," + y + "," + z + ")";
- }
- else if (path_parts == 2)
- {
- // handle slurl with (X,Y) coordinates
- std::string x = path_array[0];
- std::string y = path_array[1];
- return location + " (" + x + "," + y + ")";
- }
-
- return url;
-}
-
-std::string LLUrlEntryPlace::getLocation(const std::string &url) const
-{
- // return the part of the Url after secondlife:// part
- return ::getStringAfterToken(url, "://");
-}
-
-//
-// LLUrlEntryRegion Describes secondlife:///app/region/REGION_NAME/X/Y/Z URLs, e.g.
-// secondlife:///app/region/Ahern/128/128/0
-//
-LLUrlEntryRegion::LLUrlEntryRegion()
-{
- mPattern = boost::regex("secondlife:///app/region/[A-Za-z0-9()_%]+(/\\d+)?(/\\d+)?(/\\d+)?/?",
- boost::regex::perl|boost::regex::icase);
- mMenuName = "menu_url_slurl.xml";
- mTooltip = LLTrans::getString("TooltipSLURL");
-}
-
-std::string LLUrlEntryRegion::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
-{
- //
- // we handle SLURLs in the following formats:
- // - secondlife:///app/region/Place/X/Y/Z
- // - secondlife:///app/region/Place/X/Y
- // - secondlife:///app/region/Place/X
- // - secondlife:///app/region/Place
- //
-
- LLSD path_array = LLURI(url).pathArray();
- S32 path_parts = path_array.size();
-
- if (path_parts < 3) // no region name
- {
- LL_WARNS() << "Failed to parse url [" << url << "]" << LL_ENDL;
- return url;
- }
-
- std::string label = unescapeUrl(path_array[2]); // region name
-
- if (path_parts > 3) // secondlife:///app/region/Place/X
- {
- std::string x = path_array[3];
- label += " (" + x;
-
- if (path_parts > 4) // secondlife:///app/region/Place/X/Y
- {
- std::string y = path_array[4];
- label += "," + y;
-
- if (path_parts > 5) // secondlife:///app/region/Place/X/Y/Z
- {
- std::string z = path_array[5];
- label = label + "," + z;
- }
- }
-
- label += ")";
- }
-
- return label;
-}
-
-std::string LLUrlEntryRegion::getLocation(const std::string &url) const
-{
- LLSD path_array = LLURI(url).pathArray();
- std::string region_name = unescapeUrl(path_array[2]);
- return region_name;
-}
-
-//
-// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g.,
-// secondlife:///app/teleport/Ahern/50/50/50/
-// x-grid-location-info://lincoln.lindenlab.com/app/teleport/Ahern/50/50/50/
-//
-LLUrlEntryTeleport::LLUrlEntryTeleport()
-{
- mPattern = boost::regex(APP_HEADER_REGEX "/teleport/\\S+(/\\d+)?(/\\d+)?(/\\d+)?/?\\S*",
- boost::regex::perl|boost::regex::icase);
- mMenuName = "menu_url_teleport.xml";
- mTooltip = LLTrans::getString("TooltipTeleportUrl");
-}
-
-std::string LLUrlEntryTeleport::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
-{
- //
- // we handle teleport SLURLs in the following formats:
- // - secondlife:///app/teleport/Place/X/Y/Z
- // - secondlife:///app/teleport/Place/X/Y
- // - secondlife:///app/teleport/Place/X
- // - secondlife:///app/teleport/Place
- //
- LLURI uri(url);
- LLSD path_array = uri.pathArray();
- S32 path_parts = path_array.size();
- std::string host = uri.hostName();
- std::string label = LLTrans::getString("SLurlLabelTeleport");
- if (!host.empty())
- {
- label += " " + host;
- }
- if (path_parts == 6)
- {
- // handle teleport url with (X,Y,Z) coordinates
- std::string location = unescapeUrl(path_array[path_parts-4]);
- std::string x = path_array[path_parts-3];
- std::string y = path_array[path_parts-2];
- std::string z = path_array[path_parts-1];
- return label + " " + location + " (" + x + "," + y + "," + z + ")";
- }
- else if (path_parts == 5)
- {
- // handle teleport url with (X,Y) coordinates
- std::string location = unescapeUrl(path_array[path_parts-3]);
- std::string x = path_array[path_parts-2];
- std::string y = path_array[path_parts-1];
- return label + " " + location + " (" + x + "," + y + ")";
- }
- else if (path_parts == 4)
- {
- // handle teleport url with (X) coordinate only
- std::string location = unescapeUrl(path_array[path_parts-2]);
- std::string x = path_array[path_parts-1];
- return label + " " + location + " (" + x + ")";
- }
- else if (path_parts == 3)
- {
- // handle teleport url with no coordinates
- std::string location = unescapeUrl(path_array[path_parts-1]);
- return label + " " + location;
- }
-
- return url;
-}
-
-std::string LLUrlEntryTeleport::getLocation(const std::string &url) const
-{
- // return the part of the Url after ///app/teleport
- return ::getStringAfterToken(url, "app/teleport/");
-}
-
-//
-// LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts
-// with secondlife:// (used as a catch-all for cases not matched above)
-//
-LLUrlEntrySL::LLUrlEntrySL()
-{
- mPattern = boost::regex("secondlife://(\\w+)?(:\\d+)?/\\S+",
- boost::regex::perl|boost::regex::icase);
- mMenuName = "menu_url_slapp.xml";
- mTooltip = LLTrans::getString("TooltipSLAPP");
-}
-
-std::string LLUrlEntrySL::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
-{
- return unescapeUrl(url);
-}
-
-//
-// LLUrlEntrySLLabel Describes a generic SLURL, e.g., a Url that starts
-/// with secondlife:// with the ability to specify a custom label.
-//
-LLUrlEntrySLLabel::LLUrlEntrySLLabel()
-{
- mPattern = boost::regex("\\[secondlife://\\S+[ \t]+[^\\]]+\\]",
- boost::regex::perl|boost::regex::icase);
- mMenuName = "menu_url_slapp.xml";
- mTooltip = LLTrans::getString("TooltipSLAPP");
-}
-
-std::string LLUrlEntrySLLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
-{
- std::string label = getLabelFromWikiLink(url);
- return (!LLUrlRegistry::instance().hasUrl(label)) ? label : getUrl(url);
-}
-
-std::string LLUrlEntrySLLabel::getUrl(const std::string &string) const
-{
- return getUrlFromWikiLink(string);
-}
-
-std::string LLUrlEntrySLLabel::getTooltip(const std::string &string) const
-{
- // return a tooltip corresponding to the URL type instead of the generic one (EXT-4574)
- std::string url = getUrl(string);
- LLUrlMatch match;
- if (LLUrlRegistry::instance().findUrl(url, match))
- {
- return match.getTooltip();
- }
-
- // unrecognized URL? should not happen
- return LLUrlEntryBase::getTooltip(string);
-}
-
-bool LLUrlEntrySLLabel::underlineOnHoverOnly(const std::string &string) const
-{
- std::string url = getUrl(string);
- LLUrlMatch match;
- if (LLUrlRegistry::instance().findUrl(url, match))
- {
- return match.underlineOnHoverOnly();
- }
-
- // unrecognized URL? should not happen
- return LLUrlEntryBase::underlineOnHoverOnly(string);
-}
-
-//
-// LLUrlEntryWorldMap Describes secondlife:///<location> URLs
-//
-LLUrlEntryWorldMap::LLUrlEntryWorldMap()
-{
- mPattern = boost::regex(APP_HEADER_REGEX "/worldmap/\\S+/?(\\d+)?/?(\\d+)?/?(\\d+)?/?\\S*",
- boost::regex::perl|boost::regex::icase);
- mMenuName = "menu_url_map.xml";
- mTooltip = LLTrans::getString("TooltipMapUrl");
-}
-
-std::string LLUrlEntryWorldMap::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
-{
- //
- // we handle SLURLs in the following formats:
- // - secondlife:///app/worldmap/PLACE/X/Y/Z
- // - secondlife:///app/worldmap/PLACE/X/Y
- // - secondlife:///app/worldmap/PLACE/X
- //
- LLURI uri(url);
- LLSD path_array = uri.pathArray();
- S32 path_parts = path_array.size();
- if (path_parts < 3)
- {
- return url;
- }
-
- const std::string label = LLTrans::getString("SLurlLabelShowOnMap");
- std::string location = unescapeUrl(path_array[2]);
- std::string x = (path_parts > 3) ? path_array[3] : "128";
- std::string y = (path_parts > 4) ? path_array[4] : "128";
- std::string z = (path_parts > 5) ? path_array[5] : "0";
- return label + " " + location + " (" + x + "," + y + "," + z + ")";
-}
-
-std::string LLUrlEntryWorldMap::getLocation(const std::string &url) const
-{
- // return the part of the Url after secondlife:///app/worldmap/ part
- return ::getStringAfterToken(url, "app/worldmap/");
-}
-
-//
-// LLUrlEntryNoLink lets us turn of URL detection with <nolink>...</nolink> tags
-//
-LLUrlEntryNoLink::LLUrlEntryNoLink()
-{
- mPattern = boost::regex("<nolink>.*?</nolink>",
- boost::regex::perl|boost::regex::icase);
-}
-
-std::string LLUrlEntryNoLink::getUrl(const std::string &url) const
-{
- // return the text between the <nolink> and </nolink> tags
- return url.substr(8, url.size()-8-9);
-}
-
-std::string LLUrlEntryNoLink::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
-{
- return getUrl(url);
-}
-
-LLStyle::Params LLUrlEntryNoLink::getStyle() const
-{
- // Don't render as URL (i.e. no context menu or hand cursor).
- return LLStyle::Params().is_link(false);
-}
-
-
-//
-// LLUrlEntryIcon describes an icon with <icon>...</icon> tags
-//
-LLUrlEntryIcon::LLUrlEntryIcon()
-{
- mPattern = boost::regex("<icon\\s*>\\s*([^<]*)?\\s*</icon\\s*>",
- boost::regex::perl|boost::regex::icase);
-}
-
-std::string LLUrlEntryIcon::getUrl(const std::string &url) const
-{
- return LLStringUtil::null;
-}
-
-std::string LLUrlEntryIcon::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
-{
- return LLStringUtil::null;
-}
-
-std::string LLUrlEntryIcon::getIcon(const std::string &url)
-{
- // Grep icon info between <icon>...</icon> tags
- // matches[1] contains the icon name/path
- boost::match_results<std::string::const_iterator> matches;
- mIcon = (ll_regex_match(url, matches, mPattern) && matches[1].matched)
- ? matches[1]
- : LLStringUtil::null;
- LLStringUtil::trim(mIcon);
- return mIcon;
-}
-
-//
-// LLUrlEntryEmail Describes a generic mailto: Urls
-//
-LLUrlEntryEmail::LLUrlEntryEmail()
- : LLUrlEntryBase()
-{
- mPattern = boost::regex("(mailto:)?[\\w\\.\\-]+@[\\w\\.\\-]+\\.[a-z]{2,63}",
- boost::regex::perl | boost::regex::icase);
- mMenuName = "menu_url_email.xml";
- mTooltip = LLTrans::getString("TooltipEmail");
-}
-
-std::string LLUrlEntryEmail::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
-{
- int pos = url.find("mailto:");
-
- if (pos == std::string::npos)
- {
- return escapeUrl(url);
- }
-
- std::string ret = escapeUrl(url.substr(pos + 7, url.length() - pos + 8));
- return ret;
-}
-
-std::string LLUrlEntryEmail::getUrl(const std::string &string) const
-{
- if (string.find("mailto:") == std::string::npos)
- {
- return "mailto:" + escapeUrl(string);
- }
- return escapeUrl(string);
-}
-
-LLUrlEntryExperienceProfile::LLUrlEntryExperienceProfile()
-{
- mPattern = boost::regex(APP_HEADER_REGEX "/experience/[\\da-f-]+/profile",
- boost::regex::perl|boost::regex::icase);
- mIcon = "Generic_Experience";
- mMenuName = "menu_url_experience.xml";
-}
-
-std::string LLUrlEntryExperienceProfile::getLabel( const std::string &url, const LLUrlLabelCallback &cb )
-{
- if (!gCacheName)
- {
- // probably at the login screen, use short string for layout
- return LLTrans::getString("LoadingData");
- }
-
- std::string experience_id_string = getIDStringFromUrl(url);
- if (experience_id_string.empty())
- {
- // something went wrong, just give raw url
- return unescapeUrl(url);
- }
-
- LLUUID experience_id(experience_id_string);
- if (experience_id.isNull())
- {
- return LLTrans::getString("ExperienceNameNull");
- }
-
- const LLSD& experience_details = LLExperienceCache::instance().get(experience_id);
- if(!experience_details.isUndefined())
- {
- std::string experience_name_string = experience_details[LLExperienceCache::NAME].asString();
- return experience_name_string.empty() ? LLTrans::getString("ExperienceNameUntitled") : experience_name_string;
- }
-
- addObserver(experience_id_string, url, cb);
- LLExperienceCache::instance().get(experience_id, boost::bind(&LLUrlEntryExperienceProfile::onExperienceDetails, this, _1));
- return LLTrans::getString("LoadingData");
-
-}
-
-void LLUrlEntryExperienceProfile::onExperienceDetails( const LLSD& experience_details )
-{
- std::string name = experience_details[LLExperienceCache::NAME].asString();
- if(name.empty())
- {
- name = LLTrans::getString("ExperienceNameUntitled");
- }
- callObservers(experience_details[LLExperienceCache::EXPERIENCE_ID].asString(), name, LLStringUtil::null);
-}
-
-//
-// LLUrlEntryEmail Describes an IPv6 address
-//
-LLUrlEntryIPv6::LLUrlEntryIPv6()
- : LLUrlEntryBase()
-{
- mHostPath = "https?://\\[([a-f0-9:]+:+)+[a-f0-9]+]";
- mPattern = boost::regex(mHostPath + "(:\\d{1,5})?(/\\S*)?",
- boost::regex::perl | boost::regex::icase);
- mMenuName = "menu_url_http.xml";
- mTooltip = LLTrans::getString("TooltipHttpUrl");
-}
-
-std::string LLUrlEntryIPv6::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
-{
- boost::regex regex = boost::regex(mHostPath, boost::regex::perl | boost::regex::icase);
- boost::match_results<std::string::const_iterator> matches;
-
- if (boost::regex_search(url, matches, regex))
- {
- return url.substr(0, matches[0].length());
- }
- else
- {
- return url;
- }
-}
-
-std::string LLUrlEntryIPv6::getQuery(const std::string &url) const
-{
- boost::regex regex = boost::regex(mHostPath, boost::regex::perl | boost::regex::icase);
- boost::match_results<std::string::const_iterator> matches;
-
- return boost::regex_replace(url, regex, "");
-}
-
-std::string LLUrlEntryIPv6::getUrl(const std::string &string) const
-{
- return string;
-}
-
-
-//
-// LLUrlEntryKeybinding Displays currently assigned key
-//
-LLUrlEntryKeybinding::LLUrlEntryKeybinding()
- : LLUrlEntryBase()
- , pHandler(NULL)
-{
- mPattern = boost::regex(APP_HEADER_REGEX "/keybinding/\\w+(\\?mode=\\w+)?$",
- boost::regex::perl | boost::regex::icase);
- mMenuName = "menu_url_experience.xml";
-
- initLocalization();
-}
-
-std::string LLUrlEntryKeybinding::getLabel(const std::string& url, const LLUrlLabelCallback& cb)
-{
- std::string control = getControlName(url);
-
- std::map<std::string, LLLocalizationData>::iterator iter = mLocalizations.find(control);
-
- std::string keybind;
- if (pHandler)
- {
- keybind = pHandler->getKeyBindingAsString(getMode(url), control);
- }
-
- if (iter != mLocalizations.end())
- {
- return iter->second.mLocalization + ": " + keybind;
- }
-
- return control + ": " + keybind;
-}
-
-std::string LLUrlEntryKeybinding::getTooltip(const std::string& url) const
-{
- std::string control = getControlName(url);
-
- std::map<std::string, LLLocalizationData>::const_iterator iter = mLocalizations.find(control);
- if (iter != mLocalizations.end())
- {
- return iter->second.mTooltip;
- }
- return url;
-}
-
-std::string LLUrlEntryKeybinding::getControlName(const std::string& url) const
-{
- std::string search = "/keybinding/";
- size_t pos_start = url.find(search);
- if (pos_start == std::string::npos)
- {
- return std::string();
- }
- pos_start += search.size();
-
- size_t pos_end = url.find("?mode=");
- if (pos_end == std::string::npos)
- {
- pos_end = url.size();
- }
- return url.substr(pos_start, pos_end - pos_start);
-}
-
-std::string LLUrlEntryKeybinding::getMode(const std::string& url) const
-{
- std::string search = "?mode=";
- size_t pos_start = url.find(search);
- if (pos_start == std::string::npos)
- {
- return std::string();
- }
- pos_start += search.size();
- return url.substr(pos_start, url.size() - pos_start);
-}
-
-void LLUrlEntryKeybinding::initLocalization()
-{
- initLocalizationFromFile("control_table_contents_movement.xml");
- initLocalizationFromFile("control_table_contents_camera.xml");
- initLocalizationFromFile("control_table_contents_editing.xml");
- initLocalizationFromFile("control_table_contents_media.xml");
-}
-
-void LLUrlEntryKeybinding::initLocalizationFromFile(const std::string& filename)
-{
- LLXMLNodePtr xmlNode;
- LLScrollListCtrl::Contents contents;
- if (!LLUICtrlFactory::getLayeredXMLNode(filename, xmlNode))
- {
- LL_WARNS() << "Failed to load " << filename << LL_ENDL;
- return;
- }
- LLXUIParser parser;
- parser.readXUI(xmlNode, contents, filename);
-
- if (!contents.validateBlock())
- {
- LL_WARNS() << "Failed to validate " << filename << LL_ENDL;
- return;
- }
-
- for (LLInitParam::ParamIterator<LLScrollListItem::Params>::const_iterator row_it = contents.rows.begin();
- row_it != contents.rows.end();
- ++row_it)
- {
- std::string control = row_it->value.getValue().asString();
- if (!control.empty() && control != "menu_separator")
- {
- mLocalizations[control] =
- LLLocalizationData(
- row_it->columns.begin()->value.getValue().asString(),
- row_it->columns.begin()->tool_tip.getValue()
- );
- }
- }
-}
+/**
+ * @file llurlentry.cpp
+ * @author Martin Reddy
+ * @brief Describes the Url types that can be registered in LLUrlRegistry
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llurlentry.h"
+#include "lluictrl.h"
+#include "lluri.h"
+#include "llurlmatch.h"
+#include "llurlregistry.h"
+#include "lluriparser.h"
+
+#include "llavatarnamecache.h"
+#include "llcachename.h"
+#include "llkeyboard.h"
+#include "llregex.h"
+#include "llscrolllistctrl.h" // for LLUrlEntryKeybinding file parsing
+#include "lltrans.h"
+#include "lluicolortable.h"
+#include "message.h"
+#include "llexperiencecache.h"
+
+#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))"
+
+// Utility functions
+std::string localize_slapp_label(const std::string& url, const std::string& full_name);
+
+
+LLUrlEntryBase::LLUrlEntryBase()
+{
+}
+
+LLUrlEntryBase::~LLUrlEntryBase()
+{
+}
+
+std::string LLUrlEntryBase::getUrl(const std::string &string) const
+{
+ return escapeUrl(string);
+}
+
+//virtual
+std::string LLUrlEntryBase::getIcon(const std::string &url)
+{
+ return mIcon;
+}
+
+LLStyle::Params LLUrlEntryBase::getStyle() const
+{
+ LLStyle::Params style_params;
+ style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor");
+ style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor");
+ style_params.font.style = "UNDERLINE";
+ return style_params;
+}
+
+
+std::string LLUrlEntryBase::getIDStringFromUrl(const std::string &url) const
+{
+ // return the id from a SLURL in the format /app/{cmd}/{id}/about
+ LLURI uri(url);
+ LLSD path_array = uri.pathArray();
+ if (path_array.size() == 4)
+ {
+ return path_array.get(2).asString();
+ }
+ return "";
+}
+
+std::string LLUrlEntryBase::unescapeUrl(const std::string &url) const
+{
+ return LLURI::unescape(url);
+}
+
+std::string LLUrlEntryBase::escapeUrl(const std::string &url) const
+{
+ static std::string no_escape_chars;
+ static bool initialized = false;
+ if (!initialized)
+ {
+ no_escape_chars =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789"
+ "-._~!$?&()*+,@:;=/%#";
+
+ std::sort(no_escape_chars.begin(), no_escape_chars.end());
+ initialized = true;
+ }
+ return LLURI::escape(url, no_escape_chars, true);
+}
+
+std::string LLUrlEntryBase::getLabelFromWikiLink(const std::string &url) const
+{
+ // return the label part from [http://www.example.org Label]
+ const char *text = url.c_str();
+ S32 start = 0;
+ while (! isspace(text[start]))
+ {
+ start++;
+ }
+ while (text[start] == ' ' || text[start] == '\t')
+ {
+ start++;
+ }
+ return unescapeUrl(url.substr(start, url.size()-start-1));
+}
+
+std::string LLUrlEntryBase::getUrlFromWikiLink(const std::string &string) const
+{
+ // return the url part from [http://www.example.org Label]
+ const char *text = string.c_str();
+ S32 end = 0;
+ while (! isspace(text[end]))
+ {
+ end++;
+ }
+ return escapeUrl(string.substr(1, end-1));
+}
+
+void LLUrlEntryBase::addObserver(const std::string &id,
+ const std::string &url,
+ const LLUrlLabelCallback &cb)
+{
+ // add a callback to be notified when we have a label for the uuid
+ LLUrlEntryObserver observer;
+ observer.url = url;
+ observer.signal = new LLUrlLabelSignal();
+ if (observer.signal)
+ {
+ observer.signal->connect(cb);
+ mObservers.insert(std::pair<std::string, LLUrlEntryObserver>(id, observer));
+ }
+}
+
+// *NOTE: See also LLUrlEntryAgent::callObservers()
+void LLUrlEntryBase::callObservers(const std::string &id,
+ const std::string &label,
+ const std::string &icon)
+{
+ // notify all callbacks waiting on the given uuid
+ typedef std::multimap<std::string, LLUrlEntryObserver>::iterator observer_it;
+ std::pair<observer_it, observer_it> matching_range = mObservers.equal_range(id);
+ for (observer_it it = matching_range.first; it != matching_range.second;)
+ {
+ // call the callback - give it the new label
+ LLUrlEntryObserver &observer = it->second;
+ (*observer.signal)(it->second.url, label, icon);
+ // then remove the signal - we only need to call it once
+ delete observer.signal;
+ mObservers.erase(it++);
+ }
+}
+
+/// is this a match for a URL that should not be hyperlinked?
+bool LLUrlEntryBase::isLinkDisabled() const
+{
+ // this allows us to have a global setting to turn off text hyperlink highlighting/action
+ static LLCachedControl<bool> globally_disabled(*LLUI::getInstance()->mSettingGroups["config"], "DisableTextHyperlinkActions", false);
+
+ return globally_disabled;
+}
+
+bool LLUrlEntryBase::isWikiLinkCorrect(const std::string &labeled_url) const
+{
+ LLWString wlabel = utf8str_to_wstring(getLabelFromWikiLink(labeled_url));
+ wlabel.erase(std::remove(wlabel.begin(), wlabel.end(), L'\u200B'), wlabel.end());
+
+ // Unicode URL validation, see SL-15243
+ std::replace_if(wlabel.begin(),
+ wlabel.end(),
+ [](const llwchar &chr)
+ {
+ return (chr == L'\u2024') // "One Dot Leader"
+ || (chr == L'\uFE52') // "Small Full Stop"
+ || (chr == L'\uFF0E') // "Fullwidth Full Stop"
+ // Not a decomposition, but suficiently similar
+ || (chr == L'\u05C5'); // "Hebrew Mark Lower Dot"
+ },
+ L'\u002E'); // Dot "Full Stop"
+
+ std::replace_if(wlabel.begin(),
+ wlabel.end(),
+ [](const llwchar &chr)
+ {
+ return (chr == L'\u02D0') // "Modifier Letter Colon"
+ || (chr == L'\uFF1A') // "Fullwidth Colon"
+ || (chr == L'\u2236') // "Ratio"
+ || (chr == L'\uFE55'); // "Small Colon"
+ },
+ L'\u003A'); // Colon
+
+ std::replace_if(wlabel.begin(),
+ wlabel.end(),
+ [](const llwchar &chr)
+ {
+ return (chr == L'\uFF0F'); // "Fullwidth Solidus"
+ },
+ L'\u002F'); // Solidus
+
+ std::string label = wstring_to_utf8str(wlabel);
+ if ((label.find(".com") != std::string::npos
+ || label.find("www.") != std::string::npos)
+ && label.find("://") == std::string::npos)
+ {
+ label = "http://" + label;
+ }
+
+ return !LLUrlRegistry::instance().hasUrl(label);
+}
+
+std::string LLUrlEntryBase::urlToLabelWithGreyQuery(const std::string &url) const
+{
+ if (url.empty())
+ {
+ return url;
+ }
+ LLUriParser up(escapeUrl(url));
+ if (up.normalize() == 0)
+ {
+ std::string label;
+ up.extractParts();
+ up.glueFirst(label);
+
+ return unescapeUrl(label);
+ }
+ return std::string();
+}
+
+std::string LLUrlEntryBase::urlToGreyQuery(const std::string &url) const
+{
+ std::string escaped_url = escapeUrl(url);
+ LLUriParser up(escaped_url);
+
+ std::string label;
+ up.extractParts();
+ up.glueFirst(label, false);
+
+ size_t pos = escaped_url.find(label);
+ if (pos == std::string::npos)
+ {
+ return "";
+ }
+ pos += label.size();
+ return unescapeUrl(escaped_url.substr(pos));
+}
+
+
+static std::string getStringAfterToken(const std::string str, const std::string token)
+{
+ size_t pos = str.find(token);
+ if (pos == std::string::npos)
+ {
+ return "";
+ }
+
+ pos += token.size();
+ return str.substr(pos, str.size() - pos);
+}
+
+//
+// LLUrlEntryHTTP Describes generic http: and https: Urls
+//
+LLUrlEntryHTTP::LLUrlEntryHTTP()
+ : LLUrlEntryBase()
+{
+ mPattern = boost::regex("https?://([^\\s/?\\.#]+\\.?)+\\.\\w+(:\\d+)?(/\\S*)?",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_http.xml";
+ mTooltip = LLTrans::getString("TooltipHttpUrl");
+}
+
+std::string LLUrlEntryHTTP::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return 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);
+}
+
+//
+// LLUrlEntryHTTP Describes generic http: and https: Urls with custom label
+// We use the wikipedia syntax of [http://www.example.org Text]
+//
+LLUrlEntryHTTPLabel::LLUrlEntryHTTPLabel()
+{
+ mPattern = boost::regex("\\[https?://\\S+[ \t]+[^\\]]+\\]",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_http.xml";
+ mTooltip = LLTrans::getString("TooltipHttpUrl");
+}
+
+std::string LLUrlEntryHTTPLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ std::string label = getLabelFromWikiLink(url);
+ return (!LLUrlRegistry::instance().hasUrl(label)) ? label : getUrl(url);
+}
+
+std::string LLUrlEntryHTTPLabel::getTooltip(const std::string &string) const
+{
+ return getUrl(string);
+}
+
+std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) const
+{
+ return getUrlFromWikiLink(string);
+}
+
+LLUrlEntryInvalidSLURL::LLUrlEntryInvalidSLURL()
+ : LLUrlEntryBase()
+{
+ mPattern = boost::regex("(https?://(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
+//
+LLUrlEntrySLURL::LLUrlEntrySLURL()
+{
+ // see http://slurl.com/about.php for details on the SLURL format
+ mPattern = boost::regex("https?://(maps.secondlife.com|slurl.com)/secondlife/[^ /]+(/\\d+){0,3}(/?(\\?title|\\?img|\\?msg)=\\S*)?/?",
+ boost::regex::perl|boost::regex::icase);
+ mIcon = "Hand";
+ mMenuName = "menu_url_slurl.xml";
+ mTooltip = LLTrans::getString("TooltipSLURL");
+}
+
+std::string LLUrlEntrySLURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ //
+ // we handle SLURLs in the following formats:
+ // - http://slurl.com/secondlife/Place/X/Y/Z
+ // - http://slurl.com/secondlife/Place/X/Y
+ // - http://slurl.com/secondlife/Place/X
+ // - http://slurl.com/secondlife/Place
+ //
+
+ LLURI uri(url);
+ LLSD path_array = uri.pathArray();
+ S32 path_parts = path_array.size();
+ if (path_parts == 5)
+ {
+ // handle slurl with (X,Y,Z) coordinates
+ std::string location = unescapeUrl(path_array[path_parts-4]);
+ std::string x = path_array[path_parts-3];
+ std::string y = path_array[path_parts-2];
+ std::string z = path_array[path_parts-1];
+ return location + " (" + x + "," + y + "," + z + ")";
+ }
+ else if (path_parts == 4)
+ {
+ // handle slurl with (X,Y) coordinates
+ std::string location = unescapeUrl(path_array[path_parts-3]);
+ std::string x = path_array[path_parts-2];
+ std::string y = path_array[path_parts-1];
+ return location + " (" + x + "," + y + ")";
+ }
+ else if (path_parts == 3)
+ {
+ // handle slurl with (X) coordinate
+ std::string location = unescapeUrl(path_array[path_parts-2]);
+ std::string x = path_array[path_parts-1];
+ return location + " (" + x + ")";
+ }
+ else if (path_parts == 2)
+ {
+ // handle slurl with no coordinates
+ std::string location = unescapeUrl(path_array[path_parts-1]);
+ return location;
+ }
+
+ return url;
+}
+
+std::string LLUrlEntrySLURL::getLocation(const std::string &url) const
+{
+ // return the part of the Url after slurl.com/secondlife/
+ const std::string search_string = "/secondlife";
+ size_t pos = url.find(search_string);
+ if (pos == std::string::npos)
+ {
+ return "";
+ }
+
+ pos += search_string.size() + 1;
+ return url.substr(pos, url.size() - pos);
+}
+
+//
+// LLUrlEntrySeconlifeURL Describes *secondlife.com/ *lindenlab.com/ *secondlifegrid.net/ and *tilia-inc.com/ urls to substitute icon 'hand.png' before link
+//
+LLUrlEntrySecondlifeURL::LLUrlEntrySecondlifeURL()
+{
+ mPattern = boost::regex("((http://([-\\w\\.]*\\.)?(secondlife|lindenlab|tilia-inc)\\.com)"
+ "|"
+ "(http://([-\\w\\.]*\\.)?secondlifegrid\\.net)"
+ "|"
+ "(https://([-\\w\\.]*\\.)?(secondlife|lindenlab|tilia-inc)\\.com(:\\d{1,5})?)"
+ "|"
+ "(https://([-\\w\\.]*\\.)?secondlifegrid\\.net(:\\d{1,5})?)"
+ "|"
+ "(https?://([-\\w\\.]*\\.)?secondlife\\.io(:\\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 *lindenlab.com *secondlifegrid.net and *tilia-inc.com urls to substitute icon 'hand.png' before link
+//
+LLUrlEntrySimpleSecondlifeURL::LLUrlEntrySimpleSecondlifeURL()
+ {
+ mPattern = boost::regex("https?://([-\\w\\.]*\\.)?(secondlife|lindenlab|tilia-inc)\\.com(?!\\S)"
+ "|"
+ "https?://([-\\w\\.]*\\.)?secondlifegrid\\.net(?!\\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()
+{
+ mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/\\w+",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_agent.xml";
+ mIcon = "Generic_Person";
+}
+
+// virtual
+void LLUrlEntryAgent::callObservers(const std::string &id,
+ const std::string &label,
+ const std::string &icon)
+{
+ // notify all callbacks waiting on the given uuid
+ typedef std::multimap<std::string, LLUrlEntryObserver>::iterator observer_it;
+ std::pair<observer_it, observer_it> matching_range = mObservers.equal_range(id);
+ for (observer_it it = matching_range.first; it != matching_range.second;)
+ {
+ // call the callback - give it the new label
+ LLUrlEntryObserver &observer = it->second;
+ std::string final_label = localize_slapp_label(observer.url, label);
+ (*observer.signal)(observer.url, final_label, icon);
+ // then remove the signal - we only need to call it once
+ delete observer.signal;
+ mObservers.erase(it++);
+ }
+}
+
+void LLUrlEntryAgent::onAvatarNameCache(const LLUUID& id,
+ const LLAvatarName& av_name)
+{
+ avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(id);
+ if (it != mAvatarNameCacheConnections.end())
+ {
+ if (it->second.connected())
+ {
+ it->second.disconnect();
+ }
+ mAvatarNameCacheConnections.erase(it);
+ }
+
+ std::string label = av_name.getCompleteName();
+
+ // received the agent name from the server - tell our observers
+ callObservers(id.asString(), label, mIcon);
+}
+
+LLUUID LLUrlEntryAgent::getID(const std::string &string) const
+{
+ return LLUUID(getIDStringFromUrl(string));
+}
+
+std::string LLUrlEntryAgent::getTooltip(const std::string &string) const
+{
+ // return a tooltip corresponding to the URL type instead of the generic one
+ std::string url = getUrl(string);
+
+ if (LLStringUtil::endsWith(url, "/inspect"))
+ {
+ return LLTrans::getString("TooltipAgentInspect");
+ }
+ if (LLStringUtil::endsWith(url, "/mute"))
+ {
+ return LLTrans::getString("TooltipAgentMute");
+ }
+ if (LLStringUtil::endsWith(url, "/unmute"))
+ {
+ return LLTrans::getString("TooltipAgentUnmute");
+ }
+ if (LLStringUtil::endsWith(url, "/im"))
+ {
+ return LLTrans::getString("TooltipAgentIM");
+ }
+ if (LLStringUtil::endsWith(url, "/pay"))
+ {
+ return LLTrans::getString("TooltipAgentPay");
+ }
+ if (LLStringUtil::endsWith(url, "/offerteleport"))
+ {
+ return LLTrans::getString("TooltipAgentOfferTeleport");
+ }
+ if (LLStringUtil::endsWith(url, "/requestfriend"))
+ {
+ return LLTrans::getString("TooltipAgentRequestFriend");
+ }
+ return LLTrans::getString("TooltipAgentUrl");
+}
+
+bool LLUrlEntryAgent::underlineOnHoverOnly(const std::string &string) const
+{
+ std::string url = getUrl(string);
+ return LLStringUtil::endsWith(url, "/about") || LLStringUtil::endsWith(url, "/inspect");
+}
+
+std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ if (!gCacheName)
+ {
+ // probably at the login screen, use short string for layout
+ return LLTrans::getString("LoadingData");
+ }
+
+ std::string agent_id_string = getIDStringFromUrl(url);
+ if (agent_id_string.empty())
+ {
+ // something went wrong, just give raw url
+ return unescapeUrl(url);
+ }
+
+ LLUUID agent_id(agent_id_string);
+ if (agent_id.isNull())
+ {
+ return LLTrans::getString("AvatarNameNobody");
+ }
+
+ LLAvatarName av_name;
+ if (LLAvatarNameCache::get(agent_id, &av_name))
+ {
+ std::string label = av_name.getCompleteName();
+
+ // handle suffixes like /mute or /offerteleport
+ label = localize_slapp_label(url, label);
+ return label;
+ }
+ else
+ {
+ avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(agent_id);
+ if (it != mAvatarNameCacheConnections.end())
+ {
+ if (it->second.connected())
+ {
+ it->second.disconnect();
+ }
+ mAvatarNameCacheConnections.erase(it);
+ }
+ mAvatarNameCacheConnections[agent_id] = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgent::onAvatarNameCache, this, _1, _2));
+
+ addObserver(agent_id_string, url, cb);
+ return LLTrans::getString("LoadingData");
+ }
+}
+
+LLStyle::Params LLUrlEntryAgent::getStyle() const
+{
+ LLStyle::Params style_params = LLUrlEntryBase::getStyle();
+ style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor");
+ style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor");
+ return style_params;
+}
+
+std::string localize_slapp_label(const std::string& url, const std::string& full_name)
+{
+ // customize label string based on agent SLapp suffix
+ if (LLStringUtil::endsWith(url, "/mute"))
+ {
+ return LLTrans::getString("SLappAgentMute") + " " + full_name;
+ }
+ if (LLStringUtil::endsWith(url, "/unmute"))
+ {
+ return LLTrans::getString("SLappAgentUnmute") + " " + full_name;
+ }
+ if (LLStringUtil::endsWith(url, "/im"))
+ {
+ return LLTrans::getString("SLappAgentIM") + " " + full_name;
+ }
+ if (LLStringUtil::endsWith(url, "/pay"))
+ {
+ return LLTrans::getString("SLappAgentPay") + " " + full_name;
+ }
+ if (LLStringUtil::endsWith(url, "/offerteleport"))
+ {
+ return LLTrans::getString("SLappAgentOfferTeleport") + " " + full_name;
+ }
+ if (LLStringUtil::endsWith(url, "/requestfriend"))
+ {
+ return LLTrans::getString("SLappAgentRequestFriend") + " " + full_name;
+ }
+ if (LLStringUtil::endsWith(url, "/removefriend"))
+ {
+ return LLTrans::getString("SLappAgentRemoveFriend") + " " + full_name;
+ }
+ return full_name;
+}
+
+
+std::string LLUrlEntryAgent::getIcon(const std::string &url)
+{
+ // *NOTE: Could look up a badge here by calling getIDStringFromUrl()
+ // and looking up the badge for the agent.
+ return mIcon;
+}
+
+//
+// LLUrlEntryAgentName describes a Second Life agent name Url, e.g.,
+// 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()
+{}
+
+void LLUrlEntryAgentName::onAvatarNameCache(const LLUUID& id,
+ const LLAvatarName& av_name)
+{
+ avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(id);
+ if (it != mAvatarNameCacheConnections.end())
+ {
+ if (it->second.connected())
+ {
+ it->second.disconnect();
+ }
+ mAvatarNameCacheConnections.erase(it);
+ }
+
+ std::string label = getName(av_name);
+ // received the agent name from the server - tell our observers
+ callObservers(id.asString(), label, mIcon);
+}
+
+std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ if (!gCacheName)
+ {
+ // probably at the login screen, use short string for layout
+ return LLTrans::getString("LoadingData");
+ }
+
+ std::string agent_id_string = getIDStringFromUrl(url);
+ if (agent_id_string.empty())
+ {
+ // something went wrong, just give raw url
+ return unescapeUrl(url);
+ }
+
+ LLUUID agent_id(agent_id_string);
+ if (agent_id.isNull())
+ {
+ return LLTrans::getString("AvatarNameNobody");
+ }
+
+ LLAvatarName av_name;
+ if (LLAvatarNameCache::get(agent_id, &av_name))
+ {
+ return getName(av_name);
+ }
+ else
+ {
+ avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.find(agent_id);
+ if (it != mAvatarNameCacheConnections.end())
+ {
+ if (it->second.connected())
+ {
+ it->second.disconnect();
+ }
+ mAvatarNameCacheConnections.erase(it);
+ }
+ mAvatarNameCacheConnections[agent_id] = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgentName::onAvatarNameCache, this, _1, _2));
+
+ addObserver(agent_id_string, url, cb);
+ return LLTrans::getString("LoadingData");
+ }
+}
+
+LLStyle::Params LLUrlEntryAgentName::getStyle() const
+{
+ // don't override default colors
+ return LLStyle::Params().is_link(false);
+}
+
+//
+// LLUrlEntryAgentCompleteName describes a Second Life agent complete name Url, e.g.,
+// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename
+// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename
+//
+LLUrlEntryAgentCompleteName::LLUrlEntryAgentCompleteName()
+{
+ mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/completename",
+ boost::regex::perl|boost::regex::icase);
+}
+
+std::string LLUrlEntryAgentCompleteName::getName(const LLAvatarName& avatar_name)
+{
+ return avatar_name.getCompleteName(true, true);
+}
+
+//
+// LLUrlEntryAgentLegacyName describes a Second Life agent legacy name Url, e.g.,
+// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/legacyname
+// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/legacyname
+//
+LLUrlEntryAgentLegacyName::LLUrlEntryAgentLegacyName()
+{
+ mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/legacyname",
+ boost::regex::perl|boost::regex::icase);
+}
+
+std::string LLUrlEntryAgentLegacyName::getName(const LLAvatarName& avatar_name)
+{
+ return avatar_name.getLegacyName();
+}
+
+//
+// LLUrlEntryAgentDisplayName describes a Second Life agent display name Url, e.g.,
+// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname
+// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname
+//
+LLUrlEntryAgentDisplayName::LLUrlEntryAgentDisplayName()
+{
+ mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/displayname",
+ boost::regex::perl|boost::regex::icase);
+}
+
+std::string LLUrlEntryAgentDisplayName::getName(const LLAvatarName& avatar_name)
+{
+ return avatar_name.getDisplayName(true);
+}
+
+//
+// LLUrlEntryAgentUserName describes a Second Life agent user name Url, e.g.,
+// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username
+// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username
+//
+LLUrlEntryAgentUserName::LLUrlEntryAgentUserName()
+{
+ mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/username",
+ boost::regex::perl|boost::regex::icase);
+}
+
+std::string LLUrlEntryAgentUserName::getName(const LLAvatarName& avatar_name)
+{
+ return avatar_name.getAccountName();
+}
+
+//
+// LLUrlEntryGroup Describes a Second Life group Url, e.g.,
+// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about
+// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect
+// x-grid-location-info://lincoln.lindenlab.com/app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect
+//
+LLUrlEntryGroup::LLUrlEntryGroup()
+{
+ mPattern = boost::regex(APP_HEADER_REGEX "/group/[\\da-f-]+/\\w+",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_group.xml";
+ mIcon = "Generic_Group";
+ mTooltip = LLTrans::getString("TooltipGroupUrl");
+}
+
+
+
+void LLUrlEntryGroup::onGroupNameReceived(const LLUUID& id,
+ const std::string& name,
+ bool is_group)
+{
+ // received the group name from the server - tell our observers
+ callObservers(id.asString(), name, mIcon);
+}
+
+LLUUID LLUrlEntryGroup::getID(const std::string &string) const
+{
+ return LLUUID(getIDStringFromUrl(string));
+}
+
+
+std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ if (!gCacheName)
+ {
+ // probably at login screen, give something short for layout
+ return LLTrans::getString("LoadingData");
+ }
+
+ std::string group_id_string = getIDStringFromUrl(url);
+ if (group_id_string.empty())
+ {
+ // something went wrong, give raw url
+ return unescapeUrl(url);
+ }
+
+ LLUUID group_id(group_id_string);
+ std::string group_name;
+ if (group_id.isNull())
+ {
+ return LLTrans::getString("GroupNameNone");
+ }
+ else if (gCacheName->getGroupName(group_id, group_name))
+ {
+ return group_name;
+ }
+ else
+ {
+ gCacheName->getGroup(group_id,
+ boost::bind(&LLUrlEntryGroup::onGroupNameReceived,
+ this, _1, _2, _3));
+ addObserver(group_id_string, url, cb);
+ return LLTrans::getString("LoadingData");
+ }
+}
+
+LLStyle::Params LLUrlEntryGroup::getStyle() const
+{
+ LLStyle::Params style_params = LLUrlEntryBase::getStyle();
+ style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor");
+ style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor");
+ return style_params;
+}
+
+
+//
+// LLUrlEntryInventory Describes a Second Life inventory Url, e.g.,
+// secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select
+//
+LLUrlEntryInventory::LLUrlEntryInventory()
+{
+ //*TODO: add supporting of inventory item names with whitespaces
+ //this pattern cann't parse for example
+ //secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces&param2=value
+ //x-grid-location-info://lincoln.lindenlab.com/app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces&param2=value
+ mPattern = boost::regex(APP_HEADER_REGEX "/inventory/[\\da-f-]+/\\w+\\S*",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_inventory.xml";
+}
+
+std::string LLUrlEntryInventory::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ std::string label = getStringAfterToken(url, "name=");
+ return LLURI::unescape(label.empty() ? url : label);
+}
+
+//
+// LLUrlEntryObjectIM Describes a Second Life inspector for the object Url, e.g.,
+// secondlife:///app/objectim/7bcd7864-da6b-e43f-4486-91d28a28d95b?name=Object&owner=3de548e1-57be-cfea-2b78-83ae3ad95998&slurl=Danger!%20Danger!/200/200/30/&groupowned=1
+//
+LLUrlEntryObjectIM::LLUrlEntryObjectIM()
+{
+ mPattern = boost::regex("secondlife:///app/objectim/[\\da-f-]+\?\\S*\\w",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_objectim.xml";
+}
+
+std::string LLUrlEntryObjectIM::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ LLURI uri(url);
+ LLSD query_map = uri.queryMap();
+ if (query_map.has("name"))
+ return query_map["name"];
+ return unescapeUrl(url);
+}
+
+std::string LLUrlEntryObjectIM::getLocation(const std::string &url) const
+{
+ LLURI uri(url);
+ LLSD query_map = uri.queryMap();
+ if (query_map.has("slurl"))
+ return query_map["slurl"];
+ return LLUrlEntryBase::getLocation(url);
+}
+
+//
+// LLUrlEntryChat Describes a Second Life chat Url, e.g.,
+// secondlife:///app/chat/42/This%20Is%20a%20test
+//
+
+LLUrlEntryChat::LLUrlEntryChat()
+{
+ mPattern = boost::regex("secondlife:///app/chat/\\d+/\\S+",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_slapp.xml";
+ mTooltip = LLTrans::getString("TooltipSLAPP");
+}
+
+std::string LLUrlEntryChat::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return unescapeUrl(url);
+}
+
+// LLUrlEntryParcel statics.
+LLUUID LLUrlEntryParcel::sAgentID(LLUUID::null);
+LLUUID LLUrlEntryParcel::sSessionID(LLUUID::null);
+LLHost LLUrlEntryParcel::sRegionHost;
+bool LLUrlEntryParcel::sDisconnected(false);
+std::set<LLUrlEntryParcel*> LLUrlEntryParcel::sParcelInfoObservers;
+
+///
+/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
+/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
+/// x-grid-location-info://lincoln.lindenlab.com/app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
+///
+LLUrlEntryParcel::LLUrlEntryParcel()
+{
+ mPattern = boost::regex(APP_HEADER_REGEX "/parcel/[\\da-f-]+/about",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_parcel.xml";
+ mTooltip = LLTrans::getString("TooltipParcelUrl");
+
+ sParcelInfoObservers.insert(this);
+}
+
+LLUrlEntryParcel::~LLUrlEntryParcel()
+{
+ sParcelInfoObservers.erase(this);
+}
+
+std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ LLSD path_array = LLURI(url).pathArray();
+ S32 path_parts = path_array.size();
+
+ if (path_parts < 3) // no parcel id
+ {
+ LL_WARNS() << "Failed to parse url [" << url << "]" << LL_ENDL;
+ return url;
+ }
+
+ std::string parcel_id_string = unescapeUrl(path_array[2]); // parcel id
+
+ // Add an observer to call LLUrlLabelCallback when we have parcel name.
+ addObserver(parcel_id_string, url, cb);
+
+ LLUUID parcel_id(parcel_id_string);
+
+ sendParcelInfoRequest(parcel_id);
+
+ return unescapeUrl(url);
+}
+
+void LLUrlEntryParcel::sendParcelInfoRequest(const LLUUID& parcel_id)
+{
+ if (sRegionHost.isInvalid() || sDisconnected) return;
+
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessage("ParcelInfoRequest");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, sAgentID );
+ msg->addUUID("SessionID", sSessionID);
+ msg->nextBlock("Data");
+ msg->addUUID("ParcelID", parcel_id);
+ msg->sendReliable(sRegionHost);
+}
+
+void LLUrlEntryParcel::onParcelInfoReceived(const std::string &id, const std::string &label)
+{
+ callObservers(id, label.empty() ? LLTrans::getString("RegionInfoError") : label, mIcon);
+}
+
+// static
+void LLUrlEntryParcel::processParcelInfo(const LLParcelData& parcel_data)
+{
+ std::string label(LLStringUtil::null);
+ if (!parcel_data.name.empty())
+ {
+ label = parcel_data.name;
+ }
+ // If parcel name is empty use Sim_name (x, y, z) for parcel label.
+ else if (!parcel_data.sim_name.empty())
+ {
+ 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);
+ }
+
+ for (std::set<LLUrlEntryParcel*>::iterator iter = sParcelInfoObservers.begin();
+ iter != sParcelInfoObservers.end();
+ ++iter)
+ {
+ LLUrlEntryParcel* url_entry = *iter;
+ if (url_entry)
+ {
+ url_entry->onParcelInfoReceived(parcel_data.parcel_id.asString(), label);
+ }
+ }
+}
+
+//
+// LLUrlEntryPlace Describes secondlife://<location> URLs
+//
+LLUrlEntryPlace::LLUrlEntryPlace()
+{
+ mPattern = boost::regex("((x-grid-location-info://[-\\w\\.]+/region/)|(secondlife://))\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_slurl.xml";
+ mTooltip = LLTrans::getString("TooltipSLURL");
+}
+
+std::string LLUrlEntryPlace::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ //
+ // we handle SLURLs in the following formats:
+ // - secondlife://Place/X/Y/Z
+ // - secondlife://Place/X/Y
+ //
+ LLURI uri(url);
+ std::string location = unescapeUrl(uri.hostName());
+ LLSD path_array = uri.pathArray();
+ S32 path_parts = path_array.size();
+ if (path_parts == 3)
+ {
+ // handle slurl with (X,Y,Z) coordinates
+ std::string x = path_array[0];
+ std::string y = path_array[1];
+ std::string z = path_array[2];
+ return location + " (" + x + "," + y + "," + z + ")";
+ }
+ else if (path_parts == 2)
+ {
+ // handle slurl with (X,Y) coordinates
+ std::string x = path_array[0];
+ std::string y = path_array[1];
+ return location + " (" + x + "," + y + ")";
+ }
+
+ return url;
+}
+
+std::string LLUrlEntryPlace::getLocation(const std::string &url) const
+{
+ // return the part of the Url after secondlife:// part
+ return ::getStringAfterToken(url, "://");
+}
+
+//
+// LLUrlEntryRegion Describes secondlife:///app/region/REGION_NAME/X/Y/Z URLs, e.g.
+// secondlife:///app/region/Ahern/128/128/0
+//
+LLUrlEntryRegion::LLUrlEntryRegion()
+{
+ mPattern = boost::regex("secondlife:///app/region/[A-Za-z0-9()_%]+(/\\d+)?(/\\d+)?(/\\d+)?/?",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_slurl.xml";
+ mTooltip = LLTrans::getString("TooltipSLURL");
+}
+
+std::string LLUrlEntryRegion::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ //
+ // we handle SLURLs in the following formats:
+ // - secondlife:///app/region/Place/X/Y/Z
+ // - secondlife:///app/region/Place/X/Y
+ // - secondlife:///app/region/Place/X
+ // - secondlife:///app/region/Place
+ //
+
+ LLSD path_array = LLURI(url).pathArray();
+ S32 path_parts = path_array.size();
+
+ if (path_parts < 3) // no region name
+ {
+ LL_WARNS() << "Failed to parse url [" << url << "]" << LL_ENDL;
+ return url;
+ }
+
+ std::string label = unescapeUrl(path_array[2]); // region name
+
+ if (path_parts > 3) // secondlife:///app/region/Place/X
+ {
+ std::string x = path_array[3];
+ label += " (" + x;
+
+ if (path_parts > 4) // secondlife:///app/region/Place/X/Y
+ {
+ std::string y = path_array[4];
+ label += "," + y;
+
+ if (path_parts > 5) // secondlife:///app/region/Place/X/Y/Z
+ {
+ std::string z = path_array[5];
+ label = label + "," + z;
+ }
+ }
+
+ label += ")";
+ }
+
+ return label;
+}
+
+std::string LLUrlEntryRegion::getLocation(const std::string &url) const
+{
+ LLSD path_array = LLURI(url).pathArray();
+ std::string region_name = unescapeUrl(path_array[2]);
+ return region_name;
+}
+
+//
+// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g.,
+// secondlife:///app/teleport/Ahern/50/50/50/
+// x-grid-location-info://lincoln.lindenlab.com/app/teleport/Ahern/50/50/50/
+//
+LLUrlEntryTeleport::LLUrlEntryTeleport()
+{
+ mPattern = boost::regex(APP_HEADER_REGEX "/teleport/\\S+(/\\d+)?(/\\d+)?(/\\d+)?/?\\S*",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_teleport.xml";
+ mTooltip = LLTrans::getString("TooltipTeleportUrl");
+}
+
+std::string LLUrlEntryTeleport::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ //
+ // we handle teleport SLURLs in the following formats:
+ // - secondlife:///app/teleport/Place/X/Y/Z
+ // - secondlife:///app/teleport/Place/X/Y
+ // - secondlife:///app/teleport/Place/X
+ // - secondlife:///app/teleport/Place
+ //
+ LLURI uri(url);
+ LLSD path_array = uri.pathArray();
+ S32 path_parts = path_array.size();
+ std::string host = uri.hostName();
+ std::string label = LLTrans::getString("SLurlLabelTeleport");
+ if (!host.empty())
+ {
+ label += " " + host;
+ }
+ if (path_parts == 6)
+ {
+ // handle teleport url with (X,Y,Z) coordinates
+ std::string location = unescapeUrl(path_array[path_parts-4]);
+ std::string x = path_array[path_parts-3];
+ std::string y = path_array[path_parts-2];
+ std::string z = path_array[path_parts-1];
+ return label + " " + location + " (" + x + "," + y + "," + z + ")";
+ }
+ else if (path_parts == 5)
+ {
+ // handle teleport url with (X,Y) coordinates
+ std::string location = unescapeUrl(path_array[path_parts-3]);
+ std::string x = path_array[path_parts-2];
+ std::string y = path_array[path_parts-1];
+ return label + " " + location + " (" + x + "," + y + ")";
+ }
+ else if (path_parts == 4)
+ {
+ // handle teleport url with (X) coordinate only
+ std::string location = unescapeUrl(path_array[path_parts-2]);
+ std::string x = path_array[path_parts-1];
+ return label + " " + location + " (" + x + ")";
+ }
+ else if (path_parts == 3)
+ {
+ // handle teleport url with no coordinates
+ std::string location = unescapeUrl(path_array[path_parts-1]);
+ return label + " " + location;
+ }
+
+ return url;
+}
+
+std::string LLUrlEntryTeleport::getLocation(const std::string &url) const
+{
+ // return the part of the Url after ///app/teleport
+ return ::getStringAfterToken(url, "app/teleport/");
+}
+
+//
+// LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts
+// with secondlife:// (used as a catch-all for cases not matched above)
+//
+LLUrlEntrySL::LLUrlEntrySL()
+{
+ mPattern = boost::regex("secondlife://(\\w+)?(:\\d+)?/\\S+",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_slapp.xml";
+ mTooltip = LLTrans::getString("TooltipSLAPP");
+}
+
+std::string LLUrlEntrySL::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return unescapeUrl(url);
+}
+
+//
+// LLUrlEntrySLLabel Describes a generic SLURL, e.g., a Url that starts
+/// with secondlife:// with the ability to specify a custom label.
+//
+LLUrlEntrySLLabel::LLUrlEntrySLLabel()
+{
+ mPattern = boost::regex("\\[secondlife://\\S+[ \t]+[^\\]]+\\]",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_slapp.xml";
+ mTooltip = LLTrans::getString("TooltipSLAPP");
+}
+
+std::string LLUrlEntrySLLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ std::string label = getLabelFromWikiLink(url);
+ return (!LLUrlRegistry::instance().hasUrl(label)) ? label : getUrl(url);
+}
+
+std::string LLUrlEntrySLLabel::getUrl(const std::string &string) const
+{
+ return getUrlFromWikiLink(string);
+}
+
+std::string LLUrlEntrySLLabel::getTooltip(const std::string &string) const
+{
+ // return a tooltip corresponding to the URL type instead of the generic one (EXT-4574)
+ std::string url = getUrl(string);
+ LLUrlMatch match;
+ if (LLUrlRegistry::instance().findUrl(url, match))
+ {
+ return match.getTooltip();
+ }
+
+ // unrecognized URL? should not happen
+ return LLUrlEntryBase::getTooltip(string);
+}
+
+bool LLUrlEntrySLLabel::underlineOnHoverOnly(const std::string &string) const
+{
+ std::string url = getUrl(string);
+ LLUrlMatch match;
+ if (LLUrlRegistry::instance().findUrl(url, match))
+ {
+ return match.underlineOnHoverOnly();
+ }
+
+ // unrecognized URL? should not happen
+ return LLUrlEntryBase::underlineOnHoverOnly(string);
+}
+
+//
+// LLUrlEntryWorldMap Describes secondlife:///<location> URLs
+//
+LLUrlEntryWorldMap::LLUrlEntryWorldMap()
+{
+ mPattern = boost::regex(APP_HEADER_REGEX "/worldmap/\\S+/?(\\d+)?/?(\\d+)?/?(\\d+)?/?\\S*",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_map.xml";
+ mTooltip = LLTrans::getString("TooltipMapUrl");
+}
+
+std::string LLUrlEntryWorldMap::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ //
+ // we handle SLURLs in the following formats:
+ // - secondlife:///app/worldmap/PLACE/X/Y/Z
+ // - secondlife:///app/worldmap/PLACE/X/Y
+ // - secondlife:///app/worldmap/PLACE/X
+ //
+ LLURI uri(url);
+ LLSD path_array = uri.pathArray();
+ S32 path_parts = path_array.size();
+ if (path_parts < 3)
+ {
+ return url;
+ }
+
+ const std::string label = LLTrans::getString("SLurlLabelShowOnMap");
+ std::string location = unescapeUrl(path_array[2]);
+ std::string x = (path_parts > 3) ? path_array[3] : "128";
+ std::string y = (path_parts > 4) ? path_array[4] : "128";
+ std::string z = (path_parts > 5) ? path_array[5] : "0";
+ return label + " " + location + " (" + x + "," + y + "," + z + ")";
+}
+
+std::string LLUrlEntryWorldMap::getLocation(const std::string &url) const
+{
+ // return the part of the Url after secondlife:///app/worldmap/ part
+ return ::getStringAfterToken(url, "app/worldmap/");
+}
+
+//
+// LLUrlEntryNoLink lets us turn of URL detection with <nolink>...</nolink> tags
+//
+LLUrlEntryNoLink::LLUrlEntryNoLink()
+{
+ mPattern = boost::regex("<nolink>.*?</nolink>",
+ boost::regex::perl|boost::regex::icase);
+}
+
+std::string LLUrlEntryNoLink::getUrl(const std::string &url) const
+{
+ // return the text between the <nolink> and </nolink> tags
+ return url.substr(8, url.size()-8-9);
+}
+
+std::string LLUrlEntryNoLink::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return getUrl(url);
+}
+
+LLStyle::Params LLUrlEntryNoLink::getStyle() const
+{
+ // Don't render as URL (i.e. no context menu or hand cursor).
+ return LLStyle::Params().is_link(false);
+}
+
+
+//
+// LLUrlEntryIcon describes an icon with <icon>...</icon> tags
+//
+LLUrlEntryIcon::LLUrlEntryIcon()
+{
+ mPattern = boost::regex("<icon\\s*>\\s*([^<]*)?\\s*</icon\\s*>",
+ boost::regex::perl|boost::regex::icase);
+}
+
+std::string LLUrlEntryIcon::getUrl(const std::string &url) const
+{
+ return LLStringUtil::null;
+}
+
+std::string LLUrlEntryIcon::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return LLStringUtil::null;
+}
+
+std::string LLUrlEntryIcon::getIcon(const std::string &url)
+{
+ // Grep icon info between <icon>...</icon> tags
+ // matches[1] contains the icon name/path
+ boost::match_results<std::string::const_iterator> matches;
+ mIcon = (ll_regex_match(url, matches, mPattern) && matches[1].matched)
+ ? matches[1]
+ : LLStringUtil::null;
+ LLStringUtil::trim(mIcon);
+ return mIcon;
+}
+
+//
+// LLUrlEntryEmail Describes a generic mailto: Urls
+//
+LLUrlEntryEmail::LLUrlEntryEmail()
+ : LLUrlEntryBase()
+{
+ mPattern = boost::regex("(mailto:)?[\\w\\.\\-]+@[\\w\\.\\-]+\\.[a-z]{2,63}",
+ boost::regex::perl | boost::regex::icase);
+ mMenuName = "menu_url_email.xml";
+ mTooltip = LLTrans::getString("TooltipEmail");
+}
+
+std::string LLUrlEntryEmail::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ int pos = url.find("mailto:");
+
+ if (pos == std::string::npos)
+ {
+ return escapeUrl(url);
+ }
+
+ std::string ret = escapeUrl(url.substr(pos + 7, url.length() - pos + 8));
+ return ret;
+}
+
+std::string LLUrlEntryEmail::getUrl(const std::string &string) const
+{
+ if (string.find("mailto:") == std::string::npos)
+ {
+ return "mailto:" + escapeUrl(string);
+ }
+ return escapeUrl(string);
+}
+
+LLUrlEntryExperienceProfile::LLUrlEntryExperienceProfile()
+{
+ mPattern = boost::regex(APP_HEADER_REGEX "/experience/[\\da-f-]+/profile",
+ boost::regex::perl|boost::regex::icase);
+ mIcon = "Generic_Experience";
+ mMenuName = "menu_url_experience.xml";
+}
+
+std::string LLUrlEntryExperienceProfile::getLabel( const std::string &url, const LLUrlLabelCallback &cb )
+{
+ if (!gCacheName)
+ {
+ // probably at the login screen, use short string for layout
+ return LLTrans::getString("LoadingData");
+ }
+
+ std::string experience_id_string = getIDStringFromUrl(url);
+ if (experience_id_string.empty())
+ {
+ // something went wrong, just give raw url
+ return unescapeUrl(url);
+ }
+
+ LLUUID experience_id(experience_id_string);
+ if (experience_id.isNull())
+ {
+ return LLTrans::getString("ExperienceNameNull");
+ }
+
+ const LLSD& experience_details = LLExperienceCache::instance().get(experience_id);
+ if(!experience_details.isUndefined())
+ {
+ std::string experience_name_string = experience_details[LLExperienceCache::NAME].asString();
+ return experience_name_string.empty() ? LLTrans::getString("ExperienceNameUntitled") : experience_name_string;
+ }
+
+ addObserver(experience_id_string, url, cb);
+ LLExperienceCache::instance().get(experience_id, boost::bind(&LLUrlEntryExperienceProfile::onExperienceDetails, this, _1));
+ return LLTrans::getString("LoadingData");
+
+}
+
+void LLUrlEntryExperienceProfile::onExperienceDetails( const LLSD& experience_details )
+{
+ std::string name = experience_details[LLExperienceCache::NAME].asString();
+ if(name.empty())
+ {
+ name = LLTrans::getString("ExperienceNameUntitled");
+ }
+ callObservers(experience_details[LLExperienceCache::EXPERIENCE_ID].asString(), name, LLStringUtil::null);
+}
+
+//
+// LLUrlEntryEmail Describes an IPv6 address
+//
+LLUrlEntryIPv6::LLUrlEntryIPv6()
+ : LLUrlEntryBase()
+{
+ mHostPath = "https?://\\[([a-f0-9:]+:+)+[a-f0-9]+]";
+ mPattern = boost::regex(mHostPath + "(:\\d{1,5})?(/\\S*)?",
+ boost::regex::perl | boost::regex::icase);
+ mMenuName = "menu_url_http.xml";
+ mTooltip = LLTrans::getString("TooltipHttpUrl");
+}
+
+std::string LLUrlEntryIPv6::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ boost::regex regex = boost::regex(mHostPath, boost::regex::perl | boost::regex::icase);
+ boost::match_results<std::string::const_iterator> matches;
+
+ if (boost::regex_search(url, matches, regex))
+ {
+ return url.substr(0, matches[0].length());
+ }
+ else
+ {
+ return url;
+ }
+}
+
+std::string LLUrlEntryIPv6::getQuery(const std::string &url) const
+{
+ boost::regex regex = boost::regex(mHostPath, boost::regex::perl | boost::regex::icase);
+ boost::match_results<std::string::const_iterator> matches;
+
+ return boost::regex_replace(url, regex, "");
+}
+
+std::string LLUrlEntryIPv6::getUrl(const std::string &string) const
+{
+ return string;
+}
+
+
+//
+// LLUrlEntryKeybinding Displays currently assigned key
+//
+LLUrlEntryKeybinding::LLUrlEntryKeybinding()
+ : LLUrlEntryBase()
+ , pHandler(NULL)
+{
+ mPattern = boost::regex(APP_HEADER_REGEX "/keybinding/\\w+(\\?mode=\\w+)?$",
+ boost::regex::perl | boost::regex::icase);
+ mMenuName = "menu_url_experience.xml";
+
+ initLocalization();
+}
+
+std::string LLUrlEntryKeybinding::getLabel(const std::string& url, const LLUrlLabelCallback& cb)
+{
+ std::string control = getControlName(url);
+
+ std::map<std::string, LLLocalizationData>::iterator iter = mLocalizations.find(control);
+
+ std::string keybind;
+ if (pHandler)
+ {
+ keybind = pHandler->getKeyBindingAsString(getMode(url), control);
+ }
+
+ if (iter != mLocalizations.end())
+ {
+ return iter->second.mLocalization + ": " + keybind;
+ }
+
+ return control + ": " + keybind;
+}
+
+std::string LLUrlEntryKeybinding::getTooltip(const std::string& url) const
+{
+ std::string control = getControlName(url);
+
+ std::map<std::string, LLLocalizationData>::const_iterator iter = mLocalizations.find(control);
+ if (iter != mLocalizations.end())
+ {
+ return iter->second.mTooltip;
+ }
+ return url;
+}
+
+std::string LLUrlEntryKeybinding::getControlName(const std::string& url) const
+{
+ std::string search = "/keybinding/";
+ size_t pos_start = url.find(search);
+ if (pos_start == std::string::npos)
+ {
+ return std::string();
+ }
+ pos_start += search.size();
+
+ size_t pos_end = url.find("?mode=");
+ if (pos_end == std::string::npos)
+ {
+ pos_end = url.size();
+ }
+ return url.substr(pos_start, pos_end - pos_start);
+}
+
+std::string LLUrlEntryKeybinding::getMode(const std::string& url) const
+{
+ std::string search = "?mode=";
+ size_t pos_start = url.find(search);
+ if (pos_start == std::string::npos)
+ {
+ return std::string();
+ }
+ pos_start += search.size();
+ return url.substr(pos_start, url.size() - pos_start);
+}
+
+void LLUrlEntryKeybinding::initLocalization()
+{
+ initLocalizationFromFile("control_table_contents_movement.xml");
+ initLocalizationFromFile("control_table_contents_camera.xml");
+ initLocalizationFromFile("control_table_contents_editing.xml");
+ initLocalizationFromFile("control_table_contents_media.xml");
+}
+
+void LLUrlEntryKeybinding::initLocalizationFromFile(const std::string& filename)
+{
+ LLXMLNodePtr xmlNode;
+ LLScrollListCtrl::Contents contents;
+ if (!LLUICtrlFactory::getLayeredXMLNode(filename, xmlNode))
+ {
+ LL_WARNS() << "Failed to load " << filename << LL_ENDL;
+ return;
+ }
+ LLXUIParser parser;
+ parser.readXUI(xmlNode, contents, filename);
+
+ if (!contents.validateBlock())
+ {
+ LL_WARNS() << "Failed to validate " << filename << LL_ENDL;
+ return;
+ }
+
+ for (LLInitParam::ParamIterator<LLScrollListItem::Params>::const_iterator row_it = contents.rows.begin();
+ row_it != contents.rows.end();
+ ++row_it)
+ {
+ std::string control = row_it->value.getValue().asString();
+ if (!control.empty() && control != "menu_separator")
+ {
+ mLocalizations[control] =
+ LLLocalizationData(
+ row_it->columns.begin()->value.getValue().asString(),
+ row_it->columns.begin()->tool_tip.getValue()
+ );
+ }
+ }
+}
diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h
index 7d3728d790..463a97afb0 100644
--- a/indra/llui/llurlentry.h
+++ b/indra/llui/llurlentry.h
@@ -1,586 +1,586 @@
-/**
- * @file llurlentry.h
- * @author Martin Reddy
- * @brief Describes the Url types that can be registered in LLUrlRegistry
- *
- * $LicenseInfo:firstyear=2009&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLURLENTRY_H
-#define LL_LLURLENTRY_H
-
-#include "lluuid.h"
-#include "lluicolor.h"
-#include "llstyle.h"
-
-#include "llavatarname.h"
-#include "llhost.h" // for resolving parcel name by parcel id
-
-#include <boost/signals2.hpp>
-#include <boost/regex.hpp>
-#include <string>
-#include <map>
-
-class LLAvatarName;
-
-typedef boost::signals2::signal<void (const std::string& url,
- const std::string& label,
- const std::string& icon)> LLUrlLabelSignal;
-typedef LLUrlLabelSignal::slot_type LLUrlLabelCallback;
-
-///
-/// LLUrlEntryBase is the base class of all Url types registered in the
-/// LLUrlRegistry. Each derived classes provides a regular expression
-/// to match the Url type (e.g., http://... or secondlife://...) along
-/// with an optional icon to display next to instances of the Url in
-/// a text display and a XUI file to use for any context menu popup.
-/// Functions are also provided to compute an appropriate label and
-/// tooltip/status bar text for the Url.
-///
-/// Some derived classes of LLUrlEntryBase may wish to compute an
-/// appropriate label for a Url by asking the server for information.
-/// You must therefore provide a callback method, so that you can be
-/// notified when an updated label has been received from the server.
-/// This label should then be used to replace any previous label
-/// that you received from getLabel() for the Url in question.
-///
-class LLUrlEntryBase
-{
-public:
- LLUrlEntryBase();
- virtual ~LLUrlEntryBase();
-
- /// Return the regex pattern that matches this Url
- boost::regex getPattern() const { return mPattern; }
-
- /// Return the url from a string that matched the regex
- virtual std::string getUrl(const std::string &string) const;
-
- /// Given a matched Url, return a label for the Url
- virtual std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) { return url; }
-
- /// Return 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);
-
- /// Return the style to render the displayed text
- virtual LLStyle::Params getStyle() const;
-
- /// Given a matched Url, return a tooltip string for the hyperlink
- virtual std::string getTooltip(const std::string &string) const { return mTooltip; }
-
- /// Return the name of a XUI file containing the context menu items
- std::string getMenuName() const { return mMenuName; }
-
- /// Return the name of a SL location described by this Url, if any
- virtual std::string getLocation(const std::string &url) const { return ""; }
-
- /// 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(const std::string &url) const;
-
- 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;
- std::string unescapeUrl(const std::string &url) const;
- std::string getLabelFromWikiLink(const std::string &url) const;
- std::string getUrlFromWikiLink(const std::string &string) const;
- void addObserver(const std::string &id, const std::string &url, const LLUrlLabelCallback &cb);
- 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 {
- std::string url;
- LLUrlLabelSignal *signal;
- } LLUrlEntryObserver;
-
- boost::regex mPattern;
- std::string mIcon;
- std::string mMenuName;
- std::string mTooltip;
- std::multimap<std::string, LLUrlEntryObserver> mObservers;
-};
-
-///
-/// LLUrlEntryHTTP Describes generic http: and https: Urls
-///
-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;
-};
-
-///
-/// LLUrlEntryHTTPLabel Describes generic http: and https: Urls with custom labels
-///
-class LLUrlEntryHTTPLabel : public LLUrlEntryBase
-{
-public:
- LLUrlEntryHTTPLabel();
- /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
- /*virtual*/ std::string getTooltip(const std::string &string) const;
- /*virtual*/ std::string getUrl(const std::string &string) 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;
-};
-
-///
-/// LLUrlEntrySLURL Describes http://slurl.com/... Urls
-///
-class LLUrlEntrySLURL : public LLUrlEntryBase
-{
-public:
- LLUrlEntrySLURL();
- /*virtual*/ bool isTrusted() const { return true; }
- /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
- /*virtual*/ std::string getLocation(const std::string &url) const;
-};
-
-///
-/// 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()
- {
- for (avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.begin(); it != mAvatarNameCacheConnections.end(); ++it)
- {
- if (it->second.connected())
- {
- it->second.disconnect();
- }
- }
- mAvatarNameCacheConnections.clear();
- }
- /*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;
- /*virtual*/ LLStyle::Params getStyle() const;
- /*virtual*/ LLUUID getID(const std::string &string) const;
- /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const;
-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);
-
- typedef std::map<LLUUID, boost::signals2::connection> avatar_name_cache_connection_map_t;
- avatar_name_cache_connection_map_t mAvatarNameCacheConnections;
-};
-
-///
-/// LLUrlEntryAgentName Describes a Second Life agent name Url, e.g.,
-/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username)
-/// that displays various forms of user name
-/// This is a base class for the various implementations of name display
-class LLUrlEntryAgentName : public LLUrlEntryBase, public boost::signals2::trackable
-{
-public:
- LLUrlEntryAgentName();
- ~LLUrlEntryAgentName()
- {
- for (avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.begin(); it != mAvatarNameCacheConnections.end(); ++it)
- {
- if (it->second.connected())
- {
- it->second.disconnect();
- }
- }
- mAvatarNameCacheConnections.clear();
- }
- /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
- /*virtual*/ LLStyle::Params getStyle() const;
-protected:
- // override this to pull out relevant name fields
- virtual std::string getName(const LLAvatarName& avatar_name) = 0;
-private:
- void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name);
-
- typedef std::map<LLUUID, boost::signals2::connection> avatar_name_cache_connection_map_t;
- avatar_name_cache_connection_map_t mAvatarNameCacheConnections;
-};
-
-
-///
-/// LLUrlEntryAgentCompleteName Describes a Second Life agent name Url, e.g.,
-/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename
-/// that displays the full display name + user name for an avatar
-/// such as "James Linden (james.linden)"
-class LLUrlEntryAgentCompleteName : public LLUrlEntryAgentName
-{
-public:
- LLUrlEntryAgentCompleteName();
-private:
- /*virtual*/ std::string getName(const LLAvatarName& avatar_name);
-};
-
-class LLUrlEntryAgentLegacyName : public LLUrlEntryAgentName
-{
-public:
- LLUrlEntryAgentLegacyName();
-private:
- /*virtual*/ std::string getName(const LLAvatarName& avatar_name);
-};
-
-///
-/// LLUrlEntryAgentDisplayName Describes a Second Life agent display name Url, e.g.,
-/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname
-/// that displays the just the display name for an avatar
-/// such as "James Linden"
-class LLUrlEntryAgentDisplayName : public LLUrlEntryAgentName
-{
-public:
- LLUrlEntryAgentDisplayName();
-private:
- /*virtual*/ std::string getName(const LLAvatarName& avatar_name);
-};
-
-///
-/// LLUrlEntryAgentUserName Describes a Second Life agent username Url, e.g.,
-/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username
-/// that displays the just the display name for an avatar
-/// such as "james.linden"
-class LLUrlEntryAgentUserName : public LLUrlEntryAgentName
-{
-public:
- LLUrlEntryAgentUserName();
-private:
- /*virtual*/ std::string getName(const LLAvatarName& avatar_name);
-};
-
-///
-/// LLUrlEntryExperienceProfile Describes a Second Life experience profile Url, e.g.,
-/// secondlife:///app/experience/0e346d8b-4433-4d66-a6b0-fd37083abc4c/profile
-/// that displays the experience name
-class LLUrlEntryExperienceProfile : public LLUrlEntryBase
-{
-public:
- LLUrlEntryExperienceProfile();
- /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
-private:
- void onExperienceDetails(const LLSD& experience_details);
-};
-
-
-///
-/// LLUrlEntryGroup Describes a Second Life group Url, e.g.,
-/// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about
-///
-class LLUrlEntryGroup : public LLUrlEntryBase
-{
-public:
- LLUrlEntryGroup();
- /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
- /*virtual*/ LLStyle::Params getStyle() const;
- /*virtual*/ LLUUID getID(const std::string &string) const;
-private:
- void onGroupNameReceived(const LLUUID& id, const std::string& name, bool is_group);
-};
-
-///
-/// LLUrlEntryInventory Describes a Second Life inventory Url, e.g.,
-/// secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select
-///
-class LLUrlEntryInventory : public LLUrlEntryBase
-{
-public:
- LLUrlEntryInventory();
- /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
-private:
-};
-
-///
-/// LLUrlEntryObjectIM Describes a Second Life inspector for the object Url, e.g.,
-/// secondlife:///app/objectim/7bcd7864-da6b-e43f-4486-91d28a28d95b?name=Object&owner=3de548e1-57be-cfea-2b78-83ae3ad95998&slurl=Danger!%20Danger!/200/200/30/&groupowned=1
-///
-class LLUrlEntryObjectIM : public LLUrlEntryBase
-{
-public:
- LLUrlEntryObjectIM();
- /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
- /*virtual*/ std::string getLocation(const std::string &url) const;
-private:
-};
-
-//
-// LLUrlEntryChat Describes a Second Life chat Url, e.g.,
-// secondlife:///app/chat/42/This%20Is%20a%20test
-//
-class LLUrlEntryChat : public LLUrlEntryBase
-{
-public:
- LLUrlEntryChat();
- /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
-};
-
-///
-/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
-/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
-///
-class LLUrlEntryParcel : public LLUrlEntryBase
-{
-public:
- struct LLParcelData
- {
- LLUUID parcel_id;
- std::string name;
- std::string sim_name;
- F32 global_x;
- F32 global_y;
- F32 global_z;
- };
-
- LLUrlEntryParcel();
- ~LLUrlEntryParcel();
- /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
-
- // Sends a parcel info request to sim.
- void sendParcelInfoRequest(const LLUUID& parcel_id);
-
- // Calls observers of certain parcel id providing them with parcel label.
- void onParcelInfoReceived(const std::string &id, const std::string &label);
-
- // Processes parcel label and triggers notifying observers.
- static void processParcelInfo(const LLParcelData& parcel_data);
-
- // Next 4 setters are used to update agent and viewer connection information
- // upon events like user login, viewer disconnect and user changing region host.
- // These setters are made public to be accessible from newview and should not be
- // used in other cases.
- static void setAgentID(const LLUUID& id) { sAgentID = id; }
- static void setSessionID(const LLUUID& id) { sSessionID = id; }
- static void setRegionHost(const LLHost& host) { sRegionHost = host; }
- static void setDisconnected(bool disconnected) { sDisconnected = disconnected; }
-
-private:
- static LLUUID sAgentID;
- static LLUUID sSessionID;
- static LLHost sRegionHost;
- static bool sDisconnected;
- static std::set<LLUrlEntryParcel*> sParcelInfoObservers;
-};
-
-///
-/// LLUrlEntryPlace Describes a Second Life location Url, e.g.,
-/// secondlife://Ahern/50/50/50
-///
-class LLUrlEntryPlace : public LLUrlEntryBase
-{
-public:
- LLUrlEntryPlace();
- /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
- /*virtual*/ std::string getLocation(const std::string &url) const;
-};
-
-///
-/// LLUrlEntryRegion Describes a Second Life location Url, e.g.,
-/// secondlife:///app/region/Ahern/128/128/0
-///
-class LLUrlEntryRegion : public LLUrlEntryBase
-{
-public:
- LLUrlEntryRegion();
- /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
- /*virtual*/ std::string getLocation(const std::string &url) const;
-};
-
-///
-/// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g.,
-/// secondlife:///app/teleport/Ahern/50/50/50/
-///
-class LLUrlEntryTeleport : public LLUrlEntryBase
-{
-public:
- LLUrlEntryTeleport();
- /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
- /*virtual*/ std::string getLocation(const std::string &url) const;
-};
-
-///
-/// LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts
-/// with secondlife:// (used as a catch-all for cases not matched above)
-///
-class LLUrlEntrySL : public LLUrlEntryBase
-{
-public:
- LLUrlEntrySL();
- /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
-};
-
-///
-/// LLUrlEntrySLLabel Describes a generic SLURL, e.g., a Url that starts
-/// with secondlife:// with the ability to specify a custom label.
-///
-class LLUrlEntrySLLabel : public LLUrlEntryBase
-{
-public:
- LLUrlEntrySLLabel();
- /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
- /*virtual*/ std::string getUrl(const std::string &string) const;
- /*virtual*/ std::string getTooltip(const std::string &string) const;
- /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const;
-};
-
-///
-/// LLUrlEntryWorldMap Describes a Second Life worldmap Url, e.g.,
-/// secondlife:///app/worldmap/Ahern/50/50/50
-///
-class LLUrlEntryWorldMap : public LLUrlEntryBase
-{
-public:
- LLUrlEntryWorldMap();
- /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
- /*virtual*/ std::string getLocation(const std::string &url) const;
-};
-
-///
-/// LLUrlEntryNoLink lets us turn of URL detection with <nolink>...</nolink> tags
-///
-class LLUrlEntryNoLink : public LLUrlEntryBase
-{
-public:
- LLUrlEntryNoLink();
- /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
- /*virtual*/ std::string getUrl(const std::string &string) const;
- /*virtual*/ LLStyle::Params getStyle() const;
-};
-
-///
-/// LLUrlEntryIcon describes an icon with <icon>...</icon> tags
-///
-class LLUrlEntryIcon : public LLUrlEntryBase
-{
-public:
- LLUrlEntryIcon();
- /*virtual*/ std::string getUrl(const std::string &string) const;
- /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
- /*virtual*/ std::string getIcon(const std::string &url);
-};
-
-///
-/// LLUrlEntryEmail Describes a generic mailto: Urls
-///
-class LLUrlEntryEmail : public LLUrlEntryBase
-{
-public:
- LLUrlEntryEmail();
- /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
- /*virtual*/ std::string getUrl(const std::string &string) const;
-};
-
-///
-/// LLUrlEntryEmail Describes an IPv6 address
-///
-class LLUrlEntryIPv6 : public LLUrlEntryBase
-{
-public:
- LLUrlEntryIPv6();
- /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
- /*virtual*/ std::string getUrl(const std::string &string) const;
- /*virtual*/ std::string getQuery(const std::string &url) const;
-
- std::string mHostPath;
-};
-
-class LLKeyBindingToStringHandler;
-
-///
-/// LLUrlEntryKeybinding A way to access keybindings and show currently used one in text.
-/// secondlife:///app/keybinding/control_name
-class LLUrlEntryKeybinding: public LLUrlEntryBase
-{
-public:
- LLUrlEntryKeybinding();
- /*virtual*/ std::string getLabel(const std::string& url, const LLUrlLabelCallback& cb);
- /*virtual*/ std::string getTooltip(const std::string& url) const;
- void setHandler(LLKeyBindingToStringHandler* handler) {pHandler = handler;}
-private:
- std::string getControlName(const std::string& url) const;
- std::string getMode(const std::string& url) const;
- void initLocalization();
- void initLocalizationFromFile(const std::string& filename);
-
- struct LLLocalizationData
- {
- LLLocalizationData() {}
- LLLocalizationData(const std::string& localization, const std::string& tooltip)
- : mLocalization(localization)
- , mTooltip(tooltip)
- {}
- std::string mLocalization;
- std::string mTooltip;
- };
-
- std::map<std::string, LLLocalizationData> mLocalizations;
- LLKeyBindingToStringHandler* pHandler;
-};
-
-#endif
+/**
+ * @file llurlentry.h
+ * @author Martin Reddy
+ * @brief Describes the Url types that can be registered in LLUrlRegistry
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLURLENTRY_H
+#define LL_LLURLENTRY_H
+
+#include "lluuid.h"
+#include "lluicolor.h"
+#include "llstyle.h"
+
+#include "llavatarname.h"
+#include "llhost.h" // for resolving parcel name by parcel id
+
+#include <boost/signals2.hpp>
+#include <boost/regex.hpp>
+#include <string>
+#include <map>
+
+class LLAvatarName;
+
+typedef boost::signals2::signal<void (const std::string& url,
+ const std::string& label,
+ const std::string& icon)> LLUrlLabelSignal;
+typedef LLUrlLabelSignal::slot_type LLUrlLabelCallback;
+
+///
+/// LLUrlEntryBase is the base class of all Url types registered in the
+/// LLUrlRegistry. Each derived classes provides a regular expression
+/// to match the Url type (e.g., http://... or secondlife://...) along
+/// with an optional icon to display next to instances of the Url in
+/// a text display and a XUI file to use for any context menu popup.
+/// Functions are also provided to compute an appropriate label and
+/// tooltip/status bar text for the Url.
+///
+/// Some derived classes of LLUrlEntryBase may wish to compute an
+/// appropriate label for a Url by asking the server for information.
+/// You must therefore provide a callback method, so that you can be
+/// notified when an updated label has been received from the server.
+/// This label should then be used to replace any previous label
+/// that you received from getLabel() for the Url in question.
+///
+class LLUrlEntryBase
+{
+public:
+ LLUrlEntryBase();
+ virtual ~LLUrlEntryBase();
+
+ /// Return the regex pattern that matches this Url
+ boost::regex getPattern() const { return mPattern; }
+
+ /// Return the url from a string that matched the regex
+ virtual std::string getUrl(const std::string &string) const;
+
+ /// Given a matched Url, return a label for the Url
+ virtual std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) { return url; }
+
+ /// Return 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);
+
+ /// Return the style to render the displayed text
+ virtual LLStyle::Params getStyle() const;
+
+ /// Given a matched Url, return a tooltip string for the hyperlink
+ virtual std::string getTooltip(const std::string &string) const { return mTooltip; }
+
+ /// Return the name of a XUI file containing the context menu items
+ std::string getMenuName() const { return mMenuName; }
+
+ /// Return the name of a SL location described by this Url, if any
+ virtual std::string getLocation(const std::string &url) const { return ""; }
+
+ /// 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(const std::string &url) const;
+
+ 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;
+ std::string unescapeUrl(const std::string &url) const;
+ std::string getLabelFromWikiLink(const std::string &url) const;
+ std::string getUrlFromWikiLink(const std::string &string) const;
+ void addObserver(const std::string &id, const std::string &url, const LLUrlLabelCallback &cb);
+ 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 {
+ std::string url;
+ LLUrlLabelSignal *signal;
+ } LLUrlEntryObserver;
+
+ boost::regex mPattern;
+ std::string mIcon;
+ std::string mMenuName;
+ std::string mTooltip;
+ std::multimap<std::string, LLUrlEntryObserver> mObservers;
+};
+
+///
+/// LLUrlEntryHTTP Describes generic http: and https: Urls
+///
+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;
+};
+
+///
+/// LLUrlEntryHTTPLabel Describes generic http: and https: Urls with custom labels
+///
+class LLUrlEntryHTTPLabel : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryHTTPLabel();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getTooltip(const std::string &string) const;
+ /*virtual*/ std::string getUrl(const std::string &string) 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;
+};
+
+///
+/// LLUrlEntrySLURL Describes http://slurl.com/... Urls
+///
+class LLUrlEntrySLURL : public LLUrlEntryBase
+{
+public:
+ LLUrlEntrySLURL();
+ /*virtual*/ bool isTrusted() const { return true; }
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getLocation(const std::string &url) const;
+};
+
+///
+/// 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()
+ {
+ for (avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.begin(); it != mAvatarNameCacheConnections.end(); ++it)
+ {
+ if (it->second.connected())
+ {
+ it->second.disconnect();
+ }
+ }
+ mAvatarNameCacheConnections.clear();
+ }
+ /*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;
+ /*virtual*/ LLStyle::Params getStyle() const;
+ /*virtual*/ LLUUID getID(const std::string &string) const;
+ /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const;
+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);
+
+ typedef std::map<LLUUID, boost::signals2::connection> avatar_name_cache_connection_map_t;
+ avatar_name_cache_connection_map_t mAvatarNameCacheConnections;
+};
+
+///
+/// LLUrlEntryAgentName Describes a Second Life agent name Url, e.g.,
+/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username)
+/// that displays various forms of user name
+/// This is a base class for the various implementations of name display
+class LLUrlEntryAgentName : public LLUrlEntryBase, public boost::signals2::trackable
+{
+public:
+ LLUrlEntryAgentName();
+ ~LLUrlEntryAgentName()
+ {
+ for (avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.begin(); it != mAvatarNameCacheConnections.end(); ++it)
+ {
+ if (it->second.connected())
+ {
+ it->second.disconnect();
+ }
+ }
+ mAvatarNameCacheConnections.clear();
+ }
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ LLStyle::Params getStyle() const;
+protected:
+ // override this to pull out relevant name fields
+ virtual std::string getName(const LLAvatarName& avatar_name) = 0;
+private:
+ void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name);
+
+ typedef std::map<LLUUID, boost::signals2::connection> avatar_name_cache_connection_map_t;
+ avatar_name_cache_connection_map_t mAvatarNameCacheConnections;
+};
+
+
+///
+/// LLUrlEntryAgentCompleteName Describes a Second Life agent name Url, e.g.,
+/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename
+/// that displays the full display name + user name for an avatar
+/// such as "James Linden (james.linden)"
+class LLUrlEntryAgentCompleteName : public LLUrlEntryAgentName
+{
+public:
+ LLUrlEntryAgentCompleteName();
+private:
+ /*virtual*/ std::string getName(const LLAvatarName& avatar_name);
+};
+
+class LLUrlEntryAgentLegacyName : public LLUrlEntryAgentName
+{
+public:
+ LLUrlEntryAgentLegacyName();
+private:
+ /*virtual*/ std::string getName(const LLAvatarName& avatar_name);
+};
+
+///
+/// LLUrlEntryAgentDisplayName Describes a Second Life agent display name Url, e.g.,
+/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname
+/// that displays the just the display name for an avatar
+/// such as "James Linden"
+class LLUrlEntryAgentDisplayName : public LLUrlEntryAgentName
+{
+public:
+ LLUrlEntryAgentDisplayName();
+private:
+ /*virtual*/ std::string getName(const LLAvatarName& avatar_name);
+};
+
+///
+/// LLUrlEntryAgentUserName Describes a Second Life agent username Url, e.g.,
+/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username
+/// that displays the just the display name for an avatar
+/// such as "james.linden"
+class LLUrlEntryAgentUserName : public LLUrlEntryAgentName
+{
+public:
+ LLUrlEntryAgentUserName();
+private:
+ /*virtual*/ std::string getName(const LLAvatarName& avatar_name);
+};
+
+///
+/// LLUrlEntryExperienceProfile Describes a Second Life experience profile Url, e.g.,
+/// secondlife:///app/experience/0e346d8b-4433-4d66-a6b0-fd37083abc4c/profile
+/// that displays the experience name
+class LLUrlEntryExperienceProfile : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryExperienceProfile();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+private:
+ void onExperienceDetails(const LLSD& experience_details);
+};
+
+
+///
+/// LLUrlEntryGroup Describes a Second Life group Url, e.g.,
+/// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about
+///
+class LLUrlEntryGroup : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryGroup();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ LLStyle::Params getStyle() const;
+ /*virtual*/ LLUUID getID(const std::string &string) const;
+private:
+ void onGroupNameReceived(const LLUUID& id, const std::string& name, bool is_group);
+};
+
+///
+/// LLUrlEntryInventory Describes a Second Life inventory Url, e.g.,
+/// secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select
+///
+class LLUrlEntryInventory : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryInventory();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+private:
+};
+
+///
+/// LLUrlEntryObjectIM Describes a Second Life inspector for the object Url, e.g.,
+/// secondlife:///app/objectim/7bcd7864-da6b-e43f-4486-91d28a28d95b?name=Object&owner=3de548e1-57be-cfea-2b78-83ae3ad95998&slurl=Danger!%20Danger!/200/200/30/&groupowned=1
+///
+class LLUrlEntryObjectIM : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryObjectIM();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getLocation(const std::string &url) const;
+private:
+};
+
+//
+// LLUrlEntryChat Describes a Second Life chat Url, e.g.,
+// secondlife:///app/chat/42/This%20Is%20a%20test
+//
+class LLUrlEntryChat : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryChat();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+};
+
+///
+/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
+/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
+///
+class LLUrlEntryParcel : public LLUrlEntryBase
+{
+public:
+ struct LLParcelData
+ {
+ LLUUID parcel_id;
+ std::string name;
+ std::string sim_name;
+ F32 global_x;
+ F32 global_y;
+ F32 global_z;
+ };
+
+ LLUrlEntryParcel();
+ ~LLUrlEntryParcel();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+
+ // Sends a parcel info request to sim.
+ void sendParcelInfoRequest(const LLUUID& parcel_id);
+
+ // Calls observers of certain parcel id providing them with parcel label.
+ void onParcelInfoReceived(const std::string &id, const std::string &label);
+
+ // Processes parcel label and triggers notifying observers.
+ static void processParcelInfo(const LLParcelData& parcel_data);
+
+ // Next 4 setters are used to update agent and viewer connection information
+ // upon events like user login, viewer disconnect and user changing region host.
+ // These setters are made public to be accessible from newview and should not be
+ // used in other cases.
+ static void setAgentID(const LLUUID& id) { sAgentID = id; }
+ static void setSessionID(const LLUUID& id) { sSessionID = id; }
+ static void setRegionHost(const LLHost& host) { sRegionHost = host; }
+ static void setDisconnected(bool disconnected) { sDisconnected = disconnected; }
+
+private:
+ static LLUUID sAgentID;
+ static LLUUID sSessionID;
+ static LLHost sRegionHost;
+ static bool sDisconnected;
+ static std::set<LLUrlEntryParcel*> sParcelInfoObservers;
+};
+
+///
+/// LLUrlEntryPlace Describes a Second Life location Url, e.g.,
+/// secondlife://Ahern/50/50/50
+///
+class LLUrlEntryPlace : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryPlace();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getLocation(const std::string &url) const;
+};
+
+///
+/// LLUrlEntryRegion Describes a Second Life location Url, e.g.,
+/// secondlife:///app/region/Ahern/128/128/0
+///
+class LLUrlEntryRegion : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryRegion();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getLocation(const std::string &url) const;
+};
+
+///
+/// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g.,
+/// secondlife:///app/teleport/Ahern/50/50/50/
+///
+class LLUrlEntryTeleport : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryTeleport();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getLocation(const std::string &url) const;
+};
+
+///
+/// LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts
+/// with secondlife:// (used as a catch-all for cases not matched above)
+///
+class LLUrlEntrySL : public LLUrlEntryBase
+{
+public:
+ LLUrlEntrySL();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+};
+
+///
+/// LLUrlEntrySLLabel Describes a generic SLURL, e.g., a Url that starts
+/// with secondlife:// with the ability to specify a custom label.
+///
+class LLUrlEntrySLLabel : public LLUrlEntryBase
+{
+public:
+ LLUrlEntrySLLabel();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getUrl(const std::string &string) const;
+ /*virtual*/ std::string getTooltip(const std::string &string) const;
+ /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const;
+};
+
+///
+/// LLUrlEntryWorldMap Describes a Second Life worldmap Url, e.g.,
+/// secondlife:///app/worldmap/Ahern/50/50/50
+///
+class LLUrlEntryWorldMap : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryWorldMap();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getLocation(const std::string &url) const;
+};
+
+///
+/// LLUrlEntryNoLink lets us turn of URL detection with <nolink>...</nolink> tags
+///
+class LLUrlEntryNoLink : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryNoLink();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getUrl(const std::string &string) const;
+ /*virtual*/ LLStyle::Params getStyle() const;
+};
+
+///
+/// LLUrlEntryIcon describes an icon with <icon>...</icon> tags
+///
+class LLUrlEntryIcon : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryIcon();
+ /*virtual*/ std::string getUrl(const std::string &string) const;
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getIcon(const std::string &url);
+};
+
+///
+/// LLUrlEntryEmail Describes a generic mailto: Urls
+///
+class LLUrlEntryEmail : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryEmail();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getUrl(const std::string &string) const;
+};
+
+///
+/// LLUrlEntryEmail Describes an IPv6 address
+///
+class LLUrlEntryIPv6 : public LLUrlEntryBase
+{
+public:
+ LLUrlEntryIPv6();
+ /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+ /*virtual*/ std::string getUrl(const std::string &string) const;
+ /*virtual*/ std::string getQuery(const std::string &url) const;
+
+ std::string mHostPath;
+};
+
+class LLKeyBindingToStringHandler;
+
+///
+/// LLUrlEntryKeybinding A way to access keybindings and show currently used one in text.
+/// secondlife:///app/keybinding/control_name
+class LLUrlEntryKeybinding: public LLUrlEntryBase
+{
+public:
+ LLUrlEntryKeybinding();
+ /*virtual*/ std::string getLabel(const std::string& url, const LLUrlLabelCallback& cb);
+ /*virtual*/ std::string getTooltip(const std::string& url) const;
+ void setHandler(LLKeyBindingToStringHandler* handler) {pHandler = handler;}
+private:
+ std::string getControlName(const std::string& url) const;
+ std::string getMode(const std::string& url) const;
+ void initLocalization();
+ void initLocalizationFromFile(const std::string& filename);
+
+ struct LLLocalizationData
+ {
+ LLLocalizationData() {}
+ LLLocalizationData(const std::string& localization, const std::string& tooltip)
+ : mLocalization(localization)
+ , mTooltip(tooltip)
+ {}
+ std::string mLocalization;
+ std::string mTooltip;
+ };
+
+ std::map<std::string, LLLocalizationData> mLocalizations;
+ LLKeyBindingToStringHandler* pHandler;
+};
+
+#endif
diff --git a/indra/llui/llurlmatch.cpp b/indra/llui/llurlmatch.cpp
index 2f2ac969e1..bfa3b167b1 100644
--- a/indra/llui/llurlmatch.cpp
+++ b/indra/llui/llurlmatch.cpp
@@ -1,4 +1,4 @@
-/**
+/**
* @file llurlmatch.cpp
* @author Martin Reddy
* @brief Specifies a matched Url in a string, as returned by LLUrlRegistry
@@ -6,21 +6,21 @@
* $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$
*/
@@ -29,37 +29,37 @@
#include "llurlmatch.h"
LLUrlMatch::LLUrlMatch() :
- mStart(0),
- mEnd(0),
- mUrl(""),
- mLabel(""),
- mTooltip(""),
- mIcon(""),
- mMenuName(""),
- mLocation(""),
- mUnderlineOnHoverOnly(false),
- mTrusted(false)
+ mStart(0),
+ mEnd(0),
+ mUrl(""),
+ mLabel(""),
+ mTooltip(""),
+ mIcon(""),
+ mMenuName(""),
+ mLocation(""),
+ mUnderlineOnHoverOnly(false),
+ mTrusted(false)
{
}
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, bool trusted)
+ 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, bool trusted)
{
- mStart = start;
- mEnd = end;
- mUrl = url;
- mLabel = label;
- mQuery = query;
- mTooltip = tooltip;
- mIcon = icon;
- mStyle = style;
- mStyle.link_href = url;
- mMenuName = menu;
- mLocation = location;
- mID = id;
- mUnderlineOnHoverOnly = underline_on_hover_only;
- mTrusted = trusted;
+ mStart = start;
+ mEnd = end;
+ mUrl = url;
+ mLabel = label;
+ mQuery = query;
+ mTooltip = tooltip;
+ mIcon = icon;
+ mStyle = style;
+ mStyle.link_href = url;
+ mMenuName = menu;
+ mLocation = location;
+ mID = id;
+ mUnderlineOnHoverOnly = underline_on_hover_only;
+ mTrusted = trusted;
}
diff --git a/indra/llui/llurlmatch.h b/indra/llui/llurlmatch.h
index ff699902ca..ba822fbda6 100644
--- a/indra/llui/llurlmatch.h
+++ b/indra/llui/llurlmatch.h
@@ -1,4 +1,4 @@
-/**
+/**
* @file llurlmatch.h
* @author Martin Reddy
* @brief Specifies a matched Url in a string, as returned by LLUrlRegistry
@@ -6,21 +6,21 @@
* $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$
*/
@@ -35,7 +35,7 @@
#include "llstyle.h"
///
-/// LLUrlMatch describes a single Url that was matched within a string by
+/// LLUrlMatch describes a single Url that was matched within a string by
/// the LLUrlRegistry::findUrl() method. It includes the actual Url that
/// was matched along with its first/last character offset in the string.
/// An alternate label is also provided for creating a hyperlink, as well
@@ -45,69 +45,69 @@
class LLUrlMatch
{
public:
- LLUrlMatch();
+ LLUrlMatch();
- /// return true if this object does not contain a valid Url match yet
- bool empty() const { return mUrl.empty(); }
+ /// return true if this object does not contain a valid Url match yet
+ bool empty() const { return mUrl.empty(); }
- /// return the offset in the string for the first character of the Url
- U32 getStart() const { return mStart; }
+ /// return the offset in the string for the first character of the Url
+ U32 getStart() const { return mStart; }
- /// return the offset in the string for the last character of the Url
- U32 getEnd() const { return mEnd; }
+ /// return the offset in the string for the last character of the Url
+ U32 getEnd() const { return mEnd; }
- /// return the Url that has been matched in the input string
- std::string getUrl() const { return mUrl; }
+ /// return the Url that has been matched in the input string
+ std::string getUrl() const { return mUrl; }
- /// return a label that can be used for the display of this Url
- std::string getLabel() const { return mLabel; }
+ /// return a 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 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; }
+ /// return a message that could be displayed in a tooltip or status bar
+ std::string getTooltip() const { return mTooltip; }
- /// return the filename for an icon that can be displayed next to this Url
- std::string getIcon() const { return mIcon; }
+ /// return the filename for an icon that can be displayed next to this Url
+ std::string getIcon() const { return mIcon; }
- /// Return the color to render the displayed text
- LLStyle::Params getStyle() const { return mStyle; }
+ /// Return the color to render the displayed text
+ LLStyle::Params getStyle() const { return mStyle; }
- /// Return the name of a XUI file containing the context menu items
- std::string getMenuName() const { return mMenuName; }
+ /// Return the name of a XUI file containing the context menu items
+ std::string getMenuName() const { return mMenuName; }
- /// return the SL location that this Url describes, or "" if none.
- std::string getLocation() const { return mLocation; }
+ /// return the SL location that this Url describes, or "" if none.
+ std::string getLocation() const { return mLocation; }
- /// Should this link text be underlined only when mouse is hovered over it?
- bool underlineOnHoverOnly() const { return mUnderlineOnHoverOnly; }
+ /// 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; }
+ /// 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& 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 trusted = false);
+ /// 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& 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 trusted = false);
- const LLUUID& getID() const { return mID; }
+ const LLUUID& getID() const { return mID; }
private:
- U32 mStart;
- U32 mEnd;
- std::string mUrl;
- std::string mLabel;
- std::string mQuery;
- std::string mTooltip;
- std::string mIcon;
- std::string mMenuName;
- std::string mLocation;
- LLUUID mID;
- LLStyle::Params mStyle;
- bool mUnderlineOnHoverOnly;
- bool mTrusted;
+ U32 mStart;
+ U32 mEnd;
+ std::string mUrl;
+ std::string mLabel;
+ std::string mQuery;
+ std::string mTooltip;
+ std::string mIcon;
+ std::string mMenuName;
+ std::string mLocation;
+ LLUUID mID;
+ LLStyle::Params mStyle;
+ bool mUnderlineOnHoverOnly;
+ bool mTrusted;
};
#endif
diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp
index f1df7699e2..9c3994480c 100644
--- a/indra/llui/llurlregistry.cpp
+++ b/indra/llui/llurlregistry.cpp
@@ -1,4 +1,4 @@
-/**
+/**
* @file llurlregistry.cpp
* @author Martin Reddy
* @brief Contains a set of Url types that can be matched in a string
@@ -6,21 +6,21 @@
* $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$
*/
@@ -38,278 +38,278 @@ void LLUrlRegistryNullCallback(const std::string &url, const std::string &label,
LLUrlRegistry::LLUrlRegistry()
{
- mUrlEntry.reserve(20);
-
- // Urls are matched in the order that they were registered
- mUrlEntryNoLink = new LLUrlEntryNoLink();
- registerUrl(mUrlEntryNoLink);
- 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());
- mUrlEntryHTTPLabel = new LLUrlEntryHTTPLabel();
- registerUrl(mUrlEntryHTTPLabel);
- registerUrl(new LLUrlEntryAgentCompleteName());
- registerUrl(new LLUrlEntryAgentLegacyName());
- registerUrl(new LLUrlEntryAgentDisplayName());
- registerUrl(new LLUrlEntryAgentUserName());
- // LLUrlEntryAgent*Name must appear before LLUrlEntryAgent since
- // LLUrlEntryAgent is a less specific (catchall for agent urls)
- registerUrl(new LLUrlEntryAgent());
+ mUrlEntry.reserve(20);
+
+ // Urls are matched in the order that they were registered
+ mUrlEntryNoLink = new LLUrlEntryNoLink();
+ registerUrl(mUrlEntryNoLink);
+ 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());
+ mUrlEntryHTTPLabel = new LLUrlEntryHTTPLabel();
+ registerUrl(mUrlEntryHTTPLabel);
+ registerUrl(new LLUrlEntryAgentCompleteName());
+ registerUrl(new LLUrlEntryAgentLegacyName());
+ registerUrl(new LLUrlEntryAgentDisplayName());
+ registerUrl(new LLUrlEntryAgentUserName());
+ // LLUrlEntryAgent*Name must appear before LLUrlEntryAgent since
+ // LLUrlEntryAgent is a less specific (catchall for agent urls)
+ registerUrl(new LLUrlEntryAgent());
registerUrl(new LLUrlEntryChat());
- registerUrl(new LLUrlEntryGroup());
- registerUrl(new LLUrlEntryParcel());
- registerUrl(new LLUrlEntryTeleport());
- registerUrl(new LLUrlEntryRegion());
- registerUrl(new LLUrlEntryWorldMap());
- registerUrl(new LLUrlEntryObjectIM());
- registerUrl(new LLUrlEntryPlace());
- registerUrl(new LLUrlEntryInventory());
+ registerUrl(new LLUrlEntryGroup());
+ registerUrl(new LLUrlEntryParcel());
+ registerUrl(new LLUrlEntryTeleport());
+ registerUrl(new LLUrlEntryRegion());
+ registerUrl(new LLUrlEntryWorldMap());
+ registerUrl(new LLUrlEntryObjectIM());
+ registerUrl(new LLUrlEntryPlace());
+ registerUrl(new LLUrlEntryInventory());
registerUrl(new LLUrlEntryExperienceProfile());
mUrlEntryKeybinding = new LLUrlEntryKeybinding();
registerUrl(mUrlEntryKeybinding);
- //LLUrlEntrySL and LLUrlEntrySLLabel have more common pattern,
- //so it should be registered in the end of list
- registerUrl(new LLUrlEntrySL());
- mUrlEntrySLLabel = new LLUrlEntrySLLabel();
- registerUrl(mUrlEntrySLLabel);
- registerUrl(new LLUrlEntryEmail());
- registerUrl(new LLUrlEntryIPv6());
+ //LLUrlEntrySL and LLUrlEntrySLLabel have more common pattern,
+ //so it should be registered in the end of list
+ registerUrl(new LLUrlEntrySL());
+ mUrlEntrySLLabel = new LLUrlEntrySLLabel();
+ registerUrl(mUrlEntrySLLabel);
+ registerUrl(new LLUrlEntryEmail());
+ registerUrl(new LLUrlEntryIPv6());
}
LLUrlRegistry::~LLUrlRegistry()
{
- // free all of the LLUrlEntryBase objects we are holding
- std::vector<LLUrlEntryBase *>::iterator it;
- for (it = mUrlEntry.begin(); it != mUrlEntry.end(); ++it)
- {
- delete *it;
- }
+ // free all of the LLUrlEntryBase objects we are holding
+ std::vector<LLUrlEntryBase *>::iterator it;
+ for (it = mUrlEntry.begin(); it != mUrlEntry.end(); ++it)
+ {
+ delete *it;
+ }
}
void LLUrlRegistry::registerUrl(LLUrlEntryBase *url, bool force_front)
{
- if (url)
- {
- if (force_front) // IDEVO
- mUrlEntry.insert(mUrlEntry.begin(), url);
- else
- mUrlEntry.push_back(url);
- }
+ if (url)
+ {
+ if (force_front) // IDEVO
+ mUrlEntry.insert(mUrlEntry.begin(), url);
+ else
+ mUrlEntry.push_back(url);
+ }
}
static bool matchRegex(const char *text, boost::regex regex, U32 &start, U32 &end)
{
- boost::cmatch result;
- bool found;
-
- found = ll_regex_search(text, result, regex);
-
- if (! found)
- {
- return false;
- }
-
- // return the first/last character offset for the matched substring
- start = static_cast<U32>(result[0].first - text);
- end = static_cast<U32>(result[0].second - text) - 1;
-
- // we allow certain punctuation to terminate a Url but not match it,
- // e.g., "http://foo.com/." should just match "http://foo.com/"
- if (text[end] == '.' || text[end] == ',')
- {
- end--;
- }
- // ignore a terminating ')' when Url contains no matching '('
- // see DEV-19842 for details
- else if (text[end] == ')' && std::string(text+start, end-start).find('(') == std::string::npos)
- {
- end--;
- }
-
- else if (text[end] == ']' && std::string(text+start, end-start).find('[') == std::string::npos)
- {
- end--;
- }
-
- return true;
+ boost::cmatch result;
+ bool found;
+
+ found = ll_regex_search(text, result, regex);
+
+ if (! found)
+ {
+ return false;
+ }
+
+ // return the first/last character offset for the matched substring
+ start = static_cast<U32>(result[0].first - text);
+ end = static_cast<U32>(result[0].second - text) - 1;
+
+ // we allow certain punctuation to terminate a Url but not match it,
+ // e.g., "http://foo.com/." should just match "http://foo.com/"
+ if (text[end] == '.' || text[end] == ',')
+ {
+ end--;
+ }
+ // ignore a terminating ')' when Url contains no matching '('
+ // see DEV-19842 for details
+ else if (text[end] == ')' && std::string(text+start, end-start).find('(') == std::string::npos)
+ {
+ end--;
+ }
+
+ else if (text[end] == ']' && std::string(text+start, end-start).find('[') == std::string::npos)
+ {
+ end--;
+ }
+
+ return true;
}
static bool stringHasUrl(const std::string &text)
{
- // fast heuristic test for a URL in a string. This is used
- // to avoid lots of costly regex calls, BUT it needs to be
- // kept in sync with the LLUrlEntry regexes we support.
- return (text.find("://") != std::string::npos ||
- text.find("www.") != std::string::npos ||
- text.find(".com") != std::string::npos ||
- text.find("<nolink>") != std::string::npos ||
- text.find("<icon") != std::string::npos ||
- text.find("@") != std::string::npos);
+ // fast heuristic test for a URL in a string. This is used
+ // to avoid lots of costly regex calls, BUT it needs to be
+ // kept in sync with the LLUrlEntry regexes we support.
+ return (text.find("://") != std::string::npos ||
+ text.find("www.") != std::string::npos ||
+ text.find(".com") != std::string::npos ||
+ text.find("<nolink>") != std::string::npos ||
+ text.find("<icon") != std::string::npos ||
+ text.find("@") != std::string::npos);
}
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))
- {
- return false;
- }
-
- // find the first matching regex from all url entries in the registry
- U32 match_start = 0, match_end = 0;
- LLUrlEntryBase *match_entry = NULL;
-
- std::vector<LLUrlEntryBase *>::iterator it;
- for (it = mUrlEntry.begin(); it != mUrlEntry.end(); ++it)
- {
- //Skip for url entry icon if content is not trusted
- if((mUrlEntryIcon == *it) && ((text.find("Hand") != std::string::npos) || !is_content_trusted))
- {
- continue;
- }
-
- LLUrlEntryBase *url_entry = *it;
-
- U32 start = 0, end = 0;
- if (matchRegex(text.c_str(), url_entry->getPattern(), start, end))
- {
- // does this match occur in the string before any other match
- if (start < match_start || match_entry == NULL)
- {
-
- 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;
- }
- }
- }
-
- // did we find a match? if so, return its details in the match object
- if (match_entry)
- {
- // Skip if link is an email with an empty username (starting with @). See MAINT-5371.
- if (match_start > 0 && text.substr(match_start - 1, 1) == "@")
- return false;
-
- // 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);
- if (up.normalize() == 0)
+ // avoid costly regexes if there is clearly no URL in the text
+ if (! stringHasUrl(text))
+ {
+ return false;
+ }
+
+ // find the first matching regex from all url entries in the registry
+ U32 match_start = 0, match_end = 0;
+ LLUrlEntryBase *match_entry = NULL;
+
+ std::vector<LLUrlEntryBase *>::iterator it;
+ for (it = mUrlEntry.begin(); it != mUrlEntry.end(); ++it)
+ {
+ //Skip for url entry icon if content is not trusted
+ if((mUrlEntryIcon == *it) && ((text.find("Hand") != std::string::npos) || !is_content_trusted))
+ {
+ continue;
+ }
+
+ LLUrlEntryBase *url_entry = *it;
+
+ U32 start = 0, end = 0;
+ if (matchRegex(text.c_str(), url_entry->getPattern(), start, end))
+ {
+ // does this match occur in the string before any other match
+ if (start < match_start || match_entry == NULL)
+ {
+
+ 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;
+ }
+ }
+ }
+
+ // did we find a match? if so, return its details in the match object
+ if (match_entry)
+ {
+ // Skip if link is an email with an empty username (starting with @). See MAINT-5371.
+ if (match_start > 0 && text.substr(match_start - 1, 1) == "@")
+ return false;
+
+ // 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);
+ if (up.normalize() == 0)
{
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->isTrusted());
- return true;
- }
-
- return false;
+ }
+
+ 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->isTrusted());
+ return true;
+ }
+
+ return false;
}
bool LLUrlRegistry::findUrl(const LLWString &text, LLUrlMatch &match, const LLUrlLabelCallback &cb)
{
- // boost::regex_search() only works on char or wchar_t
- // types, but wchar_t is only 2-bytes on Win32 (not 4).
- // So we use UTF-8 to make this work the same everywhere.
- std::string utf8_text = wstring_to_utf8str(text);
- if (findUrl(utf8_text, match, cb))
- {
- // we cannot blindly return the start/end offsets from
- // the UTF-8 string because it is a variable-length
- // character encoding, so we need to update the start
- // and end values to be correct for the wide string.
- LLWString wurl = utf8str_to_wstring(match.getUrl());
- size_t start = text.find(wurl);
- if (start == std::string::npos)
- {
- return false;
- }
- S32 end = start + wurl.size() - 1;
-
- match.setValues(start, end, match.getUrl(),
- match.getLabel(),
- match.getQuery(),
- match.getTooltip(),
- match.getIcon(),
- match.getStyle(),
- match.getMenuName(),
- match.getLocation(),
- match.getID(),
- match.underlineOnHoverOnly());
- return true;
- }
- return false;
+ // boost::regex_search() only works on char or wchar_t
+ // types, but wchar_t is only 2-bytes on Win32 (not 4).
+ // So we use UTF-8 to make this work the same everywhere.
+ std::string utf8_text = wstring_to_utf8str(text);
+ if (findUrl(utf8_text, match, cb))
+ {
+ // we cannot blindly return the start/end offsets from
+ // the UTF-8 string because it is a variable-length
+ // character encoding, so we need to update the start
+ // and end values to be correct for the wide string.
+ LLWString wurl = utf8str_to_wstring(match.getUrl());
+ size_t start = text.find(wurl);
+ if (start == std::string::npos)
+ {
+ return false;
+ }
+ S32 end = start + wurl.size() - 1;
+
+ match.setValues(start, end, match.getUrl(),
+ match.getLabel(),
+ match.getQuery(),
+ match.getTooltip(),
+ match.getIcon(),
+ match.getStyle(),
+ match.getMenuName(),
+ match.getLocation(),
+ match.getID(),
+ match.underlineOnHoverOnly());
+ return true;
+ }
+ return false;
}
bool LLUrlRegistry::hasUrl(const std::string &text)
{
- LLUrlMatch match;
- return findUrl(text, match);
+ LLUrlMatch match;
+ return findUrl(text, match);
}
bool LLUrlRegistry::hasUrl(const LLWString &text)
{
- LLUrlMatch match;
- return findUrl(text, match);
+ LLUrlMatch match;
+ return findUrl(text, match);
}
bool LLUrlRegistry::isUrl(const std::string &text)
{
- LLUrlMatch match;
- if (findUrl(text, match))
- {
- return (match.getStart() == 0 && match.getEnd() >= text.size()-1);
- }
- return false;
+ LLUrlMatch match;
+ if (findUrl(text, match))
+ {
+ return (match.getStart() == 0 && match.getEnd() >= text.size()-1);
+ }
+ return false;
}
bool LLUrlRegistry::isUrl(const LLWString &text)
{
- LLUrlMatch match;
- if (findUrl(text, match))
- {
- return (match.getStart() == 0 && match.getEnd() >= text.size()-1);
- }
- return false;
+ LLUrlMatch match;
+ if (findUrl(text, match))
+ {
+ return (match.getStart() == 0 && match.getEnd() >= text.size()-1);
+ }
+ return false;
}
void LLUrlRegistry::setKeybindingHandler(LLKeyBindingToStringHandler* handler)
diff --git a/indra/llui/llurlregistry.h b/indra/llui/llurlregistry.h
index 186447c0be..64cfec3960 100644
--- a/indra/llui/llurlregistry.h
+++ b/indra/llui/llurlregistry.h
@@ -1,4 +1,4 @@
-/**
+/**
* @file llurlregistry.h
* @author Martin Reddy
* @brief Contains a set of Url types that can be matched in a string
@@ -6,21 +6,21 @@
* $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$
*/
@@ -40,8 +40,8 @@ class LLKeyBindingToStringHandler;
/// This default callback for findUrl() simply ignores any label updates
void LLUrlRegistryNullCallback(const std::string &url,
- const std::string &label,
- const std::string &icon);
+ const std::string &label,
+ const std::string &icon);
///
/// LLUrlRegistry is a singleton that contains a set of Url types that
@@ -64,43 +64,43 @@ void LLUrlRegistryNullCallback(const std::string &url,
///
class LLUrlRegistry : public LLSingleton<LLUrlRegistry>
{
- LLSINGLETON(LLUrlRegistry);
- ~LLUrlRegistry();
+ LLSINGLETON(LLUrlRegistry);
+ ~LLUrlRegistry();
public:
- /// add a new Url handler to the registry (will be freed on destruction)
- /// optionally force it to the front of the list, making it take
- /// priority over other regular expression matches for URLs
- void registerUrl(LLUrlEntryBase *url, bool force_front = false);
+ /// add a new Url handler to the registry (will be freed on destruction)
+ /// optionally force it to the front of the list, making it take
+ /// priority over other regular expression matches for URLs
+ void registerUrl(LLUrlEntryBase *url, bool force_front = false);
- /// 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,
- bool is_content_trusted = false);
+ /// 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,
+ bool is_content_trusted = false);
- /// a slightly less efficient version of findUrl for wide strings
- bool findUrl(const LLWString &text, LLUrlMatch &match,
- const LLUrlLabelCallback &cb = &LLUrlRegistryNullCallback);
+ /// a slightly less efficient version of findUrl for wide strings
+ bool findUrl(const LLWString &text, LLUrlMatch &match,
+ const LLUrlLabelCallback &cb = &LLUrlRegistryNullCallback);
- // return true if the given string contains a URL that findUrl would match
- bool hasUrl(const std::string &text);
- bool hasUrl(const LLWString &text);
+ // return true if the given string contains a URL that findUrl would match
+ bool hasUrl(const std::string &text);
+ bool hasUrl(const LLWString &text);
- // return true if the given string is a URL that findUrl would match
- bool isUrl(const std::string &text);
- bool isUrl(const LLWString &text);
+ // return true if the given string is a URL that findUrl would match
+ bool isUrl(const std::string &text);
+ bool isUrl(const LLWString &text);
// Set handler for url registry to be capable of parsing and populating keybindings
void setKeybindingHandler(LLKeyBindingToStringHandler* handler);
private:
- std::vector<LLUrlEntryBase *> mUrlEntry;
- LLUrlEntryBase* mUrlEntryTrusted;
- LLUrlEntryBase* mUrlEntryIcon;
- LLUrlEntryBase* mLLUrlEntryInvalidSLURL;
- LLUrlEntryBase* mUrlEntryHTTPLabel;
- LLUrlEntryBase* mUrlEntrySLLabel;
- LLUrlEntryBase* mUrlEntryNoLink;
+ std::vector<LLUrlEntryBase *> mUrlEntry;
+ LLUrlEntryBase* mUrlEntryTrusted;
+ LLUrlEntryBase* mUrlEntryIcon;
+ LLUrlEntryBase* mLLUrlEntryInvalidSLURL;
+ LLUrlEntryBase* mUrlEntryHTTPLabel;
+ LLUrlEntryBase* mUrlEntrySLLabel;
+ LLUrlEntryBase* mUrlEntryNoLink;
LLUrlEntryBase* mUrlEntryKeybinding;
};
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index 30cc5cd10a..c46c2d9f6c 100644
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -1,2854 +1,2882 @@
-/**
- * @file llview.cpp
- * @author James Cook
- * @brief Container for other views, anything that draws.
- *
- * $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"
-
-#define LLVIEW_CPP
-#include "llview.h"
-
-#include <sstream>
-#include <boost/tokenizer.hpp>
-#include <boost/bind.hpp>
-
-#include "llrender.h"
-#include "llevent.h"
-#include "llfocusmgr.h"
-#include "llrect.h"
-#include "llstl.h"
-#include "llui.h"
-#include "lluictrl.h"
-#include "llwindow.h"
-#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::sDebugUnicode = false;
-bool LLView::sDebugCamera = false;
-bool LLView::sIsRectDirty = false;
-LLRect LLView::sDirtyRect;
-bool LLView::sDebugRectsShowNames = true;
-bool LLView::sDebugKeys = false;
-bool LLView::sDebugMouseHandling = false;
-std::string LLView::sMouseHandlerMessage;
-bool LLView::sForceReshape = false;
-std::set<LLView*> LLView::sPreviewHighlightedElements;
-bool LLView::sHighlightingDiffs = false;
-LLView* LLView::sPreviewClickedElement = NULL;
-bool LLView::sDrawPreviewHighlights = false;
-S32 LLView::sLastLeftXML = S32_MIN;
-S32 LLView::sLastBottomXML = S32_MIN;
-std::vector<LLViewDrawContext*> LLViewDrawContext::sDrawContextStack;
-
-LLView::DrilldownFunc LLView::sDrilldown =
- boost::bind(&LLView::pointInView, _1, _2, _3, HIT_TEST_USE_BOUNDING_RECT);
-
-//#if LL_DEBUG
-bool LLView::sIsDrawing = false;
-//#endif
-
-// Compiler optimization, generate extern template
-template class LLView* LLView::getChild<class LLView>(
- const std::string& name, bool recurse) const;
-
-static LLDefaultChildRegistry::Register<LLView> r("view");
-
-void deleteView(LLView *aView)
-{
- delete aView;
-}
-
-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)
-{}
-
-LLView::Params::Params()
-: name("name", std::string("unnamed")),
- enabled("enabled", true),
- visible("visible", true),
- mouse_opaque("mouse_opaque", true),
- follows("follows"),
- hover_cursor("hover_cursor", "UI_CURSOR_ARROW"),
- use_bounding_rect("use_bounding_rect", false),
- tab_group("tab_group", 0),
- default_tab_group("default_tab_group"),
- tool_tip("tool_tip"),
- sound_flags("sound_flags", MOUSE_UP),
- layout("layout"),
- rect("rect"),
- bottom_delta("bottom_delta", S32_MAX),
- top_pad("top_pad"),
- top_delta("top_delta", S32_MAX),
- left_pad("left_pad"),
- left_delta("left_delta", S32_MAX),
- from_xui("from_xui", false),
- focus_root("focus_root", false),
- needs_translate("translate"),
- xmlns("xmlns"),
- xmlns_xsi("xmlns:xsi"),
- xsi_schemaLocation("xsi:schemaLocation"),
- xsi_type("xsi:type")
-
-{
- addSynonym(rect, "");
-}
-
-LLView::LLView(const LLView::Params& p)
-: mVisible(p.visible),
- mInDraw(false),
- mName(p.name),
- mParentView(NULL),
- mReshapeFlags(FOLLOWS_NONE),
- mFromXUI(p.from_xui),
- mIsFocusRoot(p.focus_root),
- mLastVisible(false),
- mHoverCursor(getCursorFromString(p.hover_cursor)),
- mEnabled(p.enabled),
- mMouseOpaque(p.mouse_opaque),
- mSoundFlags(p.sound_flags),
- mUseBoundingRect(p.use_bounding_rect),
- mDefaultTabGroup(p.default_tab_group),
- mLastTabGroup(0),
- mToolTipMsg((LLStringExplicit)p.tool_tip()),
- mDefaultWidgets(NULL)
-{
- // create rect first, as this will supply initial follows flags
- setShape(p.rect);
- parseFollowsFlags(p);
-}
-
-LLView::~LLView()
-{
- dirtyRect();
- //LL_INFOS() << "Deleting view " << mName << ":" << (void*) this << LL_ENDL;
- if (LLView::sIsDrawing)
- {
- LL_DEBUGS() << "Deleting view " << mName << " during UI draw() phase" << LL_ENDL;
- }
-// llassert(LLView::sIsDrawing == false);
-
-// llassert_always(sDepth == 0); // avoid deleting views while drawing! It can subtly break list iterators
-
- if( hasMouseCapture() )
- {
- //LL_WARNS() << "View holding mouse capture deleted: " << getName() << ". Mouse capture removed." << LL_ENDL;
- gFocusMgr.removeMouseCaptureWithoutCallback( this );
- }
-
- deleteAllChildren();
-
- if (mParentView != NULL)
- {
- mParentView->removeChild(this);
- }
-
- if (mDefaultWidgets)
- {
- delete mDefaultWidgets;
- mDefaultWidgets = NULL;
- }
-}
-
-// virtual
-bool LLView::isCtrl() const
-{
- return false;
-}
-
-// virtual
-bool LLView::isPanel() const
-{
- return false;
-}
-
-void LLView::setToolTip(const LLStringExplicit& msg)
-{
- mToolTipMsg = msg;
-}
-
-bool LLView::setToolTipArg(const LLStringExplicit& key, const LLStringExplicit& text)
-{
- mToolTipMsg.setArg(key, text);
- return true;
-}
-
-void LLView::setToolTipArgs( const LLStringUtil::format_map_t& args )
-{
- mToolTipMsg.setArgList(args);
-}
-
-// virtual
-void LLView::setRect(const LLRect& rect)
-{
- mRect = rect;
- updateBoundingRect();
-}
-
-void LLView::setUseBoundingRect( bool use_bounding_rect )
-{
- if (mUseBoundingRect != use_bounding_rect)
- {
- mUseBoundingRect = use_bounding_rect;
- updateBoundingRect();
- }
-}
-
-bool LLView::getUseBoundingRect() const
-{
- return mUseBoundingRect;
-}
-
-// virtual
-const std::string& LLView::getName() const
-{
- static std::string no_name("(no name)");
-
- return mName.empty() ? no_name : mName;
-}
-
-void LLView::sendChildToFront(LLView* child)
-{
-// llassert_always(sDepth == 0); // Avoid re-ordering while drawing; it can cause subtle iterator bugs
- if (child && child->getParent() == this)
- {
- // minor optimization, but more importantly,
- // won't temporarily create an empty list
- if (child != mChildList.front())
- {
- mChildList.remove( child );
- mChildList.push_front(child);
- }
- }
-}
-
-void LLView::sendChildToBack(LLView* child)
-{
-// llassert_always(sDepth == 0); // Avoid re-ordering while drawing; it can cause subtle iterator bugs
- if (child && child->getParent() == this)
- {
- // minor optimization, but more importantly,
- // won't temporarily create an empty list
- if (child != mChildList.back())
- {
- mChildList.remove( child );
- mChildList.push_back(child);
- }
- }
-}
-
-// virtual
-bool LLView::addChild(LLView* child, S32 tab_group)
-{
- if (!child)
- {
- return false;
- }
-
- if (this == child)
- {
- LL_ERRS() << "Adding view " << child->getName() << " as child of itself" << LL_ENDL;
- }
-
- // remove from current parent
- if (child->mParentView)
- {
- child->mParentView->removeChild(child);
- }
-
- // add to front of child list, as normal
- mChildList.push_front(child);
-
- // add to tab order list
- if (tab_group != 0)
- {
- mTabOrder.insert(tab_order_pair_t(child, tab_group));
- }
-
- child->mParentView = this;
- if (getVisible() && child->getVisible())
- {
- // if child isn't visible it won't affect bounding rect
- // if current view is not visible it will be recalculated
- // on visibility change
- updateBoundingRect();
- }
- mLastTabGroup = tab_group;
- return true;
-}
-
-
-bool LLView::addChildInBack(LLView* child, S32 tab_group)
-{
- if(addChild(child, tab_group))
- {
- sendChildToBack(child);
- return true;
- }
-
- return false;
-}
-
-// remove the specified child from the view, and set it's parent to NULL.
-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);
- mChildList.remove( child );
- child->mParentView = NULL;
- child_tab_order_t::iterator found = mTabOrder.find(child);
- if (found != mTabOrder.end())
- {
- mTabOrder.erase(found);
- }
- }
- else
- {
- LL_WARNS() << "\"" << child->getName() << "\" is not a child of " << getName() << LL_ENDL;
- }
- updateBoundingRect();
-}
-
-bool LLView::isInVisibleChain() const
-{
- bool visible = true;
-
- const LLView* viewp = this;
- while(viewp)
- {
- if (!viewp->getVisible())
- {
- visible = false;
- break;
- }
- viewp = viewp->getParent();
- }
-
- return visible;
-}
-
-bool LLView::isInEnabledChain() const
-{
- bool enabled = true;
-
- const LLView* viewp = this;
- while(viewp)
- {
- if (!viewp->getEnabled())
- {
- enabled = false;
- break;
- }
- viewp = viewp->getParent();
- }
-
- 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 << '/';
-
- // substitute all '/' in name with appropriate code
- std::string name = view->getName();
- std::size_t found = name.find('/');
- std::size_t start = 0;
- while (found != std::string::npos)
- {
- std::size_t sub_len = found - start;
- if (sub_len > 0)
- {
- out << name.substr(start, sub_len);
- }
- out << "%2F";
- start = found + 1;
- found = name.find('/', start);
- }
- if (start < name.size())
- {
- out << name.substr(start, name.size() - start);
- }
-}
-
-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
-{
- return true;
-}
-
-//virtual
-void LLView::setEnabled(bool enabled)
-{
- mEnabled = enabled;
-}
-
-//virtual
-bool LLView::isAvailable() const
-{
- return isInEnabledChain() && isInVisibleChain();
-}
-
-//static
-bool LLView::isAvailable(const LLView* view)
-{
- return view && view->isAvailable();
-}
-
-//virtual
-bool LLView::setLabelArg( const std::string& key, const LLStringExplicit& text )
-{
- return false;
-}
-
-//virtual
-LLRect LLView::getSnapRect() const
-{
- return mRect;
-}
-
-//virtual
-LLRect LLView::getRequiredRect()
-{
- return mRect;
-}
-
-bool LLView::focusNextRoot()
-{
- LLView::child_list_t result = LLView::getFocusRootsQuery().run(this);
- return LLView::focusNext(result);
-}
-
-bool LLView::focusPrevRoot()
-{
- LLView::child_list_t result = LLView::getFocusRootsQuery().run(this);
- return LLView::focusPrev(result);
-}
-
-// static
-bool LLView::focusNext(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();
- ++iter)
- {
- if(gFocusMgr.childHasKeyboardFocus(*iter))
- {
- focused = iter;
- break;
- }
- }
- 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.rend())
- {
- next = result.rbegin();
- }
- if ((*next)->isCtrl() && ((LLUICtrl*)*next)->hasTabStop())
- {
- LLUICtrl * ctrl = static_cast<LLUICtrl*>(*next);
- ctrl->setFocus(true);
- ctrl->onTabInto();
- gFocusMgr.triggerFocusFlash();
- return true;
- }
- ++next;
- }
- return false;
-}
-
-// static
-bool LLView::focusPrev(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();
- ++iter)
- {
- if(gFocusMgr.childHasKeyboardFocus(*iter))
- {
- focused = iter;
- break;
- }
- }
- 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.end())
- {
- next = result.begin();
- }
- if((*next)->isCtrl())
- {
- LLUICtrl * ctrl = static_cast<LLUICtrl*>(*next);
- if (!ctrl->hasFocus())
- {
- ctrl->setFocus(true);
- ctrl->onTabInto();
- gFocusMgr.triggerFocusFlash();
- }
- return true;
- }
- ++next;
- }
- return false;
-}
-
-// delete all children. Override this function if you need to
-// perform any extra clean up such as cached pointers to selected
-// children, etc.
-void LLView::deleteAllChildren()
-{
- // clear out the control ordering
- mTabOrder.clear();
-
- while (!mChildList.empty())
- {
- LLView* viewp = mChildList.front();
- viewp->mParentView = NULL;
- delete viewp;
- mChildList.pop_front();
- }
- updateBoundingRect();
-}
-
-void LLView::setAllChildrenEnabled(bool b)
-{
- for (LLView* viewp : mChildList)
- {
- viewp->setEnabled(b);
- }
-}
-
-// virtual
-void LLView::setVisible(bool visible)
-{
- if ( mVisible != visible )
- {
- mVisible = visible;
-
- // notify children of visibility change if root, or part of visible hierarchy
- if (!getParent() || getParent()->isInVisibleChain())
- {
- // tell all children of this view that the visibility may have changed
- dirtyRect();
- onVisibilityChange( visible );
- }
- updateBoundingRect();
- }
-}
-
-// virtual
-void LLView::onVisibilityChange ( bool new_visibility )
-{
- bool old_visibility;
- bool log_visibility_change = LLViewerEventRecorder::instance().getLoggingStatus();
- for (LLView* viewp : mChildList)
- {
- if (!viewp)
- {
- continue;
- }
-
- // only views that are themselves visible will have their overall visibility affected by their ancestors
- old_visibility=viewp->getVisible();
-
- if(log_visibility_change)
- {
- 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;
-
- }
- }
-}
-
-// virtual
-void LLView::onUpdateScrollToChild(const LLUICtrl * cntrl)
-{
- LLView* parent_view = getParent();
- if (parent_view)
- {
- parent_view->onUpdateScrollToChild(cntrl);
- }
-}
-
-// virtual
-void LLView::translate(S32 x, S32 y)
-{
- mRect.translate(x, y);
- updateBoundingRect();
-}
-
-// virtual
-bool LLView::canSnapTo(const LLView* other_view)
-{
- return other_view != this && other_view->getVisible();
-}
-
-// virtual
-void LLView::setSnappedTo(const LLView* snap_view)
-{
-}
-
-bool LLView::handleHover(S32 x, S32 y, MASK mask)
-{
- return childrenHandleHover( x, y, mask ) != NULL;
-}
-
-void LLView::onMouseEnter(S32 x, S32 y, MASK mask)
-{
- //LL_INFOS() << "Mouse entered " << getName() << LL_ENDL;
-}
-
-void LLView::onMouseLeave(S32 x, S32 y, MASK mask)
-{
- //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() )
- {
- for (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)
-{
- for (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)
-{
- for (LLView* viewp : mChildList)
- {
- S32 local_x = x - viewp->getRect().mLeft;
- S32 local_y = y - viewp->getRect().mBottom;
- // 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)
- || viewp->blockMouseEvent(local_x, local_y))
- {
- // This is NOT event recording related
- viewp->logMouseEvent();
- return viewp;
- }
- }
- return NULL;
-}
-
-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;
-
- for (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;
- }
-
- // 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))
- {
- return viewp;
- }
- }
- return NULL;
-}
-
-LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask)
-{
- for (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::getInstance()->mWindow->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, bool recur)
-{
- if (!getVisible())
- return NULL;
-
- for (LLView* viewp : mChildList)
- {
- S32 local_x = x - viewp->getRect().mLeft;
- S32 local_y = y - viewp->getRect().mBottom;
- 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;
-
- }
- return 0;
-}
-
-F32 LLView::getTooltipTimeout()
-{
- static LLCachedControl<F32> tooltip_fast_delay(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipFastDelay", 0.1f);
- static LLCachedControl<F32> tooltip_delay(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipDelay", 0.7f);
- // allow "scrubbing" over ui by showing next tooltip immediately
- // if previous one was still visible
- return (F32)(LLToolTipMgr::instance().toolTipVisible()
- ? tooltip_fast_delay
- : tooltip_delay);
-}
-
-bool LLView::handleToolTip(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
-
- // parents provide tooltips first, which are optionally
- // overridden by children, in case child is mouse_opaque
- std::string tooltip = getToolTip();
- if (!tooltip.empty())
- {
- static LLCachedControl<bool> allow_ui_tooltips(*LLUI::getInstance()->mSettingGroups["config"], "BasicUITooltips", true);
-
- // Even if we don't show tooltips, consume the event, nothing below should show tooltip
- if (allow_ui_tooltips)
- {
- LLToolTipMgr::instance().show(LLToolTip::Params()
- .message(tooltip)
- .sticky_rect(calcScreenRect())
- .delay_time(getTooltipTimeout()));
- }
- handled = true;
- }
-
- // child tooltips will override our own
- LLView* child_handler = childrenHandleToolTip(x, y, mask);
- if (child_handler)
- {
- handled = true;
- }
-
- return handled;
-}
-
-bool LLView::handleKey(KEY key, MASK mask, bool called_from_parent)
-{
- bool handled = false;
-
- if (getVisible() && getEnabled())
- {
- if( called_from_parent )
- {
- // Downward traversal
- handled = childrenHandleKey( key, mask ) != NULL;
- }
-
- 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)
- {
- LL_DEBUGS() << "Key handled by " << getName() << LL_ENDL;
- }
- }
- }
-
- if( !handled && !called_from_parent && mParentView)
- {
- // Upward traversal
- handled = mParentView->handleKey( key, mask, false );
- }
- return handled;
-}
-
-bool LLView::handleKeyUp(KEY key, MASK mask, bool called_from_parent)
-{
- bool handled = false;
-
- if (getVisible() && getEnabled())
- {
- if (called_from_parent)
- {
- // Downward traversal
- handled = childrenHandleKeyUp(key, mask) != NULL;
- }
-
- 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 = handleKeyUpHere(key, mask);
- if (handled)
- {
- LL_DEBUGS() << "Key handled by " << getName() << LL_ENDL;
- }
- }
- }
-
- if (!handled && !called_from_parent && mParentView)
- {
- // Upward traversal
- handled = mParentView->handleKeyUp(key, mask, false);
- }
- return handled;
-}
-
-// Called from handleKey()
-// Handles key in this object. Checking parents and children happens in handleKey()
-bool LLView::handleKeyHere(KEY key, MASK mask)
-{
- return false;
-}
-
-// Called from handleKey()
-// Handles key in this object. Checking parents and children happens in handleKey()
-bool LLView::handleKeyUpHere(KEY key, MASK mask)
-{
- return false;
-}
-
-bool LLView::handleUnicodeChar(llwchar uni_char, bool called_from_parent)
-{
- bool handled = false;
-
- if (getVisible() && getEnabled())
- {
- if( called_from_parent )
- {
- // Downward traversal
- handled = childrenHandleUnicodeChar( uni_char ) != NULL;
- }
-
- if (!handled)
- {
- handled = handleUnicodeCharHere(uni_char);
- if (handled && LLView::sDebugKeys)
- {
- LL_INFOS() << "Unicode key " << wchar_utf8_preview(uni_char) << " is handled by " << getName() << LL_ENDL;
- }
- }
- }
-
- if (!handled && !called_from_parent && mParentView)
- {
- // Upward traversal
- handled = mParentView->handleUnicodeChar(uni_char, false);
- }
-
- if (handled)
- {
- LLViewerEventRecorder::instance().logKeyUnicodeEvent(uni_char);
- }
-
- return handled;
-}
-
-
-bool LLView::handleUnicodeCharHere(llwchar uni_char )
-{
- return false;
-}
-
-
-bool LLView::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
- EDragAndDropType cargo_type, void* cargo_data,
- EAcceptance* accept,
- std::string& tooltip_msg)
-{
- return childrenHandleDragAndDrop( x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL;
-}
-
-void LLView::onMouseCaptureLost()
-{
-}
-
-bool LLView::hasMouseCapture()
-{
- return gFocusMgr.getMouseCapture() == this;
-}
-
-bool LLView::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- LLView* r = childrenHandleMouseUp( x, y, mask );
-
- return (r!=NULL);
-}
-
-bool LLView::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- LLView* r= childrenHandleMouseDown(x, y, mask );
-
- return (r!=NULL);
-}
-
-bool LLView::handleDoubleClick(S32 x, S32 y, MASK mask)
-{
- return childrenHandleDoubleClick( x, y, mask ) != NULL;
-}
-
-bool LLView::handleScrollWheel(S32 x, S32 y, S32 clicks)
-{
- return childrenHandleScrollWheel( x, y, clicks ) != NULL;
-}
-
-bool LLView::handleScrollHWheel(S32 x, S32 y, S32 clicks)
-{
- return childrenHandleScrollHWheel( x, y, clicks ) != NULL;
-}
-
-bool LLView::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- return childrenHandleRightMouseDown( x, y, mask ) != NULL;
-}
-
-bool LLView::handleRightMouseUp(S32 x, S32 y, MASK mask)
-{
- return childrenHandleRightMouseUp( x, y, mask ) != NULL;
-}
-
-bool LLView::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
-{
- return childrenHandleMiddleMouseDown( x, y, mask ) != NULL;
-}
-
-bool LLView::handleMiddleMouseUp(S32 x, S32 y, MASK mask)
-{
- return childrenHandleMiddleMouseUp( x, y, mask ) != NULL;
-}
-
-LLView* LLView::childrenHandleScrollWheel(S32 x, S32 y, S32 clicks)
-{
- return childrenHandleMouseEvent(&LLView::handleScrollWheel, x, y, clicks, false);
-}
-
-LLView* LLView::childrenHandleScrollHWheel(S32 x, S32 y, S32 clicks)
-{
- return childrenHandleMouseEvent(&LLView::handleScrollHWheel, x, y, clicks, false);
-}
-
-// Called during downward traversal
-LLView* LLView::childrenHandleKey(KEY key, MASK mask)
-{
- return childrenHandleCharEvent("Key", &LLView::handleKey, key, mask);
-}
-
-// Called during downward traversal
-LLView* LLView::childrenHandleKeyUp(KEY key, MASK mask)
-{
- return childrenHandleCharEvent("Key Up", &LLView::handleKeyUp, key, mask);
-}
-
-// Called during downward traversal
-LLView* LLView::childrenHandleUnicodeChar(llwchar uni_char)
-{
- return childrenHandleCharEvent("Unicode character", &LLView::handleUnicodeCharWithDummyMask,
- uni_char, MASK_NONE);
-}
-
-LLView* LLView::childrenHandleMouseDown(S32 x, S32 y, MASK mask)
-{
- return childrenHandleMouseEvent(&LLView::handleMouseDown, x, y, mask);
-}
-
-LLView* LLView::childrenHandleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- return childrenHandleMouseEvent(&LLView::handleRightMouseDown, x, y, mask);
-}
-
-LLView* LLView::childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask)
-{
- return childrenHandleMouseEvent(&LLView::handleMiddleMouseDown, x, y, mask);
-}
-
-LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask)
-{
- return childrenHandleMouseEvent(&LLView::handleDoubleClick, x, y, mask);
-}
-
-LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask)
-{
- return childrenHandleMouseEvent(&LLView::handleMouseUp, x, y, mask);
-}
-
-LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask)
-{
- return childrenHandleMouseEvent(&LLView::handleRightMouseUp, x, y, mask);
-}
-
-LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask)
-{
- return childrenHandleMouseEvent(&LLView::handleMiddleMouseUp, x, y, mask);
-}
-
-void LLView::draw()
-{
- drawChildren();
-}
-
-void LLView::drawChildren()
-{
- if (!mChildList.empty())
- {
- LLView* rootp = LLUI::getInstance()->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())
- {
- LLRect screen_rect = viewp->calcScreenRect();
- if ( rootp->getLocalRect().overlaps(screen_rect) && sDirtyRect.overlaps(screen_rect))
- {
- LLUI::pushMatrix();
- {
- 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)
- {
- viewp->drawDebugRect();
-
- // Check for bogus rectangle
- if (!getRect().isValid())
- {
- LL_WARNS() << "Bogus rectangle for " << getName() << " with " << mRect << LL_ENDL;
- }
- }
- }
- LLUI::popMatrix();
- }
- }
-
- }
- --sDepth;
- }
-}
-
-void LLView::dirtyRect()
-{
- LLView* child = getParent();
- LLView* parent = child ? child->getParent() : NULL;
- LLView* cur = this;
- while (child && parent && parent->getParent())
- { //find third to top-most view
- cur = child;
- child = parent;
- parent = parent->getParent();
- }
-
- if (!sIsRectDirty)
- {
- sDirtyRect = cur->calcScreenRect();
- sIsRectDirty = true;
- }
- else
- {
- sDirtyRect.unionWith(cur->calcScreenRect());
- }
-}
-
-//Draw a box for debugging.
-void LLView::drawDebugRect()
-{
- std::set<LLView*>::iterator preview_iter = std::find(sPreviewHighlightedElements.begin(), sPreviewHighlightedElements.end(), this); // figure out if it's a previewed element
-
- LLUI::pushMatrix();
- {
- // drawing solids requires texturing be disabled
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- if (getUseBoundingRect())
- {
- LLUI::translate((F32)mBoundingRect.mLeft - (F32)mRect.mLeft, (F32)mBoundingRect.mBottom - (F32)mRect.mBottom);
- }
-
- LLRect debug_rect = getUseBoundingRect() ? mBoundingRect : mRect;
-
- // draw red rectangle for the border
- LLColor4 border_color(0.25f, 0.25f, 0.25f, 1.f);
- if(preview_iter != sPreviewHighlightedElements.end())
- {
- if(LLView::sPreviewClickedElement && this == sPreviewClickedElement)
- {
- border_color = LLColor4::red;
- }
- else
- {
- static LLUIColor scroll_highlighted_color = LLUIColorTable::instance().getColor("ScrollHighlightedColor");
- border_color = scroll_highlighted_color;
- }
- }
- else
- {
- border_color.mV[sDepth%3] = 1.f;
- }
-
- gGL.color4fv( border_color.mV );
-
- gGL.begin(LLRender::LINES);
- gGL.vertex2i(0, debug_rect.getHeight() - 1);
- gGL.vertex2i(0, 0);
-
- gGL.vertex2i(0, 0);
- gGL.vertex2i(debug_rect.getWidth() - 1, 0);
-
- gGL.vertex2i(debug_rect.getWidth() - 1, 0);
- gGL.vertex2i(debug_rect.getWidth() - 1, debug_rect.getHeight() - 1);
-
- gGL.vertex2i(debug_rect.getWidth() - 1, debug_rect.getHeight() - 1);
- gGL.vertex2i(0, debug_rect.getHeight() - 1);
- gGL.end();
-
- // Draw the name if it's not a leaf node or not in editing or preview mode
- if (mChildList.size()
- && preview_iter == sPreviewHighlightedElements.end()
- && sDebugRectsShowNames)
- {
- S32 x, y;
- gGL.color4fv( border_color.mV );
-
- 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,
- LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
- }
- }
- LLUI::popMatrix();
-}
-
-void LLView::drawChild(LLView* childp, S32 x_offset, S32 y_offset, bool force_draw)
-{
- if (childp && childp->getParent() == this)
- {
- ++sDepth;
-
- if ((childp->getVisible() && childp->getRect().isValid())
- || force_draw)
- {
- gGL.matrixMode(LLRender::MM_MODELVIEW);
- LLUI::pushMatrix();
- {
- LLUI::translate((F32)childp->getRect().mLeft + x_offset, (F32)childp->getRect().mBottom + y_offset);
- childp->draw();
- }
- LLUI::popMatrix();
- }
-
- --sDepth;
- }
-}
-
-
-void LLView::reshape(S32 width, S32 height, bool called_from_parent)
-{
- // compute how much things changed and apply reshape logic to children
- S32 delta_width = width - getRect().getWidth();
- S32 delta_height = height - getRect().getHeight();
-
- if (delta_width || delta_height || sForceReshape)
- {
- // adjust our rectangle
- mRect.mRight = getRect().mLeft + width;
- mRect.mTop = getRect().mBottom + height;
-
- // move child views according to reshape flags
- for (LLView* viewp : mChildList)
- {
- if (viewp != NULL)
- {
- LLRect child_rect( viewp->mRect );
-
- if (viewp->followsRight() && viewp->followsLeft())
- {
- child_rect.mRight += delta_width;
- }
- else if (viewp->followsRight())
- {
- child_rect.mLeft += delta_width;
- child_rect.mRight += delta_width;
- }
- else if (viewp->followsLeft())
- {
- // left is 0, don't need to adjust coords
- }
- else
- {
- // BUG what to do when we don't follow anyone?
- // for now, same as followsLeft
- }
-
- if (viewp->followsTop() && viewp->followsBottom())
- {
- child_rect.mTop += delta_height;
- }
- else if (viewp->followsTop())
- {
- child_rect.mTop += delta_height;
- child_rect.mBottom += delta_height;
- }
- else if (viewp->followsBottom())
- {
- // bottom is 0, so don't need to adjust coords
- }
- else
- {
- // BUG what to do when we don't follow?
- // for now, same as bottom
- }
-
- S32 delta_x = child_rect.mLeft - viewp->getRect().mLeft;
- S32 delta_y = child_rect.mBottom - viewp->getRect().mBottom;
- viewp->translate( delta_x, delta_y );
- if (child_rect.getWidth() != viewp->getRect().getWidth()
- || child_rect.getHeight() != viewp->getRect().getHeight()
- || sForceReshape)
- {
- viewp->reshape(child_rect.getWidth(), child_rect.getHeight());
- }
- }
- }
- }
-
- if (!called_from_parent)
- {
- if (mParentView)
- {
- mParentView->reshape(mParentView->getRect().getWidth(), mParentView->getRect().getHeight(), false);
- }
- }
-
- updateBoundingRect();
-}
-
-LLRect LLView::calcBoundingRect()
-{
- LLRect local_bounding_rect = LLRect::null;
-
- for (LLView* childp : mChildList)
- {
- // ignore invisible and "top" children when calculating bounding rect
- // such as combobox popups
- if (!childp->getVisible() || childp == gFocusMgr.getTopCtrl())
- {
- continue;
- }
-
- LLRect child_bounding_rect = childp->getBoundingRect();
-
- if (local_bounding_rect.isEmpty())
- {
- // start out with bounding rect equal to first visible child's bounding rect
- local_bounding_rect = child_bounding_rect;
- }
- else
- {
- // accumulate non-null children rectangles
- if (!child_bounding_rect.isEmpty())
- {
- local_bounding_rect.unionWith(child_bounding_rect);
- }
- }
- }
-
- // convert to parent-relative coordinates
- local_bounding_rect.translate(mRect.mLeft, mRect.mBottom);
- return local_bounding_rect;
-}
-
-
-void LLView::updateBoundingRect()
-{
- if (isDead()) return;
-
- LLRect cur_rect = mBoundingRect;
-
- if (getUseBoundingRect())
- {
- mBoundingRect = calcBoundingRect();
- }
- else
- {
- mBoundingRect = mRect;
- }
-
- // give parent view a chance to resize, in case we just moved, for example
- if (getParent() && getParent()->getUseBoundingRect())
- {
- getParent()->updateBoundingRect();
- }
-
- if (mBoundingRect != cur_rect)
- {
- dirtyRect();
- }
-
-}
-
-LLRect LLView::calcScreenRect() const
-{
- LLRect screen_rect;
- localPointToScreen(0, 0, &screen_rect.mLeft, &screen_rect.mBottom);
- localPointToScreen(getRect().getWidth(), getRect().getHeight(), &screen_rect.mRight, &screen_rect.mTop);
- return screen_rect;
-}
-
-LLRect LLView::calcScreenBoundingRect() const
-{
- LLRect screen_rect;
- // get bounding rect, if used
- LLRect bounding_rect = getUseBoundingRect() ? mBoundingRect : mRect;
-
- // convert to local coordinates, as defined by mRect
- bounding_rect.translate(-mRect.mLeft, -mRect.mBottom);
-
- localPointToScreen(bounding_rect.mLeft, bounding_rect.mBottom, &screen_rect.mLeft, &screen_rect.mBottom);
- localPointToScreen(bounding_rect.mRight, bounding_rect.mTop, &screen_rect.mRight, &screen_rect.mTop);
- return screen_rect;
-}
-
-LLRect LLView::getLocalBoundingRect() const
-{
- LLRect local_bounding_rect = getBoundingRect();
- local_bounding_rect.translate(-mRect.mLeft, -mRect.mBottom);
-
- return local_bounding_rect;
-}
-
-
-LLRect LLView::getLocalRect() const
-{
- LLRect local_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
- return local_rect;
-}
-
-LLRect LLView::getLocalSnapRect() const
-{
- LLRect local_snap_rect = getSnapRect();
- local_snap_rect.translate(-getRect().mLeft, -getRect().mBottom);
- return local_snap_rect;
-}
-
-bool LLView::hasAncestor(const LLView* parentp) const
-{
- if (!parentp)
- {
- return false;
- }
-
- LLView* viewp = getParent();
- while(viewp)
- {
- if (viewp == parentp)
- {
- return true;
- }
- viewp = viewp->getParent();
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-
-bool LLView::childHasKeyboardFocus( const std::string& childname ) const
-{
- LLView *focus = dynamic_cast<LLView *>(gFocusMgr.getKeyboardFocus());
-
- while (focus != NULL)
- {
- if (focus->getName() == childname)
- {
- return true;
- }
-
- focus = focus->getParent();
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-
-bool LLView::hasChild(const std::string& childname, bool recurse) const
-{
- return findChildView(childname, recurse) != NULL;
-}
-
-//-----------------------------------------------------------------------------
-// getChildView()
-//-----------------------------------------------------------------------------
-LLView* LLView::getChildView(const std::string& name, bool recurse) const
-{
- return getChild<LLView>(name, recurse);
-}
-
-LLView* LLView::findChildView(const std::string& name, bool recurse) const
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
-
- // Look for direct children *first*
- for (LLView* childp : mChildList)
- {
- llassert(childp);
- if (childp->getName() == name)
- {
- return childp;
- }
- }
- if (recurse)
- {
- // Look inside each child as well.
- for (LLView* childp : mChildList)
- {
- llassert(childp);
- LLView* viewp = childp->findChildView(name, recurse);
- if ( viewp )
- {
- return viewp;
- }
- }
- }
- return NULL;
-}
-
-bool LLView::parentPointInView(S32 x, S32 y, EHitTestType type) const
-{
- return (getUseBoundingRect() && type == HIT_TEST_USE_BOUNDING_RECT)
- ? mBoundingRect.pointInRect( x, y )
- : mRect.pointInRect( x, y );
-}
-
-bool LLView::pointInView(S32 x, S32 y, EHitTestType type) const
-{
- return (getUseBoundingRect() && type == HIT_TEST_USE_BOUNDING_RECT)
- ? mBoundingRect.pointInRect( x + mRect.mLeft, y + mRect.mBottom )
- : mRect.localPointInRect( x, y );
-}
-
-bool LLView::blockMouseEvent(S32 x, S32 y) const
-{
- return mMouseOpaque && pointInView(x, y, HIT_TEST_IGNORE_BOUNDING_RECT);
-}
-
-// virtual
-void LLView::screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const
-{
- *local_x = screen_x - getRect().mLeft;
- *local_y = screen_y - getRect().mBottom;
-
- const LLView* cur = this;
- while( cur->mParentView )
- {
- cur = cur->mParentView;
- *local_x -= cur->getRect().mLeft;
- *local_y -= cur->getRect().mBottom;
- }
-}
-
-void LLView::localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const
-{
- *screen_x = local_x;
- *screen_y = local_y;
-
- const LLView* cur = this;
- do
- {
- LLRect cur_rect = cur->getRect();
- *screen_x += cur_rect.mLeft;
- *screen_y += cur_rect.mBottom;
- cur = cur->mParentView;
- }
- while( cur );
-}
-
-void LLView::screenRectToLocal(const LLRect& screen, LLRect* local) const
-{
- *local = screen;
- local->translate( -getRect().mLeft, -getRect().mBottom );
-
- const LLView* cur = this;
- while( cur->mParentView )
- {
- cur = cur->mParentView;
- local->translate( -cur->getRect().mLeft, -cur->getRect().mBottom );
- }
-}
-
-void LLView::localRectToScreen(const LLRect& local, LLRect* screen) const
-{
- *screen = local;
- screen->translate( getRect().mLeft, getRect().mBottom );
-
- const LLView* cur = this;
- while( cur->mParentView )
- {
- cur = cur->mParentView;
- screen->translate( cur->getRect().mLeft, cur->getRect().mBottom );
- }
-}
-
-LLView* LLView::getRootView()
-{
- LLView* view = this;
- while( view->mParentView )
- {
- view = view->mParentView;
- }
- return view;
-}
-
-LLView* LLView::findPrevSibling(LLView* child)
-{
- child_list_t::iterator prev_it = std::find(mChildList.begin(), mChildList.end(), child);
- if (prev_it != mChildList.end() && prev_it != mChildList.begin())
- {
- return *(--prev_it);
- }
- return NULL;
-}
-
-LLView* LLView::findNextSibling(LLView* child)
-{
- child_list_t::iterator next_it = std::find(mChildList.begin(), mChildList.end(), child);
- if (next_it != mChildList.end())
- {
- next_it++;
- }
-
- return (next_it != mChildList.end()) ? *next_it : NULL;
-}
-
-
-LLCoordGL getNeededTranslation(const LLRect& input, const LLRect& constraint, S32 min_overlap_pixels)
-{
- LLCoordGL delta;
-
- 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 (KEEP_ONSCREEN_PIXELS_WIDTH <= constraint.getWidth() &&
- KEEP_ONSCREEN_PIXELS_HEIGHT <= constraint.getHeight())
- {
- 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_HEIGHT < constraint.mBottom)
- {
- delta.mY = constraint.mBottom - (input.mTop - KEEP_ONSCREEN_PIXELS_HEIGHT);
- }
- }
-
- return delta;
-}
-
-// Moves the view so that it is entirely inside of constraint.
-// If the view will not fit because it's too big, aligns with the top and left.
-// (Why top and left? That's where the drag bars are for floaters.)
-bool LLView::translateIntoRect(const LLRect& constraint, S32 min_overlap_pixels)
-{
- return translateRectIntoRect(getRect(), constraint, min_overlap_pixels);
-}
-
-bool LLView::translateRectIntoRect(const LLRect& rect, const LLRect& constraint, S32 min_overlap_pixels)
-{
- LLCoordGL translation = getNeededTranslation(rect, constraint, min_overlap_pixels);
-
- if (translation.mX != 0 || translation.mY != 0)
- {
- translate(translation.mX, translation.mY);
- return true;
- }
-
- return false;
-}
-
-// move this view into "inside" but not onto "exclude"
-// NOTE: if this view is already contained in "inside", we ignore the "exclude" rect
-bool LLView::translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, S32 min_overlap_pixels)
-{
- LLCoordGL translation = getNeededTranslation(getRect(), inside, min_overlap_pixels);
-
- if (translation.mX != 0 || translation.mY != 0)
- {
- // translate ourselves into constraint rect
- translate(translation.mX, translation.mY);
-
- // do we overlap with exclusion area?
- // keep moving in the same direction to the other side of the exclusion rect
- if (exclude.overlaps(getRect()))
- {
- // moving right
- if (translation.mX > 0)
- {
- translate(exclude.mRight - getRect().mLeft, 0);
- }
- // moving left
- else if (translation.mX < 0)
- {
- translate(exclude.mLeft - getRect().mRight, 0);
- }
-
- // moving up
- if (translation.mY > 0)
- {
- translate(0, exclude.mTop - getRect().mBottom);
- }
- // moving down
- else if (translation.mY < 0)
- {
- translate(0, exclude.mBottom - getRect().mTop);
- }
- }
-
- return true;
- }
- return false;
-}
-
-
-void LLView::centerWithin(const LLRect& bounds)
-{
- S32 left = bounds.mLeft + (bounds.getWidth() - getRect().getWidth()) / 2;
- S32 bottom = bounds.mBottom + (bounds.getHeight() - getRect().getHeight()) / 2;
-
- translate( left - getRect().mLeft, bottom - getRect().mBottom );
-}
-
-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;
-
- while (cur_view)
- {
- if (cur_view == other_view)
- {
- *other_x = x;
- *other_y = y;
- return true;
- }
-
- x += cur_view->getRect().mLeft;
- y += cur_view->getRect().mBottom;
-
- cur_view = cur_view->getParent();
- root_view = cur_view;
- }
-
- // assuming common root between two views, chase other_view's parents up to root
- cur_view = other_view;
- while (cur_view)
- {
- x -= cur_view->getRect().mLeft;
- y -= cur_view->getRect().mBottom;
-
- cur_view = cur_view->getParent();
-
- if (cur_view == root_view)
- {
- *other_x = x;
- *other_y = y;
- return true;
- }
- }
-
- *other_x = x;
- *other_y = y;
- return false;
-}
-
-bool LLView::localRectToOtherView( const LLRect& local, LLRect* other, const LLView* other_view ) const
-{
- LLRect cur_rect = local;
- const LLView* cur_view = this;
- const LLView* root_view = NULL;
-
- while (cur_view)
- {
- if (cur_view == other_view)
- {
- *other = cur_rect;
- return true;
- }
-
- cur_rect.translate(cur_view->getRect().mLeft, cur_view->getRect().mBottom);
-
- cur_view = cur_view->getParent();
- root_view = cur_view;
- }
-
- // assuming common root between two views, chase other_view's parents up to root
- cur_view = other_view;
- while (cur_view)
- {
- cur_rect.translate(-cur_view->getRect().mLeft, -cur_view->getRect().mBottom);
-
- cur_view = cur_view->getParent();
-
- if (cur_view == root_view)
- {
- *other = cur_rect;
- return true;
- }
- }
-
- *other = cur_rect;
- 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>
-{
- LLSINGLETON_EMPTY_CTOR(SortByTabOrder);
- /*virtual*/ void sort(LLView * parent, LLView::child_list_t &children) const override
- {
- children.sort(CompareByTabOrder(parent->getTabOrder(), parent->getDefaultTabGroup()));
- }
-};
-
-// static
-const LLViewQuery & LLView::getTabOrderQuery()
-{
- 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;
-}
-
-// This class is only used internally by getFocusRootsQuery below.
-class LLFocusRootsFilter : public LLQueryFilter, public LLSingleton<LLFocusRootsFilter>
-{
- LLSINGLETON_EMPTY_CTOR(LLFocusRootsFilter);
- /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override
- {
- return filterResult_t(view->isCtrl() && view->isFocusRoot(), !view->isFocusRoot());
- }
-};
-
-// static
-const LLViewQuery & LLView::getFocusRootsQuery()
-{
- static LLViewQuery query;
- if(query.getPreFilters().size() == 0) {
- query.addPreFilter(LLVisibleFilter::getInstance());
- query.addPreFilter(LLEnabledFilter::getInstance());
- query.addPreFilter(LLFocusRootsFilter::getInstance());
- query.addPostFilter(LLRootsFilter::getInstance());
- }
- return query;
-}
-
-
-void LLView::setShape(const LLRect& new_rect, bool by_user)
-{
- if (new_rect != getRect())
- {
- handleReshape(new_rect, by_user);
- }
-}
-
-void LLView::handleReshape(const LLRect& new_rect, bool by_user)
-{
- reshape(new_rect.getWidth(), new_rect.getHeight());
- translate(new_rect.mLeft - getRect().mLeft, new_rect.mBottom - getRect().mBottom);
-}
-
-LLView* LLView::findSnapRect(LLRect& new_rect, const LLCoordGL& mouse_dir,
- LLView::ESnapType snap_type, S32 threshold, S32 padding)
-{
- new_rect = mRect;
- LLView* snap_view = NULL;
-
- if (!mParentView)
- {
- return NULL;
- }
-
- S32 delta_x = 0;
- S32 delta_y = 0;
- if (mouse_dir.mX >= 0)
- {
- S32 new_right = mRect.mRight;
- LLView* view = findSnapEdge(new_right, mouse_dir, SNAP_RIGHT, snap_type, threshold, padding);
- delta_x = new_right - mRect.mRight;
- snap_view = view ? view : snap_view;
- }
-
- if (mouse_dir.mX <= 0)
- {
- S32 new_left = mRect.mLeft;
- LLView* view = findSnapEdge(new_left, mouse_dir, SNAP_LEFT, snap_type, threshold, padding);
- delta_x = new_left - mRect.mLeft;
- snap_view = view ? view : snap_view;
- }
-
- if (mouse_dir.mY >= 0)
- {
- S32 new_top = mRect.mTop;
- LLView* view = findSnapEdge(new_top, mouse_dir, SNAP_TOP, snap_type, threshold, padding);
- delta_y = new_top - mRect.mTop;
- snap_view = view ? view : snap_view;
- }
-
- if (mouse_dir.mY <= 0)
- {
- S32 new_bottom = mRect.mBottom;
- LLView* view = findSnapEdge(new_bottom, mouse_dir, SNAP_BOTTOM, snap_type, threshold, padding);
- delta_y = new_bottom - mRect.mBottom;
- snap_view = view ? view : snap_view;
- }
-
- new_rect.translate(delta_x, delta_y);
- return snap_view;
-}
-
-LLView* LLView::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding)
-{
- LLRect snap_rect = getSnapRect();
- S32 snap_pos = 0;
- switch(snap_edge)
- {
- case SNAP_LEFT:
- snap_pos = snap_rect.mLeft;
- break;
- case SNAP_RIGHT:
- snap_pos = snap_rect.mRight;
- break;
- case SNAP_TOP:
- snap_pos = snap_rect.mTop;
- break;
- case SNAP_BOTTOM:
- snap_pos = snap_rect.mBottom;
- break;
- }
-
- if (!mParentView)
- {
- new_edge_val = snap_pos;
- return NULL;
- }
-
- LLView* snap_view = NULL;
-
- // If the view is near the edge of its parent, snap it to
- // the edge.
- LLRect test_rect = snap_rect;
- test_rect.stretch(padding);
-
- S32 x_threshold = threshold;
- S32 y_threshold = threshold;
-
- LLRect parent_local_snap_rect = mParentView->getLocalSnapRect();
-
- if (snap_type == SNAP_PARENT || snap_type == SNAP_PARENT_AND_SIBLINGS)
- {
- switch(snap_edge)
- {
- case SNAP_RIGHT:
- if (llabs(parent_local_snap_rect.mRight - test_rect.mRight) <= x_threshold
- && (parent_local_snap_rect.mRight - test_rect.mRight) * mouse_dir.mX >= 0)
- {
- snap_pos = parent_local_snap_rect.mRight - padding;
- snap_view = mParentView;
- x_threshold = llabs(parent_local_snap_rect.mRight - test_rect.mRight);
- }
- break;
- case SNAP_LEFT:
- if (llabs(test_rect.mLeft - parent_local_snap_rect.mLeft) <= x_threshold
- && test_rect.mLeft * mouse_dir.mX <= 0)
- {
- snap_pos = parent_local_snap_rect.mLeft + padding;
- snap_view = mParentView;
- x_threshold = llabs(test_rect.mLeft - parent_local_snap_rect.mLeft);
- }
- break;
- case SNAP_BOTTOM:
- if (llabs(test_rect.mBottom - parent_local_snap_rect.mBottom) <= y_threshold
- && test_rect.mBottom * mouse_dir.mY <= 0)
- {
- snap_pos = parent_local_snap_rect.mBottom + padding;
- snap_view = mParentView;
- y_threshold = llabs(test_rect.mBottom - parent_local_snap_rect.mBottom);
- }
- break;
- case SNAP_TOP:
- if (llabs(parent_local_snap_rect.mTop - test_rect.mTop) <= y_threshold && (parent_local_snap_rect.mTop - test_rect.mTop) * mouse_dir.mY >= 0)
- {
- snap_pos = parent_local_snap_rect.mTop - padding;
- snap_view = mParentView;
- y_threshold = llabs(parent_local_snap_rect.mTop - test_rect.mTop);
- }
- break;
- default:
- LL_ERRS() << "Invalid snap edge" << LL_ENDL;
- }
- }
-
- if (snap_type == SNAP_SIBLINGS || snap_type == SNAP_PARENT_AND_SIBLINGS)
- {
- for ( child_list_const_iter_t child_it = mParentView->getChildList()->begin();
- child_it != mParentView->getChildList()->end(); ++child_it)
- {
- LLView* siblingp = *child_it;
-
- if (!canSnapTo(siblingp)) continue;
-
- LLRect sibling_rect = siblingp->getSnapRect();
-
- switch(snap_edge)
- {
- case SNAP_RIGHT:
- if (llabs(test_rect.mRight - sibling_rect.mLeft) <= x_threshold
- && (test_rect.mRight - sibling_rect.mLeft) * mouse_dir.mX <= 0)
- {
- snap_pos = sibling_rect.mLeft - padding;
- snap_view = siblingp;
- x_threshold = llabs(test_rect.mRight - sibling_rect.mLeft);
- }
- // if snapped with sibling along other axis, check for shared edge
- else if (llabs(sibling_rect.mTop - (test_rect.mBottom - padding)) <= y_threshold
- || llabs(sibling_rect.mBottom - (test_rect.mTop + padding)) <= x_threshold)
- {
- if (llabs(test_rect.mRight - sibling_rect.mRight) <= x_threshold
- && (test_rect.mRight - sibling_rect.mRight) * mouse_dir.mX <= 0)
- {
- snap_pos = sibling_rect.mRight;
- snap_view = siblingp;
- x_threshold = llabs(test_rect.mRight - sibling_rect.mRight);
- }
- }
- break;
- case SNAP_LEFT:
- if (llabs(test_rect.mLeft - sibling_rect.mRight) <= x_threshold
- && (test_rect.mLeft - sibling_rect.mRight) * mouse_dir.mX <= 0)
- {
- snap_pos = sibling_rect.mRight + padding;
- snap_view = siblingp;
- x_threshold = llabs(test_rect.mLeft - sibling_rect.mRight);
- }
- // if snapped with sibling along other axis, check for shared edge
- else if (llabs(sibling_rect.mTop - (test_rect.mBottom - padding)) <= y_threshold
- || llabs(sibling_rect.mBottom - (test_rect.mTop + padding)) <= y_threshold)
- {
- if (llabs(test_rect.mLeft - sibling_rect.mLeft) <= x_threshold
- && (test_rect.mLeft - sibling_rect.mLeft) * mouse_dir.mX <= 0)
- {
- snap_pos = sibling_rect.mLeft;
- snap_view = siblingp;
- x_threshold = llabs(test_rect.mLeft - sibling_rect.mLeft);
- }
- }
- break;
- case SNAP_BOTTOM:
- if (llabs(test_rect.mBottom - sibling_rect.mTop) <= y_threshold
- && (test_rect.mBottom - sibling_rect.mTop) * mouse_dir.mY <= 0)
- {
- snap_pos = sibling_rect.mTop + padding;
- snap_view = siblingp;
- y_threshold = llabs(test_rect.mBottom - sibling_rect.mTop);
- }
- // if snapped with sibling along other axis, check for shared edge
- else if (llabs(sibling_rect.mRight - (test_rect.mLeft - padding)) <= x_threshold
- || llabs(sibling_rect.mLeft - (test_rect.mRight + padding)) <= x_threshold)
- {
- if (llabs(test_rect.mBottom - sibling_rect.mBottom) <= y_threshold
- && (test_rect.mBottom - sibling_rect.mBottom) * mouse_dir.mY <= 0)
- {
- snap_pos = sibling_rect.mBottom;
- snap_view = siblingp;
- y_threshold = llabs(test_rect.mBottom - sibling_rect.mBottom);
- }
- }
- break;
- case SNAP_TOP:
- if (llabs(test_rect.mTop - sibling_rect.mBottom) <= y_threshold
- && (test_rect.mTop - sibling_rect.mBottom) * mouse_dir.mY <= 0)
- {
- snap_pos = sibling_rect.mBottom - padding;
- snap_view = siblingp;
- y_threshold = llabs(test_rect.mTop - sibling_rect.mBottom);
- }
- // if snapped with sibling along other axis, check for shared edge
- else if (llabs(sibling_rect.mRight - (test_rect.mLeft - padding)) <= x_threshold
- || llabs(sibling_rect.mLeft - (test_rect.mRight + padding)) <= x_threshold)
- {
- if (llabs(test_rect.mTop - sibling_rect.mTop) <= y_threshold
- && (test_rect.mTop - sibling_rect.mTop) * mouse_dir.mY <= 0)
- {
- snap_pos = sibling_rect.mTop;
- snap_view = siblingp;
- y_threshold = llabs(test_rect.mTop - sibling_rect.mTop);
- }
- }
- break;
- default:
- LL_ERRS() << "Invalid snap edge" << LL_ENDL;
- }
- }
- }
-
- new_edge_val = snap_pos;
- return snap_view;
-}
-
-//-----------------------------------------------------------------------------
-// Listener dispatch functions
-//-----------------------------------------------------------------------------
-
-
-LLControlVariable *LLView::findControl(const std::string& name)
-{
- // parse the name to locate which group it belongs to
- std::size_t key_pos= name.find(".");
- if(key_pos!= std::string::npos )
- {
- std::string control_group_key = name.substr(0, key_pos);
- LLControlVariable* control;
- // check if it's in the control group that name indicated
- if(LLUI::getInstance()->mSettingGroups[control_group_key])
- {
- control = LLUI::getInstance()->mSettingGroups[control_group_key]->getControl(name);
- if (control)
- {
- return control;
- }
- }
- }
-
- LLControlGroup& control_group = LLUI::getInstance()->getControlControlGroup(name);
- return control_group.getControl(name);
-}
-
-void LLView::initFromParams(const LLView::Params& params)
-{
- LLRect required_rect = getRequiredRect();
-
- S32 width = llmax(getRect().getWidth(), required_rect.getWidth());
- S32 height = llmax(getRect().getHeight(), required_rect.getHeight());
-
- reshape(width, height);
-
- // call virtual methods with most recent data
- // use getters because these values might not come through parameter block
- setEnabled(getEnabled());
- setVisible(getVisible());
-
- if (!params.name().empty())
- {
- setName(params.name());
- }
-
- mLayout = params.layout();
-}
-
-void LLView::parseFollowsFlags(const LLView::Params& params)
-{
- // preserve follows flags set by code if user did not override
- if (!params.follows.isProvided())
- {
- return;
- }
-
- // interpret either string or bitfield version of follows
- if (params.follows.string.isChosen())
- {
- setFollows(FOLLOWS_NONE);
-
- std::string follows = params.follows.string;
-
- typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
- boost::char_separator<char> sep("|");
- tokenizer tokens(follows, sep);
- tokenizer::iterator token_iter = tokens.begin();
-
- while(token_iter != tokens.end())
- {
- const std::string& token_str = *token_iter;
- if (token_str == "left")
- {
- setFollowsLeft();
- }
- else if (token_str == "right")
- {
- setFollowsRight();
- }
- else if (token_str == "top")
- {
- setFollowsTop();
- }
- else if (token_str == "bottom")
- {
- setFollowsBottom();
- }
- else if (token_str == "all")
- {
- setFollowsAll();
- }
- ++token_iter;
- }
- }
- else if (params.follows.flags.isChosen())
- {
- setFollows(params.follows.flags);
- }
-}
-
-
-// static
-//LLFontGL::HAlign LLView::selectFontHAlign(LLXMLNodePtr node)
-//{
-// LLFontGL::HAlign gl_hfont_align = LLFontGL::LEFT;
-//
-// if (node->hasAttribute("halign"))
-// {
-// std::string horizontal_align_name;
-// node->getAttributeString("halign", horizontal_align_name);
-// gl_hfont_align = LLFontGL::hAlignFromName(horizontal_align_name);
-// }
-// return gl_hfont_align;
-//}
-
-// Return the rectangle of the last-constructed child,
-// if present and a first-class widget (eg, not a close box or drag handle)
-// Returns true if found
-static bool get_last_child_rect(LLView* parent, LLRect *rect)
-{
- if (!parent) return false;
-
- LLView::child_list_t::const_iterator itor =
- parent->getChildList()->begin();
- for (;itor != parent->getChildList()->end(); ++itor)
- {
- LLView *last_view = (*itor);
- if (last_view->getFromXUI())
- {
- *rect = last_view->getRect();
- return true;
- }
- }
- return false;
-}
-
-//static
-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())
- {
- p.layout = parent->getLayout();
- }
-
- if (layout_rect.isEmpty())
- {
- layout_rect = parent->getLocalRect();
- }
-
- // overwrite uninitialized rect params, using context
- LLRect default_rect = parent->getLocalRect();
-
- bool layout_topleft = (p.layout() == "topleft");
-
- // convert negative or centered coordinates to parent relative values
- // Note: some of this logic matches the logic in TypedParam<LLRect>::setValueFromBlock()
- if (p.rect.left.isProvided())
- {
- p.rect.left = 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
- p.rect.bottom = layout_rect.mBottom + layout_rect.mTop - p.rect.bottom;
- }
- }
- if (p.rect.top.isProvided())
- {
- p.rect.top = p.rect.top + ((p.rect.top >= 0) ? layout_rect.mBottom : layout_rect.mTop);
- if (layout_topleft)
- {
- //invert top to bottom
- p.rect.top = layout_rect.mBottom + layout_rect.mTop - p.rect.top;
- }
- }
-
- // 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;
- }
-
- default_rect.translate(0, default_rect.getHeight());
-
- // If there was a recently constructed child, use its rectangle
- get_last_child_rect(parent, &default_rect);
-
- 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
- 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);
- }
-
- 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);
- }
- 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);
-
- // 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);
-
- }
- 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);
- }
-}
-
-static S32 invert_vertical(S32 y, LLView* parent)
-{
- if (y < 0)
- {
- // already based on top-left, just invert
- return -y;
- }
- else if (parent)
- {
- // use parent to flip coordinate
- S32 parent_height = parent->getRect().getHeight();
- return parent_height - y;
- }
- else
- {
- LL_WARNS() << "Attempting to convert layout to top-left with no parent" << LL_ENDL;
- return y;
- }
-}
-
-// Assumes that input is in bottom-left coordinates, hence must call
-// _before_ convert_coords_to_top_left().
-static void convert_to_relative_layout(LLView::Params& p, LLView* parent)
-{
- // Use setupParams to get the final widget rectangle
- // according to our wacky layout rules.
- LLView::Params final = p;
- LLView::applyXUILayout(final, parent);
- // Must actually extract the rectangle to get consistent
- // right = left+width, top = bottom+height
- LLRect final_rect = final.rect;
-
- // We prefer to write out top edge instead of bottom, regardless
- // of whether we use relative positioning
- bool converted_top = false;
-
- // Look for a last rectangle
- LLRect last_rect;
- if (get_last_child_rect(parent, &last_rect))
- {
- // ...we have a previous widget to compare to
- const S32 EDGE_THRESHOLD_PIXELS = 4;
- S32 left_pad = final_rect.mLeft - last_rect.mRight;
- S32 left_delta = final_rect.mLeft - last_rect.mLeft;
- S32 top_pad = final_rect.mTop - last_rect.mBottom;
- S32 top_delta = final_rect.mTop - last_rect.mTop;
- // If my left edge is almost the same, or my top edge is
- // almost the same...
- if (llabs(left_delta) <= EDGE_THRESHOLD_PIXELS
- || llabs(top_delta) <= EDGE_THRESHOLD_PIXELS)
- {
- // ...use relative positioning
- // prefer top_pad if widgets are stacking vertically
- // (coordinate system is still bottom-left here)
- if (top_pad < 0)
- {
- p.top_pad = top_pad;
- p.top_delta.setProvided(false);
- }
- else
- {
- p.top_pad.setProvided(false);
- p.top_delta = top_delta;
- }
- // null out other vertical specifiers
- p.rect.top.setProvided(false);
- p.rect.bottom.setProvided(false);
- p.bottom_delta.setProvided(false);
- converted_top = true;
-
- // prefer left_pad if widgets are stacking horizontally
- if (left_pad > 0)
- {
- p.left_pad = left_pad;
- p.left_delta.setProvided(false);
- }
- else
- {
- p.left_pad.setProvided(false);
- p.left_delta = left_delta;
- }
- p.rect.left.setProvided(false);
- p.rect.right.setProvided(false);
- }
- }
-
- if (!converted_top)
- {
- // ...this is the first widget, or one that wasn't aligned
- // prefer top/left specification
- p.rect.top = final_rect.mTop;
- p.rect.bottom.setProvided(false);
- p.bottom_delta.setProvided(false);
- p.top_pad.setProvided(false);
- p.top_delta.setProvided(false);
- }
-}
-
-static void convert_coords_to_top_left(LLView::Params& p, LLView* parent)
-{
- // Convert the coordinate system to be top-left based.
- if (p.rect.top.isProvided())
- {
- p.rect.top = invert_vertical(p.rect.top, parent);
- }
- if (p.rect.bottom.isProvided())
- {
- p.rect.bottom = invert_vertical(p.rect.bottom, parent);
- }
- if (p.top_pad.isProvided())
- {
- p.top_pad = -p.top_pad;
- }
- if (p.top_delta.isProvided())
- {
- p.top_delta = -p.top_delta;
- }
- if (p.bottom_delta.isProvided())
- {
- p.bottom_delta = -p.bottom_delta;
- }
- p.layout = "topleft";
-}
-
-//static
-void LLView::setupParamsForExport(Params& p, LLView* parent)
-{
- // Don't convert if already top-left based
- if (p.layout() == "topleft")
- {
- return;
- }
-
- // heuristic: Many of our floaters and panels were bulk-exported.
- // These specify exactly bottom/left and height/width.
- // Others were done by hand using bottom_delta and/or left_delta.
- // Some rely on not specifying left to mean align with left edge.
- // Try to convert both to use relative layout, but using top-left
- // coordinates.
- // Avoid rectangles where top/bottom/left/right was specified.
- if (p.rect.height.isProvided() && p.rect.width.isProvided())
- {
- if (p.rect.bottom.isProvided() && p.rect.left.isProvided())
- {
- // standard bulk export, convert it
- convert_to_relative_layout(p, parent);
- }
- else if (p.rect.bottom.isProvided() && p.left_delta.isProvided())
- {
- // hand layout with left_delta
- convert_to_relative_layout(p, parent);
- }
- else if (p.bottom_delta.isProvided())
- {
- // hand layout with bottom_delta
- // don't check for p.rect.left or p.left_delta because sometimes
- // this layout doesn't set it for widgets that are left-aligned
- convert_to_relative_layout(p, parent);
- }
- }
-
- convert_coords_to_top_left(p, parent);
-}
-
-LLView::tree_iterator_t LLView::beginTreeDFS()
-{
- return tree_iterator_t(this,
- boost::bind(boost::mem_fn(&LLView::beginChild), _1),
- boost::bind(boost::mem_fn(&LLView::endChild), _1));
-}
-
-LLView::tree_iterator_t LLView::endTreeDFS()
-{
- // an empty iterator is an "end" iterator
- return tree_iterator_t();
-}
-
-LLView::tree_post_iterator_t LLView::beginTreeDFSPost()
-{
- return tree_post_iterator_t(this,
- boost::bind(boost::mem_fn(&LLView::beginChild), _1),
- boost::bind(boost::mem_fn(&LLView::endChild), _1));
-}
-
-LLView::tree_post_iterator_t LLView::endTreeDFSPost()
-{
- // an empty iterator is an "end" iterator
- return tree_post_iterator_t();
-}
-
-LLView::bfs_tree_iterator_t LLView::beginTreeBFS()
-{
- return bfs_tree_iterator_t(this,
- boost::bind(boost::mem_fn(&LLView::beginChild), _1),
- boost::bind(boost::mem_fn(&LLView::endChild), _1));
-}
-
-LLView::bfs_tree_iterator_t LLView::endTreeBFS()
-{
- // an empty iterator is an "end" iterator
- return bfs_tree_iterator_t();
-}
-
-
-LLView::root_to_view_iterator_t LLView::beginRootToView()
-{
- return root_to_view_iterator_t(this, boost::bind(&LLView::getParent, _1));
-}
-
-LLView::root_to_view_iterator_t LLView::endRootToView()
-{
- return root_to_view_iterator_t();
-}
-
-
-// only create maps on demand, as they incur heap allocation/deallocation cost
-// when a view is constructed/deconstructed
-LLView& LLView::getDefaultWidgetContainer() const
-{
- if (!mDefaultWidgets)
- {
- LLView::Params p;
- p.name = "default widget container";
- p.visible = false; // ensures default widgets can't steal focus, etc.
- mDefaultWidgets = new LLView(p);
- }
- return *mDefaultWidgets;
-}
-
-S32 LLView::notifyParent(const LLSD& info)
-{
- LLView* parent = getParent();
- if(parent)
- return parent->notifyParent(info);
- return 0;
-}
-bool LLView::notifyChildren(const LLSD& info)
-{
- bool ret = false;
- for (LLView* childp : mChildList)
- {
- ret = ret || childp->notifyChildren(info);
- }
- return ret;
-}
-
-// convenient accessor for draw context
-const LLViewDrawContext& LLView::getDrawContext()
-{
- return LLViewDrawContext::getCurrentContext();
-}
-
-const LLViewDrawContext& LLViewDrawContext::getCurrentContext()
-{
- static LLViewDrawContext default_context;
-
- if (sDrawContextStack.empty())
- return default_context;
-
- return *sDrawContextStack.back();
-}
-
-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);
-}
+/**
+ * @file llview.cpp
+ * @author James Cook
+ * @brief Container for other views, anything that draws.
+ *
+ * $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"
+
+#define LLVIEW_CPP
+#include "llview.h"
+
+#include <sstream>
+#include <boost/tokenizer.hpp>
+#include <boost/bind.hpp>
+
+#include "llrender.h"
+#include "llevent.h"
+#include "llfocusmgr.h"
+#include "llrect.h"
+#include "llstl.h"
+#include "llui.h"
+#include "lluictrl.h"
+#include "llwindow.h"
+#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::sDebugUnicode = false;
+bool LLView::sDebugCamera = false;
+bool LLView::sIsRectDirty = false;
+LLRect LLView::sDirtyRect;
+bool LLView::sDebugRectsShowNames = true;
+bool LLView::sDebugKeys = false;
+bool LLView::sDebugMouseHandling = false;
+std::string LLView::sMouseHandlerMessage;
+bool LLView::sForceReshape = false;
+std::set<LLView*> LLView::sPreviewHighlightedElements;
+bool LLView::sHighlightingDiffs = false;
+LLView* LLView::sPreviewClickedElement = NULL;
+bool LLView::sDrawPreviewHighlights = false;
+S32 LLView::sLastLeftXML = S32_MIN;
+S32 LLView::sLastBottomXML = S32_MIN;
+std::vector<LLViewDrawContext*> LLViewDrawContext::sDrawContextStack;
+
+LLView::DrilldownFunc LLView::sDrilldown =
+ boost::bind(&LLView::pointInView, _1, _2, _3, HIT_TEST_USE_BOUNDING_RECT);
+
+//#if LL_DEBUG
+bool LLView::sIsDrawing = false;
+//#endif
+
+// Compiler optimization, generate extern template
+template class LLView* LLView::getChild<class LLView>(
+ const std::string& name, bool recurse) const;
+
+static LLDefaultChildRegistry::Register<LLView> r("view");
+
+void deleteView(LLView *aView)
+{
+ delete aView;
+}
+
+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)
+{}
+
+LLView::Params::Params()
+: name("name", std::string("unnamed")),
+ enabled("enabled", true),
+ visible("visible", true),
+ mouse_opaque("mouse_opaque", true),
+ follows("follows"),
+ hover_cursor("hover_cursor", "UI_CURSOR_ARROW"),
+ use_bounding_rect("use_bounding_rect", false),
+ tab_group("tab_group", 0),
+ default_tab_group("default_tab_group"),
+ tool_tip("tool_tip"),
+ sound_flags("sound_flags", MOUSE_UP),
+ layout("layout"),
+ rect("rect"),
+ bottom_delta("bottom_delta", S32_MAX),
+ top_pad("top_pad"),
+ top_delta("top_delta", S32_MAX),
+ left_pad("left_pad"),
+ left_delta("left_delta", S32_MAX),
+ from_xui("from_xui", false),
+ focus_root("focus_root", false),
+ needs_translate("translate"),
+ xmlns("xmlns"),
+ xmlns_xsi("xmlns:xsi"),
+ xsi_schemaLocation("xsi:schemaLocation"),
+ xsi_type("xsi:type")
+
+{
+ addSynonym(rect, "");
+}
+
+LLView::LLView(const LLView::Params& p)
+: mVisible(p.visible),
+ mInDraw(false),
+ mName(p.name),
+ mParentView(NULL),
+ mReshapeFlags(FOLLOWS_NONE),
+ mFromXUI(p.from_xui),
+ mIsFocusRoot(p.focus_root),
+ mLastVisible(false),
+ mHoverCursor(getCursorFromString(p.hover_cursor)),
+ mEnabled(p.enabled),
+ mMouseOpaque(p.mouse_opaque),
+ mSoundFlags(p.sound_flags),
+ mUseBoundingRect(p.use_bounding_rect),
+ mDefaultTabGroup(p.default_tab_group),
+ mLastTabGroup(0),
+ mToolTipMsg((LLStringExplicit)p.tool_tip()),
+ mDefaultWidgets(NULL)
+{
+ // create rect first, as this will supply initial follows flags
+ setShape(p.rect);
+ parseFollowsFlags(p);
+}
+
+LLView::~LLView()
+{
+ dirtyRect();
+ //LL_INFOS() << "Deleting view " << mName << ":" << (void*) this << LL_ENDL;
+ if (LLView::sIsDrawing)
+ {
+ LL_DEBUGS() << "Deleting view " << mName << " during UI draw() phase" << LL_ENDL;
+ }
+// llassert(LLView::sIsDrawing == false);
+
+// llassert_always(sDepth == 0); // avoid deleting views while drawing! It can subtly break list iterators
+
+ if( hasMouseCapture() )
+ {
+ //LL_WARNS() << "View holding mouse capture deleted: " << getName() << ". Mouse capture removed." << LL_ENDL;
+ gFocusMgr.removeMouseCaptureWithoutCallback( this );
+ }
+
+ deleteAllChildren();
+
+ if (mParentView != NULL)
+ {
+ mParentView->removeChild(this);
+ }
+
+ if (mDefaultWidgets)
+ {
+ delete mDefaultWidgets;
+ mDefaultWidgets = NULL;
+ }
+}
+
+// virtual
+bool LLView::isCtrl() const
+{
+ return false;
+}
+
+// virtual
+bool LLView::isPanel() const
+{
+ return false;
+}
+
+void LLView::setToolTip(const LLStringExplicit& msg)
+{
+ mToolTipMsg = msg;
+}
+
+bool LLView::setToolTipArg(const LLStringExplicit& key, const LLStringExplicit& text)
+{
+ mToolTipMsg.setArg(key, text);
+ return true;
+}
+
+void LLView::setToolTipArgs( const LLStringUtil::format_map_t& args )
+{
+ mToolTipMsg.setArgList(args);
+}
+
+// virtual
+void LLView::setRect(const LLRect& rect)
+{
+ mRect = rect;
+ updateBoundingRect();
+}
+
+void LLView::setUseBoundingRect( bool use_bounding_rect )
+{
+ if (mUseBoundingRect != use_bounding_rect)
+ {
+ mUseBoundingRect = use_bounding_rect;
+ updateBoundingRect();
+ }
+}
+
+bool LLView::getUseBoundingRect() const
+{
+ return mUseBoundingRect;
+}
+
+// virtual
+const std::string& LLView::getName() const
+{
+ static std::string no_name("(no name)");
+
+ return mName.empty() ? no_name : mName;
+}
+
+void LLView::sendChildToFront(LLView* child)
+{
+// llassert_always(sDepth == 0); // Avoid re-ordering while drawing; it can cause subtle iterator bugs
+ if (child && child->getParent() == this)
+ {
+ // minor optimization, but more importantly,
+ // won't temporarily create an empty list
+ if (child != mChildList.front())
+ {
+ mChildList.remove( child );
+ mChildList.push_front(child);
+ }
+ }
+}
+
+void LLView::sendChildToBack(LLView* child)
+{
+// llassert_always(sDepth == 0); // Avoid re-ordering while drawing; it can cause subtle iterator bugs
+ if (child && child->getParent() == this)
+ {
+ // minor optimization, but more importantly,
+ // won't temporarily create an empty list
+ if (child != mChildList.back())
+ {
+ mChildList.remove( child );
+ mChildList.push_back(child);
+ }
+ }
+}
+
+// virtual
+bool LLView::addChild(LLView* child, S32 tab_group)
+{
+ if (!child)
+ {
+ return false;
+ }
+
+ if (this == child)
+ {
+ LL_ERRS() << "Adding view " << child->getName() << " as child of itself" << LL_ENDL;
+ }
+
+ // remove from current parent
+ if (child->mParentView)
+ {
+ child->mParentView->removeChild(child);
+ }
+
+ // add to front of child list, as normal
+ mChildList.push_front(child);
+
+ // add to tab order list
+ if (tab_group != 0)
+ {
+ mTabOrder.insert(tab_order_pair_t(child, tab_group));
+ }
+
+ child->mParentView = this;
+ if (getVisible() && child->getVisible())
+ {
+ // if child isn't visible it won't affect bounding rect
+ // if current view is not visible it will be recalculated
+ // on visibility change
+ updateBoundingRect();
+ }
+ mLastTabGroup = tab_group;
+ return true;
+}
+
+
+bool LLView::addChildInBack(LLView* child, S32 tab_group)
+{
+ if(addChild(child, tab_group))
+ {
+ sendChildToBack(child);
+ return true;
+ }
+
+ return false;
+}
+
+// remove the specified child from the view, and set it's parent to NULL.
+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);
+ mChildList.remove( child );
+ child->mParentView = NULL;
+ child_tab_order_t::iterator found = mTabOrder.find(child);
+ if (found != mTabOrder.end())
+ {
+ mTabOrder.erase(found);
+ }
+ }
+ else
+ {
+ LL_WARNS() << "\"" << child->getName() << "\" is not a child of " << getName() << LL_ENDL;
+ }
+ updateBoundingRect();
+}
+
+bool LLView::isInVisibleChain() const
+{
+ bool visible = true;
+
+ const LLView* viewp = this;
+ while(viewp)
+ {
+ if (!viewp->getVisible())
+ {
+ visible = false;
+ break;
+ }
+ viewp = viewp->getParent();
+ }
+
+ return visible;
+}
+
+bool LLView::isInEnabledChain() const
+{
+ bool enabled = true;
+
+ const LLView* viewp = this;
+ while(viewp)
+ {
+ if (!viewp->getEnabled())
+ {
+ enabled = false;
+ break;
+ }
+ viewp = viewp->getParent();
+ }
+
+ 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 << '/';
+
+ // substitute all '/' in name with appropriate code
+ std::string name = view->getName();
+ std::size_t found = name.find('/');
+ std::size_t start = 0;
+ while (found != std::string::npos)
+ {
+ std::size_t sub_len = found - start;
+ if (sub_len > 0)
+ {
+ out << name.substr(start, sub_len);
+ }
+ out << "%2F";
+ start = found + 1;
+ found = name.find('/', start);
+ }
+ if (start < name.size())
+ {
+ out << name.substr(start, name.size() - start);
+ }
+}
+
+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
+{
+ return true;
+}
+
+//virtual
+void LLView::setEnabled(bool enabled)
+{
+ mEnabled = enabled;
+}
+
+//virtual
+bool LLView::isAvailable() const
+{
+ return isInEnabledChain() && isInVisibleChain();
+}
+
+//static
+bool LLView::isAvailable(const LLView* view)
+{
+ return view && view->isAvailable();
+}
+
+//virtual
+bool LLView::setLabelArg( const std::string& key, const LLStringExplicit& text )
+{
+ return false;
+}
+
+//virtual
+LLRect LLView::getSnapRect() const
+{
+ return mRect;
+}
+
+//virtual
+LLRect LLView::getRequiredRect()
+{
+ return mRect;
+}
+
+bool LLView::focusNextRoot()
+{
+ LLView::child_list_t result = LLView::getFocusRootsQuery().run(this);
+ return LLView::focusNext(result);
+}
+
+bool LLView::focusPrevRoot()
+{
+ LLView::child_list_t result = LLView::getFocusRootsQuery().run(this);
+ return LLView::focusPrev(result);
+}
+
+// static
+bool LLView::focusNext(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();
+ ++iter)
+ {
+ if(gFocusMgr.childHasKeyboardFocus(*iter))
+ {
+ focused = iter;
+ break;
+ }
+ }
+ 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.rend())
+ {
+ next = result.rbegin();
+ }
+ if ((*next)->isCtrl() && ((LLUICtrl*)*next)->hasTabStop())
+ {
+ LLUICtrl * ctrl = static_cast<LLUICtrl*>(*next);
+ ctrl->setFocus(true);
+ ctrl->onTabInto();
+ gFocusMgr.triggerFocusFlash();
+ return true;
+ }
+ ++next;
+ }
+ return false;
+}
+
+// static
+bool LLView::focusPrev(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();
+ ++iter)
+ {
+ if(gFocusMgr.childHasKeyboardFocus(*iter))
+ {
+ focused = iter;
+ break;
+ }
+ }
+ 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.end())
+ {
+ next = result.begin();
+ }
+ if((*next)->isCtrl())
+ {
+ LLUICtrl * ctrl = static_cast<LLUICtrl*>(*next);
+ if (!ctrl->hasFocus())
+ {
+ ctrl->setFocus(true);
+ ctrl->onTabInto();
+ gFocusMgr.triggerFocusFlash();
+ }
+ return true;
+ }
+ ++next;
+ }
+ return false;
+}
+
+// delete all children. Override this function if you need to
+// perform any extra clean up such as cached pointers to selected
+// children, etc.
+void LLView::deleteAllChildren()
+{
+ // clear out the control ordering
+ mTabOrder.clear();
+
+ while (!mChildList.empty())
+ {
+ LLView* viewp = mChildList.front();
+ viewp->mParentView = NULL;
+ delete viewp;
+ mChildList.pop_front();
+ }
+ updateBoundingRect();
+}
+
+void LLView::setAllChildrenEnabled(bool b)
+{
+ for (LLView* viewp : mChildList)
+ {
+ viewp->setEnabled(b);
+ }
+}
+
+// virtual
+void LLView::setVisible(bool visible)
+{
+ if ( mVisible != visible )
+ {
+ mVisible = visible;
+
+ // notify children of visibility change if root, or part of visible hierarchy
+ if (!getParent() || getParent()->isInVisibleChain())
+ {
+ // tell all children of this view that the visibility may have changed
+ dirtyRect();
+ onVisibilityChange( visible );
+ }
+ updateBoundingRect();
+ }
+}
+
+// virtual
+void LLView::onVisibilityChange ( bool new_visibility )
+{
+ bool old_visibility;
+ bool log_visibility_change = LLViewerEventRecorder::instance().getLoggingStatus();
+ for (LLView* viewp : mChildList)
+ {
+ if (!viewp)
+ {
+ continue;
+ }
+
+ // only views that are themselves visible will have their overall visibility affected by their ancestors
+ old_visibility=viewp->getVisible();
+
+ if(log_visibility_change)
+ {
+ 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;
+
+ }
+ }
+}
+
+// virtual
+void LLView::onUpdateScrollToChild(const LLUICtrl * cntrl)
+{
+ LLView* parent_view = getParent();
+ if (parent_view)
+ {
+ parent_view->onUpdateScrollToChild(cntrl);
+ }
+}
+
+// virtual
+void LLView::translate(S32 x, S32 y)
+{
+ mRect.translate(x, y);
+ updateBoundingRect();
+}
+
+// virtual
+bool LLView::canSnapTo(const LLView* other_view)
+{
+ return other_view != this && other_view->getVisible();
+}
+
+// virtual
+void LLView::setSnappedTo(const LLView* snap_view)
+{
+}
+
+bool LLView::handleHover(S32 x, S32 y, MASK mask)
+{
+ return childrenHandleHover( x, y, mask ) != NULL;
+}
+
+void LLView::onMouseEnter(S32 x, S32 y, MASK mask)
+{
+ //LL_INFOS() << "Mouse entered " << getName() << LL_ENDL;
+}
+
+void LLView::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+ //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() )
+ {
+ for (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)
+{
+ for (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)
+{
+ for (LLView* viewp : mChildList)
+ {
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ // 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)
+ || viewp->blockMouseEvent(local_x, local_y))
+ {
+ // This is NOT event recording related
+ viewp->logMouseEvent();
+ return viewp;
+ }
+ }
+ return NULL;
+}
+
+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;
+
+ for (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;
+ }
+
+ // 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))
+ {
+ return viewp;
+ }
+ }
+ return NULL;
+}
+
+LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask)
+{
+ for (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::getInstance()->mWindow->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, bool recur)
+{
+ if (!getVisible())
+ return NULL;
+
+ for (LLView* viewp : mChildList)
+ {
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ 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;
+
+ }
+ return 0;
+}
+
+F32 LLView::getTooltipTimeout()
+{
+ static LLCachedControl<F32> tooltip_fast_delay(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipFastDelay", 0.1f);
+ static LLCachedControl<F32> tooltip_delay(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipDelay", 0.7f);
+ // allow "scrubbing" over ui by showing next tooltip immediately
+ // if previous one was still visible
+ return (F32)(LLToolTipMgr::instance().toolTipVisible()
+ ? tooltip_fast_delay
+ : tooltip_delay);
+}
+
+// virtual
+const std::string LLView::getToolTip() const
+{
+ if (sDebugUnicode)
+ {
+ std::string text = getText();
+ if (!text.empty())
+ {
+ const std::string& name = getName();
+ std::string tooltip = llformat("Name: \"%s\"", name.c_str());
+
+ if (const LLFontGL* font = getFont())
+ {
+ tooltip += llformat("\nFont: %s (%s)",
+ font->getFontDesc().getName().c_str(),
+ font->getFontDesc().getSize().c_str()
+ );
+ }
+
+ tooltip += "\n\n" + utf8str_showBytesUTF8(text);
+
+ return tooltip;
+ }
+ }
+
+ return mToolTipMsg.getString();
+}
+
+bool LLView::handleToolTip(S32 x, S32 y, MASK mask)
+{
+ bool handled = false;
+
+ // parents provide tooltips first, which are optionally
+ // overridden by children, in case child is mouse_opaque
+ std::string tooltip = getToolTip();
+ if (!tooltip.empty())
+ {
+ static LLCachedControl<bool> allow_ui_tooltips(*LLUI::getInstance()->mSettingGroups["config"], "BasicUITooltips", true);
+
+ // Even if we don't show tooltips, consume the event, nothing below should show tooltip
+ if (allow_ui_tooltips)
+ {
+ LLToolTipMgr::instance().show(LLToolTip::Params()
+ .message(tooltip)
+ .sticky_rect(calcScreenRect())
+ .delay_time(getTooltipTimeout()));
+ }
+ handled = true;
+ }
+
+ // child tooltips will override our own
+ LLView* child_handler = childrenHandleToolTip(x, y, mask);
+ if (child_handler)
+ {
+ handled = true;
+ }
+
+ return handled;
+}
+
+bool LLView::handleKey(KEY key, MASK mask, bool called_from_parent)
+{
+ bool handled = false;
+
+ if (getVisible() && getEnabled())
+ {
+ if( called_from_parent )
+ {
+ // Downward traversal
+ handled = childrenHandleKey( key, mask ) != NULL;
+ }
+
+ 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)
+ {
+ LL_DEBUGS() << "Key handled by " << getName() << LL_ENDL;
+ }
+ }
+ }
+
+ if( !handled && !called_from_parent && mParentView)
+ {
+ // Upward traversal
+ handled = mParentView->handleKey( key, mask, false );
+ }
+ return handled;
+}
+
+bool LLView::handleKeyUp(KEY key, MASK mask, bool called_from_parent)
+{
+ bool handled = false;
+
+ if (getVisible() && getEnabled())
+ {
+ if (called_from_parent)
+ {
+ // Downward traversal
+ handled = childrenHandleKeyUp(key, mask) != NULL;
+ }
+
+ 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 = handleKeyUpHere(key, mask);
+ if (handled)
+ {
+ LL_DEBUGS() << "Key handled by " << getName() << LL_ENDL;
+ }
+ }
+ }
+
+ if (!handled && !called_from_parent && mParentView)
+ {
+ // Upward traversal
+ handled = mParentView->handleKeyUp(key, mask, false);
+ }
+ return handled;
+}
+
+// Called from handleKey()
+// Handles key in this object. Checking parents and children happens in handleKey()
+bool LLView::handleKeyHere(KEY key, MASK mask)
+{
+ return false;
+}
+
+// Called from handleKey()
+// Handles key in this object. Checking parents and children happens in handleKey()
+bool LLView::handleKeyUpHere(KEY key, MASK mask)
+{
+ return false;
+}
+
+bool LLView::handleUnicodeChar(llwchar uni_char, bool called_from_parent)
+{
+ bool handled = false;
+
+ if (getVisible() && getEnabled())
+ {
+ if( called_from_parent )
+ {
+ // Downward traversal
+ handled = childrenHandleUnicodeChar( uni_char ) != NULL;
+ }
+
+ if (!handled)
+ {
+ handled = handleUnicodeCharHere(uni_char);
+ if (handled && LLView::sDebugKeys)
+ {
+ LL_INFOS() << "Unicode key " << wchar_utf8_preview(uni_char) << " is handled by " << getName() << LL_ENDL;
+ }
+ }
+ }
+
+ if (!handled && !called_from_parent && mParentView)
+ {
+ // Upward traversal
+ handled = mParentView->handleUnicodeChar(uni_char, false);
+ }
+
+ if (handled)
+ {
+ LLViewerEventRecorder::instance().logKeyUnicodeEvent(uni_char);
+ }
+
+ return handled;
+}
+
+
+bool LLView::handleUnicodeCharHere(llwchar uni_char )
+{
+ return false;
+}
+
+
+bool LLView::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
+ EDragAndDropType cargo_type, void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ return childrenHandleDragAndDrop( x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL;
+}
+
+void LLView::onMouseCaptureLost()
+{
+}
+
+bool LLView::hasMouseCapture()
+{
+ return gFocusMgr.getMouseCapture() == this;
+}
+
+bool LLView::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ LLView* r = childrenHandleMouseUp( x, y, mask );
+
+ return (r!=NULL);
+}
+
+bool LLView::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLView* r= childrenHandleMouseDown(x, y, mask );
+
+ return (r!=NULL);
+}
+
+bool LLView::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ return childrenHandleDoubleClick( x, y, mask ) != NULL;
+}
+
+bool LLView::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ return childrenHandleScrollWheel( x, y, clicks ) != NULL;
+}
+
+bool LLView::handleScrollHWheel(S32 x, S32 y, S32 clicks)
+{
+ return childrenHandleScrollHWheel( x, y, clicks ) != NULL;
+}
+
+bool LLView::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ return childrenHandleRightMouseDown( x, y, mask ) != NULL;
+}
+
+bool LLView::handleRightMouseUp(S32 x, S32 y, MASK mask)
+{
+ return childrenHandleRightMouseUp( x, y, mask ) != NULL;
+}
+
+bool LLView::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
+{
+ return childrenHandleMiddleMouseDown( x, y, mask ) != NULL;
+}
+
+bool LLView::handleMiddleMouseUp(S32 x, S32 y, MASK mask)
+{
+ return childrenHandleMiddleMouseUp( x, y, mask ) != NULL;
+}
+
+LLView* LLView::childrenHandleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ return childrenHandleMouseEvent(&LLView::handleScrollWheel, x, y, clicks, false);
+}
+
+LLView* LLView::childrenHandleScrollHWheel(S32 x, S32 y, S32 clicks)
+{
+ return childrenHandleMouseEvent(&LLView::handleScrollHWheel, x, y, clicks, false);
+}
+
+// Called during downward traversal
+LLView* LLView::childrenHandleKey(KEY key, MASK mask)
+{
+ return childrenHandleCharEvent("Key", &LLView::handleKey, key, mask);
+}
+
+// Called during downward traversal
+LLView* LLView::childrenHandleKeyUp(KEY key, MASK mask)
+{
+ return childrenHandleCharEvent("Key Up", &LLView::handleKeyUp, key, mask);
+}
+
+// Called during downward traversal
+LLView* LLView::childrenHandleUnicodeChar(llwchar uni_char)
+{
+ return childrenHandleCharEvent("Unicode character", &LLView::handleUnicodeCharWithDummyMask,
+ uni_char, MASK_NONE);
+}
+
+LLView* LLView::childrenHandleMouseDown(S32 x, S32 y, MASK mask)
+{
+ return childrenHandleMouseEvent(&LLView::handleMouseDown, x, y, mask);
+}
+
+LLView* LLView::childrenHandleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ return childrenHandleMouseEvent(&LLView::handleRightMouseDown, x, y, mask);
+}
+
+LLView* LLView::childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask)
+{
+ return childrenHandleMouseEvent(&LLView::handleMiddleMouseDown, x, y, mask);
+}
+
+LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ return childrenHandleMouseEvent(&LLView::handleDoubleClick, x, y, mask);
+}
+
+LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask)
+{
+ return childrenHandleMouseEvent(&LLView::handleMouseUp, x, y, mask);
+}
+
+LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask)
+{
+ return childrenHandleMouseEvent(&LLView::handleRightMouseUp, x, y, mask);
+}
+
+LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask)
+{
+ return childrenHandleMouseEvent(&LLView::handleMiddleMouseUp, x, y, mask);
+}
+
+void LLView::draw()
+{
+ drawChildren();
+}
+
+void LLView::drawChildren()
+{
+ if (!mChildList.empty())
+ {
+ LLView* rootp = LLUI::getInstance()->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())
+ {
+ LLRect screen_rect = viewp->calcScreenRect();
+ if ( rootp->getLocalRect().overlaps(screen_rect) && sDirtyRect.overlaps(screen_rect))
+ {
+ LLUI::pushMatrix();
+ {
+ 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)
+ {
+ viewp->drawDebugRect();
+
+ // Check for bogus rectangle
+ if (!getRect().isValid())
+ {
+ LL_WARNS() << "Bogus rectangle for " << getName() << " with " << mRect << LL_ENDL;
+ }
+ }
+ }
+ LLUI::popMatrix();
+ }
+ }
+
+ }
+ --sDepth;
+ }
+}
+
+void LLView::dirtyRect()
+{
+ LLView* child = getParent();
+ LLView* parent = child ? child->getParent() : NULL;
+ LLView* cur = this;
+ while (child && parent && parent->getParent())
+ { //find third to top-most view
+ cur = child;
+ child = parent;
+ parent = parent->getParent();
+ }
+
+ if (!sIsRectDirty)
+ {
+ sDirtyRect = cur->calcScreenRect();
+ sIsRectDirty = true;
+ }
+ else
+ {
+ sDirtyRect.unionWith(cur->calcScreenRect());
+ }
+}
+
+//Draw a box for debugging.
+void LLView::drawDebugRect()
+{
+ std::set<LLView*>::iterator preview_iter = std::find(sPreviewHighlightedElements.begin(), sPreviewHighlightedElements.end(), this); // figure out if it's a previewed element
+
+ LLUI::pushMatrix();
+ {
+ // drawing solids requires texturing be disabled
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ if (getUseBoundingRect())
+ {
+ LLUI::translate((F32)mBoundingRect.mLeft - (F32)mRect.mLeft, (F32)mBoundingRect.mBottom - (F32)mRect.mBottom);
+ }
+
+ LLRect debug_rect = getUseBoundingRect() ? mBoundingRect : mRect;
+
+ // draw red rectangle for the border
+ LLColor4 border_color(0.25f, 0.25f, 0.25f, 1.f);
+ if(preview_iter != sPreviewHighlightedElements.end())
+ {
+ if(LLView::sPreviewClickedElement && this == sPreviewClickedElement)
+ {
+ border_color = LLColor4::red;
+ }
+ else
+ {
+ static LLUIColor scroll_highlighted_color = LLUIColorTable::instance().getColor("ScrollHighlightedColor");
+ border_color = scroll_highlighted_color;
+ }
+ }
+ else
+ {
+ border_color.mV[sDepth%3] = 1.f;
+ }
+
+ gGL.color4fv( border_color.mV );
+
+ gGL.begin(LLRender::LINES);
+ gGL.vertex2i(0, debug_rect.getHeight() - 1);
+ gGL.vertex2i(0, 0);
+
+ gGL.vertex2i(0, 0);
+ gGL.vertex2i(debug_rect.getWidth() - 1, 0);
+
+ gGL.vertex2i(debug_rect.getWidth() - 1, 0);
+ gGL.vertex2i(debug_rect.getWidth() - 1, debug_rect.getHeight() - 1);
+
+ gGL.vertex2i(debug_rect.getWidth() - 1, debug_rect.getHeight() - 1);
+ gGL.vertex2i(0, debug_rect.getHeight() - 1);
+ gGL.end();
+
+ // Draw the name if it's not a leaf node or not in editing or preview mode
+ if (mChildList.size()
+ && preview_iter == sPreviewHighlightedElements.end()
+ && sDebugRectsShowNames)
+ {
+ S32 x, y;
+ gGL.color4fv( border_color.mV );
+
+ 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,
+ LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
+ }
+ }
+ LLUI::popMatrix();
+}
+
+void LLView::drawChild(LLView* childp, S32 x_offset, S32 y_offset, bool force_draw)
+{
+ if (childp && childp->getParent() == this)
+ {
+ ++sDepth;
+
+ if ((childp->getVisible() && childp->getRect().isValid())
+ || force_draw)
+ {
+ gGL.matrixMode(LLRender::MM_MODELVIEW);
+ LLUI::pushMatrix();
+ {
+ LLUI::translate((F32)childp->getRect().mLeft + x_offset, (F32)childp->getRect().mBottom + y_offset);
+ childp->draw();
+ }
+ LLUI::popMatrix();
+ }
+
+ --sDepth;
+ }
+}
+
+
+void LLView::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ // compute how much things changed and apply reshape logic to children
+ S32 delta_width = width - getRect().getWidth();
+ S32 delta_height = height - getRect().getHeight();
+
+ if (delta_width || delta_height || sForceReshape)
+ {
+ // adjust our rectangle
+ mRect.mRight = getRect().mLeft + width;
+ mRect.mTop = getRect().mBottom + height;
+
+ // move child views according to reshape flags
+ for (LLView* viewp : mChildList)
+ {
+ if (viewp != NULL)
+ {
+ LLRect child_rect( viewp->mRect );
+
+ if (viewp->followsRight() && viewp->followsLeft())
+ {
+ child_rect.mRight += delta_width;
+ }
+ else if (viewp->followsRight())
+ {
+ child_rect.mLeft += delta_width;
+ child_rect.mRight += delta_width;
+ }
+ else if (viewp->followsLeft())
+ {
+ // left is 0, don't need to adjust coords
+ }
+ else
+ {
+ // BUG what to do when we don't follow anyone?
+ // for now, same as followsLeft
+ }
+
+ if (viewp->followsTop() && viewp->followsBottom())
+ {
+ child_rect.mTop += delta_height;
+ }
+ else if (viewp->followsTop())
+ {
+ child_rect.mTop += delta_height;
+ child_rect.mBottom += delta_height;
+ }
+ else if (viewp->followsBottom())
+ {
+ // bottom is 0, so don't need to adjust coords
+ }
+ else
+ {
+ // BUG what to do when we don't follow?
+ // for now, same as bottom
+ }
+
+ S32 delta_x = child_rect.mLeft - viewp->getRect().mLeft;
+ S32 delta_y = child_rect.mBottom - viewp->getRect().mBottom;
+ viewp->translate( delta_x, delta_y );
+ if (child_rect.getWidth() != viewp->getRect().getWidth()
+ || child_rect.getHeight() != viewp->getRect().getHeight()
+ || sForceReshape)
+ {
+ viewp->reshape(child_rect.getWidth(), child_rect.getHeight());
+ }
+ }
+ }
+ }
+
+ if (!called_from_parent)
+ {
+ if (mParentView)
+ {
+ mParentView->reshape(mParentView->getRect().getWidth(), mParentView->getRect().getHeight(), false);
+ }
+ }
+
+ updateBoundingRect();
+}
+
+LLRect LLView::calcBoundingRect()
+{
+ LLRect local_bounding_rect = LLRect::null;
+
+ for (LLView* childp : mChildList)
+ {
+ // ignore invisible and "top" children when calculating bounding rect
+ // such as combobox popups
+ if (!childp->getVisible() || childp == gFocusMgr.getTopCtrl())
+ {
+ continue;
+ }
+
+ LLRect child_bounding_rect = childp->getBoundingRect();
+
+ if (local_bounding_rect.isEmpty())
+ {
+ // start out with bounding rect equal to first visible child's bounding rect
+ local_bounding_rect = child_bounding_rect;
+ }
+ else
+ {
+ // accumulate non-null children rectangles
+ if (!child_bounding_rect.isEmpty())
+ {
+ local_bounding_rect.unionWith(child_bounding_rect);
+ }
+ }
+ }
+
+ // convert to parent-relative coordinates
+ local_bounding_rect.translate(mRect.mLeft, mRect.mBottom);
+ return local_bounding_rect;
+}
+
+
+void LLView::updateBoundingRect()
+{
+ if (isDead()) return;
+
+ LLRect cur_rect = mBoundingRect;
+
+ if (getUseBoundingRect())
+ {
+ mBoundingRect = calcBoundingRect();
+ }
+ else
+ {
+ mBoundingRect = mRect;
+ }
+
+ // give parent view a chance to resize, in case we just moved, for example
+ if (getParent() && getParent()->getUseBoundingRect())
+ {
+ getParent()->updateBoundingRect();
+ }
+
+ if (mBoundingRect != cur_rect)
+ {
+ dirtyRect();
+ }
+
+}
+
+LLRect LLView::calcScreenRect() const
+{
+ LLRect screen_rect;
+ localPointToScreen(0, 0, &screen_rect.mLeft, &screen_rect.mBottom);
+ localPointToScreen(getRect().getWidth(), getRect().getHeight(), &screen_rect.mRight, &screen_rect.mTop);
+ return screen_rect;
+}
+
+LLRect LLView::calcScreenBoundingRect() const
+{
+ LLRect screen_rect;
+ // get bounding rect, if used
+ LLRect bounding_rect = getUseBoundingRect() ? mBoundingRect : mRect;
+
+ // convert to local coordinates, as defined by mRect
+ bounding_rect.translate(-mRect.mLeft, -mRect.mBottom);
+
+ localPointToScreen(bounding_rect.mLeft, bounding_rect.mBottom, &screen_rect.mLeft, &screen_rect.mBottom);
+ localPointToScreen(bounding_rect.mRight, bounding_rect.mTop, &screen_rect.mRight, &screen_rect.mTop);
+ return screen_rect;
+}
+
+LLRect LLView::getLocalBoundingRect() const
+{
+ LLRect local_bounding_rect = getBoundingRect();
+ local_bounding_rect.translate(-mRect.mLeft, -mRect.mBottom);
+
+ return local_bounding_rect;
+}
+
+
+LLRect LLView::getLocalRect() const
+{
+ LLRect local_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
+ return local_rect;
+}
+
+LLRect LLView::getLocalSnapRect() const
+{
+ LLRect local_snap_rect = getSnapRect();
+ local_snap_rect.translate(-getRect().mLeft, -getRect().mBottom);
+ return local_snap_rect;
+}
+
+bool LLView::hasAncestor(const LLView* parentp) const
+{
+ if (!parentp)
+ {
+ return false;
+ }
+
+ LLView* viewp = getParent();
+ while(viewp)
+ {
+ if (viewp == parentp)
+ {
+ return true;
+ }
+ viewp = viewp->getParent();
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+
+bool LLView::childHasKeyboardFocus( const std::string& childname ) const
+{
+ LLView *focus = dynamic_cast<LLView *>(gFocusMgr.getKeyboardFocus());
+
+ while (focus != NULL)
+ {
+ if (focus->getName() == childname)
+ {
+ return true;
+ }
+
+ focus = focus->getParent();
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+
+bool LLView::hasChild(const std::string& childname, bool recurse) const
+{
+ return findChildView(childname, recurse) != NULL;
+}
+
+//-----------------------------------------------------------------------------
+// getChildView()
+//-----------------------------------------------------------------------------
+LLView* LLView::getChildView(const std::string& name, bool recurse) const
+{
+ return getChild<LLView>(name, recurse);
+}
+
+LLView* LLView::findChildView(const std::string& name, bool recurse) const
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;
+
+ // Look for direct children *first*
+ for (LLView* childp : mChildList)
+ {
+ llassert(childp);
+ if (childp->getName() == name)
+ {
+ return childp;
+ }
+ }
+ if (recurse)
+ {
+ // Look inside each child as well.
+ for (LLView* childp : mChildList)
+ {
+ llassert(childp);
+ LLView* viewp = childp->findChildView(name, recurse);
+ if ( viewp )
+ {
+ return viewp;
+ }
+ }
+ }
+ return NULL;
+}
+
+bool LLView::parentPointInView(S32 x, S32 y, EHitTestType type) const
+{
+ return (getUseBoundingRect() && type == HIT_TEST_USE_BOUNDING_RECT)
+ ? mBoundingRect.pointInRect( x, y )
+ : mRect.pointInRect( x, y );
+}
+
+bool LLView::pointInView(S32 x, S32 y, EHitTestType type) const
+{
+ return (getUseBoundingRect() && type == HIT_TEST_USE_BOUNDING_RECT)
+ ? mBoundingRect.pointInRect( x + mRect.mLeft, y + mRect.mBottom )
+ : mRect.localPointInRect( x, y );
+}
+
+bool LLView::blockMouseEvent(S32 x, S32 y) const
+{
+ return mMouseOpaque && pointInView(x, y, HIT_TEST_IGNORE_BOUNDING_RECT);
+}
+
+// virtual
+void LLView::screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const
+{
+ *local_x = screen_x - getRect().mLeft;
+ *local_y = screen_y - getRect().mBottom;
+
+ const LLView* cur = this;
+ while( cur->mParentView )
+ {
+ cur = cur->mParentView;
+ *local_x -= cur->getRect().mLeft;
+ *local_y -= cur->getRect().mBottom;
+ }
+}
+
+void LLView::localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const
+{
+ *screen_x = local_x;
+ *screen_y = local_y;
+
+ const LLView* cur = this;
+ do
+ {
+ LLRect cur_rect = cur->getRect();
+ *screen_x += cur_rect.mLeft;
+ *screen_y += cur_rect.mBottom;
+ cur = cur->mParentView;
+ }
+ while( cur );
+}
+
+void LLView::screenRectToLocal(const LLRect& screen, LLRect* local) const
+{
+ *local = screen;
+ local->translate( -getRect().mLeft, -getRect().mBottom );
+
+ const LLView* cur = this;
+ while( cur->mParentView )
+ {
+ cur = cur->mParentView;
+ local->translate( -cur->getRect().mLeft, -cur->getRect().mBottom );
+ }
+}
+
+void LLView::localRectToScreen(const LLRect& local, LLRect* screen) const
+{
+ *screen = local;
+ screen->translate( getRect().mLeft, getRect().mBottom );
+
+ const LLView* cur = this;
+ while( cur->mParentView )
+ {
+ cur = cur->mParentView;
+ screen->translate( cur->getRect().mLeft, cur->getRect().mBottom );
+ }
+}
+
+LLView* LLView::getRootView()
+{
+ LLView* view = this;
+ while( view->mParentView )
+ {
+ view = view->mParentView;
+ }
+ return view;
+}
+
+LLView* LLView::findPrevSibling(LLView* child)
+{
+ child_list_t::iterator prev_it = std::find(mChildList.begin(), mChildList.end(), child);
+ if (prev_it != mChildList.end() && prev_it != mChildList.begin())
+ {
+ return *(--prev_it);
+ }
+ return NULL;
+}
+
+LLView* LLView::findNextSibling(LLView* child)
+{
+ child_list_t::iterator next_it = std::find(mChildList.begin(), mChildList.end(), child);
+ if (next_it != mChildList.end())
+ {
+ next_it++;
+ }
+
+ return (next_it != mChildList.end()) ? *next_it : NULL;
+}
+
+
+LLCoordGL getNeededTranslation(const LLRect& input, const LLRect& constraint, S32 min_overlap_pixels)
+{
+ LLCoordGL delta;
+
+ 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 (KEEP_ONSCREEN_PIXELS_WIDTH <= constraint.getWidth() &&
+ KEEP_ONSCREEN_PIXELS_HEIGHT <= constraint.getHeight())
+ {
+ 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_HEIGHT < constraint.mBottom)
+ {
+ delta.mY = constraint.mBottom - (input.mTop - KEEP_ONSCREEN_PIXELS_HEIGHT);
+ }
+ }
+
+ return delta;
+}
+
+// Moves the view so that it is entirely inside of constraint.
+// If the view will not fit because it's too big, aligns with the top and left.
+// (Why top and left? That's where the drag bars are for floaters.)
+bool LLView::translateIntoRect(const LLRect& constraint, S32 min_overlap_pixels)
+{
+ return translateRectIntoRect(getRect(), constraint, min_overlap_pixels);
+}
+
+bool LLView::translateRectIntoRect(const LLRect& rect, const LLRect& constraint, S32 min_overlap_pixels)
+{
+ LLCoordGL translation = getNeededTranslation(rect, constraint, min_overlap_pixels);
+
+ if (translation.mX != 0 || translation.mY != 0)
+ {
+ translate(translation.mX, translation.mY);
+ return true;
+ }
+
+ return false;
+}
+
+// move this view into "inside" but not onto "exclude"
+// NOTE: if this view is already contained in "inside", we ignore the "exclude" rect
+bool LLView::translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, S32 min_overlap_pixels)
+{
+ LLCoordGL translation = getNeededTranslation(getRect(), inside, min_overlap_pixels);
+
+ if (translation.mX != 0 || translation.mY != 0)
+ {
+ // translate ourselves into constraint rect
+ translate(translation.mX, translation.mY);
+
+ // do we overlap with exclusion area?
+ // keep moving in the same direction to the other side of the exclusion rect
+ if (exclude.overlaps(getRect()))
+ {
+ // moving right
+ if (translation.mX > 0)
+ {
+ translate(exclude.mRight - getRect().mLeft, 0);
+ }
+ // moving left
+ else if (translation.mX < 0)
+ {
+ translate(exclude.mLeft - getRect().mRight, 0);
+ }
+
+ // moving up
+ if (translation.mY > 0)
+ {
+ translate(0, exclude.mTop - getRect().mBottom);
+ }
+ // moving down
+ else if (translation.mY < 0)
+ {
+ translate(0, exclude.mBottom - getRect().mTop);
+ }
+ }
+
+ return true;
+ }
+ return false;
+}
+
+
+void LLView::centerWithin(const LLRect& bounds)
+{
+ S32 left = bounds.mLeft + (bounds.getWidth() - getRect().getWidth()) / 2;
+ S32 bottom = bounds.mBottom + (bounds.getHeight() - getRect().getHeight()) / 2;
+
+ translate( left - getRect().mLeft, bottom - getRect().mBottom );
+}
+
+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;
+
+ while (cur_view)
+ {
+ if (cur_view == other_view)
+ {
+ *other_x = x;
+ *other_y = y;
+ return true;
+ }
+
+ x += cur_view->getRect().mLeft;
+ y += cur_view->getRect().mBottom;
+
+ cur_view = cur_view->getParent();
+ root_view = cur_view;
+ }
+
+ // assuming common root between two views, chase other_view's parents up to root
+ cur_view = other_view;
+ while (cur_view)
+ {
+ x -= cur_view->getRect().mLeft;
+ y -= cur_view->getRect().mBottom;
+
+ cur_view = cur_view->getParent();
+
+ if (cur_view == root_view)
+ {
+ *other_x = x;
+ *other_y = y;
+ return true;
+ }
+ }
+
+ *other_x = x;
+ *other_y = y;
+ return false;
+}
+
+bool LLView::localRectToOtherView( const LLRect& local, LLRect* other, const LLView* other_view ) const
+{
+ LLRect cur_rect = local;
+ const LLView* cur_view = this;
+ const LLView* root_view = NULL;
+
+ while (cur_view)
+ {
+ if (cur_view == other_view)
+ {
+ *other = cur_rect;
+ return true;
+ }
+
+ cur_rect.translate(cur_view->getRect().mLeft, cur_view->getRect().mBottom);
+
+ cur_view = cur_view->getParent();
+ root_view = cur_view;
+ }
+
+ // assuming common root between two views, chase other_view's parents up to root
+ cur_view = other_view;
+ while (cur_view)
+ {
+ cur_rect.translate(-cur_view->getRect().mLeft, -cur_view->getRect().mBottom);
+
+ cur_view = cur_view->getParent();
+
+ if (cur_view == root_view)
+ {
+ *other = cur_rect;
+ return true;
+ }
+ }
+
+ *other = cur_rect;
+ 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>
+{
+ LLSINGLETON_EMPTY_CTOR(SortByTabOrder);
+ /*virtual*/ void sort(LLView * parent, LLView::child_list_t &children) const override
+ {
+ children.sort(CompareByTabOrder(parent->getTabOrder(), parent->getDefaultTabGroup()));
+ }
+};
+
+// static
+const LLViewQuery & LLView::getTabOrderQuery()
+{
+ 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;
+}
+
+// This class is only used internally by getFocusRootsQuery below.
+class LLFocusRootsFilter : public LLQueryFilter, public LLSingleton<LLFocusRootsFilter>
+{
+ LLSINGLETON_EMPTY_CTOR(LLFocusRootsFilter);
+ /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override
+ {
+ return filterResult_t(view->isCtrl() && view->isFocusRoot(), !view->isFocusRoot());
+ }
+};
+
+// static
+const LLViewQuery & LLView::getFocusRootsQuery()
+{
+ static LLViewQuery query;
+ if(query.getPreFilters().size() == 0) {
+ query.addPreFilter(LLVisibleFilter::getInstance());
+ query.addPreFilter(LLEnabledFilter::getInstance());
+ query.addPreFilter(LLFocusRootsFilter::getInstance());
+ query.addPostFilter(LLRootsFilter::getInstance());
+ }
+ return query;
+}
+
+
+void LLView::setShape(const LLRect& new_rect, bool by_user)
+{
+ if (new_rect != getRect())
+ {
+ handleReshape(new_rect, by_user);
+ }
+}
+
+void LLView::handleReshape(const LLRect& new_rect, bool by_user)
+{
+ reshape(new_rect.getWidth(), new_rect.getHeight());
+ translate(new_rect.mLeft - getRect().mLeft, new_rect.mBottom - getRect().mBottom);
+}
+
+LLView* LLView::findSnapRect(LLRect& new_rect, const LLCoordGL& mouse_dir,
+ LLView::ESnapType snap_type, S32 threshold, S32 padding)
+{
+ new_rect = mRect;
+ LLView* snap_view = NULL;
+
+ if (!mParentView)
+ {
+ return NULL;
+ }
+
+ S32 delta_x = 0;
+ S32 delta_y = 0;
+ if (mouse_dir.mX >= 0)
+ {
+ S32 new_right = mRect.mRight;
+ LLView* view = findSnapEdge(new_right, mouse_dir, SNAP_RIGHT, snap_type, threshold, padding);
+ delta_x = new_right - mRect.mRight;
+ snap_view = view ? view : snap_view;
+ }
+
+ if (mouse_dir.mX <= 0)
+ {
+ S32 new_left = mRect.mLeft;
+ LLView* view = findSnapEdge(new_left, mouse_dir, SNAP_LEFT, snap_type, threshold, padding);
+ delta_x = new_left - mRect.mLeft;
+ snap_view = view ? view : snap_view;
+ }
+
+ if (mouse_dir.mY >= 0)
+ {
+ S32 new_top = mRect.mTop;
+ LLView* view = findSnapEdge(new_top, mouse_dir, SNAP_TOP, snap_type, threshold, padding);
+ delta_y = new_top - mRect.mTop;
+ snap_view = view ? view : snap_view;
+ }
+
+ if (mouse_dir.mY <= 0)
+ {
+ S32 new_bottom = mRect.mBottom;
+ LLView* view = findSnapEdge(new_bottom, mouse_dir, SNAP_BOTTOM, snap_type, threshold, padding);
+ delta_y = new_bottom - mRect.mBottom;
+ snap_view = view ? view : snap_view;
+ }
+
+ new_rect.translate(delta_x, delta_y);
+ return snap_view;
+}
+
+LLView* LLView::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding)
+{
+ LLRect snap_rect = getSnapRect();
+ S32 snap_pos = 0;
+ switch(snap_edge)
+ {
+ case SNAP_LEFT:
+ snap_pos = snap_rect.mLeft;
+ break;
+ case SNAP_RIGHT:
+ snap_pos = snap_rect.mRight;
+ break;
+ case SNAP_TOP:
+ snap_pos = snap_rect.mTop;
+ break;
+ case SNAP_BOTTOM:
+ snap_pos = snap_rect.mBottom;
+ break;
+ }
+
+ if (!mParentView)
+ {
+ new_edge_val = snap_pos;
+ return NULL;
+ }
+
+ LLView* snap_view = NULL;
+
+ // If the view is near the edge of its parent, snap it to
+ // the edge.
+ LLRect test_rect = snap_rect;
+ test_rect.stretch(padding);
+
+ S32 x_threshold = threshold;
+ S32 y_threshold = threshold;
+
+ LLRect parent_local_snap_rect = mParentView->getLocalSnapRect();
+
+ if (snap_type == SNAP_PARENT || snap_type == SNAP_PARENT_AND_SIBLINGS)
+ {
+ switch(snap_edge)
+ {
+ case SNAP_RIGHT:
+ if (llabs(parent_local_snap_rect.mRight - test_rect.mRight) <= x_threshold
+ && (parent_local_snap_rect.mRight - test_rect.mRight) * mouse_dir.mX >= 0)
+ {
+ snap_pos = parent_local_snap_rect.mRight - padding;
+ snap_view = mParentView;
+ x_threshold = llabs(parent_local_snap_rect.mRight - test_rect.mRight);
+ }
+ break;
+ case SNAP_LEFT:
+ if (llabs(test_rect.mLeft - parent_local_snap_rect.mLeft) <= x_threshold
+ && test_rect.mLeft * mouse_dir.mX <= 0)
+ {
+ snap_pos = parent_local_snap_rect.mLeft + padding;
+ snap_view = mParentView;
+ x_threshold = llabs(test_rect.mLeft - parent_local_snap_rect.mLeft);
+ }
+ break;
+ case SNAP_BOTTOM:
+ if (llabs(test_rect.mBottom - parent_local_snap_rect.mBottom) <= y_threshold
+ && test_rect.mBottom * mouse_dir.mY <= 0)
+ {
+ snap_pos = parent_local_snap_rect.mBottom + padding;
+ snap_view = mParentView;
+ y_threshold = llabs(test_rect.mBottom - parent_local_snap_rect.mBottom);
+ }
+ break;
+ case SNAP_TOP:
+ if (llabs(parent_local_snap_rect.mTop - test_rect.mTop) <= y_threshold && (parent_local_snap_rect.mTop - test_rect.mTop) * mouse_dir.mY >= 0)
+ {
+ snap_pos = parent_local_snap_rect.mTop - padding;
+ snap_view = mParentView;
+ y_threshold = llabs(parent_local_snap_rect.mTop - test_rect.mTop);
+ }
+ break;
+ default:
+ LL_ERRS() << "Invalid snap edge" << LL_ENDL;
+ }
+ }
+
+ if (snap_type == SNAP_SIBLINGS || snap_type == SNAP_PARENT_AND_SIBLINGS)
+ {
+ for ( child_list_const_iter_t child_it = mParentView->getChildList()->begin();
+ child_it != mParentView->getChildList()->end(); ++child_it)
+ {
+ LLView* siblingp = *child_it;
+
+ if (!canSnapTo(siblingp)) continue;
+
+ LLRect sibling_rect = siblingp->getSnapRect();
+
+ switch(snap_edge)
+ {
+ case SNAP_RIGHT:
+ if (llabs(test_rect.mRight - sibling_rect.mLeft) <= x_threshold
+ && (test_rect.mRight - sibling_rect.mLeft) * mouse_dir.mX <= 0)
+ {
+ snap_pos = sibling_rect.mLeft - padding;
+ snap_view = siblingp;
+ x_threshold = llabs(test_rect.mRight - sibling_rect.mLeft);
+ }
+ // if snapped with sibling along other axis, check for shared edge
+ else if (llabs(sibling_rect.mTop - (test_rect.mBottom - padding)) <= y_threshold
+ || llabs(sibling_rect.mBottom - (test_rect.mTop + padding)) <= x_threshold)
+ {
+ if (llabs(test_rect.mRight - sibling_rect.mRight) <= x_threshold
+ && (test_rect.mRight - sibling_rect.mRight) * mouse_dir.mX <= 0)
+ {
+ snap_pos = sibling_rect.mRight;
+ snap_view = siblingp;
+ x_threshold = llabs(test_rect.mRight - sibling_rect.mRight);
+ }
+ }
+ break;
+ case SNAP_LEFT:
+ if (llabs(test_rect.mLeft - sibling_rect.mRight) <= x_threshold
+ && (test_rect.mLeft - sibling_rect.mRight) * mouse_dir.mX <= 0)
+ {
+ snap_pos = sibling_rect.mRight + padding;
+ snap_view = siblingp;
+ x_threshold = llabs(test_rect.mLeft - sibling_rect.mRight);
+ }
+ // if snapped with sibling along other axis, check for shared edge
+ else if (llabs(sibling_rect.mTop - (test_rect.mBottom - padding)) <= y_threshold
+ || llabs(sibling_rect.mBottom - (test_rect.mTop + padding)) <= y_threshold)
+ {
+ if (llabs(test_rect.mLeft - sibling_rect.mLeft) <= x_threshold
+ && (test_rect.mLeft - sibling_rect.mLeft) * mouse_dir.mX <= 0)
+ {
+ snap_pos = sibling_rect.mLeft;
+ snap_view = siblingp;
+ x_threshold = llabs(test_rect.mLeft - sibling_rect.mLeft);
+ }
+ }
+ break;
+ case SNAP_BOTTOM:
+ if (llabs(test_rect.mBottom - sibling_rect.mTop) <= y_threshold
+ && (test_rect.mBottom - sibling_rect.mTop) * mouse_dir.mY <= 0)
+ {
+ snap_pos = sibling_rect.mTop + padding;
+ snap_view = siblingp;
+ y_threshold = llabs(test_rect.mBottom - sibling_rect.mTop);
+ }
+ // if snapped with sibling along other axis, check for shared edge
+ else if (llabs(sibling_rect.mRight - (test_rect.mLeft - padding)) <= x_threshold
+ || llabs(sibling_rect.mLeft - (test_rect.mRight + padding)) <= x_threshold)
+ {
+ if (llabs(test_rect.mBottom - sibling_rect.mBottom) <= y_threshold
+ && (test_rect.mBottom - sibling_rect.mBottom) * mouse_dir.mY <= 0)
+ {
+ snap_pos = sibling_rect.mBottom;
+ snap_view = siblingp;
+ y_threshold = llabs(test_rect.mBottom - sibling_rect.mBottom);
+ }
+ }
+ break;
+ case SNAP_TOP:
+ if (llabs(test_rect.mTop - sibling_rect.mBottom) <= y_threshold
+ && (test_rect.mTop - sibling_rect.mBottom) * mouse_dir.mY <= 0)
+ {
+ snap_pos = sibling_rect.mBottom - padding;
+ snap_view = siblingp;
+ y_threshold = llabs(test_rect.mTop - sibling_rect.mBottom);
+ }
+ // if snapped with sibling along other axis, check for shared edge
+ else if (llabs(sibling_rect.mRight - (test_rect.mLeft - padding)) <= x_threshold
+ || llabs(sibling_rect.mLeft - (test_rect.mRight + padding)) <= x_threshold)
+ {
+ if (llabs(test_rect.mTop - sibling_rect.mTop) <= y_threshold
+ && (test_rect.mTop - sibling_rect.mTop) * mouse_dir.mY <= 0)
+ {
+ snap_pos = sibling_rect.mTop;
+ snap_view = siblingp;
+ y_threshold = llabs(test_rect.mTop - sibling_rect.mTop);
+ }
+ }
+ break;
+ default:
+ LL_ERRS() << "Invalid snap edge" << LL_ENDL;
+ }
+ }
+ }
+
+ new_edge_val = snap_pos;
+ return snap_view;
+}
+
+//-----------------------------------------------------------------------------
+// Listener dispatch functions
+//-----------------------------------------------------------------------------
+
+
+LLControlVariable *LLView::findControl(const std::string& name)
+{
+ // parse the name to locate which group it belongs to
+ std::size_t key_pos= name.find(".");
+ if(key_pos!= std::string::npos )
+ {
+ std::string control_group_key = name.substr(0, key_pos);
+ LLControlVariable* control;
+ // check if it's in the control group that name indicated
+ if(LLUI::getInstance()->mSettingGroups[control_group_key])
+ {
+ control = LLUI::getInstance()->mSettingGroups[control_group_key]->getControl(name);
+ if (control)
+ {
+ return control;
+ }
+ }
+ }
+
+ LLControlGroup& control_group = LLUI::getInstance()->getControlControlGroup(name);
+ return control_group.getControl(name);
+}
+
+void LLView::initFromParams(const LLView::Params& params)
+{
+ LLRect required_rect = getRequiredRect();
+
+ S32 width = llmax(getRect().getWidth(), required_rect.getWidth());
+ S32 height = llmax(getRect().getHeight(), required_rect.getHeight());
+
+ reshape(width, height);
+
+ // call virtual methods with most recent data
+ // use getters because these values might not come through parameter block
+ setEnabled(getEnabled());
+ setVisible(getVisible());
+
+ if (!params.name().empty())
+ {
+ setName(params.name());
+ }
+
+ mLayout = params.layout();
+}
+
+void LLView::parseFollowsFlags(const LLView::Params& params)
+{
+ // preserve follows flags set by code if user did not override
+ if (!params.follows.isProvided())
+ {
+ return;
+ }
+
+ // interpret either string or bitfield version of follows
+ if (params.follows.string.isChosen())
+ {
+ setFollows(FOLLOWS_NONE);
+
+ std::string follows = params.follows.string;
+
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("|");
+ tokenizer tokens(follows, sep);
+ tokenizer::iterator token_iter = tokens.begin();
+
+ while(token_iter != tokens.end())
+ {
+ const std::string& token_str = *token_iter;
+ if (token_str == "left")
+ {
+ setFollowsLeft();
+ }
+ else if (token_str == "right")
+ {
+ setFollowsRight();
+ }
+ else if (token_str == "top")
+ {
+ setFollowsTop();
+ }
+ else if (token_str == "bottom")
+ {
+ setFollowsBottom();
+ }
+ else if (token_str == "all")
+ {
+ setFollowsAll();
+ }
+ ++token_iter;
+ }
+ }
+ else if (params.follows.flags.isChosen())
+ {
+ setFollows(params.follows.flags);
+ }
+}
+
+
+// static
+//LLFontGL::HAlign LLView::selectFontHAlign(LLXMLNodePtr node)
+//{
+// LLFontGL::HAlign gl_hfont_align = LLFontGL::LEFT;
+//
+// if (node->hasAttribute("halign"))
+// {
+// std::string horizontal_align_name;
+// node->getAttributeString("halign", horizontal_align_name);
+// gl_hfont_align = LLFontGL::hAlignFromName(horizontal_align_name);
+// }
+// return gl_hfont_align;
+//}
+
+// Return the rectangle of the last-constructed child,
+// if present and a first-class widget (eg, not a close box or drag handle)
+// Returns true if found
+static bool get_last_child_rect(LLView* parent, LLRect *rect)
+{
+ if (!parent) return false;
+
+ LLView::child_list_t::const_iterator itor =
+ parent->getChildList()->begin();
+ for (;itor != parent->getChildList()->end(); ++itor)
+ {
+ LLView *last_view = (*itor);
+ if (last_view->getFromXUI())
+ {
+ *rect = last_view->getRect();
+ return true;
+ }
+ }
+ return false;
+}
+
+//static
+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())
+ {
+ p.layout = parent->getLayout();
+ }
+
+ if (layout_rect.isEmpty())
+ {
+ layout_rect = parent->getLocalRect();
+ }
+
+ // overwrite uninitialized rect params, using context
+ LLRect default_rect = parent->getLocalRect();
+
+ bool layout_topleft = (p.layout() == "topleft");
+
+ // convert negative or centered coordinates to parent relative values
+ // Note: some of this logic matches the logic in TypedParam<LLRect>::setValueFromBlock()
+ if (p.rect.left.isProvided())
+ {
+ p.rect.left = 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
+ p.rect.bottom = layout_rect.mBottom + layout_rect.mTop - p.rect.bottom;
+ }
+ }
+ if (p.rect.top.isProvided())
+ {
+ p.rect.top = p.rect.top + ((p.rect.top >= 0) ? layout_rect.mBottom : layout_rect.mTop);
+ if (layout_topleft)
+ {
+ //invert top to bottom
+ p.rect.top = layout_rect.mBottom + layout_rect.mTop - p.rect.top;
+ }
+ }
+
+ // 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;
+ }
+
+ default_rect.translate(0, default_rect.getHeight());
+
+ // If there was a recently constructed child, use its rectangle
+ get_last_child_rect(parent, &default_rect);
+
+ 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
+ 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);
+ }
+
+ 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);
+ }
+ 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);
+
+ // 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);
+
+ }
+ 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);
+ }
+}
+
+static S32 invert_vertical(S32 y, LLView* parent)
+{
+ if (y < 0)
+ {
+ // already based on top-left, just invert
+ return -y;
+ }
+ else if (parent)
+ {
+ // use parent to flip coordinate
+ S32 parent_height = parent->getRect().getHeight();
+ return parent_height - y;
+ }
+ else
+ {
+ LL_WARNS() << "Attempting to convert layout to top-left with no parent" << LL_ENDL;
+ return y;
+ }
+}
+
+// Assumes that input is in bottom-left coordinates, hence must call
+// _before_ convert_coords_to_top_left().
+static void convert_to_relative_layout(LLView::Params& p, LLView* parent)
+{
+ // Use setupParams to get the final widget rectangle
+ // according to our wacky layout rules.
+ LLView::Params final = p;
+ LLView::applyXUILayout(final, parent);
+ // Must actually extract the rectangle to get consistent
+ // right = left+width, top = bottom+height
+ LLRect final_rect = final.rect;
+
+ // We prefer to write out top edge instead of bottom, regardless
+ // of whether we use relative positioning
+ bool converted_top = false;
+
+ // Look for a last rectangle
+ LLRect last_rect;
+ if (get_last_child_rect(parent, &last_rect))
+ {
+ // ...we have a previous widget to compare to
+ const S32 EDGE_THRESHOLD_PIXELS = 4;
+ S32 left_pad = final_rect.mLeft - last_rect.mRight;
+ S32 left_delta = final_rect.mLeft - last_rect.mLeft;
+ S32 top_pad = final_rect.mTop - last_rect.mBottom;
+ S32 top_delta = final_rect.mTop - last_rect.mTop;
+ // If my left edge is almost the same, or my top edge is
+ // almost the same...
+ if (llabs(left_delta) <= EDGE_THRESHOLD_PIXELS
+ || llabs(top_delta) <= EDGE_THRESHOLD_PIXELS)
+ {
+ // ...use relative positioning
+ // prefer top_pad if widgets are stacking vertically
+ // (coordinate system is still bottom-left here)
+ if (top_pad < 0)
+ {
+ p.top_pad = top_pad;
+ p.top_delta.setProvided(false);
+ }
+ else
+ {
+ p.top_pad.setProvided(false);
+ p.top_delta = top_delta;
+ }
+ // null out other vertical specifiers
+ p.rect.top.setProvided(false);
+ p.rect.bottom.setProvided(false);
+ p.bottom_delta.setProvided(false);
+ converted_top = true;
+
+ // prefer left_pad if widgets are stacking horizontally
+ if (left_pad > 0)
+ {
+ p.left_pad = left_pad;
+ p.left_delta.setProvided(false);
+ }
+ else
+ {
+ p.left_pad.setProvided(false);
+ p.left_delta = left_delta;
+ }
+ p.rect.left.setProvided(false);
+ p.rect.right.setProvided(false);
+ }
+ }
+
+ if (!converted_top)
+ {
+ // ...this is the first widget, or one that wasn't aligned
+ // prefer top/left specification
+ p.rect.top = final_rect.mTop;
+ p.rect.bottom.setProvided(false);
+ p.bottom_delta.setProvided(false);
+ p.top_pad.setProvided(false);
+ p.top_delta.setProvided(false);
+ }
+}
+
+static void convert_coords_to_top_left(LLView::Params& p, LLView* parent)
+{
+ // Convert the coordinate system to be top-left based.
+ if (p.rect.top.isProvided())
+ {
+ p.rect.top = invert_vertical(p.rect.top, parent);
+ }
+ if (p.rect.bottom.isProvided())
+ {
+ p.rect.bottom = invert_vertical(p.rect.bottom, parent);
+ }
+ if (p.top_pad.isProvided())
+ {
+ p.top_pad = -p.top_pad;
+ }
+ if (p.top_delta.isProvided())
+ {
+ p.top_delta = -p.top_delta;
+ }
+ if (p.bottom_delta.isProvided())
+ {
+ p.bottom_delta = -p.bottom_delta;
+ }
+ p.layout = "topleft";
+}
+
+//static
+void LLView::setupParamsForExport(Params& p, LLView* parent)
+{
+ // Don't convert if already top-left based
+ if (p.layout() == "topleft")
+ {
+ return;
+ }
+
+ // heuristic: Many of our floaters and panels were bulk-exported.
+ // These specify exactly bottom/left and height/width.
+ // Others were done by hand using bottom_delta and/or left_delta.
+ // Some rely on not specifying left to mean align with left edge.
+ // Try to convert both to use relative layout, but using top-left
+ // coordinates.
+ // Avoid rectangles where top/bottom/left/right was specified.
+ if (p.rect.height.isProvided() && p.rect.width.isProvided())
+ {
+ if (p.rect.bottom.isProvided() && p.rect.left.isProvided())
+ {
+ // standard bulk export, convert it
+ convert_to_relative_layout(p, parent);
+ }
+ else if (p.rect.bottom.isProvided() && p.left_delta.isProvided())
+ {
+ // hand layout with left_delta
+ convert_to_relative_layout(p, parent);
+ }
+ else if (p.bottom_delta.isProvided())
+ {
+ // hand layout with bottom_delta
+ // don't check for p.rect.left or p.left_delta because sometimes
+ // this layout doesn't set it for widgets that are left-aligned
+ convert_to_relative_layout(p, parent);
+ }
+ }
+
+ convert_coords_to_top_left(p, parent);
+}
+
+LLView::tree_iterator_t LLView::beginTreeDFS()
+{
+ return tree_iterator_t(this,
+ boost::bind(boost::mem_fn(&LLView::beginChild), _1),
+ boost::bind(boost::mem_fn(&LLView::endChild), _1));
+}
+
+LLView::tree_iterator_t LLView::endTreeDFS()
+{
+ // an empty iterator is an "end" iterator
+ return tree_iterator_t();
+}
+
+LLView::tree_post_iterator_t LLView::beginTreeDFSPost()
+{
+ return tree_post_iterator_t(this,
+ boost::bind(boost::mem_fn(&LLView::beginChild), _1),
+ boost::bind(boost::mem_fn(&LLView::endChild), _1));
+}
+
+LLView::tree_post_iterator_t LLView::endTreeDFSPost()
+{
+ // an empty iterator is an "end" iterator
+ return tree_post_iterator_t();
+}
+
+LLView::bfs_tree_iterator_t LLView::beginTreeBFS()
+{
+ return bfs_tree_iterator_t(this,
+ boost::bind(boost::mem_fn(&LLView::beginChild), _1),
+ boost::bind(boost::mem_fn(&LLView::endChild), _1));
+}
+
+LLView::bfs_tree_iterator_t LLView::endTreeBFS()
+{
+ // an empty iterator is an "end" iterator
+ return bfs_tree_iterator_t();
+}
+
+
+LLView::root_to_view_iterator_t LLView::beginRootToView()
+{
+ return root_to_view_iterator_t(this, boost::bind(&LLView::getParent, _1));
+}
+
+LLView::root_to_view_iterator_t LLView::endRootToView()
+{
+ return root_to_view_iterator_t();
+}
+
+
+// only create maps on demand, as they incur heap allocation/deallocation cost
+// when a view is constructed/deconstructed
+LLView& LLView::getDefaultWidgetContainer() const
+{
+ if (!mDefaultWidgets)
+ {
+ LLView::Params p;
+ p.name = "default widget container";
+ p.visible = false; // ensures default widgets can't steal focus, etc.
+ mDefaultWidgets = new LLView(p);
+ }
+ return *mDefaultWidgets;
+}
+
+S32 LLView::notifyParent(const LLSD& info)
+{
+ LLView* parent = getParent();
+ if(parent)
+ return parent->notifyParent(info);
+ return 0;
+}
+bool LLView::notifyChildren(const LLSD& info)
+{
+ bool ret = false;
+ for (LLView* childp : mChildList)
+ {
+ ret = ret || childp->notifyChildren(info);
+ }
+ return ret;
+}
+
+// convenient accessor for draw context
+const LLViewDrawContext& LLView::getDrawContext()
+{
+ return LLViewDrawContext::getCurrentContext();
+}
+
+const LLViewDrawContext& LLViewDrawContext::getCurrentContext()
+{
+ static LLViewDrawContext default_context;
+
+ if (sDrawContextStack.empty())
+ return default_context;
+
+ return *sDrawContextStack.back();
+}
+
+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 dcc77a79d1..ddb0a3dbbf 100644
--- a/indra/llui/llview.h
+++ b/indra/llui/llview.h
@@ -1,735 +1,737 @@
-/**
- * @file llview.h
- * @brief Container for other views, anything that draws.
- *
- * $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_LLVIEW_H
-#define LL_LLVIEW_H
-
-// A view is an area in a window that can draw. It might represent
-// the HUD or a dialog box or a button. It can also contain sub-views
-// and child widgets
-
-#include "stdtypes.h"
-#include "llcoord.h"
-#include "llfontgl.h"
-#include "llhandle.h"
-#include "llmortician.h"
-#include "llmousehandler.h"
-#include "llstring.h"
-#include "llrect.h"
-#include "llui.h"
-#include "lluistring.h"
-#include "llviewquery.h"
-#include "lluistring.h"
-#include "llcursortypes.h"
-#include "lluictrlfactory.h"
-#include "lltreeiterators.h"
-#include "llfocusmgr.h"
-
-#include <list>
-#include <boost/function.hpp>
-#include <boost/noncopyable.hpp>
-
-class LLSD;
-
-const U32 FOLLOWS_NONE = 0x00;
-const U32 FOLLOWS_LEFT = 0x01;
-const U32 FOLLOWS_RIGHT = 0x02;
-const U32 FOLLOWS_TOP = 0x10;
-const U32 FOLLOWS_BOTTOM = 0x20;
-const U32 FOLLOWS_ALL = 0x33;
-
-const bool MOUSE_OPAQUE = true;
-const bool NOT_MOUSE_OPAQUE = false;
-
-const U32 GL_NAME_UI_RESERVED = 2;
-
-
-// maintains render state during traversal of UI tree
-class LLViewDrawContext
-{
-public:
- F32 mAlpha;
-
- LLViewDrawContext(F32 alpha = 1.f)
- : mAlpha(alpha)
- {
- if (!sDrawContextStack.empty())
- {
- LLViewDrawContext* context_top = sDrawContextStack.back();
- // merge with top of stack
- mAlpha *= context_top->mAlpha;
- }
- sDrawContextStack.push_back(this);
- }
-
- ~LLViewDrawContext()
- {
- sDrawContextStack.pop_back();
- }
-
- static const LLViewDrawContext& getCurrentContext();
-
-private:
- static std::vector<LLViewDrawContext*> sDrawContextStack;
-};
-
-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:
-
- enum EOrientation { HORIZONTAL, VERTICAL, ORIENTATION_COUNT };
-
- struct Follows : public LLInitParam::ChoiceBlock<Follows>
- {
- Alternative<std::string> string;
- Alternative<U32> flags;
-
- Follows();
- };
-
- struct Params : public LLInitParam::Block<Params>
- {
- Mandatory<std::string> name;
-
- Optional<bool> enabled,
- visible,
- mouse_opaque,
- use_bounding_rect,
- from_xui,
- focus_root;
-
- Optional<S32> tab_group,
- default_tab_group;
- Optional<std::string> tool_tip;
-
- Optional<S32> sound_flags;
- Optional<Follows> follows;
- Optional<std::string> hover_cursor;
-
- Optional<std::string> layout;
- Optional<LLRect> rect;
-
- // Historical bottom-left layout used bottom_delta and left_delta
- // for relative positioning. New layout "topleft" prefers specifying
- // based on top edge.
- Optional<S32> bottom_delta, // from last bottom to my bottom
- top_pad, // from last bottom to my top
- top_delta, // from last top to my top
- left_pad, // from last right to my left
- left_delta; // from last left to my left
-
- //FIXME: get parent context involved in parsing traversal
- Ignored needs_translate, // cue for translation tools
- xmlns, // xml namespace
- xmlns_xsi, // xml namespace
- xsi_schemaLocation, // xml schema
- xsi_type; // xml schema type
-
- Params();
- };
-
- // most widgets are valid children of LLView
- typedef LLDefaultChildRegistry child_registry_t;
-
- void initFromParams(const LLView::Params&);
-
-protected:
- LLView(const LLView::Params&);
- friend class LLUICtrlFactory;
-
-private:
- // widgets in general are not copyable
- LLView(const LLView& other);
-public:
-//#if LL_DEBUG
- static bool sIsDrawing;
-//#endif
- enum ESoundFlags
- {
- SILENT = 0,
- MOUSE_DOWN = 1,
- MOUSE_UP = 2
- };
-
- enum ESnapType
- {
- SNAP_PARENT,
- SNAP_SIBLINGS,
- SNAP_PARENT_AND_SIBLINGS
- };
-
- enum ESnapEdge
- {
- SNAP_LEFT,
- SNAP_TOP,
- SNAP_RIGHT,
- SNAP_BOTTOM
- };
-
- typedef std::list<LLView*> child_list_t;
- typedef child_list_t::iterator child_list_iter_t;
- typedef child_list_t::const_iterator child_list_const_iter_t;
- 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::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 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;
- typedef child_tab_order_t::const_reverse_iterator child_tab_order_const_reverse_iter_t;
-
- virtual ~LLView();
-
- // Some UI widgets need to be added as controls. Others need to
- // be added as regular view children. isCtrl should return true
- // if a widget needs to be added as a ctrl
- virtual bool isCtrl() const;
-
- virtual bool isPanel() const;
-
- //
- // MANIPULATORS
- //
- void setMouseOpaque( bool b ) { mMouseOpaque = b; }
- bool getMouseOpaque() const { return mMouseOpaque; }
- void setToolTip( const LLStringExplicit& msg );
- bool setToolTipArg( const LLStringExplicit& key, const LLStringExplicit& text );
- void setToolTipArgs( const LLStringUtil::format_map_t& args );
-
- virtual void setRect(const LLRect &rect);
- void setFollows(U32 flags) { mReshapeFlags = flags; }
-
- // deprecated, use setFollows() with FOLLOWS_LEFT | FOLLOWS_TOP, etc.
- void setFollowsNone() { mReshapeFlags = FOLLOWS_NONE; }
- void setFollowsLeft() { mReshapeFlags |= FOLLOWS_LEFT; }
- void setFollowsTop() { mReshapeFlags |= FOLLOWS_TOP; }
- void setFollowsRight() { mReshapeFlags |= FOLLOWS_RIGHT; }
- void setFollowsBottom() { mReshapeFlags |= FOLLOWS_BOTTOM; }
- void setFollowsAll() { mReshapeFlags |= FOLLOWS_ALL; }
-
- void setSoundFlags(U8 flags) { mSoundFlags = flags; }
- void setName(std::string name) { mName = name; }
- void setUseBoundingRect( bool use_bounding_rect );
- bool getUseBoundingRect() const;
-
- ECursorType getHoverCursor() { return mHoverCursor; }
-
- static F32 getTooltipTimeout();
- virtual const std::string getToolTip() const { return mToolTipMsg.getString(); }
-
- void sendChildToFront(LLView* child);
- void sendChildToBack(LLView* child);
-
- virtual bool addChild(LLView* view, S32 tab_group = 0);
-
- // implemented in terms of addChild()
- bool addChildInBack(LLView* view, S32 tab_group = 0);
-
- // remove the specified child from the view, and set it's parent to NULL.
- virtual void removeChild(LLView* view);
-
- virtual bool postBuild() { return true; }
-
- const child_tab_order_t& getTabOrder() const { return mTabOrder; }
-
- void setDefaultTabGroup(S32 d) { mDefaultTabGroup = d; }
- S32 getDefaultTabGroup() const { return mDefaultTabGroup; }
- S32 getLastTabGroup() { return mLastTabGroup; }
-
- bool isInVisibleChain() const;
- bool isInEnabledChain() const;
-
- void setFocusRoot(bool b) { mIsFocusRoot = b; }
- bool isFocusRoot() const { return mIsFocusRoot; }
- virtual bool canFocusChildren() const;
-
- bool focusNextRoot();
- bool focusPrevRoot();
-
- // Normally we want the app menus to get priority on accelerated keys
- // However, sometimes we want to give specific views a first chance
- // iat handling them. (eg. the script editor)
- virtual bool hasAccelerators() const { return false; };
-
- // delete all children. Override this function if you need to
- // perform any extra clean up such as cached pointers to selected
- // children, etc.
- virtual void deleteAllChildren();
-
- void setAllChildrenEnabled(bool b);
-
- virtual void setVisible(bool visible);
- void setVisibleDirect(bool visible) { mVisible = visible; }
- 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
- /// words, can a user actually interact with this?
- virtual bool isAvailable() const;
- /// The static isAvailable() tests an LLView* that could be NULL.
- static bool isAvailable(const LLView* view);
- U8 getSoundFlags() const { return mSoundFlags; }
-
- virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text );
-
- virtual void onVisibilityChange ( bool new_visibility );
- virtual void onUpdateScrollToChild(const LLUICtrl * cntrl);
-
- void pushVisible(bool visible) { mLastVisible = mVisible; setVisible(visible); }
- void popVisible() { setVisible(mLastVisible); }
- bool getLastVisible() const { return mLastVisible; }
-
- U32 getFollows() const { return mReshapeFlags; }
- bool followsLeft() const { return mReshapeFlags & FOLLOWS_LEFT; }
- bool followsRight() const { return mReshapeFlags & FOLLOWS_RIGHT; }
- bool followsTop() const { return mReshapeFlags & FOLLOWS_TOP; }
- bool followsBottom() const { return mReshapeFlags & FOLLOWS_BOTTOM; }
- bool followsAll() const { return mReshapeFlags & FOLLOWS_ALL; }
-
- const LLRect& getRect() const { return mRect; }
- const LLRect& getBoundingRect() const { return mBoundingRect; }
- LLRect getLocalBoundingRect() const;
- LLRect calcScreenRect() const;
- LLRect calcScreenBoundingRect() const;
- LLRect getLocalRect() const;
- virtual LLRect getSnapRect() const;
- LLRect getLocalSnapRect() const;
-
- std::string getLayout() { return mLayout; }
-
- // Override and return required size for this object. 0 for width/height means don't care.
- virtual LLRect getRequiredRect();
- LLRect calcBoundingRect();
- void updateBoundingRect();
-
- LLView* getRootView();
- LLView* getParent() const { return mParentView; }
- LLView* getFirstChild() const { return (mChildList.empty()) ? NULL : *(mChildList.begin()); }
- LLView* findPrevSibling(LLView* child);
- LLView* findNextSibling(LLView* child);
- S32 getChildCount() const { return (S32)mChildList.size(); }
- template<class _Pr3> void sortChildren(_Pr3 _Pred) { mChildList.sort(_Pred); }
- bool hasAncestor(const LLView* parentp) const;
- bool hasChild(const std::string& childname, bool recurse = false) const;
- bool childHasKeyboardFocus( const std::string& childname ) const;
-
- // these iterators are used for collapsing various tree traversals into for loops
- typedef LLTreeDFSIter<LLView, child_list_const_iter_t> tree_iterator_t;
- tree_iterator_t beginTreeDFS();
- tree_iterator_t endTreeDFS();
-
- typedef LLTreeDFSPostIter<LLView, child_list_const_iter_t> tree_post_iterator_t;
- tree_post_iterator_t beginTreeDFSPost();
- tree_post_iterator_t endTreeDFSPost();
-
- typedef LLTreeBFSIter<LLView, child_list_const_iter_t> bfs_tree_iterator_t;
- bfs_tree_iterator_t beginTreeBFS();
- bfs_tree_iterator_t endTreeBFS();
-
-
- typedef LLTreeDownIter<LLView> root_to_view_iterator_t;
- root_to_view_iterator_t beginRootToView();
- root_to_view_iterator_t endRootToView();
-
- //
- // UTILITIES
- //
-
- // Default behavior is to use reshape flags to resize child views
- 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, S32 min_overlap_pixels = S32_MAX);
- bool translateRectIntoRect( const LLRect& rect, 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);
- virtual LLView* findSnapRect(LLRect& new_rect, const LLCoordGL& mouse_dir, LLView::ESnapType snap_type, S32 threshold, S32 padding = 0);
- virtual LLView* findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding = 0);
- virtual bool canSnapTo(const LLView* other_view);
- virtual void setSnappedTo(const LLView* snap_view);
-
- // inherited from LLFocusableElement
- /* virtual */ bool handleKey(KEY key, MASK mask, bool called_from_parent);
- /* virtual */ bool handleKeyUp(KEY key, MASK mask, bool called_from_parent);
- /* virtual */ bool handleUnicodeChar(llwchar uni_char, bool called_from_parent);
-
- virtual bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
- EDragAndDropType cargo_type,
- void* cargo_data,
- EAcceptance* accept,
- std::string& tooltip_msg);
-
- virtual void draw();
-
- void parseFollowsFlags(const LLView::Params& params);
-
- // Some widgets, like close box buttons, don't need to be saved
- bool getFromXUI() const { return mFromXUI; }
- void setFromXUI(bool b) { mFromXUI = b; }
-
- typedef enum e_hit_test_type
- {
- HIT_TEST_USE_BOUNDING_RECT,
- HIT_TEST_IGNORE_BOUNDING_RECT
- }EHitTestType;
-
- bool parentPointInView(S32 x, S32 y, EHitTestType type = HIT_TEST_USE_BOUNDING_RECT) const;
- bool pointInView(S32 x, S32 y, EHitTestType type = HIT_TEST_USE_BOUNDING_RECT) const;
- 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, 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;
-
- LLControlVariable *findControl(const std::string& name);
-
- const child_list_t* getChildList() const { return &mChildList; }
- child_list_const_iter_t beginChild() const { return mChildList.begin(); }
- child_list_const_iter_t endChild() const { return mChildList.end(); }
-
- // LLMouseHandler functions
- // Default behavior is to pass events to children
- /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleMiddleMouseUp(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleMiddleMouseDown(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks);
- /*virtual*/ bool handleScrollHWheel(S32 x, S32 y, S32 clicks);
- /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleRightMouseUp(S32 x, S32 y, MASK mask);
- /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask);
-
- /*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, 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
- {
- LLView* child = findChildView(name, recurse);
- T* result = dynamic_cast<T*>(child);
- return result;
- }
-
- template <class T> T* getChild(const std::string& name, bool recurse = true) const;
-
- template <class T> T& getChildRef(const std::string& name, bool recurse = true) const
- {
- return *getChild<T>(name, recurse);
- }
-
- virtual LLView* getChildView(const std::string& name, bool recurse = true) const;
- virtual LLView* findChildView(const std::string& name, bool recurse = true) const;
-
- template <class T> T* getDefaultWidget(const std::string& name) const
- {
- LLView* widgetp = getDefaultWidgetContainer().findChildView(name);
- 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
- //////////////////////////////////////////////
- //static LLFontGL::HAlign selectFontHAlign(LLXMLNodePtr node);
-
- // focuses the item in the list after the currently-focused item, wrapping if necessary
- static bool focusNext(LLView::child_list_t & result);
- // focuses the item in the list before the currently-focused item, wrapping if necessary
- static bool focusPrev(LLView::child_list_t & result);
-
- // returns query for iterating over controls in tab order
- static const LLViewQuery & getTabOrderQuery();
- // return query for iterating over focus roots in tab order
- static const LLViewQuery & getFocusRootsQuery();
-
- static LLWindow* getWindow(void) { return LLUI::getInstance()->mWindow; }
-
- // Set up params after XML load before calling new(),
- // usually to adjust layout.
- 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.
- static void setupParamsForExport(Params& p, LLView* parent);
-
- //virtual bool addChildFromParam(const LLInitParam::BaseBlock& params) { return true; }
- virtual bool handleKeyHere(KEY key, MASK mask);
- virtual bool handleKeyUpHere(KEY key, MASK mask);
- virtual bool handleUnicodeCharHere(llwchar uni_char);
-
- virtual void handleReshape(const LLRect& rect, bool by_user);
- virtual void dirtyRect();
-
- //send custom notification to LLView parent
- virtual S32 notifyParent(const LLSD& info);
-
- //send custom notification to all view childrend
- // return true if _any_ children return true. otherwise false.
- virtual bool notifyChildren(const LLSD& info);
-
- //send custom notification to current view
- virtual S32 notify(const LLSD& info) { return 0;};
-
- static const LLViewDrawContext& getDrawContext();
-
- // 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* childrenHandleKeyUp(KEY key, MASK mask);
- LLView* childrenHandleUnicodeChar(llwchar uni_char);
- LLView* childrenHandleDragAndDrop(S32 x, S32 y, MASK mask,
- bool drop,
- EDragAndDropType type,
- void* data,
- EAcceptance* accept,
- std::string& tooltip_msg);
-
- LLView* childrenHandleHover(S32 x, S32 y, MASK mask);
- LLView* childrenHandleMouseUp(S32 x, S32 y, MASK mask);
- LLView* childrenHandleMouseDown(S32 x, S32 y, MASK mask);
- LLView* childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask);
- LLView* childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask);
- LLView* childrenHandleDoubleClick(S32 x, S32 y, MASK mask);
- LLView* childrenHandleScrollWheel(S32 x, S32 y, S32 clicks);
- LLView* childrenHandleScrollHWheel(S32 x, S32 y, S32 clicks);
- LLView* childrenHandleRightMouseDown(S32 x, S32 y, MASK mask);
- LLView* childrenHandleRightMouseUp(S32 x, S32 y, MASK mask);
- LLView* childrenHandleToolTip(S32 x, S32 y, MASK mask);
-
- ECursorType mHoverCursor;
-
- 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;
-
- // 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 mTabOrder;
- S32 mDefaultTabGroup;
- S32 mLastTabGroup;
-
- bool mEnabled; // Enabled means "accepts input that has an effect on the state of the application."
- // A disabled view, for example, may still have a scrollbar that responds to mouse events.
- bool mMouseOpaque; // Opaque views handle all mouse events that are over their rect.
- LLUIString mToolTipMsg; // isNull() is true if none.
-
- U8 mSoundFlags;
- bool mFromXUI;
-
- bool mIsFocusRoot;
- bool mUseBoundingRect; // hit test against bounding rectangle that includes all child elements
-
- bool mLastVisible;
-
- bool mInDraw;
-
- static LLWindow* sWindow; // All root views must know about their window.
-
- typedef std::map<std::string, LLView*> default_widget_map_t;
- // allocate this map no demand, as it is rarely needed
- mutable LLView* mDefaultWidgets;
-
- 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;
-
- // Draw debug rectangles around widgets to help with alignment and spacing
- static bool sDebugRects;
-
- // Show hexadecimal byte values of unicode symbols in a tooltip
- static bool sDebugUnicode;
-
- // Show camera position and direction in Camera Controls floater
- static bool sDebugCamera;
-
- static bool sIsRectDirty;
- static LLRect sDirtyRect;
-
- // Draw widget names and sizes when drawing debug rectangles, turning this
- // off is useful to make the rectangles themselves easier to see.
- static bool sDebugRectsShowNames;
-
- static bool sDebugKeys;
- static bool sDebugMouseHandling;
- static std::string sMouseHandlerMessage;
- static S32 sSelectID;
- static std::set<LLView*> sPreviewHighlightedElements; // DEV-16869
- static bool sHighlightingDiffs; // DEV-16869
- static LLView* sPreviewClickedElement; // DEV-16869
- static bool sDrawPreviewHighlights;
- static S32 sLastLeftXML;
- static S32 sLastBottomXML;
- static bool sForceReshape;
-};
-
-namespace LLInitParam
-{
-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
-{
- LLView* child = findChildView(name, recurse);
- T* result = dynamic_cast<T*>(child);
- if (!result)
- {
- // did we find *something* with that name?
- if (child)
- {
- 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)
- {
- result = LLUICtrlFactory::getDefaultWidget<T>(name);
- if (!result)
- {
- LL_ERRS() << "Failed to create dummy " << typeid(T).name() << LL_ENDL;
- }
-
- // *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().
- LL_WARNS() << "Making dummy " << typeid(T).name() << " named \"" << name << "\" in " << getName() << LL_ENDL;
-
- getDefaultWidgetContainer().addChild(result);
- }
- }
- return result;
-}
-
-// Compiler optimization - don't generate these specializations inline,
-// require explicit specialization. See llbutton.cpp for an example.
-#ifndef LLVIEW_CPP
-extern template class LLView* LLView::getChild<class LLView>(
- const std::string& name, bool recurse) const;
-#endif
-
-#endif //LL_LLVIEW_H
+/**
+ * @file llview.h
+ * @brief Container for other views, anything that draws.
+ *
+ * $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_LLVIEW_H
+#define LL_LLVIEW_H
+
+// A view is an area in a window that can draw. It might represent
+// the HUD or a dialog box or a button. It can also contain sub-views
+// and child widgets
+
+#include "stdtypes.h"
+#include "llcoord.h"
+#include "llfontgl.h"
+#include "llhandle.h"
+#include "llmortician.h"
+#include "llmousehandler.h"
+#include "llstring.h"
+#include "llrect.h"
+#include "llui.h"
+#include "lluistring.h"
+#include "llviewquery.h"
+#include "lluistring.h"
+#include "llcursortypes.h"
+#include "lluictrlfactory.h"
+#include "lltreeiterators.h"
+#include "llfocusmgr.h"
+
+#include <list>
+#include <boost/function.hpp>
+#include <boost/noncopyable.hpp>
+
+class LLSD;
+
+const U32 FOLLOWS_NONE = 0x00;
+const U32 FOLLOWS_LEFT = 0x01;
+const U32 FOLLOWS_RIGHT = 0x02;
+const U32 FOLLOWS_TOP = 0x10;
+const U32 FOLLOWS_BOTTOM = 0x20;
+const U32 FOLLOWS_ALL = 0x33;
+
+const bool MOUSE_OPAQUE = true;
+const bool NOT_MOUSE_OPAQUE = false;
+
+const U32 GL_NAME_UI_RESERVED = 2;
+
+
+// maintains render state during traversal of UI tree
+class LLViewDrawContext
+{
+public:
+ F32 mAlpha;
+
+ LLViewDrawContext(F32 alpha = 1.f)
+ : mAlpha(alpha)
+ {
+ if (!sDrawContextStack.empty())
+ {
+ LLViewDrawContext* context_top = sDrawContextStack.back();
+ // merge with top of stack
+ mAlpha *= context_top->mAlpha;
+ }
+ sDrawContextStack.push_back(this);
+ }
+
+ ~LLViewDrawContext()
+ {
+ sDrawContextStack.pop_back();
+ }
+
+ static const LLViewDrawContext& getCurrentContext();
+
+private:
+ static std::vector<LLViewDrawContext*> sDrawContextStack;
+};
+
+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:
+
+ enum EOrientation { HORIZONTAL, VERTICAL, ORIENTATION_COUNT };
+
+ struct Follows : public LLInitParam::ChoiceBlock<Follows>
+ {
+ Alternative<std::string> string;
+ Alternative<U32> flags;
+
+ Follows();
+ };
+
+ struct Params : public LLInitParam::Block<Params>
+ {
+ Mandatory<std::string> name;
+
+ Optional<bool> enabled,
+ visible,
+ mouse_opaque,
+ use_bounding_rect,
+ from_xui,
+ focus_root;
+
+ Optional<S32> tab_group,
+ default_tab_group;
+ Optional<std::string> tool_tip;
+
+ Optional<S32> sound_flags;
+ Optional<Follows> follows;
+ Optional<std::string> hover_cursor;
+
+ Optional<std::string> layout;
+ Optional<LLRect> rect;
+
+ // Historical bottom-left layout used bottom_delta and left_delta
+ // for relative positioning. New layout "topleft" prefers specifying
+ // based on top edge.
+ Optional<S32> bottom_delta, // from last bottom to my bottom
+ top_pad, // from last bottom to my top
+ top_delta, // from last top to my top
+ left_pad, // from last right to my left
+ left_delta; // from last left to my left
+
+ //FIXME: get parent context involved in parsing traversal
+ Ignored needs_translate, // cue for translation tools
+ xmlns, // xml namespace
+ xmlns_xsi, // xml namespace
+ xsi_schemaLocation, // xml schema
+ xsi_type; // xml schema type
+
+ Params();
+ };
+
+ // most widgets are valid children of LLView
+ typedef LLDefaultChildRegistry child_registry_t;
+
+ void initFromParams(const LLView::Params&);
+
+protected:
+ LLView(const LLView::Params&);
+ friend class LLUICtrlFactory;
+
+private:
+ // widgets in general are not copyable
+ LLView(const LLView& other);
+public:
+//#if LL_DEBUG
+ static bool sIsDrawing;
+//#endif
+ enum ESoundFlags
+ {
+ SILENT = 0,
+ MOUSE_DOWN = 1,
+ MOUSE_UP = 2
+ };
+
+ enum ESnapType
+ {
+ SNAP_PARENT,
+ SNAP_SIBLINGS,
+ SNAP_PARENT_AND_SIBLINGS
+ };
+
+ enum ESnapEdge
+ {
+ SNAP_LEFT,
+ SNAP_TOP,
+ SNAP_RIGHT,
+ SNAP_BOTTOM
+ };
+
+ typedef std::list<LLView*> child_list_t;
+ typedef child_list_t::iterator child_list_iter_t;
+ typedef child_list_t::const_iterator child_list_const_iter_t;
+ 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::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 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;
+ typedef child_tab_order_t::const_reverse_iterator child_tab_order_const_reverse_iter_t;
+
+ virtual ~LLView();
+
+ // Some UI widgets need to be added as controls. Others need to
+ // be added as regular view children. isCtrl should return true
+ // if a widget needs to be added as a ctrl
+ virtual bool isCtrl() const;
+
+ virtual bool isPanel() const;
+
+ //
+ // MANIPULATORS
+ //
+ void setMouseOpaque( bool b ) { mMouseOpaque = b; }
+ bool getMouseOpaque() const { return mMouseOpaque; }
+ void setToolTip( const LLStringExplicit& msg );
+ bool setToolTipArg( const LLStringExplicit& key, const LLStringExplicit& text );
+ void setToolTipArgs( const LLStringUtil::format_map_t& args );
+
+ virtual void setRect(const LLRect &rect);
+ void setFollows(U32 flags) { mReshapeFlags = flags; }
+
+ // deprecated, use setFollows() with FOLLOWS_LEFT | FOLLOWS_TOP, etc.
+ void setFollowsNone() { mReshapeFlags = FOLLOWS_NONE; }
+ void setFollowsLeft() { mReshapeFlags |= FOLLOWS_LEFT; }
+ void setFollowsTop() { mReshapeFlags |= FOLLOWS_TOP; }
+ void setFollowsRight() { mReshapeFlags |= FOLLOWS_RIGHT; }
+ void setFollowsBottom() { mReshapeFlags |= FOLLOWS_BOTTOM; }
+ void setFollowsAll() { mReshapeFlags |= FOLLOWS_ALL; }
+
+ void setSoundFlags(U8 flags) { mSoundFlags = flags; }
+ void setName(std::string name) { mName = name; }
+ void setUseBoundingRect( bool use_bounding_rect );
+ bool getUseBoundingRect() const;
+
+ ECursorType getHoverCursor() { return mHoverCursor; }
+
+ static F32 getTooltipTimeout();
+ virtual const std::string getToolTip() const;
+ virtual const std::string& getText() const { return LLStringUtil::null; }
+ virtual const LLFontGL* getFont() const { return nullptr; }
+
+ void sendChildToFront(LLView* child);
+ void sendChildToBack(LLView* child);
+
+ virtual bool addChild(LLView* view, S32 tab_group = 0);
+
+ // implemented in terms of addChild()
+ bool addChildInBack(LLView* view, S32 tab_group = 0);
+
+ // remove the specified child from the view, and set it's parent to NULL.
+ virtual void removeChild(LLView* view);
+
+ virtual bool postBuild() { return true; }
+
+ const child_tab_order_t& getTabOrder() const { return mTabOrder; }
+
+ void setDefaultTabGroup(S32 d) { mDefaultTabGroup = d; }
+ S32 getDefaultTabGroup() const { return mDefaultTabGroup; }
+ S32 getLastTabGroup() { return mLastTabGroup; }
+
+ bool isInVisibleChain() const;
+ bool isInEnabledChain() const;
+
+ void setFocusRoot(bool b) { mIsFocusRoot = b; }
+ bool isFocusRoot() const { return mIsFocusRoot; }
+ virtual bool canFocusChildren() const;
+
+ bool focusNextRoot();
+ bool focusPrevRoot();
+
+ // Normally we want the app menus to get priority on accelerated keys
+ // However, sometimes we want to give specific views a first chance
+ // iat handling them. (eg. the script editor)
+ virtual bool hasAccelerators() const { return false; };
+
+ // delete all children. Override this function if you need to
+ // perform any extra clean up such as cached pointers to selected
+ // children, etc.
+ virtual void deleteAllChildren();
+
+ void setAllChildrenEnabled(bool b);
+
+ virtual void setVisible(bool visible);
+ void setVisibleDirect(bool visible) { mVisible = visible; }
+ 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
+ /// words, can a user actually interact with this?
+ virtual bool isAvailable() const;
+ /// The static isAvailable() tests an LLView* that could be NULL.
+ static bool isAvailable(const LLView* view);
+ U8 getSoundFlags() const { return mSoundFlags; }
+
+ virtual bool setLabelArg( const std::string& key, const LLStringExplicit& text );
+
+ virtual void onVisibilityChange ( bool new_visibility );
+ virtual void onUpdateScrollToChild(const LLUICtrl * cntrl);
+
+ void pushVisible(bool visible) { mLastVisible = mVisible; setVisible(visible); }
+ void popVisible() { setVisible(mLastVisible); }
+ bool getLastVisible() const { return mLastVisible; }
+
+ U32 getFollows() const { return mReshapeFlags; }
+ bool followsLeft() const { return mReshapeFlags & FOLLOWS_LEFT; }
+ bool followsRight() const { return mReshapeFlags & FOLLOWS_RIGHT; }
+ bool followsTop() const { return mReshapeFlags & FOLLOWS_TOP; }
+ bool followsBottom() const { return mReshapeFlags & FOLLOWS_BOTTOM; }
+ bool followsAll() const { return mReshapeFlags & FOLLOWS_ALL; }
+
+ const LLRect& getRect() const { return mRect; }
+ const LLRect& getBoundingRect() const { return mBoundingRect; }
+ LLRect getLocalBoundingRect() const;
+ LLRect calcScreenRect() const;
+ LLRect calcScreenBoundingRect() const;
+ LLRect getLocalRect() const;
+ virtual LLRect getSnapRect() const;
+ LLRect getLocalSnapRect() const;
+
+ std::string getLayout() { return mLayout; }
+
+ // Override and return required size for this object. 0 for width/height means don't care.
+ virtual LLRect getRequiredRect();
+ LLRect calcBoundingRect();
+ void updateBoundingRect();
+
+ LLView* getRootView();
+ LLView* getParent() const { return mParentView; }
+ LLView* getFirstChild() const { return (mChildList.empty()) ? NULL : *(mChildList.begin()); }
+ LLView* findPrevSibling(LLView* child);
+ LLView* findNextSibling(LLView* child);
+ S32 getChildCount() const { return (S32)mChildList.size(); }
+ template<class _Pr3> void sortChildren(_Pr3 _Pred) { mChildList.sort(_Pred); }
+ bool hasAncestor(const LLView* parentp) const;
+ bool hasChild(const std::string& childname, bool recurse = false) const;
+ bool childHasKeyboardFocus( const std::string& childname ) const;
+
+ // these iterators are used for collapsing various tree traversals into for loops
+ typedef LLTreeDFSIter<LLView, child_list_const_iter_t> tree_iterator_t;
+ tree_iterator_t beginTreeDFS();
+ tree_iterator_t endTreeDFS();
+
+ typedef LLTreeDFSPostIter<LLView, child_list_const_iter_t> tree_post_iterator_t;
+ tree_post_iterator_t beginTreeDFSPost();
+ tree_post_iterator_t endTreeDFSPost();
+
+ typedef LLTreeBFSIter<LLView, child_list_const_iter_t> bfs_tree_iterator_t;
+ bfs_tree_iterator_t beginTreeBFS();
+ bfs_tree_iterator_t endTreeBFS();
+
+
+ typedef LLTreeDownIter<LLView> root_to_view_iterator_t;
+ root_to_view_iterator_t beginRootToView();
+ root_to_view_iterator_t endRootToView();
+
+ //
+ // UTILITIES
+ //
+
+ // Default behavior is to use reshape flags to resize child views
+ 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, S32 min_overlap_pixels = S32_MAX);
+ bool translateRectIntoRect( const LLRect& rect, 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);
+ virtual LLView* findSnapRect(LLRect& new_rect, const LLCoordGL& mouse_dir, LLView::ESnapType snap_type, S32 threshold, S32 padding = 0);
+ virtual LLView* findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding = 0);
+ virtual bool canSnapTo(const LLView* other_view);
+ virtual void setSnappedTo(const LLView* snap_view);
+
+ // inherited from LLFocusableElement
+ /* virtual */ bool handleKey(KEY key, MASK mask, bool called_from_parent);
+ /* virtual */ bool handleKeyUp(KEY key, MASK mask, bool called_from_parent);
+ /* virtual */ bool handleUnicodeChar(llwchar uni_char, bool called_from_parent);
+
+ virtual bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg);
+
+ virtual void draw();
+
+ void parseFollowsFlags(const LLView::Params& params);
+
+ // Some widgets, like close box buttons, don't need to be saved
+ bool getFromXUI() const { return mFromXUI; }
+ void setFromXUI(bool b) { mFromXUI = b; }
+
+ typedef enum e_hit_test_type
+ {
+ HIT_TEST_USE_BOUNDING_RECT,
+ HIT_TEST_IGNORE_BOUNDING_RECT
+ }EHitTestType;
+
+ bool parentPointInView(S32 x, S32 y, EHitTestType type = HIT_TEST_USE_BOUNDING_RECT) const;
+ bool pointInView(S32 x, S32 y, EHitTestType type = HIT_TEST_USE_BOUNDING_RECT) const;
+ 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, 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;
+
+ LLControlVariable *findControl(const std::string& name);
+
+ const child_list_t* getChildList() const { return &mChildList; }
+ child_list_const_iter_t beginChild() const { return mChildList.begin(); }
+ child_list_const_iter_t endChild() const { return mChildList.end(); }
+
+ // LLMouseHandler functions
+ // Default behavior is to pass events to children
+ /*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleMiddleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleMiddleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleDoubleClick(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleScrollWheel(S32 x, S32 y, S32 clicks);
+ /*virtual*/ bool handleScrollHWheel(S32 x, S32 y, S32 clicks);
+ /*virtual*/ bool handleRightMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleRightMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ bool handleToolTip(S32 x, S32 y, MASK mask);
+
+ /*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, 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
+ {
+ LLView* child = findChildView(name, recurse);
+ T* result = dynamic_cast<T*>(child);
+ return result;
+ }
+
+ template <class T> T* getChild(const std::string& name, bool recurse = true) const;
+
+ template <class T> T& getChildRef(const std::string& name, bool recurse = true) const
+ {
+ return *getChild<T>(name, recurse);
+ }
+
+ virtual LLView* getChildView(const std::string& name, bool recurse = true) const;
+ virtual LLView* findChildView(const std::string& name, bool recurse = true) const;
+
+ template <class T> T* getDefaultWidget(const std::string& name) const
+ {
+ LLView* widgetp = getDefaultWidgetContainer().findChildView(name);
+ 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
+ //////////////////////////////////////////////
+ //static LLFontGL::HAlign selectFontHAlign(LLXMLNodePtr node);
+
+ // focuses the item in the list after the currently-focused item, wrapping if necessary
+ static bool focusNext(LLView::child_list_t & result);
+ // focuses the item in the list before the currently-focused item, wrapping if necessary
+ static bool focusPrev(LLView::child_list_t & result);
+
+ // returns query for iterating over controls in tab order
+ static const LLViewQuery & getTabOrderQuery();
+ // return query for iterating over focus roots in tab order
+ static const LLViewQuery & getFocusRootsQuery();
+
+ static LLWindow* getWindow(void) { return LLUI::getInstance()->mWindow; }
+
+ // Set up params after XML load before calling new(),
+ // usually to adjust layout.
+ 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.
+ static void setupParamsForExport(Params& p, LLView* parent);
+
+ //virtual bool addChildFromParam(const LLInitParam::BaseBlock& params) { return true; }
+ virtual bool handleKeyHere(KEY key, MASK mask);
+ virtual bool handleKeyUpHere(KEY key, MASK mask);
+ virtual bool handleUnicodeCharHere(llwchar uni_char);
+
+ virtual void handleReshape(const LLRect& rect, bool by_user);
+ virtual void dirtyRect();
+
+ //send custom notification to LLView parent
+ virtual S32 notifyParent(const LLSD& info);
+
+ //send custom notification to all view childrend
+ // return true if _any_ children return true. otherwise false.
+ virtual bool notifyChildren(const LLSD& info);
+
+ //send custom notification to current view
+ virtual S32 notify(const LLSD& info) { return 0;};
+
+ static const LLViewDrawContext& getDrawContext();
+
+ // 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* childrenHandleKeyUp(KEY key, MASK mask);
+ LLView* childrenHandleUnicodeChar(llwchar uni_char);
+ LLView* childrenHandleDragAndDrop(S32 x, S32 y, MASK mask,
+ bool drop,
+ EDragAndDropType type,
+ void* data,
+ EAcceptance* accept,
+ std::string& tooltip_msg);
+
+ LLView* childrenHandleHover(S32 x, S32 y, MASK mask);
+ LLView* childrenHandleMouseUp(S32 x, S32 y, MASK mask);
+ LLView* childrenHandleMouseDown(S32 x, S32 y, MASK mask);
+ LLView* childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask);
+ LLView* childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask);
+ LLView* childrenHandleDoubleClick(S32 x, S32 y, MASK mask);
+ LLView* childrenHandleScrollWheel(S32 x, S32 y, S32 clicks);
+ LLView* childrenHandleScrollHWheel(S32 x, S32 y, S32 clicks);
+ LLView* childrenHandleRightMouseDown(S32 x, S32 y, MASK mask);
+ LLView* childrenHandleRightMouseUp(S32 x, S32 y, MASK mask);
+ LLView* childrenHandleToolTip(S32 x, S32 y, MASK mask);
+
+ ECursorType mHoverCursor;
+
+ 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;
+
+ // 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 mTabOrder;
+ S32 mDefaultTabGroup;
+ S32 mLastTabGroup;
+
+ bool mEnabled; // Enabled means "accepts input that has an effect on the state of the application."
+ // A disabled view, for example, may still have a scrollbar that responds to mouse events.
+ bool mMouseOpaque; // Opaque views handle all mouse events that are over their rect.
+ LLUIString mToolTipMsg; // isNull() is true if none.
+
+ U8 mSoundFlags;
+ bool mFromXUI;
+
+ bool mIsFocusRoot;
+ bool mUseBoundingRect; // hit test against bounding rectangle that includes all child elements
+
+ bool mLastVisible;
+
+ bool mInDraw;
+
+ static LLWindow* sWindow; // All root views must know about their window.
+
+ typedef std::map<std::string, LLView*> default_widget_map_t;
+ // allocate this map no demand, as it is rarely needed
+ mutable LLView* mDefaultWidgets;
+
+ 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;
+
+ // Draw debug rectangles around widgets to help with alignment and spacing
+ static bool sDebugRects;
+
+ // Show hexadecimal byte values of unicode symbols in a tooltip
+ static bool sDebugUnicode;
+
+ // Show camera position and direction in Camera Controls floater
+ static bool sDebugCamera;
+
+ static bool sIsRectDirty;
+ static LLRect sDirtyRect;
+
+ // Draw widget names and sizes when drawing debug rectangles, turning this
+ // off is useful to make the rectangles themselves easier to see.
+ static bool sDebugRectsShowNames;
+
+ static bool sDebugKeys;
+ static bool sDebugMouseHandling;
+ static std::string sMouseHandlerMessage;
+ static S32 sSelectID;
+ static std::set<LLView*> sPreviewHighlightedElements; // DEV-16869
+ static bool sHighlightingDiffs; // DEV-16869
+ static LLView* sPreviewClickedElement; // DEV-16869
+ static bool sDrawPreviewHighlights;
+ static S32 sLastLeftXML;
+ static S32 sLastBottomXML;
+ static bool sForceReshape;
+};
+
+namespace LLInitParam
+{
+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
+{
+ LLView* child = findChildView(name, recurse);
+ T* result = dynamic_cast<T*>(child);
+ if (!result)
+ {
+ // did we find *something* with that name?
+ if (child)
+ {
+ 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)
+ {
+ result = LLUICtrlFactory::getDefaultWidget<T>(name);
+ if (!result)
+ {
+ LL_ERRS() << "Failed to create dummy " << typeid(T).name() << LL_ENDL;
+ }
+
+ // *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().
+ LL_WARNS() << "Making dummy " << typeid(T).name() << " named \"" << name << "\" in " << getName() << LL_ENDL;
+
+ getDefaultWidgetContainer().addChild(result);
+ }
+ }
+ return result;
+}
+
+// Compiler optimization - don't generate these specializations inline,
+// require explicit specialization. See llbutton.cpp for an example.
+#ifndef LLVIEW_CPP
+extern template class LLView* LLView::getChild<class LLView>(
+ const std::string& name, bool recurse) const;
+#endif
+
+#endif //LL_LLVIEW_H
diff --git a/indra/llui/llviewborder.cpp b/indra/llui/llviewborder.cpp
index bca566b7df..241192e21c 100644
--- a/indra/llui/llviewborder.cpp
+++ b/indra/llui/llviewborder.cpp
@@ -1,270 +1,270 @@
-/**
- * @file llviewborder.cpp
- *
- * $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 "llviewborder.h"
-#include "llrender.h"
-#include "llfocusmgr.h"
-#include "lluictrlfactory.h"
-#include "lluiimage.h"
-
-static LLDefaultChildRegistry::Register<LLViewBorder> r("view_border");
-
-void LLViewBorder::BevelValues::declareValues()
-{
- declare("in", LLViewBorder::BEVEL_IN);
- declare("out", LLViewBorder::BEVEL_OUT);
- declare("bright", LLViewBorder::BEVEL_BRIGHT);
- declare("none", LLViewBorder::BEVEL_NONE);
-}
-
-void LLViewBorder::StyleValues::declareValues()
-{
- declare("line", LLViewBorder::STYLE_LINE);
- declare("texture", LLViewBorder::STYLE_TEXTURE);
-}
-
-LLViewBorder::Params::Params()
-: bevel_style("bevel_style", BEVEL_OUT),
- render_style("border_style", STYLE_LINE),
- border_thickness("border_thickness"),
- highlight_light_color("highlight_light_color"),
- highlight_dark_color("highlight_dark_color"),
- shadow_light_color("shadow_light_color"),
- shadow_dark_color("shadow_dark_color")
-{
- addSynonym(border_thickness, "thickness");
- addSynonym(render_style, "style");
-}
-
-
-LLViewBorder::LLViewBorder(const LLViewBorder::Params& p)
-: LLView(p),
- mTexture( NULL ),
- mHasKeyboardFocus( false ),
- mBorderWidth(p.border_thickness),
- mHighlightLight(p.highlight_light_color()),
- mHighlightDark(p.highlight_dark_color()),
- mShadowLight(p.shadow_light_color()),
- mShadowDark(p.shadow_dark_color()),
- mBevel(p.bevel_style),
- mStyle(p.render_style)
-{}
-
-void LLViewBorder::setColors( const LLColor4& shadow_dark, const LLColor4& highlight_light )
-{
- mShadowDark = shadow_dark;
- mHighlightLight = highlight_light;
-}
-
-void LLViewBorder::setColorsExtended( const LLColor4& shadow_light, const LLColor4& shadow_dark,
- const LLColor4& highlight_light, const LLColor4& highlight_dark )
-{
- mShadowDark = shadow_dark;
- mShadowLight = shadow_light;
- mHighlightLight = highlight_light;
- mHighlightDark = highlight_dark;
-}
-
-void LLViewBorder::setTexture( const LLUUID &image_id )
-{
- mTexture = LLUI::getUIImageByID(image_id);
-}
-
-
-void LLViewBorder::draw()
-{
- if( STYLE_LINE == mStyle )
- {
- if( 0 == mBorderWidth )
- {
- // no visible border
- }
- else
- if( 1 == mBorderWidth )
- {
- drawOnePixelLines();
- }
- else
- if( 2 == mBorderWidth )
- {
- drawTwoPixelLines();
- }
- else
- {
- llassert( false ); // not implemented
- }
- }
-
- LLView::draw();
-}
-
-void LLViewBorder::drawOnePixelLines()
-{
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- LLColor4 top_color = mHighlightLight.get();
- LLColor4 bottom_color = mHighlightLight.get();
- switch( mBevel )
- {
- case BEVEL_OUT:
- top_color = mHighlightLight.get();
- bottom_color = mShadowDark.get();
- break;
- case BEVEL_IN:
- top_color = mShadowDark.get();
- bottom_color = mHighlightLight.get();
- break;
- case BEVEL_NONE:
- // use defaults
- break;
- default:
- llassert(0);
- }
-
- if( mHasKeyboardFocus )
- {
- top_color = gFocusMgr.getFocusColor();
- bottom_color = top_color;
-
- LLUI::setLineWidth(lerp(1.f, 3.f, gFocusMgr.getFocusFlashAmt()));
- }
-
- S32 left = 0;
- S32 top = getRect().getHeight();
- S32 right = getRect().getWidth();
- S32 bottom = 0;
-
- gGL.color4fv( top_color.mV );
- gl_line_2d(left, bottom, left, top);
- gl_line_2d(left, top, right, top);
-
- gGL.color4fv( bottom_color.mV );
- gl_line_2d(right, top, right, bottom);
- gl_line_2d(left, bottom, right, bottom);
-
- LLUI::setLineWidth(1.f);
-}
-
-void LLViewBorder::drawTwoPixelLines()
-{
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- LLColor4 focus_color = gFocusMgr.getFocusColor();
-
- LLColor4 top_in_color;
- LLColor4 top_out_color;
- LLColor4 bottom_in_color;
- LLColor4 bottom_out_color;
-
- switch( mBevel )
- {
- case BEVEL_OUT:
- top_in_color = mHighlightLight.get();
- top_out_color = mHighlightDark.get();
- bottom_in_color = mShadowLight.get();
- bottom_out_color = mShadowDark.get();
- break;
- case BEVEL_IN:
- top_in_color = mShadowDark.get();
- top_out_color = mShadowLight.get();
- bottom_in_color = mHighlightDark.get();
- bottom_out_color = mHighlightLight.get();
- break;
- case BEVEL_BRIGHT:
- top_in_color = mHighlightLight.get();
- top_out_color = mHighlightLight.get();
- bottom_in_color = mHighlightLight.get();
- bottom_out_color = mHighlightLight.get();
- break;
- case BEVEL_NONE:
- top_in_color = mShadowDark.get();
- top_out_color = mShadowDark.get();
- bottom_in_color = mShadowDark.get();
- bottom_out_color = mShadowDark.get();
- // use defaults
- break;
- default:
- llassert(0);
- }
-
- if( mHasKeyboardFocus )
- {
- top_out_color = focus_color;
- bottom_out_color = focus_color;
- }
-
- S32 left = 0;
- S32 top = getRect().getHeight();
- S32 right = getRect().getWidth();
- S32 bottom = 0;
-
- // draw borders
- gGL.color3fv( top_out_color.mV );
- gl_line_2d(left, bottom, left, top-1);
- gl_line_2d(left, top-1, right, top-1);
-
- gGL.color3fv( top_in_color.mV );
- gl_line_2d(left+1, bottom+1, left+1, top-2);
- gl_line_2d(left+1, top-2, right-1, top-2);
-
- gGL.color3fv( bottom_out_color.mV );
- gl_line_2d(right-1, top-1, right-1, bottom);
- gl_line_2d(left, bottom, right, bottom);
-
- gGL.color3fv( bottom_in_color.mV );
- gl_line_2d(right-2, top-2, right-2, bottom+1);
- gl_line_2d(left+1, bottom+1, right-1, bottom+1);
-}
-
-bool LLViewBorder::getBevelFromAttribute(LLXMLNodePtr node, LLViewBorder::EBevel& bevel_style)
-{
- if (node->hasAttribute("bevel_style"))
- {
- std::string bevel_string;
- node->getAttributeString("bevel_style", bevel_string);
- LLStringUtil::toLower(bevel_string);
-
- if (bevel_string == "none")
- {
- bevel_style = LLViewBorder::BEVEL_NONE;
- }
- else if (bevel_string == "in")
- {
- bevel_style = LLViewBorder::BEVEL_IN;
- }
- else if (bevel_string == "out")
- {
- bevel_style = LLViewBorder::BEVEL_OUT;
- }
- else if (bevel_string == "bright")
- {
- bevel_style = LLViewBorder::BEVEL_BRIGHT;
- }
- return true;
- }
- return false;
-}
-
+/**
+ * @file llviewborder.cpp
+ *
+ * $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 "llviewborder.h"
+#include "llrender.h"
+#include "llfocusmgr.h"
+#include "lluictrlfactory.h"
+#include "lluiimage.h"
+
+static LLDefaultChildRegistry::Register<LLViewBorder> r("view_border");
+
+void LLViewBorder::BevelValues::declareValues()
+{
+ declare("in", LLViewBorder::BEVEL_IN);
+ declare("out", LLViewBorder::BEVEL_OUT);
+ declare("bright", LLViewBorder::BEVEL_BRIGHT);
+ declare("none", LLViewBorder::BEVEL_NONE);
+}
+
+void LLViewBorder::StyleValues::declareValues()
+{
+ declare("line", LLViewBorder::STYLE_LINE);
+ declare("texture", LLViewBorder::STYLE_TEXTURE);
+}
+
+LLViewBorder::Params::Params()
+: bevel_style("bevel_style", BEVEL_OUT),
+ render_style("border_style", STYLE_LINE),
+ border_thickness("border_thickness"),
+ highlight_light_color("highlight_light_color"),
+ highlight_dark_color("highlight_dark_color"),
+ shadow_light_color("shadow_light_color"),
+ shadow_dark_color("shadow_dark_color")
+{
+ addSynonym(border_thickness, "thickness");
+ addSynonym(render_style, "style");
+}
+
+
+LLViewBorder::LLViewBorder(const LLViewBorder::Params& p)
+: LLView(p),
+ mTexture( NULL ),
+ mHasKeyboardFocus( false ),
+ mBorderWidth(p.border_thickness),
+ mHighlightLight(p.highlight_light_color()),
+ mHighlightDark(p.highlight_dark_color()),
+ mShadowLight(p.shadow_light_color()),
+ mShadowDark(p.shadow_dark_color()),
+ mBevel(p.bevel_style),
+ mStyle(p.render_style)
+{}
+
+void LLViewBorder::setColors( const LLColor4& shadow_dark, const LLColor4& highlight_light )
+{
+ mShadowDark = shadow_dark;
+ mHighlightLight = highlight_light;
+}
+
+void LLViewBorder::setColorsExtended( const LLColor4& shadow_light, const LLColor4& shadow_dark,
+ const LLColor4& highlight_light, const LLColor4& highlight_dark )
+{
+ mShadowDark = shadow_dark;
+ mShadowLight = shadow_light;
+ mHighlightLight = highlight_light;
+ mHighlightDark = highlight_dark;
+}
+
+void LLViewBorder::setTexture( const LLUUID &image_id )
+{
+ mTexture = LLUI::getUIImageByID(image_id);
+}
+
+
+void LLViewBorder::draw()
+{
+ if( STYLE_LINE == mStyle )
+ {
+ if( 0 == mBorderWidth )
+ {
+ // no visible border
+ }
+ else
+ if( 1 == mBorderWidth )
+ {
+ drawOnePixelLines();
+ }
+ else
+ if( 2 == mBorderWidth )
+ {
+ drawTwoPixelLines();
+ }
+ else
+ {
+ llassert( false ); // not implemented
+ }
+ }
+
+ LLView::draw();
+}
+
+void LLViewBorder::drawOnePixelLines()
+{
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ LLColor4 top_color = mHighlightLight.get();
+ LLColor4 bottom_color = mHighlightLight.get();
+ switch( mBevel )
+ {
+ case BEVEL_OUT:
+ top_color = mHighlightLight.get();
+ bottom_color = mShadowDark.get();
+ break;
+ case BEVEL_IN:
+ top_color = mShadowDark.get();
+ bottom_color = mHighlightLight.get();
+ break;
+ case BEVEL_NONE:
+ // use defaults
+ break;
+ default:
+ llassert(0);
+ }
+
+ if( mHasKeyboardFocus )
+ {
+ top_color = gFocusMgr.getFocusColor();
+ bottom_color = top_color;
+
+ LLUI::setLineWidth(lerp(1.f, 3.f, gFocusMgr.getFocusFlashAmt()));
+ }
+
+ S32 left = 0;
+ S32 top = getRect().getHeight();
+ S32 right = getRect().getWidth();
+ S32 bottom = 0;
+
+ gGL.color4fv( top_color.mV );
+ gl_line_2d(left, bottom, left, top);
+ gl_line_2d(left, top, right, top);
+
+ gGL.color4fv( bottom_color.mV );
+ gl_line_2d(right, top, right, bottom);
+ gl_line_2d(left, bottom, right, bottom);
+
+ LLUI::setLineWidth(1.f);
+}
+
+void LLViewBorder::drawTwoPixelLines()
+{
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+
+ LLColor4 focus_color = gFocusMgr.getFocusColor();
+
+ LLColor4 top_in_color;
+ LLColor4 top_out_color;
+ LLColor4 bottom_in_color;
+ LLColor4 bottom_out_color;
+
+ switch( mBevel )
+ {
+ case BEVEL_OUT:
+ top_in_color = mHighlightLight.get();
+ top_out_color = mHighlightDark.get();
+ bottom_in_color = mShadowLight.get();
+ bottom_out_color = mShadowDark.get();
+ break;
+ case BEVEL_IN:
+ top_in_color = mShadowDark.get();
+ top_out_color = mShadowLight.get();
+ bottom_in_color = mHighlightDark.get();
+ bottom_out_color = mHighlightLight.get();
+ break;
+ case BEVEL_BRIGHT:
+ top_in_color = mHighlightLight.get();
+ top_out_color = mHighlightLight.get();
+ bottom_in_color = mHighlightLight.get();
+ bottom_out_color = mHighlightLight.get();
+ break;
+ case BEVEL_NONE:
+ top_in_color = mShadowDark.get();
+ top_out_color = mShadowDark.get();
+ bottom_in_color = mShadowDark.get();
+ bottom_out_color = mShadowDark.get();
+ // use defaults
+ break;
+ default:
+ llassert(0);
+ }
+
+ if( mHasKeyboardFocus )
+ {
+ top_out_color = focus_color;
+ bottom_out_color = focus_color;
+ }
+
+ S32 left = 0;
+ S32 top = getRect().getHeight();
+ S32 right = getRect().getWidth();
+ S32 bottom = 0;
+
+ // draw borders
+ gGL.color3fv( top_out_color.mV );
+ gl_line_2d(left, bottom, left, top-1);
+ gl_line_2d(left, top-1, right, top-1);
+
+ gGL.color3fv( top_in_color.mV );
+ gl_line_2d(left+1, bottom+1, left+1, top-2);
+ gl_line_2d(left+1, top-2, right-1, top-2);
+
+ gGL.color3fv( bottom_out_color.mV );
+ gl_line_2d(right-1, top-1, right-1, bottom);
+ gl_line_2d(left, bottom, right, bottom);
+
+ gGL.color3fv( bottom_in_color.mV );
+ gl_line_2d(right-2, top-2, right-2, bottom+1);
+ gl_line_2d(left+1, bottom+1, right-1, bottom+1);
+}
+
+bool LLViewBorder::getBevelFromAttribute(LLXMLNodePtr node, LLViewBorder::EBevel& bevel_style)
+{
+ if (node->hasAttribute("bevel_style"))
+ {
+ std::string bevel_string;
+ node->getAttributeString("bevel_style", bevel_string);
+ LLStringUtil::toLower(bevel_string);
+
+ if (bevel_string == "none")
+ {
+ bevel_style = LLViewBorder::BEVEL_NONE;
+ }
+ else if (bevel_string == "in")
+ {
+ bevel_style = LLViewBorder::BEVEL_IN;
+ }
+ else if (bevel_string == "out")
+ {
+ bevel_style = LLViewBorder::BEVEL_OUT;
+ }
+ else if (bevel_string == "bright")
+ {
+ bevel_style = LLViewBorder::BEVEL_BRIGHT;
+ }
+ return true;
+ }
+ return false;
+}
+
diff --git a/indra/llui/llviewborder.h b/indra/llui/llviewborder.h
index e287f79848..6a2bcb4b20 100644
--- a/indra/llui/llviewborder.h
+++ b/indra/llui/llviewborder.h
@@ -1,110 +1,110 @@
-/**
- * @file llviewborder.h
- * @brief A customizable decorative border. Does not interact with mouse events.
- *
- * $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_LLVIEWBORDER_H
-#define LL_LLVIEWBORDER_H
-
-#include "llview.h"
-
-class LLViewBorder : public LLView
-{
-public:
- typedef enum e_bevel { BEVEL_IN, BEVEL_OUT, BEVEL_BRIGHT, BEVEL_NONE } EBevel ;
- typedef enum e_style { STYLE_LINE, STYLE_TEXTURE } EStyle;
-
- struct BevelValues
- : public LLInitParam::TypeValuesHelper<LLViewBorder::EBevel, BevelValues>
- {
- static void declareValues();
- };
-
- struct StyleValues
- : public LLInitParam::TypeValuesHelper<LLViewBorder::EStyle, StyleValues>
- {
- static void declareValues();
- };
-
- struct Params : public LLInitParam::Block<Params, LLView::Params>
- {
- Optional<EBevel, BevelValues> bevel_style;
- Optional<EStyle, StyleValues> render_style;
- Optional<S32> border_thickness;
-
- Optional<LLUIColor> highlight_light_color,
- highlight_dark_color,
- shadow_light_color,
- shadow_dark_color;
-
- Params();
- };
-protected:
- LLViewBorder(const Params&);
- friend class LLUICtrlFactory;
-public:
- virtual void setValue(const LLSD& val) { setRect(LLRect(val)); }
-
- virtual bool isCtrl() const { return false; }
-
- // llview functionality
- virtual void draw();
-
- static bool getBevelFromAttribute(LLXMLNodePtr node, LLViewBorder::EBevel& bevel_style);
-
- void setBorderWidth(S32 width) { mBorderWidth = width; }
- S32 getBorderWidth() const { return mBorderWidth; }
- void setBevel(EBevel bevel) { mBevel = bevel; }
- EBevel getBevel() const { return mBevel; }
- void setColors( const LLColor4& shadow_dark, const LLColor4& highlight_light );
- void setColorsExtended( const LLColor4& shadow_light, const LLColor4& shadow_dark,
- const LLColor4& highlight_light, const LLColor4& highlight_dark );
- void setTexture( const class LLUUID &image_id );
-
- LLColor4 getHighlightLight() {return mHighlightLight.get();}
- LLColor4 getShadowDark() {return mHighlightDark.get();}
-
- EStyle getStyle() const { return mStyle; }
-
- void setKeyboardFocusHighlight( bool b ) { mHasKeyboardFocus = b; }
-
-private:
- void drawOnePixelLines();
- void drawTwoPixelLines();
- void drawTextures();
-
- EBevel mBevel;
- EStyle mStyle;
- LLUIColor mHighlightLight;
- LLUIColor mHighlightDark;
- LLUIColor mShadowLight;
- LLUIColor mShadowDark;
- LLUIColor mBackgroundColor;
- S32 mBorderWidth;
- LLPointer<LLUIImage> mTexture;
- bool mHasKeyboardFocus;
-};
-
-#endif // LL_LLVIEWBORDER_H
-
+/**
+ * @file llviewborder.h
+ * @brief A customizable decorative border. Does not interact with mouse events.
+ *
+ * $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_LLVIEWBORDER_H
+#define LL_LLVIEWBORDER_H
+
+#include "llview.h"
+
+class LLViewBorder : public LLView
+{
+public:
+ typedef enum e_bevel { BEVEL_IN, BEVEL_OUT, BEVEL_BRIGHT, BEVEL_NONE } EBevel ;
+ typedef enum e_style { STYLE_LINE, STYLE_TEXTURE } EStyle;
+
+ struct BevelValues
+ : public LLInitParam::TypeValuesHelper<LLViewBorder::EBevel, BevelValues>
+ {
+ static void declareValues();
+ };
+
+ struct StyleValues
+ : public LLInitParam::TypeValuesHelper<LLViewBorder::EStyle, StyleValues>
+ {
+ static void declareValues();
+ };
+
+ struct Params : public LLInitParam::Block<Params, LLView::Params>
+ {
+ Optional<EBevel, BevelValues> bevel_style;
+ Optional<EStyle, StyleValues> render_style;
+ Optional<S32> border_thickness;
+
+ Optional<LLUIColor> highlight_light_color,
+ highlight_dark_color,
+ shadow_light_color,
+ shadow_dark_color;
+
+ Params();
+ };
+protected:
+ LLViewBorder(const Params&);
+ friend class LLUICtrlFactory;
+public:
+ virtual void setValue(const LLSD& val) { setRect(LLRect(val)); }
+
+ virtual bool isCtrl() const { return false; }
+
+ // llview functionality
+ virtual void draw();
+
+ static bool getBevelFromAttribute(LLXMLNodePtr node, LLViewBorder::EBevel& bevel_style);
+
+ void setBorderWidth(S32 width) { mBorderWidth = width; }
+ S32 getBorderWidth() const { return mBorderWidth; }
+ void setBevel(EBevel bevel) { mBevel = bevel; }
+ EBevel getBevel() const { return mBevel; }
+ void setColors( const LLColor4& shadow_dark, const LLColor4& highlight_light );
+ void setColorsExtended( const LLColor4& shadow_light, const LLColor4& shadow_dark,
+ const LLColor4& highlight_light, const LLColor4& highlight_dark );
+ void setTexture( const class LLUUID &image_id );
+
+ LLColor4 getHighlightLight() {return mHighlightLight.get();}
+ LLColor4 getShadowDark() {return mHighlightDark.get();}
+
+ EStyle getStyle() const { return mStyle; }
+
+ void setKeyboardFocusHighlight( bool b ) { mHasKeyboardFocus = b; }
+
+private:
+ void drawOnePixelLines();
+ void drawTwoPixelLines();
+ void drawTextures();
+
+ EBevel mBevel;
+ EStyle mStyle;
+ LLUIColor mHighlightLight;
+ LLUIColor mHighlightDark;
+ LLUIColor mShadowLight;
+ LLUIColor mShadowDark;
+ LLUIColor mBackgroundColor;
+ S32 mBorderWidth;
+ LLPointer<LLUIImage> mTexture;
+ bool mHasKeyboardFocus;
+};
+
+#endif // LL_LLVIEWBORDER_H
+
diff --git a/indra/llui/llviewereventrecorder.cpp b/indra/llui/llviewereventrecorder.cpp
index 87f83a6493..177cfb98ce 100644
--- a/indra/llui/llviewereventrecorder.cpp
+++ b/indra/llui/llviewereventrecorder.cpp
@@ -1,296 +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, ENOENT);
-
-
- mLogFilename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.llsd");
- LLFile::rename(mLogFilename, old_log_ui_events_to_llsd_file, ENOENT);
-
-}
-
-
-bool LLViewerEventRecorder::displayViewerEventRecorderMenuItems() {
- return LLUI::getInstance()->mSettingGroups["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::getInstance()->resolvePath(LLUI::getInstance()->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::getInstance()->mSettingGroups["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);
-
-
-}
+/**
+ * @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, ENOENT);
+
+
+ mLogFilename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.llsd");
+ LLFile::rename(mLogFilename, old_log_ui_events_to_llsd_file, ENOENT);
+
+}
+
+
+bool LLViewerEventRecorder::displayViewerEventRecorderMenuItems() {
+ return LLUI::getInstance()->mSettingGroups["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::getInstance()->resolvePath(LLUI::getInstance()->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::getInstance()->mSettingGroups["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/llviewinject.cpp b/indra/llui/llviewinject.cpp
index 46c5839f8e..6da00de5d4 100644
--- a/indra/llui/llviewinject.cpp
+++ b/indra/llui/llviewinject.cpp
@@ -3,7 +3,7 @@
* @author Nat Goodspeed
* @date 2011-08-16
* @brief Implementation for llviewinject.
- *
+ *
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
* Copyright (c) 2011, Linden Research, Inc.
* $/LicenseInfo$
diff --git a/indra/llui/llviewinject.h b/indra/llui/llviewinject.h
index 7f18ec6fbe..c4218e28ff 100644
--- a/indra/llui/llviewinject.h
+++ b/indra/llui/llviewinject.h
@@ -3,7 +3,7 @@
* @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$
diff --git a/indra/llui/llviewmodel.cpp b/indra/llui/llviewmodel.cpp
index a400eb70c0..93106b344f 100644
--- a/indra/llui/llviewmodel.cpp
+++ b/indra/llui/llviewmodel.cpp
@@ -3,25 +3,25 @@
* @author Nat Goodspeed
* @date 2008-08-08
* @brief Implementation for llviewmodel.
- *
+ *
* $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$
*/
@@ -37,13 +37,13 @@
///
LLViewModel::LLViewModel()
-: mDirty(false)
+: mDirty(false)
{
}
/// Instantiate an LLViewModel with an existing data value
LLViewModel::LLViewModel(const LLSD& value)
-: mDirty(false)
+: mDirty(false)
{
setValue(value);
}
@@ -65,23 +65,23 @@ LLSD LLViewModel::getValue() const
///
LLTextViewModel::LLTextViewModel()
: LLViewModel(false),
- mUpdateFromDisplay(false)
+ mUpdateFromDisplay(false)
{
}
/// Instantiate an LLViewModel with an existing data value
LLTextViewModel::LLTextViewModel(const LLSD& value)
: LLViewModel(value),
- mUpdateFromDisplay(false)
+ mUpdateFromDisplay(false)
{
}
/// Update the stored value
void LLTextViewModel::setValue(const LLSD& value)
{
- // approximate LLSD storage usage
- LLViewModel::setValue(value);
- mDisplay = utf8str_to_wstring(value.asString());
+ // approximate LLSD storage usage
+ LLViewModel::setValue(value);
+ mDisplay = utf8str_to_wstring(mStringValue = value.asString());
// mDisplay and mValue agree
mUpdateFromDisplay = false;
@@ -93,31 +93,42 @@ 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.
- mDisplay = value;
- mDirty = true;
+ mDisplay = value;
+ 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
// doing.
mUpdateFromDisplay = true;
}
-LLSD LLTextViewModel::getValue() const
+inline void updateFromDisplayIfNeeded(const LLTextViewModel* model)
{
- // Has anyone called setDisplay() since the last setValue()? If so, have
- // to convert mDisplay back to UTF8.
- if (mUpdateFromDisplay)
+ // Has anyone called setDisplay() since the last setValue()?
+ // If so, have to convert mDisplay back to UTF8.
+ if (model->mUpdateFromDisplay)
{
- // The fact that we're lazily updating fields in this object should be
- // transparent to clients, which is why this method is left
- // conventionally const. Nor do we particularly want to make these
- // members mutable. Just cast away constness in this one place.
- LLTextViewModel* nthis = const_cast<LLTextViewModel*>(this);
+ // The fact that we're lazily updating fields
+ // in this object should be transparent to clients,
+ // which is why this method is left conventionally const.
+ // Nor do we particularly want to make these members mutable.
+ // Just cast away constness in this one place.
+ LLTextViewModel* nthis = const_cast<LLTextViewModel*>(model);
nthis->mUpdateFromDisplay = false;
- nthis->mValue = wstring_to_utf8str(mDisplay);
+ nthis->mValue = nthis->mStringValue = wstring_to_utf8str(model->mDisplay);
}
- return LLViewModel::getValue();
}
+LLSD LLTextViewModel::getValue() const
+{
+ updateFromDisplayIfNeeded(this);
+ return mValue;
+}
+
+const std::string& LLTextViewModel::getStringValue() const
+{
+ updateFromDisplayIfNeeded(this);
+ return mStringValue;
+}
////////////////////////////////////////////////////////////////////////////
diff --git a/indra/llui/llviewmodel.h b/indra/llui/llviewmodel.h
index e7dceb6c31..6cf2200a81 100644
--- a/indra/llui/llviewmodel.h
+++ b/indra/llui/llviewmodel.h
@@ -8,25 +8,25 @@
* underlying a specific widget object -- as in our case -- rather
* than the business "model" object underlying the overall "view"
* presented by the collection of widgets.
- *
+ *
* $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$
*/
@@ -61,8 +61,8 @@ 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:
LLViewModel();
@@ -79,7 +79,7 @@ public:
/// Once the value has been saved to a file, or otherwise consumed by the
/// app, we no longer need to enable the Save button
void resetDirty() { mDirty = false; }
- //
+ //
void setDirty() { mDirty = true; }
protected:
@@ -88,7 +88,7 @@ protected:
};
/**
- * LLTextViewModel stores a value displayed as text.
+ * LLTextViewModel stores a value displayed as text.
*/
class LLTextViewModel: public LLViewModel
{
@@ -96,15 +96,16 @@ public:
LLTextViewModel();
/// Instantiate an LLViewModel with an existing data value
LLTextViewModel(const LLSD& value);
-
- // LLViewModel functions
+
+ // LLViewModel functions
virtual void setValue(const LLSD& value);
virtual LLSD getValue() const;
+ const std::string& getStringValue() const;
- // New functions
+ // New functions
/// Get the stored value in string form
const LLWString& getDisplay() const { return mDisplay; }
- LLWString& getEditableDisplay() { mDirty = true; mUpdateFromDisplay = true; return mDisplay; }
+ LLWString& getEditableDisplay() { mDirty = true; mUpdateFromDisplay = true; return mDisplay; }
/**
* Set the display string directly (see LLTextEditor). What the user is
@@ -112,14 +113,19 @@ public:
* UTF-8 value.
*/
void setDisplay(const LLWString& value);
-
+
private:
+ std::string mStringValue;
+
/// To avoid converting every widget's stored value from LLSD to LLWString
/// every frame, cache the converted value
LLWString mDisplay;
+
/// As the user edits individual characters (setDisplay()), defer
/// LLWString-to-UTF8 conversions until s/he's done.
bool mUpdateFromDisplay;
+
+ friend void updateFromDisplayIfNeeded(const LLTextViewModel* model);
};
/**
@@ -145,73 +151,73 @@ public:
//namespace LLViewModel
//{
-// class Value
-// {
-// public:
-// Value(const LLSD& value = LLSD());
+// class Value
+// {
+// public:
+// Value(const LLSD& value = LLSD());
//
-// LLSD getValue() const { return mValue; }
-// void setValue(const LLSD& value) { mValue = value; }
+// LLSD getValue() const { return mValue; }
+// void setValue(const LLSD& value) { mValue = value; }
//
-// bool isAvailable() const { return false; }
-// bool isReadOnly() const { return false; }
+// bool isAvailable() const { return false; }
+// bool isReadOnly() const { return false; }
//
-// bool undo() { return false; }
-// bool redo() { return false; }
+// bool undo() { return false; }
+// bool redo() { return false; }
//
-// /// Has the value been changed since last time we checked?
-// bool isDirty() const { return mDirty; }
-// /// Once the value has been saved to a file, or otherwise consumed by the
-// /// app, we no longer need to enable the Save button
-// void resetDirty() { mDirty = false; }
-// //
-// void setDirty() { mDirty = true; }
+// /// Has the value been changed since last time we checked?
+// bool isDirty() const { return mDirty; }
+// /// Once the value has been saved to a file, or otherwise consumed by the
+// /// app, we no longer need to enable the Save button
+// void resetDirty() { mDirty = false; }
+// //
+// void setDirty() { mDirty = true; }
//
-// protected:
-// LLSD mValue;
-// bool mDirty;
-// };
+// protected:
+// LLSD mValue;
+// bool mDirty;
+// };
//
-// class Numeric : public Value
-// {
-// public:
-// Numeric(S32 value = 0);
-// Numeric(F32 value);
+// class Numeric : public Value
+// {
+// public:
+// Numeric(S32 value = 0);
+// Numeric(F32 value);
//
-// F32 getPrecision();
-// F32 getMin();
-// F32 getMax();
+// F32 getPrecision();
+// F32 getMin();
+// F32 getMax();
//
-// void increment();
-// void decrement();
-// };
+// void increment();
+// void decrement();
+// };
//
-// class MultipleValues : public Value
-// {
-// class Selector
-// {};
+// class MultipleValues : public Value
+// {
+// class Selector
+// {};
//
-// MultipleValues();
-// virtual S32 numElements();
-// };
+// MultipleValues();
+// virtual S32 numElements();
+// };
//
-// class Tuple : public MultipleValues
-// {
-// Tuple(S32 size);
-// LLSD getValue(S32 which) const;
-// void setValue(S32 which, const LLSD& value);
-// };
+// class Tuple : public MultipleValues
+// {
+// Tuple(S32 size);
+// LLSD getValue(S32 which) const;
+// void setValue(S32 which, const LLSD& value);
+// };
//
-// class List : public MultipleValues
-// {
-// List();
+// class List : public MultipleValues
+// {
+// List();
//
-// void add(const ValueModel& value);
-// bool remove(const Selector& item);
+// void add(const ValueModel& value);
+// bool remove(const Selector& item);
//
-// void setSortElement(const Selector& element);
-// void sort();
-// };
+// void setSortElement(const Selector& element);
+// void sort();
+// };
//
//};
#endif /* ! defined(LL_LLVIEWMODEL_H) */
diff --git a/indra/llui/llviewquery.cpp b/indra/llui/llviewquery.cpp
index 3ad5e71a2e..336d884553 100644
--- a/indra/llui/llviewquery.cpp
+++ b/indra/llui/llviewquery.cpp
@@ -1,136 +1,136 @@
-/**
- * @file llviewquery.cpp
- * @brief Implementation of view query class.
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llview.h"
-#include "lluictrl.h"
-#include "llviewquery.h"
-
-void LLQuerySorter::sort(LLView * parent, viewList_t &children) const {}
-
-filterResult_t LLLeavesFilter::operator() (const LLView* const view, const viewList_t & children) const
-{
- return filterResult_t(children.empty(), true);
-}
-
-filterResult_t LLRootsFilter::operator() (const LLView* const view, const viewList_t & children) const
-{
- return filterResult_t(true, false);
-}
-
-filterResult_t LLVisibleFilter::operator() (const LLView* const view, const viewList_t & children) const
-{
- return filterResult_t(view->getVisible(), view->getVisible());
-}
-filterResult_t LLEnabledFilter::operator() (const LLView* const view, const viewList_t & children) const
-{
- return filterResult_t(view->getEnabled(), view->getEnabled());
-}
-filterResult_t LLTabStopFilter::operator() (const LLView* const view, const viewList_t & children) const
-{
- return filterResult_t(view->isCtrl() && static_cast<const LLUICtrl*>(view)->hasTabStop(),
- view->canFocusChildren());
-}
-
-filterResult_t LLCtrlFilter::operator() (const LLView* const view, const viewList_t & children) const
-{
- return filterResult_t(view->isCtrl(),true);
-}
-
-//
-// LLViewQuery
-//
-
-viewList_t LLViewQuery::run(LLView* view) const
-{
- viewList_t result;
-
- // prefilter gets immediate children of view
- filterResult_t pre = runFilters(view, *view->getChildList(), mPreFilters);
- if(!pre.first && !pre.second)
- {
- // not including ourselves or the children
- // nothing more to do
- return result;
- }
-
- viewList_t filtered_children;
- filterResult_t post(true, true);
- if(pre.second)
- {
- // run filters on children
- filterChildren(view, filtered_children);
- // only run post filters if this element passed pre filters
- // so if you failed to pass the pre filter, you can't filter out children in post
- if (pre.first)
- {
- post = runFilters(view, filtered_children, mPostFilters);
- }
- }
-
- if(pre.first && post.first)
- {
- result.push_back(view);
- }
-
- if(pre.second && post.second)
- {
- result.insert(result.end(), filtered_children.begin(), filtered_children.end());
- }
-
- return result;
-}
-
-void LLViewQuery::filterChildren(LLView* parent_view, viewList_t & filtered_children) const
-{
- LLView::child_list_t views(*(parent_view->getChildList()));
- if (mSorterp)
- {
- mSorterp->sort(parent_view, views); // sort the children per the sorter
- }
- for(LLView::child_list_iter_t iter = views.begin();
- iter != views.end();
- iter++)
- {
- viewList_t indiv_children = this->run(*iter);
- filtered_children.splice(filtered_children.end(), indiv_children);
- }
-}
-
-filterResult_t LLViewQuery::runFilters(LLView * view, const viewList_t children, const filterList_t filters) const
-{
- filterResult_t result = filterResult_t(true, true);
- for(filterList_const_iter_t iter = filters.begin();
- iter != filters.end();
- iter++)
- {
- filterResult_t filtered = (**iter)(view, children);
- result.first = result.first && filtered.first;
- result.second = result.second && filtered.second;
- }
- return result;
-}
+/**
+ * @file llviewquery.cpp
+ * @brief Implementation of view query class.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llview.h"
+#include "lluictrl.h"
+#include "llviewquery.h"
+
+void LLQuerySorter::sort(LLView * parent, viewList_t &children) const {}
+
+filterResult_t LLLeavesFilter::operator() (const LLView* const view, const viewList_t & children) const
+{
+ return filterResult_t(children.empty(), true);
+}
+
+filterResult_t LLRootsFilter::operator() (const LLView* const view, const viewList_t & children) const
+{
+ return filterResult_t(true, false);
+}
+
+filterResult_t LLVisibleFilter::operator() (const LLView* const view, const viewList_t & children) const
+{
+ return filterResult_t(view->getVisible(), view->getVisible());
+}
+filterResult_t LLEnabledFilter::operator() (const LLView* const view, const viewList_t & children) const
+{
+ return filterResult_t(view->getEnabled(), view->getEnabled());
+}
+filterResult_t LLTabStopFilter::operator() (const LLView* const view, const viewList_t & children) const
+{
+ return filterResult_t(view->isCtrl() && static_cast<const LLUICtrl*>(view)->hasTabStop(),
+ view->canFocusChildren());
+}
+
+filterResult_t LLCtrlFilter::operator() (const LLView* const view, const viewList_t & children) const
+{
+ return filterResult_t(view->isCtrl(),true);
+}
+
+//
+// LLViewQuery
+//
+
+viewList_t LLViewQuery::run(LLView* view) const
+{
+ viewList_t result;
+
+ // prefilter gets immediate children of view
+ filterResult_t pre = runFilters(view, *view->getChildList(), mPreFilters);
+ if(!pre.first && !pre.second)
+ {
+ // not including ourselves or the children
+ // nothing more to do
+ return result;
+ }
+
+ viewList_t filtered_children;
+ filterResult_t post(true, true);
+ if(pre.second)
+ {
+ // run filters on children
+ filterChildren(view, filtered_children);
+ // only run post filters if this element passed pre filters
+ // so if you failed to pass the pre filter, you can't filter out children in post
+ if (pre.first)
+ {
+ post = runFilters(view, filtered_children, mPostFilters);
+ }
+ }
+
+ if(pre.first && post.first)
+ {
+ result.push_back(view);
+ }
+
+ if(pre.second && post.second)
+ {
+ result.insert(result.end(), filtered_children.begin(), filtered_children.end());
+ }
+
+ return result;
+}
+
+void LLViewQuery::filterChildren(LLView* parent_view, viewList_t & filtered_children) const
+{
+ LLView::child_list_t views(*(parent_view->getChildList()));
+ if (mSorterp)
+ {
+ mSorterp->sort(parent_view, views); // sort the children per the sorter
+ }
+ for(LLView::child_list_iter_t iter = views.begin();
+ iter != views.end();
+ iter++)
+ {
+ viewList_t indiv_children = this->run(*iter);
+ filtered_children.splice(filtered_children.end(), indiv_children);
+ }
+}
+
+filterResult_t LLViewQuery::runFilters(LLView * view, const viewList_t children, const filterList_t filters) const
+{
+ filterResult_t result = filterResult_t(true, true);
+ for(filterList_const_iter_t iter = filters.begin();
+ iter != filters.end();
+ iter++)
+ {
+ filterResult_t filtered = (**iter)(view, children);
+ result.first = result.first && filtered.first;
+ result.second = result.second && filtered.second;
+ }
+ return result;
+}
diff --git a/indra/llui/llviewquery.h b/indra/llui/llviewquery.h
index c7d1ffac20..59c580c08d 100644
--- a/indra/llui/llviewquery.h
+++ b/indra/llui/llviewquery.h
@@ -1,137 +1,137 @@
-/**
- * @file llviewquery.h
- * @brief Query algorithm for flattening and filtering the view hierarchy.
- *
- * $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_LLVIEWQUERY_H
-#define LL_LLVIEWQUERY_H
-
-#include <list>
-
-#include "llsingleton.h"
-#include "llui.h"
-
-class LLView;
-
-typedef std::list<LLView *> viewList_t;
-typedef std::pair<bool, bool> filterResult_t;
-
-// Abstract base class for all query filters.
-class LLQueryFilter
-{
-public:
- virtual ~LLQueryFilter() {};
- virtual filterResult_t operator() (const LLView* const view, const viewList_t & children) const = 0;
-};
-
-class LLQuerySorter
-{
-public:
- virtual ~LLQuerySorter() {};
- virtual void sort(LLView * parent, viewList_t &children) const;
-};
-
-class LLLeavesFilter : public LLQueryFilter, public LLSingleton<LLLeavesFilter>
-{
- LLSINGLETON_EMPTY_CTOR(LLLeavesFilter);
- /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;
-};
-
-class LLRootsFilter : public LLQueryFilter, public LLSingleton<LLRootsFilter>
-{
- LLSINGLETON_EMPTY_CTOR(LLRootsFilter);
- /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;
-};
-
-class LLVisibleFilter : public LLQueryFilter, public LLSingleton<LLVisibleFilter>
-{
- LLSINGLETON_EMPTY_CTOR(LLVisibleFilter);
- /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;
-};
-
-class LLEnabledFilter : public LLQueryFilter, public LLSingleton<LLEnabledFilter>
-{
- LLSINGLETON_EMPTY_CTOR(LLEnabledFilter);
- /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;
-};
-
-class LLTabStopFilter : public LLQueryFilter, public LLSingleton<LLTabStopFilter>
-{
- LLSINGLETON_EMPTY_CTOR(LLTabStopFilter);
- /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;
-};
-
-class LLCtrlFilter : public LLQueryFilter, public LLSingleton<LLCtrlFilter>
-{
- LLSINGLETON_EMPTY_CTOR(LLCtrlFilter);
- /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;
-};
-
-template <class T>
-class LLWidgetTypeFilter : public LLQueryFilter
-{
- /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const
- {
- return filterResult_t(dynamic_cast<const T*>(view) != NULL, true);
- }
-
-};
-
-// Algorithm for flattening
-class LLViewQuery
-{
-public:
- typedef std::list<const LLQueryFilter*> filterList_t;
- typedef filterList_t::iterator filterList_iter_t;
- typedef filterList_t::const_iterator filterList_const_iter_t;
-
- LLViewQuery() : mPreFilters(), mPostFilters(), mSorterp() {}
- virtual ~LLViewQuery() {}
-
- void addPreFilter(const LLQueryFilter* prefilter) { mPreFilters.push_back(prefilter); }
- void addPostFilter(const LLQueryFilter* postfilter) { mPostFilters.push_back(postfilter); }
- const filterList_t & getPreFilters() const { return mPreFilters; }
- const filterList_t & getPostFilters() const { return mPostFilters; }
-
- void setSorter(const LLQuerySorter* sorter) { mSorterp = sorter; }
- const LLQuerySorter* getSorter() const { return mSorterp; }
-
- viewList_t run(LLView * view) const;
- // syntactic sugar
- viewList_t operator () (LLView * view) const { return run(view); }
-
- // override this method to provide iteration over other types of children
- virtual void filterChildren(LLView * view, viewList_t& filtered_children) const;
-
-private:
-
- filterResult_t runFilters(LLView * view, const viewList_t children, const filterList_t filters) const;
-
- filterList_t mPreFilters;
- filterList_t mPostFilters;
- const LLQuerySorter* mSorterp;
-};
-
-
-#endif // LL_LLVIEWQUERY_H
+/**
+ * @file llviewquery.h
+ * @brief Query algorithm for flattening and filtering the view hierarchy.
+ *
+ * $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_LLVIEWQUERY_H
+#define LL_LLVIEWQUERY_H
+
+#include <list>
+
+#include "llsingleton.h"
+#include "llui.h"
+
+class LLView;
+
+typedef std::list<LLView *> viewList_t;
+typedef std::pair<bool, bool> filterResult_t;
+
+// Abstract base class for all query filters.
+class LLQueryFilter
+{
+public:
+ virtual ~LLQueryFilter() {};
+ virtual filterResult_t operator() (const LLView* const view, const viewList_t & children) const = 0;
+};
+
+class LLQuerySorter
+{
+public:
+ virtual ~LLQuerySorter() {};
+ virtual void sort(LLView * parent, viewList_t &children) const;
+};
+
+class LLLeavesFilter : public LLQueryFilter, public LLSingleton<LLLeavesFilter>
+{
+ LLSINGLETON_EMPTY_CTOR(LLLeavesFilter);
+ /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;
+};
+
+class LLRootsFilter : public LLQueryFilter, public LLSingleton<LLRootsFilter>
+{
+ LLSINGLETON_EMPTY_CTOR(LLRootsFilter);
+ /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;
+};
+
+class LLVisibleFilter : public LLQueryFilter, public LLSingleton<LLVisibleFilter>
+{
+ LLSINGLETON_EMPTY_CTOR(LLVisibleFilter);
+ /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;
+};
+
+class LLEnabledFilter : public LLQueryFilter, public LLSingleton<LLEnabledFilter>
+{
+ LLSINGLETON_EMPTY_CTOR(LLEnabledFilter);
+ /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;
+};
+
+class LLTabStopFilter : public LLQueryFilter, public LLSingleton<LLTabStopFilter>
+{
+ LLSINGLETON_EMPTY_CTOR(LLTabStopFilter);
+ /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;
+};
+
+class LLCtrlFilter : public LLQueryFilter, public LLSingleton<LLCtrlFilter>
+{
+ LLSINGLETON_EMPTY_CTOR(LLCtrlFilter);
+ /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override;
+};
+
+template <class T>
+class LLWidgetTypeFilter : public LLQueryFilter
+{
+ /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const
+ {
+ return filterResult_t(dynamic_cast<const T*>(view) != NULL, true);
+ }
+
+};
+
+// Algorithm for flattening
+class LLViewQuery
+{
+public:
+ typedef std::list<const LLQueryFilter*> filterList_t;
+ typedef filterList_t::iterator filterList_iter_t;
+ typedef filterList_t::const_iterator filterList_const_iter_t;
+
+ LLViewQuery() : mPreFilters(), mPostFilters(), mSorterp() {}
+ virtual ~LLViewQuery() {}
+
+ void addPreFilter(const LLQueryFilter* prefilter) { mPreFilters.push_back(prefilter); }
+ void addPostFilter(const LLQueryFilter* postfilter) { mPostFilters.push_back(postfilter); }
+ const filterList_t & getPreFilters() const { return mPreFilters; }
+ const filterList_t & getPostFilters() const { return mPostFilters; }
+
+ void setSorter(const LLQuerySorter* sorter) { mSorterp = sorter; }
+ const LLQuerySorter* getSorter() const { return mSorterp; }
+
+ viewList_t run(LLView * view) const;
+ // syntactic sugar
+ viewList_t operator () (LLView * view) const { return run(view); }
+
+ // override this method to provide iteration over other types of children
+ virtual void filterChildren(LLView * view, viewList_t& filtered_children) const;
+
+private:
+
+ filterResult_t runFilters(LLView * view, const viewList_t children, const filterList_t filters) const;
+
+ filterList_t mPreFilters;
+ filterList_t mPostFilters;
+ const LLQuerySorter* mSorterp;
+};
+
+
+#endif // LL_LLVIEWQUERY_H
diff --git a/indra/llui/llvirtualtrackball.cpp b/indra/llui/llvirtualtrackball.cpp
index 78b35621b3..78651ff3cb 100644
--- a/indra/llui/llvirtualtrackball.cpp
+++ b/indra/llui/llvirtualtrackball.cpp
@@ -1,520 +1,520 @@
-/**
-* @file LLVirtualTrackball.cpp
-* @author Andrey Lihatskiy
-* @brief Implementation for LLVirtualTrackball
-*
-* $LicenseInfo:firstyear=2001&license=viewerlgpl$
-* Second Life Viewer Source Code
-* Copyright (C) 2018, 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 for positioning the sun and the moon in the celestial sphere.
-
-#include "linden_common.h"
-#include "llvirtualtrackball.h"
-#include "llstring.h"
-#include "llrect.h"
-#include "lluictrlfactory.h"
-#include "llrender.h"
-
-// Globals
-static LLDefaultChildRegistry::Register<LLVirtualTrackball> register_virtual_trackball("sun_moon_trackball");
-
-const LLVector3 VectorZero(1.0f, 0.0f, 0.0f);
-
-LLVirtualTrackball::Params::Params()
- : border("border"),
- image_moon_back("image_moon_back"),
- image_moon_front("image_moon_front"),
- image_sphere("image_sphere"),
- image_sun_back("image_sun_back"),
- image_sun_front("image_sun_front"),
- btn_rotate_top("button_rotate_top"),
- btn_rotate_bottom("button_rotate_bottom"),
- btn_rotate_left("button_rotate_left"),
- btn_rotate_right("button_rotate_right"),
- thumb_mode("thumb_mode"),
- lbl_N("labelN"),
- lbl_S("labelS"),
- lbl_W("labelW"),
- lbl_E("labelE"),
- increment_angle_mouse("increment_angle_mouse", 0.5f),
- increment_angle_btn("increment_angle_btn", 3.0f)
-{
-}
-
-LLVirtualTrackball::LLVirtualTrackball(const LLVirtualTrackball::Params& p)
- : LLUICtrl(p),
- mImgMoonBack(p.image_moon_back),
- mImgMoonFront(p.image_moon_front),
- mImgSunBack(p.image_sun_back),
- mImgSunFront(p.image_sun_front),
- mImgSphere(p.image_sphere),
- mThumbMode(p.thumb_mode() == "moon" ? ThumbMode::MOON : ThumbMode::SUN),
- mIncrementMouse(DEG_TO_RAD * p.increment_angle_mouse()),
- mIncrementBtn(DEG_TO_RAD * p.increment_angle_btn())
-{
- LLRect border_rect = getLocalRect();
- S32 centerX = border_rect.getCenterX();
- S32 centerY = border_rect.getCenterY();
- U32 btn_size = 32; // width & height
- U32 axis_offset_lt = 16; // offset from the axis for left/top sides
- U32 axis_offset_rb = btn_size - axis_offset_lt; // and for right/bottom
-
- LLViewBorder::Params border = p.border;
- border.rect(border_rect);
- mBorder = LLUICtrlFactory::create<LLViewBorder>(border);
- addChild(mBorder);
-
-
- LLButton::Params btn_rt = p.btn_rotate_top;
- btn_rt.rect(LLRect(centerX - axis_offset_lt, border_rect.mTop, centerX + axis_offset_rb, border_rect.mTop - btn_size));
- btn_rt.click_callback.function(boost::bind(&LLVirtualTrackball::onRotateTopClick, this));
- btn_rt.mouse_held_callback.function(boost::bind(&LLVirtualTrackball::onRotateTopClick, this));
- btn_rt.mouseenter_callback.function(boost::bind(&LLVirtualTrackball::onRotateTopMouseEnter, this));
- mBtnRotateTop = LLUICtrlFactory::create<LLButton>(btn_rt);
- addChild(mBtnRotateTop);
-
- LLTextBox::Params lbl_N = p.lbl_N;
- LLRect rect_N = btn_rt.rect;
- //rect_N.translate(btn_rt.rect().getWidth(), 0);
- lbl_N.rect = rect_N;
- lbl_N.initial_value(lbl_N.label());
- mLabelN = LLUICtrlFactory::create<LLTextBox>(lbl_N);
- addChild(mLabelN);
-
-
- LLButton::Params btn_rr = p.btn_rotate_right;
- btn_rr.rect(LLRect(border_rect.mRight - btn_size, centerY + axis_offset_lt, border_rect.mRight, centerY - axis_offset_rb));
- btn_rr.click_callback.function(boost::bind(&LLVirtualTrackball::onRotateRightClick, this));
- btn_rr.mouse_held_callback.function(boost::bind(&LLVirtualTrackball::onRotateRightClick, this));
- btn_rr.mouseenter_callback.function(boost::bind(&LLVirtualTrackball::onRotateRightMouseEnter, this));
- mBtnRotateRight = LLUICtrlFactory::create<LLButton>(btn_rr);
- addChild(mBtnRotateRight);
-
- LLTextBox::Params lbl_E = p.lbl_E;
- LLRect rect_E = btn_rr.rect;
- //rect_E.translate(0, -1 * btn_rr.rect().getHeight());
- lbl_E.rect = rect_E;
- lbl_E.initial_value(lbl_E.label());
- mLabelE = LLUICtrlFactory::create<LLTextBox>(lbl_E);
- addChild(mLabelE);
-
-
- LLButton::Params btn_rb = p.btn_rotate_bottom;
- btn_rb.rect(LLRect(centerX - axis_offset_lt, border_rect.mBottom + btn_size, centerX + axis_offset_rb, border_rect.mBottom));
- btn_rb.click_callback.function(boost::bind(&LLVirtualTrackball::onRotateBottomClick, this));
- btn_rb.mouse_held_callback.function(boost::bind(&LLVirtualTrackball::onRotateBottomClick, this));
- btn_rb.mouseenter_callback.function(boost::bind(&LLVirtualTrackball::onRotateBottomMouseEnter, this));
- mBtnRotateBottom = LLUICtrlFactory::create<LLButton>(btn_rb);
- addChild(mBtnRotateBottom);
-
- LLTextBox::Params lbl_S = p.lbl_S;
- LLRect rect_S = btn_rb.rect;
- //rect_S.translate(btn_rb.rect().getWidth(), 0);
- lbl_S.rect = rect_S;
- lbl_S.initial_value(lbl_S.label());
- mLabelS = LLUICtrlFactory::create<LLTextBox>(lbl_S);
- addChild(mLabelS);
-
-
- LLButton::Params btn_rl = p.btn_rotate_left;
- btn_rl.rect(LLRect(border_rect.mLeft, centerY + axis_offset_lt, border_rect.mLeft + btn_size, centerY - axis_offset_rb));
- btn_rl.click_callback.function(boost::bind(&LLVirtualTrackball::onRotateLeftClick, this));
- btn_rl.mouse_held_callback.function(boost::bind(&LLVirtualTrackball::onRotateLeftClick, this));
- btn_rl.mouseenter_callback.function(boost::bind(&LLVirtualTrackball::onRotateLeftMouseEnter, this));
- mBtnRotateLeft = LLUICtrlFactory::create<LLButton>(btn_rl);
- addChild(mBtnRotateLeft);
-
- LLTextBox::Params lbl_W = p.lbl_W;
- LLRect rect_W = btn_rl.rect;
- //rect_W.translate(0, -1* btn_rl.rect().getHeight());
- lbl_W.rect = rect_W;
- lbl_W.initial_value(lbl_W.label());
- mLabelW = LLUICtrlFactory::create<LLTextBox>(lbl_W);
- addChild(mLabelW);
-
-
- LLPanel::Params touch_area;
- touch_area.rect = LLRect(centerX - mImgSphere->getWidth() / 2,
- centerY + mImgSphere->getHeight() / 2,
- centerX + mImgSphere->getWidth() / 2,
- centerY - mImgSphere->getHeight() / 2);
- mTouchArea = LLUICtrlFactory::create<LLPanel>(touch_area);
- addChild(mTouchArea);
-}
-
-LLVirtualTrackball::~LLVirtualTrackball()
-{
-}
-
-bool LLVirtualTrackball::postBuild()
-{
- return true;
-}
-
-
-void LLVirtualTrackball::drawThumb(S32 x, S32 y, ThumbMode mode, bool upperHemi)
-{
- LLUIImage* thumb;
- if (mode == ThumbMode::SUN)
- {
- if (upperHemi)
- {
- thumb = mImgSunFront;
- }
- else
- {
- thumb = mImgSunBack;
- }
- }
- else
- {
- if (upperHemi)
- {
- thumb = mImgMoonFront;
- }
- else
- {
- thumb = mImgMoonBack;
- }
- }
- thumb->draw(LLRect(x - thumb->getWidth() / 2,
- y + thumb->getHeight() / 2,
- x + thumb->getWidth() / 2,
- y - thumb->getHeight() / 2));
-}
-
-bool LLVirtualTrackball::pointInTouchCircle(S32 x, S32 y) const
-{
- S32 centerX = mTouchArea->getRect().getCenterX();
- S32 centerY = mTouchArea->getRect().getCenterY();
-
- bool in_circle = pow(x - centerX, 2) + pow(y - centerY, 2) <= pow(mTouchArea->getRect().getWidth() / 2, 2);
- return in_circle;
-}
-
-void LLVirtualTrackball::draw()
-{
- LLVector3 draw_point = VectorZero * mValue;
-
- S32 halfwidth = mTouchArea->getRect().getWidth() / 2;
- S32 halfheight = mTouchArea->getRect().getHeight() / 2;
- draw_point.mV[VX] = (draw_point.mV[VX] + 1.0) * halfwidth + mTouchArea->getRect().mLeft;
- draw_point.mV[VY] = (draw_point.mV[VY] + 1.0) * halfheight + mTouchArea->getRect().mBottom;
- bool upper_hemisphere = (draw_point.mV[VZ] >= 0.f);
-
- mImgSphere->draw(mTouchArea->getRect(), upper_hemisphere ? UI_VERTEX_COLOR : UI_VERTEX_COLOR % 0.5f);
- drawThumb(draw_point.mV[VX], draw_point.mV[VY], mThumbMode, upper_hemisphere);
-
-
- if (LLView::sDebugRects)
- {
- gGL.color4fv(LLColor4::red.mV);
- gl_circle_2d(mTouchArea->getRect().getCenterX(), mTouchArea->getRect().getCenterY(), mImgSphere->getWidth() / 2, 60, false);
- gl_circle_2d(draw_point.mV[VX], draw_point.mV[VY], mImgSunFront->getWidth() / 2, 12, false);
- }
-
- // hide the direction labels when disabled
- bool enabled = isInEnabledChain();
- mLabelN->setVisible(enabled);
- mLabelE->setVisible(enabled);
- mLabelS->setVisible(enabled);
- mLabelW->setVisible(enabled);
-
- LLView::draw();
-}
-
-void LLVirtualTrackball::onRotateTopClick()
-{
- if (getEnabled())
- {
- LLQuaternion delta;
- delta.setAngleAxis(mIncrementBtn, 1, 0, 0);
- mValue *= delta;
- setValueAndCommit(mValue);
-
- make_ui_sound("UISndClick");
- }
-}
-
-void LLVirtualTrackball::onRotateBottomClick()
-{
- if (getEnabled())
- {
- LLQuaternion delta;
- delta.setAngleAxis(mIncrementBtn, -1, 0, 0);
- mValue *= delta;
- setValueAndCommit(mValue);
-
- make_ui_sound("UISndClick");
- }
-}
-
-void LLVirtualTrackball::onRotateLeftClick()
-{
- if (getEnabled())
- {
- LLQuaternion delta;
- delta.setAngleAxis(mIncrementBtn, 0, 1, 0);
- mValue *= delta;
- setValueAndCommit(mValue);
-
- make_ui_sound("UISndClick");
- }
-}
-
-void LLVirtualTrackball::onRotateRightClick()
-{
- if (getEnabled())
- {
- LLQuaternion delta;
- delta.setAngleAxis(mIncrementBtn, 0, -1, 0);
- mValue *= delta;
- setValueAndCommit(mValue);
-
- make_ui_sound("UISndClick");
- }
-}
-
-void LLVirtualTrackball::onRotateTopMouseEnter()
-{
- mBtnRotateTop->setHighlight(true);
-}
-
-void LLVirtualTrackball::onRotateBottomMouseEnter()
-{
- mBtnRotateBottom->setHighlight(true);
-}
-
-void LLVirtualTrackball::onRotateLeftMouseEnter()
-{
- mBtnRotateLeft->setHighlight(true);
-}
-
-void LLVirtualTrackball::onRotateRightMouseEnter()
-{
- mBtnRotateRight->setHighlight(true);
-}
-
-void LLVirtualTrackball::setValue(const LLSD& value)
-{
- if (value.isArray() && value.size() == 4)
- {
- mValue.setValue(value);
- }
-}
-
-void LLVirtualTrackball::setRotation(const LLQuaternion &value)
-{
- mValue = value;
-}
-
-void LLVirtualTrackball::setValue(F32 x, F32 y, F32 z, F32 w)
-{
- mValue.set(x, y, z, w);
-}
-
-void LLVirtualTrackball::setValueAndCommit(const LLQuaternion &value)
-{
- mValue = value;
- onCommit();
-}
-
-LLSD LLVirtualTrackball::getValue() const
-{
- return mValue.getValue();
-}
-
-LLQuaternion LLVirtualTrackball::getRotation() const
-{
- return mValue;
-}
-
-// static
-void LLVirtualTrackball::getAzimuthAndElevation(const LLQuaternion &quat, F32 &azimuth, F32 &elevation)
-{
- // LLQuaternion has own function to get azimuth, but it doesn't appear to return correct values (meant for 2d?)
- LLVector3 point = VectorZero * quat;
-
- if (!is_approx_zero(point.mV[VX]) || !is_approx_zero(point.mV[VY]))
- {
- azimuth = atan2f(point.mV[VX], point.mV[VY]);
- }
- else
- {
- azimuth = 0;
- }
-
- azimuth -= F_PI_BY_TWO;
-
- if (azimuth < 0)
- {
- azimuth += F_PI * 2;
- }
-
- // while vector is '1', F32 is not sufficiently precise and we can get
- // values like 1.0000012 which will result in -90deg angle instead of 90deg
- F32 z = llclamp(point.mV[VZ], -1.f, 1.f);
- elevation = asin(z); // because VectorZero's length is '1'
-}
-
-// static
-void LLVirtualTrackball::getAzimuthAndElevationDeg(const LLQuaternion &quat, F32 &azimuth, F32 &elevation)
-{
- getAzimuthAndElevation(quat, azimuth, elevation);
- azimuth *= RAD_TO_DEG;
- elevation *= RAD_TO_DEG;
-}
-
-bool LLVirtualTrackball::handleHover(S32 x, S32 y, MASK mask)
-{
- if (hasMouseCapture())
- {
- if (mDragMode == DRAG_SCROLL)
- { // trackball (move to roll) mode
- LLQuaternion delta;
-
- F32 rotX = x - mPrevX;
- F32 rotY = y - mPrevY;
-
- if (abs(rotX) > 1)
- {
- F32 direction = (rotX < 0) ? -1 : 1;
- delta.setAngleAxis(mIncrementMouse * abs(rotX), 0, direction, 0); // changing X - rotate around Y axis
- mValue *= delta;
- }
-
- if (abs(rotY) > 1)
- {
- F32 direction = (rotY < 0) ? 1 : -1; // reverse for Y (value increases from bottom to top)
- delta.setAngleAxis(mIncrementMouse * abs(rotY), direction, 0, 0); // changing Y - rotate around X axis
- mValue *= delta;
- }
- }
- else
- { // set on click mode
- if (!pointInTouchCircle(x, y))
- {
- return true; // don't drag outside the circle
- }
-
- F32 radius = mTouchArea->getRect().getWidth() / 2;
- F32 xx = x - mTouchArea->getRect().getCenterX();
- F32 yy = y - mTouchArea->getRect().getCenterY();
- F32 dist = sqrt(pow(xx, 2) + pow(yy, 2));
-
- F32 azimuth = llclamp(acosf(xx / dist), 0.0f, F_PI);
- F32 altitude = llclamp(acosf(dist / radius), 0.0f, F_PI_BY_TWO);
-
- if (yy < 0)
- {
- azimuth = F_TWO_PI - azimuth;
- }
-
- LLVector3 draw_point = VectorZero * mValue;
- if (draw_point.mV[VZ] >= 0.f)
- {
- if (is_approx_zero(altitude)) // don't change the hemisphere
- {
- altitude = F_APPROXIMATELY_ZERO;
- }
- altitude *= -1;
- }
-
- mValue.setAngleAxis(altitude, 0, 1, 0);
- LLQuaternion az_quat;
- az_quat.setAngleAxis(azimuth, 0, 0, 1);
- mValue *= az_quat;
- }
-
- // we are doing a lot of F32 mathematical operations with loss of precision,
- // re-normalize to compensate
- mValue.normalize();
-
- mPrevX = x;
- mPrevY = y;
- onCommit();
- }
- return true;
-}
-
-bool LLVirtualTrackball::handleMouseUp(S32 x, S32 y, MASK mask)
-{
- if (hasMouseCapture())
- {
- mPrevX = 0;
- mPrevY = 0;
- gFocusMgr.setMouseCapture(NULL);
- make_ui_sound("UISndClickRelease");
- }
- return LLView::handleMouseUp(x, y, mask);
-}
-
-bool LLVirtualTrackball::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- if (pointInTouchCircle(x, y))
- {
- mPrevX = x;
- mPrevY = y;
- gFocusMgr.setMouseCapture(this);
- mDragMode = (mask == MASK_CONTROL) ? DRAG_SCROLL : DRAG_SET;
- make_ui_sound("UISndClick");
- }
- return LLView::handleMouseDown(x, y, mask);
-}
-
-bool LLVirtualTrackball::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- if (pointInTouchCircle(x, y))
- {
-
- //make_ui_sound("UISndClick");
- }
- return LLView::handleRightMouseDown(x, y, mask);
-}
-
-bool LLVirtualTrackball::handleKeyHere(KEY key, MASK mask)
-{
- bool handled = false;
- switch (key)
- {
- case KEY_DOWN:
- onRotateTopClick();
- handled = true;
- break;
- case KEY_LEFT:
- onRotateRightClick();
- handled = true;
- break;
- case KEY_UP:
- onRotateBottomClick();
- handled = true;
- break;
- case KEY_RIGHT:
- onRotateLeftClick();
- handled = true;
- break;
- default:
- break;
- }
- return handled;
-}
-
+/**
+* @file LLVirtualTrackball.cpp
+* @author Andrey Lihatskiy
+* @brief Implementation for LLVirtualTrackball
+*
+* $LicenseInfo:firstyear=2001&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2018, 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 for positioning the sun and the moon in the celestial sphere.
+
+#include "linden_common.h"
+#include "llvirtualtrackball.h"
+#include "llstring.h"
+#include "llrect.h"
+#include "lluictrlfactory.h"
+#include "llrender.h"
+
+// Globals
+static LLDefaultChildRegistry::Register<LLVirtualTrackball> register_virtual_trackball("sun_moon_trackball");
+
+const LLVector3 VectorZero(1.0f, 0.0f, 0.0f);
+
+LLVirtualTrackball::Params::Params()
+ : border("border"),
+ image_moon_back("image_moon_back"),
+ image_moon_front("image_moon_front"),
+ image_sphere("image_sphere"),
+ image_sun_back("image_sun_back"),
+ image_sun_front("image_sun_front"),
+ btn_rotate_top("button_rotate_top"),
+ btn_rotate_bottom("button_rotate_bottom"),
+ btn_rotate_left("button_rotate_left"),
+ btn_rotate_right("button_rotate_right"),
+ thumb_mode("thumb_mode"),
+ lbl_N("labelN"),
+ lbl_S("labelS"),
+ lbl_W("labelW"),
+ lbl_E("labelE"),
+ increment_angle_mouse("increment_angle_mouse", 0.5f),
+ increment_angle_btn("increment_angle_btn", 3.0f)
+{
+}
+
+LLVirtualTrackball::LLVirtualTrackball(const LLVirtualTrackball::Params& p)
+ : LLUICtrl(p),
+ mImgMoonBack(p.image_moon_back),
+ mImgMoonFront(p.image_moon_front),
+ mImgSunBack(p.image_sun_back),
+ mImgSunFront(p.image_sun_front),
+ mImgSphere(p.image_sphere),
+ mThumbMode(p.thumb_mode() == "moon" ? ThumbMode::MOON : ThumbMode::SUN),
+ mIncrementMouse(DEG_TO_RAD * p.increment_angle_mouse()),
+ mIncrementBtn(DEG_TO_RAD * p.increment_angle_btn())
+{
+ LLRect border_rect = getLocalRect();
+ S32 centerX = border_rect.getCenterX();
+ S32 centerY = border_rect.getCenterY();
+ U32 btn_size = 32; // width & height
+ U32 axis_offset_lt = 16; // offset from the axis for left/top sides
+ U32 axis_offset_rb = btn_size - axis_offset_lt; // and for right/bottom
+
+ LLViewBorder::Params border = p.border;
+ border.rect(border_rect);
+ mBorder = LLUICtrlFactory::create<LLViewBorder>(border);
+ addChild(mBorder);
+
+
+ LLButton::Params btn_rt = p.btn_rotate_top;
+ btn_rt.rect(LLRect(centerX - axis_offset_lt, border_rect.mTop, centerX + axis_offset_rb, border_rect.mTop - btn_size));
+ btn_rt.click_callback.function(boost::bind(&LLVirtualTrackball::onRotateTopClick, this));
+ btn_rt.mouse_held_callback.function(boost::bind(&LLVirtualTrackball::onRotateTopClick, this));
+ btn_rt.mouseenter_callback.function(boost::bind(&LLVirtualTrackball::onRotateTopMouseEnter, this));
+ mBtnRotateTop = LLUICtrlFactory::create<LLButton>(btn_rt);
+ addChild(mBtnRotateTop);
+
+ LLTextBox::Params lbl_N = p.lbl_N;
+ LLRect rect_N = btn_rt.rect;
+ //rect_N.translate(btn_rt.rect().getWidth(), 0);
+ lbl_N.rect = rect_N;
+ lbl_N.initial_value(lbl_N.label());
+ mLabelN = LLUICtrlFactory::create<LLTextBox>(lbl_N);
+ addChild(mLabelN);
+
+
+ LLButton::Params btn_rr = p.btn_rotate_right;
+ btn_rr.rect(LLRect(border_rect.mRight - btn_size, centerY + axis_offset_lt, border_rect.mRight, centerY - axis_offset_rb));
+ btn_rr.click_callback.function(boost::bind(&LLVirtualTrackball::onRotateRightClick, this));
+ btn_rr.mouse_held_callback.function(boost::bind(&LLVirtualTrackball::onRotateRightClick, this));
+ btn_rr.mouseenter_callback.function(boost::bind(&LLVirtualTrackball::onRotateRightMouseEnter, this));
+ mBtnRotateRight = LLUICtrlFactory::create<LLButton>(btn_rr);
+ addChild(mBtnRotateRight);
+
+ LLTextBox::Params lbl_E = p.lbl_E;
+ LLRect rect_E = btn_rr.rect;
+ //rect_E.translate(0, -1 * btn_rr.rect().getHeight());
+ lbl_E.rect = rect_E;
+ lbl_E.initial_value(lbl_E.label());
+ mLabelE = LLUICtrlFactory::create<LLTextBox>(lbl_E);
+ addChild(mLabelE);
+
+
+ LLButton::Params btn_rb = p.btn_rotate_bottom;
+ btn_rb.rect(LLRect(centerX - axis_offset_lt, border_rect.mBottom + btn_size, centerX + axis_offset_rb, border_rect.mBottom));
+ btn_rb.click_callback.function(boost::bind(&LLVirtualTrackball::onRotateBottomClick, this));
+ btn_rb.mouse_held_callback.function(boost::bind(&LLVirtualTrackball::onRotateBottomClick, this));
+ btn_rb.mouseenter_callback.function(boost::bind(&LLVirtualTrackball::onRotateBottomMouseEnter, this));
+ mBtnRotateBottom = LLUICtrlFactory::create<LLButton>(btn_rb);
+ addChild(mBtnRotateBottom);
+
+ LLTextBox::Params lbl_S = p.lbl_S;
+ LLRect rect_S = btn_rb.rect;
+ //rect_S.translate(btn_rb.rect().getWidth(), 0);
+ lbl_S.rect = rect_S;
+ lbl_S.initial_value(lbl_S.label());
+ mLabelS = LLUICtrlFactory::create<LLTextBox>(lbl_S);
+ addChild(mLabelS);
+
+
+ LLButton::Params btn_rl = p.btn_rotate_left;
+ btn_rl.rect(LLRect(border_rect.mLeft, centerY + axis_offset_lt, border_rect.mLeft + btn_size, centerY - axis_offset_rb));
+ btn_rl.click_callback.function(boost::bind(&LLVirtualTrackball::onRotateLeftClick, this));
+ btn_rl.mouse_held_callback.function(boost::bind(&LLVirtualTrackball::onRotateLeftClick, this));
+ btn_rl.mouseenter_callback.function(boost::bind(&LLVirtualTrackball::onRotateLeftMouseEnter, this));
+ mBtnRotateLeft = LLUICtrlFactory::create<LLButton>(btn_rl);
+ addChild(mBtnRotateLeft);
+
+ LLTextBox::Params lbl_W = p.lbl_W;
+ LLRect rect_W = btn_rl.rect;
+ //rect_W.translate(0, -1* btn_rl.rect().getHeight());
+ lbl_W.rect = rect_W;
+ lbl_W.initial_value(lbl_W.label());
+ mLabelW = LLUICtrlFactory::create<LLTextBox>(lbl_W);
+ addChild(mLabelW);
+
+
+ LLPanel::Params touch_area;
+ touch_area.rect = LLRect(centerX - mImgSphere->getWidth() / 2,
+ centerY + mImgSphere->getHeight() / 2,
+ centerX + mImgSphere->getWidth() / 2,
+ centerY - mImgSphere->getHeight() / 2);
+ mTouchArea = LLUICtrlFactory::create<LLPanel>(touch_area);
+ addChild(mTouchArea);
+}
+
+LLVirtualTrackball::~LLVirtualTrackball()
+{
+}
+
+bool LLVirtualTrackball::postBuild()
+{
+ return true;
+}
+
+
+void LLVirtualTrackball::drawThumb(S32 x, S32 y, ThumbMode mode, bool upperHemi)
+{
+ LLUIImage* thumb;
+ if (mode == ThumbMode::SUN)
+ {
+ if (upperHemi)
+ {
+ thumb = mImgSunFront;
+ }
+ else
+ {
+ thumb = mImgSunBack;
+ }
+ }
+ else
+ {
+ if (upperHemi)
+ {
+ thumb = mImgMoonFront;
+ }
+ else
+ {
+ thumb = mImgMoonBack;
+ }
+ }
+ thumb->draw(LLRect(x - thumb->getWidth() / 2,
+ y + thumb->getHeight() / 2,
+ x + thumb->getWidth() / 2,
+ y - thumb->getHeight() / 2));
+}
+
+bool LLVirtualTrackball::pointInTouchCircle(S32 x, S32 y) const
+{
+ S32 centerX = mTouchArea->getRect().getCenterX();
+ S32 centerY = mTouchArea->getRect().getCenterY();
+
+ bool in_circle = pow(x - centerX, 2) + pow(y - centerY, 2) <= pow(mTouchArea->getRect().getWidth() / 2, 2);
+ return in_circle;
+}
+
+void LLVirtualTrackball::draw()
+{
+ LLVector3 draw_point = VectorZero * mValue;
+
+ S32 halfwidth = mTouchArea->getRect().getWidth() / 2;
+ S32 halfheight = mTouchArea->getRect().getHeight() / 2;
+ draw_point.mV[VX] = (draw_point.mV[VX] + 1.0) * halfwidth + mTouchArea->getRect().mLeft;
+ draw_point.mV[VY] = (draw_point.mV[VY] + 1.0) * halfheight + mTouchArea->getRect().mBottom;
+ bool upper_hemisphere = (draw_point.mV[VZ] >= 0.f);
+
+ mImgSphere->draw(mTouchArea->getRect(), upper_hemisphere ? UI_VERTEX_COLOR : UI_VERTEX_COLOR % 0.5f);
+ drawThumb(draw_point.mV[VX], draw_point.mV[VY], mThumbMode, upper_hemisphere);
+
+
+ if (LLView::sDebugRects)
+ {
+ gGL.color4fv(LLColor4::red.mV);
+ gl_circle_2d(mTouchArea->getRect().getCenterX(), mTouchArea->getRect().getCenterY(), mImgSphere->getWidth() / 2, 60, false);
+ gl_circle_2d(draw_point.mV[VX], draw_point.mV[VY], mImgSunFront->getWidth() / 2, 12, false);
+ }
+
+ // hide the direction labels when disabled
+ bool enabled = isInEnabledChain();
+ mLabelN->setVisible(enabled);
+ mLabelE->setVisible(enabled);
+ mLabelS->setVisible(enabled);
+ mLabelW->setVisible(enabled);
+
+ LLView::draw();
+}
+
+void LLVirtualTrackball::onRotateTopClick()
+{
+ if (getEnabled())
+ {
+ LLQuaternion delta;
+ delta.setAngleAxis(mIncrementBtn, 1, 0, 0);
+ mValue *= delta;
+ setValueAndCommit(mValue);
+
+ make_ui_sound("UISndClick");
+ }
+}
+
+void LLVirtualTrackball::onRotateBottomClick()
+{
+ if (getEnabled())
+ {
+ LLQuaternion delta;
+ delta.setAngleAxis(mIncrementBtn, -1, 0, 0);
+ mValue *= delta;
+ setValueAndCommit(mValue);
+
+ make_ui_sound("UISndClick");
+ }
+}
+
+void LLVirtualTrackball::onRotateLeftClick()
+{
+ if (getEnabled())
+ {
+ LLQuaternion delta;
+ delta.setAngleAxis(mIncrementBtn, 0, 1, 0);
+ mValue *= delta;
+ setValueAndCommit(mValue);
+
+ make_ui_sound("UISndClick");
+ }
+}
+
+void LLVirtualTrackball::onRotateRightClick()
+{
+ if (getEnabled())
+ {
+ LLQuaternion delta;
+ delta.setAngleAxis(mIncrementBtn, 0, -1, 0);
+ mValue *= delta;
+ setValueAndCommit(mValue);
+
+ make_ui_sound("UISndClick");
+ }
+}
+
+void LLVirtualTrackball::onRotateTopMouseEnter()
+{
+ mBtnRotateTop->setHighlight(true);
+}
+
+void LLVirtualTrackball::onRotateBottomMouseEnter()
+{
+ mBtnRotateBottom->setHighlight(true);
+}
+
+void LLVirtualTrackball::onRotateLeftMouseEnter()
+{
+ mBtnRotateLeft->setHighlight(true);
+}
+
+void LLVirtualTrackball::onRotateRightMouseEnter()
+{
+ mBtnRotateRight->setHighlight(true);
+}
+
+void LLVirtualTrackball::setValue(const LLSD& value)
+{
+ if (value.isArray() && value.size() == 4)
+ {
+ mValue.setValue(value);
+ }
+}
+
+void LLVirtualTrackball::setRotation(const LLQuaternion &value)
+{
+ mValue = value;
+}
+
+void LLVirtualTrackball::setValue(F32 x, F32 y, F32 z, F32 w)
+{
+ mValue.set(x, y, z, w);
+}
+
+void LLVirtualTrackball::setValueAndCommit(const LLQuaternion &value)
+{
+ mValue = value;
+ onCommit();
+}
+
+LLSD LLVirtualTrackball::getValue() const
+{
+ return mValue.getValue();
+}
+
+LLQuaternion LLVirtualTrackball::getRotation() const
+{
+ return mValue;
+}
+
+// static
+void LLVirtualTrackball::getAzimuthAndElevation(const LLQuaternion &quat, F32 &azimuth, F32 &elevation)
+{
+ // LLQuaternion has own function to get azimuth, but it doesn't appear to return correct values (meant for 2d?)
+ LLVector3 point = VectorZero * quat;
+
+ if (!is_approx_zero(point.mV[VX]) || !is_approx_zero(point.mV[VY]))
+ {
+ azimuth = atan2f(point.mV[VX], point.mV[VY]);
+ }
+ else
+ {
+ azimuth = 0;
+ }
+
+ azimuth -= F_PI_BY_TWO;
+
+ if (azimuth < 0)
+ {
+ azimuth += F_PI * 2;
+ }
+
+ // while vector is '1', F32 is not sufficiently precise and we can get
+ // values like 1.0000012 which will result in -90deg angle instead of 90deg
+ F32 z = llclamp(point.mV[VZ], -1.f, 1.f);
+ elevation = asin(z); // because VectorZero's length is '1'
+}
+
+// static
+void LLVirtualTrackball::getAzimuthAndElevationDeg(const LLQuaternion &quat, F32 &azimuth, F32 &elevation)
+{
+ getAzimuthAndElevation(quat, azimuth, elevation);
+ azimuth *= RAD_TO_DEG;
+ elevation *= RAD_TO_DEG;
+}
+
+bool LLVirtualTrackball::handleHover(S32 x, S32 y, MASK mask)
+{
+ if (hasMouseCapture())
+ {
+ if (mDragMode == DRAG_SCROLL)
+ { // trackball (move to roll) mode
+ LLQuaternion delta;
+
+ F32 rotX = x - mPrevX;
+ F32 rotY = y - mPrevY;
+
+ if (abs(rotX) > 1)
+ {
+ F32 direction = (rotX < 0) ? -1 : 1;
+ delta.setAngleAxis(mIncrementMouse * abs(rotX), 0, direction, 0); // changing X - rotate around Y axis
+ mValue *= delta;
+ }
+
+ if (abs(rotY) > 1)
+ {
+ F32 direction = (rotY < 0) ? 1 : -1; // reverse for Y (value increases from bottom to top)
+ delta.setAngleAxis(mIncrementMouse * abs(rotY), direction, 0, 0); // changing Y - rotate around X axis
+ mValue *= delta;
+ }
+ }
+ else
+ { // set on click mode
+ if (!pointInTouchCircle(x, y))
+ {
+ return true; // don't drag outside the circle
+ }
+
+ F32 radius = mTouchArea->getRect().getWidth() / 2;
+ F32 xx = x - mTouchArea->getRect().getCenterX();
+ F32 yy = y - mTouchArea->getRect().getCenterY();
+ F32 dist = sqrt(pow(xx, 2) + pow(yy, 2));
+
+ F32 azimuth = llclamp(acosf(xx / dist), 0.0f, F_PI);
+ F32 altitude = llclamp(acosf(dist / radius), 0.0f, F_PI_BY_TWO);
+
+ if (yy < 0)
+ {
+ azimuth = F_TWO_PI - azimuth;
+ }
+
+ LLVector3 draw_point = VectorZero * mValue;
+ if (draw_point.mV[VZ] >= 0.f)
+ {
+ if (is_approx_zero(altitude)) // don't change the hemisphere
+ {
+ altitude = F_APPROXIMATELY_ZERO;
+ }
+ altitude *= -1;
+ }
+
+ mValue.setAngleAxis(altitude, 0, 1, 0);
+ LLQuaternion az_quat;
+ az_quat.setAngleAxis(azimuth, 0, 0, 1);
+ mValue *= az_quat;
+ }
+
+ // we are doing a lot of F32 mathematical operations with loss of precision,
+ // re-normalize to compensate
+ mValue.normalize();
+
+ mPrevX = x;
+ mPrevY = y;
+ onCommit();
+ }
+ return true;
+}
+
+bool LLVirtualTrackball::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ if (hasMouseCapture())
+ {
+ mPrevX = 0;
+ mPrevY = 0;
+ gFocusMgr.setMouseCapture(NULL);
+ make_ui_sound("UISndClickRelease");
+ }
+ return LLView::handleMouseUp(x, y, mask);
+}
+
+bool LLVirtualTrackball::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (pointInTouchCircle(x, y))
+ {
+ mPrevX = x;
+ mPrevY = y;
+ gFocusMgr.setMouseCapture(this);
+ mDragMode = (mask == MASK_CONTROL) ? DRAG_SCROLL : DRAG_SET;
+ make_ui_sound("UISndClick");
+ }
+ return LLView::handleMouseDown(x, y, mask);
+}
+
+bool LLVirtualTrackball::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (pointInTouchCircle(x, y))
+ {
+
+ //make_ui_sound("UISndClick");
+ }
+ return LLView::handleRightMouseDown(x, y, mask);
+}
+
+bool LLVirtualTrackball::handleKeyHere(KEY key, MASK mask)
+{
+ bool handled = false;
+ switch (key)
+ {
+ case KEY_DOWN:
+ onRotateTopClick();
+ handled = true;
+ break;
+ case KEY_LEFT:
+ onRotateRightClick();
+ handled = true;
+ break;
+ case KEY_UP:
+ onRotateBottomClick();
+ handled = true;
+ break;
+ case KEY_RIGHT:
+ onRotateLeftClick();
+ handled = true;
+ break;
+ default:
+ break;
+ }
+ return handled;
+}
+
diff --git a/indra/llui/llvirtualtrackball.h b/indra/llui/llvirtualtrackball.h
index ebf7c8ba7b..54ed6082ea 100644
--- a/indra/llui/llvirtualtrackball.h
+++ b/indra/llui/llvirtualtrackball.h
@@ -1,163 +1,163 @@
-/**
-* @file virtualtrackball.h
-* @author Andrey Lihatskiy
-* @brief Header file for LLVirtualTrackball
-*
-* $LicenseInfo:firstyear=2001&license=viewerlgpl$
-* Second Life Viewer Source Code
-* Copyright (C) 2018, 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 for positioning the sun and the moon in the celestial sphere.
-
-#ifndef LL_LLVIRTUALTRACKBALL_H
-#define LL_LLVIRTUALTRACKBALL_H
-
-#include "lluictrl.h"
-#include "llpanel.h"
-#include "lltextbox.h"
-#include "llbutton.h"
-
-class LLVirtualTrackball
- : public LLUICtrl
-{
-public:
- enum ThumbMode
- {
- SUN,
- MOON
- };
- enum DragMode
- {
- DRAG_SET,
- DRAG_SCROLL
- };
-
- struct Params
- : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Optional<LLViewBorder::Params> border;
- Optional<LLUIImage*> image_moon_back,
- image_moon_front,
- image_sphere,
- image_sun_back,
- image_sun_front;
-
- Optional<std::string> thumb_mode;
- Optional<F32> increment_angle_mouse,
- increment_angle_btn;
-
- Optional<LLTextBox::Params> lbl_N,
- lbl_S,
- lbl_W,
- lbl_E;
-
- Optional<LLButton::Params> btn_rotate_top,
- btn_rotate_bottom,
- btn_rotate_left,
- btn_rotate_right;
-
- Params();
- };
-
-
- virtual ~LLVirtualTrackball();
- /*virtual*/ bool postBuild();
-
- virtual bool handleHover(S32 x, S32 y, MASK mask);
- virtual bool handleMouseUp(S32 x, S32 y, MASK mask);
- virtual bool handleMouseDown(S32 x, S32 y, MASK mask);
- virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask);
- virtual bool handleKeyHere(KEY key, MASK mask);
-
- virtual void draw();
-
- virtual void setValue(const LLSD& value);
- void setValue(F32 x, F32 y, F32 z, F32 w);
- virtual LLSD getValue() const;
-
- void setRotation(const LLQuaternion &value);
- LLQuaternion getRotation() const;
-
- static void getAzimuthAndElevation(const LLQuaternion &quat, F32 &azimuth, F32 &elevation);
- static void getAzimuthAndElevationDeg(const LLQuaternion &quat, F32 &azimuth, F32 &elevation);
-
-protected:
- friend class LLUICtrlFactory;
- LLVirtualTrackball(const Params&);
- void onEditChange();
-
-protected:
- LLTextBox* mNLabel;
- LLTextBox* mELabel;
- LLTextBox* mSLabel;
- LLTextBox* mWLabel;
-
- LLButton* mBtnRotateTop;
- LLButton* mBtnRotateBottom;
- LLButton* mBtnRotateLeft;
- LLButton* mBtnRotateRight;
-
- LLTextBox* mLabelN;
- LLTextBox* mLabelS;
- LLTextBox* mLabelW;
- LLTextBox* mLabelE;
-
- LLPanel* mTouchArea;
- LLViewBorder* mBorder;
-
-private:
- void setValueAndCommit(const LLQuaternion &value);
- void drawThumb(S32 x, S32 y, ThumbMode mode, bool upperHemi = true);
- bool pointInTouchCircle(S32 x, S32 y) const;
-
- void onRotateTopClick();
- void onRotateBottomClick();
- void onRotateLeftClick();
- void onRotateRightClick();
-
- void onRotateTopMouseEnter();
- void onRotateBottomMouseEnter();
- void onRotateLeftMouseEnter();
- void onRotateRightMouseEnter();
-
- S32 mPrevX;
- S32 mPrevY;
-
- LLUIImage* mImgMoonBack;
- LLUIImage* mImgMoonFront;
- LLUIImage* mImgSunBack;
- LLUIImage* mImgSunFront;
- LLUIImage* mImgBtnRotTop;
- LLUIImage* mImgBtnRotLeft;
- LLUIImage* mImgBtnRotRight;
- LLUIImage* mImgBtnRotBottom;
- LLUIImage* mImgSphere;
-
- LLQuaternion mValue;
- ThumbMode mThumbMode;
- DragMode mDragMode;
-
- F32 mIncrementMouse;
- F32 mIncrementBtn;
-};
-
-#endif
-
+/**
+* @file virtualtrackball.h
+* @author Andrey Lihatskiy
+* @brief Header file for LLVirtualTrackball
+*
+* $LicenseInfo:firstyear=2001&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2018, 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 for positioning the sun and the moon in the celestial sphere.
+
+#ifndef LL_LLVIRTUALTRACKBALL_H
+#define LL_LLVIRTUALTRACKBALL_H
+
+#include "lluictrl.h"
+#include "llpanel.h"
+#include "lltextbox.h"
+#include "llbutton.h"
+
+class LLVirtualTrackball
+ : public LLUICtrl
+{
+public:
+ enum ThumbMode
+ {
+ SUN,
+ MOON
+ };
+ enum DragMode
+ {
+ DRAG_SET,
+ DRAG_SCROLL
+ };
+
+ struct Params
+ : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<LLViewBorder::Params> border;
+ Optional<LLUIImage*> image_moon_back,
+ image_moon_front,
+ image_sphere,
+ image_sun_back,
+ image_sun_front;
+
+ Optional<std::string> thumb_mode;
+ Optional<F32> increment_angle_mouse,
+ increment_angle_btn;
+
+ Optional<LLTextBox::Params> lbl_N,
+ lbl_S,
+ lbl_W,
+ lbl_E;
+
+ Optional<LLButton::Params> btn_rotate_top,
+ btn_rotate_bottom,
+ btn_rotate_left,
+ btn_rotate_right;
+
+ Params();
+ };
+
+
+ virtual ~LLVirtualTrackball();
+ /*virtual*/ bool postBuild();
+
+ virtual bool handleHover(S32 x, S32 y, MASK mask);
+ virtual bool handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual bool handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask);
+ virtual bool handleKeyHere(KEY key, MASK mask);
+
+ virtual void draw();
+
+ virtual void setValue(const LLSD& value);
+ void setValue(F32 x, F32 y, F32 z, F32 w);
+ virtual LLSD getValue() const;
+
+ void setRotation(const LLQuaternion &value);
+ LLQuaternion getRotation() const;
+
+ static void getAzimuthAndElevation(const LLQuaternion &quat, F32 &azimuth, F32 &elevation);
+ static void getAzimuthAndElevationDeg(const LLQuaternion &quat, F32 &azimuth, F32 &elevation);
+
+protected:
+ friend class LLUICtrlFactory;
+ LLVirtualTrackball(const Params&);
+ void onEditChange();
+
+protected:
+ LLTextBox* mNLabel;
+ LLTextBox* mELabel;
+ LLTextBox* mSLabel;
+ LLTextBox* mWLabel;
+
+ LLButton* mBtnRotateTop;
+ LLButton* mBtnRotateBottom;
+ LLButton* mBtnRotateLeft;
+ LLButton* mBtnRotateRight;
+
+ LLTextBox* mLabelN;
+ LLTextBox* mLabelS;
+ LLTextBox* mLabelW;
+ LLTextBox* mLabelE;
+
+ LLPanel* mTouchArea;
+ LLViewBorder* mBorder;
+
+private:
+ void setValueAndCommit(const LLQuaternion &value);
+ void drawThumb(S32 x, S32 y, ThumbMode mode, bool upperHemi = true);
+ bool pointInTouchCircle(S32 x, S32 y) const;
+
+ void onRotateTopClick();
+ void onRotateBottomClick();
+ void onRotateLeftClick();
+ void onRotateRightClick();
+
+ void onRotateTopMouseEnter();
+ void onRotateBottomMouseEnter();
+ void onRotateLeftMouseEnter();
+ void onRotateRightMouseEnter();
+
+ S32 mPrevX;
+ S32 mPrevY;
+
+ LLUIImage* mImgMoonBack;
+ LLUIImage* mImgMoonFront;
+ LLUIImage* mImgSunBack;
+ LLUIImage* mImgSunFront;
+ LLUIImage* mImgBtnRotTop;
+ LLUIImage* mImgBtnRotLeft;
+ LLUIImage* mImgBtnRotRight;
+ LLUIImage* mImgBtnRotBottom;
+ LLUIImage* mImgSphere;
+
+ LLQuaternion mValue;
+ ThumbMode mThumbMode;
+ DragMode mDragMode;
+
+ F32 mIncrementMouse;
+ F32 mIncrementBtn;
+};
+
+#endif
+
diff --git a/indra/llui/llwindowshade.cpp b/indra/llui/llwindowshade.cpp
index f5c463c961..e48bc94b0a 100644
--- a/indra/llui/llwindowshade.cpp
+++ b/indra/llui/llwindowshade.cpp
@@ -5,21 +5,21 @@
* $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$
*/
@@ -40,353 +40,353 @@ 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)
+: bg_image("bg_image"),
+ modal("modal", false),
+ text_color("text_color"),
+ shade_color("shade_color"),
+ can_close("can_close", true)
{
- changeDefault(mouse_opaque, false);
+ changeDefault(mouse_opaque, false);
}
LLWindowShade::LLWindowShade(const LLWindowShade::Params& params)
-: LLUICtrl(params),
- mModal(params.modal),
- mFormHeight(0),
- mTextColor(params.text_color)
+: LLUICtrl(params),
+ mModal(params.modal),
+ mFormHeight(0),
+ mTextColor(params.text_color)
{
- setFocusRoot(true);
+ setFocusRoot(true);
}
void LLWindowShade::initFromParams(const LLWindowShade::Params& params)
{
- LLUICtrl::initFromParams(params);
-
- LLLayoutStack::Params layout_p;
- layout_p.name = "notification_stack";
- layout_p.rect = params.rect;
- layout_p.follows.flags = FOLLOWS_ALL;
- layout_p.mouse_opaque = false;
- layout_p.orientation = LLLayoutStack::VERTICAL;
- layout_p.border_size = 0;
-
- LLLayoutStack* stackp = LLUICtrlFactory::create<LLLayoutStack>(layout_p);
- addChild(stackp);
-
- LLLayoutPanel::Params panel_p;
- 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;
- panel_p.background_visible = true;
- panel_p.bg_alpha_image = params.bg_image;
- panel_p.auto_resize = false;
- LLLayoutPanel* notification_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
- stackp->addChild(notification_panel);
-
- panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>();
- panel_p.auto_resize = true;
- panel_p.user_resize = false;
- panel_p.rect = params.rect;
- panel_p.name = "background_area";
- panel_p.mouse_opaque = false;
- panel_p.background_visible = false;
- panel_p.bg_alpha_color = params.shade_color;
- LLLayoutPanel* dummy_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
- stackp->addChild(dummy_panel);
-
- layout_p = LLUICtrlFactory::getDefaultParams<LLLayoutStack>();
- layout_p.rect = LLRect(0, 30, 800, 0);
- layout_p.follows.flags = FOLLOWS_ALL;
- layout_p.orientation = LLLayoutStack::HORIZONTAL;
- stackp = LLUICtrlFactory::create<LLLayoutStack>(layout_p);
- notification_panel->addChild(stackp);
-
- panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>();
- panel_p.rect.height = 30;
- LLLayoutPanel* panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
- stackp->addChild(panel);
-
- LLIconCtrl::Params icon_p;
- icon_p.name = "notification_icon";
- icon_p.rect = LLRect(5, 25, 21, 10);
- panel->addChild(LLUICtrlFactory::create<LLIconCtrl>(icon_p));
-
- LLTextBox::Params text_p;
- 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();
- text_p.font.style = "BOLD";
- text_p.name = "notification_text";
- text_p.use_ellipses = true;
- text_p.wrap = true;
- panel->addChild(LLUICtrlFactory::create<LLTextBox>(text_p));
-
- panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>();
- panel_p.auto_resize = false;
- panel_p.user_resize = false;
- panel_p.name="form_elements";
- 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);
-
- 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);
+ LLUICtrl::initFromParams(params);
+
+ LLLayoutStack::Params layout_p;
+ layout_p.name = "notification_stack";
+ layout_p.rect = params.rect;
+ layout_p.follows.flags = FOLLOWS_ALL;
+ layout_p.mouse_opaque = false;
+ layout_p.orientation = LLLayoutStack::VERTICAL;
+ layout_p.border_size = 0;
+
+ LLLayoutStack* stackp = LLUICtrlFactory::create<LLLayoutStack>(layout_p);
+ addChild(stackp);
+
+ LLLayoutPanel::Params panel_p;
+ 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;
+ panel_p.background_visible = true;
+ panel_p.bg_alpha_image = params.bg_image;
+ panel_p.auto_resize = false;
+ LLLayoutPanel* notification_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
+ stackp->addChild(notification_panel);
+
+ panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>();
+ panel_p.auto_resize = true;
+ panel_p.user_resize = false;
+ panel_p.rect = params.rect;
+ panel_p.name = "background_area";
+ panel_p.mouse_opaque = false;
+ panel_p.background_visible = false;
+ panel_p.bg_alpha_color = params.shade_color;
+ LLLayoutPanel* dummy_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
+ stackp->addChild(dummy_panel);
+
+ layout_p = LLUICtrlFactory::getDefaultParams<LLLayoutStack>();
+ layout_p.rect = LLRect(0, 30, 800, 0);
+ layout_p.follows.flags = FOLLOWS_ALL;
+ layout_p.orientation = LLLayoutStack::HORIZONTAL;
+ stackp = LLUICtrlFactory::create<LLLayoutStack>(layout_p);
+ notification_panel->addChild(stackp);
+
+ panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>();
+ panel_p.rect.height = 30;
+ LLLayoutPanel* panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
+ stackp->addChild(panel);
+
+ LLIconCtrl::Params icon_p;
+ icon_p.name = "notification_icon";
+ icon_p.rect = LLRect(5, 25, 21, 10);
+ panel->addChild(LLUICtrlFactory::create<LLIconCtrl>(icon_p));
+
+ LLTextBox::Params text_p;
+ 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();
+ text_p.font.style = "BOLD";
+ text_p.name = "notification_text";
+ text_p.use_ellipses = true;
+ text_p.wrap = true;
+ panel->addChild(LLUICtrlFactory::create<LLTextBox>(text_p));
+
+ panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>();
+ panel_p.auto_resize = false;
+ panel_p.user_resize = false;
+ panel_p.name="form_elements";
+ 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);
+
+ 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))
- {
- getChildRef<LLLayoutPanel>("background_area").setBackgroundVisible(false);
- setMouseOpaque(false);
- }
+ 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))
+ {
+ getChildRef<LLLayoutPanel>("background_area").setBackgroundVisible(false);
+ setMouseOpaque(false);
+ }
}
void LLWindowShade::hide()
{
- getChildRef<LLLayoutPanel>("notification_area").setVisible(false);
+ getChildRef<LLLayoutPanel>("notification_area").setVisible(false);
}
void LLWindowShade::onCloseNotification()
{
- if (!mNotifications.empty())
- LLNotifications::instance().cancel(mNotifications.back());
+ 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);
+ 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;
+ LLNotificationPtr notify = getCurrentNotification();
+ if (!notify) return;
- mNotificationResponse[name] = true;
+ mNotificationResponse[name] = true;
- notify->respond(mNotificationResponse);
+ notify->respond(mNotificationResponse);
}
void LLWindowShade::onEnterNotificationText(LLUICtrl* ctrl, const std::string& name)
{
- mNotificationResponse[name] = ctrl->getValue().asString();
+ mNotificationResponse[name] = ctrl->getValue().asString();
}
void LLWindowShade::show(LLNotificationPtr notification)
{
- mNotifications.push_back(notification);
+ mNotifications.push_back(notification);
- displayLatestNotification();
+ 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(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;
- const S32 WIDGET_HEIGHT = 24;
- const S32 LINE_EDITOR_WIDTH = 120;
- S32 cur_x = FORM_PADDING_HORIZONTAL;
- S32 cur_y = FORM_PADDING_VERTICAL + WIDGET_HEIGHT;
- S32 form_width = cur_x;
-
- if (ignore_type != LLNotificationForm::IGNORE_NO)
- {
- LLCheckBoxCtrl::Params checkbox_p;
- checkbox_p.name = "ignore_check";
- checkbox_p.rect = LLRect(cur_x, cur_y, cur_x, cur_y - WIDGET_HEIGHT);
- checkbox_p.label = formp->getIgnoreMessage();
- checkbox_p.label_text.text_color = LLColor4::black;
- checkbox_p.commit_callback.function = boost::bind(&LLWindowShade::onClickIgnore, this, _1);
- checkbox_p.initial_value = formp->getIgnored();
-
- LLCheckBoxCtrl* check = LLUICtrlFactory::create<LLCheckBoxCtrl>(checkbox_p);
- check->setRect(check->getBoundingRect());
- form_elements.addChild(check);
- cur_x = check->getRect().mRight + FORM_PADDING_HORIZONTAL;
- form_width = llmax(form_width, cur_x);
- }
-
- for (S32 i = 0; i < formp->getNumElements(); i++)
- {
- LLSD form_element = formp->getElement(i);
- std::string type = form_element["type"].asString();
- if (type == "button")
- {
- LLButton::Params button_p;
- button_p.name = form_element["name"];
- button_p.label = form_element["text"];
- button_p.rect = LLRect(cur_x, cur_y, cur_x, cur_y - WIDGET_HEIGHT);
- button_p.click_callback.function = boost::bind(&LLWindowShade::onClickNotificationButton, this, form_element["name"].asString());
- button_p.auto_resize = true;
-
- LLButton* button = LLUICtrlFactory::create<LLButton>(button_p);
- button->autoResize();
- form_elements.addChild(button);
-
- if (form_element["default"].asBoolean())
- {
- form_elements.setDefaultBtn(button);
- }
-
- cur_x = button->getRect().mRight + FORM_PADDING_HORIZONTAL;
- form_width = llmax(form_width, cur_x);
- }
- else if (type == "text" || type == "password")
- {
- // if not at beginning of line...
- if (cur_x != FORM_PADDING_HORIZONTAL)
- {
- // start new line
- cur_x = FORM_PADDING_HORIZONTAL;
- cur_y -= WIDGET_HEIGHT + FORM_PADDING_VERTICAL;
- }
- LLTextBox::Params label_p;
- label_p.name = form_element["name"].asString() + "_label";
- label_p.rect = LLRect(cur_x, cur_y, cur_x + LINE_EDITOR_WIDTH, cur_y - WIDGET_HEIGHT);
- label_p.initial_value = form_element["text"];
- label_p.text_color = mTextColor;
- label_p.font_valign = LLFontGL::VCENTER;
- label_p.v_pad = 5;
- LLTextBox* textbox = LLUICtrlFactory::create<LLTextBox>(label_p);
- textbox->reshapeToFitText();
- 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;
-
- LLLineEditor::Params line_p;
- line_p.name = form_element["name"];
- line_p.keystroke_callback = boost::bind(&LLWindowShade::onEnterNotificationText, this, _1, form_element["name"].asString());
- line_p.is_password = type == "password";
- line_p.rect = LLRect(cur_x, cur_y, cur_x + LINE_EDITOR_WIDTH, cur_y - WIDGET_HEIGHT);
-
- LLLineEditor* line_editor = LLUICtrlFactory::create<LLLineEditor>(line_p);
- form_elements.addChild(line_editor);
- form_width = llmax(form_width, cur_x + LINE_EDITOR_WIDTH + FORM_PADDING_HORIZONTAL);
-
- // reset to start of next line
- cur_x = FORM_PADDING_HORIZONTAL;
- cur_y -= WIDGET_HEIGHT + FORM_PADDING_VERTICAL;
- }
- }
-
- mFormHeight = form_elements.getRect().getHeight() - (cur_y - WIDGET_HEIGHT - FORM_PADDING_VERTICAL);
- form_elements.reshape(form_width, mFormHeight);
- form_elements.setMinDim(form_width);
-
- // move all form elements back onto form surface
- S32 delta_y = WIDGET_HEIGHT + FORM_PADDING_VERTICAL - cur_y;
- for (child_list_const_iter_t it = form_elements.getChildList()->begin(), end_it = form_elements.getChildList()->end();
- it != end_it;
- ++it)
- {
- (*it)->translate(0, delta_y);
- }
-
- getChildRef<LLLayoutPanel>("notification_area").setVisible(true);
- getChildRef<LLLayoutPanel>("background_area").setBackgroundVisible(mModal);
-
- setMouseOpaque(mModal);
+ 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(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;
+ const S32 WIDGET_HEIGHT = 24;
+ const S32 LINE_EDITOR_WIDTH = 120;
+ S32 cur_x = FORM_PADDING_HORIZONTAL;
+ S32 cur_y = FORM_PADDING_VERTICAL + WIDGET_HEIGHT;
+ S32 form_width = cur_x;
+
+ if (ignore_type != LLNotificationForm::IGNORE_NO)
+ {
+ LLCheckBoxCtrl::Params checkbox_p;
+ checkbox_p.name = "ignore_check";
+ checkbox_p.rect = LLRect(cur_x, cur_y, cur_x, cur_y - WIDGET_HEIGHT);
+ checkbox_p.label = formp->getIgnoreMessage();
+ checkbox_p.label_text.text_color = LLColor4::black;
+ checkbox_p.commit_callback.function = boost::bind(&LLWindowShade::onClickIgnore, this, _1);
+ checkbox_p.initial_value = formp->getIgnored();
+
+ LLCheckBoxCtrl* check = LLUICtrlFactory::create<LLCheckBoxCtrl>(checkbox_p);
+ check->setRect(check->getBoundingRect());
+ form_elements.addChild(check);
+ cur_x = check->getRect().mRight + FORM_PADDING_HORIZONTAL;
+ form_width = llmax(form_width, cur_x);
+ }
+
+ for (S32 i = 0; i < formp->getNumElements(); i++)
+ {
+ LLSD form_element = formp->getElement(i);
+ std::string type = form_element["type"].asString();
+ if (type == "button")
+ {
+ LLButton::Params button_p;
+ button_p.name = form_element["name"];
+ button_p.label = form_element["text"];
+ button_p.rect = LLRect(cur_x, cur_y, cur_x, cur_y - WIDGET_HEIGHT);
+ button_p.click_callback.function = boost::bind(&LLWindowShade::onClickNotificationButton, this, form_element["name"].asString());
+ button_p.auto_resize = true;
+
+ LLButton* button = LLUICtrlFactory::create<LLButton>(button_p);
+ button->autoResize();
+ form_elements.addChild(button);
+
+ if (form_element["default"].asBoolean())
+ {
+ form_elements.setDefaultBtn(button);
+ }
+
+ cur_x = button->getRect().mRight + FORM_PADDING_HORIZONTAL;
+ form_width = llmax(form_width, cur_x);
+ }
+ else if (type == "text" || type == "password")
+ {
+ // if not at beginning of line...
+ if (cur_x != FORM_PADDING_HORIZONTAL)
+ {
+ // start new line
+ cur_x = FORM_PADDING_HORIZONTAL;
+ cur_y -= WIDGET_HEIGHT + FORM_PADDING_VERTICAL;
+ }
+ LLTextBox::Params label_p;
+ label_p.name = form_element["name"].asString() + "_label";
+ label_p.rect = LLRect(cur_x, cur_y, cur_x + LINE_EDITOR_WIDTH, cur_y - WIDGET_HEIGHT);
+ label_p.initial_value = form_element["text"];
+ label_p.text_color = mTextColor;
+ label_p.font_valign = LLFontGL::VCENTER;
+ label_p.v_pad = 5;
+ LLTextBox* textbox = LLUICtrlFactory::create<LLTextBox>(label_p);
+ textbox->reshapeToFitText();
+ 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;
+
+ LLLineEditor::Params line_p;
+ line_p.name = form_element["name"];
+ line_p.keystroke_callback = boost::bind(&LLWindowShade::onEnterNotificationText, this, _1, form_element["name"].asString());
+ line_p.is_password = type == "password";
+ line_p.rect = LLRect(cur_x, cur_y, cur_x + LINE_EDITOR_WIDTH, cur_y - WIDGET_HEIGHT);
+
+ LLLineEditor* line_editor = LLUICtrlFactory::create<LLLineEditor>(line_p);
+ form_elements.addChild(line_editor);
+ form_width = llmax(form_width, cur_x + LINE_EDITOR_WIDTH + FORM_PADDING_HORIZONTAL);
+
+ // reset to start of next line
+ cur_x = FORM_PADDING_HORIZONTAL;
+ cur_y -= WIDGET_HEIGHT + FORM_PADDING_VERTICAL;
+ }
+ }
+
+ mFormHeight = form_elements.getRect().getHeight() - (cur_y - WIDGET_HEIGHT - FORM_PADDING_VERTICAL);
+ form_elements.reshape(form_width, mFormHeight);
+ form_elements.setMinDim(form_width);
+
+ // move all form elements back onto form surface
+ S32 delta_y = WIDGET_HEIGHT + FORM_PADDING_VERTICAL - cur_y;
+ for (child_list_const_iter_t it = form_elements.getChildList()->begin(), end_it = form_elements.getChildList()->end();
+ it != end_it;
+ ++it)
+ {
+ (*it)->translate(0, delta_y);
+ }
+
+ getChildRef<LLLayoutPanel>("notification_area").setVisible(true);
+ getChildRef<LLLayoutPanel>("background_area").setBackgroundVisible(mModal);
+
+ setMouseOpaque(mModal);
}
void LLWindowShade::setBackgroundImage(LLUIImage* image)
{
- getChild<LLLayoutPanel>("notification_area")->setTransparentImage(image);
+ getChild<LLLayoutPanel>("notification_area")->setTransparentImage(image);
}
void LLWindowShade::setTextColor(LLColor4 color)
{
- getChild<LLTextBox>("notification_text")->setColor(color);
+ getChild<LLTextBox>("notification_text")->setColor(color);
}
bool LLWindowShade::isShown() const
{
- return getChildRef<LLLayoutPanel>("notification_area").getVisible();
+ return getChildRef<LLLayoutPanel>("notification_area").getVisible();
}
void LLWindowShade::setCanClose(bool can_close)
{
- getChildView("close_panel")->setVisible(can_close);
+ getChildView("close_panel")->setVisible(can_close);
}
LLNotificationPtr LLWindowShade::getCurrentNotification()
{
- if (mNotifications.empty())
- {
- return LLNotificationPtr();
- }
- return mNotifications.back();
+ if (mNotifications.empty())
+ {
+ return LLNotificationPtr();
+ }
+ return mNotifications.back();
}
diff --git a/indra/llui/llwindowshade.h b/indra/llui/llwindowshade.h
index 6d753d1161..a401394d78 100644
--- a/indra/llui/llwindowshade.h
+++ b/indra/llui/llwindowshade.h
@@ -5,21 +5,21 @@
* $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$
*/
@@ -34,45 +34,45 @@
class LLWindowShade : public LLUICtrl
{
public:
- struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Optional<LLUIImage*> bg_image;
- Optional<LLUIColor> text_color,
- shade_color;
- Optional<bool> modal,
- can_close;
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<LLUIImage*> bg_image;
+ Optional<LLUIColor> text_color,
+ shade_color;
+ Optional<bool> modal,
+ can_close;
+
+ Params();
+ };
- Params();
- };
+ void show(LLNotificationPtr);
+ /*virtual*/ void draw();
+ void hide();
- void show(LLNotificationPtr);
- /*virtual*/ void draw();
- void hide();
-
- bool isShown() const;
+ bool isShown() const;
- void setBackgroundImage(LLUIImage* image);
- void setTextColor(LLColor4 color);
- void setCanClose(bool can_close);
+ void setBackgroundImage(LLUIImage* image);
+ void setTextColor(LLColor4 color);
+ void setCanClose(bool can_close);
private:
- void displayLatestNotification();
- LLNotificationPtr getCurrentNotification();
- friend class LLUICtrlFactory;
+ void displayLatestNotification();
+ LLNotificationPtr getCurrentNotification();
+ friend class LLUICtrlFactory;
- LLWindowShade(const Params& p);
- void initFromParams(const Params& params);
+ LLWindowShade(const Params& p);
+ void initFromParams(const Params& params);
- void onCloseNotification();
- void onClickNotificationButton(const std::string& name);
- void onEnterNotificationText(LLUICtrl* ctrl, const std::string& name);
- void onClickIgnore(LLUICtrl* ctrl);
+ void onCloseNotification();
+ void onClickNotificationButton(const std::string& name);
+ void onEnterNotificationText(LLUICtrl* ctrl, const std::string& name);
+ void onClickIgnore(LLUICtrl* ctrl);
- std::vector<LLNotificationPtr> mNotifications;
- LLSD mNotificationResponse;
- bool mModal;
- S32 mFormHeight;
- LLUIColor mTextColor;
+ std::vector<LLNotificationPtr> mNotifications;
+ LLSD mNotificationResponse;
+ bool mModal;
+ S32 mFormHeight;
+ LLUIColor mTextColor;
};
#endif // LL_LLWINDOWSHADE_H
diff --git a/indra/llui/llxuiparser.cpp b/indra/llui/llxuiparser.cpp
index f8d4a61721..bfa91b3729 100644
--- a/indra/llui/llxuiparser.cpp
+++ b/indra/llui/llxuiparser.cpp
@@ -1,1765 +1,1765 @@
-/**
- * @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";
-
-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)
-{
- bool value;
- LLXUIParser& self = static_cast<LLXUIParser&>(parser);
- bool success = self.mCurReadNode->getBoolValue(1, &value);
- *((bool*)val_ptr) = value;
- 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)
-{
- std::string warning_msg = llformat("%s:\t%s(%d)", message.c_str(), mCurFileName.c_str(), mCurReadNode->getLineNumber());
- Parser::parserWarning(warning_msg);
-}
-
-void LLXUIParser::parserError(const std::string& message)
-{
- std::string error_msg = llformat("%s:\t%s(%d)", message.c_str(), mCurFileName.c_str(), mCurReadNode->getLineNumber());
- Parser::parserError(error_msg);
-}
-
-
-//
-// 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)
-{
- std::string warning_msg = llformat("%s:\t%s", message.c_str(), mCurFileName.c_str());
- Parser::parserWarning(warning_msg);
-}
-
-void LLSimpleXUIParser::parserError(const std::string& message)
-{
- std::string error_msg = llformat("%s:\t%s", message.c_str(), mCurFileName.c_str());
- Parser::parserError(error_msg);
-}
-
-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;
-}
+/**
+ * @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";
+
+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)
+{
+ bool value;
+ LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+ bool success = self.mCurReadNode->getBoolValue(1, &value);
+ *((bool*)val_ptr) = value;
+ 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)
+{
+ std::string warning_msg = llformat("%s:\t%s(%d)", message.c_str(), mCurFileName.c_str(), mCurReadNode->getLineNumber());
+ Parser::parserWarning(warning_msg);
+}
+
+void LLXUIParser::parserError(const std::string& message)
+{
+ std::string error_msg = llformat("%s:\t%s(%d)", message.c_str(), mCurFileName.c_str(), mCurReadNode->getLineNumber());
+ Parser::parserError(error_msg);
+}
+
+
+//
+// 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)
+{
+ std::string warning_msg = llformat("%s:\t%s", message.c_str(), mCurFileName.c_str());
+ Parser::parserWarning(warning_msg);
+}
+
+void LLSimpleXUIParser::parserError(const std::string& message)
+{
+ std::string error_msg = llformat("%s:\t%s", message.c_str(), mCurFileName.c_str());
+ Parser::parserError(error_msg);
+}
+
+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
index eb0eac8194..f755c12cbf 100644
--- a/indra/llui/llxuiparser.h
+++ b/indra/llui/llxuiparser.h
@@ -1,25 +1,25 @@
-/**
+/**
* @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$
*/
@@ -40,9 +40,9 @@ class LLView;
// lookup widget type by name
class LLWidgetTypeRegistry
-: public LLRegistrySingleton<std::string, const std::type_info*, LLWidgetTypeRegistry>
+: public LLRegistrySingleton<std::string, const std::type_info*, LLWidgetTypeRegistry>
{
- LLSINGLETON_EMPTY_CTOR(LLWidgetTypeRegistry);
+ LLSINGLETON_EMPTY_CTOR(LLWidgetTypeRegistry);
};
@@ -54,30 +54,30 @@ typedef LLRegistry<std::string, LLWidgetCreatorFunc> widget_registry_t;
class LLChildRegistryRegistry
: public LLRegistrySingleton<const std::type_info*, widget_registry_t, LLChildRegistryRegistry>
{
- LLSINGLETON_EMPTY_CTOR(LLChildRegistryRegistry);
+ LLSINGLETON_EMPTY_CTOR(LLChildRegistryRegistry);
};
class LLXSDWriter : public LLInitParam::Parser
{
- LOG_CLASS(LLXSDWriter);
+ LOG_CLASS(LLXSDWriter);
public:
- void writeXSD(const std::string& name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace);
+ void writeXSD(const std::string& name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace);
- /*virtual*/ std::string getCurrentElementName() { return LLStringUtil::null; }
- /*virtual*/ std::string getCurrentFileName() { return LLStringUtil::null; }
- LLXSDWriter();
- ~LLXSDWriter();
+ /*virtual*/ std::string getCurrentElementName() { return LLStringUtil::null; }
+ /*virtual*/ std::string getCurrentFileName() { 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;
+ 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;
};
@@ -87,7 +87,7 @@ protected:
class LLXUIXSDWriter : public LLXSDWriter
{
public:
- void writeXSD(const std::string& name, const std::string& path, const LLInitParam::BaseBlock& block);
+ void writeXSD(const std::string& name, const std::string& path, const LLInitParam::BaseBlock& block);
};
@@ -98,97 +98,97 @@ class LLXUIParser : public LLInitParam::Parser
LOG_CLASS(LLXUIParser);
public:
- LLXUIParser();
- typedef LLInitParam::Parser::name_stack_t name_stack_t;
-
- /*virtual*/ std::string getCurrentElementName();
- /*virtual*/ std::string getCurrentFileName() { return mCurFileName; }
- /*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);
- }
+ LLXUIParser();
+ typedef LLInitParam::Parser::name_stack_t name_stack_t;
+
+ /*virtual*/ std::string getCurrentElementName();
+ /*virtual*/ std::string getCurrentFileName() { return mCurFileName; }
+ /*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);
+ 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;
+ 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
+// 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
+// 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
@@ -198,62 +198,62 @@ 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);
+ 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();
+ LLSimpleXUIParser(element_start_callback_t element_cb = NULL);
+ virtual ~LLSimpleXUIParser();
- /*virtual*/ std::string getCurrentElementName();
- /*virtual*/ std::string getCurrentFileName() { return mCurFileName; }
- /*virtual*/ void parserWarning(const std::string& message);
- /*virtual*/ void parserError(const std::string& message);
+ /*virtual*/ std::string getCurrentElementName();
+ /*virtual*/ std::string getCurrentFileName() { return mCurFileName; }
+ /*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);
+ 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);
+ //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;
+ 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;
};
diff --git a/indra/llui/llxyvector.h b/indra/llui/llxyvector.h
index 4a20275267..9dbd5808eb 100644
--- a/indra/llui/llxyvector.h
+++ b/indra/llui/llxyvector.h
@@ -1,122 +1,122 @@
-/**
-* @file llxyvector.h
-* @author Andrey Lihatskiy
-* @brief Header file for LLXYVector
-*
-* $LicenseInfo:firstyear=2001&license=viewerlgpl$
-* Second Life Viewer Source Code
-* Copyright (C) 2018, 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 allows to set two related vector magnitudes by manipulating a single vector on a plane.
-
-#ifndef LL_LLXYVECTOR_H
-#define LL_LLXYVECTOR_H
-
-#include "lluictrl.h"
-#include "llpanel.h"
-#include "lltextbox.h"
-#include "lllineeditor.h"
-
-class LLXYVector
- : public LLUICtrl
-{
-public:
- struct Params
- : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Optional<LLLineEditor::Params> x_entry;
- Optional<LLLineEditor::Params> y_entry;
- Optional<LLPanel::Params> touch_area;
- Optional<LLViewBorder::Params> border;
- Optional<S32> edit_bar_height;
- Optional<S32> padding;
- Optional<S32> label_width;
- Optional<F32> min_val_x;
- Optional<F32> max_val_x;
- Optional<F32> increment_x;
- Optional<F32> min_val_y;
- Optional<F32> max_val_y;
- Optional<F32> increment_y;
- Optional<LLUIColor> arrow_color;
- Optional<LLUIColor> ghost_color;
- Optional<LLUIColor> area_color;
- Optional<LLUIColor> grid_color;
- Optional<bool> logarithmic;
-
- Params();
- };
-
-
- virtual ~LLXYVector();
- /*virtual*/ bool postBuild();
-
- virtual bool handleHover(S32 x, S32 y, MASK mask);
- virtual bool handleMouseUp(S32 x, S32 y, MASK mask);
- virtual bool handleMouseDown(S32 x, S32 y, MASK mask);
-
- virtual void draw();
-
- virtual void setValue(const LLSD& value);
- void setValue(F32 x, F32 y);
- virtual LLSD getValue() const;
-
-protected:
- friend class LLUICtrlFactory;
- LLXYVector(const Params&);
- void onEditChange();
-
-protected:
- LLTextBox* mXLabel;
- LLTextBox* mYLabel;
- LLLineEditor* mXEntry;
- LLLineEditor* mYEntry;
- LLPanel* mTouchArea;
- LLViewBorder* mBorder;
-
-private:
- void update();
- void setValueAndCommit(F32 x, F32 y);
-
- F32 mValueX;
- F32 mValueY;
-
- F32 mMinValueX;
- F32 mMaxValueX;
- F32 mIncrementX;
- F32 mMinValueY;
- F32 mMaxValueY;
- F32 mIncrementY;
-
- U32 mGhostX;
- U32 mGhostY;
-
- LLUIColor mArrowColor;
- LLUIColor mGhostColor;
- LLUIColor mAreaColor;
- LLUIColor mGridColor;
-
- bool mLogarithmic;
- F32 mLogScaleX;
- F32 mLogScaleY;
-};
-
-#endif
-
+/**
+* @file llxyvector.h
+* @author Andrey Lihatskiy
+* @brief Header file for LLXYVector
+*
+* $LicenseInfo:firstyear=2001&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2018, 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 allows to set two related vector magnitudes by manipulating a single vector on a plane.
+
+#ifndef LL_LLXYVECTOR_H
+#define LL_LLXYVECTOR_H
+
+#include "lluictrl.h"
+#include "llpanel.h"
+#include "lltextbox.h"
+#include "lllineeditor.h"
+
+class LLXYVector
+ : public LLUICtrl
+{
+public:
+ struct Params
+ : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<LLLineEditor::Params> x_entry;
+ Optional<LLLineEditor::Params> y_entry;
+ Optional<LLPanel::Params> touch_area;
+ Optional<LLViewBorder::Params> border;
+ Optional<S32> edit_bar_height;
+ Optional<S32> padding;
+ Optional<S32> label_width;
+ Optional<F32> min_val_x;
+ Optional<F32> max_val_x;
+ Optional<F32> increment_x;
+ Optional<F32> min_val_y;
+ Optional<F32> max_val_y;
+ Optional<F32> increment_y;
+ Optional<LLUIColor> arrow_color;
+ Optional<LLUIColor> ghost_color;
+ Optional<LLUIColor> area_color;
+ Optional<LLUIColor> grid_color;
+ Optional<bool> logarithmic;
+
+ Params();
+ };
+
+
+ virtual ~LLXYVector();
+ /*virtual*/ bool postBuild();
+
+ virtual bool handleHover(S32 x, S32 y, MASK mask);
+ virtual bool handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual bool handleMouseDown(S32 x, S32 y, MASK mask);
+
+ virtual void draw();
+
+ virtual void setValue(const LLSD& value);
+ void setValue(F32 x, F32 y);
+ virtual LLSD getValue() const;
+
+protected:
+ friend class LLUICtrlFactory;
+ LLXYVector(const Params&);
+ void onEditChange();
+
+protected:
+ LLTextBox* mXLabel;
+ LLTextBox* mYLabel;
+ LLLineEditor* mXEntry;
+ LLLineEditor* mYEntry;
+ LLPanel* mTouchArea;
+ LLViewBorder* mBorder;
+
+private:
+ void update();
+ void setValueAndCommit(F32 x, F32 y);
+
+ F32 mValueX;
+ F32 mValueY;
+
+ F32 mMinValueX;
+ F32 mMaxValueX;
+ F32 mIncrementX;
+ F32 mMinValueY;
+ F32 mMaxValueY;
+ F32 mIncrementY;
+
+ U32 mGhostX;
+ U32 mGhostY;
+
+ LLUIColor mArrowColor;
+ LLUIColor mGhostColor;
+ LLUIColor mAreaColor;
+ LLUIColor mGridColor;
+
+ bool mLogarithmic;
+ F32 mLogScaleX;
+ F32 mLogScaleY;
+};
+
+#endif
+
diff --git a/indra/llui/tests/llurlentry_stub.cpp b/indra/llui/tests/llurlentry_stub.cpp
index 338be1808d..95d1586495 100755
--- a/indra/llui/tests/llurlentry_stub.cpp
+++ b/indra/llui/tests/llurlentry_stub.cpp
@@ -6,21 +6,21 @@
* $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$
*/
@@ -38,13 +38,13 @@
// Stub for LLAvatarNameCache
bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name)
{
- return false;
+ return false;
}
LLAvatarNameCache::callback_connection_t LLAvatarNameCache::get(const LLUUID& agent_id, callback_slot_t slot)
{
- callback_connection_t connection;
- return connection;
+ callback_connection_t connection;
+ return connection;
}
//
@@ -52,24 +52,24 @@ LLAvatarNameCache::callback_connection_t LLAvatarNameCache::get(const LLUUID& ag
//
BOOL LLCacheName::getFullName(const LLUUID& id, std::string& fullname)
{
- fullname = "Lynx Linden";
- return TRUE;
+ fullname = "Lynx Linden";
+ return TRUE;
}
BOOL LLCacheName::getGroupName(const LLUUID& id, std::string& group)
{
- group = "My Group";
- return TRUE;
+ group = "My Group";
+ return TRUE;
}
boost::signals2::connection LLCacheName::get(const LLUUID& id, bool is_group, const LLCacheNameCallback& callback)
{
- return boost::signals2::connection();
+ return boost::signals2::connection();
}
boost::signals2::connection LLCacheName::getGroup(const LLUUID& id, const LLCacheNameCallback& callback)
{
- return boost::signals2::connection();
+ return boost::signals2::connection();
}
LLCacheName* gCacheName = NULL;
@@ -80,12 +80,12 @@ LLCacheName* gCacheName = NULL;
class LLTrans
{
public:
- 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 LLStringUtil::format_map_t& args);
};
std::string LLTrans::getString(const std::string &xml_desc, const LLStringUtil::format_map_t& args)
{
- return std::string();
+ return std::string();
}
//
@@ -102,65 +102,65 @@ LLStyle::Params::Params()
namespace LLInitParam
{
- ParamValue<LLUIColor>::ParamValue(const LLUIColor& color)
- : super_t(color)
- {}
-
- void ParamValue<LLUIColor>::updateValueFromBlock()
- {}
-
- void ParamValue<LLUIColor>::updateBlockFromValue(bool)
- {}
-
- bool ParamCompare<const LLFontGL*, false>::equals(const LLFontGL* a, const LLFontGL* b)
- {
- return false;
- }
-
- ParamValue<const LLFontGL*>::ParamValue(const LLFontGL* fontp)
- : super_t(fontp)
- {}
-
- void ParamValue<const LLFontGL*>::updateValueFromBlock()
- {}
-
- void ParamValue<const LLFontGL*>::updateBlockFromValue(bool)
- {}
-
- void TypeValues<LLFontGL::HAlign>::declareValues()
- {}
-
- void TypeValues<LLFontGL::VAlign>::declareValues()
- {}
-
- void TypeValues<LLFontGL::ShadowType>::declareValues()
- {}
-
- void ParamValue<LLUIImage*>::updateValueFromBlock()
- {}
-
- void ParamValue<LLUIImage*>::updateBlockFromValue(bool)
- {}
-
-
- bool ParamCompare<LLUIImage*, false>::equals(
- LLUIImage* const &a,
- LLUIImage* const &b)
- {
- return false;
- }
-
- bool ParamCompare<LLUIColor, false>::equals(const LLUIColor &a, const LLUIColor &b)
- {
- return false;
- }
+ ParamValue<LLUIColor>::ParamValue(const LLUIColor& color)
+ : super_t(color)
+ {}
+
+ void ParamValue<LLUIColor>::updateValueFromBlock()
+ {}
+
+ void ParamValue<LLUIColor>::updateBlockFromValue(bool)
+ {}
+
+ bool ParamCompare<const LLFontGL*, false>::equals(const LLFontGL* a, const LLFontGL* b)
+ {
+ return false;
+ }
+
+ ParamValue<const LLFontGL*>::ParamValue(const LLFontGL* fontp)
+ : super_t(fontp)
+ {}
+
+ void ParamValue<const LLFontGL*>::updateValueFromBlock()
+ {}
+
+ void ParamValue<const LLFontGL*>::updateBlockFromValue(bool)
+ {}
+
+ void TypeValues<LLFontGL::HAlign>::declareValues()
+ {}
+
+ void TypeValues<LLFontGL::VAlign>::declareValues()
+ {}
+
+ void TypeValues<LLFontGL::ShadowType>::declareValues()
+ {}
+
+ void ParamValue<LLUIImage*>::updateValueFromBlock()
+ {}
+
+ void ParamValue<LLUIImage*>::updateBlockFromValue(bool)
+ {}
+
+
+ bool ParamCompare<LLUIImage*, false>::equals(
+ LLUIImage* const &a,
+ LLUIImage* const &b)
+ {
+ return false;
+ }
+
+ bool ParamCompare<LLUIColor, false>::equals(const LLUIColor &a, const LLUIColor &b)
+ {
+ return false;
+ }
}
//static
LLFontGL* LLFontGL::getFontDefault()
{
- return NULL;
+ return NULL;
}
char const* const _PREHASH_AgentData = (char *)"AgentData";
diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp
index 2b44e61dea..3f0b230a4f 100644
--- a/indra/llui/tests/llurlentry_test.cpp
+++ b/indra/llui/tests/llurlentry_test.cpp
@@ -1,935 +1,935 @@
-/**
- * @file llurlentry_test.cpp
- * @author Martin Reddy
- * @brief Unit tests for LLUrlEntry objects
- *
- * $LicenseInfo:firstyear=2009&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "../llurlentry.h"
-#include "../lluictrl.h"
-//#include "llurlentry_stub.cpp"
-#include "lltut.h"
-#include "../lluicolortable.h"
-#include "../llrender/lluiimage.h"
-#include "../llmessage/llexperiencecache.h"
-
-#include <boost/regex.hpp>
-
-#if LL_WINDOWS
-// because something pulls in window and lldxdiag dependencies which in turn need wbemuuid.lib
- #pragma comment(lib, "wbemuuid.lib")
-#endif
-
-
-// namespace LLExperienceCache
-// {
-// const LLSD& get( const LLUUID& key)
-// {
-// static LLSD boo;
-// return boo;
-// }
-//
-// void get( const LLUUID& key, callback_slot_t slot ){}
-//
-// }
-
-/*==========================================================================*|
-typedef std::map<std::string, LLControlGroup*> settings_map_t;
-settings_map_t LLUI::sSettingGroups;
-
-bool LLControlGroup::getBOOL(const std::string& name)
-{
- return false;
-}
-
-LLUIColor LLUIColorTable::getColor(const std::string& name, const LLColor4& default_color) const
-{
- return LLUIColor();
-}
-
-LLUIColor::LLUIColor() : mColorPtr(NULL) {}
-
-LLUIImage::LLUIImage(const std::string& name, LLPointer<LLTexture> image)
-{
-}
-
-LLUIImage::~LLUIImage()
-{
-}
-
-//virtual
-S32 LLUIImage::getWidth() const
-{
- return 0;
-}
-
-//virtual
-S32 LLUIImage::getHeight() const
-{
- return 0;
-}
-|*==========================================================================*/
-
-namespace tut
-{
- struct LLUrlEntryData
- {
- };
-
- typedef test_group<LLUrlEntryData> factory;
- typedef factory::object object;
-}
-
-namespace
-{
- tut::factory tf("LLUrlEntry");
-}
-
-namespace tut
-{
- void testRegex(const std::string &testname, LLUrlEntryBase &entry,
- const char *text, const std::string &expected)
- {
- boost::regex regex = entry.getPattern();
- std::string url = "";
- boost::cmatch result;
- bool found = boost::regex_search(text, result, regex);
- if (found)
- {
- S32 start = static_cast<U32>(result[0].first - text);
- S32 end = static_cast<U32>(result[0].second - text);
- url = entry.getUrl(std::string(text+start, end-start));
- }
- ensure_equals(testname, url, expected);
- }
-
- void dummyCallback(const std::string &url, const std::string &label, const std::string& icon)
- {
- }
-
- void testLabel(const std::string &testname, LLUrlEntryBase &entry,
- const char *text, const std::string &expected)
- {
- boost::regex regex = entry.getPattern();
- std::string label = "";
- boost::cmatch result;
- bool found = boost::regex_search(text, result, regex);
- if (found)
- {
- S32 start = static_cast<U32>(result[0].first - text);
- S32 end = static_cast<U32>(result[0].second - text);
- std::string url = std::string(text+start, end-start);
- label = entry.getLabel(url, boost::bind(dummyCallback, _1, _2, _3));
- }
- ensure_equals(testname, label, expected);
- }
-
- void testLocation(const std::string &testname, LLUrlEntryBase &entry,
- const char *text, const std::string &expected)
- {
- boost::regex regex = entry.getPattern();
- std::string location = "";
- boost::cmatch result;
- bool found = boost::regex_search(text, result, regex);
- if (found)
- {
- S32 start = static_cast<U32>(result[0].first - text);
- S32 end = static_cast<U32>(result[0].second - text);
- std::string url = std::string(text+start, end-start);
- location = entry.getLocation(url);
- }
- ensure_equals(testname, location, expected);
- }
-
-
- template<> template<>
- void object::test<1>()
- {
- //
- // test LLUrlEntryHTTP - standard http Urls
- //
- LLUrlEntryHTTP url;
-
- testRegex("no valid url", url,
- "htp://slurl.com/",
- "");
-
- testRegex("simple http (1)", url,
- "http://slurl.com/",
- "http://slurl.com/");
-
- testRegex("simple http (2)", url,
- "http://slurl.com",
- "http://slurl.com");
-
- testRegex("simple http (3)", url,
- "http://slurl.com/about.php",
- "http://slurl.com/about.php");
-
- testRegex("simple https", url,
- "https://slurl.com/about.php",
- "https://slurl.com/about.php");
-
- testRegex("http in text (1)", url,
- "XX http://slurl.com/ XX",
- "http://slurl.com/");
-
- testRegex("http in text (2)", url,
- "XX http://slurl.com/about.php XX",
- "http://slurl.com/about.php");
-
- testRegex("https in text", url,
- "XX https://slurl.com/about.php XX",
- "https://slurl.com/about.php");
-
- testRegex("two http urls", url,
- "XX http://slurl.com/about.php http://secondlife.com/ XX",
- "http://slurl.com/about.php");
-
- testRegex("http url with port and username", url,
- "XX http://nobody@slurl.com:80/about.php http://secondlife.com/ XX",
- "http://nobody@slurl.com:80/about.php");
-
- testRegex("http url with port, username, and query string", url,
- "XX http://nobody@slurl.com:80/about.php?title=hi%20there http://secondlife.com/ XX",
- "http://nobody@slurl.com:80/about.php?title=hi%20there");
-
- // note: terminating commas will be removed by LLUrlRegistry:findUrl()
- testRegex("http url with commas in middle and terminating", url,
- "XX http://slurl.com/?title=Hi,There, XX",
- "http://slurl.com/?title=Hi,There,");
-
- // note: terminating periods will be removed by LLUrlRegistry:findUrl()
- testRegex("http url with periods in middle and terminating", url,
- "XX http://slurl.com/index.php. XX",
- "http://slurl.com/index.php.");
-
- // DEV-19842: Closing parenthesis ")" breaks urls
- testRegex("http url with brackets (1)", url,
- "XX http://en.wikipedia.org/wiki/JIRA_(software) XX",
- "http://en.wikipedia.org/wiki/JIRA_(software)");
-
- // DEV-19842: Closing parenthesis ")" breaks urls
- testRegex("http url with brackets (2)", url,
- "XX http://jira.secondlife.com/secure/attachment/17990/eggy+avs+in+1.21.0+(93713)+public+nightly.jpg XX",
- "http://jira.secondlife.com/secure/attachment/17990/eggy+avs+in+1.21.0+(93713)+public+nightly.jpg");
-
- // DEV-10353: URLs in chat log terminated incorrectly when newline in chat
- testRegex("http url with newlines", url,
- "XX\nhttp://www.secondlife.com/\nXX",
- "http://www.secondlife.com/");
-
- testRegex("http url without tld shouldn't be decorated (1)", url,
- "http://test",
- "");
-
- testRegex("http url without tld shouldn't be decorated (2)", url,
- "http://test .com",
- "");
- }
-
- template<> template<>
- void object::test<2>()
- {
- //
- // test LLUrlEntryHTTPLabel - wiki-style http Urls with labels
- //
- LLUrlEntryHTTPLabel url;
-
- testRegex("invalid wiki url [1]", url,
- "[http://www.example.org]",
- "");
-
- testRegex("invalid wiki url [2]", url,
- "[http://www.example.org",
- "");
-
- testRegex("invalid wiki url [3]", url,
- "[http://www.example.org Label",
- "");
-
- testRegex("example.org with label (spaces)", url,
- "[http://www.example.org Text]",
- "http://www.example.org");
-
- testRegex("example.org with label (tabs)", url,
- "[http://www.example.org\t Text]",
- "http://www.example.org");
-
- testRegex("SL http URL with label", url,
- "[http://www.secondlife.com/ Second Life]",
- "http://www.secondlife.com/");
-
- testRegex("SL https URL with label", url,
- "XXX [https://www.secondlife.com/ Second Life] YYY",
- "https://www.secondlife.com/");
-
- testRegex("SL http URL with label", url,
- "[http://www.secondlife.com/?test=Hi%20There Second Life]",
- "http://www.secondlife.com/?test=Hi%20There");
- }
-
- template<> template<>
- void object::test<3>()
- {
- //
- // test LLUrlEntrySLURL - second life URLs
- //
- LLUrlEntrySLURL url;
-
- testRegex("no valid slurl [1]", url,
- "htp://slurl.com/secondlife/Ahern/50/50/50/",
- "");
-
- testRegex("no valid slurl [2]", url,
- "http://slurl.com/secondlife/",
- "");
-
- testRegex("no valid slurl [3]", url,
- "hhtp://slurl.com/secondlife/Ahern/50/FOO/50/",
- "");
-
- testRegex("Ahern (50,50,50) [1]", url,
- "http://slurl.com/secondlife/Ahern/50/50/50/",
- "http://slurl.com/secondlife/Ahern/50/50/50/");
-
- testRegex("Ahern (50,50,50) [2]", url,
- "XXX http://slurl.com/secondlife/Ahern/50/50/50/ XXX",
- "http://slurl.com/secondlife/Ahern/50/50/50/");
-
- testRegex("Ahern (50,50,50) [3]", url,
- "XXX http://slurl.com/secondlife/Ahern/50/50/50 XXX",
- "http://slurl.com/secondlife/Ahern/50/50/50");
-
- testRegex("Ahern (50,50,50) multicase", url,
- "XXX http://SLUrl.com/SecondLife/Ahern/50/50/50/ XXX",
- "http://SLUrl.com/SecondLife/Ahern/50/50/50/");
-
- testRegex("Ahern (50,50) [1]", url,
- "XXX http://slurl.com/secondlife/Ahern/50/50/ XXX",
- "http://slurl.com/secondlife/Ahern/50/50/");
-
- testRegex("Ahern (50,50) [2]", url,
- "XXX http://slurl.com/secondlife/Ahern/50/50 XXX",
- "http://slurl.com/secondlife/Ahern/50/50");
-
- testRegex("Ahern (50)", url,
- "XXX http://slurl.com/secondlife/Ahern/50 XXX",
- "http://slurl.com/secondlife/Ahern/50");
-
- testRegex("Ahern", url,
- "XXX http://slurl.com/secondlife/Ahern/ XXX",
- "http://slurl.com/secondlife/Ahern/");
-
- testRegex("Ahern SLURL with title", url,
- "XXX http://slurl.com/secondlife/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE! XXX",
- "http://slurl.com/secondlife/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE!");
-
- testRegex("Ahern SLURL with msg", url,
- "XXX http://slurl.com/secondlife/Ahern/50/50/50/?msg=Your%20text%20here. XXX",
- "http://slurl.com/secondlife/Ahern/50/50/50/?msg=Your%20text%20here.");
-
- // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat
- testRegex("SLURL with brackets", url,
- "XXX http://slurl.com/secondlife/Burning%20Life%20(Hyper)/27/210/30 XXX",
- "http://slurl.com/secondlife/Burning%20Life%20(Hyper)/27/210/30");
-
- // DEV-35459: SLURLs and teleport Links not parsed properly
- testRegex("SLURL with quote", url,
- "XXX http://slurl.com/secondlife/A'ksha%20Oasis/41/166/701 XXX",
- "http://slurl.com/secondlife/A%27ksha%20Oasis/41/166/701");
- }
-
- template<> template<>
- void object::test<4>()
- {
- //
- // test LLUrlEntryAgent - secondlife://app/agent Urls
- //
- LLUrlEntryAgent url;
-
- testRegex("Invalid Agent Url", url,
- "secondlife:///app/agent/0e346d8b-4433-4d66-XXXX-fd37083abc4c/about",
- "");
-
- testRegex("Agent Url ", url,
- "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about",
- "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
-
- testRegex("Agent Url in text", url,
- "XXX secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about XXX",
- "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
-
- testRegex("Agent Url multicase", url,
- "XXX secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/About XXX",
- "secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/About");
-
- testRegex("Agent Url alternate command", url,
- "XXX secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar",
- "secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar");
-
- testRegex("Standalone Agent Url ", url,
- "x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about",
- "x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
-
- testRegex("Standalone Agent Url Multicase with Text", url,
- "M x-grid-location-info://lincoln.lindenlab.com/app/AGENT/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about M",
- "x-grid-location-info://lincoln.lindenlab.com/app/AGENT/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
- }
-
- template<> template<>
- void object::test<5>()
- {
- //
- // test LLUrlEntryGroup - secondlife://app/group Urls
- //
- LLUrlEntryGroup url;
-
- testRegex("Invalid Group Url", url,
- "secondlife:///app/group/00005ff3-4044-c79f-XXXX-fb28ae0df991/about",
- "");
-
- testRegex("Group Url ", url,
- "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about",
- "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about");
-
- testRegex("Group Url ", url,
- "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect",
- "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect");
-
- testRegex("Group Url in text", url,
- "XXX secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about XXX",
- "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about");
-
- testRegex("Group Url multicase", url,
- "XXX secondlife:///APP/Group/00005FF3-4044-c79f-9de8-fb28ae0df991/About XXX",
- "secondlife:///APP/Group/00005FF3-4044-c79f-9de8-fb28ae0df991/About");
-
- testRegex("Standalone Group Url ", url,
- "x-grid-location-info://lincoln.lindenlab.com/app/group/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about",
- "x-grid-location-info://lincoln.lindenlab.com/app/group/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
-
- testRegex("Standalone Group Url Multicase ith Text", url,
- "M x-grid-location-info://lincoln.lindenlab.com/app/GROUP/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about M",
- "x-grid-location-info://lincoln.lindenlab.com/app/GROUP/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
-
- }
-
- template<> template<>
- void object::test<6>()
- {
- //
- // test LLUrlEntryPlace - secondlife://<location> URLs
- //
- LLUrlEntryPlace url;
-
- testRegex("no valid slurl [1]", url,
- "secondlife://Ahern/FOO/50/",
- "");
-
- testRegex("Ahern (50,50,50) [1]", url,
- "secondlife://Ahern/50/50/50/",
- "secondlife://Ahern/50/50/50/");
-
- testRegex("Ahern (50,50,50) [2]", url,
- "XXX secondlife://Ahern/50/50/50/ XXX",
- "secondlife://Ahern/50/50/50/");
-
- testRegex("Ahern (50,50,50) [3]", url,
- "XXX secondlife://Ahern/50/50/50 XXX",
- "secondlife://Ahern/50/50/50");
-
- testRegex("Ahern (50,50,50) multicase", url,
- "XXX SecondLife://Ahern/50/50/50/ XXX",
- "SecondLife://Ahern/50/50/50/");
-
- testRegex("Ahern (50,50) [1]", url,
- "XXX secondlife://Ahern/50/50/ XXX",
- "secondlife://Ahern/50/50/");
-
- testRegex("Ahern (50,50) [2]", url,
- "XXX secondlife://Ahern/50/50 XXX",
- "secondlife://Ahern/50/50");
-
- // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat
- testRegex("SLURL with brackets", url,
- "XXX secondlife://Burning%20Life%20(Hyper)/27/210/30 XXX",
- "secondlife://Burning%20Life%20(Hyper)/27/210/30");
-
- // DEV-35459: SLURLs and teleport Links not parsed properly
- testRegex("SLURL with quote", url,
- "XXX secondlife://A'ksha%20Oasis/41/166/701 XXX",
- "secondlife://A%27ksha%20Oasis/41/166/701");
-
- testRegex("Standalone All Hands (50,50) [2] with text", url,
- "XXX x-grid-location-info://lincoln.lindenlab.com/region/All%20Hands/50/50/50 XXX",
- "x-grid-location-info://lincoln.lindenlab.com/region/All%20Hands/50/50/50");
- }
-
- template<> template<>
- void object::test<7>()
- {
- //
- // test LLUrlEntryParcel - secondlife://app/parcel Urls
- //
- LLUrlEntryParcel url;
-
- testRegex("Invalid Classified Url", url,
- "secondlife:///app/parcel/0000060e-4b39-e00b-XXXX-d98b1934e3a8/about",
- "");
-
- testRegex("Classified Url ", url,
- "secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about",
- "secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about");
-
- testRegex("Classified Url in text", url,
- "XXX secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about XXX",
- "secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about");
-
- testRegex("Classified Url multicase", url,
- "XXX secondlife:///APP/Parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/About XXX",
- "secondlife:///APP/Parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/About");
- }
- template<> template<>
- void object::test<8>()
- {
- //
- // test LLUrlEntryTeleport - secondlife://app/teleport URLs
- //
- LLUrlEntryTeleport url;
-
- testRegex("no valid teleport [1]", url,
- "http://slurl.com/secondlife/Ahern/50/50/50/",
- "");
-
- testRegex("no valid teleport [2]", url,
- "secondlife:///app/teleport/",
- "");
-
- testRegex("no valid teleport [3]", url,
- "second-life:///app/teleport/Ahern/50/50/50/",
- "");
-
- testRegex("no valid teleport [3]", url,
- "hhtp://slurl.com/secondlife/Ahern/50/FOO/50/",
- "");
-
- testRegex("Ahern (50,50,50) [1]", url,
- "secondlife:///app/teleport/Ahern/50/50/50/",
- "secondlife:///app/teleport/Ahern/50/50/50/");
-
- testRegex("Ahern (50,50,50) [2]", url,
- "XXX secondlife:///app/teleport/Ahern/50/50/50/ XXX",
- "secondlife:///app/teleport/Ahern/50/50/50/");
-
- testRegex("Ahern (50,50,50) [3]", url,
- "XXX secondlife:///app/teleport/Ahern/50/50/50 XXX",
- "secondlife:///app/teleport/Ahern/50/50/50");
-
- testRegex("Ahern (50,50,50) multicase", url,
- "XXX secondlife:///app/teleport/Ahern/50/50/50/ XXX",
- "secondlife:///app/teleport/Ahern/50/50/50/");
-
- testRegex("Ahern (50,50) [1]", url,
- "XXX secondlife:///app/teleport/Ahern/50/50/ XXX",
- "secondlife:///app/teleport/Ahern/50/50/");
-
- testRegex("Ahern (50,50) [2]", url,
- "XXX secondlife:///app/teleport/Ahern/50/50 XXX",
- "secondlife:///app/teleport/Ahern/50/50");
-
- testRegex("Ahern (50)", url,
- "XXX secondlife:///app/teleport/Ahern/50 XXX",
- "secondlife:///app/teleport/Ahern/50");
-
- testRegex("Ahern", url,
- "XXX secondlife:///app/teleport/Ahern/ XXX",
- "secondlife:///app/teleport/Ahern/");
-
- testRegex("Ahern teleport with title", url,
- "XXX secondlife:///app/teleport/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE! XXX",
- "secondlife:///app/teleport/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE!");
-
- testRegex("Ahern teleport with msg", url,
- "XXX secondlife:///app/teleport/Ahern/50/50/50/?msg=Your%20text%20here. XXX",
- "secondlife:///app/teleport/Ahern/50/50/50/?msg=Your%20text%20here.");
-
- // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat
- testRegex("Teleport with brackets", url,
- "XXX secondlife:///app/teleport/Burning%20Life%20(Hyper)/27/210/30 XXX",
- "secondlife:///app/teleport/Burning%20Life%20(Hyper)/27/210/30");
-
- // DEV-35459: SLURLs and teleport Links not parsed properly
- testRegex("Teleport url with quote", url,
- "XXX secondlife:///app/teleport/A'ksha%20Oasis/41/166/701 XXX",
- "secondlife:///app/teleport/A%27ksha%20Oasis/41/166/701");
-
- testRegex("Standalone All Hands", url,
- "XXX x-grid-location-info://lincoln.lindenlab.com/app/teleport/All%20Hands/50/50/50 XXX",
- "x-grid-location-info://lincoln.lindenlab.com/app/teleport/All%20Hands/50/50/50");
- }
-
- template<> template<>
- void object::test<9>()
- {
- //
- // test LLUrlEntrySL - general secondlife:// URLs
- //
- LLUrlEntrySL url;
-
- testRegex("no valid slapp [1]", url,
- "http:///app/",
- "");
-
- testRegex("valid slapp [1]", url,
- "secondlife:///app/",
- "secondlife:///app/");
-
- testRegex("valid slapp [2]", url,
- "secondlife:///app/teleport/Ahern/50/50/50/",
- "secondlife:///app/teleport/Ahern/50/50/50/");
-
- testRegex("valid slapp [3]", url,
- "secondlife:///app/foo",
- "secondlife:///app/foo");
-
- testRegex("valid slapp [4]", url,
- "secondlife:///APP/foo?title=Hi%20There",
- "secondlife:///APP/foo?title=Hi%20There");
-
- testRegex("valid slapp [5]", url,
- "secondlife://host/app/",
- "secondlife://host/app/");
-
- testRegex("valid slapp [6]", url,
- "secondlife://host:8080/foo/bar",
- "secondlife://host:8080/foo/bar");
- }
-
- template<> template<>
- void object::test<10>()
- {
- //
- // test LLUrlEntrySLLabel - general secondlife:// URLs with labels
- //
- LLUrlEntrySLLabel url;
-
- testRegex("invalid wiki url [1]", url,
- "[secondlife:///app/]",
- "");
-
- testRegex("invalid wiki url [2]", url,
- "[secondlife:///app/",
- "");
-
- testRegex("invalid wiki url [3]", url,
- "[secondlife:///app/ Label",
- "");
-
- testRegex("agent slurl with label (spaces)", url,
- "[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about Text]",
- "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
-
- testRegex("agent slurl with label (tabs)", url,
- "[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about\t Text]",
- "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
-
- testRegex("agent slurl with label", url,
- "[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about FirstName LastName]",
- "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
-
- testRegex("teleport slurl with label", url,
- "XXX [secondlife:///app/teleport/Ahern/50/50/50/ Teleport to Ahern] YYY",
- "secondlife:///app/teleport/Ahern/50/50/50/");
- }
-
- template<> template<>
- void object::test<11>()
- {
- //
- // test LLUrlEntryNoLink - turn off hyperlinking
- //
- LLUrlEntryNoLink url;
-
- testRegex("<nolink> [1]", url,
- "<nolink>google.com</nolink>",
- "google.com");
-
- testRegex("<nolink> [2]", url,
- "<nolink>google.com",
- "");
-
- testRegex("<nolink> [3]", url,
- "google.com</nolink>",
- "");
-
- testRegex("<nolink> [4]", url,
- "<nolink>Hello World</nolink>",
- "Hello World");
-
- testRegex("<nolink> [5]", url,
- "<nolink>My Object</nolink>",
- "My Object");
- }
-
- template<> template<>
- void object::test<12>()
- {
- //
- // test LLUrlEntryRegion - secondlife:///app/region/<location> URLs
- //
- LLUrlEntryRegion url;
-
- // Regex tests.
- testRegex("no valid region", url,
- "secondlife:///app/region/",
- "");
-
- testRegex("invalid coords", url,
- "secondlife:///app/region/Korea2/a/b/c",
- "secondlife:///app/region/Korea2/"); // don't count invalid coords
-
- testRegex("Ahern (50,50,50) [1]", url,
- "secondlife:///app/region/Ahern/50/50/50/",
- "secondlife:///app/region/Ahern/50/50/50/");
-
- testRegex("Ahern (50,50,50) [2]", url,
- "XXX secondlife:///app/region/Ahern/50/50/50/ XXX",
- "secondlife:///app/region/Ahern/50/50/50/");
-
- testRegex("Ahern (50,50,50) [3]", url,
- "XXX secondlife:///app/region/Ahern/50/50/50 XXX",
- "secondlife:///app/region/Ahern/50/50/50");
-
- testRegex("Ahern (50,50,50) multicase", url,
- "XXX secondlife:///app/region/Ahern/50/50/50/ XXX",
- "secondlife:///app/region/Ahern/50/50/50/");
-
- testRegex("Ahern (50,50) [1]", url,
- "XXX secondlife:///app/region/Ahern/50/50/ XXX",
- "secondlife:///app/region/Ahern/50/50/");
-
- testRegex("Ahern (50,50) [2]", url,
- "XXX secondlife:///app/region/Ahern/50/50 XXX",
- "secondlife:///app/region/Ahern/50/50");
-
- // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat
- testRegex("Region with brackets", url,
- "XXX secondlife:///app/region/Burning%20Life%20(Hyper)/27/210/30 XXX",
- "secondlife:///app/region/Burning%20Life%20(Hyper)/27/210/30");
-
- // Rendering tests.
- testLabel("Render /app/region/Ahern/50/50/50/", url,
- "secondlife:///app/region/Ahern/50/50/50/",
- "Ahern (50,50,50)");
-
- testLabel("Render /app/region/Ahern/50/50/50", url,
- "secondlife:///app/region/Ahern/50/50/50",
- "Ahern (50,50,50)");
-
- testLabel("Render /app/region/Ahern/50/50/", url,
- "secondlife:///app/region/Ahern/50/50/",
- "Ahern (50,50)");
-
- testLabel("Render /app/region/Ahern/50/50", url,
- "secondlife:///app/region/Ahern/50/50",
- "Ahern (50,50)");
-
- testLabel("Render /app/region/Ahern/50/", url,
- "secondlife:///app/region/Ahern/50/",
- "Ahern (50)");
-
- testLabel("Render /app/region/Ahern/50", url,
- "secondlife:///app/region/Ahern/50",
- "Ahern (50)");
-
- testLabel("Render /app/region/Ahern/", url,
- "secondlife:///app/region/Ahern/",
- "Ahern");
-
- testLabel("Render /app/region/Ahern/ within context", url,
- "XXX secondlife:///app/region/Ahern/ XXX",
- "Ahern");
-
- testLabel("Render /app/region/Ahern", url,
- "secondlife:///app/region/Ahern",
- "Ahern");
-
- testLabel("Render /app/region/Ahern within context", url,
- "XXX secondlife:///app/region/Ahern XXX",
- "Ahern");
-
- testLabel("Render /app/region/Product%20Engine/", url,
- "secondlife:///app/region/Product%20Engine/",
- "Product Engine");
-
- testLabel("Render /app/region/Product%20Engine", url,
- "secondlife:///app/region/Product%20Engine",
- "Product Engine");
-
- // Location parsing texts.
- testLocation("Location /app/region/Ahern/50/50/50/", url,
- "secondlife:///app/region/Ahern/50/50/50/",
- "Ahern");
-
- testLocation("Location /app/region/Product%20Engine", url,
- "secondlife:///app/region/Product%20Engine",
- "Product Engine");
- }
-
- template<> template<>
- void object::test<13>()
- {
- //
- // test LLUrlEntryemail - general emails
- //
- LLUrlEntryEmail url;
-
- // Regex tests.
- testRegex("match e-mail addresses", url,
- "test@lindenlab.com",
- "mailto:test@lindenlab.com");
-
- testRegex("match e-mail addresses with mailto: prefix", url,
- "mailto:test@lindenlab.com",
- "mailto:test@lindenlab.com");
-
- testRegex("match e-mail addresses with different domains", url,
- "test@foo.org.us",
- "mailto:test@foo.org.us");
-
- testRegex("match e-mail addresses with different domains", url,
- "test@foo.bar",
- "mailto:test@foo.bar");
-
- testRegex("don't match incorrect e-mail addresses", url,
- "test @foo.com",
- "");
-
- testRegex("don't match incorrect e-mail addresses", url,
- "test@ foo.com",
- "");
- }
-
- template<> template<>
- void object::test<14>()
- {
- //
- // test LLUrlEntrySimpleSecondlifeURL - http://*.secondlife.com/* and http://*lindenlab.com/* urls
- //
- LLUrlEntrySecondlifeURL url;
-
- testRegex("match urls with protocol", url,
- "this url should match http://lindenlab.com/products/second-life",
- "http://lindenlab.com/products/second-life");
-
- testRegex("match urls with protocol", url,
- "search something https://marketplace.secondlife.com/products/search on marketplace and test the https",
- "https://marketplace.secondlife.com/products/search");
-
- testRegex("match HTTPS urls with port", url,
- "let's specify some port https://secondlife.com:888/status",
- "https://secondlife.com:888/status");
-
- testRegex("don't match HTTP urls with port", url,
- "let's specify some port for HTTP http://secondlife.com:888/status",
- "");
-
- testRegex("don't match urls w/o protocol", url,
- "looks like an url something www.marketplace.secondlife.com/products but no https prefix",
- "");
-
- testRegex("but with a protocol www is fine", url,
- "so let's add a protocol https://www.marketplace.secondlife.com:8888/products",
- "https://www.marketplace.secondlife.com:8888/products");
-
- testRegex("don't match urls w/o protocol", url,
- "and even no www something secondlife.com/status",
- "");
- }
-
- template<> template<>
- void object::test<15>()
- {
- //
- // test LLUrlEntrySimpleSecondlifeURL - http://*.secondlife.com and http://*lindenlab.com urls
- //
-
- LLUrlEntrySimpleSecondlifeURL url;
-
- testRegex("match urls with a protocol", url,
- "this url should match http://lindenlab.com",
- "http://lindenlab.com");
-
- testRegex("match urls with a protocol", url,
- "search something https://marketplace.secondlife.com on marketplace and test the https",
- "https://marketplace.secondlife.com");
-
- testRegex("don't match urls w/o protocol", url,
- "looks like an url something www.marketplace.secondlife.com but no https prefix",
- "");
-
- testRegex("but with a protocol www is fine", url,
- "so let's add a protocol http://www.marketplace.secondlife.com",
- "http://www.marketplace.secondlife.com");
-
- testRegex("don't match urls w/o protocol", url,
- "and even no www something lindenlab.com",
- "");
- }
-
- template<> template<>
- void object::test<16>()
- {
- //
- // test LLUrlEntryIPv6
- //
- LLUrlEntryIPv6 url;
-
- // Regex tests.
- testRegex("match urls with a protocol", url,
- "this url should match http://[::1]",
- "http://[::1]");
-
- testRegex("match urls with a protocol and query", url,
- "this url should match http://[::1]/file.mp3",
- "http://[::1]/file.mp3");
-
- testRegex("match urls with a protocol", url,
- "this url should match http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]",
- "http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]");
-
- testRegex("match urls with port", url,
- "let's specify some port http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]:8080",
- "http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]:8080");
-
- testRegex("don't match urls w/o protocol", url,
- "looks like an url something [2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d] but no https prefix",
- "");
-
- testRegex("don't match incorrect urls", url,
- "http://[ 2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d ]",
- "");
- }
-}
+/**
+ * @file llurlentry_test.cpp
+ * @author Martin Reddy
+ * @brief Unit tests for LLUrlEntry objects
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "../llurlentry.h"
+#include "../lluictrl.h"
+//#include "llurlentry_stub.cpp"
+#include "lltut.h"
+#include "../lluicolortable.h"
+#include "../llrender/lluiimage.h"
+#include "../llmessage/llexperiencecache.h"
+
+#include <boost/regex.hpp>
+
+#if LL_WINDOWS
+// because something pulls in window and lldxdiag dependencies which in turn need wbemuuid.lib
+ #pragma comment(lib, "wbemuuid.lib")
+#endif
+
+
+// namespace LLExperienceCache
+// {
+// const LLSD& get( const LLUUID& key)
+// {
+// static LLSD boo;
+// return boo;
+// }
+//
+// void get( const LLUUID& key, callback_slot_t slot ){}
+//
+// }
+
+/*==========================================================================*|
+typedef std::map<std::string, LLControlGroup*> settings_map_t;
+settings_map_t LLUI::sSettingGroups;
+
+bool LLControlGroup::getBOOL(const std::string& name)
+{
+ return false;
+}
+
+LLUIColor LLUIColorTable::getColor(const std::string& name, const LLColor4& default_color) const
+{
+ return LLUIColor();
+}
+
+LLUIColor::LLUIColor() : mColorPtr(NULL) {}
+
+LLUIImage::LLUIImage(const std::string& name, LLPointer<LLTexture> image)
+{
+}
+
+LLUIImage::~LLUIImage()
+{
+}
+
+//virtual
+S32 LLUIImage::getWidth() const
+{
+ return 0;
+}
+
+//virtual
+S32 LLUIImage::getHeight() const
+{
+ return 0;
+}
+|*==========================================================================*/
+
+namespace tut
+{
+ struct LLUrlEntryData
+ {
+ };
+
+ typedef test_group<LLUrlEntryData> factory;
+ typedef factory::object object;
+}
+
+namespace
+{
+ tut::factory tf("LLUrlEntry");
+}
+
+namespace tut
+{
+ void testRegex(const std::string &testname, LLUrlEntryBase &entry,
+ const char *text, const std::string &expected)
+ {
+ boost::regex regex = entry.getPattern();
+ std::string url = "";
+ boost::cmatch result;
+ bool found = boost::regex_search(text, result, regex);
+ if (found)
+ {
+ S32 start = static_cast<U32>(result[0].first - text);
+ S32 end = static_cast<U32>(result[0].second - text);
+ url = entry.getUrl(std::string(text+start, end-start));
+ }
+ ensure_equals(testname, url, expected);
+ }
+
+ void dummyCallback(const std::string &url, const std::string &label, const std::string& icon)
+ {
+ }
+
+ void testLabel(const std::string &testname, LLUrlEntryBase &entry,
+ const char *text, const std::string &expected)
+ {
+ boost::regex regex = entry.getPattern();
+ std::string label = "";
+ boost::cmatch result;
+ bool found = boost::regex_search(text, result, regex);
+ if (found)
+ {
+ S32 start = static_cast<U32>(result[0].first - text);
+ S32 end = static_cast<U32>(result[0].second - text);
+ std::string url = std::string(text+start, end-start);
+ label = entry.getLabel(url, boost::bind(dummyCallback, _1, _2, _3));
+ }
+ ensure_equals(testname, label, expected);
+ }
+
+ void testLocation(const std::string &testname, LLUrlEntryBase &entry,
+ const char *text, const std::string &expected)
+ {
+ boost::regex regex = entry.getPattern();
+ std::string location = "";
+ boost::cmatch result;
+ bool found = boost::regex_search(text, result, regex);
+ if (found)
+ {
+ S32 start = static_cast<U32>(result[0].first - text);
+ S32 end = static_cast<U32>(result[0].second - text);
+ std::string url = std::string(text+start, end-start);
+ location = entry.getLocation(url);
+ }
+ ensure_equals(testname, location, expected);
+ }
+
+
+ template<> template<>
+ void object::test<1>()
+ {
+ //
+ // test LLUrlEntryHTTP - standard http Urls
+ //
+ LLUrlEntryHTTP url;
+
+ testRegex("no valid url", url,
+ "htp://slurl.com/",
+ "");
+
+ testRegex("simple http (1)", url,
+ "http://slurl.com/",
+ "http://slurl.com/");
+
+ testRegex("simple http (2)", url,
+ "http://slurl.com",
+ "http://slurl.com");
+
+ testRegex("simple http (3)", url,
+ "http://slurl.com/about.php",
+ "http://slurl.com/about.php");
+
+ testRegex("simple https", url,
+ "https://slurl.com/about.php",
+ "https://slurl.com/about.php");
+
+ testRegex("http in text (1)", url,
+ "XX http://slurl.com/ XX",
+ "http://slurl.com/");
+
+ testRegex("http in text (2)", url,
+ "XX http://slurl.com/about.php XX",
+ "http://slurl.com/about.php");
+
+ testRegex("https in text", url,
+ "XX https://slurl.com/about.php XX",
+ "https://slurl.com/about.php");
+
+ testRegex("two http urls", url,
+ "XX http://slurl.com/about.php http://secondlife.com/ XX",
+ "http://slurl.com/about.php");
+
+ testRegex("http url with port and username", url,
+ "XX http://nobody@slurl.com:80/about.php http://secondlife.com/ XX",
+ "http://nobody@slurl.com:80/about.php");
+
+ testRegex("http url with port, username, and query string", url,
+ "XX http://nobody@slurl.com:80/about.php?title=hi%20there http://secondlife.com/ XX",
+ "http://nobody@slurl.com:80/about.php?title=hi%20there");
+
+ // note: terminating commas will be removed by LLUrlRegistry:findUrl()
+ testRegex("http url with commas in middle and terminating", url,
+ "XX http://slurl.com/?title=Hi,There, XX",
+ "http://slurl.com/?title=Hi,There,");
+
+ // note: terminating periods will be removed by LLUrlRegistry:findUrl()
+ testRegex("http url with periods in middle and terminating", url,
+ "XX http://slurl.com/index.php. XX",
+ "http://slurl.com/index.php.");
+
+ // DEV-19842: Closing parenthesis ")" breaks urls
+ testRegex("http url with brackets (1)", url,
+ "XX http://en.wikipedia.org/wiki/JIRA_(software) XX",
+ "http://en.wikipedia.org/wiki/JIRA_(software)");
+
+ // DEV-19842: Closing parenthesis ")" breaks urls
+ testRegex("http url with brackets (2)", url,
+ "XX http://jira.secondlife.com/secure/attachment/17990/eggy+avs+in+1.21.0+(93713)+public+nightly.jpg XX",
+ "http://jira.secondlife.com/secure/attachment/17990/eggy+avs+in+1.21.0+(93713)+public+nightly.jpg");
+
+ // DEV-10353: URLs in chat log terminated incorrectly when newline in chat
+ testRegex("http url with newlines", url,
+ "XX\nhttp://www.secondlife.com/\nXX",
+ "http://www.secondlife.com/");
+
+ testRegex("http url without tld shouldn't be decorated (1)", url,
+ "http://test",
+ "");
+
+ testRegex("http url without tld shouldn't be decorated (2)", url,
+ "http://test .com",
+ "");
+ }
+
+ template<> template<>
+ void object::test<2>()
+ {
+ //
+ // test LLUrlEntryHTTPLabel - wiki-style http Urls with labels
+ //
+ LLUrlEntryHTTPLabel url;
+
+ testRegex("invalid wiki url [1]", url,
+ "[http://www.example.org]",
+ "");
+
+ testRegex("invalid wiki url [2]", url,
+ "[http://www.example.org",
+ "");
+
+ testRegex("invalid wiki url [3]", url,
+ "[http://www.example.org Label",
+ "");
+
+ testRegex("example.org with label (spaces)", url,
+ "[http://www.example.org Text]",
+ "http://www.example.org");
+
+ testRegex("example.org with label (tabs)", url,
+ "[http://www.example.org\t Text]",
+ "http://www.example.org");
+
+ testRegex("SL http URL with label", url,
+ "[http://www.secondlife.com/ Second Life]",
+ "http://www.secondlife.com/");
+
+ testRegex("SL https URL with label", url,
+ "XXX [https://www.secondlife.com/ Second Life] YYY",
+ "https://www.secondlife.com/");
+
+ testRegex("SL http URL with label", url,
+ "[http://www.secondlife.com/?test=Hi%20There Second Life]",
+ "http://www.secondlife.com/?test=Hi%20There");
+ }
+
+ template<> template<>
+ void object::test<3>()
+ {
+ //
+ // test LLUrlEntrySLURL - second life URLs
+ //
+ LLUrlEntrySLURL url;
+
+ testRegex("no valid slurl [1]", url,
+ "htp://slurl.com/secondlife/Ahern/50/50/50/",
+ "");
+
+ testRegex("no valid slurl [2]", url,
+ "http://slurl.com/secondlife/",
+ "");
+
+ testRegex("no valid slurl [3]", url,
+ "hhtp://slurl.com/secondlife/Ahern/50/FOO/50/",
+ "");
+
+ testRegex("Ahern (50,50,50) [1]", url,
+ "http://slurl.com/secondlife/Ahern/50/50/50/",
+ "http://slurl.com/secondlife/Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50,50) [2]", url,
+ "XXX http://slurl.com/secondlife/Ahern/50/50/50/ XXX",
+ "http://slurl.com/secondlife/Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50,50) [3]", url,
+ "XXX http://slurl.com/secondlife/Ahern/50/50/50 XXX",
+ "http://slurl.com/secondlife/Ahern/50/50/50");
+
+ testRegex("Ahern (50,50,50) multicase", url,
+ "XXX http://SLUrl.com/SecondLife/Ahern/50/50/50/ XXX",
+ "http://SLUrl.com/SecondLife/Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50) [1]", url,
+ "XXX http://slurl.com/secondlife/Ahern/50/50/ XXX",
+ "http://slurl.com/secondlife/Ahern/50/50/");
+
+ testRegex("Ahern (50,50) [2]", url,
+ "XXX http://slurl.com/secondlife/Ahern/50/50 XXX",
+ "http://slurl.com/secondlife/Ahern/50/50");
+
+ testRegex("Ahern (50)", url,
+ "XXX http://slurl.com/secondlife/Ahern/50 XXX",
+ "http://slurl.com/secondlife/Ahern/50");
+
+ testRegex("Ahern", url,
+ "XXX http://slurl.com/secondlife/Ahern/ XXX",
+ "http://slurl.com/secondlife/Ahern/");
+
+ testRegex("Ahern SLURL with title", url,
+ "XXX http://slurl.com/secondlife/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE! XXX",
+ "http://slurl.com/secondlife/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE!");
+
+ testRegex("Ahern SLURL with msg", url,
+ "XXX http://slurl.com/secondlife/Ahern/50/50/50/?msg=Your%20text%20here. XXX",
+ "http://slurl.com/secondlife/Ahern/50/50/50/?msg=Your%20text%20here.");
+
+ // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat
+ testRegex("SLURL with brackets", url,
+ "XXX http://slurl.com/secondlife/Burning%20Life%20(Hyper)/27/210/30 XXX",
+ "http://slurl.com/secondlife/Burning%20Life%20(Hyper)/27/210/30");
+
+ // DEV-35459: SLURLs and teleport Links not parsed properly
+ testRegex("SLURL with quote", url,
+ "XXX http://slurl.com/secondlife/A'ksha%20Oasis/41/166/701 XXX",
+ "http://slurl.com/secondlife/A%27ksha%20Oasis/41/166/701");
+ }
+
+ template<> template<>
+ void object::test<4>()
+ {
+ //
+ // test LLUrlEntryAgent - secondlife://app/agent Urls
+ //
+ LLUrlEntryAgent url;
+
+ testRegex("Invalid Agent Url", url,
+ "secondlife:///app/agent/0e346d8b-4433-4d66-XXXX-fd37083abc4c/about",
+ "");
+
+ testRegex("Agent Url ", url,
+ "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about",
+ "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
+
+ testRegex("Agent Url in text", url,
+ "XXX secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about XXX",
+ "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
+
+ testRegex("Agent Url multicase", url,
+ "XXX secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/About XXX",
+ "secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/About");
+
+ testRegex("Agent Url alternate command", url,
+ "XXX secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar",
+ "secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar");
+
+ testRegex("Standalone Agent Url ", url,
+ "x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about",
+ "x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
+
+ testRegex("Standalone Agent Url Multicase with Text", url,
+ "M x-grid-location-info://lincoln.lindenlab.com/app/AGENT/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about M",
+ "x-grid-location-info://lincoln.lindenlab.com/app/AGENT/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
+ }
+
+ template<> template<>
+ void object::test<5>()
+ {
+ //
+ // test LLUrlEntryGroup - secondlife://app/group Urls
+ //
+ LLUrlEntryGroup url;
+
+ testRegex("Invalid Group Url", url,
+ "secondlife:///app/group/00005ff3-4044-c79f-XXXX-fb28ae0df991/about",
+ "");
+
+ testRegex("Group Url ", url,
+ "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about",
+ "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about");
+
+ testRegex("Group Url ", url,
+ "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect",
+ "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect");
+
+ testRegex("Group Url in text", url,
+ "XXX secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about XXX",
+ "secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about");
+
+ testRegex("Group Url multicase", url,
+ "XXX secondlife:///APP/Group/00005FF3-4044-c79f-9de8-fb28ae0df991/About XXX",
+ "secondlife:///APP/Group/00005FF3-4044-c79f-9de8-fb28ae0df991/About");
+
+ testRegex("Standalone Group Url ", url,
+ "x-grid-location-info://lincoln.lindenlab.com/app/group/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about",
+ "x-grid-location-info://lincoln.lindenlab.com/app/group/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
+
+ testRegex("Standalone Group Url Multicase ith Text", url,
+ "M x-grid-location-info://lincoln.lindenlab.com/app/GROUP/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about M",
+ "x-grid-location-info://lincoln.lindenlab.com/app/GROUP/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
+
+ }
+
+ template<> template<>
+ void object::test<6>()
+ {
+ //
+ // test LLUrlEntryPlace - secondlife://<location> URLs
+ //
+ LLUrlEntryPlace url;
+
+ testRegex("no valid slurl [1]", url,
+ "secondlife://Ahern/FOO/50/",
+ "");
+
+ testRegex("Ahern (50,50,50) [1]", url,
+ "secondlife://Ahern/50/50/50/",
+ "secondlife://Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50,50) [2]", url,
+ "XXX secondlife://Ahern/50/50/50/ XXX",
+ "secondlife://Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50,50) [3]", url,
+ "XXX secondlife://Ahern/50/50/50 XXX",
+ "secondlife://Ahern/50/50/50");
+
+ testRegex("Ahern (50,50,50) multicase", url,
+ "XXX SecondLife://Ahern/50/50/50/ XXX",
+ "SecondLife://Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50) [1]", url,
+ "XXX secondlife://Ahern/50/50/ XXX",
+ "secondlife://Ahern/50/50/");
+
+ testRegex("Ahern (50,50) [2]", url,
+ "XXX secondlife://Ahern/50/50 XXX",
+ "secondlife://Ahern/50/50");
+
+ // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat
+ testRegex("SLURL with brackets", url,
+ "XXX secondlife://Burning%20Life%20(Hyper)/27/210/30 XXX",
+ "secondlife://Burning%20Life%20(Hyper)/27/210/30");
+
+ // DEV-35459: SLURLs and teleport Links not parsed properly
+ testRegex("SLURL with quote", url,
+ "XXX secondlife://A'ksha%20Oasis/41/166/701 XXX",
+ "secondlife://A%27ksha%20Oasis/41/166/701");
+
+ testRegex("Standalone All Hands (50,50) [2] with text", url,
+ "XXX x-grid-location-info://lincoln.lindenlab.com/region/All%20Hands/50/50/50 XXX",
+ "x-grid-location-info://lincoln.lindenlab.com/region/All%20Hands/50/50/50");
+ }
+
+ template<> template<>
+ void object::test<7>()
+ {
+ //
+ // test LLUrlEntryParcel - secondlife://app/parcel Urls
+ //
+ LLUrlEntryParcel url;
+
+ testRegex("Invalid Classified Url", url,
+ "secondlife:///app/parcel/0000060e-4b39-e00b-XXXX-d98b1934e3a8/about",
+ "");
+
+ testRegex("Classified Url ", url,
+ "secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about",
+ "secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about");
+
+ testRegex("Classified Url in text", url,
+ "XXX secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about XXX",
+ "secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about");
+
+ testRegex("Classified Url multicase", url,
+ "XXX secondlife:///APP/Parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/About XXX",
+ "secondlife:///APP/Parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/About");
+ }
+ template<> template<>
+ void object::test<8>()
+ {
+ //
+ // test LLUrlEntryTeleport - secondlife://app/teleport URLs
+ //
+ LLUrlEntryTeleport url;
+
+ testRegex("no valid teleport [1]", url,
+ "http://slurl.com/secondlife/Ahern/50/50/50/",
+ "");
+
+ testRegex("no valid teleport [2]", url,
+ "secondlife:///app/teleport/",
+ "");
+
+ testRegex("no valid teleport [3]", url,
+ "second-life:///app/teleport/Ahern/50/50/50/",
+ "");
+
+ testRegex("no valid teleport [3]", url,
+ "hhtp://slurl.com/secondlife/Ahern/50/FOO/50/",
+ "");
+
+ testRegex("Ahern (50,50,50) [1]", url,
+ "secondlife:///app/teleport/Ahern/50/50/50/",
+ "secondlife:///app/teleport/Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50,50) [2]", url,
+ "XXX secondlife:///app/teleport/Ahern/50/50/50/ XXX",
+ "secondlife:///app/teleport/Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50,50) [3]", url,
+ "XXX secondlife:///app/teleport/Ahern/50/50/50 XXX",
+ "secondlife:///app/teleport/Ahern/50/50/50");
+
+ testRegex("Ahern (50,50,50) multicase", url,
+ "XXX secondlife:///app/teleport/Ahern/50/50/50/ XXX",
+ "secondlife:///app/teleport/Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50) [1]", url,
+ "XXX secondlife:///app/teleport/Ahern/50/50/ XXX",
+ "secondlife:///app/teleport/Ahern/50/50/");
+
+ testRegex("Ahern (50,50) [2]", url,
+ "XXX secondlife:///app/teleport/Ahern/50/50 XXX",
+ "secondlife:///app/teleport/Ahern/50/50");
+
+ testRegex("Ahern (50)", url,
+ "XXX secondlife:///app/teleport/Ahern/50 XXX",
+ "secondlife:///app/teleport/Ahern/50");
+
+ testRegex("Ahern", url,
+ "XXX secondlife:///app/teleport/Ahern/ XXX",
+ "secondlife:///app/teleport/Ahern/");
+
+ testRegex("Ahern teleport with title", url,
+ "XXX secondlife:///app/teleport/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE! XXX",
+ "secondlife:///app/teleport/Ahern/50/50/50/?title=YOUR%20TITLE%20HERE!");
+
+ testRegex("Ahern teleport with msg", url,
+ "XXX secondlife:///app/teleport/Ahern/50/50/50/?msg=Your%20text%20here. XXX",
+ "secondlife:///app/teleport/Ahern/50/50/50/?msg=Your%20text%20here.");
+
+ // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat
+ testRegex("Teleport with brackets", url,
+ "XXX secondlife:///app/teleport/Burning%20Life%20(Hyper)/27/210/30 XXX",
+ "secondlife:///app/teleport/Burning%20Life%20(Hyper)/27/210/30");
+
+ // DEV-35459: SLURLs and teleport Links not parsed properly
+ testRegex("Teleport url with quote", url,
+ "XXX secondlife:///app/teleport/A'ksha%20Oasis/41/166/701 XXX",
+ "secondlife:///app/teleport/A%27ksha%20Oasis/41/166/701");
+
+ testRegex("Standalone All Hands", url,
+ "XXX x-grid-location-info://lincoln.lindenlab.com/app/teleport/All%20Hands/50/50/50 XXX",
+ "x-grid-location-info://lincoln.lindenlab.com/app/teleport/All%20Hands/50/50/50");
+ }
+
+ template<> template<>
+ void object::test<9>()
+ {
+ //
+ // test LLUrlEntrySL - general secondlife:// URLs
+ //
+ LLUrlEntrySL url;
+
+ testRegex("no valid slapp [1]", url,
+ "http:///app/",
+ "");
+
+ testRegex("valid slapp [1]", url,
+ "secondlife:///app/",
+ "secondlife:///app/");
+
+ testRegex("valid slapp [2]", url,
+ "secondlife:///app/teleport/Ahern/50/50/50/",
+ "secondlife:///app/teleport/Ahern/50/50/50/");
+
+ testRegex("valid slapp [3]", url,
+ "secondlife:///app/foo",
+ "secondlife:///app/foo");
+
+ testRegex("valid slapp [4]", url,
+ "secondlife:///APP/foo?title=Hi%20There",
+ "secondlife:///APP/foo?title=Hi%20There");
+
+ testRegex("valid slapp [5]", url,
+ "secondlife://host/app/",
+ "secondlife://host/app/");
+
+ testRegex("valid slapp [6]", url,
+ "secondlife://host:8080/foo/bar",
+ "secondlife://host:8080/foo/bar");
+ }
+
+ template<> template<>
+ void object::test<10>()
+ {
+ //
+ // test LLUrlEntrySLLabel - general secondlife:// URLs with labels
+ //
+ LLUrlEntrySLLabel url;
+
+ testRegex("invalid wiki url [1]", url,
+ "[secondlife:///app/]",
+ "");
+
+ testRegex("invalid wiki url [2]", url,
+ "[secondlife:///app/",
+ "");
+
+ testRegex("invalid wiki url [3]", url,
+ "[secondlife:///app/ Label",
+ "");
+
+ testRegex("agent slurl with label (spaces)", url,
+ "[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about Text]",
+ "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
+
+ testRegex("agent slurl with label (tabs)", url,
+ "[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about\t Text]",
+ "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
+
+ testRegex("agent slurl with label", url,
+ "[secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about FirstName LastName]",
+ "secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");
+
+ testRegex("teleport slurl with label", url,
+ "XXX [secondlife:///app/teleport/Ahern/50/50/50/ Teleport to Ahern] YYY",
+ "secondlife:///app/teleport/Ahern/50/50/50/");
+ }
+
+ template<> template<>
+ void object::test<11>()
+ {
+ //
+ // test LLUrlEntryNoLink - turn off hyperlinking
+ //
+ LLUrlEntryNoLink url;
+
+ testRegex("<nolink> [1]", url,
+ "<nolink>google.com</nolink>",
+ "google.com");
+
+ testRegex("<nolink> [2]", url,
+ "<nolink>google.com",
+ "");
+
+ testRegex("<nolink> [3]", url,
+ "google.com</nolink>",
+ "");
+
+ testRegex("<nolink> [4]", url,
+ "<nolink>Hello World</nolink>",
+ "Hello World");
+
+ testRegex("<nolink> [5]", url,
+ "<nolink>My Object</nolink>",
+ "My Object");
+ }
+
+ template<> template<>
+ void object::test<12>()
+ {
+ //
+ // test LLUrlEntryRegion - secondlife:///app/region/<location> URLs
+ //
+ LLUrlEntryRegion url;
+
+ // Regex tests.
+ testRegex("no valid region", url,
+ "secondlife:///app/region/",
+ "");
+
+ testRegex("invalid coords", url,
+ "secondlife:///app/region/Korea2/a/b/c",
+ "secondlife:///app/region/Korea2/"); // don't count invalid coords
+
+ testRegex("Ahern (50,50,50) [1]", url,
+ "secondlife:///app/region/Ahern/50/50/50/",
+ "secondlife:///app/region/Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50,50) [2]", url,
+ "XXX secondlife:///app/region/Ahern/50/50/50/ XXX",
+ "secondlife:///app/region/Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50,50) [3]", url,
+ "XXX secondlife:///app/region/Ahern/50/50/50 XXX",
+ "secondlife:///app/region/Ahern/50/50/50");
+
+ testRegex("Ahern (50,50,50) multicase", url,
+ "XXX secondlife:///app/region/Ahern/50/50/50/ XXX",
+ "secondlife:///app/region/Ahern/50/50/50/");
+
+ testRegex("Ahern (50,50) [1]", url,
+ "XXX secondlife:///app/region/Ahern/50/50/ XXX",
+ "secondlife:///app/region/Ahern/50/50/");
+
+ testRegex("Ahern (50,50) [2]", url,
+ "XXX secondlife:///app/region/Ahern/50/50 XXX",
+ "secondlife:///app/region/Ahern/50/50");
+
+ // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat
+ testRegex("Region with brackets", url,
+ "XXX secondlife:///app/region/Burning%20Life%20(Hyper)/27/210/30 XXX",
+ "secondlife:///app/region/Burning%20Life%20(Hyper)/27/210/30");
+
+ // Rendering tests.
+ testLabel("Render /app/region/Ahern/50/50/50/", url,
+ "secondlife:///app/region/Ahern/50/50/50/",
+ "Ahern (50,50,50)");
+
+ testLabel("Render /app/region/Ahern/50/50/50", url,
+ "secondlife:///app/region/Ahern/50/50/50",
+ "Ahern (50,50,50)");
+
+ testLabel("Render /app/region/Ahern/50/50/", url,
+ "secondlife:///app/region/Ahern/50/50/",
+ "Ahern (50,50)");
+
+ testLabel("Render /app/region/Ahern/50/50", url,
+ "secondlife:///app/region/Ahern/50/50",
+ "Ahern (50,50)");
+
+ testLabel("Render /app/region/Ahern/50/", url,
+ "secondlife:///app/region/Ahern/50/",
+ "Ahern (50)");
+
+ testLabel("Render /app/region/Ahern/50", url,
+ "secondlife:///app/region/Ahern/50",
+ "Ahern (50)");
+
+ testLabel("Render /app/region/Ahern/", url,
+ "secondlife:///app/region/Ahern/",
+ "Ahern");
+
+ testLabel("Render /app/region/Ahern/ within context", url,
+ "XXX secondlife:///app/region/Ahern/ XXX",
+ "Ahern");
+
+ testLabel("Render /app/region/Ahern", url,
+ "secondlife:///app/region/Ahern",
+ "Ahern");
+
+ testLabel("Render /app/region/Ahern within context", url,
+ "XXX secondlife:///app/region/Ahern XXX",
+ "Ahern");
+
+ testLabel("Render /app/region/Product%20Engine/", url,
+ "secondlife:///app/region/Product%20Engine/",
+ "Product Engine");
+
+ testLabel("Render /app/region/Product%20Engine", url,
+ "secondlife:///app/region/Product%20Engine",
+ "Product Engine");
+
+ // Location parsing texts.
+ testLocation("Location /app/region/Ahern/50/50/50/", url,
+ "secondlife:///app/region/Ahern/50/50/50/",
+ "Ahern");
+
+ testLocation("Location /app/region/Product%20Engine", url,
+ "secondlife:///app/region/Product%20Engine",
+ "Product Engine");
+ }
+
+ template<> template<>
+ void object::test<13>()
+ {
+ //
+ // test LLUrlEntryemail - general emails
+ //
+ LLUrlEntryEmail url;
+
+ // Regex tests.
+ testRegex("match e-mail addresses", url,
+ "test@lindenlab.com",
+ "mailto:test@lindenlab.com");
+
+ testRegex("match e-mail addresses with mailto: prefix", url,
+ "mailto:test@lindenlab.com",
+ "mailto:test@lindenlab.com");
+
+ testRegex("match e-mail addresses with different domains", url,
+ "test@foo.org.us",
+ "mailto:test@foo.org.us");
+
+ testRegex("match e-mail addresses with different domains", url,
+ "test@foo.bar",
+ "mailto:test@foo.bar");
+
+ testRegex("don't match incorrect e-mail addresses", url,
+ "test @foo.com",
+ "");
+
+ testRegex("don't match incorrect e-mail addresses", url,
+ "test@ foo.com",
+ "");
+ }
+
+ template<> template<>
+ void object::test<14>()
+ {
+ //
+ // test LLUrlEntrySimpleSecondlifeURL - http://*.secondlife.com/* and http://*lindenlab.com/* urls
+ //
+ LLUrlEntrySecondlifeURL url;
+
+ testRegex("match urls with protocol", url,
+ "this url should match http://lindenlab.com/products/second-life",
+ "http://lindenlab.com/products/second-life");
+
+ testRegex("match urls with protocol", url,
+ "search something https://marketplace.secondlife.com/products/search on marketplace and test the https",
+ "https://marketplace.secondlife.com/products/search");
+
+ testRegex("match HTTPS urls with port", url,
+ "let's specify some port https://secondlife.com:888/status",
+ "https://secondlife.com:888/status");
+
+ testRegex("don't match HTTP urls with port", url,
+ "let's specify some port for HTTP http://secondlife.com:888/status",
+ "");
+
+ testRegex("don't match urls w/o protocol", url,
+ "looks like an url something www.marketplace.secondlife.com/products but no https prefix",
+ "");
+
+ testRegex("but with a protocol www is fine", url,
+ "so let's add a protocol https://www.marketplace.secondlife.com:8888/products",
+ "https://www.marketplace.secondlife.com:8888/products");
+
+ testRegex("don't match urls w/o protocol", url,
+ "and even no www something secondlife.com/status",
+ "");
+ }
+
+ template<> template<>
+ void object::test<15>()
+ {
+ //
+ // test LLUrlEntrySimpleSecondlifeURL - http://*.secondlife.com and http://*lindenlab.com urls
+ //
+
+ LLUrlEntrySimpleSecondlifeURL url;
+
+ testRegex("match urls with a protocol", url,
+ "this url should match http://lindenlab.com",
+ "http://lindenlab.com");
+
+ testRegex("match urls with a protocol", url,
+ "search something https://marketplace.secondlife.com on marketplace and test the https",
+ "https://marketplace.secondlife.com");
+
+ testRegex("don't match urls w/o protocol", url,
+ "looks like an url something www.marketplace.secondlife.com but no https prefix",
+ "");
+
+ testRegex("but with a protocol www is fine", url,
+ "so let's add a protocol http://www.marketplace.secondlife.com",
+ "http://www.marketplace.secondlife.com");
+
+ testRegex("don't match urls w/o protocol", url,
+ "and even no www something lindenlab.com",
+ "");
+ }
+
+ template<> template<>
+ void object::test<16>()
+ {
+ //
+ // test LLUrlEntryIPv6
+ //
+ LLUrlEntryIPv6 url;
+
+ // Regex tests.
+ testRegex("match urls with a protocol", url,
+ "this url should match http://[::1]",
+ "http://[::1]");
+
+ testRegex("match urls with a protocol and query", url,
+ "this url should match http://[::1]/file.mp3",
+ "http://[::1]/file.mp3");
+
+ testRegex("match urls with a protocol", url,
+ "this url should match http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]",
+ "http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]");
+
+ testRegex("match urls with port", url,
+ "let's specify some port http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]:8080",
+ "http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]:8080");
+
+ testRegex("don't match urls w/o protocol", url,
+ "looks like an url something [2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d] but no https prefix",
+ "");
+
+ testRegex("don't match incorrect urls", url,
+ "http://[ 2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d ]",
+ "");
+ }
+}
diff --git a/indra/llui/tests/llurlmatch_test.cpp b/indra/llui/tests/llurlmatch_test.cpp
index 843886eb69..d03efbc1c9 100644
--- a/indra/llui/tests/llurlmatch_test.cpp
+++ b/indra/llui/tests/llurlmatch_test.cpp
@@ -6,21 +6,21 @@
* $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$
*/
@@ -34,7 +34,7 @@
// link seams
LLUIColor::LLUIColor()
- : mColorPtr(NULL)
+ : mColorPtr(NULL)
{}
LLStyle::Params::Params()
@@ -52,228 +52,228 @@ LLUIImage::~LLUIImage()
//virtual
S32 LLUIImage::getWidth() const
{
- return 0;
+ return 0;
}
//virtual
S32 LLUIImage::getHeight() const
{
- return 0;
+ return 0;
}
namespace LLInitParam
{
- ParamValue<LLUIColor>::ParamValue(const LLUIColor& color)
- : super_t(color)
- {}
-
- void ParamValue<LLUIColor>::updateValueFromBlock()
- {}
-
- void ParamValue<LLUIColor>::updateBlockFromValue(bool)
- {}
-
- bool ParamCompare<const LLFontGL*, false>::equals(const LLFontGL* a, const LLFontGL* b)
- {
- return false;
- }
-
-
- ParamValue<const LLFontGL*>::ParamValue(const LLFontGL* fontp)
- : super_t(fontp)
- {}
-
- void ParamValue<const LLFontGL*>::updateValueFromBlock()
- {}
-
- void ParamValue<const LLFontGL*>::updateBlockFromValue(bool)
- {}
-
- void TypeValues<LLFontGL::HAlign>::declareValues()
- {}
-
- void TypeValues<LLFontGL::VAlign>::declareValues()
- {}
-
- void TypeValues<LLFontGL::ShadowType>::declareValues()
- {}
-
- void ParamValue<LLUIImage*>::updateValueFromBlock()
- {}
-
- void ParamValue<LLUIImage*>::updateBlockFromValue(bool)
- {}
-
- bool ParamCompare<LLUIImage*, false>::equals(
- LLUIImage* const &a,
- LLUIImage* const &b)
- {
- return false;
- }
-
- bool ParamCompare<LLUIColor, false>::equals(const LLUIColor &a, const LLUIColor &b)
- {
- return false;
- }
+ ParamValue<LLUIColor>::ParamValue(const LLUIColor& color)
+ : super_t(color)
+ {}
+
+ void ParamValue<LLUIColor>::updateValueFromBlock()
+ {}
+
+ void ParamValue<LLUIColor>::updateBlockFromValue(bool)
+ {}
+
+ bool ParamCompare<const LLFontGL*, false>::equals(const LLFontGL* a, const LLFontGL* b)
+ {
+ return false;
+ }
+
+
+ ParamValue<const LLFontGL*>::ParamValue(const LLFontGL* fontp)
+ : super_t(fontp)
+ {}
+
+ void ParamValue<const LLFontGL*>::updateValueFromBlock()
+ {}
+
+ void ParamValue<const LLFontGL*>::updateBlockFromValue(bool)
+ {}
+
+ void TypeValues<LLFontGL::HAlign>::declareValues()
+ {}
+
+ void TypeValues<LLFontGL::VAlign>::declareValues()
+ {}
+
+ void TypeValues<LLFontGL::ShadowType>::declareValues()
+ {}
+
+ void ParamValue<LLUIImage*>::updateValueFromBlock()
+ {}
+
+ void ParamValue<LLUIImage*>::updateBlockFromValue(bool)
+ {}
+
+ bool ParamCompare<LLUIImage*, false>::equals(
+ LLUIImage* const &a,
+ LLUIImage* const &b)
+ {
+ return false;
+ }
+
+ bool ParamCompare<LLUIColor, false>::equals(const LLUIColor &a, const LLUIColor &b)
+ {
+ return false;
+ }
}
//static
LLFontGL* LLFontGL::getFontDefault()
{
- return NULL;
+ return NULL;
}
namespace tut
{
- struct LLUrlMatchData
- {
- };
+ struct LLUrlMatchData
+ {
+ };
- typedef test_group<LLUrlMatchData> factory;
- typedef factory::object object;
+ typedef test_group<LLUrlMatchData> factory;
+ typedef factory::object object;
}
namespace
{
- tut::factory tf("LLUrlMatch");
+ tut::factory tf("LLUrlMatch");
}
namespace tut
{
- template<> template<>
- void object::test<1>()
- {
- //
- // test the empty() method
- //
- LLUrlMatch match;
- ensure("empty()", match.empty());
-
- match.setValues(0, 1, "http://secondlife.com", "", "Second Life", "", "", LLStyle::Params(), "", "", LLUUID::null);
- ensure("! empty()", ! match.empty());
- }
-
- template<> template<>
- void object::test<2>()
- {
- //
- // test the getStart() method
- //
- LLUrlMatch match;
- ensure_equals("getStart() == 0", match.getStart(), 0);
-
- match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
- ensure_equals("getStart() == 10", match.getStart(), 10);
- }
-
- template<> template<>
- void object::test<3>()
- {
- //
- // test the getEnd() method
- //
- LLUrlMatch match;
- ensure_equals("getEnd() == 0", match.getEnd(), 0);
-
- match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
- ensure_equals("getEnd() == 20", match.getEnd(), 20);
- }
-
- template<> template<>
- void object::test<4>()
- {
- //
- // test the getUrl() method
- //
- LLUrlMatch match;
- ensure_equals("getUrl() == ''", match.getUrl(), "");
-
- match.setValues(10, 20, "http://slurl.com/", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
- ensure_equals("getUrl() == 'http://slurl.com/'", match.getUrl(), "http://slurl.com/");
-
- match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
- ensure_equals("getUrl() == '' (2)", match.getUrl(), "");
- }
-
- template<> template<>
- void object::test<5>()
- {
- //
- // test the getLabel() method
- //
- LLUrlMatch match;
- ensure_equals("getLabel() == ''", match.getLabel(), "");
-
- match.setValues(10, 20, "", "Label", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
- ensure_equals("getLabel() == 'Label'", match.getLabel(), "Label");
-
- match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
- ensure_equals("getLabel() == '' (2)", match.getLabel(), "");
- }
-
- template<> template<>
- void object::test<6>()
- {
- //
- // test the getTooltip() method
- //
- LLUrlMatch match;
- ensure_equals("getTooltip() == ''", match.getTooltip(), "");
-
- match.setValues(10, 20, "", "", "", "Info", "", LLStyle::Params(), "", "", LLUUID::null);
- ensure_equals("getTooltip() == 'Info'", match.getTooltip(), "Info");
-
- match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
- ensure_equals("getTooltip() == '' (2)", match.getTooltip(), "");
- }
-
- template<> template<>
- void object::test<7>()
- {
- //
- // test the getIcon() method
- //
- LLUrlMatch match;
- ensure_equals("getIcon() == ''", match.getIcon(), "");
-
- match.setValues(10, 20, "", "", "", "", "Icon", LLStyle::Params(), "", "", LLUUID::null);
- ensure_equals("getIcon() == 'Icon'", match.getIcon(), "Icon");
-
- match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
- ensure_equals("getIcon() == '' (2)", match.getIcon(), "");
- }
-
- template<> template<>
- void object::test<8>()
- {
- //
- // test the getMenuName() method
- //
- LLUrlMatch match;
- ensure("getMenuName() empty", match.getMenuName().empty());
-
- match.setValues(10, 20, "", "", "", "", "Icon", 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);
- ensure("getMenuName() empty (2)", match.getMenuName().empty());
- }
-
- template<> template<>
- void object::test<9>()
- {
- //
- // test the getLocation() method
- //
- LLUrlMatch match;
- ensure("getLocation() empty", match.getLocation().empty());
-
- match.setValues(10, 20, "", "", "", "", "Icon", LLStyle::Params(), "xui_file.xml", "Paris", LLUUID::null);
- ensure_equals("getLocation() == \"Paris\"", match.getLocation(), "Paris");
-
- match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
- ensure("getLocation() empty (2)", match.getLocation().empty());
- }
+ template<> template<>
+ void object::test<1>()
+ {
+ //
+ // test the empty() method
+ //
+ LLUrlMatch match;
+ ensure("empty()", match.empty());
+
+ match.setValues(0, 1, "http://secondlife.com", "", "Second Life", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ ensure("! empty()", ! match.empty());
+ }
+
+ template<> template<>
+ void object::test<2>()
+ {
+ //
+ // test the getStart() method
+ //
+ LLUrlMatch match;
+ ensure_equals("getStart() == 0", match.getStart(), 0);
+
+ match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ ensure_equals("getStart() == 10", match.getStart(), 10);
+ }
+
+ template<> template<>
+ void object::test<3>()
+ {
+ //
+ // test the getEnd() method
+ //
+ LLUrlMatch match;
+ ensure_equals("getEnd() == 0", match.getEnd(), 0);
+
+ match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ ensure_equals("getEnd() == 20", match.getEnd(), 20);
+ }
+
+ template<> template<>
+ void object::test<4>()
+ {
+ //
+ // test the getUrl() method
+ //
+ LLUrlMatch match;
+ ensure_equals("getUrl() == ''", match.getUrl(), "");
+
+ match.setValues(10, 20, "http://slurl.com/", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ ensure_equals("getUrl() == 'http://slurl.com/'", match.getUrl(), "http://slurl.com/");
+
+ match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ ensure_equals("getUrl() == '' (2)", match.getUrl(), "");
+ }
+
+ template<> template<>
+ void object::test<5>()
+ {
+ //
+ // test the getLabel() method
+ //
+ LLUrlMatch match;
+ ensure_equals("getLabel() == ''", match.getLabel(), "");
+
+ match.setValues(10, 20, "", "Label", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ ensure_equals("getLabel() == 'Label'", match.getLabel(), "Label");
+
+ match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ ensure_equals("getLabel() == '' (2)", match.getLabel(), "");
+ }
+
+ template<> template<>
+ void object::test<6>()
+ {
+ //
+ // test the getTooltip() method
+ //
+ LLUrlMatch match;
+ ensure_equals("getTooltip() == ''", match.getTooltip(), "");
+
+ match.setValues(10, 20, "", "", "", "Info", "", LLStyle::Params(), "", "", LLUUID::null);
+ ensure_equals("getTooltip() == 'Info'", match.getTooltip(), "Info");
+
+ match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ ensure_equals("getTooltip() == '' (2)", match.getTooltip(), "");
+ }
+
+ template<> template<>
+ void object::test<7>()
+ {
+ //
+ // test the getIcon() method
+ //
+ LLUrlMatch match;
+ ensure_equals("getIcon() == ''", match.getIcon(), "");
+
+ match.setValues(10, 20, "", "", "", "", "Icon", LLStyle::Params(), "", "", LLUUID::null);
+ ensure_equals("getIcon() == 'Icon'", match.getIcon(), "Icon");
+
+ match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ ensure_equals("getIcon() == '' (2)", match.getIcon(), "");
+ }
+
+ template<> template<>
+ void object::test<8>()
+ {
+ //
+ // test the getMenuName() method
+ //
+ LLUrlMatch match;
+ ensure("getMenuName() empty", match.getMenuName().empty());
+
+ match.setValues(10, 20, "", "", "", "", "Icon", 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);
+ ensure("getMenuName() empty (2)", match.getMenuName().empty());
+ }
+
+ template<> template<>
+ void object::test<9>()
+ {
+ //
+ // test the getLocation() method
+ //
+ LLUrlMatch match;
+ ensure("getLocation() empty", match.getLocation().empty());
+
+ match.setValues(10, 20, "", "", "", "", "Icon", LLStyle::Params(), "xui_file.xml", "Paris", LLUUID::null);
+ ensure_equals("getLocation() == \"Paris\"", match.getLocation(), "Paris");
+
+ match.setValues(10, 20, "", "", "", "", "", LLStyle::Params(), "", "", LLUUID::null);
+ ensure("getLocation() empty (2)", match.getLocation().empty());
+ }
}