summaryrefslogtreecommitdiff
path: root/indra/llui
diff options
context:
space:
mode:
authorJames Cook <james@lindenlab.com>2009-10-03 23:40:28 +0000
committerJames Cook <james@lindenlab.com>2009-10-03 23:40:28 +0000
commitada0f4fa221f2c7070fb02a2b7ff903bdde11c45 (patch)
tree0ede83511c304110138c01d16da2fff55162ef31 /indra/llui
parentb1a280841e1823a19658923a8eefeb67d1d70735 (diff)
Merge inspectors UI project, gooey-4, into viewer-2 trunk. Added new tooltips to 3D avatars, 2D avatar names, and 3D objects. Refactors tooltips and text boxes, line editors, and text editors. Breaks LLExpandableTextBox, but a fix is coming.
Resolved conflicts in lltexteditor.cpp, llchatitemscontainerctrl.cpp, llchatmsgbox.cpp, llfloaterbuycurrency.cpp, llnearbychat.cpp, floater_buy_currency.xml, and ru/strings.xml Merging revisions 134925-135157 of svn+ssh://svn.lindenlab.com/svn/linden/branches/gooey/gooey-4 into C:\source\viewer-2.0.0-3, respecting ancestry
Diffstat (limited to 'indra/llui')
-rw-r--r--indra/llui/CMakeLists.txt4
-rw-r--r--indra/llui/llbutton.cpp6
-rw-r--r--indra/llui/llbutton.h3
-rw-r--r--indra/llui/llcheckboxctrl.cpp2
-rw-r--r--indra/llui/llcombobox.cpp6
-rw-r--r--indra/llui/llcombobox.h2
-rw-r--r--indra/llui/llconsole.cpp4
-rw-r--r--indra/llui/llconsole.h7
-rw-r--r--indra/llui/lldraghandle.cpp9
-rw-r--r--indra/llui/lldraghandle.h6
-rw-r--r--indra/llui/llfloater.cpp2
-rw-r--r--indra/llui/llfloater.h2
-rw-r--r--indra/llui/llfocusmgr.cpp2
-rw-r--r--indra/llui/lllayoutstack.cpp2
-rw-r--r--indra/llui/lllayoutstack.h4
-rw-r--r--indra/llui/lllineeditor.cpp16
-rw-r--r--indra/llui/lllineeditor.h6
-rw-r--r--indra/llui/llmenubutton.cpp135
-rw-r--r--indra/llui/llmenubutton.h68
-rw-r--r--indra/llui/llmenugl.cpp96
-rw-r--r--indra/llui/llmenugl.h12
-rw-r--r--indra/llui/llmultisliderctrl.cpp2
-rw-r--r--indra/llui/llpanel.cpp9
-rw-r--r--indra/llui/llpanel.h6
-rw-r--r--indra/llui/llradiogroup.cpp2
-rw-r--r--indra/llui/llradiogroup.h13
-rw-r--r--indra/llui/llscrollbar.cpp13
-rw-r--r--indra/llui/llscrollbar.h3
-rw-r--r--indra/llui/llscrollcontainer.cpp50
-rw-r--r--indra/llui/llscrollcontainer.h4
-rw-r--r--indra/llui/llscrolllistcell.cpp7
-rw-r--r--indra/llui/llscrolllistcell.h13
-rw-r--r--indra/llui/llscrolllistctrl.cpp64
-rw-r--r--indra/llui/llscrolllistctrl.h6
-rw-r--r--indra/llui/llscrolllistitem.cpp1
-rw-r--r--indra/llui/llscrolllistitem.h2
-rw-r--r--indra/llui/llsliderctrl.cpp2
-rw-r--r--indra/llui/llspinctrl.cpp2
-rw-r--r--indra/llui/llstatview.cpp2
-rw-r--r--indra/llui/llstyle.cpp2
-rw-r--r--indra/llui/llstyle.h16
-rw-r--r--indra/llui/lltabcontainer.cpp8
-rw-r--r--indra/llui/lltabcontainer.h2
-rw-r--r--indra/llui/lltextbase.cpp2273
-rw-r--r--indra/llui/lltextbase.h382
-rw-r--r--indra/llui/lltextbox.cpp636
-rw-r--r--indra/llui/lltextbox.h95
-rw-r--r--indra/llui/lltexteditor.cpp1981
-rw-r--r--indra/llui/lltexteditor.h293
-rw-r--r--indra/llui/lltextparser.cpp4
-rw-r--r--indra/llui/lltextparser.h12
-rw-r--r--indra/llui/lltoggleablemenu.cpp82
-rw-r--r--indra/llui/lltoggleablemenu.h65
-rw-r--r--indra/llui/lltooltip.cpp334
-rw-r--r--indra/llui/lltooltip.h90
-rw-r--r--indra/llui/llui.cpp49
-rw-r--r--indra/llui/llui.h5
-rw-r--r--indra/llui/lluictrl.cpp30
-rw-r--r--indra/llui/lluictrl.h34
-rw-r--r--indra/llui/lluictrlfactory.cpp14
-rw-r--r--indra/llui/llurlentry.cpp10
-rw-r--r--indra/llui/llurlentry.h20
-rw-r--r--indra/llui/llurlmatch.cpp5
-rw-r--r--indra/llui/llurlmatch.h20
-rw-r--r--indra/llui/llurlregistry.cpp8
-rw-r--r--indra/llui/llview.cpp377
-rw-r--r--indra/llui/llview.h25
-rw-r--r--indra/llui/tests/llurlentry_test.cpp13
-rw-r--r--indra/llui/tests/llurlmatch_test.cpp34
69 files changed, 3766 insertions, 3748 deletions
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index 0a284f0088..d9169f57f9 100644
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -51,6 +51,7 @@ set(llui_SOURCE_FILES
lllayoutstack.cpp
lllineeditor.cpp
lllocalcliprect.cpp
+ llmenubutton.cpp
llmenugl.cpp
llmodaldialog.cpp
llmultifloater.cpp
@@ -87,6 +88,7 @@ set(llui_SOURCE_FILES
lltextparser.cpp
lltransientfloatermgr.cpp
lltransutil.cpp
+ lltoggleablemenu.cpp
lltooltip.cpp
llui.cpp
lluicolortable.cpp
@@ -137,6 +139,7 @@ set(llui_HEADER_FILES
lllazyvalue.h
lllineeditor.h
lllocalcliprect.h
+ llmenubutton.h
llmenugl.h
llmodaldialog.h
llmultifloater.h
@@ -171,6 +174,7 @@ set(llui_HEADER_FILES
lltextbox.h
lltexteditor.h
lltextparser.h
+ lltoggleablemenu.h
lltooltip.h
lltransientfloatermgr.h
lltransutil.h
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index b9613b502c..219c2ee254 100644
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -145,7 +145,8 @@ LLButton::LLButton(const LLButton::Params& p)
mRightHPad(p.pad_right),
mHoverGlowStrength(p.hover_glow_amount),
mCommitOnReturn(p.commit_on_return),
- mFadeWhenDisabled(FALSE)
+ mFadeWhenDisabled(FALSE),
+ mForcePressedState(FALSE)
{
static LLUICachedControl<S32> llbutton_orig_h_pad ("UIButtonOrigHPad", 0);
static Params default_params(LLUICtrlFactory::getDefaultParams<LLButton>());
@@ -536,7 +537,8 @@ void LLButton::draw()
bool enabled = isInEnabledChain();
bool pressed = pressed_by_keyboard
- || (hasMouseCapture() && pointInView(local_mouse_x, local_mouse_y));
+ || (hasMouseCapture() && pointInView(local_mouse_x, local_mouse_y))
+ || mForcePressedState;
bool selected = getToggleState();
bool use_glow_effect = FALSE;
diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h
index 04716d605b..73ba457d34 100644
--- a/indra/llui/llbutton.h
+++ b/indra/llui/llbutton.h
@@ -233,6 +233,8 @@ public:
static void toggleFloaterAndSetToggleState(LLUICtrl* ctrl, const LLSD& sdname);
static void setFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname);
static void showHelp(LLUICtrl* ctrl, const LLSD& sdname);
+
+ void setForcePressedState(BOOL b) { mForcePressedState = b; }
protected:
const LLPointer<LLUIImage>& getImageUnselected() const { return mImageUnselected; }
@@ -310,6 +312,7 @@ private:
BOOL mNeedsHighlight;
BOOL mCommitOnReturn;
BOOL mFadeWhenDisabled;
+ BOOL mForcePressedState;
LLFrameTimer mFlashingTimer;
};
diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp
index 455b17ffc7..7f0f9751db 100644
--- a/indra/llui/llcheckboxctrl.cpp
+++ b/indra/llui/llcheckboxctrl.cpp
@@ -97,7 +97,7 @@ LLCheckBoxCtrl::LLCheckBoxCtrl(const LLCheckBoxCtrl::Params& p)
LLTextBox::Params tbparams = p.label_text;
tbparams.rect(label_rect);
- tbparams.text(local_label);
+ tbparams.initial_value(local_label);
if (p.font.isProvided())
{
tbparams.font(p.font);
diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp
index 0170ac0c6a..36e309d639 100644
--- a/indra/llui/llcombobox.cpp
+++ b/indra/llui/llcombobox.cpp
@@ -714,11 +714,11 @@ void LLComboBox::onItemSelected(const LLSD& data)
}
}
-BOOL LLComboBox::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen)
+BOOL LLComboBox::handleToolTip(S32 x, S32 y, MASK mask)
{
std::string tool_tip;
- if(LLUICtrl::handleToolTip(x, y, msg, sticky_rect_screen))
+ if(LLUICtrl::handleToolTip(x, y, mask))
{
return TRUE;
}
@@ -731,7 +731,7 @@ BOOL LLComboBox::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_re
if( !tool_tip.empty() )
{
- LLToolTipMgr::instance().show(LLToolTipParams()
+ LLToolTipMgr::instance().show(LLToolTip::Params()
.message(tool_tip)
.sticky_rect(calcScreenRect()));
}
diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h
index 68cbfeeeeb..6285ca5170 100644
--- a/indra/llui/llcombobox.h
+++ b/indra/llui/llcombobox.h
@@ -113,7 +113,7 @@ public:
// LLView interface
virtual void onFocusLost();
- virtual BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect);
+ virtual BOOL handleToolTip(S32 x, S32 y, MASK mask);
virtual BOOL handleKeyHere(KEY key, MASK mask);
virtual BOOL handleUnicodeCharHere(llwchar uni_char);
diff --git a/indra/llui/llconsole.cpp b/indra/llui/llconsole.cpp
index 1e8b8a5537..285ce82d2d 100644
--- a/indra/llui/llconsole.cpp
+++ b/indra/llui/llconsole.cpp
@@ -61,7 +61,7 @@ const F32 FADE_DURATION = 2.f;
const S32 MIN_CONSOLE_WIDTH = 200;
LLConsole::LLConsole(const LLConsole::Params& p)
-: LLView(p),
+: LLUICtrl(p),
LLFixedBuffer(p.max_lines),
mLinePersistTime(p.persist_time), // seconds
mFont(p.font)
@@ -94,7 +94,7 @@ void LLConsole::reshape(S32 width, S32 height, BOOL called_from_parent)
mConsoleWidth = new_width;
mConsoleHeight= new_height;
- LLView::reshape(new_width, new_height, called_from_parent);
+ LLUICtrl::reshape(new_width, new_height, called_from_parent);
for(paragraph_t::iterator paragraph_it = mParagraphs.begin(); paragraph_it != mParagraphs.end(); paragraph_it++)
{
diff --git a/indra/llui/llconsole.h b/indra/llui/llconsole.h
index 56e1614948..5800a82922 100644
--- a/indra/llui/llconsole.h
+++ b/indra/llui/llconsole.h
@@ -34,14 +34,13 @@
#define LL_LLCONSOLE_H
#include "llfixedbuffer.h"
-#include "llview.h"
+#include "lluictrl.h"
#include "v4color.h"
#include <deque>
-class LLFontGL;
class LLSD;
-class LLConsole : public LLFixedBuffer, public LLView
+class LLConsole : public LLFixedBuffer, public LLUICtrl
{
public:
typedef enum e_font_size
@@ -51,7 +50,7 @@ public:
BIG = 1
} EFontSize;
- struct Params : public LLInitParam::Block<Params, LLView::Params>
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
{
Optional<U32> max_lines;
Optional<F32> persist_time;
diff --git a/indra/llui/lldraghandle.cpp b/indra/llui/lldraghandle.cpp
index 6e8e37ded3..8eccd709ce 100644
--- a/indra/llui/lldraghandle.cpp
+++ b/indra/llui/lldraghandle.cpp
@@ -108,7 +108,7 @@ void LLDragHandleTop::setTitle(const std::string& title)
LLTextBox::Params params;
params.name("Drag Handle Title");
params.rect(getRect());
- params.text(trimmed_title);
+ params.initial_value(trimmed_title);
params.font(font);
params.follows.flags(FOLLOWS_TOP | FOLLOWS_LEFT | FOLLOWS_RIGHT);
params.font_shadow(LLFontGL::DROP_SHADOW_SOFT);
@@ -120,7 +120,7 @@ void LLDragHandleTop::setTitle(const std::string& title)
}
-const std::string& LLDragHandleTop::getTitle() const
+std::string LLDragHandleTop::getTitle() const
{
return mTitleBox == NULL ? LLStringUtil::null : mTitleBox->getText();
}
@@ -138,7 +138,7 @@ void LLDragHandleLeft::setTitle(const std::string& )
}
-const std::string& LLDragHandleLeft::getTitle() const
+std::string LLDragHandleLeft::getTitle() const
{
return LLStringUtil::null;
}
@@ -256,7 +256,8 @@ void LLDragHandleTop::reshapeTitleBox()
getRect().getWidth() - LEFT_PAD - RIGHT_PAD,
title_height);
- mTitleBox->setRect( title_rect );
+ // calls reshape on mTitleBox
+ mTitleBox->setShape( title_rect );
}
void LLDragHandleTop::reshape(S32 width, S32 height, BOOL called_from_parent)
diff --git a/indra/llui/lldraghandle.h b/indra/llui/lldraghandle.h
index 88ec1d21f8..dc5410787b 100644
--- a/indra/llui/lldraghandle.h
+++ b/indra/llui/lldraghandle.h
@@ -74,7 +74,7 @@ public:
void setTitleVisible(BOOL visible);
virtual void setTitle( const std::string& title ) = 0;
- virtual const std::string& getTitle() const = 0;
+ virtual std::string getTitle() const = 0;
virtual BOOL handleHover(S32 x, S32 y, MASK mask);
virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
@@ -112,7 +112,7 @@ protected:
friend class LLUICtrlFactory;
public:
virtual void setTitle( const std::string& title );
- virtual const std::string& getTitle() const;
+ virtual std::string getTitle() const;
virtual void draw();
virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
@@ -130,7 +130,7 @@ protected:
friend class LLUICtrlFactory;
public:
virtual void setTitle( const std::string& title );
- virtual const std::string& getTitle() const;
+ virtual std::string getTitle() const;
virtual void draw();
virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index ff0288a32f..564e4d748f 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -785,7 +785,7 @@ void LLFloater::applyTitle()
}
}
-const std::string& LLFloater::getCurrentTitle() const
+std::string LLFloater::getCurrentTitle() const
{
return mDragHandle ? mDragHandle->getTitle() : LLStringUtil::null;
}
diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h
index 2a31ba4e8f..1dc5177f81 100644
--- a/indra/llui/llfloater.h
+++ b/indra/llui/llfloater.h
@@ -171,7 +171,7 @@ public:
LLMultiFloater* getHost();
void applyTitle();
- const std::string& getCurrentTitle() const;
+ std::string getCurrentTitle() const;
void setTitle( const std::string& title);
std::string getTitle() const;
void setShortTitle( const std::string& short_title );
diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp
index 279cbaa923..00a80478cf 100644
--- a/indra/llui/llfocusmgr.cpp
+++ b/indra/llui/llfocusmgr.cpp
@@ -274,7 +274,7 @@ BOOL LLFocusMgr::childHasKeyboardFocus(const LLView* parent ) const
// Returns TRUE is parent or any descedent of parent is the mouse captor.
BOOL LLFocusMgr::childHasMouseCapture( const LLView* parent ) const
{
- if( mMouseCaptor && mMouseCaptor->isView() )
+ if( mMouseCaptor && dynamic_cast<LLView*>(mMouseCaptor) != NULL )
{
LLView* captor_view = (LLView*)mMouseCaptor;
while( captor_view )
diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp
index 2d582c0568..5eade72b61 100644
--- a/indra/llui/lllayoutstack.cpp
+++ b/indra/llui/lllayoutstack.cpp
@@ -35,7 +35,9 @@
#include "linden_common.h"
#include "lllayoutstack.h"
+
#include "lllocalcliprect.h"
+#include "llpanel.h"
#include "llresizebar.h"
#include "llcriticaldamp.h"
diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h
index 9459b9990c..49cbe7270f 100644
--- a/indra/llui/lllayoutstack.h
+++ b/indra/llui/lllayoutstack.h
@@ -34,7 +34,9 @@
#ifndef LL_LLLAYOUTSTACK_H
#define LL_LLLAYOUTSTACK_H
-#include "llpanel.h"
+#include "llview.h"
+
+class LLPanel;
class LLLayoutStack : public LLView
{
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index ede67ad17d..693ea5bb45 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -107,9 +107,6 @@ LLLineEditor::Params::Params()
highlight_color("highlight_color"),
preedit_bg_color("preedit_bg_color"),
border(""),
- is_unicode("is_unicode"),
- drop_shadow_visible("drop_shadow_visible"),
- border_drop_shadow_visible("border_drop_shadow_visible"),
bg_visible("bg_visible"),
text_pad_left("text_pad_left"),
text_pad_right("text_pad_right"),
@@ -544,18 +541,13 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask)
{
return TRUE;
}
- if (mSelectAllonFocusReceived
- && gFocusMgr.getKeyboardFocus() != this)
- {
- setFocus( TRUE );
- }
- else
+
+ if (!mSelectAllonFocusReceived
+ || gFocusMgr.getKeyboardFocus() == this)
{
mLastSelectionStart = -1;
mLastSelectionStart = -1;
- setFocus( TRUE );
-
if (mask & MASK_SHIFT)
{
// Handle selection extension
@@ -621,6 +613,8 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask)
gFocusMgr.setMouseCapture( this );
}
+ setFocus(TRUE);
+
// delay cursor flashing
mKeystrokeTimer.reset();
diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h
index 339aad30fb..48d68b9935 100644
--- a/indra/llui/lllineeditor.h
+++ b/indra/llui/lllineeditor.h
@@ -105,10 +105,7 @@ public:
Optional<S32> text_pad_left,
text_pad_right;
- Ignored is_unicode,
- drop_shadow_visible,
- border_drop_shadow_visible,
- bg_visible;
+ Ignored bg_visible;
Params();
};
@@ -280,6 +277,7 @@ private:
virtual void getPreeditRange(S32 *position, S32 *length) const;
virtual BOOL getPreeditLocation(S32 query_position, LLCoordGL *coord, LLRect *bounds, LLRect *control) const;
virtual S32 getPreeditFontSize() const;
+ virtual LLWString getPreeditString() const { return getWText(); }
protected:
LLUIString mText; // The string being edited.
diff --git a/indra/llui/llmenubutton.cpp b/indra/llui/llmenubutton.cpp
new file mode 100644
index 0000000000..2bb6749c83
--- /dev/null
+++ b/indra/llui/llmenubutton.cpp
@@ -0,0 +1,135 @@
+/**
+ * @file llbutton.cpp
+ * @brief LLButton base class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llmenubutton.h"
+
+// Linden library includes
+#include "llmenugl.h"
+#include "llstring.h"
+#include "v4color.h"
+
+static LLDefaultChildRegistry::Register<LLMenuButton> r("menu_button");
+
+
+LLMenuButton::Params::Params()
+: menu_filename("menu_filename")
+{
+}
+
+
+LLMenuButton::LLMenuButton(const LLMenuButton::Params& p)
+: LLButton(p),
+ mMenu(NULL),
+ mMenuVisibleLastFrame(false)
+{
+ std::string menu_filename = p.menu_filename;
+
+ if (!menu_filename.empty())
+ {
+ mMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
+ if (!mMenu)
+ {
+ llwarns << "Error loading menu_button menu" << llendl;
+ }
+ }
+}
+
+void LLMenuButton::toggleMenu()
+{
+ if(!mMenu)
+ return;
+
+ if (mMenu->getVisible() || mMenuVisibleLastFrame)
+ {
+ mMenu->setVisible(FALSE);
+ }
+ else
+ {
+ LLRect rect = getRect();
+ //mMenu->needsArrange(); //so it recalculates the visible elements
+ LLMenuGL::showPopup(getParent(), mMenu, rect.mLeft, rect.mBottom);
+ }
+}
+
+
+BOOL LLMenuButton::handleKeyHere(KEY key, MASK mask )
+{
+ if( KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key))
+ {
+ toggleMenu();
+ return TRUE;
+ }
+
+ if (mMenu && mMenu->getVisible() && key == KEY_ESCAPE && mask == MASK_NONE)
+ {
+ mMenu->setVisible(FALSE);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL LLMenuButton::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (hasTabStop() && !getIsChrome())
+ {
+ setFocus(TRUE);
+ }
+
+ toggleMenu();
+
+ if (getSoundFlags() & MOUSE_DOWN)
+ {
+ make_ui_sound("UISndClick");
+ }
+
+ return TRUE;
+}
+
+void LLMenuButton::draw()
+{
+ //we save this off so next frame when we try to close it by
+ //button click, and it hides menus before we get to it, we know
+ mMenuVisibleLastFrame = mMenu && mMenu->getVisible();
+
+ if (mMenuVisibleLastFrame)
+ {
+ setForcePressedState(TRUE);
+ }
+
+ LLButton::draw();
+
+ setForcePressedState(FALSE);
+}
+
diff --git a/indra/llui/llmenubutton.h b/indra/llui/llmenubutton.h
new file mode 100644
index 0000000000..94b0e4355d
--- /dev/null
+++ b/indra/llui/llmenubutton.h
@@ -0,0 +1,68 @@
+/**
+ * @file llbutton.h
+ * @brief Header for buttons
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLMENUBUTTON_H
+#define LL_LLMENUBUTTON_H
+
+#include "llbutton.h"
+
+class LLMenuGL;
+
+class LLMenuButton
+: public LLButton
+{
+public:
+ struct Params
+ : public LLInitParam::Block<Params, LLButton::Params>
+ {
+ // filename for it's toggleable menu
+ Optional<std::string> menu_filename;
+
+ Params();
+ };
+
+ void toggleMenu();
+ /*virtual*/ void draw();
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask );
+
+protected:
+ friend class LLUICtrlFactory;
+ LLMenuButton(const Params&);
+
+private:
+ LLMenuGL* mMenu;
+ bool mMenuVisibleLastFrame;
+};
+
+
+#endif // LL_LLMENUBUTTON_H
diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp
index f2d147ac39..14bee0465c 100644
--- a/indra/llui/llmenugl.cpp
+++ b/indra/llui/llmenugl.cpp
@@ -781,6 +781,10 @@ LLMenuItemCallGL::LLMenuItemCallGL(const LLMenuItemCallGL::Params& p)
void LLMenuItemCallGL::initFromParams(const Params& p)
{
+ if (p.on_visible.isProvided())
+ {
+ initVisibleCallback(p.on_visible, mVisibleSignal);
+ }
if (p.on_enable.isProvided())
{
initEnableCallback(p.on_enable, mEnableSignal);
@@ -823,9 +827,19 @@ void LLMenuItemCallGL::updateEnabled( void )
}
}
+void LLMenuItemCallGL::updateVisible( void )
+{
+ if (mVisibleSignal.num_slots() > 0)
+ {
+ bool visible = mVisibleSignal(this, LLSD());
+ setVisible(visible);
+ }
+}
+
void LLMenuItemCallGL::buildDrawLabel( void )
{
updateEnabled();
+ updateVisible();
LLMenuItemGL::buildDrawLabel();
}
@@ -1224,23 +1238,7 @@ void LLMenuItemBranchGL::openMenu()
rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS);
}
branch->setRect( rect );
- S32 x = 0;
- S32 y = 0;
- branch->localPointToOtherView( 0, 0, &x, &y, branch->getParent() );
- S32 delta_x = 0;
- S32 delta_y = 0;
- if( y < menu_region_rect.mBottom )
- {
- delta_y = menu_region_rect.mBottom - y;
- }
-
- S32 menu_region_width = menu_region_rect.getWidth();
- if( x - menu_region_rect.mLeft > menu_region_width - rect.getWidth() )
- {
- // move sub-menu over to left side
- delta_x = llmax(-x, (-1 * (rect.getWidth() + getRect().getWidth())));
- }
- branch->translate( delta_x, delta_y );
+ branch->translateIntoRectWithExclusion( menu_region_rect, getMenu()->getRect(), FALSE );
branch->setVisible( TRUE );
branch->getParent()->sendChildToFront(branch);
@@ -1935,6 +1933,9 @@ void LLMenuGL::arrange( void )
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()
@@ -1976,6 +1977,9 @@ void LLMenuGL::arrange( void )
for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
{
+ // do first so LLMenuGLItemCall can call on_visible to determine if visible
+ (*item_iter)->buildDrawLabel();
+
if ((*item_iter)->getVisible())
{
if (!getTornOff()
@@ -2162,7 +2166,7 @@ void LLMenuGL::arrange( void )
item_list_t::iterator item_iter;
for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
- {
+ {
if ((*item_iter)->getVisible())
{
if (mScrollable)
@@ -2193,7 +2197,6 @@ void LLMenuGL::arrange( void )
}
}
(*item_iter)->setRect( rect );
- (*item_iter)->buildDrawLabel();
}
}
}
@@ -2936,11 +2939,27 @@ void hide_top_view( LLView* view )
// static
void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y)
{
+ const S32 CURSOR_HEIGHT = 22; // Approximate "normal" cursor size
+ const S32 CURSOR_WIDTH = 12;
+
// Save click point for detecting cursor moves before mouse-up.
// Must be in local coords to compare with mouseUp events.
// If the mouse doesn't move, the menu will stay open ala the Mac.
// See also LLContextMenu::show()
S32 mouse_x, mouse_y;
+
+ // Resetting scrolling position
+ if (menu->isScrollable())
+ {
+ menu->mFirstVisibleItem = NULL;
+ }
+
+ menu->setVisible( TRUE );
+
+ // Fix menu rect if needed.
+ menu->needsArrange();
+ menu->arrangeAndClear();
+
LLUI::getMousePositionLocal(menu->getParent(), &mouse_x, &mouse_y);
LLMenuHolderGL::sContextMenuSpawnPos.set(mouse_x,mouse_y);
@@ -2948,7 +2967,6 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y)
const S32 HPAD = 2;
LLRect rect = menu->getRect();
- //LLView* cur_view = spawning_view;
S32 left = x + HPAD;
S32 top = y;
spawning_view->localPointToOtherView(left, top, &left, &top, menu->getParent());
@@ -2956,37 +2974,19 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y)
rect.getWidth(), rect.getHeight() );
menu->setRect( rect );
- // Resetting scrolling position
- if (menu->isScrollable())
- {
- menu->mFirstVisibleItem = NULL;
- menu->needsArrange();
- }
- menu->arrangeAndClear(); // Fix menu rect if needed.
- rect = menu->getRect();
// Adjust context menu to fit onscreen
- S32 bottom;
- left = rect.mLeft;
- bottom = rect.mBottom;
- S32 delta_x = 0;
- S32 delta_y = 0;
- if( bottom < menu_region_rect.mBottom )
- {
- // At this point, we need to move the context menu to the
- // other side of the mouse.
- delta_y = (rect.getHeight() + 2 * HPAD);
- }
-
- if( left > menu_region_rect.mRight - rect.getWidth() )
- {
- // At this point, we need to move the context menu to the
- // other side of the mouse.
- delta_x = -(rect.getWidth() + 2 * HPAD);
- }
- menu->translate( delta_x, delta_y );
- menu->setVisible( TRUE );
+ LLRect mouse_rect;
+ const S32 MOUSE_CURSOR_PADDING = 5;
+ mouse_rect.setLeftTopAndSize(mouse_x - MOUSE_CURSOR_PADDING,
+ mouse_y + MOUSE_CURSOR_PADDING,
+ CURSOR_WIDTH + MOUSE_CURSOR_PADDING * 2,
+ CURSOR_HEIGHT + MOUSE_CURSOR_PADDING * 2);
+ menu->translateIntoRectWithExclusion( menu_region_rect, mouse_rect, FALSE );
menu->getParent()->sendChildToFront(menu);
+
+
+
}
///============================================================================
diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h
index 8309fedf7f..48887ec352 100644
--- a/indra/llui/llmenugl.h
+++ b/indra/llui/llmenugl.h
@@ -268,15 +268,18 @@ public:
{
Optional<EnableCallbackParam > on_enable;
Optional<CommitCallbackParam > on_click;
+ Optional<VisibleCallbackParam > on_visible;
Params()
: on_enable("on_enable"),
- on_click("on_click")
+ on_click("on_click"),
+ on_visible("on_visible")
{}
};
protected:
LLMenuItemCallGL(const Params&);
friend class LLUICtrlFactory;
void updateEnabled( void );
+ void updateVisible( void );
public:
void initFromParams(const Params& p);
@@ -300,10 +303,15 @@ public:
{
return mEnableSignal.connect(cb);
}
-
+
+ boost::signals2::connection setVisibleCallback( const visible_signal_t::slot_type& cb )
+ {
+ return mVisibleSignal.connect(cb);
+ }
private:
enable_signal_t mEnableSignal;
+ visible_signal_t mVisibleSignal;
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/indra/llui/llmultisliderctrl.cpp b/indra/llui/llmultisliderctrl.cpp
index 0fbb7ced54..a9f462173d 100644
--- a/indra/llui/llmultisliderctrl.cpp
+++ b/indra/llui/llmultisliderctrl.cpp
@@ -98,7 +98,7 @@ LLMultiSliderCtrl::LLMultiSliderCtrl(const LLMultiSliderCtrl::Params& p)
LLTextBox::Params params;
params.name("MultiSliderCtrl Label");
params.rect(label_rect);
- params.text(p.label);
+ params.initial_value(p.label());
params.font(p.font);
mLabelBox = LLUICtrlFactory::create<LLTextBox> (params);
addChild(mLabelBox);
diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp
index b9bbb4db22..1695aee2b8 100644
--- a/indra/llui/llpanel.cpp
+++ b/indra/llui/llpanel.cpp
@@ -819,15 +819,6 @@ void LLPanel::childSetPrevalidate(const std::string& id, BOOL (*func)(const LLWS
}
}
-void LLPanel::childSetWrappedText(const std::string& id, const std::string& text, bool visible)
-{
- LLTextBox* child = findChild<LLTextBox>(id);
- if (child)
- {
- child->setVisible(visible);
- child->setWrappedText(text);
- }
-}
void LLPanel::childSetAction(const std::string& id, boost::function<void(void*)> function, void* value)
{
diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h
index 8b23ea7030..0594762333 100644
--- a/indra/llui/llpanel.h
+++ b/indra/llui/llpanel.h
@@ -209,9 +209,6 @@ public:
void childShowTab(const std::string& id, const std::string& tabname, bool visible = true);
LLPanel *childGetVisibleTab(const std::string& id) const;
- // LLTextBox
- void childSetWrappedText(const std::string& id, const std::string& text, bool visible = true);
-
// LLTextBox/LLTextEditor/LLLineEditor
void childSetText(const std::string& id, const LLStringExplicit& text) { childSetValue(id, LLSD(text)); }
@@ -243,7 +240,8 @@ protected:
LLCallbackMap::map_t mFactoryMap;
CommitCallbackRegistry::ScopedRegistrar mCommitCallbackRegistrar;
EnableCallbackRegistry::ScopedRegistrar mEnableCallbackRegistrar;
-
+ VisibleCallbackRegistry::ScopedRegistrar mVisibleCallbackRegistrar;
+
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
diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp
index d1ea5843eb..f9f0307d17 100644
--- a/indra/llui/llradiogroup.cpp
+++ b/indra/llui/llradiogroup.cpp
@@ -55,6 +55,8 @@ LLRadioGroup::Params::Params()
name = "radio_group";
mouse_opaque = true;
follows.flags = FOLLOWS_LEFT | FOLLOWS_TOP;
+ // radio items are not tabbable until they are selected
+ tab_stop = false;
}
LLRadioGroup::LLRadioGroup(const LLRadioGroup::Params& p)
diff --git a/indra/llui/llradiogroup.h b/indra/llui/llradiogroup.h
index 914548b6aa..1e9b5115f8 100644
--- a/indra/llui/llradiogroup.h
+++ b/indra/llui/llradiogroup.h
@@ -47,18 +47,7 @@ class LLRadioCtrl : public LLCheckBoxCtrl
{
public:
struct Params : public LLInitParam::Block<Params, LLCheckBoxCtrl::Params>
- {
- Ignored length;
- Ignored type;
-
- Params()
- : length("length"),
- type("type")
- {
- // radio items are not tabbable until they are selected
- tab_stop = false;
- }
- };
+ {};
/*virtual*/ ~LLRadioCtrl();
/*virtual*/ void setValue(const LLSD& value);
diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp
index ed150ac50c..7db34a0608 100644
--- a/indra/llui/llscrollbar.cpp
+++ b/indra/llui/llscrollbar.cpp
@@ -88,8 +88,6 @@ LLScrollbar::LLScrollbar(const Params & p)
mCurGlowStrength(0.f),
mTrackColor( p.track_color() ),
mThumbColor ( p.thumb_color() ),
- mOnScrollEndCallback( NULL ),
- mOnScrollEndData( NULL ),
mThumbImageV(p.thumb_image_vertical),
mThumbImageH(p.thumb_image_horizontal),
mTrackImageV(p.track_image_vertical),
@@ -243,11 +241,6 @@ void LLScrollbar::updateThumbRect()
mThumbRect.mRight = thumb_start + thumb_length;
mThumbRect.mBottom = 0;
}
-
- if (mOnScrollEndCallback && mOnScrollEndData && (mDocPos == getDocPosMax()))
- {
- mOnScrollEndCallback(mOnScrollEndData);
- }
}
BOOL LLScrollbar::handleMouseDown(S32 x, S32 y, MASK mask)
@@ -568,12 +561,6 @@ void LLScrollbar::draw()
}
}
- BOOL was_scrolled_to_bottom = (getDocPos() == getDocPosMax());
- if (mOnScrollEndCallback && was_scrolled_to_bottom)
- {
- mOnScrollEndCallback(mOnScrollEndData);
- }
-
// Draw children
LLView::draw();
} // end draw
diff --git a/indra/llui/llscrollbar.h b/indra/llui/llscrollbar.h
index 7e88b16561..e4c5712fb7 100644
--- a/indra/llui/llscrollbar.h
+++ b/indra/llui/llscrollbar.h
@@ -164,9 +164,6 @@ private:
LLUIImagePtr mTrackImageH;
S32 mThickness;
-
- void (*mOnScrollEndCallback)(void*);
- void *mOnScrollEndData;
};
diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp
index cd5926fb6b..5597d494fe 100644
--- a/indra/llui/llscrollcontainer.cpp
+++ b/indra/llui/llscrollcontainer.cpp
@@ -76,6 +76,7 @@ LLScrollContainer::Params::Params()
: is_opaque("opaque"),
bg_color("color"),
border_visible("border_visible"),
+ hide_scrollbar("hide_scrollbar"),
min_auto_scroll_rate("min_auto_scroll_rate", 100),
max_auto_scroll_rate("max_auto_scroll_rate", 1000),
reserve_scroll_corner("reserve_scroll_corner", false)
@@ -93,6 +94,7 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)
mAutoScrollRate( 0.f ),
mBackgroundColor(p.bg_color()),
mIsOpaque(p.is_opaque),
+ mHideScrollbar(p.hide_scrollbar),
mReserveScrollCorner(p.reserve_scroll_corner),
mMinAutoScrollRate(p.min_auto_scroll_rate),
mMaxAutoScrollRate(p.max_auto_scroll_rate),
@@ -349,28 +351,33 @@ void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height
S32 doc_width = doc_rect.getWidth();
S32 doc_height = doc_rect.getHeight();
- *visible_width = getRect().getWidth() - 2 * mBorder->getBorderWidth();
- *visible_height = getRect().getHeight() - 2 * mBorder->getBorderWidth();
+ S32 border_width = (mBorder->getVisible() ? 2 * mBorder->getBorderWidth() : 0);
+ *visible_width = getRect().getWidth() - border_width;
+ *visible_height = getRect().getHeight() - border_width;
*show_v_scrollbar = FALSE;
- if( *visible_height < doc_height )
- {
- *show_v_scrollbar = TRUE;
- *visible_width -= scrollbar_size;
- }
-
*show_h_scrollbar = FALSE;
- if( *visible_width < doc_width )
- {
- *show_h_scrollbar = TRUE;
- *visible_height -= scrollbar_size;
- // Must retest now that visible_height has changed
- if( !*show_v_scrollbar && (*visible_height < doc_height) )
+ if (!mHideScrollbar)
+ {
+ if( *visible_height < doc_height )
{
*show_v_scrollbar = TRUE;
*visible_width -= scrollbar_size;
}
+
+ if( *visible_width < doc_width )
+ {
+ *show_h_scrollbar = TRUE;
+ *visible_height -= scrollbar_size;
+
+ // Must retest now that visible_height has changed
+ if( !*show_v_scrollbar && (*visible_height < doc_height) )
+ {
+ *show_v_scrollbar = TRUE;
+ *visible_width -= scrollbar_size;
+ }
+ }
}
}
@@ -457,19 +464,6 @@ void LLScrollContainer::draw()
sDepth--;
}
}
-
- if (sDebugRects)
- {
- drawDebugRect();
- }
-
- //// *HACK: also draw debug rectangles around currently-being-edited LLView, and any elements that are being highlighted by GUI preview code (see LLFloaterUIPreview)
- //std::set<LLView*>::iterator iter = std::find(sPreviewHighlightedElements.begin(), sPreviewHighlightedElements.end(), this);
- //if ((sEditingUI && this == sEditingUIView) || (iter != sPreviewHighlightedElements.end() && sDrawPreviewHighlights))
- //{
- // drawDebugRect();
- //}
-
} // end draw
bool LLScrollContainer::addChild(LLView* view, S32 tab_group)
@@ -598,7 +592,7 @@ LLRect LLScrollContainer::getContentWindowRect() const
BOOL show_h_scrollbar = FALSE;
BOOL show_v_scrollbar = FALSE;
calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
- S32 border_width = mBorder->getBorderWidth();
+ S32 border_width = mBorder->getVisible() ? mBorder->getBorderWidth() : 0;
scroller_view_rect.setOriginAndSize(border_width,
show_h_scrollbar ? mScrollbar[HORIZONTAL]->getRect().mTop : border_width,
visible_width,
diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h
index 8385bca02f..ac8ffe5258 100644
--- a/indra/llui/llscrollcontainer.h
+++ b/indra/llui/llscrollcontainer.h
@@ -68,7 +68,8 @@ public:
{
Optional<bool> is_opaque,
reserve_scroll_corner,
- border_visible;
+ border_visible,
+ hide_scrollbar;
Optional<F32> min_auto_scroll_rate,
max_auto_scroll_rate;
Optional<LLUIColor> bg_color;
@@ -139,6 +140,7 @@ private:
F32 mAutoScrollRate;
F32 mMinAutoScrollRate;
F32 mMaxAutoScrollRate;
+ bool mHideScrollbar;
};
diff --git a/indra/llui/llscrolllistcell.cpp b/indra/llui/llscrolllistcell.cpp
index e28da91305..a7c268758a 100644
--- a/indra/llui/llscrolllistcell.cpp
+++ b/indra/llui/llscrolllistcell.cpp
@@ -204,6 +204,13 @@ BOOL LLScrollListText::isText() const
return TRUE;
}
+BOOL LLScrollListText::needsToolTip() const
+{
+ // show tooltips for truncated text
+ return mFont->getWidth(mText.getString()) > getWidth();
+}
+
+
//virtual
BOOL LLScrollListText::getVisible() const
{
diff --git a/indra/llui/llscrolllistcell.h b/indra/llui/llscrolllistcell.h
index 9d3fa65f64..758623f121 100644
--- a/indra/llui/llscrolllistcell.h
+++ b/indra/llui/llscrolllistcell.h
@@ -94,16 +94,18 @@ public:
LLScrollListCell(const LLScrollListCell::Params&);
virtual ~LLScrollListCell() {};
- virtual void draw(const LLColor4& color, const LLColor4& highlight_color) const = 0; // truncate to given width, if possible
+
+ virtual void draw(const LLColor4& color, const LLColor4& highlight_color) const {}; // truncate to given width, if possible
virtual S32 getWidth() const {return mWidth;}
virtual S32 getContentWidth() const { return 0; }
- virtual S32 getHeight() const = 0;
+ virtual S32 getHeight() const { return 0; }
virtual const LLSD getValue() const;
virtual void setValue(const LLSD& value) { }
virtual BOOL getVisible() const { return TRUE; }
virtual void setWidth(S32 width) { mWidth = width; }
virtual void highlightText(S32 offset, S32 num_chars) {}
- virtual BOOL isText() const = 0;
+ virtual BOOL isText() const { return FALSE; }
+ virtual BOOL needsToolTip() const { return FALSE; }
virtual void setColor(const LLColor4&) {}
virtual void onCommit() {};
@@ -120,8 +122,6 @@ public:
LLScrollListSpacer(const LLScrollListCell::Params& p) : LLScrollListCell(p) {}
/*virtual*/ ~LLScrollListSpacer() {};
/*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const {}
- /*virtual*/ S32 getHeight() const { return 0; }
- /*virtual*/ BOOL isText() const { return FALSE; }
};
/*
@@ -143,6 +143,7 @@ public:
/*virtual*/ void setColor(const LLColor4&);
/*virtual*/ BOOL isText() const;
+ /*virtual*/ BOOL needsToolTip() const;
void setText(const LLStringExplicit& text);
void setFontStyle(const U8 font_style);
@@ -175,7 +176,6 @@ public:
/*virtual*/ S32 getHeight() const;
/*virtual*/ const LLSD getValue() const;
/*virtual*/ void setColor(const LLColor4&);
- /*virtual*/ BOOL isText()const { return FALSE; }
/*virtual*/ void setValue(const LLSD& value);
private:
@@ -202,7 +202,6 @@ public:
/*virtual*/ void setEnabled(BOOL enable);
LLCheckBoxCtrl* getCheckBox() { return mCheckBox; }
- /*virtual*/ BOOL isText() const { return FALSE; }
private:
LLCheckBoxCtrl* mCheckBox;
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index 54e42bf642..af05686c70 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -218,7 +218,7 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p)
sbparams.orientation(LLScrollbar::VERTICAL);
sbparams.doc_size(getItemCount());
sbparams.doc_pos(mScrollLines);
- sbparams.page_size( mPageLines ? mPageLines : getItemCount() );
+ 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);
@@ -475,10 +475,7 @@ void LLScrollListCtrl::updateLayout()
getChildView("comment_text")->setShape(mItemListRect);
// how many lines of content in a single "page"
- S32 page_lines = mLineHeight? mItemListRect.getHeight() / mLineHeight : getItemCount();
- //if mPageLines is NOT provided display all item
- if(mPageLines)
- page_lines = mPageLines;
+ S32 page_lines = getLinesPerPage();
BOOL scrollbar_visible = mLineHeight * getItemCount() > mItemListRect.getHeight();
if (scrollbar_visible)
@@ -1386,7 +1383,7 @@ void LLScrollListCtrl::drawItems()
S32 y = mItemListRect.mTop - mLineHeight;
// allow for partial line at bottom
- S32 num_page_lines = (mPageLines)? mPageLines : getItemCount() + 1;
+ S32 num_page_lines = getLinesPerPage();
LLRect item_rect;
@@ -1529,7 +1526,19 @@ BOOL LLScrollListCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks)
return handled;
}
-BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen)
+// *NOTE: Requires a valid row_index and column_index
+LLRect LLScrollListCtrl::getCellRect(S32 row_index, S32 column_index)
+{
+ LLRect cell_rect;
+ S32 rect_left = getColumnOffsetFromIndex(column_index) + mItemListRect.mLeft;
+ S32 rect_bottom = getRowOffsetFromIndex(row_index);
+ LLScrollListColumn* columnp = getColumn(column_index);
+ cell_rect.setOriginAndSize(rect_left, rect_bottom,
+ rect_left + columnp->getWidth(), mLineHeight);
+ return cell_rect;
+}
+
+BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, MASK mask)
{
S32 column_index = getColumnIndexFromOffset(x);
LLScrollListColumn* columnp = getColumn(column_index);
@@ -1543,20 +1552,23 @@ BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sti
{
LLScrollListCell* hit_cell = hit_item->getColumn(column_index);
if (!hit_cell) return FALSE;
- //S32 cell_required_width = hit_cell->getContentWidth();
if (hit_cell
- && hit_cell->isText())
+ && hit_cell->isText()
+ && hit_cell->needsToolTip())
{
- S32 rect_left = getColumnOffsetFromIndex(column_index) + mItemListRect.mLeft;
- S32 rect_bottom = getRowOffsetFromIndex(getItemIndex(hit_item));
- LLRect cell_rect;
- cell_rect.setOriginAndSize(rect_left, rect_bottom, rect_left + columnp->getWidth(), mLineHeight);
+ S32 row_index = getItemIndex(hit_item);
+ LLRect cell_rect = getCellRect(row_index, column_index);
// Convert rect local to screen coordinates
LLRect sticky_rect;
localRectToScreen(cell_rect, &sticky_rect);
- LLToolTipMgr::instance().show(LLToolTipParams()
- .message(hit_cell->getValue().asString())
- .sticky_rect(sticky_rect));
+
+ // display tooltip exactly over original cell, in same font
+ LLToolTipMgr::instance().show(LLToolTip::Params()
+ .message(hit_cell->getValue().asString())
+ .font(LLFontGL::getFontSansSerifSmall())
+ .pos(LLCoordGL(sticky_rect.mLeft - 5, sticky_rect.mTop + 4))
+ .delay_time(0.2f)
+ .sticky_rect(sticky_rect));
}
handled = TRUE;
}
@@ -1565,7 +1577,7 @@ BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sti
LLScrollColumnHeader* headerp = columnp->mHeader;
if (headerp && !handled)
{
- handled = headerp->handleToolTip(x, y, msg, sticky_rect_screen);
+ handled = headerp->handleToolTip(x, y, mask);
}
return handled;
@@ -1877,7 +1889,7 @@ LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y )
mLineHeight );
// allow for partial line at bottom
- S32 num_page_lines = (mPageLines)? mPageLines : getItemCount() + 1;
+ S32 num_page_lines = getLinesPerPage();
S32 line = 0;
item_list::iterator iter;
@@ -2336,6 +2348,20 @@ BOOL LLScrollListCtrl::setSort(S32 column_idx, BOOL ascending)
}
}
+S32 LLScrollListCtrl::getLinesPerPage()
+{
+ //if mPageLines is NOT provided display all item
+ if(mPageLines)
+ {
+ return mPageLines;
+ }
+ else
+ {
+ return mLineHeight ? mItemListRect.getHeight() / mLineHeight : getItemCount();
+ }
+}
+
+
// Called by scrollbar
void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar )
{
@@ -2442,7 +2468,7 @@ void LLScrollListCtrl::scrollToShowSelected()
}
S32 lowest = mScrollLines;
- S32 page_lines = (mPageLines)? mPageLines : getItemCount();
+ S32 page_lines = getLinesPerPage();
S32 highest = mScrollLines + page_lines;
if (index < lowest)
diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h
index 83b2f71037..78bc60db6e 100644
--- a/indra/llui/llscrolllistctrl.h
+++ b/indra/llui/llscrolllistctrl.h
@@ -288,7 +288,7 @@ public:
/*virtual*/ BOOL handleKeyHere(KEY key, MASK mask);
/*virtual*/ BOOL handleUnicodeCharHere(llwchar uni_char);
/*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
- /*virtual*/ BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect);
+ /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask);
/*virtual*/ void setEnabled(BOOL enabled);
/*virtual*/ void setFocus( BOOL b );
/*virtual*/ void onFocusReceived();
@@ -307,6 +307,9 @@ public:
LLRect getItemListRect() { return mItemListRect; }
+ /// Returns rect, in local coords, of a given row/column
+ LLRect getCellRect(S32 row_index, S32 column_index);
+
// Used "internally" by the scroll bar.
void onScrollChange( S32 new_pos, LLScrollbar* src );
@@ -390,6 +393,7 @@ private:
void deselectItem(LLScrollListItem* itemp);
void commitIfChanged();
BOOL setSort(S32 column, BOOL ascending);
+ S32 getLinesPerPage();
static void showNameDetails(std::string id, bool is_group);
static void copyNameToClipboard(std::string id, bool is_group);
diff --git a/indra/llui/llscrolllistitem.cpp b/indra/llui/llscrolllistitem.cpp
index 2ac6925c78..2c5e0d24cd 100644
--- a/indra/llui/llscrolllistitem.cpp
+++ b/indra/llui/llscrolllistitem.cpp
@@ -36,7 +36,6 @@
#include "llscrolllistitem.h"
#include "llrect.h"
-#include "llresmgr.h" // LLFONT_SANSSERIF_SMALL
#include "llui.h"
diff --git a/indra/llui/llscrolllistitem.h b/indra/llui/llscrolllistitem.h
index c2b7effbc7..0ec7fbcc2c 100644
--- a/indra/llui/llscrolllistitem.h
+++ b/indra/llui/llscrolllistitem.h
@@ -34,10 +34,8 @@
#ifndef LLSCROLLLISTITEM_H
#define LLSCROLLLISTITEM_H
-#include "llfontgl.h" // LLFontGL::HAlign
#include "llpointer.h" // LLPointer<>
#include "llsd.h"
-#include "lluistring.h"
#include "v4color.h"
#include "llinitparam.h"
#include "llscrolllistcell.h"
diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp
index fb71b60725..3ecf629082 100644
--- a/indra/llui/llsliderctrl.cpp
+++ b/indra/llui/llsliderctrl.cpp
@@ -83,7 +83,7 @@ LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p)
LLTextBox::Params params(p.slider_label);
params.rect.setIfNotProvided(label_rect);
params.font.setIfNotProvided(p.font);
- params.text(p.label);
+ params.initial_value(p.label());
mLabelBox = LLUICtrlFactory::create<LLTextBox> (params);
addChild(mLabelBox);
}
diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp
index 83d71006aa..bedf16a397 100644
--- a/indra/llui/llspinctrl.cpp
+++ b/indra/llui/llspinctrl.cpp
@@ -90,7 +90,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
LLTextBox::Params params;
params.name("SpinCtrl Label");
params.rect(label_rect);
- params.text(p.label);
+ params.initial_value(p.label());
if (p.font.isProvided())
{
params.font(p.font);
diff --git a/indra/llui/llstatview.cpp b/indra/llui/llstatview.cpp
index 4ba01eb441..9580c76e2a 100644
--- a/indra/llui/llstatview.cpp
+++ b/indra/llui/llstatview.cpp
@@ -67,5 +67,7 @@ LLStatView::~LLStatView()
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/llstyle.cpp b/indra/llui/llstyle.cpp
index c16ac08014..fd3f88d1f6 100644
--- a/indra/llui/llstyle.cpp
+++ b/indra/llui/llstyle.cpp
@@ -40,7 +40,7 @@
LLStyle::Params::Params()
: visible("visible", true),
- drop_shadow("drop_shadow", false),
+ drop_shadow("drop_shadow", LLFontGL::NO_SHADOW),
color("color", LLColor4::black),
font("font", LLFontGL::getFontMonospace()),
image("image"),
diff --git a/indra/llui/llstyle.h b/indra/llui/llstyle.h
index 5e8883afd7..c769964136 100644
--- a/indra/llui/llstyle.h
+++ b/indra/llui/llstyle.h
@@ -44,12 +44,12 @@ class LLStyle : public LLRefCount
public:
struct Params : public LLInitParam::Block<Params>
{
- Optional<bool> visible,
- drop_shadow;
- Optional<LLUIColor> color;
- Optional<const LLFontGL*> font;
- Optional<LLUIImage*> image;
- Optional<std::string> link_href;
+ Optional<bool> visible;
+ Optional<LLFontGL::ShadowType> drop_shadow;
+ Optional<LLUIColor> color;
+ Optional<const LLFontGL*> font;
+ Optional<LLUIImage*> image;
+ Optional<std::string> link_href;
Params();
};
LLStyle(const Params& p = Params());
@@ -60,6 +60,8 @@ public:
BOOL isVisible() const;
void setVisible(BOOL is_visible);
+ LLFontGL::ShadowType getShadowType() { return mDropShadow; }
+
void setFont(const LLFontGL* font);
const LLFontGL* getFont() const;
@@ -94,7 +96,7 @@ public:
BOOL mItalic;
BOOL mBold;
BOOL mUnderline;
- BOOL mDropShadow;
+ LLFontGL::ShadowType mDropShadow;
protected:
~LLStyle() { }
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index b6eed3ef18..b1067ad6f3 100644
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -596,10 +596,10 @@ BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
}
// virtual
-BOOL LLTabContainer::handleToolTip( S32 x, S32 y, std::string& msg, LLRect& sticky_rect )
+BOOL LLTabContainer::handleToolTip( S32 x, S32 y, MASK mask)
{
static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
- BOOL handled = LLPanel::handleToolTip( x, y, msg, sticky_rect );
+ BOOL handled = LLPanel::handleToolTip( x, y, mask);
if (!handled && getTabCount() > 0)
{
LLTabTuple* firsttuple = getTab(0);
@@ -629,7 +629,7 @@ BOOL LLTabContainer::handleToolTip( S32 x, S32 y, std::string& msg, LLRect& stic
tuple->mButton->setVisible( TRUE );
S32 local_x = x - tuple->mButton->getRect().mLeft;
S32 local_y = y - tuple->mButton->getRect().mBottom;
- handled = tuple->mButton->handleToolTip( local_x, local_y, msg, sticky_rect );
+ handled = tuple->mButton->handleToolTip( local_x, local_y, mask);
if( handled )
{
break;
@@ -906,7 +906,7 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
LLTextBox::Params params;
params.name(trimmed_label);
params.rect(btn_rect);
- params.text(trimmed_label);
+ params.initial_value(trimmed_label);
params.font(font);
textbox = LLUICtrlFactory::create<LLTextBox> (params);
diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h
index 89a0346896..7bbecc1abc 100644
--- a/indra/llui/lltabcontainer.h
+++ b/indra/llui/lltabcontainer.h
@@ -99,7 +99,7 @@ public:
/*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask );
/*virtual*/ BOOL handleHover( S32 x, S32 y, MASK mask );
/*virtual*/ BOOL handleMouseUp( S32 x, S32 y, MASK mask );
- /*virtual*/ BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect );
+ /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleKeyHere(KEY key, MASK mask);
/*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
EDragAndDropType type, void* cargo_data,
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index 0fd6a14187..6c048aa908 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -34,25 +34,2011 @@
#include "linden_common.h"
#include "lltextbase.h"
-#include "llstl.h"
-#include "llview.h"
-#include "llwindow.h"
+
+#include "lllocalcliprect.h"
#include "llmenugl.h"
+#include "llscrollcontainer.h"
+#include "llstl.h"
+#include "lltextparser.h"
#include "lltooltip.h"
#include "lluictrl.h"
#include "llurlaction.h"
#include "llurlregistry.h"
-
+#include "llview.h"
+#include "llwindow.h"
#include <boost/bind.hpp>
-// global state for all text fields
-LLUIColor LLTextBase::mLinkColor = LLColor4::blue;
+const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds
+const S32 CURSOR_THICKNESS = 2;
+
+LLTextBase::line_info::line_info(S32 index_start, S32 index_end, LLRect rect, S32 line_num)
+: mDocIndexStart(index_start),
+ mDocIndexEnd(index_end),
+ mRect(rect),
+ mLineNum(line_num)
+{}
bool LLTextBase::compare_segment_end::operator()(const LLTextSegmentPtr& a, const LLTextSegmentPtr& b) const
{
return a->getEnd() < b->getEnd();
}
+
+// helper functors
+struct LLTextBase::compare_bottom
+{
+ bool operator()(const S32& a, const LLTextBase::line_info& b) const
+ {
+ return a > b.mRect.mBottom; // bottom of a is higher than bottom of b
+ }
+
+ bool operator()(const LLTextBase::line_info& a, const S32& b) const
+ {
+ return a.mRect.mBottom > b; // bottom of a is higher than bottom of b
+ }
+
+ bool operator()(const LLTextBase::line_info& a, const LLTextBase::line_info& b) const
+ {
+ return a.mRect.mBottom > b.mRect.mBottom; // bottom of a is higher than bottom of b
+ }
+
+};
+
+// helper functors
+struct LLTextBase::compare_top
+{
+ bool operator()(const S32& a, const LLTextBase::line_info& b) const
+ {
+ return a > b.mRect.mTop; // top of a is higher than top of b
+ }
+
+ bool operator()(const LLTextBase::line_info& a, const S32& b) const
+ {
+ return a.mRect.mTop > b; // top of a is higher than top of b
+ }
+
+ bool operator()(const LLTextBase::line_info& a, const LLTextBase::line_info& b) const
+ {
+ return a.mRect.mTop > b.mRect.mTop; // top of a is higher than top of b
+ }
+};
+
+struct LLTextBase::line_end_compare
+{
+ bool operator()(const S32& pos, const LLTextBase::line_info& info) const
+ {
+ return (pos < info.mDocIndexEnd);
+ }
+
+ bool operator()(const LLTextBase::line_info& info, const S32& pos) const
+ {
+ return (info.mDocIndexEnd < pos);
+ }
+
+ bool operator()(const LLTextBase::line_info& a, const LLTextBase::line_info& b) const
+ {
+ return (a.mDocIndexEnd < b.mDocIndexEnd);
+ }
+
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+// LLTextBase::DocumentPanel
+//
+
+
+LLTextBase::DocumentPanel::DocumentPanel(const Params& p)
+: LLPanel(p)
+{}
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// LLTextBase
+//
+
+// register LLTextBase::Params under name "textbase"
+static LLWidgetNameRegistry::StaticRegistrar sRegisterTextBaseParams(&typeid(LLTextBase::Params), "textbase");
+
+LLTextBase::LineSpacingParams::LineSpacingParams()
+: multiple("multiple", 1.f),
+ pixels("pixels", 0)
+{
+}
+
+
+LLTextBase::Params::Params()
+: cursor_color("cursor_color"),
+ text_color("text_color"),
+ text_readonly_color("text_readonly_color"),
+ bg_visible("bg_visible", false),
+ border_visible("border_visible", false),
+ bg_readonly_color("bg_readonly_color"),
+ bg_writeable_color("bg_writeable_color"),
+ bg_focus_color("bg_focus_color"),
+ hide_scrollbar("hide_scrollbar"),
+ clip_to_rect("clip_to_rect", true),
+ track_end("track_end", false),
+ read_only("read_only", false),
+ v_pad("v_pad", 0),
+ h_pad("h_pad", 0),
+ line_spacing("line_spacing"),
+ max_text_length("max_length", 255),
+ font_shadow("font_shadow"),
+ wrap("wrap"),
+ use_ellipses("use_ellipses", false),
+ allow_html("allow_html", false),
+ parse_highlights("parse_highlights", false)
+{
+ addSynonym(track_end, "track_bottom");
+ addSynonym(wrap, "word_wrap");
+}
+
+
+LLTextBase::LLTextBase(const LLTextBase::Params &p)
+: LLUICtrl(p, LLTextViewModelPtr(new LLTextViewModel)),
+ mURLClickSignal(),
+ mMaxTextByteLength( p.max_text_length ),
+ mDefaultFont(p.font),
+ mFontShadow(p.font_shadow),
+ mPopupMenu(NULL),
+ mReadOnly(p.read_only),
+ mCursorColor(p.cursor_color),
+ mFgColor(p.text_color),
+ mBorderVisible( p.border_visible ),
+ mReadOnlyFgColor(p.text_readonly_color),
+ mWriteableBgColor(p.bg_writeable_color),
+ mReadOnlyBgColor(p.bg_readonly_color),
+ mFocusBgColor(p.bg_focus_color),
+ mReflowNeeded(FALSE),
+ mCursorPos( 0 ),
+ mScrollNeeded(FALSE),
+ mDesiredXPixel(-1),
+ mHPad(p.h_pad),
+ mVPad(p.v_pad),
+ mHAlign(p.font_halign),
+ mLineSpacingMult(p.line_spacing.multiple),
+ mLineSpacingPixels(p.line_spacing.pixels),
+ mTrackEnd( p.track_end ),
+ mScrollIndex(-1),
+ mSelectionStart( 0 ),
+ mSelectionEnd( 0 ),
+ mIsSelecting( FALSE ),
+ mClip(p.clip_to_rect),
+ mWordWrap(p.wrap),
+ mUseEllipses( p.use_ellipses ),
+ mParseHTML(p.allow_html),
+ mParseHighlights(p.parse_highlights)
+{
+ 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.hide_scrollbar = p.hide_scrollbar;
+ scroll_params.border_visible = p.border_visible;
+ mScroller = LLUICtrlFactory::create<LLScrollContainer>(scroll_params);
+ addChild(mScroller);
+
+ LLPanel::Params panel_params;
+ panel_params.name = "text_contents";
+ panel_params.rect = LLRect(0, 500, 500, 0);
+ panel_params.background_visible = p.bg_visible;
+ panel_params.background_opaque = true;
+ panel_params.mouse_opaque = false;
+
+ mDocumentPanel = LLUICtrlFactory::create<DocumentPanel>(panel_params);
+ mScroller->addChild(mDocumentPanel);
+
+ createDefaultSegment();
+
+ updateTextRect();
+}
+
+LLTextBase::~LLTextBase()
+{
+ delete mPopupMenu;
+ clearSegments();
+}
+
+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;
+ }
+
+ // HACK: text editors always need to be enabled so that we can scroll
+ LLView::setEnabled(true);
+}
+
+bool LLTextBase::truncate()
+{
+ BOOL did_truncate = FALSE;
+
+ // First rough check - if we're less than 1/4th the size, we're OK
+ if (getLength() >= S32(mMaxTextByteLength / 4))
+ {
+ // Have to check actual byte size
+ LLWString text(getWText());
+ S32 utf8_byte_size = wstring_utf8_length(text);
+ if ( utf8_byte_size > mMaxTextByteLength )
+ {
+ // Truncate safely in UTF-8
+ std::string temp_utf8_text = wstring_to_utf8str(text);
+ temp_utf8_text = utf8str_truncate( temp_utf8_text, mMaxTextByteLength );
+ getViewModel()->setDisplay(utf8str_to_wstring( temp_utf8_text ));
+ did_truncate = TRUE;
+ }
+ }
+
+ return did_truncate;
+}
+
+LLStyle::Params LLTextBase::getDefaultStyle()
+{
+ LLColor4 text_color = ( mReadOnly ? mReadOnlyFgColor.get() : mFgColor.get() );
+ return LLStyle::Params().color(text_color).font(mDefaultFont).drop_shadow(mFontShadow);
+}
+
+void LLTextBase::onValueChange(S32 start, S32 end)
+{
+}
+
+
+// Draws the black box behind the selected text
+void LLTextBase::drawSelectionBackground()
+{
+ // Draw selection even if we don't have keyboard focus for search/replace
+ if( hasSelection() && !mLineInfoList.empty())
+ {
+ LLWString text = getWText();
+ std::vector<LLRect> selection_rects;
+
+ S32 selection_left = llmin( mSelectionStart, mSelectionEnd );
+ S32 selection_right = llmax( mSelectionStart, mSelectionEnd );
+ LLRect selection_rect = mTextRect;
+
+ // Skip through the lines we aren't drawing.
+ LLRect content_display_rect = mScroller->getVisibleContentRect();
+
+ // binary search for line that starts before top of visible buffer
+ line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mTop, compare_bottom());
+ line_list_t::const_iterator end_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mBottom, compare_top());
+
+ bool done = false;
+
+ // Find the coordinates of the selected area
+ for (;line_iter != end_iter && !done; ++line_iter)
+ {
+ // is selection visible on this line?
+ if (line_iter->mDocIndexEnd > selection_left && line_iter->mDocIndexStart < selection_right)
+ {
+ segment_set_t::iterator segment_iter;
+ S32 segment_offset;
+ getSegmentAndOffset(line_iter->mDocIndexStart, &segment_iter, &segment_offset);
+
+ LLRect selection_rect;
+ selection_rect.mLeft = line_iter->mRect.mLeft;
+ selection_rect.mRight = line_iter->mRect.mLeft;
+ selection_rect.mBottom = line_iter->mRect.mBottom;
+ selection_rect.mTop = line_iter->mRect.mTop;
+
+ for(;segment_iter != mSegments.end(); ++segment_iter, segment_offset = 0)
+ {
+ LLTextSegmentPtr segmentp = *segment_iter;
+
+ S32 segment_line_start = segmentp->getStart() + segment_offset;
+ S32 segment_line_end = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd);
+
+ // if selection after beginning of segment
+ if(selection_left >= segment_line_start)
+ {
+ S32 num_chars = llmin(selection_left, segment_line_end) - segment_line_start;
+ selection_rect.mLeft += segmentp->getWidth(segment_offset, num_chars);
+ }
+
+ // if 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)
+ selection_rect.mRight += segmentp->getWidth(segment_offset, segment_line_end - segment_line_start);
+ }
+ // else if selection ends on current segment...
+ else
+ {
+ S32 num_chars = selection_right - segment_line_start;
+ selection_rect.mRight += segmentp->getWidth(segment_offset, num_chars);
+
+ break;
+ }
+ }
+ selection_rects.push_back(selection_rect);
+ }
+ }
+
+ // Draw the selection box (we're using a box instead of reversing the colors on the selected text).
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ const LLColor4& color = mReadOnly ? mReadOnlyBgColor.get() : mWriteableBgColor.get();
+ F32 alpha = hasFocus() ? 0.7f : 0.3f;
+ alpha *= getDrawContext().mAlpha;
+ gGL.color4f( 1.f - color.mV[0], 1.f - color.mV[1], 1.f - color.mV[2], alpha );
+
+ for (std::vector<LLRect>::iterator rect_it = selection_rects.begin();
+ rect_it != selection_rects.end();
+ ++rect_it)
+ {
+ LLRect selection_rect = *rect_it;
+ selection_rect.translate(mTextRect.mLeft - content_display_rect.mLeft, mTextRect.mBottom - content_display_rect.mBottom);
+ gl_rect_2d(selection_rect);
+ }
+ }
+}
+
+void LLTextBase::drawCursor()
+{
+ F32 alpha = getDrawContext().mAlpha;
+
+ if( hasFocus()
+ && gFocusMgr.getAppHasFocus()
+ && !mReadOnly)
+ {
+ 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
+ {
+ //segmentp = mSegments.back();
+ 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 width = llmax(CURSOR_THICKNESS, segmentp->getWidth(mCursorPos - segmentp->getStart(), 1));
+ 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;
+ if (segmentp)
+ {
+ text_color = segmentp->getColor();
+ fontp = segmentp->getStyle()->getFont();
+ }
+ else if (mReadOnly)
+ {
+ text_color = mReadOnlyFgColor.get();
+ fontp = mDefaultFont;
+ }
+ else
+ {
+ text_color = mFgColor.get();
+ fontp = mDefaultFont;
+ }
+ fontp->render(text, mCursorPos, cursor_rect.mLeft, cursor_rect.mTop,
+ LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], alpha),
+ LLFontGL::LEFT, LLFontGL::TOP,
+ LLFontGL::NORMAL,
+ LLFontGL::NO_SHADOW,
+ 1);
+ }
+
+ // Make sure the IME is in the right place
+ LLRect screen_pos = calcScreenRect();
+ LLCoordGL ime_pos( screen_pos.mLeft + llfloor(cursor_rect.mLeft), screen_pos.mBottom + llfloor(cursor_rect.mTop) );
+
+ ime_pos.mX = (S32) (ime_pos.mX * LLUI::sGLScaleFactor.mV[VX]);
+ ime_pos.mY = (S32) (ime_pos.mY * LLUI::sGLScaleFactor.mV[VY]);
+ getWindow()->setLanguageTextInput( ime_pos );
+ }
+ }
+}
+
+void LLTextBase::drawText()
+{
+ LLWString text = getWText();
+ const S32 text_len = getLength();
+ if( text_len <= 0 )
+ {
+ return;
+ }
+ S32 selection_left = -1;
+ S32 selection_right = -1;
+ // Draw selection even if we don't have keyboard focus for search/replace
+ if( hasSelection())
+ {
+ selection_left = llmin( mSelectionStart, mSelectionEnd );
+ selection_right = llmax( mSelectionStart, mSelectionEnd );
+ }
+
+ LLRect scrolled_view_rect = mScroller->getVisibleContentRect();
+ LLRect content_rect = mScroller->getContentWindowRect();
+ std::pair<S32, S32> line_range = getVisibleLines();
+ S32 first_line = line_range.first;
+ S32 last_line = line_range.second;
+ if (first_line >= last_line)
+ {
+ return;
+ }
+
+ S32 line_start = getLineStart(first_line);
+ // find first text segment that spans top of visible portion of text buffer
+ segment_set_t::iterator seg_iter = getSegIterContaining(line_start);
+ if (seg_iter == mSegments.end())
+ {
+ return;
+ }
+
+ LLTextSegmentPtr cur_segment = *seg_iter;
+
+ for (S32 cur_line = first_line; cur_line < last_line; cur_line++)
+ {
+ line_info& line = mLineInfoList[cur_line];
+
+ if ((line.mRect.mTop - scrolled_view_rect.mBottom) < mTextRect.mBottom)
+ {
+ break;
+ }
+
+ S32 next_start = -1;
+ S32 line_end = text_len;
+
+ if ((cur_line + 1) < getLineCount())
+ {
+ next_start = getLineStart(cur_line + 1);
+ line_end = next_start;
+ }
+ if ( text[line_end-1] == '\n' )
+ {
+ --line_end;
+ }
+
+ LLRect text_rect(line.mRect.mLeft + mTextRect.mLeft - scrolled_view_rect.mLeft,
+ line.mRect.mTop - scrolled_view_rect.mBottom + mTextRect.mBottom,
+ mDocumentPanel->getRect().getWidth() - scrolled_view_rect.mLeft,
+ line.mRect.mBottom - scrolled_view_rect.mBottom + mTextRect.mBottom);
+
+ // draw a single line of text
+ S32 seg_start = line_start;
+ while( seg_start < line_end )
+ {
+ while( cur_segment->getEnd() <= seg_start )
+ {
+ seg_iter++;
+ if (seg_iter == mSegments.end())
+ {
+ llwarns << "Ran off the segmentation end!" << llendl;
+
+ return;
+ }
+ cur_segment = *seg_iter;
+ }
+
+ S32 clipped_end = llmin( line_end, cur_segment->getEnd() ) - cur_segment->getStart();
+ text_rect.mLeft = (S32)(cur_segment->draw(seg_start - cur_segment->getStart(), clipped_end, selection_left, selection_right, text_rect));
+
+ seg_start = clipped_end + cur_segment->getStart();
+ }
+
+ line_start = next_start;
+ }
+}
+
+///////////////////////////////////////////////////////////////////
+// Returns change in number of characters in mWText
+
+S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::segment_vec_t* segments )
+{
+ LLWString text(getWText());
+ S32 old_len = text.length(); // length() returns character length
+ S32 insert_len = wstr.length();
+
+ pos = getEditableIndex(pos, true);
+
+ segment_set_t::iterator seg_iter = getSegIterContaining(pos);
+
+ LLTextSegmentPtr default_segment;
+
+ LLTextSegmentPtr segmentp;
+ if (seg_iter != mSegments.end())
+ {
+ segmentp = *seg_iter;
+ }
+ else
+ {
+ //segmentp = mSegments.back();
+ return pos;
+ }
+
+ if (segmentp->canEdit())
+ {
+ segmentp->setEnd(segmentp->getEnd() + insert_len);
+ if (seg_iter != mSegments.end())
+ {
+ ++seg_iter;
+ }
+ }
+ else
+ {
+ // create default editable segment to hold new text
+ default_segment = new LLNormalTextSegment( new LLStyle(getDefaultStyle()), pos, pos + insert_len, *this);
+ }
+
+ // shift remaining segments to right
+ for(;seg_iter != mSegments.end(); ++seg_iter)
+ {
+ LLTextSegmentPtr segmentp = *seg_iter;
+ segmentp->setStart(segmentp->getStart() + insert_len);
+ segmentp->setEnd(segmentp->getEnd() + insert_len);
+ }
+
+ // insert new segments
+ if (segments)
+ {
+ if (default_segment.notNull())
+ {
+ // potentially overwritten by segments passed in
+ insertSegment(default_segment);
+ }
+ for (segment_vec_t::iterator seg_iter = segments->begin();
+ seg_iter != segments->end();
+ ++seg_iter)
+ {
+ LLTextSegment* segmentp = *seg_iter;
+ insertSegment(segmentp);
+ }
+ }
+
+ text.insert(pos, wstr);
+ getViewModel()->setDisplay(text);
+
+ if ( truncate() )
+ {
+ // The user's not getting everything he's hoping for
+ make_ui_sound("UISndBadKeystroke");
+ insert_len = getLength() - old_len;
+ }
+
+ onValueChange(pos, pos + insert_len);
+
+ return insert_len;
+}
+
+S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length)
+{
+ LLWString text(getWText());
+ segment_set_t::iterator seg_iter = getSegIterContaining(pos);
+ while(seg_iter != mSegments.end())
+ {
+ LLTextSegmentPtr segmentp = *seg_iter;
+ S32 end = pos + length;
+ if (segmentp->getStart() < pos)
+ {
+ // deleting from middle of segment
+ if (segmentp->getEnd() > end)
+ {
+ segmentp->setEnd(segmentp->getEnd() - length);
+ }
+ // truncating segment
+ else
+ {
+ segmentp->setEnd(pos);
+ }
+ }
+ else if (segmentp->getStart() < end)
+ {
+ // deleting entire segment
+ if (segmentp->getEnd() <= end)
+ {
+ // remove segment
+ segmentp->unlinkFromDocument(this);
+ segment_set_t::iterator seg_to_erase(seg_iter++);
+ mSegments.erase(seg_to_erase);
+ continue;
+ }
+ // deleting head of segment
+ else
+ {
+ segmentp->setStart(pos);
+ segmentp->setEnd(segmentp->getEnd() - length);
+ }
+ }
+ else
+ {
+ // shifting segments backward to fill deleted portion
+ segmentp->setStart(segmentp->getStart() - length);
+ segmentp->setEnd(segmentp->getEnd() - length);
+ }
+ ++seg_iter;
+ }
+
+ text.erase(pos, length);
+ getViewModel()->setDisplay(text);
+
+ // recreate default segment in case we erased everything
+ createDefaultSegment();
+
+ onValueChange(pos, pos);
+
+ return -length; // This will be wrong if someone calls removeStringNoUndo with an excessive length
+}
+
+S32 LLTextBase::overwriteCharNoUndo(S32 pos, llwchar wc)
+{
+ if (pos > (S32)getLength())
+ {
+ return 0;
+ }
+ LLWString text(getWText());
+ text[pos] = wc;
+ getViewModel()->setDisplay(text);
+
+ onValueChange(pos, pos + 1);
+
+ return 1;
+}
+
+
+void LLTextBase::createDefaultSegment()
+{
+ // ensures that there is always at least one segment
+ if (mSegments.empty())
+ {
+ LLTextSegmentPtr default_segment = new LLNormalTextSegment( new LLStyle(getDefaultStyle()), 0, getLength() + 1, *this);
+ 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());
+
+ if (cur_seg_iter == mSegments.end())
+ {
+ mSegments.insert(segment_to_insert);
+ segment_to_insert->linkToDocument(this);
+ }
+ else
+ {
+ LLTextSegmentPtr cur_segmentp = *cur_seg_iter;
+ 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
+ ++cur_seg_iter;
+ // insert remainder of old segment
+ LLTextSegmentPtr remainder_segment = new LLNormalTextSegment( cur_segmentp->getStyle(), segment_to_insert->getStart(), old_segment_end, *this);
+ cur_seg_iter = mSegments.insert(cur_seg_iter, remainder_segment);
+ remainder_segment->linkToDocument(this);
+ // insert new segment before remainder of old segment
+ cur_seg_iter = mSegments.insert(cur_seg_iter, segment_to_insert);
+
+ segment_to_insert->linkToDocument(this);
+ // move to "remanider" segment and start truncation there
+ ++cur_seg_iter;
+ }
+ else
+ {
+ cur_seg_iter = mSegments.insert(cur_seg_iter, segment_to_insert);
+ ++cur_seg_iter;
+ segment_to_insert->linkToDocument(this);
+ }
+
+ // now delete/truncate remaining segments as necessary
+ while(cur_seg_iter != mSegments.end())
+ {
+ cur_segmentp = *cur_seg_iter;
+ if (cur_segmentp->getEnd() <= segment_to_insert->getEnd())
+ {
+ cur_segmentp->unlinkFromDocument(this);
+ segment_set_t::iterator seg_to_erase(cur_seg_iter++);
+ mSegments.erase(seg_to_erase);
+ }
+ else
+ {
+ cur_segmentp->setStart(segment_to_insert->getEnd());
+ break;
+ }
+ }
+ }
+}
+
+BOOL LLTextBase::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleMouseDown(x, y, mask))
+ {
+ return TRUE;
+ }
+
+ return LLUICtrl::handleMouseDown(x, y, mask);
+}
+
+BOOL LLTextBase::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleMouseUp(x, y, mask))
+ {
+ // Did we just click on a link?
+ if (cur_segment->getStyle()
+ && cur_segment->getStyle()->isLink())
+ {
+ // *TODO: send URL here?
+ mURLClickSignal(this, LLSD() );
+ }
+ return TRUE;
+ }
+
+ return LLUICtrl::handleMouseUp(x, y, mask);
+}
+
+BOOL LLTextBase::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleMiddleMouseDown(x, y, mask))
+ {
+ return TRUE;
+ }
+
+ return LLUICtrl::handleMiddleMouseDown(x, y, mask);
+}
+
+BOOL LLTextBase::handleMiddleMouseUp(S32 x, S32 y, MASK mask)
+{
+ LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleMiddleMouseUp(x, y, mask))
+ {
+ return TRUE;
+ }
+
+ return LLUICtrl::handleMiddleMouseUp(x, y, mask);
+}
+
+BOOL LLTextBase::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleRightMouseDown(x, y, mask))
+ {
+ return TRUE;
+ }
+
+ return LLUICtrl::handleRightMouseDown(x, y, mask);
+}
+
+BOOL LLTextBase::handleRightMouseUp(S32 x, S32 y, MASK mask)
+{
+ LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleRightMouseUp(x, y, mask))
+ {
+ return TRUE;
+ }
+
+ return LLUICtrl::handleRightMouseUp(x, y, mask);
+}
+
+BOOL LLTextBase::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleDoubleClick(x, y, mask))
+ {
+ return TRUE;
+ }
+
+ return LLUICtrl::handleDoubleClick(x, y, mask);
+}
+
+BOOL LLTextBase::handleHover(S32 x, S32 y, MASK mask)
+{
+ LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleHover(x, y, mask))
+ {
+ return TRUE;
+ }
+
+ return LLUICtrl::handleHover(x, y, mask);
+}
+
+BOOL LLTextBase::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleScrollWheel(x, y, clicks))
+ {
+ return TRUE;
+ }
+
+ return LLUICtrl::handleScrollWheel(x, y, clicks);
+}
+
+BOOL LLTextBase::handleToolTip(S32 x, S32 y, MASK mask)
+{
+ LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y);
+ if (cur_segment && cur_segment->handleToolTip(x, y, mask))
+ {
+ return TRUE;
+ }
+
+ return LLUICtrl::handleToolTip(x, y, mask);
+}
+
+
+void LLTextBase::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLUICtrl::reshape( width, height, called_from_parent );
+
+ // do this first after reshape, because other things depend on
+ // up-to-date mTextRect
+ updateTextRect();
+
+ needsReflow();
+}
+
+void LLTextBase::draw()
+{
+ // reflow if needed, on demand
+ reflow();
+
+ // then update scroll position, as cursor may have moved
+ updateScrollFromCursor();
+
+ LLColor4 bg_color = mReadOnly
+ ? mReadOnlyBgColor.get()
+ : hasFocus()
+ ? mFocusBgColor.get()
+ : mWriteableBgColor.get();
+
+ mDocumentPanel->setBackgroundColor(bg_color);
+
+ LLUICtrl::draw();
+ {
+ LLLocalClipRect clip(mTextRect, mClip);
+ drawSelectionBackground();
+ drawText();
+ drawCursor();
+ }
+}
+
+//virtual
+void LLTextBase::clear()
+{
+ getViewModel()->setDisplay(LLWStringUtil::null);
+ clearSegments();
+}
+
+//virtual
+void LLTextBase::setColor( const LLColor4& c )
+{
+ mFgColor = c;
+}
+
+//virtual
+void LLTextBase::setValue(const LLSD& value )
+{
+ setText(value.asString());
+}
+
+//virtual
+void LLTextBase::deselect()
+{
+ mSelectionStart = 0;
+ mSelectionEnd = 0;
+ mIsSelecting = FALSE;
+}
+
+
+// Sets the scrollbar from the cursor position
+void LLTextBase::updateScrollFromCursor()
+{
+ // Update scroll position even in read-only mode (when there's no cursor displayed)
+ // because startOfDoc()/endOfDoc() modify cursor position. See EXT-736.
+
+ if (!mScrollNeeded)
+ {
+ return;
+ }
+ mScrollNeeded = FALSE;
+
+ // scroll so that the cursor is at the top of the page
+ LLRect scroller_doc_window = mScroller->getVisibleContentRect();
+ LLRect cursor_rect_doc = getLocalRectFromDocIndex(mCursorPos);
+ cursor_rect_doc.translate(scroller_doc_window.mLeft, scroller_doc_window.mBottom);
+ mScroller->scrollToShowRect(cursor_rect_doc, LLRect(0, scroller_doc_window.getHeight() - 5, scroller_doc_window.getWidth(), 5));
+}
+
+S32 LLTextBase::getLeftOffset(S32 width)
+{
+ switch (mHAlign)
+ {
+ case LLFontGL::LEFT:
+ return 0;
+ case LLFontGL::HCENTER:
+ return (mTextRect.getWidth() - width) / 2;
+ case LLFontGL::RIGHT:
+ return mTextRect.getWidth() - width;
+ default:
+ return 0;
+ }
+}
+
+
+static LLFastTimer::DeclareTimer FTM_TEXT_REFLOW ("Text Reflow");
+void LLTextBase::reflow(S32 start_index)
+{
+ if (!mReflowNeeded) return;
+
+ LLFastTimer ft(FTM_TEXT_REFLOW);
+
+ updateSegments();
+
+ while(mReflowNeeded)
+ {
+ mReflowNeeded = FALSE;
+
+ bool scrolled_to_bottom = mScroller->isAtBottom();
+
+ LLRect old_cursor_rect = getLocalRectFromDocIndex(mCursorPos);
+ bool follow_selection = mTextRect.overlaps(old_cursor_rect); // cursor is visible
+ S32 first_line = getFirstVisibleLine();
+
+ // if scroll anchor not on first line, update it to first character of first line
+ if (!mLineInfoList.empty()
+ && (mScrollIndex < mLineInfoList[first_line].mDocIndexStart
+ || mScrollIndex >= mLineInfoList[first_line].mDocIndexEnd))
+ {
+ mScrollIndex = mLineInfoList[first_line].mDocIndexStart;
+ }
+ LLRect first_char_rect = getLocalRectFromDocIndex(mScrollIndex);
+
+ S32 cur_top = 0;
+
+ if (getLength())
+ {
+ segment_set_t::iterator seg_iter = mSegments.begin();
+ S32 seg_offset = 0;
+ S32 line_start_index = 0;
+ const S32 text_width = mTextRect.getWidth(); // optionally reserve room for margin
+ S32 remaining_pixels = text_width;
+ LLWString text(getWText());
+ S32 line_count = 0;
+
+ // find and erase line info structs starting at start_index and going to end of document
+ if (!mLineInfoList.empty())
+ {
+ // find first element whose end comes after start_index
+ line_list_t::iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), start_index, line_end_compare());
+ line_start_index = iter->mDocIndexStart;
+ line_count = iter->mLineNum;
+ getSegmentAndOffset(iter->mDocIndexStart, &seg_iter, &seg_offset);
+ mLineInfoList.erase(iter, mLineInfoList.end());
+ }
+
+ S32 line_height = 0;
+
+ while(seg_iter != mSegments.end())
+ {
+ LLTextSegmentPtr segment = *seg_iter;
+
+ // track maximum height of any segment on this line
+ line_height = llmax(line_height, segment->getMaxHeight());
+ S32 cur_index = segment->getStart() + seg_offset;
+ // find run of text from this segment that we can display on one line
+ S32 end_index = cur_index;
+ while(end_index < segment->getEnd() && text[end_index] != '\n')
+ {
+ ++end_index;
+ }
+
+ // ask segment how many character fit in remaining space
+ S32 max_characters = end_index - cur_index;
+ S32 character_count = segment->getNumChars(getWordWrap() ? llmax(0, remaining_pixels) : S32_MAX,
+ seg_offset,
+ cur_index - line_start_index,
+ max_characters);
+
+
+ S32 segment_width = segment->getWidth(seg_offset, character_count);
+ remaining_pixels -= segment_width;
+ S32 text_left = getLeftOffset(text_width - remaining_pixels);
+
+ seg_offset += character_count;
+
+ S32 last_segment_char_on_line = segment->getStart() + seg_offset;
+
+ // if we didn't finish the current segment...
+ if (last_segment_char_on_line < segment->getEnd())
+ {
+ // set up index for next line
+ // ...skip newline, we don't want to draw
+ S32 next_line_count = line_count;
+ if (text[last_segment_char_on_line] == '\n')
+ {
+ seg_offset++;
+ last_segment_char_on_line++;
+ next_line_count++;
+ }
+
+ // add line info and keep going
+ mLineInfoList.push_back(line_info(
+ line_start_index,
+ last_segment_char_on_line,
+ LLRect(text_left,
+ cur_top,
+ text_left + (text_width - remaining_pixels),
+ cur_top - line_height),
+ line_count));
+
+ line_start_index = segment->getStart() + seg_offset;
+ cur_top -= llround((F32)line_height * mLineSpacingMult) + mLineSpacingPixels;
+ remaining_pixels = text_width;
+ line_height = 0;
+ line_count = next_line_count;
+ }
+ // ...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,
+ LLRect(text_left,
+ cur_top,
+ text_left + (text_width - remaining_pixels),
+ cur_top - line_height),
+ line_count));
+ cur_top -= llround((F32)line_height * mLineSpacingMult) + mLineSpacingPixels;
+ break;
+ }
+ // finished a segment and there are segments remaining on this line
+ else
+ {
+ // subtract pixels used and increment segment
+ ++seg_iter;
+ seg_offset = 0;
+ }
+ }
+ }
+
+ if (mLineInfoList.empty())
+ {
+ mContentsRect = LLRect(0, mVPad, mHPad, 0);
+ }
+ else
+ {
+
+ mContentsRect = mLineInfoList.begin()->mRect;
+ for (line_list_t::const_iterator line_iter = ++mLineInfoList.begin();
+ line_iter != mLineInfoList.end();
+ ++line_iter)
+ {
+ mContentsRect.unionWith(line_iter->mRect);
+ }
+
+ mContentsRect.mRight += mHPad;
+ mContentsRect.mTop += mVPad;
+ // get around rounding errors when clipping text against rectangle
+ mContentsRect.stretch(1);
+ }
+
+ // change mDocumentPanel document size to accomodate reflowed text
+ LLRect document_rect;
+ document_rect.setOriginAndSize(1, 1,
+ mScroller->getContentWindowRect().getWidth(),
+ llmax(mScroller->getContentWindowRect().getHeight(), mContentsRect.getHeight()));
+ mDocumentPanel->setShape(document_rect);
+
+ // after making document big enough to hold all the text, move the text to fit in the document
+ if (!mLineInfoList.empty())
+ {
+ S32 delta_pos = mDocumentPanel->getRect().getHeight() - mLineInfoList.begin()->mRect.mTop - mVPad;
+ // 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);
+ }
+ mContentsRect.translate(0, delta_pos);
+ }
+
+ // calculate visible region for diplaying text
+ updateTextRect();
+
+ 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())
+ {
+ LLRect visible_content_rect = mScroller->getVisibleContentRect();
+ 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 = getLocalRectFromDocIndex(mCursorPos);
+ new_cursor_rect_doc.translate(visible_content_rect.mLeft, visible_content_rect.mBottom);
+ mScroller->scrollToShowRect(new_cursor_rect_doc, old_cursor_rect);
+ //llassert_always(getLocalRectFromDocIndex(mCursorPos).mBottom == old_cursor_rect.mBottom);
+ }
+ else
+ {
+ // keep first line of text visible
+ LLRect new_first_char_rect = getLocalRectFromDocIndex(mScrollIndex);
+ new_first_char_rect.translate(visible_content_rect.mLeft, visible_content_rect.mBottom);
+ mScroller->scrollToShowRect(new_first_char_rect, first_char_rect);
+ //llassert_always(getLocalRectFromDocIndex(mScrollIndex).mBottom == first_char_rect.mBottom);
+ }
+ }
+ }
+
+
+ // reset desired x cursor position
+ updateCursorXPos();
+}
+
+LLRect LLTextBase::getContentsRect()
+{
+ reflow();
+ return mContentsRect;
+}
+
+
+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::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 = mScroller->getVisibleContentRect();
+
+ // 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() const
+{
+ LLRect visible_region = mScroller->getVisibleContentRect();
+
+ // binary search for line that starts before top of visible buffer and starts before end of visible buffer
+ line_list_t::const_iterator first_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_bottom());
+ line_list_t::const_iterator 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)
+{
+ mDocumentPanel->addChild(view);
+}
+
+void LLTextBase::removeDocumentChild(LLView* view)
+{
+ mDocumentPanel->removeChild(view);
+}
+
+
+static LLFastTimer::DeclareTimer FTM_UPDATE_TEXT_SEGMENTS("Update Text Segments");
+void LLTextBase::updateSegments()
+{
+ LLFastTimer ft(FTM_UPDATE_TEXT_SEGMENTS);
+ createDefaultSegment();
+}
+
+void LLTextBase::getSegmentAndOffset( S32 startpos, segment_set_t::const_iterator* seg_iter, S32* offsetp ) const
+{
+ *seg_iter = getSegIterContaining(startpos);
+ if (*seg_iter == mSegments.end())
+ {
+ *offsetp = 0;
+ }
+ else
+ {
+ *offsetp = startpos - (**seg_iter)->getStart();
+ }
+}
+
+void LLTextBase::getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp )
+{
+ *seg_iter = getSegIterContaining(startpos);
+ if (*seg_iter == mSegments.end())
+ {
+ *offsetp = 0;
+ }
+ else
+ {
+ *offsetp = startpos - (**seg_iter)->getStart();
+ }
+}
+
+LLTextBase::segment_set_t::iterator LLTextBase::getSegIterContaining(S32 index)
+{
+ segment_set_t::iterator it = mSegments.upper_bound(new LLIndexSegment(index));
+ return it;
+}
+
+LLTextBase::segment_set_t::const_iterator LLTextBase::getSegIterContaining(S32 index) const
+{
+ LLTextBase::segment_set_t::const_iterator it = mSegments.upper_bound(new LLIndexSegment(index));
+ return it;
+}
+
+// Finds the text segment (if any) at the give local screen position
+LLTextSegmentPtr LLTextBase::getSegmentAtLocalPos( S32 x, S32 y )
+{
+ // Find the cursor position at the requested local screen position
+ S32 offset = getDocIndexFromLocalCoord( x, y, FALSE );
+ segment_set_t::iterator seg_iter = getSegIterContaining(offset);
+ if (seg_iter != mSegments.end())
+ {
+ return *seg_iter;
+ }
+ else
+ {
+ return LLTextSegmentPtr();
+ }
+}
+
+void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url)
+{
+ // work out the XUI menu file to use for this url
+ LLUrlMatch match;
+ std::string url = in_url;
+ if (! LLUrlRegistry::instance().findUrl(url, match))
+ {
+ return;
+ }
+
+ std::string xui_file = match.getMenuName();
+ if (xui_file.empty())
+ {
+ return;
+ }
+
+ // set up the callbacks for all of the potential menu items, N.B. we
+ // don't use const ref strings in callbacks in case url goes out of scope
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+ registrar.add("Url.Open", boost::bind(&LLUrlAction::openURL, url));
+ registrar.add("Url.OpenInternal", boost::bind(&LLUrlAction::openURLInternal, url));
+ registrar.add("Url.OpenExternal", boost::bind(&LLUrlAction::openURLExternal, url));
+ registrar.add("Url.Execute", boost::bind(&LLUrlAction::executeSLURL, url));
+ registrar.add("Url.Teleport", boost::bind(&LLUrlAction::teleportToLocation, url));
+ registrar.add("Url.CopyLabel", boost::bind(&LLUrlAction::copyLabelToClipboard, url));
+ registrar.add("Url.CopyUrl", boost::bind(&LLUrlAction::copyURLToClipboard, url));
+
+ // create and return the context menu from the XUI file
+ delete mPopupMenu;
+ mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(xui_file, LLMenuGL::sMenuContainer,
+ LLMenuHolderGL::child_registry_t::instance());
+ if (mPopupMenu)
+ {
+ mPopupMenu->show(x, y);
+ LLMenuGL::showPopup(this, mPopupMenu, x, y);
+ }
+}
+
+void LLTextBase::setText(const LLStringExplicit &utf8str)
+{
+ // clear out the existing text and segments
+ clear();
+
+ truncate();
+
+ createDefaultSegment();
+
+ startOfDoc();
+ deselect();
+
+ // append the new text (supports Url linking)
+ std::string text(utf8str);
+ LLStringUtil::removeCRLF(text);
+
+ appendText(text, false);
+
+ needsReflow();
+
+ //resetDirty();
+ onValueChange(0, getLength());
+}
+
+//virtual
+std::string LLTextBase::getText() const
+{
+ return getViewModel()->getValue().asString();
+}
+
+void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params)
+{
+ LLStyle::Params style_params(input_params);
+ style_params.fillFrom(getDefaultStyle());
+
+ if (!style_params.font.isProvided())
+ {
+ style_params.font = mDefaultFont;
+ }
+ if (!style_params.drop_shadow.isProvided())
+ {
+ style_params.drop_shadow = mFontShadow;
+ }
+
+ S32 part = (S32)LLTextParser::WHOLE;
+ if(mParseHTML)
+ {
+ S32 start=0,end=0;
+ LLUrlMatch match;
+ std::string text = new_text;
+ while ( LLUrlRegistry::instance().findUrl(text, match,
+ boost::bind(&LLTextBase::replaceUrlLabel, this, _1, _2)) )
+ {
+ start = match.getStart();
+ end = match.getEnd()+1;
+
+ LLStyle::Params link_params = style_params;
+ link_params.color = match.getColor();
+ // apply font name from requested style_params
+ std::string font_name = LLFontGL::nameFromFont(style_params.font());
+ link_params.font.name.setIfNotProvided(font_name);
+ link_params.font.style = "UNDERLINE";
+ link_params.link_href = match.getUrl();
+
+ // output the text before the Url
+ if (start > 0)
+ {
+ if (part == (S32)LLTextParser::WHOLE ||
+ part == (S32)LLTextParser::START)
+ {
+ part = (S32)LLTextParser::START;
+ }
+ else
+ {
+ part = (S32)LLTextParser::MIDDLE;
+ }
+ std::string subtext=text.substr(0,start);
+ appendAndHighlightText(subtext, prepend_newline, part, style_params);
+ prepend_newline = false;
+ }
+
+ // output an optional icon before the Url
+ if (! match.getIcon().empty())
+ {
+ LLUIImagePtr image = LLUI::getUIImage(match.getIcon());
+ if (image)
+ {
+ LLStyle::Params icon;
+ icon.image = image;
+ // HACK: fix spacing of images and remove the fixed char spacing
+ appendAndHighlightText(" ", prepend_newline, part, icon);
+ prepend_newline = false;
+ }
+ }
+ // output the styled Url
+ appendAndHighlightText(match.getLabel(), prepend_newline, part, link_params);
+ prepend_newline = false;
+
+ // set the tooltip for the Url label
+ if (! match.getTooltip().empty())
+ {
+ segment_set_t::iterator it = getSegIterContaining(getLength()-1);
+ if (it != mSegments.end())
+ {
+ LLTextSegmentPtr segment = *it;
+ segment->setToolTip(match.getTooltip());
+ }
+ }
+
+ // move on to the rest of the text after the Url
+ if (end < (S32)text.length())
+ {
+ text = text.substr(end,text.length() - end);
+ end=0;
+ part=(S32)LLTextParser::END;
+ }
+ else
+ {
+ break;
+ }
+ }
+ if (part != (S32)LLTextParser::WHOLE) part=(S32)LLTextParser::END;
+ if (end < (S32)text.length()) appendAndHighlightText(text, prepend_newline, part, style_params);
+ }
+ else
+ {
+ appendAndHighlightText(new_text, prepend_newline, part, style_params);
+ }
+}
+
+void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepend_newline, S32 highlight_part, const LLStyle::Params& stylep)
+{
+ if (new_text.empty()) return;
+
+ // 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);
+
+ LLTextParser* highlight = LLTextParser::getInstance();
+
+ if (mParseHighlights && highlight)
+ {
+ LLStyle::Params highlight_params = stylep;
+
+ LLSD pieces = highlight->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;
+ if (prepend_newline && (i == 0 || pieces.size() <= 1 ))
+ {
+ wide_text = utf8str_to_wstring(std::string("\n") + pieces[i]["text"].asString());
+ }
+ else
+ {
+ wide_text = utf8str_to_wstring(pieces[i]["text"].asString());
+ }
+ S32 cur_length = getLength();
+ LLTextSegmentPtr segmentp = new LLNormalTextSegment(new LLStyle(highlight_params), cur_length, cur_length + wide_text.size(), *this);
+ segment_vec_t segments;
+ segments.push_back(segmentp);
+ insertStringNoUndo(cur_length, wide_text, &segments);
+ }
+ }
+ else
+ {
+ LLWString wide_text;
+
+ // Add carriage return if not first line
+ if (getLength() != 0
+ && prepend_newline)
+ {
+ wide_text = utf8str_to_wstring(std::string("\n") + new_text);
+ }
+ else
+ {
+ wide_text = utf8str_to_wstring(new_text);
+ }
+
+ segment_vec_t segments;
+ S32 segment_start = old_length;
+ S32 segment_end = old_length + wide_text.size();
+ segments.push_back(new LLNormalTextSegment(new LLStyle(stylep), segment_start, segment_end, *this ));
+
+ insertStringNoUndo(getLength(), wide_text, &segments);
+ }
+
+ needsReflow();
+
+ // 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 LLTextBase::replaceUrlLabel(const std::string &url,
+ const std::string &label)
+{
+ // get the full (wide) text for the editor so we can change it
+ LLWString text = getWText();
+ LLWString wlabel = utf8str_to_wstring(label);
+ bool modified = false;
+ S32 seg_start = 0;
+
+ // iterate through each segment looking for ones styled as links
+ segment_set_t::iterator it;
+ for (it = mSegments.begin(); it != mSegments.end(); ++it)
+ {
+ LLTextSegment *seg = *it;
+ const LLStyleSP style = seg->getStyle();
+
+ // update segment start/end length in case we replaced text earlier
+ S32 seg_length = seg->getEnd() - seg->getStart();
+ seg->setStart(seg_start);
+ seg->setEnd(seg_start + seg_length);
+
+ // if we find a link with our Url, then replace the label
+ if (style->isLink() && style->getLinkHREF() == url)
+ {
+ S32 start = seg->getStart();
+ S32 end = seg->getEnd();
+ text = text.substr(0, start) + wlabel + text.substr(end, text.size() - end + 1);
+ seg->setEnd(start + wlabel.size());
+ modified = true;
+ }
+
+ // work out the character offset for the next segment
+ seg_start = seg->getEnd();
+ }
+
+ // update the editor with the new (wide) text string
+ if (modified)
+ {
+ getViewModel()->setDisplay(text);
+ deselect();
+ setCursorPos(mCursorPos);
+ needsReflow();
+ }
+}
+
+
+void LLTextBase::setWText(const LLWString& text)
+{
+ setText(wstring_to_utf8str(text));
+}
+
+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 ) const
+{
+ // Figure out which line we're nearest to.
+ LLRect visible_region = mScroller->getVisibleContentRect();
+
+ // binary search for line that starts before local_y
+ line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), local_y - mTextRect.mBottom + visible_region.mBottom, compare_bottom());
+
+ if (line_iter == mLineInfoList.end())
+ {
+ return getLength(); // past the end
+ }
+
+ S32 pos = getLength();
+ S32 start_x = mTextRect.mLeft + line_iter->mRect.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 - 1) - segment_line_start;
+ S32 text_width = segmentp->getWidth(line_seg_offset, segment_line_length);
+ if (local_x < start_x + text_width // cursor to left of right edge of text
+ || segmentp->getEnd() >= line_iter->mDocIndexEnd - 1) // or this segment wraps to next line
+ {
+ // Figure out which character we're nearest to.
+ S32 offset;
+ if (!segmentp->canEdit())
+ {
+ S32 segment_width = segmentp->getWidth(0, segmentp->getEnd() - segmentp->getStart());
+ 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;
+ }
+ start_x += text_width;
+ }
+
+ return pos;
+}
+
+LLRect LLTextBase::getLocalRectFromDocIndex(S32 pos) const
+{
+ LLRect local_rect;
+ if (mLineInfoList.empty())
+ {
+ local_rect = mTextRect;
+ local_rect.mBottom = local_rect.mTop - (S32)(mDefaultFont->getLineHeight());
+ return local_rect;
+ }
+
+ // clamp pos to valid values
+ pos = llclamp(pos, 0, mLineInfoList.back().mDocIndexEnd - 1);
+
+
+ // find line that contains cursor
+ line_list_t::const_iterator line_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), pos, line_end_compare());
+
+ LLRect scrolled_view_rect = mScroller->getVisibleContentRect();
+ local_rect.mLeft = mTextRect.mLeft - scrolled_view_rect.mLeft + line_iter->mRect.mLeft;
+ local_rect.mBottom = mTextRect.mBottom + (line_iter->mRect.mBottom - scrolled_view_rect.mBottom);
+ local_rect.mTop = mTextRect.mBottom + (line_iter->mRect.mTop - scrolled_view_rect.mBottom);
+
+ segment_set_t::iterator line_seg_iter;
+ S32 line_seg_offset;
+ segment_set_t::iterator cursor_seg_iter;
+ S32 cursor_seg_offset;
+ getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset);
+ getSegmentAndOffset(pos, &cursor_seg_iter, &cursor_seg_offset);
+
+ while(line_seg_iter != mSegments.end())
+ {
+ const LLTextSegmentPtr segmentp = *line_seg_iter;
+
+ if (line_seg_iter == cursor_seg_iter)
+ {
+ // cursor advanced to right based on difference in offset of cursor to start of line
+ local_rect.mLeft += segmentp->getWidth(line_seg_offset, cursor_seg_offset - line_seg_offset);
+
+ break;
+ }
+ else
+ {
+ // add remainder of current text segment to cursor position
+ local_rect.mLeft += segmentp->getWidth(line_seg_offset, (segmentp->getEnd() - segmentp->getStart()) - line_seg_offset);
+ // 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;
+ }
+ }
+
+ local_rect.mRight = local_rect.mLeft;
+
+ 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);
+}
+
+void LLTextBase::endOfDoc()
+{
+ setCursorPos(getLength());
+}
+
+void LLTextBase::changePage( S32 delta )
+{
+ const S32 PIXEL_OVERLAP_ON_PAGE_CHANGE = 10;
+ if (delta == 0) return;
+
+ LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos);
+
+ if( delta == -1 )
+ {
+ mScroller->pageUp(PIXEL_OVERLAP_ON_PAGE_CHANGE);
+ }
+ else
+ if( delta == 1 )
+ {
+ mScroller->pageDown(PIXEL_OVERLAP_ON_PAGE_CHANGE);
+ }
+
+ if (getLocalRectFromDocIndex(mCursorPos) == cursor_rect)
+ {
+ // cursor didn't change apparent position, so move to top or bottom of document, respectively
+ if (delta < 0)
+ {
+ startOfDoc();
+ }
+ else
+ {
+ endOfDoc();
+ }
+ }
+ else
+ {
+ setCursorAtLocalPos(cursor_rect.getCenterX(), cursor_rect.getCenterY(), true, false);
+ }
+}
+
+// Picks a new cursor position based on the screen size of text being drawn.
+void LLTextBase::setCursorAtLocalPos( S32 local_x, S32 local_y, bool round, bool keep_cursor_offset )
+{
+ setCursorPos(getDocIndexFromLocalCoord(local_x, local_y, round), keep_cursor_offset);
+}
+
+
+void LLTextBase::changeLine( S32 delta )
+{
+ S32 line = getLineNumFromDocIndex(mCursorPos);
+
+ S32 new_line = line;
+ if( (delta < 0) && (line > 0 ) )
+ {
+ new_line = line - 1;
+ }
+ else if( (delta > 0) && (line < (getLineCount() - 1)) )
+ {
+ new_line = line + 1;
+ }
+
+ LLRect visible_region = mScroller->getVisibleContentRect();
+
+ S32 new_cursor_pos = getDocIndexFromLocalCoord(mDesiredXPixel, mLineInfoList[new_line].mRect.mBottom + mTextRect.mBottom - visible_region.mBottom, TRUE);
+ setCursorPos(new_cursor_pos, true);
+}
+
+
+bool LLTextBase::setCursor(S32 row, S32 column)
+{
+ if (0 <= row && row < (S32)mLineInfoList.size())
+ {
+ S32 doc_pos = mLineInfoList[row].mDocIndexStart;
+ column = llclamp(column, 0, mLineInfoList[row].mDocIndexEnd - mLineInfoList[row].mDocIndexStart - 1);
+ doc_pos += column;
+ updateCursorXPos();
+
+ return setCursorPos(doc_pos);
+ }
+ return false;
+}
+
+
+bool LLTextBase::setCursorPos(S32 cursor_pos, bool keep_cursor_offset)
+{
+ S32 new_cursor_pos = cursor_pos;
+ if (new_cursor_pos != mCursorPos)
+ {
+ new_cursor_pos = getEditableIndex(new_cursor_pos, new_cursor_pos >= mCursorPos);
+ }
+
+ mCursorPos = llclamp(new_cursor_pos, 0, (S32)getLength());
+ needsScroll();
+ if (!keep_cursor_offset)
+ updateCursorXPos();
+ // did we get requested position?
+ return new_cursor_pos == cursor_pos;
+}
+
+// constraint cursor to editable segments of document
+S32 LLTextBase::getEditableIndex(S32 index, bool increasing_direction)
+{
+ segment_set_t::iterator segment_iter;
+ S32 offset;
+ getSegmentAndOffset(index, &segment_iter, &offset);
+ if (segment_iter == mSegments.end())
+ {
+ return 0;
+ }
+
+ LLTextSegmentPtr segmentp = *segment_iter;
+
+ if (segmentp->canEdit())
+ {
+ return segmentp->getStart() + offset;
+ }
+ else if (segmentp->getStart() < index && index < segmentp->getEnd())
+ {
+ // bias towards document end
+ if (increasing_direction)
+ {
+ return segmentp->getEnd();
+ }
+ // bias towards document start
+ else
+ {
+ return segmentp->getStart();
+ }
+ }
+ else
+ {
+ return index;
+ }
+}
+
+void LLTextBase::updateTextRect()
+{
+ LLRect old_text_rect = mTextRect;
+ mTextRect = mScroller->getContentWindowRect();
+ //FIXME: replace border with image?
+ if (mBorderVisible)
+ {
+ mTextRect.stretch(-1);
+ }
+ mTextRect.mLeft += mHPad;
+ mTextRect.mTop -= mVPad;
+ if (mTextRect != old_text_rect)
+ {
+ needsReflow();
+ }
+}
+
+
+void LLTextBase::startSelection()
+{
+ if( !mIsSelecting )
+ {
+ mIsSelecting = TRUE;
+ mSelectionStart = mCursorPos;
+ mSelectionEnd = mCursorPos;
+ }
+}
+
+void LLTextBase::endSelection()
+{
+ if( mIsSelecting )
+ {
+ mIsSelecting = FALSE;
+ mSelectionEnd = mCursorPos;
+ }
+}
+
//
// LLTextSegment
//
@@ -69,17 +2055,29 @@ S32 LLTextSegment::getMaxHeight() const { return 0; }
bool LLTextSegment::canEdit() const { return false; }
void LLTextSegment::unlinkFromDocument(LLTextBase*) {}
void LLTextSegment::linkToDocument(LLTextBase*) {}
-void LLTextSegment::setHasMouseHover(bool hover) {}
const LLColor4& LLTextSegment::getColor() const { return LLColor4::white; }
void LLTextSegment::setColor(const LLColor4 &color) {}
const LLStyleSP LLTextSegment::getStyle() const {static LLStyleSP sp(new LLStyle()); return sp; }
void LLTextSegment::setStyle(const LLStyleSP &style) {}
void LLTextSegment::setToken( LLKeywordToken* token ) {}
LLKeywordToken* LLTextSegment::getToken() const { return NULL; }
-BOOL LLTextSegment::getToolTip( std::string& msg ) const { return FALSE; }
void LLTextSegment::setToolTip( const std::string &msg ) {}
void LLTextSegment::dump() const {}
-
+BOOL LLTextSegment::handleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; }
+BOOL LLTextSegment::handleMouseUp(S32 x, S32 y, MASK mask) { return FALSE; }
+BOOL LLTextSegment::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; }
+BOOL LLTextSegment::handleMiddleMouseUp(S32 x, S32 y, MASK mask) { return FALSE; }
+BOOL LLTextSegment::handleRightMouseDown(S32 x, S32 y, MASK mask) { return FALSE; }
+BOOL LLTextSegment::handleRightMouseUp(S32 x, S32 y, MASK mask) { return FALSE; }
+BOOL LLTextSegment::handleDoubleClick(S32 x, S32 y, MASK mask) { return FALSE; }
+BOOL LLTextSegment::handleHover(S32 x, S32 y, MASK mask) { return FALSE; }
+BOOL LLTextSegment::handleScrollWheel(S32 x, S32 y, S32 clicks) { return FALSE; }
+BOOL LLTextSegment::handleToolTip(S32 x, S32 y, MASK mask) { return FALSE; }
+std::string LLTextSegment::getName() const { return ""; }
+void LLTextSegment::onMouseCaptureLost() {}
+void LLTextSegment::screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const {}
+void LLTextSegment::localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const {}
+BOOL LLTextSegment::hasMouseCapture() { return FALSE; }
//
// LLNormalTextSegment
@@ -89,7 +2087,6 @@ LLNormalTextSegment::LLNormalTextSegment( const LLStyleSP& style, S32 start, S32
: LLTextSegment(start, end),
mStyle( style ),
mToken(NULL),
- mHasMouseHover(false),
mEditor(editor)
{
mMaxHeight = llceil(mStyle->getFont()->getLineHeight());
@@ -98,7 +2095,6 @@ LLNormalTextSegment::LLNormalTextSegment( const LLStyleSP& style, S32 start, S32
LLNormalTextSegment::LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible)
: LLTextSegment(start, end),
mToken(NULL),
- mHasMouseHover(false),
mEditor(editor)
{
mStyle = new LLStyle(LLStyle::Params().visible(is_visible).color(color));
@@ -115,21 +2111,25 @@ F32 LLNormalTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selec
LLUIImagePtr image = mStyle->getImage();
S32 style_image_height = image->getHeight();
S32 style_image_width = image->getWidth();
- image->draw(draw_rect.mLeft, draw_rect.mTop-style_image_height,
+ // Center the image vertically
+ S32 image_bottom = draw_rect.getCenterY() - (style_image_height/2);
+ image->draw(draw_rect.mLeft, image_bottom,
style_image_width, style_image_height);
}
- return drawClippedSegment( getStart() + start, getStart() + end, selection_start, selection_end, draw_rect.mLeft, draw_rect.mBottom);
+ 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, F32 x, F32 y)
+F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRect rect)
{
+ F32 alpha = LLViewDrawContext::getCurrentContext().mAlpha;
+
const LLWString &text = mEditor.getWText();
- F32 right_x = x;
+ F32 right_x = rect.mLeft;
if (!mStyle->isVisible())
{
return right_x;
@@ -137,7 +2137,7 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele
const LLFontGL* font = mStyle->getFont();
- LLColor4 color = mStyle->getColor();
+ LLColor4 color = mStyle->getColor() % alpha;
font = mStyle->getFont();
@@ -147,9 +2147,9 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele
S32 start = seg_start;
S32 end = llmin( selection_start, seg_end );
S32 length = end - start;
- font->render(text, start, x, y, color, LLFontGL::LEFT, LLFontGL::BOTTOM, 0, LLFontGL::NO_SHADOW, length, S32_MAX, &right_x, mEditor.allowsEmbeddedItems());
+ font->render(text, start, rect.mLeft, rect.mTop, color, LLFontGL::LEFT, LLFontGL::TOP, 0, mStyle->getShadowType(), length, rect.getWidth(), &right_x, mEditor.getUseEllipses());
}
- x = right_x;
+ rect.mLeft = (S32)ceil(right_x);
if( (selection_start < seg_end) && (selection_end > seg_start) )
{
@@ -158,18 +2158,18 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele
S32 end = llmin( selection_end, seg_end );
S32 length = end - start;
- font->render(text, start, x, y,
+ font->render(text, start, rect.mLeft, rect.mTop,
LLColor4( 1.f - color.mV[0], 1.f - color.mV[1], 1.f - color.mV[2], 1.f ),
- LLFontGL::LEFT, LLFontGL::BOTTOM, 0, LLFontGL::NO_SHADOW, length, S32_MAX, &right_x, mEditor.allowsEmbeddedItems());
+ LLFontGL::LEFT, LLFontGL::TOP, 0, LLFontGL::NO_SHADOW, length, rect.mRight, &right_x, mEditor.getUseEllipses());
}
- x = right_x;
+ rect.mLeft = (S32)ceil(right_x);
if( selection_end < seg_end )
{
// Draw normally
S32 start = llmax( selection_end, seg_start );
S32 end = seg_end;
S32 length = end - start;
- font->render(text, start, x, y, color, LLFontGL::LEFT, LLFontGL::BOTTOM, 0, LLFontGL::NO_SHADOW, length, S32_MAX, &right_x, mEditor.allowsEmbeddedItems());
+ font->render(text, start, rect.mLeft, rect.mTop, color, LLFontGL::LEFT, LLFontGL::TOP, 0, mStyle->getShadowType(), length, rect.mRight, &right_x, mEditor.getUseEllipses());
}
return right_x;
}
@@ -179,21 +2179,54 @@ S32 LLNormalTextSegment::getMaxHeight() const
return mMaxHeight;
}
-BOOL LLNormalTextSegment::getToolTip(std::string& msg) const
+BOOL LLNormalTextSegment::handleHover(S32 x, S32 y, MASK mask)
+{
+ if (getStyle() && getStyle()->isLink())
+ {
+ LLUI::getWindow()->setCursor(UI_CURSOR_HAND);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLNormalTextSegment::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (getStyle() && getStyle()->isLink())
+ {
+ mEditor.createUrlContextMenu(x, y, getStyle()->getLinkHREF());
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLNormalTextSegment::handleMouseUp(S32 x, S32 y, MASK mask)
{
+ if (getStyle() && getStyle()->isLink())
+ {
+ LLUrlAction::clickAction(getStyle()->getLinkHREF());
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL LLNormalTextSegment::handleToolTip(S32 x, S32 y, MASK mask)
+{
+ std::string msg;
// do we have a tooltip for a loaded keyword (for script editor)?
if (mToken && !mToken->getToolTip().empty())
{
const LLWString& wmsg = mToken->getToolTip();
- msg = wstring_to_utf8str(wmsg);
+ LLToolTipMgr::instance().show(wstring_to_utf8str(wmsg));
return TRUE;
}
// or do we have an explicitly set tooltip (e.g., for Urls)
- if (! mTooltip.empty())
+ if (!mTooltip.empty())
{
- msg = mTooltip;
+ LLToolTipMgr::instance().show(mTooltip);
return TRUE;
}
+
return FALSE;
}
@@ -258,201 +2291,69 @@ void LLNormalTextSegment::dump() const
llendl;
}
-//////////////////////////////////////////////////////////////////////////
+
//
-// LLTextBase
+// LLInlineViewSegment
//
-LLTextBase::LLTextBase(const LLUICtrl::Params &p) :
- mHoverSegment(NULL),
- mDefaultFont(p.font),
- mParseHTML(TRUE),
- mPopupMenu(NULL)
-{
-}
-
-LLTextBase::~LLTextBase()
+LLInlineViewSegment::LLInlineViewSegment(LLView* view, S32 start, S32 end)
+: LLTextSegment(start, end),
+ mView(view)
{
- clearSegments();
-}
+}
-void LLTextBase::clearSegments()
+LLInlineViewSegment::~LLInlineViewSegment()
{
- setHoverSegment(NULL);
- mSegments.clear();
+ mView->die();
}
-void LLTextBase::setHoverSegment(LLTextSegmentPtr segment)
+S32 LLInlineViewSegment::getWidth(S32 first_char, S32 num_chars) const
{
- if (mHoverSegment)
+ if (first_char == 0 && num_chars == 0)
{
- mHoverSegment->setHasMouseHover(false);
- }
- if (segment)
- {
- segment->setHasMouseHover(true);
- }
- mHoverSegment = segment;
-}
-
-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;
+ return 0;
}
else
{
- *offsetp = startpos - (**seg_iter)->getStart();
+ return mView->getRect().getWidth();
}
}
-void LLTextBase::getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp )
+S32 LLInlineViewSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const
{
- *seg_iter = getSegIterContaining(startpos);
- if (*seg_iter == mSegments.end())
+ if (line_offset != 0 && num_pixels < mView->getRect().getWidth())
{
- *offsetp = 0;
+ return 0;
}
else
{
- *offsetp = startpos - (**seg_iter)->getStart();
+ return mEnd - mStart;
}
}
-LLTextBase::segment_set_t::iterator LLTextBase::getSegIterContaining(S32 index)
+void LLInlineViewSegment::updateLayout(const LLTextBase& editor)
{
- segment_set_t::iterator it = mSegments.upper_bound(new LLIndexSegment(index));
- return it;
+ LLRect start_rect = editor.getLocalRectFromDocIndex(mStart);
+ LLRect doc_rect = editor.getDocumentPanel()->getRect();
+ mView->setOrigin(doc_rect.mLeft + start_rect.mLeft, doc_rect.mBottom + start_rect.mBottom);
}
-LLTextBase::segment_set_t::const_iterator LLTextBase::getSegIterContaining(S32 index) const
+F32 LLInlineViewSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect)
{
- LLTextBase::segment_set_t::const_iterator it = mSegments.upper_bound(new LLIndexSegment(index));
- return it;
+ return (F32)(draw_rect.mLeft + mView->getRect().getWidth());
}
-// Finds the text segment (if any) at the give local screen position
-LLTextSegmentPtr LLTextBase::getSegmentAtLocalPos( S32 x, S32 y )
+S32 LLInlineViewSegment::getMaxHeight() const
{
- // Find the cursor position at the requested local screen position
- S32 offset = getDocIndexFromLocalCoord( x, y, FALSE );
- segment_set_t::iterator seg_iter = getSegIterContaining(offset);
- if (seg_iter != mSegments.end())
- {
- return *seg_iter;
- }
- else
- {
- return LLTextSegmentPtr();
- }
+ return mView->getRect().getHeight();
}
-BOOL LLTextBase::handleHoverOverUrl(S32 x, S32 y)
+void LLInlineViewSegment::unlinkFromDocument(LLTextBase* editor)
{
- setHoverSegment(NULL);
-
- // Check to see if we're over an HTML-style link
- LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y );
- if (cur_segment)
- {
- setHoverSegment(cur_segment);
-
- LLStyleSP style = cur_segment->getStyle();
- if (style && style->isLink())
- {
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-BOOL LLTextBase::handleMouseUpOverUrl(S32 x, S32 y)
-{
- if (mParseHTML && mHoverSegment)
- {
- LLStyleSP style = mHoverSegment->getStyle();
- if (style && style->isLink())
- {
- LLUrlAction::clickAction(style->getLinkHREF());
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-BOOL LLTextBase::handleRightMouseDownOverUrl(LLView *view, S32 x, S32 y)
-{
- // pop up a context menu for any Url under the cursor
- const LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y);
- if (cur_segment && cur_segment->getStyle() && cur_segment->getStyle()->isLink())
- {
- delete mPopupMenu;
- mPopupMenu = createUrlContextMenu(cur_segment->getStyle()->getLinkHREF());
- if (mPopupMenu)
- {
- mPopupMenu->show(x, y);
- LLMenuGL::showPopup(view, mPopupMenu, x, y);
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-BOOL LLTextBase::handleToolTipForUrl(LLView *view, S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen)
-{
- std::string tooltip_msg;
- const LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y );
- if (cur_segment && cur_segment->getToolTip( tooltip_msg ) && view)
- {
- // Use a slop area around the cursor
- const S32 SLOP = 8;
- // Convert rect local to screen coordinates
- view->localPointToScreen(x - SLOP, y - SLOP, &(sticky_rect_screen.mLeft),
- &(sticky_rect_screen.mBottom));
- sticky_rect_screen.mRight = sticky_rect_screen.mLeft + 2 * SLOP;
- sticky_rect_screen.mTop = sticky_rect_screen.mBottom + 2 * SLOP;
-
- LLToolTipMgr::instance().show(LLToolTipParams()
- .message(tooltip_msg)
- .sticky_rect(sticky_rect_screen));
- return TRUE;
- }
- return FALSE;
+ editor->removeDocumentChild(mView);
}
-LLContextMenu *LLTextBase::createUrlContextMenu(const std::string &in_url)
+void LLInlineViewSegment::linkToDocument(LLTextBase* editor)
{
- // 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 NULL;
- }
-
- std::string xui_file = match.getMenuName();
- if (xui_file.empty())
- {
- return NULL;
- }
-
- // set up the callbacks for all of the potential menu items, N.B. we
- // don't use const ref strings in callbacks in case url goes out of scope
- LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
- registrar.add("Url.Open", boost::bind(&LLUrlAction::openURL, url));
- registrar.add("Url.OpenInternal", boost::bind(&LLUrlAction::openURLInternal, url));
- registrar.add("Url.OpenExternal", boost::bind(&LLUrlAction::openURLExternal, url));
- registrar.add("Url.Execute", boost::bind(&LLUrlAction::executeSLURL, url));
- registrar.add("Url.Teleport", boost::bind(&LLUrlAction::teleportToLocation, url));
- registrar.add("Url.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
- return LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(xui_file, LLMenuGL::sMenuContainer,
- LLMenuHolderGL::child_registry_t::instance());
+ editor->addDocumentChild(mView);
}
diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h
index 82b9f6a43f..d0287a99ca 100644
--- a/indra/llui/lltextbase.h
+++ b/indra/llui/lltextbase.h
@@ -35,13 +35,16 @@
#define LL_LLTEXTBASE_H
#include "v4color.h"
+#include "lleditmenuhandler.h"
#include "llstyle.h"
#include "llkeywords.h"
-#include "lluictrl.h"
+#include "llpanel.h"
#include <string>
#include <set>
+#include <boost/signals2.hpp>
+
class LLContextMenu;
class LLTextSegment;
@@ -52,64 +55,307 @@ typedef LLPointer<LLTextSegment> LLTextSegmentPtr;
/// as LLTextEditor and LLTextBox. It implements shared functionality
/// such as Url highlighting and opening.
///
-class LLTextBase
+class LLTextBase
+: public LLUICtrl,
+ protected LLEditMenuHandler
{
public:
- LLTextBase(const LLUICtrl::Params &p);
- virtual ~LLTextBase();
+ struct LineSpacingParams : public LLInitParam::Choice<LineSpacingParams>
+ {
+ Alternative<F32> multiple;
+ Alternative<S32> pixels;
+ LineSpacingParams();
+ };
+
+ struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ {
+ Optional<LLUIColor> cursor_color,
+ text_color,
+ text_readonly_color,
+ bg_readonly_color,
+ bg_writeable_color,
+ bg_focus_color;
+
+ Optional<bool> bg_visible,
+ border_visible,
+ track_end,
+ read_only,
+ hide_scrollbar,
+ clip_to_rect,
+ wrap,
+ use_ellipses,
+ allow_html,
+ parse_highlights;
+
+ Optional<S32> v_pad,
+ h_pad;
+
+ Optional<LineSpacingParams>
+ line_spacing;
+
+ Optional<S32> max_text_length;
+
+ Optional<LLFontGL::ShadowType> font_shadow;
+
+ Params();
+ };
+
+ // LLMouseHandler interface
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMiddleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask);
+
+ // LLView interface
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+ /*virtual*/ void draw();
+
+ // LLUICtrl interface
+ /*virtual*/ BOOL acceptsTextInput() const { return !mReadOnly; }
+ /*virtual*/ void clear();
+ /*virtual*/ void setColor( const LLColor4& c );
+ /*virtual*/ void setValue(const LLSD& value );
+ /*virtual*/ LLTextViewModel* getViewModel() const;
+
+ // LLEditMenuHandler interface
+ /*virtual*/ void deselect();
- /// specify the color to display Url hyperlinks in the text
- static void setLinkColor(LLColor4 color) { mLinkColor = color; }
+ // used by LLTextSegment layout code
+ bool getWordWrap() { return mWordWrap; }
+ bool getUseEllipses() { return mUseEllipses; }
+ bool truncate(); // returns true of truncation occurred
- /// enable/disable the automatic hyperlinking of Urls in the text
- void setParseHTML(BOOL parsing) { mParseHTML=parsing; }
+ // TODO: move into LLTextSegment?
+ void createUrlContextMenu(S32 x, S32 y, const std::string &url); // create a popup context menu for the given Url
- // public text editing virtual methods
- virtual LLWString getWText() const = 0;
- virtual BOOL allowsEmbeddedItems() const { return FALSE; }
- virtual BOOL getWordWrap() { return mWordWrap; }
- virtual S32 getLength() const = 0;
+
+ // Text accessors
+ // TODO: add optional style parameter
+ virtual void setText(const LLStringExplicit &utf8str); // uses default style
+ virtual std::string getText() const;
+
+ // wide-char versions
+ void setWText(const LLWString& text);
+ LLWString getWText() const;
+
+ void appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params = LLStyle::Params());
+
+ S32 getLength() const { return getWText().length(); }
+ S32 getLineCount() const { return mLineInfoList.size(); }
+
+ class DocumentPanel : public LLPanel
+ {
+ public:
+ DocumentPanel(const Params&);
+ };
+ void addDocumentChild(LLView* view);
+ void removeDocumentChild(LLView* view);
+ const DocumentPanel* getDocumentPanel() const { return mDocumentPanel; }
+ LLRect getTextRect() { return mTextRect; }
+ LLRect getContentsRect();
+
+ S32 getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const;
+ LLRect getLocalRectFromDocIndex(S32 pos) const;
+
+ void setReadOnly(bool read_only) { mReadOnly = read_only; }
+ bool getReadOnly() { return mReadOnly; }
+
+ // 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 );
+
+ const LLFontGL* getDefaultFont() const { return mDefaultFont; }
+
+public:
+ // Fired when a URL link is clicked
+ commit_signal_t mURLClickSignal;
protected:
+ // helper structs
+ struct compare_bottom;
+ struct compare_top;
+ struct line_end_compare;
+ typedef std::vector<LLTextSegmentPtr> segment_vec_t;
+
+ // Abstract inner base class representing an undoable editor command.
+ // Concrete sub-classes can be defined for operations such as insert, remove, etc.
+ // Used as arguments to the execute() method below.
+ class TextCmd
+ {
+ public:
+ TextCmd( S32 pos, BOOL group_with_next, LLTextSegmentPtr segment = LLTextSegmentPtr() )
+ : mPos(pos),
+ mGroupWithNext(group_with_next)
+ {
+ if (segment.notNull())
+ {
+ mSegments.push_back(segment);
+ }
+ }
+ virtual ~TextCmd() {}
+ virtual BOOL execute(LLTextBase* editor, S32* delta) = 0;
+ virtual S32 undo(LLTextBase* editor) = 0;
+ virtual S32 redo(LLTextBase* editor) = 0;
+ virtual BOOL canExtend(S32 pos) const { return FALSE; }
+ virtual void blockExtensions() {}
+ virtual BOOL extendAndExecute( LLTextBase* editor, S32 pos, llwchar c, S32* delta ) { llassert(0); return 0; }
+ virtual BOOL hasExtCharValue( llwchar value ) const { return FALSE; }
+
+ // Defined here so they can access protected LLTextEditor editing methods
+ S32 insert(LLTextBase* editor, S32 pos, const LLWString &wstr) { return editor->insertStringNoUndo( pos, wstr, &mSegments ); }
+ S32 remove(LLTextBase* editor, S32 pos, S32 length) { return editor->removeStringNoUndo( pos, length ); }
+ S32 overwrite(LLTextBase* editor, S32 pos, llwchar wc) { return editor->overwriteCharNoUndo(pos, wc); }
+
+ S32 getPosition() const { return mPos; }
+ BOOL groupWithNext() const { return mGroupWithNext; }
+
+ protected:
+ const S32 mPos;
+ BOOL mGroupWithNext;
+ segment_vec_t mSegments;
+ };
+
struct compare_segment_end
{
bool operator()(const LLTextSegmentPtr& a, const LLTextSegmentPtr& b) const;
};
typedef std::multiset<LLTextSegmentPtr, compare_segment_end> segment_set_t;
- // routines to 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 );
+ // protected member variables
+ // List of offsets and segment index of the start of each line. Always has at least one node (0).
+ struct line_info
+ {
+ line_info(S32 index_start, S32 index_end, LLRect rect, S32 line_num);
+ S32 mDocIndexStart;
+ S32 mDocIndexEnd;
+ LLRect mRect;
+ S32 mLineNum; // actual line count (ignoring soft newlines due to word wrap)
+ };
+ typedef std::vector<line_info> line_list_t;
+
+ // member functions
+ LLTextBase(const Params &p);
+ virtual ~LLTextBase();
+ void initFromParams(const Params& p);
+ LLStyle::Params getDefaultStyle();
+ virtual void onValueChange(S32 start, S32 end);
+
+ // draw methods
+ void drawSelectionBackground(); // draws the black box behind the selected text
+ void drawCursor();
+ void drawText();
+
+ // modify contents
+ S32 insertStringNoUndo(S32 pos, const LLWString &wstr, segment_vec_t* segments = NULL); // returns num of chars actually inserted
+ S32 removeStringNoUndo(S32 pos, S32 length);
+ S32 overwriteCharNoUndo(S32 pos, llwchar wc);
+ void appendAndHighlightText(const std::string &new_text, bool prepend_newline, S32 highlight_part, const LLStyle::Params& stylep);
+
+
+ // 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 );
segment_set_t::iterator getSegIterContaining(S32 index);
segment_set_t::const_iterator getSegIterContaining(S32 index) const;
- void clearSegments();
- void setHoverSegment(LLTextSegmentPtr segment);
+ void clearSegments();
+ void createDefaultSegment();
+ virtual void updateSegments();
+ void insertSegment(LLTextSegmentPtr segment_to_insert);
+
+ // manage lines
+ S32 getLineStart( 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() const;
+ S32 getLeftOffset(S32 width);
+ void reflow(S32 start_index = 0);
- // event handling for Urls within the text field
- BOOL handleHoverOverUrl(S32 x, S32 y);
- BOOL handleMouseUpOverUrl(S32 x, S32 y);
- BOOL handleRightMouseDownOverUrl(LLView *view, S32 x, S32 y);
- BOOL handleToolTipForUrl(LLView *view, S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen);
+ // 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();
- // pure virtuals that have to be implemented by any subclasses
- virtual S32 getLineCount() const = 0;
- virtual S32 getLineStart( S32 line ) const = 0;
- virtual S32 getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const = 0;
+ // text selection
+ bool hasSelection() const { return (mSelectionStart !=mSelectionEnd); }
+ void startSelection();
+ void endSelection();
- // protected member variables
- static LLUIColor mLinkColor;
- const LLFontGL *mDefaultFont;
- segment_set_t mSegments;
- LLTextSegmentPtr mHoverSegment;
- BOOL mParseHTML;
- BOOL mWordWrap;
+ // misc
+ void updateTextRect();
+ void needsReflow() { mReflowNeeded = TRUE; }
+ void needsScroll() { mScrollNeeded = TRUE; }
+ void replaceUrlLabel(const std::string &url, const std::string &label);
-private:
- // create a popup context menu for the given Url
- static LLContextMenu *createUrlContextMenu(const std::string &url);
+protected:
+ // text segmentation and flow
+ segment_set_t mSegments;
+ line_list_t mLineInfoList;
+ LLRect mTextRect; // The rect in which text is drawn. Excludes borders.
+ LLRect mContentsRect;
+
+ // colors
+ LLUIColor mCursorColor;
+ LLUIColor mFgColor;
+ LLUIColor mReadOnlyFgColor;
+ LLUIColor mWriteableBgColor;
+ LLUIColor mReadOnlyBgColor;
+ LLUIColor mFocusBgColor;
+
+ // cursor
+ S32 mCursorPos; // I-beam is just after the mCursorPos-th character.
+ S32 mDesiredXPixel; // X pixel position where the user wants the cursor to be
+ LLFrameTimer mCursorBlinkTimer; // timer that controls cursor blinking
+
+ // selection
+ S32 mSelectionStart;
+ S32 mSelectionEnd;
+
+ BOOL mIsSelecting; // Are we in the middle of a drag-select?
+
+ // configuration
+ S32 mHPad; // padding on left of text
+ S32 mVPad; // padding above text
+ LLFontGL::HAlign mHAlign;
+ 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
+ const LLFontGL* mDefaultFont; // font that is used when none specified
+ LLFontGL::ShadowType mFontShadow;
+ bool mBorderVisible;
+ bool mParseHTML; // make URLs interactive
+ bool mParseHighlights; // highlight user-defined keywords
+ bool mWordWrap;
+ bool mUseEllipses;
+ bool mTrackEnd; // if true, keeps scroll position at end of document during resize
+ bool mReadOnly;
+ bool mClip;
+ S32 mMaxTextByteLength; // Maximum length mText is allowed to be in bytes
+
+ // support widgets
+ LLContextMenu* mPopupMenu;
+ DocumentPanel* mDocumentPanel;
+ class LLScrollContainer* mScroller;
+
+ // transient state
+ bool mReflowNeeded; // need to reflow text because of change to text contents or display region
+ 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
- LLContextMenu *mPopupMenu;
};
///
@@ -118,7 +364,7 @@ private:
/// includes a start/end offset from the start of the string, a
/// style to render with, an optional tooltip, etc.
///
-class LLTextSegment : public LLRefCount
+class LLTextSegment : public LLRefCount, public LLMouseHandler
{
public:
LLTextSegment(S32 start, S32 end) : mStart(start), mEnd(end){};
@@ -134,17 +380,32 @@ public:
virtual void unlinkFromDocument(class LLTextBase* editor);
virtual void linkToDocument(class LLTextBase* editor);
- virtual void setHasMouseHover(bool hover);
virtual const LLColor4& getColor() const;
virtual void setColor(const LLColor4 &color);
virtual const LLStyleSP getStyle() const;
virtual void setStyle(const LLStyleSP &style);
virtual void setToken( LLKeywordToken* token );
virtual LLKeywordToken* getToken() const;
- virtual BOOL getToolTip( std::string& msg ) const;
virtual void setToolTip(const std::string& tooltip);
virtual void dump() const;
+ // LLMouseHandler interface
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMiddleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask);
+ /*virtual*/ std::string getName() const;
+ /*virtual*/ void onMouseCaptureLost();
+ /*virtual*/ void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const;
+ /*virtual*/ void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const;
+ /*virtual*/ BOOL hasMouseCapture();
+
S32 getStart() const { return mStart; }
void setStart(S32 start) { mStart = start; }
S32 getEnd() const { return mEnd; }
@@ -167,7 +428,6 @@ public:
/*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect);
/*virtual*/ S32 getMaxHeight() const;
/*virtual*/ bool canEdit() const { return true; }
- /*virtual*/ void setHasMouseHover(bool hover) { mHasMouseHover = hover; }
/*virtual*/ const LLColor4& getColor() const { return mStyle->getColor(); }
/*virtual*/ void setColor(const LLColor4 &color) { mStyle->setColor(color); }
/*virtual*/ const LLStyleSP getStyle() const { return mStyle; }
@@ -178,15 +438,20 @@ public:
/*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 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, F32 x, F32 y);
+ F32 drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRect rect);
+protected:
class LLTextBase& mEditor;
- LLStyleSP mStyle;
- S32 mMaxHeight;
- LLKeywordToken* mToken;
- bool mHasMouseHover;
- std::string mTooltip;
+ LLStyleSP mStyle;
+ S32 mMaxHeight;
+ LLKeywordToken* mToken;
+ std::string mTooltip;
};
class LLIndexSegment : public LLTextSegment
@@ -195,4 +460,23 @@ public:
LLIndexSegment(S32 pos) : LLTextSegment(pos, pos) {}
};
+class LLInlineViewSegment : public LLTextSegment
+{
+public:
+ LLInlineViewSegment(LLView* widget, S32 start, S32 end);
+ ~LLInlineViewSegment();
+ /*virtual*/ S32 getWidth(S32 first_char, S32 num_chars) const;
+ /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const;
+ /*virtual*/ void updateLayout(const class LLTextBase& editor);
+ /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect);
+ /*virtuaL*/ S32 getMaxHeight() const;
+ /*virtual*/ bool canEdit() const { return false; }
+ /*virtual*/ void unlinkFromDocument(class LLTextBase* editor);
+ /*virtual*/ void linkToDocument(class LLTextBase* editor);
+
+private:
+ LLView* mView;
+};
+
+
#endif
diff --git a/indra/llui/lltextbox.cpp b/indra/llui/lltextbox.cpp
index 132bef0296..3feca136be 100644
--- a/indra/llui/lltextbox.cpp
+++ b/indra/llui/lltextbox.cpp
@@ -40,47 +40,10 @@
static LLDefaultChildRegistry::Register<LLTextBox> r("text");
-LLTextBox::Params::Params()
-: text_color("text_color"),
- length("length"),
- type("type"),
- border_visible("border_visible", false),
- border_drop_shadow_visible("border_drop_shadow_visible", false),
- bg_visible("bg_visible", false),
- use_ellipses("use_ellipses"),
- word_wrap("word_wrap", false),
- drop_shadow_visible("drop_shadow_visible"),
- disabled_color("disabled_color"),
- background_color("background_color"),
- v_pad("v_pad", 0),
- h_pad("h_pad", 0),
- line_spacing("line_spacing", 0),
- text("text"),
- font_shadow("font_shadow", LLFontGL::NO_SHADOW)
-{}
-
LLTextBox::LLTextBox(const LLTextBox::Params& p)
-: LLUICtrl(p),
- LLTextBase(p),
- mBackgroundVisible( p.bg_visible ),
- mBorderVisible( p.border_visible ),
- mShadowType( p.font_shadow ),
- mBorderDropShadowVisible( p.border_drop_shadow_visible ),
- mUseEllipses( p.use_ellipses ),
- mHPad(p.h_pad),
- mVPad(p.v_pad),
- mVAlign( LLFontGL::TOP ),
- mClickedCallback(NULL),
- mTextColor(p.text_color()),
- mDisabledColor(p.disabled_color()),
- mBackgroundColor(p.background_color()),
- mHAlign(p.font_halign),
- mLineSpacing(p.line_spacing),
- mDidWordWrap(FALSE)
-{
- mWordWrap = p.word_wrap;
- setText( p.text() );
-}
+: LLTextBase(p),
+ mClickedCallback(NULL)
+{}
BOOL LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask)
{
@@ -101,6 +64,11 @@ BOOL LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask)
}
}
+ if (!handled)
+ {
+ handled = LLTextBase::handleMouseDown(x, y, mask);
+ }
+
return handled;
}
@@ -125,528 +93,60 @@ BOOL LLTextBox::handleMouseUp(S32 x, S32 y, MASK mask)
}
// handle clicks on Urls in the textbox first
- if (! handleMouseUpOverUrl(x, y))
+ handled = LLTextBase::handleMouseUp(x, y, mask);
+
+ // 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)
{
- // 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();
- }
+ mClickedCallback();
}
}
return handled;
}
-BOOL LLTextBox::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- // pop up a context menu for any Url under the cursor
- return handleRightMouseDownOverUrl(this, x, y);
-}
-
-BOOL LLTextBox::handleHover(S32 x, S32 y, MASK mask)
-{
- // Check to see if we're over an HTML-style link
- if (handleHoverOverUrl(x, y))
- {
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl;
- getWindow()->setCursor(UI_CURSOR_HAND);
- return TRUE;
- }
-
- return LLView::handleHover(x,y,mask);
-}
-
-BOOL LLTextBox::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen)
-{
- if (handleToolTipForUrl(this, x, y, msg, sticky_rect_screen))
- {
- return TRUE;
- }
-
- return LLUICtrl::handleToolTip(x, y, msg, sticky_rect_screen);
-}
-
void LLTextBox::setText(const LLStringExplicit& text)
{
- if(mWordWrap && !mDidWordWrap)
- {
- setWrappedText(text);
- }
- else
- {
- mText.assign(text);
- updateDisplayTextAndSegments();
- }
-}
-
-void LLTextBox::setLineLengths()
-{
- mLineLengthList.clear();
+ // does string argument insertion
+ mText.assign(text);
- std::string::size_type cur = 0;
- std::string::size_type len = mDisplayText.size();
-
- while (cur < len)
- {
- std::string::size_type end = mDisplayText.find('\n', cur);
- std::string::size_type runLen;
-
- if (end == std::string::npos)
- {
- runLen = len - cur;
- cur = len;
- }
- else
- {
- runLen = end - cur;
- cur = end + 1; // skip the new line character
- }
-
- mLineLengthList.push_back( (S32)runLen );
- }
-}
-
-LLWString LLTextBox::wrapText(const LLWString &wtext, S32 &hoffset, S32 &line_num, F32 max_width)
-{
- LLWString final_wtext;
-
- LLWString::size_type cur = 0;
- LLWString::size_type len = wtext.size();
- while (cur < len)
- {
- LLWString::size_type end = wtext.find('\n', cur);
- if (end == LLWString::npos)
- {
- end = len;
- }
-
- bool charsRemaining = true;
- LLWString::size_type runLen = end - cur;
- if (runLen > 0)
- {
- // work out how many chars can fit onto the current line
- LLWString run(wtext, cur, runLen);
- LLWString::size_type useLen =
- mDefaultFont->maxDrawableChars(run.c_str(), max_width-hoffset, runLen, TRUE);
- charsRemaining = (cur + useLen < len);
-
- // try to break lines on word boundaries
- if (useLen < run.size())
- {
- LLWString::size_type prev_use_len = useLen;
- while (useLen > 0 && ! isspace(run[useLen-1]) && ! ispunct(run[useLen-1]))
- {
- --useLen;
- }
- if (useLen == 0)
- {
- useLen = prev_use_len;
- }
- }
-
- // add the chars that could fit onto one line to our result
- final_wtext.append(wtext, cur, useLen);
- cur += useLen;
- hoffset += mDefaultFont->getWidth(run.substr(0, useLen).c_str());
-
- // abort if not enough room to add any more characters
- if (useLen == 0)
- {
- break;
- }
- }
-
- if (charsRemaining)
- {
- if (wtext[cur] == '\n')
- {
- cur += 1;
- }
- final_wtext += '\n';
- hoffset = 0;
- line_num += 1;
- }
- }
-
- return final_wtext;
-}
-
-void LLTextBox::setWrappedText(const LLStringExplicit& in_text, F32 max_width)
-{
- mDidWordWrap = TRUE;
- setText(wstring_to_utf8str(getWrappedText(in_text, max_width)));
-}
-
-LLWString LLTextBox::getWrappedText(const LLStringExplicit& in_text, F32 max_width)
-{
- //
- // we don't want to wrap Urls otherwise we won't be able to detect their
- // presence for hyperlinking. So we look for all Urls, and then word wrap
- // the text before and after, but never break a Url in the middle. We
- // also need to consider that the Url will be displayed as a label (not
- // necessary the actual Url string).
- //
-
- if (max_width < 0.0f)
- {
- max_width = (F32)getRect().getWidth();
- }
-
- LLWString wtext = utf8str_to_wstring(in_text);
- LLWString final_wtext;
- S32 line_num = 1;
- S32 hoffset = 0;
-
- // find the next Url in the text string
- LLUrlMatch match;
- while ( LLUrlRegistry::instance().findUrl(wtext, match))
- {
- S32 start = match.getStart();
- S32 end = match.getEnd() + 1;
-
- // perform word wrap on the text before the Url
- final_wtext += wrapText(wtext.substr(0, start), hoffset, line_num, max_width);
-
- // add the Url (but compute width based on its label)
- S32 label_width = mDefaultFont->getWidth(match.getLabel());
- if (hoffset > 0 && hoffset + label_width > max_width)
- {
- final_wtext += '\n';
- line_num++;
- hoffset = 0;
- }
- final_wtext += wtext.substr(start, end-start);
- hoffset += label_width;
- if (hoffset > max_width)
- {
- final_wtext += '\n';
- line_num++;
- hoffset = 0;
- // eat any leading whitespace on the next line
- while (isspace(wtext[end]) && end < (S32)wtext.size())
- {
- end++;
- }
- }
-
- // move on to the rest of the text after the Url
- wtext = wtext.substr(end, wtext.size() - end + 1);
- }
-
- final_wtext += wrapText(wtext, hoffset, line_num, max_width);
- return final_wtext;
+ LLTextBase::setText(mText.getString());
}
S32 LLTextBox::getTextPixelWidth()
{
- S32 max_line_width = 0;
- if( mLineLengthList.size() > 0 )
- {
- S32 cur_pos = 0;
- for (std::vector<S32>::iterator iter = mLineLengthList.begin();
- iter != mLineLengthList.end(); ++iter)
- {
- S32 line_length = *iter;
- S32 line_width = mDefaultFont->getWidth( mDisplayText.c_str(), cur_pos, line_length );
- if( line_width > max_line_width )
- {
- max_line_width = line_width;
- }
- cur_pos += line_length+1;
- }
- }
- else
- {
- max_line_width = mDefaultFont->getWidth(mDisplayText.c_str());
- }
- return max_line_width;
+ return getContentsRect().getWidth();
}
S32 LLTextBox::getTextPixelHeight()
{
- S32 num_lines = mLineLengthList.size();
- if( num_lines < 1 )
- {
- num_lines = 1;
- }
- return (S32)(num_lines * mDefaultFont->getLineHeight());
-}
-
-void LLTextBox::setValue(const LLSD& value )
-{
- mDidWordWrap = FALSE;
- setText(value.asString());
+ return getContentsRect().getHeight();
}
BOOL LLTextBox::setTextArg( const std::string& key, const LLStringExplicit& text )
{
mText.setArg(key, text);
- updateDisplayTextAndSegments();
- return TRUE;
-}
-
-void LLTextBox::draw()
-{
- F32 alpha = getDrawContext().mAlpha;
+ LLTextBase::setText(mText.getString());
- if (mBorderVisible)
- {
- gl_rect_2d_offset_local(getLocalRect(), 2, FALSE);
- }
-
- if( mBorderDropShadowVisible )
- {
- static LLUIColor color_drop_shadow = LLUIColorTable::instance().getColor("ColorDropShadow");
- static LLUICachedControl<S32> drop_shadow_tooltip ("DropShadowTooltip", 0);
- gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0,
- color_drop_shadow % alpha, drop_shadow_tooltip);
- }
-
- if (mBackgroundVisible)
- {
- LLRect r( 0, getRect().getHeight(), getRect().getWidth(), 0 );
- gl_rect_2d( r, mBackgroundColor.get() % alpha );
- }
-
- S32 text_x = 0;
- switch( mHAlign )
- {
- case LLFontGL::LEFT:
- text_x = mHPad;
- break;
- case LLFontGL::HCENTER:
- text_x = getRect().getWidth() / 2;
- break;
- case LLFontGL::RIGHT:
- text_x = getRect().getWidth() - mHPad;
- break;
- }
-
- S32 text_y = getRect().getHeight() - mVPad;
-
- if ( getEnabled() )
- {
- drawText( text_x, text_y, mDisplayText, mTextColor.get() );
- }
- else
- {
- drawText( text_x, text_y, mDisplayText, mDisabledColor.get() );
- }
-
- if (sDebugRects)
- {
- drawDebugRect();
- }
-
- //// *HACK: also draw debug rectangles around currently-being-edited LLView, and any elements that are being highlighted by GUI preview code (see LLFloaterUIPreview)
- //std::set<LLView*>::iterator iter = std::find(sPreviewHighlightedElements.begin(), sPreviewHighlightedElements.end(), this);
- //if ((sEditingUI && this == sEditingUIView) || (iter != sPreviewHighlightedElements.end() && sDrawPreviewHighlights))
- //{
- // drawDebugRect();
- //}
+ return TRUE;
}
-void LLTextBox::reshape(S32 width, S32 height, BOOL called_from_parent)
-{
- // reparse line lengths (don't need to recalculate the display text)
- setLineLengths();
- LLView::reshape(width, height, called_from_parent);
-}
-
-void LLTextBox::drawText( S32 x, S32 y, const LLWString &text, const LLColor4& color )
-{
- F32 alpha = getDrawContext().mAlpha;
- if (mSegments.size() > 1)
- {
- // we have Urls (or other multi-styled segments)
- drawTextSegments(x, y, text);
- }
- else if( mLineLengthList.empty() )
- {
- // simple case of 1 line of text in one style
- mDefaultFont->render(text, 0, (F32)x, (F32)y, color % alpha,
- mHAlign, mVAlign,
- 0,
- mShadowType,
- S32_MAX, getRect().getWidth(), NULL, mUseEllipses);
- }
- else
- {
- // simple case of multiple lines of text, all in the same style
- S32 cur_pos = 0;
- for (std::vector<S32>::iterator iter = mLineLengthList.begin();
- iter != mLineLengthList.end(); ++iter)
- {
- S32 line_length = *iter;
- mDefaultFont->render(text, cur_pos, (F32)x, (F32)y, color % alpha,
- mHAlign, mVAlign,
- 0,
- mShadowType,
- line_length, getRect().getWidth(), NULL, mUseEllipses );
- cur_pos += line_length + 1;
- S32 line_height = llfloor(mDefaultFont->getLineHeight()) + mLineSpacing;
- y -= line_height;
- if(y < line_height)
- break;
- }
- }
-}
void LLTextBox::reshapeToFitText()
{
- // wrap remaining lines that did not fit on call to setWrappedText()
- setLineLengths();
+ reflow();
S32 width = getTextPixelWidth();
S32 height = getTextPixelHeight();
- reshape( width + 2 * mHPad, height + 2 * mVPad );
-}
-
-S32 LLTextBox::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const
-{
- // Returns the character offset for the character under the local (x, y) coordinate.
- // When 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.
-
- LLRect rect = getLocalRect();
- rect.mLeft += mHPad;
- rect.mRight -= mHPad;
- rect.mTop += mVPad;
- rect.mBottom -= mVPad;
-
- // Figure out which line we're nearest to.
- S32 total_lines = getLineCount();
- S32 line_height = llround( mDefaultFont->getLineHeight() ) + mLineSpacing;
- S32 line = (rect.mTop - 1 - local_y) / line_height;
- if (line >= total_lines)
- {
- return getLength(); // past the end
- }
-
- line = llclamp( line, 0, total_lines );
- S32 line_start = getLineStart(line);
- S32 next_start = getLineStart(line+1);
- S32 line_end = (next_start != line_start) ? next_start - 1 : getLength();
- if (line_start == -1)
- {
- return 0;
- }
-
- S32 line_len = line_end - line_start;
- S32 pos = mDefaultFont->charFromPixelOffset(mDisplayText.c_str(), line_start,
- (F32)(local_x - rect.mLeft),
- (F32)rect.getWidth(),
- line_len, round);
-
- return line_start + pos;
+ reshape( width + 2 * mHPad, height + 2 * mVPad, FALSE );
}
-S32 LLTextBox::getLineStart( S32 line ) const
-{
- line = llclamp(line, 0, getLineCount()-1);
-
- S32 result = 0;
- for (int i = 0; i < line; i++)
- {
- result += mLineLengthList[i] + 1 /* add newline */;
- }
-
- return result;
-}
-
-void LLTextBox::updateDisplayTextAndSegments()
-{
- // remove any previous segment list
- clearSegments();
-
- // if URL parsing is turned off, then not much to bo
- if (! mParseHTML)
- {
- mDisplayText = mText.getWString();
- setLineLengths();
- return;
- }
-
- // create unique text segments for Urls
- mDisplayText.clear();
- S32 end = 0;
- LLUrlMatch match;
- LLWString text = mText.getWString();
-
- // find the next Url in the text string
- while ( LLUrlRegistry::instance().findUrl(text, match,
- boost::bind(&LLTextBox::onUrlLabelUpdated, this, _1, _2)) )
- {
- // work out the char offset for the start/end of the url
- S32 url_start = match.getStart();
- S32 url_end = match.getEnd();
-
- // and the char offset for the label in the display text
- S32 seg_start = mDisplayText.size();
- S32 start = seg_start + url_start;
- S32 end = start + match.getLabel().size();
-
- // create a segment for the text before the Url
- mSegments.insert(new LLNormalTextSegment(new LLStyle(), seg_start, start, *this));
- mDisplayText += text.substr(0, url_start);
-
- // create a segment for the Url text
- LLStyleSP html(new LLStyle);
- html->setVisible(true);
- html->setColor(mLinkColor);
- html->mUnderline = TRUE;
- html->setLinkHREF(match.getUrl());
-
- LLNormalTextSegment *html_seg = new LLNormalTextSegment(html, start, end, *this);
- html_seg->setToolTip(match.getTooltip());
-
- mSegments.insert(html_seg);
- mDisplayText += utf8str_to_wstring(match.getLabel());
-
- // move on to the rest of the text after the Url
- text = text.substr(url_end+1, text.size() - url_end);
- }
-
- // output a segment for the remaining text
- if (text.size() > 0)
- {
- mSegments.insert(new LLNormalTextSegment(new LLStyle(), end, end + text.size(), *this));
- mDisplayText += text;
- }
-
- // strip whitespace from the end of the text
- while (mDisplayText.size() > 0 && isspace(mDisplayText[mDisplayText.size()-1]))
- {
- mDisplayText = mDisplayText.substr(0, mDisplayText.size() - 1);
-
- segment_set_t::iterator it = getSegIterContaining(mDisplayText.size());
- if (it != mSegments.end())
- {
- LLTextSegmentPtr seg = *it;
- seg->setEnd(seg->getEnd()-1);
- }
- }
-
- // we may have changed the line lengths, so recalculate them
- setLineLengths();
-}
void LLTextBox::onUrlLabelUpdated(const std::string &url, const std::string &label)
{
- if (mDidWordWrap)
- {
- // re-word wrap as the url label lengths may have changed
- setWrappedText(mText.getString());
- }
- else
- {
- // or just update the display text with the latest Url labels
- updateDisplayTextAndSegments();
- }
+ needsReflow();
}
bool LLTextBox::isClickable() const
@@ -676,89 +176,3 @@ bool LLTextBox::isClickable() const
return false;
}
-void LLTextBox::drawTextSegments(S32 init_x, S32 init_y, const LLWString &text)
-{
- F32 alpha = getDrawContext().mAlpha;
-
- const S32 text_len = text.length();
- if (text_len <= 0)
- {
- return;
- }
-
- S32 cur_line = 0;
- S32 num_lines = getLineCount();
- S32 line_start = getLineStart(cur_line);
- S32 line_height = llround( mDefaultFont->getLineHeight() ) + mLineSpacing;
- F32 text_y = (F32) init_y;
- segment_set_t::iterator cur_seg = mSegments.begin();
-
- // render a line of text at a time
- const LLRect textRect = getLocalRect();
- while((textRect.mBottom <= text_y) && (cur_line < num_lines))
- {
- 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;
- }
-
- // render all segments on this line
- F32 text_x = init_x;
- S32 seg_start = line_start;
- while (seg_start < line_end && cur_seg != mSegments.end())
- {
- // move to the next segment (or continue the previous one)
- LLTextSegment *cur_segment = *cur_seg;
- while (cur_segment->getEnd() <= seg_start)
- {
- if (++cur_seg == mSegments.end())
- {
- return;
- }
- cur_segment = *cur_seg;
- }
-
- // Draw a segment within the line
- S32 clipped_end = llmin( line_end, cur_segment->getEnd() );
- S32 clipped_len = clipped_end - seg_start;
- if( clipped_len > 0 )
- {
- LLStyleSP style = cur_segment->getStyle();
- if (style && style->isVisible())
- {
- // work out the color for the segment
- LLColor4 color ;
- if (getEnabled())
- {
- color = style->isLink() ? mLinkColor.get() : mTextColor.get();
- }
- else
- {
- color = mDisabledColor.get();
- }
- color = color % alpha;
-
- // render a single line worth for this segment
- mDefaultFont->render(text, seg_start, text_x, text_y, color,
- mHAlign, mVAlign, 0, mShadowType, clipped_len,
- textRect.getWidth(), &text_x, mUseEllipses);
- }
-
- seg_start += clipped_len;
- }
- }
-
- // move down one line
- text_y -= (F32)line_height;
- line_start = next_start;
- cur_line++;
- }
-}
diff --git a/indra/llui/lltextbox.h b/indra/llui/lltextbox.h
index 0517325e70..f8c4447b62 100644
--- a/indra/llui/lltextbox.h
+++ b/indra/llui/lltextbox.h
@@ -33,47 +33,21 @@
#ifndef LL_LLTEXTBOX_H
#define LL_LLTEXTBOX_H
-#include "lluictrl.h"
#include "v4color.h"
#include "llstring.h"
#include "lluistring.h"
#include "lltextbase.h"
class LLTextBox :
- public LLTextBase,
- public LLUICtrl
+ public LLTextBase
{
public:
// *TODO: Add callback to Params
typedef boost::function<void (void)> callback_t;
- struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
- {
- Optional<std::string> text;
-
- Optional<bool> border_visible,
- border_drop_shadow_visible,
- bg_visible,
- use_ellipses,
- word_wrap;
-
- Optional<LLFontGL::ShadowType> font_shadow;
-
- Ignored drop_shadow_visible,
- type,
- length;
-
- Optional<LLUIColor> text_color,
- disabled_color,
- background_color;
-
- Optional<S32> v_pad,
- h_pad,
- line_spacing;
-
- Params();
- };
+ struct Params : public LLInitParam::Block<Params, LLTextBase::Params>
+ {};
protected:
LLTextBox(const Params&);
@@ -82,84 +56,33 @@ protected:
public:
virtual ~LLTextBox() {}
- virtual void draw();
- virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
-
virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
- virtual BOOL handleHover(S32 x, S32 y, MASK mask);
- virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
- virtual BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen);
- void setColor( const LLColor4& c ) { mTextColor = c; }
- void setDisabledColor( const LLColor4& c) { mDisabledColor = c; }
- void setBackgroundColor( const LLColor4& c) { mBackgroundColor = c; }
-
- void setText( const LLStringExplicit& text );
- void setWrappedText(const LLStringExplicit& text, F32 max_width = -1.f); // -1 means use existing control width
- void setUseEllipses( BOOL use_ellipses ) { mUseEllipses = use_ellipses; }
+ /*virtual*/ void setText( const LLStringExplicit& text );
- void setBackgroundVisible(BOOL visible) { mBackgroundVisible = visible; }
- void setBorderVisible(BOOL visible) { mBorderVisible = visible; }
- void setBorderDropshadowVisible(BOOL visible){ mBorderDropShadowVisible = visible; }
- void setHPad(S32 pixels) { mHPad = pixels; }
- void setVPad(S32 pixels) { mVPad = pixels; }
void setRightAlign() { mHAlign = LLFontGL::RIGHT; }
void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; }
void setClickedCallback( boost::function<void (void*)> cb, void* userdata = NULL ){ mClickedCallback = boost::bind(cb, userdata); } // mouse down and up within button
- const LLFontGL* getFont() const { return mDefaultFont; }
- void setFont(const LLFontGL* font) { mDefaultFont = font; }
+ //const LLFontGL* getFont() const { return mDefaultFont; }
+ //void setFont(const LLFontGL* font) { mDefaultFont = font; }
void reshapeToFitText();
- const std::string& getText() const { return mText.getString(); }
- LLWString getWText() const { return mDisplayText; }
+ //const std::string& getText() const { return mText.getString(); }
S32 getTextPixelWidth();
S32 getTextPixelHeight();
- S32 getLength() const { return mDisplayText.length(); }
- virtual void setValue(const LLSD& value );
virtual LLSD getValue() const { return LLSD(getText()); }
virtual BOOL setTextArg( const std::string& key, const LLStringExplicit& text );
protected:
- S32 getLineCount() const { return mLineLengthList.size(); }
- S32 getLineStart( S32 line ) const;
- S32 getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const;
- LLWString getWrappedText(const LLStringExplicit& in_text, F32 max_width = -1.f);
- void setLineLengths();
- void updateDisplayTextAndSegments();
- virtual void drawText(S32 x, S32 y, const LLWString &text, const LLColor4& color );
void onUrlLabelUpdated(const std::string &url, const std::string &label);
bool isClickable() const;
- LLWString wrapText(const LLWString &wtext, S32 &hoffset, S32 &line_num, F32 max_width);
- void drawTextSegments(S32 x, S32 y, const LLWString &text);
-
- LLUIString mText;
- LLWString mDisplayText;
- LLUIColor mTextColor;
- LLUIColor mDisabledColor;
- LLUIColor mBackgroundColor;
- LLUIColor mBorderColor;
-
- BOOL mBackgroundVisible;
- BOOL mBorderVisible;
- BOOL mDidWordWrap;
-
- LLFontGL::ShadowType mShadowType;
- BOOL mBorderDropShadowVisible;
- BOOL mUseEllipses;
-
- S32 mLineSpacing;
-
- S32 mHPad;
- S32 mVPad;
- LLFontGL::HAlign mHAlign;
- LLFontGL::VAlign mVAlign;
- std::vector<S32> mLineLengthList;
- callback_t mClickedCallback;
+ LLUIString mText;
+ callback_t mClickedCallback;
};
#endif
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index 39f09b297f..997c5b8fa8 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -77,106 +77,31 @@ static LLDefaultChildRegistry::Register<LLTextEditor> r("simple_text_editor");
//
const S32 UI_TEXTEDITOR_LINE_NUMBER_MARGIN = 32;
const S32 UI_TEXTEDITOR_LINE_NUMBER_DIGITS = 4;
-const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds
-const S32 CURSOR_THICKNESS = 2;
const S32 SPACES_PER_TAB = 4;
-
-// helper functors
-struct LLTextEditor::compare_bottom
-{
- bool operator()(const S32& a, const LLTextEditor::line_info& b) const
- {
- return a > b.mBottom; // bottom of a is higher than bottom of b
- }
-
- bool operator()(const LLTextEditor::line_info& a, const S32& b) const
- {
- return a.mBottom > b; // bottom of a is higher than bottom of b
- }
-
- bool operator()(const LLTextEditor::line_info& a, const LLTextEditor::line_info& b) const
- {
- return a.mBottom > b.mBottom; // bottom of a is higher than bottom of b
- }
-
-};
-
-// helper functors
-struct LLTextEditor::compare_top
-{
- bool operator()(const S32& a, const LLTextEditor::line_info& b) const
- {
- return a > b.mTop; // top of a is higher than top of b
- }
-
- bool operator()(const LLTextEditor::line_info& a, const S32& b) const
- {
- return a.mTop > b; // top of a is higher than top of b
- }
-
- bool operator()(const LLTextEditor::line_info& a, const LLTextEditor::line_info& b) const
- {
- return a.mTop > b.mTop; // top of a is higher than top of b
- }
-};
-
-struct LLTextEditor::line_end_compare
-{
- bool operator()(const S32& pos, const LLTextEditor::line_info& info) const
- {
- return (pos < info.mDocIndexEnd);
- }
-
- bool operator()(const LLTextEditor::line_info& info, const S32& pos) const
- {
- return (info.mDocIndexEnd < pos);
- }
-
- bool operator()(const LLTextEditor::line_info& a, const LLTextEditor::line_info& b) const
- {
- return (a.mDocIndexEnd < b.mDocIndexEnd);
- }
-
-};
-
-//
-// DocumentPanel
-//
-
-class DocumentPanel : public LLPanel
-{
-public:
- DocumentPanel(const Params&);
-};
-
-DocumentPanel::DocumentPanel(const Params& p)
-: LLPanel(p)
-{}
-
///////////////////////////////////////////////////////////////////
-class LLTextEditor::LLTextCmdInsert : public LLTextEditor::LLTextCmd
+class LLTextEditor::TextCmdInsert : public LLTextBase::TextCmd
{
public:
- LLTextCmdInsert(S32 pos, BOOL group_with_next, const LLWString &ws, LLTextSegmentPtr segment)
- : LLTextCmd(pos, group_with_next, segment), mWString(ws)
+ TextCmdInsert(S32 pos, BOOL group_with_next, const LLWString &ws, LLTextSegmentPtr segment)
+ : TextCmd(pos, group_with_next, segment), mWString(ws)
{
}
- virtual ~LLTextCmdInsert() {}
- virtual BOOL execute( LLTextEditor* editor, S32* delta )
+ virtual ~TextCmdInsert() {}
+ virtual BOOL execute( LLTextBase* editor, S32* delta )
{
*delta = insert(editor, getPosition(), mWString );
LLWStringUtil::truncate(mWString, *delta);
//mWString = wstring_truncate(mWString, *delta);
return (*delta != 0);
}
- virtual S32 undo( LLTextEditor* editor )
+ virtual S32 undo( LLTextBase* editor )
{
remove(editor, getPosition(), mWString.length() );
return getPosition();
}
- virtual S32 redo( LLTextEditor* editor )
+ virtual S32 redo( LLTextBase* editor )
{
insert(editor, getPosition(), mWString );
return getPosition() + mWString.length();
@@ -187,11 +112,11 @@ private:
};
///////////////////////////////////////////////////////////////////
-class LLTextEditor::LLTextCmdAddChar : public LLTextEditor::LLTextCmd
+class LLTextEditor::TextCmdAddChar : public LLTextBase::TextCmd
{
public:
- LLTextCmdAddChar( S32 pos, BOOL group_with_next, llwchar wc, LLTextSegmentPtr segment)
- : LLTextCmd(pos, group_with_next, segment), mWString(1, wc), mBlockExtensions(FALSE)
+ TextCmdAddChar( S32 pos, BOOL group_with_next, llwchar wc, LLTextSegmentPtr segment)
+ : TextCmd(pos, group_with_next, segment), mWString(1, wc), mBlockExtensions(FALSE)
{
}
virtual void blockExtensions()
@@ -205,14 +130,14 @@ public:
return !mBlockExtensions && (pos == getPosition() + (S32)mWString.length());
}
- virtual BOOL execute( LLTextEditor* editor, S32* delta )
+ virtual BOOL execute( LLTextBase* editor, S32* delta )
{
*delta = insert(editor, getPosition(), mWString);
LLWStringUtil::truncate(mWString, *delta);
//mWString = wstring_truncate(mWString, *delta);
return (*delta != 0);
}
- virtual BOOL extendAndExecute( LLTextEditor* editor, S32 pos, llwchar wc, S32* delta )
+ virtual BOOL extendAndExecute( LLTextBase* editor, S32 pos, llwchar wc, S32* delta )
{
LLWString ws;
ws += wc;
@@ -224,12 +149,12 @@ public:
}
return (*delta != 0);
}
- virtual S32 undo( LLTextEditor* editor )
+ virtual S32 undo( LLTextBase* editor )
{
remove(editor, getPosition(), mWString.length() );
return getPosition();
}
- virtual S32 redo( LLTextEditor* editor )
+ virtual S32 redo( LLTextBase* editor )
{
insert(editor, getPosition(), mWString );
return getPosition() + mWString.length();
@@ -243,25 +168,25 @@ private:
///////////////////////////////////////////////////////////////////
-class LLTextEditor::LLTextCmdOverwriteChar : public LLTextEditor::LLTextCmd
+class LLTextEditor::TextCmdOverwriteChar : public LLTextBase::TextCmd
{
public:
- LLTextCmdOverwriteChar( S32 pos, BOOL group_with_next, llwchar wc)
- : LLTextCmd(pos, group_with_next), mChar(wc), mOldChar(0) {}
+ TextCmdOverwriteChar( S32 pos, BOOL group_with_next, llwchar wc)
+ : TextCmd(pos, group_with_next), mChar(wc), mOldChar(0) {}
- virtual BOOL execute( LLTextEditor* editor, S32* delta )
+ virtual BOOL execute( LLTextBase* editor, S32* delta )
{
- mOldChar = editor->getWChar(getPosition());
+ mOldChar = editor->getWText()[getPosition()];
overwrite(editor, getPosition(), mChar);
*delta = 0;
return TRUE;
}
- virtual S32 undo( LLTextEditor* editor )
+ virtual S32 undo( LLTextBase* editor )
{
overwrite(editor, getPosition(), mOldChar);
return getPosition();
}
- virtual S32 redo( LLTextEditor* editor )
+ virtual S32 redo( LLTextBase* editor )
{
overwrite(editor, getPosition(), mChar);
return getPosition()+1;
@@ -274,26 +199,26 @@ private:
///////////////////////////////////////////////////////////////////
-class LLTextEditor::LLTextCmdRemove : public LLTextEditor::LLTextCmd
+class LLTextEditor::TextCmdRemove : public LLTextBase::TextCmd
{
public:
- LLTextCmdRemove( S32 pos, BOOL group_with_next, S32 len, segment_vec_t& segments ) :
- LLTextCmd(pos, group_with_next), mLen(len)
+ TextCmdRemove( S32 pos, BOOL group_with_next, S32 len, segment_vec_t& segments ) :
+ TextCmd(pos, group_with_next), mLen(len)
{
std::swap(mSegments, segments);
}
- virtual BOOL execute( LLTextEditor* editor, S32* delta )
+ virtual BOOL execute( LLTextBase* editor, S32* delta )
{
- mWString = editor->getWSubString(getPosition(), mLen);
+ mWString = editor->getWText().substr(getPosition(), mLen);
*delta = remove(editor, getPosition(), mLen );
return (*delta != 0);
}
- virtual S32 undo( LLTextEditor* editor )
+ virtual S32 undo( LLTextBase* editor )
{
insert(editor, getPosition(), mWString);
return getPosition() + mWString.length();
}
- virtual S32 redo( LLTextEditor* editor )
+ virtual S32 redo( LLTextBase* editor )
{
remove(editor, getPosition(), mLen );
return getPosition();
@@ -307,138 +232,61 @@ private:
///////////////////////////////////////////////////////////////////
LLTextEditor::Params::Params()
: default_text("default_text"),
- max_text_length("max_length", 255),
- read_only("read_only", false),
embedded_items("embedded_items", false),
- hide_scrollbar("hide_scrollbar"),
- hide_border("hide_border", false),
- word_wrap("word_wrap", false),
ignore_tab("ignore_tab", true),
- track_bottom("track_bottom", false),
handle_edit_keys_directly("handle_edit_keys_directly", false),
show_line_numbers("show_line_numbers", false),
- cursor_color("cursor_color"),
default_color("default_color"),
- text_color("text_color"),
- text_readonly_color("text_readonly_color"),
- bg_readonly_color("bg_readonly_color"),
- bg_writeable_color("bg_writeable_color"),
- bg_focus_color("bg_focus_color"),
- link_color("link_color"),
- commit_on_focus_lost("commit_on_focus_lost", false),
- length("length"), // ignored
- type("type"), // ignored
- is_unicode("is_unicode")// ignored
+ commit_on_focus_lost("commit_on_focus_lost", false)
{}
LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
- LLUICtrl(p, LLTextViewModelPtr(new LLTextViewModel)),
LLTextBase(p),
- mMaxTextByteLength( p.max_text_length ),
mBaseDocIsPristine(TRUE),
mPristineCmd( NULL ),
mLastCmd( NULL ),
- mCursorPos( 0 ),
- mIsSelecting( FALSE ),
- mSelectionStart( 0 ),
- mSelectionEnd( 0 ),
- mOnScrollEndData( NULL ),
- mCursorColor( p.cursor_color() ),
- mFgColor( p.text_color() ),
mDefaultColor( p.default_color() ),
- mReadOnlyFgColor( p.text_readonly_color() ),
- mWriteableBgColor( p.bg_writeable_color() ),
- mReadOnlyBgColor( p.bg_readonly_color() ),
- mFocusBgColor( p.bg_focus_color() ),
- mLinkColor( p.link_color() ),
- mReadOnly(p.read_only),
mShowLineNumbers ( p.show_line_numbers ),
mCommitOnFocusLost( p.commit_on_focus_lost),
- mTrackBottom( p.track_bottom ),
mAllowEmbeddedItems( p.embedded_items ),
mHandleEditKeysDirectly( p.handle_edit_keys_directly ),
mMouseDownX(0),
mMouseDownY(0),
- mLastSelectionX(-1),
- mReflowNeeded(FALSE),
- mScrollNeeded(FALSE),
- mLastSelectionY(-1),
- mParseHighlights(FALSE),
- mTabsToNextField(p.ignore_tab),
- mScrollIndex(-1)
+ mTabsToNextField(p.ignore_tab)
{
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
-
- mWordWrap = p.word_wrap;
mDefaultFont = p.font;
- mParseHTML = FALSE;
mSourceID.generate();
- // reset desired x cursor position
- mDesiredXPixel = -1;
-
- 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;
- mScroller = LLUICtrlFactory::create<LLScrollContainer>(scroll_params);
- addChild(mScroller);
-
- LLPanel::Params panel_params;
- panel_params.name = "text_contents";
- panel_params.rect = LLRect(0, 500, 500, 0);
- panel_params.background_visible = true;
- panel_params.background_opaque = true;
- panel_params.mouse_opaque = false;
-
- mDocumentPanel = LLUICtrlFactory::create<DocumentPanel>(panel_params);
- mScroller->addChild(mDocumentPanel);
-
- updateTextRect();
-
- static LLUICachedControl<S32> text_editor_border ("UITextEditorBorder", 0);
+ //FIXME: use image?
LLViewBorder::Params params;
params.name = "text ed border";
params.rect = getLocalRect();
params.bevel_style = LLViewBorder::BEVEL_IN;
- params.border_thickness = text_editor_border;
+ params.border_thickness = 1;
+ params.visible = p.border_visible;
mBorder = LLUICtrlFactory::create<LLViewBorder> (params);
addChild( mBorder );
- mBorder->setVisible(!p.hide_border);
-
- createDefaultSegment();
- appendText(p.default_text, FALSE, FALSE);
+ setText(p.default_text());
+ if (mShowLineNumbers)
+ {
+ mHPad += UI_TEXTEDITOR_LINE_NUMBER_MARGIN;
+ updateTextRect();
+ }
}
void LLTextEditor::initFromParams( const LLTextEditor::Params& p)
{
- resetDirty(); // Update saved text state
- LLUICtrl::initFromParams(p);
- // HACK: work around enabled == readonly design bug -- RN
- // setEnabled will modify our read only status, so do this after
- // LLUICtrl::initFromParams
- if (p.read_only.isProvided())
- {
- mReadOnly = p.read_only;
- }
-
+ LLTextBase::initFromParams(p);
+
if (p.commit_on_focus_lost.isProvided())
{
mCommitOnFocusLost = p.commit_on_focus_lost;
}
- updateSegments();
updateAllowingLanguageInput();
-
- // HACK: text editors always need to be enabled so that we can scroll
- LLView::setEnabled(true);
}
LLTextEditor::~LLTextEditor()
@@ -455,282 +303,18 @@ LLTextEditor::~LLTextEditor()
std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer());
}
-LLTextViewModel* LLTextEditor::getViewModel() const
-{
- return (LLTextViewModel*)mViewModel.get();
-}
-
-static LLFastTimer::DeclareTimer FTM_TEXT_REFLOW ("Text Reflow");
-void LLTextEditor::reflow(S32 start_index)
-{
- if (!mReflowNeeded) return;
-
- LLFastTimer ft(FTM_TEXT_REFLOW);
- static LLUICachedControl<S32> texteditor_vpad_top ("UITextEditorVPadTop", 0);
-
- updateSegments();
-
- while(mReflowNeeded)
- {
- bool scrolled_to_bottom = mScroller->isAtBottom();
- mReflowNeeded = FALSE;
-
- LLRect old_cursor_rect = getLocalRectFromDocIndex(mCursorPos);
- bool follow_selection = mTextRect.overlaps(old_cursor_rect); // cursor is visible
- S32 first_line = getFirstVisibleLine();
- // if scroll anchor not on first line, update it to first character of first line
- if (!mLineInfoList.empty()
- && (mScrollIndex < mLineInfoList[first_line].mDocIndexStart
- || mScrollIndex >= mLineInfoList[first_line].mDocIndexEnd))
- {
- mScrollIndex = mLineInfoList[first_line].mDocIndexStart;
- }
- LLRect first_char_rect = getLocalRectFromDocIndex(mScrollIndex);
- //first_char_rect.intersectWith(mTextRect);
-
- S32 cur_top = -texteditor_vpad_top;
-
- if (getLength())
- {
- segment_set_t::iterator seg_iter = mSegments.begin();
- S32 seg_offset = 0;
- S32 line_start_index = 0;
- S32 text_width = mTextRect.getWidth(); // optionally reserve room for margin
- S32 remaining_pixels = text_width;
- LLWString text(getWText());
- S32 line_count = 0;
-
- // find and erase line info structs starting at start_index and going to end of document
- if (!mLineInfoList.empty())
- {
- // find first element whose end comes after start_index
- line_list_t::iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), start_index, line_end_compare());
- line_start_index = iter->mDocIndexStart;
- line_count = iter->mLineNum;
- getSegmentAndOffset(iter->mDocIndexStart, &seg_iter, &seg_offset);
- mLineInfoList.erase(iter, mLineInfoList.end());
- }
-
- // reserve enough space for line numbers
- S32 line_height = mShowLineNumbers ? (S32)(LLFontGL::getFontMonospace()->getLineHeight()) : 0;
-
- while(seg_iter != mSegments.end())
- {
- LLTextSegmentPtr segment = *seg_iter;
-
- // track maximum height of any segment on this line
- line_height = llmax(line_height, segment->getMaxHeight());
- S32 cur_index = segment->getStart() + seg_offset;
- // find run of text from this segment that we can display on one line
- S32 end_index = cur_index;
- while(end_index < segment->getEnd() && text[end_index] != '\n')
- {
- ++end_index;
- }
-
- // ask segment how many character fit in remaining space
- S32 max_characters = end_index - cur_index;
- S32 character_count = segment->getNumChars(llmax(0, remaining_pixels), seg_offset, cur_index - line_start_index, max_characters);
-
- seg_offset += character_count;
-
- S32 last_segment_char_on_line = segment->getStart() + seg_offset;
-
- // if we didn't finish the current segment...
- if (last_segment_char_on_line < segment->getEnd())
- {
- // set up index for next line
- // ...skip newline, we don't want to draw
- S32 next_line_count = line_count;
- if (text[last_segment_char_on_line] == '\n')
- {
- seg_offset++;
- last_segment_char_on_line++;
- next_line_count++;
- }
-
- // add line info and keep going
- mLineInfoList.push_back(line_info(line_start_index, last_segment_char_on_line, cur_top, cur_top - line_height, line_count));
-
- line_start_index = segment->getStart() + seg_offset;
- cur_top -= line_height;
- remaining_pixels = text_width;
- line_height = 0;
- line_count = next_line_count;
- }
- // ...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, cur_top, cur_top - line_height, line_count));
- cur_top -= line_height;
- break;
- }
- // finished a segment and there are segments remaining on this line
- else
- {
- // subtract pixels used and increment segment
- remaining_pixels -= segment->getWidth(seg_offset, character_count);
- ++seg_iter;
- seg_offset = 0;
- }
- }
- }
-
- // change mDocumentPanel document size to accomodate reflowed text
- LLRect document_rect;
- document_rect.setOriginAndSize(1, 1,
- mScroller->getContentWindowRect().getWidth(),
- llmax(mScroller->getContentWindowRect().getHeight(), -cur_top));
- mDocumentPanel->setShape(document_rect);
-
- // after making document big enough to hold all the text, move the text to fit in the document
- if (!mLineInfoList.empty())
- {
- S32 delta_pos = mDocumentPanel->getRect().getHeight() - mLineInfoList.begin()->mTop - texteditor_vpad_top;
- // move line segments to fit new document rect
- for (line_list_t::iterator it = mLineInfoList.begin(); it != mLineInfoList.end(); ++it)
- {
- it->mTop += delta_pos;
- it->mBottom += delta_pos;
- }
- }
-
- // calculate visible region for diplaying text
- updateTextRect();
-
- 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())
- {
- LLRect visible_content_rect = mScroller->getVisibleContentRect();
- if (scrolled_to_bottom && mTrackBottom)
- {
- // 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 = getLocalRectFromDocIndex(mCursorPos);
- new_cursor_rect_doc.translate(visible_content_rect.mLeft, visible_content_rect.mBottom);
- mScroller->scrollToShowRect(new_cursor_rect_doc, old_cursor_rect);
- //llassert_always(getLocalRectFromDocIndex(mCursorPos).mBottom == old_cursor_rect.mBottom);
- }
- else
- {
- // keep first line of text visible
- LLRect new_first_char_rect = getLocalRectFromDocIndex(mScrollIndex);
- new_first_char_rect.translate(visible_content_rect.mLeft, visible_content_rect.mBottom);
- mScroller->scrollToShowRect(new_first_char_rect, first_char_rect);
- //llassert_always(getLocalRectFromDocIndex(mScrollIndex).mBottom == first_char_rect.mBottom);
- }
- }
- }
-
- // reset desired x cursor position
- updateCursorXPos();
-}
-
////////////////////////////////////////////////////////////
// LLTextEditor
// Public methods
-BOOL LLTextEditor::truncate()
-{
- BOOL did_truncate = FALSE;
-
- // First rough check - if we're less than 1/4th the size, we're OK
- if (getLength() >= S32(mMaxTextByteLength / 4))
- {
- // Have to check actual byte size
- LLWString text(getWText());
- S32 utf8_byte_size = wstring_utf8_length(text);
- if ( utf8_byte_size > mMaxTextByteLength )
- {
- // Truncate safely in UTF-8
- std::string temp_utf8_text = wstring_to_utf8str(text);
- temp_utf8_text = utf8str_truncate( temp_utf8_text, mMaxTextByteLength );
- getViewModel()->setDisplay(utf8str_to_wstring( temp_utf8_text ));
- did_truncate = TRUE;
- }
- }
-
- return did_truncate;
-}
-
void LLTextEditor::setText(const LLStringExplicit &utf8str)
{
- // clear out the existing text and segments
- clearSegments();
-
- getViewModel()->setValue("");
-
- truncate();
- blockUndo();
-
- createDefaultSegment();
-
- startOfDoc();
- deselect();
-
- // append the new text (supports Url linking)
- std::string text(utf8str);
- LLStringUtil::removeCRLF(text);
- appendStyledText(text, false, false, LLStyle::Params());
-
- needsReflow();
-
- resetDirty();
-
- onValueChange(0, getLength());
-}
-
-void LLTextEditor::setWText(const LLWString &wtext)
-{
- // clear out the existing text and segments
- clearSegments();
-
- getViewModel()->setDisplay(LLWString());
-
- truncate();
blockUndo();
-
- createDefaultSegment();
-
- startOfDoc();
deselect();
- // append the new text (supports Url linking)
- appendStyledText(wstring_to_utf8str(wtext), false, false, LLStyle::Params());
-
- needsReflow();
+ LLTextBase::setText(utf8str);
resetDirty();
-
- onValueChange(0, getLength());
-}
-
-// virtual
-void LLTextEditor::setValue(const LLSD& value)
-{
- setText(value.asString());
-}
-
-std::string LLTextEditor::getText() const
-{
- if (mAllowEmbeddedItems)
- {
- llwarns << "getText() called on text with embedded items (not supported)" << llendl;
- }
- return getViewModel()->getValue().asString();
}
void LLTextEditor::selectNext(const std::string& search_text_in, BOOL case_insensitive, BOOL wrap)
@@ -828,12 +412,6 @@ void LLTextEditor::replaceTextAll(const std::string& search_text, const std::str
}
}
-// Picks a new cursor position based on the screen size of text being drawn.
-void LLTextEditor::setCursorAtLocalPos( S32 local_x, S32 local_y, bool round, bool keep_cursor_offset )
-{
- setCursorPos(getDocIndexFromLocalCoord(local_x, local_y, round), keep_cursor_offset);
-}
-
S32 LLTextEditor::prevWordPos(S32 cursorPos) const
{
LLWString wtext(getWText());
@@ -862,60 +440,6 @@ S32 LLTextEditor::nextWordPos(S32 cursorPos) const
return cursorPos;
}
-S32 LLTextEditor::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 LLTextEditor::getLineHeight( S32 line ) const
-{
- S32 num_lines = getLineCount();
- if (num_lines == 0)
- {
- return 0;
- }
-
- line = llclamp(line, 0, num_lines-1);
- return mLineInfoList[line].mTop - mLineInfoList[line].mBottom;
-}
-
-// Given an offset into text (pos), find the corresponding line (from the start of the doc) and an offset into the line.
-void LLTextEditor::getLineAndOffset( S32 startpos, S32* linep, S32* offsetp, bool include_wordwrap) const
-{
- if (mLineInfoList.empty())
- {
- *linep = 0;
- *offsetp = startpos;
- }
- else
- {
- line_list_t::const_iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), startpos, line_end_compare());
- if (include_wordwrap)
- {
- *linep = iter - mLineInfoList.begin();
- }
- else
- {
- if (iter == mLineInfoList.end())
- {
- *linep = mLineInfoList.back().mLineNum;
- }
- else
- {
- *linep = iter->mLineNum;
- }
- }
- *offsetp = startpos - iter->mDocIndexStart;
- }
-}
-
const LLTextSegmentPtr LLTextEditor::getPreviousSegment() const
{
// find segment index at character to left of cursor (or rightmost edge of selection)
@@ -957,201 +481,6 @@ void LLTextEditor::getSegmentsInRange(LLTextEditor::segment_vec_t& segments_out,
}
}
-// 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 LLTextEditor::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const
-{
- // Figure out which line we're nearest to.
- LLRect visible_region = mScroller->getVisibleContentRect();
-
- // binary search for line that starts before local_y
- line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), local_y - mTextRect.mBottom + visible_region.mBottom, compare_bottom());
-
- if (line_iter == mLineInfoList.end())
- {
- return getLength(); // past the end
- }
-
- S32 pos = getLength();
- S32 start_x = mTextRect.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 - 1) - segment_line_start;
- S32 text_width = segmentp->getWidth(line_seg_offset, segment_line_length);
- if (local_x < start_x + text_width // cursor to left of right edge of text
- || segmentp->getEnd() >= line_iter->mDocIndexEnd - 1) // or this segment wraps to next line
- {
- // Figure out which character we're nearest to.
- S32 offset;
- if (!segmentp->canEdit())
- {
- S32 segment_width = segmentp->getWidth(0, segmentp->getEnd() - segmentp->getStart());
- 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;
- }
- start_x += text_width;
- }
-
- return pos;
-}
-
-LLRect LLTextEditor::getLocalRectFromDocIndex(S32 pos) const
-{
- LLRect local_rect(mTextRect);
- local_rect.mBottom = local_rect.mTop - (S32)(mDefaultFont->getLineHeight());
- if (mLineInfoList.empty())
- {
- return local_rect;
- }
-
- // clamp pos to valid values
- pos = llclamp(pos, 0, mLineInfoList.back().mDocIndexEnd - 1);
-
-
- // find line that contains cursor
- line_list_t::const_iterator line_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), pos, line_end_compare());
-
- LLRect scrolled_view_rect = mScroller->getVisibleContentRect();
- local_rect.mLeft = mTextRect.mLeft - scrolled_view_rect.mLeft;
- local_rect.mBottom = mTextRect.mBottom + (line_iter->mBottom - scrolled_view_rect.mBottom);
- local_rect.mTop = mTextRect.mBottom + (line_iter->mTop - scrolled_view_rect.mBottom);
-
- segment_set_t::iterator line_seg_iter;
- S32 line_seg_offset;
- segment_set_t::iterator cursor_seg_iter;
- S32 cursor_seg_offset;
- getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset);
- getSegmentAndOffset(pos, &cursor_seg_iter, &cursor_seg_offset);
-
- while(line_seg_iter != mSegments.end())
- {
- const LLTextSegmentPtr segmentp = *line_seg_iter;
-
- if (line_seg_iter == cursor_seg_iter)
- {
- // cursor advanced to right based on difference in offset of cursor to start of line
- local_rect.mLeft += segmentp->getWidth(line_seg_offset, cursor_seg_offset - line_seg_offset);
-
- break;
- }
- else
- {
- // add remainder of current text segment to cursor position
- local_rect.mLeft += segmentp->getWidth(line_seg_offset, (segmentp->getEnd() - segmentp->getStart()) - line_seg_offset);
- // 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;
- }
- }
-
- local_rect.mRight = local_rect.mLeft;
-
- return local_rect;
-}
-
-void LLTextEditor::addDocumentChild(LLView* view)
-{
- mDocumentPanel->addChild(view);
-}
-
-void LLTextEditor::removeDocumentChild(LLView* view)
-{
- mDocumentPanel->removeChild(view);
-}
-
-bool LLTextEditor::setCursor(S32 row, S32 column)
-{
- if (0 <= row && row < (S32)mLineInfoList.size())
- {
- S32 doc_pos = mLineInfoList[row].mDocIndexStart;
- column = llclamp(column, 0, mLineInfoList[row].mDocIndexEnd - mLineInfoList[row].mDocIndexStart - 1);
- doc_pos += column;
- updateCursorXPos();
-
- return setCursorPos(doc_pos);
- }
- return false;
-}
-
-bool LLTextEditor::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;
-}
-
-void LLTextEditor::updateCursorXPos()
-{
- // reset desired x cursor position
- mDesiredXPixel = getLocalRectFromDocIndex(mCursorPos).mLeft;
-}
-
-// constraint cursor to editable segments of document
-// NOTE: index must be within document range
-S32 LLTextEditor::getEditableIndex(S32 index, bool increasing_direction)
-{
- segment_set_t::iterator segment_iter;
- S32 offset;
- getSegmentAndOffset(index, &segment_iter, &offset);
-
- 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;
- }
-}
-
// virtual
BOOL LLTextEditor::canDeselect() const
{
@@ -1167,25 +496,6 @@ void LLTextEditor::deselect()
}
-void LLTextEditor::startSelection()
-{
- if( !mIsSelecting )
- {
- mIsSelecting = TRUE;
- mSelectionStart = mCursorPos;
- mSelectionEnd = mCursorPos;
- }
-}
-
-void LLTextEditor::endSelection()
-{
- if( mIsSelecting )
- {
- mIsSelecting = FALSE;
- mSelectionEnd = mCursorPos;
- }
-}
-
BOOL LLTextEditor::selectionContainsLineBreaks()
{
if (hasSelection())
@@ -1334,23 +644,12 @@ void LLTextEditor::selectAll()
setCursorPos(mSelectionEnd);
}
-
-BOOL LLTextEditor::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen)
-{
- if (childrenHandleToolTip(x, y, msg, sticky_rect_screen))
- {
- return TRUE;
- }
-
- return handleToolTipForUrl(this, x, y, msg, sticky_rect_screen);
-}
-
BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
// Let scrollbar have first dibs
- handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
+ handled = LLTextBase::handleMouseDown(x, y, mask);
if( !handled )
{
@@ -1398,7 +697,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
}
// Delay cursor flashing
- resetKeystrokeTimer();
+ resetCursorBlink();
return handled;
}
@@ -1407,7 +706,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
BOOL LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
- handled = childrenHandleMiddleMouseDown(x, y, mask) != NULL;
+ handled = LLTextBase::handleMouseDown(x, y, mask);
if (!handled)
{
@@ -1424,19 +723,12 @@ BOOL LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask)
{
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
BOOL handled = FALSE;
if(hasMouseCapture() )
{
if( mIsSelecting )
{
- if (x != mLastSelectionX || y != mLastSelectionY)
- {
- mLastSelectionX = x;
- mLastSelectionY = y;
- }
-
mScroller->autoScroll(x, y);
S32 clamped_x = llclamp(x, mTextRect.mLeft, mTextRect.mRight);
@@ -1453,32 +745,19 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask)
if( !handled )
{
// Pass to children
- handled = LLView::childrenHandleHover(x, y, mask) != NULL;
+ handled = LLTextBase::handleHover(x, y, mask);
}
if( handled )
{
// Delay cursor flashing
- resetKeystrokeTimer();
+ resetCursorBlink();
}
- // Opaque
if( !handled )
{
- // Check to see if we're over an HTML-style link
- handled = handleHoverOverUrl(x, y);
- if( handled )
- {
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl;
- getWindow()->setCursor(UI_CURSOR_HAND);
- }
-
- if( !handled )
- {
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
- getWindow()->setCursor(UI_CURSOR_IBEAM);
- handled = TRUE;
- }
+ getWindow()->setCursor(UI_CURSOR_IBEAM);
+ handled = TRUE;
}
return handled;
@@ -1489,8 +768,12 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
- // let scrollbar have first dibs
- handled = LLView::childrenHandleMouseUp(x, y, mask) != NULL;
+ // if I'm not currently selecting text
+ if (!(hasSelection() && hasMouseCapture()))
+ {
+ // let text segments handle mouse event
+ handled = LLTextBase::handleMouseUp(x, y, mask);
+ }
if( !handled )
{
@@ -1503,11 +786,6 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)
endSelection();
}
- if( !hasSelection() && hasMouseCapture() )
- {
- handleMouseUpOverUrl(x, y);
- }
-
// take selection to 'primary' clipboard
updatePrimary();
@@ -1515,7 +793,7 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)
}
// Delay cursor flashing
- resetKeystrokeTimer();
+ resetCursorBlink();
if( hasMouseCapture() )
{
@@ -1532,8 +810,8 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
- // let scrollbar have first dibs
- handled = LLView::childrenHandleDoubleClick(x, y, mask) != NULL;
+ // let scrollbar and text segments have first dibs
+ handled = LLTextBase::handleDoubleClick(x, y, mask);
if( !handled )
{
@@ -1571,7 +849,7 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
mIsSelecting = FALSE;
// delay cursor flashing
- resetKeystrokeTimer();
+ resetCursorBlink();
// take selection to 'primary' clipboard
updatePrimary();
@@ -1583,35 +861,18 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
}
-// Allow calling cards to be dropped onto text fields. Append the name and
-// a carriage return.
-// virtual
-BOOL LLTextEditor::handleDragAndDrop(S32 x, S32 y, MASK mask,
- BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
- EAcceptance *accept,
- std::string& tooltip_msg)
-{
- *accept = ACCEPT_NO;
-
- return TRUE;
-}
-
//----------------------------------------------------------------------------
// Returns change in number of characters in mText
-S32 LLTextEditor::execute( LLTextCmd* cmd )
+S32 LLTextEditor::execute( TextCmd* cmd )
{
S32 delta = 0;
if( cmd->execute(this, &delta) )
{
// Delete top of undo stack
undo_stack_t::iterator enditer = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd);
- if (enditer != mUndoStack.begin())
- {
- --enditer;
- std::for_each(mUndoStack.begin(), enditer, DeletePointer());
- mUndoStack.erase(mUndoStack.begin(), enditer);
- }
+ std::for_each(mUndoStack.begin(), enditer, DeletePointer());
+ mUndoStack.erase(mUndoStack.begin(), enditer);
// Push the new command is now on the top (front) of the undo stack.
mUndoStack.push_front(cmd);
mLastCmd = cmd;
@@ -1627,7 +888,7 @@ S32 LLTextEditor::execute( LLTextCmd* cmd )
S32 LLTextEditor::insert(S32 pos, const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment)
{
- return execute( new LLTextCmdInsert( pos, group_with_next_op, wstr, segment ) );
+ return execute( new TextCmdInsert( pos, group_with_next_op, wstr, segment ) );
}
S32 LLTextEditor::remove(S32 pos, S32 length, bool group_with_next_op)
@@ -1638,12 +899,7 @@ S32 LLTextEditor::remove(S32 pos, S32 length, bool group_with_next_op)
// store text segments
getSegmentsInRange(segments_to_remove, pos, pos + length, false);
- return execute( new LLTextCmdRemove( pos, group_with_next_op, end_pos - pos, segments_to_remove ) );
-}
-
-S32 LLTextEditor::append(const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment)
-{
- return insert(getLength(), wstr, group_with_next_op, segment);
+ return execute( new TextCmdRemove( pos, group_with_next_op, end_pos - pos, segments_to_remove ) );
}
S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc)
@@ -1654,7 +910,7 @@ S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc)
}
else
{
- return execute(new LLTextCmdOverwriteChar(pos, FALSE, wc));
+ return execute(new TextCmdOverwriteChar(pos, FALSE, wc));
}
}
@@ -1674,8 +930,7 @@ void LLTextEditor::removeCharOrTab()
if (text[mCursorPos - 1] == ' ')
{
// Try to remove a "tab"
- S32 line, offset;
- getLineAndOffset(mCursorPos, &line, &offset);
+ S32 offset = getLineOffsetFromDocIndex(mCursorPos);
if (offset > 0)
{
chars_to_remove = offset % SPACES_PER_TAB;
@@ -1749,7 +1004,7 @@ S32 LLTextEditor::addChar(S32 pos, llwchar wc)
}
else
{
- return execute(new LLTextCmdAddChar(pos, FALSE, wc, LLTextSegmentPtr()));
+ return execute(new TextCmdAddChar(pos, FALSE, wc, LLTextSegmentPtr()));
}
}
@@ -2349,8 +1604,7 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask, BOOL* return
deleteSelection(FALSE);
}
- S32 line, offset;
- getLineAndOffset( mCursorPos, &line, &offset );
+ S32 offset = getLineOffsetFromDocIndex(mCursorPos);
S32 spaces_needed = SPACES_PER_TAB - (offset % SPACES_PER_TAB);
for( S32 i=0; i < spaces_needed; i++ )
@@ -2481,7 +1735,7 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask )
if( handled )
{
- resetKeystrokeTimer();
+ resetCursorBlink();
// Most keystrokes will make the selection box go away, but not all will.
if( !selection_modified &&
@@ -2534,7 +1788,7 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char)
if( handled )
{
- resetKeystrokeTimer();
+ resetCursorBlink();
// Most keystrokes will make the selection box go away, but not all will.
deselect();
@@ -2573,8 +1827,7 @@ void LLTextEditor::doDelete()
if( (text[ mCursorPos ] == ' ') && (mCursorPos + SPACES_PER_TAB < getLength()) )
{
// Try to remove a full tab's worth of spaces
- S32 line, offset;
- getLineAndOffset( mCursorPos, &line, &offset );
+ S32 offset = getLineOffsetFromDocIndex(mCursorPos);
chars_to_remove = SPACES_PER_TAB - (offset % SPACES_PER_TAB);
if( chars_to_remove == 0 )
{
@@ -2694,7 +1947,7 @@ void LLTextEditor::redo()
void LLTextEditor::onFocusReceived()
{
- LLUICtrl::onFocusReceived();
+ LLTextBase::onFocusReceived();
updateAllowingLanguageInput();
}
@@ -2717,19 +1970,19 @@ void LLTextEditor::onFocusLost()
// Make sure cursor is shown again
getWindow()->showCursorFromMouseMove();
- LLUICtrl::onFocusLost();
+ LLTextBase::onFocusLost();
}
void LLTextEditor::onCommit()
{
setControlValue(getValue());
- LLUICtrl::onCommit();
+ LLTextBase::onCommit();
}
void LLTextEditor::setEnabled(BOOL enabled)
{
// just treat enabled as read-only flag
- BOOL read_only = !enabled;
+ bool read_only = !enabled;
if (read_only != mReadOnly)
{
mReadOnly = read_only;
@@ -2738,195 +1991,6 @@ void LLTextEditor::setEnabled(BOOL enabled)
}
}
-void LLTextEditor::drawBackground()
-{
- S32 left = 0;
- S32 top = getRect().getHeight();
- S32 bottom = 0;
-
- LLColor4 bg_color = mReadOnly ? mReadOnlyBgColor.get()
- : hasFocus() ? mFocusBgColor.get() : mWriteableBgColor.get();
- if( mShowLineNumbers ) {
- gl_rect_2d(left, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN, bottom, mReadOnlyBgColor.get() ); // line number area always read-only
- gl_rect_2d(UI_TEXTEDITOR_LINE_NUMBER_MARGIN, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN-1, bottom, LLColor4::grey3); // separator
- }
-}
-
-// Draws the black box behind the selected text
-void LLTextEditor::drawSelectionBackground()
-{
- // Draw selection even if we don't have keyboard focus for search/replace
- if( hasSelection() && !mLineInfoList.empty())
- {
- LLWString text = getWText();
- std::vector<LLRect> selection_rects;
-
- S32 selection_left = llmin( mSelectionStart, mSelectionEnd );
- S32 selection_right = llmax( mSelectionStart, mSelectionEnd );
- LLRect selection_rect = mTextRect;
-
- // Skip through the lines we aren't drawing.
- LLRect content_display_rect = mScroller->getVisibleContentRect();
-
- // binary search for line that starts before top of visible buffer
- line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mTop, compare_bottom());
- line_list_t::const_iterator end_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mBottom, compare_top());
-
- bool done = false;
-
- // Find the coordinates of the selected area
- for (;line_iter != end_iter && !done; ++line_iter)
- {
- // is selection visible on this line?
- if (line_iter->mDocIndexEnd > selection_left && line_iter->mDocIndexStart < selection_right)
- {
- segment_set_t::iterator segment_iter;
- S32 segment_offset;
- getSegmentAndOffset(line_iter->mDocIndexStart, &segment_iter, &segment_offset);
-
- LLRect selection_rect;
- selection_rect.mLeft = 0;
- selection_rect.mRight = 0;
- selection_rect.mBottom = line_iter->mBottom;
- selection_rect.mTop = line_iter->mTop;
-
- for(;segment_iter != mSegments.end(); ++segment_iter, segment_offset = 0)
- {
- LLTextSegmentPtr segmentp = *segment_iter;
-
- S32 segment_line_start = segmentp->getStart() + segment_offset;
- S32 segment_line_end = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd);
-
- // if selection after beginning of segment
- if(selection_left >= segment_line_start)
- {
- S32 num_chars = llmin(selection_left, segment_line_end) - segment_line_start;
- selection_rect.mLeft += segmentp->getWidth(segment_offset, num_chars);
- }
-
- // if 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)
- selection_rect.mRight += segmentp->getWidth(segment_offset, segment_line_end - segment_line_start);
- }
- // else if selection ends on current segment...
- else
- {
- S32 num_chars = selection_right - segment_line_start;
- selection_rect.mRight += segmentp->getWidth(segment_offset, num_chars);
-
- break;
- }
- }
- selection_rects.push_back(selection_rect);
- }
- }
-
- // Draw the selection box (we're using a box instead of reversing the colors on the selected text).
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- const LLColor4& color = mReadOnly ? mReadOnlyBgColor.get() : mWriteableBgColor.get();
- F32 alpha = hasFocus() ? 0.7f : 0.3f;
- gGL.color4f( 1.f - color.mV[0], 1.f - color.mV[1], 1.f - color.mV[2], alpha );
-
- for (std::vector<LLRect>::iterator rect_it = selection_rects.begin();
- rect_it != selection_rects.end();
- ++rect_it)
- {
- LLRect selection_rect = *rect_it;
- selection_rect.translate(mTextRect.mLeft - content_display_rect.mLeft, mTextRect.mBottom - content_display_rect.mBottom);
- gl_rect_2d(selection_rect);
- }
- }
-}
-
-void LLTextEditor::drawCursor()
-{
- if( hasFocus()
- && gFocusMgr.getAppHasFocus()
- && !mReadOnly)
- {
- 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
- {
- //segmentp = mSegments.back();
- return;
- }
-
- // Draw the cursor
- // (Flash the cursor every half second starting a fixed time after the last keystroke)
- F32 elapsed = mKeystrokeTimer.getElapsedTimeF32();
- if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) )
- {
-
- if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
- {
- S32 width = llmax(CURSOR_THICKNESS, segmentp->getWidth(mCursorPos - segmentp->getStart(), 1));
- cursor_rect.mRight = cursor_rect.mLeft + width;
- }
- else
- {
- cursor_rect.mRight = cursor_rect.mLeft + CURSOR_THICKNESS;
- }
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- gGL.color4fv( mCursorColor.get().mV );
-
- gl_rect_2d(cursor_rect);
-
- if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection() && text[mCursorPos] != '\n')
- {
- LLColor4 text_color;
- const LLFontGL* fontp;
- if (segmentp)
- {
- text_color = segmentp->getColor();
- fontp = segmentp->getStyle()->getFont();
- }
- else if (mReadOnly)
- {
- text_color = mReadOnlyFgColor.get();
- fontp = mDefaultFont;
- }
- else
- {
- text_color = mFgColor.get();
- fontp = mDefaultFont;
- }
- fontp->render(text, mCursorPos, cursor_rect.mLeft, cursor_rect.mBottom,
- LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], 1.f),
- LLFontGL::LEFT, LLFontGL::BOTTOM,
- LLFontGL::NORMAL,
- LLFontGL::NO_SHADOW,
- 1);
- }
-
- // Make sure the IME is in the right place
- LLRect screen_pos = calcScreenRect();
- LLCoordGL ime_pos( screen_pos.mLeft + llfloor(cursor_rect.mLeft), screen_pos.mBottom + llfloor(cursor_rect.mTop) );
-
- ime_pos.mX = (S32) (ime_pos.mX * LLUI::sGLScaleFactor.mV[VX]);
- ime_pos.mY = (S32) (ime_pos.mY * LLUI::sGLScaleFactor.mV[VY]);
- getWindow()->setLanguageTextInput( ime_pos );
- }
- }
-}
-
void LLTextEditor::drawPreeditMarker()
{
static LLUICachedControl<F32> preedit_marker_brightness ("UIPreeditMarkerBrightness", 0);
@@ -3032,96 +2096,6 @@ void LLTextEditor::drawPreeditMarker()
}
-void LLTextEditor::drawText()
-{
- LLWString text = getWText();
- const S32 text_len = getLength();
- if( text_len <= 0 )
- {
- return;
- }
- S32 selection_left = -1;
- S32 selection_right = -1;
- // Draw selection even if we don't have keyboard focus for search/replace
- if( hasSelection())
- {
- selection_left = llmin( mSelectionStart, mSelectionEnd );
- selection_right = llmax( mSelectionStart, mSelectionEnd );
- }
-
- LLGLSUIDefault gls_ui;
- LLRect scrolled_view_rect = mScroller->getVisibleContentRect();
- LLRect content_rect = mScroller->getContentWindowRect();
- S32 first_line = getFirstVisibleLine();
- S32 num_lines = getLineCount();
- if (first_line >= num_lines)
- {
- return;
- }
-
- S32 line_start = getLineStart(first_line);
- // find first text segment that spans top of visible portion of text buffer
- segment_set_t::iterator seg_iter = getSegIterContaining(line_start);
- if (seg_iter == mSegments.end())
- {
- return;
- }
-
- LLTextSegmentPtr cur_segment = *seg_iter;
-
- for (S32 cur_line = first_line; cur_line < num_lines; cur_line++)
- {
- line_info& line = mLineInfoList[cur_line];
-
- if ((line.mTop - scrolled_view_rect.mBottom) < mTextRect.mBottom)
- {
- break;
- }
-
- 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;
- }
-
- LLRect text_rect(mTextRect.mLeft - scrolled_view_rect.mLeft,
- line.mTop - scrolled_view_rect.mBottom + mTextRect.mBottom,
- mTextRect.getWidth() - scrolled_view_rect.mLeft,
- line.mBottom - scrolled_view_rect.mBottom + mTextRect.mBottom);
-
- // draw a single line of text
- S32 seg_start = line_start;
- while( seg_start < line_end )
- {
- while( cur_segment->getEnd() <= seg_start )
- {
- seg_iter++;
- if (seg_iter == mSegments.end())
- {
- llwarns << "Ran off the segmentation end!" << llendl;
-
- return;
- }
- cur_segment = *seg_iter;
- }
-
- S32 clipped_end = llmin( line_end, cur_segment->getEnd() ) - cur_segment->getStart();
- text_rect.mLeft = (S32)(cur_segment->draw(seg_start - cur_segment->getStart(), clipped_end, selection_left, selection_right, text_rect));
-
- seg_start = clipped_end + cur_segment->getStart();
- }
-
- line_start = next_start;
- }
-}
-
void LLTextEditor::drawLineNumbers()
{
LLGLSUIDefault gls_ui;
@@ -3136,24 +2110,31 @@ void LLTextEditor::drawLineNumbers()
return;
}
- S32 cursor_line = getCurrentLine();
+ S32 cursor_line = getLineNumFromDocIndex(mCursorPos);
if (mShowLineNumbers)
{
+ S32 left = 0;
+ S32 top = getRect().getHeight();
+ S32 bottom = 0;
+
+ gl_rect_2d(left, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN, bottom, mReadOnlyBgColor.get() ); // line number area always read-only
+ gl_rect_2d(UI_TEXTEDITOR_LINE_NUMBER_MARGIN, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN-1, bottom, LLColor4::grey3); // separator
+
S32 last_line_num = -1;
for (S32 cur_line = first_line; cur_line < num_lines; cur_line++)
{
line_info& line = mLineInfoList[cur_line];
- if ((line.mTop - scrolled_view_rect.mBottom) < mTextRect.mBottom)
+ if ((line.mRect.mTop - scrolled_view_rect.mBottom) < mTextRect.mBottom)
{
break;
}
- S32 line_bottom = line.mBottom - scrolled_view_rect.mBottom + mTextRect.mBottom;
+ S32 line_bottom = line.mRect.mBottom - scrolled_view_rect.mBottom + mTextRect.mBottom;
// draw the line numbers
- if(line.mLineNum != last_line_num && line.mTop <= scrolled_view_rect.mTop)
+ if(line.mLineNum != last_line_num && line.mRect.mTop <= scrolled_view_rect.mTop)
{
const LLFontGL *num_font = LLFontGL::getFontMonospace();
const LLWString ltext = utf8str_to_wstring(llformat("%d", line.mLineNum ));
@@ -3180,58 +2161,23 @@ void LLTextEditor::drawLineNumbers()
void LLTextEditor::draw()
{
- // reflow if needed, on demand
- reflow();
-
- // then update scroll position, as cursor may have moved
- updateScrollFromCursor();
-
- LLColor4 bg_color = mReadOnly
- ? mReadOnlyBgColor.get()
- : hasFocus()
- ? mFocusBgColor.get()
- : mWriteableBgColor.get();
-
- mDocumentPanel->setBackgroundColor(bg_color);
-
- LLView::draw();
- drawBackground(); //overlays scrolling panel bg
- drawLineNumbers();
-
{
// pad clipping rectangle so that cursor can draw at full width
// when at left edge of mTextRect
LLRect clip_rect(mTextRect);
clip_rect.stretch(1);
LLLocalClipRect clip(clip_rect);
- drawSelectionBackground();
drawPreeditMarker();
- drawText();
- drawCursor();
}
+ LLTextBase::draw();
+ drawLineNumbers();
+
//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);
}
-
-S32 LLTextEditor::getFirstVisibleLine() const
-{
- LLRect visible_region = mScroller->getVisibleContentRect();
-
- // 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();
-}
-
-// virtual
-void LLTextEditor::clear()
-{
- setText(LLStringUtil::null);
-}
-
// Start or stop the editor from accepting text-editing keystrokes
// see also LLLineEditor
void LLTextEditor::setFocus( BOOL new_state )
@@ -3247,7 +2193,7 @@ void LLTextEditor::setFocus( BOOL new_state )
getWindow()->allowLanguageTextInput(this, FALSE);
}
- LLUICtrl::setFocus( new_state );
+ LLTextBase::setFocus( new_state );
if( new_state )
{
@@ -3255,7 +2201,7 @@ void LLTextEditor::setFocus( BOOL new_state )
gEditMenuHandler = this;
// Don't start the cursor flashing right away
- resetKeystrokeTimer();
+ resetCursorBlink();
}
else
{
@@ -3269,96 +2215,6 @@ void LLTextEditor::setFocus( BOOL new_state )
}
}
-// virtual
-BOOL LLTextEditor::acceptsTextInput() const
-{
- return !mReadOnly;
-}
-
-// Given a line (from the start of the doc) and an offset into the line, find the offset (pos) into text.
-S32 LLTextEditor::getPos( S32 line, S32 offset )
-{
- S32 line_start = getLineStart(line);
- S32 next_start = getLineStart(line+1);
- if (next_start == line_start)
- {
- next_start = getLength() + 1;
- }
- S32 line_length = next_start - line_start - 1;
- line_length = llmax(line_length, 0);
- return line_start + llmin( offset, line_length );
-}
-
-
-void LLTextEditor::changePage( S32 delta )
-{
- const S32 PIXEL_OVERLAP_ON_PAGE_CHANGE = 10;
- if (delta == 0) return;
-
- //RN: use pixel heights
- S32 line, offset;
- getLineAndOffset( mCursorPos, &line, &offset );
-
- 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);
- }
-}
-
-void LLTextEditor::changeLine( S32 delta )
-{
- S32 line, offset;
- getLineAndOffset( mCursorPos, &line, &offset );
-
- S32 new_line = line;
- if( (delta < 0) && (line > 0 ) )
- {
- new_line = line - 1;
- }
- else if( (delta > 0) && (line < (getLineCount() - 1)) )
- {
- new_line = line + 1;
- }
-
- LLRect visible_region = mScroller->getVisibleContentRect();
-
- S32 new_cursor_pos = getDocIndexFromLocalCoord(mDesiredXPixel, mLineInfoList[new_line].mBottom + mTextRect.mBottom - visible_region.mBottom, TRUE);
- setCursorPos(new_cursor_pos, true);
-}
-
-
-void LLTextEditor::startOfLine()
-{
- S32 line, offset;
- getLineAndOffset( mCursorPos, &line, &offset );
- setCursorPos(mCursorPos - offset);
-}
-
-
// public
void LLTextEditor::setCursorAndScrollToEnd()
{
@@ -3366,92 +2222,16 @@ void LLTextEditor::setCursorAndScrollToEnd()
endOfDoc();
}
-void LLTextEditor::getLineAndColumnForPosition( S32 position, S32* line, S32* col, BOOL include_wordwrap )
-{
- getLineAndOffset( mCursorPos, line, col, include_wordwrap );
-}
-
void LLTextEditor::getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wordwrap )
{
- getLineAndColumnForPosition(mCursorPos, line, col, include_wordwrap);
-}
-
-S32 LLTextEditor::getCurrentLine()
-{
- return getLineForPosition(mCursorPos);
-}
-
-S32 LLTextEditor::getLineForPosition(S32 position)
-{
- S32 line, col;
- getLineAndColumnForPosition(position, &line, &col, FALSE);
- return line;
-}
-
-
-void LLTextEditor::endOfLine()
-{
- S32 line, offset;
- getLineAndOffset( mCursorPos, &line, &offset );
- S32 num_lines = getLineCount();
- if (line + 1 >= num_lines)
- {
- setCursorPos(getLength());
- }
- else
- {
- setCursorPos( getLineStart(line + 1) - 1 );
- }
-}
-
-void LLTextEditor::startOfDoc()
-{
- setCursorPos(0);
-}
-
-void LLTextEditor::endOfDoc()
-{
- setCursorPos(getLength());
-}
-
-// Sets the scrollbar from the cursor position
-void LLTextEditor::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)
- {
- return;
- }
- mScrollNeeded = FALSE;
-
- S32 line, offset;
- getLineAndOffset( mCursorPos, &line, &offset );
-
- // scroll so that the cursor is at the top of the page
- LLRect scroller_doc_window = mScroller->getVisibleContentRect();
- LLRect cursor_rect_doc = getLocalRectFromDocIndex(mCursorPos);
- cursor_rect_doc.translate(scroller_doc_window.mLeft, scroller_doc_window.mBottom);
- mScroller->scrollToShowRect(cursor_rect_doc, LLRect(0, scroller_doc_window.getHeight() - 5, scroller_doc_window.getWidth(), 5));
-}
-
-void LLTextEditor::reshape(S32 width, S32 height, BOOL called_from_parent)
-{
- LLView::reshape( width, height, called_from_parent );
-
- // do this first after reshape, because other things depend on
- // up-to-date mTextRect
- updateTextRect();
-
- needsReflow();
+ *line = getLineNumFromDocIndex(mCursorPos, include_wordwrap);
+ *col = getLineOffsetFromDocIndex(mCursorPos, include_wordwrap);
}
void LLTextEditor::autoIndent()
{
// Count the number of spaces in the current line
- S32 line, offset;
- getLineAndOffset( mCursorPos, &line, &offset );
+ S32 line = getLineNumFromDocIndex(mCursorPos);
S32 line_start = getLineStart(line);
S32 space_count = 0;
S32 i;
@@ -3496,221 +2276,6 @@ void LLTextEditor::insertText(const std::string &new_text)
setEnabled( enabled );
}
-
-void LLTextEditor::appendColoredText(const std::string &new_text,
- bool allow_undo,
- bool prepend_newline,
- const LLColor4 &color,
- const std::string& font_name)
-{
- LLColor4 lcolor=color;
- if (mParseHighlights)
- {
- LLTextParser* highlight = LLTextParser::getInstance();
- highlight->parseFullLineHighlights(new_text, &lcolor);
- }
-
- LLStyle::Params style_params;
- style_params.color = lcolor;
- if (font_name.empty())
- {
- style_params.font = mDefaultFont;
- }
- else
- {
- style_params.font.name = font_name;
- }
- appendStyledText(new_text, allow_undo, prepend_newline, style_params);
-}
-
-void LLTextEditor::appendStyledText(const std::string &new_text,
- bool allow_undo,
- bool prepend_newline,
- const LLStyle::Params& style_params)
-{
- S32 part = (S32)LLTextParser::WHOLE;
- if(mParseHTML)
- {
-
- S32 start=0,end=0;
- LLUrlMatch match;
- std::string text = new_text;
- while ( LLUrlRegistry::instance().findUrl(text, match,
- boost::bind(&LLTextEditor::onUrlLabelUpdated, this, _1, _2)) )
- {
- start = match.getStart();
- end = match.getEnd()+1;
-
- LLStyle::Params link_params = style_params;
- link_params.color = mLinkColor;
- link_params.font.style = "UNDERLINE";
- link_params.link_href = match.getUrl();
-
- // output the text before the Url
- if (start > 0)
- {
- if (part == (S32)LLTextParser::WHOLE ||
- part == (S32)LLTextParser::START)
- {
- part = (S32)LLTextParser::START;
- }
- else
- {
- part = (S32)LLTextParser::MIDDLE;
- }
- std::string subtext=text.substr(0,start);
- appendHighlightedText(subtext,allow_undo, prepend_newline, part, style_params);
- prepend_newline = false;
- }
-
- // output the styled Url
- appendText(match.getLabel(),allow_undo, prepend_newline, link_params);
- prepend_newline = false;
-
- // set the tooltip for the Url label
- if (! match.getTooltip().empty())
- {
- segment_set_t::iterator it = getSegIterContaining(getLength()-1);
- if (it != mSegments.end())
- {
- LLTextSegmentPtr segment = *it;
- segment->setToolTip(match.getTooltip());
- }
- }
-
- // output an optional icon after the Url
- if (! match.getIcon().empty())
- {
- LLUIImagePtr image = LLUI::getUIImage(match.getIcon());
- if (image)
- {
- LLStyle::Params icon;
- icon.image = image;
- // TODO: fix spacing of images and remove the fixed char spacing
- appendText(" ", allow_undo, prepend_newline, icon);
- }
- }
-
- // 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()) appendHighlightedText(text,allow_undo, prepend_newline, part, style_params);
- }
- else
- {
- appendHighlightedText(new_text, allow_undo, prepend_newline, part, style_params);
- }
-}
-
-void LLTextEditor::appendHighlightedText(const std::string &new_text,
- bool allow_undo,
- bool prepend_newline,
- S32 highlight_part,
- const LLStyle::Params& style_params)
-{
- if (mParseHighlights)
- {
- LLTextParser* highlight = LLTextParser::getInstance();
-
- if (highlight && !style_params.isDefault())
- {
- LLStyle::Params highlight_params = style_params;
-
- LLSD pieces = highlight->parsePartialLineHighlights(new_text, highlight_params.color(), highlight_part);
- bool lprepend=prepend_newline;
- for (S32 i=0;i<pieces.size();i++)
- {
- LLSD color_llsd = pieces[i]["color"];
- LLColor4 lcolor;
- lcolor.setValue(color_llsd);
- highlight_params.color = lcolor;
- if (i != 0 && (pieces.size() > 1) ) lprepend=FALSE;
- appendText((std::string)pieces[i]["text"], allow_undo, lprepend, highlight_params);
- }
- return;
- }
- }
- appendText(new_text, allow_undo, prepend_newline, style_params);
-}
-
-// Appends new text to end of document
-void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool prepend_newline,
- const LLStyle::Params& stylep)
-{
- if (new_text.empty()) return;
-
- // 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 wide_text;
-
- // Add carriage return if not first line
- if (getLength() != 0
- && prepend_newline)
- {
- wide_text = utf8str_to_wstring(std::string("\n") + new_text);
- }
- else
- {
- wide_text = utf8str_to_wstring(new_text);
- }
-
- LLTextSegmentPtr segmentp;
- if (!stylep.isDefault())
- {
- S32 segment_start = old_length;
- S32 segment_end = old_length + wide_text.size();
- segmentp = new LLNormalTextSegment(new LLStyle(stylep), segment_start, segment_end, *this );
- }
-
- append(wide_text, TRUE, segmentp);
-
- needsReflow();
-
- // 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::appendWidget(LLView* widget, const std::string &widget_text, bool allow_undo, bool prepend_newline)
{
// Save old state
@@ -3739,7 +2304,7 @@ void LLTextEditor::appendWidget(LLView* widget, const std::string &widget_text,
}
LLTextSegmentPtr segment = new LLInlineViewSegment(widget, old_length, old_length + widget_text.size());
- append(widget_wide_text, FALSE, segment);
+ insert(getLength(), widget_wide_text, FALSE, segment);
needsReflow();
@@ -3767,12 +2332,6 @@ void LLTextEditor::appendWidget(LLView* widget, const std::string &widget_text,
}
}
-void LLTextEditor::onUrlLabelUpdated(const std::string &url,
- const std::string &label)
-{
- // LLUrlRegistry has given us a new label for one of our Urls
- replaceUrlLabel(url, label);
-}
void LLTextEditor::replaceUrlLabel(const std::string &url,
const std::string &label)
@@ -3830,164 +2389,10 @@ void LLTextEditor::removeTextFromEnd(S32 num_chars)
mSelectionStart = llclamp(mSelectionStart, 0, len);
mSelectionEnd = llclamp(mSelectionEnd, 0, len);
- reflow();
+ needsReflow();
needsScroll();
}
-///////////////////////////////////////////////////////////////////
-// Returns change in number of characters in mWText
-
-S32 LLTextEditor::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextEditor::segment_vec_t* segments )
-{
- LLWString text(getWText());
- S32 old_len = text.length(); // length() returns character length
- S32 insert_len = wstr.length();
-
- pos = getEditableIndex(pos, true);
-
- segment_set_t::iterator seg_iter = getSegIterContaining(pos);
-
- LLTextSegmentPtr default_segment;
-
- LLTextSegmentPtr segmentp;
- if (seg_iter != mSegments.end())
- {
- segmentp = *seg_iter;
- }
- else
- {
- //segmentp = mSegments.back();
- return pos;
- }
-
- if (segmentp->canEdit())
- {
- segmentp->setEnd(segmentp->getEnd() + insert_len);
- if (seg_iter != mSegments.end())
- {
- ++seg_iter;
- }
- }
- else
- {
- // create default editable segment to hold new text
- default_segment = new LLNormalTextSegment( getDefaultStyle(), pos, pos + insert_len, *this);
- }
-
- // shift remaining segments to right
- for(;seg_iter != mSegments.end(); ++seg_iter)
- {
- LLTextSegmentPtr segmentp = *seg_iter;
- segmentp->setStart(segmentp->getStart() + insert_len);
- segmentp->setEnd(segmentp->getEnd() + insert_len);
- }
-
- // insert new segments
- if (segments)
- {
- if (default_segment.notNull())
- {
- // potentially overwritten by segments passed in
- insertSegment(default_segment);
- }
- for (segment_vec_t::iterator seg_iter = segments->begin();
- seg_iter != segments->end();
- ++seg_iter)
- {
- LLTextSegment* segmentp = *seg_iter;
- insertSegment(segmentp);
- }
- }
-
- text.insert(pos, wstr);
- getViewModel()->setDisplay(text);
-
- if ( truncate() )
- {
- // The user's not getting everything he's hoping for
- make_ui_sound("UISndBadKeystroke");
- insert_len = getLength() - old_len;
- }
-
- onValueChange(pos, pos + insert_len);
-
- return insert_len;
-}
-
-S32 LLTextEditor::removeStringNoUndo(S32 pos, S32 length)
-{
- LLWString text(getWText());
- segment_set_t::iterator seg_iter = getSegIterContaining(pos);
- while(seg_iter != mSegments.end())
- {
- LLTextSegmentPtr segmentp = *seg_iter;
- S32 end = pos + length;
- if (segmentp->getStart() < pos)
- {
- // deleting from middle of segment
- if (segmentp->getEnd() > end)
- {
- segmentp->setEnd(segmentp->getEnd() - length);
- }
- // truncating segment
- else
- {
- segmentp->setEnd(pos);
- }
- }
- else if (segmentp->getStart() < end)
- {
- // deleting entire segment
- if (segmentp->getEnd() <= end)
- {
- // remove segment
- segmentp->unlinkFromDocument(this);
- segment_set_t::iterator seg_to_erase(seg_iter++);
- mSegments.erase(seg_to_erase);
- continue;
- }
- // deleting head of segment
- else
- {
- segmentp->setStart(pos);
- segmentp->setEnd(segmentp->getEnd() - length);
- }
- }
- else
- {
- // shifting segments backward to fill deleted portion
- segmentp->setStart(segmentp->getStart() - length);
- segmentp->setEnd(segmentp->getEnd() - length);
- }
- ++seg_iter;
- }
-
- text.erase(pos, length);
- getViewModel()->setDisplay(text);
-
- // recreate default segment in case we erased everything
- createDefaultSegment();
-
- onValueChange(pos, pos);
-
- return -length; // This will be wrong if someone calls removeStringNoUndo with an excessive length
-}
-
-S32 LLTextEditor::overwriteCharNoUndo(S32 pos, llwchar wc)
-{
- if (pos > (S32)getLength())
- {
- return 0;
- }
- LLWString text(getWText());
- text[pos] = wc;
- getViewModel()->setDisplay(text);
-
- onValueChange(pos, pos + 1);
-
- return 1;
-}
-
//----------------------------------------------------------------------------
void LLTextEditor::makePristine()
@@ -4051,29 +2456,13 @@ BOOL LLTextEditor::tryToRevertToPristineState()
}
-void LLTextEditor::updateTextRect()
-{
- static LLUICachedControl<S32> texteditor_border ("UITextEditorBorder", 0);
- static LLUICachedControl<S32> texteditor_h_pad ("UITextEditorHPad", 0);
-
- LLRect old_text_rect = mTextRect;
- mTextRect = mScroller->getContentWindowRect();
- mTextRect.stretch(texteditor_border * -1);
- mTextRect.mLeft += texteditor_h_pad;
- mTextRect.mLeft += mShowLineNumbers ? UI_TEXTEDITOR_LINE_NUMBER_MARGIN : 0;
- if (mTextRect != old_text_rect)
- {
- needsReflow();
- }
-}
-
-LLFastTimer::DeclareTimer FTM_TEXT_EDITOR_LOAD_KEYWORD("Text Editor Load Keywords");
+static LLFastTimer::DeclareTimer FTM_SYNTAX_HIGHLIGHTING("Syntax Highlighting");
void LLTextEditor::loadKeywords(const std::string& filename,
const std::vector<std::string>& funcs,
const std::vector<std::string>& tooltips,
const LLColor3& color)
{
- LLFastTimer ft(FTM_TEXT_EDITOR_LOAD_KEYWORD);
+ LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING);
if(mKeywords.loadFromFile(filename))
{
S32 count = llmin(funcs.size(), tooltips.size());
@@ -4094,27 +2483,9 @@ void LLTextEditor::loadKeywords(const std::string& filename,
}
}
-void LLTextEditor::createDefaultSegment()
-{
- // ensures that there is always at least one segment
- if (mSegments.empty())
- {
- LLTextSegmentPtr default_segment = new LLNormalTextSegment( getDefaultStyle(), 0, getLength() + 1, *this);
- mSegments.insert(default_segment);
- default_segment->linkToDocument(this);
- }
-}
-
-LLStyleSP LLTextEditor::getDefaultStyle()
-{
- LLColor4 text_color = ( mReadOnly ? mReadOnlyFgColor.get() : mFgColor.get() );
- return LLStyleSP(new LLStyle(LLStyle::Params().color(text_color).font(mDefaultFont)));
-}
-
-LLFastTimer::DeclareTimer FTM_UPDATE_TEXT_SEGMENTS("Update Text Segments");
void LLTextEditor::updateSegments()
{
- LLFastTimer ft(FTM_UPDATE_TEXT_SEGMENTS);
+ LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING);
if (mKeywords.isLoaded())
{
// HACK: No non-ascii keywords for now
@@ -4125,11 +2496,11 @@ void LLTextEditor::updateSegments()
segment_set_t::iterator insert_it = mSegments.begin();
for (segment_vec_t::iterator list_it = segment_list.begin(); list_it != segment_list.end(); ++list_it)
{
- insert_it = mSegments.insert(insert_it, *list_it);
+ insertSegment(*list_it);
}
}
- createDefaultSegment();
+ LLTextBase::updateSegments();
}
void LLTextEditor::updateLinkSegments()
@@ -4155,66 +2526,7 @@ void LLTextEditor::updateLinkSegments()
}
}
-void LLTextEditor::insertSegment(LLTextSegmentPtr segment_to_insert)
-{
- if (segment_to_insert.isNull())
- {
- return;
- }
-
- segment_set_t::iterator cur_seg_iter = getSegIterContaining(segment_to_insert->getStart());
- if (cur_seg_iter == mSegments.end())
- {
- mSegments.insert(segment_to_insert);
- segment_to_insert->linkToDocument(this);
- }
- else
- {
- LLTextSegmentPtr cur_segmentp = *cur_seg_iter;
- 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
- ++cur_seg_iter;
- // insert remainder of old segment
- LLTextSegmentPtr remainder_segment = new LLNormalTextSegment( cur_segmentp->getStyle(), segment_to_insert->getStart(), old_segment_end, *this);
- cur_seg_iter = mSegments.insert(cur_seg_iter, remainder_segment);
- remainder_segment->linkToDocument(this);
- // insert new segment before remainder of old segment
- cur_seg_iter = mSegments.insert(cur_seg_iter, segment_to_insert);
-
- segment_to_insert->linkToDocument(this);
- // move to "remanider" segment and start truncation there
- ++cur_seg_iter;
- }
- else
- {
- cur_seg_iter = mSegments.insert(cur_seg_iter, segment_to_insert);
- ++cur_seg_iter;
- segment_to_insert->linkToDocument(this);
- }
-
- // now delete/truncate remaining segments as necessary
- while(cur_seg_iter != mSegments.end())
- {
- cur_segmentp = *cur_seg_iter;
- if (cur_segmentp->getEnd() <= segment_to_insert->getEnd())
- {
- cur_segmentp->unlinkFromDocument(this);
- segment_set_t::iterator seg_to_erase(cur_seg_iter++);
- mSegments.erase(seg_to_erase);
- }
- else
- {
- cur_segmentp->setStart(segment_to_insert->getEnd());
- break;
- }
- }
- }
-}
void LLTextEditor::onMouseCaptureLost()
{
@@ -4400,7 +2712,7 @@ void LLTextEditor::updatePreedit(const LLWString &preedit_string,
if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
{
- mPreeditOverwrittenWString = getWSubString(insert_preedit_at, mPreeditWString.length());
+ mPreeditOverwrittenWString = getWText().substr(insert_preedit_at, mPreeditWString.length());
removeStringNoUndo(insert_preedit_at, mPreeditWString.length());
}
else
@@ -4415,7 +2727,7 @@ void LLTextEditor::updatePreedit(const LLWString &preedit_string,
setCursorPos(insert_preedit_at + caret_position);
// Update of the preedit should be caused by some key strokes.
- mKeystrokeTimer.reset();
+ resetCursorBlink();
onKeyStroke();
}
@@ -4578,93 +2890,6 @@ S32 LLTextEditor::getPreeditFontSize() const
return llround(mDefaultFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]);
}
-LLWString LLTextEditor::getWText() const
-{
- return getViewModel()->getDisplay();
-}
-
-void LLTextEditor::onValueChange(S32 start, S32 end)
-{
-}
-
-//
-// LLInlineViewSegment
-//
-
-LLInlineViewSegment::LLInlineViewSegment(LLView* view, S32 start, S32 end)
-: LLTextSegment(start, end),
- mView(view)
-{
-}
-
-LLInlineViewSegment::~LLInlineViewSegment()
-{
- mView->die();
-}
-
-S32 LLInlineViewSegment::getWidth(S32 first_char, S32 num_chars) const
-{
- if (first_char == 0 && num_chars == 0)
- {
- return 0;
- }
- else
- {
- return mView->getRect().getWidth();
- }
-}
-
-S32 LLInlineViewSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const
-{
- if (line_offset != 0 && num_pixels < mView->getRect().getWidth())
- {
- return 0;
- }
- else
- {
- return mEnd - mStart;
- }
-}
-
-void LLInlineViewSegment::updateLayout(const LLTextBase& editor)
-{
- const LLTextEditor *ed = dynamic_cast<const LLTextEditor *>(&editor);
- if (ed)
- {
- LLRect start_rect = ed->getLocalRectFromDocIndex(mStart);
- LLRect doc_rect = ed->getDocumentPanel()->getRect();
- mView->setOrigin(doc_rect.mLeft + start_rect.mLeft, doc_rect.mBottom + start_rect.mBottom);
- }
-}
-
-F32 LLInlineViewSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect)
-{
- return (F32)(draw_rect.mLeft + mView->getRect().getWidth());
-}
-
-S32 LLInlineViewSegment::getMaxHeight() const
-{
- return mView->getRect().getHeight();
-}
-
-void LLInlineViewSegment::unlinkFromDocument(LLTextBase* editor)
-{
- LLTextEditor *ed = dynamic_cast<LLTextEditor *>(editor);
- if (ed)
- {
- ed->removeDocumentChild(mView);
- }
-}
-
-void LLInlineViewSegment::linkToDocument(LLTextBase* editor)
-{
- LLTextEditor *ed = dynamic_cast<LLTextEditor *>(editor);
- if (ed)
- {
- ed->addDocumentChild(mView);
- }
-}
-
BOOL LLTextEditor::isDirty() const
{
if(mReadOnly)
diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h
index a04261c4be..0e5707a3a6 100644
--- a/indra/llui/lltexteditor.h
+++ b/indra/llui/lltexteditor.h
@@ -37,7 +37,6 @@
#include "llrect.h"
#include "llkeywords.h"
-#include "lluictrl.h"
#include "llframetimer.h"
#include "lldarray.h"
#include "llstyle.h"
@@ -52,66 +51,27 @@
class LLFontGL;
class LLScrollbar;
class LLKeywordToken;
-class LLTextCmd;
+class TextCmd;
class LLUICtrlFactory;
class LLScrollContainer;
-class LLInlineViewSegment : public LLTextSegment
-{
-public:
- LLInlineViewSegment(LLView* widget, S32 start, S32 end);
- ~LLInlineViewSegment();
- /*virtual*/ S32 getWidth(S32 first_char, S32 num_chars) const;
- /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const;
- /*virtual*/ void updateLayout(const class LLTextBase& editor);
- /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect);
- /*virtuaL*/ S32 getMaxHeight() const;
- /*virtual*/ bool canEdit() const { return false; }
- /*virtual*/ void unlinkFromDocument(class LLTextBase* editor);
- /*virtual*/ void linkToDocument(class LLTextBase* editor);
-
-private:
- LLView* mView;
-};
-
class LLTextEditor :
public LLTextBase,
- public LLUICtrl,
- private LLEditMenuHandler,
protected LLPreeditor
{
public:
- struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+ struct Params : public LLInitParam::Block<Params, LLTextBase::Params>
{
Optional<std::string> default_text;
- Optional<S32> max_text_length;
- Optional<bool> read_only,
- embedded_items,
- word_wrap,
+ Optional<bool> embedded_items,
ignore_tab,
- hide_border,
- track_bottom,
handle_edit_keys_directly,
show_line_numbers,
commit_on_focus_lost;
//colors
- Optional<LLUIColor> cursor_color,
- default_color,
- text_color,
- text_readonly_color,
- bg_readonly_color,
- bg_writeable_color,
- bg_focus_color,
- link_color;
-
- Optional<LLViewBorder::Params> border;
-
- Ignored type,
- length,
- is_unicode,
- hide_scrollbar;
+ Optional<LLUIColor> default_color;
Params();
};
@@ -128,15 +88,6 @@ public:
static const llwchar LAST_EMBEDDED_CHAR = 0x10ffff;
static const S32 MAX_EMBEDDED_ITEMS = LAST_EMBEDDED_CHAR - FIRST_EMBEDDED_CHAR + 1;
-
- struct compare_segment_end
- {
- bool operator()(const LLTextSegmentPtr& a, const LLTextSegmentPtr& b) const
- {
- return a->getEnd() < b->getEnd();
- }
- };
-
virtual ~LLTextEditor();
typedef boost::signals2::signal<void (LLTextEditor* caller)> keystroke_signal_t;
@@ -155,14 +106,9 @@ public:
virtual BOOL handleKeyHere(KEY key, MASK mask );
virtual BOOL handleUnicodeCharHere(llwchar uni_char);
- virtual BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect);
- virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
- EDragAndDropType cargo_type, void *cargo_data,
- EAcceptance *accept, std::string& tooltip_msg);
virtual void onMouseCaptureLost();
// view overrides
- virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
virtual void draw();
virtual void onFocusReceived();
virtual void onFocusLost();
@@ -170,11 +116,8 @@ public:
virtual void setEnabled(BOOL enabled);
// uictrl overrides
- virtual void clear();
virtual void setFocus( BOOL b );
- virtual BOOL acceptsTextInput() const;
virtual BOOL isDirty() const;
- virtual void setValue(const LLSD& value);
// LLEditMenuHandler interface
virtual void undo();
@@ -201,12 +144,9 @@ public:
virtual void deselect();
virtual BOOL canDeselect() const;
- virtual void onValueChange(S32 start, S32 end);
-
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);
- BOOL hasSelection() const { return (mSelectionStart !=mSelectionEnd); }
void replaceUrlLabel(const std::string &url, const std::string &label);
// Undo/redo stack
@@ -216,9 +156,6 @@ public:
virtual void makePristine();
BOOL isPristine() const;
BOOL allowsEmbeddedItems() const { return mAllowEmbeddedItems; }
- S32 getLength() const { return getWText().length(); }
- void setReadOnly(bool read_only) { mReadOnly = read_only; }
- bool getReadOnly() { return mReadOnly; }
//
// Text manipulation
@@ -226,25 +163,10 @@ public:
// inserts text at cursor
void insertText(const std::string &text);
- // appends text at end
- void appendText(const std::string &wtext, bool allow_undo, bool prepend_newline,
- const LLStyle::Params& style = LLStyle::Params());
-
- void appendColoredText(const std::string &wtext, bool allow_undo,
- bool prepend_newline,
- const LLColor4 &color,
- const std::string& font_name = LLStringUtil::null);
- // if styled text starts a line, you need to prepend a newline.
- void appendStyledText(const std::string &new_text, bool allow_undo,
- bool prepend_newline,
- const LLStyle::Params& style);
- void appendHighlightedText(const std::string &new_text, bool allow_undo,
- bool prepend_newline, S32 highlight_part,
- const LLStyle::Params& style);
+
void appendWidget(LLView* widget, const std::string &widget_text, bool allow_undo, bool prepend_newline);
// Non-undoable
void setText(const LLStringExplicit &utf8str);
- void setWText(const LLWString &wtext);
// Removes text from the end of document
@@ -253,14 +175,9 @@ public:
BOOL tryToRevertToPristineState();
- bool setCursor(S32 row, S32 column);
- bool setCursorPos(S32 offset, bool keep_cursor_offset = false);
void setCursorAndScrollToEnd();
- void getLineAndColumnForPosition( S32 position, S32* line, S32* col, BOOL include_wordwrap );
void getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wordwrap );
- S32 getLineForPosition(S32 position);
- S32 getCurrentLine();
void loadKeywords(const std::string& filename,
const std::vector<std::string>& funcs,
@@ -277,55 +194,17 @@ public:
virtual BOOL importBuffer(const char* buffer, S32 length );
virtual BOOL exportBuffer(std::string& buffer );
- const class DocumentPanel* getDocumentPanel() const { return mDocumentPanel; }
-
const LLUUID& getSourceID() const { return mSourceID; }
- // Callbacks
- std::string getText() const;
-
- // Callback for when a Url has been resolved by the server
- void onUrlLabelUpdated(const std::string &url, const std::string &label);
-
- // Getters
- LLWString getWText() const;
- llwchar getWChar(S32 pos) const { return getWText()[pos]; }
- LLWString getWSubString(S32 pos, S32 len) const { return getWText().substr(pos, len); }
-
- typedef std::vector<LLTextSegmentPtr> segment_vec_t;
-
const LLTextSegmentPtr getPreviousSegment() const;
void getSelectedSegments(segment_vec_t& segments) const;
- void getSegmentsInRange(segment_vec_t& segments, S32 start, S32 end, bool include_partial) const;
- LLRect getLocalRectFromDocIndex(S32 index) const;
-
- void addDocumentChild(LLView* view);
- void removeDocumentChild(LLView* view);
-
protected:
- // Change cursor
- void startOfLine();
- void endOfLine();
- void startOfDoc();
- void endOfDoc();
-
void drawPreeditMarker();
- void needsReflow() { mReflowNeeded = TRUE; }
- void needsScroll() { mScrollNeeded = TRUE; }
- void updateCursorXPos();
-
- void updateScrollFromCursor();
- void updateTextRect();
- const LLRect& getTextRect() const { return mTextRect; }
-
void assignEmbedded(const std::string &s);
- BOOL truncate(); // Returns true if truncation occurs
void removeCharOrTab();
- void setCursorAtLocalPos(S32 x, S32 y, bool round, bool keep_cursor_offset = false);
- /*virtual*/ S32 getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const;
void indentSelectedLines( S32 spaces );
S32 indentLine( S32 pos, S32 spaces );
@@ -340,68 +219,21 @@ protected:
BOOL handleEditKey(const KEY key, const MASK mask);
BOOL selectionContainsLineBreaks();
- void startSelection();
- void endSelection();
void deleteSelection(BOOL transient_operation);
S32 prevWordPos(S32 cursorPos) const;
S32 nextWordPos(S32 cursorPos) const;
- S32 getLineCount() const { return mLineInfoList.size(); }
- S32 getLineStart( S32 line ) const;
- S32 getLineHeight( S32 line ) const;
- void getLineAndOffset(S32 pos, S32* linep, S32* offsetp, bool include_wordwrap = true) const;
- S32 getPos(S32 line, S32 offset);
-
- void changePage(S32 delta);
- void changeLine(S32 delta);
-
void autoIndent();
void findEmbeddedItemSegments(S32 start, S32 end);
- void insertSegment(LLTextSegmentPtr segment_to_insert);
-
+ void getSegmentsInRange(segment_vec_t& segments, S32 start, S32 end, bool include_partial) const;
+
virtual llwchar pasteEmbeddedItem(llwchar ext_char) { return ext_char; }
- // Abstract inner base class representing an undoable editor command.
- // Concrete sub-classes can be defined for operations such as insert, remove, etc.
- // Used as arguments to the execute() method below.
- class LLTextCmd
- {
- public:
- LLTextCmd( S32 pos, BOOL group_with_next, LLTextSegmentPtr segment = LLTextSegmentPtr() )
- : mPos(pos),
- mGroupWithNext(group_with_next)
- {
- if (segment.notNull())
- {
- mSegments.push_back(segment);
- }
- }
- virtual ~LLTextCmd() {}
- virtual BOOL execute(LLTextEditor* editor, S32* delta) = 0;
- virtual S32 undo(LLTextEditor* editor) = 0;
- virtual S32 redo(LLTextEditor* editor) = 0;
- virtual BOOL canExtend(S32 pos) const { return FALSE; }
- virtual void blockExtensions() {}
- virtual BOOL extendAndExecute( LLTextEditor* editor, S32 pos, llwchar c, S32* delta ) { llassert(0); return 0; }
- virtual BOOL hasExtCharValue( llwchar value ) const { return FALSE; }
-
- // Defined here so they can access protected LLTextEditor editing methods
- S32 insert(LLTextEditor* editor, S32 pos, const LLWString &wstr) { return editor->insertStringNoUndo( pos, wstr, &mSegments ); }
- S32 remove(LLTextEditor* editor, S32 pos, S32 length) { return editor->removeStringNoUndo( pos, length ); }
- S32 overwrite(LLTextEditor* editor, S32 pos, llwchar wc) { return editor->overwriteCharNoUndo(pos, wc); }
-
- S32 getPosition() const { return mPos; }
- BOOL groupWithNext() const { return mGroupWithNext; }
-
- protected:
- const S32 mPos;
- BOOL mGroupWithNext;
- segment_vec_t mSegments;
- };
+
// Here's the method that takes and applies text commands.
- S32 execute(LLTextCmd* cmd);
+ S32 execute(TextCmd* cmd);
// Undoable operations
void addChar(llwchar c); // at mCursorPos
@@ -411,15 +243,7 @@ protected:
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);
- S32 append(const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment);
- // Direct operations
- 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 resetKeystrokeTimer() { mKeystrokeTimer.reset(); }
-
void updateAllowingLanguageInput();
BOOL hasPreeditString() const;
@@ -432,6 +256,7 @@ protected:
virtual void getSelectionRange(S32 *position, S32 *length) const;
virtual BOOL getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const;
virtual S32 getPreeditFontSize() const;
+ virtual LLWString getPreeditString() const { return getWText(); }
//
// Protected data
//
@@ -439,74 +264,31 @@ protected:
// as possible behind protected accessor methods.
//
- // I-beam is just after the mCursorPos-th character.
- S32 mCursorPos;
-
// Use these to determine if a click on an embedded item is a drag or not.
S32 mMouseDownX;
S32 mMouseDownY;
- // Are we in the middle of a drag-select? To figure out if there is a current
- // selection, call hasSelection().
- BOOL mIsSelecting;
- S32 mSelectionStart;
- S32 mSelectionEnd;
- S32 mLastSelectionX;
- S32 mLastSelectionY;
-
- BOOL mParseHighlights;
-
- // Scrollbar data
- class DocumentPanel* mDocumentPanel;
- LLScrollContainer* mScroller;
-
- void *mOnScrollEndData;
-
LLWString mPreeditWString;
LLWString mPreeditOverwrittenWString;
std::vector<S32> mPreeditPositions;
std::vector<BOOL> mPreeditStandouts;
- S32 mScrollIndex; // index into document that controls default scroll position
-
protected:
- LLUIColor mCursorColor;
- LLUIColor mFgColor;
- LLUIColor mDefaultColor;
- LLUIColor mReadOnlyFgColor;
- LLUIColor mWriteableBgColor;
- LLUIColor mReadOnlyBgColor;
- LLUIColor mFocusBgColor;
- LLUIColor mLinkColor;
+ LLUIColor mDefaultColor;
- BOOL mReadOnly;
- BOOL mShowLineNumbers;
+ BOOL mShowLineNumbers;
- void updateSegments();
- void updateLinkSegments();
+ /*virtual*/ void updateSegments();
+ void updateLinkSegments();
private:
-
//
// Methods
//
void pasteHelper(bool is_primary);
- virtual LLTextViewModel* getViewModel() const;
- void reflow(S32 startpos = 0);
-
- void createDefaultSegment();
- LLStyleSP getDefaultStyle();
- S32 getEditableIndex(S32 index, bool increasing_direction);
-
- void drawBackground();
- void drawSelectionBackground();
- void drawCursor();
- void drawText();
void drawLineNumbers();
- S32 getFirstVisibleLine() const;
-
void onKeyStroke();
//
@@ -514,56 +296,25 @@ private:
//
LLKeywords mKeywords;
- // Concrete LLTextCmd sub-classes used by the LLTextEditor base class
- class LLTextCmdInsert;
- class LLTextCmdAddChar;
- class LLTextCmdOverwriteChar;
- class LLTextCmdRemove;
-
- S32 mMaxTextByteLength; // Maximum length mText is allowed to be in bytes
+ // Concrete TextCmd sub-classes used by the LLTextEditor base class
+ class TextCmdInsert;
+ class TextCmdAddChar;
+ class TextCmdOverwriteChar;
+ class TextCmdRemove;
class LLViewBorder* mBorder;
BOOL mBaseDocIsPristine;
- LLTextCmd* mPristineCmd;
+ TextCmd* mPristineCmd;
- LLTextCmd* mLastCmd;
+ TextCmd* mLastCmd;
- typedef std::deque<LLTextCmd*> undo_stack_t;
+ typedef std::deque<TextCmd*> undo_stack_t;
undo_stack_t mUndoStack;
- S32 mDesiredXPixel; // X pixel position where the user wants the cursor to be
- LLRect mTextRect; // The rect in which text is drawn. Excludes borders.
- // List of offsets and segment index of the start of each line. Always has at least one node (0).
- struct line_info
- {
- line_info(S32 index_start, S32 index_end, S32 top, S32 bottom, S32 line_num)
- : mDocIndexStart(index_start),
- mDocIndexEnd(index_end),
- mTop(top),
- mBottom(bottom),
- mLineNum(line_num)
- {}
- S32 mDocIndexStart;
- S32 mDocIndexEnd;
- S32 mTop;
- S32 mBottom;
- S32 mLineNum; // actual line count (ignoring soft newlines due to word wrap)
- };
- struct compare_bottom;
- struct compare_top;
- struct line_end_compare;
- typedef std::vector<line_info> line_list_t;
- line_list_t mLineInfoList;
- BOOL mReflowNeeded;
- BOOL mScrollNeeded;
-
- LLFrameTimer mKeystrokeTimer;
-
BOOL mTabsToNextField; // if true, tab moves focus to next field, else inserts spaces
BOOL mCommitOnFocusLost;
BOOL mTakesFocus;
- BOOL mTrackBottom; // if true, keeps scroll position at bottom during resize
BOOL mAllowEmbeddedItems;
diff --git a/indra/llui/lltextparser.cpp b/indra/llui/lltextparser.cpp
index 707dd0afdd..76a39e3094 100644
--- a/indra/llui/lltextparser.cpp
+++ b/indra/llui/lltextparser.cpp
@@ -103,7 +103,7 @@ S32 LLTextParser::findPattern(const std::string &text, LLSD highlight)
return found;
}
-LLSD LLTextParser::parsePartialLineHighlights(const std::string &text, const LLColor4 &color, S32 part, S32 index)
+LLSD LLTextParser::parsePartialLineHighlights(const std::string &text, const LLColor4 &color, EHighlightPosition part, S32 index)
{
//evil recursive string atomizer.
LLSD ret_llsd, start_llsd, middle_llsd, end_llsd;
@@ -122,7 +122,7 @@ LLSD LLTextParser::parsePartialLineHighlights(const std::string &text, const LLC
{
S32 end = std::string(mHighlights[i]["pattern"]).length();
S32 len = text.length();
- S32 newpart;
+ EHighlightPosition newpart;
if (start==0)
{
start_llsd[0]["text"] =text.substr(0,end);
diff --git a/indra/llui/lltextparser.h b/indra/llui/lltextparser.h
index fb1a7758b7..072ac0f300 100644
--- a/indra/llui/lltextparser.h
+++ b/indra/llui/lltextparser.h
@@ -34,8 +34,6 @@
#ifndef LL_LLTEXTPARSER_H
#define LL_LLTEXTPARSER_H
-#include "lltextparser.h"
-
#include "llsd.h"
class LLUUID;
@@ -45,17 +43,17 @@ class LLColor4;
class LLTextParser
{
public:
- enum ConditionType { CONTAINS, MATCHES, STARTS_WITH, ENDS_WITH };
- enum HighlightType { PART, ALL };
- enum HighlightPosition { WHOLE, START, MIDDLE, END };
- enum DialogAction { ACTION_NONE, ACTION_CLOSE, ACTION_ADD, ACTION_COPY, ACTION_UPDATE };
+ typedef enum e_condition_type { CONTAINS, MATCHES, STARTS_WITH, ENDS_WITH } EConditionType;
+ typedef enum e_highlight_type { PART, ALL } EHighlightType;
+ typedef enum e_highlight_position { WHOLE, START, MIDDLE, END } EHighlightPosition;
+ typedef enum e_dialog_action { ACTION_NONE, ACTION_CLOSE, ACTION_ADD, ACTION_COPY, ACTION_UPDATE } EDialogAction;
static LLTextParser* getInstance();
LLTextParser(){};
~LLTextParser();
S32 findPattern(const std::string &text, LLSD highlight);
- LLSD parsePartialLineHighlights(const std::string &text,const LLColor4 &color,S32 part=WHOLE, S32 index=0);
+ LLSD parsePartialLineHighlights(const std::string &text,const LLColor4 &color, EHighlightPosition part=WHOLE, S32 index=0);
bool parseFullLineHighlights(const std::string &text, LLColor4 *color);
std::string getFileName();
diff --git a/indra/llui/lltoggleablemenu.cpp b/indra/llui/lltoggleablemenu.cpp
new file mode 100644
index 0000000000..717e135412
--- /dev/null
+++ b/indra/llui/lltoggleablemenu.cpp
@@ -0,0 +1,82 @@
+/**
+ * @file lltoggleablemenu.cpp
+ * @brief Menu toggled by a button press
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ *
+ * Copyright (c) 2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+
+#include "linden_common.h"
+
+#include "lltoggleablemenu.h"
+#include "lluictrlfactory.h"
+
+static LLDefaultChildRegistry::Register<LLToggleableMenu> r("toggleable_menu");
+
+LLToggleableMenu::LLToggleableMenu(const LLToggleableMenu::Params& p)
+: LLMenuGL(p),
+ mClosedByButtonClick(false)
+{
+}
+
+// virtual
+void LLToggleableMenu::handleVisibilityChange (BOOL curVisibilityIn)
+{
+ S32 x,y;
+ LLUI::getMousePositionLocal(LLUI::getRootView(), &x, &y);
+
+ if (!curVisibilityIn && mButtonRect.pointInRect(x, y))
+ {
+ mClosedByButtonClick = true;
+ }
+}
+
+void LLToggleableMenu::setButtonRect(const LLRect& rect, LLView* current_view)
+{
+ LLRect screen;
+ current_view->localRectToScreen(rect, &screen);
+ mButtonRect = screen;
+}
+
+bool LLToggleableMenu::toggleVisibility()
+{
+ if (mClosedByButtonClick)
+ {
+ mClosedByButtonClick = false;
+ return false;
+ }
+
+ if (getVisible())
+ {
+ setVisible(FALSE);
+ mClosedByButtonClick = false;
+ return false;
+ }
+
+ return true;
+}
diff --git a/indra/llui/lltoggleablemenu.h b/indra/llui/lltoggleablemenu.h
new file mode 100644
index 0000000000..3cd66e04a8
--- /dev/null
+++ b/indra/llui/lltoggleablemenu.h
@@ -0,0 +1,65 @@
+/**
+ * @file lltoggleablemenu.h
+ * @brief Menu toggled by a button press
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ *
+ * Copyright (c) 2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTOGGLEABLEMENU_H
+#define LL_LLTOGGLEABLEMENU_H
+
+#include "llmenugl.h"
+
+class LLToggleableMenu : public LLMenuGL
+{
+public:
+ //adding blank params to work around registration issue
+ //where LLToggleableMenu was owning the LLMenuGL param
+ //and menu.xml was never loaded
+ struct Params : public LLInitParam::Block<Params, LLMenuGL::Params>
+ {};
+protected:
+ LLToggleableMenu(const Params&);
+ friend class LLUICtrlFactory;
+public:
+ virtual void handleVisibilityChange (BOOL curVisibilityIn);
+
+ // Converts the given local button rect to a screen rect
+ void setButtonRect(const LLRect& rect, LLView* current_view);
+
+ // Returns "true" if menu was not closed by button click
+ // and is not still visible. If menu is visible toggles
+ // its visibility off.
+ bool toggleVisibility();
+
+protected:
+ bool mClosedByButtonClick;
+ LLRect mButtonRect;
+};
+
+#endif // LL_LLTOGGLEABLEMENU_H
diff --git a/indra/llui/lltooltip.cpp b/indra/llui/lltooltip.cpp
index 5c017dabd7..8f5c029816 100644
--- a/indra/llui/lltooltip.cpp
+++ b/indra/llui/lltooltip.cpp
@@ -36,7 +36,6 @@
#include "lltooltip.h"
// Library includes
-#include "llpanel.h"
#include "lltextbox.h"
#include "lliconctrl.h"
#include "llui.h" // positionViewNearMouse()
@@ -45,7 +44,6 @@
//
// Constants
//
-const F32 DELAY_BEFORE_SHOW_TIP = 0.35f;
//
// Local globals
@@ -57,6 +55,11 @@ LLToolTipView *gToolTipView = NULL;
// Member functions
//
+LLToolTipView::Params::Params()
+{
+ mouse_opaque = false;
+}
+
LLToolTipView::LLToolTipView(const LLToolTipView::Params& p)
: LLView(p)
{
@@ -64,10 +67,7 @@ LLToolTipView::LLToolTipView(const LLToolTipView::Params& p)
void LLToolTipView::draw()
{
- if (LLUI::getWindow()->isCursorHidden() )
- {
- LLToolTipMgr::instance().hideToolTips();
- }
+ LLToolTipMgr::instance().updateToolTipVisibility();
// do the usual thing
LLView::draw();
@@ -80,17 +80,10 @@ BOOL LLToolTipView::handleHover(S32 x, S32 y, MASK mask)
LLToolTipMgr& tooltip_mgr = LLToolTipMgr::instance();
- // hide existing tooltips when mouse moves out of sticky rect
- if (tooltip_mgr.toolTipVisible()
- && !tooltip_mgr.getStickyRect().pointInRect(x, y))
- {
- tooltip_mgr.hideToolTips();
- }
-
- // allow new tooltips whenever mouse moves
if (x != last_x && y != last_y)
{
- tooltip_mgr.enableToolTips();
+ // allow new tooltips because mouse moved
+ tooltip_mgr.unblockToolTips();
}
last_x = x;
@@ -100,96 +93,84 @@ BOOL LLToolTipView::handleHover(S32 x, S32 y, MASK mask)
BOOL LLToolTipView::handleMouseDown(S32 x, S32 y, MASK mask)
{
- LLToolTipMgr::instance().hideToolTips();
+ LLToolTipMgr::instance().blockToolTips();
return LLView::handleMouseDown(x, y, mask);
}
BOOL LLToolTipView::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
{
- LLToolTipMgr::instance().hideToolTips();
+ LLToolTipMgr::instance().blockToolTips();
return LLView::handleMiddleMouseDown(x, y, mask);
}
BOOL LLToolTipView::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
- LLToolTipMgr::instance().hideToolTips();
+ LLToolTipMgr::instance().blockToolTips();
return LLView::handleRightMouseDown(x, y, mask);
}
BOOL LLToolTipView::handleScrollWheel( S32 x, S32 y, S32 clicks )
{
- LLToolTipMgr::instance().hideToolTips();
+ LLToolTipMgr::instance().blockToolTips();
return FALSE;
}
void LLToolTipView::onMouseLeave(S32 x, S32 y, MASK mask)
{
- LLToolTipMgr::instance().hideToolTips();
+ LLToolTipMgr::instance().blockToolTips();
}
void LLToolTipView::drawStickyRect()
{
- gl_rect_2d(LLToolTipMgr::instance().getStickyRect(), LLColor4::white, false);
+ gl_rect_2d(LLToolTipMgr::instance().getMouseNearRect(), LLColor4::white, false);
}
//
// LLToolTip
//
-class LLToolTip : public LLPanel
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLPanel::Params>
- {
- Mandatory<F32> visible_time;
-
- Optional<LLToolTipParams::click_callback_t> click_callback;
- Optional<LLUIImage*> image;
-
- Params()
- {
- //use_bounding_rect = true;
- }
- };
- /*virtual*/ void draw();
- /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
-
- /*virtual*/ void setValue(const LLSD& value);
- /*virtual*/ void setVisible(BOOL visible);
- bool isFading() { return mFadeTimer.getStarted(); }
-
- LLToolTip(const Params& p);
-
-private:
- LLTextBox* mTextBox;
- LLFrameTimer mFadeTimer;
- F32 mVisibleTime;
- bool mHasClickCallback;
-};
static LLDefaultChildRegistry::Register<LLToolTip> r("tool_tip");
-const S32 TOOLTIP_PADDING = 4;
+
+LLToolTip::Params::Params()
+: max_width("max_width", 200),
+ padding("padding", 4),
+ pos("pos"),
+ message("message"),
+ delay_time("delay_time", LLUI::sSettingGroups["config"]->getF32( "ToolTipDelay" )),
+ visible_time_over("visible_time_over", LLUI::sSettingGroups["config"]->getF32( "ToolTipVisibleTimeOver" )),
+ visible_time_near("visible_time_near", LLUI::sSettingGroups["config"]->getF32( "ToolTipVisibleTimeNear" )),
+ visible_time_far("visible_time_far", LLUI::sSettingGroups["config"]->getF32( "ToolTipVisibleTimeFar" )),
+ sticky_rect("sticky_rect"),
+ image("image")
+{
+ name = "tooltip";
+ font = LLFontGL::getFontSansSerif();
+ bg_opaque_color = LLUIColorTable::instance().getColor( "ToolTipBgColor" );
+ background_visible = true;
+}
LLToolTip::LLToolTip(const LLToolTip::Params& p)
: LLPanel(p),
- mVisibleTime(p.visible_time),
- mHasClickCallback(p.click_callback.isProvided())
+ mMaxWidth(p.max_width),
+ mHasClickCallback(p.click_callback.isProvided()),
+ mPadding(p.padding)
{
LLTextBox::Params params;
- params.text = "tip_text";
- params.name = params.text;
+ params.initial_value = "tip_text";
+ params.name = params.initial_value().asString();
// bake textbox padding into initial rect
- params.rect = LLRect (TOOLTIP_PADDING, TOOLTIP_PADDING + 1, TOOLTIP_PADDING + 1, TOOLTIP_PADDING);
+ params.rect = LLRect (mPadding, mPadding + 1, mPadding + 1, mPadding);
params.follows.flags = FOLLOWS_ALL;
- params.h_pad = 4;
- params.v_pad = 2;
+ params.h_pad = 0;
+ params.v_pad = 0;
params.mouse_opaque = false;
params.text_color = LLUIColorTable::instance().getColor( "ToolTipTextColor" );
params.bg_visible = false;
- params.font.style = "NORMAL";
- //params.border_drop_shadow_visible = true;
+ params.font = p.font;
+ params.use_ellipses = true;
mTextBox = LLUICtrlFactory::create<LLTextBox> (params);
addChild(mTextBox);
@@ -198,8 +179,9 @@ LLToolTip::LLToolTip(const LLToolTip::Params& p)
LLIconCtrl::Params icon_params;
icon_params.name = "tooltip_icon";
LLRect icon_rect;
- const S32 TOOLTIP_ICON_SIZE = 18;
- icon_rect.setOriginAndSize(TOOLTIP_PADDING, TOOLTIP_PADDING, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE);
+ LLUIImage* imagep = p.image;
+ const S32 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.follows.flags = FOLLOWS_LEFT | FOLLOWS_BOTTOM;
icon_params.image = p.image;
@@ -218,13 +200,20 @@ LLToolTip::LLToolTip(const LLToolTip::Params& p)
void LLToolTip::setValue(const LLSD& value)
{
- mTextBox->setWrappedText(value.asString());
- mTextBox->reshapeToFitText();
+ const S32 REALLY_LARGE_HEIGHT = 10000;
+ reshape(mMaxWidth, REALLY_LARGE_HEIGHT);
+
+ mTextBox->setValue(value);
+
+ LLRect text_contents_rect = mTextBox->getContentsRect();
+ S32 text_width = llmin(mMaxWidth, text_contents_rect.getWidth());
+ S32 text_height = text_contents_rect.getHeight();
+ mTextBox->reshape(text_width, text_height);
// reshape tooltip panel to fit text box
LLRect tooltip_rect = calcBoundingRect();
- tooltip_rect.mTop += TOOLTIP_PADDING;
- tooltip_rect.mRight += TOOLTIP_PADDING;
+ tooltip_rect.mTop += mPadding;
+ tooltip_rect.mRight += mPadding;
tooltip_rect.mBottom = 0;
tooltip_rect.mLeft = 0;
@@ -234,19 +223,21 @@ void LLToolTip::setValue(const LLSD& value)
void LLToolTip::setVisible(BOOL visible)
{
// fade out tooltip over time
- if (!visible)
+ 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();
}
}
- else
- {
- mFadeTimer.stop();
- LLPanel::setVisible(TRUE);
- }
}
BOOL LLToolTip::handleHover(S32 x, S32 y, MASK mask)
@@ -263,11 +254,6 @@ void LLToolTip::draw()
{
F32 alpha = 1.f;
- if (LLUI::getMouseIdleTime() > mVisibleTime)
- {
- LLToolTipMgr::instance().hideToolTips();
- }
-
if (mFadeTimer.getStarted())
{
F32 tool_tip_fade_time = LLUI::sSettingGroups["config"]->getF32("ToolTipFadeTime");
@@ -287,94 +273,90 @@ void LLToolTip::draw()
}
}
+bool LLToolTip::isFading()
+{
+ return mFadeTimer.getStarted();
+}
+
+F32 LLToolTip::getVisibleTime()
+{
+ return mVisibleTimer.getStarted() ? mVisibleTimer.getElapsedTimeF32() : 0.f;
+}
+
+bool LLToolTip::hasClickCallback()
+{
+ return mHasClickCallback;
+}
//
// LLToolTipMgr
//
-LLToolTipParams::LLToolTipParams()
-: pos("pos"),
- message("message"),
- delay_time("delay_time", LLUI::sSettingGroups["config"]->getF32( "ToolTipDelay" )),
- visible_time("visible_time", LLUI::sSettingGroups["config"]->getF32( "ToolTipVisibleTime" )),
- sticky_rect("sticky_rect"),
- width("width", 200),
- image("image")
-{}
LLToolTipMgr::LLToolTipMgr()
-: mToolTip(NULL)
-{
-}
+: mToolTip(NULL),
+ mNeedsToolTip(false)
+{}
-LLToolTip* LLToolTipMgr::createToolTip(const LLToolTipParams& params)
+void LLToolTipMgr::createToolTip(const LLToolTip::Params& params)
{
- S32 mouse_x;
- S32 mouse_y;
- LLUI::getMousePositionLocal(gToolTipView->getParent(), &mouse_x, &mouse_y);
+ // block all other tooltips until tooltips re-enabled (e.g. mouse moved)
+ blockToolTips();
+ delete mToolTip;
- LLToolTip::Params tooltip_params;
- tooltip_params.name = "tooltip";
- tooltip_params.mouse_opaque = true;
+ LLToolTip::Params tooltip_params(params);
+ // block mouse events if there is a click handler registered (specifically, hover)
+ tooltip_params.mouse_opaque = params.click_callback.isProvided();
tooltip_params.rect = LLRect (0, 1, 1, 0);
- tooltip_params.bg_opaque_color = LLUIColorTable::instance().getColor( "ToolTipBgColor" );
- tooltip_params.background_visible = true;
- tooltip_params.visible_time = params.visible_time;
- if (params.image.isProvided())
- {
- tooltip_params.image = params.image;
- }
- if (params.click_callback.isProvided())
- {
- tooltip_params.click_callback = params.click_callback;
- }
-
- LLToolTip* tooltip = LLUICtrlFactory::create<LLToolTip> (tooltip_params);
- // make tooltip fixed width and tall enough to fit text
- tooltip->reshape(params.width, 2000);
- tooltip->setValue(params.message());
- gToolTipView->addChild(tooltip);
+ mToolTip = LLUICtrlFactory::create<LLToolTip> (tooltip_params);
+ mToolTip->setValue(params.message());
+ gToolTipView->addChild(mToolTip);
if (params.pos.isProvided())
{
+ LLCoordGL pos = params.pos;
// try to spawn at requested position
- LLUI::positionViewNearMouse(tooltip, params.pos.x, params.pos.y);
+ LLUI::positionViewNearMouse(mToolTip, pos.mX, pos.mY);
}
else
{
// just spawn at mouse location
- LLUI::positionViewNearMouse(tooltip);
+ LLUI::positionViewNearMouse(mToolTip);
}
//...update "sticky" rect and tooltip position
if (params.sticky_rect.isProvided())
{
- mToolTipStickyRect = params.sticky_rect;
+ mMouseNearRect = params.sticky_rect;
}
else
{
- // otherwise just use one pixel rect around mouse cursor
- mToolTipStickyRect.setOriginAndSize(mouse_x, mouse_y, 1, 1);
+ S32 mouse_x;
+ S32 mouse_y;
+ LLUI::getMousePositionLocal(gToolTipView->getParent(), &mouse_x, &mouse_y);
+
+ // allow mouse a little bit of slop before changing tooltips
+ mMouseNearRect.setCenterAndSize(mouse_x, mouse_y, 3, 3);
}
-
- if (params.click_callback.isProvided())
+
+ // 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
- mToolTipStickyRect.unionWith(tooltip->getRect());
+ mMouseNearRect.unionWith(mToolTip->getRect());
}
-
- return tooltip;
}
void LLToolTipMgr::show(const std::string& msg)
{
- show(LLToolTipParams().message(msg));
+ show(LLToolTip::Params().message(msg));
}
-void LLToolTipMgr::show(const LLToolTipParams& params)
+void LLToolTipMgr::show(const LLToolTip::Params& params)
{
if (!params.validateBlock())
{
@@ -382,38 +364,42 @@ void LLToolTipMgr::show(const LLToolTipParams& params)
return;
}
- bool tooltip_shown = mToolTip
- && mToolTip->getVisible()
- && !mToolTip->isFading();
-
- // if tooltip contents change, hide existing tooltip
- if (tooltip_shown && mLastToolTipMessage != params.message())
- {
- hideToolTips();
- }
+ S32 mouse_x;
+ S32 mouse_y;
+ LLUI::getMousePositionLocal(gToolTipView, &mouse_x, &mouse_y);
+ // are we ready to show the tooltip?
if (!mToolTipsBlocked // we haven't hit a key, moved the mouse, etc.
- && LLUI::getMouseIdleTime() > params.delay_time // the mouse has been still long enough
- && !tooltip_shown) // tooltip not visible
+ && LLUI::getMouseIdleTime() > params.delay_time) // the mouse has been still long enough
{
- // create new tooltip at mouse cursor position
- delete mToolTip;
- mToolTip = createToolTip(params);
+ bool tooltip_changed = mLastToolTipParams.message() != params.message()
+ || mLastToolTipParams.pos() != params.pos();
+
+ bool tooltip_shown = mToolTip
+ && mToolTip->getVisible()
+ && !mToolTip->isFading();
- // remember this tooltip so we know when it changes
- mLastToolTipMessage = params.message();
+ mNeedsToolTip = tooltip_changed || !tooltip_shown;
+ // store description of tooltip for later creation
+ mNextToolTipParams = params;
}
}
// allow new tooltips to be created, e.g. after mouse has moved
-void LLToolTipMgr::enableToolTips()
+void LLToolTipMgr::unblockToolTips()
{
mToolTipsBlocked = false;
}
+// disallow new tooltips until unblockTooltips called
+void LLToolTipMgr::blockToolTips()
+{
+ hideToolTips();
+ mToolTipsBlocked = true;
+}
+
void LLToolTipMgr::hideToolTips()
{
- mToolTipsBlocked = true;
if (mToolTip)
{
mToolTip->setVisible(FALSE);
@@ -422,7 +408,7 @@ void LLToolTipMgr::hideToolTips()
bool LLToolTipMgr::toolTipVisible()
{
- return mToolTip ? mToolTip->getVisible() : false;
+ return mToolTip ? mToolTip->isInVisibleChain() : false;
}
LLRect LLToolTipMgr::getToolTipRect()
@@ -435,11 +421,63 @@ LLRect LLToolTipMgr::getToolTipRect()
}
-LLRect LLToolTipMgr::getStickyRect()
+LLRect LLToolTipMgr::getMouseNearRect()
{
- if (!mToolTip) return LLRect();
+ 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;
+ }
- return mToolTip->isInVisibleChain() ? mToolTipStickyRect : LLRect();
+ // hide tooltips when mouse cursor is hidden
+ if (LLUI::getWindow()->isCursorHidden())
+ {
+ blockToolTips();
+ return;
+ }
+
+ // hide existing tooltips if they have timed out
+ S32 mouse_x, mouse_y;
+ LLUI::getMousePositionLocal(gToolTipView, &mouse_x, &mouse_y);
+
+ F32 tooltip_timeout = 0.f;
+ if (toolTipVisible())
+ {
+ // mouse far away from tooltip
+ tooltip_timeout = mLastToolTipParams.visible_time_far;
+ // mouse near rect will only include the tooltip if the
+ // tooltip is clickable
+ if (mMouseNearRect.pointInRect(mouse_x, mouse_y))
+ {
+ // mouse "close" to tooltip
+ tooltip_timeout = mLastToolTipParams.visible_time_near;
+
+ // if tooltip is clickable (has large mMouseNearRect)
+ // than having cursor over tooltip keeps it up indefinitely
+ if (mToolTip->parentPointInView(mouse_x, mouse_y))
+ {
+ // mouse over tooltip itself, don't time out
+ tooltip_timeout = mLastToolTipParams.visible_time_over;
+ }
+ }
+
+ if (mToolTip->getVisibleTime() > tooltip_timeout)
+ {
+ hideToolTips();
+ }
+ }
}
+
+
// EOF
diff --git a/indra/llui/lltooltip.h b/indra/llui/lltooltip.h
index fb7f942099..6715da1611 100644
--- a/indra/llui/lltooltip.h
+++ b/indra/llui/lltooltip.h
@@ -36,7 +36,7 @@
// Library includes
#include "llsingleton.h"
#include "llinitparam.h"
-#include "llview.h"
+#include "llpanel.h"
//
// Classes
@@ -46,10 +46,7 @@ class LLToolTipView : public LLView
public:
struct Params : public LLInitParam::Block<Params, LLView::Params>
{
- Params()
- {
- mouse_opaque = false;
- }
+ Params();
};
LLToolTipView(const LLToolTipView::Params&);
/*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
@@ -65,57 +62,82 @@ public:
/*virtual*/ void draw();
};
-struct LLToolTipPosParams : public LLInitParam::Block<LLToolTipPosParams>
+class LLToolTip : public LLPanel
{
- Mandatory<S32> x,
- y;
- LLToolTipPosParams()
- : x("x"),
- y("y")
- {}
-};
+public:
+ struct Params : public LLInitParam::Block<Params, LLPanel::Params>
+ {
+ typedef boost::function<void(void)> click_callback_t;
-struct LLToolTipParams : public LLInitParam::Block<LLToolTipParams>
-{
- typedef boost::function<void(void)> click_callback_t;
+ Mandatory<std::string> 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;
- Mandatory<std::string> message;
-
- Optional<LLToolTipPosParams> pos;
- Optional<F32> delay_time,
- visible_time;
- Optional<LLRect> sticky_rect;
- Optional<S32> width;
- Optional<LLUIImage*> image;
+ Optional<click_callback_t> click_callback;
+ Optional<LLUIImage*> image;
+ Optional<S32> max_width;
+ Optional<S32> padding;
+
+ Params();
+ };
+ /*virtual*/ void draw();
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
- Optional<click_callback_t> click_callback;
+ /*virtual*/ void setValue(const LLSD& value);
+ /*virtual*/ void setVisible(BOOL visible);
- LLToolTipParams();
- LLToolTipParams(const std::string& message);
+ bool isFading();
+ F32 getVisibleTime();
+ bool hasClickCallback();
+
+ LLToolTip(const Params& p);
+
+private:
+ class LLTextBox* mTextBox;
+ LLFrameTimer mFadeTimer;
+ LLFrameTimer mVisibleTimer;
+ S32 mMaxWidth;
+ bool mHasClickCallback;
+ S32 mPadding; // pixels
};
+
class LLToolTipMgr : public LLSingleton<LLToolTipMgr>
{
LOG_CLASS(LLToolTipMgr);
public:
LLToolTipMgr();
- void show(const LLToolTipParams& params);
+ void show(const LLToolTip::Params& params);
void show(const std::string& message);
- void enableToolTips();
+ void unblockToolTips();
+ void blockToolTips();
+
void hideToolTips();
bool toolTipVisible();
LLRect getToolTipRect();
-
- LLRect getStickyRect();
+ LLRect getMouseNearRect();
+ void updateToolTipVisibility();
private:
- class LLToolTip* createToolTip(const LLToolTipParams& params);
+ void createToolTip(const LLToolTip::Params& params);
bool mToolTipsBlocked;
class LLToolTip* mToolTip;
- std::string mLastToolTipMessage;
- LLRect mToolTipStickyRect;
+
+ // 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;
};
//
diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp
index d5b67f53b7..c89e5944fa 100644
--- a/indra/llui/llui.cpp
+++ b/indra/llui/llui.cpp
@@ -55,6 +55,7 @@
#include "llfloater.h"
#include "llfloaterreg.h"
#include "llmenugl.h"
+#include "llmenubutton.h"
#include "llwindow.h"
// for registration
@@ -90,6 +91,7 @@ std::list<std::string> gUntranslated;
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");
+static LLDefaultChildRegistry::Register<LLMenuButton> register_menu_button("menu_button");
//
@@ -1644,6 +1646,17 @@ void LLUI::setMousePositionScreen(S32 x, S32 y)
}
//static
+void LLUI::getMousePositionScreen(S32 *x, S32 *y)
+{
+ LLCoordWindow cursor_pos_window;
+ getWindow()->getCursorPosition(&cursor_pos_window);
+ LLCoordGL cursor_pos_gl;
+ getWindow()->convertCoords(cursor_pos_window, &cursor_pos_gl);
+ *x = llround((F32)cursor_pos_gl.mX / sGLScaleFactor.mV[VX]);
+ *y = llround((F32)cursor_pos_gl.mY / sGLScaleFactor.mV[VX]);
+}
+
+//static
void LLUI::setMousePositionLocal(const LLView* viewp, S32 x, S32 y)
{
S32 screen_x, screen_y;
@@ -1655,15 +1668,12 @@ void LLUI::setMousePositionLocal(const LLView* viewp, S32 x, S32 y)
//static
void LLUI::getMousePositionLocal(const LLView* viewp, S32 *x, S32 *y)
{
- LLCoordWindow cursor_pos_window;
- LLView::getWindow()->getCursorPosition(&cursor_pos_window);
- LLCoordGL cursor_pos_gl;
- LLView::getWindow()->convertCoords(cursor_pos_window, &cursor_pos_gl);
- cursor_pos_gl.mX = llround((F32)cursor_pos_gl.mX / LLUI::sGLScaleFactor.mV[VX]);
- cursor_pos_gl.mY = llround((F32)cursor_pos_gl.mY / LLUI::sGLScaleFactor.mV[VY]);
- viewp->screenPointToLocal(cursor_pos_gl.mX, cursor_pos_gl.mY, x, y);
+ S32 screen_x, screen_y;
+ getMousePositionScreen(&screen_x, &screen_y);
+ viewp->screenPointToLocal(screen_x, screen_y, x, y);
}
+
// On Windows, the user typically sets the language when they install the
// app (by running it with a shortcut that sets InstallLanguage). On Mac,
// or on Windows if the SecondLife.exe executable is run directly, the
@@ -1835,14 +1845,14 @@ LLControlGroup& LLUI::getControlControlGroup (const std::string& controlname)
// 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 = 22; // Approximate "normal" cursor size
- const S32 CURSOR_WIDTH = 12;
+ const S32 CURSOR_HEIGHT = 18; // Approximate "normal" cursor size
+ const S32 CURSOR_WIDTH = 9;
LLView* parent = view->getParent();
S32 mouse_x;
S32 mouse_y;
- LLUI::getMousePositionLocal(parent, &mouse_x, &mouse_y);
+ LLUI::getMousePositionScreen(&mouse_x, &mouse_y);
// If no spawn location provided, use mouse position
if (spawn_x == S32_MAX || spawn_y == S32_MAX)
@@ -1856,12 +1866,13 @@ void LLUI::positionViewNearMouse(LLView* view, S32 spawn_x, S32 spawn_y)
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);
+ mouse_y + MOUSE_CURSOR_PADDING,
+ CURSOR_WIDTH + MOUSE_CURSOR_PADDING * 2,
+ CURSOR_HEIGHT + MOUSE_CURSOR_PADDING * 2);
S32 local_x, local_y;
- view->getParent()->screenPointToLocal(spawn_x, spawn_y, &local_x, &local_y);
+ // convert screen coordinates to tooltipview-local coordinates
+ parent->screenPointToLocal(spawn_x, spawn_y, &local_x, &local_y);
// Start at spawn position (using left/top)
view->setOrigin( local_x, local_y - view->getRect().getHeight());
@@ -1915,12 +1926,14 @@ namespace LLInitParam
}
};
- TypedParam<const LLFontGL*>::TypedParam(BlockDescriptor& descriptor, const char* name, const LLFontGL*const value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count)
- : super_t(descriptor, name, value, func, min_count, max_count),
- name(""),
+ TypedParam<const LLFontGL*>::TypedParam(BlockDescriptor& descriptor, const char* _name, const LLFontGL*const value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count)
+ : super_t(descriptor, _name, value, func, min_count, max_count),
+ name("name"),
size("size"),
style("style")
- {}
+ {
+ addSynonym(name, "");
+ }
const LLFontGL* TypedParam<const LLFontGL*>::getValueFromBlock() const
{
diff --git a/indra/llui/llui.h b/indra/llui/llui.h
index 86cb516500..f071e8dc47 100644
--- a/indra/llui/llui.h
+++ b/indra/llui/llui.h
@@ -190,6 +190,7 @@ public:
static void setRootView(LLView* view) { sRootView = view; }
static std::string locateSkin(const std::string& filename);
static void setMousePositionScreen(S32 x, S32 y);
+ static void getMousePositionScreen(S32 *x, S32 *y);
static void setMousePositionLocal(const LLView* viewp, S32 x, S32 y);
static void getMousePositionLocal(const LLView* viewp, S32 *x, S32 *y);
static void setScaleFactor(const LLVector2& scale_factor);
@@ -409,8 +410,8 @@ namespace LLInitParam
{
typedef BlockValue<const LLFontGL*> super_t;
public:
- Optional<std::string> name,
- size,
+ Mandatory<std::string> name;
+ Optional<std::string> size,
style;
TypedParam(BlockDescriptor& descriptor, const char* name, const LLFontGL* const value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count);
diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp
index fe99d9c267..5b72f87a78 100644
--- a/indra/llui/lluictrl.cpp
+++ b/indra/llui/lluictrl.cpp
@@ -49,7 +49,12 @@ LLUICtrl::Params::Params()
validate_callback("validate_callback"),
mouseenter_callback("mouseenter_callback"),
mouseleave_callback("mouseleave_callback"),
- control_name("control_name")
+ 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");
}
@@ -212,6 +217,29 @@ void LLUICtrl::initEnableCallback(const EnableCallbackParam& cb, enable_signal_t
}
}
+void LLUICtrl::initVisibleCallback(const VisibleCallbackParam& cb, visible_signal_t& sig)
+{
+ // Set the callback function
+ if (cb.function.isProvided())
+ {
+ if (cb.parameter.isProvided())
+ sig.connect(boost::bind(cb.function(), this, cb.parameter));
+ else
+ sig.connect(cb.function());
+ }
+ else
+ {
+ visible_callback_t* func = (VisibleCallbackRegistry::getValue(cb.function_name));
+ if (func)
+ {
+ if (cb.parameter.isProvided())
+ sig.connect(boost::bind((*func), this, cb.parameter));
+ else
+ sig.connect(*func);
+ }
+ }
+}
+
// virtual
void LLUICtrl::onMouseEnter(S32 x, S32 y, MASK mask)
{
diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h
index c2502732f3..69207eb8ea 100644
--- a/indra/llui/lluictrl.h
+++ b/indra/llui/lluictrl.h
@@ -34,14 +34,15 @@
#ifndef LL_LLUICTRL_H
#define LL_LLUICTRL_H
-#include "llboost.h"
+//#include "llboost.h"
#include "llrect.h"
#include "llsd.h"
#include <boost/function.hpp>
+#include <boost/signals2.hpp>
#include "llinitparam.h"
#include "llview.h"
-#include "llviewmodel.h"
+#include "llviewmodel.h" // *TODO move dependency to .cpp file
const BOOL TAKE_FOCUS_YES = TRUE;
const BOOL TAKE_FOCUS_NO = FALSE;
@@ -62,6 +63,9 @@ public:
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;
+ typedef boost::function<bool (LLUICtrl* ctrl, const LLSD& param)> visible_callback_t;
+ typedef boost::signals2::signal<bool (LLUICtrl* ctrl, const LLSD& param), boost_boolean_combiner> visible_signal_t;
+
struct CallbackParam : public LLInitParam::Block<CallbackParam>
{
Ignored name;
@@ -91,6 +95,11 @@ public:
Optional<enable_callback_t> function;
};
+ struct VisibleCallbackParam : public LLInitParam::Block<VisibleCallbackParam, CallbackParam >
+ {
+ Optional<visible_callback_t> function;
+ };
+
struct EnableControls : public LLInitParam::Choice<EnableControls>
{
Alternative<std::string> enabled;
@@ -107,9 +116,12 @@ public:
Alternative<std::string> invisible;
ControlVisibility()
- : visible("visiblity_control"),
- invisible("invisiblity_control")
- {}
+ : visible("visibility_control"),
+ invisible("invisibility_control")
+ {
+ addSynonym(visible, "visiblity_control");
+ addSynonym(invisible, "invisiblity_control");
+ }
};
struct Params : public LLInitParam::Block<Params, LLView::Params>
{
@@ -128,6 +140,15 @@ public:
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();
};
@@ -142,6 +163,7 @@ protected:
void initCommitCallback(const CommitCallbackParam& cb, commit_signal_t& sig);
void initEnableCallback(const EnableCallbackParam& cb, enable_signal_t& sig);
+ void initVisibleCallback(const VisibleCallbackParam& cb, visible_signal_t& sig);
// We need this virtual so we can override it with derived versions
virtual LLViewModel* getViewModel() const;
@@ -259,6 +281,8 @@ public:
class CommitCallbackRegistry : public CallbackRegistry<commit_callback_t, CommitCallbackRegistry>{};
class EnableCallbackRegistry : public CallbackRegistry<enable_callback_t, EnableCallbackRegistry>{};
+ class VisibleCallbackRegistry : public CallbackRegistry<visible_callback_t, VisibleCallbackRegistry>{};
+
protected:
diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp
index 538e1ec492..4ce6677294 100644
--- a/indra/llui/lluictrlfactory.cpp
+++ b/indra/llui/lluictrlfactory.cpp
@@ -113,8 +113,20 @@ void LLUICtrlFactory::createChildren(LLView* viewp, LLXMLNodePtr node, const wid
if (!instance().createFromXML(child_node, viewp, LLStringUtil::null, registry, outputChild))
{
+ // child_node is not a valid child for the current parent
std::string child_name = std::string(child_node->getName()->mString);
- llwarns << "Could not create widget named " << child_node->getName()->mString << llendl;
+ if (LLDefaultChildRegistry::instance().getValue(child_name))
+ {
+ // This means that the registry assocaited with the parent widget does not have an entry
+ // for the child widget
+ // You might need to add something like:
+ // static ParentWidgetRegistry::Register<ChildWidgetType> register("child_widget_name");
+ llwarns << child_name << " is not a valid child of " << node->getName()->mString << llendl;
+ }
+ else
+ {
+ llwarns << "Could not create widget named " << child_node->getName()->mString << llendl;
+ }
}
if (outputChild && !outputChild->mChildren && outputChild->mAttributes.empty() && outputChild->getValue().empty())
diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp
index c20212c375..c1da73fa83 100644
--- a/indra/llui/llurlentry.cpp
+++ b/indra/llui/llurlentry.cpp
@@ -36,8 +36,10 @@
#include "lluri.h"
#include "llcachename.h"
#include "lltrans.h"
+#include "lluicolortable.h"
LLUrlEntryBase::LLUrlEntryBase()
+: mColor(LLUIColorTable::instance().getColor("HTMLLinkColor"))
{
}
@@ -260,10 +262,11 @@ std::string LLUrlEntrySLURL::getLocation(const std::string &url) const
//
LLUrlEntryAgent::LLUrlEntryAgent()
{
- mPattern = boost::regex("secondlife:///app/agent/[\\da-f-]+/about",
+ mPattern = boost::regex("secondlife:///app/agent/[\\da-f-]+/\\w+",
boost::regex::perl|boost::regex::icase);
mMenuName = "menu_url_agent.xml";
- mTooltip = LLTrans::getString("TooltipAgentUrl");
+ mIcon = "Generic_Person";
+ mColor = LLUIColorTable::instance().getColor("AgentLinkColor");
}
void LLUrlEntryAgent::onAgentNameReceived(const LLUUID& id,
@@ -293,7 +296,7 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa
}
}
- return unescapeUrl(url);
+ return LLTrans::getString("LoadingData");//unescapeUrl(url);
}
//
@@ -305,6 +308,7 @@ LLUrlEntryGroup::LLUrlEntryGroup()
mPattern = boost::regex("secondlife:///app/group/[\\da-f-]+/about",
boost::regex::perl|boost::regex::icase);
mMenuName = "menu_url_group.xml";
+ mIcon = "Generic_Group";
mTooltip = LLTrans::getString("TooltipGroupUrl");
}
diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h
index 54053872df..afb2fdcde9 100644
--- a/indra/llui/llurlentry.h
+++ b/indra/llui/llurlentry.h
@@ -35,7 +35,7 @@
#define LL_LLURLENTRY_H
#include "lluuid.h"
-
+#include "lluicolor.h"
#include <boost/signals2.hpp>
#include <boost/regex.hpp>
#include <string>
@@ -77,13 +77,16 @@ public:
virtual std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) { return url; }
/// Return an icon that can be displayed next to Urls of this type
- const std::string &getIcon() const { return mIcon; }
+ std::string getIcon() const { return mIcon; }
+
+ /// Return the color to render the displayed text
+ LLUIColor getColor() const { return mColor; }
/// Given a matched Url, return a tooltip string for the hyperlink
std::string getTooltip() const { return mTooltip; }
/// Return the name of a XUI file containing the context menu items
- const std::string getMenuName() const { return mMenuName; }
+ 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 ""; }
@@ -102,11 +105,12 @@ protected:
LLUrlLabelSignal *signal;
} LLUrlEntryObserver;
- boost::regex mPattern;
- std::string mIcon;
- std::string mMenuName;
- std::string mTooltip;
- std::multimap<std::string, LLUrlEntryObserver> mObservers;
+ boost::regex mPattern;
+ std::string mIcon;
+ std::string mMenuName;
+ std::string mTooltip;
+ LLUIColor mColor;
+ std::multimap<std::string, LLUrlEntryObserver> mObservers;
};
///
diff --git a/indra/llui/llurlmatch.cpp b/indra/llui/llurlmatch.cpp
index 7eec4c4a65..3b47145a22 100644
--- a/indra/llui/llurlmatch.cpp
+++ b/indra/llui/llurlmatch.cpp
@@ -47,8 +47,8 @@ LLUrlMatch::LLUrlMatch() :
void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url,
const std::string &label, const std::string &tooltip,
- const std::string &icon, const std::string &menu,
- const std::string &location)
+ const std::string &icon, const LLUIColor& color,
+ const std::string &menu, const std::string &location)
{
mStart = start;
mEnd = end;
@@ -56,6 +56,7 @@ void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url,
mLabel = label;
mTooltip = tooltip;
mIcon = icon;
+ mColor = color;
mMenuName = menu;
mLocation = location;
}
diff --git a/indra/llui/llurlmatch.h b/indra/llui/llurlmatch.h
index 0711e41443..7f5767923a 100644
--- a/indra/llui/llurlmatch.h
+++ b/indra/llui/llurlmatch.h
@@ -38,6 +38,7 @@
#include <string>
#include <vector>
+#include "lluicolor.h"
///
/// LLUrlMatch describes a single Url that was matched within a string by
@@ -62,27 +63,31 @@ public:
U32 getEnd() const { return mEnd; }
/// return the Url that has been matched in the input string
- const std::string &getUrl() const { return mUrl; }
+ std::string getUrl() const { return mUrl; }
/// return a label that can be used for the display of this Url
- const std::string &getLabel() const { return mLabel; }
+ std::string getLabel() const { return mLabel; }
/// return a message that could be displayed in a tooltip or status bar
- const std::string &getTooltip() const { return mTooltip; }
+ std::string getTooltip() const { return mTooltip; }
/// return the filename for an icon that can be displayed next to this Url
- const std::string &getIcon() const { return mIcon; }
+ std::string getIcon() const { return mIcon; }
+
+ /// Return the color to render the displayed text
+ LLUIColor getColor() const { return mColor; }
/// Return the name of a XUI file containing the context menu items
- const std::string getMenuName() const { return mMenuName; }
+ std::string getMenuName() const { return mMenuName; }
/// return the SL location that this Url describes, or "" if none.
- const std::string &getLocation() const { return mLocation; }
+ std::string getLocation() const { return mLocation; }
/// Change the contents of this match object (used by LLUrlRegistry)
void setValues(U32 start, U32 end, const std::string &url, const std::string &label,
const std::string &tooltip, const std::string &icon,
- const std::string &menu, const std::string &location);
+ const LLUIColor& color, const std::string &menu,
+ const std::string &location);
private:
U32 mStart;
@@ -93,6 +98,7 @@ private:
std::string mIcon;
std::string mMenuName;
std::string mLocation;
+ LLUIColor mColor;
};
#endif
diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp
index 6f5c694b1b..8413de0837 100644
--- a/indra/llui/llurlregistry.cpp
+++ b/indra/llui/llurlregistry.cpp
@@ -155,6 +155,7 @@ bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LL
match_entry->getLabel(url, cb),
match_entry->getTooltip(),
match_entry->getIcon(),
+ match_entry->getColor(),
match_entry->getMenuName(),
match_entry->getLocation(url));
return true;
@@ -183,9 +184,10 @@ bool LLUrlRegistry::findUrl(const LLWString &text, LLUrlMatch &match, const LLUr
}
S32 end = start + wurl.size() - 1;
- match.setValues(start, end, match.getUrl(), match.getLabel(),
- match.getTooltip(), match.getIcon(),
- match.getMenuName(), match.getLocation());
+ match.setValues(start, end, match.getUrl(),
+ match.getLabel(), match.getTooltip(),
+ match.getIcon(), match.getColor(),
+ match.getMenuName(), match.getLocation());
return true;
}
return false;
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index 10cb3fb377..1df8838738 100644
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -92,9 +92,6 @@ LLView::Params::Params()
default_tab_group("default_tab_group"),
tool_tip("tool_tip"),
sound_flags("sound_flags", MOUSE_UP),
- font("font", LLFontGL::getFontSansSerif()),
- font_halign("halign"),
- font_valign("valign"),
layout("layout"),
rect("rect"),
bottom_delta("bottom_delta", S32_MAX),
@@ -172,12 +169,6 @@ LLView::~LLView()
}
// virtual
-BOOL LLView::isView() const
-{
- return TRUE;
-}
-
-// virtual
BOOL LLView::isCtrl() const
{
return FALSE;
@@ -227,10 +218,9 @@ BOOL LLView::getUseBoundingRect()
}
// virtual
-const std::string& LLView::getName() const
+std::string LLView::getName() const
{
- static const std::string unnamed("(no name)");
- return mName.empty() ? unnamed : mName;
+ return mName.empty() ? std::string("(no name)") : mName;
}
void LLView::sendChildToFront(LLView* child)
@@ -634,16 +624,7 @@ void LLView::setSnappedTo(const LLView* snap_view)
BOOL LLView::handleHover(S32 x, S32 y, MASK mask)
{
- BOOL handled = childrenHandleHover( x, y, mask ) != NULL;
- if( !handled
- && blockMouseEvent(x, y) )
- {
- LLUI::sWindow->setCursor(mHoverCursor);
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl;
- handled = TRUE;
- }
-
- return handled;
+ return childrenHandleHover( x, y, mask ) != NULL;
}
void LLView::onMouseEnter(S32 x, S32 y, MASK mask)
@@ -657,7 +638,7 @@ void LLView::onMouseLeave(S32 x, S32 y, MASK mask)
}
-LLView* LLView::childrenHandleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen)
+LLView* LLView::childrenHandleToolTip(S32 x, S32 y, MASK mask)
{
LLView* handled_view = NULL;
for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
@@ -665,13 +646,13 @@ LLView* LLView::childrenHandleToolTip(S32 x, S32 y, std::string& msg, LLRect& st
LLView* viewp = *child_it;
S32 local_x = x - viewp->getRect().mLeft;
S32 local_y = y - viewp->getRect().mBottom;
- if(!viewp->pointInView(local_x, local_y) ||
- !viewp->getVisible())
+ if(!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible())
{
continue;
}
- if(viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen) )
+ if (viewp->handleToolTip(local_x, local_y, mask) )
{
if (sDebugMouseHandling)
{
@@ -682,20 +663,22 @@ LLView* LLView::childrenHandleToolTip(S32 x, S32 y, std::string& msg, LLRect& st
break;
}
- if( viewp->blockMouseEvent(x, y) )
+ if (viewp->blockMouseEvent(local_x, local_y))
{
handled_view = viewp;
+ break;
}
}
return handled_view;
}
-BOOL LLView::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen)
+
+BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
// parents provide tooltips first, which are optionally
- // overridden by children
+ // overridden by children, in case child is mouse_opaque
if (!mToolTipMsg.empty())
{
// allow "scrubbing" over ui by showing next tooltip immediately
@@ -703,7 +686,7 @@ BOOL LLView::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_s
F32 timeout = LLToolTipMgr::instance().toolTipVisible()
? 0.f
: LLUI::sSettingGroups["config"]->getF32( "ToolTipDelay" );
- LLToolTipMgr::instance().show(LLToolTipParams()
+ LLToolTipMgr::instance().show(LLToolTip::Params()
.message(mToolTipMsg)
.sticky_rect(calcScreenRect())
.delay_time(timeout));
@@ -712,7 +695,7 @@ BOOL LLView::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_s
}
// child tooltips will override our own
- LLView* child_handler = childrenHandleToolTip(x, y, msg, sticky_rect_screen);
+ LLView* child_handler = childrenHandleToolTip(x, y, mask);
if (child_handler)
{
handled = TRUE;
@@ -720,7 +703,6 @@ BOOL LLView::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_s
return handled;
}
-
BOOL LLView::handleKey(KEY key, MASK mask, BOOL called_from_parent)
{
BOOL handled = FALSE;
@@ -801,20 +783,7 @@ BOOL LLView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
EAcceptance* accept,
std::string& tooltip_msg)
{
- // CRO this is an experiment to allow drag and drop into object inventory based on the DragAndDrop tool's permissions rather than the parent
- BOOL handled = childrenHandleDragAndDrop( x, y, mask, drop,
- cargo_type,
- cargo_data,
- accept,
- tooltip_msg) != NULL;
- if( !handled && blockMouseEvent(x, y) )
- {
- *accept = ACCEPT_NO;
- handled = TRUE;
- lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLView " << getName() << llendl;
- }
-
- return handled;
+ return childrenHandleDragAndDrop( x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL;
}
LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask,
@@ -824,28 +793,33 @@ LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask,
EAcceptance* accept,
std::string& tooltip_msg)
{
- LLView* handled_view = FALSE;
- // CRO this is an experiment to allow drag and drop into object inventory based on the DragAndDrop tool's permissions rather than the parent
- if( getVisible() )
-// if( getVisible() && getEnabled() )
+ LLView* handled_view = NULL;
+ for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
{
- for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ if( !viewp->pointInView(local_x, local_y) ||
+ !viewp->getVisible() ||
+ !viewp->getEnabled())
{
- LLView* viewp = *child_it;
- S32 local_x = x - viewp->getRect().mLeft;
- S32 local_y = y - viewp->getRect().mBottom;
- if( viewp->pointInView(local_x, local_y) &&
- viewp->getVisible() &&
- viewp->getEnabled() &&
- viewp->handleDragAndDrop(local_x, local_y, mask, drop,
- cargo_type,
- cargo_data,
- accept,
- tooltip_msg))
- {
- handled_view = viewp;
- break;
- }
+ continue;
+ }
+ if (viewp->handleDragAndDrop(local_x, local_y, mask, drop,
+ cargo_type,
+ cargo_data,
+ accept,
+ tooltip_msg))
+ {
+ handled_view = viewp;
+ break;
+ }
+
+ if (viewp->blockMouseEvent(x, y))
+ {
+ *accept = ACCEPT_NO;
+ handled_view = viewp;
+ break;
}
}
return handled_view;
@@ -862,105 +836,42 @@ BOOL LLView::hasMouseCapture()
BOOL LLView::handleMouseUp(S32 x, S32 y, MASK mask)
{
- BOOL handled = childrenHandleMouseUp( x, y, mask ) != NULL;
- if( !handled && blockMouseEvent(x, y) )
- {
- handled = TRUE;
- }
- return handled;
+ return childrenHandleMouseUp( x, y, mask ) != NULL;
}
BOOL LLView::handleMouseDown(S32 x, S32 y, MASK mask)
{
- LLView* handled_view = childrenHandleMouseDown( x, y, mask );
- BOOL handled = (handled_view != NULL);
- if( !handled && blockMouseEvent(x, y) )
- {
- handled = TRUE;
- handled_view = this;
- }
-
- //// HACK If we're editing UI, select the leaf view that ate the click.
- //if (sEditingUI && handled_view)
- //{
- // // need to find leaf views, big hack
- // LLButton* buttonp = dynamic_cast<LLButton*>(handled_view);
- // LLLineEditor* line_editorp = dynamic_cast<LLLineEditor*>(handled_view);
- // LLTextEditor* text_editorp = dynamic_cast<LLTextEditor*>(handled_view);
- // LLTextBox* text_boxp = dynamic_cast<LLTextBox*>(handled_view);
- // if (buttonp
- // || line_editorp
- // || text_editorp
- // || text_boxp)
- // {
- // sEditingUIView = handled_view;
- // }
- //}
-
- return handled;
+ return childrenHandleMouseDown( x, y, mask ) != NULL;
}
BOOL LLView::handleDoubleClick(S32 x, S32 y, MASK mask)
{
- BOOL handled = childrenHandleDoubleClick( x, y, mask ) != NULL;
- if( !handled && blockMouseEvent(x, y) )
- {
- handleMouseDown(x, y, mask);
- handled = TRUE;
- }
- return handled;
+ return childrenHandleDoubleClick( x, y, mask ) != NULL;
}
BOOL LLView::handleScrollWheel(S32 x, S32 y, S32 clicks)
{
- if( getVisible() && getEnabled() )
- {
- return childrenHandleScrollWheel( x, y, clicks ) != NULL;
- }
- return FALSE;
+ return childrenHandleScrollWheel( x, y, clicks ) != NULL;
}
BOOL LLView::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
- BOOL handled = childrenHandleRightMouseDown( x, y, mask ) != NULL;
- if( !handled && blockMouseEvent(x, y) )
- {
- handled = TRUE;
- }
- return handled;
+ return childrenHandleRightMouseDown( x, y, mask ) != NULL;
}
BOOL LLView::handleRightMouseUp(S32 x, S32 y, MASK mask)
{
- BOOL handled = childrenHandleRightMouseUp( x, y, mask ) != NULL;
- if( !handled && blockMouseEvent(x, y) )
- {
- handled = TRUE;
- }
- return handled;
+ return childrenHandleRightMouseUp( x, y, mask ) != NULL;
}
BOOL LLView::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
{
- LLView* handled_view = childrenHandleMiddleMouseDown( x, y, mask );
- BOOL handled = (handled_view != NULL);
- if( !handled && blockMouseEvent(x, y) )
- {
- handled = TRUE;
- handled_view = this;
- }
-
- return handled;
+ return childrenHandleMiddleMouseDown( x, y, mask ) != NULL;
}
BOOL LLView::handleMiddleMouseUp(S32 x, S32 y, MASK mask)
{
- BOOL handled = childrenHandleMiddleMouseUp( x, y, mask ) != NULL;
- if( !handled && blockMouseEvent(x, y) )
- {
- handled = TRUE;
- }
- return handled;
+ return childrenHandleMiddleMouseUp( x, y, mask ) != NULL;
}
@@ -974,10 +885,14 @@ LLView* LLView::childrenHandleScrollWheel(S32 x, S32 y, S32 clicks)
LLView* viewp = *child_it;
S32 local_x = x - viewp->getRect().mLeft;
S32 local_y = y - viewp->getRect().mBottom;
- if (viewp->pointInView(local_x, local_y)
- && viewp->getVisible()
- && viewp->getEnabled()
- && viewp->handleScrollWheel( local_x, local_y, clicks ))
+ if (!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible()
+ || !viewp->getEnabled())
+ {
+ continue;
+ }
+
+ if (viewp->handleScrollWheel( local_x, local_y, clicks ))
{
if (sDebugMouseHandling)
{
@@ -987,6 +902,12 @@ LLView* LLView::childrenHandleScrollWheel(S32 x, S32 y, S32 clicks)
handled_view = viewp;
break;
}
+
+ if (viewp->blockMouseEvent(local_x, local_y))
+ {
+ handled_view = viewp;
+ break;
+ }
}
}
return handled_view;
@@ -1002,10 +923,14 @@ LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask)
LLView* viewp = *child_it;
S32 local_x = x - viewp->getRect().mLeft;
S32 local_y = y - viewp->getRect().mBottom;
- if(viewp->pointInView(local_x, local_y) &&
- viewp->getVisible() &&
- viewp->getEnabled() &&
- viewp->handleHover(local_x, local_y, mask) )
+ if(!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible()
+ || !viewp->getEnabled())
+ {
+ continue;
+ }
+
+ if (viewp->handleHover(local_x, local_y, mask) )
{
if (sDebugMouseHandling)
{
@@ -1015,6 +940,14 @@ LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask)
handled_view = viewp;
break;
}
+
+ if (viewp->blockMouseEvent(local_x, local_y))
+ {
+ LLUI::sWindow->setCursor(viewp->getHoverCursor());
+
+ handled_view = viewp;
+ break;
+ }
}
}
return handled_view;
@@ -1080,10 +1013,14 @@ LLView* LLView::childrenHandleMouseDown(S32 x, S32 y, MASK mask)
S32 local_x = x - viewp->getRect().mLeft;
S32 local_y = y - viewp->getRect().mBottom;
- if (viewp->pointInView(local_x, local_y) &&
- viewp->getVisible() &&
- viewp->getEnabled() &&
- viewp->handleMouseDown( local_x, local_y, mask ))
+ if (!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible()
+ || !viewp->getEnabled())
+ {
+ continue;
+ }
+
+ if(viewp->handleMouseDown( local_x, local_y, mask ))
{
if (sDebugMouseHandling)
{
@@ -1092,6 +1029,12 @@ LLView* LLView::childrenHandleMouseDown(S32 x, S32 y, MASK mask)
handled_view = viewp;
break;
}
+
+ if(viewp->blockMouseEvent(local_x, local_y))
+ {
+ handled_view = viewp;
+ break;
+ }
}
return handled_view;
}
@@ -1107,10 +1050,15 @@ LLView* LLView::childrenHandleRightMouseDown(S32 x, S32 y, MASK mask)
LLView* viewp = *child_it;
S32 local_x = x - viewp->getRect().mLeft;
S32 local_y = y - viewp->getRect().mBottom;
- if (viewp->pointInView(local_x, local_y) &&
- viewp->getVisible() &&
- viewp->getEnabled() &&
- viewp->handleRightMouseDown( local_x, local_y, mask ))
+
+ if (!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible()
+ || !viewp->getEnabled())
+ {
+ continue;
+ }
+
+ if (viewp->handleRightMouseDown( local_x, local_y, mask ))
{
if (sDebugMouseHandling)
{
@@ -1120,6 +1068,12 @@ LLView* LLView::childrenHandleRightMouseDown(S32 x, S32 y, MASK mask)
handled_view = viewp;
break;
}
+
+ if (viewp->blockMouseEvent(local_x, local_y))
+ {
+ handled_view = viewp;
+ break;
+ }
}
}
return handled_view;
@@ -1136,10 +1090,14 @@ LLView* LLView::childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask)
LLView* viewp = *child_it;
S32 local_x = x - viewp->getRect().mLeft;
S32 local_y = y - viewp->getRect().mBottom;
- if (viewp->pointInView(local_x, local_y) &&
- viewp->getVisible() &&
- viewp->getEnabled() &&
- viewp->handleMiddleMouseDown( local_x, local_y, mask ))
+ if (!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible()
+ || !viewp->getEnabled())
+ {
+ continue;
+ }
+
+ if(viewp->handleMiddleMouseDown( local_x, local_y, mask ))
{
if (sDebugMouseHandling)
{
@@ -1148,6 +1106,12 @@ LLView* LLView::childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask)
handled_view = viewp;
break;
}
+
+ if (viewp->blockMouseEvent(local_x, local_y))
+ {
+ handled_view = viewp;
+ break;
+ }
}
}
return handled_view;
@@ -1164,10 +1128,15 @@ LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask)
LLView* viewp = *child_it;
S32 local_x = x - viewp->getRect().mLeft;
S32 local_y = y - viewp->getRect().mBottom;
- if (viewp->pointInView(local_x, local_y) &&
- viewp->getVisible() &&
- viewp->getEnabled() &&
- viewp->handleDoubleClick( local_x, local_y, mask ))
+
+ if (!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible()
+ || !viewp->getEnabled())
+ {
+ continue;
+ }
+
+ if (viewp->handleDoubleClick( local_x, local_y, mask ))
{
if (sDebugMouseHandling)
{
@@ -1176,6 +1145,12 @@ LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask)
handled_view = viewp;
break;
}
+
+ if (viewp->blockMouseEvent(local_x, local_y))
+ {
+ handled_view = viewp;
+ break;
+ }
}
}
return handled_view;
@@ -1191,12 +1166,13 @@ LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask)
LLView* viewp = *child_it;
S32 local_x = x - viewp->getRect().mLeft;
S32 local_y = y - viewp->getRect().mBottom;
- if (!viewp->pointInView(local_x, local_y))
- continue;
- if (!viewp->getVisible())
- continue;
- if (!viewp->getEnabled())
+ if (!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible()
+ || !viewp->getEnabled())
+ {
continue;
+ }
+
if (viewp->handleMouseUp( local_x, local_y, mask ))
{
if (sDebugMouseHandling)
@@ -1206,6 +1182,12 @@ LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask)
handled_view = viewp;
break;
}
+
+ if (viewp->blockMouseEvent(local_x, local_y))
+ {
+ handled_view = viewp;
+ break;
+ }
}
}
return handled_view;
@@ -1221,10 +1203,14 @@ LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask)
LLView* viewp = *child_it;
S32 local_x = x - viewp->getRect().mLeft;
S32 local_y = y - viewp->getRect().mBottom;
- if (viewp->pointInView(local_x, local_y) &&
- viewp->getVisible() &&
- viewp->getEnabled() &&
- viewp->handleRightMouseUp( local_x, local_y, mask ))
+ if (!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible()
+ || !viewp->getEnabled() )
+ {
+ continue;
+ }
+
+ if(viewp->handleRightMouseUp( local_x, local_y, mask ))
{
if (sDebugMouseHandling)
{
@@ -1233,6 +1219,12 @@ LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask)
handled_view = viewp;
break;
}
+
+ if(viewp->blockMouseEvent(local_x, local_y))
+ {
+ handled_view = viewp;
+ break;
+ }
}
}
return handled_view;
@@ -1248,10 +1240,14 @@ LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask)
LLView* viewp = *child_it;
S32 local_x = x - viewp->getRect().mLeft;
S32 local_y = y - viewp->getRect().mBottom;
- if (viewp->pointInView(local_x, local_y) &&
- viewp->getVisible() &&
- viewp->getEnabled() &&
- viewp->handleMiddleMouseUp( local_x, local_y, mask ))
+ if (!viewp->pointInView(local_x, local_y)
+ || !viewp->getVisible()
+ || !viewp->getEnabled())
+ {
+ continue;
+ }
+
+ if(viewp->handleMiddleMouseUp( local_x, local_y, mask ))
{
if (sDebugMouseHandling)
{
@@ -1260,6 +1256,12 @@ LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask)
handled_view = viewp;
break;
}
+
+ if (viewp->blockMouseEvent(local_x, local_y))
+ {
+ handled_view = viewp;
+ break;
+ }
}
}
return handled_view;
@@ -1272,18 +1274,6 @@ void LLView::draw()
void LLView::drawChildren()
{
- if (sDebugRects)
- {
- drawDebugRect();
-
- // Check for bogus rectangle
- if (getRect().mRight <= getRect().mLeft
- || getRect().mTop <= getRect().mBottom)
- {
- llwarns << "Bogus rectangle for " << getName() << " with " << mRect << llendl;
- }
- }
-
if (!mChildList.empty())
{
LLRect rootRect = getRootView()->getRect();
@@ -1307,6 +1297,17 @@ void LLView::drawChildren()
{
LLUI::translate((F32)viewp->getRect().mLeft, (F32)viewp->getRect().mBottom, 0.f);
viewp->draw();
+
+ if (sDebugRects)
+ {
+ viewp->drawDebugRect();
+
+ // Check for bogus rectangle
+ if (!getRect().isValid())
+ {
+ llwarns << "Bogus rectangle for " << getName() << " with " << mRect << llendl;
+ }
+ }
}
LLUI::popMatrix();
}
@@ -1353,10 +1354,6 @@ void LLView::drawDebugRect()
// draw red rectangle for the border
LLColor4 border_color(0.f, 0.f, 0.f, 1.f);
- //if (sEditingUI)
- //{
- // border_color.mV[0] = 1.f;
- //}
if(preview_iter != sPreviewHighlightedElements.end())
{
if(LLView::sPreviewClickedElement && this == sPreviewClickedElement)
diff --git a/indra/llui/llview.h b/indra/llui/llview.h
index 7a37d6f430..7ddff2bd9e 100644
--- a/indra/llui/llview.h
+++ b/indra/llui/llview.h
@@ -42,8 +42,6 @@
#include "llfontgl.h"
#include "llmortician.h"
#include "llmousehandler.h"
-#include "llnametable.h"
-#include "llsd.h"
#include "llstring.h"
#include "llrect.h"
#include "llui.h"
@@ -58,6 +56,8 @@
#include <list>
+class LLSD;
+
const U32 FOLLOWS_NONE = 0x00;
const U32 FOLLOWS_LEFT = 0x01;
const U32 FOLLOWS_RIGHT = 0x02;
@@ -124,21 +124,16 @@ public:
Optional<bool> enabled,
visible,
mouse_opaque,
- use_bounding_rect;
+ use_bounding_rect,
+ from_xui;
Optional<S32> tab_group,
default_tab_group;
Optional<std::string> tool_tip;
Optional<S32> sound_flags;
- Optional<bool> from_xui;
Optional<Follows> follows;
Optional<std::string> hover_cursor;
-
- // font params
- Optional<const LLFontGL*> font;
- Optional<LLFontGL::HAlign> font_halign;
- Optional<LLFontGL::VAlign> font_valign;
Optional<std::string> layout;
Optional<LLRect> rect;
@@ -223,9 +218,6 @@ public:
virtual ~LLView();
- // Hack to support LLFocusMgr (from LLMouseHandler)
- /*virtual*/ BOOL isView() const;
-
// Some UI widgets need to be added as controls. Others need to
// be added as regular view children. isCtrl should return TRUE
// if a widget needs to be added as a ctrl
@@ -258,6 +250,8 @@ public:
void setUseBoundingRect( BOOL use_bounding_rect );
BOOL getUseBoundingRect();
+ ECursorType getHoverCursor() { return mHoverCursor; }
+
const std::string& getToolTip() const { return mToolTipMsg.getString(); }
void sendChildToFront(LLView* child);
@@ -443,12 +437,11 @@ public:
/*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
/*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
/*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask);
- /*virtual*/ BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect); // Display mToolTipMsg if no child handles it.
+ /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask);
- /*virtual*/ const std::string& getName() const;
+ /*virtual*/ std::string getName() const;
/*virtual*/ void onMouseCaptureLost();
/*virtual*/ BOOL hasMouseCapture();
- /*virtual*/ BOOL isView(); // Hack to support LLFocusMgr
/*virtual*/ void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const;
/*virtual*/ void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const;
@@ -545,7 +538,7 @@ protected:
LLView* childrenHandleScrollWheel(S32 x, S32 y, S32 clicks);
LLView* childrenHandleRightMouseDown(S32 x, S32 y, MASK mask);
LLView* childrenHandleRightMouseUp(S32 x, S32 y, MASK mask);
- LLView* childrenHandleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect);
+ LLView* childrenHandleToolTip(S32 x, S32 y, MASK mask);
ECursorType mHoverCursor;
diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp
index 1e7a0f7f2c..468fae2ec5 100644
--- a/indra/llui/tests/llurlentry_test.cpp
+++ b/indra/llui/tests/llurlentry_test.cpp
@@ -24,9 +24,17 @@
#include "../llurlentry.h"
#include "llurlentry_stub.cpp"
#include "lltut.h"
+#include "../lluicolortable.h"
#include <boost/regex.hpp>
+LLUIColor LLUIColorTable::getColor(const std::string& name, const LLColor4& default_color) const
+{
+ return LLUIColor();
+}
+
+LLUIColor::LLUIColor() {}
+
namespace tut
{
struct LLUrlEntryData
@@ -276,6 +284,11 @@ namespace tut
testRegex("Agent Url multicase", r,
"XXX secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/About XXX",
"secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/About");
+
+ testRegex("Agent Url alternate command", r,
+ "XXX secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar",
+ "secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar");
+
}
template<> template<>
diff --git a/indra/llui/tests/llurlmatch_test.cpp b/indra/llui/tests/llurlmatch_test.cpp
index 4dae49db90..e8cf135346 100644
--- a/indra/llui/tests/llurlmatch_test.cpp
+++ b/indra/llui/tests/llurlmatch_test.cpp
@@ -23,6 +23,10 @@
#include "../llurlmatch.h"
#include "lltut.h"
+// link seam
+LLUIColor::LLUIColor()
+{}
+
namespace tut
{
struct LLUrlMatchData
@@ -49,7 +53,7 @@ namespace tut
LLUrlMatch match;
ensure("empty()", match.empty());
- match.setValues(0, 1, "http://secondlife.com", "Second Life", "", "", "", "");
+ match.setValues(0, 1, "http://secondlife.com", "Second Life", "", "", LLUIColor(), "", "");
ensure("! empty()", ! match.empty());
}
@@ -62,7 +66,7 @@ namespace tut
LLUrlMatch match;
ensure_equals("getStart() == 0", match.getStart(), 0);
- match.setValues(10, 20, "", "", "", "", "", "");
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "");
ensure_equals("getStart() == 10", match.getStart(), 10);
}
@@ -75,7 +79,7 @@ namespace tut
LLUrlMatch match;
ensure_equals("getEnd() == 0", match.getEnd(), 0);
- match.setValues(10, 20, "", "", "", "", "", "");
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "");
ensure_equals("getEnd() == 20", match.getEnd(), 20);
}
@@ -88,10 +92,10 @@ namespace tut
LLUrlMatch match;
ensure_equals("getUrl() == ''", match.getUrl(), "");
- match.setValues(10, 20, "http://slurl.com/", "", "", "", "", "");
+ match.setValues(10, 20, "http://slurl.com/", "", "", "", LLUIColor(), "", "");
ensure_equals("getUrl() == 'http://slurl.com/'", match.getUrl(), "http://slurl.com/");
- match.setValues(10, 20, "", "", "", "", "", "");
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "");
ensure_equals("getUrl() == '' (2)", match.getUrl(), "");
}
@@ -104,10 +108,10 @@ namespace tut
LLUrlMatch match;
ensure_equals("getLabel() == ''", match.getLabel(), "");
- match.setValues(10, 20, "", "Label", "", "", "", "");
+ match.setValues(10, 20, "", "Label", "", "", LLUIColor(), "", "");
ensure_equals("getLabel() == 'Label'", match.getLabel(), "Label");
- match.setValues(10, 20, "", "", "", "", "", "");
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "");
ensure_equals("getLabel() == '' (2)", match.getLabel(), "");
}
@@ -120,10 +124,10 @@ namespace tut
LLUrlMatch match;
ensure_equals("getTooltip() == ''", match.getTooltip(), "");
- match.setValues(10, 20, "", "", "Info", "", "", "");
+ match.setValues(10, 20, "", "", "Info", "", LLUIColor(), "", "");
ensure_equals("getTooltip() == 'Info'", match.getTooltip(), "Info");
- match.setValues(10, 20, "", "", "", "", "", "");
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "");
ensure_equals("getTooltip() == '' (2)", match.getTooltip(), "");
}
@@ -136,10 +140,10 @@ namespace tut
LLUrlMatch match;
ensure_equals("getIcon() == ''", match.getIcon(), "");
- match.setValues(10, 20, "", "", "", "Icon", "", "");
+ match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "", "");
ensure_equals("getIcon() == 'Icon'", match.getIcon(), "Icon");
- match.setValues(10, 20, "", "", "", "", "", "");
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "");
ensure_equals("getIcon() == '' (2)", match.getIcon(), "");
}
@@ -152,10 +156,10 @@ namespace tut
LLUrlMatch match;
ensure("getMenuName() empty", match.getMenuName().empty());
- match.setValues(10, 20, "", "", "", "Icon", "xui_file.xml", "");
+ match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "");
ensure_equals("getMenuName() == \"xui_file.xml\"", match.getMenuName(), "xui_file.xml");
- match.setValues(10, 20, "", "", "", "", "", "");
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "");
ensure("getMenuName() empty (2)", match.getMenuName().empty());
}
@@ -168,10 +172,10 @@ namespace tut
LLUrlMatch match;
ensure("getLocation() empty", match.getLocation().empty());
- match.setValues(10, 20, "", "", "", "Icon", "xui_file.xml", "Paris");
+ match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "Paris");
ensure_equals("getLocation() == \"Paris\"", match.getLocation(), "Paris");
- match.setValues(10, 20, "", "", "", "", "", "");
+ match.setValues(10, 20, "", "", "", "", LLUIColor(), "", "");
ensure("getLocation() empty (2)", match.getLocation().empty());
}
}