From ada0f4fa221f2c7070fb02a2b7ff903bdde11c45 Mon Sep 17 00:00:00 2001 From: James Cook Date: Sat, 3 Oct 2009 23:40:28 +0000 Subject: 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 --- indra/llui/CMakeLists.txt | 4 + indra/llui/llbutton.cpp | 6 +- indra/llui/llbutton.h | 3 + indra/llui/llcheckboxctrl.cpp | 2 +- indra/llui/llcombobox.cpp | 6 +- indra/llui/llcombobox.h | 2 +- indra/llui/llconsole.cpp | 4 +- indra/llui/llconsole.h | 7 +- indra/llui/lldraghandle.cpp | 9 +- indra/llui/lldraghandle.h | 6 +- indra/llui/llfloater.cpp | 2 +- indra/llui/llfloater.h | 2 +- indra/llui/llfocusmgr.cpp | 2 +- indra/llui/lllayoutstack.cpp | 2 + indra/llui/lllayoutstack.h | 4 +- indra/llui/lllineeditor.cpp | 16 +- indra/llui/lllineeditor.h | 6 +- indra/llui/llmenubutton.cpp | 135 ++ indra/llui/llmenubutton.h | 68 + indra/llui/llmenugl.cpp | 96 +- indra/llui/llmenugl.h | 12 +- indra/llui/llmultisliderctrl.cpp | 2 +- indra/llui/llpanel.cpp | 9 - indra/llui/llpanel.h | 6 +- indra/llui/llradiogroup.cpp | 2 + indra/llui/llradiogroup.h | 13 +- indra/llui/llscrollbar.cpp | 13 - indra/llui/llscrollbar.h | 3 - indra/llui/llscrollcontainer.cpp | 50 +- indra/llui/llscrollcontainer.h | 4 +- indra/llui/llscrolllistcell.cpp | 7 + indra/llui/llscrolllistcell.h | 13 +- indra/llui/llscrolllistctrl.cpp | 64 +- indra/llui/llscrolllistctrl.h | 6 +- indra/llui/llscrolllistitem.cpp | 1 - indra/llui/llscrolllistitem.h | 2 - indra/llui/llsliderctrl.cpp | 2 +- indra/llui/llspinctrl.cpp | 2 +- indra/llui/llstatview.cpp | 2 + indra/llui/llstyle.cpp | 2 +- indra/llui/llstyle.h | 16 +- indra/llui/lltabcontainer.cpp | 8 +- indra/llui/lltabcontainer.h | 2 +- indra/llui/lltextbase.cpp | 2277 +++++++++++++++++++++++++++++++--- indra/llui/lltextbase.h | 382 +++++- indra/llui/lltextbox.cpp | 636 +--------- indra/llui/lltextbox.h | 95 +- indra/llui/lltexteditor.cpp | 2141 +++----------------------------- indra/llui/lltexteditor.h | 293 +---- indra/llui/lltextparser.cpp | 4 +- indra/llui/lltextparser.h | 12 +- indra/llui/lltoggleablemenu.cpp | 82 ++ indra/llui/lltoggleablemenu.h | 65 + indra/llui/lltooltip.cpp | 334 ++--- indra/llui/lltooltip.h | 90 +- indra/llui/llui.cpp | 49 +- indra/llui/llui.h | 5 +- indra/llui/lluictrl.cpp | 30 +- indra/llui/lluictrl.h | 34 +- indra/llui/lluictrlfactory.cpp | 14 +- indra/llui/llurlentry.cpp | 10 +- indra/llui/llurlentry.h | 20 +- indra/llui/llurlmatch.cpp | 5 +- indra/llui/llurlmatch.h | 20 +- indra/llui/llurlregistry.cpp | 8 +- indra/llui/llview.cpp | 377 +++--- indra/llui/llview.h | 25 +- indra/llui/tests/llurlentry_test.cpp | 13 + indra/llui/tests/llurlmatch_test.cpp | 34 +- 69 files changed, 3848 insertions(+), 3830 deletions(-) create mode 100644 indra/llui/llmenubutton.cpp create mode 100644 indra/llui/llmenubutton.h create mode 100644 indra/llui/lltoggleablemenu.cpp create mode 100644 indra/llui/lltoggleablemenu.h (limited to 'indra/llui') 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 llbutton_orig_h_pad ("UIButtonOrigHPad", 0); static Params default_params(LLUICtrlFactory::getDefaultParams()); @@ -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& 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 -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 + struct Params : public LLInitParam::Block { Optional max_lines; Optional 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(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 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 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(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 + { + // filename for it's toggleable menu + Optional 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 on_enable; Optional on_click; + Optional 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 (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(id); - if (child) - { - child->setVisible(visible); - child->setWrappedText(text); - } -} void LLPanel::childSetAction(const std::string& id, boost::function 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 - { - 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::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 is_opaque, reserve_scroll_corner, - border_visible; + border_visible, + hide_scrollbar; Optional min_auto_scroll_rate, max_auto_scroll_rate; Optional 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 (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 r1("stat_bar"); static StatViewRegistry::Register r2("stat_view"); +// stat_view can be a child of panels/etc. +static LLDefaultChildRegistry::Register 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 { - Optional visible, - drop_shadow; - Optional color; - Optional font; - Optional image; - Optional link_href; + Optional visible; + Optional drop_shadow; + Optional color; + Optional font; + Optional image; + Optional 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 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 (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,23 +34,2009 @@ #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 + +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(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(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 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::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 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 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(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(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; +} -#include +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; + } -// global state for all text fields -LLUIColor LLTextBase::mLinkColor = LLColor4::blue; + // clamp pos to valid values + pos = llclamp(pos, 0, mLineInfoList.back().mDocIndexEnd - 1); -bool LLTextBase::compare_segment_end::operator()(const LLTextSegmentPtr& a, const LLTextSegmentPtr& b) const + + // 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() { - return a->getEnd() < b->getEnd(); + // 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; + } } // @@ -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() -{ - clearSegments(); -} - -void LLTextBase::clearSegments() -{ - setHoverSegment(NULL); - mSegments.clear(); -} - -void LLTextBase::setHoverSegment(LLTextSegmentPtr segment) +LLInlineViewSegment::LLInlineViewSegment(LLView* view, S32 start, S32 end) +: LLTextSegment(start, end), + mView(view) { - if (mHoverSegment) - { - 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 +LLInlineViewSegment::~LLInlineViewSegment() { - *seg_iter = getSegIterContaining(startpos); - if (*seg_iter == mSegments.end()) - { - *offsetp = 0; - } - else - { - *offsetp = startpos - (**seg_iter)->getStart(); - } + mView->die(); } -void LLTextBase::getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp ) +S32 LLInlineViewSegment::getWidth(S32 first_char, S32 num_chars) const { - *seg_iter = getSegIterContaining(startpos); - if (*seg_iter == mSegments.end()) + if (first_char == 0 && num_chars == 0) { - *offsetp = 0; + return 0; } else { - *offsetp = startpos - (**seg_iter)->getStart(); + return mView->getRect().getWidth(); } } -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 ) +S32 LLInlineViewSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) 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()) + if (line_offset != 0 && num_pixels < mView->getRect().getWidth()) { - return *seg_iter; + return 0; } else { - return LLTextSegmentPtr(); + return mEnd - mStart; } } -BOOL LLTextBase::handleHoverOverUrl(S32 x, S32 y) +void LLInlineViewSegment::updateLayout(const 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; + 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); } -BOOL LLTextBase::handleMouseUpOverUrl(S32 x, S32 y) +F32 LLInlineViewSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect) { - if (mParseHTML && mHoverSegment) - { - LLStyleSP style = mHoverSegment->getStyle(); - if (style && style->isLink()) - { - LLUrlAction::clickAction(style->getLinkHREF()); - return TRUE; - } - } - - return FALSE; + return (F32)(draw_rect.mLeft + mView->getRect().getWidth()); } -BOOL LLTextBase::handleRightMouseDownOverUrl(LLView *view, S32 x, S32 y) +S32 LLInlineViewSegment::getMaxHeight() const { - // 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; + return mView->getRect().getHeight(); } -BOOL LLTextBase::handleToolTipForUrl(LLView *view, S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen) +void LLInlineViewSegment::unlinkFromDocument(LLTextBase* editor) { - 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(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 #include +#include + class LLContextMenu; class LLTextSegment; @@ -52,64 +55,307 @@ typedef LLPointer 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 + { + Alternative multiple; + Alternative pixels; + LineSpacingParams(); + }; + + struct Params : public LLInitParam::Block + { + Optional cursor_color, + text_color, + text_readonly_color, + bg_readonly_color, + bg_writeable_color, + bg_focus_color; + + Optional bg_visible, + border_visible, + track_end, + read_only, + hide_scrollbar, + clip_to_rect, + wrap, + use_ellipses, + allow_html, + parse_highlights; + + Optional v_pad, + h_pad; + + Optional + line_spacing; + + Optional max_text_length; + + Optional 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 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 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_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 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 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::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 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::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::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 callback_t; - struct Params : public LLInitParam::Block - { - Optional text; - - Optional border_visible, - border_drop_shadow_visible, - bg_visible, - use_ellipses, - word_wrap; - - Optional font_shadow; - - Ignored drop_shadow_visible, - type, - length; - - Optional text_color, - disabled_color, - background_color; - - Optional v_pad, - h_pad, - line_spacing; - - Params(); - }; + struct Params : public LLInitParam::Block + {}; 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 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 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 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 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(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(panel_params); - mScroller->addChild(mDocumentPanel); - - updateTextRect(); - - static LLUICachedControl 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 (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 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,272 +481,58 @@ 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. +// virtual +BOOL LLTextEditor::canDeselect() const +{ + return hasSelection(); +} + -S32 LLTextEditor::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const +void LLTextEditor::deselect() { - // Figure out which line we're nearest to. - LLRect visible_region = mScroller->getVisibleContentRect(); + mSelectionStart = 0; + mSelectionEnd = 0; + mIsSelecting = FALSE; +} - // 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()) +BOOL LLTextEditor::selectionContainsLineBreaks() +{ + if (hasSelection()) { - 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 + S32 left = llmin(mSelectionStart, mSelectionEnd); + S32 right = left + llabs(mSelectionStart - mSelectionEnd); + + LLWString wtext = getWText(); + for( S32 i = left; i < right; i++ ) { - // 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 + if (wtext[i] == '\n') { - offset = segmentp->getOffset(local_x - start_x, line_seg_offset, segment_line_length, round); + return TRUE; } - pos = segment_line_start + offset; - break; } - start_x += text_width; } - - return pos; + return FALSE; } -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()); +S32 LLTextEditor::indentLine( S32 pos, S32 spaces ) +{ + // Assumes that pos is at the start of the line + // spaces may be positive (indent) or negative (unindent). + // Returns the actual number of characters added or removed. - 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); + llassert(pos >= 0); + llassert(pos <= getLength() ); - 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); + S32 delta_spaces = 0; - while(line_seg_iter != mSegments.end()) + if (spaces >= 0) { - 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 + // Indent + for(S32 i=0; i < spaces; i++) { - // 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 -{ - return hasSelection(); -} - - -void LLTextEditor::deselect() -{ - mSelectionStart = 0; - mSelectionEnd = 0; - mIsSelecting = FALSE; -} - - -void LLTextEditor::startSelection() -{ - if( !mIsSelecting ) - { - mIsSelecting = TRUE; - mSelectionStart = mCursorPos; - mSelectionEnd = mCursorPos; - } -} - -void LLTextEditor::endSelection() -{ - if( mIsSelecting ) - { - mIsSelecting = FALSE; - mSelectionEnd = mCursorPos; - } -} - -BOOL LLTextEditor::selectionContainsLineBreaks() -{ - if (hasSelection()) - { - S32 left = llmin(mSelectionStart, mSelectionEnd); - S32 right = left + llabs(mSelectionStart - mSelectionEnd); - - LLWString wtext = getWText(); - for( S32 i = left; i < right; i++ ) - { - if (wtext[i] == '\n') - { - return TRUE; - } - } - } - return FALSE; -} - - -S32 LLTextEditor::indentLine( S32 pos, S32 spaces ) -{ - // Assumes that pos is at the start of the line - // spaces may be positive (indent) or negative (unindent). - // Returns the actual number of characters added or removed. - - llassert(pos >= 0); - llassert(pos <= getLength() ); - - S32 delta_spaces = 0; - - if (spaces >= 0) - { - // Indent - for(S32 i=0; i < spaces; i++) - { - delta_spaces += addChar(pos, ' '); + delta_spaces += addChar(pos, ' '); } } else @@ -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 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 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::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 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,461 +2201,80 @@ void LLTextEditor::setFocus( BOOL new_state ) gEditMenuHandler = this; // Don't start the cursor flashing right away - resetKeystrokeTimer(); - } - else - { - // Route menu back to the default - if( gEditMenuHandler == this ) - { - gEditMenuHandler = NULL; - } - - endSelection(); - } -} - -// 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() -{ - deselect(); - 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(); -} - -void LLTextEditor::autoIndent() -{ - // Count the number of spaces in the current line - S32 line, offset; - getLineAndOffset( mCursorPos, &line, &offset ); - S32 line_start = getLineStart(line); - S32 space_count = 0; - S32 i; - - LLWString text = getWText(); - while( ' ' == text[line_start] ) - { - space_count++; - line_start++; - } - - // If we're starting a braced section, indent one level. - if( (mCursorPos > 0) && (text[mCursorPos -1] == '{') ) - { - space_count += SPACES_PER_TAB; - } - - // Insert that number of spaces on the new line - addChar( '\n' ); - for( i = 0; i < space_count; i++ ) - { - addChar( ' ' ); - } -} - -// Inserts new text at the cursor position -void LLTextEditor::insertText(const std::string &new_text) -{ - BOOL enabled = getEnabled(); - setEnabled( TRUE ); - - // Delete any selected characters (the insertion replaces them) - if( hasSelection() ) - { - deleteSelection(TRUE); - } - - setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), FALSE, LLTextSegmentPtr() )); - - needsReflow(); - - setEnabled( enabled ); -} - - -void LLTextEditor::appendColoredText(const std::string &new_text, - bool allow_undo, - bool prepend_newline, - const LLColor4 &color, - const std::string& font_name) -{ - LLColor4 lcolor=color; - if (mParseHighlights) - { - LLTextParser* highlight = LLTextParser::getInstance(); - highlight->parseFullLineHighlights(new_text, &lcolor); - } - - 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) + resetCursorBlink(); + } + else { - LLTextParser* highlight = LLTextParser::getInstance(); - - if (highlight && !style_params.isDefault()) + // Route menu back to the default + if( gEditMenuHandler == this ) { - 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 1) ) lprepend=FALSE; - appendText((std::string)pieces[i]["text"], allow_undo, lprepend, highlight_params); - } - return; + gEditMenuHandler = NULL; } + + endSelection(); } - 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) +// public +void LLTextEditor::setCursorAndScrollToEnd() { - 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(); + endOfDoc(); +} - setCursorPos(old_length); +void LLTextEditor::getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wordwrap ) +{ + *line = getLineNumFromDocIndex(mCursorPos, include_wordwrap); + *col = getLineOffsetFromDocIndex(mCursorPos, include_wordwrap); +} - LLWString wide_text; +void LLTextEditor::autoIndent() +{ + // Count the number of spaces in the current line + S32 line = getLineNumFromDocIndex(mCursorPos); + S32 line_start = getLineStart(line); + S32 space_count = 0; + S32 i; - // Add carriage return if not first line - if (getLength() != 0 - && prepend_newline) + LLWString text = getWText(); + while( ' ' == text[line_start] ) { - wide_text = utf8str_to_wstring(std::string("\n") + new_text); + space_count++; + line_start++; } - else + + // If we're starting a braced section, indent one level. + if( (mCursorPos > 0) && (text[mCursorPos -1] == '{') ) { - wide_text = utf8str_to_wstring(new_text); + space_count += SPACES_PER_TAB; } - LLTextSegmentPtr segmentp; - if (!stylep.isDefault()) + // Insert that number of spaces on the new line + addChar( '\n' ); + for( i = 0; i < space_count; i++ ) { - S32 segment_start = old_length; - S32 segment_end = old_length + wide_text.size(); - segmentp = new LLNormalTextSegment(new LLStyle(stylep), segment_start, segment_end, *this ); + addChar( ' ' ); } +} - append(wide_text, TRUE, segmentp); - - needsReflow(); - - // Set the cursor and scroll position - if( selection_start != selection_end ) - { - mSelectionStart = selection_start; - mSelectionEnd = selection_end; +// Inserts new text at the cursor position +void LLTextEditor::insertText(const std::string &new_text) +{ + BOOL enabled = getEnabled(); + setEnabled( TRUE ); - mIsSelecting = was_selecting; - setCursorPos(cursor_pos); - } - else if( cursor_was_at_end ) - { - setCursorPos(getLength()); - } - else + // Delete any selected characters (the insertion replaces them) + if( hasSelection() ) { - setCursorPos(cursor_pos); + deleteSelection(TRUE); } - if( !allow_undo ) - { - blockUndo(); - } -} + setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), FALSE, LLTextSegmentPtr() )); + + needsReflow(); + setEnabled( enabled ); +} void LLTextEditor::appendWidget(LLView* widget, const std::string &widget_text, bool allow_undo, bool prepend_newline) { @@ -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 texteditor_border ("UITextEditorBorder", 0); - static LLUICachedControl 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& funcs, const std::vector& 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(&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(editor); - if (ed) - { - ed->removeDocumentChild(mView); - } -} - -void LLInlineViewSegment::linkToDocument(LLTextBase* editor) -{ - LLTextEditor *ed = dynamic_cast(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 + struct Params : public LLInitParam::Block { Optional default_text; - Optional max_text_length; - Optional read_only, - embedded_items, - word_wrap, + Optional embedded_items, ignore_tab, - hide_border, - track_bottom, handle_edit_keys_directly, show_line_numbers, commit_on_focus_lost; //colors - Optional cursor_color, - default_color, - text_color, - text_readonly_color, - bg_readonly_color, - bg_writeable_color, - bg_focus_color, - link_color; - - Optional border; - - Ignored type, - length, - is_unicode, - hide_scrollbar; + Optional 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 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& 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 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 mPreeditPositions; std::vector 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 undo_stack_t; + typedef std::deque 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_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 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 + {}; +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 - { - Mandatory visible_time; - - Optional click_callback; - Optional 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 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 (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 (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 (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() - { - 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 +class LLToolTip : public LLPanel { - Mandatory x, - y; - LLToolTipPosParams() - : x("x"), - y("y") - {} -}; +public: + struct Params : public LLInitParam::Block + { + typedef boost::function click_callback_t; -struct LLToolTipParams : public LLInitParam::Block -{ - typedef boost::function click_callback_t; + Mandatory message; + + Optional pos; + Optional 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 sticky_rect; + Optional font; - Mandatory message; - - Optional pos; - Optional delay_time, - visible_time; - Optional sticky_rect; - Optional width; - Optional image; + Optional click_callback; + Optional image; + Optional max_width; + Optional padding; + + Params(); + }; + /*virtual*/ void draw(); + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); - Optional 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 { 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 gUntranslated; static LLDefaultChildRegistry::Register register_filter_editor("filter_editor"); static LLDefaultChildRegistry::Register register_flyout_button("flyout_button"); static LLDefaultChildRegistry::Register register_search_editor("search_editor"); +static LLDefaultChildRegistry::Register register_menu_button("menu_button"); // @@ -1643,6 +1645,17 @@ void LLUI::setMousePositionScreen(S32 x, S32 y) LLView::getWindow()->setCursorPosition(window_point); } +//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) { @@ -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::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::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::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 super_t; public: - Optional name, - size, + Mandatory name; + Optional 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 +#include #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 enable_callback_t; typedef boost::signals2::signal enable_signal_t; + typedef boost::function visible_callback_t; + typedef boost::signals2::signal visible_signal_t; + struct CallbackParam : public LLInitParam::Block { Ignored name; @@ -91,6 +95,11 @@ public: Optional function; }; + struct VisibleCallbackParam : public LLInitParam::Block + { + Optional function; + }; + struct EnableControls : public LLInitParam::Choice { Alternative enabled; @@ -107,9 +116,12 @@ public: Alternative 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 { @@ -128,6 +140,15 @@ public: Optional enabled_controls; Optional controls_visibility; + // font params + Optional font; + Optional font_halign; + Optional 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{}; class EnableCallbackRegistry : public CallbackRegistry{}; + class VisibleCallbackRegistry : public CallbackRegistry{}; + 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 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 #include #include @@ -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 mObservers; + boost::regex mPattern; + std::string mIcon; + std::string mMenuName; + std::string mTooltip; + LLUIColor mColor; + std::multimap 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 #include +#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), @@ -171,12 +168,6 @@ LLView::~LLView() } } -// virtual -BOOL LLView::isView() const -{ - return TRUE; -} - // virtual BOOL LLView::isCtrl() const { @@ -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(handled_view); - // LLLineEditor* line_editorp = dynamic_cast(handled_view); - // LLTextEditor* text_editorp = dynamic_cast(handled_view); - // LLTextBox* text_boxp = dynamic_cast(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 +class LLSD; + const U32 FOLLOWS_NONE = 0x00; const U32 FOLLOWS_LEFT = 0x01; const U32 FOLLOWS_RIGHT = 0x02; @@ -124,21 +124,16 @@ public: Optional enabled, visible, mouse_opaque, - use_bounding_rect; + use_bounding_rect, + from_xui; Optional tab_group, default_tab_group; Optional tool_tip; Optional sound_flags; - Optional from_xui; Optional follows; Optional hover_cursor; - - // font params - Optional font; - Optional font_halign; - Optional font_valign; Optional layout; Optional 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 +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()); } } -- cgit v1.2.3 From 92aaa3778872a660bed78b1ab7e77dc441200d3b Mon Sep 17 00:00:00 2001 From: James Cook Date: Mon, 5 Oct 2009 16:17:51 +0000 Subject: Tweak inspector tooltip icon spacing. --- indra/llui/lltooltip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llui') diff --git a/indra/llui/lltooltip.cpp b/indra/llui/lltooltip.cpp index 8f5c029816..d742281f30 100644 --- a/indra/llui/lltooltip.cpp +++ b/indra/llui/lltooltip.cpp @@ -189,7 +189,7 @@ LLToolTip::LLToolTip(const LLToolTip::Params& p) addChild(LLUICtrlFactory::create(icon_params)); // move text over to fit image in - mTextBox->translate(TOOLTIP_ICON_SIZE,0); + mTextBox->translate(TOOLTIP_ICON_SIZE + mPadding, 0); } if (p.click_callback.isProvided()) -- cgit v1.2.3