diff options
241 files changed, 6881 insertions, 5329 deletions
diff --git a/indra/integration_tests/llui_libtest/llwidgetreg.cpp b/indra/integration_tests/llui_libtest/llwidgetreg.cpp index 5a97f2aefd..316fd810c0 100644 --- a/indra/integration_tests/llui_libtest/llwidgetreg.cpp +++ b/indra/integration_tests/llui_libtest/llwidgetreg.cpp @@ -37,6 +37,7 @@ #include "llcombobox.h" #include "llcontainerview.h" #include "lliconctrl.h" +#include "llmenubutton.h" #include "llmenugl.h" #include "llmultislider.h" #include "llmultisliderctrl.h" @@ -64,6 +65,7 @@ void LLWidgetReg::initClass(bool register_widgets) if (register_widgets) { LLDefaultChildRegistry::Register<LLButton> button("button"); + LLDefaultChildRegistry::Register<LLMenuButton> menu_button("menu_button"); LLDefaultChildRegistry::Register<LLCheckBoxCtrl> check_box("check_box"); LLDefaultChildRegistry::Register<LLComboBox> combo_box("combo_box"); LLDefaultChildRegistry::Register<LLFilterEditor> filter_editor("filter_editor"); diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h index 4401be1679..bb598a2be1 100644 --- a/indra/llcommon/llpreprocessor.h +++ b/indra/llcommon/llpreprocessor.h @@ -117,7 +117,9 @@ using snprintf_hack::snprintf; #if defined(LL_WINDOWS) #define BOOST_REGEX_NO_LIB 1 #define CURL_STATICLIB 1 +#ifndef XML_STATIC #define XML_STATIC +#endif #endif // LL_WINDOWS diff --git a/indra/llcommon/llsafehandle.h b/indra/llcommon/llsafehandle.h index 1f7c682fd1..5862f1a341 100644 --- a/indra/llcommon/llsafehandle.h +++ b/indra/llcommon/llsafehandle.h @@ -77,6 +77,7 @@ public: Type* operator->() { return nonNull(mPointer); } Type* get() const { return mPointer; } + void clear() { assign(NULL); } // we disallow these operations as they expose our null objects to direct manipulation // and bypass the reference counting semantics //const Type& operator*() const { return *nonNull(mPointer); } diff --git a/indra/llcommon/llsecondlifeurls.cpp b/indra/llcommon/llsecondlifeurls.cpp index 6323d9d36b..9e67872ffd 100644 --- a/indra/llcommon/llsecondlifeurls.cpp +++ b/indra/llcommon/llsecondlifeurls.cpp @@ -34,7 +34,7 @@ #include "llsecondlifeurls.h" /* const std::string CREATE_ACCOUNT_URL ( - "http://secondlife.com/registration/"); + "http://join.secondlife.com/"); const std::string MANAGE_ACCOUNT ( "http://secondlife.com/account/"); // *TODO: NOT USED diff --git a/indra/llcommon/llstringtable.cpp b/indra/llcommon/llstringtable.cpp index 27f9036b8b..2f3a994d8f 100644 --- a/indra/llcommon/llstringtable.cpp +++ b/indra/llcommon/llstringtable.cpp @@ -38,6 +38,23 @@ LLStringTable gStringTable(32768); +LLStringTableEntry::LLStringTableEntry(const char *str) +: mString(NULL), mCount(1) +{ + // Copy string + U32 length = (U32)strlen(str) + 1; /*Flawfinder: ignore*/ + length = llmin(length, MAX_STRINGS_LENGTH); + mString = new char[length]; + strncpy(mString, str, length); /*Flawfinder: ignore*/ + mString[length - 1] = 0; +} + +LLStringTableEntry::~LLStringTableEntry() +{ + delete [] mString; + mCount = 0; +} + LLStringTable::LLStringTable(int tablesize) : mUniqueEntries(0) { diff --git a/indra/llcommon/llstringtable.h b/indra/llcommon/llstringtable.h index 4492063275..888361b0b9 100644 --- a/indra/llcommon/llstringtable.h +++ b/indra/llcommon/llstringtable.h @@ -59,21 +59,9 @@ const U32 MAX_STRINGS_LENGTH = 256; class LLStringTableEntry { public: - LLStringTableEntry(const char *str) - : mString(NULL), mCount(1) - { - // Copy string - U32 length = (U32)strlen(str) + 1; /*Flawfinder: ignore*/ - length = llmin(length, MAX_STRINGS_LENGTH); - mString = new char[length]; - strncpy(mString, str, length); /*Flawfinder: ignore*/ - mString[length - 1] = 0; - } - ~LLStringTableEntry() - { - delete [] mString; - mCount = 0; - } + LLStringTableEntry(const char *str); + ~LLStringTableEntry(); + void incCount() { mCount++; } BOOL decCount() { return --mCount; } diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h index 45810a101d..2c3e9c7333 100644 --- a/indra/llcommon/llversionviewer.h +++ b/indra/llcommon/llversionviewer.h @@ -38,6 +38,6 @@ const S32 LL_VERSION_MINOR = 0; const S32 LL_VERSION_PATCH = 0; const S32 LL_VERSION_BUILD = 0; -const char * const LL_CHANNEL = "Second Life 2009"; +const char * const LL_CHANNEL = "Second Life Developer"; #endif diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index d76b23248d..8f943182b8 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -484,7 +484,8 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch S32 start_of_last_word = 0; BOOL in_word = FALSE; - F32 scaled_max_pixels = (F32)llceil(max_pixels * sScaleX); + // avoid S32 overflow when max_pixels == S32_MAX by staying in floating point + F32 scaled_max_pixels = ceil(max_pixels * sScaleX); S32 i; for (i=0; (i < max_chars); i++) diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 0a284f0088..d9169f57f9 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -51,6 +51,7 @@ set(llui_SOURCE_FILES lllayoutstack.cpp lllineeditor.cpp lllocalcliprect.cpp + llmenubutton.cpp llmenugl.cpp llmodaldialog.cpp llmultifloater.cpp @@ -87,6 +88,7 @@ set(llui_SOURCE_FILES lltextparser.cpp lltransientfloatermgr.cpp lltransutil.cpp + lltoggleablemenu.cpp lltooltip.cpp llui.cpp lluicolortable.cpp @@ -137,6 +139,7 @@ set(llui_HEADER_FILES lllazyvalue.h lllineeditor.h lllocalcliprect.h + llmenubutton.h llmenugl.h llmodaldialog.h llmultifloater.h @@ -171,6 +174,7 @@ set(llui_HEADER_FILES lltextbox.h lltexteditor.h lltextparser.h + lltoggleablemenu.h lltooltip.h lltransientfloatermgr.h lltransutil.h diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index b9613b502c..219c2ee254 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -145,7 +145,8 @@ LLButton::LLButton(const LLButton::Params& p) mRightHPad(p.pad_right), mHoverGlowStrength(p.hover_glow_amount), mCommitOnReturn(p.commit_on_return), - mFadeWhenDisabled(FALSE) + mFadeWhenDisabled(FALSE), + mForcePressedState(FALSE) { static LLUICachedControl<S32> llbutton_orig_h_pad ("UIButtonOrigHPad", 0); static Params default_params(LLUICtrlFactory::getDefaultParams<LLButton>()); @@ -536,7 +537,8 @@ void LLButton::draw() bool enabled = isInEnabledChain(); bool pressed = pressed_by_keyboard - || (hasMouseCapture() && pointInView(local_mouse_x, local_mouse_y)); + || (hasMouseCapture() && pointInView(local_mouse_x, local_mouse_y)) + || mForcePressedState; bool selected = getToggleState(); bool use_glow_effect = FALSE; diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index 04716d605b..73ba457d34 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -233,6 +233,8 @@ public: static void toggleFloaterAndSetToggleState(LLUICtrl* ctrl, const LLSD& sdname); static void setFloaterToggle(LLUICtrl* ctrl, const LLSD& sdname); static void showHelp(LLUICtrl* ctrl, const LLSD& sdname); + + void setForcePressedState(BOOL b) { mForcePressedState = b; } protected: const LLPointer<LLUIImage>& getImageUnselected() const { return mImageUnselected; } @@ -310,6 +312,7 @@ private: BOOL mNeedsHighlight; BOOL mCommitOnReturn; BOOL mFadeWhenDisabled; + BOOL mForcePressedState; LLFrameTimer mFlashingTimer; }; diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp index 455b17ffc7..7f0f9751db 100644 --- a/indra/llui/llcheckboxctrl.cpp +++ b/indra/llui/llcheckboxctrl.cpp @@ -97,7 +97,7 @@ LLCheckBoxCtrl::LLCheckBoxCtrl(const LLCheckBoxCtrl::Params& p) LLTextBox::Params tbparams = p.label_text; tbparams.rect(label_rect); - tbparams.text(local_label); + tbparams.initial_value(local_label); if (p.font.isProvided()) { tbparams.font(p.font); diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index 0170ac0c6a..36e309d639 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -714,11 +714,11 @@ void LLComboBox::onItemSelected(const LLSD& data) } } -BOOL LLComboBox::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen) +BOOL LLComboBox::handleToolTip(S32 x, S32 y, MASK mask) { std::string tool_tip; - if(LLUICtrl::handleToolTip(x, y, msg, sticky_rect_screen)) + if(LLUICtrl::handleToolTip(x, y, mask)) { return TRUE; } @@ -731,7 +731,7 @@ BOOL LLComboBox::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_re if( !tool_tip.empty() ) { - LLToolTipMgr::instance().show(LLToolTipParams() + LLToolTipMgr::instance().show(LLToolTip::Params() .message(tool_tip) .sticky_rect(calcScreenRect())); } diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h index 68cbfeeeeb..6285ca5170 100644 --- a/indra/llui/llcombobox.h +++ b/indra/llui/llcombobox.h @@ -113,7 +113,7 @@ public: // LLView interface virtual void onFocusLost(); - virtual BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect); + virtual BOOL handleToolTip(S32 x, S32 y, MASK mask); virtual BOOL handleKeyHere(KEY key, MASK mask); virtual BOOL handleUnicodeCharHere(llwchar uni_char); diff --git a/indra/llui/llconsole.cpp b/indra/llui/llconsole.cpp index 1e8b8a5537..285ce82d2d 100644 --- a/indra/llui/llconsole.cpp +++ b/indra/llui/llconsole.cpp @@ -61,7 +61,7 @@ const F32 FADE_DURATION = 2.f; const S32 MIN_CONSOLE_WIDTH = 200; LLConsole::LLConsole(const LLConsole::Params& p) -: LLView(p), +: LLUICtrl(p), LLFixedBuffer(p.max_lines), mLinePersistTime(p.persist_time), // seconds mFont(p.font) @@ -94,7 +94,7 @@ void LLConsole::reshape(S32 width, S32 height, BOOL called_from_parent) mConsoleWidth = new_width; mConsoleHeight= new_height; - LLView::reshape(new_width, new_height, called_from_parent); + LLUICtrl::reshape(new_width, new_height, called_from_parent); for(paragraph_t::iterator paragraph_it = mParagraphs.begin(); paragraph_it != mParagraphs.end(); paragraph_it++) { diff --git a/indra/llui/llconsole.h b/indra/llui/llconsole.h index 56e1614948..5800a82922 100644 --- a/indra/llui/llconsole.h +++ b/indra/llui/llconsole.h @@ -34,14 +34,13 @@ #define LL_LLCONSOLE_H #include "llfixedbuffer.h" -#include "llview.h" +#include "lluictrl.h" #include "v4color.h" #include <deque> -class LLFontGL; class LLSD; -class LLConsole : public LLFixedBuffer, public LLView +class LLConsole : public LLFixedBuffer, public LLUICtrl { public: typedef enum e_font_size @@ -51,7 +50,7 @@ public: BIG = 1 } EFontSize; - struct Params : public LLInitParam::Block<Params, LLView::Params> + struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> { Optional<U32> max_lines; Optional<F32> persist_time; diff --git a/indra/llui/lldraghandle.cpp b/indra/llui/lldraghandle.cpp index 6e8e37ded3..8eccd709ce 100644 --- a/indra/llui/lldraghandle.cpp +++ b/indra/llui/lldraghandle.cpp @@ -108,7 +108,7 @@ void LLDragHandleTop::setTitle(const std::string& title) LLTextBox::Params params; params.name("Drag Handle Title"); params.rect(getRect()); - params.text(trimmed_title); + params.initial_value(trimmed_title); params.font(font); params.follows.flags(FOLLOWS_TOP | FOLLOWS_LEFT | FOLLOWS_RIGHT); params.font_shadow(LLFontGL::DROP_SHADOW_SOFT); @@ -120,7 +120,7 @@ void LLDragHandleTop::setTitle(const std::string& title) } -const std::string& LLDragHandleTop::getTitle() const +std::string LLDragHandleTop::getTitle() const { return mTitleBox == NULL ? LLStringUtil::null : mTitleBox->getText(); } @@ -138,7 +138,7 @@ void LLDragHandleLeft::setTitle(const std::string& ) } -const std::string& LLDragHandleLeft::getTitle() const +std::string LLDragHandleLeft::getTitle() const { return LLStringUtil::null; } @@ -256,7 +256,8 @@ void LLDragHandleTop::reshapeTitleBox() getRect().getWidth() - LEFT_PAD - RIGHT_PAD, title_height); - mTitleBox->setRect( title_rect ); + // calls reshape on mTitleBox + mTitleBox->setShape( title_rect ); } void LLDragHandleTop::reshape(S32 width, S32 height, BOOL called_from_parent) diff --git a/indra/llui/lldraghandle.h b/indra/llui/lldraghandle.h index 88ec1d21f8..dc5410787b 100644 --- a/indra/llui/lldraghandle.h +++ b/indra/llui/lldraghandle.h @@ -74,7 +74,7 @@ public: void setTitleVisible(BOOL visible); virtual void setTitle( const std::string& title ) = 0; - virtual const std::string& getTitle() const = 0; + virtual std::string getTitle() const = 0; virtual BOOL handleHover(S32 x, S32 y, MASK mask); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); @@ -112,7 +112,7 @@ protected: friend class LLUICtrlFactory; public: virtual void setTitle( const std::string& title ); - virtual const std::string& getTitle() const; + virtual std::string getTitle() const; virtual void draw(); virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); @@ -130,7 +130,7 @@ protected: friend class LLUICtrlFactory; public: virtual void setTitle( const std::string& title ); - virtual const std::string& getTitle() const; + virtual std::string getTitle() const; virtual void draw(); virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index ff0288a32f..564e4d748f 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -785,7 +785,7 @@ void LLFloater::applyTitle() } } -const std::string& LLFloater::getCurrentTitle() const +std::string LLFloater::getCurrentTitle() const { return mDragHandle ? mDragHandle->getTitle() : LLStringUtil::null; } diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 2a31ba4e8f..1dc5177f81 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -171,7 +171,7 @@ public: LLMultiFloater* getHost(); void applyTitle(); - const std::string& getCurrentTitle() const; + std::string getCurrentTitle() const; void setTitle( const std::string& title); std::string getTitle() const; void setShortTitle( const std::string& short_title ); diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp index 279cbaa923..00a80478cf 100644 --- a/indra/llui/llfocusmgr.cpp +++ b/indra/llui/llfocusmgr.cpp @@ -274,7 +274,7 @@ BOOL LLFocusMgr::childHasKeyboardFocus(const LLView* parent ) const // Returns TRUE is parent or any descedent of parent is the mouse captor. BOOL LLFocusMgr::childHasMouseCapture( const LLView* parent ) const { - if( mMouseCaptor && mMouseCaptor->isView() ) + if( mMouseCaptor && dynamic_cast<LLView*>(mMouseCaptor) != NULL ) { LLView* captor_view = (LLView*)mMouseCaptor; while( captor_view ) diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 2d582c0568..5eade72b61 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -35,7 +35,9 @@ #include "linden_common.h" #include "lllayoutstack.h" + #include "lllocalcliprect.h" +#include "llpanel.h" #include "llresizebar.h" #include "llcriticaldamp.h" diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h index 9459b9990c..49cbe7270f 100644 --- a/indra/llui/lllayoutstack.h +++ b/indra/llui/lllayoutstack.h @@ -34,7 +34,9 @@ #ifndef LL_LLLAYOUTSTACK_H #define LL_LLLAYOUTSTACK_H -#include "llpanel.h" +#include "llview.h" + +class LLPanel; class LLLayoutStack : public LLView { diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index ede67ad17d..693ea5bb45 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -107,9 +107,6 @@ LLLineEditor::Params::Params() highlight_color("highlight_color"), preedit_bg_color("preedit_bg_color"), border(""), - is_unicode("is_unicode"), - drop_shadow_visible("drop_shadow_visible"), - border_drop_shadow_visible("border_drop_shadow_visible"), bg_visible("bg_visible"), text_pad_left("text_pad_left"), text_pad_right("text_pad_right"), @@ -544,18 +541,13 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) { return TRUE; } - if (mSelectAllonFocusReceived - && gFocusMgr.getKeyboardFocus() != this) - { - setFocus( TRUE ); - } - else + + if (!mSelectAllonFocusReceived + || gFocusMgr.getKeyboardFocus() == this) { mLastSelectionStart = -1; mLastSelectionStart = -1; - setFocus( TRUE ); - if (mask & MASK_SHIFT) { // Handle selection extension @@ -621,6 +613,8 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) gFocusMgr.setMouseCapture( this ); } + setFocus(TRUE); + // delay cursor flashing mKeystrokeTimer.reset(); diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index 339aad30fb..48d68b9935 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -105,10 +105,7 @@ public: Optional<S32> text_pad_left, text_pad_right; - Ignored is_unicode, - drop_shadow_visible, - border_drop_shadow_visible, - bg_visible; + Ignored bg_visible; Params(); }; @@ -280,6 +277,7 @@ private: virtual void getPreeditRange(S32 *position, S32 *length) const; virtual BOOL getPreeditLocation(S32 query_position, LLCoordGL *coord, LLRect *bounds, LLRect *control) const; virtual S32 getPreeditFontSize() const; + virtual LLWString getPreeditString() const { return getWText(); } protected: LLUIString mText; // The string being edited. diff --git a/indra/llui/llmenubutton.cpp b/indra/llui/llmenubutton.cpp new file mode 100644 index 0000000000..2bb6749c83 --- /dev/null +++ b/indra/llui/llmenubutton.cpp @@ -0,0 +1,135 @@ +/** + * @file llbutton.cpp + * @brief LLButton base class + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llmenubutton.h" + +// Linden library includes +#include "llmenugl.h" +#include "llstring.h" +#include "v4color.h" + +static LLDefaultChildRegistry::Register<LLMenuButton> r("menu_button"); + + +LLMenuButton::Params::Params() +: menu_filename("menu_filename") +{ +} + + +LLMenuButton::LLMenuButton(const LLMenuButton::Params& p) +: LLButton(p), + mMenu(NULL), + mMenuVisibleLastFrame(false) +{ + std::string menu_filename = p.menu_filename; + + if (!menu_filename.empty()) + { + mMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); + if (!mMenu) + { + llwarns << "Error loading menu_button menu" << llendl; + } + } +} + +void LLMenuButton::toggleMenu() +{ + if(!mMenu) + return; + + if (mMenu->getVisible() || mMenuVisibleLastFrame) + { + mMenu->setVisible(FALSE); + } + else + { + LLRect rect = getRect(); + //mMenu->needsArrange(); //so it recalculates the visible elements + LLMenuGL::showPopup(getParent(), mMenu, rect.mLeft, rect.mBottom); + } +} + + +BOOL LLMenuButton::handleKeyHere(KEY key, MASK mask ) +{ + if( KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key)) + { + toggleMenu(); + return TRUE; + } + + if (mMenu && mMenu->getVisible() && key == KEY_ESCAPE && mask == MASK_NONE) + { + mMenu->setVisible(FALSE); + return TRUE; + } + + return FALSE; +} + +BOOL LLMenuButton::handleMouseDown(S32 x, S32 y, MASK mask) +{ + if (hasTabStop() && !getIsChrome()) + { + setFocus(TRUE); + } + + toggleMenu(); + + if (getSoundFlags() & MOUSE_DOWN) + { + make_ui_sound("UISndClick"); + } + + return TRUE; +} + +void LLMenuButton::draw() +{ + //we save this off so next frame when we try to close it by + //button click, and it hides menus before we get to it, we know + mMenuVisibleLastFrame = mMenu && mMenu->getVisible(); + + if (mMenuVisibleLastFrame) + { + setForcePressedState(TRUE); + } + + LLButton::draw(); + + setForcePressedState(FALSE); +} + diff --git a/indra/llui/llmenubutton.h b/indra/llui/llmenubutton.h new file mode 100644 index 0000000000..94b0e4355d --- /dev/null +++ b/indra/llui/llmenubutton.h @@ -0,0 +1,68 @@ +/** + * @file llbutton.h + * @brief Header for buttons + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLMENUBUTTON_H +#define LL_LLMENUBUTTON_H + +#include "llbutton.h" + +class LLMenuGL; + +class LLMenuButton +: public LLButton +{ +public: + struct Params + : public LLInitParam::Block<Params, LLButton::Params> + { + // filename for it's toggleable menu + Optional<std::string> menu_filename; + + Params(); + }; + + void toggleMenu(); + /*virtual*/ void draw(); + /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask ); + +protected: + friend class LLUICtrlFactory; + LLMenuButton(const Params&); + +private: + LLMenuGL* mMenu; + bool mMenuVisibleLastFrame; +}; + + +#endif // LL_LLMENUBUTTON_H diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index f2d147ac39..14bee0465c 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -781,6 +781,10 @@ LLMenuItemCallGL::LLMenuItemCallGL(const LLMenuItemCallGL::Params& p) void LLMenuItemCallGL::initFromParams(const Params& p) { + if (p.on_visible.isProvided()) + { + initVisibleCallback(p.on_visible, mVisibleSignal); + } if (p.on_enable.isProvided()) { initEnableCallback(p.on_enable, mEnableSignal); @@ -823,9 +827,19 @@ void LLMenuItemCallGL::updateEnabled( void ) } } +void LLMenuItemCallGL::updateVisible( void ) +{ + if (mVisibleSignal.num_slots() > 0) + { + bool visible = mVisibleSignal(this, LLSD()); + setVisible(visible); + } +} + void LLMenuItemCallGL::buildDrawLabel( void ) { updateEnabled(); + updateVisible(); LLMenuItemGL::buildDrawLabel(); } @@ -1224,23 +1238,7 @@ void LLMenuItemBranchGL::openMenu() rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS); } branch->setRect( rect ); - S32 x = 0; - S32 y = 0; - branch->localPointToOtherView( 0, 0, &x, &y, branch->getParent() ); - S32 delta_x = 0; - S32 delta_y = 0; - if( y < menu_region_rect.mBottom ) - { - delta_y = menu_region_rect.mBottom - y; - } - - S32 menu_region_width = menu_region_rect.getWidth(); - if( x - menu_region_rect.mLeft > menu_region_width - rect.getWidth() ) - { - // move sub-menu over to left side - delta_x = llmax(-x, (-1 * (rect.getWidth() + getRect().getWidth()))); - } - branch->translate( delta_x, delta_y ); + branch->translateIntoRectWithExclusion( menu_region_rect, getMenu()->getRect(), FALSE ); branch->setVisible( TRUE ); branch->getParent()->sendChildToFront(branch); @@ -1935,6 +1933,9 @@ void LLMenuGL::arrange( void ) item_list_t::iterator item_iter; for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) { + // do first so LLMenuGLItemCall can call on_visible to determine if visible + (*item_iter)->buildDrawLabel(); + if ((*item_iter)->getVisible()) { if (!getTornOff() @@ -1976,6 +1977,9 @@ void LLMenuGL::arrange( void ) for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) { + // do first so LLMenuGLItemCall can call on_visible to determine if visible + (*item_iter)->buildDrawLabel(); + if ((*item_iter)->getVisible()) { if (!getTornOff() @@ -2162,7 +2166,7 @@ void LLMenuGL::arrange( void ) item_list_t::iterator item_iter; for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter) - { + { if ((*item_iter)->getVisible()) { if (mScrollable) @@ -2193,7 +2197,6 @@ void LLMenuGL::arrange( void ) } } (*item_iter)->setRect( rect ); - (*item_iter)->buildDrawLabel(); } } } @@ -2936,11 +2939,27 @@ void hide_top_view( LLView* view ) // static void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y) { + const S32 CURSOR_HEIGHT = 22; // Approximate "normal" cursor size + const S32 CURSOR_WIDTH = 12; + // Save click point for detecting cursor moves before mouse-up. // Must be in local coords to compare with mouseUp events. // If the mouse doesn't move, the menu will stay open ala the Mac. // See also LLContextMenu::show() S32 mouse_x, mouse_y; + + // Resetting scrolling position + if (menu->isScrollable()) + { + menu->mFirstVisibleItem = NULL; + } + + menu->setVisible( TRUE ); + + // Fix menu rect if needed. + menu->needsArrange(); + menu->arrangeAndClear(); + LLUI::getMousePositionLocal(menu->getParent(), &mouse_x, &mouse_y); LLMenuHolderGL::sContextMenuSpawnPos.set(mouse_x,mouse_y); @@ -2948,7 +2967,6 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y) const S32 HPAD = 2; LLRect rect = menu->getRect(); - //LLView* cur_view = spawning_view; S32 left = x + HPAD; S32 top = y; spawning_view->localPointToOtherView(left, top, &left, &top, menu->getParent()); @@ -2956,37 +2974,19 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y) rect.getWidth(), rect.getHeight() ); menu->setRect( rect ); - // Resetting scrolling position - if (menu->isScrollable()) - { - menu->mFirstVisibleItem = NULL; - menu->needsArrange(); - } - menu->arrangeAndClear(); // Fix menu rect if needed. - rect = menu->getRect(); // Adjust context menu to fit onscreen - S32 bottom; - left = rect.mLeft; - bottom = rect.mBottom; - S32 delta_x = 0; - S32 delta_y = 0; - if( bottom < menu_region_rect.mBottom ) - { - // At this point, we need to move the context menu to the - // other side of the mouse. - delta_y = (rect.getHeight() + 2 * HPAD); - } - - if( left > menu_region_rect.mRight - rect.getWidth() ) - { - // At this point, we need to move the context menu to the - // other side of the mouse. - delta_x = -(rect.getWidth() + 2 * HPAD); - } - menu->translate( delta_x, delta_y ); - menu->setVisible( TRUE ); + LLRect mouse_rect; + const S32 MOUSE_CURSOR_PADDING = 5; + mouse_rect.setLeftTopAndSize(mouse_x - MOUSE_CURSOR_PADDING, + mouse_y + MOUSE_CURSOR_PADDING, + CURSOR_WIDTH + MOUSE_CURSOR_PADDING * 2, + CURSOR_HEIGHT + MOUSE_CURSOR_PADDING * 2); + menu->translateIntoRectWithExclusion( menu_region_rect, mouse_rect, FALSE ); menu->getParent()->sendChildToFront(menu); + + + } ///============================================================================ diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index 8309fedf7f..48887ec352 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -268,15 +268,18 @@ public: { Optional<EnableCallbackParam > on_enable; Optional<CommitCallbackParam > on_click; + Optional<VisibleCallbackParam > on_visible; Params() : on_enable("on_enable"), - on_click("on_click") + on_click("on_click"), + on_visible("on_visible") {} }; protected: LLMenuItemCallGL(const Params&); friend class LLUICtrlFactory; void updateEnabled( void ); + void updateVisible( void ); public: void initFromParams(const Params& p); @@ -300,10 +303,15 @@ public: { return mEnableSignal.connect(cb); } - + + boost::signals2::connection setVisibleCallback( const visible_signal_t::slot_type& cb ) + { + return mVisibleSignal.connect(cb); + } private: enable_signal_t mEnableSignal; + visible_signal_t mVisibleSignal; }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/indra/llui/llmultisliderctrl.cpp b/indra/llui/llmultisliderctrl.cpp index 0fbb7ced54..a9f462173d 100644 --- a/indra/llui/llmultisliderctrl.cpp +++ b/indra/llui/llmultisliderctrl.cpp @@ -98,7 +98,7 @@ LLMultiSliderCtrl::LLMultiSliderCtrl(const LLMultiSliderCtrl::Params& p) LLTextBox::Params params; params.name("MultiSliderCtrl Label"); params.rect(label_rect); - params.text(p.label); + params.initial_value(p.label()); params.font(p.font); mLabelBox = LLUICtrlFactory::create<LLTextBox> (params); addChild(mLabelBox); diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index b9bbb4db22..1695aee2b8 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -819,15 +819,6 @@ void LLPanel::childSetPrevalidate(const std::string& id, BOOL (*func)(const LLWS } } -void LLPanel::childSetWrappedText(const std::string& id, const std::string& text, bool visible) -{ - LLTextBox* child = findChild<LLTextBox>(id); - if (child) - { - child->setVisible(visible); - child->setWrappedText(text); - } -} void LLPanel::childSetAction(const std::string& id, boost::function<void(void*)> function, void* value) { diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h index 8b23ea7030..0594762333 100644 --- a/indra/llui/llpanel.h +++ b/indra/llui/llpanel.h @@ -209,9 +209,6 @@ public: void childShowTab(const std::string& id, const std::string& tabname, bool visible = true); LLPanel *childGetVisibleTab(const std::string& id) const; - // LLTextBox - void childSetWrappedText(const std::string& id, const std::string& text, bool visible = true); - // LLTextBox/LLTextEditor/LLLineEditor void childSetText(const std::string& id, const LLStringExplicit& text) { childSetValue(id, LLSD(text)); } @@ -243,7 +240,8 @@ protected: LLCallbackMap::map_t mFactoryMap; CommitCallbackRegistry::ScopedRegistrar mCommitCallbackRegistrar; EnableCallbackRegistry::ScopedRegistrar mEnableCallbackRegistrar; - + VisibleCallbackRegistry::ScopedRegistrar mVisibleCallbackRegistrar; + commit_signal_t mVisibleSignal; // Called when visibility changes, passes new visibility as LLSD() std::string mHelpTopic; // the name of this panel's help topic to display in the Help Viewer diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp index d1ea5843eb..f9f0307d17 100644 --- a/indra/llui/llradiogroup.cpp +++ b/indra/llui/llradiogroup.cpp @@ -55,6 +55,8 @@ LLRadioGroup::Params::Params() name = "radio_group"; mouse_opaque = true; follows.flags = FOLLOWS_LEFT | FOLLOWS_TOP; + // radio items are not tabbable until they are selected + tab_stop = false; } LLRadioGroup::LLRadioGroup(const LLRadioGroup::Params& p) diff --git a/indra/llui/llradiogroup.h b/indra/llui/llradiogroup.h index 914548b6aa..1e9b5115f8 100644 --- a/indra/llui/llradiogroup.h +++ b/indra/llui/llradiogroup.h @@ -47,18 +47,7 @@ class LLRadioCtrl : public LLCheckBoxCtrl { public: struct Params : public LLInitParam::Block<Params, LLCheckBoxCtrl::Params> - { - Ignored length; - Ignored type; - - Params() - : length("length"), - type("type") - { - // radio items are not tabbable until they are selected - tab_stop = false; - } - }; + {}; /*virtual*/ ~LLRadioCtrl(); /*virtual*/ void setValue(const LLSD& value); diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp index ed150ac50c..7db34a0608 100644 --- a/indra/llui/llscrollbar.cpp +++ b/indra/llui/llscrollbar.cpp @@ -88,8 +88,6 @@ LLScrollbar::LLScrollbar(const Params & p) mCurGlowStrength(0.f), mTrackColor( p.track_color() ), mThumbColor ( p.thumb_color() ), - mOnScrollEndCallback( NULL ), - mOnScrollEndData( NULL ), mThumbImageV(p.thumb_image_vertical), mThumbImageH(p.thumb_image_horizontal), mTrackImageV(p.track_image_vertical), @@ -243,11 +241,6 @@ void LLScrollbar::updateThumbRect() mThumbRect.mRight = thumb_start + thumb_length; mThumbRect.mBottom = 0; } - - if (mOnScrollEndCallback && mOnScrollEndData && (mDocPos == getDocPosMax())) - { - mOnScrollEndCallback(mOnScrollEndData); - } } BOOL LLScrollbar::handleMouseDown(S32 x, S32 y, MASK mask) @@ -568,12 +561,6 @@ void LLScrollbar::draw() } } - BOOL was_scrolled_to_bottom = (getDocPos() == getDocPosMax()); - if (mOnScrollEndCallback && was_scrolled_to_bottom) - { - mOnScrollEndCallback(mOnScrollEndData); - } - // Draw children LLView::draw(); } // end draw diff --git a/indra/llui/llscrollbar.h b/indra/llui/llscrollbar.h index 7e88b16561..e4c5712fb7 100644 --- a/indra/llui/llscrollbar.h +++ b/indra/llui/llscrollbar.h @@ -164,9 +164,6 @@ private: LLUIImagePtr mTrackImageH; S32 mThickness; - - void (*mOnScrollEndCallback)(void*); - void *mOnScrollEndData; }; diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index cd5926fb6b..5597d494fe 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -76,6 +76,7 @@ LLScrollContainer::Params::Params() : is_opaque("opaque"), bg_color("color"), border_visible("border_visible"), + hide_scrollbar("hide_scrollbar"), min_auto_scroll_rate("min_auto_scroll_rate", 100), max_auto_scroll_rate("max_auto_scroll_rate", 1000), reserve_scroll_corner("reserve_scroll_corner", false) @@ -93,6 +94,7 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p) mAutoScrollRate( 0.f ), mBackgroundColor(p.bg_color()), mIsOpaque(p.is_opaque), + mHideScrollbar(p.hide_scrollbar), mReserveScrollCorner(p.reserve_scroll_corner), mMinAutoScrollRate(p.min_auto_scroll_rate), mMaxAutoScrollRate(p.max_auto_scroll_rate), @@ -349,28 +351,33 @@ void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height S32 doc_width = doc_rect.getWidth(); S32 doc_height = doc_rect.getHeight(); - *visible_width = getRect().getWidth() - 2 * mBorder->getBorderWidth(); - *visible_height = getRect().getHeight() - 2 * mBorder->getBorderWidth(); + S32 border_width = (mBorder->getVisible() ? 2 * mBorder->getBorderWidth() : 0); + *visible_width = getRect().getWidth() - border_width; + *visible_height = getRect().getHeight() - border_width; *show_v_scrollbar = FALSE; - if( *visible_height < doc_height ) - { - *show_v_scrollbar = TRUE; - *visible_width -= scrollbar_size; - } - *show_h_scrollbar = FALSE; - if( *visible_width < doc_width ) - { - *show_h_scrollbar = TRUE; - *visible_height -= scrollbar_size; - // Must retest now that visible_height has changed - if( !*show_v_scrollbar && (*visible_height < doc_height) ) + if (!mHideScrollbar) + { + if( *visible_height < doc_height ) { *show_v_scrollbar = TRUE; *visible_width -= scrollbar_size; } + + if( *visible_width < doc_width ) + { + *show_h_scrollbar = TRUE; + *visible_height -= scrollbar_size; + + // Must retest now that visible_height has changed + if( !*show_v_scrollbar && (*visible_height < doc_height) ) + { + *show_v_scrollbar = TRUE; + *visible_width -= scrollbar_size; + } + } } } @@ -457,19 +464,6 @@ void LLScrollContainer::draw() sDepth--; } } - - if (sDebugRects) - { - drawDebugRect(); - } - - //// *HACK: also draw debug rectangles around currently-being-edited LLView, and any elements that are being highlighted by GUI preview code (see LLFloaterUIPreview) - //std::set<LLView*>::iterator iter = std::find(sPreviewHighlightedElements.begin(), sPreviewHighlightedElements.end(), this); - //if ((sEditingUI && this == sEditingUIView) || (iter != sPreviewHighlightedElements.end() && sDrawPreviewHighlights)) - //{ - // drawDebugRect(); - //} - } // end draw bool LLScrollContainer::addChild(LLView* view, S32 tab_group) @@ -598,7 +592,7 @@ LLRect LLScrollContainer::getContentWindowRect() const BOOL show_h_scrollbar = FALSE; BOOL show_v_scrollbar = FALSE; calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); - S32 border_width = mBorder->getBorderWidth(); + S32 border_width = mBorder->getVisible() ? mBorder->getBorderWidth() : 0; scroller_view_rect.setOriginAndSize(border_width, show_h_scrollbar ? mScrollbar[HORIZONTAL]->getRect().mTop : border_width, visible_width, diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h index 8385bca02f..ac8ffe5258 100644 --- a/indra/llui/llscrollcontainer.h +++ b/indra/llui/llscrollcontainer.h @@ -68,7 +68,8 @@ public: { Optional<bool> is_opaque, reserve_scroll_corner, - border_visible; + border_visible, + hide_scrollbar; Optional<F32> min_auto_scroll_rate, max_auto_scroll_rate; Optional<LLUIColor> bg_color; @@ -139,6 +140,7 @@ private: F32 mAutoScrollRate; F32 mMinAutoScrollRate; F32 mMaxAutoScrollRate; + bool mHideScrollbar; }; diff --git a/indra/llui/llscrolllistcell.cpp b/indra/llui/llscrolllistcell.cpp index e28da91305..a7c268758a 100644 --- a/indra/llui/llscrolllistcell.cpp +++ b/indra/llui/llscrolllistcell.cpp @@ -204,6 +204,13 @@ BOOL LLScrollListText::isText() const return TRUE; } +BOOL LLScrollListText::needsToolTip() const +{ + // show tooltips for truncated text + return mFont->getWidth(mText.getString()) > getWidth(); +} + + //virtual BOOL LLScrollListText::getVisible() const { diff --git a/indra/llui/llscrolllistcell.h b/indra/llui/llscrolllistcell.h index 9d3fa65f64..758623f121 100644 --- a/indra/llui/llscrolllistcell.h +++ b/indra/llui/llscrolllistcell.h @@ -94,16 +94,18 @@ public: LLScrollListCell(const LLScrollListCell::Params&); virtual ~LLScrollListCell() {}; - virtual void draw(const LLColor4& color, const LLColor4& highlight_color) const = 0; // truncate to given width, if possible + + virtual void draw(const LLColor4& color, const LLColor4& highlight_color) const {}; // truncate to given width, if possible virtual S32 getWidth() const {return mWidth;} virtual S32 getContentWidth() const { return 0; } - virtual S32 getHeight() const = 0; + virtual S32 getHeight() const { return 0; } virtual const LLSD getValue() const; virtual void setValue(const LLSD& value) { } virtual BOOL getVisible() const { return TRUE; } virtual void setWidth(S32 width) { mWidth = width; } virtual void highlightText(S32 offset, S32 num_chars) {} - virtual BOOL isText() const = 0; + virtual BOOL isText() const { return FALSE; } + virtual BOOL needsToolTip() const { return FALSE; } virtual void setColor(const LLColor4&) {} virtual void onCommit() {}; @@ -120,8 +122,6 @@ public: LLScrollListSpacer(const LLScrollListCell::Params& p) : LLScrollListCell(p) {} /*virtual*/ ~LLScrollListSpacer() {}; /*virtual*/ void draw(const LLColor4& color, const LLColor4& highlight_color) const {} - /*virtual*/ S32 getHeight() const { return 0; } - /*virtual*/ BOOL isText() const { return FALSE; } }; /* @@ -143,6 +143,7 @@ public: /*virtual*/ void setColor(const LLColor4&); /*virtual*/ BOOL isText() const; + /*virtual*/ BOOL needsToolTip() const; void setText(const LLStringExplicit& text); void setFontStyle(const U8 font_style); @@ -175,7 +176,6 @@ public: /*virtual*/ S32 getHeight() const; /*virtual*/ const LLSD getValue() const; /*virtual*/ void setColor(const LLColor4&); - /*virtual*/ BOOL isText()const { return FALSE; } /*virtual*/ void setValue(const LLSD& value); private: @@ -202,7 +202,6 @@ public: /*virtual*/ void setEnabled(BOOL enable); LLCheckBoxCtrl* getCheckBox() { return mCheckBox; } - /*virtual*/ BOOL isText() const { return FALSE; } private: LLCheckBoxCtrl* mCheckBox; diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 54e42bf642..af05686c70 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -218,7 +218,7 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p) sbparams.orientation(LLScrollbar::VERTICAL); sbparams.doc_size(getItemCount()); sbparams.doc_pos(mScrollLines); - sbparams.page_size( mPageLines ? mPageLines : getItemCount() ); + sbparams.page_size( getLinesPerPage() ); sbparams.change_callback(boost::bind(&LLScrollListCtrl::onScrollChange, this, _1, _2)); sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM); sbparams.visible(false); @@ -475,10 +475,7 @@ void LLScrollListCtrl::updateLayout() getChildView("comment_text")->setShape(mItemListRect); // how many lines of content in a single "page" - S32 page_lines = mLineHeight? mItemListRect.getHeight() / mLineHeight : getItemCount(); - //if mPageLines is NOT provided display all item - if(mPageLines) - page_lines = mPageLines; + S32 page_lines = getLinesPerPage(); BOOL scrollbar_visible = mLineHeight * getItemCount() > mItemListRect.getHeight(); if (scrollbar_visible) @@ -1386,7 +1383,7 @@ void LLScrollListCtrl::drawItems() S32 y = mItemListRect.mTop - mLineHeight; // allow for partial line at bottom - S32 num_page_lines = (mPageLines)? mPageLines : getItemCount() + 1; + S32 num_page_lines = getLinesPerPage(); LLRect item_rect; @@ -1529,7 +1526,19 @@ BOOL LLScrollListCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) return handled; } -BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen) +// *NOTE: Requires a valid row_index and column_index +LLRect LLScrollListCtrl::getCellRect(S32 row_index, S32 column_index) +{ + LLRect cell_rect; + S32 rect_left = getColumnOffsetFromIndex(column_index) + mItemListRect.mLeft; + S32 rect_bottom = getRowOffsetFromIndex(row_index); + LLScrollListColumn* columnp = getColumn(column_index); + cell_rect.setOriginAndSize(rect_left, rect_bottom, + rect_left + columnp->getWidth(), mLineHeight); + return cell_rect; +} + +BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, MASK mask) { S32 column_index = getColumnIndexFromOffset(x); LLScrollListColumn* columnp = getColumn(column_index); @@ -1543,20 +1552,23 @@ BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sti { LLScrollListCell* hit_cell = hit_item->getColumn(column_index); if (!hit_cell) return FALSE; - //S32 cell_required_width = hit_cell->getContentWidth(); if (hit_cell - && hit_cell->isText()) + && hit_cell->isText() + && hit_cell->needsToolTip()) { - S32 rect_left = getColumnOffsetFromIndex(column_index) + mItemListRect.mLeft; - S32 rect_bottom = getRowOffsetFromIndex(getItemIndex(hit_item)); - LLRect cell_rect; - cell_rect.setOriginAndSize(rect_left, rect_bottom, rect_left + columnp->getWidth(), mLineHeight); + S32 row_index = getItemIndex(hit_item); + LLRect cell_rect = getCellRect(row_index, column_index); // Convert rect local to screen coordinates LLRect sticky_rect; localRectToScreen(cell_rect, &sticky_rect); - LLToolTipMgr::instance().show(LLToolTipParams() - .message(hit_cell->getValue().asString()) - .sticky_rect(sticky_rect)); + + // display tooltip exactly over original cell, in same font + LLToolTipMgr::instance().show(LLToolTip::Params() + .message(hit_cell->getValue().asString()) + .font(LLFontGL::getFontSansSerifSmall()) + .pos(LLCoordGL(sticky_rect.mLeft - 5, sticky_rect.mTop + 4)) + .delay_time(0.2f) + .sticky_rect(sticky_rect)); } handled = TRUE; } @@ -1565,7 +1577,7 @@ BOOL LLScrollListCtrl::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sti LLScrollColumnHeader* headerp = columnp->mHeader; if (headerp && !handled) { - handled = headerp->handleToolTip(x, y, msg, sticky_rect_screen); + handled = headerp->handleToolTip(x, y, mask); } return handled; @@ -1877,7 +1889,7 @@ LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y ) mLineHeight ); // allow for partial line at bottom - S32 num_page_lines = (mPageLines)? mPageLines : getItemCount() + 1; + S32 num_page_lines = getLinesPerPage(); S32 line = 0; item_list::iterator iter; @@ -2336,6 +2348,20 @@ BOOL LLScrollListCtrl::setSort(S32 column_idx, BOOL ascending) } } +S32 LLScrollListCtrl::getLinesPerPage() +{ + //if mPageLines is NOT provided display all item + if(mPageLines) + { + return mPageLines; + } + else + { + return mLineHeight ? mItemListRect.getHeight() / mLineHeight : getItemCount(); + } +} + + // Called by scrollbar void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar ) { @@ -2442,7 +2468,7 @@ void LLScrollListCtrl::scrollToShowSelected() } S32 lowest = mScrollLines; - S32 page_lines = (mPageLines)? mPageLines : getItemCount(); + S32 page_lines = getLinesPerPage(); S32 highest = mScrollLines + page_lines; if (index < lowest) diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 83b2f71037..78bc60db6e 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -288,7 +288,7 @@ public: /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); /*virtual*/ BOOL handleUnicodeCharHere(llwchar uni_char); /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); - /*virtual*/ BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect); + /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask); /*virtual*/ void setEnabled(BOOL enabled); /*virtual*/ void setFocus( BOOL b ); /*virtual*/ void onFocusReceived(); @@ -307,6 +307,9 @@ public: LLRect getItemListRect() { return mItemListRect; } + /// Returns rect, in local coords, of a given row/column + LLRect getCellRect(S32 row_index, S32 column_index); + // Used "internally" by the scroll bar. void onScrollChange( S32 new_pos, LLScrollbar* src ); @@ -390,6 +393,7 @@ private: void deselectItem(LLScrollListItem* itemp); void commitIfChanged(); BOOL setSort(S32 column, BOOL ascending); + S32 getLinesPerPage(); static void showNameDetails(std::string id, bool is_group); static void copyNameToClipboard(std::string id, bool is_group); diff --git a/indra/llui/llscrolllistitem.cpp b/indra/llui/llscrolllistitem.cpp index 2ac6925c78..2c5e0d24cd 100644 --- a/indra/llui/llscrolllistitem.cpp +++ b/indra/llui/llscrolllistitem.cpp @@ -36,7 +36,6 @@ #include "llscrolllistitem.h" #include "llrect.h" -#include "llresmgr.h" // LLFONT_SANSSERIF_SMALL #include "llui.h" diff --git a/indra/llui/llscrolllistitem.h b/indra/llui/llscrolllistitem.h index c2b7effbc7..0ec7fbcc2c 100644 --- a/indra/llui/llscrolllistitem.h +++ b/indra/llui/llscrolllistitem.h @@ -34,10 +34,8 @@ #ifndef LLSCROLLLISTITEM_H #define LLSCROLLLISTITEM_H -#include "llfontgl.h" // LLFontGL::HAlign #include "llpointer.h" // LLPointer<> #include "llsd.h" -#include "lluistring.h" #include "v4color.h" #include "llinitparam.h" #include "llscrolllistcell.h" diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp index fb71b60725..3ecf629082 100644 --- a/indra/llui/llsliderctrl.cpp +++ b/indra/llui/llsliderctrl.cpp @@ -83,7 +83,7 @@ LLSliderCtrl::LLSliderCtrl(const LLSliderCtrl::Params& p) LLTextBox::Params params(p.slider_label); params.rect.setIfNotProvided(label_rect); params.font.setIfNotProvided(p.font); - params.text(p.label); + params.initial_value(p.label()); mLabelBox = LLUICtrlFactory::create<LLTextBox> (params); addChild(mLabelBox); } diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index 83d71006aa..bedf16a397 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -90,7 +90,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p) LLTextBox::Params params; params.name("SpinCtrl Label"); params.rect(label_rect); - params.text(p.label); + params.initial_value(p.label()); if (p.font.isProvided()) { params.font(p.font); diff --git a/indra/llui/llstatview.cpp b/indra/llui/llstatview.cpp index 4ba01eb441..9580c76e2a 100644 --- a/indra/llui/llstatview.cpp +++ b/indra/llui/llstatview.cpp @@ -67,5 +67,7 @@ LLStatView::~LLStatView() static StatViewRegistry::Register<LLStatBar> r1("stat_bar"); static StatViewRegistry::Register<LLStatView> r2("stat_view"); +// stat_view can be a child of panels/etc. +static LLDefaultChildRegistry::Register<LLStatView> r3("stat_view"); diff --git a/indra/llui/llstyle.cpp b/indra/llui/llstyle.cpp index c16ac08014..fd3f88d1f6 100644 --- a/indra/llui/llstyle.cpp +++ b/indra/llui/llstyle.cpp @@ -40,7 +40,7 @@ LLStyle::Params::Params() : visible("visible", true), - drop_shadow("drop_shadow", false), + drop_shadow("drop_shadow", LLFontGL::NO_SHADOW), color("color", LLColor4::black), font("font", LLFontGL::getFontMonospace()), image("image"), diff --git a/indra/llui/llstyle.h b/indra/llui/llstyle.h index 5e8883afd7..c769964136 100644 --- a/indra/llui/llstyle.h +++ b/indra/llui/llstyle.h @@ -44,12 +44,12 @@ class LLStyle : public LLRefCount public: struct Params : public LLInitParam::Block<Params> { - Optional<bool> visible, - drop_shadow; - Optional<LLUIColor> color; - Optional<const LLFontGL*> font; - Optional<LLUIImage*> image; - Optional<std::string> link_href; + Optional<bool> visible; + Optional<LLFontGL::ShadowType> drop_shadow; + Optional<LLUIColor> color; + Optional<const LLFontGL*> font; + Optional<LLUIImage*> image; + Optional<std::string> link_href; Params(); }; LLStyle(const Params& p = Params()); @@ -60,6 +60,8 @@ public: BOOL isVisible() const; void setVisible(BOOL is_visible); + LLFontGL::ShadowType getShadowType() { return mDropShadow; } + void setFont(const LLFontGL* font); const LLFontGL* getFont() const; @@ -94,7 +96,7 @@ public: BOOL mItalic; BOOL mBold; BOOL mUnderline; - BOOL mDropShadow; + LLFontGL::ShadowType mDropShadow; protected: ~LLStyle() { } diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index b6eed3ef18..b1067ad6f3 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -596,10 +596,10 @@ BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask ) } // virtual -BOOL LLTabContainer::handleToolTip( S32 x, S32 y, std::string& msg, LLRect& sticky_rect ) +BOOL LLTabContainer::handleToolTip( S32 x, S32 y, MASK mask) { static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0); - BOOL handled = LLPanel::handleToolTip( x, y, msg, sticky_rect ); + BOOL handled = LLPanel::handleToolTip( x, y, mask); if (!handled && getTabCount() > 0) { LLTabTuple* firsttuple = getTab(0); @@ -629,7 +629,7 @@ BOOL LLTabContainer::handleToolTip( S32 x, S32 y, std::string& msg, LLRect& stic tuple->mButton->setVisible( TRUE ); S32 local_x = x - tuple->mButton->getRect().mLeft; S32 local_y = y - tuple->mButton->getRect().mBottom; - handled = tuple->mButton->handleToolTip( local_x, local_y, msg, sticky_rect ); + handled = tuple->mButton->handleToolTip( local_x, local_y, mask); if( handled ) { break; @@ -906,7 +906,7 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel) LLTextBox::Params params; params.name(trimmed_label); params.rect(btn_rect); - params.text(trimmed_label); + params.initial_value(trimmed_label); params.font(font); textbox = LLUICtrlFactory::create<LLTextBox> (params); diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h index 89a0346896..7bbecc1abc 100644 --- a/indra/llui/lltabcontainer.h +++ b/indra/llui/lltabcontainer.h @@ -99,7 +99,7 @@ public: /*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask ); /*virtual*/ BOOL handleHover( S32 x, S32 y, MASK mask ); /*virtual*/ BOOL handleMouseUp( S32 x, S32 y, MASK mask ); - /*virtual*/ BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect ); + /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType type, void* cargo_data, diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 0fd6a14187..6c048aa908 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -34,25 +34,2011 @@ #include "linden_common.h" #include "lltextbase.h" -#include "llstl.h" -#include "llview.h" -#include "llwindow.h" + +#include "lllocalcliprect.h" #include "llmenugl.h" +#include "llscrollcontainer.h" +#include "llstl.h" +#include "lltextparser.h" #include "lltooltip.h" #include "lluictrl.h" #include "llurlaction.h" #include "llurlregistry.h" - +#include "llview.h" +#include "llwindow.h" #include <boost/bind.hpp> -// global state for all text fields -LLUIColor LLTextBase::mLinkColor = LLColor4::blue; +const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds +const S32 CURSOR_THICKNESS = 2; + +LLTextBase::line_info::line_info(S32 index_start, S32 index_end, LLRect rect, S32 line_num) +: mDocIndexStart(index_start), + mDocIndexEnd(index_end), + mRect(rect), + mLineNum(line_num) +{} bool LLTextBase::compare_segment_end::operator()(const LLTextSegmentPtr& a, const LLTextSegmentPtr& b) const { return a->getEnd() < b->getEnd(); } + +// helper functors +struct LLTextBase::compare_bottom +{ + bool operator()(const S32& a, const LLTextBase::line_info& b) const + { + return a > b.mRect.mBottom; // bottom of a is higher than bottom of b + } + + bool operator()(const LLTextBase::line_info& a, const S32& b) const + { + return a.mRect.mBottom > b; // bottom of a is higher than bottom of b + } + + bool operator()(const LLTextBase::line_info& a, const LLTextBase::line_info& b) const + { + return a.mRect.mBottom > b.mRect.mBottom; // bottom of a is higher than bottom of b + } + +}; + +// helper functors +struct LLTextBase::compare_top +{ + bool operator()(const S32& a, const LLTextBase::line_info& b) const + { + return a > b.mRect.mTop; // top of a is higher than top of b + } + + bool operator()(const LLTextBase::line_info& a, const S32& b) const + { + return a.mRect.mTop > b; // top of a is higher than top of b + } + + bool operator()(const LLTextBase::line_info& a, const LLTextBase::line_info& b) const + { + return a.mRect.mTop > b.mRect.mTop; // top of a is higher than top of b + } +}; + +struct LLTextBase::line_end_compare +{ + bool operator()(const S32& pos, const LLTextBase::line_info& info) const + { + return (pos < info.mDocIndexEnd); + } + + bool operator()(const LLTextBase::line_info& info, const S32& pos) const + { + return (info.mDocIndexEnd < pos); + } + + bool operator()(const LLTextBase::line_info& a, const LLTextBase::line_info& b) const + { + return (a.mDocIndexEnd < b.mDocIndexEnd); + } + +}; + +////////////////////////////////////////////////////////////////////////// +// +// LLTextBase::DocumentPanel +// + + +LLTextBase::DocumentPanel::DocumentPanel(const Params& p) +: LLPanel(p) +{} + + +////////////////////////////////////////////////////////////////////////// +// +// LLTextBase +// + +// register LLTextBase::Params under name "textbase" +static LLWidgetNameRegistry::StaticRegistrar sRegisterTextBaseParams(&typeid(LLTextBase::Params), "textbase"); + +LLTextBase::LineSpacingParams::LineSpacingParams() +: multiple("multiple", 1.f), + pixels("pixels", 0) +{ +} + + +LLTextBase::Params::Params() +: cursor_color("cursor_color"), + text_color("text_color"), + text_readonly_color("text_readonly_color"), + bg_visible("bg_visible", false), + border_visible("border_visible", false), + bg_readonly_color("bg_readonly_color"), + bg_writeable_color("bg_writeable_color"), + bg_focus_color("bg_focus_color"), + hide_scrollbar("hide_scrollbar"), + clip_to_rect("clip_to_rect", true), + track_end("track_end", false), + read_only("read_only", false), + v_pad("v_pad", 0), + h_pad("h_pad", 0), + line_spacing("line_spacing"), + max_text_length("max_length", 255), + font_shadow("font_shadow"), + wrap("wrap"), + use_ellipses("use_ellipses", false), + allow_html("allow_html", false), + parse_highlights("parse_highlights", false) +{ + addSynonym(track_end, "track_bottom"); + addSynonym(wrap, "word_wrap"); +} + + +LLTextBase::LLTextBase(const LLTextBase::Params &p) +: LLUICtrl(p, LLTextViewModelPtr(new LLTextViewModel)), + mURLClickSignal(), + mMaxTextByteLength( p.max_text_length ), + mDefaultFont(p.font), + mFontShadow(p.font_shadow), + mPopupMenu(NULL), + mReadOnly(p.read_only), + mCursorColor(p.cursor_color), + mFgColor(p.text_color), + mBorderVisible( p.border_visible ), + mReadOnlyFgColor(p.text_readonly_color), + mWriteableBgColor(p.bg_writeable_color), + mReadOnlyBgColor(p.bg_readonly_color), + mFocusBgColor(p.bg_focus_color), + mReflowNeeded(FALSE), + mCursorPos( 0 ), + mScrollNeeded(FALSE), + mDesiredXPixel(-1), + mHPad(p.h_pad), + mVPad(p.v_pad), + mHAlign(p.font_halign), + mLineSpacingMult(p.line_spacing.multiple), + mLineSpacingPixels(p.line_spacing.pixels), + mTrackEnd( p.track_end ), + mScrollIndex(-1), + mSelectionStart( 0 ), + mSelectionEnd( 0 ), + mIsSelecting( FALSE ), + mClip(p.clip_to_rect), + mWordWrap(p.wrap), + mUseEllipses( p.use_ellipses ), + mParseHTML(p.allow_html), + mParseHighlights(p.parse_highlights) +{ + LLScrollContainer::Params scroll_params; + scroll_params.name = "text scroller"; + scroll_params.rect = getLocalRect(); + scroll_params.follows.flags = FOLLOWS_ALL; + scroll_params.is_opaque = false; + scroll_params.mouse_opaque = false; + scroll_params.min_auto_scroll_rate = 200; + scroll_params.max_auto_scroll_rate = 800; + scroll_params.hide_scrollbar = p.hide_scrollbar; + scroll_params.border_visible = p.border_visible; + mScroller = LLUICtrlFactory::create<LLScrollContainer>(scroll_params); + addChild(mScroller); + + LLPanel::Params panel_params; + panel_params.name = "text_contents"; + panel_params.rect = LLRect(0, 500, 500, 0); + panel_params.background_visible = p.bg_visible; + panel_params.background_opaque = true; + panel_params.mouse_opaque = false; + + mDocumentPanel = LLUICtrlFactory::create<DocumentPanel>(panel_params); + mScroller->addChild(mDocumentPanel); + + createDefaultSegment(); + + updateTextRect(); +} + +LLTextBase::~LLTextBase() +{ + delete mPopupMenu; + clearSegments(); +} + +void LLTextBase::initFromParams(const LLTextBase::Params& p) +{ + LLUICtrl::initFromParams(p); + resetDirty(); // Update saved text state + updateSegments(); + + // HACK: work around enabled == readonly design bug -- RN + // setEnabled will modify our read only status, so do this after + // LLTextBase::initFromParams + if (p.read_only.isProvided()) + { + mReadOnly = p.read_only; + } + + // HACK: text editors always need to be enabled so that we can scroll + LLView::setEnabled(true); +} + +bool LLTextBase::truncate() +{ + BOOL did_truncate = FALSE; + + // First rough check - if we're less than 1/4th the size, we're OK + if (getLength() >= S32(mMaxTextByteLength / 4)) + { + // Have to check actual byte size + LLWString text(getWText()); + S32 utf8_byte_size = wstring_utf8_length(text); + if ( utf8_byte_size > mMaxTextByteLength ) + { + // Truncate safely in UTF-8 + std::string temp_utf8_text = wstring_to_utf8str(text); + temp_utf8_text = utf8str_truncate( temp_utf8_text, mMaxTextByteLength ); + getViewModel()->setDisplay(utf8str_to_wstring( temp_utf8_text )); + did_truncate = TRUE; + } + } + + return did_truncate; +} + +LLStyle::Params LLTextBase::getDefaultStyle() +{ + LLColor4 text_color = ( mReadOnly ? mReadOnlyFgColor.get() : mFgColor.get() ); + return LLStyle::Params().color(text_color).font(mDefaultFont).drop_shadow(mFontShadow); +} + +void LLTextBase::onValueChange(S32 start, S32 end) +{ +} + + +// Draws the black box behind the selected text +void LLTextBase::drawSelectionBackground() +{ + // Draw selection even if we don't have keyboard focus for search/replace + if( hasSelection() && !mLineInfoList.empty()) + { + LLWString text = getWText(); + std::vector<LLRect> selection_rects; + + S32 selection_left = llmin( mSelectionStart, mSelectionEnd ); + S32 selection_right = llmax( mSelectionStart, mSelectionEnd ); + LLRect selection_rect = mTextRect; + + // Skip through the lines we aren't drawing. + LLRect content_display_rect = mScroller->getVisibleContentRect(); + + // binary search for line that starts before top of visible buffer + line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mTop, compare_bottom()); + line_list_t::const_iterator end_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mBottom, compare_top()); + + bool done = false; + + // Find the coordinates of the selected area + for (;line_iter != end_iter && !done; ++line_iter) + { + // is selection visible on this line? + if (line_iter->mDocIndexEnd > selection_left && line_iter->mDocIndexStart < selection_right) + { + segment_set_t::iterator segment_iter; + S32 segment_offset; + getSegmentAndOffset(line_iter->mDocIndexStart, &segment_iter, &segment_offset); + + LLRect selection_rect; + selection_rect.mLeft = line_iter->mRect.mLeft; + selection_rect.mRight = line_iter->mRect.mLeft; + selection_rect.mBottom = line_iter->mRect.mBottom; + selection_rect.mTop = line_iter->mRect.mTop; + + for(;segment_iter != mSegments.end(); ++segment_iter, segment_offset = 0) + { + LLTextSegmentPtr segmentp = *segment_iter; + + S32 segment_line_start = segmentp->getStart() + segment_offset; + S32 segment_line_end = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd); + + // if selection after beginning of segment + if(selection_left >= segment_line_start) + { + S32 num_chars = llmin(selection_left, segment_line_end) - segment_line_start; + selection_rect.mLeft += segmentp->getWidth(segment_offset, num_chars); + } + + // if selection spans end of current segment... + if (selection_right > segment_line_end) + { + // extend selection slightly beyond end of line + // to indicate selection of newline character (use "n" character to determine width) + selection_rect.mRight += segmentp->getWidth(segment_offset, segment_line_end - segment_line_start); + } + // else if selection ends on current segment... + else + { + S32 num_chars = selection_right - segment_line_start; + selection_rect.mRight += segmentp->getWidth(segment_offset, num_chars); + + break; + } + } + selection_rects.push_back(selection_rect); + } + } + + // Draw the selection box (we're using a box instead of reversing the colors on the selected text). + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + const LLColor4& color = mReadOnly ? mReadOnlyBgColor.get() : mWriteableBgColor.get(); + F32 alpha = hasFocus() ? 0.7f : 0.3f; + alpha *= getDrawContext().mAlpha; + gGL.color4f( 1.f - color.mV[0], 1.f - color.mV[1], 1.f - color.mV[2], alpha ); + + for (std::vector<LLRect>::iterator rect_it = selection_rects.begin(); + rect_it != selection_rects.end(); + ++rect_it) + { + LLRect selection_rect = *rect_it; + selection_rect.translate(mTextRect.mLeft - content_display_rect.mLeft, mTextRect.mBottom - content_display_rect.mBottom); + gl_rect_2d(selection_rect); + } + } +} + +void LLTextBase::drawCursor() +{ + F32 alpha = getDrawContext().mAlpha; + + if( hasFocus() + && gFocusMgr.getAppHasFocus() + && !mReadOnly) + { + LLWString wtext = getWText(); + const llwchar* text = wtext.c_str(); + + LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos); + cursor_rect.translate(-1, 0); + segment_set_t::iterator seg_it = getSegIterContaining(mCursorPos); + + // take style from last segment + LLTextSegmentPtr segmentp; + + if (seg_it != mSegments.end()) + { + segmentp = *seg_it; + } + else + { + //segmentp = mSegments.back(); + return; + } + + // Draw the cursor + // (Flash the cursor every half second starting a fixed time after the last keystroke) + F32 elapsed = mCursorBlinkTimer.getElapsedTimeF32(); + if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) ) + { + + if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection()) + { + S32 width = llmax(CURSOR_THICKNESS, segmentp->getWidth(mCursorPos - segmentp->getStart(), 1)); + cursor_rect.mRight = cursor_rect.mLeft + width; + } + else + { + cursor_rect.mRight = cursor_rect.mLeft + CURSOR_THICKNESS; + } + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + LLColor4 cursor_color = mCursorColor.get() % alpha; + gGL.color4fv( cursor_color.mV ); + + gl_rect_2d(cursor_rect); + + if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection() && text[mCursorPos] != '\n') + { + LLColor4 text_color; + const LLFontGL* fontp; + if (segmentp) + { + text_color = segmentp->getColor(); + fontp = segmentp->getStyle()->getFont(); + } + else if (mReadOnly) + { + text_color = mReadOnlyFgColor.get(); + fontp = mDefaultFont; + } + else + { + text_color = mFgColor.get(); + fontp = mDefaultFont; + } + fontp->render(text, mCursorPos, cursor_rect.mLeft, cursor_rect.mTop, + LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], alpha), + LLFontGL::LEFT, LLFontGL::TOP, + LLFontGL::NORMAL, + LLFontGL::NO_SHADOW, + 1); + } + + // Make sure the IME is in the right place + LLRect screen_pos = calcScreenRect(); + LLCoordGL ime_pos( screen_pos.mLeft + llfloor(cursor_rect.mLeft), screen_pos.mBottom + llfloor(cursor_rect.mTop) ); + + ime_pos.mX = (S32) (ime_pos.mX * LLUI::sGLScaleFactor.mV[VX]); + ime_pos.mY = (S32) (ime_pos.mY * LLUI::sGLScaleFactor.mV[VY]); + getWindow()->setLanguageTextInput( ime_pos ); + } + } +} + +void LLTextBase::drawText() +{ + LLWString text = getWText(); + const S32 text_len = getLength(); + if( text_len <= 0 ) + { + return; + } + S32 selection_left = -1; + S32 selection_right = -1; + // Draw selection even if we don't have keyboard focus for search/replace + if( hasSelection()) + { + selection_left = llmin( mSelectionStart, mSelectionEnd ); + selection_right = llmax( mSelectionStart, mSelectionEnd ); + } + + LLRect scrolled_view_rect = mScroller->getVisibleContentRect(); + LLRect content_rect = mScroller->getContentWindowRect(); + std::pair<S32, S32> line_range = getVisibleLines(); + S32 first_line = line_range.first; + S32 last_line = line_range.second; + if (first_line >= last_line) + { + return; + } + + S32 line_start = getLineStart(first_line); + // find first text segment that spans top of visible portion of text buffer + segment_set_t::iterator seg_iter = getSegIterContaining(line_start); + if (seg_iter == mSegments.end()) + { + return; + } + + LLTextSegmentPtr cur_segment = *seg_iter; + + for (S32 cur_line = first_line; cur_line < last_line; cur_line++) + { + line_info& line = mLineInfoList[cur_line]; + + if ((line.mRect.mTop - scrolled_view_rect.mBottom) < mTextRect.mBottom) + { + break; + } + + S32 next_start = -1; + S32 line_end = text_len; + + if ((cur_line + 1) < getLineCount()) + { + next_start = getLineStart(cur_line + 1); + line_end = next_start; + } + if ( text[line_end-1] == '\n' ) + { + --line_end; + } + + LLRect text_rect(line.mRect.mLeft + mTextRect.mLeft - scrolled_view_rect.mLeft, + line.mRect.mTop - scrolled_view_rect.mBottom + mTextRect.mBottom, + mDocumentPanel->getRect().getWidth() - scrolled_view_rect.mLeft, + line.mRect.mBottom - scrolled_view_rect.mBottom + mTextRect.mBottom); + + // draw a single line of text + S32 seg_start = line_start; + while( seg_start < line_end ) + { + while( cur_segment->getEnd() <= seg_start ) + { + seg_iter++; + if (seg_iter == mSegments.end()) + { + llwarns << "Ran off the segmentation end!" << llendl; + + return; + } + cur_segment = *seg_iter; + } + + S32 clipped_end = llmin( line_end, cur_segment->getEnd() ) - cur_segment->getStart(); + text_rect.mLeft = (S32)(cur_segment->draw(seg_start - cur_segment->getStart(), clipped_end, selection_left, selection_right, text_rect)); + + seg_start = clipped_end + cur_segment->getStart(); + } + + line_start = next_start; + } +} + +/////////////////////////////////////////////////////////////////// +// Returns change in number of characters in mWText + +S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::segment_vec_t* segments ) +{ + LLWString text(getWText()); + S32 old_len = text.length(); // length() returns character length + S32 insert_len = wstr.length(); + + pos = getEditableIndex(pos, true); + + segment_set_t::iterator seg_iter = getSegIterContaining(pos); + + LLTextSegmentPtr default_segment; + + LLTextSegmentPtr segmentp; + if (seg_iter != mSegments.end()) + { + segmentp = *seg_iter; + } + else + { + //segmentp = mSegments.back(); + return pos; + } + + if (segmentp->canEdit()) + { + segmentp->setEnd(segmentp->getEnd() + insert_len); + if (seg_iter != mSegments.end()) + { + ++seg_iter; + } + } + else + { + // create default editable segment to hold new text + default_segment = new LLNormalTextSegment( new LLStyle(getDefaultStyle()), pos, pos + insert_len, *this); + } + + // shift remaining segments to right + for(;seg_iter != mSegments.end(); ++seg_iter) + { + LLTextSegmentPtr segmentp = *seg_iter; + segmentp->setStart(segmentp->getStart() + insert_len); + segmentp->setEnd(segmentp->getEnd() + insert_len); + } + + // insert new segments + if (segments) + { + if (default_segment.notNull()) + { + // potentially overwritten by segments passed in + insertSegment(default_segment); + } + for (segment_vec_t::iterator seg_iter = segments->begin(); + seg_iter != segments->end(); + ++seg_iter) + { + LLTextSegment* segmentp = *seg_iter; + insertSegment(segmentp); + } + } + + text.insert(pos, wstr); + getViewModel()->setDisplay(text); + + if ( truncate() ) + { + // The user's not getting everything he's hoping for + make_ui_sound("UISndBadKeystroke"); + insert_len = getLength() - old_len; + } + + onValueChange(pos, pos + insert_len); + + return insert_len; +} + +S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length) +{ + LLWString text(getWText()); + segment_set_t::iterator seg_iter = getSegIterContaining(pos); + while(seg_iter != mSegments.end()) + { + LLTextSegmentPtr segmentp = *seg_iter; + S32 end = pos + length; + if (segmentp->getStart() < pos) + { + // deleting from middle of segment + if (segmentp->getEnd() > end) + { + segmentp->setEnd(segmentp->getEnd() - length); + } + // truncating segment + else + { + segmentp->setEnd(pos); + } + } + else if (segmentp->getStart() < end) + { + // deleting entire segment + if (segmentp->getEnd() <= end) + { + // remove segment + segmentp->unlinkFromDocument(this); + segment_set_t::iterator seg_to_erase(seg_iter++); + mSegments.erase(seg_to_erase); + continue; + } + // deleting head of segment + else + { + segmentp->setStart(pos); + segmentp->setEnd(segmentp->getEnd() - length); + } + } + else + { + // shifting segments backward to fill deleted portion + segmentp->setStart(segmentp->getStart() - length); + segmentp->setEnd(segmentp->getEnd() - length); + } + ++seg_iter; + } + + text.erase(pos, length); + getViewModel()->setDisplay(text); + + // recreate default segment in case we erased everything + createDefaultSegment(); + + onValueChange(pos, pos); + + return -length; // This will be wrong if someone calls removeStringNoUndo with an excessive length +} + +S32 LLTextBase::overwriteCharNoUndo(S32 pos, llwchar wc) +{ + if (pos > (S32)getLength()) + { + return 0; + } + LLWString text(getWText()); + text[pos] = wc; + getViewModel()->setDisplay(text); + + onValueChange(pos, pos + 1); + + return 1; +} + + +void LLTextBase::createDefaultSegment() +{ + // ensures that there is always at least one segment + if (mSegments.empty()) + { + LLTextSegmentPtr default_segment = new LLNormalTextSegment( new LLStyle(getDefaultStyle()), 0, getLength() + 1, *this); + mSegments.insert(default_segment); + default_segment->linkToDocument(this); + } +} + +void LLTextBase::insertSegment(LLTextSegmentPtr segment_to_insert) +{ + if (segment_to_insert.isNull()) + { + return; + } + + segment_set_t::iterator cur_seg_iter = getSegIterContaining(segment_to_insert->getStart()); + + if (cur_seg_iter == mSegments.end()) + { + mSegments.insert(segment_to_insert); + segment_to_insert->linkToDocument(this); + } + else + { + LLTextSegmentPtr cur_segmentp = *cur_seg_iter; + if (cur_segmentp->getStart() < segment_to_insert->getStart()) + { + S32 old_segment_end = cur_segmentp->getEnd(); + // split old at start point for new segment + cur_segmentp->setEnd(segment_to_insert->getStart()); + // advance to next segment + ++cur_seg_iter; + // insert remainder of old segment + LLTextSegmentPtr remainder_segment = new LLNormalTextSegment( cur_segmentp->getStyle(), segment_to_insert->getStart(), old_segment_end, *this); + cur_seg_iter = mSegments.insert(cur_seg_iter, remainder_segment); + remainder_segment->linkToDocument(this); + // insert new segment before remainder of old segment + cur_seg_iter = mSegments.insert(cur_seg_iter, segment_to_insert); + + segment_to_insert->linkToDocument(this); + // move to "remanider" segment and start truncation there + ++cur_seg_iter; + } + else + { + cur_seg_iter = mSegments.insert(cur_seg_iter, segment_to_insert); + ++cur_seg_iter; + segment_to_insert->linkToDocument(this); + } + + // now delete/truncate remaining segments as necessary + while(cur_seg_iter != mSegments.end()) + { + cur_segmentp = *cur_seg_iter; + if (cur_segmentp->getEnd() <= segment_to_insert->getEnd()) + { + cur_segmentp->unlinkFromDocument(this); + segment_set_t::iterator seg_to_erase(cur_seg_iter++); + mSegments.erase(seg_to_erase); + } + else + { + cur_segmentp->setStart(segment_to_insert->getEnd()); + break; + } + } + } +} + +BOOL LLTextBase::handleMouseDown(S32 x, S32 y, MASK mask) +{ + LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->handleMouseDown(x, y, mask)) + { + return TRUE; + } + + return LLUICtrl::handleMouseDown(x, y, mask); +} + +BOOL LLTextBase::handleMouseUp(S32 x, S32 y, MASK mask) +{ + LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->handleMouseUp(x, y, mask)) + { + // Did we just click on a link? + if (cur_segment->getStyle() + && cur_segment->getStyle()->isLink()) + { + // *TODO: send URL here? + mURLClickSignal(this, LLSD() ); + } + return TRUE; + } + + return LLUICtrl::handleMouseUp(x, y, mask); +} + +BOOL LLTextBase::handleMiddleMouseDown(S32 x, S32 y, MASK mask) +{ + LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->handleMiddleMouseDown(x, y, mask)) + { + return TRUE; + } + + return LLUICtrl::handleMiddleMouseDown(x, y, mask); +} + +BOOL LLTextBase::handleMiddleMouseUp(S32 x, S32 y, MASK mask) +{ + LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->handleMiddleMouseUp(x, y, mask)) + { + return TRUE; + } + + return LLUICtrl::handleMiddleMouseUp(x, y, mask); +} + +BOOL LLTextBase::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->handleRightMouseDown(x, y, mask)) + { + return TRUE; + } + + return LLUICtrl::handleRightMouseDown(x, y, mask); +} + +BOOL LLTextBase::handleRightMouseUp(S32 x, S32 y, MASK mask) +{ + LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->handleRightMouseUp(x, y, mask)) + { + return TRUE; + } + + return LLUICtrl::handleRightMouseUp(x, y, mask); +} + +BOOL LLTextBase::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->handleDoubleClick(x, y, mask)) + { + return TRUE; + } + + return LLUICtrl::handleDoubleClick(x, y, mask); +} + +BOOL LLTextBase::handleHover(S32 x, S32 y, MASK mask) +{ + LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->handleHover(x, y, mask)) + { + return TRUE; + } + + return LLUICtrl::handleHover(x, y, mask); +} + +BOOL LLTextBase::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->handleScrollWheel(x, y, clicks)) + { + return TRUE; + } + + return LLUICtrl::handleScrollWheel(x, y, clicks); +} + +BOOL LLTextBase::handleToolTip(S32 x, S32 y, MASK mask) +{ + LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y); + if (cur_segment && cur_segment->handleToolTip(x, y, mask)) + { + return TRUE; + } + + return LLUICtrl::handleToolTip(x, y, mask); +} + + +void LLTextBase::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + LLUICtrl::reshape( width, height, called_from_parent ); + + // do this first after reshape, because other things depend on + // up-to-date mTextRect + updateTextRect(); + + needsReflow(); +} + +void LLTextBase::draw() +{ + // reflow if needed, on demand + reflow(); + + // then update scroll position, as cursor may have moved + updateScrollFromCursor(); + + LLColor4 bg_color = mReadOnly + ? mReadOnlyBgColor.get() + : hasFocus() + ? mFocusBgColor.get() + : mWriteableBgColor.get(); + + mDocumentPanel->setBackgroundColor(bg_color); + + LLUICtrl::draw(); + { + LLLocalClipRect clip(mTextRect, mClip); + drawSelectionBackground(); + drawText(); + drawCursor(); + } +} + +//virtual +void LLTextBase::clear() +{ + getViewModel()->setDisplay(LLWStringUtil::null); + clearSegments(); +} + +//virtual +void LLTextBase::setColor( const LLColor4& c ) +{ + mFgColor = c; +} + +//virtual +void LLTextBase::setValue(const LLSD& value ) +{ + setText(value.asString()); +} + +//virtual +void LLTextBase::deselect() +{ + mSelectionStart = 0; + mSelectionEnd = 0; + mIsSelecting = FALSE; +} + + +// Sets the scrollbar from the cursor position +void LLTextBase::updateScrollFromCursor() +{ + // Update scroll position even in read-only mode (when there's no cursor displayed) + // because startOfDoc()/endOfDoc() modify cursor position. See EXT-736. + + if (!mScrollNeeded) + { + return; + } + mScrollNeeded = FALSE; + + // scroll so that the cursor is at the top of the page + LLRect scroller_doc_window = mScroller->getVisibleContentRect(); + LLRect cursor_rect_doc = getLocalRectFromDocIndex(mCursorPos); + cursor_rect_doc.translate(scroller_doc_window.mLeft, scroller_doc_window.mBottom); + mScroller->scrollToShowRect(cursor_rect_doc, LLRect(0, scroller_doc_window.getHeight() - 5, scroller_doc_window.getWidth(), 5)); +} + +S32 LLTextBase::getLeftOffset(S32 width) +{ + switch (mHAlign) + { + case LLFontGL::LEFT: + return 0; + case LLFontGL::HCENTER: + return (mTextRect.getWidth() - width) / 2; + case LLFontGL::RIGHT: + return mTextRect.getWidth() - width; + default: + return 0; + } +} + + +static LLFastTimer::DeclareTimer FTM_TEXT_REFLOW ("Text Reflow"); +void LLTextBase::reflow(S32 start_index) +{ + if (!mReflowNeeded) return; + + LLFastTimer ft(FTM_TEXT_REFLOW); + + updateSegments(); + + while(mReflowNeeded) + { + mReflowNeeded = FALSE; + + bool scrolled_to_bottom = mScroller->isAtBottom(); + + LLRect old_cursor_rect = getLocalRectFromDocIndex(mCursorPos); + bool follow_selection = mTextRect.overlaps(old_cursor_rect); // cursor is visible + S32 first_line = getFirstVisibleLine(); + + // if scroll anchor not on first line, update it to first character of first line + if (!mLineInfoList.empty() + && (mScrollIndex < mLineInfoList[first_line].mDocIndexStart + || mScrollIndex >= mLineInfoList[first_line].mDocIndexEnd)) + { + mScrollIndex = mLineInfoList[first_line].mDocIndexStart; + } + LLRect first_char_rect = getLocalRectFromDocIndex(mScrollIndex); + + S32 cur_top = 0; + + if (getLength()) + { + segment_set_t::iterator seg_iter = mSegments.begin(); + S32 seg_offset = 0; + S32 line_start_index = 0; + const S32 text_width = mTextRect.getWidth(); // optionally reserve room for margin + S32 remaining_pixels = text_width; + LLWString text(getWText()); + S32 line_count = 0; + + // find and erase line info structs starting at start_index and going to end of document + if (!mLineInfoList.empty()) + { + // find first element whose end comes after start_index + line_list_t::iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), start_index, line_end_compare()); + line_start_index = iter->mDocIndexStart; + line_count = iter->mLineNum; + getSegmentAndOffset(iter->mDocIndexStart, &seg_iter, &seg_offset); + mLineInfoList.erase(iter, mLineInfoList.end()); + } + + S32 line_height = 0; + + while(seg_iter != mSegments.end()) + { + LLTextSegmentPtr segment = *seg_iter; + + // track maximum height of any segment on this line + line_height = llmax(line_height, segment->getMaxHeight()); + S32 cur_index = segment->getStart() + seg_offset; + // find run of text from this segment that we can display on one line + S32 end_index = cur_index; + while(end_index < segment->getEnd() && text[end_index] != '\n') + { + ++end_index; + } + + // ask segment how many character fit in remaining space + S32 max_characters = end_index - cur_index; + S32 character_count = segment->getNumChars(getWordWrap() ? llmax(0, remaining_pixels) : S32_MAX, + seg_offset, + cur_index - line_start_index, + max_characters); + + + S32 segment_width = segment->getWidth(seg_offset, character_count); + remaining_pixels -= segment_width; + S32 text_left = getLeftOffset(text_width - remaining_pixels); + + seg_offset += character_count; + + S32 last_segment_char_on_line = segment->getStart() + seg_offset; + + // if we didn't finish the current segment... + if (last_segment_char_on_line < segment->getEnd()) + { + // set up index for next line + // ...skip newline, we don't want to draw + S32 next_line_count = line_count; + if (text[last_segment_char_on_line] == '\n') + { + seg_offset++; + last_segment_char_on_line++; + next_line_count++; + } + + // add line info and keep going + mLineInfoList.push_back(line_info( + line_start_index, + last_segment_char_on_line, + LLRect(text_left, + cur_top, + text_left + (text_width - remaining_pixels), + cur_top - line_height), + line_count)); + + line_start_index = segment->getStart() + seg_offset; + cur_top -= llround((F32)line_height * mLineSpacingMult) + mLineSpacingPixels; + remaining_pixels = text_width; + line_height = 0; + line_count = next_line_count; + } + // ...just consumed last segment.. + else if (++segment_set_t::iterator(seg_iter) == mSegments.end()) + { + mLineInfoList.push_back(line_info( + line_start_index, + last_segment_char_on_line, + LLRect(text_left, + cur_top, + text_left + (text_width - remaining_pixels), + cur_top - line_height), + line_count)); + cur_top -= llround((F32)line_height * mLineSpacingMult) + mLineSpacingPixels; + break; + } + // finished a segment and there are segments remaining on this line + else + { + // subtract pixels used and increment segment + ++seg_iter; + seg_offset = 0; + } + } + } + + if (mLineInfoList.empty()) + { + mContentsRect = LLRect(0, mVPad, mHPad, 0); + } + else + { + + mContentsRect = mLineInfoList.begin()->mRect; + for (line_list_t::const_iterator line_iter = ++mLineInfoList.begin(); + line_iter != mLineInfoList.end(); + ++line_iter) + { + mContentsRect.unionWith(line_iter->mRect); + } + + mContentsRect.mRight += mHPad; + mContentsRect.mTop += mVPad; + // get around rounding errors when clipping text against rectangle + mContentsRect.stretch(1); + } + + // change mDocumentPanel document size to accomodate reflowed text + LLRect document_rect; + document_rect.setOriginAndSize(1, 1, + mScroller->getContentWindowRect().getWidth(), + llmax(mScroller->getContentWindowRect().getHeight(), mContentsRect.getHeight())); + mDocumentPanel->setShape(document_rect); + + // after making document big enough to hold all the text, move the text to fit in the document + if (!mLineInfoList.empty()) + { + S32 delta_pos = mDocumentPanel->getRect().getHeight() - mLineInfoList.begin()->mRect.mTop - mVPad; + // move line segments to fit new document rect + for (line_list_t::iterator it = mLineInfoList.begin(); it != mLineInfoList.end(); ++it) + { + it->mRect.translate(0, delta_pos); + } + mContentsRect.translate(0, delta_pos); + } + + // calculate visible region for diplaying text + updateTextRect(); + + for (segment_set_t::iterator segment_it = mSegments.begin(); + segment_it != mSegments.end(); + ++segment_it) + { + LLTextSegmentPtr segmentp = *segment_it; + segmentp->updateLayout(*this); + + } + + // apply scroll constraints after reflowing text + if (!hasMouseCapture()) + { + LLRect visible_content_rect = mScroller->getVisibleContentRect(); + if (scrolled_to_bottom && mTrackEnd) + { + // keep bottom of text buffer visible + endOfDoc(); + } + else if (hasSelection() && follow_selection) + { + // keep cursor in same vertical position on screen when selecting text + LLRect new_cursor_rect_doc = getLocalRectFromDocIndex(mCursorPos); + new_cursor_rect_doc.translate(visible_content_rect.mLeft, visible_content_rect.mBottom); + mScroller->scrollToShowRect(new_cursor_rect_doc, old_cursor_rect); + //llassert_always(getLocalRectFromDocIndex(mCursorPos).mBottom == old_cursor_rect.mBottom); + } + else + { + // keep first line of text visible + LLRect new_first_char_rect = getLocalRectFromDocIndex(mScrollIndex); + new_first_char_rect.translate(visible_content_rect.mLeft, visible_content_rect.mBottom); + mScroller->scrollToShowRect(new_first_char_rect, first_char_rect); + //llassert_always(getLocalRectFromDocIndex(mScrollIndex).mBottom == first_char_rect.mBottom); + } + } + } + + + // reset desired x cursor position + updateCursorXPos(); +} + +LLRect LLTextBase::getContentsRect() +{ + reflow(); + return mContentsRect; +} + + +void LLTextBase::clearSegments() +{ + mSegments.clear(); + createDefaultSegment(); +} + +S32 LLTextBase::getLineStart( S32 line ) const +{ + S32 num_lines = getLineCount(); + if (num_lines == 0) + { + return 0; + } + + line = llclamp(line, 0, num_lines-1); + return mLineInfoList[line].mDocIndexStart; +} + + +S32 LLTextBase::getLineNumFromDocIndex( S32 doc_index, bool include_wordwrap) const +{ + if (mLineInfoList.empty()) + { + return 0; + } + else + { + line_list_t::const_iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), doc_index, line_end_compare()); + if (include_wordwrap) + { + return iter - mLineInfoList.begin(); + } + else + { + if (iter == mLineInfoList.end()) + { + return mLineInfoList.back().mLineNum; + } + else + { + return iter->mLineNum; + } + } + } +} + +// Given an offset into text (pos), find the corresponding line (from the start of the doc) and an offset into the line. +S32 LLTextBase::getLineOffsetFromDocIndex( S32 startpos, bool include_wordwrap) const +{ + if (mLineInfoList.empty()) + { + return startpos; + } + else + { + line_list_t::const_iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), startpos, line_end_compare()); + return startpos - iter->mDocIndexStart; + } +} + +S32 LLTextBase::getFirstVisibleLine() const +{ + LLRect visible_region = mScroller->getVisibleContentRect(); + + // binary search for line that starts before top of visible buffer + line_list_t::const_iterator iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_bottom()); + + return iter - mLineInfoList.begin(); +} + +std::pair<S32, S32> LLTextBase::getVisibleLines() const +{ + LLRect visible_region = mScroller->getVisibleContentRect(); + + // binary search for line that starts before top of visible buffer and starts before end of visible buffer + line_list_t::const_iterator first_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_bottom()); + line_list_t::const_iterator last_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mBottom, compare_top()); + + return std::pair<S32, S32>(first_iter - mLineInfoList.begin(), last_iter - mLineInfoList.begin()); +} + + + +LLTextViewModel* LLTextBase::getViewModel() const +{ + return (LLTextViewModel*)mViewModel.get(); +} + +void LLTextBase::addDocumentChild(LLView* view) +{ + mDocumentPanel->addChild(view); +} + +void LLTextBase::removeDocumentChild(LLView* view) +{ + mDocumentPanel->removeChild(view); +} + + +static LLFastTimer::DeclareTimer FTM_UPDATE_TEXT_SEGMENTS("Update Text Segments"); +void LLTextBase::updateSegments() +{ + LLFastTimer ft(FTM_UPDATE_TEXT_SEGMENTS); + createDefaultSegment(); +} + +void LLTextBase::getSegmentAndOffset( S32 startpos, segment_set_t::const_iterator* seg_iter, S32* offsetp ) const +{ + *seg_iter = getSegIterContaining(startpos); + if (*seg_iter == mSegments.end()) + { + *offsetp = 0; + } + else + { + *offsetp = startpos - (**seg_iter)->getStart(); + } +} + +void LLTextBase::getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp ) +{ + *seg_iter = getSegIterContaining(startpos); + if (*seg_iter == mSegments.end()) + { + *offsetp = 0; + } + else + { + *offsetp = startpos - (**seg_iter)->getStart(); + } +} + +LLTextBase::segment_set_t::iterator LLTextBase::getSegIterContaining(S32 index) +{ + segment_set_t::iterator it = mSegments.upper_bound(new LLIndexSegment(index)); + return it; +} + +LLTextBase::segment_set_t::const_iterator LLTextBase::getSegIterContaining(S32 index) const +{ + LLTextBase::segment_set_t::const_iterator it = mSegments.upper_bound(new LLIndexSegment(index)); + return it; +} + +// Finds the text segment (if any) at the give local screen position +LLTextSegmentPtr LLTextBase::getSegmentAtLocalPos( S32 x, S32 y ) +{ + // Find the cursor position at the requested local screen position + S32 offset = getDocIndexFromLocalCoord( x, y, FALSE ); + segment_set_t::iterator seg_iter = getSegIterContaining(offset); + if (seg_iter != mSegments.end()) + { + return *seg_iter; + } + else + { + return LLTextSegmentPtr(); + } +} + +void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url) +{ + // work out the XUI menu file to use for this url + LLUrlMatch match; + std::string url = in_url; + if (! LLUrlRegistry::instance().findUrl(url, match)) + { + return; + } + + std::string xui_file = match.getMenuName(); + if (xui_file.empty()) + { + return; + } + + // set up the callbacks for all of the potential menu items, N.B. we + // don't use const ref strings in callbacks in case url goes out of scope + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + registrar.add("Url.Open", boost::bind(&LLUrlAction::openURL, url)); + registrar.add("Url.OpenInternal", boost::bind(&LLUrlAction::openURLInternal, url)); + registrar.add("Url.OpenExternal", boost::bind(&LLUrlAction::openURLExternal, url)); + registrar.add("Url.Execute", boost::bind(&LLUrlAction::executeSLURL, url)); + registrar.add("Url.Teleport", boost::bind(&LLUrlAction::teleportToLocation, url)); + registrar.add("Url.CopyLabel", boost::bind(&LLUrlAction::copyLabelToClipboard, url)); + registrar.add("Url.CopyUrl", boost::bind(&LLUrlAction::copyURLToClipboard, url)); + + // create and return the context menu from the XUI file + delete mPopupMenu; + mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(xui_file, LLMenuGL::sMenuContainer, + LLMenuHolderGL::child_registry_t::instance()); + if (mPopupMenu) + { + mPopupMenu->show(x, y); + LLMenuGL::showPopup(this, mPopupMenu, x, y); + } +} + +void LLTextBase::setText(const LLStringExplicit &utf8str) +{ + // clear out the existing text and segments + clear(); + + truncate(); + + createDefaultSegment(); + + startOfDoc(); + deselect(); + + // append the new text (supports Url linking) + std::string text(utf8str); + LLStringUtil::removeCRLF(text); + + appendText(text, false); + + needsReflow(); + + //resetDirty(); + onValueChange(0, getLength()); +} + +//virtual +std::string LLTextBase::getText() const +{ + return getViewModel()->getValue().asString(); +} + +void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params) +{ + LLStyle::Params style_params(input_params); + style_params.fillFrom(getDefaultStyle()); + + if (!style_params.font.isProvided()) + { + style_params.font = mDefaultFont; + } + if (!style_params.drop_shadow.isProvided()) + { + style_params.drop_shadow = mFontShadow; + } + + S32 part = (S32)LLTextParser::WHOLE; + if(mParseHTML) + { + S32 start=0,end=0; + LLUrlMatch match; + std::string text = new_text; + while ( LLUrlRegistry::instance().findUrl(text, match, + boost::bind(&LLTextBase::replaceUrlLabel, this, _1, _2)) ) + { + start = match.getStart(); + end = match.getEnd()+1; + + LLStyle::Params link_params = style_params; + link_params.color = match.getColor(); + // apply font name from requested style_params + std::string font_name = LLFontGL::nameFromFont(style_params.font()); + link_params.font.name.setIfNotProvided(font_name); + link_params.font.style = "UNDERLINE"; + link_params.link_href = match.getUrl(); + + // output the text before the Url + if (start > 0) + { + if (part == (S32)LLTextParser::WHOLE || + part == (S32)LLTextParser::START) + { + part = (S32)LLTextParser::START; + } + else + { + part = (S32)LLTextParser::MIDDLE; + } + std::string subtext=text.substr(0,start); + appendAndHighlightText(subtext, prepend_newline, part, style_params); + prepend_newline = false; + } + + // output an optional icon before the Url + if (! match.getIcon().empty()) + { + LLUIImagePtr image = LLUI::getUIImage(match.getIcon()); + if (image) + { + LLStyle::Params icon; + icon.image = image; + // HACK: fix spacing of images and remove the fixed char spacing + appendAndHighlightText(" ", prepend_newline, part, icon); + prepend_newline = false; + } + } + // output the styled Url + appendAndHighlightText(match.getLabel(), prepend_newline, part, link_params); + prepend_newline = false; + + // set the tooltip for the Url label + if (! match.getTooltip().empty()) + { + segment_set_t::iterator it = getSegIterContaining(getLength()-1); + if (it != mSegments.end()) + { + LLTextSegmentPtr segment = *it; + segment->setToolTip(match.getTooltip()); + } + } + + // move on to the rest of the text after the Url + if (end < (S32)text.length()) + { + text = text.substr(end,text.length() - end); + end=0; + part=(S32)LLTextParser::END; + } + else + { + break; + } + } + if (part != (S32)LLTextParser::WHOLE) part=(S32)LLTextParser::END; + if (end < (S32)text.length()) appendAndHighlightText(text, prepend_newline, part, style_params); + } + else + { + appendAndHighlightText(new_text, prepend_newline, part, style_params); + } +} + +void LLTextBase::appendAndHighlightText(const std::string &new_text, bool prepend_newline, S32 highlight_part, const LLStyle::Params& stylep) +{ + if (new_text.empty()) return; + + // Save old state + S32 selection_start = mSelectionStart; + S32 selection_end = mSelectionEnd; + BOOL was_selecting = mIsSelecting; + S32 cursor_pos = mCursorPos; + S32 old_length = getLength(); + BOOL cursor_was_at_end = (mCursorPos == old_length); + + deselect(); + + setCursorPos(old_length); + + LLTextParser* highlight = LLTextParser::getInstance(); + + if (mParseHighlights && highlight) + { + LLStyle::Params highlight_params = stylep; + + LLSD pieces = highlight->parsePartialLineHighlights(new_text, highlight_params.color(), (LLTextParser::EHighlightPosition)highlight_part); + for (S32 i = 0; i < pieces.size(); i++) + { + LLSD color_llsd = pieces[i]["color"]; + LLColor4 lcolor; + lcolor.setValue(color_llsd); + highlight_params.color = lcolor; + + LLWString wide_text; + if (prepend_newline && (i == 0 || pieces.size() <= 1 )) + { + wide_text = utf8str_to_wstring(std::string("\n") + pieces[i]["text"].asString()); + } + else + { + wide_text = utf8str_to_wstring(pieces[i]["text"].asString()); + } + S32 cur_length = getLength(); + LLTextSegmentPtr segmentp = new LLNormalTextSegment(new LLStyle(highlight_params), cur_length, cur_length + wide_text.size(), *this); + segment_vec_t segments; + segments.push_back(segmentp); + insertStringNoUndo(cur_length, wide_text, &segments); + } + } + else + { + LLWString wide_text; + + // Add carriage return if not first line + if (getLength() != 0 + && prepend_newline) + { + wide_text = utf8str_to_wstring(std::string("\n") + new_text); + } + else + { + wide_text = utf8str_to_wstring(new_text); + } + + segment_vec_t segments; + S32 segment_start = old_length; + S32 segment_end = old_length + wide_text.size(); + segments.push_back(new LLNormalTextSegment(new LLStyle(stylep), segment_start, segment_end, *this )); + + insertStringNoUndo(getLength(), wide_text, &segments); + } + + needsReflow(); + + // Set the cursor and scroll position + if( selection_start != selection_end ) + { + mSelectionStart = selection_start; + mSelectionEnd = selection_end; + + mIsSelecting = was_selecting; + setCursorPos(cursor_pos); + } + else if( cursor_was_at_end ) + { + setCursorPos(getLength()); + } + else + { + setCursorPos(cursor_pos); + } + + //if( !allow_undo ) + //{ + // blockUndo(); + //} +} + + +void LLTextBase::replaceUrlLabel(const std::string &url, + const std::string &label) +{ + // get the full (wide) text for the editor so we can change it + LLWString text = getWText(); + LLWString wlabel = utf8str_to_wstring(label); + bool modified = false; + S32 seg_start = 0; + + // iterate through each segment looking for ones styled as links + segment_set_t::iterator it; + for (it = mSegments.begin(); it != mSegments.end(); ++it) + { + LLTextSegment *seg = *it; + const LLStyleSP style = seg->getStyle(); + + // update segment start/end length in case we replaced text earlier + S32 seg_length = seg->getEnd() - seg->getStart(); + seg->setStart(seg_start); + seg->setEnd(seg_start + seg_length); + + // if we find a link with our Url, then replace the label + if (style->isLink() && style->getLinkHREF() == url) + { + S32 start = seg->getStart(); + S32 end = seg->getEnd(); + text = text.substr(0, start) + wlabel + text.substr(end, text.size() - end + 1); + seg->setEnd(start + wlabel.size()); + modified = true; + } + + // work out the character offset for the next segment + seg_start = seg->getEnd(); + } + + // update the editor with the new (wide) text string + if (modified) + { + getViewModel()->setDisplay(text); + deselect(); + setCursorPos(mCursorPos); + needsReflow(); + } +} + + +void LLTextBase::setWText(const LLWString& text) +{ + setText(wstring_to_utf8str(text)); +} + +LLWString LLTextBase::getWText() const +{ + return getViewModel()->getDisplay(); +} + +// If round is true, if the position is on the right half of a character, the cursor +// will be put to its right. If round is false, the cursor will always be put to the +// character's left. + +S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const +{ + // Figure out which line we're nearest to. + LLRect visible_region = mScroller->getVisibleContentRect(); + + // binary search for line that starts before local_y + line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), local_y - mTextRect.mBottom + visible_region.mBottom, compare_bottom()); + + if (line_iter == mLineInfoList.end()) + { + return getLength(); // past the end + } + + S32 pos = getLength(); + S32 start_x = mTextRect.mLeft + line_iter->mRect.mLeft; + + segment_set_t::iterator line_seg_iter; + S32 line_seg_offset; + for(getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset); + line_seg_iter != mSegments.end(); + ++line_seg_iter, line_seg_offset = 0) + { + const LLTextSegmentPtr segmentp = *line_seg_iter; + + S32 segment_line_start = segmentp->getStart() + line_seg_offset; + S32 segment_line_length = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd - 1) - segment_line_start; + S32 text_width = segmentp->getWidth(line_seg_offset, segment_line_length); + if (local_x < start_x + text_width // cursor to left of right edge of text + || segmentp->getEnd() >= line_iter->mDocIndexEnd - 1) // or this segment wraps to next line + { + // Figure out which character we're nearest to. + S32 offset; + if (!segmentp->canEdit()) + { + S32 segment_width = segmentp->getWidth(0, segmentp->getEnd() - segmentp->getStart()); + if (round && local_x - start_x > segment_width / 2) + { + offset = segment_line_length; + } + else + { + offset = 0; + } + } + else + { + offset = segmentp->getOffset(local_x - start_x, line_seg_offset, segment_line_length, round); + } + pos = segment_line_start + offset; + break; + } + start_x += text_width; + } + + return pos; +} + +LLRect LLTextBase::getLocalRectFromDocIndex(S32 pos) const +{ + LLRect local_rect; + if (mLineInfoList.empty()) + { + local_rect = mTextRect; + local_rect.mBottom = local_rect.mTop - (S32)(mDefaultFont->getLineHeight()); + return local_rect; + } + + // clamp pos to valid values + pos = llclamp(pos, 0, mLineInfoList.back().mDocIndexEnd - 1); + + + // find line that contains cursor + line_list_t::const_iterator line_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), pos, line_end_compare()); + + LLRect scrolled_view_rect = mScroller->getVisibleContentRect(); + local_rect.mLeft = mTextRect.mLeft - scrolled_view_rect.mLeft + line_iter->mRect.mLeft; + local_rect.mBottom = mTextRect.mBottom + (line_iter->mRect.mBottom - scrolled_view_rect.mBottom); + local_rect.mTop = mTextRect.mBottom + (line_iter->mRect.mTop - scrolled_view_rect.mBottom); + + segment_set_t::iterator line_seg_iter; + S32 line_seg_offset; + segment_set_t::iterator cursor_seg_iter; + S32 cursor_seg_offset; + getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset); + getSegmentAndOffset(pos, &cursor_seg_iter, &cursor_seg_offset); + + while(line_seg_iter != mSegments.end()) + { + const LLTextSegmentPtr segmentp = *line_seg_iter; + + if (line_seg_iter == cursor_seg_iter) + { + // cursor advanced to right based on difference in offset of cursor to start of line + local_rect.mLeft += segmentp->getWidth(line_seg_offset, cursor_seg_offset - line_seg_offset); + + break; + } + else + { + // add remainder of current text segment to cursor position + local_rect.mLeft += segmentp->getWidth(line_seg_offset, (segmentp->getEnd() - segmentp->getStart()) - line_seg_offset); + // offset will be 0 for all segments after the first + line_seg_offset = 0; + // go to next text segment on this line + ++line_seg_iter; + } + } + + local_rect.mRight = local_rect.mLeft; + + return local_rect; +} + +void LLTextBase::updateCursorXPos() +{ + // reset desired x cursor position + mDesiredXPixel = getLocalRectFromDocIndex(mCursorPos).mLeft; +} + + +void LLTextBase::startOfLine() +{ + S32 offset = getLineOffsetFromDocIndex(mCursorPos); + setCursorPos(mCursorPos - offset); +} + +void LLTextBase::endOfLine() +{ + S32 line = getLineNumFromDocIndex(mCursorPos); + S32 num_lines = getLineCount(); + if (line + 1 >= num_lines) + { + setCursorPos(getLength()); + } + else + { + setCursorPos( getLineStart(line + 1) - 1 ); + } +} + +void LLTextBase::startOfDoc() +{ + setCursorPos(0); +} + +void LLTextBase::endOfDoc() +{ + setCursorPos(getLength()); +} + +void LLTextBase::changePage( S32 delta ) +{ + const S32 PIXEL_OVERLAP_ON_PAGE_CHANGE = 10; + if (delta == 0) return; + + LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos); + + if( delta == -1 ) + { + mScroller->pageUp(PIXEL_OVERLAP_ON_PAGE_CHANGE); + } + else + if( delta == 1 ) + { + mScroller->pageDown(PIXEL_OVERLAP_ON_PAGE_CHANGE); + } + + if (getLocalRectFromDocIndex(mCursorPos) == cursor_rect) + { + // cursor didn't change apparent position, so move to top or bottom of document, respectively + if (delta < 0) + { + startOfDoc(); + } + else + { + endOfDoc(); + } + } + else + { + setCursorAtLocalPos(cursor_rect.getCenterX(), cursor_rect.getCenterY(), true, false); + } +} + +// Picks a new cursor position based on the screen size of text being drawn. +void LLTextBase::setCursorAtLocalPos( S32 local_x, S32 local_y, bool round, bool keep_cursor_offset ) +{ + setCursorPos(getDocIndexFromLocalCoord(local_x, local_y, round), keep_cursor_offset); +} + + +void LLTextBase::changeLine( S32 delta ) +{ + S32 line = getLineNumFromDocIndex(mCursorPos); + + S32 new_line = line; + if( (delta < 0) && (line > 0 ) ) + { + new_line = line - 1; + } + else if( (delta > 0) && (line < (getLineCount() - 1)) ) + { + new_line = line + 1; + } + + LLRect visible_region = mScroller->getVisibleContentRect(); + + S32 new_cursor_pos = getDocIndexFromLocalCoord(mDesiredXPixel, mLineInfoList[new_line].mRect.mBottom + mTextRect.mBottom - visible_region.mBottom, TRUE); + setCursorPos(new_cursor_pos, true); +} + + +bool LLTextBase::setCursor(S32 row, S32 column) +{ + if (0 <= row && row < (S32)mLineInfoList.size()) + { + S32 doc_pos = mLineInfoList[row].mDocIndexStart; + column = llclamp(column, 0, mLineInfoList[row].mDocIndexEnd - mLineInfoList[row].mDocIndexStart - 1); + doc_pos += column; + updateCursorXPos(); + + return setCursorPos(doc_pos); + } + return false; +} + + +bool LLTextBase::setCursorPos(S32 cursor_pos, bool keep_cursor_offset) +{ + S32 new_cursor_pos = cursor_pos; + if (new_cursor_pos != mCursorPos) + { + new_cursor_pos = getEditableIndex(new_cursor_pos, new_cursor_pos >= mCursorPos); + } + + mCursorPos = llclamp(new_cursor_pos, 0, (S32)getLength()); + needsScroll(); + if (!keep_cursor_offset) + updateCursorXPos(); + // did we get requested position? + return new_cursor_pos == cursor_pos; +} + +// constraint cursor to editable segments of document +S32 LLTextBase::getEditableIndex(S32 index, bool increasing_direction) +{ + segment_set_t::iterator segment_iter; + S32 offset; + getSegmentAndOffset(index, &segment_iter, &offset); + if (segment_iter == mSegments.end()) + { + return 0; + } + + LLTextSegmentPtr segmentp = *segment_iter; + + if (segmentp->canEdit()) + { + return segmentp->getStart() + offset; + } + else if (segmentp->getStart() < index && index < segmentp->getEnd()) + { + // bias towards document end + if (increasing_direction) + { + return segmentp->getEnd(); + } + // bias towards document start + else + { + return segmentp->getStart(); + } + } + else + { + return index; + } +} + +void LLTextBase::updateTextRect() +{ + LLRect old_text_rect = mTextRect; + mTextRect = mScroller->getContentWindowRect(); + //FIXME: replace border with image? + if (mBorderVisible) + { + mTextRect.stretch(-1); + } + mTextRect.mLeft += mHPad; + mTextRect.mTop -= mVPad; + if (mTextRect != old_text_rect) + { + needsReflow(); + } +} + + +void LLTextBase::startSelection() +{ + if( !mIsSelecting ) + { + mIsSelecting = TRUE; + mSelectionStart = mCursorPos; + mSelectionEnd = mCursorPos; + } +} + +void LLTextBase::endSelection() +{ + if( mIsSelecting ) + { + mIsSelecting = FALSE; + mSelectionEnd = mCursorPos; + } +} + // // LLTextSegment // @@ -69,17 +2055,29 @@ S32 LLTextSegment::getMaxHeight() const { return 0; } bool LLTextSegment::canEdit() const { return false; } void LLTextSegment::unlinkFromDocument(LLTextBase*) {} void LLTextSegment::linkToDocument(LLTextBase*) {} -void LLTextSegment::setHasMouseHover(bool hover) {} const LLColor4& LLTextSegment::getColor() const { return LLColor4::white; } void LLTextSegment::setColor(const LLColor4 &color) {} const LLStyleSP LLTextSegment::getStyle() const {static LLStyleSP sp(new LLStyle()); return sp; } void LLTextSegment::setStyle(const LLStyleSP &style) {} void LLTextSegment::setToken( LLKeywordToken* token ) {} LLKeywordToken* LLTextSegment::getToken() const { return NULL; } -BOOL LLTextSegment::getToolTip( std::string& msg ) const { return FALSE; } void LLTextSegment::setToolTip( const std::string &msg ) {} void LLTextSegment::dump() const {} - +BOOL LLTextSegment::handleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; } +BOOL LLTextSegment::handleMouseUp(S32 x, S32 y, MASK mask) { return FALSE; } +BOOL LLTextSegment::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; } +BOOL LLTextSegment::handleMiddleMouseUp(S32 x, S32 y, MASK mask) { return FALSE; } +BOOL LLTextSegment::handleRightMouseDown(S32 x, S32 y, MASK mask) { return FALSE; } +BOOL LLTextSegment::handleRightMouseUp(S32 x, S32 y, MASK mask) { return FALSE; } +BOOL LLTextSegment::handleDoubleClick(S32 x, S32 y, MASK mask) { return FALSE; } +BOOL LLTextSegment::handleHover(S32 x, S32 y, MASK mask) { return FALSE; } +BOOL LLTextSegment::handleScrollWheel(S32 x, S32 y, S32 clicks) { return FALSE; } +BOOL LLTextSegment::handleToolTip(S32 x, S32 y, MASK mask) { return FALSE; } +std::string LLTextSegment::getName() const { return ""; } +void LLTextSegment::onMouseCaptureLost() {} +void LLTextSegment::screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const {} +void LLTextSegment::localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const {} +BOOL LLTextSegment::hasMouseCapture() { return FALSE; } // // LLNormalTextSegment @@ -89,7 +2087,6 @@ LLNormalTextSegment::LLNormalTextSegment( const LLStyleSP& style, S32 start, S32 : LLTextSegment(start, end), mStyle( style ), mToken(NULL), - mHasMouseHover(false), mEditor(editor) { mMaxHeight = llceil(mStyle->getFont()->getLineHeight()); @@ -98,7 +2095,6 @@ LLNormalTextSegment::LLNormalTextSegment( const LLStyleSP& style, S32 start, S32 LLNormalTextSegment::LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible) : LLTextSegment(start, end), mToken(NULL), - mHasMouseHover(false), mEditor(editor) { mStyle = new LLStyle(LLStyle::Params().visible(is_visible).color(color)); @@ -115,21 +2111,25 @@ F32 LLNormalTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selec LLUIImagePtr image = mStyle->getImage(); S32 style_image_height = image->getHeight(); S32 style_image_width = image->getWidth(); - image->draw(draw_rect.mLeft, draw_rect.mTop-style_image_height, + // Center the image vertically + S32 image_bottom = draw_rect.getCenterY() - (style_image_height/2); + image->draw(draw_rect.mLeft, image_bottom, style_image_width, style_image_height); } - return drawClippedSegment( getStart() + start, getStart() + end, selection_start, selection_end, draw_rect.mLeft, draw_rect.mBottom); + return drawClippedSegment( getStart() + start, getStart() + end, selection_start, selection_end, draw_rect); } return draw_rect.mLeft; } // Draws a single text segment, reversing the color for selection if needed. -F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, F32 x, F32 y) +F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRect rect) { + F32 alpha = LLViewDrawContext::getCurrentContext().mAlpha; + const LLWString &text = mEditor.getWText(); - F32 right_x = x; + F32 right_x = rect.mLeft; if (!mStyle->isVisible()) { return right_x; @@ -137,7 +2137,7 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele const LLFontGL* font = mStyle->getFont(); - LLColor4 color = mStyle->getColor(); + LLColor4 color = mStyle->getColor() % alpha; font = mStyle->getFont(); @@ -147,9 +2147,9 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele S32 start = seg_start; S32 end = llmin( selection_start, seg_end ); S32 length = end - start; - font->render(text, start, x, y, color, LLFontGL::LEFT, LLFontGL::BOTTOM, 0, LLFontGL::NO_SHADOW, length, S32_MAX, &right_x, mEditor.allowsEmbeddedItems()); + font->render(text, start, rect.mLeft, rect.mTop, color, LLFontGL::LEFT, LLFontGL::TOP, 0, mStyle->getShadowType(), length, rect.getWidth(), &right_x, mEditor.getUseEllipses()); } - x = right_x; + rect.mLeft = (S32)ceil(right_x); if( (selection_start < seg_end) && (selection_end > seg_start) ) { @@ -158,18 +2158,18 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele S32 end = llmin( selection_end, seg_end ); S32 length = end - start; - font->render(text, start, x, y, + font->render(text, start, rect.mLeft, rect.mTop, LLColor4( 1.f - color.mV[0], 1.f - color.mV[1], 1.f - color.mV[2], 1.f ), - LLFontGL::LEFT, LLFontGL::BOTTOM, 0, LLFontGL::NO_SHADOW, length, S32_MAX, &right_x, mEditor.allowsEmbeddedItems()); + LLFontGL::LEFT, LLFontGL::TOP, 0, LLFontGL::NO_SHADOW, length, rect.mRight, &right_x, mEditor.getUseEllipses()); } - x = right_x; + rect.mLeft = (S32)ceil(right_x); if( selection_end < seg_end ) { // Draw normally S32 start = llmax( selection_end, seg_start ); S32 end = seg_end; S32 length = end - start; - font->render(text, start, x, y, color, LLFontGL::LEFT, LLFontGL::BOTTOM, 0, LLFontGL::NO_SHADOW, length, S32_MAX, &right_x, mEditor.allowsEmbeddedItems()); + font->render(text, start, rect.mLeft, rect.mTop, color, LLFontGL::LEFT, LLFontGL::TOP, 0, mStyle->getShadowType(), length, rect.mRight, &right_x, mEditor.getUseEllipses()); } return right_x; } @@ -179,21 +2179,54 @@ S32 LLNormalTextSegment::getMaxHeight() const return mMaxHeight; } -BOOL LLNormalTextSegment::getToolTip(std::string& msg) const +BOOL LLNormalTextSegment::handleHover(S32 x, S32 y, MASK mask) +{ + if (getStyle() && getStyle()->isLink()) + { + LLUI::getWindow()->setCursor(UI_CURSOR_HAND); + return TRUE; + } + return FALSE; +} + +BOOL LLNormalTextSegment::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + if (getStyle() && getStyle()->isLink()) + { + mEditor.createUrlContextMenu(x, y, getStyle()->getLinkHREF()); + return TRUE; + } + return FALSE; +} + +BOOL LLNormalTextSegment::handleMouseUp(S32 x, S32 y, MASK mask) { + if (getStyle() && getStyle()->isLink()) + { + LLUrlAction::clickAction(getStyle()->getLinkHREF()); + return TRUE; + } + + return FALSE; +} + +BOOL LLNormalTextSegment::handleToolTip(S32 x, S32 y, MASK mask) +{ + std::string msg; // do we have a tooltip for a loaded keyword (for script editor)? if (mToken && !mToken->getToolTip().empty()) { const LLWString& wmsg = mToken->getToolTip(); - msg = wstring_to_utf8str(wmsg); + LLToolTipMgr::instance().show(wstring_to_utf8str(wmsg)); return TRUE; } // or do we have an explicitly set tooltip (e.g., for Urls) - if (! mTooltip.empty()) + if (!mTooltip.empty()) { - msg = mTooltip; + LLToolTipMgr::instance().show(mTooltip); return TRUE; } + return FALSE; } @@ -258,201 +2291,69 @@ void LLNormalTextSegment::dump() const llendl; } -////////////////////////////////////////////////////////////////////////// + // -// LLTextBase +// LLInlineViewSegment // -LLTextBase::LLTextBase(const LLUICtrl::Params &p) : - mHoverSegment(NULL), - mDefaultFont(p.font), - mParseHTML(TRUE), - mPopupMenu(NULL) -{ -} - -LLTextBase::~LLTextBase() +LLInlineViewSegment::LLInlineViewSegment(LLView* view, S32 start, S32 end) +: LLTextSegment(start, end), + mView(view) { - clearSegments(); -} +} -void LLTextBase::clearSegments() +LLInlineViewSegment::~LLInlineViewSegment() { - setHoverSegment(NULL); - mSegments.clear(); + mView->die(); } -void LLTextBase::setHoverSegment(LLTextSegmentPtr segment) +S32 LLInlineViewSegment::getWidth(S32 first_char, S32 num_chars) const { - if (mHoverSegment) + if (first_char == 0 && num_chars == 0) { - mHoverSegment->setHasMouseHover(false); - } - if (segment) - { - segment->setHasMouseHover(true); - } - mHoverSegment = segment; -} - -void LLTextBase::getSegmentAndOffset( S32 startpos, segment_set_t::const_iterator* seg_iter, S32* offsetp ) const -{ - *seg_iter = getSegIterContaining(startpos); - if (*seg_iter == mSegments.end()) - { - *offsetp = 0; + return 0; } else { - *offsetp = startpos - (**seg_iter)->getStart(); + return mView->getRect().getWidth(); } } -void LLTextBase::getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp ) +S32 LLInlineViewSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const { - *seg_iter = getSegIterContaining(startpos); - if (*seg_iter == mSegments.end()) + if (line_offset != 0 && num_pixels < mView->getRect().getWidth()) { - *offsetp = 0; + return 0; } else { - *offsetp = startpos - (**seg_iter)->getStart(); + return mEnd - mStart; } } -LLTextBase::segment_set_t::iterator LLTextBase::getSegIterContaining(S32 index) +void LLInlineViewSegment::updateLayout(const LLTextBase& editor) { - segment_set_t::iterator it = mSegments.upper_bound(new LLIndexSegment(index)); - return it; + LLRect start_rect = editor.getLocalRectFromDocIndex(mStart); + LLRect doc_rect = editor.getDocumentPanel()->getRect(); + mView->setOrigin(doc_rect.mLeft + start_rect.mLeft, doc_rect.mBottom + start_rect.mBottom); } -LLTextBase::segment_set_t::const_iterator LLTextBase::getSegIterContaining(S32 index) const +F32 LLInlineViewSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect) { - LLTextBase::segment_set_t::const_iterator it = mSegments.upper_bound(new LLIndexSegment(index)); - return it; + return (F32)(draw_rect.mLeft + mView->getRect().getWidth()); } -// Finds the text segment (if any) at the give local screen position -LLTextSegmentPtr LLTextBase::getSegmentAtLocalPos( S32 x, S32 y ) +S32 LLInlineViewSegment::getMaxHeight() const { - // Find the cursor position at the requested local screen position - S32 offset = getDocIndexFromLocalCoord( x, y, FALSE ); - segment_set_t::iterator seg_iter = getSegIterContaining(offset); - if (seg_iter != mSegments.end()) - { - return *seg_iter; - } - else - { - return LLTextSegmentPtr(); - } + return mView->getRect().getHeight(); } -BOOL LLTextBase::handleHoverOverUrl(S32 x, S32 y) +void LLInlineViewSegment::unlinkFromDocument(LLTextBase* editor) { - setHoverSegment(NULL); - - // Check to see if we're over an HTML-style link - LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); - if (cur_segment) - { - setHoverSegment(cur_segment); - - LLStyleSP style = cur_segment->getStyle(); - if (style && style->isLink()) - { - return TRUE; - } - } - - return FALSE; -} - -BOOL LLTextBase::handleMouseUpOverUrl(S32 x, S32 y) -{ - if (mParseHTML && mHoverSegment) - { - LLStyleSP style = mHoverSegment->getStyle(); - if (style && style->isLink()) - { - LLUrlAction::clickAction(style->getLinkHREF()); - return TRUE; - } - } - - return FALSE; -} - -BOOL LLTextBase::handleRightMouseDownOverUrl(LLView *view, S32 x, S32 y) -{ - // pop up a context menu for any Url under the cursor - const LLTextSegment* cur_segment = getSegmentAtLocalPos(x, y); - if (cur_segment && cur_segment->getStyle() && cur_segment->getStyle()->isLink()) - { - delete mPopupMenu; - mPopupMenu = createUrlContextMenu(cur_segment->getStyle()->getLinkHREF()); - if (mPopupMenu) - { - mPopupMenu->show(x, y); - LLMenuGL::showPopup(view, mPopupMenu, x, y); - return TRUE; - } - } - - return FALSE; -} - -BOOL LLTextBase::handleToolTipForUrl(LLView *view, S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen) -{ - std::string tooltip_msg; - const LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); - if (cur_segment && cur_segment->getToolTip( tooltip_msg ) && view) - { - // Use a slop area around the cursor - const S32 SLOP = 8; - // Convert rect local to screen coordinates - view->localPointToScreen(x - SLOP, y - SLOP, &(sticky_rect_screen.mLeft), - &(sticky_rect_screen.mBottom)); - sticky_rect_screen.mRight = sticky_rect_screen.mLeft + 2 * SLOP; - sticky_rect_screen.mTop = sticky_rect_screen.mBottom + 2 * SLOP; - - LLToolTipMgr::instance().show(LLToolTipParams() - .message(tooltip_msg) - .sticky_rect(sticky_rect_screen)); - return TRUE; - } - return FALSE; + editor->removeDocumentChild(mView); } -LLContextMenu *LLTextBase::createUrlContextMenu(const std::string &in_url) +void LLInlineViewSegment::linkToDocument(LLTextBase* editor) { - // work out the XUI menu file to use for this url - LLUrlMatch match; - std::string url = in_url; - if (! LLUrlRegistry::instance().findUrl(url, match)) - { - return NULL; - } - - std::string xui_file = match.getMenuName(); - if (xui_file.empty()) - { - return NULL; - } - - // set up the callbacks for all of the potential menu items, N.B. we - // don't use const ref strings in callbacks in case url goes out of scope - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - registrar.add("Url.Open", boost::bind(&LLUrlAction::openURL, url)); - registrar.add("Url.OpenInternal", boost::bind(&LLUrlAction::openURLInternal, url)); - registrar.add("Url.OpenExternal", boost::bind(&LLUrlAction::openURLExternal, url)); - registrar.add("Url.Execute", boost::bind(&LLUrlAction::executeSLURL, url)); - registrar.add("Url.Teleport", boost::bind(&LLUrlAction::teleportToLocation, url)); - registrar.add("Url.CopyLabel", boost::bind(&LLUrlAction::copyLabelToClipboard, url)); - registrar.add("Url.CopyUrl", boost::bind(&LLUrlAction::copyURLToClipboard, url)); - - // create and return the context menu from the XUI file - return LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(xui_file, LLMenuGL::sMenuContainer, - LLMenuHolderGL::child_registry_t::instance()); + editor->addDocumentChild(mView); } diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 82b9f6a43f..d0287a99ca 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -35,13 +35,16 @@ #define LL_LLTEXTBASE_H #include "v4color.h" +#include "lleditmenuhandler.h" #include "llstyle.h" #include "llkeywords.h" -#include "lluictrl.h" +#include "llpanel.h" #include <string> #include <set> +#include <boost/signals2.hpp> + class LLContextMenu; class LLTextSegment; @@ -52,64 +55,307 @@ typedef LLPointer<LLTextSegment> LLTextSegmentPtr; /// as LLTextEditor and LLTextBox. It implements shared functionality /// such as Url highlighting and opening. /// -class LLTextBase +class LLTextBase +: public LLUICtrl, + protected LLEditMenuHandler { public: - LLTextBase(const LLUICtrl::Params &p); - virtual ~LLTextBase(); + struct LineSpacingParams : public LLInitParam::Choice<LineSpacingParams> + { + Alternative<F32> multiple; + Alternative<S32> pixels; + LineSpacingParams(); + }; + + struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> + { + Optional<LLUIColor> cursor_color, + text_color, + text_readonly_color, + bg_readonly_color, + bg_writeable_color, + bg_focus_color; + + Optional<bool> bg_visible, + border_visible, + track_end, + read_only, + hide_scrollbar, + clip_to_rect, + wrap, + use_ellipses, + allow_html, + parse_highlights; + + Optional<S32> v_pad, + h_pad; + + Optional<LineSpacingParams> + line_spacing; + + Optional<S32> max_text_length; + + Optional<LLFontGL::ShadowType> font_shadow; + + Params(); + }; + + // LLMouseHandler interface + /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleMiddleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); + /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask); + + // LLView interface + /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + /*virtual*/ void draw(); + + // LLUICtrl interface + /*virtual*/ BOOL acceptsTextInput() const { return !mReadOnly; } + /*virtual*/ void clear(); + /*virtual*/ void setColor( const LLColor4& c ); + /*virtual*/ void setValue(const LLSD& value ); + /*virtual*/ LLTextViewModel* getViewModel() const; + + // LLEditMenuHandler interface + /*virtual*/ void deselect(); - /// specify the color to display Url hyperlinks in the text - static void setLinkColor(LLColor4 color) { mLinkColor = color; } + // used by LLTextSegment layout code + bool getWordWrap() { return mWordWrap; } + bool getUseEllipses() { return mUseEllipses; } + bool truncate(); // returns true of truncation occurred - /// enable/disable the automatic hyperlinking of Urls in the text - void setParseHTML(BOOL parsing) { mParseHTML=parsing; } + // TODO: move into LLTextSegment? + void createUrlContextMenu(S32 x, S32 y, const std::string &url); // create a popup context menu for the given Url - // public text editing virtual methods - virtual LLWString getWText() const = 0; - virtual BOOL allowsEmbeddedItems() const { return FALSE; } - virtual BOOL getWordWrap() { return mWordWrap; } - virtual S32 getLength() const = 0; + + // Text accessors + // TODO: add optional style parameter + virtual void setText(const LLStringExplicit &utf8str); // uses default style + virtual std::string getText() const; + + // wide-char versions + void setWText(const LLWString& text); + LLWString getWText() const; + + void appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params = LLStyle::Params()); + + S32 getLength() const { return getWText().length(); } + S32 getLineCount() const { return mLineInfoList.size(); } + + class DocumentPanel : public LLPanel + { + public: + DocumentPanel(const Params&); + }; + void addDocumentChild(LLView* view); + void removeDocumentChild(LLView* view); + const DocumentPanel* getDocumentPanel() const { return mDocumentPanel; } + LLRect getTextRect() { return mTextRect; } + LLRect getContentsRect(); + + S32 getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const; + LLRect getLocalRectFromDocIndex(S32 pos) const; + + void setReadOnly(bool read_only) { mReadOnly = read_only; } + bool getReadOnly() { return mReadOnly; } + + // cursor manipulation + bool setCursor(S32 row, S32 column); + bool setCursorPos(S32 cursor_pos, bool keep_cursor_offset = false); + void startOfLine(); + void endOfLine(); + void startOfDoc(); + void endOfDoc(); + void changePage( S32 delta ); + void changeLine( S32 delta ); + + const LLFontGL* getDefaultFont() const { return mDefaultFont; } + +public: + // Fired when a URL link is clicked + commit_signal_t mURLClickSignal; protected: + // helper structs + struct compare_bottom; + struct compare_top; + struct line_end_compare; + typedef std::vector<LLTextSegmentPtr> segment_vec_t; + + // Abstract inner base class representing an undoable editor command. + // Concrete sub-classes can be defined for operations such as insert, remove, etc. + // Used as arguments to the execute() method below. + class TextCmd + { + public: + TextCmd( S32 pos, BOOL group_with_next, LLTextSegmentPtr segment = LLTextSegmentPtr() ) + : mPos(pos), + mGroupWithNext(group_with_next) + { + if (segment.notNull()) + { + mSegments.push_back(segment); + } + } + virtual ~TextCmd() {} + virtual BOOL execute(LLTextBase* editor, S32* delta) = 0; + virtual S32 undo(LLTextBase* editor) = 0; + virtual S32 redo(LLTextBase* editor) = 0; + virtual BOOL canExtend(S32 pos) const { return FALSE; } + virtual void blockExtensions() {} + virtual BOOL extendAndExecute( LLTextBase* editor, S32 pos, llwchar c, S32* delta ) { llassert(0); return 0; } + virtual BOOL hasExtCharValue( llwchar value ) const { return FALSE; } + + // Defined here so they can access protected LLTextEditor editing methods + S32 insert(LLTextBase* editor, S32 pos, const LLWString &wstr) { return editor->insertStringNoUndo( pos, wstr, &mSegments ); } + S32 remove(LLTextBase* editor, S32 pos, S32 length) { return editor->removeStringNoUndo( pos, length ); } + S32 overwrite(LLTextBase* editor, S32 pos, llwchar wc) { return editor->overwriteCharNoUndo(pos, wc); } + + S32 getPosition() const { return mPos; } + BOOL groupWithNext() const { return mGroupWithNext; } + + protected: + const S32 mPos; + BOOL mGroupWithNext; + segment_vec_t mSegments; + }; + struct compare_segment_end { bool operator()(const LLTextSegmentPtr& a, const LLTextSegmentPtr& b) const; }; typedef std::multiset<LLTextSegmentPtr, compare_segment_end> segment_set_t; - // routines to manage segments - void getSegmentAndOffset( S32 startpos, segment_set_t::const_iterator* seg_iter, S32* offsetp ) const; - void getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp ); - LLTextSegmentPtr getSegmentAtLocalPos( S32 x, S32 y ); + // protected member variables + // List of offsets and segment index of the start of each line. Always has at least one node (0). + struct line_info + { + line_info(S32 index_start, S32 index_end, LLRect rect, S32 line_num); + S32 mDocIndexStart; + S32 mDocIndexEnd; + LLRect mRect; + S32 mLineNum; // actual line count (ignoring soft newlines due to word wrap) + }; + typedef std::vector<line_info> line_list_t; + + // member functions + LLTextBase(const Params &p); + virtual ~LLTextBase(); + void initFromParams(const Params& p); + LLStyle::Params getDefaultStyle(); + virtual void onValueChange(S32 start, S32 end); + + // draw methods + void drawSelectionBackground(); // draws the black box behind the selected text + void drawCursor(); + void drawText(); + + // modify contents + S32 insertStringNoUndo(S32 pos, const LLWString &wstr, segment_vec_t* segments = NULL); // returns num of chars actually inserted + S32 removeStringNoUndo(S32 pos, S32 length); + S32 overwriteCharNoUndo(S32 pos, llwchar wc); + void appendAndHighlightText(const std::string &new_text, bool prepend_newline, S32 highlight_part, const LLStyle::Params& stylep); + + + // manage segments + void getSegmentAndOffset( S32 startpos, segment_set_t::const_iterator* seg_iter, S32* offsetp ) const; + void getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp ); + LLTextSegmentPtr getSegmentAtLocalPos( S32 x, S32 y ); segment_set_t::iterator getSegIterContaining(S32 index); segment_set_t::const_iterator getSegIterContaining(S32 index) const; - void clearSegments(); - void setHoverSegment(LLTextSegmentPtr segment); + void clearSegments(); + void createDefaultSegment(); + virtual void updateSegments(); + void insertSegment(LLTextSegmentPtr segment_to_insert); + + // manage lines + S32 getLineStart( S32 line ) const; + S32 getLineNumFromDocIndex( S32 doc_index, bool include_wordwrap = true) const; + S32 getLineOffsetFromDocIndex( S32 doc_index, bool include_wordwrap = true) const; + S32 getFirstVisibleLine() const; + std::pair<S32, S32> getVisibleLines() const; + S32 getLeftOffset(S32 width); + void reflow(S32 start_index = 0); - // event handling for Urls within the text field - BOOL handleHoverOverUrl(S32 x, S32 y); - BOOL handleMouseUpOverUrl(S32 x, S32 y); - BOOL handleRightMouseDownOverUrl(LLView *view, S32 x, S32 y); - BOOL handleToolTipForUrl(LLView *view, S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen); + // cursor + void updateCursorXPos(); + void setCursorAtLocalPos( S32 local_x, S32 local_y, bool round, bool keep_cursor_offset=false ); + S32 getEditableIndex(S32 index, bool increasing_direction); // constraint cursor to editable segments of document + void resetCursorBlink() { mCursorBlinkTimer.reset(); } + void updateScrollFromCursor(); - // pure virtuals that have to be implemented by any subclasses - virtual S32 getLineCount() const = 0; - virtual S32 getLineStart( S32 line ) const = 0; - virtual S32 getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const = 0; + // text selection + bool hasSelection() const { return (mSelectionStart !=mSelectionEnd); } + void startSelection(); + void endSelection(); - // protected member variables - static LLUIColor mLinkColor; - const LLFontGL *mDefaultFont; - segment_set_t mSegments; - LLTextSegmentPtr mHoverSegment; - BOOL mParseHTML; - BOOL mWordWrap; + // misc + void updateTextRect(); + void needsReflow() { mReflowNeeded = TRUE; } + void needsScroll() { mScrollNeeded = TRUE; } + void replaceUrlLabel(const std::string &url, const std::string &label); -private: - // create a popup context menu for the given Url - static LLContextMenu *createUrlContextMenu(const std::string &url); +protected: + // text segmentation and flow + segment_set_t mSegments; + line_list_t mLineInfoList; + LLRect mTextRect; // The rect in which text is drawn. Excludes borders. + LLRect mContentsRect; + + // colors + LLUIColor mCursorColor; + LLUIColor mFgColor; + LLUIColor mReadOnlyFgColor; + LLUIColor mWriteableBgColor; + LLUIColor mReadOnlyBgColor; + LLUIColor mFocusBgColor; + + // cursor + S32 mCursorPos; // I-beam is just after the mCursorPos-th character. + S32 mDesiredXPixel; // X pixel position where the user wants the cursor to be + LLFrameTimer mCursorBlinkTimer; // timer that controls cursor blinking + + // selection + S32 mSelectionStart; + S32 mSelectionEnd; + + BOOL mIsSelecting; // Are we in the middle of a drag-select? + + // configuration + S32 mHPad; // padding on left of text + S32 mVPad; // padding above text + LLFontGL::HAlign mHAlign; + F32 mLineSpacingMult; // multiple of line height used as space for a single line of text (e.g. 1.5 to get 50% padding) + S32 mLineSpacingPixels; // padding between lines + const LLFontGL* mDefaultFont; // font that is used when none specified + LLFontGL::ShadowType mFontShadow; + bool mBorderVisible; + bool mParseHTML; // make URLs interactive + bool mParseHighlights; // highlight user-defined keywords + bool mWordWrap; + bool mUseEllipses; + bool mTrackEnd; // if true, keeps scroll position at end of document during resize + bool mReadOnly; + bool mClip; + S32 mMaxTextByteLength; // Maximum length mText is allowed to be in bytes + + // support widgets + LLContextMenu* mPopupMenu; + DocumentPanel* mDocumentPanel; + class LLScrollContainer* mScroller; + + // transient state + bool mReflowNeeded; // need to reflow text because of change to text contents or display region + bool mScrollNeeded; // need to change scroll region because of change to cursor position + S32 mScrollIndex; // index of first character to keep visible in scroll region - LLContextMenu *mPopupMenu; }; /// @@ -118,7 +364,7 @@ private: /// includes a start/end offset from the start of the string, a /// style to render with, an optional tooltip, etc. /// -class LLTextSegment : public LLRefCount +class LLTextSegment : public LLRefCount, public LLMouseHandler { public: LLTextSegment(S32 start, S32 end) : mStart(start), mEnd(end){}; @@ -134,17 +380,32 @@ public: virtual void unlinkFromDocument(class LLTextBase* editor); virtual void linkToDocument(class LLTextBase* editor); - virtual void setHasMouseHover(bool hover); virtual const LLColor4& getColor() const; virtual void setColor(const LLColor4 &color); virtual const LLStyleSP getStyle() const; virtual void setStyle(const LLStyleSP &style); virtual void setToken( LLKeywordToken* token ); virtual LLKeywordToken* getToken() const; - virtual BOOL getToolTip( std::string& msg ) const; virtual void setToolTip(const std::string& tooltip); virtual void dump() const; + // LLMouseHandler interface + /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleMiddleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); + /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask); + /*virtual*/ std::string getName() const; + /*virtual*/ void onMouseCaptureLost(); + /*virtual*/ void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const; + /*virtual*/ void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const; + /*virtual*/ BOOL hasMouseCapture(); + S32 getStart() const { return mStart; } void setStart(S32 start) { mStart = start; } S32 getEnd() const { return mEnd; } @@ -167,7 +428,6 @@ public: /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect); /*virtual*/ S32 getMaxHeight() const; /*virtual*/ bool canEdit() const { return true; } - /*virtual*/ void setHasMouseHover(bool hover) { mHasMouseHover = hover; } /*virtual*/ const LLColor4& getColor() const { return mStyle->getColor(); } /*virtual*/ void setColor(const LLColor4 &color) { mStyle->setColor(color); } /*virtual*/ const LLStyleSP getStyle() const { return mStyle; } @@ -178,15 +438,20 @@ public: /*virtual*/ void setToolTip(const std::string& tooltip); /*virtual*/ void dump() const; + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask); + protected: - F32 drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, F32 x, F32 y); + F32 drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRect rect); +protected: class LLTextBase& mEditor; - LLStyleSP mStyle; - S32 mMaxHeight; - LLKeywordToken* mToken; - bool mHasMouseHover; - std::string mTooltip; + LLStyleSP mStyle; + S32 mMaxHeight; + LLKeywordToken* mToken; + std::string mTooltip; }; class LLIndexSegment : public LLTextSegment @@ -195,4 +460,23 @@ public: LLIndexSegment(S32 pos) : LLTextSegment(pos, pos) {} }; +class LLInlineViewSegment : public LLTextSegment +{ +public: + LLInlineViewSegment(LLView* widget, S32 start, S32 end); + ~LLInlineViewSegment(); + /*virtual*/ S32 getWidth(S32 first_char, S32 num_chars) const; + /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const; + /*virtual*/ void updateLayout(const class LLTextBase& editor); + /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect); + /*virtuaL*/ S32 getMaxHeight() const; + /*virtual*/ bool canEdit() const { return false; } + /*virtual*/ void unlinkFromDocument(class LLTextBase* editor); + /*virtual*/ void linkToDocument(class LLTextBase* editor); + +private: + LLView* mView; +}; + + #endif diff --git a/indra/llui/lltextbox.cpp b/indra/llui/lltextbox.cpp index 132bef0296..3feca136be 100644 --- a/indra/llui/lltextbox.cpp +++ b/indra/llui/lltextbox.cpp @@ -40,47 +40,10 @@ static LLDefaultChildRegistry::Register<LLTextBox> r("text"); -LLTextBox::Params::Params() -: text_color("text_color"), - length("length"), - type("type"), - border_visible("border_visible", false), - border_drop_shadow_visible("border_drop_shadow_visible", false), - bg_visible("bg_visible", false), - use_ellipses("use_ellipses"), - word_wrap("word_wrap", false), - drop_shadow_visible("drop_shadow_visible"), - disabled_color("disabled_color"), - background_color("background_color"), - v_pad("v_pad", 0), - h_pad("h_pad", 0), - line_spacing("line_spacing", 0), - text("text"), - font_shadow("font_shadow", LLFontGL::NO_SHADOW) -{} - LLTextBox::LLTextBox(const LLTextBox::Params& p) -: LLUICtrl(p), - LLTextBase(p), - mBackgroundVisible( p.bg_visible ), - mBorderVisible( p.border_visible ), - mShadowType( p.font_shadow ), - mBorderDropShadowVisible( p.border_drop_shadow_visible ), - mUseEllipses( p.use_ellipses ), - mHPad(p.h_pad), - mVPad(p.v_pad), - mVAlign( LLFontGL::TOP ), - mClickedCallback(NULL), - mTextColor(p.text_color()), - mDisabledColor(p.disabled_color()), - mBackgroundColor(p.background_color()), - mHAlign(p.font_halign), - mLineSpacing(p.line_spacing), - mDidWordWrap(FALSE) -{ - mWordWrap = p.word_wrap; - setText( p.text() ); -} +: LLTextBase(p), + mClickedCallback(NULL) +{} BOOL LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask) { @@ -101,6 +64,11 @@ BOOL LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask) } } + if (!handled) + { + handled = LLTextBase::handleMouseDown(x, y, mask); + } + return handled; } @@ -125,528 +93,60 @@ BOOL LLTextBox::handleMouseUp(S32 x, S32 y, MASK mask) } // handle clicks on Urls in the textbox first - if (! handleMouseUpOverUrl(x, y)) + handled = LLTextBase::handleMouseUp(x, y, mask); + + // DO THIS AT THE VERY END to allow the button to be destroyed + // as a result of being clicked. If mouseup in the widget, + // it's been clicked + if (mClickedCallback && !handled) { - // DO THIS AT THE VERY END to allow the button to be destroyed - // as a result of being clicked. If mouseup in the widget, - // it's been clicked - if (mClickedCallback && ! handled) - { - mClickedCallback(); - } + mClickedCallback(); } } return handled; } -BOOL LLTextBox::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - // pop up a context menu for any Url under the cursor - return handleRightMouseDownOverUrl(this, x, y); -} - -BOOL LLTextBox::handleHover(S32 x, S32 y, MASK mask) -{ - // Check to see if we're over an HTML-style link - if (handleHoverOverUrl(x, y)) - { - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl; - getWindow()->setCursor(UI_CURSOR_HAND); - return TRUE; - } - - return LLView::handleHover(x,y,mask); -} - -BOOL LLTextBox::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen) -{ - if (handleToolTipForUrl(this, x, y, msg, sticky_rect_screen)) - { - return TRUE; - } - - return LLUICtrl::handleToolTip(x, y, msg, sticky_rect_screen); -} - void LLTextBox::setText(const LLStringExplicit& text) { - if(mWordWrap && !mDidWordWrap) - { - setWrappedText(text); - } - else - { - mText.assign(text); - updateDisplayTextAndSegments(); - } -} - -void LLTextBox::setLineLengths() -{ - mLineLengthList.clear(); + // does string argument insertion + mText.assign(text); - std::string::size_type cur = 0; - std::string::size_type len = mDisplayText.size(); - - while (cur < len) - { - std::string::size_type end = mDisplayText.find('\n', cur); - std::string::size_type runLen; - - if (end == std::string::npos) - { - runLen = len - cur; - cur = len; - } - else - { - runLen = end - cur; - cur = end + 1; // skip the new line character - } - - mLineLengthList.push_back( (S32)runLen ); - } -} - -LLWString LLTextBox::wrapText(const LLWString &wtext, S32 &hoffset, S32 &line_num, F32 max_width) -{ - LLWString final_wtext; - - LLWString::size_type cur = 0; - LLWString::size_type len = wtext.size(); - while (cur < len) - { - LLWString::size_type end = wtext.find('\n', cur); - if (end == LLWString::npos) - { - end = len; - } - - bool charsRemaining = true; - LLWString::size_type runLen = end - cur; - if (runLen > 0) - { - // work out how many chars can fit onto the current line - LLWString run(wtext, cur, runLen); - LLWString::size_type useLen = - mDefaultFont->maxDrawableChars(run.c_str(), max_width-hoffset, runLen, TRUE); - charsRemaining = (cur + useLen < len); - - // try to break lines on word boundaries - if (useLen < run.size()) - { - LLWString::size_type prev_use_len = useLen; - while (useLen > 0 && ! isspace(run[useLen-1]) && ! ispunct(run[useLen-1])) - { - --useLen; - } - if (useLen == 0) - { - useLen = prev_use_len; - } - } - - // add the chars that could fit onto one line to our result - final_wtext.append(wtext, cur, useLen); - cur += useLen; - hoffset += mDefaultFont->getWidth(run.substr(0, useLen).c_str()); - - // abort if not enough room to add any more characters - if (useLen == 0) - { - break; - } - } - - if (charsRemaining) - { - if (wtext[cur] == '\n') - { - cur += 1; - } - final_wtext += '\n'; - hoffset = 0; - line_num += 1; - } - } - - return final_wtext; -} - -void LLTextBox::setWrappedText(const LLStringExplicit& in_text, F32 max_width) -{ - mDidWordWrap = TRUE; - setText(wstring_to_utf8str(getWrappedText(in_text, max_width))); -} - -LLWString LLTextBox::getWrappedText(const LLStringExplicit& in_text, F32 max_width) -{ - // - // we don't want to wrap Urls otherwise we won't be able to detect their - // presence for hyperlinking. So we look for all Urls, and then word wrap - // the text before and after, but never break a Url in the middle. We - // also need to consider that the Url will be displayed as a label (not - // necessary the actual Url string). - // - - if (max_width < 0.0f) - { - max_width = (F32)getRect().getWidth(); - } - - LLWString wtext = utf8str_to_wstring(in_text); - LLWString final_wtext; - S32 line_num = 1; - S32 hoffset = 0; - - // find the next Url in the text string - LLUrlMatch match; - while ( LLUrlRegistry::instance().findUrl(wtext, match)) - { - S32 start = match.getStart(); - S32 end = match.getEnd() + 1; - - // perform word wrap on the text before the Url - final_wtext += wrapText(wtext.substr(0, start), hoffset, line_num, max_width); - - // add the Url (but compute width based on its label) - S32 label_width = mDefaultFont->getWidth(match.getLabel()); - if (hoffset > 0 && hoffset + label_width > max_width) - { - final_wtext += '\n'; - line_num++; - hoffset = 0; - } - final_wtext += wtext.substr(start, end-start); - hoffset += label_width; - if (hoffset > max_width) - { - final_wtext += '\n'; - line_num++; - hoffset = 0; - // eat any leading whitespace on the next line - while (isspace(wtext[end]) && end < (S32)wtext.size()) - { - end++; - } - } - - // move on to the rest of the text after the Url - wtext = wtext.substr(end, wtext.size() - end + 1); - } - - final_wtext += wrapText(wtext, hoffset, line_num, max_width); - return final_wtext; + LLTextBase::setText(mText.getString()); } S32 LLTextBox::getTextPixelWidth() { - S32 max_line_width = 0; - if( mLineLengthList.size() > 0 ) - { - S32 cur_pos = 0; - for (std::vector<S32>::iterator iter = mLineLengthList.begin(); - iter != mLineLengthList.end(); ++iter) - { - S32 line_length = *iter; - S32 line_width = mDefaultFont->getWidth( mDisplayText.c_str(), cur_pos, line_length ); - if( line_width > max_line_width ) - { - max_line_width = line_width; - } - cur_pos += line_length+1; - } - } - else - { - max_line_width = mDefaultFont->getWidth(mDisplayText.c_str()); - } - return max_line_width; + return getContentsRect().getWidth(); } S32 LLTextBox::getTextPixelHeight() { - S32 num_lines = mLineLengthList.size(); - if( num_lines < 1 ) - { - num_lines = 1; - } - return (S32)(num_lines * mDefaultFont->getLineHeight()); -} - -void LLTextBox::setValue(const LLSD& value ) -{ - mDidWordWrap = FALSE; - setText(value.asString()); + return getContentsRect().getHeight(); } BOOL LLTextBox::setTextArg( const std::string& key, const LLStringExplicit& text ) { mText.setArg(key, text); - updateDisplayTextAndSegments(); - return TRUE; -} - -void LLTextBox::draw() -{ - F32 alpha = getDrawContext().mAlpha; + LLTextBase::setText(mText.getString()); - if (mBorderVisible) - { - gl_rect_2d_offset_local(getLocalRect(), 2, FALSE); - } - - if( mBorderDropShadowVisible ) - { - static LLUIColor color_drop_shadow = LLUIColorTable::instance().getColor("ColorDropShadow"); - static LLUICachedControl<S32> drop_shadow_tooltip ("DropShadowTooltip", 0); - gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0, - color_drop_shadow % alpha, drop_shadow_tooltip); - } - - if (mBackgroundVisible) - { - LLRect r( 0, getRect().getHeight(), getRect().getWidth(), 0 ); - gl_rect_2d( r, mBackgroundColor.get() % alpha ); - } - - S32 text_x = 0; - switch( mHAlign ) - { - case LLFontGL::LEFT: - text_x = mHPad; - break; - case LLFontGL::HCENTER: - text_x = getRect().getWidth() / 2; - break; - case LLFontGL::RIGHT: - text_x = getRect().getWidth() - mHPad; - break; - } - - S32 text_y = getRect().getHeight() - mVPad; - - if ( getEnabled() ) - { - drawText( text_x, text_y, mDisplayText, mTextColor.get() ); - } - else - { - drawText( text_x, text_y, mDisplayText, mDisabledColor.get() ); - } - - if (sDebugRects) - { - drawDebugRect(); - } - - //// *HACK: also draw debug rectangles around currently-being-edited LLView, and any elements that are being highlighted by GUI preview code (see LLFloaterUIPreview) - //std::set<LLView*>::iterator iter = std::find(sPreviewHighlightedElements.begin(), sPreviewHighlightedElements.end(), this); - //if ((sEditingUI && this == sEditingUIView) || (iter != sPreviewHighlightedElements.end() && sDrawPreviewHighlights)) - //{ - // drawDebugRect(); - //} + return TRUE; } -void LLTextBox::reshape(S32 width, S32 height, BOOL called_from_parent) -{ - // reparse line lengths (don't need to recalculate the display text) - setLineLengths(); - LLView::reshape(width, height, called_from_parent); -} - -void LLTextBox::drawText( S32 x, S32 y, const LLWString &text, const LLColor4& color ) -{ - F32 alpha = getDrawContext().mAlpha; - if (mSegments.size() > 1) - { - // we have Urls (or other multi-styled segments) - drawTextSegments(x, y, text); - } - else if( mLineLengthList.empty() ) - { - // simple case of 1 line of text in one style - mDefaultFont->render(text, 0, (F32)x, (F32)y, color % alpha, - mHAlign, mVAlign, - 0, - mShadowType, - S32_MAX, getRect().getWidth(), NULL, mUseEllipses); - } - else - { - // simple case of multiple lines of text, all in the same style - S32 cur_pos = 0; - for (std::vector<S32>::iterator iter = mLineLengthList.begin(); - iter != mLineLengthList.end(); ++iter) - { - S32 line_length = *iter; - mDefaultFont->render(text, cur_pos, (F32)x, (F32)y, color % alpha, - mHAlign, mVAlign, - 0, - mShadowType, - line_length, getRect().getWidth(), NULL, mUseEllipses ); - cur_pos += line_length + 1; - S32 line_height = llfloor(mDefaultFont->getLineHeight()) + mLineSpacing; - y -= line_height; - if(y < line_height) - break; - } - } -} void LLTextBox::reshapeToFitText() { - // wrap remaining lines that did not fit on call to setWrappedText() - setLineLengths(); + reflow(); S32 width = getTextPixelWidth(); S32 height = getTextPixelHeight(); - reshape( width + 2 * mHPad, height + 2 * mVPad ); -} - -S32 LLTextBox::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const -{ - // Returns the character offset for the character under the local (x, y) coordinate. - // When round is true, if the position is on the right half of a character, the cursor - // will be put to its right. If round is false, the cursor will always be put to the - // character's left. - - LLRect rect = getLocalRect(); - rect.mLeft += mHPad; - rect.mRight -= mHPad; - rect.mTop += mVPad; - rect.mBottom -= mVPad; - - // Figure out which line we're nearest to. - S32 total_lines = getLineCount(); - S32 line_height = llround( mDefaultFont->getLineHeight() ) + mLineSpacing; - S32 line = (rect.mTop - 1 - local_y) / line_height; - if (line >= total_lines) - { - return getLength(); // past the end - } - - line = llclamp( line, 0, total_lines ); - S32 line_start = getLineStart(line); - S32 next_start = getLineStart(line+1); - S32 line_end = (next_start != line_start) ? next_start - 1 : getLength(); - if (line_start == -1) - { - return 0; - } - - S32 line_len = line_end - line_start; - S32 pos = mDefaultFont->charFromPixelOffset(mDisplayText.c_str(), line_start, - (F32)(local_x - rect.mLeft), - (F32)rect.getWidth(), - line_len, round); - - return line_start + pos; + reshape( width + 2 * mHPad, height + 2 * mVPad, FALSE ); } -S32 LLTextBox::getLineStart( S32 line ) const -{ - line = llclamp(line, 0, getLineCount()-1); - - S32 result = 0; - for (int i = 0; i < line; i++) - { - result += mLineLengthList[i] + 1 /* add newline */; - } - - return result; -} - -void LLTextBox::updateDisplayTextAndSegments() -{ - // remove any previous segment list - clearSegments(); - - // if URL parsing is turned off, then not much to bo - if (! mParseHTML) - { - mDisplayText = mText.getWString(); - setLineLengths(); - return; - } - - // create unique text segments for Urls - mDisplayText.clear(); - S32 end = 0; - LLUrlMatch match; - LLWString text = mText.getWString(); - - // find the next Url in the text string - while ( LLUrlRegistry::instance().findUrl(text, match, - boost::bind(&LLTextBox::onUrlLabelUpdated, this, _1, _2)) ) - { - // work out the char offset for the start/end of the url - S32 url_start = match.getStart(); - S32 url_end = match.getEnd(); - - // and the char offset for the label in the display text - S32 seg_start = mDisplayText.size(); - S32 start = seg_start + url_start; - S32 end = start + match.getLabel().size(); - - // create a segment for the text before the Url - mSegments.insert(new LLNormalTextSegment(new LLStyle(), seg_start, start, *this)); - mDisplayText += text.substr(0, url_start); - - // create a segment for the Url text - LLStyleSP html(new LLStyle); - html->setVisible(true); - html->setColor(mLinkColor); - html->mUnderline = TRUE; - html->setLinkHREF(match.getUrl()); - - LLNormalTextSegment *html_seg = new LLNormalTextSegment(html, start, end, *this); - html_seg->setToolTip(match.getTooltip()); - - mSegments.insert(html_seg); - mDisplayText += utf8str_to_wstring(match.getLabel()); - - // move on to the rest of the text after the Url - text = text.substr(url_end+1, text.size() - url_end); - } - - // output a segment for the remaining text - if (text.size() > 0) - { - mSegments.insert(new LLNormalTextSegment(new LLStyle(), end, end + text.size(), *this)); - mDisplayText += text; - } - - // strip whitespace from the end of the text - while (mDisplayText.size() > 0 && isspace(mDisplayText[mDisplayText.size()-1])) - { - mDisplayText = mDisplayText.substr(0, mDisplayText.size() - 1); - - segment_set_t::iterator it = getSegIterContaining(mDisplayText.size()); - if (it != mSegments.end()) - { - LLTextSegmentPtr seg = *it; - seg->setEnd(seg->getEnd()-1); - } - } - - // we may have changed the line lengths, so recalculate them - setLineLengths(); -} void LLTextBox::onUrlLabelUpdated(const std::string &url, const std::string &label) { - if (mDidWordWrap) - { - // re-word wrap as the url label lengths may have changed - setWrappedText(mText.getString()); - } - else - { - // or just update the display text with the latest Url labels - updateDisplayTextAndSegments(); - } + needsReflow(); } bool LLTextBox::isClickable() const @@ -676,89 +176,3 @@ bool LLTextBox::isClickable() const return false; } -void LLTextBox::drawTextSegments(S32 init_x, S32 init_y, const LLWString &text) -{ - F32 alpha = getDrawContext().mAlpha; - - const S32 text_len = text.length(); - if (text_len <= 0) - { - return; - } - - S32 cur_line = 0; - S32 num_lines = getLineCount(); - S32 line_start = getLineStart(cur_line); - S32 line_height = llround( mDefaultFont->getLineHeight() ) + mLineSpacing; - F32 text_y = (F32) init_y; - segment_set_t::iterator cur_seg = mSegments.begin(); - - // render a line of text at a time - const LLRect textRect = getLocalRect(); - while((textRect.mBottom <= text_y) && (cur_line < num_lines)) - { - S32 next_start = -1; - S32 line_end = text_len; - - if ((cur_line + 1) < num_lines) - { - next_start = getLineStart(cur_line + 1); - line_end = next_start; - } - if ( text[line_end-1] == '\n' ) - { - --line_end; - } - - // render all segments on this line - F32 text_x = init_x; - S32 seg_start = line_start; - while (seg_start < line_end && cur_seg != mSegments.end()) - { - // move to the next segment (or continue the previous one) - LLTextSegment *cur_segment = *cur_seg; - while (cur_segment->getEnd() <= seg_start) - { - if (++cur_seg == mSegments.end()) - { - return; - } - cur_segment = *cur_seg; - } - - // Draw a segment within the line - S32 clipped_end = llmin( line_end, cur_segment->getEnd() ); - S32 clipped_len = clipped_end - seg_start; - if( clipped_len > 0 ) - { - LLStyleSP style = cur_segment->getStyle(); - if (style && style->isVisible()) - { - // work out the color for the segment - LLColor4 color ; - if (getEnabled()) - { - color = style->isLink() ? mLinkColor.get() : mTextColor.get(); - } - else - { - color = mDisabledColor.get(); - } - color = color % alpha; - - // render a single line worth for this segment - mDefaultFont->render(text, seg_start, text_x, text_y, color, - mHAlign, mVAlign, 0, mShadowType, clipped_len, - textRect.getWidth(), &text_x, mUseEllipses); - } - - seg_start += clipped_len; - } - } - - // move down one line - text_y -= (F32)line_height; - line_start = next_start; - cur_line++; - } -} diff --git a/indra/llui/lltextbox.h b/indra/llui/lltextbox.h index 0517325e70..f8c4447b62 100644 --- a/indra/llui/lltextbox.h +++ b/indra/llui/lltextbox.h @@ -33,47 +33,21 @@ #ifndef LL_LLTEXTBOX_H #define LL_LLTEXTBOX_H -#include "lluictrl.h" #include "v4color.h" #include "llstring.h" #include "lluistring.h" #include "lltextbase.h" class LLTextBox : - public LLTextBase, - public LLUICtrl + public LLTextBase { public: // *TODO: Add callback to Params typedef boost::function<void (void)> callback_t; - struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> - { - Optional<std::string> text; - - Optional<bool> border_visible, - border_drop_shadow_visible, - bg_visible, - use_ellipses, - word_wrap; - - Optional<LLFontGL::ShadowType> font_shadow; - - Ignored drop_shadow_visible, - type, - length; - - Optional<LLUIColor> text_color, - disabled_color, - background_color; - - Optional<S32> v_pad, - h_pad, - line_spacing; - - Params(); - }; + struct Params : public LLInitParam::Block<Params, LLTextBase::Params> + {}; protected: LLTextBox(const Params&); @@ -82,84 +56,33 @@ protected: public: virtual ~LLTextBox() {} - virtual void draw(); - virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); - virtual BOOL handleHover(S32 x, S32 y, MASK mask); - virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen); - void setColor( const LLColor4& c ) { mTextColor = c; } - void setDisabledColor( const LLColor4& c) { mDisabledColor = c; } - void setBackgroundColor( const LLColor4& c) { mBackgroundColor = c; } - - void setText( const LLStringExplicit& text ); - void setWrappedText(const LLStringExplicit& text, F32 max_width = -1.f); // -1 means use existing control width - void setUseEllipses( BOOL use_ellipses ) { mUseEllipses = use_ellipses; } + /*virtual*/ void setText( const LLStringExplicit& text ); - void setBackgroundVisible(BOOL visible) { mBackgroundVisible = visible; } - void setBorderVisible(BOOL visible) { mBorderVisible = visible; } - void setBorderDropshadowVisible(BOOL visible){ mBorderDropShadowVisible = visible; } - void setHPad(S32 pixels) { mHPad = pixels; } - void setVPad(S32 pixels) { mVPad = pixels; } void setRightAlign() { mHAlign = LLFontGL::RIGHT; } void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; } void setClickedCallback( boost::function<void (void*)> cb, void* userdata = NULL ){ mClickedCallback = boost::bind(cb, userdata); } // mouse down and up within button - const LLFontGL* getFont() const { return mDefaultFont; } - void setFont(const LLFontGL* font) { mDefaultFont = font; } + //const LLFontGL* getFont() const { return mDefaultFont; } + //void setFont(const LLFontGL* font) { mDefaultFont = font; } void reshapeToFitText(); - const std::string& getText() const { return mText.getString(); } - LLWString getWText() const { return mDisplayText; } + //const std::string& getText() const { return mText.getString(); } S32 getTextPixelWidth(); S32 getTextPixelHeight(); - S32 getLength() const { return mDisplayText.length(); } - virtual void setValue(const LLSD& value ); virtual LLSD getValue() const { return LLSD(getText()); } virtual BOOL setTextArg( const std::string& key, const LLStringExplicit& text ); protected: - S32 getLineCount() const { return mLineLengthList.size(); } - S32 getLineStart( S32 line ) const; - S32 getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const; - LLWString getWrappedText(const LLStringExplicit& in_text, F32 max_width = -1.f); - void setLineLengths(); - void updateDisplayTextAndSegments(); - virtual void drawText(S32 x, S32 y, const LLWString &text, const LLColor4& color ); void onUrlLabelUpdated(const std::string &url, const std::string &label); bool isClickable() const; - LLWString wrapText(const LLWString &wtext, S32 &hoffset, S32 &line_num, F32 max_width); - void drawTextSegments(S32 x, S32 y, const LLWString &text); - - LLUIString mText; - LLWString mDisplayText; - LLUIColor mTextColor; - LLUIColor mDisabledColor; - LLUIColor mBackgroundColor; - LLUIColor mBorderColor; - - BOOL mBackgroundVisible; - BOOL mBorderVisible; - BOOL mDidWordWrap; - - LLFontGL::ShadowType mShadowType; - BOOL mBorderDropShadowVisible; - BOOL mUseEllipses; - - S32 mLineSpacing; - - S32 mHPad; - S32 mVPad; - LLFontGL::HAlign mHAlign; - LLFontGL::VAlign mVAlign; - std::vector<S32> mLineLengthList; - callback_t mClickedCallback; + LLUIString mText; + callback_t mClickedCallback; }; #endif diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 39f09b297f..997c5b8fa8 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -77,106 +77,31 @@ static LLDefaultChildRegistry::Register<LLTextEditor> r("simple_text_editor"); // const S32 UI_TEXTEDITOR_LINE_NUMBER_MARGIN = 32; const S32 UI_TEXTEDITOR_LINE_NUMBER_DIGITS = 4; -const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds -const S32 CURSOR_THICKNESS = 2; const S32 SPACES_PER_TAB = 4; - -// helper functors -struct LLTextEditor::compare_bottom -{ - bool operator()(const S32& a, const LLTextEditor::line_info& b) const - { - return a > b.mBottom; // bottom of a is higher than bottom of b - } - - bool operator()(const LLTextEditor::line_info& a, const S32& b) const - { - return a.mBottom > b; // bottom of a is higher than bottom of b - } - - bool operator()(const LLTextEditor::line_info& a, const LLTextEditor::line_info& b) const - { - return a.mBottom > b.mBottom; // bottom of a is higher than bottom of b - } - -}; - -// helper functors -struct LLTextEditor::compare_top -{ - bool operator()(const S32& a, const LLTextEditor::line_info& b) const - { - return a > b.mTop; // top of a is higher than top of b - } - - bool operator()(const LLTextEditor::line_info& a, const S32& b) const - { - return a.mTop > b; // top of a is higher than top of b - } - - bool operator()(const LLTextEditor::line_info& a, const LLTextEditor::line_info& b) const - { - return a.mTop > b.mTop; // top of a is higher than top of b - } -}; - -struct LLTextEditor::line_end_compare -{ - bool operator()(const S32& pos, const LLTextEditor::line_info& info) const - { - return (pos < info.mDocIndexEnd); - } - - bool operator()(const LLTextEditor::line_info& info, const S32& pos) const - { - return (info.mDocIndexEnd < pos); - } - - bool operator()(const LLTextEditor::line_info& a, const LLTextEditor::line_info& b) const - { - return (a.mDocIndexEnd < b.mDocIndexEnd); - } - -}; - -// -// DocumentPanel -// - -class DocumentPanel : public LLPanel -{ -public: - DocumentPanel(const Params&); -}; - -DocumentPanel::DocumentPanel(const Params& p) -: LLPanel(p) -{} - /////////////////////////////////////////////////////////////////// -class LLTextEditor::LLTextCmdInsert : public LLTextEditor::LLTextCmd +class LLTextEditor::TextCmdInsert : public LLTextBase::TextCmd { public: - LLTextCmdInsert(S32 pos, BOOL group_with_next, const LLWString &ws, LLTextSegmentPtr segment) - : LLTextCmd(pos, group_with_next, segment), mWString(ws) + TextCmdInsert(S32 pos, BOOL group_with_next, const LLWString &ws, LLTextSegmentPtr segment) + : TextCmd(pos, group_with_next, segment), mWString(ws) { } - virtual ~LLTextCmdInsert() {} - virtual BOOL execute( LLTextEditor* editor, S32* delta ) + virtual ~TextCmdInsert() {} + virtual BOOL execute( LLTextBase* editor, S32* delta ) { *delta = insert(editor, getPosition(), mWString ); LLWStringUtil::truncate(mWString, *delta); //mWString = wstring_truncate(mWString, *delta); return (*delta != 0); } - virtual S32 undo( LLTextEditor* editor ) + virtual S32 undo( LLTextBase* editor ) { remove(editor, getPosition(), mWString.length() ); return getPosition(); } - virtual S32 redo( LLTextEditor* editor ) + virtual S32 redo( LLTextBase* editor ) { insert(editor, getPosition(), mWString ); return getPosition() + mWString.length(); @@ -187,11 +112,11 @@ private: }; /////////////////////////////////////////////////////////////////// -class LLTextEditor::LLTextCmdAddChar : public LLTextEditor::LLTextCmd +class LLTextEditor::TextCmdAddChar : public LLTextBase::TextCmd { public: - LLTextCmdAddChar( S32 pos, BOOL group_with_next, llwchar wc, LLTextSegmentPtr segment) - : LLTextCmd(pos, group_with_next, segment), mWString(1, wc), mBlockExtensions(FALSE) + TextCmdAddChar( S32 pos, BOOL group_with_next, llwchar wc, LLTextSegmentPtr segment) + : TextCmd(pos, group_with_next, segment), mWString(1, wc), mBlockExtensions(FALSE) { } virtual void blockExtensions() @@ -205,14 +130,14 @@ public: return !mBlockExtensions && (pos == getPosition() + (S32)mWString.length()); } - virtual BOOL execute( LLTextEditor* editor, S32* delta ) + virtual BOOL execute( LLTextBase* editor, S32* delta ) { *delta = insert(editor, getPosition(), mWString); LLWStringUtil::truncate(mWString, *delta); //mWString = wstring_truncate(mWString, *delta); return (*delta != 0); } - virtual BOOL extendAndExecute( LLTextEditor* editor, S32 pos, llwchar wc, S32* delta ) + virtual BOOL extendAndExecute( LLTextBase* editor, S32 pos, llwchar wc, S32* delta ) { LLWString ws; ws += wc; @@ -224,12 +149,12 @@ public: } return (*delta != 0); } - virtual S32 undo( LLTextEditor* editor ) + virtual S32 undo( LLTextBase* editor ) { remove(editor, getPosition(), mWString.length() ); return getPosition(); } - virtual S32 redo( LLTextEditor* editor ) + virtual S32 redo( LLTextBase* editor ) { insert(editor, getPosition(), mWString ); return getPosition() + mWString.length(); @@ -243,25 +168,25 @@ private: /////////////////////////////////////////////////////////////////// -class LLTextEditor::LLTextCmdOverwriteChar : public LLTextEditor::LLTextCmd +class LLTextEditor::TextCmdOverwriteChar : public LLTextBase::TextCmd { public: - LLTextCmdOverwriteChar( S32 pos, BOOL group_with_next, llwchar wc) - : LLTextCmd(pos, group_with_next), mChar(wc), mOldChar(0) {} + TextCmdOverwriteChar( S32 pos, BOOL group_with_next, llwchar wc) + : TextCmd(pos, group_with_next), mChar(wc), mOldChar(0) {} - virtual BOOL execute( LLTextEditor* editor, S32* delta ) + virtual BOOL execute( LLTextBase* editor, S32* delta ) { - mOldChar = editor->getWChar(getPosition()); + mOldChar = editor->getWText()[getPosition()]; overwrite(editor, getPosition(), mChar); *delta = 0; return TRUE; } - virtual S32 undo( LLTextEditor* editor ) + virtual S32 undo( LLTextBase* editor ) { overwrite(editor, getPosition(), mOldChar); return getPosition(); } - virtual S32 redo( LLTextEditor* editor ) + virtual S32 redo( LLTextBase* editor ) { overwrite(editor, getPosition(), mChar); return getPosition()+1; @@ -274,26 +199,26 @@ private: /////////////////////////////////////////////////////////////////// -class LLTextEditor::LLTextCmdRemove : public LLTextEditor::LLTextCmd +class LLTextEditor::TextCmdRemove : public LLTextBase::TextCmd { public: - LLTextCmdRemove( S32 pos, BOOL group_with_next, S32 len, segment_vec_t& segments ) : - LLTextCmd(pos, group_with_next), mLen(len) + TextCmdRemove( S32 pos, BOOL group_with_next, S32 len, segment_vec_t& segments ) : + TextCmd(pos, group_with_next), mLen(len) { std::swap(mSegments, segments); } - virtual BOOL execute( LLTextEditor* editor, S32* delta ) + virtual BOOL execute( LLTextBase* editor, S32* delta ) { - mWString = editor->getWSubString(getPosition(), mLen); + mWString = editor->getWText().substr(getPosition(), mLen); *delta = remove(editor, getPosition(), mLen ); return (*delta != 0); } - virtual S32 undo( LLTextEditor* editor ) + virtual S32 undo( LLTextBase* editor ) { insert(editor, getPosition(), mWString); return getPosition() + mWString.length(); } - virtual S32 redo( LLTextEditor* editor ) + virtual S32 redo( LLTextBase* editor ) { remove(editor, getPosition(), mLen ); return getPosition(); @@ -307,138 +232,61 @@ private: /////////////////////////////////////////////////////////////////// LLTextEditor::Params::Params() : default_text("default_text"), - max_text_length("max_length", 255), - read_only("read_only", false), embedded_items("embedded_items", false), - hide_scrollbar("hide_scrollbar"), - hide_border("hide_border", false), - word_wrap("word_wrap", false), ignore_tab("ignore_tab", true), - track_bottom("track_bottom", false), handle_edit_keys_directly("handle_edit_keys_directly", false), show_line_numbers("show_line_numbers", false), - cursor_color("cursor_color"), default_color("default_color"), - text_color("text_color"), - text_readonly_color("text_readonly_color"), - bg_readonly_color("bg_readonly_color"), - bg_writeable_color("bg_writeable_color"), - bg_focus_color("bg_focus_color"), - link_color("link_color"), - commit_on_focus_lost("commit_on_focus_lost", false), - length("length"), // ignored - type("type"), // ignored - is_unicode("is_unicode")// ignored + commit_on_focus_lost("commit_on_focus_lost", false) {} LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : - LLUICtrl(p, LLTextViewModelPtr(new LLTextViewModel)), LLTextBase(p), - mMaxTextByteLength( p.max_text_length ), mBaseDocIsPristine(TRUE), mPristineCmd( NULL ), mLastCmd( NULL ), - mCursorPos( 0 ), - mIsSelecting( FALSE ), - mSelectionStart( 0 ), - mSelectionEnd( 0 ), - mOnScrollEndData( NULL ), - mCursorColor( p.cursor_color() ), - mFgColor( p.text_color() ), mDefaultColor( p.default_color() ), - mReadOnlyFgColor( p.text_readonly_color() ), - mWriteableBgColor( p.bg_writeable_color() ), - mReadOnlyBgColor( p.bg_readonly_color() ), - mFocusBgColor( p.bg_focus_color() ), - mLinkColor( p.link_color() ), - mReadOnly(p.read_only), mShowLineNumbers ( p.show_line_numbers ), mCommitOnFocusLost( p.commit_on_focus_lost), - mTrackBottom( p.track_bottom ), mAllowEmbeddedItems( p.embedded_items ), mHandleEditKeysDirectly( p.handle_edit_keys_directly ), mMouseDownX(0), mMouseDownY(0), - mLastSelectionX(-1), - mReflowNeeded(FALSE), - mScrollNeeded(FALSE), - mLastSelectionY(-1), - mParseHighlights(FALSE), - mTabsToNextField(p.ignore_tab), - mScrollIndex(-1) + mTabsToNextField(p.ignore_tab) { - static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); - - mWordWrap = p.word_wrap; mDefaultFont = p.font; - mParseHTML = FALSE; mSourceID.generate(); - // reset desired x cursor position - mDesiredXPixel = -1; - - LLScrollContainer::Params scroll_params; - scroll_params.name = "text scroller"; - scroll_params.rect = getLocalRect(); - scroll_params.follows.flags = FOLLOWS_ALL; - scroll_params.is_opaque = false; - scroll_params.mouse_opaque = false; - scroll_params.min_auto_scroll_rate = 200; - scroll_params.max_auto_scroll_rate = 800; - mScroller = LLUICtrlFactory::create<LLScrollContainer>(scroll_params); - addChild(mScroller); - - LLPanel::Params panel_params; - panel_params.name = "text_contents"; - panel_params.rect = LLRect(0, 500, 500, 0); - panel_params.background_visible = true; - panel_params.background_opaque = true; - panel_params.mouse_opaque = false; - - mDocumentPanel = LLUICtrlFactory::create<DocumentPanel>(panel_params); - mScroller->addChild(mDocumentPanel); - - updateTextRect(); - - static LLUICachedControl<S32> text_editor_border ("UITextEditorBorder", 0); + //FIXME: use image? LLViewBorder::Params params; params.name = "text ed border"; params.rect = getLocalRect(); params.bevel_style = LLViewBorder::BEVEL_IN; - params.border_thickness = text_editor_border; + params.border_thickness = 1; + params.visible = p.border_visible; mBorder = LLUICtrlFactory::create<LLViewBorder> (params); addChild( mBorder ); - mBorder->setVisible(!p.hide_border); - - createDefaultSegment(); - appendText(p.default_text, FALSE, FALSE); + setText(p.default_text()); + if (mShowLineNumbers) + { + mHPad += UI_TEXTEDITOR_LINE_NUMBER_MARGIN; + updateTextRect(); + } } void LLTextEditor::initFromParams( const LLTextEditor::Params& p) { - resetDirty(); // Update saved text state - LLUICtrl::initFromParams(p); - // HACK: work around enabled == readonly design bug -- RN - // setEnabled will modify our read only status, so do this after - // LLUICtrl::initFromParams - if (p.read_only.isProvided()) - { - mReadOnly = p.read_only; - } - + LLTextBase::initFromParams(p); + if (p.commit_on_focus_lost.isProvided()) { mCommitOnFocusLost = p.commit_on_focus_lost; } - updateSegments(); updateAllowingLanguageInput(); - - // HACK: text editors always need to be enabled so that we can scroll - LLView::setEnabled(true); } LLTextEditor::~LLTextEditor() @@ -455,282 +303,18 @@ LLTextEditor::~LLTextEditor() std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer()); } -LLTextViewModel* LLTextEditor::getViewModel() const -{ - return (LLTextViewModel*)mViewModel.get(); -} - -static LLFastTimer::DeclareTimer FTM_TEXT_REFLOW ("Text Reflow"); -void LLTextEditor::reflow(S32 start_index) -{ - if (!mReflowNeeded) return; - - LLFastTimer ft(FTM_TEXT_REFLOW); - static LLUICachedControl<S32> texteditor_vpad_top ("UITextEditorVPadTop", 0); - - updateSegments(); - - while(mReflowNeeded) - { - bool scrolled_to_bottom = mScroller->isAtBottom(); - mReflowNeeded = FALSE; - - LLRect old_cursor_rect = getLocalRectFromDocIndex(mCursorPos); - bool follow_selection = mTextRect.overlaps(old_cursor_rect); // cursor is visible - S32 first_line = getFirstVisibleLine(); - // if scroll anchor not on first line, update it to first character of first line - if (!mLineInfoList.empty() - && (mScrollIndex < mLineInfoList[first_line].mDocIndexStart - || mScrollIndex >= mLineInfoList[first_line].mDocIndexEnd)) - { - mScrollIndex = mLineInfoList[first_line].mDocIndexStart; - } - LLRect first_char_rect = getLocalRectFromDocIndex(mScrollIndex); - //first_char_rect.intersectWith(mTextRect); - - S32 cur_top = -texteditor_vpad_top; - - if (getLength()) - { - segment_set_t::iterator seg_iter = mSegments.begin(); - S32 seg_offset = 0; - S32 line_start_index = 0; - S32 text_width = mTextRect.getWidth(); // optionally reserve room for margin - S32 remaining_pixels = text_width; - LLWString text(getWText()); - S32 line_count = 0; - - // find and erase line info structs starting at start_index and going to end of document - if (!mLineInfoList.empty()) - { - // find first element whose end comes after start_index - line_list_t::iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), start_index, line_end_compare()); - line_start_index = iter->mDocIndexStart; - line_count = iter->mLineNum; - getSegmentAndOffset(iter->mDocIndexStart, &seg_iter, &seg_offset); - mLineInfoList.erase(iter, mLineInfoList.end()); - } - - // reserve enough space for line numbers - S32 line_height = mShowLineNumbers ? (S32)(LLFontGL::getFontMonospace()->getLineHeight()) : 0; - - while(seg_iter != mSegments.end()) - { - LLTextSegmentPtr segment = *seg_iter; - - // track maximum height of any segment on this line - line_height = llmax(line_height, segment->getMaxHeight()); - S32 cur_index = segment->getStart() + seg_offset; - // find run of text from this segment that we can display on one line - S32 end_index = cur_index; - while(end_index < segment->getEnd() && text[end_index] != '\n') - { - ++end_index; - } - - // ask segment how many character fit in remaining space - S32 max_characters = end_index - cur_index; - S32 character_count = segment->getNumChars(llmax(0, remaining_pixels), seg_offset, cur_index - line_start_index, max_characters); - - seg_offset += character_count; - - S32 last_segment_char_on_line = segment->getStart() + seg_offset; - - // if we didn't finish the current segment... - if (last_segment_char_on_line < segment->getEnd()) - { - // set up index for next line - // ...skip newline, we don't want to draw - S32 next_line_count = line_count; - if (text[last_segment_char_on_line] == '\n') - { - seg_offset++; - last_segment_char_on_line++; - next_line_count++; - } - - // add line info and keep going - mLineInfoList.push_back(line_info(line_start_index, last_segment_char_on_line, cur_top, cur_top - line_height, line_count)); - - line_start_index = segment->getStart() + seg_offset; - cur_top -= line_height; - remaining_pixels = text_width; - line_height = 0; - line_count = next_line_count; - } - // ...just consumed last segment.. - else if (++segment_set_t::iterator(seg_iter) == mSegments.end()) - { - mLineInfoList.push_back(line_info(line_start_index, last_segment_char_on_line, cur_top, cur_top - line_height, line_count)); - cur_top -= line_height; - break; - } - // finished a segment and there are segments remaining on this line - else - { - // subtract pixels used and increment segment - remaining_pixels -= segment->getWidth(seg_offset, character_count); - ++seg_iter; - seg_offset = 0; - } - } - } - - // change mDocumentPanel document size to accomodate reflowed text - LLRect document_rect; - document_rect.setOriginAndSize(1, 1, - mScroller->getContentWindowRect().getWidth(), - llmax(mScroller->getContentWindowRect().getHeight(), -cur_top)); - mDocumentPanel->setShape(document_rect); - - // after making document big enough to hold all the text, move the text to fit in the document - if (!mLineInfoList.empty()) - { - S32 delta_pos = mDocumentPanel->getRect().getHeight() - mLineInfoList.begin()->mTop - texteditor_vpad_top; - // move line segments to fit new document rect - for (line_list_t::iterator it = mLineInfoList.begin(); it != mLineInfoList.end(); ++it) - { - it->mTop += delta_pos; - it->mBottom += delta_pos; - } - } - - // calculate visible region for diplaying text - updateTextRect(); - - for (segment_set_t::iterator segment_it = mSegments.begin(); - segment_it != mSegments.end(); - ++segment_it) - { - LLTextSegmentPtr segmentp = *segment_it; - segmentp->updateLayout(*this); - - } - - // apply scroll constraints after reflowing text - if (!hasMouseCapture()) - { - LLRect visible_content_rect = mScroller->getVisibleContentRect(); - if (scrolled_to_bottom && mTrackBottom) - { - // keep bottom of text buffer visible - endOfDoc(); - } - else if (hasSelection() && follow_selection) - { - // keep cursor in same vertical position on screen when selecting text - LLRect new_cursor_rect_doc = getLocalRectFromDocIndex(mCursorPos); - new_cursor_rect_doc.translate(visible_content_rect.mLeft, visible_content_rect.mBottom); - mScroller->scrollToShowRect(new_cursor_rect_doc, old_cursor_rect); - //llassert_always(getLocalRectFromDocIndex(mCursorPos).mBottom == old_cursor_rect.mBottom); - } - else - { - // keep first line of text visible - LLRect new_first_char_rect = getLocalRectFromDocIndex(mScrollIndex); - new_first_char_rect.translate(visible_content_rect.mLeft, visible_content_rect.mBottom); - mScroller->scrollToShowRect(new_first_char_rect, first_char_rect); - //llassert_always(getLocalRectFromDocIndex(mScrollIndex).mBottom == first_char_rect.mBottom); - } - } - } - - // reset desired x cursor position - updateCursorXPos(); -} - //////////////////////////////////////////////////////////// // LLTextEditor // Public methods -BOOL LLTextEditor::truncate() -{ - BOOL did_truncate = FALSE; - - // First rough check - if we're less than 1/4th the size, we're OK - if (getLength() >= S32(mMaxTextByteLength / 4)) - { - // Have to check actual byte size - LLWString text(getWText()); - S32 utf8_byte_size = wstring_utf8_length(text); - if ( utf8_byte_size > mMaxTextByteLength ) - { - // Truncate safely in UTF-8 - std::string temp_utf8_text = wstring_to_utf8str(text); - temp_utf8_text = utf8str_truncate( temp_utf8_text, mMaxTextByteLength ); - getViewModel()->setDisplay(utf8str_to_wstring( temp_utf8_text )); - did_truncate = TRUE; - } - } - - return did_truncate; -} - void LLTextEditor::setText(const LLStringExplicit &utf8str) { - // clear out the existing text and segments - clearSegments(); - - getViewModel()->setValue(""); - - truncate(); - blockUndo(); - - createDefaultSegment(); - - startOfDoc(); - deselect(); - - // append the new text (supports Url linking) - std::string text(utf8str); - LLStringUtil::removeCRLF(text); - appendStyledText(text, false, false, LLStyle::Params()); - - needsReflow(); - - resetDirty(); - - onValueChange(0, getLength()); -} - -void LLTextEditor::setWText(const LLWString &wtext) -{ - // clear out the existing text and segments - clearSegments(); - - getViewModel()->setDisplay(LLWString()); - - truncate(); blockUndo(); - - createDefaultSegment(); - - startOfDoc(); deselect(); - // append the new text (supports Url linking) - appendStyledText(wstring_to_utf8str(wtext), false, false, LLStyle::Params()); - - needsReflow(); + LLTextBase::setText(utf8str); resetDirty(); - - onValueChange(0, getLength()); -} - -// virtual -void LLTextEditor::setValue(const LLSD& value) -{ - setText(value.asString()); -} - -std::string LLTextEditor::getText() const -{ - if (mAllowEmbeddedItems) - { - llwarns << "getText() called on text with embedded items (not supported)" << llendl; - } - return getViewModel()->getValue().asString(); } void LLTextEditor::selectNext(const std::string& search_text_in, BOOL case_insensitive, BOOL wrap) @@ -828,12 +412,6 @@ void LLTextEditor::replaceTextAll(const std::string& search_text, const std::str } } -// Picks a new cursor position based on the screen size of text being drawn. -void LLTextEditor::setCursorAtLocalPos( S32 local_x, S32 local_y, bool round, bool keep_cursor_offset ) -{ - setCursorPos(getDocIndexFromLocalCoord(local_x, local_y, round), keep_cursor_offset); -} - S32 LLTextEditor::prevWordPos(S32 cursorPos) const { LLWString wtext(getWText()); @@ -862,60 +440,6 @@ S32 LLTextEditor::nextWordPos(S32 cursorPos) const return cursorPos; } -S32 LLTextEditor::getLineStart( S32 line ) const -{ - S32 num_lines = getLineCount(); - if (num_lines == 0) - { - return 0; - } - - line = llclamp(line, 0, num_lines-1); - return mLineInfoList[line].mDocIndexStart; -} - -S32 LLTextEditor::getLineHeight( S32 line ) const -{ - S32 num_lines = getLineCount(); - if (num_lines == 0) - { - return 0; - } - - line = llclamp(line, 0, num_lines-1); - return mLineInfoList[line].mTop - mLineInfoList[line].mBottom; -} - -// Given an offset into text (pos), find the corresponding line (from the start of the doc) and an offset into the line. -void LLTextEditor::getLineAndOffset( S32 startpos, S32* linep, S32* offsetp, bool include_wordwrap) const -{ - if (mLineInfoList.empty()) - { - *linep = 0; - *offsetp = startpos; - } - else - { - line_list_t::const_iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), startpos, line_end_compare()); - if (include_wordwrap) - { - *linep = iter - mLineInfoList.begin(); - } - else - { - if (iter == mLineInfoList.end()) - { - *linep = mLineInfoList.back().mLineNum; - } - else - { - *linep = iter->mLineNum; - } - } - *offsetp = startpos - iter->mDocIndexStart; - } -} - const LLTextSegmentPtr LLTextEditor::getPreviousSegment() const { // find segment index at character to left of cursor (or rightmost edge of selection) @@ -957,201 +481,6 @@ void LLTextEditor::getSegmentsInRange(LLTextEditor::segment_vec_t& segments_out, } } -// If round is true, if the position is on the right half of a character, the cursor -// will be put to its right. If round is false, the cursor will always be put to the -// character's left. - -S32 LLTextEditor::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const -{ - // Figure out which line we're nearest to. - LLRect visible_region = mScroller->getVisibleContentRect(); - - // binary search for line that starts before local_y - line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), local_y - mTextRect.mBottom + visible_region.mBottom, compare_bottom()); - - if (line_iter == mLineInfoList.end()) - { - return getLength(); // past the end - } - - S32 pos = getLength(); - S32 start_x = mTextRect.mLeft; - - segment_set_t::iterator line_seg_iter; - S32 line_seg_offset; - for(getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset); - line_seg_iter != mSegments.end(); - ++line_seg_iter, line_seg_offset = 0) - { - const LLTextSegmentPtr segmentp = *line_seg_iter; - - S32 segment_line_start = segmentp->getStart() + line_seg_offset; - S32 segment_line_length = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd - 1) - segment_line_start; - S32 text_width = segmentp->getWidth(line_seg_offset, segment_line_length); - if (local_x < start_x + text_width // cursor to left of right edge of text - || segmentp->getEnd() >= line_iter->mDocIndexEnd - 1) // or this segment wraps to next line - { - // Figure out which character we're nearest to. - S32 offset; - if (!segmentp->canEdit()) - { - S32 segment_width = segmentp->getWidth(0, segmentp->getEnd() - segmentp->getStart()); - if (round && local_x - start_x > segment_width / 2) - { - offset = segment_line_length; - } - else - { - offset = 0; - } - } - else - { - offset = segmentp->getOffset(local_x - start_x, line_seg_offset, segment_line_length, round); - } - pos = segment_line_start + offset; - break; - } - start_x += text_width; - } - - return pos; -} - -LLRect LLTextEditor::getLocalRectFromDocIndex(S32 pos) const -{ - LLRect local_rect(mTextRect); - local_rect.mBottom = local_rect.mTop - (S32)(mDefaultFont->getLineHeight()); - if (mLineInfoList.empty()) - { - return local_rect; - } - - // clamp pos to valid values - pos = llclamp(pos, 0, mLineInfoList.back().mDocIndexEnd - 1); - - - // find line that contains cursor - line_list_t::const_iterator line_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), pos, line_end_compare()); - - LLRect scrolled_view_rect = mScroller->getVisibleContentRect(); - local_rect.mLeft = mTextRect.mLeft - scrolled_view_rect.mLeft; - local_rect.mBottom = mTextRect.mBottom + (line_iter->mBottom - scrolled_view_rect.mBottom); - local_rect.mTop = mTextRect.mBottom + (line_iter->mTop - scrolled_view_rect.mBottom); - - segment_set_t::iterator line_seg_iter; - S32 line_seg_offset; - segment_set_t::iterator cursor_seg_iter; - S32 cursor_seg_offset; - getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset); - getSegmentAndOffset(pos, &cursor_seg_iter, &cursor_seg_offset); - - while(line_seg_iter != mSegments.end()) - { - const LLTextSegmentPtr segmentp = *line_seg_iter; - - if (line_seg_iter == cursor_seg_iter) - { - // cursor advanced to right based on difference in offset of cursor to start of line - local_rect.mLeft += segmentp->getWidth(line_seg_offset, cursor_seg_offset - line_seg_offset); - - break; - } - else - { - // add remainder of current text segment to cursor position - local_rect.mLeft += segmentp->getWidth(line_seg_offset, (segmentp->getEnd() - segmentp->getStart()) - line_seg_offset); - // offset will be 0 for all segments after the first - line_seg_offset = 0; - // go to next text segment on this line - ++line_seg_iter; - } - } - - local_rect.mRight = local_rect.mLeft; - - return local_rect; -} - -void LLTextEditor::addDocumentChild(LLView* view) -{ - mDocumentPanel->addChild(view); -} - -void LLTextEditor::removeDocumentChild(LLView* view) -{ - mDocumentPanel->removeChild(view); -} - -bool LLTextEditor::setCursor(S32 row, S32 column) -{ - if (0 <= row && row < (S32)mLineInfoList.size()) - { - S32 doc_pos = mLineInfoList[row].mDocIndexStart; - column = llclamp(column, 0, mLineInfoList[row].mDocIndexEnd - mLineInfoList[row].mDocIndexStart - 1); - doc_pos += column; - updateCursorXPos(); - - return setCursorPos(doc_pos); - } - return false; -} - -bool LLTextEditor::setCursorPos(S32 cursor_pos, bool keep_cursor_offset) -{ - S32 new_cursor_pos = cursor_pos; - if (new_cursor_pos != mCursorPos) - { - new_cursor_pos = getEditableIndex(new_cursor_pos, new_cursor_pos >= mCursorPos); - } - - mCursorPos = llclamp(new_cursor_pos, 0, (S32)getLength()); - needsScroll(); - if (!keep_cursor_offset) - updateCursorXPos(); - // did we get requested position? - return new_cursor_pos == cursor_pos; -} - -void LLTextEditor::updateCursorXPos() -{ - // reset desired x cursor position - mDesiredXPixel = getLocalRectFromDocIndex(mCursorPos).mLeft; -} - -// constraint cursor to editable segments of document -// NOTE: index must be within document range -S32 LLTextEditor::getEditableIndex(S32 index, bool increasing_direction) -{ - segment_set_t::iterator segment_iter; - S32 offset; - getSegmentAndOffset(index, &segment_iter, &offset); - - LLTextSegmentPtr segmentp = *segment_iter; - - if (segmentp->canEdit()) - { - return segmentp->getStart() + offset; - } - else if (segmentp->getStart() < index && index < segmentp->getEnd()) - { - // bias towards document end - if (increasing_direction) - { - return segmentp->getEnd(); - } - // bias towards document start - else - { - return segmentp->getStart(); - } - } - else - { - return index; - } -} - // virtual BOOL LLTextEditor::canDeselect() const { @@ -1167,25 +496,6 @@ void LLTextEditor::deselect() } -void LLTextEditor::startSelection() -{ - if( !mIsSelecting ) - { - mIsSelecting = TRUE; - mSelectionStart = mCursorPos; - mSelectionEnd = mCursorPos; - } -} - -void LLTextEditor::endSelection() -{ - if( mIsSelecting ) - { - mIsSelecting = FALSE; - mSelectionEnd = mCursorPos; - } -} - BOOL LLTextEditor::selectionContainsLineBreaks() { if (hasSelection()) @@ -1334,23 +644,12 @@ void LLTextEditor::selectAll() setCursorPos(mSelectionEnd); } - -BOOL LLTextEditor::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen) -{ - if (childrenHandleToolTip(x, y, msg, sticky_rect_screen)) - { - return TRUE; - } - - return handleToolTipForUrl(this, x, y, msg, sticky_rect_screen); -} - BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; // Let scrollbar have first dibs - handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL; + handled = LLTextBase::handleMouseDown(x, y, mask); if( !handled ) { @@ -1398,7 +697,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) } // Delay cursor flashing - resetKeystrokeTimer(); + resetCursorBlink(); return handled; } @@ -1407,7 +706,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) BOOL LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; - handled = childrenHandleMiddleMouseDown(x, y, mask) != NULL; + handled = LLTextBase::handleMouseDown(x, y, mask); if (!handled) { @@ -1424,19 +723,12 @@ BOOL LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask) { - static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); BOOL handled = FALSE; if(hasMouseCapture() ) { if( mIsSelecting ) { - if (x != mLastSelectionX || y != mLastSelectionY) - { - mLastSelectionX = x; - mLastSelectionY = y; - } - mScroller->autoScroll(x, y); S32 clamped_x = llclamp(x, mTextRect.mLeft, mTextRect.mRight); @@ -1453,32 +745,19 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask) if( !handled ) { // Pass to children - handled = LLView::childrenHandleHover(x, y, mask) != NULL; + handled = LLTextBase::handleHover(x, y, mask); } if( handled ) { // Delay cursor flashing - resetKeystrokeTimer(); + resetCursorBlink(); } - // Opaque if( !handled ) { - // Check to see if we're over an HTML-style link - handled = handleHoverOverUrl(x, y); - if( handled ) - { - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl; - getWindow()->setCursor(UI_CURSOR_HAND); - } - - if( !handled ) - { - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl; - getWindow()->setCursor(UI_CURSOR_IBEAM); - handled = TRUE; - } + getWindow()->setCursor(UI_CURSOR_IBEAM); + handled = TRUE; } return handled; @@ -1489,8 +768,12 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; - // let scrollbar have first dibs - handled = LLView::childrenHandleMouseUp(x, y, mask) != NULL; + // if I'm not currently selecting text + if (!(hasSelection() && hasMouseCapture())) + { + // let text segments handle mouse event + handled = LLTextBase::handleMouseUp(x, y, mask); + } if( !handled ) { @@ -1503,11 +786,6 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) endSelection(); } - if( !hasSelection() && hasMouseCapture() ) - { - handleMouseUpOverUrl(x, y); - } - // take selection to 'primary' clipboard updatePrimary(); @@ -1515,7 +793,7 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) } // Delay cursor flashing - resetKeystrokeTimer(); + resetCursorBlink(); if( hasMouseCapture() ) { @@ -1532,8 +810,8 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; - // let scrollbar have first dibs - handled = LLView::childrenHandleDoubleClick(x, y, mask) != NULL; + // let scrollbar and text segments have first dibs + handled = LLTextBase::handleDoubleClick(x, y, mask); if( !handled ) { @@ -1571,7 +849,7 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) mIsSelecting = FALSE; // delay cursor flashing - resetKeystrokeTimer(); + resetCursorBlink(); // take selection to 'primary' clipboard updatePrimary(); @@ -1583,35 +861,18 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) } -// Allow calling cards to be dropped onto text fields. Append the name and -// a carriage return. -// virtual -BOOL LLTextEditor::handleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, EDragAndDropType cargo_type, void *cargo_data, - EAcceptance *accept, - std::string& tooltip_msg) -{ - *accept = ACCEPT_NO; - - return TRUE; -} - //---------------------------------------------------------------------------- // Returns change in number of characters in mText -S32 LLTextEditor::execute( LLTextCmd* cmd ) +S32 LLTextEditor::execute( TextCmd* cmd ) { S32 delta = 0; if( cmd->execute(this, &delta) ) { // Delete top of undo stack undo_stack_t::iterator enditer = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd); - if (enditer != mUndoStack.begin()) - { - --enditer; - std::for_each(mUndoStack.begin(), enditer, DeletePointer()); - mUndoStack.erase(mUndoStack.begin(), enditer); - } + std::for_each(mUndoStack.begin(), enditer, DeletePointer()); + mUndoStack.erase(mUndoStack.begin(), enditer); // Push the new command is now on the top (front) of the undo stack. mUndoStack.push_front(cmd); mLastCmd = cmd; @@ -1627,7 +888,7 @@ S32 LLTextEditor::execute( LLTextCmd* cmd ) S32 LLTextEditor::insert(S32 pos, const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment) { - return execute( new LLTextCmdInsert( pos, group_with_next_op, wstr, segment ) ); + return execute( new TextCmdInsert( pos, group_with_next_op, wstr, segment ) ); } S32 LLTextEditor::remove(S32 pos, S32 length, bool group_with_next_op) @@ -1638,12 +899,7 @@ S32 LLTextEditor::remove(S32 pos, S32 length, bool group_with_next_op) // store text segments getSegmentsInRange(segments_to_remove, pos, pos + length, false); - return execute( new LLTextCmdRemove( pos, group_with_next_op, end_pos - pos, segments_to_remove ) ); -} - -S32 LLTextEditor::append(const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment) -{ - return insert(getLength(), wstr, group_with_next_op, segment); + return execute( new TextCmdRemove( pos, group_with_next_op, end_pos - pos, segments_to_remove ) ); } S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc) @@ -1654,7 +910,7 @@ S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc) } else { - return execute(new LLTextCmdOverwriteChar(pos, FALSE, wc)); + return execute(new TextCmdOverwriteChar(pos, FALSE, wc)); } } @@ -1674,8 +930,7 @@ void LLTextEditor::removeCharOrTab() if (text[mCursorPos - 1] == ' ') { // Try to remove a "tab" - S32 line, offset; - getLineAndOffset(mCursorPos, &line, &offset); + S32 offset = getLineOffsetFromDocIndex(mCursorPos); if (offset > 0) { chars_to_remove = offset % SPACES_PER_TAB; @@ -1749,7 +1004,7 @@ S32 LLTextEditor::addChar(S32 pos, llwchar wc) } else { - return execute(new LLTextCmdAddChar(pos, FALSE, wc, LLTextSegmentPtr())); + return execute(new TextCmdAddChar(pos, FALSE, wc, LLTextSegmentPtr())); } } @@ -2349,8 +1604,7 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask, BOOL* return deleteSelection(FALSE); } - S32 line, offset; - getLineAndOffset( mCursorPos, &line, &offset ); + S32 offset = getLineOffsetFromDocIndex(mCursorPos); S32 spaces_needed = SPACES_PER_TAB - (offset % SPACES_PER_TAB); for( S32 i=0; i < spaces_needed; i++ ) @@ -2481,7 +1735,7 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask ) if( handled ) { - resetKeystrokeTimer(); + resetCursorBlink(); // Most keystrokes will make the selection box go away, but not all will. if( !selection_modified && @@ -2534,7 +1788,7 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char) if( handled ) { - resetKeystrokeTimer(); + resetCursorBlink(); // Most keystrokes will make the selection box go away, but not all will. deselect(); @@ -2573,8 +1827,7 @@ void LLTextEditor::doDelete() if( (text[ mCursorPos ] == ' ') && (mCursorPos + SPACES_PER_TAB < getLength()) ) { // Try to remove a full tab's worth of spaces - S32 line, offset; - getLineAndOffset( mCursorPos, &line, &offset ); + S32 offset = getLineOffsetFromDocIndex(mCursorPos); chars_to_remove = SPACES_PER_TAB - (offset % SPACES_PER_TAB); if( chars_to_remove == 0 ) { @@ -2694,7 +1947,7 @@ void LLTextEditor::redo() void LLTextEditor::onFocusReceived() { - LLUICtrl::onFocusReceived(); + LLTextBase::onFocusReceived(); updateAllowingLanguageInput(); } @@ -2717,19 +1970,19 @@ void LLTextEditor::onFocusLost() // Make sure cursor is shown again getWindow()->showCursorFromMouseMove(); - LLUICtrl::onFocusLost(); + LLTextBase::onFocusLost(); } void LLTextEditor::onCommit() { setControlValue(getValue()); - LLUICtrl::onCommit(); + LLTextBase::onCommit(); } void LLTextEditor::setEnabled(BOOL enabled) { // just treat enabled as read-only flag - BOOL read_only = !enabled; + bool read_only = !enabled; if (read_only != mReadOnly) { mReadOnly = read_only; @@ -2738,195 +1991,6 @@ void LLTextEditor::setEnabled(BOOL enabled) } } -void LLTextEditor::drawBackground() -{ - S32 left = 0; - S32 top = getRect().getHeight(); - S32 bottom = 0; - - LLColor4 bg_color = mReadOnly ? mReadOnlyBgColor.get() - : hasFocus() ? mFocusBgColor.get() : mWriteableBgColor.get(); - if( mShowLineNumbers ) { - gl_rect_2d(left, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN, bottom, mReadOnlyBgColor.get() ); // line number area always read-only - gl_rect_2d(UI_TEXTEDITOR_LINE_NUMBER_MARGIN, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN-1, bottom, LLColor4::grey3); // separator - } -} - -// Draws the black box behind the selected text -void LLTextEditor::drawSelectionBackground() -{ - // Draw selection even if we don't have keyboard focus for search/replace - if( hasSelection() && !mLineInfoList.empty()) - { - LLWString text = getWText(); - std::vector<LLRect> selection_rects; - - S32 selection_left = llmin( mSelectionStart, mSelectionEnd ); - S32 selection_right = llmax( mSelectionStart, mSelectionEnd ); - LLRect selection_rect = mTextRect; - - // Skip through the lines we aren't drawing. - LLRect content_display_rect = mScroller->getVisibleContentRect(); - - // binary search for line that starts before top of visible buffer - line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mTop, compare_bottom()); - line_list_t::const_iterator end_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mBottom, compare_top()); - - bool done = false; - - // Find the coordinates of the selected area - for (;line_iter != end_iter && !done; ++line_iter) - { - // is selection visible on this line? - if (line_iter->mDocIndexEnd > selection_left && line_iter->mDocIndexStart < selection_right) - { - segment_set_t::iterator segment_iter; - S32 segment_offset; - getSegmentAndOffset(line_iter->mDocIndexStart, &segment_iter, &segment_offset); - - LLRect selection_rect; - selection_rect.mLeft = 0; - selection_rect.mRight = 0; - selection_rect.mBottom = line_iter->mBottom; - selection_rect.mTop = line_iter->mTop; - - for(;segment_iter != mSegments.end(); ++segment_iter, segment_offset = 0) - { - LLTextSegmentPtr segmentp = *segment_iter; - - S32 segment_line_start = segmentp->getStart() + segment_offset; - S32 segment_line_end = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd); - - // if selection after beginning of segment - if(selection_left >= segment_line_start) - { - S32 num_chars = llmin(selection_left, segment_line_end) - segment_line_start; - selection_rect.mLeft += segmentp->getWidth(segment_offset, num_chars); - } - - // if selection spans end of current segment... - if (selection_right > segment_line_end) - { - // extend selection slightly beyond end of line - // to indicate selection of newline character (use "n" character to determine width) - selection_rect.mRight += segmentp->getWidth(segment_offset, segment_line_end - segment_line_start); - } - // else if selection ends on current segment... - else - { - S32 num_chars = selection_right - segment_line_start; - selection_rect.mRight += segmentp->getWidth(segment_offset, num_chars); - - break; - } - } - selection_rects.push_back(selection_rect); - } - } - - // Draw the selection box (we're using a box instead of reversing the colors on the selected text). - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - const LLColor4& color = mReadOnly ? mReadOnlyBgColor.get() : mWriteableBgColor.get(); - F32 alpha = hasFocus() ? 0.7f : 0.3f; - gGL.color4f( 1.f - color.mV[0], 1.f - color.mV[1], 1.f - color.mV[2], alpha ); - - for (std::vector<LLRect>::iterator rect_it = selection_rects.begin(); - rect_it != selection_rects.end(); - ++rect_it) - { - LLRect selection_rect = *rect_it; - selection_rect.translate(mTextRect.mLeft - content_display_rect.mLeft, mTextRect.mBottom - content_display_rect.mBottom); - gl_rect_2d(selection_rect); - } - } -} - -void LLTextEditor::drawCursor() -{ - if( hasFocus() - && gFocusMgr.getAppHasFocus() - && !mReadOnly) - { - LLWString wtext = getWText(); - const llwchar* text = wtext.c_str(); - - LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos); - cursor_rect.translate(-1, 0); - segment_set_t::iterator seg_it = getSegIterContaining(mCursorPos); - - // take style from last segment - LLTextSegmentPtr segmentp; - - if (seg_it != mSegments.end()) - { - segmentp = *seg_it; - } - else - { - //segmentp = mSegments.back(); - return; - } - - // Draw the cursor - // (Flash the cursor every half second starting a fixed time after the last keystroke) - F32 elapsed = mKeystrokeTimer.getElapsedTimeF32(); - if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) ) - { - - if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection()) - { - S32 width = llmax(CURSOR_THICKNESS, segmentp->getWidth(mCursorPos - segmentp->getStart(), 1)); - cursor_rect.mRight = cursor_rect.mLeft + width; - } - else - { - cursor_rect.mRight = cursor_rect.mLeft + CURSOR_THICKNESS; - } - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - gGL.color4fv( mCursorColor.get().mV ); - - gl_rect_2d(cursor_rect); - - if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection() && text[mCursorPos] != '\n') - { - LLColor4 text_color; - const LLFontGL* fontp; - if (segmentp) - { - text_color = segmentp->getColor(); - fontp = segmentp->getStyle()->getFont(); - } - else if (mReadOnly) - { - text_color = mReadOnlyFgColor.get(); - fontp = mDefaultFont; - } - else - { - text_color = mFgColor.get(); - fontp = mDefaultFont; - } - fontp->render(text, mCursorPos, cursor_rect.mLeft, cursor_rect.mBottom, - LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], 1.f), - LLFontGL::LEFT, LLFontGL::BOTTOM, - LLFontGL::NORMAL, - LLFontGL::NO_SHADOW, - 1); - } - - // Make sure the IME is in the right place - LLRect screen_pos = calcScreenRect(); - LLCoordGL ime_pos( screen_pos.mLeft + llfloor(cursor_rect.mLeft), screen_pos.mBottom + llfloor(cursor_rect.mTop) ); - - ime_pos.mX = (S32) (ime_pos.mX * LLUI::sGLScaleFactor.mV[VX]); - ime_pos.mY = (S32) (ime_pos.mY * LLUI::sGLScaleFactor.mV[VY]); - getWindow()->setLanguageTextInput( ime_pos ); - } - } -} - void LLTextEditor::drawPreeditMarker() { static LLUICachedControl<F32> preedit_marker_brightness ("UIPreeditMarkerBrightness", 0); @@ -3032,96 +2096,6 @@ void LLTextEditor::drawPreeditMarker() } -void LLTextEditor::drawText() -{ - LLWString text = getWText(); - const S32 text_len = getLength(); - if( text_len <= 0 ) - { - return; - } - S32 selection_left = -1; - S32 selection_right = -1; - // Draw selection even if we don't have keyboard focus for search/replace - if( hasSelection()) - { - selection_left = llmin( mSelectionStart, mSelectionEnd ); - selection_right = llmax( mSelectionStart, mSelectionEnd ); - } - - LLGLSUIDefault gls_ui; - LLRect scrolled_view_rect = mScroller->getVisibleContentRect(); - LLRect content_rect = mScroller->getContentWindowRect(); - S32 first_line = getFirstVisibleLine(); - S32 num_lines = getLineCount(); - if (first_line >= num_lines) - { - return; - } - - S32 line_start = getLineStart(first_line); - // find first text segment that spans top of visible portion of text buffer - segment_set_t::iterator seg_iter = getSegIterContaining(line_start); - if (seg_iter == mSegments.end()) - { - return; - } - - LLTextSegmentPtr cur_segment = *seg_iter; - - for (S32 cur_line = first_line; cur_line < num_lines; cur_line++) - { - line_info& line = mLineInfoList[cur_line]; - - if ((line.mTop - scrolled_view_rect.mBottom) < mTextRect.mBottom) - { - break; - } - - S32 next_start = -1; - S32 line_end = text_len; - - if ((cur_line + 1) < num_lines) - { - next_start = getLineStart(cur_line + 1); - line_end = next_start; - } - if ( text[line_end-1] == '\n' ) - { - --line_end; - } - - LLRect text_rect(mTextRect.mLeft - scrolled_view_rect.mLeft, - line.mTop - scrolled_view_rect.mBottom + mTextRect.mBottom, - mTextRect.getWidth() - scrolled_view_rect.mLeft, - line.mBottom - scrolled_view_rect.mBottom + mTextRect.mBottom); - - // draw a single line of text - S32 seg_start = line_start; - while( seg_start < line_end ) - { - while( cur_segment->getEnd() <= seg_start ) - { - seg_iter++; - if (seg_iter == mSegments.end()) - { - llwarns << "Ran off the segmentation end!" << llendl; - - return; - } - cur_segment = *seg_iter; - } - - S32 clipped_end = llmin( line_end, cur_segment->getEnd() ) - cur_segment->getStart(); - text_rect.mLeft = (S32)(cur_segment->draw(seg_start - cur_segment->getStart(), clipped_end, selection_left, selection_right, text_rect)); - - seg_start = clipped_end + cur_segment->getStart(); - } - - line_start = next_start; - } -} - void LLTextEditor::drawLineNumbers() { LLGLSUIDefault gls_ui; @@ -3136,24 +2110,31 @@ void LLTextEditor::drawLineNumbers() return; } - S32 cursor_line = getCurrentLine(); + S32 cursor_line = getLineNumFromDocIndex(mCursorPos); if (mShowLineNumbers) { + S32 left = 0; + S32 top = getRect().getHeight(); + S32 bottom = 0; + + gl_rect_2d(left, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN, bottom, mReadOnlyBgColor.get() ); // line number area always read-only + gl_rect_2d(UI_TEXTEDITOR_LINE_NUMBER_MARGIN, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN-1, bottom, LLColor4::grey3); // separator + S32 last_line_num = -1; for (S32 cur_line = first_line; cur_line < num_lines; cur_line++) { line_info& line = mLineInfoList[cur_line]; - if ((line.mTop - scrolled_view_rect.mBottom) < mTextRect.mBottom) + if ((line.mRect.mTop - scrolled_view_rect.mBottom) < mTextRect.mBottom) { break; } - S32 line_bottom = line.mBottom - scrolled_view_rect.mBottom + mTextRect.mBottom; + S32 line_bottom = line.mRect.mBottom - scrolled_view_rect.mBottom + mTextRect.mBottom; // draw the line numbers - if(line.mLineNum != last_line_num && line.mTop <= scrolled_view_rect.mTop) + if(line.mLineNum != last_line_num && line.mRect.mTop <= scrolled_view_rect.mTop) { const LLFontGL *num_font = LLFontGL::getFontMonospace(); const LLWString ltext = utf8str_to_wstring(llformat("%d", line.mLineNum )); @@ -3180,58 +2161,23 @@ void LLTextEditor::drawLineNumbers() void LLTextEditor::draw() { - // reflow if needed, on demand - reflow(); - - // then update scroll position, as cursor may have moved - updateScrollFromCursor(); - - LLColor4 bg_color = mReadOnly - ? mReadOnlyBgColor.get() - : hasFocus() - ? mFocusBgColor.get() - : mWriteableBgColor.get(); - - mDocumentPanel->setBackgroundColor(bg_color); - - LLView::draw(); - drawBackground(); //overlays scrolling panel bg - drawLineNumbers(); - { // pad clipping rectangle so that cursor can draw at full width // when at left edge of mTextRect LLRect clip_rect(mTextRect); clip_rect.stretch(1); LLLocalClipRect clip(clip_rect); - drawSelectionBackground(); drawPreeditMarker(); - drawText(); - drawCursor(); } + LLTextBase::draw(); + drawLineNumbers(); + //RN: the decision was made to always show the orange border for keyboard focus but do not put an insertion caret // when in readonly mode mBorder->setKeyboardFocusHighlight( hasFocus() );// && !mReadOnly); } - -S32 LLTextEditor::getFirstVisibleLine() const -{ - LLRect visible_region = mScroller->getVisibleContentRect(); - - // binary search for line that starts before top of visible buffer - line_list_t::const_iterator iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_bottom()); - - return iter - mLineInfoList.begin(); -} - -// virtual -void LLTextEditor::clear() -{ - setText(LLStringUtil::null); -} - // Start or stop the editor from accepting text-editing keystrokes // see also LLLineEditor void LLTextEditor::setFocus( BOOL new_state ) @@ -3247,7 +2193,7 @@ void LLTextEditor::setFocus( BOOL new_state ) getWindow()->allowLanguageTextInput(this, FALSE); } - LLUICtrl::setFocus( new_state ); + LLTextBase::setFocus( new_state ); if( new_state ) { @@ -3255,7 +2201,7 @@ void LLTextEditor::setFocus( BOOL new_state ) gEditMenuHandler = this; // Don't start the cursor flashing right away - resetKeystrokeTimer(); + resetCursorBlink(); } else { @@ -3269,96 +2215,6 @@ void LLTextEditor::setFocus( BOOL new_state ) } } -// virtual -BOOL LLTextEditor::acceptsTextInput() const -{ - return !mReadOnly; -} - -// Given a line (from the start of the doc) and an offset into the line, find the offset (pos) into text. -S32 LLTextEditor::getPos( S32 line, S32 offset ) -{ - S32 line_start = getLineStart(line); - S32 next_start = getLineStart(line+1); - if (next_start == line_start) - { - next_start = getLength() + 1; - } - S32 line_length = next_start - line_start - 1; - line_length = llmax(line_length, 0); - return line_start + llmin( offset, line_length ); -} - - -void LLTextEditor::changePage( S32 delta ) -{ - const S32 PIXEL_OVERLAP_ON_PAGE_CHANGE = 10; - if (delta == 0) return; - - //RN: use pixel heights - S32 line, offset; - getLineAndOffset( mCursorPos, &line, &offset ); - - LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos); - - if( delta == -1 ) - { - mScroller->pageUp(PIXEL_OVERLAP_ON_PAGE_CHANGE); - } - else - if( delta == 1 ) - { - mScroller->pageDown(PIXEL_OVERLAP_ON_PAGE_CHANGE); - } - - if (getLocalRectFromDocIndex(mCursorPos) == cursor_rect) - { - // cursor didn't change apparent position, so move to top or bottom of document, respectively - if (delta < 0) - { - startOfDoc(); - } - else - { - endOfDoc(); - } - } - else - { - setCursorAtLocalPos(cursor_rect.getCenterX(), cursor_rect.getCenterY(), true, false); - } -} - -void LLTextEditor::changeLine( S32 delta ) -{ - S32 line, offset; - getLineAndOffset( mCursorPos, &line, &offset ); - - S32 new_line = line; - if( (delta < 0) && (line > 0 ) ) - { - new_line = line - 1; - } - else if( (delta > 0) && (line < (getLineCount() - 1)) ) - { - new_line = line + 1; - } - - LLRect visible_region = mScroller->getVisibleContentRect(); - - S32 new_cursor_pos = getDocIndexFromLocalCoord(mDesiredXPixel, mLineInfoList[new_line].mBottom + mTextRect.mBottom - visible_region.mBottom, TRUE); - setCursorPos(new_cursor_pos, true); -} - - -void LLTextEditor::startOfLine() -{ - S32 line, offset; - getLineAndOffset( mCursorPos, &line, &offset ); - setCursorPos(mCursorPos - offset); -} - - // public void LLTextEditor::setCursorAndScrollToEnd() { @@ -3366,92 +2222,16 @@ void LLTextEditor::setCursorAndScrollToEnd() endOfDoc(); } -void LLTextEditor::getLineAndColumnForPosition( S32 position, S32* line, S32* col, BOOL include_wordwrap ) -{ - getLineAndOffset( mCursorPos, line, col, include_wordwrap ); -} - void LLTextEditor::getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wordwrap ) { - getLineAndColumnForPosition(mCursorPos, line, col, include_wordwrap); -} - -S32 LLTextEditor::getCurrentLine() -{ - return getLineForPosition(mCursorPos); -} - -S32 LLTextEditor::getLineForPosition(S32 position) -{ - S32 line, col; - getLineAndColumnForPosition(position, &line, &col, FALSE); - return line; -} - - -void LLTextEditor::endOfLine() -{ - S32 line, offset; - getLineAndOffset( mCursorPos, &line, &offset ); - S32 num_lines = getLineCount(); - if (line + 1 >= num_lines) - { - setCursorPos(getLength()); - } - else - { - setCursorPos( getLineStart(line + 1) - 1 ); - } -} - -void LLTextEditor::startOfDoc() -{ - setCursorPos(0); -} - -void LLTextEditor::endOfDoc() -{ - setCursorPos(getLength()); -} - -// Sets the scrollbar from the cursor position -void LLTextEditor::updateScrollFromCursor() -{ - // Update scroll position even in read-only mode (when there's no cursor displayed) - // because startOfDoc()/endOfDoc() modify cursor position. See EXT-736. - - if (!mScrollNeeded) - { - return; - } - mScrollNeeded = FALSE; - - S32 line, offset; - getLineAndOffset( mCursorPos, &line, &offset ); - - // scroll so that the cursor is at the top of the page - LLRect scroller_doc_window = mScroller->getVisibleContentRect(); - LLRect cursor_rect_doc = getLocalRectFromDocIndex(mCursorPos); - cursor_rect_doc.translate(scroller_doc_window.mLeft, scroller_doc_window.mBottom); - mScroller->scrollToShowRect(cursor_rect_doc, LLRect(0, scroller_doc_window.getHeight() - 5, scroller_doc_window.getWidth(), 5)); -} - -void LLTextEditor::reshape(S32 width, S32 height, BOOL called_from_parent) -{ - LLView::reshape( width, height, called_from_parent ); - - // do this first after reshape, because other things depend on - // up-to-date mTextRect - updateTextRect(); - - needsReflow(); + *line = getLineNumFromDocIndex(mCursorPos, include_wordwrap); + *col = getLineOffsetFromDocIndex(mCursorPos, include_wordwrap); } void LLTextEditor::autoIndent() { // Count the number of spaces in the current line - S32 line, offset; - getLineAndOffset( mCursorPos, &line, &offset ); + S32 line = getLineNumFromDocIndex(mCursorPos); S32 line_start = getLineStart(line); S32 space_count = 0; S32 i; @@ -3496,221 +2276,6 @@ void LLTextEditor::insertText(const std::string &new_text) setEnabled( enabled ); } - -void LLTextEditor::appendColoredText(const std::string &new_text, - bool allow_undo, - bool prepend_newline, - const LLColor4 &color, - const std::string& font_name) -{ - LLColor4 lcolor=color; - if (mParseHighlights) - { - LLTextParser* highlight = LLTextParser::getInstance(); - highlight->parseFullLineHighlights(new_text, &lcolor); - } - - LLStyle::Params style_params; - style_params.color = lcolor; - if (font_name.empty()) - { - style_params.font = mDefaultFont; - } - else - { - style_params.font.name = font_name; - } - appendStyledText(new_text, allow_undo, prepend_newline, style_params); -} - -void LLTextEditor::appendStyledText(const std::string &new_text, - bool allow_undo, - bool prepend_newline, - const LLStyle::Params& style_params) -{ - S32 part = (S32)LLTextParser::WHOLE; - if(mParseHTML) - { - - S32 start=0,end=0; - LLUrlMatch match; - std::string text = new_text; - while ( LLUrlRegistry::instance().findUrl(text, match, - boost::bind(&LLTextEditor::onUrlLabelUpdated, this, _1, _2)) ) - { - start = match.getStart(); - end = match.getEnd()+1; - - LLStyle::Params link_params = style_params; - link_params.color = mLinkColor; - link_params.font.style = "UNDERLINE"; - link_params.link_href = match.getUrl(); - - // output the text before the Url - if (start > 0) - { - if (part == (S32)LLTextParser::WHOLE || - part == (S32)LLTextParser::START) - { - part = (S32)LLTextParser::START; - } - else - { - part = (S32)LLTextParser::MIDDLE; - } - std::string subtext=text.substr(0,start); - appendHighlightedText(subtext,allow_undo, prepend_newline, part, style_params); - prepend_newline = false; - } - - // output the styled Url - appendText(match.getLabel(),allow_undo, prepend_newline, link_params); - prepend_newline = false; - - // set the tooltip for the Url label - if (! match.getTooltip().empty()) - { - segment_set_t::iterator it = getSegIterContaining(getLength()-1); - if (it != mSegments.end()) - { - LLTextSegmentPtr segment = *it; - segment->setToolTip(match.getTooltip()); - } - } - - // output an optional icon after the Url - if (! match.getIcon().empty()) - { - LLUIImagePtr image = LLUI::getUIImage(match.getIcon()); - if (image) - { - LLStyle::Params icon; - icon.image = image; - // TODO: fix spacing of images and remove the fixed char spacing - appendText(" ", allow_undo, prepend_newline, icon); - } - } - - // move on to the rest of the text after the Url - if (end < (S32)text.length()) - { - text = text.substr(end,text.length() - end); - end=0; - part=(S32)LLTextParser::END; - } - else - { - break; - } - } - if (part != (S32)LLTextParser::WHOLE) part=(S32)LLTextParser::END; - if (end < (S32)text.length()) appendHighlightedText(text,allow_undo, prepend_newline, part, style_params); - } - else - { - appendHighlightedText(new_text, allow_undo, prepend_newline, part, style_params); - } -} - -void LLTextEditor::appendHighlightedText(const std::string &new_text, - bool allow_undo, - bool prepend_newline, - S32 highlight_part, - const LLStyle::Params& style_params) -{ - if (mParseHighlights) - { - LLTextParser* highlight = LLTextParser::getInstance(); - - if (highlight && !style_params.isDefault()) - { - LLStyle::Params highlight_params = style_params; - - LLSD pieces = highlight->parsePartialLineHighlights(new_text, highlight_params.color(), highlight_part); - bool lprepend=prepend_newline; - for (S32 i=0;i<pieces.size();i++) - { - LLSD color_llsd = pieces[i]["color"]; - LLColor4 lcolor; - lcolor.setValue(color_llsd); - highlight_params.color = lcolor; - if (i != 0 && (pieces.size() > 1) ) lprepend=FALSE; - appendText((std::string)pieces[i]["text"], allow_undo, lprepend, highlight_params); - } - return; - } - } - appendText(new_text, allow_undo, prepend_newline, style_params); -} - -// Appends new text to end of document -void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool prepend_newline, - const LLStyle::Params& stylep) -{ - if (new_text.empty()) return; - - // Save old state - S32 selection_start = mSelectionStart; - S32 selection_end = mSelectionEnd; - BOOL was_selecting = mIsSelecting; - S32 cursor_pos = mCursorPos; - S32 old_length = getLength(); - BOOL cursor_was_at_end = (mCursorPos == old_length); - - deselect(); - - setCursorPos(old_length); - - LLWString wide_text; - - // Add carriage return if not first line - if (getLength() != 0 - && prepend_newline) - { - wide_text = utf8str_to_wstring(std::string("\n") + new_text); - } - else - { - wide_text = utf8str_to_wstring(new_text); - } - - LLTextSegmentPtr segmentp; - if (!stylep.isDefault()) - { - S32 segment_start = old_length; - S32 segment_end = old_length + wide_text.size(); - segmentp = new LLNormalTextSegment(new LLStyle(stylep), segment_start, segment_end, *this ); - } - - append(wide_text, TRUE, segmentp); - - needsReflow(); - - // Set the cursor and scroll position - if( selection_start != selection_end ) - { - mSelectionStart = selection_start; - mSelectionEnd = selection_end; - - mIsSelecting = was_selecting; - setCursorPos(cursor_pos); - } - else if( cursor_was_at_end ) - { - setCursorPos(getLength()); - } - else - { - setCursorPos(cursor_pos); - } - - if( !allow_undo ) - { - blockUndo(); - } -} - - void LLTextEditor::appendWidget(LLView* widget, const std::string &widget_text, bool allow_undo, bool prepend_newline) { // Save old state @@ -3739,7 +2304,7 @@ void LLTextEditor::appendWidget(LLView* widget, const std::string &widget_text, } LLTextSegmentPtr segment = new LLInlineViewSegment(widget, old_length, old_length + widget_text.size()); - append(widget_wide_text, FALSE, segment); + insert(getLength(), widget_wide_text, FALSE, segment); needsReflow(); @@ -3767,12 +2332,6 @@ void LLTextEditor::appendWidget(LLView* widget, const std::string &widget_text, } } -void LLTextEditor::onUrlLabelUpdated(const std::string &url, - const std::string &label) -{ - // LLUrlRegistry has given us a new label for one of our Urls - replaceUrlLabel(url, label); -} void LLTextEditor::replaceUrlLabel(const std::string &url, const std::string &label) @@ -3830,164 +2389,10 @@ void LLTextEditor::removeTextFromEnd(S32 num_chars) mSelectionStart = llclamp(mSelectionStart, 0, len); mSelectionEnd = llclamp(mSelectionEnd, 0, len); - reflow(); + needsReflow(); needsScroll(); } -/////////////////////////////////////////////////////////////////// -// Returns change in number of characters in mWText - -S32 LLTextEditor::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextEditor::segment_vec_t* segments ) -{ - LLWString text(getWText()); - S32 old_len = text.length(); // length() returns character length - S32 insert_len = wstr.length(); - - pos = getEditableIndex(pos, true); - - segment_set_t::iterator seg_iter = getSegIterContaining(pos); - - LLTextSegmentPtr default_segment; - - LLTextSegmentPtr segmentp; - if (seg_iter != mSegments.end()) - { - segmentp = *seg_iter; - } - else - { - //segmentp = mSegments.back(); - return pos; - } - - if (segmentp->canEdit()) - { - segmentp->setEnd(segmentp->getEnd() + insert_len); - if (seg_iter != mSegments.end()) - { - ++seg_iter; - } - } - else - { - // create default editable segment to hold new text - default_segment = new LLNormalTextSegment( getDefaultStyle(), pos, pos + insert_len, *this); - } - - // shift remaining segments to right - for(;seg_iter != mSegments.end(); ++seg_iter) - { - LLTextSegmentPtr segmentp = *seg_iter; - segmentp->setStart(segmentp->getStart() + insert_len); - segmentp->setEnd(segmentp->getEnd() + insert_len); - } - - // insert new segments - if (segments) - { - if (default_segment.notNull()) - { - // potentially overwritten by segments passed in - insertSegment(default_segment); - } - for (segment_vec_t::iterator seg_iter = segments->begin(); - seg_iter != segments->end(); - ++seg_iter) - { - LLTextSegment* segmentp = *seg_iter; - insertSegment(segmentp); - } - } - - text.insert(pos, wstr); - getViewModel()->setDisplay(text); - - if ( truncate() ) - { - // The user's not getting everything he's hoping for - make_ui_sound("UISndBadKeystroke"); - insert_len = getLength() - old_len; - } - - onValueChange(pos, pos + insert_len); - - return insert_len; -} - -S32 LLTextEditor::removeStringNoUndo(S32 pos, S32 length) -{ - LLWString text(getWText()); - segment_set_t::iterator seg_iter = getSegIterContaining(pos); - while(seg_iter != mSegments.end()) - { - LLTextSegmentPtr segmentp = *seg_iter; - S32 end = pos + length; - if (segmentp->getStart() < pos) - { - // deleting from middle of segment - if (segmentp->getEnd() > end) - { - segmentp->setEnd(segmentp->getEnd() - length); - } - // truncating segment - else - { - segmentp->setEnd(pos); - } - } - else if (segmentp->getStart() < end) - { - // deleting entire segment - if (segmentp->getEnd() <= end) - { - // remove segment - segmentp->unlinkFromDocument(this); - segment_set_t::iterator seg_to_erase(seg_iter++); - mSegments.erase(seg_to_erase); - continue; - } - // deleting head of segment - else - { - segmentp->setStart(pos); - segmentp->setEnd(segmentp->getEnd() - length); - } - } - else - { - // shifting segments backward to fill deleted portion - segmentp->setStart(segmentp->getStart() - length); - segmentp->setEnd(segmentp->getEnd() - length); - } - ++seg_iter; - } - - text.erase(pos, length); - getViewModel()->setDisplay(text); - - // recreate default segment in case we erased everything - createDefaultSegment(); - - onValueChange(pos, pos); - - return -length; // This will be wrong if someone calls removeStringNoUndo with an excessive length -} - -S32 LLTextEditor::overwriteCharNoUndo(S32 pos, llwchar wc) -{ - if (pos > (S32)getLength()) - { - return 0; - } - LLWString text(getWText()); - text[pos] = wc; - getViewModel()->setDisplay(text); - - onValueChange(pos, pos + 1); - - return 1; -} - //---------------------------------------------------------------------------- void LLTextEditor::makePristine() @@ -4051,29 +2456,13 @@ BOOL LLTextEditor::tryToRevertToPristineState() } -void LLTextEditor::updateTextRect() -{ - static LLUICachedControl<S32> texteditor_border ("UITextEditorBorder", 0); - static LLUICachedControl<S32> texteditor_h_pad ("UITextEditorHPad", 0); - - LLRect old_text_rect = mTextRect; - mTextRect = mScroller->getContentWindowRect(); - mTextRect.stretch(texteditor_border * -1); - mTextRect.mLeft += texteditor_h_pad; - mTextRect.mLeft += mShowLineNumbers ? UI_TEXTEDITOR_LINE_NUMBER_MARGIN : 0; - if (mTextRect != old_text_rect) - { - needsReflow(); - } -} - -LLFastTimer::DeclareTimer FTM_TEXT_EDITOR_LOAD_KEYWORD("Text Editor Load Keywords"); +static LLFastTimer::DeclareTimer FTM_SYNTAX_HIGHLIGHTING("Syntax Highlighting"); void LLTextEditor::loadKeywords(const std::string& filename, const std::vector<std::string>& funcs, const std::vector<std::string>& tooltips, const LLColor3& color) { - LLFastTimer ft(FTM_TEXT_EDITOR_LOAD_KEYWORD); + LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING); if(mKeywords.loadFromFile(filename)) { S32 count = llmin(funcs.size(), tooltips.size()); @@ -4094,27 +2483,9 @@ void LLTextEditor::loadKeywords(const std::string& filename, } } -void LLTextEditor::createDefaultSegment() -{ - // ensures that there is always at least one segment - if (mSegments.empty()) - { - LLTextSegmentPtr default_segment = new LLNormalTextSegment( getDefaultStyle(), 0, getLength() + 1, *this); - mSegments.insert(default_segment); - default_segment->linkToDocument(this); - } -} - -LLStyleSP LLTextEditor::getDefaultStyle() -{ - LLColor4 text_color = ( mReadOnly ? mReadOnlyFgColor.get() : mFgColor.get() ); - return LLStyleSP(new LLStyle(LLStyle::Params().color(text_color).font(mDefaultFont))); -} - -LLFastTimer::DeclareTimer FTM_UPDATE_TEXT_SEGMENTS("Update Text Segments"); void LLTextEditor::updateSegments() { - LLFastTimer ft(FTM_UPDATE_TEXT_SEGMENTS); + LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING); if (mKeywords.isLoaded()) { // HACK: No non-ascii keywords for now @@ -4125,11 +2496,11 @@ void LLTextEditor::updateSegments() segment_set_t::iterator insert_it = mSegments.begin(); for (segment_vec_t::iterator list_it = segment_list.begin(); list_it != segment_list.end(); ++list_it) { - insert_it = mSegments.insert(insert_it, *list_it); + insertSegment(*list_it); } } - createDefaultSegment(); + LLTextBase::updateSegments(); } void LLTextEditor::updateLinkSegments() @@ -4155,66 +2526,7 @@ void LLTextEditor::updateLinkSegments() } } -void LLTextEditor::insertSegment(LLTextSegmentPtr segment_to_insert) -{ - if (segment_to_insert.isNull()) - { - return; - } - - segment_set_t::iterator cur_seg_iter = getSegIterContaining(segment_to_insert->getStart()); - if (cur_seg_iter == mSegments.end()) - { - mSegments.insert(segment_to_insert); - segment_to_insert->linkToDocument(this); - } - else - { - LLTextSegmentPtr cur_segmentp = *cur_seg_iter; - if (cur_segmentp->getStart() < segment_to_insert->getStart()) - { - S32 old_segment_end = cur_segmentp->getEnd(); - // split old at start point for new segment - cur_segmentp->setEnd(segment_to_insert->getStart()); - // advance to next segment - ++cur_seg_iter; - // insert remainder of old segment - LLTextSegmentPtr remainder_segment = new LLNormalTextSegment( cur_segmentp->getStyle(), segment_to_insert->getStart(), old_segment_end, *this); - cur_seg_iter = mSegments.insert(cur_seg_iter, remainder_segment); - remainder_segment->linkToDocument(this); - // insert new segment before remainder of old segment - cur_seg_iter = mSegments.insert(cur_seg_iter, segment_to_insert); - - segment_to_insert->linkToDocument(this); - // move to "remanider" segment and start truncation there - ++cur_seg_iter; - } - else - { - cur_seg_iter = mSegments.insert(cur_seg_iter, segment_to_insert); - ++cur_seg_iter; - segment_to_insert->linkToDocument(this); - } - - // now delete/truncate remaining segments as necessary - while(cur_seg_iter != mSegments.end()) - { - cur_segmentp = *cur_seg_iter; - if (cur_segmentp->getEnd() <= segment_to_insert->getEnd()) - { - cur_segmentp->unlinkFromDocument(this); - segment_set_t::iterator seg_to_erase(cur_seg_iter++); - mSegments.erase(seg_to_erase); - } - else - { - cur_segmentp->setStart(segment_to_insert->getEnd()); - break; - } - } - } -} void LLTextEditor::onMouseCaptureLost() { @@ -4400,7 +2712,7 @@ void LLTextEditor::updatePreedit(const LLWString &preedit_string, if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) { - mPreeditOverwrittenWString = getWSubString(insert_preedit_at, mPreeditWString.length()); + mPreeditOverwrittenWString = getWText().substr(insert_preedit_at, mPreeditWString.length()); removeStringNoUndo(insert_preedit_at, mPreeditWString.length()); } else @@ -4415,7 +2727,7 @@ void LLTextEditor::updatePreedit(const LLWString &preedit_string, setCursorPos(insert_preedit_at + caret_position); // Update of the preedit should be caused by some key strokes. - mKeystrokeTimer.reset(); + resetCursorBlink(); onKeyStroke(); } @@ -4578,93 +2890,6 @@ S32 LLTextEditor::getPreeditFontSize() const return llround(mDefaultFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]); } -LLWString LLTextEditor::getWText() const -{ - return getViewModel()->getDisplay(); -} - -void LLTextEditor::onValueChange(S32 start, S32 end) -{ -} - -// -// LLInlineViewSegment -// - -LLInlineViewSegment::LLInlineViewSegment(LLView* view, S32 start, S32 end) -: LLTextSegment(start, end), - mView(view) -{ -} - -LLInlineViewSegment::~LLInlineViewSegment() -{ - mView->die(); -} - -S32 LLInlineViewSegment::getWidth(S32 first_char, S32 num_chars) const -{ - if (first_char == 0 && num_chars == 0) - { - return 0; - } - else - { - return mView->getRect().getWidth(); - } -} - -S32 LLInlineViewSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const -{ - if (line_offset != 0 && num_pixels < mView->getRect().getWidth()) - { - return 0; - } - else - { - return mEnd - mStart; - } -} - -void LLInlineViewSegment::updateLayout(const LLTextBase& editor) -{ - const LLTextEditor *ed = dynamic_cast<const LLTextEditor *>(&editor); - if (ed) - { - LLRect start_rect = ed->getLocalRectFromDocIndex(mStart); - LLRect doc_rect = ed->getDocumentPanel()->getRect(); - mView->setOrigin(doc_rect.mLeft + start_rect.mLeft, doc_rect.mBottom + start_rect.mBottom); - } -} - -F32 LLInlineViewSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect) -{ - return (F32)(draw_rect.mLeft + mView->getRect().getWidth()); -} - -S32 LLInlineViewSegment::getMaxHeight() const -{ - return mView->getRect().getHeight(); -} - -void LLInlineViewSegment::unlinkFromDocument(LLTextBase* editor) -{ - LLTextEditor *ed = dynamic_cast<LLTextEditor *>(editor); - if (ed) - { - ed->removeDocumentChild(mView); - } -} - -void LLInlineViewSegment::linkToDocument(LLTextBase* editor) -{ - LLTextEditor *ed = dynamic_cast<LLTextEditor *>(editor); - if (ed) - { - ed->addDocumentChild(mView); - } -} - BOOL LLTextEditor::isDirty() const { if(mReadOnly) diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index a04261c4be..0e5707a3a6 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -37,7 +37,6 @@ #include "llrect.h" #include "llkeywords.h" -#include "lluictrl.h" #include "llframetimer.h" #include "lldarray.h" #include "llstyle.h" @@ -52,66 +51,27 @@ class LLFontGL; class LLScrollbar; class LLKeywordToken; -class LLTextCmd; +class TextCmd; class LLUICtrlFactory; class LLScrollContainer; -class LLInlineViewSegment : public LLTextSegment -{ -public: - LLInlineViewSegment(LLView* widget, S32 start, S32 end); - ~LLInlineViewSegment(); - /*virtual*/ S32 getWidth(S32 first_char, S32 num_chars) const; - /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const; - /*virtual*/ void updateLayout(const class LLTextBase& editor); - /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect); - /*virtuaL*/ S32 getMaxHeight() const; - /*virtual*/ bool canEdit() const { return false; } - /*virtual*/ void unlinkFromDocument(class LLTextBase* editor); - /*virtual*/ void linkToDocument(class LLTextBase* editor); - -private: - LLView* mView; -}; - class LLTextEditor : public LLTextBase, - public LLUICtrl, - private LLEditMenuHandler, protected LLPreeditor { public: - struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> + struct Params : public LLInitParam::Block<Params, LLTextBase::Params> { Optional<std::string> default_text; - Optional<S32> max_text_length; - Optional<bool> read_only, - embedded_items, - word_wrap, + Optional<bool> embedded_items, ignore_tab, - hide_border, - track_bottom, handle_edit_keys_directly, show_line_numbers, commit_on_focus_lost; //colors - Optional<LLUIColor> cursor_color, - default_color, - text_color, - text_readonly_color, - bg_readonly_color, - bg_writeable_color, - bg_focus_color, - link_color; - - Optional<LLViewBorder::Params> border; - - Ignored type, - length, - is_unicode, - hide_scrollbar; + Optional<LLUIColor> default_color; Params(); }; @@ -128,15 +88,6 @@ public: static const llwchar LAST_EMBEDDED_CHAR = 0x10ffff; static const S32 MAX_EMBEDDED_ITEMS = LAST_EMBEDDED_CHAR - FIRST_EMBEDDED_CHAR + 1; - - struct compare_segment_end - { - bool operator()(const LLTextSegmentPtr& a, const LLTextSegmentPtr& b) const - { - return a->getEnd() < b->getEnd(); - } - }; - virtual ~LLTextEditor(); typedef boost::signals2::signal<void (LLTextEditor* caller)> keystroke_signal_t; @@ -155,14 +106,9 @@ public: virtual BOOL handleKeyHere(KEY key, MASK mask ); virtual BOOL handleUnicodeCharHere(llwchar uni_char); - virtual BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect); - virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, void *cargo_data, - EAcceptance *accept, std::string& tooltip_msg); virtual void onMouseCaptureLost(); // view overrides - virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); virtual void draw(); virtual void onFocusReceived(); virtual void onFocusLost(); @@ -170,11 +116,8 @@ public: virtual void setEnabled(BOOL enabled); // uictrl overrides - virtual void clear(); virtual void setFocus( BOOL b ); - virtual BOOL acceptsTextInput() const; virtual BOOL isDirty() const; - virtual void setValue(const LLSD& value); // LLEditMenuHandler interface virtual void undo(); @@ -201,12 +144,9 @@ public: virtual void deselect(); virtual BOOL canDeselect() const; - virtual void onValueChange(S32 start, S32 end); - void selectNext(const std::string& search_text_in, BOOL case_insensitive, BOOL wrap = TRUE); BOOL replaceText(const std::string& search_text, const std::string& replace_text, BOOL case_insensitive, BOOL wrap = TRUE); void replaceTextAll(const std::string& search_text, const std::string& replace_text, BOOL case_insensitive); - BOOL hasSelection() const { return (mSelectionStart !=mSelectionEnd); } void replaceUrlLabel(const std::string &url, const std::string &label); // Undo/redo stack @@ -216,9 +156,6 @@ public: virtual void makePristine(); BOOL isPristine() const; BOOL allowsEmbeddedItems() const { return mAllowEmbeddedItems; } - S32 getLength() const { return getWText().length(); } - void setReadOnly(bool read_only) { mReadOnly = read_only; } - bool getReadOnly() { return mReadOnly; } // // Text manipulation @@ -226,25 +163,10 @@ public: // inserts text at cursor void insertText(const std::string &text); - // appends text at end - void appendText(const std::string &wtext, bool allow_undo, bool prepend_newline, - const LLStyle::Params& style = LLStyle::Params()); - - void appendColoredText(const std::string &wtext, bool allow_undo, - bool prepend_newline, - const LLColor4 &color, - const std::string& font_name = LLStringUtil::null); - // if styled text starts a line, you need to prepend a newline. - void appendStyledText(const std::string &new_text, bool allow_undo, - bool prepend_newline, - const LLStyle::Params& style); - void appendHighlightedText(const std::string &new_text, bool allow_undo, - bool prepend_newline, S32 highlight_part, - const LLStyle::Params& style); + void appendWidget(LLView* widget, const std::string &widget_text, bool allow_undo, bool prepend_newline); // Non-undoable void setText(const LLStringExplicit &utf8str); - void setWText(const LLWString &wtext); // Removes text from the end of document @@ -253,14 +175,9 @@ public: BOOL tryToRevertToPristineState(); - bool setCursor(S32 row, S32 column); - bool setCursorPos(S32 offset, bool keep_cursor_offset = false); void setCursorAndScrollToEnd(); - void getLineAndColumnForPosition( S32 position, S32* line, S32* col, BOOL include_wordwrap ); void getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wordwrap ); - S32 getLineForPosition(S32 position); - S32 getCurrentLine(); void loadKeywords(const std::string& filename, const std::vector<std::string>& funcs, @@ -277,55 +194,17 @@ public: virtual BOOL importBuffer(const char* buffer, S32 length ); virtual BOOL exportBuffer(std::string& buffer ); - const class DocumentPanel* getDocumentPanel() const { return mDocumentPanel; } - const LLUUID& getSourceID() const { return mSourceID; } - // Callbacks - std::string getText() const; - - // Callback for when a Url has been resolved by the server - void onUrlLabelUpdated(const std::string &url, const std::string &label); - - // Getters - LLWString getWText() const; - llwchar getWChar(S32 pos) const { return getWText()[pos]; } - LLWString getWSubString(S32 pos, S32 len) const { return getWText().substr(pos, len); } - - typedef std::vector<LLTextSegmentPtr> segment_vec_t; - const LLTextSegmentPtr getPreviousSegment() const; void getSelectedSegments(segment_vec_t& segments) const; - void getSegmentsInRange(segment_vec_t& segments, S32 start, S32 end, bool include_partial) const; - LLRect getLocalRectFromDocIndex(S32 index) const; - - void addDocumentChild(LLView* view); - void removeDocumentChild(LLView* view); - protected: - // Change cursor - void startOfLine(); - void endOfLine(); - void startOfDoc(); - void endOfDoc(); - void drawPreeditMarker(); - void needsReflow() { mReflowNeeded = TRUE; } - void needsScroll() { mScrollNeeded = TRUE; } - void updateCursorXPos(); - - void updateScrollFromCursor(); - void updateTextRect(); - const LLRect& getTextRect() const { return mTextRect; } - void assignEmbedded(const std::string &s); - BOOL truncate(); // Returns true if truncation occurs void removeCharOrTab(); - void setCursorAtLocalPos(S32 x, S32 y, bool round, bool keep_cursor_offset = false); - /*virtual*/ S32 getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const; void indentSelectedLines( S32 spaces ); S32 indentLine( S32 pos, S32 spaces ); @@ -340,68 +219,21 @@ protected: BOOL handleEditKey(const KEY key, const MASK mask); BOOL selectionContainsLineBreaks(); - void startSelection(); - void endSelection(); void deleteSelection(BOOL transient_operation); S32 prevWordPos(S32 cursorPos) const; S32 nextWordPos(S32 cursorPos) const; - S32 getLineCount() const { return mLineInfoList.size(); } - S32 getLineStart( S32 line ) const; - S32 getLineHeight( S32 line ) const; - void getLineAndOffset(S32 pos, S32* linep, S32* offsetp, bool include_wordwrap = true) const; - S32 getPos(S32 line, S32 offset); - - void changePage(S32 delta); - void changeLine(S32 delta); - void autoIndent(); void findEmbeddedItemSegments(S32 start, S32 end); - void insertSegment(LLTextSegmentPtr segment_to_insert); - + void getSegmentsInRange(segment_vec_t& segments, S32 start, S32 end, bool include_partial) const; + virtual llwchar pasteEmbeddedItem(llwchar ext_char) { return ext_char; } - // Abstract inner base class representing an undoable editor command. - // Concrete sub-classes can be defined for operations such as insert, remove, etc. - // Used as arguments to the execute() method below. - class LLTextCmd - { - public: - LLTextCmd( S32 pos, BOOL group_with_next, LLTextSegmentPtr segment = LLTextSegmentPtr() ) - : mPos(pos), - mGroupWithNext(group_with_next) - { - if (segment.notNull()) - { - mSegments.push_back(segment); - } - } - virtual ~LLTextCmd() {} - virtual BOOL execute(LLTextEditor* editor, S32* delta) = 0; - virtual S32 undo(LLTextEditor* editor) = 0; - virtual S32 redo(LLTextEditor* editor) = 0; - virtual BOOL canExtend(S32 pos) const { return FALSE; } - virtual void blockExtensions() {} - virtual BOOL extendAndExecute( LLTextEditor* editor, S32 pos, llwchar c, S32* delta ) { llassert(0); return 0; } - virtual BOOL hasExtCharValue( llwchar value ) const { return FALSE; } - - // Defined here so they can access protected LLTextEditor editing methods - S32 insert(LLTextEditor* editor, S32 pos, const LLWString &wstr) { return editor->insertStringNoUndo( pos, wstr, &mSegments ); } - S32 remove(LLTextEditor* editor, S32 pos, S32 length) { return editor->removeStringNoUndo( pos, length ); } - S32 overwrite(LLTextEditor* editor, S32 pos, llwchar wc) { return editor->overwriteCharNoUndo(pos, wc); } - - S32 getPosition() const { return mPos; } - BOOL groupWithNext() const { return mGroupWithNext; } - - protected: - const S32 mPos; - BOOL mGroupWithNext; - segment_vec_t mSegments; - }; + // Here's the method that takes and applies text commands. - S32 execute(LLTextCmd* cmd); + S32 execute(TextCmd* cmd); // Undoable operations void addChar(llwchar c); // at mCursorPos @@ -411,15 +243,7 @@ protected: S32 removeChar(S32 pos); S32 insert(S32 pos, const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment); S32 remove(S32 pos, S32 length, bool group_with_next_op); - S32 append(const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment); - // Direct operations - S32 insertStringNoUndo(S32 pos, const LLWString &wstr, segment_vec_t* segments = NULL); // returns num of chars actually inserted - S32 removeStringNoUndo(S32 pos, S32 length); - S32 overwriteCharNoUndo(S32 pos, llwchar wc); - - void resetKeystrokeTimer() { mKeystrokeTimer.reset(); } - void updateAllowingLanguageInput(); BOOL hasPreeditString() const; @@ -432,6 +256,7 @@ protected: virtual void getSelectionRange(S32 *position, S32 *length) const; virtual BOOL getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect *bounds, LLRect *control) const; virtual S32 getPreeditFontSize() const; + virtual LLWString getPreeditString() const { return getWText(); } // // Protected data // @@ -439,74 +264,31 @@ protected: // as possible behind protected accessor methods. // - // I-beam is just after the mCursorPos-th character. - S32 mCursorPos; - // Use these to determine if a click on an embedded item is a drag or not. S32 mMouseDownX; S32 mMouseDownY; - // Are we in the middle of a drag-select? To figure out if there is a current - // selection, call hasSelection(). - BOOL mIsSelecting; - S32 mSelectionStart; - S32 mSelectionEnd; - S32 mLastSelectionX; - S32 mLastSelectionY; - - BOOL mParseHighlights; - - // Scrollbar data - class DocumentPanel* mDocumentPanel; - LLScrollContainer* mScroller; - - void *mOnScrollEndData; - LLWString mPreeditWString; LLWString mPreeditOverwrittenWString; std::vector<S32> mPreeditPositions; std::vector<BOOL> mPreeditStandouts; - S32 mScrollIndex; // index into document that controls default scroll position - protected: - LLUIColor mCursorColor; - LLUIColor mFgColor; - LLUIColor mDefaultColor; - LLUIColor mReadOnlyFgColor; - LLUIColor mWriteableBgColor; - LLUIColor mReadOnlyBgColor; - LLUIColor mFocusBgColor; - LLUIColor mLinkColor; + LLUIColor mDefaultColor; - BOOL mReadOnly; - BOOL mShowLineNumbers; + BOOL mShowLineNumbers; - void updateSegments(); - void updateLinkSegments(); + /*virtual*/ void updateSegments(); + void updateLinkSegments(); private: - // // Methods // void pasteHelper(bool is_primary); - virtual LLTextViewModel* getViewModel() const; - void reflow(S32 startpos = 0); - - void createDefaultSegment(); - LLStyleSP getDefaultStyle(); - S32 getEditableIndex(S32 index, bool increasing_direction); - - void drawBackground(); - void drawSelectionBackground(); - void drawCursor(); - void drawText(); void drawLineNumbers(); - S32 getFirstVisibleLine() const; - void onKeyStroke(); // @@ -514,56 +296,25 @@ private: // LLKeywords mKeywords; - // Concrete LLTextCmd sub-classes used by the LLTextEditor base class - class LLTextCmdInsert; - class LLTextCmdAddChar; - class LLTextCmdOverwriteChar; - class LLTextCmdRemove; - - S32 mMaxTextByteLength; // Maximum length mText is allowed to be in bytes + // Concrete TextCmd sub-classes used by the LLTextEditor base class + class TextCmdInsert; + class TextCmdAddChar; + class TextCmdOverwriteChar; + class TextCmdRemove; class LLViewBorder* mBorder; BOOL mBaseDocIsPristine; - LLTextCmd* mPristineCmd; + TextCmd* mPristineCmd; - LLTextCmd* mLastCmd; + TextCmd* mLastCmd; - typedef std::deque<LLTextCmd*> undo_stack_t; + typedef std::deque<TextCmd*> undo_stack_t; undo_stack_t mUndoStack; - S32 mDesiredXPixel; // X pixel position where the user wants the cursor to be - LLRect mTextRect; // The rect in which text is drawn. Excludes borders. - // List of offsets and segment index of the start of each line. Always has at least one node (0). - struct line_info - { - line_info(S32 index_start, S32 index_end, S32 top, S32 bottom, S32 line_num) - : mDocIndexStart(index_start), - mDocIndexEnd(index_end), - mTop(top), - mBottom(bottom), - mLineNum(line_num) - {} - S32 mDocIndexStart; - S32 mDocIndexEnd; - S32 mTop; - S32 mBottom; - S32 mLineNum; // actual line count (ignoring soft newlines due to word wrap) - }; - struct compare_bottom; - struct compare_top; - struct line_end_compare; - typedef std::vector<line_info> line_list_t; - line_list_t mLineInfoList; - BOOL mReflowNeeded; - BOOL mScrollNeeded; - - LLFrameTimer mKeystrokeTimer; - BOOL mTabsToNextField; // if true, tab moves focus to next field, else inserts spaces BOOL mCommitOnFocusLost; BOOL mTakesFocus; - BOOL mTrackBottom; // if true, keeps scroll position at bottom during resize BOOL mAllowEmbeddedItems; diff --git a/indra/llui/lltextparser.cpp b/indra/llui/lltextparser.cpp index 707dd0afdd..76a39e3094 100644 --- a/indra/llui/lltextparser.cpp +++ b/indra/llui/lltextparser.cpp @@ -103,7 +103,7 @@ S32 LLTextParser::findPattern(const std::string &text, LLSD highlight) return found; } -LLSD LLTextParser::parsePartialLineHighlights(const std::string &text, const LLColor4 &color, S32 part, S32 index) +LLSD LLTextParser::parsePartialLineHighlights(const std::string &text, const LLColor4 &color, EHighlightPosition part, S32 index) { //evil recursive string atomizer. LLSD ret_llsd, start_llsd, middle_llsd, end_llsd; @@ -122,7 +122,7 @@ LLSD LLTextParser::parsePartialLineHighlights(const std::string &text, const LLC { S32 end = std::string(mHighlights[i]["pattern"]).length(); S32 len = text.length(); - S32 newpart; + EHighlightPosition newpart; if (start==0) { start_llsd[0]["text"] =text.substr(0,end); diff --git a/indra/llui/lltextparser.h b/indra/llui/lltextparser.h index fb1a7758b7..072ac0f300 100644 --- a/indra/llui/lltextparser.h +++ b/indra/llui/lltextparser.h @@ -34,8 +34,6 @@ #ifndef LL_LLTEXTPARSER_H #define LL_LLTEXTPARSER_H -#include "lltextparser.h" - #include "llsd.h" class LLUUID; @@ -45,17 +43,17 @@ class LLColor4; class LLTextParser { public: - enum ConditionType { CONTAINS, MATCHES, STARTS_WITH, ENDS_WITH }; - enum HighlightType { PART, ALL }; - enum HighlightPosition { WHOLE, START, MIDDLE, END }; - enum DialogAction { ACTION_NONE, ACTION_CLOSE, ACTION_ADD, ACTION_COPY, ACTION_UPDATE }; + typedef enum e_condition_type { CONTAINS, MATCHES, STARTS_WITH, ENDS_WITH } EConditionType; + typedef enum e_highlight_type { PART, ALL } EHighlightType; + typedef enum e_highlight_position { WHOLE, START, MIDDLE, END } EHighlightPosition; + typedef enum e_dialog_action { ACTION_NONE, ACTION_CLOSE, ACTION_ADD, ACTION_COPY, ACTION_UPDATE } EDialogAction; static LLTextParser* getInstance(); LLTextParser(){}; ~LLTextParser(); S32 findPattern(const std::string &text, LLSD highlight); - LLSD parsePartialLineHighlights(const std::string &text,const LLColor4 &color,S32 part=WHOLE, S32 index=0); + LLSD parsePartialLineHighlights(const std::string &text,const LLColor4 &color, EHighlightPosition part=WHOLE, S32 index=0); bool parseFullLineHighlights(const std::string &text, LLColor4 *color); std::string getFileName(); diff --git a/indra/llui/lltoggleablemenu.cpp b/indra/llui/lltoggleablemenu.cpp new file mode 100644 index 0000000000..717e135412 --- /dev/null +++ b/indra/llui/lltoggleablemenu.cpp @@ -0,0 +1,82 @@ +/** + * @file lltoggleablemenu.cpp + * @brief Menu toggled by a button press + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + + +#include "linden_common.h" + +#include "lltoggleablemenu.h" +#include "lluictrlfactory.h" + +static LLDefaultChildRegistry::Register<LLToggleableMenu> r("toggleable_menu"); + +LLToggleableMenu::LLToggleableMenu(const LLToggleableMenu::Params& p) +: LLMenuGL(p), + mClosedByButtonClick(false) +{ +} + +// virtual +void LLToggleableMenu::handleVisibilityChange (BOOL curVisibilityIn) +{ + S32 x,y; + LLUI::getMousePositionLocal(LLUI::getRootView(), &x, &y); + + if (!curVisibilityIn && mButtonRect.pointInRect(x, y)) + { + mClosedByButtonClick = true; + } +} + +void LLToggleableMenu::setButtonRect(const LLRect& rect, LLView* current_view) +{ + LLRect screen; + current_view->localRectToScreen(rect, &screen); + mButtonRect = screen; +} + +bool LLToggleableMenu::toggleVisibility() +{ + if (mClosedByButtonClick) + { + mClosedByButtonClick = false; + return false; + } + + if (getVisible()) + { + setVisible(FALSE); + mClosedByButtonClick = false; + return false; + } + + return true; +} diff --git a/indra/llui/lltoggleablemenu.h b/indra/llui/lltoggleablemenu.h new file mode 100644 index 0000000000..3cd66e04a8 --- /dev/null +++ b/indra/llui/lltoggleablemenu.h @@ -0,0 +1,65 @@ +/** + * @file lltoggleablemenu.h + * @brief Menu toggled by a button press + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLTOGGLEABLEMENU_H +#define LL_LLTOGGLEABLEMENU_H + +#include "llmenugl.h" + +class LLToggleableMenu : public LLMenuGL +{ +public: + //adding blank params to work around registration issue + //where LLToggleableMenu was owning the LLMenuGL param + //and menu.xml was never loaded + struct Params : public LLInitParam::Block<Params, LLMenuGL::Params> + {}; +protected: + LLToggleableMenu(const Params&); + friend class LLUICtrlFactory; +public: + virtual void handleVisibilityChange (BOOL curVisibilityIn); + + // Converts the given local button rect to a screen rect + void setButtonRect(const LLRect& rect, LLView* current_view); + + // Returns "true" if menu was not closed by button click + // and is not still visible. If menu is visible toggles + // its visibility off. + bool toggleVisibility(); + +protected: + bool mClosedByButtonClick; + LLRect mButtonRect; +}; + +#endif // LL_LLTOGGLEABLEMENU_H diff --git a/indra/llui/lltooltip.cpp b/indra/llui/lltooltip.cpp index 5c017dabd7..8f5c029816 100644 --- a/indra/llui/lltooltip.cpp +++ b/indra/llui/lltooltip.cpp @@ -36,7 +36,6 @@ #include "lltooltip.h" // Library includes -#include "llpanel.h" #include "lltextbox.h" #include "lliconctrl.h" #include "llui.h" // positionViewNearMouse() @@ -45,7 +44,6 @@ // // Constants // -const F32 DELAY_BEFORE_SHOW_TIP = 0.35f; // // Local globals @@ -57,6 +55,11 @@ LLToolTipView *gToolTipView = NULL; // Member functions // +LLToolTipView::Params::Params() +{ + mouse_opaque = false; +} + LLToolTipView::LLToolTipView(const LLToolTipView::Params& p) : LLView(p) { @@ -64,10 +67,7 @@ LLToolTipView::LLToolTipView(const LLToolTipView::Params& p) void LLToolTipView::draw() { - if (LLUI::getWindow()->isCursorHidden() ) - { - LLToolTipMgr::instance().hideToolTips(); - } + LLToolTipMgr::instance().updateToolTipVisibility(); // do the usual thing LLView::draw(); @@ -80,17 +80,10 @@ BOOL LLToolTipView::handleHover(S32 x, S32 y, MASK mask) LLToolTipMgr& tooltip_mgr = LLToolTipMgr::instance(); - // hide existing tooltips when mouse moves out of sticky rect - if (tooltip_mgr.toolTipVisible() - && !tooltip_mgr.getStickyRect().pointInRect(x, y)) - { - tooltip_mgr.hideToolTips(); - } - - // allow new tooltips whenever mouse moves if (x != last_x && y != last_y) { - tooltip_mgr.enableToolTips(); + // allow new tooltips because mouse moved + tooltip_mgr.unblockToolTips(); } last_x = x; @@ -100,96 +93,84 @@ BOOL LLToolTipView::handleHover(S32 x, S32 y, MASK mask) BOOL LLToolTipView::handleMouseDown(S32 x, S32 y, MASK mask) { - LLToolTipMgr::instance().hideToolTips(); + LLToolTipMgr::instance().blockToolTips(); return LLView::handleMouseDown(x, y, mask); } BOOL LLToolTipView::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { - LLToolTipMgr::instance().hideToolTips(); + LLToolTipMgr::instance().blockToolTips(); return LLView::handleMiddleMouseDown(x, y, mask); } BOOL LLToolTipView::handleRightMouseDown(S32 x, S32 y, MASK mask) { - LLToolTipMgr::instance().hideToolTips(); + LLToolTipMgr::instance().blockToolTips(); return LLView::handleRightMouseDown(x, y, mask); } BOOL LLToolTipView::handleScrollWheel( S32 x, S32 y, S32 clicks ) { - LLToolTipMgr::instance().hideToolTips(); + LLToolTipMgr::instance().blockToolTips(); return FALSE; } void LLToolTipView::onMouseLeave(S32 x, S32 y, MASK mask) { - LLToolTipMgr::instance().hideToolTips(); + LLToolTipMgr::instance().blockToolTips(); } void LLToolTipView::drawStickyRect() { - gl_rect_2d(LLToolTipMgr::instance().getStickyRect(), LLColor4::white, false); + gl_rect_2d(LLToolTipMgr::instance().getMouseNearRect(), LLColor4::white, false); } // // LLToolTip // -class LLToolTip : public LLPanel -{ -public: - struct Params : public LLInitParam::Block<Params, LLPanel::Params> - { - Mandatory<F32> visible_time; - - Optional<LLToolTipParams::click_callback_t> click_callback; - Optional<LLUIImage*> image; - - Params() - { - //use_bounding_rect = true; - } - }; - /*virtual*/ void draw(); - /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); - - /*virtual*/ void setValue(const LLSD& value); - /*virtual*/ void setVisible(BOOL visible); - bool isFading() { return mFadeTimer.getStarted(); } - - LLToolTip(const Params& p); - -private: - LLTextBox* mTextBox; - LLFrameTimer mFadeTimer; - F32 mVisibleTime; - bool mHasClickCallback; -}; static LLDefaultChildRegistry::Register<LLToolTip> r("tool_tip"); -const S32 TOOLTIP_PADDING = 4; + +LLToolTip::Params::Params() +: max_width("max_width", 200), + padding("padding", 4), + pos("pos"), + message("message"), + delay_time("delay_time", LLUI::sSettingGroups["config"]->getF32( "ToolTipDelay" )), + visible_time_over("visible_time_over", LLUI::sSettingGroups["config"]->getF32( "ToolTipVisibleTimeOver" )), + visible_time_near("visible_time_near", LLUI::sSettingGroups["config"]->getF32( "ToolTipVisibleTimeNear" )), + visible_time_far("visible_time_far", LLUI::sSettingGroups["config"]->getF32( "ToolTipVisibleTimeFar" )), + sticky_rect("sticky_rect"), + image("image") +{ + name = "tooltip"; + font = LLFontGL::getFontSansSerif(); + bg_opaque_color = LLUIColorTable::instance().getColor( "ToolTipBgColor" ); + background_visible = true; +} LLToolTip::LLToolTip(const LLToolTip::Params& p) : LLPanel(p), - mVisibleTime(p.visible_time), - mHasClickCallback(p.click_callback.isProvided()) + mMaxWidth(p.max_width), + mHasClickCallback(p.click_callback.isProvided()), + mPadding(p.padding) { LLTextBox::Params params; - params.text = "tip_text"; - params.name = params.text; + params.initial_value = "tip_text"; + params.name = params.initial_value().asString(); // bake textbox padding into initial rect - params.rect = LLRect (TOOLTIP_PADDING, TOOLTIP_PADDING + 1, TOOLTIP_PADDING + 1, TOOLTIP_PADDING); + params.rect = LLRect (mPadding, mPadding + 1, mPadding + 1, mPadding); params.follows.flags = FOLLOWS_ALL; - params.h_pad = 4; - params.v_pad = 2; + params.h_pad = 0; + params.v_pad = 0; params.mouse_opaque = false; params.text_color = LLUIColorTable::instance().getColor( "ToolTipTextColor" ); params.bg_visible = false; - params.font.style = "NORMAL"; - //params.border_drop_shadow_visible = true; + params.font = p.font; + params.use_ellipses = true; mTextBox = LLUICtrlFactory::create<LLTextBox> (params); addChild(mTextBox); @@ -198,8 +179,9 @@ LLToolTip::LLToolTip(const LLToolTip::Params& p) LLIconCtrl::Params icon_params; icon_params.name = "tooltip_icon"; LLRect icon_rect; - const S32 TOOLTIP_ICON_SIZE = 18; - icon_rect.setOriginAndSize(TOOLTIP_PADDING, TOOLTIP_PADDING, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE); + LLUIImage* imagep = p.image; + const S32 TOOLTIP_ICON_SIZE = (imagep ? imagep->getWidth() : 16); + icon_rect.setOriginAndSize(mPadding, mPadding, TOOLTIP_ICON_SIZE, TOOLTIP_ICON_SIZE); icon_params.rect = icon_rect; icon_params.follows.flags = FOLLOWS_LEFT | FOLLOWS_BOTTOM; icon_params.image = p.image; @@ -218,13 +200,20 @@ LLToolTip::LLToolTip(const LLToolTip::Params& p) void LLToolTip::setValue(const LLSD& value) { - mTextBox->setWrappedText(value.asString()); - mTextBox->reshapeToFitText(); + const S32 REALLY_LARGE_HEIGHT = 10000; + reshape(mMaxWidth, REALLY_LARGE_HEIGHT); + + mTextBox->setValue(value); + + LLRect text_contents_rect = mTextBox->getContentsRect(); + S32 text_width = llmin(mMaxWidth, text_contents_rect.getWidth()); + S32 text_height = text_contents_rect.getHeight(); + mTextBox->reshape(text_width, text_height); // reshape tooltip panel to fit text box LLRect tooltip_rect = calcBoundingRect(); - tooltip_rect.mTop += TOOLTIP_PADDING; - tooltip_rect.mRight += TOOLTIP_PADDING; + tooltip_rect.mTop += mPadding; + tooltip_rect.mRight += mPadding; tooltip_rect.mBottom = 0; tooltip_rect.mLeft = 0; @@ -234,19 +223,21 @@ void LLToolTip::setValue(const LLSD& value) void LLToolTip::setVisible(BOOL visible) { // fade out tooltip over time - if (!visible) + if (visible) + { + mVisibleTimer.start(); + mFadeTimer.stop(); + LLPanel::setVisible(TRUE); + } + else { + mVisibleTimer.stop(); // don't actually change mVisible state, start fade out transition instead if (!mFadeTimer.getStarted()) { mFadeTimer.start(); } } - else - { - mFadeTimer.stop(); - LLPanel::setVisible(TRUE); - } } BOOL LLToolTip::handleHover(S32 x, S32 y, MASK mask) @@ -263,11 +254,6 @@ void LLToolTip::draw() { F32 alpha = 1.f; - if (LLUI::getMouseIdleTime() > mVisibleTime) - { - LLToolTipMgr::instance().hideToolTips(); - } - if (mFadeTimer.getStarted()) { F32 tool_tip_fade_time = LLUI::sSettingGroups["config"]->getF32("ToolTipFadeTime"); @@ -287,94 +273,90 @@ void LLToolTip::draw() } } +bool LLToolTip::isFading() +{ + return mFadeTimer.getStarted(); +} + +F32 LLToolTip::getVisibleTime() +{ + return mVisibleTimer.getStarted() ? mVisibleTimer.getElapsedTimeF32() : 0.f; +} + +bool LLToolTip::hasClickCallback() +{ + return mHasClickCallback; +} // // LLToolTipMgr // -LLToolTipParams::LLToolTipParams() -: pos("pos"), - message("message"), - delay_time("delay_time", LLUI::sSettingGroups["config"]->getF32( "ToolTipDelay" )), - visible_time("visible_time", LLUI::sSettingGroups["config"]->getF32( "ToolTipVisibleTime" )), - sticky_rect("sticky_rect"), - width("width", 200), - image("image") -{} LLToolTipMgr::LLToolTipMgr() -: mToolTip(NULL) -{ -} +: mToolTip(NULL), + mNeedsToolTip(false) +{} -LLToolTip* LLToolTipMgr::createToolTip(const LLToolTipParams& params) +void LLToolTipMgr::createToolTip(const LLToolTip::Params& params) { - S32 mouse_x; - S32 mouse_y; - LLUI::getMousePositionLocal(gToolTipView->getParent(), &mouse_x, &mouse_y); + // block all other tooltips until tooltips re-enabled (e.g. mouse moved) + blockToolTips(); + delete mToolTip; - LLToolTip::Params tooltip_params; - tooltip_params.name = "tooltip"; - tooltip_params.mouse_opaque = true; + LLToolTip::Params tooltip_params(params); + // block mouse events if there is a click handler registered (specifically, hover) + tooltip_params.mouse_opaque = params.click_callback.isProvided(); tooltip_params.rect = LLRect (0, 1, 1, 0); - tooltip_params.bg_opaque_color = LLUIColorTable::instance().getColor( "ToolTipBgColor" ); - tooltip_params.background_visible = true; - tooltip_params.visible_time = params.visible_time; - if (params.image.isProvided()) - { - tooltip_params.image = params.image; - } - if (params.click_callback.isProvided()) - { - tooltip_params.click_callback = params.click_callback; - } - - LLToolTip* tooltip = LLUICtrlFactory::create<LLToolTip> (tooltip_params); - // make tooltip fixed width and tall enough to fit text - tooltip->reshape(params.width, 2000); - tooltip->setValue(params.message()); - gToolTipView->addChild(tooltip); + mToolTip = LLUICtrlFactory::create<LLToolTip> (tooltip_params); + mToolTip->setValue(params.message()); + gToolTipView->addChild(mToolTip); if (params.pos.isProvided()) { + LLCoordGL pos = params.pos; // try to spawn at requested position - LLUI::positionViewNearMouse(tooltip, params.pos.x, params.pos.y); + LLUI::positionViewNearMouse(mToolTip, pos.mX, pos.mY); } else { // just spawn at mouse location - LLUI::positionViewNearMouse(tooltip); + LLUI::positionViewNearMouse(mToolTip); } //...update "sticky" rect and tooltip position if (params.sticky_rect.isProvided()) { - mToolTipStickyRect = params.sticky_rect; + mMouseNearRect = params.sticky_rect; } else { - // otherwise just use one pixel rect around mouse cursor - mToolTipStickyRect.setOriginAndSize(mouse_x, mouse_y, 1, 1); + S32 mouse_x; + S32 mouse_y; + LLUI::getMousePositionLocal(gToolTipView->getParent(), &mouse_x, &mouse_y); + + // allow mouse a little bit of slop before changing tooltips + mMouseNearRect.setCenterAndSize(mouse_x, mouse_y, 3, 3); } - - if (params.click_callback.isProvided()) + + // allow mouse to move all the way to the tooltip without changing tooltips + // (tooltip can still time out) + if (mToolTip->hasClickCallback()) { // keep tooltip up when we mouse over it - mToolTipStickyRect.unionWith(tooltip->getRect()); + mMouseNearRect.unionWith(mToolTip->getRect()); } - - return tooltip; } void LLToolTipMgr::show(const std::string& msg) { - show(LLToolTipParams().message(msg)); + show(LLToolTip::Params().message(msg)); } -void LLToolTipMgr::show(const LLToolTipParams& params) +void LLToolTipMgr::show(const LLToolTip::Params& params) { if (!params.validateBlock()) { @@ -382,38 +364,42 @@ void LLToolTipMgr::show(const LLToolTipParams& params) return; } - bool tooltip_shown = mToolTip - && mToolTip->getVisible() - && !mToolTip->isFading(); - - // if tooltip contents change, hide existing tooltip - if (tooltip_shown && mLastToolTipMessage != params.message()) - { - hideToolTips(); - } + S32 mouse_x; + S32 mouse_y; + LLUI::getMousePositionLocal(gToolTipView, &mouse_x, &mouse_y); + // are we ready to show the tooltip? if (!mToolTipsBlocked // we haven't hit a key, moved the mouse, etc. - && LLUI::getMouseIdleTime() > params.delay_time // the mouse has been still long enough - && !tooltip_shown) // tooltip not visible + && LLUI::getMouseIdleTime() > params.delay_time) // the mouse has been still long enough { - // create new tooltip at mouse cursor position - delete mToolTip; - mToolTip = createToolTip(params); + bool tooltip_changed = mLastToolTipParams.message() != params.message() + || mLastToolTipParams.pos() != params.pos(); + + bool tooltip_shown = mToolTip + && mToolTip->getVisible() + && !mToolTip->isFading(); - // remember this tooltip so we know when it changes - mLastToolTipMessage = params.message(); + mNeedsToolTip = tooltip_changed || !tooltip_shown; + // store description of tooltip for later creation + mNextToolTipParams = params; } } // allow new tooltips to be created, e.g. after mouse has moved -void LLToolTipMgr::enableToolTips() +void LLToolTipMgr::unblockToolTips() { mToolTipsBlocked = false; } +// disallow new tooltips until unblockTooltips called +void LLToolTipMgr::blockToolTips() +{ + hideToolTips(); + mToolTipsBlocked = true; +} + void LLToolTipMgr::hideToolTips() { - mToolTipsBlocked = true; if (mToolTip) { mToolTip->setVisible(FALSE); @@ -422,7 +408,7 @@ void LLToolTipMgr::hideToolTips() bool LLToolTipMgr::toolTipVisible() { - return mToolTip ? mToolTip->getVisible() : false; + return mToolTip ? mToolTip->isInVisibleChain() : false; } LLRect LLToolTipMgr::getToolTipRect() @@ -435,11 +421,63 @@ LLRect LLToolTipMgr::getToolTipRect() } -LLRect LLToolTipMgr::getStickyRect() +LLRect LLToolTipMgr::getMouseNearRect() { - if (!mToolTip) return LLRect(); + return toolTipVisible() ? mMouseNearRect : LLRect(); +} + +// every frame, determine if current tooltip should be hidden +void LLToolTipMgr::updateToolTipVisibility() +{ + // create new tooltip if we have one ready to go + if (mNeedsToolTip) + { + mNeedsToolTip = false; + createToolTip(mNextToolTipParams); + mLastToolTipParams = mNextToolTipParams; + + return; + } - return mToolTip->isInVisibleChain() ? mToolTipStickyRect : LLRect(); + // hide tooltips when mouse cursor is hidden + if (LLUI::getWindow()->isCursorHidden()) + { + blockToolTips(); + return; + } + + // hide existing tooltips if they have timed out + S32 mouse_x, mouse_y; + LLUI::getMousePositionLocal(gToolTipView, &mouse_x, &mouse_y); + + F32 tooltip_timeout = 0.f; + if (toolTipVisible()) + { + // mouse far away from tooltip + tooltip_timeout = mLastToolTipParams.visible_time_far; + // mouse near rect will only include the tooltip if the + // tooltip is clickable + if (mMouseNearRect.pointInRect(mouse_x, mouse_y)) + { + // mouse "close" to tooltip + tooltip_timeout = mLastToolTipParams.visible_time_near; + + // if tooltip is clickable (has large mMouseNearRect) + // than having cursor over tooltip keeps it up indefinitely + if (mToolTip->parentPointInView(mouse_x, mouse_y)) + { + // mouse over tooltip itself, don't time out + tooltip_timeout = mLastToolTipParams.visible_time_over; + } + } + + if (mToolTip->getVisibleTime() > tooltip_timeout) + { + hideToolTips(); + } + } } + + // EOF diff --git a/indra/llui/lltooltip.h b/indra/llui/lltooltip.h index fb7f942099..6715da1611 100644 --- a/indra/llui/lltooltip.h +++ b/indra/llui/lltooltip.h @@ -36,7 +36,7 @@ // Library includes #include "llsingleton.h" #include "llinitparam.h" -#include "llview.h" +#include "llpanel.h" // // Classes @@ -46,10 +46,7 @@ class LLToolTipView : public LLView public: struct Params : public LLInitParam::Block<Params, LLView::Params> { - Params() - { - mouse_opaque = false; - } + Params(); }; LLToolTipView(const LLToolTipView::Params&); /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); @@ -65,57 +62,82 @@ public: /*virtual*/ void draw(); }; -struct LLToolTipPosParams : public LLInitParam::Block<LLToolTipPosParams> +class LLToolTip : public LLPanel { - Mandatory<S32> x, - y; - LLToolTipPosParams() - : x("x"), - y("y") - {} -}; +public: + struct Params : public LLInitParam::Block<Params, LLPanel::Params> + { + typedef boost::function<void(void)> click_callback_t; -struct LLToolTipParams : public LLInitParam::Block<LLToolTipParams> -{ - typedef boost::function<void(void)> click_callback_t; + Mandatory<std::string> message; + + Optional<LLCoordGL> pos; + Optional<F32> delay_time, + visible_time_over, // time for which tooltip is visible while mouse on it + visible_time_near, // time for which tooltip is visible while mouse near it + visible_time_far; // time for which tooltip is visible while mouse moved away + Optional<LLRect> sticky_rect; + Optional<const LLFontGL*> font; - Mandatory<std::string> message; - - Optional<LLToolTipPosParams> pos; - Optional<F32> delay_time, - visible_time; - Optional<LLRect> sticky_rect; - Optional<S32> width; - Optional<LLUIImage*> image; + Optional<click_callback_t> click_callback; + Optional<LLUIImage*> image; + Optional<S32> max_width; + Optional<S32> padding; + + Params(); + }; + /*virtual*/ void draw(); + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); - Optional<click_callback_t> click_callback; + /*virtual*/ void setValue(const LLSD& value); + /*virtual*/ void setVisible(BOOL visible); - LLToolTipParams(); - LLToolTipParams(const std::string& message); + bool isFading(); + F32 getVisibleTime(); + bool hasClickCallback(); + + LLToolTip(const Params& p); + +private: + class LLTextBox* mTextBox; + LLFrameTimer mFadeTimer; + LLFrameTimer mVisibleTimer; + S32 mMaxWidth; + bool mHasClickCallback; + S32 mPadding; // pixels }; + class LLToolTipMgr : public LLSingleton<LLToolTipMgr> { LOG_CLASS(LLToolTipMgr); public: LLToolTipMgr(); - void show(const LLToolTipParams& params); + void show(const LLToolTip::Params& params); void show(const std::string& message); - void enableToolTips(); + void unblockToolTips(); + void blockToolTips(); + void hideToolTips(); bool toolTipVisible(); LLRect getToolTipRect(); - - LLRect getStickyRect(); + LLRect getMouseNearRect(); + void updateToolTipVisibility(); private: - class LLToolTip* createToolTip(const LLToolTipParams& params); + void createToolTip(const LLToolTip::Params& params); bool mToolTipsBlocked; class LLToolTip* mToolTip; - std::string mLastToolTipMessage; - LLRect mToolTipStickyRect; + + // tooltip creation is deferred until the UI is drawn every frame + // so the last tooltip to be created in a given frame will win + LLToolTip::Params mLastToolTipParams; // description of last tooltip we showed + LLToolTip::Params mNextToolTipParams; // description of next tooltip we want to show + bool mNeedsToolTip; // do we want to show a tooltip + + LLRect mMouseNearRect; }; // diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index d5b67f53b7..c89e5944fa 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -55,6 +55,7 @@ #include "llfloater.h" #include "llfloaterreg.h" #include "llmenugl.h" +#include "llmenubutton.h" #include "llwindow.h" // for registration @@ -90,6 +91,7 @@ std::list<std::string> gUntranslated; static LLDefaultChildRegistry::Register<LLFilterEditor> register_filter_editor("filter_editor"); static LLDefaultChildRegistry::Register<LLFlyoutButton> register_flyout_button("flyout_button"); static LLDefaultChildRegistry::Register<LLSearchEditor> register_search_editor("search_editor"); +static LLDefaultChildRegistry::Register<LLMenuButton> register_menu_button("menu_button"); // @@ -1644,6 +1646,17 @@ void LLUI::setMousePositionScreen(S32 x, S32 y) } //static +void LLUI::getMousePositionScreen(S32 *x, S32 *y) +{ + LLCoordWindow cursor_pos_window; + getWindow()->getCursorPosition(&cursor_pos_window); + LLCoordGL cursor_pos_gl; + getWindow()->convertCoords(cursor_pos_window, &cursor_pos_gl); + *x = llround((F32)cursor_pos_gl.mX / sGLScaleFactor.mV[VX]); + *y = llround((F32)cursor_pos_gl.mY / sGLScaleFactor.mV[VX]); +} + +//static void LLUI::setMousePositionLocal(const LLView* viewp, S32 x, S32 y) { S32 screen_x, screen_y; @@ -1655,15 +1668,12 @@ void LLUI::setMousePositionLocal(const LLView* viewp, S32 x, S32 y) //static void LLUI::getMousePositionLocal(const LLView* viewp, S32 *x, S32 *y) { - LLCoordWindow cursor_pos_window; - LLView::getWindow()->getCursorPosition(&cursor_pos_window); - LLCoordGL cursor_pos_gl; - LLView::getWindow()->convertCoords(cursor_pos_window, &cursor_pos_gl); - cursor_pos_gl.mX = llround((F32)cursor_pos_gl.mX / LLUI::sGLScaleFactor.mV[VX]); - cursor_pos_gl.mY = llround((F32)cursor_pos_gl.mY / LLUI::sGLScaleFactor.mV[VY]); - viewp->screenPointToLocal(cursor_pos_gl.mX, cursor_pos_gl.mY, x, y); + S32 screen_x, screen_y; + getMousePositionScreen(&screen_x, &screen_y); + viewp->screenPointToLocal(screen_x, screen_y, x, y); } + // On Windows, the user typically sets the language when they install the // app (by running it with a shortcut that sets InstallLanguage). On Mac, // or on Windows if the SecondLife.exe executable is run directly, the @@ -1835,14 +1845,14 @@ LLControlGroup& LLUI::getControlControlGroup (const std::string& controlname) // spawn_x and spawn_y are top left corner of view in screen GL coordinates void LLUI::positionViewNearMouse(LLView* view, S32 spawn_x, S32 spawn_y) { - const S32 CURSOR_HEIGHT = 22; // Approximate "normal" cursor size - const S32 CURSOR_WIDTH = 12; + const S32 CURSOR_HEIGHT = 18; // Approximate "normal" cursor size + const S32 CURSOR_WIDTH = 9; LLView* parent = view->getParent(); S32 mouse_x; S32 mouse_y; - LLUI::getMousePositionLocal(parent, &mouse_x, &mouse_y); + LLUI::getMousePositionScreen(&mouse_x, &mouse_y); // If no spawn location provided, use mouse position if (spawn_x == S32_MAX || spawn_y == S32_MAX) @@ -1856,12 +1866,13 @@ void LLUI::positionViewNearMouse(LLView* view, S32 spawn_x, S32 spawn_y) LLRect mouse_rect; const S32 MOUSE_CURSOR_PADDING = 5; mouse_rect.setLeftTopAndSize(mouse_x - MOUSE_CURSOR_PADDING, - mouse_y + MOUSE_CURSOR_PADDING, - CURSOR_WIDTH + MOUSE_CURSOR_PADDING * 2, - CURSOR_HEIGHT + MOUSE_CURSOR_PADDING * 2); + mouse_y + MOUSE_CURSOR_PADDING, + CURSOR_WIDTH + MOUSE_CURSOR_PADDING * 2, + CURSOR_HEIGHT + MOUSE_CURSOR_PADDING * 2); S32 local_x, local_y; - view->getParent()->screenPointToLocal(spawn_x, spawn_y, &local_x, &local_y); + // convert screen coordinates to tooltipview-local coordinates + parent->screenPointToLocal(spawn_x, spawn_y, &local_x, &local_y); // Start at spawn position (using left/top) view->setOrigin( local_x, local_y - view->getRect().getHeight()); @@ -1915,12 +1926,14 @@ namespace LLInitParam } }; - TypedParam<const LLFontGL*>::TypedParam(BlockDescriptor& descriptor, const char* name, const LLFontGL*const value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count) - : super_t(descriptor, name, value, func, min_count, max_count), - name(""), + TypedParam<const LLFontGL*>::TypedParam(BlockDescriptor& descriptor, const char* _name, const LLFontGL*const value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count) + : super_t(descriptor, _name, value, func, min_count, max_count), + name("name"), size("size"), style("style") - {} + { + addSynonym(name, ""); + } const LLFontGL* TypedParam<const LLFontGL*>::getValueFromBlock() const { diff --git a/indra/llui/llui.h b/indra/llui/llui.h index 86cb516500..f071e8dc47 100644 --- a/indra/llui/llui.h +++ b/indra/llui/llui.h @@ -190,6 +190,7 @@ public: static void setRootView(LLView* view) { sRootView = view; } static std::string locateSkin(const std::string& filename); static void setMousePositionScreen(S32 x, S32 y); + static void getMousePositionScreen(S32 *x, S32 *y); static void setMousePositionLocal(const LLView* viewp, S32 x, S32 y); static void getMousePositionLocal(const LLView* viewp, S32 *x, S32 *y); static void setScaleFactor(const LLVector2& scale_factor); @@ -409,8 +410,8 @@ namespace LLInitParam { typedef BlockValue<const LLFontGL*> super_t; public: - Optional<std::string> name, - size, + Mandatory<std::string> name; + Optional<std::string> size, style; TypedParam(BlockDescriptor& descriptor, const char* name, const LLFontGL* const value, ParamDescriptor::validation_func_t func, S32 min_count, S32 max_count); diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index fe99d9c267..5b72f87a78 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -49,7 +49,12 @@ LLUICtrl::Params::Params() validate_callback("validate_callback"), mouseenter_callback("mouseenter_callback"), mouseleave_callback("mouseleave_callback"), - control_name("control_name") + control_name("control_name"), + font("font", LLFontGL::getFontSansSerif()), + font_halign("halign"), + font_valign("valign"), + length("length"), // ignore LLXMLNode cruft + type("type") // ignore LLXMLNode cruft { addSynonym(initial_value, "initial_value"); } @@ -212,6 +217,29 @@ void LLUICtrl::initEnableCallback(const EnableCallbackParam& cb, enable_signal_t } } +void LLUICtrl::initVisibleCallback(const VisibleCallbackParam& cb, visible_signal_t& sig) +{ + // Set the callback function + if (cb.function.isProvided()) + { + if (cb.parameter.isProvided()) + sig.connect(boost::bind(cb.function(), this, cb.parameter)); + else + sig.connect(cb.function()); + } + else + { + visible_callback_t* func = (VisibleCallbackRegistry::getValue(cb.function_name)); + if (func) + { + if (cb.parameter.isProvided()) + sig.connect(boost::bind((*func), this, cb.parameter)); + else + sig.connect(*func); + } + } +} + // virtual void LLUICtrl::onMouseEnter(S32 x, S32 y, MASK mask) { diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index c2502732f3..69207eb8ea 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -34,14 +34,15 @@ #ifndef LL_LLUICTRL_H #define LL_LLUICTRL_H -#include "llboost.h" +//#include "llboost.h" #include "llrect.h" #include "llsd.h" #include <boost/function.hpp> +#include <boost/signals2.hpp> #include "llinitparam.h" #include "llview.h" -#include "llviewmodel.h" +#include "llviewmodel.h" // *TODO move dependency to .cpp file const BOOL TAKE_FOCUS_YES = TRUE; const BOOL TAKE_FOCUS_NO = FALSE; @@ -62,6 +63,9 @@ public: typedef boost::function<bool (LLUICtrl* ctrl, const LLSD& param)> enable_callback_t; typedef boost::signals2::signal<bool (LLUICtrl* ctrl, const LLSD& param), boost_boolean_combiner> enable_signal_t; + typedef boost::function<bool (LLUICtrl* ctrl, const LLSD& param)> visible_callback_t; + typedef boost::signals2::signal<bool (LLUICtrl* ctrl, const LLSD& param), boost_boolean_combiner> visible_signal_t; + struct CallbackParam : public LLInitParam::Block<CallbackParam> { Ignored name; @@ -91,6 +95,11 @@ public: Optional<enable_callback_t> function; }; + struct VisibleCallbackParam : public LLInitParam::Block<VisibleCallbackParam, CallbackParam > + { + Optional<visible_callback_t> function; + }; + struct EnableControls : public LLInitParam::Choice<EnableControls> { Alternative<std::string> enabled; @@ -107,9 +116,12 @@ public: Alternative<std::string> invisible; ControlVisibility() - : visible("visiblity_control"), - invisible("invisiblity_control") - {} + : visible("visibility_control"), + invisible("invisibility_control") + { + addSynonym(visible, "visiblity_control"); + addSynonym(invisible, "invisiblity_control"); + } }; struct Params : public LLInitParam::Block<Params, LLView::Params> { @@ -128,6 +140,15 @@ public: Optional<EnableControls> enabled_controls; Optional<ControlVisibility> controls_visibility; + // font params + Optional<const LLFontGL*> font; + Optional<LLFontGL::HAlign> font_halign; + Optional<LLFontGL::VAlign> font_valign; + + // cruft from LLXMLNode implementation + Ignored type, + length; + Params(); }; @@ -142,6 +163,7 @@ protected: void initCommitCallback(const CommitCallbackParam& cb, commit_signal_t& sig); void initEnableCallback(const EnableCallbackParam& cb, enable_signal_t& sig); + void initVisibleCallback(const VisibleCallbackParam& cb, visible_signal_t& sig); // We need this virtual so we can override it with derived versions virtual LLViewModel* getViewModel() const; @@ -259,6 +281,8 @@ public: class CommitCallbackRegistry : public CallbackRegistry<commit_callback_t, CommitCallbackRegistry>{}; class EnableCallbackRegistry : public CallbackRegistry<enable_callback_t, EnableCallbackRegistry>{}; + class VisibleCallbackRegistry : public CallbackRegistry<visible_callback_t, VisibleCallbackRegistry>{}; + protected: diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index 538e1ec492..4ce6677294 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -113,8 +113,20 @@ void LLUICtrlFactory::createChildren(LLView* viewp, LLXMLNodePtr node, const wid if (!instance().createFromXML(child_node, viewp, LLStringUtil::null, registry, outputChild)) { + // child_node is not a valid child for the current parent std::string child_name = std::string(child_node->getName()->mString); - llwarns << "Could not create widget named " << child_node->getName()->mString << llendl; + if (LLDefaultChildRegistry::instance().getValue(child_name)) + { + // This means that the registry assocaited with the parent widget does not have an entry + // for the child widget + // You might need to add something like: + // static ParentWidgetRegistry::Register<ChildWidgetType> register("child_widget_name"); + llwarns << child_name << " is not a valid child of " << node->getName()->mString << llendl; + } + else + { + llwarns << "Could not create widget named " << child_node->getName()->mString << llendl; + } } if (outputChild && !outputChild->mChildren && outputChild->mAttributes.empty() && outputChild->getValue().empty()) diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index c20212c375..c1da73fa83 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -36,8 +36,10 @@ #include "lluri.h" #include "llcachename.h" #include "lltrans.h" +#include "lluicolortable.h" LLUrlEntryBase::LLUrlEntryBase() +: mColor(LLUIColorTable::instance().getColor("HTMLLinkColor")) { } @@ -260,10 +262,11 @@ std::string LLUrlEntrySLURL::getLocation(const std::string &url) const // LLUrlEntryAgent::LLUrlEntryAgent() { - mPattern = boost::regex("secondlife:///app/agent/[\\da-f-]+/about", + mPattern = boost::regex("secondlife:///app/agent/[\\da-f-]+/\\w+", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_agent.xml"; - mTooltip = LLTrans::getString("TooltipAgentUrl"); + mIcon = "Generic_Person"; + mColor = LLUIColorTable::instance().getColor("AgentLinkColor"); } void LLUrlEntryAgent::onAgentNameReceived(const LLUUID& id, @@ -293,7 +296,7 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa } } - return unescapeUrl(url); + return LLTrans::getString("LoadingData");//unescapeUrl(url); } // @@ -305,6 +308,7 @@ LLUrlEntryGroup::LLUrlEntryGroup() mPattern = boost::regex("secondlife:///app/group/[\\da-f-]+/about", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_group.xml"; + mIcon = "Generic_Group"; mTooltip = LLTrans::getString("TooltipGroupUrl"); } diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index 54053872df..afb2fdcde9 100644 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -35,7 +35,7 @@ #define LL_LLURLENTRY_H #include "lluuid.h" - +#include "lluicolor.h" #include <boost/signals2.hpp> #include <boost/regex.hpp> #include <string> @@ -77,13 +77,16 @@ public: virtual std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) { return url; } /// Return an icon that can be displayed next to Urls of this type - const std::string &getIcon() const { return mIcon; } + std::string getIcon() const { return mIcon; } + + /// Return the color to render the displayed text + LLUIColor getColor() const { return mColor; } /// Given a matched Url, return a tooltip string for the hyperlink std::string getTooltip() const { return mTooltip; } /// Return the name of a XUI file containing the context menu items - const std::string getMenuName() const { return mMenuName; } + std::string getMenuName() const { return mMenuName; } /// Return the name of a SL location described by this Url, if any virtual std::string getLocation(const std::string &url) const { return ""; } @@ -102,11 +105,12 @@ protected: LLUrlLabelSignal *signal; } LLUrlEntryObserver; - boost::regex mPattern; - std::string mIcon; - std::string mMenuName; - std::string mTooltip; - std::multimap<std::string, LLUrlEntryObserver> mObservers; + boost::regex mPattern; + std::string mIcon; + std::string mMenuName; + std::string mTooltip; + LLUIColor mColor; + std::multimap<std::string, LLUrlEntryObserver> mObservers; }; /// diff --git a/indra/llui/llurlmatch.cpp b/indra/llui/llurlmatch.cpp index 7eec4c4a65..3b47145a22 100644 --- a/indra/llui/llurlmatch.cpp +++ b/indra/llui/llurlmatch.cpp @@ -47,8 +47,8 @@ LLUrlMatch::LLUrlMatch() : void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url, const std::string &label, const std::string &tooltip, - const std::string &icon, const std::string &menu, - const std::string &location) + const std::string &icon, const LLUIColor& color, + const std::string &menu, const std::string &location) { mStart = start; mEnd = end; @@ -56,6 +56,7 @@ void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url, mLabel = label; mTooltip = tooltip; mIcon = icon; + mColor = color; mMenuName = menu; mLocation = location; } diff --git a/indra/llui/llurlmatch.h b/indra/llui/llurlmatch.h index 0711e41443..7f5767923a 100644 --- a/indra/llui/llurlmatch.h +++ b/indra/llui/llurlmatch.h @@ -38,6 +38,7 @@ #include <string> #include <vector> +#include "lluicolor.h" /// /// LLUrlMatch describes a single Url that was matched within a string by @@ -62,27 +63,31 @@ public: U32 getEnd() const { return mEnd; } /// return the Url that has been matched in the input string - const std::string &getUrl() const { return mUrl; } + std::string getUrl() const { return mUrl; } /// return a label that can be used for the display of this Url - const std::string &getLabel() const { return mLabel; } + std::string getLabel() const { return mLabel; } /// return a message that could be displayed in a tooltip or status bar - const std::string &getTooltip() const { return mTooltip; } + std::string getTooltip() const { return mTooltip; } /// return the filename for an icon that can be displayed next to this Url - const std::string &getIcon() const { return mIcon; } + std::string getIcon() const { return mIcon; } + + /// Return the color to render the displayed text + LLUIColor getColor() const { return mColor; } /// Return the name of a XUI file containing the context menu items - const std::string getMenuName() const { return mMenuName; } + std::string getMenuName() const { return mMenuName; } /// return the SL location that this Url describes, or "" if none. - const std::string &getLocation() const { return mLocation; } + std::string getLocation() const { return mLocation; } /// Change the contents of this match object (used by LLUrlRegistry) void setValues(U32 start, U32 end, const std::string &url, const std::string &label, const std::string &tooltip, const std::string &icon, - const std::string &menu, const std::string &location); + const LLUIColor& color, const std::string &menu, + const std::string &location); private: U32 mStart; @@ -93,6 +98,7 @@ private: std::string mIcon; std::string mMenuName; std::string mLocation; + LLUIColor mColor; }; #endif diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp index 6f5c694b1b..8413de0837 100644 --- a/indra/llui/llurlregistry.cpp +++ b/indra/llui/llurlregistry.cpp @@ -155,6 +155,7 @@ bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LL match_entry->getLabel(url, cb), match_entry->getTooltip(), match_entry->getIcon(), + match_entry->getColor(), match_entry->getMenuName(), match_entry->getLocation(url)); return true; @@ -183,9 +184,10 @@ bool LLUrlRegistry::findUrl(const LLWString &text, LLUrlMatch &match, const LLUr } S32 end = start + wurl.size() - 1; - match.setValues(start, end, match.getUrl(), match.getLabel(), - match.getTooltip(), match.getIcon(), - match.getMenuName(), match.getLocation()); + match.setValues(start, end, match.getUrl(), + match.getLabel(), match.getTooltip(), + match.getIcon(), match.getColor(), + match.getMenuName(), match.getLocation()); return true; } return false; diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 10cb3fb377..1df8838738 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -92,9 +92,6 @@ LLView::Params::Params() default_tab_group("default_tab_group"), tool_tip("tool_tip"), sound_flags("sound_flags", MOUSE_UP), - font("font", LLFontGL::getFontSansSerif()), - font_halign("halign"), - font_valign("valign"), layout("layout"), rect("rect"), bottom_delta("bottom_delta", S32_MAX), @@ -172,12 +169,6 @@ LLView::~LLView() } // virtual -BOOL LLView::isView() const -{ - return TRUE; -} - -// virtual BOOL LLView::isCtrl() const { return FALSE; @@ -227,10 +218,9 @@ BOOL LLView::getUseBoundingRect() } // virtual -const std::string& LLView::getName() const +std::string LLView::getName() const { - static const std::string unnamed("(no name)"); - return mName.empty() ? unnamed : mName; + return mName.empty() ? std::string("(no name)") : mName; } void LLView::sendChildToFront(LLView* child) @@ -634,16 +624,7 @@ void LLView::setSnappedTo(const LLView* snap_view) BOOL LLView::handleHover(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleHover( x, y, mask ) != NULL; - if( !handled - && blockMouseEvent(x, y) ) - { - LLUI::sWindow->setCursor(mHoverCursor); - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl; - handled = TRUE; - } - - return handled; + return childrenHandleHover( x, y, mask ) != NULL; } void LLView::onMouseEnter(S32 x, S32 y, MASK mask) @@ -657,7 +638,7 @@ void LLView::onMouseLeave(S32 x, S32 y, MASK mask) } -LLView* LLView::childrenHandleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen) +LLView* LLView::childrenHandleToolTip(S32 x, S32 y, MASK mask) { LLView* handled_view = NULL; for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) @@ -665,13 +646,13 @@ LLView* LLView::childrenHandleToolTip(S32 x, S32 y, std::string& msg, LLRect& st LLView* viewp = *child_it; S32 local_x = x - viewp->getRect().mLeft; S32 local_y = y - viewp->getRect().mBottom; - if(!viewp->pointInView(local_x, local_y) || - !viewp->getVisible()) + if(!viewp->pointInView(local_x, local_y) + || !viewp->getVisible()) { continue; } - if(viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen) ) + if (viewp->handleToolTip(local_x, local_y, mask) ) { if (sDebugMouseHandling) { @@ -682,20 +663,22 @@ LLView* LLView::childrenHandleToolTip(S32 x, S32 y, std::string& msg, LLRect& st break; } - if( viewp->blockMouseEvent(x, y) ) + if (viewp->blockMouseEvent(local_x, local_y)) { handled_view = viewp; + break; } } return handled_view; } -BOOL LLView::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen) + +BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; // parents provide tooltips first, which are optionally - // overridden by children + // overridden by children, in case child is mouse_opaque if (!mToolTipMsg.empty()) { // allow "scrubbing" over ui by showing next tooltip immediately @@ -703,7 +686,7 @@ BOOL LLView::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_s F32 timeout = LLToolTipMgr::instance().toolTipVisible() ? 0.f : LLUI::sSettingGroups["config"]->getF32( "ToolTipDelay" ); - LLToolTipMgr::instance().show(LLToolTipParams() + LLToolTipMgr::instance().show(LLToolTip::Params() .message(mToolTipMsg) .sticky_rect(calcScreenRect()) .delay_time(timeout)); @@ -712,7 +695,7 @@ BOOL LLView::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_s } // child tooltips will override our own - LLView* child_handler = childrenHandleToolTip(x, y, msg, sticky_rect_screen); + LLView* child_handler = childrenHandleToolTip(x, y, mask); if (child_handler) { handled = TRUE; @@ -720,7 +703,6 @@ BOOL LLView::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_s return handled; } - BOOL LLView::handleKey(KEY key, MASK mask, BOOL called_from_parent) { BOOL handled = FALSE; @@ -801,20 +783,7 @@ BOOL LLView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EAcceptance* accept, std::string& tooltip_msg) { - // CRO this is an experiment to allow drag and drop into object inventory based on the DragAndDrop tool's permissions rather than the parent - BOOL handled = childrenHandleDragAndDrop( x, y, mask, drop, - cargo_type, - cargo_data, - accept, - tooltip_msg) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - *accept = ACCEPT_NO; - handled = TRUE; - lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLView " << getName() << llendl; - } - - return handled; + return childrenHandleDragAndDrop( x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL; } LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask, @@ -824,28 +793,33 @@ LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask, EAcceptance* accept, std::string& tooltip_msg) { - LLView* handled_view = FALSE; - // CRO this is an experiment to allow drag and drop into object inventory based on the DragAndDrop tool's permissions rather than the parent - if( getVisible() ) -// if( getVisible() && getEnabled() ) + LLView* handled_view = NULL; + for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) { - for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) + LLView* viewp = *child_it; + S32 local_x = x - viewp->getRect().mLeft; + S32 local_y = y - viewp->getRect().mBottom; + if( !viewp->pointInView(local_x, local_y) || + !viewp->getVisible() || + !viewp->getEnabled()) { - LLView* viewp = *child_it; - S32 local_x = x - viewp->getRect().mLeft; - S32 local_y = y - viewp->getRect().mBottom; - if( viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleDragAndDrop(local_x, local_y, mask, drop, - cargo_type, - cargo_data, - accept, - tooltip_msg)) - { - handled_view = viewp; - break; - } + continue; + } + if (viewp->handleDragAndDrop(local_x, local_y, mask, drop, + cargo_type, + cargo_data, + accept, + tooltip_msg)) + { + handled_view = viewp; + break; + } + + if (viewp->blockMouseEvent(x, y)) + { + *accept = ACCEPT_NO; + handled_view = viewp; + break; } } return handled_view; @@ -862,105 +836,42 @@ BOOL LLView::hasMouseCapture() BOOL LLView::handleMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleMouseUp( x, y, mask ) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - } - return handled; + return childrenHandleMouseUp( x, y, mask ) != NULL; } BOOL LLView::handleMouseDown(S32 x, S32 y, MASK mask) { - LLView* handled_view = childrenHandleMouseDown( x, y, mask ); - BOOL handled = (handled_view != NULL); - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - handled_view = this; - } - - //// HACK If we're editing UI, select the leaf view that ate the click. - //if (sEditingUI && handled_view) - //{ - // // need to find leaf views, big hack - // LLButton* buttonp = dynamic_cast<LLButton*>(handled_view); - // LLLineEditor* line_editorp = dynamic_cast<LLLineEditor*>(handled_view); - // LLTextEditor* text_editorp = dynamic_cast<LLTextEditor*>(handled_view); - // LLTextBox* text_boxp = dynamic_cast<LLTextBox*>(handled_view); - // if (buttonp - // || line_editorp - // || text_editorp - // || text_boxp) - // { - // sEditingUIView = handled_view; - // } - //} - - return handled; + return childrenHandleMouseDown( x, y, mask ) != NULL; } BOOL LLView::handleDoubleClick(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleDoubleClick( x, y, mask ) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - handleMouseDown(x, y, mask); - handled = TRUE; - } - return handled; + return childrenHandleDoubleClick( x, y, mask ) != NULL; } BOOL LLView::handleScrollWheel(S32 x, S32 y, S32 clicks) { - if( getVisible() && getEnabled() ) - { - return childrenHandleScrollWheel( x, y, clicks ) != NULL; - } - return FALSE; + return childrenHandleScrollWheel( x, y, clicks ) != NULL; } BOOL LLView::handleRightMouseDown(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleRightMouseDown( x, y, mask ) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - } - return handled; + return childrenHandleRightMouseDown( x, y, mask ) != NULL; } BOOL LLView::handleRightMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleRightMouseUp( x, y, mask ) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - } - return handled; + return childrenHandleRightMouseUp( x, y, mask ) != NULL; } BOOL LLView::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { - LLView* handled_view = childrenHandleMiddleMouseDown( x, y, mask ); - BOOL handled = (handled_view != NULL); - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - handled_view = this; - } - - return handled; + return childrenHandleMiddleMouseDown( x, y, mask ) != NULL; } BOOL LLView::handleMiddleMouseUp(S32 x, S32 y, MASK mask) { - BOOL handled = childrenHandleMiddleMouseUp( x, y, mask ) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - } - return handled; + return childrenHandleMiddleMouseUp( x, y, mask ) != NULL; } @@ -974,10 +885,14 @@ LLView* LLView::childrenHandleScrollWheel(S32 x, S32 y, S32 clicks) LLView* viewp = *child_it; S32 local_x = x - viewp->getRect().mLeft; S32 local_y = y - viewp->getRect().mBottom; - if (viewp->pointInView(local_x, local_y) - && viewp->getVisible() - && viewp->getEnabled() - && viewp->handleScrollWheel( local_x, local_y, clicks )) + if (!viewp->pointInView(local_x, local_y) + || !viewp->getVisible() + || !viewp->getEnabled()) + { + continue; + } + + if (viewp->handleScrollWheel( local_x, local_y, clicks )) { if (sDebugMouseHandling) { @@ -987,6 +902,12 @@ LLView* LLView::childrenHandleScrollWheel(S32 x, S32 y, S32 clicks) handled_view = viewp; break; } + + if (viewp->blockMouseEvent(local_x, local_y)) + { + handled_view = viewp; + break; + } } } return handled_view; @@ -1002,10 +923,14 @@ LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask) LLView* viewp = *child_it; S32 local_x = x - viewp->getRect().mLeft; S32 local_y = y - viewp->getRect().mBottom; - if(viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleHover(local_x, local_y, mask) ) + if(!viewp->pointInView(local_x, local_y) + || !viewp->getVisible() + || !viewp->getEnabled()) + { + continue; + } + + if (viewp->handleHover(local_x, local_y, mask) ) { if (sDebugMouseHandling) { @@ -1015,6 +940,14 @@ LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask) handled_view = viewp; break; } + + if (viewp->blockMouseEvent(local_x, local_y)) + { + LLUI::sWindow->setCursor(viewp->getHoverCursor()); + + handled_view = viewp; + break; + } } } return handled_view; @@ -1080,10 +1013,14 @@ LLView* LLView::childrenHandleMouseDown(S32 x, S32 y, MASK mask) S32 local_x = x - viewp->getRect().mLeft; S32 local_y = y - viewp->getRect().mBottom; - if (viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleMouseDown( local_x, local_y, mask )) + if (!viewp->pointInView(local_x, local_y) + || !viewp->getVisible() + || !viewp->getEnabled()) + { + continue; + } + + if(viewp->handleMouseDown( local_x, local_y, mask )) { if (sDebugMouseHandling) { @@ -1092,6 +1029,12 @@ LLView* LLView::childrenHandleMouseDown(S32 x, S32 y, MASK mask) handled_view = viewp; break; } + + if(viewp->blockMouseEvent(local_x, local_y)) + { + handled_view = viewp; + break; + } } return handled_view; } @@ -1107,10 +1050,15 @@ LLView* LLView::childrenHandleRightMouseDown(S32 x, S32 y, MASK mask) LLView* viewp = *child_it; S32 local_x = x - viewp->getRect().mLeft; S32 local_y = y - viewp->getRect().mBottom; - if (viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleRightMouseDown( local_x, local_y, mask )) + + if (!viewp->pointInView(local_x, local_y) + || !viewp->getVisible() + || !viewp->getEnabled()) + { + continue; + } + + if (viewp->handleRightMouseDown( local_x, local_y, mask )) { if (sDebugMouseHandling) { @@ -1120,6 +1068,12 @@ LLView* LLView::childrenHandleRightMouseDown(S32 x, S32 y, MASK mask) handled_view = viewp; break; } + + if (viewp->blockMouseEvent(local_x, local_y)) + { + handled_view = viewp; + break; + } } } return handled_view; @@ -1136,10 +1090,14 @@ LLView* LLView::childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask) LLView* viewp = *child_it; S32 local_x = x - viewp->getRect().mLeft; S32 local_y = y - viewp->getRect().mBottom; - if (viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleMiddleMouseDown( local_x, local_y, mask )) + if (!viewp->pointInView(local_x, local_y) + || !viewp->getVisible() + || !viewp->getEnabled()) + { + continue; + } + + if(viewp->handleMiddleMouseDown( local_x, local_y, mask )) { if (sDebugMouseHandling) { @@ -1148,6 +1106,12 @@ LLView* LLView::childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask) handled_view = viewp; break; } + + if (viewp->blockMouseEvent(local_x, local_y)) + { + handled_view = viewp; + break; + } } } return handled_view; @@ -1164,10 +1128,15 @@ LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask) LLView* viewp = *child_it; S32 local_x = x - viewp->getRect().mLeft; S32 local_y = y - viewp->getRect().mBottom; - if (viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleDoubleClick( local_x, local_y, mask )) + + if (!viewp->pointInView(local_x, local_y) + || !viewp->getVisible() + || !viewp->getEnabled()) + { + continue; + } + + if (viewp->handleDoubleClick( local_x, local_y, mask )) { if (sDebugMouseHandling) { @@ -1176,6 +1145,12 @@ LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask) handled_view = viewp; break; } + + if (viewp->blockMouseEvent(local_x, local_y)) + { + handled_view = viewp; + break; + } } } return handled_view; @@ -1191,12 +1166,13 @@ LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask) LLView* viewp = *child_it; S32 local_x = x - viewp->getRect().mLeft; S32 local_y = y - viewp->getRect().mBottom; - if (!viewp->pointInView(local_x, local_y)) - continue; - if (!viewp->getVisible()) - continue; - if (!viewp->getEnabled()) + if (!viewp->pointInView(local_x, local_y) + || !viewp->getVisible() + || !viewp->getEnabled()) + { continue; + } + if (viewp->handleMouseUp( local_x, local_y, mask )) { if (sDebugMouseHandling) @@ -1206,6 +1182,12 @@ LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask) handled_view = viewp; break; } + + if (viewp->blockMouseEvent(local_x, local_y)) + { + handled_view = viewp; + break; + } } } return handled_view; @@ -1221,10 +1203,14 @@ LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask) LLView* viewp = *child_it; S32 local_x = x - viewp->getRect().mLeft; S32 local_y = y - viewp->getRect().mBottom; - if (viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleRightMouseUp( local_x, local_y, mask )) + if (!viewp->pointInView(local_x, local_y) + || !viewp->getVisible() + || !viewp->getEnabled() ) + { + continue; + } + + if(viewp->handleRightMouseUp( local_x, local_y, mask )) { if (sDebugMouseHandling) { @@ -1233,6 +1219,12 @@ LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask) handled_view = viewp; break; } + + if(viewp->blockMouseEvent(local_x, local_y)) + { + handled_view = viewp; + break; + } } } return handled_view; @@ -1248,10 +1240,14 @@ LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask) LLView* viewp = *child_it; S32 local_x = x - viewp->getRect().mLeft; S32 local_y = y - viewp->getRect().mBottom; - if (viewp->pointInView(local_x, local_y) && - viewp->getVisible() && - viewp->getEnabled() && - viewp->handleMiddleMouseUp( local_x, local_y, mask )) + if (!viewp->pointInView(local_x, local_y) + || !viewp->getVisible() + || !viewp->getEnabled()) + { + continue; + } + + if(viewp->handleMiddleMouseUp( local_x, local_y, mask )) { if (sDebugMouseHandling) { @@ -1260,6 +1256,12 @@ LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask) handled_view = viewp; break; } + + if (viewp->blockMouseEvent(local_x, local_y)) + { + handled_view = viewp; + break; + } } } return handled_view; @@ -1272,18 +1274,6 @@ void LLView::draw() void LLView::drawChildren() { - if (sDebugRects) - { - drawDebugRect(); - - // Check for bogus rectangle - if (getRect().mRight <= getRect().mLeft - || getRect().mTop <= getRect().mBottom) - { - llwarns << "Bogus rectangle for " << getName() << " with " << mRect << llendl; - } - } - if (!mChildList.empty()) { LLRect rootRect = getRootView()->getRect(); @@ -1307,6 +1297,17 @@ void LLView::drawChildren() { LLUI::translate((F32)viewp->getRect().mLeft, (F32)viewp->getRect().mBottom, 0.f); viewp->draw(); + + if (sDebugRects) + { + viewp->drawDebugRect(); + + // Check for bogus rectangle + if (!getRect().isValid()) + { + llwarns << "Bogus rectangle for " << getName() << " with " << mRect << llendl; + } + } } LLUI::popMatrix(); } @@ -1353,10 +1354,6 @@ void LLView::drawDebugRect() // draw red rectangle for the border LLColor4 border_color(0.f, 0.f, 0.f, 1.f); - //if (sEditingUI) - //{ - // border_color.mV[0] = 1.f; - //} if(preview_iter != sPreviewHighlightedElements.end()) { if(LLView::sPreviewClickedElement && this == sPreviewClickedElement) diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 7a37d6f430..7ddff2bd9e 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -42,8 +42,6 @@ #include "llfontgl.h" #include "llmortician.h" #include "llmousehandler.h" -#include "llnametable.h" -#include "llsd.h" #include "llstring.h" #include "llrect.h" #include "llui.h" @@ -58,6 +56,8 @@ #include <list> +class LLSD; + const U32 FOLLOWS_NONE = 0x00; const U32 FOLLOWS_LEFT = 0x01; const U32 FOLLOWS_RIGHT = 0x02; @@ -124,21 +124,16 @@ public: Optional<bool> enabled, visible, mouse_opaque, - use_bounding_rect; + use_bounding_rect, + from_xui; Optional<S32> tab_group, default_tab_group; Optional<std::string> tool_tip; Optional<S32> sound_flags; - Optional<bool> from_xui; Optional<Follows> follows; Optional<std::string> hover_cursor; - - // font params - Optional<const LLFontGL*> font; - Optional<LLFontGL::HAlign> font_halign; - Optional<LLFontGL::VAlign> font_valign; Optional<std::string> layout; Optional<LLRect> rect; @@ -223,9 +218,6 @@ public: virtual ~LLView(); - // Hack to support LLFocusMgr (from LLMouseHandler) - /*virtual*/ BOOL isView() const; - // Some UI widgets need to be added as controls. Others need to // be added as regular view children. isCtrl should return TRUE // if a widget needs to be added as a ctrl @@ -258,6 +250,8 @@ public: void setUseBoundingRect( BOOL use_bounding_rect ); BOOL getUseBoundingRect(); + ECursorType getHoverCursor() { return mHoverCursor; } + const std::string& getToolTip() const { return mToolTipMsg.getString(); } void sendChildToFront(LLView* child); @@ -443,12 +437,11 @@ public: /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask); - /*virtual*/ BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect); // Display mToolTipMsg if no child handles it. + /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask); - /*virtual*/ const std::string& getName() const; + /*virtual*/ std::string getName() const; /*virtual*/ void onMouseCaptureLost(); /*virtual*/ BOOL hasMouseCapture(); - /*virtual*/ BOOL isView(); // Hack to support LLFocusMgr /*virtual*/ void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const; /*virtual*/ void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const; @@ -545,7 +538,7 @@ protected: LLView* childrenHandleScrollWheel(S32 x, S32 y, S32 clicks); LLView* childrenHandleRightMouseDown(S32 x, S32 y, MASK mask); LLView* childrenHandleRightMouseUp(S32 x, S32 y, MASK mask); - LLView* childrenHandleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect); + LLView* childrenHandleToolTip(S32 x, S32 y, MASK mask); ECursorType mHoverCursor; diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp index 1e7a0f7f2c..468fae2ec5 100644 --- a/indra/llui/tests/llurlentry_test.cpp +++ b/indra/llui/tests/llurlentry_test.cpp @@ -24,9 +24,17 @@ #include "../llurlentry.h" #include "llurlentry_stub.cpp" #include "lltut.h" +#include "../lluicolortable.h" #include <boost/regex.hpp> +LLUIColor LLUIColorTable::getColor(const std::string& name, const LLColor4& default_color) const +{ + return LLUIColor(); +} + +LLUIColor::LLUIColor() {} + namespace tut { struct LLUrlEntryData @@ -276,6 +284,11 @@ namespace tut testRegex("Agent Url multicase", r, "XXX secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/About XXX", "secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/About"); + + testRegex("Agent Url alternate command", r, + "XXX secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar", + "secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar"); + } template<> template<> diff --git a/indra/llui/tests/llurlmatch_test.cpp b/indra/llui/tests/llurlmatch_test.cpp index 4dae49db90..e8cf135346 100644 --- a/indra/llui/tests/llurlmatch_test.cpp +++ b/indra/llui/tests/llurlmatch_test.cpp @@ -23,6 +23,10 @@ #include "../llurlmatch.h" #include "lltut.h" +// link seam +LLUIColor::LLUIColor() +{} + namespace tut { struct LLUrlMatchData @@ -49,7 +53,7 @@ namespace tut LLUrlMatch match; ensure("empty()", match.empty()); - match.setValues(0, 1, "http://secondlife.com", "Second Life", "", "", "", ""); + match.setValues(0, 1, "http://secondlife.com", "Second Life", "", "", LLUIColor(), "", ""); ensure("! empty()", ! match.empty()); } @@ -62,7 +66,7 @@ namespace tut LLUrlMatch match; ensure_equals("getStart() == 0", match.getStart(), 0); - match.setValues(10, 20, "", "", "", "", "", ""); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", ""); ensure_equals("getStart() == 10", match.getStart(), 10); } @@ -75,7 +79,7 @@ namespace tut LLUrlMatch match; ensure_equals("getEnd() == 0", match.getEnd(), 0); - match.setValues(10, 20, "", "", "", "", "", ""); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", ""); ensure_equals("getEnd() == 20", match.getEnd(), 20); } @@ -88,10 +92,10 @@ namespace tut LLUrlMatch match; ensure_equals("getUrl() == ''", match.getUrl(), ""); - match.setValues(10, 20, "http://slurl.com/", "", "", "", "", ""); + match.setValues(10, 20, "http://slurl.com/", "", "", "", LLUIColor(), "", ""); ensure_equals("getUrl() == 'http://slurl.com/'", match.getUrl(), "http://slurl.com/"); - match.setValues(10, 20, "", "", "", "", "", ""); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", ""); ensure_equals("getUrl() == '' (2)", match.getUrl(), ""); } @@ -104,10 +108,10 @@ namespace tut LLUrlMatch match; ensure_equals("getLabel() == ''", match.getLabel(), ""); - match.setValues(10, 20, "", "Label", "", "", "", ""); + match.setValues(10, 20, "", "Label", "", "", LLUIColor(), "", ""); ensure_equals("getLabel() == 'Label'", match.getLabel(), "Label"); - match.setValues(10, 20, "", "", "", "", "", ""); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", ""); ensure_equals("getLabel() == '' (2)", match.getLabel(), ""); } @@ -120,10 +124,10 @@ namespace tut LLUrlMatch match; ensure_equals("getTooltip() == ''", match.getTooltip(), ""); - match.setValues(10, 20, "", "", "Info", "", "", ""); + match.setValues(10, 20, "", "", "Info", "", LLUIColor(), "", ""); ensure_equals("getTooltip() == 'Info'", match.getTooltip(), "Info"); - match.setValues(10, 20, "", "", "", "", "", ""); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", ""); ensure_equals("getTooltip() == '' (2)", match.getTooltip(), ""); } @@ -136,10 +140,10 @@ namespace tut LLUrlMatch match; ensure_equals("getIcon() == ''", match.getIcon(), ""); - match.setValues(10, 20, "", "", "", "Icon", "", ""); + match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "", ""); ensure_equals("getIcon() == 'Icon'", match.getIcon(), "Icon"); - match.setValues(10, 20, "", "", "", "", "", ""); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", ""); ensure_equals("getIcon() == '' (2)", match.getIcon(), ""); } @@ -152,10 +156,10 @@ namespace tut LLUrlMatch match; ensure("getMenuName() empty", match.getMenuName().empty()); - match.setValues(10, 20, "", "", "", "Icon", "xui_file.xml", ""); + match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", ""); ensure_equals("getMenuName() == \"xui_file.xml\"", match.getMenuName(), "xui_file.xml"); - match.setValues(10, 20, "", "", "", "", "", ""); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", ""); ensure("getMenuName() empty (2)", match.getMenuName().empty()); } @@ -168,10 +172,10 @@ namespace tut LLUrlMatch match; ensure("getLocation() empty", match.getLocation().empty()); - match.setValues(10, 20, "", "", "", "Icon", "xui_file.xml", "Paris"); + match.setValues(10, 20, "", "", "", "Icon", LLUIColor(), "xui_file.xml", "Paris"); ensure_equals("getLocation() == \"Paris\"", match.getLocation(), "Paris"); - match.setValues(10, 20, "", "", "", "", "", ""); + match.setValues(10, 20, "", "", "", "", LLUIColor(), "", ""); ensure("getLocation() empty (2)", match.getLocation().empty()); } } diff --git a/indra/llwindow/llmousehandler.h b/indra/llwindow/llmousehandler.h index b5dbbc53fb..d758a1638a 100644 --- a/indra/llwindow/llmousehandler.h +++ b/indra/llwindow/llmousehandler.h @@ -70,14 +70,11 @@ public: virtual BOOL handleHover(S32 x, S32 y, MASK mask) = 0; virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks) = 0; - virtual BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen) = 0; - virtual const std::string& getName() const = 0; + virtual BOOL handleToolTip(S32 x, S32 y, MASK mask) = 0; + virtual std::string getName() const = 0; virtual void onMouseCaptureLost() = 0; - // Hack to support LLFocusMgr - virtual BOOL isView() const = 0; - virtual void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const = 0; virtual void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const = 0; diff --git a/indra/llwindow/llpreeditor.h b/indra/llwindow/llpreeditor.h index dd63a98606..fcb39515e7 100644 --- a/indra/llwindow/llpreeditor.h +++ b/indra/llwindow/llpreeditor.h @@ -94,7 +94,7 @@ public: // Get the contents of this preeditor as a LLWString. If there is an active preedit, // the returned LLWString contains it. - virtual LLWString getWText() const = 0; + virtual LLWString getPreeditString() const = 0; // Handle a UTF-32 char on this preeditor, i.e., add the character // to the contents. diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index 82dc5e4a13..d2760e3d59 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -2016,7 +2016,7 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e // Although the spec. is unclear, replace range should // not present when there is an active preedit. We just // ignore the case. markAsPreedit will detect the case and warn it. - const LLWString & text = mPreeditor->getWText(); + const LLWString & text = mPreeditor->getPreeditString(); const S32 location = wstring_wstring_length_from_utf16_length(text, 0, range.location); const S32 length = wstring_wstring_length_from_utf16_length(text, location, range.length); mPreeditor->markAsPreedit(location, length); @@ -2214,7 +2214,7 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e { S32 preedit, preedit_length; mPreeditor->getPreeditRange(&preedit, &preedit_length); - const LLWString & text = mPreeditor->getWText(); + const LLWString & text = mPreeditor->getPreeditString(); LLCoordGL caret_coord; LLRect preedit_bounds; @@ -2251,7 +2251,7 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e mPreeditor->getSelectionRange(&selection, &selection_length); if (selection_length) { - const LLWString text = mPreeditor->getWText().substr(selection, selection_length); + const LLWString text = mPreeditor->getPreeditString().substr(selection, selection_length); const llutf16string text_utf16 = wstring_to_utf16str(text); result = SetEventParameter(event, kEventParamTextInputReplyText, typeUnicodeText, text_utf16.length() * sizeof(U16), text_utf16.c_str()); @@ -2637,7 +2637,7 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e S32 preedit, preedit_length; mPreeditor->getPreeditRange(&preedit, &preedit_length); - const LLWString & text = mPreeditor->getWText(); + const LLWString & text = mPreeditor->getPreeditString(); const CFIndex length = wstring_utf16_length(text, 0, preedit) + wstring_utf16_length(text, preedit + preedit_length, text.length()); result = SetEventParameter(event, kEventParamTSMDocAccessCharacterCount, typeCFIndex, sizeof(length), &length); @@ -2654,7 +2654,7 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e S32 preedit, preedit_length; mPreeditor->getPreeditRange(&preedit, &preedit_length); - const LLWString & text = mPreeditor->getWText(); + const LLWString & text = mPreeditor->getPreeditString(); CFRange range; if (preedit_length) @@ -2688,7 +2688,7 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e { S32 preedit, preedit_length; mPreeditor->getPreeditRange(&preedit, &preedit_length); - const LLWString & text = mPreeditor->getWText(); + const LLWString & text = mPreeditor->getPreeditString(); // The GetCharacters event of TSMDA has a fundamental flaw; // An input method need to decide the starting offset and length diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 9936b24292..0faa3e93ff 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -3544,7 +3544,7 @@ BOOL LLWindowWin32::handleImeRequests(U32 request, U32 param, LRESULT *result) // WCHARs, i.e., UTF-16 encoding units, so we can't simply pass the // number to getPreeditLocation. - const LLWString & wtext = mPreeditor->getWText(); + const LLWString & wtext = mPreeditor->getPreeditString(); S32 preedit, preedit_length; mPreeditor->getPreeditRange(&preedit, &preedit_length); LLCoordGL caret_coord; @@ -3571,7 +3571,7 @@ BOOL LLWindowWin32::handleImeRequests(U32 request, U32 param, LRESULT *result) case IMR_RECONVERTSTRING: { mPreeditor->resetPreedit(); - const LLWString & wtext = mPreeditor->getWText(); + const LLWString & wtext = mPreeditor->getPreeditString(); S32 select, select_length; mPreeditor->getSelectionRange(&select, &select_length); @@ -3613,7 +3613,7 @@ BOOL LLWindowWin32::handleImeRequests(U32 request, U32 param, LRESULT *result) } case IMR_DOCUMENTFEED: { - const LLWString & wtext = mPreeditor->getWText(); + const LLWString & wtext = mPreeditor->getPreeditString(); S32 preedit, preedit_length; mPreeditor->getPreeditRange(&preedit, &preedit_length); diff --git a/indra/llxml/llxmlnode.h b/indra/llxml/llxmlnode.h index 818d774f73..7823607c3b 100644 --- a/indra/llxml/llxmlnode.h +++ b/indra/llxml/llxmlnode.h @@ -34,7 +34,7 @@ #define LL_LLXMLNODE_H #ifndef XML_STATIC -#define XML_STATIC 1 +#define XML_STATIC #endif #ifdef LL_STANDALONE #include <expat.h> diff --git a/indra/llxml/llxmlparser.h b/indra/llxml/llxmlparser.h index d7595f6a68..fa9e8175e2 100644 --- a/indra/llxml/llxmlparser.h +++ b/indra/llxml/llxmlparser.h @@ -34,7 +34,7 @@ #define LL_LLXMLPARSER_H #ifndef XML_STATIC -#define XML_STATIC 1 +#define XML_STATIC #endif #ifdef LL_STANDALONE #include <expat.h> diff --git a/indra/llxuixml/llinitparam.h b/indra/llxuixml/llinitparam.h index baa782916e..88bc430504 100644 --- a/indra/llxuixml/llinitparam.h +++ b/indra/llxuixml/llinitparam.h @@ -1568,11 +1568,11 @@ namespace LLInitParam public Param { public: - typedef BlockValue<T> self_t; - typedef Block<TypedParam<T, TypeValues<T>, false> > block_t; - typedef const T& value_const_ref_t; - typedef value_const_ref_t value_assignment_t; - typedef typename TypeValues<T>::KeyCache key_cache_t; + typedef BlockValue<T> self_t; + typedef Block<TypedParam<T, TypeValues<T>, false> > block_t; + typedef const T& value_const_ref_t; + typedef value_const_ref_t value_assignment_t; + typedef typename TypeValues<T>::KeyCache key_cache_t; BlockValue(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) : Param(block_descriptor.mCurrentBlockPtr), @@ -1754,11 +1754,16 @@ namespace LLInitParam protected: value_assignment_t get() const { - if (mData.mLastParamVersion < BaseBlock::getLastChangeVersion() && block_t::validateBlock(true)) + // if some parameters were provided, issue warnings on invalid blocks + if (Param::getProvided() && (mData.mLastParamVersion < BaseBlock::getLastChangeVersion())) { - mData.mValue = static_cast<const DERIVED*>(this)->getValueFromBlock(); - mData.clearKey(); - mData.mLastParamVersion = BaseBlock::getLastChangeVersion(); + // go ahead and issue warnings at this point if any param is invalid + if(block_t::validateBlock(false)) + { + mData.mValue = static_cast<const DERIVED*>(this)->getValueFromBlock(); + mData.clearKey(); + mData.mLastParamVersion = BaseBlock::getLastChangeVersion(); + } } return mData.mValue; diff --git a/indra/llxuixml/lltrans.cpp b/indra/llxuixml/lltrans.cpp index 2efc475f57..e974dbd0ba 100644 --- a/indra/llxuixml/lltrans.cpp +++ b/indra/llxuixml/lltrans.cpp @@ -194,3 +194,68 @@ bool LLTrans::findString(std::string &result, const std::string &xml_desc, const return false; } } + +//static +std::string LLTrans::getCountString(const std::string& language, const std::string& xml_desc, S32 count) +{ + // Compute which string identifier to use + const char* form = ""; + if (language == "ru") // Russian + { + // From GNU ngettext() + // Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2; + if (count % 10 == 1 + && count % 100 != 11) + { + // singular, "1 item" + form = "A"; + } + else if (count % 10 >= 2 + && count % 10 <= 4 + && (count % 100 < 10 || count % 100 >= 20) ) + { + // special case "2 items", "23 items", but not "13 items" + form = "B"; + } + else + { + // English-style plural, "5 items" + form = "C"; + } + } + else if (language == "fr" || language == "pt") // French, Brazilian Portuguese + { + // French and Portuguese treat zero as a singular "0 item" not "0 items" + if (count == 0 || count == 1) + { + form = "A"; + } + else + { + // English-style plural + form = "B"; + } + } + else // default + { + // languages like English with 2 forms, singular and plural + if (count == 1) + { + // "1 item" + form = "A"; + } + else + { + // "2 items", also use plural for "0 items" + form = "B"; + } + } + + // Translate that string + LLStringUtil::format_map_t args; + args["[COUNT]"] = llformat("%d", count); + + // Look up "AgeYearsB" or "AgeWeeksC" including the "form" + std::string key = llformat("%s%s", xml_desc.c_str(), form); + return getString(key, args); +} diff --git a/indra/llxuixml/lltrans.h b/indra/llxuixml/lltrans.h index 340d70e434..79df5802e5 100644 --- a/indra/llxuixml/lltrans.h +++ b/indra/llxuixml/lltrans.h @@ -80,6 +80,12 @@ public: static std::string getString(const std::string &xml_desc, const LLStringUtil::format_map_t& args); static bool findString(std::string &result, const std::string &xml_desc, const LLStringUtil::format_map_t& args); + // Returns translated string with [COUNT] replaced with a number, following + // special per-language logic for plural nouns. For example, some languages + // may have different plurals for 0, 1, 2 and > 2. + // See "AgeWeeksA", "AgeWeeksB", etc. in strings.xml for examples. + static std::string getCountString(const std::string& language, const std::string& xml_desc, S32 count); + /** * @brief Returns a translated string * @param xml_desc String's description diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index b217a5b781..14a37981a6 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -106,6 +106,7 @@ set(viewer_SOURCE_FILES llconfirmationmanager.cpp llcurrencyuimanager.cpp llcylinder.cpp + lldateutil.cpp lldebugmessagebox.cpp lldebugview.cpp lldelayedgestureerror.cpp @@ -241,6 +242,7 @@ set(viewer_SOURCE_FILES llimview.cpp llimcontrolpanel.cpp llinspectavatar.cpp + llinspectobject.cpp llinventorybridge.cpp llinventoryclipboard.cpp llinventoryfilter.cpp @@ -395,7 +397,6 @@ set(viewer_SOURCE_FILES lltoastimpanel.cpp lltoastnotifypanel.cpp lltoastpanel.cpp - lltoggleablemenu.cpp lltoolbar.cpp lltoolbrush.cpp lltoolcomp.cpp @@ -576,6 +577,7 @@ set(viewer_HEADER_FILES llconfirmationmanager.h llcurrencyuimanager.h llcylinder.h + lldateutil.h lldebugmessagebox.h lldebugview.h lldelayedgestureerror.h @@ -711,6 +713,7 @@ set(viewer_HEADER_FILES llimview.h llimcontrolpanel.h llinspectavatar.h + llinspectobject.h llinventorybridge.h llinventoryclipboard.h llinventoryfilter.h @@ -865,7 +868,6 @@ set(viewer_HEADER_FILES lltoastimpanel.h lltoastnotifypanel.h lltoastpanel.h - lltoggleablemenu.h lltool.h lltoolbar.h lltoolbrush.h @@ -1538,9 +1540,12 @@ if (INSTALL) include(${CMAKE_CURRENT_SOURCE_DIR}/ViewerInstall.cmake) endif (INSTALL) +# To add a viewer unit test, just add the test .cpp file below +# This creates a separate test project per file listed. include(LLAddBuildTest) SET(viewer_TEST_SOURCE_FILES llagentaccess.cpp + lldateutil.cpp llviewerhelputil.cpp ) set_source_files_properties( diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 99b662a63f..94a5f5c52b 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -3673,7 +3673,7 @@ <key>Type</key> <string>F32</string> <key>Value</key> - <string>0.1</string> + <real>0.15</real> </map> <key>InstallLanguage</key> <map> @@ -7585,7 +7585,7 @@ <key>Type</key> <string>Boolean</string> <key>Value</key> - <integer>1</integer> + <integer>0</integer> </map> <key>ShowCameraButton</key> <map> @@ -8456,18 +8456,40 @@ <key>Value</key> <real>0.2</real> </map> - <key>ToolTipVisibleTime</key> - <map> - <key>Comment</key> - <string>Fade tooltip after mouse is idle for this long</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>F32</string> - <key>Value</key> - <real>10.0</real> - </map> - <key>ToolboxAutoMove</key> + <key>ToolTipVisibleTimeFar</key> + <map> + <key>Comment</key> + <string>Fade tooltip after after time passes (seconds) while mouse not near tooltip</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>1.0</real> + </map> + <key>ToolTipVisibleTimeNear</key> + <map> + <key>Comment</key> + <string>Fade tooltip after after time passes (seconds) while mouse near tooltip</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>5.0</real> + </map> + <key>ToolTipVisibleTimeOver</key> + <map> + <key>Comment</key> + <string>Fade tooltip after after time passes (seconds) while mouse over tooltip</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>1000.0</real> + </map> + <key>ToolboxAutoMove</key> <map> <key>Comment</key> <string>[NOT USED]</string> @@ -9655,39 +9677,6 @@ <key>Value</key> <integer>15</integer> </map> - <key>UITextEditorBorder</key> - <map> - <key>Comment</key> - <string>UI Text Editor Border</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>S32</string> - <key>Value</key> - <integer>1</integer> - </map> - <key>UITextEditorHPad</key> - <map> - <key>Comment</key> - <string>UI Text Horizontal Pad</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>S32</string> - <key>Value</key> - <integer>4</integer> - </map> - <key>UITextEditorVPadTop</key> - <map> - <key>Comment</key> - <string>UI Text Vertical Pad Top</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>S32</string> - <key>Value</key> - <integer>4</integer> - </map> <key>UploadBakedTexOld</key> <map> <key>Comment</key> diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi index 73a548cdc6..a7322749ca 100644 --- a/indra/newview/installers/windows/installer_template.nsi +++ b/indra/newview/installers/windows/installer_template.nsi @@ -758,7 +758,7 @@ CreateShortCut "$SMPROGRAMS\$INSTSHORTCUT\$INSTSHORTCUT.lnk" \ WriteINIStr "$SMPROGRAMS\$INSTSHORTCUT\SL Create Account.url" \ "InternetShortcut" "URL" \ - "http://www.secondlife.com/registration/" + "http://join.secondlife.com/" WriteINIStr "$SMPROGRAMS\$INSTSHORTCUT\SL Your Account.url" \ "InternetShortcut" "URL" \ "http://www.secondlife.com/account/" diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 30e0a5770c..41cbc21fe9 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -696,9 +696,6 @@ bool LLAppViewer::init() // Let code in llui access the viewer help floater LLUI::sHelpImpl = LLViewerHelp::getInstance(); - // Set the link color for any Urls in text fields - LLTextBase::setLinkColor( LLUIColorTable::instance().getColor("HTMLLinkColor") ); - // Load translations for tooltips LLFloater::initClass(); @@ -1424,6 +1421,9 @@ bool LLAppViewer::cleanup() gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""),mask); } + // Turn off Space Navigator and similar devices + LLViewerJoystick::getInstance()->terminate(); + removeMarkerFile(); // Any crashes from here on we'll just have to ignore writeDebugInfo(); @@ -2379,7 +2379,6 @@ void LLAppViewer::cleanupSavedSettings() } gSavedSettings.setF32("MapScale", gMapScale ); - gSavedSettings.setBOOL("ShowHoverTips", gToolTipView->getVisible()); // Some things are cached in LLAgent. if (gAgent.mInitialized) diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 1676bb1d44..92b2768f39 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -39,16 +39,22 @@ #include "lldarray.h" #include "llnotifications.h" +#include "roles_constants.h" // for GP_MEMBER_INVITE + #include "llagent.h" #include "llappviewer.h" // for gLastVersionChannel #include "llcachename.h" #include "llcallingcard.h" // for LLAvatarTracker -#include "llgivemoney.h" // foe LLFloaterPay +#include "llfloatergroupinvite.h" +#include "llfloatergroups.h" +#include "llfloaterreg.h" +#include "llgivemoney.h" #include "llinventorymodel.h" // for gInventory.findCategoryUUIDForType #include "llimview.h" // for gIMMgr #include "llmutelist.h" #include "llrecentpeople.h" #include "llsidetray.h" +#include "llviewerobjectlist.h" #include "llviewermessage.h" // for handle_lure #include "llviewerregion.h" @@ -244,6 +250,17 @@ void LLAvatarActions::toggleBlock(const LLUUID& id) } } +void LLAvatarActions::inviteToGroup(const LLUUID& id) +{ + LLFloaterGroupPicker* widget = LLFloaterReg::showTypedInstance<LLFloaterGroupPicker>("group_picker", LLSD(id)); + if (widget) + { + widget->center(); + widget->setPowersMask(GP_MEMBER_INVITE); + widget->setSelectGroupCallback(boost::bind(callback_invite_to_group, _1, id)); + } +} + //== private methods ======================================================================================== // static @@ -294,6 +311,16 @@ bool LLAvatarActions::handlePay(const LLSD& notification, const LLSD& response, } // static +void LLAvatarActions::callback_invite_to_group(LLUUID group_id, LLUUID id) +{ + std::vector<LLUUID> agent_ids; + agent_ids.push_back(id); + + LLFloaterGroupInvite::showForGroup(group_id, &agent_ids); +} + + +// static bool LLAvatarActions::callbackAddFriendWithMessage(const LLSD& notification, const LLSD& response) { S32 option = LLNotification::getSelectedOption(notification, response); diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h index e911715c70..512f673b43 100644 --- a/indra/newview/llavataractions.h +++ b/indra/newview/llavataractions.h @@ -96,11 +96,17 @@ public: */ static bool isBlocked(const LLUUID& id); + /** + * Invite avatar to a group. + */ + static void inviteToGroup(const LLUUID& id); + private: static bool callbackAddFriend(const LLSD& notification, const LLSD& response); static bool callbackAddFriendWithMessage(const LLSD& notification, const LLSD& response); static bool handleRemove(const LLSD& notification, const LLSD& response); static bool handlePay(const LLSD& notification, const LLSD& response, LLUUID avatar_id); + static void callback_invite_to_group(LLUUID group_id, LLUUID id); // Just request friendship, no dialog. static void requestFriendship(const LLUUID& target_id, const std::string& target_name, const std::string& message); diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp index 665dffc8c6..51545bcc07 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -148,7 +148,7 @@ void LLAvatarListItem::setAvatarId(const LLUUID& id) void LLAvatarListItem::onInfoBtnClick() { - LLFloaterReg::showInstance("inspect_avatar", mAvatarId); + LLFloaterReg::showInstance("inspect_avatar", LLSD().insert("avatar_id", mAvatarId)); /* TODO fix positioning of inspector localPointToScreen(mXPos, mYPos, &mXPos, &mYPos); diff --git a/indra/newview/llavatarpropertiesprocessor.cpp b/indra/newview/llavatarpropertiesprocessor.cpp index e568b9c526..5c68ac8cdb 100644 --- a/indra/newview/llavatarpropertiesprocessor.cpp +++ b/indra/newview/llavatarpropertiesprocessor.cpp @@ -42,12 +42,18 @@ #include "llavatarconstants.h" // AVATAR_TRANSACTED, etc. #include "lldate.h" #include "lltrans.h" +#include "llui.h" // LLUI::getLanguage() #include "message.h" LLAvatarPropertiesProcessor::LLAvatarPropertiesProcessor() { } +LLAvatarPropertiesProcessor::~LLAvatarPropertiesProcessor() +{ + llinfos << "JAMESDEBUG cleanup avatar properties processor" << llendl; +} + void LLAvatarPropertiesProcessor::addObserver(const LLUUID& avatar_id, LLAvatarPropertiesObserver* observer) { // Check if that observer is already in mObservers for that avatar_id @@ -172,103 +178,6 @@ void LLAvatarPropertiesProcessor::sendAvatarPropertiesUpdate(const LLAvatarData* gAgent.sendReliableMessage(); } -//static -std::string LLAvatarPropertiesProcessor::ageFromDate(const std::string& date_string) -{ - // Convert string date to malleable representation - S32 month, day, year; - S32 matched = sscanf(date_string.c_str(), "%d/%d/%d", &month, &day, &year); - if (matched != 3) return "???"; - - // Create ISO-8601 date string - std::string iso8601_date_string = - llformat("%04d-%02d-%02dT00:00:00Z", year, month, day); - LLDate date(iso8601_date_string); - - // Correct for the fact that account creation dates are in Pacific time, - // == UTC - 8 - F64 date_secs_since_epoch = date.secondsSinceEpoch(); - date_secs_since_epoch += 8.0 * 60.0 * 60.0; - - // Convert seconds from epoch to seconds from now - F64 now_secs_since_epoch = LLDate::now().secondsSinceEpoch(); - F64 age_secs = now_secs_since_epoch - date_secs_since_epoch; - - // We don't care about sub-day times - const F64 SEC_PER_DAY = 24.0 * 60.0 * 60.0; - S32 age_days = lltrunc(age_secs / SEC_PER_DAY); - - // Assume most values won't be used to fill in the format string: - // "[AGEYEARS][AGEMONTHS][AGEWEEKS][AGEDAYS]old" - LLStringUtil::format_map_t final_args; - final_args["[AGEYEARS]"] = ""; - final_args["[AGEMONTHS]"] = ""; - final_args["[AGEWEEKS]"] = ""; - final_args["[AGEDAYS]"] = ""; - - // Try for age in round number of years - LLStringUtil::format_map_t args; - S32 age_years = age_days / 365; - age_days = age_days % 365; - if (age_years > 1) - { - args["[YEARS]"] = llformat("%d", age_years); - final_args["[AGEYEARS]"] = LLTrans::getString("AgeYears", args); - } - else if (age_years == 1) - { - final_args["[AGEYEARS]"] = LLTrans::getString("Age1Year"); - } - // fall through because we show years + months for ages > 1 year - - S32 age_months = age_days / 30; - age_days = age_days % 30; - if (age_months > 1) - { - args["[MONTHS]"] = llformat("%d", age_months); - final_args["[AGEMONTHS]"] = LLTrans::getString("AgeMonths", args); - // Either N years M months, or just M months, - // so we can exit. - return LLTrans::getString("YearsMonthsOld", final_args); - } - else if (age_months == 1) - { - final_args["[AGEMONTHS]"] = LLTrans::getString("Age1Month"); - return LLTrans::getString("YearsMonthsOld", final_args); - } - - // Now for age in weeks - S32 age_weeks = age_days / 7; - age_days = age_days % 7; - if (age_weeks > 1) - { - args["[WEEKS]"] = llformat("%d", age_weeks); - final_args["[AGEWEEKS]"] = LLTrans::getString("AgeWeeks", args); - return LLTrans::getString("WeeksOld", final_args); - } - else if (age_weeks == 1) - { - final_args["[AGEWEEKS]"] = LLTrans::getString("Age1Week"); - return LLTrans::getString("WeeksOld", final_args); - } - - // Down to days now - if (age_days > 1) - { - args["[DAYS]"] = llformat("%d", age_days); - final_args["[AGEDAYS]"] = LLTrans::getString("AgeDays", args); - return LLTrans::getString("DaysOld", final_args); - } - else if (age_days == 1) - { - final_args["[AGEDAYS]"] = LLTrans::getString("Age1Day"); - return LLTrans::getString("DaysOld", final_args); - } - else - { - return LLTrans::getString("TodayOld"); - } -} //static diff --git a/indra/newview/llavatarpropertiesprocessor.h b/indra/newview/llavatarpropertiesprocessor.h index 79d109f1db..ea80c3d4f8 100644 --- a/indra/newview/llavatarpropertiesprocessor.h +++ b/indra/newview/llavatarpropertiesprocessor.h @@ -36,6 +36,7 @@ #include "lluuid.h" #include "llsingleton.h" #include "v3dmath.h" // LLVector3d +#include <list> #include <map> /* @@ -147,8 +148,7 @@ class LLAvatarPropertiesProcessor public: LLAvatarPropertiesProcessor(); - virtual ~LLAvatarPropertiesProcessor() - {} + virtual ~LLAvatarPropertiesProcessor(); void addObserver(const LLUUID& avatar_id, LLAvatarPropertiesObserver* observer); @@ -174,11 +174,6 @@ public: void sendPickDelete(const LLUUID& pick_id); - // Convert a date provided by the server (MM/DD/YYYY) into a localized, - // human-readable age (1 year, 2 months) using translation strings from - // the XML file. - static std::string ageFromDate(const std::string& date_string); - // Returns translated, human readable string for account type, such // as "Resident" or "Linden Employee". Used for profiles, inspectors. static std::string accountType(const LLAvatarData* avatar_data); diff --git a/indra/newview/llchatitemscontainerctrl.cpp b/indra/newview/llchatitemscontainerctrl.cpp index c2d7e0d935..25620c2aed 100644 --- a/indra/newview/llchatitemscontainerctrl.cpp +++ b/indra/newview/llchatitemscontainerctrl.cpp @@ -236,7 +236,7 @@ BOOL LLNearbyChatToastPanel::handleMouseDown (S32 x, S32 y, MASK mask) S32 local_y = y - msg_inspector->getRect().mBottom - caption->getRect().mBottom; if(msg_inspector->pointInView(local_x, local_y)) { - LLFloaterReg::showInstance("inspect_avatar", mFromID); + LLFloaterReg::showInstance("inspect_avatar", LLSD().insert("avatar_id", mFromID)); } else { @@ -262,7 +262,7 @@ bool LLNearbyChatToastPanel::canAddText () LLChatMsgBox* msg_text = findChild<LLChatMsgBox>("msg_text"); if(!msg_text) return false; - return msg_text->getTextLinesNum()<10; + return msg_text->getLineCount()<10; } BOOL LLNearbyChatToastPanel::handleRightMouseDown(S32 x, S32 y, MASK mask) diff --git a/indra/newview/llchatmsgbox.cpp b/indra/newview/llchatmsgbox.cpp index bd0c36b44a..6eaafc9059 100644 --- a/indra/newview/llchatmsgbox.cpp +++ b/indra/newview/llchatmsgbox.cpp @@ -39,88 +39,60 @@ static LLDefaultChildRegistry::Register<LLChatMsgBox> r("text_chat"); -LLChatMsgBox::Params::Params() : - block_spacing("block_spacing", 10) +class ChatSeparator : public LLTextSegment { - line_spacing = 4; -} +public: + ChatSeparator(S32 start, S32 end) + : LLTextSegment(start, end), + mEditor(NULL) + {} -LLChatMsgBox::LLChatMsgBox(const Params& p) : - LLTextBox(p), - mBlockSpacing(p.block_spacing) -{} + /*virtual*/ void linkToDocument(class LLTextBase* editor) + { + mEditor = editor; + } -void LLChatMsgBox::addText( const LLStringExplicit& text ) -{ - LLWString t = mText.getWString(); - if (! t.empty()) + /*virtual*/ void unlinkFromDocument(class LLTextBase* editor) { - t += '\n'; + mEditor = NULL; } - t += getWrappedText(text); - LLTextBox::setText(wstring_to_utf8str(t)); - mSeparatorOffset.push_back(getLength()); -} -void LLChatMsgBox::setText(const LLStringExplicit& text) -{ - mSeparatorOffset.clear(); - mText.clear(); - addText(text); -} + /*virtual*/ S32 getWidth(S32 first_char, S32 num_chars) const + { + return mEditor->getDocumentPanel()->getRect().getWidth(); + } -void LLChatMsgBox::setValue(const LLSD& value ) -{ - setText(value.asString()); -} + /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect) + { + gl_line_2d(draw_rect.mLeft + 5, draw_rect.getCenterY(), draw_rect.mRight - 5, draw_rect.getCenterY(), LLColor4::grey); + return draw_rect.getWidth(); + } + +private: + LLTextBase* mEditor; +}; -S32 LLChatMsgBox::getTextPixelHeight() -{ - S32 num_blocks = mSeparatorOffset.size(); - S32 num_lines = getTextLinesNum(); - return (S32)(num_lines * mDefaultFont->getLineHeight() + \ - (num_lines-1) * mLineSpacing + \ - (num_blocks-1) * mBlockSpacing + \ - 2 * mLineSpacing); -} -S32 LLChatMsgBox::getTextLinesNum() +LLChatMsgBox::Params::Params() : + block_spacing("block_spacing", 10) { - S32 num_lines = getLineCount(); - if (num_lines < 1) - { - num_lines = 1; - } - - return num_lines; + line_spacing.pixels = 4; } -void LLChatMsgBox::drawText(S32 x, S32 y, const LLWString &text, const LLColor4 &color) -{ - S32 start = 0; - S32 width = getRect().getWidth()-10; +LLChatMsgBox::LLChatMsgBox(const Params& p) : + LLTextBox(p), + mBlockSpacing(p.block_spacing) +{} - // iterate through each block of text that has been added - y -= mLineSpacing; - for (std::vector<S32>::iterator it = mSeparatorOffset.begin(); it != mSeparatorOffset.end() ;) +void LLChatMsgBox::addText( const LLStringExplicit& text ) +{ + S32 length = getLength(); + // if there is existing text, add a separator + if (length > 0) { - // display the text for this block - S32 num_chars = *it - start; - LLWString text = mDisplayText.substr(start, num_chars); - LLTextBox::drawText(x, y, text, color); - - // exit the loop if this is the last text block - start += num_chars + 1; // skip the newline - if (++it == mSeparatorOffset.end()) - { - break; - } - - // output a separator line between blocks - S32 num_lines = std::count(text.begin(), text.end(), '\n') + 1; - y -= num_lines * (llfloor(mDefaultFont->getLineHeight()) + mLineSpacing); - S32 sep_y = y - mBlockSpacing/2 + mLineSpacing/2; - gl_line_2d(5, sep_y, width, sep_y, LLColor4::grey); - y -= mBlockSpacing; + // chat separator exists right before the null terminator + insertSegment(new ChatSeparator(length - 1, length - 1)); } + // prepend newline only if there is some existing text + appendText(text, length > 0); } diff --git a/indra/newview/llchatmsgbox.h b/indra/newview/llchatmsgbox.h index b81b740bdc..df29db58c3 100644 --- a/indra/newview/llchatmsgbox.h +++ b/indra/newview/llchatmsgbox.h @@ -61,18 +61,10 @@ protected: friend class LLUICtrlFactory; public: - void setText(const LLStringExplicit &text); void addText(const LLStringExplicit &text); - S32 getTextPixelHeight(); - S32 getTextLinesNum(); - - /*virtual*/ void setValue(const LLSD &value); - /*virtual*/ void drawText(S32 x, S32 y, const LLWString &text, const LLColor4 &color); - private: S32 mBlockSpacing; - std::vector<S32> mSeparatorOffset; }; #endif diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index 98e492cada..23664fa6d6 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -1305,7 +1305,7 @@ void LLChicletNotificationCounterCtrl::setCounter(S32 counter) LLRect LLChicletNotificationCounterCtrl::getRequiredRect() { LLRect rc; - S32 text_width = getFont()->getWidth(getText()); + S32 text_width = getContentsRect().getWidth(); rc.mRight = rc.mLeft + llmax(text_width, mInitialWidth); diff --git a/indra/newview/llcolorswatch.cpp b/indra/newview/llcolorswatch.cpp index 113f4c2c54..7b75c77a1e 100644 --- a/indra/newview/llcolorswatch.cpp +++ b/indra/newview/llcolorswatch.cpp @@ -87,7 +87,7 @@ LLColorSwatchCtrl::LLColorSwatchCtrl(const Params& p) tp.rect(LLRect( 0, BTN_HEIGHT_SMALL, getRect().getWidth(), 0 )); } - tp.text(p.label); + tp.initial_value(p.label()); mCaption = LLUICtrlFactory::create<LLTextBox>(tp); addChild( mCaption ); diff --git a/indra/newview/lldateutil.cpp b/indra/newview/lldateutil.cpp new file mode 100644 index 0000000000..040fad3c4a --- /dev/null +++ b/indra/newview/lldateutil.cpp @@ -0,0 +1,140 @@ +/** +* @file lldateutil.cpp +* +* $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 "llviewerprecompiledheaders.h" + +#include "lldateutil.h" + +// Linden libraries +#include "lltrans.h" +#include "llui.h" + +static S32 age_days_from_date(const std::string& date_string, + const LLDate& now) +{ + // Convert string date to malleable representation + S32 month, day, year; + S32 matched = sscanf(date_string.c_str(), "%d/%d/%d", &month, &day, &year); + if (matched != 3) return S32_MIN; + + // Create ISO-8601 date string + std::string iso8601_date_string = + llformat("%04d-%02d-%02dT00:00:00Z", year, month, day); + LLDate date(iso8601_date_string); + + // Correct for the fact that account creation dates are in Pacific time, + // == UTC - 8 + F64 date_secs_since_epoch = date.secondsSinceEpoch(); + date_secs_since_epoch += 8.0 * 60.0 * 60.0; + + // Convert seconds from epoch to seconds from now + F64 now_secs_since_epoch = now.secondsSinceEpoch(); + F64 age_secs = now_secs_since_epoch - date_secs_since_epoch; + + // We don't care about sub-day times + const F64 SEC_PER_DAY = 24.0 * 60.0 * 60.0; + S32 age_days = lltrunc(age_secs / SEC_PER_DAY); + + return age_days; +} + +std::string LLDateUtil::ageFromDate(const std::string& date_string, + const LLDate& now) +{ + S32 age_days = age_days_from_date(date_string, now); + if (age_days == S32_MIN) return "???"; + + // Noun pluralization depends on language + std::string lang = LLUI::getLanguage(); + + // Try for age in round number of years + LLStringUtil::format_map_t args; + S32 age_years = age_days / 365; + age_days = age_days % 365; + // *NOTE: This is wrong. Not all months have 30 days, but we don't have a library + // for relative date arithmetic. :-( JC + S32 age_months = age_days / 30; + age_days = age_days % 30; + + if (age_months > 0 || age_years > 0) + { + args["[AGEYEARS]"] = + LLTrans::getCountString(lang, "AgeYears", age_years); + args["[AGEMONTHS]"] = + LLTrans::getCountString(lang, "AgeMonths", age_months); + + // We want to display times like: + // 2 year 2 months + // 2 years (implicitly 0 months) + // 11 months + if (age_years > 0) + { + if (age_months > 0) + { + return LLTrans::getString("YearsMonthsOld", args); + } + else + { + return LLTrans::getString("YearsOld", args); + } + } + else // age_years == 0 + { + return LLTrans::getString("MonthsOld", args); + } + } + // you're 0 months old, display in weeks or days + + // Now for age in weeks + S32 age_weeks = age_days / 7; + age_days = age_days % 7; + if (age_weeks > 0) + { + args["[AGEWEEKS]"] = + LLTrans::getCountString(lang, "AgeWeeks", age_weeks); + return LLTrans::getString("WeeksOld", args); + } + + // Down to days now + if (age_days > 0) + { + args["[AGEDAYS]"] = + LLTrans::getCountString(lang, "AgeDays", age_days); + return LLTrans::getString("DaysOld", args); + } + + return LLTrans::getString("TodayOld"); +} + +std::string LLDateUtil::ageFromDate(const std::string& date_string) +{ + return ageFromDate(date_string, LLDate::now()); +} diff --git a/indra/newview/lldateutil.h b/indra/newview/lldateutil.h new file mode 100644 index 0000000000..041be07f12 --- /dev/null +++ b/indra/newview/lldateutil.h @@ -0,0 +1,49 @@ +/** +* @file lldateutil.h +* +* $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 LLDATEUTIL_H +#define LLDATEUTIL_H + +class LLDate; + +namespace LLDateUtil +{ + // Convert a date provided by the server (MM/DD/YYYY) into a localized, + // human-readable age (1 year, 2 months) using translation strings. + // Pass LLDate::now() for now. + // Used for avatar inspectors and profiles. + std::string ageFromDate(const std::string& date_string, const LLDate& now); + + // Calls the above with LLDate::now() + std::string ageFromDate(const std::string& date_string); +} + +#endif diff --git a/indra/newview/lldebugmessagebox.cpp b/indra/newview/lldebugmessagebox.cpp index 786473eb9b..29e375c9fa 100644 --- a/indra/newview/lldebugmessagebox.cpp +++ b/indra/newview/lldebugmessagebox.cpp @@ -131,7 +131,7 @@ LLDebugVarMessageBox::LLDebugVarMessageBox(const std::string& title, EDebugVarTy LLTextBox::Params params; params.name("value"); - params.text(params.name); + params.initial_value(params.name()); params.rect(LLRect(20,20,190,0)); mText = LLUICtrlFactory::create<LLTextBox> (params); addChild(mText); diff --git a/indra/newview/llexpandabletextbox.cpp b/indra/newview/llexpandabletextbox.cpp index 131f9ceaf0..f8f5db9d7e 100644 --- a/indra/newview/llexpandabletextbox.cpp +++ b/indra/newview/llexpandabletextbox.cpp @@ -78,6 +78,9 @@ void LLExpandableTextBox::LLTextBoxEx::draw() LLUICtrl::draw(); } +/* LLTextBox has been rewritten, the variables referenced in this code +no longer exist. + void LLExpandableTextBox::LLTextBoxEx::drawText( S32 x, S32 y, const LLWString &text, const LLColor4& color ) { // *NOTE:dzaporozhan: @@ -141,6 +144,7 @@ void LLExpandableTextBox::LLTextBoxEx::drawText( S32 x, S32 y, const LLWString & } } } +*/ void LLExpandableTextBox::LLTextBoxEx::showExpandText(S32 y) { @@ -161,8 +165,14 @@ S32 LLExpandableTextBox::LLTextBoxEx::getCropTextWidth() return mExpandTextBox->getRect().mLeft - getHPad() * 2; } +/* +// *NOTE:James: +// LLTextBox::drawText() has been completely rewritten, as it now handles +// arbitrarily styled segments of text. This needs to be rebuilt. + void LLExpandableTextBox::LLTextBoxEx::drawTextSegments(S32 init_x, S32 init_y, const LLWString &text) { + // *NOTE:dzaporozhan: // Copy/paste from LLTextBox::drawTextSegments in order to modify last // line width if needed and who "More" link @@ -270,6 +280,7 @@ void LLExpandableTextBox::LLTextBoxEx::drawTextSegments(S32 init_x, S32 init_y, } } } +*/ S32 LLExpandableTextBox::LLTextBoxEx::getVerticalTextDelta() { @@ -422,8 +433,11 @@ void LLExpandableTextBox::expandTextBox() // disable horizontal scrollbar text_box_rect.mRight -= scrollbar_size; + // text box size has changed - redo text wrap - mTextBox->setWrappedText(mText, text_box_rect.getWidth()); + // Should be handled automatically in reshape() below. JC + //mTextBox->setWrappedText(mText, text_box_rect.getWidth()); + // recalculate text delta since text wrap changed text height text_delta = mTextBox->getVerticalTextDelta() + mTextBox->getVPad() * 2; } @@ -460,7 +474,8 @@ void LLExpandableTextBox::collapseTextBox() updateTextBoxRect(); - mTextBox->setWrappedText(mText); + // Should be handled automatically in reshape above. JC + //mTextBox->setWrappedText(mText); if(gFocusMgr.getTopCtrl() == this) { gFocusMgr.setTopCtrl(NULL); diff --git a/indra/newview/llexpandabletextbox.h b/indra/newview/llexpandabletextbox.h index 0a5a4c8b75..0b9c3f7258 100644 --- a/indra/newview/llexpandabletextbox.h +++ b/indra/newview/llexpandabletextbox.h @@ -64,17 +64,17 @@ protected: */ /*virtual*/ void draw(); - /** - * Draws simple text(no urls) line by line, will show or hide "More" link - * if needed. - */ - /*virtual*/ void drawText( S32 x, S32 y, const LLWString &text, const LLColor4& color ); - - /** - * Draws segmented text(with urls) line by line. Will show or hide "More" link - * if needed - */ - void drawTextSegments(S32 x, S32 y, const LLWString &text); +// /** +// * Draws simple text(no urls) line by line, will show or hide "More" link +// * if needed. +// */ +// /*virtual*/ void drawText( S32 x, S32 y, const LLWString &text, const LLColor4& color ); +// +// /** +// * Draws segmented text(with urls) line by line. Will show or hide "More" link +// * if needed +// */ +// void drawTextSegments(S32 x, S32 y, const LLWString &text); /** * Returns difference between text box height and text height. diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp index 8af3a8b539..0bd4389b50 100644 --- a/indra/newview/llfasttimerview.cpp +++ b/indra/newview/llfasttimerview.cpp @@ -244,7 +244,7 @@ BOOL LLFastTimerView::handleHover(S32 x, S32 y, MASK mask) } -BOOL LLFastTimerView::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen) +BOOL LLFastTimerView::handleToolTip(S32 x, S32 y, MASK mask) { if(LLFastTimer::sPauseHistory && mBarRect.pointInRect(x, y)) { @@ -254,7 +254,7 @@ BOOL LLFastTimerView::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& stic LLRect screen_rect; localRectToScreen(mToolTipRect, &screen_rect); - LLToolTipMgr::instance().show(LLToolTipParams() + LLToolTipMgr::instance().show(LLToolTip::Params() .message(mHoverTimer->getToolTip(LLFastTimer::NamedTimer::HISTORY_NUM - mScrollIndex - mHoverBarIndex)) .sticky_rect(screen_rect)); diff --git a/indra/newview/llfasttimerview.h b/indra/newview/llfasttimerview.h index 97e4e94460..2bb023ab14 100644 --- a/indra/newview/llfasttimerview.h +++ b/indra/newview/llfasttimerview.h @@ -57,7 +57,7 @@ public: virtual BOOL handleRightMouseDown(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 handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen); + virtual BOOL handleToolTip(S32 x, S32 y, MASK mask); virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); virtual void draw(); diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index ea947a5565..48fcb6b6de 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -123,7 +123,7 @@ class LLFavoriteLandmarkButton : public LLButton { public: - BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect) + BOOL handleToolTip(S32 x, S32 y, MASK mask) { LLToolTipMgr::instance().show(mUrlGetter.getSLURL()); return TRUE; @@ -201,7 +201,7 @@ private: class LLFavoriteLandmarkMenuItem : public LLMenuItemCallGL { public: - BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect) + BOOL handleToolTip(S32 x, S32 y, MASK mask) { LLToolTipMgr::instance().show(mUrlGetter.getSLURL()); return TRUE; diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp index e28d223a10..a4c38d03aa 100644 --- a/indra/newview/llfloaterabout.cpp +++ b/indra/newview/llfloaterabout.cpp @@ -115,9 +115,6 @@ BOOL LLFloaterAbout::postBuild() getChild<LLUICtrl>("copy_btn")->setCommitCallback( boost::bind(&LLFloaterAbout::onClickCopyToClipboard, this)); - // make sure that we handle hyperlinks in the About text - support_widget->setParseHTML(TRUE); - // Version string std::string version = LLTrans::getString("APP_NAME") + llformat(" %d.%d.%d (%d) %s %s (%s)\n", @@ -241,7 +238,11 @@ BOOL LLFloaterAbout::postBuild() support.append(getString ("PacketsLost", args) + "\n"); } - support_widget->appendColoredText(support, FALSE, FALSE, LLUIColorTable::instance().getColor("TextFgReadOnlyColor")); + support_widget->appendText(support, + FALSE, + LLStyle::Params() + .color(LLUIColorTable::instance().getColor("TextFgReadOnlyColor"))); + support_widget->blockUndo(); // Fix views support_widget->setCursorPos(0); diff --git a/indra/newview/llfloaterbuy.cpp b/indra/newview/llfloaterbuy.cpp index a7aaf71ef6..473d5ce827 100644 --- a/indra/newview/llfloaterbuy.cpp +++ b/indra/newview/llfloaterbuy.cpp @@ -73,6 +73,8 @@ BOOL LLFloaterBuy::postBuild() // This also avoids problems where the user resizes the application window // mid-session and the saved rect is off-center. center(); + + mCloseSignal.connect(boost::bind(&LLFloaterBuy::onClose, this)); return TRUE; } @@ -307,3 +309,8 @@ void LLFloaterBuy::onClickCancel() { closeFloater(); } + +void LLFloaterBuy::onClose() +{ + mObjectSelection.clear(); +} diff --git a/indra/newview/llfloaterbuy.h b/indra/newview/llfloaterbuy.h index ee54303267..2ec66136b2 100644 --- a/indra/newview/llfloaterbuy.h +++ b/indra/newview/llfloaterbuy.h @@ -70,6 +70,7 @@ protected: void onClickBuy(); void onClickCancel(); + void onClose(); private: LLSafeHandle<LLObjectSelection> mObjectSelection; diff --git a/indra/newview/llfloaterbuyland.cpp b/indra/newview/llfloaterbuyland.cpp index 7075719299..2c2a5107f5 100644 --- a/indra/newview/llfloaterbuyland.cpp +++ b/indra/newview/llfloaterbuyland.cpp @@ -50,6 +50,7 @@ #include "lllineeditor.h" #include "llnotify.h" #include "llparcel.h" +#include "llslurl.h" #include "llstatusbar.h" #include "lltextbox.h" #include "lltexturectrl.h" @@ -71,7 +72,7 @@ const F32 GROUP_LAND_BONUS_FACTOR = 1.1f; const F64 CURRENCY_ESTIMATE_FREQUENCY = 0.5; // how long of a pause in typing a currency buy amount before an - // esimate is fetched from the server + // estimate is fetched from the server class LLFloaterBuyLandUI : public LLFloater @@ -177,6 +178,11 @@ public: void sendBuyLand(); void updateNames(); + // Name cache callback + void updateGroupName(const LLUUID& id, + const std::string& first_name, + const std::string& last_name, + BOOL is_group); void refreshUI(); @@ -201,16 +207,6 @@ public: }; -static void cacheNameUpdateRefreshesBuyLand(const LLUUID&, - const std::string&, const std::string&, BOOL) -{ - LLFloaterBuyLandUI* ui = LLFloaterReg::findTypedInstance<LLFloaterBuyLandUI>("buy_land"); - if (ui) - { - ui->updateNames(); - } -} - // static void LLFloaterBuyLand::buyLand( LLViewerRegion* region, LLParcelSelectionHandle parcel, bool is_for_group) @@ -296,13 +292,6 @@ LLFloaterBuyLandUI::LLFloaterBuyLandUI(const LLSD& key) mChildren(*this), mCurrency(*this), mTransaction(0), mParcelBuyInfo(0) { - static bool observingCacheName = false; - if (!observingCacheName) - { - gCacheName->addObserver(&cacheNameUpdateRefreshesBuyLand); - observingCacheName = true; - } - LLViewerParcelMgr::getInstance()->addObserver(&mParcelSelectionObserver); // LLUICtrlFactory::getInstance()->buildFloater(sInstance, "floater_buy_land.xml"); @@ -788,14 +777,30 @@ void LLFloaterBuyLandUI::updateNames() } else if (parcelp->getIsGroupOwned()) { - gCacheName->getGroupName(parcelp->getGroupID(), mParcelSellerName); + gCacheName->get(parcelp->getGroupID(), TRUE, + boost::bind(&LLFloaterBuyLandUI::updateGroupName, this, + _1, _2, _3, _4)); } else { - gCacheName->getFullName(parcelp->getOwnerID(), mParcelSellerName); + mParcelSellerName = + LLSLURL::buildCommand("agent", parcelp->getOwnerID(), "inspect"); } } +void LLFloaterBuyLandUI::updateGroupName(const LLUUID& id, + const std::string& first_name, + const std::string& last_name, + BOOL is_group) +{ + LLParcel* parcelp = mParcel->getParcel(); + if (parcelp + && parcelp->getGroupID() == id) + { + // request is current + mParcelSellerName = first_name; + } +} void LLFloaterBuyLandUI::startTransaction(TransactionType type, const LLXMLRPCValue& params) { @@ -1036,9 +1041,7 @@ void LLFloaterBuyLandUI::refreshUI() if (message) { message->setVisible(true); - message->setWrappedText( - !mCanBuy ? mCannotBuyReason : "(waiting for data)" - ); + message->setValue(LLSD(!mCanBuy ? mCannotBuyReason : "(waiting for data)")); } childSetVisible("error_web", @@ -1148,7 +1151,7 @@ void LLFloaterBuyLandUI::refreshUI() } } - childSetWrappedText("land_use_reason", message); + childSetValue("land_use_reason", message); childShow("step_2"); childShow("land_use_action"); diff --git a/indra/newview/llfloaterchat.cpp b/indra/newview/llfloaterchat.cpp index ca43f41d05..6d2e959352 100644 --- a/indra/newview/llfloaterchat.cpp +++ b/indra/newview/llfloaterchat.cpp @@ -56,6 +56,7 @@ #include "llchatbar.h" #include "llrecentpeople.h" #include "llpanelblockedlist.h" +#include "llslurl.h" #include "llstatusbar.h" #include "llviewertexteditor.h" #include "llviewergesture.h" // for triggering gestures @@ -162,7 +163,7 @@ void add_timestamped_line(LLViewerTextEditor* edit, LLChat chat, const LLColor4& if (chat.mSourceType == CHAT_SOURCE_AGENT && chat.mFromID != LLUUID::null) { - chat.mURL = llformat("secondlife:///app/agent/%s/about",chat.mFromID.asString().c_str()); + chat.mURL = LLSLURL::buildCommand("agent", chat.mFromID, "inspect"); } // If the chat line has an associated url, link it up to the name. @@ -171,10 +172,12 @@ void add_timestamped_line(LLViewerTextEditor* edit, LLChat chat, const LLColor4& { std::string start_line = line.substr(0, chat.mFromName.length() + 1); line = line.substr(chat.mFromName.length() + 1); - edit->appendStyledText(start_line, false, prepend_newline, LLStyleMap::instance().lookup(chat.mFromID,chat.mURL)); + edit->appendText(start_line, prepend_newline, LLStyleMap::instance().lookup(chat.mFromID,chat.mURL)); + edit->blockUndo(); prepend_newline = false; } - edit->appendColoredText(line, false, prepend_newline, color); + edit->appendText(line, prepend_newline, LLStyle::Params().color(color)); + edit->blockUndo(); } void log_chat_text(const LLChat& chat) @@ -216,12 +219,6 @@ void LLFloaterChat::addChatHistory(const LLChat& chat, bool log_to_file) LLViewerTextEditor* history_editor = chat_floater->getChild<LLViewerTextEditor>("Chat History Editor"); LLViewerTextEditor* history_editor_with_mute = chat_floater->getChild<LLViewerTextEditor>("Chat History Editor with mute"); - history_editor->setParseHTML(TRUE); - history_editor_with_mute->setParseHTML(TRUE); - - history_editor->setParseHighlights(TRUE); - history_editor_with_mute->setParseHighlights(TRUE); - if (!chat.mMuted) { add_timestamped_line(history_editor, chat, color); diff --git a/indra/newview/llfloaterfriends.cpp b/indra/newview/llfloaterfriends.cpp index 0c77d88efb..2c66ab502d 100644 --- a/indra/newview/llfloaterfriends.cpp +++ b/indra/newview/llfloaterfriends.cpp @@ -237,7 +237,7 @@ BOOL LLPanelFriends::addFriend(const LLUUID& agent_id) LLSD& friend_column = element["columns"][LIST_FRIEND_NAME]; friend_column["column"] = "friend_name"; friend_column["value"] = fullname; - friend_column["font"] = "SANSSERIF"; + friend_column["font"]["name"] = "SANSSERIF"; friend_column["font"]["style"] = "NORMAL"; LLSD& online_status_column = element["columns"][LIST_ONLINE_STATUS]; @@ -614,7 +614,7 @@ void LLPanelFriends::onClickPay(void* user_data) std::vector<LLUUID> ids = panelp->getSelectedIDs(); if(ids.size() == 1) { - handle_pay_by_id(ids[0]); + LLAvatarActions::pay(ids[0]); } } diff --git a/indra/newview/llfloatergesture.cpp b/indra/newview/llfloatergesture.cpp index 431bc09d86..e0fe87f9ae 100644 --- a/indra/newview/llfloatergesture.cpp +++ b/indra/newview/llfloatergesture.cpp @@ -205,7 +205,7 @@ void LLFloaterGesture::buildGestureList() element["columns"][0]["column"] = "trigger"; element["columns"][0]["value"] = gesture->mTrigger; - element["columns"][0]["font"] = "SANSSERIF"; + element["columns"][0]["font"]["name"] = "SANSSERIF"; element["columns"][0]["font"]["style"] = font_style; std::string key_string = LLKeyboard::stringFromKey(gesture->mKey); @@ -232,13 +232,13 @@ void LLFloaterGesture::buildGestureList() } element["columns"][1]["column"] = "shortcut"; element["columns"][1]["value"] = buffer; - element["columns"][1]["font"] = "SANSSERIF"; + element["columns"][1]["font"]["name"] = "SANSSERIF"; element["columns"][1]["font"]["style"] = font_style; // hidden column for sorting element["columns"][2]["column"] = "key"; element["columns"][2]["value"] = key_string; - element["columns"][2]["font"] = "SANSSERIF"; + element["columns"][2]["font"]["name"] = "SANSSERIF"; element["columns"][2]["font"]["style"] = font_style; // Only add "playing" if we've got the name, less confusing. JC @@ -248,26 +248,26 @@ void LLFloaterGesture::buildGestureList() } element["columns"][3]["column"] = "name"; element["columns"][3]["value"] = item_name; - element["columns"][3]["font"] = "SANSSERIF"; + element["columns"][3]["font"]["name"] = "SANSSERIF"; element["columns"][3]["font"]["style"] = font_style; } else { element["columns"][0]["column"] = "trigger"; element["columns"][0]["value"] = ""; - element["columns"][0]["font"] = "SANSSERIF"; + element["columns"][0]["font"]["name"] = "SANSSERIF"; element["columns"][0]["font"]["style"] = font_style; element["columns"][0]["column"] = "trigger"; element["columns"][0]["value"] = "---"; - element["columns"][0]["font"] = "SANSSERIF"; + element["columns"][0]["font"]["name"] = "SANSSERIF"; element["columns"][0]["font"]["style"] = font_style; element["columns"][2]["column"] = "key"; element["columns"][2]["value"] = "~~~"; - element["columns"][2]["font"] = "SANSSERIF"; + element["columns"][2]["font"]["name"] = "SANSSERIF"; element["columns"][2]["font"]["style"] = font_style; element["columns"][3]["column"] = "name"; element["columns"][3]["value"] = item_name; - element["columns"][3]["font"] = "SANSSERIF"; + element["columns"][3]["font"]["name"] = "SANSSERIF"; element["columns"][3]["font"]["style"] = font_style; } list->addElement(element, ADD_BOTTOM); diff --git a/indra/newview/llfloatergroups.cpp b/indra/newview/llfloatergroups.cpp index b1f40d9d1d..3648898f28 100644 --- a/indra/newview/llfloatergroups.cpp +++ b/indra/newview/llfloatergroups.cpp @@ -372,7 +372,7 @@ void init_group_list(LLScrollListCtrl* ctrl, const LLUUID& highlight_id, U64 pow element["id"] = id; element["columns"][0]["column"] = "name"; element["columns"][0]["value"] = group_datap->mName; - element["columns"][0]["font"] = "SANSSERIF"; + element["columns"][0]["font"]["name"] = "SANSSERIF"; element["columns"][0]["font"]["style"] = style; group_list->addElement(element, ADD_SORTED); @@ -390,7 +390,7 @@ void init_group_list(LLScrollListCtrl* ctrl, const LLUUID& highlight_id, U64 pow element["id"] = LLUUID::null; element["columns"][0]["column"] = "name"; element["columns"][0]["value"] = LLTrans::getString("GroupsNone"); - element["columns"][0]["font"] = "SANSSERIF"; + element["columns"][0]["font"]["name"] = "SANSSERIF"; element["columns"][0]["font"]["style"] = style; group_list->addElement(element, ADD_TOP); diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 3fe7d8d9da..a378a511b5 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -62,6 +62,7 @@ #include "llscrolllistitem.h" #include "llscrolllistcell.h" #include "llselectmgr.h" +#include "llslurl.h" #include "llspinctrl.h" #include "lltabcontainer.h" #include "lltextbox.h" @@ -754,7 +755,7 @@ void LLPanelLandGeneral::refreshNames() else { // Figure out the owner's name - gCacheName->getFullName(parcel->getOwnerID(), owner); + owner = LLSLURL::buildCommand("agent", parcel->getOwnerID(), "inspect"); } if(LLParcel::OS_LEASE_PENDING == parcel->getOwnershipStatus()) @@ -763,18 +764,11 @@ void LLPanelLandGeneral::refreshNames() } mTextOwner->setText(owner); - std::string group; - if(!parcel->getGroupID().isNull()) - { - gCacheName->getGroupName(parcel->getGroupID(), group); - } - mTextGroup->setText(group); - const LLUUID& auth_buyer_id = parcel->getAuthorizedBuyerID(); if(auth_buyer_id.notNull()) { std::string name; - gCacheName->getFullName(auth_buyer_id, name); + name = LLSLURL::buildCommand("agent", auth_buyer_id, "inspect"); mSaleInfoForSale2->setTextArg("[BUYER]", name); } else @@ -787,7 +781,20 @@ void LLPanelLandGeneral::refreshNames() // virtual void LLPanelLandGeneral::draw() { - refreshNames(); + LLParcel *parcel = mParcel->getParcel(); + if (parcel) + { + std::string group; + if (!parcel->getGroupID().isNull()) + { + // *TODO: Change to "inspect" when we have group inspectors and + // move into refreshNames() above + // group = LLSLURL::buildCommand("group", parcel->getGroupID(), "inspect"); + gCacheName->getGroupName(parcel->getGroupID(), group); + } + mTextGroup->setText(group); + } + LLPanel::draw(); } diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 10276ba36d..0330a8c692 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -37,7 +37,6 @@ #include <algorithm> #include <functional> -#include "llcachename.h" #include "lldir.h" #include "lldispatcher.h" #include "llglheaders.h" @@ -67,6 +66,7 @@ #include "llnamelistctrl.h" #include "llscrolllistitem.h" #include "llsliderctrl.h" +#include "llslurl.h" #include "llspinctrl.h" #include "lltabcontainer.h" #include "lltextbox.h" @@ -2560,30 +2560,6 @@ void LLPanelEstateInfo::setAccessAllowedEnabled(bool enable_agent, } } -// static -void LLPanelEstateInfo::callbackCacheName( - const LLUUID& id, - const std::string& first, - const std::string& last, - BOOL is_group) -{ - LLPanelEstateInfo* self = LLFloaterRegionInfo::getPanelEstate(); - if (!self) return; - - std::string name; - - if (id.isNull()) - { - name = "(none)"; - } - else - { - name = first + " " + last; - } - - self->setOwnerName(name); -} - void LLPanelEstateInfo::clearAccessLists() { LLNameListCtrl* name_list = getChild<LLNameListCtrl>("allowed_avatar_name_list"); @@ -2960,7 +2936,7 @@ BOOL LLPanelEstateCovenant::sendUpdate() return TRUE; } -const std::string& LLPanelEstateCovenant::getEstateName() const +std::string LLPanelEstateCovenant::getEstateName() const { return mEstateNameText->getText(); } @@ -3011,7 +2987,7 @@ void LLPanelEstateCovenant::updateEstateOwnerName(const std::string& name) } } -const std::string& LLPanelEstateCovenant::getOwnerName() const +std::string LLPanelEstateCovenant::getOwnerName() const { return mEstateOwnerText->getText(); } @@ -3069,8 +3045,9 @@ bool LLDispatchEstateUpdateInfo::operator()( LLUUID owner_id(strings[1]); regionp->setOwner(owner_id); // Update estate owner name in UI - const BOOL is_group = FALSE; - gCacheName->get(owner_id, is_group, &LLPanelEstateInfo::callbackCacheName); + std::string owner_name = + LLSLURL::buildCommand("agent", owner_id, "inspect"); + panel->setOwnerName(owner_name); U32 estate_id = strtoul(strings[2].c_str(), NULL, 10); panel->setEstateID(estate_id); diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h index be4becf7e7..68ed4e0c89 100644 --- a/indra/newview/llfloaterregioninfo.h +++ b/indra/newview/llfloaterregioninfo.h @@ -338,14 +338,6 @@ public: // are ignored, so must disable UI. void setAccessAllowedEnabled(bool enable_agent, bool enable_group, bool enable_ban); - // this must have the same function signature as - // llmessage/llcachename.h:LLCacheNameCallback - static void callbackCacheName( - const LLUUID& id, - const std::string& first, - const std::string& last, - BOOL is_group); - protected: virtual BOOL sendUpdate(); // confirmation dialog callback @@ -400,9 +392,9 @@ public: const LLUUID& getCovenantID() const { return mCovenantID; } void setCovenantID(const LLUUID& id) { mCovenantID = id; } - const std::string& getEstateName() const; + std::string getEstateName() const; void setEstateName(const std::string& name); - const std::string& getOwnerName() const; + std::string getOwnerName() const; void setOwnerName(const std::string& name); void setCovenantTextEditor(const std::string& text); diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index 3dcdc2f56e..1ec869da73 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -32,14 +32,13 @@ #include "llviewerprecompiledheaders.h" -#include <sstream> - // self include #include "llfloaterreporter.h" +#include <sstream> + // linden library includes #include "llassetstorage.h" -#include "llcachename.h" #include "llfontgl.h" #include "llgl.h" // for renderer #include "llinventory.h" @@ -48,18 +47,14 @@ #include "llversionviewer.h" #include "message.h" #include "v3math.h" -#include "lltexteditor.h" // viewer project includes #include "llagent.h" #include "llbutton.h" -#include "llcheckboxctrl.h" #include "llfloaterreg.h" -#include "lllineeditor.h" #include "lltexturectrl.h" #include "llscrolllistctrl.h" #include "llimview.h" -#include "lltextbox.h" #include "lldispatcher.h" #include "llviewerobject.h" #include "llviewerregion.h" @@ -72,6 +67,7 @@ #include "lltoolobjpicker.h" #include "lltoolmgr.h" #include "llresourcedata.h" // for LLResourceData +#include "llslurl.h" #include "llviewerwindow.h" #include "llviewertexturelist.h" #include "llworldmap.h" @@ -103,6 +99,7 @@ LLFloaterReporter::LLFloaterReporter(const LLSD& key) mObjectID(), mScreenID(), mAbuserID(), + mOwnerName(), mDeselectOnClose( FALSE ), mPicking( FALSE), mPosition(), @@ -158,6 +155,7 @@ BOOL LLFloaterReporter::postBuild() // Default text to be blank childSetText("object_name", LLStringUtil::null); childSetText("owner_name", LLStringUtil::null); + mOwnerName = LLStringUtil::null; childSetFocus("summary_edit"); @@ -174,8 +172,8 @@ BOOL LLFloaterReporter::postBuild() // abuser name is selected from a list - LLLineEditor* le = getChild<LLLineEditor>("abuser_name_edit"); - le->setEnabled( FALSE ); + LLUICtrl* le = getChild<LLUICtrl>("abuser_name_edit"); + le->setEnabled( false ); setPosBox((LLVector3d)mPosition.getValue()); LLButton* pick_btn = getChild<LLButton>("pick_btn"); @@ -299,9 +297,12 @@ void LLFloaterReporter::getObjectInfo(const LLUUID& object_id) object_owner.append("Unknown"); } childSetText("object_name", object_owner); - childSetText("owner_name", object_owner); + std::string owner_link = + LLSLURL::buildCommand("agent", mObjectID, "inspect"); + childSetText("owner_name", owner_link); childSetText("abuser_name_edit", object_owner); mAbuserID = object_id; + mOwnerName = object_owner; } else { @@ -445,6 +446,7 @@ void LLFloaterReporter::onClickObjPicker(void *userdata) self->mPicking = TRUE; self->childSetText("object_name", LLStringUtil::null); self->childSetText("owner_name", LLStringUtil::null); + self->mOwnerName = LLStringUtil::null; LLButton* pick_btn = self->getChild<LLButton>("pick_btn"); if (pick_btn) pick_btn->setToggleState(TRUE); } @@ -505,9 +507,12 @@ void LLFloaterReporter::showFromObject(const LLUUID& object_id) void LLFloaterReporter::setPickedObjectProperties(const std::string& object_name, const std::string& owner_name, const LLUUID owner_id) { childSetText("object_name", object_name); - childSetText("owner_name", owner_name); + std::string owner_link = + LLSLURL::buildCommand("agent", owner_id, "inspect"); + childSetText("owner_name", owner_link); childSetText("abuser_name_edit", owner_name); mAbuserID = owner_id; + mOwnerName = owner_name; } @@ -608,11 +613,10 @@ LLSD LLFloaterReporter::gatherReport() << LL_VIEWER_BUILD << std::endl << std::endl; std::string object_name = childGetText("object_name"); - std::string owner_name = childGetText("owner_name"); - if (!object_name.empty() && !owner_name.empty()) + if (!object_name.empty() && !mOwnerName.empty()) { details << "Object: " << object_name << "\n"; - details << "Owner: " << owner_name << "\n"; + details << "Owner: " << mOwnerName << "\n"; } diff --git a/indra/newview/llfloaterreporter.h b/indra/newview/llfloaterreporter.h index 7e8f05e3fc..917f513641 100644 --- a/indra/newview/llfloaterreporter.h +++ b/indra/newview/llfloaterreporter.h @@ -128,6 +128,8 @@ private: LLUUID mObjectID; LLUUID mScreenID; LLUUID mAbuserID; + // Store the real name, not the link, for upstream reporting + std::string mOwnerName; BOOL mDeselectOnClose; BOOL mPicking; LLVector3 mPosition; diff --git a/indra/newview/llfloaterscriptdebug.cpp b/indra/newview/llfloaterscriptdebug.cpp index 328fb6450e..3bf1848efb 100644 --- a/indra/newview/llfloaterscriptdebug.cpp +++ b/indra/newview/llfloaterscriptdebug.cpp @@ -167,6 +167,7 @@ void LLFloaterScriptDebugOutput::addLine(const std::string &utf8mesg, const std: setShortTitle(user_name); } - mHistoryEditor->appendColoredText(utf8mesg, false, true, color); + mHistoryEditor->appendText(utf8mesg, true, LLStyle::Params().color(color)); + mHistoryEditor->blockUndo(); } diff --git a/indra/newview/llfloatersellland.cpp b/indra/newview/llfloatersellland.cpp index fe98c84301..9e203c4269 100644 --- a/indra/newview/llfloatersellland.cpp +++ b/indra/newview/llfloatersellland.cpp @@ -131,7 +131,6 @@ LLFloaterSellLandUI::LLFloaterSellLandUI(const LLSD& key) mRegion(0) { LLViewerParcelMgr::getInstance()->addObserver(&mParcelSelectionObserver); -// LLUICtrlFactory::getInstance()->buildFloater(sInstance, "floater_sell_land.xml"); mCloseSignal.connect(boost::bind(&LLFloaterSellLandUI::onClose, this)); } @@ -153,7 +152,7 @@ void LLFloaterSellLandUI::SelectionObserver::changed() { mFloater->closeFloater(); } - else + else if (mFloater->getVisible()) // only update selection if sell land ui in use { mFloater->setParcel(LLViewerParcelMgr::getInstance()->getSelectionRegion(), LLViewerParcelMgr::getInstance()->getParcelSelection()); @@ -176,7 +175,7 @@ BOOL LLFloaterSellLandUI::postBuild() bool LLFloaterSellLandUI::setParcel(LLViewerRegion* region, LLParcelSelectionHandle parcel) { - if (!parcel->getParcel()) // || !can_agent_modify_parcel(parcel)) // can_agent_modify_parcel was deprecated by GROUPS + if (!parcel->getParcel()) { return false; } diff --git a/indra/newview/llfloatertestinspectors.cpp b/indra/newview/llfloatertestinspectors.cpp index c56586cb95..8af011c17a 100644 --- a/indra/newview/llfloatertestinspectors.cpp +++ b/indra/newview/llfloatertestinspectors.cpp @@ -44,6 +44,8 @@ LLFloaterTestInspectors::LLFloaterTestInspectors(const LLSD& seed) { mCommitCallbackRegistrar.add("ShowAvatarInspector", boost::bind(&LLFloaterTestInspectors::showAvatarInspector, this, _1, _2)); + mCommitCallbackRegistrar.add("ShowObjectInspector", + boost::bind(&LLFloaterTestInspectors::showObjectInspector, this, _1, _2)); } LLFloaterTestInspectors::~LLFloaterTestInspectors() @@ -77,7 +79,12 @@ void LLFloaterTestInspectors::showAvatarInspector(LLUICtrl*, const LLSD& avatar_ id = avatar_id.asUUID(); } // spawns off mouse position automatically - LLFloaterReg::showInstance("inspect_avatar", id); + LLFloaterReg::showInstance("inspect_avatar", LLSD().insert("avatar_id", id)); +} + +void LLFloaterTestInspectors::showObjectInspector(LLUICtrl*, const LLSD& object_id) +{ + LLFloaterReg::showInstance("inspect_object", LLSD().insert("object_id", object_id)); } void LLFloaterTestInspectors::onClickAvatar2D() diff --git a/indra/newview/llfloatertestinspectors.h b/indra/newview/llfloatertestinspectors.h index d2dc2248bb..6555aad4e8 100644 --- a/indra/newview/llfloatertestinspectors.h +++ b/indra/newview/llfloatertestinspectors.h @@ -50,6 +50,7 @@ private: // Button callback to show void showAvatarInspector(LLUICtrl*, const LLSD& avatar_id); + void showObjectInspector(LLUICtrl*, const LLSD& avatar_id); // Debug function hookups for buttons void onClickAvatar2D(); diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp index 7dc29379e4..c08996cc26 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -36,7 +36,7 @@ #include "llfontgl.h" #include "llcoord.h" -#include "llgl.h" +//#include "llgl.h" #include "llagent.h" #include "llbutton.h" diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp index 2eb4e7580e..266252efea 100644 --- a/indra/newview/llfloateruipreview.cpp +++ b/indra/newview/llfloateruipreview.cpp @@ -500,6 +500,14 @@ void LLFloaterUIPreview::refreshList() } } found = TRUE; + while(found) // for every inspector file that matches the pattern + { + if((found = gDirUtilp->getNextFileInDir(getLocalizedDirectory(), "inspect_*.xml", name, FALSE))) // get next file matching pattern + { + addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path) + } + } + found = TRUE; while(found) // for every menu file that matches the pattern { if((found = gDirUtilp->getNextFileInDir(getLocalizedDirectory(), "menu_*.xml", name, FALSE))) // get next file matching pattern @@ -596,20 +604,12 @@ void LLFloaterUIPreview::addFloaterEntry(const std::string& path) void LLFloaterUIPreview::onClickDisplayFloater(S32 caller_id) { displayFloater(TRUE, caller_id); - if(caller_id == PRIMARY_FLOATER) - { - mDisplayedFloater->center(); // move displayed floater to the center of the screen - } } // Saves the current floater/panel void LLFloaterUIPreview::onClickSaveFloater(S32 caller_id) { displayFloater(TRUE, caller_id, true); - if(caller_id == PRIMARY_FLOATER) - { - mDisplayedFloater->center(); // move displayed floater to the center of the screen - } } // Saves all floater/panels @@ -672,7 +672,8 @@ void LLFloaterUIPreview::displayFloater(BOOL click, S32 ID, bool save) *floaterp = new LLPreviewedFloater(this); - if(!strncmp(path.c_str(),"floater_",8)) // if it's a floater + if(!strncmp(path.c_str(),"floater_",8) + || !strncmp(path.c_str(), "inspect_", 8)) // if it's a floater { if (save) { @@ -774,13 +775,6 @@ void LLFloaterUIPreview::displayFloater(BOOL click, S32 ID, bool save) mCloseOtherButton_2->setEnabled(TRUE); } - // *TODO: Make the secondary floater pop up next to the primary one. Doesn't seem to always work if secondary was up first... - if((mDisplayedFloater && ID == 2) || (mDisplayedFloater_2 && ID == 1)) - { - mDisplayedFloater_2->setSnapTarget(mDisplayedFloater->getHandle()); - mDisplayedFloater->addDependentFloater(mDisplayedFloater_2); - } - // Add localization to title so user knows whether it's localized or defaulted to en std::string full_path = getLocalizedDirectory() + path; std::string floater_lang = "EN"; @@ -793,6 +787,9 @@ void LLFloaterUIPreview::displayFloater(BOOL click, S32 ID, bool save) (ID == 1 ? " - Primary" : " - Secondary") + std::string("]"); (*floaterp)->setTitle(new_title); + (*floaterp)->center(); + addDependentFloater(*floaterp); + if(click && ID == 1 && !save) { // set up live file to track it diff --git a/indra/newview/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp index 490929e5a6..ef54ee7d4a 100644 --- a/indra/newview/llfolderviewitem.cpp +++ b/indra/newview/llfolderviewitem.cpp @@ -563,7 +563,7 @@ const std::string& LLFolderViewItem::getSearchableLabel() const return mSearchableLabel; } -const std::string& LLFolderViewItem::getName( void ) const +std::string LLFolderViewItem::getName( void ) const { if(mListener) { @@ -947,18 +947,6 @@ void LLFolderViewItem::draw() } } } - - if( sDebugRects ) - { - drawDebugRect(); - } - - //// *HACK: also draw debug rectangles around currently-being-edited LLView, and any elements that are being highlighted by GUI preview code (see LLFloaterUIPreview) - //std::set<LLView*>::iterator iter = std::find(sPreviewHighlightedElements.begin(), sPreviewHighlightedElements.end(), this); - //if ((sEditingUI && this == sEditingUIView) || (iter != sPreviewHighlightedElements.end() && sDrawPreviewHighlights)) - //{ - // drawDebugRect(); - //} } diff --git a/indra/newview/llfolderviewitem.h b/indra/newview/llfolderviewitem.h index eec885fd29..32134670c8 100644 --- a/indra/newview/llfolderviewitem.h +++ b/indra/newview/llfolderviewitem.h @@ -255,7 +255,7 @@ public: // This method returns the actual name of the thing being // viewed. This method will ask the viewed object itself. - const std::string& getName( void ) const; + std::string getName( void ) const; const std::string& getSearchableLabel( void ) const; diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp index 905857f393..4caef8e000 100644 --- a/indra/newview/llgrouplist.cpp +++ b/indra/newview/llgrouplist.cpp @@ -301,13 +301,22 @@ void LLGroupListItem::setGroupIconVisible(bool visible) void LLGroupListItem::setActive(bool active) { // Active group should be bold. - LLFontDescriptor new_desc(mGroupNameBox->getFont()->getFontDesc()); + LLFontDescriptor new_desc(mGroupNameBox->getDefaultFont()->getFontDesc()); // *NOTE dzaporozhan // On Windows LLFontGL::NORMAL will not remove LLFontGL::BOLD if font // is predefined as bold (SansSerifSmallBold, for example) new_desc.setStyle(active ? LLFontGL::BOLD : LLFontGL::NORMAL); - mGroupNameBox->setFont(LLFontGL::getFont(new_desc)); + LLFontGL* new_font = LLFontGL::getFont(new_desc); + LLStyle::Params style_params; + style_params.font = new_font; + + // *NOTE: You cannot set the style on a text box anymore, you must + // rebuild the text. This will cause problems if the text contains + // hyperlinks, as their styles will be wrong. + std::string text = mGroupNameBox->getText(); + mGroupNameBox->clear(); + mGroupNameBox->appendText(text, false, style_params); } void LLGroupListItem::onInfoBtnClick() diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index d0be581f6d..dde36ac25b 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -200,7 +200,6 @@ BOOL LLIMFloater::postBuild() childSetCommitCallback("chat_editor", onSendMsg, this); mHistoryEditor = getChild<LLViewerTextEditor>("im_text"); - mHistoryEditor->setParseHTML(TRUE); setTitle(LLIMModel::instance().getName(mSessionID)); setDocked(true); @@ -361,19 +360,21 @@ void LLIMFloater::updateMessages() if (mLastFromName != from) { message << from << " ----- " << msg["time"].asString(); - mHistoryEditor->appendColoredText(message.str(), false, - prepend_newline, divider_color); + mHistoryEditor->appendText(message.str(), + prepend_newline, LLStyle::Params().color(divider_color) ); message.str(""); mLastFromName = from; } message << msg["message"].asString(); - mHistoryEditor->appendColoredText(message.str(), false, - prepend_newline, chat_color); + mHistoryEditor->appendText(message.str(), + prepend_newline, + LLStyle::Params().color(chat_color) ); message.str(""); mLastMessageIndex = msg["index"].asInteger(); } + mHistoryEditor->blockUndo(); mHistoryEditor->setCursorAndScrollToEnd(); } diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index abd3cd4def..89a885090c 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -1078,8 +1078,6 @@ BOOL LLFloaterIMPanel::postBuild() //close_btn->setClickedCallback(&LLFloaterIMPanel::onClickClose, this); mHistoryEditor = getChild<LLViewerTextEditor>("im_history"); - mHistoryEditor->setParseHTML(TRUE); - mHistoryEditor->setParseHighlights(TRUE); if ( IM_SESSION_GROUP_START == mDialog ) { @@ -1334,16 +1332,18 @@ void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, const LLColor4 // Don't hotlink any messages from the system (e.g. "Second Life:"), so just add those in plain text. if (name == SYSTEM_FROM) { - mHistoryEditor->appendColoredText(name + separator_string, false, prepend_newline, color); + mHistoryEditor->appendText(name + separator_string, prepend_newline, LLStyle::Params().color(color)); } else { // Convert the name to a hotlink and add to message. - mHistoryEditor->appendStyledText(name + separator_string, false, prepend_newline, LLStyleMap::instance().lookupAgent(source)); + mHistoryEditor->appendText(name + separator_string, prepend_newline, LLStyleMap::instance().lookupAgent(source)); } prepend_newline = false; } - mHistoryEditor->appendColoredText(utf8msg, false, prepend_newline, color); + mHistoryEditor->appendText(utf8msg, prepend_newline, LLStyle::Params().color(color)); + mHistoryEditor->blockUndo(); + S32 im_log_option = gSavedPerAccountSettings.getS32("IMLogOptions"); if (log_to_file && (im_log_option!=LOG_CHAT)) { @@ -1859,7 +1859,8 @@ void LLFloaterIMPanel::chatFromLogFile(LLLogChat::ELogLineType type, std::string } //self->addHistoryLine(line, LLColor4::grey, FALSE); - self->mHistoryEditor->appendColoredText(message, false, true, LLUIColorTable::instance().getColor("ChatHistoryTextColor")); + self->mHistoryEditor->appendText(message, true, LLStyle::Params().color(LLUIColorTable::instance().getColor("ChatHistoryTextColor"))); + self->mHistoryEditor->blockUndo(); } void LLFloaterIMPanel::showSessionStartError( @@ -1934,4 +1935,3 @@ bool LLFloaterIMPanel::onConfirmForceCloseError(const LLSD& notification, const } return false; } - diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp index 72f89d2e72..a08d9e1163 100644 --- a/indra/newview/llinspectavatar.cpp +++ b/indra/newview/llinspectavatar.cpp @@ -34,15 +34,106 @@ #include "llinspectavatar.h" // viewer files +#include "llagent.h" #include "llagentdata.h" #include "llavataractions.h" #include "llavatarpropertiesprocessor.h" #include "llcallingcard.h" - -// linden libraries +#include "lldateutil.h" // ageFromDate() +#include "llfloaterreporter.h" +#include "llfloaterworldmap.h" +#include "llmutelist.h" +#include "llpanelblockedlist.h" +#include "llviewermenu.h" +#include "llvoiceclient.h" + +// Linden libraries +#include "llcontrol.h" // LLCachedControl +#include "llfloater.h" +#include "llfloaterreg.h" #include "lltooltip.h" // positionViewNearMouse() #include "lluictrl.h" +class LLFetchAvatarData; + + +////////////////////////////////////////////////////////////////////////////// +// LLInspectAvatar +////////////////////////////////////////////////////////////////////////////// + +// Avatar Inspector, a small information window used when clicking +// on avatar names in the 2D UI and in the ambient inspector widget for +// the 3D world. +class LLInspectAvatar : public LLFloater +{ + friend class LLFloaterReg; + +public: + // avatar_id - Avatar ID for which to show information + // Inspector will be positioned relative to current mouse position + LLInspectAvatar(const LLSD& avatar_id); + virtual ~LLInspectAvatar(); + + /*virtual*/ BOOL postBuild(void); + /*virtual*/ void draw(); + + // Because floater is single instance, need to re-parse data on each spawn + // (for example, inspector about same avatar but in different position) + /*virtual*/ void onOpen(const LLSD& avatar_id); + + // Inspectors close themselves when they lose focus + /*virtual*/ void onFocusLost(); + + // Update view based on information from avatar properties processor + void processAvatarData(LLAvatarData* data); + +private: + // Make network requests for all the data to display in this view. + // Used on construction and if avatar id changes. + void requestUpdate(); + + // Set the volume slider to this user's current client-side volume setting, + // hiding/disabling if the user is not nearby. + void updateVolumeSlider(); + + // Button callbacks + void onClickAddFriend(); + void onClickViewProfile(); + void onClickIM(); + void onClickTeleport(); + void onClickInviteToGroup(); + void onClickPay(); + void onClickBlock(); + void onClickReport(); + bool onVisibleFindOnMap(); + bool onVisibleGodMode(); + void onClickMuteVolume(); + void onFindOnMap(); + void onVolumeChange(const LLSD& data); + + // Callback for gCacheName to look up avatar name + void nameUpdatedCallback( + const LLUUID& id, + const std::string& first, + const std::string& last, + BOOL is_group); + +private: + LLUUID mAvatarID; + // Need avatar name information to spawn friend add request + std::string mAvatarName; + LLUUID mPartnerID; + // an in-flight request for avatar properties from LLAvatarPropertiesProcessor + // is represented by this object + LLFetchAvatarData* mPropertiesRequest; + LLFrameTimer mCloseTimer; + LLFrameTimer mOpenTimer; +}; + +////////////////////////////////////////////////////////////////////////////// +// LLFetchAvatarData +////////////////////////////////////////////////////////////////////////////// + // This object represents a pending request for avatar properties information class LLFetchAvatarData : public LLAvatarPropertiesObserver { @@ -50,8 +141,8 @@ public: // If the inspector closes it will delete the pending request object, so the // inspector pointer will be valid for the lifetime of this object LLFetchAvatarData(const LLUUID& avatar_id, LLInspectAvatar* inspector) - : mAvatarID(avatar_id), - mInspector(inspector) + : mAvatarID(avatar_id), + mInspector(inspector) { LLAvatarPropertiesProcessor* processor = LLAvatarPropertiesProcessor::getInstance(); @@ -61,14 +152,14 @@ public: // properties processor) processor->sendAvatarPropertiesRequest(mAvatarID); } - + ~LLFetchAvatarData() { // remove ourselves as an observer LLAvatarPropertiesProcessor::getInstance()-> - removeObserver(mAvatarID, this); + removeObserver(mAvatarID, this); } - + void processProperties(void* data, EAvatarProcessorType type) { // route the data to the inspector @@ -79,7 +170,7 @@ public: mInspector->processAvatarData(avatar_data); } } - + // Store avatar ID so we can un-register the observer on destruction LLUUID mAvatarID; LLInspectAvatar* mInspector; @@ -88,10 +179,24 @@ public: LLInspectAvatar::LLInspectAvatar(const LLSD& sd) : LLFloater( LLSD() ), // single_instance, doesn't really need key mAvatarID(), // set in onOpen() - mFirstName(), - mLastName(), - mPropertiesRequest(NULL) + mPartnerID(), + mAvatarName(), + mPropertiesRequest(NULL), + mCloseTimer() { + mCommitCallbackRegistrar.add("InspectAvatar.ViewProfile", boost::bind(&LLInspectAvatar::onClickViewProfile, this)); + mCommitCallbackRegistrar.add("InspectAvatar.AddFriend", boost::bind(&LLInspectAvatar::onClickAddFriend, this)); + mCommitCallbackRegistrar.add("InspectAvatar.IM", boost::bind(&LLInspectAvatar::onClickIM, this)); + mCommitCallbackRegistrar.add("InspectAvatar.Teleport", boost::bind(&LLInspectAvatar::onClickTeleport, this)); + mCommitCallbackRegistrar.add("InspectAvatar.InviteToGroup", boost::bind(&LLInspectAvatar::onClickInviteToGroup, this)); + mCommitCallbackRegistrar.add("InspectAvatar.Pay", boost::bind(&LLInspectAvatar::onClickPay, this)); + mCommitCallbackRegistrar.add("InspectAvatar.Block", boost::bind(&LLInspectAvatar::onClickBlock, this)); + mCommitCallbackRegistrar.add("InspectAvatar.Report", boost::bind(&LLInspectAvatar::onClickReport, this)); + mCommitCallbackRegistrar.add("InspectAvatar.FindOnMap", boost::bind(&LLInspectAvatar::onFindOnMap, this)); + mVisibleCallbackRegistrar.add("InspectAvatar.VisibleFindOnMap", boost::bind(&LLInspectAvatar::onVisibleFindOnMap, this)); + mVisibleCallbackRegistrar.add("InspectAvatar.VisibleGodMode", boost::bind(&LLInspectAvatar::onVisibleGodMode, this)); + + // can't make the properties request until the widgets are constructed // as it might return immediately, so do it in postBuild. } @@ -100,6 +205,7 @@ LLInspectAvatar::~LLInspectAvatar() { // clean up any pending requests so they don't call back into a deleted // view + llinfos << "JAMESDEBUG cleanup inspect avatar" << llendl; delete mPropertiesRequest; mPropertiesRequest = NULL; } @@ -113,18 +219,35 @@ BOOL LLInspectAvatar::postBuild(void) getChild<LLUICtrl>("view_profile_btn")->setCommitCallback( boost::bind(&LLInspectAvatar::onClickViewProfile, this) ); + getChild<LLUICtrl>("mute_btn")->setCommitCallback( + boost::bind(&LLInspectAvatar::onClickMuteVolume, this) ); + + getChild<LLUICtrl>("volume_slider")->setCommitCallback( + boost::bind(&LLInspectAvatar::onVolumeChange, this, _2)); + return TRUE; } void LLInspectAvatar::draw() { - static LLCachedControl<F32> FADE_OUT_TIME(*LLUI::sSettingGroups["config"], "InspectorFadeTime", 1.f); - if (mCloseTimer.getStarted()) + static LLCachedControl<F32> FADE_TIME(*LLUI::sSettingGroups["config"], "InspectorFadeTime", 1.f); + if (mOpenTimer.getStarted()) { - F32 alpha = clamp_rescale(mCloseTimer.getElapsedTimeF32(), 0.f, FADE_OUT_TIME, 1.f, 0.f); + F32 alpha = clamp_rescale(mOpenTimer.getElapsedTimeF32(), 0.f, FADE_TIME, 0.f, 1.f); LLViewDrawContext context(alpha); LLFloater::draw(); - if (mCloseTimer.getElapsedTimeF32() > FADE_OUT_TIME) + if (alpha == 1.f) + { + mOpenTimer.stop(); + } + + } + else if (mCloseTimer.getStarted()) + { + F32 alpha = clamp_rescale(mCloseTimer.getElapsedTimeF32(), 0.f, FADE_TIME, 1.f, 0.f); + LLViewDrawContext context(alpha); + LLFloater::draw(); + if (mCloseTimer.getElapsedTimeF32() > FADE_TIME) { closeFloater(false); } @@ -142,9 +265,11 @@ void LLInspectAvatar::draw() void LLInspectAvatar::onOpen(const LLSD& data) { mCloseTimer.stop(); + mOpenTimer.start(); // Extract appropriate avatar id - mAvatarID = data.isUUID() ? data : data["avatar_id"]; + mAvatarID = data["avatar_id"]; + mPartnerID = LLUUID::null; // Position the inspector relative to the mouse cursor // Similar to how tooltips are positioned @@ -160,6 +285,8 @@ void LLInspectAvatar::onOpen(const LLSD& data) // can't call from constructor as widgets are not built yet requestUpdate(); + + updateVolumeSlider(); } //virtual @@ -167,6 +294,7 @@ void LLInspectAvatar::onFocusLost() { // Start closing when we lose focus mCloseTimer.start(); + mOpenTimer.stop(); } void LLInspectAvatar::requestUpdate() @@ -178,7 +306,9 @@ void LLInspectAvatar::requestUpdate() getChild<LLUICtrl>("user_subtitle")-> setValue("Test subtitle"); getChild<LLUICtrl>("user_details")-> - setValue("Test details\nTest line 2"); + setValue("Test details"); + getChild<LLUICtrl>("user_partner")-> + setValue("Test partner"); return; } @@ -186,6 +316,7 @@ void LLInspectAvatar::requestUpdate() getChild<LLUICtrl>("user_name")->setValue(""); getChild<LLUICtrl>("user_subtitle")->setValue(""); getChild<LLUICtrl>("user_details")->setValue(""); + getChild<LLUICtrl>("user_partner")->setValue(""); // Make a new request for properties delete mPropertiesRequest; @@ -212,49 +343,187 @@ void LLInspectAvatar::processAvatarData(LLAvatarData* data) { LLStringUtil::format_map_t args; args["[BORN_ON]"] = data->born_on; - args["[AGE]"] = LLAvatarPropertiesProcessor::ageFromDate(data->born_on); + args["[AGE]"] = LLDateUtil::ageFromDate(data->born_on); args["[SL_PROFILE]"] = data->about_text; args["[RW_PROFILE"] = data->fl_about_text; args["[ACCTTYPE]"] = LLAvatarPropertiesProcessor::accountType(data); - args["[PAYMENTINFO]"] = LLAvatarPropertiesProcessor::paymentInfo(data); + std::string payment_info = LLAvatarPropertiesProcessor::paymentInfo(data); + args["[PAYMENTINFO]"] = payment_info; + args["[COMMA]"] = (payment_info.empty() ? "" : ","); std::string subtitle = getString("Subtitle", args); getChild<LLUICtrl>("user_subtitle")->setValue( LLSD(subtitle) ); std::string details = getString("Details", args); getChild<LLUICtrl>("user_details")->setValue( LLSD(details) ); + // Look up partner name, if there is one + mPartnerID = data->partner_id; + if (mPartnerID.notNull()) + { + gCacheName->get(mPartnerID, FALSE, + boost::bind(&LLInspectAvatar::nameUpdatedCallback, + this, _1, _2, _3, _4)); + } + // Delete the request object as it has been satisfied delete mPropertiesRequest; mPropertiesRequest = NULL; } +void LLInspectAvatar::updateVolumeSlider() +{ + // By convention, we only display and toggle voice mutes, not all mutes + bool is_muted = LLMuteList::getInstance()-> + isMuted(mAvatarID, LLMute::flagVoiceChat); + bool voice_enabled = gVoiceClient->getVoiceEnabled(mAvatarID); + + LLUICtrl* mute_btn = getChild<LLUICtrl>("mute_btn"); + mute_btn->setEnabled( voice_enabled ); + mute_btn->setValue( is_muted ); + + LLUICtrl* volume_slider = getChild<LLUICtrl>("volume_slider"); + volume_slider->setEnabled( voice_enabled && !is_muted ); + const F32 DEFAULT_VOLUME = 0.5f; + F32 volume; + if (is_muted) + { + // it's clearer to display their volume as zero + volume = 0.f; + } + else if (!voice_enabled) + { + // use nominal value rather than 0 + volume = DEFAULT_VOLUME; + } + else + { + // actual volume + volume = gVoiceClient->getUserVolume(mAvatarID); + + // *HACK: Voice client doesn't have any data until user actually + // says something. + if (volume == 0.f) + { + volume = DEFAULT_VOLUME; + } + } + volume_slider->setValue( (F64)volume ); +} + +void LLInspectAvatar::onClickMuteVolume() +{ + // By convention, we only display and toggle voice mutes, not all mutes + LLMuteList* mute_list = LLMuteList::getInstance(); + bool is_muted = mute_list->isMuted(mAvatarID, LLMute::flagVoiceChat); + + LLMute mute(mAvatarID, mAvatarName, LLMute::AGENT); + if (!is_muted) + { + mute_list->add(mute, LLMute::flagVoiceChat); + } + else + { + mute_list->remove(mute, LLMute::flagVoiceChat); + } + + updateVolumeSlider(); +} + +void LLInspectAvatar::onVolumeChange(const LLSD& data) +{ + F32 volume = (F32)data.asReal(); + gVoiceClient->setUserVolume(mAvatarID, volume); +} + void LLInspectAvatar::nameUpdatedCallback( const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group) { - // Possibly a request for an older inspector - if (id != mAvatarID) return; - - mFirstName = first; - mLastName = last; - std::string name = first + " " + last; - - childSetValue("user_name", LLSD(name) ); + if (id == mAvatarID) + { + mAvatarName = first + " " + last; + childSetValue("user_name", LLSD(mAvatarName) ); + } + + if (id == mPartnerID) + { + LLStringUtil::format_map_t args; + args["[PARTNER]"] = first + " " + last; + std::string partner = getString("Partner", args); + getChild<LLUICtrl>("user_partner")->setValue(partner); + } + // Otherwise possibly a request for an older inspector, ignore it } void LLInspectAvatar::onClickAddFriend() { - std::string name; - name.assign(mFirstName); - name.append(" "); - name.append(mLastName); - - LLAvatarActions::requestFriendshipDialog(mAvatarID, name); + LLAvatarActions::requestFriendshipDialog(mAvatarID, mAvatarName); } void LLInspectAvatar::onClickViewProfile() { + // hide inspector when showing profile + setFocus(FALSE); LLAvatarActions::showProfile(mAvatarID); + +} + +bool LLInspectAvatar::onVisibleFindOnMap() +{ + return gAgent.isGodlike() || is_agent_mappable(mAvatarID); +} + +bool LLInspectAvatar::onVisibleGodMode() +{ + return gAgent.isGodlike(); +} + +void LLInspectAvatar::onClickIM() +{ + LLAvatarActions::startIM(mAvatarID); +} + +void LLInspectAvatar::onClickTeleport() +{ + LLAvatarActions::offerTeleport(mAvatarID); +} + +void LLInspectAvatar::onClickInviteToGroup() +{ + LLAvatarActions::inviteToGroup(mAvatarID); +} + +void LLInspectAvatar::onClickPay() +{ + LLAvatarActions::pay(mAvatarID); +} + +void LLInspectAvatar::onClickBlock() +{ + LLMute mute(mAvatarID, mAvatarName, LLMute::AGENT); + LLMuteList::getInstance()->add(mute); + LLPanelBlockedList::showPanelAndSelect(mute.mID); +} + +void LLInspectAvatar::onClickReport() +{ + LLFloaterReporter::showFromObject(mAvatarID); +} + + +void LLInspectAvatar::onFindOnMap() +{ + gFloaterWorldMap->trackAvatar(mAvatarID, mAvatarName); + LLFloaterReg::showInstance("world_map"); +} + +////////////////////////////////////////////////////////////////////////////// +// LLInspectAvatarUtil +////////////////////////////////////////////////////////////////////////////// +void LLInspectAvatarUtil::registerFloater() +{ + LLFloaterReg::add("inspect_avatar", "inspect_avatar.xml", + &LLFloaterReg::build<LLInspectAvatar>); } diff --git a/indra/newview/llinspectavatar.h b/indra/newview/llinspectavatar.h index 8d490382d2..179ad1ffe1 100644 --- a/indra/newview/llinspectavatar.h +++ b/indra/newview/llinspectavatar.h @@ -32,63 +32,10 @@ #ifndef LLINSPECTAVATAR_H #define LLINSPECTAVATAR_H -#include "llfloater.h" - -struct LLAvatarData; -class LLFetchAvatarData; - -// Avatar Inspector, a small information window used when clicking -// on avatar names in the 2D UI and in the ambient inspector widget for -// the 3D world. -class LLInspectAvatar : public LLFloater +namespace LLInspectAvatarUtil { - friend class LLFloaterReg; - -public: - // avatar_id - Avatar ID for which to show information - // Inspector will be positioned relative to current mouse position - LLInspectAvatar(const LLSD& avatar_id); - virtual ~LLInspectAvatar(); - - /*virtual*/ BOOL postBuild(void); - /*virtual*/ void draw(); - - // Because floater is single instance, need to re-parse data on each spawn - // (for example, inspector about same avatar but in different position) - /*virtual*/ void onOpen(const LLSD& avatar_id); - - // Inspectors close themselves when they lose focus - /*virtual*/ void onFocusLost(); - - // Update view based on information from avatar properties processor - void processAvatarData(LLAvatarData* data); - -private: - // Make network requests for all the data to display in this view. - // Used on construction and if avatar id changes. - void requestUpdate(); - - // Button callbacks - void onClickAddFriend(); - void onClickViewProfile(); - - // Callback for gCacheName to look up avatar name - void nameUpdatedCallback( - const LLUUID& id, - const std::string& first, - const std::string& last, - BOOL is_group); - -private: - LLUUID mAvatarID; - // Need avatar name information to spawn friend add request - std::string mFirstName; - std::string mLastName; - // an in-flight request for avatar properties from LLAvatarPropertiesProcessor - // is represented by this object - LLFetchAvatarData* mPropertiesRequest; - LLFrameTimer mCloseTimer; -}; - + // Register with LLFloaterReg + void registerFloater(); +} #endif diff --git a/indra/newview/llinspectobject.cpp b/indra/newview/llinspectobject.cpp new file mode 100644 index 0000000000..b0e6273c41 --- /dev/null +++ b/indra/newview/llinspectobject.cpp @@ -0,0 +1,563 @@ +/** + * @file llinspectobject.cpp + * + * $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 "llviewerprecompiledheaders.h" + +#include "llinspectobject.h" + +// Viewer +#include "llnotifications.h" // *TODO: Eliminate, add LLNotificationsUtil wrapper +#include "llselectmgr.h" +#include "llslurl.h" +#include "llviewermenu.h" // handle_object_touch(), handle_buy() +#include "llviewerobjectlist.h" // to select the requested object + +// Linden libraries +#include "llbutton.h" // setLabel(), not virtual! +#include "llclickaction.h" +#include "llcontrol.h" // LLCachedControl +#include "llfloater.h" +#include "llfloaterreg.h" +#include "llresmgr.h" // getMonetaryString +#include "llsafehandle.h" +#include "lltextbox.h" // for description truncation +#include "lltrans.h" +#include "llui.h" // positionViewNearMouse() +#include "lluictrl.h" + +class LLViewerObject; + +// *TODO: Abstract out base class for LLInspectObject and LLInspectObject + +////////////////////////////////////////////////////////////////////////////// +// LLInspectObject +////////////////////////////////////////////////////////////////////////////// + +// Object Inspector, a small information window used when clicking +// in the ambient inspector widget for objects in the 3D world. +class LLInspectObject : public LLFloater +{ + friend class LLFloaterReg; + +public: + // object_id - Root object ID for which to show information + // Inspector will be positioned relative to current mouse position + LLInspectObject(const LLSD& object_id); + virtual ~LLInspectObject(); + + /*virtual*/ BOOL postBuild(void); + /*virtual*/ void draw(); + + // Because floater is single instance, need to re-parse data on each spawn + // (for example, inspector about same avatar but in different position) + /*virtual*/ void onOpen(const LLSD& avatar_id); + + // Release the selection and do other cleanup + void onClose(); + + // Inspectors close themselves when they lose focus + /*virtual*/ void onFocusLost(); + +private: + // Refresh displayed data with information from selection manager + void update(); + + void hideButtons(); + void updateButtons(LLSelectNode* nodep); + void updateSitLabel(LLSelectNode* nodep); + void updateTouchLabel(LLSelectNode* nodep); + + void updateName(LLSelectNode* nodep); + void updateDescription(LLSelectNode* nodep); + void updatePrice(LLSelectNode* nodep); + + void updateCreator(LLSelectNode* nodep); + + void onClickBuy(); + void onClickPay(); + void onClickTakeFreeCopy(); + void onClickTouch(); + void onClickSit(); + void onClickOpen(); + void onClickMoreInfo(); + +private: + LLUUID mObjectID; + LLFrameTimer mOpenTimer; + LLFrameTimer mCloseTimer; + LLSafeHandle<LLObjectSelection> mObjectSelection; +}; + +LLInspectObject::LLInspectObject(const LLSD& sd) +: LLFloater( LLSD() ), // single_instance, doesn't really need key + mObjectID(), // set in onOpen() + mCloseTimer(), + mOpenTimer() +{ + // can't make the properties request until the widgets are constructed + // as it might return immediately, so do it in postBuild. + mCommitCallbackRegistrar.add("InspectObject.Buy", boost::bind(&LLInspectObject::onClickBuy, this)); + mCommitCallbackRegistrar.add("InspectObject.Pay", boost::bind(&LLInspectObject::onClickPay, this)); + mCommitCallbackRegistrar.add("InspectObject.TakeFreeCopy", boost::bind(&LLInspectObject::onClickTakeFreeCopy, this)); + mCommitCallbackRegistrar.add("InspectObject.Touch", boost::bind(&LLInspectObject::onClickTouch, this)); + mCommitCallbackRegistrar.add("InspectObject.Sit", boost::bind(&LLInspectObject::onClickSit, this)); + mCommitCallbackRegistrar.add("InspectObject.Open", boost::bind(&LLInspectObject::onClickOpen, this)); + mCommitCallbackRegistrar.add("InspectObject.MoreInfo", boost::bind(&LLInspectObject::onClickMoreInfo, this)); +} + + +LLInspectObject::~LLInspectObject() +{ +} + +/*virtual*/ +BOOL LLInspectObject::postBuild(void) +{ + // The XML file has sample data in it. Clear that out so we don't + // flicker when data arrives off network. + getChild<LLUICtrl>("object_name")->setValue(""); + getChild<LLUICtrl>("object_creator")->setValue(""); + getChild<LLUICtrl>("object_description")->setValue(""); + + // Set buttons invisible until we know what this object can do + hideButtons(); + + // Hide floater when name links clicked + LLTextBox* textbox = getChild<LLTextBox>("object_creator"); + textbox->mURLClickSignal.connect( + boost::bind(&LLInspectObject::closeFloater, this, false) ); + + // Hook up functionality + getChild<LLUICtrl>("buy_btn")->setCommitCallback( + boost::bind(&LLInspectObject::onClickBuy, this)); + getChild<LLUICtrl>("pay_btn")->setCommitCallback( + boost::bind(&LLInspectObject::onClickPay, this)); + getChild<LLUICtrl>("take_free_copy_btn")->setCommitCallback( + boost::bind(&LLInspectObject::onClickTakeFreeCopy, this)); + getChild<LLUICtrl>("touch_btn")->setCommitCallback( + boost::bind(&LLInspectObject::onClickTouch, this)); + getChild<LLUICtrl>("sit_btn")->setCommitCallback( + boost::bind(&LLInspectObject::onClickSit, this)); + getChild<LLUICtrl>("open_btn")->setCommitCallback( + boost::bind(&LLInspectObject::onClickOpen, this)); + getChild<LLUICtrl>("more_info_btn")->setCommitCallback( + boost::bind(&LLInspectObject::onClickMoreInfo, this)); + + // Watch for updates to selection properties off the network + LLSelectMgr::getInstance()->mUpdateSignal.connect( + boost::bind(&LLInspectObject::update, this) ); + + mCloseSignal.connect( boost::bind(&LLInspectObject::onClose, this) ); + + return TRUE; +} + +void LLInspectObject::draw() +{ + static LLCachedControl<F32> FADE_OUT_TIME(*LLUI::sSettingGroups["config"], "InspectorFadeTime", 1.f); + if (mOpenTimer.getStarted()) + { + F32 alpha = clamp_rescale(mOpenTimer.getElapsedTimeF32(), 0.f, FADE_OUT_TIME, 0.f, 1.f); + LLViewDrawContext context(alpha); + LLFloater::draw(); + } + else if (mCloseTimer.getStarted()) + { + F32 alpha = clamp_rescale(mCloseTimer.getElapsedTimeF32(), 0.f, FADE_OUT_TIME, 1.f, 0.f); + LLViewDrawContext context(alpha); + LLFloater::draw(); + if (mCloseTimer.getElapsedTimeF32() > FADE_OUT_TIME) + { + closeFloater(false); + } + } + else + { + LLFloater::draw(); + } +} + + +// Multiple calls to showInstance("inspect_avatar", foo) will provide different +// LLSD for foo, which we will catch here. +//virtual +void LLInspectObject::onOpen(const LLSD& data) +{ + mCloseTimer.stop(); + mOpenTimer.start(); + + // Extract appropriate avatar id + mObjectID = data["object_id"]; + + // Position the inspector relative to the mouse cursor + // Similar to how tooltips are positioned + // See LLToolTipMgr::createToolTip + if (data.has("pos")) + { + LLUI::positionViewNearMouse(this, data["pos"]["x"].asInteger(), data["pos"]["y"].asInteger()); + } + else + { + LLUI::positionViewNearMouse(this); + } + + // Promote hovered object to a complete selection, which will also force + // a request for selected object data off the network + LLViewerObject* obj = gObjectList.findObject( mObjectID ); + if (obj) + { + LLSelectMgr::instance().deselectAll(); + mObjectSelection = LLSelectMgr::instance().selectObjectAndFamily(obj); + + // Mark this as a transient selection + struct SetTransient : public LLSelectedNodeFunctor + { + bool apply(LLSelectNode* node) + { + node->setTransient(TRUE); + return true; + } + } functor; + mObjectSelection->applyToNodes(&functor); + } +} + +void LLInspectObject::onClose() +{ + // Release selection to deselect + mObjectSelection = NULL; +} + +//virtual +void LLInspectObject::onFocusLost() +{ + // Start closing when we lose focus + mCloseTimer.start(); + mOpenTimer.stop(); +} + + +void LLInspectObject::update() +{ + // Performance optimization, because we listen to updates from select mgr + // but we're never destroyed. + if (!getVisible()) return; + + LLObjectSelection* selection = LLSelectMgr::getInstance()->getSelection(); + if (!selection) return; + + LLSelectNode* nodep = selection->getFirstRootNode(); + if (!nodep) return; + + updateButtons(nodep); + updateName(nodep); + updateDescription(nodep); + updateCreator(nodep); + updatePrice(nodep); +} + +void LLInspectObject::hideButtons() +{ + getChild<LLUICtrl>("buy_btn")->setVisible(false); + getChild<LLUICtrl>("pay_btn")->setVisible(false); + getChild<LLUICtrl>("take_free_copy_btn")->setVisible(false); + getChild<LLUICtrl>("touch_btn")->setVisible(false); + getChild<LLUICtrl>("sit_btn")->setVisible(false); + getChild<LLUICtrl>("open_btn")->setVisible(false); +} + +// *TODO: Extract this method from lltoolpie.cpp and put somewhere shared +extern U8 final_click_action(LLViewerObject*); + +// Choose the "most relevant" operation for this object, and show a button for +// that operation as the left-most button in the inspector. +void LLInspectObject::updateButtons(LLSelectNode* nodep) +{ + // We'll start with everyone hidden and show the ones we need + hideButtons(); + + LLViewerObject* object = nodep->getObject(); + LLViewerObject *parent = (LLViewerObject*)object->getParent(); + bool for_copy = anyone_copy_selection(nodep); + bool for_sale = enable_buy_object(); + S32 price = nodep->mSaleInfo.getSalePrice(); + U8 click_action = final_click_action(object); + + if (for_copy + || (for_sale && price == 0)) + { + // Free copies have priority over other operations + getChild<LLUICtrl>("take_free_copy_btn")->setVisible(true); + } + else if (for_sale) + { + getChild<LLUICtrl>("buy_btn")->setVisible(true); + } + else if ( enable_pay_object() ) + { + getChild<LLUICtrl>("pay_btn")->setVisible(true); + } + else if (click_action == CLICK_ACTION_SIT) + { + // Click-action sit must come before "open" because many objects on + // which you can sit have scripts, and hence can be opened + getChild<LLUICtrl>("sit_btn")->setVisible(true); + updateSitLabel(nodep); + } + else if (object->flagHandleTouch() + || (parent && parent->flagHandleTouch())) + { + getChild<LLUICtrl>("touch_btn")->setVisible(true); + updateTouchLabel(nodep); + } + else if ( enable_object_open() ) + { + // Open is last because anything with a script in it can be opened + getChild<LLUICtrl>("open_btn")->setVisible(true); + } + else + { + // By default, we can sit on anything + getChild<LLUICtrl>("sit_btn")->setVisible(true); + updateSitLabel(nodep); + } + + // No flash + focusFirstItem(FALSE, FALSE); +} + +void LLInspectObject::updateSitLabel(LLSelectNode* nodep) +{ + LLButton* sit_btn = getChild<LLButton>("sit_btn"); + if (!nodep->mSitName.empty()) + { + sit_btn->setLabel( nodep->mSitName ); + } + else + { + sit_btn->setLabel( getString("Sit") ); + } +} + +void LLInspectObject::updateTouchLabel(LLSelectNode* nodep) +{ + LLButton* sit_btn = getChild<LLButton>("touch_btn"); + if (!nodep->mTouchName.empty()) + { + sit_btn->setLabel( nodep->mTouchName ); + } + else + { + sit_btn->setLabel( getString("Touch") ); + } +} + +void LLInspectObject::updateName(LLSelectNode* nodep) +{ + std::string name; + if (!nodep->mName.empty()) + { + name = nodep->mName; + } + else + { + name = LLTrans::getString("TooltipNoName"); + } + getChild<LLUICtrl>("object_name")->setValue(name); +} + +void LLInspectObject::updateDescription(LLSelectNode* nodep) +{ + const char* const DEFAULT_DESC = "(No Description)"; + std::string desc; + if (!nodep->mDescription.empty() + && nodep->mDescription != DEFAULT_DESC) + { + desc = nodep->mDescription; + } + + LLTextBox* textbox = getChild<LLTextBox>("object_description"); + textbox->setValue(desc); + + // Truncate description text to fit in widget + // *HACK: OMG, use lower-left corner to truncate text + // Don't round the position, we want the left of the character + S32 corner_index = textbox->getDocIndexFromLocalCoord( 0, 0, FALSE); + LLWString desc_wide = textbox->getWText(); + // index == length if position is past last character + if (corner_index < (S32)desc_wide.length()) + { + desc_wide = desc_wide.substr(0, corner_index); + textbox->setWText(desc_wide); + } +} + +void LLInspectObject::updateCreator(LLSelectNode* nodep) +{ + // final information for display + LLStringUtil::format_map_t args; + std::string text; + + // Leave text blank until data loaded + if (nodep->mValid) + { + // Utilize automatic translation of SLURL into name to display + // a clickable link + // Objects cannot be created by a group, so use agent URL format + LLUUID creator_id = nodep->mPermissions->getCreator(); + std::string creator_url = + LLSLURL::buildCommand("agent", creator_id, "about"); + args["[CREATOR]"] = creator_url; + + // created by one user but owned by another + std::string owner_url; + LLUUID owner_id; + bool group_owned = nodep->mPermissions->isGroupOwned(); + if (group_owned) + { + owner_id = nodep->mPermissions->getGroup(); + owner_url = LLSLURL::buildCommand("group", owner_id, "about"); + } + else + { + owner_id = nodep->mPermissions->getOwner(); + owner_url = LLSLURL::buildCommand("agent", owner_id, "about"); + } + args["[OWNER]"] = owner_url; + + if (creator_id == owner_id) + { + // common case, created and owned by one user + text = getString("Creator", args); + } + else + { + text = getString("CreatorAndOwner", args); + } + } + getChild<LLUICtrl>("object_creator")->setValue(text); +} + +void LLInspectObject::updatePrice(LLSelectNode* nodep) +{ + // *TODO: Only look these up once and use for both updateButtons and here + bool for_copy = anyone_copy_selection(nodep); + bool for_sale = enable_buy_object(); + S32 price = nodep->mSaleInfo.getSalePrice(); + + bool show_price_icon = false; + std::string line; + if (for_copy + || (for_sale && price == 0)) + { + line = getString("PriceFree"); + show_price_icon = true; + } + else if (for_sale) + { + LLStringUtil::format_map_t args; + args["[AMOUNT]"] = LLResMgr::getInstance()->getMonetaryString(price); + line = getString("Price", args); + show_price_icon = true; + } + getChild<LLUICtrl>("price_text")->setValue(line); + getChild<LLUICtrl>("price_icon")->setVisible(show_price_icon); +} + +void LLInspectObject::onClickBuy() +{ + handle_buy(); + closeFloater(); +} + +void LLInspectObject::onClickPay() +{ + handle_give_money_dialog(); + closeFloater(); +} + +void LLInspectObject::onClickTakeFreeCopy() +{ + LLObjectSelection* selection = LLSelectMgr::getInstance()->getSelection(); + if (!selection) return; + + LLSelectNode* nodep = selection->getFirstRootNode(); + if (!nodep) return; + + // Figure out if this is a "free buy" or a "take copy" + bool for_copy = anyone_copy_selection(nodep); + // Prefer to just take a free copy + if (for_copy) + { + handle_take_copy(); + } + else + { + // Buy for free (confusing, but that's how it is) + handle_buy(); + } + closeFloater(); +} + +void LLInspectObject::onClickTouch() +{ + handle_object_touch(); + closeFloater(); +} + +void LLInspectObject::onClickSit() +{ + handle_object_sit_or_stand(); + closeFloater(); +} + +void LLInspectObject::onClickOpen() +{ + LLFloaterReg::showInstance("openobject"); + closeFloater(); +} + +void LLInspectObject::onClickMoreInfo() +{ + // *TODO: Show object info side panel, once that is implemented. + LLNotifications::instance().add("ClickUnimplemented"); + closeFloater(); +} + +////////////////////////////////////////////////////////////////////////////// +// LLInspectObjectUtil +////////////////////////////////////////////////////////////////////////////// +void LLInspectObjectUtil::registerFloater() +{ + LLFloaterReg::add("inspect_object", "inspect_object.xml", + &LLFloaterReg::build<LLInspectObject>); +} + diff --git a/indra/newview/llinspectobject.h b/indra/newview/llinspectobject.h new file mode 100644 index 0000000000..aa45f401c0 --- /dev/null +++ b/indra/newview/llinspectobject.h @@ -0,0 +1,40 @@ +/** + * @file llinspectobject.h + * + * $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 LLINSPECTOBJECT_H +#define LLINSPECTOBJECT_H + +namespace LLInspectObjectUtil +{ + void registerFloater(); +} + +#endif diff --git a/indra/newview/lljoystickbutton.cpp b/indra/newview/lljoystickbutton.cpp index efc03b3d88..4fd3b7bddc 100644 --- a/indra/newview/lljoystickbutton.cpp +++ b/indra/newview/lljoystickbutton.cpp @@ -537,18 +537,6 @@ void LLJoystickCameraRotate::draw() { drawRotatedImage( getImageSelected()->getImage(), 3 ); } - - if (sDebugRects) - { - drawDebugRect(); - } - - //// *HACK: also draw debug rectangles around currently-being-edited LLView, and any elements that are being highlighted by GUI preview code (see LLFloaterUIPreview) - //std::set<LLView*>::iterator iter = std::find(sPreviewHighlightedElements.begin(), sPreviewHighlightedElements.end(), this); - //if ((sEditingUI && this == sEditingUIView) || (iter != sPreviewHighlightedElements.end() && sDrawPreviewHighlights)) - //{ - // drawDebugRect(); - //} } // Draws image rotated by multiples of 90 degrees @@ -723,18 +711,6 @@ void LLJoystickCameraZoom::draw() { getImageUnselected()->draw( 0, 0 ); } - - if (sDebugRects) - { - drawDebugRect(); - } - - //// *HACK: also draw debug rectangles around currently-being-edited LLView, and any elements that are being highlighted by GUI preview code (see LLFloaterUIPreview) - //std::set<LLView*>::iterator iter = std::find(sPreviewHighlightedElements.begin(), sPreviewHighlightedElements.end(), this); - //if ((sEditingUI && this == sEditingUIView) || (iter != sPreviewHighlightedElements.end() && sDrawPreviewHighlights)) - //{ - // drawDebugRect(); - //} } void LLJoystickCameraZoom::updateSlop() diff --git a/indra/newview/lllistview.cpp b/indra/newview/lllistview.cpp index 3019d5d3d5..f4f3b1df78 100644 --- a/indra/newview/lllistview.cpp +++ b/indra/newview/lllistview.cpp @@ -57,7 +57,7 @@ LLListView::LLListView(const Params& p) LLRect label_rect(0, 20, 300, 0); LLTextBox::Params text_box_params; text_box_params.rect(label_rect); - text_box_params.text("This is a list-view"); + text_box_params.initial_value("This is a list-view"); mLabel = LLUICtrlFactory::create<LLTextBox>(text_box_params); addChild(mLabel); } diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp index 68dc3854db..16a10dc502 100644 --- a/indra/newview/lllocationinputctrl.cpp +++ b/indra/newview/lllocationinputctrl.cpp @@ -290,10 +290,10 @@ void LLLocationInputCtrl::hideList() focusTextEntry(); } -BOOL LLLocationInputCtrl::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen) +BOOL LLLocationInputCtrl::handleToolTip(S32 x, S32 y, MASK mask) { // Let the buttons show their tooltips. - if (LLUICtrl::handleToolTip(x, y, msg, sticky_rect_screen) && !msg.empty()) + if (LLUICtrl::handleToolTip(x, y, mask)) { if (mList->getRect().pointInRect(x, y)) { diff --git a/indra/newview/lllocationinputctrl.h b/indra/newview/lllocationinputctrl.h index 6edae9a9e2..c74a294ca3 100644 --- a/indra/newview/lllocationinputctrl.h +++ b/indra/newview/lllocationinputctrl.h @@ -71,7 +71,7 @@ public: // LLView interface /*virtual*/ void setEnabled(BOOL enabled); - /*virtual*/ BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect); + /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); /*virtual*/ void onFocusReceived(); /*virtual*/ void onFocusLost(); diff --git a/indra/newview/llmenucommands.cpp b/indra/newview/llmenucommands.cpp index 1666ec1336..f61177d581 100644 --- a/indra/newview/llmenucommands.cpp +++ b/indra/newview/llmenucommands.cpp @@ -49,7 +49,6 @@ #include "llfloaterchat.h" #include "llfloaterdirectory.h" #include "llfloaterworldmap.h" -#include "llgivemoney.h" #include "lllineeditor.h" #include "llnotify.h" #include "llstatusbar.h" @@ -68,11 +67,6 @@ #include "llfocusmgr.h" #include "llnearbychatbar.h" -void handle_pay_by_id(const LLUUID& agent_id) -{ - const BOOL is_group = FALSE; - LLFloaterPay::payDirectly(&give_money, agent_id, is_group); -} void handle_mouselook(void*) { diff --git a/indra/newview/llmenucommands.h b/indra/newview/llmenucommands.h index 368c6fe752..a3611ef6d1 100644 --- a/indra/newview/llmenucommands.h +++ b/indra/newview/llmenucommands.h @@ -35,7 +35,6 @@ class LLUUID; -void handle_pay_by_id(const LLUUID& agent_id); void handle_mouselook(void*); void handle_chat(void*); void handle_return_key(void*); diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp index 8ef6b25c50..541db0ca6e 100644 --- a/indra/newview/llnamelistctrl.cpp +++ b/indra/newview/llnamelistctrl.cpp @@ -32,16 +32,18 @@ #include "llviewerprecompiledheaders.h" -#include <boost/tokenizer.hpp> - #include "llnamelistctrl.h" +#include <boost/tokenizer.hpp> + #include "llcachename.h" +#include "llfloaterreg.h" #include "llinventory.h" #include "llscrolllistitem.h" #include "llscrolllistcell.h" #include "llscrolllistcolumn.h" #include "llsdparam.h" +#include "lltooltip.h" static LLDefaultChildRegistry::Register<LLNameListCtrl> r("name_list"); @@ -128,6 +130,60 @@ BOOL LLNameListCtrl::handleDragAndDrop( return handled; } +void LLNameListCtrl::showAvatarInspector(const LLUUID& avatar_id) +{ + LLSD key; + key["avatar_id"] = avatar_id; + LLFloaterReg::showInstance("inspect_avatar", key); +} + +//virtual +BOOL LLNameListCtrl::handleToolTip(S32 x, S32 y, MASK mask) +{ + BOOL handled = FALSE; + S32 column_index = getColumnIndexFromOffset(x); + LLScrollListItem* hit_item = hitItem(x, y); + if (hit_item) + { + if (column_index == mNameColumnIndex) + { + // ...this is the column with the avatar name + LLScrollListCell* hit_cell = hit_item->getColumn(column_index); + if (hit_cell) + { + 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); + + // Spawn at right side of cell + LLCoordGL pos( sticky_rect.mRight - 16, sticky_rect.mTop ); + LLPointer<LLUIImage> icon = LLUI::getUIImage("Info_Small"); + LLUUID avatar_id = hit_item->getValue().asUUID(); + + LLToolTip::Params params; + params.background_visible( false ); + params.click_callback( boost::bind(&LLNameListCtrl::showAvatarInspector, this, avatar_id) ); + params.delay_time(0.0f); // spawn instantly on hover + params.image( icon ); + params.message(""); + params.padding(0); + params.pos(pos); + params.sticky_rect(sticky_rect); + + LLToolTipMgr::getInstance()->show(params); + handled = TRUE; + } + } + } + if (!handled) + { + handled = LLScrollListCtrl::handleToolTip(x, y, mask); + } + return handled; +} + // public void LLNameListCtrl::addGroupNameItem(const LLUUID& group_id, EAddPosition pos, BOOL enabled) diff --git a/indra/newview/llnamelistctrl.h b/indra/newview/llnamelistctrl.h index 070b6c4f4f..d0f0ec4d21 100644 --- a/indra/newview/llnamelistctrl.h +++ b/indra/newview/llnamelistctrl.h @@ -110,15 +110,20 @@ public: static void refreshAll(const LLUUID& id, const std::string& firstname, const std::string& lastname, BOOL is_group); - virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, + // LLView interface + /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void *cargo_data, EAcceptance *accept, std::string& tooltip_msg); + /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask); void setAllowCallingCardDrop(BOOL b) { mAllowCallingCardDrop = b; } /*virtual*/ void updateColumns(); private: + void showAvatarInspector(const LLUUID& avatar_id); + +private: S32 mNameColumnIndex; std::string mNameColumn; BOOL mAllowCallingCardDrop; diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 8430937933..cc5f37b903 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -51,6 +51,7 @@ //for LLViewerTextEditor support #include "llagent.h" // gAgent #include "llfloaterscriptdebug.h" +#include "llslurl.h" #include "llviewertexteditor.h" #include "llstylemap.h" @@ -207,7 +208,7 @@ void LLNearbyChat::add_timestamped_line(const LLChat& chat, const LLColor4& colo if (chat.mSourceType == CHAT_SOURCE_AGENT && chat.mFromID != LLUUID::null) { - str_URL = llformat("secondlife:///app/agent/%s/about",chat.mFromID.asString().c_str()); + str_URL = LLSLURL::buildCommand("agent", chat.mFromID, "inspect"); } // If the chat line has an associated url, link it up to the name. @@ -216,20 +217,31 @@ void LLNearbyChat::add_timestamped_line(const LLChat& chat, const LLColor4& colo { std::string start_line = line.substr(0, chat.mFromName.length() + 1); line = line.substr(chat.mFromName.length() + 1); - mChatHistoryEditor->appendStyledText(start_line, false, prepend_newline, LLStyleMap::instance().lookup(chat.mFromID,str_URL)); + mChatHistoryEditor->appendText(start_line, prepend_newline, + LLStyleMap::instance().lookup(chat.mFromID,str_URL)); + mChatHistoryEditor->blockUndo(); prepend_newline = false; } S32 font_size = gSavedSettings.getS32("ChatFontSize"); - std::string font_name = ""; - - if (0 == font_size) - font_name = "small"; - else if (2 == font_size) - font_name = "sansserifbig"; + const LLFontGL* fontp = NULL; + switch(font_size) + { + case 0: + fontp = LLFontGL::getFontSansSerifSmall(); + break; + default: + case 1: + fontp = LLFontGL::getFontSansSerif(); + break; + case 2: + fontp = LLFontGL::getFontSansSerifBig(); + break; + } - mChatHistoryEditor->appendColoredText(line, false, prepend_newline, color, font_name); + mChatHistoryEditor->appendText(line, prepend_newline, LLStyle::Params().color(color).font(fontp)); + mChatHistoryEditor->blockUndo(); } void LLNearbyChat::addMessage(const LLChat& chat) @@ -250,11 +262,6 @@ void LLNearbyChat::addMessage(const LLChat& chat) } // could flash the chat button in the status bar here. JC - - - mChatHistoryEditor->setParseHTML(TRUE); - mChatHistoryEditor->setParseHighlights(TRUE); - if (!chat.mMuted) add_timestamped_line(chat, color); } diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp index 5e65f2244d..bd6e6cd0cb 100644 --- a/indra/newview/llnetmap.cpp +++ b/indra/newview/llnetmap.cpp @@ -500,7 +500,7 @@ BOOL LLNetMap::handleScrollWheel(S32 x, S32 y, S32 clicks) return TRUE; } -BOOL LLNetMap::handleToolTip( S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen ) +BOOL LLNetMap::handleToolTip( S32 x, S32 y, MASK mask ) { if (gDisconnected) { @@ -530,7 +530,7 @@ BOOL LLNetMap::handleToolTip( S32 x, S32 y, std::string& msg, LLRect& sticky_rec args["[REGION]"] = ""; } - msg = mToolTipMsg; + std::string msg = mToolTipMsg; LLStringUtil::format(msg, args); LLRect sticky_rect; @@ -545,7 +545,7 @@ BOOL LLNetMap::handleToolTip( S32 x, S32 y, std::string& msg, LLRect& sticky_rec sticky_rect.mTop = sticky_rect.mBottom + 2 * SLOP; } - LLToolTipMgr::instance().show(LLToolTipParams() + LLToolTipMgr::instance().show(LLToolTip::Params() .message(msg) .sticky_rect(sticky_rect)); diff --git a/indra/newview/llnetmap.h b/indra/newview/llnetmap.h index 7598154480..821c348835 100644 --- a/indra/newview/llnetmap.h +++ b/indra/newview/llnetmap.h @@ -67,7 +67,7 @@ public: /*virtual*/ void draw(); /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); - /*virtual*/ BOOL handleToolTip( S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen ); + /*virtual*/ BOOL handleToolTip( S32 x, S32 y, MASK mask); void setScale( F32 scale ); void setRotateMap( BOOL b ) { mRotateMap = b; } diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index 7ccff73080..c543f85f22 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -38,6 +38,7 @@ #include "llavatarconstants.h" // AVATAR_ONLINE #include "llcallingcard.h" #include "llcombobox.h" +#include "lldateutil.h" #include "llimview.h" #include "lltexteditor.h" #include "lltexturectrl.h" @@ -447,7 +448,7 @@ void LLPanelAvatarProfile::processGroupProperties(const LLAvatarGroups* avatar_g void LLPanelAvatarProfile::fillCommonData(const LLAvatarData* avatar_data) { - childSetValue("register_date", LLAvatarPropertiesProcessor::ageFromDate(avatar_data->born_on)); + childSetValue("register_date", LLDateUtil::ageFromDate(avatar_data->born_on)); childSetValue("sl_description_edit", avatar_data->about_text); childSetValue("fl_description_edit",avatar_data->fl_about_text); childSetValue("2nd_life_pic", avatar_data->image_id); diff --git a/indra/newview/llpanelavatartag.cpp b/indra/newview/llpanelavatartag.cpp index e66c36287b..03ad19f911 100644 --- a/indra/newview/llpanelavatartag.cpp +++ b/indra/newview/llpanelavatartag.cpp @@ -64,29 +64,7 @@ BOOL LLPanelAvatarTag::postBuild() void LLPanelAvatarTag::draw() { - - ///TODO: ANGELA do something similar to fade the panel out -/* // HACK: assuming tooltip background is in ToolTipBGColor, perform fade out - LLColor4 bg_color = LLUIColorTable::instance().getColor( "ToolTipBgColor" ); - if (tooltip_vis) - { - mToolTipFadeTimer.stop(); - mToolTip->setBackgroundColor(bg_color); - } - else - { - if (!mToolTipFadeTimer.getStarted()) - { - mToolTipFadeTimer.start(); - } - F32 tool_tip_fade_time = gSavedSettings.getF32("ToolTipFadeTime"); - bg_color.mV[VALPHA] = clamp_rescale(mToolTipFadeTimer.getElapsedTimeF32(), 0.f, tool_tip_fade_time, bg_color.mV[VALPHA], 0.f); - mToolTip->setBackgroundColor(bg_color); - } - - // above interpolation of bg_color alpha is guaranteed to reach 0.f exactly - mToolTip->setVisible( bg_color.mV[VALPHA] != 0.f ); - */ + } void LLPanelAvatarTag::setName(const std::string& name) { diff --git a/indra/newview/llpanelblockedlist.h b/indra/newview/llpanelblockedlist.h index 52b74a184b..0993f46f79 100644 --- a/indra/newview/llpanelblockedlist.h +++ b/indra/newview/llpanelblockedlist.h @@ -35,6 +35,7 @@ #include "llpanel.h" #include "llmutelist.h" +#include "llfloater.h" // #include <vector> // class LLButton; diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index 2e1d971995..0331fad60c 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -721,15 +721,18 @@ void LLPanelGroupGeneral::updateMembers() row["id"] = member->getID(); row["columns"][0]["column"] = "name"; + row["columns"][0]["font"]["name"] = "SANSSERIF_SMALL"; row["columns"][0]["font"]["style"] = style; // value is filled in by name list control row["columns"][1]["column"] = "title"; row["columns"][1]["value"] = member->getTitle(); + row["columns"][1]["font"]["name"] = "SANSSERIF_SMALL"; row["columns"][1]["font"]["style"] = style; row["columns"][2]["column"] = "online"; row["columns"][2]["value"] = member->getOnlineStatus(); + row["columns"][2]["font"]["name"] = "SANSSERIF_SMALL"; row["columns"][2]["font"]["style"] = style; sSDTime += sd_timer.getElapsedTimeF32(); diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index 99bb760b61..1521c1113a 100644 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -634,6 +634,7 @@ void LLPanelGroupSubTab::buildActionCategory(LLScrollListCtrl* ctrl, row["columns"][1]["column"] = "action"; row["columns"][1]["value"] = action_set->mActionSetData->mName; + row["columns"][1]["font"]["name"] = "SANSSERIF_SMALL"; row["columns"][1]["font"]["style"] = "BOLD"; LLScrollListItem* title_row = ctrl->addElement(row, ADD_BOTTOM, action_set->mActionSetData); diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index a7f66f3293..24e76e2c6e 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -178,10 +178,10 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, if (LLPanelLogin::sInstance) { llwarns << "Duplicate instance of login view deleted" << llendl; - delete LLPanelLogin::sInstance; - // Don't leave bad pointer in gFocusMgr gFocusMgr.setDefaultKeyboardFocus(NULL); + + delete LLPanelLogin::sInstance; } LLPanelLogin::sInstance = this; diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index b2a0a01005..c94c38983d 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -621,7 +621,7 @@ void LLPanelPeople::updateButtons() buttonSetEnabled("chat_btn", item_selected); } -const std::string& LLPanelPeople::getActiveTabName() const +std::string LLPanelPeople::getActiveTabName() const { return mTabContainer->getCurrentPanel()->getName(); } diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h index de27814388..03802015ce 100644 --- a/indra/newview/llpanelpeople.h +++ b/indra/newview/llpanelpeople.h @@ -63,7 +63,7 @@ private: void updateRecentList(); void updateButtons(); - const std::string& getActiveTabName() const; + std::string getActiveTabName() const; LLUUID getCurrentItemID() const; void buttonSetVisible(std::string btn_name, BOOL visible); void buttonSetEnabled(const std::string& btn_name, bool enabled); diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp index 42f9906409..2d3f901370 100644 --- a/indra/newview/llpanelpermissions.cpp +++ b/indra/newview/llpanelpermissions.cpp @@ -85,9 +85,6 @@ BOOL LLPanelPermissions::postBuild() childSetPrevalidate("Object Description",LLLineEditor::prevalidatePrintableNotPipe); - childSetAction("button owner profile",LLPanelPermissions::onClickOwner,this); - childSetAction("button creator profile",LLPanelPermissions::onClickCreator,this); - getChild<LLUICtrl>("button set group")->setCommitCallback(boost::bind(&LLPanelPermissions::onClickGroup,this)); childSetCommitCallback("checkbox share with group",LLPanelPermissions::onCommitGroupShare,this); @@ -162,12 +159,10 @@ void LLPanelPermissions::refresh() childSetEnabled("Creator:",false); childSetText("Creator Name",LLStringUtil::null); childSetEnabled("Creator Name",false); - childSetEnabled("button creator profile",false); childSetEnabled("Owner:",false); childSetText("Owner Name",LLStringUtil::null); childSetEnabled("Owner Name",false); - childSetEnabled("button owner profile",false); childSetEnabled("Group:",false); childSetText("Group Name",LLStringUtil::null); @@ -275,7 +270,6 @@ void LLPanelPermissions::refresh() childSetText("Creator Name",creator_name); childSetEnabled("Creator Name",TRUE); - childSetEnabled("button creator profile", creators_identical && mCreatorID.notNull() ); // Update owner text field childSetEnabled("Owner:",true); @@ -310,7 +304,6 @@ void LLPanelPermissions::refresh() childSetText("Owner Name",owner_name); childSetEnabled("Owner Name",TRUE); - childSetEnabled("button owner profile",owners_identical && (mOwnerID.notNull() || LLSelectMgr::getInstance()->selectIsGroupOwned())); // update group text field childSetEnabled("Group:",true); @@ -803,31 +796,6 @@ void LLPanelPermissions::onClickRelease(void*) LLSelectMgr::getInstance()->sendOwner(LLUUID::null, LLUUID::null); } -// static -void LLPanelPermissions::onClickCreator(void *data) -{ - LLPanelPermissions *self = (LLPanelPermissions *)data; - - LLAvatarActions::showProfile(self->mCreatorID); -} - -// static -void LLPanelPermissions::onClickOwner(void *data) -{ - LLPanelPermissions *self = (LLPanelPermissions *)data; - - if (LLSelectMgr::getInstance()->selectIsGroupOwned()) - { - LLUUID group_id; - LLSelectMgr::getInstance()->selectGetGroup(group_id); - LLGroupActions::show(group_id); - } - else - { - LLAvatarActions::showProfile(self->mOwnerID); - } -} - void LLPanelPermissions::onClickGroup() { LLUUID owner_id; diff --git a/indra/newview/llpanelpermissions.h b/indra/newview/llpanelpermissions.h index 481efe178e..805a4dbe97 100644 --- a/indra/newview/llpanelpermissions.h +++ b/indra/newview/llpanelpermissions.h @@ -42,12 +42,6 @@ // Panel for permissions of an object. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLCheckBoxCtrl; -class LLTextBox; -class LLButton; -class LLLineEditor; -class LLRadioGroup; -class LLComboBox; class LLNameBox; class LLPanelPermissions : public LLPanel @@ -56,31 +50,24 @@ public: LLPanelPermissions(); virtual ~LLPanelPermissions(); - virtual BOOL postBuild(); + /*virtual*/ BOOL postBuild(); - // MANIPULATORS void refresh(); // refresh all labels as needed protected: // statics static void onClickClaim(void*); static void onClickRelease(void*); - static void onClickCreator(void*); - static void onClickOwner(void*); void onClickGroup(); void cbGroupID(LLUUID group_id); static void onClickDeedToGroup(void*); static void onCommitPerm(LLUICtrl *ctrl, void *data, U8 field, U32 perm); -// static void onCommitGroupMove(LLUICtrl *ctrl, void *data); -// static void onCommitGroupCopy(LLUICtrl *ctrl, void *data); -// static void onCommitGroupModify(LLUICtrl *ctrl, void *data); static void onCommitGroupShare(LLUICtrl *ctrl, void *data); static void onCommitEveryoneMove(LLUICtrl *ctrl, void *data); static void onCommitEveryoneCopy(LLUICtrl *ctrl, void *data); - //static void onCommitEveryoneModify(LLUICtrl *ctrl, void *data); static void onCommitNextOwnerModify(LLUICtrl* ctrl, void* data); static void onCommitNextOwnerCopy(LLUICtrl* ctrl, void* data); diff --git a/indra/newview/llpanelpick.cpp b/indra/newview/llpanelpick.cpp index 5df3d4f1d6..424e453a2f 100644 --- a/indra/newview/llpanelpick.cpp +++ b/indra/newview/llpanelpick.cpp @@ -356,14 +356,7 @@ std::string LLPanelPick::createLocationText(const std::string& owner_name, const void LLPanelPick::setPickName(std::string name) { - if (mEditMode) - { - childSetValue(XML_NAME, name); - } - else - { - childSetWrappedText(XML_NAME, name); - } + childSetValue(XML_NAME, name); //preserving non-wrapped text for info/edit modes switching mName = name; @@ -371,14 +364,7 @@ void LLPanelPick::setPickName(std::string name) void LLPanelPick::setPickDesc(std::string desc) { - if (mEditMode) - { - childSetValue(XML_DESC, desc); - } - else - { - childSetWrappedText(XML_DESC, desc); - } + childSetValue(XML_DESC, desc); //preserving non-wrapped text for info/edit modes switching mDesc = desc; @@ -386,7 +372,7 @@ void LLPanelPick::setPickDesc(std::string desc) void LLPanelPick::setPickLocation(const std::string& location) { - childSetWrappedText(XML_LOCATION, location); + childSetValue(XML_LOCATION, location); //preserving non-wrapped text for info/edit modes switching mLocation = location; diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index be28129451..3bd2645be3 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -35,6 +35,7 @@ #include "llagent.h" #include "llavataractions.h" +#include "llfloaterreg.h" #include "llcommandhandler.h" #include "llpanelpicks.h" #include "lltabcontainer.h" @@ -52,15 +53,21 @@ public: LLMediaCtrl* web) { if (params.size() < 2) return false; - LLUUID agent_id; - if (!agent_id.set(params[0], FALSE)) + LLUUID avatar_id; + if (!avatar_id.set(params[0], FALSE)) { return false; } if (params[1].asString() == "about") { - LLAvatarActions::showProfile(agent_id); + LLAvatarActions::showProfile(avatar_id); + return true; + } + + if (params[1].asString() == "inspect") + { + LLFloaterReg::showInstance("inspect_avatar", LLSD().insert("avatar_id", avatar_id)); return true; } return false; diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp index 04827e3a78..faca950963 100644 --- a/indra/newview/llpreviewgesture.cpp +++ b/indra/newview/llpreviewgesture.cpp @@ -421,9 +421,7 @@ BOOL LLPreviewGesture::postBuild() mStepList = list; // Options - text = getChild<LLTextBox>("options_text"); - text->setBorderVisible(TRUE); - mOptionsText = text; + mOptionsText = getChild<LLTextBox>("options_text"); combo = getChild<LLComboBox>( "animation_list"); combo->setVisible(FALSE); diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index 29320522d9..ab9cfbf850 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -85,12 +85,8 @@ LLPreviewNotecard::~LLPreviewNotecard() BOOL LLPreviewNotecard::postBuild() { LLViewerTextEditor *ed = getChild<LLViewerTextEditor>("Notecard Editor"); - if (ed) - { - ed->setParseHTML(TRUE); - ed->setNotecardInfo(mItemUUID, mObjectID, getKey()); - ed->makePristine(); - } + ed->setNotecardInfo(mItemUUID, mObjectID, getKey()); + ed->makePristine(); childSetAction("Save", onClickSave, this); childSetVisible("lock", FALSE); diff --git a/indra/newview/llprogressview.cpp b/indra/newview/llprogressview.cpp index 3c6645f116..5f6b210767 100644 --- a/indra/newview/llprogressview.cpp +++ b/indra/newview/llprogressview.cpp @@ -196,7 +196,7 @@ void LLProgressView::draw() void LLProgressView::setText(const std::string& text) { - getChild<LLTextBox>("progress_text")->setWrappedText(LLStringExplicit(text)); + getChild<LLUICtrl>("progress_text")->setValue(text); } void LLProgressView::setPercent(const F32 percent) @@ -207,7 +207,7 @@ void LLProgressView::setPercent(const F32 percent) void LLProgressView::setMessage(const std::string& msg) { mMessage = msg; - getChild<LLTextBox>("message_text")->setWrappedText(LLStringExplicit(mMessage)); + getChild<LLUICtrl>("message_text")->setValue(mMessage); } void LLProgressView::setCancelButtonVisible(BOOL b, const std::string& label) diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index ae8c9f770b..d163ceb30e 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -43,6 +43,7 @@ #include "llrender.h" #include "llpermissions.h" #include "llpermissionsflags.h" +#include "lltrans.h" #include "llundo.h" #include "lluuid.h" #include "llvolume.h" @@ -66,6 +67,7 @@ #include "llinventorymodel.h" #include "llmenugl.h" #include "llmutelist.h" +#include "llslurl.h" #include "llstatusbar.h" #include "llsurface.h" #include "lltool.h" @@ -805,11 +807,13 @@ LLObjectSelectionHandle LLSelectMgr::setHoverObject(LLViewerObject *objectp, S32 // NOTE: there is only ever one linked set in mHoverObjects if (mHoverObjects->getFirstRootObject() != objectp) { + // Collect all of the objects std::vector<LLViewerObject*> objects; objectp = objectp->getRootEdit(); objectp->addThisAndNonJointChildren(objects); + mHoverObjects->deleteAllNodes(); for (std::vector<LLViewerObject*>::iterator iter = objects.begin(); iter != objects.end(); ++iter) { @@ -818,7 +822,7 @@ LLObjectSelectionHandle LLSelectMgr::setHoverObject(LLViewerObject *objectp, S32 nodep->selectTE(face, TRUE); mHoverObjects->addNodeAtEnd(nodep); } - + requestObjectPropertiesFamily(objectp); } @@ -2389,6 +2393,7 @@ BOOL LLSelectMgr::selectGetCreator(LLUUID& result_id, std::string& name) } if (first_id.isNull()) { + name = LLTrans::getString("AvatarNameNobody"); return FALSE; } @@ -2396,11 +2401,11 @@ BOOL LLSelectMgr::selectGetCreator(LLUUID& result_id, std::string& name) if (identical) { - gCacheName->getFullName(first_id, name); + name = LLSLURL::buildCommand("agent", first_id, "inspect"); } else { - name.assign( "(multiple)" ); + name = LLTrans::getString("AvatarNameMultiple"); } return identical; @@ -2455,20 +2460,21 @@ BOOL LLSelectMgr::selectGetOwner(LLUUID& result_id, std::string& name) BOOL public_owner = (first_id.isNull() && !first_group_owned); if (first_group_owned) { - name.assign( "(Group Owned)"); + // *TODO: We don't have group inspectors yet + name = LLSLURL::buildCommand("group", first_id, "about"); } else if(!public_owner) { - gCacheName->getFullName(first_id, name); + name = LLSLURL::buildCommand("agent", first_id, "inspect"); } else { - name.assign("Public"); + name = LLTrans::getString("AvatarNameNobody"); } } else { - name.assign( "(multiple)" ); + name = LLTrans::getString("AvatarNameMultiple"); } return identical; @@ -2519,7 +2525,7 @@ BOOL LLSelectMgr::selectGetLastOwner(LLUUID& result_id, std::string& name) BOOL public_owner = (first_id.isNull()); if(!public_owner) { - gCacheName->getFullName(first_id, name); + name = LLSLURL::buildCommand("agent", first_id, "inspect"); } else { @@ -5449,15 +5455,17 @@ void LLSelectNode::renderOneSilhouette(const LLColor4 &color) // Utility Functions // -// Update everyone who cares about the selection list +// *DEPRECATED: See header comment. void dialog_refresh_all() { - if (gNoRender) - { - return; - } + // This is the easiest place to fire the update signal, as it will + // make cleaning up the functions below easier. Also, sometimes entities + // outside the selection manager change properties of selected objects + // and call into this function. Yuck. + LLSelectMgr::getInstance()->mUpdateSignal(); - //could refresh selected object info in toolbar here + // *TODO: Eliminate all calls into outside classes below, make those + // objects register with the update signal. gFloaterTools->dirty(); @@ -5853,6 +5861,27 @@ void LLSelectMgr::setAgentHUDZoom(F32 target_zoom, F32 current_zoom) gAgent.mHUDCurZoom = current_zoom; } +///////////////////////////////////////////////////////////////////////////// +// Object selection iterator helpers +///////////////////////////////////////////////////////////////////////////// +bool LLObjectSelection::is_root::operator()(LLSelectNode *node) +{ + LLViewerObject* object = node->getObject(); + return (object != NULL) && !node->mIndividualSelection && (object->isRootEdit() || object->isJointChild()); +} + +bool LLObjectSelection::is_valid_root::operator()(LLSelectNode *node) +{ + LLViewerObject* object = node->getObject(); + return (object != NULL) && node->mValid && !node->mIndividualSelection && (object->isRootEdit() || object->isJointChild()); +} + +bool LLObjectSelection::is_root_object::operator()(LLSelectNode *node) +{ + LLViewerObject* object = node->getObject(); + return (object != NULL) && (object->isRootEdit() || object->isJointChild()); +} + LLObjectSelection::LLObjectSelection() : LLRefCount(), mSelectType(SELECT_TYPE_WORLD) diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index 9e02170d74..26ac95060f 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -35,7 +35,6 @@ #include "llcharacter.h" #include "lleditmenuhandler.h" -#include "llstring.h" #include "llundo.h" #include "lluuid.h" #include "llpointer.h" @@ -48,14 +47,15 @@ #include "llframetimer.h" #include "llbbox.h" #include "llpermissions.h" -#include "llviewerobject.h" #include "llcontrol.h" +#include "llviewerobject.h" // LLObjectSelection::getSelectedTEValue template + #include <deque> -#include "boost/iterator/filter_iterator.hpp" +#include <boost/iterator/filter_iterator.hpp> +#include <boost/signals2.hpp> class LLMessageSystem; class LLViewerTexture; -class LLViewerObject; class LLColor4; class LLVector3; class LLSelectNode; @@ -203,13 +203,9 @@ class LLObjectSelection : public LLRefCount protected: ~LLObjectSelection(); - // List public: typedef std::list<LLSelectNode*> list_t; -private: - list_t mList; -public: // Iterators struct is_non_null { @@ -235,11 +231,7 @@ public: struct is_root { - bool operator()(LLSelectNode* node) - { - LLViewerObject* object = node->getObject(); - return (object != NULL) && !node->mIndividualSelection && (object->isRootEdit() || object->isJointChild()); - } + bool operator()(LLSelectNode* node); }; typedef boost::filter_iterator<is_root, list_t::iterator > root_iterator; root_iterator root_begin() { return root_iterator(mList.begin(), mList.end()); } @@ -247,11 +239,7 @@ public: struct is_valid_root { - bool operator()(LLSelectNode* node) - { - LLViewerObject* object = node->getObject(); - return (object != NULL) && node->mValid && !node->mIndividualSelection && (object->isRootEdit() || object->isJointChild()); - } + bool operator()(LLSelectNode* node); }; typedef boost::filter_iterator<is_root, list_t::iterator > valid_root_iterator; valid_root_iterator valid_root_begin() { return valid_root_iterator(mList.begin(), mList.end()); } @@ -259,11 +247,7 @@ public: struct is_root_object { - bool operator()(LLSelectNode* node) - { - LLViewerObject* object = node->getObject(); - return (object != NULL) && (object->isRootEdit() || object->isJointChild()); - } + bool operator()(LLSelectNode* node); }; typedef boost::filter_iterator<is_root_object, list_t::iterator > root_object_iterator; root_object_iterator root_object_begin() { return root_object_iterator(mList.begin(), mList.end()); } @@ -326,6 +310,7 @@ public: ESelectType getSelectType() const { return mSelectType; } private: + list_t mList; const LLObjectSelection &operator=(const LLObjectSelection &); LLPointer<LLViewerObject> mPrimaryObject; @@ -398,13 +383,16 @@ public: // Add //////////////////////////////////////////////////////////////// - // For when you want just a child object. - LLObjectSelectionHandle selectObjectOnly(LLViewerObject* object, S32 face = SELECT_ALL_TES); - // This method is meant to select an object, and then select all - // of the ancestors and descendents. This should be the normal behavior. + // of the ancestors and descendants. This should be the normal behavior. + // + // *NOTE: You must hold on to the object selection handle, otherwise + // the objects will be automatically deselected in 1 frame. LLObjectSelectionHandle selectObjectAndFamily(LLViewerObject* object, BOOL add_to_end = FALSE); + // For when you want just a child object. + LLObjectSelectionHandle selectObjectOnly(LLViewerObject* object, S32 face = SELECT_ALL_TES); + // Same as above, but takes a list of objects. Used by rectangle select. LLObjectSelectionHandle selectObjectAndFamily(const std::vector<LLViewerObject*>& object_list, BOOL send_to_sim = TRUE); @@ -691,7 +679,13 @@ private: static void packPermissionsHead(void* user_data); static void packGodlikeHead(void* user_data); static bool confirmDelete(const LLSD& notification, const LLSD& response, LLObjectSelectionHandle handle); - + +public: + // Observer/callback support for when object selection changes or + // properties are received/updated + typedef boost::signals2::signal< void ()> update_signal_t; + update_signal_t mUpdateSignal; + private: LLPointer<LLViewerTexture> mSilhouetteImagep; LLObjectSelectionHandle mSelectedObjects; @@ -723,8 +717,10 @@ private: LLAnimPauseRequest mPauseRequest; }; -// Utilities -void dialog_refresh_all(); // Update subscribers to the selection list +// *DEPRECATED: For callbacks or observers, use +// LLSelectMgr::getInstance()->mUpdateSignal.connect( callback ) +// Update subscribers to the selection list +void dialog_refresh_all(); // Templates //----------------------------------------------------------------------------- diff --git a/indra/newview/llslurl.cpp b/indra/newview/llslurl.cpp index 836fe9729d..37e268ad34 100644 --- a/indra/newview/llslurl.cpp +++ b/indra/newview/llslurl.cpp @@ -106,6 +106,14 @@ std::string LLSLURL::buildSLURL(const std::string& regionname, S32 x, S32 y, S32 } // static +std::string LLSLURL::buildCommand(const char* noun, const LLUUID& id, const char* verb) +{ + std::string slurl = llformat("secondlife:///app/%s/%s/%s", + noun, id.asString().c_str(), verb); + return slurl; +} + +// static std::string LLSLURL::buildUnescapedSLURL(const std::string& regionname, S32 x, S32 y, S32 z) { std::string unescapedslurl = PREFIX_SLURL + regionname + llformat("/%d/%d/%d",x,y,z); diff --git a/indra/newview/llslurl.h b/indra/newview/llslurl.h index 8af2bdfb83..05b0143e72 100644 --- a/indra/newview/llslurl.h +++ b/indra/newview/llslurl.h @@ -73,6 +73,9 @@ public: */ static std::string buildSLURL(const std::string& regionname, S32 x, S32 y, S32 z); + /// Build a SLURL like secondlife:///app/agent/<uuid>/inspect + static std::string buildCommand(const char* noun, const LLUUID& id, const char* verb); + /** * builds: http://slurl.com/secondlife/Region Name/x/y/z/ without escaping result url. */ diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 053b863b6d..62435c6288 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -195,6 +195,10 @@ #include "lldxhardware.h" #endif +#if (LL_LINUX || LL_SOLARIS) && LL_GTK +#include <glib/gspawn.h> +#endif + // // exported globals // @@ -1824,9 +1828,9 @@ bool idle_startup() { gCacheName = new LLCacheName(gMessageSystem); gCacheName->addObserver(&callback_cache_name); - gCacheName->LocalizeCacheName("waiting", LLTrans::getString("CacheWaiting")); - gCacheName->LocalizeCacheName("nobody", LLTrans::getString("CacheNobody")); - gCacheName->LocalizeCacheName("none", LLTrans::getString("CacheNone")); + gCacheName->LocalizeCacheName("waiting", LLTrans::getString("AvatarNameWaiting")); + gCacheName->LocalizeCacheName("nobody", LLTrans::getString("AvatarNameNobody")); + gCacheName->LocalizeCacheName("none", LLTrans::getString("GroupNameNone")); // Load stored cache if possible LLAppViewer::instance()->loadNameCache(); } diff --git a/indra/newview/llstylemap.cpp b/indra/newview/llstylemap.cpp index fc125dcf9c..2485563cbc 100644 --- a/indra/newview/llstylemap.cpp +++ b/indra/newview/llstylemap.cpp @@ -33,8 +33,10 @@ #include "llviewerprecompiledheaders.h" #include "llstylemap.h" + #include "llstring.h" #include "llui.h" +#include "llslurl.h" #include "llviewercontrol.h" #include "llagent.h" @@ -47,7 +49,8 @@ const LLStyle::Params &LLStyleMap::lookupAgent(const LLUUID &source) if (source != LLUUID::null && source != gAgent.getID() ) { style_params.color.control = "HTMLLinkColor"; - style_params.link_href = llformat("secondlife:///app/agent/%s/about",source.asString().c_str()); + style_params.link_href = + LLSLURL::buildCommand("agent", source, "inspect"); } else { @@ -55,7 +58,7 @@ const LLStyle::Params &LLStyleMap::lookupAgent(const LLUUID &source) style_params.color = LLColor4::white; } - mMap[source] = LLStyle::Params(); + mMap[source] = style_params; } return mMap[source]; } diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 1b47fa43c7..9fc91e2971 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -882,7 +882,7 @@ LLTextureCtrl::LLTextureCtrl(const LLTextureCtrl::Params& p) LLTextBox::Params params(p.caption_text); params.name(p.label); params.rect(LLRect( 0, BTN_HEIGHT_SMALL, getRect().getWidth(), 0 )); - params.text(p.label); + params.initial_value(p.label()); params.follows.flags(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM); mCaption = LLUICtrlFactory::create<LLTextBox> (params); addChild( mCaption ); diff --git a/indra/newview/lltoastalertpanel.cpp b/indra/newview/lltoastalertpanel.cpp index 4309f56710..d9be6b172c 100644 --- a/indra/newview/lltoastalertpanel.cpp +++ b/indra/newview/lltoastalertpanel.cpp @@ -168,6 +168,7 @@ LLToastAlertPanel::LLToastAlertPanel( LLNotificationPtr notification, bool modal params.name("Alert message"); params.font(font); params.tab_stop(false); + params.wrap(true); params.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP); LLTextBox * msg_box = LLUICtrlFactory::create<LLTextBox> (params); @@ -178,7 +179,7 @@ LLToastAlertPanel::LLToastAlertPanel( LLNotificationPtr notification, bool modal - LINE_HEIGHT // title bar - 3*VPAD - BTN_HEIGHT; msg_box->reshape( MAX_ALLOWED_MSG_WIDTH, max_allowed_msg_height ); - msg_box->setWrappedText(msg, (F32)MAX_ALLOWED_MSG_WIDTH); + msg_box->setValue(msg); msg_box->reshapeToFitText(); const LLRect& text_rect = msg_box->getRect(); diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp index 9761a45d83..86b162247a 100644 --- a/indra/newview/lltoastnotifypanel.cpp +++ b/indra/newview/lltoastnotifypanel.cpp @@ -144,10 +144,11 @@ LLToastNotifyPanel::LLToastNotifyPanel(LLNotificationPtr& notification) : LLToas params.mouse_opaque(false); params.font.style("BOLD"); params.text_color(LLUIColorTable::instance().getColor("NotifyCautionWarnColor")); - params.background_color(LLUIColorTable::instance().getColor("NotifyCautionBoxColor")); + params.bg_readonly_color(LLUIColorTable::instance().getColor("NotifyCautionBoxColor")); params.border_visible(false); + params.wrap(true); caution_box = LLUICtrlFactory::create<LLTextBox> (params); - caution_box->setWrappedText(notification->getMessage()); + caution_box->setValue(notification->getMessage()); addChild(caution_box); @@ -175,13 +176,13 @@ LLToastNotifyPanel::LLToastNotifyPanel(LLNotificationPtr& notification) : LLToas params.default_text(mMessage); params.font(sFont); params.embedded_items(false); - params.word_wrap(true); + params.wrap(true); params.tab_stop(false); params.mouse_opaque(false); params.bg_readonly_color(LLColor4::transparent); params.text_readonly_color(LLUIColorTable::instance().getColor("NotifyTextColor")); params.enabled(false); - params.hide_border(true); + params.border_visible(false); text = LLUICtrlFactory::create<LLTextEditor> (params); addChild(text); } diff --git a/indra/newview/lltoastpanel.cpp b/indra/newview/lltoastpanel.cpp index e884d89ce4..68ad043129 100644 --- a/indra/newview/lltoastpanel.cpp +++ b/indra/newview/lltoastpanel.cpp @@ -56,7 +56,7 @@ void LLToastPanel::snapToMessageHeight(LLTextBox* message, S32 maxLineCount) if (message->getVisible()) { S32 heightDelta = 0; - S32 maxTextHeight = (S32)(message->getFont()->getLineHeight() * maxLineCount); + S32 maxTextHeight = (S32)(message->getDefaultFont()->getLineHeight() * maxLineCount); LLRect messageRect = message->getRect(); S32 oldTextHeight = messageRect.getHeight(); diff --git a/indra/newview/lltool.cpp b/indra/newview/lltool.cpp index 880d5d5e12..669a62a238 100644 --- a/indra/newview/lltool.cpp +++ b/indra/newview/lltool.cpp @@ -155,7 +155,7 @@ BOOL LLTool::handleMiddleMouseUp(S32 x, S32 y, MASK mask) return FALSE; } -BOOL LLTool::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen) +BOOL LLTool::handleToolTip(S32 x, S32 y, MASK mask) { // by default, didn't handle it // llinfos << "LLTool::handleToolTip" << llendl; diff --git a/indra/newview/lltool.h b/indra/newview/lltool.h index bef4a2e1fd..c3573cd38c 100644 --- a/indra/newview/lltool.h +++ b/indra/newview/lltool.h @@ -66,7 +66,7 @@ public: virtual BOOL handleDoubleClick(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 handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen); + virtual BOOL handleToolTip(S32 x, S32 y, MASK mask); // Return FALSE to allow context menu to be shown. virtual void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const @@ -74,7 +74,7 @@ public: virtual void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const { *screen_x = local_x; *screen_y = local_y; } - virtual const std::string& getName() const { return mName; } + virtual std::string getName() const { return mName; } // New virtual functions virtual LLViewerObject* getEditingObject() { return NULL; } diff --git a/indra/newview/lltoolbar.cpp b/indra/newview/lltoolbar.cpp index c7c9f07504..0572f9a698 100644 --- a/indra/newview/lltoolbar.cpp +++ b/indra/newview/lltoolbar.cpp @@ -271,6 +271,7 @@ void LLToolBar::updateCommunicateList() contact_sd["columns"][0]["value"] = LLFloaterMyFriends::getInstance()->getShortTitle(); if (LLFloaterMyFriends::getInstance() == frontmost_floater) { + contact_sd["columns"][0]["font"]["name"] = "SANSSERIF_SMALL"; contact_sd["columns"][0]["font"]["style"] = "BOLD"; // make sure current tab is selected in list if (selected.isUndefined()) @@ -286,6 +287,7 @@ void LLToolBar::updateCommunicateList() if (LLFloaterChat::getInstance() == frontmost_floater) { + communicate_sd["columns"][0]["font"]["name"] = "SANSSERIF_SMALL"; communicate_sd["columns"][0]["font"]["style"] = "BOLD"; if (selected.isUndefined()) { @@ -318,6 +320,7 @@ void LLToolBar::updateCommunicateList() im_sd["columns"][0]["value"] = floater_title; if (im_floaterp == frontmost_floater) { + im_sd["columns"][0]["font"]["name"] = "SANSSERIF_SMALL"; im_sd["columns"][0]["font"]["style"] = "BOLD"; if (selected.isUndefined()) { diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index c3064ffa43..bc0a654eb9 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -762,11 +762,11 @@ BOOL LLToolDragAndDrop::handleKey(KEY key, MASK mask) return FALSE; } -BOOL LLToolDragAndDrop::handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen) +BOOL LLToolDragAndDrop::handleToolTip(S32 x, S32 y, MASK mask) { if (!mToolTipMsg.empty()) { - LLToolTipMgr::instance().show(LLToolTipParams() + LLToolTipMgr::instance().show(LLToolTip::Params() .message(mToolTipMsg) .delay_time(gSavedSettings.getF32( "DragAndDropToolTipDelay" ))); return TRUE; diff --git a/indra/newview/lltooldraganddrop.h b/indra/newview/lltooldraganddrop.h index acf01869e7..e1536acf75 100644 --- a/indra/newview/lltooldraganddrop.h +++ b/indra/newview/lltooldraganddrop.h @@ -59,7 +59,7 @@ public: virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); virtual BOOL handleHover(S32 x, S32 y, MASK mask); virtual BOOL handleKey(KEY key, MASK mask); - virtual BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen); + virtual BOOL handleToolTip(S32 x, S32 y, MASK mask); virtual void onMouseCaptureLost(); virtual void handleDeselect(); diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index d5db224143..5525c359fc 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -70,9 +70,6 @@ #include "llworld.h" #include "llui.h" #include "llweb.h" -#include "llinspectavatar.h" - -extern void handle_buy(void*); extern BOOL gDebugClicks; @@ -212,7 +209,7 @@ BOOL LLToolPie::pickLeftMouseDownCallback() case CLICK_ACTION_SIT: if ((gAgent.getAvatarObject() != NULL) && (!gAgent.getAvatarObject()->isSitting())) // agent not already sitting { - handle_sit_or_stand(); + handle_object_sit_or_stand(); // put focus in world when sitting on an object gFocusMgr.setKeyboardFocus(NULL); return TRUE; @@ -456,7 +453,7 @@ void LLToolPie::selectionPropertiesReceived() switch (click_action) { case CLICK_ACTION_BUY: - handle_buy(NULL); + handle_buy(); break; case CLICK_ACTION_PAY: handle_give_money_dialog(); @@ -586,10 +583,39 @@ BOOL LLToolPie::handleDoubleClick(S32 x, S32 y, MASK mask) return FALSE; } -//FIXME - RN: get this in LLToolSelectLand too or share some other way? -const char* DEFAULT_DESC = "(No Description)"; +static bool needs_tooltip(LLSelectNode* nodep) +{ + LLViewerObject* object = nodep->getObject(); + LLViewerObject *parent = (LLViewerObject *)object->getParent(); + if (object->flagHandleTouch() + || (parent && parent->flagHandleTouch()) + || object->flagTakesMoney() + || (parent && parent->flagTakesMoney()) + || object->flagAllowInventoryAdd() + ) + { + return true; + } + + U8 click_action = final_click_action(object); + if (click_action != 0) + { + return true; + } + + if (nodep->mValid) + { + bool anyone_copy = anyone_copy_selection(nodep); + bool for_sale = for_sale_selection(nodep); + if (anyone_copy || for_sale) + { + return true; + } + } + return false; +} -BOOL LLToolPie::handleToolTip(S32 local_x, S32 local_y, std::string& msg, LLRect& sticky_rect_screen) +BOOL LLToolPie::handleToolTip(S32 local_x, S32 local_y, MASK mask) { if (!LLUI::sSettingGroups["config"]->getBOOL("ShowHoverTips")) return TRUE; if (!mHoverPick.isValid()) return TRUE; @@ -636,8 +662,11 @@ BOOL LLToolPie::handleToolTip(S32 local_x, S32 local_y, std::string& msg, LLRect line.clear(); if (hover_object->isAvatar()) { - // only show tooltip if inspector not already open - if (!LLFloaterReg::instanceVisible("inspect_avatar")) + // only show tooltip if same inspector not already open + LLFloater* existing_inspector = LLFloaterReg::findInstance("inspect_avatar"); + if (!existing_inspector + || !existing_inspector->getVisible() + || existing_inspector->getKey()["avatar_id"].asUUID() != hover_object->getID()) { std::string avatar_name; LLNameValue* firstname = hover_object->getNVPair("FirstName"); @@ -650,12 +679,15 @@ BOOL LLToolPie::handleToolTip(S32 local_x, S32 local_y, std::string& msg, LLRect { avatar_name = LLTrans::getString("TooltipPerson"); } - LLToolTipParams params; - params.message(avatar_name); - params.image.name("Info"); - params.sticky_rect(gViewerWindow->getVirtualWorldViewRect()); - params.click_callback(boost::bind(showAvatarInspector, hover_object->getID())); - LLToolTipMgr::instance().show(params); + + // *HACK: We may select this object, so pretend it was clicked + mPick = mHoverPick; + LLToolTipMgr::instance().show(LLToolTip::Params() + .message(avatar_name) + .image(LLUI::getUIImage("Info")) + .click_callback(boost::bind(showAvatarInspector, hover_object->getID())) + .visible_time_near(6.f) + .visible_time_far(3.f)); } } else @@ -667,177 +699,38 @@ BOOL LLToolPie::handleToolTip(S32 local_x, S32 local_y, std::string& msg, LLRect // // Default prefs will suppress display unless the object is interactive // - BOOL suppressObjectHoverDisplay = !gSavedSettings.getBOOL("ShowAllObjectHoverTip"); - + bool show_all_object_tips = + (bool)gSavedSettings.getBOOL("ShowAllObjectHoverTip"); LLSelectNode *nodep = LLSelectMgr::getInstance()->getHoverNode(); - if (nodep) + + // only show tooltip if same inspector not already open + LLFloater* existing_inspector = LLFloaterReg::findInstance("inspect_object"); + if (nodep && + (!existing_inspector + || !existing_inspector->getVisible() + || existing_inspector->getKey()["object_id"].asUUID() != hover_object->getID())) { - line.clear(); if (nodep->mName.empty()) { - line.append(LLTrans::getString("TooltipNoName")); - } - else - { - line.append( nodep->mName ); - } - tooltip_msg.append(line); - tooltip_msg.push_back('\n'); - - if (!nodep->mDescription.empty() - && nodep->mDescription != DEFAULT_DESC) - { - tooltip_msg.append( nodep->mDescription ); - tooltip_msg.push_back('\n'); - } - - // Line: "Owner: James Linden" - line.clear(); - line.append(LLTrans::getString("TooltipOwner") + " "); - - if (nodep->mValid) - { - LLUUID owner; - std::string name; - if (!nodep->mPermissions->isGroupOwned()) - { - owner = nodep->mPermissions->getOwner(); - if (LLUUID::null == owner) - { - line.append(LLTrans::getString("TooltipPublic")); - } - else if(gCacheName->getFullName(owner, name)) - { - line.append(name); - } - else - { - line.append(LLTrans::getString("RetrievingData")); - } - } - else - { - std::string name; - owner = nodep->mPermissions->getGroup(); - if (gCacheName->getGroupName(owner, name)) - { - line.append(name); - line.append(LLTrans::getString("TooltipIsGroup")); - } - else - { - line.append(LLTrans::getString("RetrievingData")); - } - } + tooltip_msg.append(LLTrans::getString("TooltipNoName")); } else { - line.append(LLTrans::getString("RetrievingData")); + tooltip_msg.append( nodep->mName ); } - tooltip_msg.append(line); - tooltip_msg.push_back('\n'); - // Build a line describing any special properties of this object. - LLViewerObject *object = hover_object; - LLViewerObject *parent = (LLViewerObject *)object->getParent(); - - if (object && - (object->usePhysics() || - object->flagScripted() || - object->flagHandleTouch() || (parent && parent->flagHandleTouch()) || - object->flagTakesMoney() || (parent && parent->flagTakesMoney()) || - object->flagAllowInventoryAdd() || - object->flagTemporary() || - object->flagPhantom()) ) - { - line.clear(); - if (object->flagScripted()) - { - line.append(LLTrans::getString("TooltipFlagScript") + " "); - } - - if (object->usePhysics()) - { - line.append(LLTrans::getString("TooltipFlagPhysics") + " "); - } - - if (object->flagHandleTouch() || (parent && parent->flagHandleTouch()) ) - { - line.append(LLTrans::getString("TooltipFlagTouch") + " "); - suppressObjectHoverDisplay = FALSE; // Show tip - } - - if (object->flagTakesMoney() || (parent && parent->flagTakesMoney()) ) - { - line.append(LLTrans::getString("TooltipFlagL$") + " "); - suppressObjectHoverDisplay = FALSE; // Show tip - } - - if (object->flagAllowInventoryAdd()) - { - line.append(LLTrans::getString("TooltipFlagDropInventory") + " "); - suppressObjectHoverDisplay = FALSE; // Show tip - } - - if (object->flagPhantom()) - { - line.append(LLTrans::getString("TooltipFlagPhantom") + " "); - } - - if (object->flagTemporary()) - { - line.append(LLTrans::getString("TooltipFlagTemporary") + " "); - } - - if (object->usePhysics() || - object->flagHandleTouch() || - (parent && parent->flagHandleTouch()) ) - { - line.append(LLTrans::getString("TooltipFlagRightClickMenu") + " "); - } - tooltip_msg.append(line); - tooltip_msg.push_back('\n'); - } - - // Free to copy / For Sale: L$ - line.clear(); - if (nodep->mValid) - { - BOOL for_copy = nodep->mPermissions->getMaskEveryone() & PERM_COPY && object->permCopy(); - BOOL for_sale = nodep->mSaleInfo.isForSale() && - nodep->mPermissions->getMaskOwner() & PERM_TRANSFER && - (nodep->mPermissions->getMaskOwner() & PERM_COPY || - nodep->mSaleInfo.getSaleType() != LLSaleInfo::FS_COPY); - if (for_copy) - { - line.append(LLTrans::getString("TooltipFreeToCopy")); - suppressObjectHoverDisplay = FALSE; // Show tip - } - else if (for_sale) - { - LLStringUtil::format_map_t args; - args["[AMOUNT]"] = llformat("%d", nodep->mSaleInfo.getSalePrice()); - line.append(LLTrans::getString("TooltipForSaleL$", args)); - suppressObjectHoverDisplay = FALSE; // Show tip - } - else - { - // Nothing if not for sale - // line.append("Not for sale"); - } - } - else - { - LLStringUtil::format_map_t args; - args["[MESSAGE]"] = LLTrans::getString("RetrievingData"); - line.append(LLTrans::getString("TooltipForSaleMsg", args)); - } - tooltip_msg.append(line); - tooltip_msg.push_back('\n'); + bool needs_tip = needs_tooltip(nodep); - if (!suppressObjectHoverDisplay) + if (show_all_object_tips || needs_tip) { - LLToolTipMgr::instance().show(tooltip_msg); + // We may select this object, so pretend it was clicked + mPick = mHoverPick; + LLToolTipMgr::instance().show(LLToolTip::Params() + .message(tooltip_msg) + .image(LLUI::getUIImage("Info")) + .click_callback(boost::bind(showObjectInspector, hover_object->getID())) + .visible_time_near(6.f) + .visible_time_far(3.f)); } } } @@ -990,18 +883,23 @@ BOOL LLToolPie::handleToolTip(S32 local_x, S32 local_y, std::string& msg, LLRect tooltip_msg.append(line); tooltip_msg.push_back('\n'); } - LLToolTipMgr::instance().show(tooltip_msg); + + // trim last newlines + if (!tooltip_msg.empty()) + { + tooltip_msg.erase(tooltip_msg.size() - 1); + LLToolTipMgr::instance().show(tooltip_msg); + } } return TRUE; } -// static -void LLToolPie::showAvatarInspector(const LLUUID& avatar_id) +static void show_inspector(const char* inspector, const char* param, const LLUUID& source_id) { LLSD params; - params["avatar_id"] = avatar_id; + params[param] = source_id; if (LLToolTipMgr::instance().toolTipVisible()) { LLRect rect = LLToolTipMgr::instance().getToolTipRect(); @@ -1009,7 +907,19 @@ void LLToolPie::showAvatarInspector(const LLUUID& avatar_id) params["pos"]["y"] = rect.mTop; } - LLFloaterReg::showInstance("inspect_avatar", params); + LLFloaterReg::showInstance(inspector, params); +} + +// static +void LLToolPie::showAvatarInspector(const LLUUID& avatar_id) +{ + show_inspector("inspect_avatar", "avatar_id", avatar_id); +} + +// static +void LLToolPie::showObjectInspector(const LLUUID& object_id) +{ + show_inspector("inspect_object", "object_id", object_id); } void LLToolPie::handleDeselect() diff --git a/indra/newview/lltoolpie.h b/indra/newview/lltoolpie.h index f6a67c13b1..5faedbec5a 100644 --- a/indra/newview/lltoolpie.h +++ b/indra/newview/lltoolpie.h @@ -55,7 +55,7 @@ public: virtual BOOL handleHover(S32 x, S32 y, MASK mask); virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask); virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); - virtual BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen); + virtual BOOL handleToolTip(S32 x, S32 y, MASK mask); virtual void render(); @@ -76,7 +76,8 @@ public: static void selectionPropertiesReceived(); - static void showAvatarInspector(const LLUUID& id); + static void showAvatarInspector(const LLUUID& avatar_id); + static void showObjectInspector(const LLUUID& object_id); private: BOOL outsideSlop (S32 x, S32 y, S32 start_x, S32 start_y); BOOL pickLeftMouseDownCallback(); diff --git a/indra/newview/lltoolpipette.cpp b/indra/newview/lltoolpipette.cpp index 9a92f2ae3f..beebb47537 100644 --- a/indra/newview/lltoolpipette.cpp +++ b/indra/newview/lltoolpipette.cpp @@ -93,7 +93,7 @@ BOOL LLToolPipette::handleHover(S32 x, S32 y, MASK mask) return FALSE; } -BOOL LLToolPipette::handleToolTip(S32 x, S32 y, std::string& msg, LLRect &sticky_rect_screen) +BOOL LLToolPipette::handleToolTip(S32 x, S32 y, MASK mask) { if (mTooltipMsg.empty()) { @@ -102,7 +102,7 @@ BOOL LLToolPipette::handleToolTip(S32 x, S32 y, std::string& msg, LLRect &sticky LLRect sticky_rect; sticky_rect.setCenterAndSize(x, y, 20, 20); - LLToolTipMgr::instance().show(LLToolTipParams() + LLToolTipMgr::instance().show(LLToolTip::Params() .message(mTooltipMsg) .sticky_rect(sticky_rect)); diff --git a/indra/newview/lltoolpipette.h b/indra/newview/lltoolpipette.h index cce5b6ce54..f86939cfeb 100644 --- a/indra/newview/lltoolpipette.h +++ b/indra/newview/lltoolpipette.h @@ -56,7 +56,7 @@ public: 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 handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen); + virtual BOOL handleToolTip(S32 x, S32 y, MASK mask); // Note: Don't return connection; use boost::bind + boost::signals2::trackable to disconnect slots typedef boost::signals2::signal<void (const LLTextureEntry& te)> signal_t; diff --git a/indra/newview/lluploaddialog.cpp b/indra/newview/lluploaddialog.cpp index 153e3e7382..577b5952e5 100644 --- a/indra/newview/lluploaddialog.cpp +++ b/indra/newview/lluploaddialog.cpp @@ -82,7 +82,7 @@ LLUploadDialog::LLUploadDialog( const std::string& msg) LLTextBox::Params params; params.name("Filename"); params.rect(msg_rect); - params.text("Filename"); + params.initial_value("Filename"); params.font(font); mLabelBox[line_num] = LLUICtrlFactory::create<LLTextBox> (params); addChild(mLabelBox[line_num]); diff --git a/indra/newview/llviewchildren.cpp b/indra/newview/llviewchildren.cpp index 842ffc7f9a..41eafa871d 100644 --- a/indra/newview/llviewchildren.cpp +++ b/indra/newview/llviewchildren.cpp @@ -74,17 +74,6 @@ void LLViewChildren::setText( } } -void LLViewChildren::setWrappedText( - const std::string& id, const std::string& text, bool visible) -{ - LLTextBox* child = mParent.getChild<LLTextBox>(id); - if (child) - { - child->setVisible(visible); - child->setWrappedText(text); - } -} - void LLViewChildren::setBadge(const std::string& id, Badge badge, bool visible) { LLIconCtrl* child = mParent.getChild<LLIconCtrl>(id); diff --git a/indra/newview/llviewchildren.h b/indra/newview/llviewchildren.h index 9263d77bdc..6cfa535a94 100644 --- a/indra/newview/llviewchildren.h +++ b/indra/newview/llviewchildren.h @@ -53,8 +53,6 @@ public: // LLTextBox void setText(const std::string& id, const std::string& text, bool visible = true); - void setWrappedText(const std::string& id, - const std::string& text, bool visible = true); // LLIconCtrl enum Badge { BADGE_OK, BADGE_NOTE, BADGE_WARN, BADGE_ERROR }; diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 2f656479f6..0b21f3565d 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -110,6 +110,7 @@ #include "llfloaterwindlight.h" #include "llfloaterworldmap.h" #include "llinspectavatar.h" +#include "llinspectobject.h" #include "llmediaremotectrl.h" #include "llmoveview.h" #include "llnearbychat.h" @@ -172,8 +173,8 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("impanel", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLIMFloater>); LLFloaterReg::add("inventory", "floater_inventory.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterInventory>); LLFloaterReg::add("inspect", "floater_inspect.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterInspect>); - LLFloaterReg::add("inspect_avatar", "inspect_avatar.xml", - &LLFloaterReg::build<LLInspectAvatar>); + LLInspectAvatarUtil::registerFloater(); + LLInspectObjectUtil::registerFloater(); LLFloaterReg::add("lagmeter", "floater_lagmeter.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterLagMeter>); LLFloaterReg::add("land_holdings", "floater_land_holdings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterLandHoldings>); diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp index 9f7e4d338b..b919e3d1c1 100644 --- a/indra/newview/llviewerjoystick.cpp +++ b/indra/newview/llviewerjoystick.cpp @@ -282,7 +282,7 @@ void LLViewerJoystick::terminate() ndof_libcleanup(); llinfos << "Terminated connection with NDOF device." << llendl; - + mDriverState = JDS_UNINITIALIZED; #endif } diff --git a/indra/newview/llviewerjoystick.h b/indra/newview/llviewerjoystick.h index 6be9db3313..b565ed5696 100644 --- a/indra/newview/llviewerjoystick.h +++ b/indra/newview/llviewerjoystick.h @@ -56,6 +56,8 @@ public: virtual ~LLViewerJoystick(); void init(bool autoenable); + void terminate(); + void updateStatus(); void scanJoystick(); void moveObjects(bool reset = false); @@ -76,7 +78,6 @@ public: protected: void updateEnabled(bool autoenable); - void terminate(); void handleRun(F32 inc); void agentSlide(F32 inc); void agentPush(F32 inc); diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index 775f72d56f..c9ba5841e9 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -193,12 +193,11 @@ public: /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask) { return FALSE; }; /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) { return FALSE; }; /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask) { return FALSE; }; - /*virtual*/ BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen) { return FALSE; }; + /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask) { return FALSE; }; /*virtual*/ BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; }; /*virtual*/ BOOL handleMiddleMouseUp(S32 x, S32 y, MASK mask) {return FALSE; }; - /*virtual*/ const std::string& getName() const { return LLStringUtil::null; }; + /*virtual*/ std::string getName() const { return LLStringUtil::null; }; - /*virtual*/ BOOL isView() const { return FALSE; }; /*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() { return gFocusMgr.getMouseCapture() == this; }; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 12253455a3..d95992412f 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -80,6 +80,7 @@ #include "lldrawpoolalpha.h" #include "lldrawpooltree.h" #include "llface.h" +#include "llfilepicker.h" #include "llfirstuse.h" #include "llfirsttimetipmanager.h" #include "llfloater.h" @@ -295,8 +296,7 @@ S32 selection_price(); BOOL enable_take(); void handle_take(); bool confirm_take(const LLSD& notification, const LLSD& response); -BOOL enable_buy(void*); -void handle_buy(void *); + void handle_buy_object(LLSaleInfo sale_info); void handle_buy_contents(LLSaleInfo sale_info); @@ -2490,12 +2490,10 @@ class LLObjectEnableReportAbuse : public view_listener_t } }; -class LLObjectTouch : public view_listener_t +void handle_object_touch() { - bool handleEvent(const LLSD& userdata) - { LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - if (!object) return true; + if (!object) return; LLPickInfo pick = LLToolPie::getInstance()->getPick(); @@ -2534,19 +2532,20 @@ class LLObjectTouch : public view_listener_t msg->addVector3("Normal", pick.mNormal); msg->addVector3("Binormal", pick.mBinormal); msg->sendMessage(object->getRegion()->getHost()); +} - return true; - } -}; - +bool enable_object_touch() +{ + LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + return obj && obj->flagHandleTouch(); +} // One object must have touch sensor class LLObjectEnableTouch : public view_listener_t { bool handleEvent(const LLSD& userdata) { - LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - bool new_value = obj && obj->flagHandleTouch(); + bool new_value = enable_object_touch(); // Update label based on the node touch name if available. std::string touch_text; @@ -2596,23 +2595,18 @@ class LLObjectOpen : public view_listener_t } }; */ -class LLObjectEnableOpen : public view_listener_t +bool enable_object_open() { - bool handleEvent(const LLSD& userdata) - { - // Look for contents in root object, which is all the LLFloaterOpenObject - // understands. - LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - bool new_value = (obj != NULL); - if (new_value) - { - LLViewerObject* root = obj->getRootEdit(); - if (!root) new_value = false; - else new_value = root->allowOpen(); - } - return new_value; - } -}; + // Look for contents in root object, which is all the LLFloaterOpenObject + // understands. + LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + if (!obj) return false; + + LLViewerObject* root = obj->getRootEdit(); + if (!root) return false; + + return root->allowOpen(); +} class LLViewJoystickFlycam : public view_listener_t @@ -2666,52 +2660,50 @@ class LLObjectBuild : public view_listener_t } }; -class LLObjectEdit : public view_listener_t + +void handle_object_edit() { - bool handleEvent(const LLSD& userdata) + LLViewerParcelMgr::getInstance()->deselectLand(); + + if (gAgent.getFocusOnAvatar() && !LLToolMgr::getInstance()->inEdit()) { - LLViewerParcelMgr::getInstance()->deselectLand(); + LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); - if (gAgent.getFocusOnAvatar() && !LLToolMgr::getInstance()->inEdit()) + if (selection->getSelectType() == SELECT_TYPE_HUD || !gSavedSettings.getBOOL("EditCameraMovement")) { - LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); - - if (selection->getSelectType() == SELECT_TYPE_HUD || !gSavedSettings.getBOOL("EditCameraMovement")) - { - // always freeze camera in space, even if camera doesn't move - // so, for example, follow cam scripts can't affect you when in build mode - gAgent.setFocusGlobal(gAgent.calcFocusPositionTargetGlobal(), LLUUID::null); - gAgent.setFocusOnAvatar(FALSE, ANIMATE); - } - else + // always freeze camera in space, even if camera doesn't move + // so, for example, follow cam scripts can't affect you when in build mode + gAgent.setFocusGlobal(gAgent.calcFocusPositionTargetGlobal(), LLUUID::null); + gAgent.setFocusOnAvatar(FALSE, ANIMATE); + } + else + { + gAgent.setFocusOnAvatar(FALSE, ANIMATE); + LLViewerObject* selected_objectp = selection->getFirstRootObject(); + if (selected_objectp) { - gAgent.setFocusOnAvatar(FALSE, ANIMATE); - LLViewerObject* selected_objectp = selection->getFirstRootObject(); - if (selected_objectp) - { - // zoom in on object center instead of where we clicked, as we need to see the manipulator handles - gAgent.setFocusGlobal(selected_objectp->getPositionGlobal(), selected_objectp->getID()); - gAgent.cameraZoomIn(0.666f); - gAgent.cameraOrbitOver( 30.f * DEG_TO_RAD ); - gViewerWindow->moveCursorToCenter(); + // zoom in on object center instead of where we clicked, as we need to see the manipulator handles + gAgent.setFocusGlobal(selected_objectp->getPositionGlobal(), selected_objectp->getID()); + gAgent.cameraZoomIn(0.666f); + gAgent.cameraOrbitOver( 30.f * DEG_TO_RAD ); + gViewerWindow->moveCursorToCenter(); } } - } - - LLFloaterReg::showInstance("build"); - - LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset); - gFloaterTools->setEditTool( LLToolCompTranslate::getInstance() ); - - LLViewerJoystick::getInstance()->moveObjects(true); - LLViewerJoystick::getInstance()->setNeedsReset(true); - - // Could be first use - LLFirstUse::useBuild(); - return true; } -}; - + + LLFloaterReg::showInstance("build"); + + LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset); + gFloaterTools->setEditTool( LLToolCompTranslate::getInstance() ); + + LLViewerJoystick::getInstance()->moveObjects(true); + LLViewerJoystick::getInstance()->setNeedsReset(true); + + // Could be first use + LLFirstUse::useBuild(); + return; + +} //--------------------------------------------------------------------------- // Land pie menu //--------------------------------------------------------------------------- @@ -2795,24 +2787,21 @@ BOOL enable_object_build(void*) return can_build; } -class LLEnableEdit : public view_listener_t +bool enable_object_edit() { - bool handleEvent(const LLSD& userdata) + // *HACK: The new "prelude" Help Islands have a build sandbox area, + // so users need the Edit and Create pie menu options when they are + // there. Eventually this needs to be replaced with code that only + // lets you edit objects if you have permission to do so (edit perms, + // group edit, god). See also lltoolbar.cpp. JC + bool enable = true; + if (gAgent.inPrelude()) { - // *HACK: The new "prelude" Help Islands have a build sandbox area, - // so users need the Edit and Create pie menu options when they are - // there. Eventually this needs to be replaced with code that only - // lets you edit objects if you have permission to do so (edit perms, - // group edit, god). See also lltoolbar.cpp. JC - bool enable = true; - if (gAgent.inPrelude()) - { - enable = LLViewerParcelMgr::getInstance()->agentCanBuild() - || LLSelectMgr::getInstance()->getSelection()->isAttachment(); - } - return enable; + enable = LLViewerParcelMgr::getInstance()->agentCanBuild() + || LLSelectMgr::getInstance()->getSelection()->isAttachment(); } -}; + return enable; +} class LLSelfRemoveAllAttachments : public view_listener_t { @@ -2866,27 +2855,24 @@ BOOL enable_has_attachments(void*) // } //} -class LLObjectEnableMute : public view_listener_t +bool enable_object_mute() { - bool handleEvent(const LLSD& userdata) + LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + bool new_value = (object != NULL); + if (new_value) { - LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - bool new_value = (object != NULL); - if (new_value) + LLVOAvatar* avatar = find_avatar_from_object(object); + if (avatar) { - LLVOAvatar* avatar = find_avatar_from_object(object); - if (avatar) - { - // It's an avatar - LLNameValue *lastname = avatar->getNVPair("LastName"); - BOOL is_linden = lastname && !LLStringUtil::compareStrings(lastname->getString(), "Linden"); - BOOL is_self = avatar->isSelf(); - new_value = !is_linden && !is_self; - } + // It's an avatar + LLNameValue *lastname = avatar->getNVPair("LastName"); + BOOL is_linden = lastname && !LLStringUtil::compareStrings(lastname->getString(), "Linden"); + BOOL is_self = avatar->isSelf(); + new_value = !is_linden && !is_self; } - return new_value; } -}; + return new_value; +} class LLObjectMute : public view_listener_t { @@ -3345,36 +3331,29 @@ void append_aggregate(std::string& string, const LLAggregatePermissions& ag_perm string.append(buffer); } -BOOL enable_buy(void*) +bool enable_buy_object() { // In order to buy, there must only be 1 purchaseable object in // the selection manger. - if(LLSelectMgr::getInstance()->getSelection()->getRootObjectCount() != 1) return FALSE; + if(LLSelectMgr::getInstance()->getSelection()->getRootObjectCount() != 1) return false; LLViewerObject* obj = NULL; LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode(); if(node) { obj = node->getObject(); - if(!obj) return FALSE; + if(!obj) return false; - if(node->mSaleInfo.isForSale() && node->mPermissions->getMaskOwner() & PERM_TRANSFER && - (node->mPermissions->getMaskOwner() & PERM_COPY || node->mSaleInfo.getSaleType() != LLSaleInfo::FS_COPY)) + if( for_sale_selection(node) ) { - if(obj->permAnyOwner()) return TRUE; + // *NOTE: Is this needed? This checks to see if anyone owns the + // object, dating back to when we had "public" objects owned by + // no one. JC + if(obj->permAnyOwner()) return true; } } - return FALSE; + return false; } -class LLObjectEnableBuy : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = enable_buy(NULL); - return new_value; - } -}; - // Note: This will only work if the selected object's data has been // received by the viewer and cached in the selection manager. void handle_buy_object(LLSaleInfo sale_info) @@ -3717,20 +3696,35 @@ class LLEditEnableCustomizeAvatar : public view_listener_t } }; +bool enable_sit_object() +{ + LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + + if (object && object->getPCode() == LL_PCODE_VOLUME) + { + return true; + } + else + { + return false; + } +} + + // only works on pie menu -bool handle_sit_or_stand() +void handle_object_sit_or_stand() { LLPickInfo pick = LLToolPie::getInstance()->getPick(); LLViewerObject *object = pick.getObject();; if (!object || pick.mPickType == LLPickInfo::PICK_FLORA) { - return true; + return; } if (sitting_on_selection()) { gAgent.standUp(); - return true; + return; } // get object selection offset @@ -3748,17 +3742,8 @@ bool handle_sit_or_stand() object->getRegion()->sendReliableMessage(); } - return true; } -class LLObjectSitOrStand : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - return handle_sit_or_stand(); - } -}; - void near_sit_down_point(BOOL success, void *) { if (success) @@ -4228,18 +4213,14 @@ void derez_objects(EDeRezDestination dest, const LLUUID& dest_id) } } -class LLToolsTakeCopy : public view_listener_t +void handle_take_copy() { - bool handleEvent(const LLSD& userdata) - { - if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) return true; - - const LLUUID& category_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_OBJECT); - derez_objects(DRD_ACQUIRE_TO_AGENT_INVENTORY, category_id); + if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) return; - return true; - } -}; + LLUUID category_id = + gInventory.findCategoryUUIDForType(LLAssetType::AT_OBJECT); + derez_objects(DRD_ACQUIRE_TO_AGENT_INVENTORY, category_id); +} // You can return an object to its owner if it is on your land. @@ -4485,43 +4466,50 @@ BOOL enable_take() return FALSE; } -class LLToolsBuyOrTake : public view_listener_t + +void handle_buy_or_take() { - bool handleEvent(const LLSD& userdata) + if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) { - if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) - { - return true; - } + return; + } - if (is_selection_buy_not_take()) - { - S32 total_price = selection_price(); + if (is_selection_buy_not_take()) + { + S32 total_price = selection_price(); - if (total_price <= gStatusBar->getBalance() || total_price == 0) - { - handle_buy(NULL); - } - else - { - LLFloaterBuyCurrency::buyCurrency( - "Buying this costs", total_price); - } + if (total_price <= gStatusBar->getBalance() || total_price == 0) + { + handle_buy(); } else { - handle_take(); + LLFloaterBuyCurrency::buyCurrency( + "Buying this costs", total_price); } - return true; } -}; + else + { + handle_take(); + } +} + +bool visible_buy_object() +{ + return is_selection_buy_not_take() && enable_buy_object(); +} + +bool visible_take_object() +{ + return !is_selection_buy_not_take() && enable_take(); +} class LLToolsEnableBuyOrTake : public view_listener_t { bool handleEvent(const LLSD& userdata) { bool is_buy = is_selection_buy_not_take(); - bool new_value = is_buy ? enable_buy(NULL) : enable_take(); + bool new_value = is_buy ? enable_buy_object() : enable_take(); // Update label std::string label; @@ -4632,7 +4620,7 @@ void show_buy_currency(const char* extra) LLNotifications::instance().add("PromptGoToCurrencyPage", args);//, LLSD(), callback_show_buy_currency); } -void handle_buy(void*) +void handle_buy() { if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) return; @@ -4650,14 +4638,20 @@ void handle_buy(void*) } } -class LLObjectBuy : public view_listener_t +bool anyone_copy_selection(LLSelectNode* nodep) { - bool handleEvent(const LLSD& userdata) - { - handle_buy(NULL); - return true; - } -}; + bool perm_copy = (bool)(nodep->getObject()->permCopy()); + bool all_copy = (bool)(nodep->mPermissions->getMaskEveryone() & PERM_COPY); + return perm_copy && all_copy; +} + +bool for_sale_selection(LLSelectNode* nodep) +{ + return nodep->mSaleInfo.isForSale() + && nodep->mPermissions->getMaskOwner() & PERM_TRANSFER + && (nodep->mPermissions->getMaskOwner() & PERM_COPY + || nodep->mSaleInfo.getSaleType() != LLSaleInfo::FS_COPY); +} BOOL sitting_on_selection() { @@ -4985,28 +4979,24 @@ class LLEditDelete : public view_listener_t } }; -class LLObjectEnableDelete : public view_listener_t +bool enable_object_delete() { - bool handleEvent(const LLSD& userdata) - { - bool new_value = + bool new_value = #ifdef HACKED_GODLIKE_VIEWER - TRUE; + TRUE; #else # ifdef TOGGLE_HACKED_GODLIKE_VIEWER - (!LLViewerLogin::getInstance()->isInProductionGrid() - && gAgent.isGodlike()) || + (!LLViewerLogin::getInstance()->isInProductionGrid() + && gAgent.isGodlike()) || # endif - LLSelectMgr::getInstance()->canDoDelete(); + LLSelectMgr::getInstance()->canDoDelete(); #endif - return new_value; - } -}; + return new_value; +} -class LLObjectDelete : public view_listener_t +void handle_object_delete() { - bool handleEvent(const LLSD& userdata) - { + if (LLSelectMgr::getInstance()) { LLSelectMgr::getInstance()->doDelete(); @@ -5018,9 +5008,8 @@ class LLObjectDelete : public view_listener_t // When deleting an object we may not actually be done // Keep selection so we know what to delete when confirmation is needed about the delete gPieObject->hide(); - return true; - } -}; + return; +} void handle_force_delete(void*) { @@ -5377,29 +5366,6 @@ class LLToolsLookAtSelection : public view_listener_t } }; -void callback_invite_to_group(LLUUID group_id, LLUUID dest_id) -{ - std::vector<LLUUID> agent_ids; - agent_ids.push_back(dest_id); - - LLFloaterGroupInvite::showForGroup(group_id, &agent_ids); -} - -void invite_to_group(const LLUUID& dest_id) -{ - LLViewerObject* dest = gObjectList.findObject(dest_id); - if(dest && dest->isAvatar()) - { - LLFloaterGroupPicker* widget = LLFloaterReg::showTypedInstance<LLFloaterGroupPicker>("group_picker", LLSD(gAgent.getID())); - if (widget) - { - widget->center(); - widget->setPowersMask(GP_MEMBER_INVITE); - widget->setSelectGroupCallback(boost::bind(callback_invite_to_group, _1, dest_id)); - } - } -} - class LLAvatarInviteToGroup : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -5407,7 +5373,7 @@ class LLAvatarInviteToGroup : public view_listener_t LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); if(avatar) { - invite_to_group(avatar->getID()); + LLAvatarActions::inviteToGroup(avatar->getID()); } return true; } @@ -5475,7 +5441,7 @@ bool complete_give_money(const LLSD& notification, const LLSD& response, LLObjec return false; } -bool handle_give_money_dialog() +void handle_give_money_dialog() { LLNotification::Params params("BusyModePay"); params.functor.function(boost::bind(complete_give_money, _1, _2, LLSelectMgr::getInstance()->getSelection())); @@ -5489,38 +5455,28 @@ bool handle_give_money_dialog() { LLNotifications::instance().forceResponse(params, 1); } - return true; } -class LLPayObject : public view_listener_t +bool enable_pay_avatar() { - bool handleEvent(const LLSD& userdata) - { - return handle_give_money_dialog(); - } -}; + LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + LLVOAvatar* avatar = find_avatar_from_object(obj); + return (avatar != NULL); +} -class LLEnablePayObject : public view_listener_t +bool enable_pay_object() { - bool handleEvent(const LLSD& userdata) + LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + if( object ) { - LLVOAvatar* avatar = find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()); - bool new_value = (avatar != NULL); - if (!new_value) + LLViewerObject *parent = (LLViewerObject *)object->getParent(); + if((object->flagTakesMoney()) || (parent && parent->flagTakesMoney())) { - LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - if( object ) - { - LLViewerObject *parent = (LLViewerObject *)object->getParent(); - if((object->flagTakesMoney()) || (parent && parent->flagTakesMoney())) - { - new_value = true; - } - } + return true; } - return new_value; } -}; + return false; +} class LLObjectEnableSitOrStand : public view_listener_t { @@ -6157,7 +6113,7 @@ class LLAttachmentEnableDetach : public view_listener_t }; // Used to tell if the selected object can be attached to your avatar. -BOOL object_selected_and_point_valid(const LLSD&) +BOOL object_selected_and_point_valid() { LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); for (LLObjectSelection::root_iterator iter = selection->root_begin(); @@ -6187,7 +6143,7 @@ BOOL object_selected_and_point_valid(const LLSD&) BOOL object_is_wearable() { - if (!object_selected_and_point_valid(LLSD())) + if (!object_selected_and_point_valid()) { return FALSE; } @@ -6209,15 +6165,6 @@ BOOL object_is_wearable() } -// Also for seeing if object can be attached. See above. -class LLObjectEnableWear : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - return object_selected_and_point_valid(LLSD()); - } -}; - class LLAttachmentPointFilled : public view_listener_t { bool handleEvent(const LLSD& user_data) @@ -6564,40 +6511,37 @@ class LLEditableSelectedMono : public view_listener_t } }; -class LLToolsEnableTakeCopy : public view_listener_t +bool enable_object_take_copy() { - bool handleEvent(const LLSD& userdata) + bool all_valid = false; + if (LLSelectMgr::getInstance()) { - bool all_valid = false; - if (LLSelectMgr::getInstance()) + if (!LLSelectMgr::getInstance()->getSelection()->isEmpty()) { - if (!LLSelectMgr::getInstance()->getSelection()->isEmpty()) - { - all_valid = true; + all_valid = true; #ifndef HACKED_GODLIKE_VIEWER # ifdef TOGGLE_HACKED_GODLIKE_VIEWER - if (LLViewerLogin::getInstance()->isInProductionGrid() - || !gAgent.isGodlike()) + if (LLViewerLogin::getInstance()->isInProductionGrid() + || !gAgent.isGodlike()) # endif + { + struct f : public LLSelectedObjectFunctor { - struct f : public LLSelectedObjectFunctor + virtual bool apply(LLViewerObject* obj) { - virtual bool apply(LLViewerObject* obj) - { - return (!obj->permCopy() || obj->isAttachment()); - } - } func; - const bool firstonly = true; - bool any_invalid = LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func, firstonly); - all_valid = !any_invalid; - } -#endif // HACKED_GODLIKE_VIEWER + return (!obj->permCopy() || obj->isAttachment()); + } + } func; + const bool firstonly = true; + bool any_invalid = LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func, firstonly); + all_valid = !any_invalid; } +#endif // HACKED_GODLIKE_VIEWER } - - return all_valid; } -}; + + return all_valid; +} class LLHasAsset : public LLInventoryCollectFunctor @@ -7701,13 +7645,14 @@ void initialize_menus() LLUICtrl::EnableCallbackRegistry::Registrar& enable = LLUICtrl::EnableCallbackRegistry::currentRegistrar(); LLUICtrl::CommitCallbackRegistry::Registrar& commit = LLUICtrl::CommitCallbackRegistry::currentRegistrar(); + LLUICtrl::VisibleCallbackRegistry::Registrar& visible = LLUICtrl::VisibleCallbackRegistry::currentRegistrar(); // Enable God Mode view_listener_t::addMenu(new LLEnableGodCustomerService(), "EnableGodCustomerService"); // Agent commit.add("Agent.toggleFlying", boost::bind(&LLAgent::toggleFlying)); - enable.add("Agent.emableFlying", boost::bind(&LLAgent::enableFlying)); + enable.add("Agent.enableFlying", boost::bind(&LLAgent::enableFlying)); // File menu init_menu_file(); @@ -7801,8 +7746,8 @@ void initialize_menus() view_listener_t::addMenu(new LLToolsReleaseKeys(), "Tools.ReleaseKeys"); view_listener_t::addMenu(new LLToolsEnableReleaseKeys(), "Tools.EnableReleaseKeys"); view_listener_t::addMenu(new LLToolsLookAtSelection(), "Tools.LookAtSelection"); - view_listener_t::addMenu(new LLToolsBuyOrTake(), "Tools.BuyOrTake"); - view_listener_t::addMenu(new LLToolsTakeCopy(), "Tools.TakeCopy"); + commit.add("Tools.BuyOrTake", boost::bind(&handle_buy_or_take)); + commit.add("Tools.TakeCopy", boost::bind(&handle_take_copy)); view_listener_t::addMenu(new LLToolsSaveToInventory(), "Tools.SaveToInventory"); view_listener_t::addMenu(new LLToolsSaveToObjectInventory(), "Tools.SaveToObjectInventory"); view_listener_t::addMenu(new LLToolsSelectedScriptAction(), "Tools.SelectedScriptAction"); @@ -7811,7 +7756,8 @@ void initialize_menus() view_listener_t::addMenu(new LLToolsEnableLink(), "Tools.EnableLink"); view_listener_t::addMenu(new LLToolsEnableUnlink(), "Tools.EnableUnlink"); view_listener_t::addMenu(new LLToolsEnableBuyOrTake(), "Tools.EnableBuyOrTake"); - view_listener_t::addMenu(new LLToolsEnableTakeCopy(), "Tools.EnableTakeCopy"); + visible.add("Tools.VisibleTakeCopy", boost::bind(&enable_object_take_copy)); + enable.add("Tools.EnableTakeCopy", boost::bind(&enable_object_take_copy)); view_listener_t::addMenu(new LLToolsEnableSaveToInventory(), "Tools.EnableSaveToInventory"); view_listener_t::addMenu(new LLToolsEnableSaveToObjectInventory(), "Tools.EnableSaveToObjectInventory"); @@ -8012,31 +7958,51 @@ void initialize_menus() view_listener_t::addMenu(new LLAvatarSendIM(), "Avatar.SendIM"); view_listener_t::addMenu(new LLAvatarReportAbuse(), "Avatar.ReportAbuse"); - view_listener_t::addMenu(new LLObjectEnableMute(), "Avatar.EnableMute"); view_listener_t::addMenu(new LLAvatarEnableAddFriend(), "Avatar.EnableAddFriend"); view_listener_t::addMenu(new LLAvatarEnableFreezeEject(), "Avatar.EnableFreezeEject"); // Object pie menu view_listener_t::addMenu(new LLObjectBuild(), "Object.Build"); - view_listener_t::addMenu(new LLObjectTouch(), "Object.Touch"); - view_listener_t::addMenu(new LLObjectSitOrStand(), "Object.SitOrStand"); - view_listener_t::addMenu(new LLObjectDelete(), "Object.Delete"); + commit.add("Object.Touch", boost::bind(&handle_object_touch)); + commit.add("Object.SitOrStand", boost::bind(&handle_object_sit_or_stand)); + visible.add("Object.EnableSit", boost::bind(&enable_sit_object)); + commit.add("Object.Delete", boost::bind(&handle_object_delete)); view_listener_t::addMenu(new LLObjectAttachToAvatar(), "Object.AttachToAvatar"); view_listener_t::addMenu(new LLObjectReturn(), "Object.Return"); view_listener_t::addMenu(new LLObjectReportAbuse(), "Object.ReportAbuse"); view_listener_t::addMenu(new LLObjectMute(), "Object.Mute"); - view_listener_t::addMenu(new LLObjectBuy(), "Object.Buy"); - view_listener_t::addMenu(new LLObjectEdit(), "Object.Edit"); - view_listener_t::addMenu(new LLObjectEnableOpen(), "Object.EnableOpen"); + visible.add("Object.VisibleTake", boost::bind(&visible_take_object)); + visible.add("Object.VisibleBuy", boost::bind(&visible_buy_object)); + + commit.add("Object.Buy", boost::bind(&handle_buy)); + commit.add("Object.Edit", boost::bind(&handle_object_edit)); + + commit.add("Object.Take", boost::bind(&handle_take)); + + enable.add("Object.EnableOpen", boost::bind(&enable_object_open)); + visible.add("Object.VisibleOpen", boost::bind(&enable_object_open)); + + enable.add("Object.EnableTouch", boost::bind(&enable_object_touch)); + visible.add("Object.VisibleTouch", boost::bind(&enable_object_touch)); + view_listener_t::addMenu(new LLObjectEnableTouch(), "Object.EnableTouch"); view_listener_t::addMenu(new LLObjectEnableSitOrStand(), "Object.EnableSitOrStand"); - view_listener_t::addMenu(new LLObjectEnableDelete(), "Object.EnableDelete"); - view_listener_t::addMenu(new LLObjectEnableWear(), "Object.EnableWear"); + + enable.add("Object.EnableDelete", boost::bind(&enable_object_delete)); + visible.add("Object.VisibleDelete", boost::bind(&enable_object_delete)); + + enable.add("Object.EnableWear", boost::bind(&object_selected_and_point_valid)); + visible.add("Object.VisibleWear", boost::bind(&object_selected_and_point_valid)); + view_listener_t::addMenu(new LLObjectEnableReturn(), "Object.EnableReturn"); view_listener_t::addMenu(new LLObjectEnableReportAbuse(), "Object.EnableReportAbuse"); - view_listener_t::addMenu(new LLObjectEnableMute(), "Object.EnableMute"); - view_listener_t::addMenu(new LLObjectEnableBuy(), "Object.EnableBuy"); + + enable.add("Avatar.EnableMute", boost::bind(&enable_object_mute)); + enable.add("Object.EnableMute", boost::bind(&enable_object_mute)); + visible.add("Object.VisibleMute", boost::bind(&enable_object_mute)); + + enable.add("Object.EnableBuy", boost::bind(&enable_buy_object)); /*view_listener_t::addMenu(new LLObjectVisibleTouch(), "Object.VisibleTouch"); view_listener_t::addMenu(new LLObjectVisibleCustomTouch(), "Object.VisibleCustomTouch"); @@ -8068,10 +8034,13 @@ void initialize_menus() view_listener_t::addMenu(new LLToggleControl(), "ToggleControl"); view_listener_t::addMenu(new LLCheckControl(), "CheckControl"); view_listener_t::addMenu(new LLGoToObject(), "GoToObject"); - view_listener_t::addMenu(new LLPayObject(), "PayObject"); + commit.add("PayObject", boost::bind(&handle_give_money_dialog)); - view_listener_t::addMenu(new LLEnablePayObject(), "EnablePayObject"); - view_listener_t::addMenu(new LLEnableEdit(), "EnableEdit"); + enable.add("EnablePayObject", boost::bind(&enable_pay_object)); + visible.add("VisiblePayObject", boost::bind(&enable_pay_object)); + enable.add("EnablePayAvatar", boost::bind(&enable_pay_avatar)); + enable.add("EnableEdit", boost::bind(&enable_object_edit)); + visible.add("Object.VisibleEdit", boost::bind(&enable_object_edit)); view_listener_t::addMenu(new LLFloaterVisible(), "FloaterVisible"); view_listener_t::addMenu(new LLSomethingSelected(), "SomethingSelected"); diff --git a/indra/newview/llviewermenu.h b/indra/newview/llviewermenu.h index dd6aac2dd3..75f4f08bde 100644 --- a/indra/newview/llviewermenu.h +++ b/indra/newview/llviewermenu.h @@ -36,14 +36,12 @@ #include "llmenugl.h" #include "llsafehandle.h" -//newview includes -#include "llfilepicker.h" - +class LLSD; class LLUICtrl; class LLView; class LLParcelSelection; class LLObjectSelection; - +class LLSelectNode; void init_menus(); void cleanup_menus(); @@ -83,7 +81,6 @@ void confirm_replace_attachment(S32 option, void* user_data); void handle_detach_from_avatar(const LLSD& user_data); void attach_label(std::string& label, const LLSD&); void detach_label(std::string& label, const LLSD&); -BOOL object_selected_and_point_valid(const LLSD&); void handle_detach(void*); BOOL enable_god_full(void* user_data); BOOL enable_god_liaison(void* user_data); @@ -96,21 +93,34 @@ void exchange_callingcard(const LLUUID& dest_id); void handle_gestures(void*); void handle_sit_down(void*); void handle_object_build(void*); +void handle_object_touch(); +bool enable_object_open(); + +// Buy either contents or object itself +void handle_buy(); + +void handle_take_copy(); + +// Can anyone take a free copy of the object? +// *TODO: Move to separate file +bool anyone_copy_selection(LLSelectNode* nodep); + +// Is this selected object for sale? +// *TODO: Move to separate file +bool for_sale_selection(LLSelectNode* nodep); + void handle_save_snapshot(void *); void handle_toggle_flycam(); -bool handle_sit_or_stand(); -bool handle_give_money_dialog(); +void handle_object_sit_or_stand(); +void handle_give_money_dialog(); +bool enable_pay_object(); +bool enable_buy_object(); bool handle_go_to(); // Export to XML or Collada void handle_export_selected( void * ); -// Pass in an empty string and this function will build a string that -// describes buyer permissions. -class LLSaleInfo; -class LLPermissions; - class LLViewerMenuHolderGL : public LLMenuHolderGL { public: diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 390e1fe032..2e8580907e 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -103,6 +103,7 @@ #include "llsidetray.h" #include "llstartup.h" #include "llsky.h" +#include "llslurl.h" #include "llstatenums.h" #include "llstatusbar.h" #include "llimview.h" @@ -168,9 +169,6 @@ extern BOOL gDebugClicks; // function prototypes void open_offer(const std::vector<LLUUID>& items, const std::string& from_name); bool check_offer_throttle(const std::string& from_name, bool check_only); -void callbackCacheEstateOwnerName(const LLUUID& id, - const std::string& first, const std::string& last, - BOOL is_group); //inventory offer throttle globals LLFrameTimer gThrottleTimer; @@ -5576,10 +5574,17 @@ void process_covenant_reply(LLMessageSystem* msg, void**) LLPanelLandCovenant::updateEstateName(estate_name); LLFloaterBuyLand::updateEstateName(estate_name); + std::string owner_name = + LLSLURL::buildCommand("agent", estate_owner_id, "inspect"); + LLPanelEstateCovenant::updateEstateOwnerName(owner_name); + LLPanelLandCovenant::updateEstateOwnerName(owner_name); + LLFloaterBuyLand::updateEstateOwnerName(owner_name); + LLPanelPlaceInfo* panel = LLSideTray::getInstance()->findChild<LLPanelPlaceInfo>("panel_place_info"); if (panel) { panel->updateEstateName(estate_name); + panel->updateEstateOwnerName(owner_name); } // standard message, not from system @@ -5607,8 +5612,6 @@ void process_covenant_reply(LLMessageSystem* msg, void**) LLPanelLandCovenant::updateLastModified(last_modified); LLFloaterBuyLand::updateLastModified(last_modified); - gCacheName->get(estate_owner_id, false, &callbackCacheEstateOwnerName); - // load the actual covenant asset data const BOOL high_priority = TRUE; if (covenant_id.notNull()) @@ -5638,32 +5641,10 @@ void process_covenant_reply(LLMessageSystem* msg, void**) LLPanelEstateCovenant::updateCovenantText(covenant_text, covenant_id); LLPanelLandCovenant::updateCovenantText(covenant_text); LLFloaterBuyLand::updateCovenantText(covenant_text, covenant_id); - panel->updateCovenantText(covenant_text); - } -} - -void callbackCacheEstateOwnerName(const LLUUID& id, - const std::string& first, const std::string& last, - BOOL is_group) -{ - std::string name; - - if (id.isNull()) - { - name = LLTrans::getString("none_text"); - } - else - { - name = first + " " + last; - } - LLPanelEstateCovenant::updateEstateOwnerName(name); - LLPanelLandCovenant::updateEstateOwnerName(name); - LLFloaterBuyLand::updateEstateOwnerName(name); - - LLPanelPlaceInfo* panel = LLSideTray::getInstance()->findChild<LLPanelPlaceInfo>("panel_place_info"); - if (panel) - { - panel->updateEstateOwnerName(name); + if (panel) + { + panel->updateCovenantText(covenant_text); + } } } diff --git a/indra/newview/llviewerparcelmgr.h b/indra/newview/llviewerparcelmgr.h index 275c79fd3b..3964a56bf6 100644 --- a/indra/newview/llviewerparcelmgr.h +++ b/indra/newview/llviewerparcelmgr.h @@ -71,6 +71,9 @@ const F32 PARCEL_POST_HEIGHT = 0.666f; // Base class for people who want to "observe" changes in the viewer // parcel selection. + +//FIXME: this should be done by grabbing a floating parcel selection and observing changes on it, not the parcel mgr +//--RN class LLParcelObserver { public: diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp index ceb2698223..b853bcb46e 100644 --- a/indra/newview/llviewertexteditor.cpp +++ b/indra/newview/llviewertexteditor.cpp @@ -32,37 +32,37 @@ #include "llviewerprecompiledheaders.h" +#include "llviewertexteditor.h" + +#include "llagent.h" +#include "llaudioengine.h" +#include "llavataractions.h" +#include "llfloaterchat.h" #include "llfloaterreg.h" +#include "llfloaterworldmap.h" #include "llfocusmgr.h" -#include "llaudioengine.h" -#include "llagent.h" #include "llinventory.h" #include "llinventorybridge.h" #include "llinventorymodel.h" - -#include "llviewertexteditor.h" - -#include "llfloaterchat.h" -#include "llfloaterworldmap.h" +#include "llmemorystream.h" +#include "llmenugl.h" +#include "llnotecard.h" #include "llnotify.h" #include "llpanelplaces.h" #include "llpreview.h" -#include "llpreviewtexture.h" #include "llpreviewnotecard.h" +#include "llpreviewtexture.h" #include "llscrollbar.h" +#include "llscrollcontainer.h" #include "llsidetray.h" #include "lltooldraganddrop.h" +#include "lltooltip.h" #include "lltrans.h" +#include "lluictrlfactory.h" #include "llviewercontrol.h" +#include "llviewerinventory.h" #include "llviewertexturelist.h" #include "llviewerwindow.h" -#include "llviewerinventory.h" -#include "lluictrlfactory.h" -#include "llnotecard.h" -#include "llmemorystream.h" -#include "llmenugl.h" -#include "llscrollcontainer.h" -#include "llavataractions.h" #include "llappviewer.h" // for gPacificDaylightTime @@ -142,8 +142,6 @@ public: } } - //virtual S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const; - //virtual void updateLayout(const class LLTextEditor& editor); /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const { @@ -167,7 +165,7 @@ public: } F32 right_x; - mStyle->getFont()->render(mLabel, 0, image_rect.mRight + EMBEDDED_ITEM_LABEL_PADDING, draw_rect.mBottom, color, LLFontGL::LEFT, LLFontGL::BOTTOM, mHasMouseHover ? LLFontGL::UNDERLINE : 0, LLFontGL::NO_SHADOW, mLabel.length(), S32_MAX, &right_x); + mStyle->getFont()->render(mLabel, 0, image_rect.mRight + EMBEDDED_ITEM_LABEL_PADDING, draw_rect.mBottom, color, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::UNDERLINE, LLFontGL::NO_SHADOW, mLabel.length(), S32_MAX, &right_x); return right_x; } @@ -176,20 +174,21 @@ public: return llmax(mImage->getHeight(), llceil(mStyle->getFont()->getLineHeight())); } /*virtual*/ bool canEdit() const { return false; } - //virtual void unlinkFromDocument(class LLTextEditor* editor); - //virtual void linkToDocument(class LLTextEditor* editor); - virtual void setHasMouseHover(bool hover) + + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask) { - mHasMouseHover = hover; + LLUI::getWindow()->setCursor(UI_CURSOR_HAND); + return TRUE; } - //virtual const LLColor4& getColor() const; - //virtual void setColor(const LLColor4 &color); - //virtual void setStyle(const LLStyleSP &style); - virtual BOOL getToolTip( std::string& msg ) const + virtual BOOL handleToolTip(S32 x, S32 y, MASK mask ) { - msg = mToolTip; - return TRUE; + if (!mToolTip.empty()) + { + LLToolTipMgr::instance().show(mToolTip); + return TRUE; + } + return FALSE; } /*virtual*/ const LLStyleSP getStyle() const { return mStyle; } @@ -562,17 +561,17 @@ void LLEmbeddedItems::markSaved() /////////////////////////////////////////////////////////////////// -class LLViewerTextEditor::LLTextCmdInsertEmbeddedItem : public LLTextEditor::LLTextCmd +class LLViewerTextEditor::TextCmdInsertEmbeddedItem : public LLTextBase::TextCmd { public: - LLTextCmdInsertEmbeddedItem( S32 pos, LLInventoryItem* item ) - : LLTextCmd(pos, FALSE), + TextCmdInsertEmbeddedItem( S32 pos, LLInventoryItem* item ) + : TextCmd(pos, FALSE), mExtCharValue(0) { mItem = item; } - virtual BOOL execute( LLTextEditor* editor, S32* delta ) + virtual BOOL execute( LLTextBase* editor, S32* delta ) { LLViewerTextEditor* viewer_editor = (LLViewerTextEditor*)editor; // Take this opportunity to remove any unused embedded items from this editor @@ -587,13 +586,13 @@ public: return FALSE; } - virtual S32 undo( LLTextEditor* editor ) + virtual S32 undo( LLTextBase* editor ) { remove(editor, getPosition(), 1); return getPosition(); } - virtual S32 redo( LLTextEditor* editor ) + virtual S32 redo( LLTextBase* editor ) { LLWString ws; ws += mExtCharValue; @@ -635,7 +634,6 @@ LLViewerTextEditor::LLViewerTextEditor(const LLViewerTextEditor::Params& p) mDragItemSaved(FALSE), mInventoryCallback(new LLEmbeddedNotecardOpener) { - mParseHTML = p.allow_html; mEmbeddedItemList = new LLEmbeddedItems(this); mInventoryCallback->setEditor(this); } @@ -673,7 +671,7 @@ BOOL LLViewerTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) llwchar wc = 0; if (mCursorPos < getLength()) { - wc = getWChar(mCursorPos); + wc = getWText()[mCursorPos]; } LLInventoryItem* item_at_pos = LLEmbeddedItems::getEmbeddedItem(wc); if (item_at_pos) @@ -773,22 +771,6 @@ BOOL LLViewerTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) return handled; } -BOOL LLViewerTextEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - // pop up a context menu for any Url under the cursor - if (handleRightMouseDownOverUrl(this, x, y)) - { - return TRUE; - } - - if (childrenHandleRightMouseDown(x, y, mask) != NULL) - { - return TRUE; - } - - return FALSE; -} - BOOL LLViewerTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; @@ -955,7 +937,7 @@ std::string LLViewerTextEditor::getEmbeddedText() LLWString outtextw; for (S32 i=0; i<(S32)getWText().size(); i++) { - llwchar wch = getWChar(i); + llwchar wch = getWText()[i]; if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR ) { S32 index = mEmbeddedItemList->getIndexFromEmbeddedChar(wch); @@ -998,7 +980,8 @@ std::string LLViewerTextEditor::appendTime(bool prepend_newline) substitution["datetime"] = (S32) utc_time; LLStringUtil::format (timeStr, substitution); - appendColoredText(timeStr, false, prepend_newline, LLColor4::grey); + appendText(timeStr, prepend_newline, LLStyle::Params().color(LLColor4::grey)); + blockUndo(); return timeStr; } @@ -1057,7 +1040,7 @@ BOOL LLViewerTextEditor::openEmbeddedItemAtPos(S32 pos) { if( pos < getLength()) { - llwchar wc = getWChar(pos); + llwchar wc = getWText()[pos]; LLInventoryItem* item = LLEmbeddedItems::getEmbeddedItem( wc ); if( item ) { @@ -1227,7 +1210,7 @@ bool LLViewerTextEditor::onCopyToInvDialog(const LLSD& notification, const LLSD& // Returns change in number of characters in mWText S32 LLViewerTextEditor::insertEmbeddedItem( S32 pos, LLInventoryItem* item ) { - return execute( new LLTextCmdInsertEmbeddedItem( pos, item ) ); + return execute( new TextCmdInsertEmbeddedItem( pos, item ) ); } bool LLViewerTextEditor::importStream(std::istream& str) diff --git a/indra/newview/llviewertexteditor.h b/indra/newview/llviewertexteditor.h index 100fa343af..ba0c40cb2e 100644 --- a/indra/newview/llviewertexteditor.h +++ b/indra/newview/llviewertexteditor.h @@ -43,10 +43,7 @@ class LLViewerTextEditor : public LLTextEditor public: struct Params : public LLInitParam::Block<Params, LLTextEditor::Params> { - Optional<bool> allow_html; - Params() - : allow_html("allow_html", false) { name = "text_editor"; } @@ -64,7 +61,6 @@ public: // mousehandler overrides virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); - virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleHover(S32 x, S32 y, MASK mask); virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask ); @@ -139,7 +135,7 @@ private: // Inner classes // - class LLTextCmdInsertEmbeddedItem; + class TextCmdInsertEmbeddedItem; }; #endif // LL_VIEWERTEXTEDITOR_H diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 4a0efbaddc..24d00cba16 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -835,9 +835,13 @@ void LLViewerWindow::handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask // Save mouse point for access during idle() and display() LLCoordGL mouse_point(x, y); - saveLastMouse(mouse_point); - LLUI::resetMouseIdleTimer(); + if (mouse_point != mCurrentMousePoint) + { + LLUI::resetMouseIdleTimer(); + } + + saveLastMouse(mouse_point); mWindow->showCursorFromMouseMove(); @@ -1866,7 +1870,6 @@ void LLViewerWindow::setMenuBackgroundColor(bool god_mode, bool dev_grid) if(gStatusBar) { gStatusBar->setBackgroundColor( new_bg_color ); - gStatusBar->getChild<LLTextBox>("HealthText")->setBackgroundColor(new_bg_color); } } @@ -2022,7 +2025,7 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) } // hide tooltips on keypress - LLToolTipMgr::instance().hideToolTips(); + LLToolTipMgr::instance().blockToolTips(); // Explicit hack for debug menu. if ((MASK_ALT & mask) && @@ -2609,16 +2612,16 @@ void LLViewerWindow::updateUI() append_xui_tooltip(tooltip_view, tool_tip_msg); screen_sticky_rect.intersectWith(tooltip_view->calcScreenRect()); - LLToolTipMgr::instance().show(LLToolTipParams() + LLToolTipMgr::instance().show(LLToolTip::Params() .message(tool_tip_msg) .sticky_rect(screen_sticky_rect) - .width(400)); + .max_width(400)); } // if there is a mouse captor, nothing else gets a tooltip else if (mouse_captor) { mouse_captor->screenPointToLocal(x, y, &local_x, &local_y); - tool_tip_handled = mouse_captor->handleToolTip(local_x, local_y, tool_tip_msg, screen_sticky_rect ); + tool_tip_handled = mouse_captor->handleToolTip(local_x, local_y, mask); } else { @@ -2626,20 +2629,20 @@ void LLViewerWindow::updateUI() if (!tool_tip_handled && top_ctrl) { top_ctrl->screenPointToLocal(x, y, &local_x, &local_y); - tool_tip_handled = top_ctrl->handleToolTip(local_x, local_y, tool_tip_msg, screen_sticky_rect ); + tool_tip_handled = top_ctrl->handleToolTip(local_x, local_y, mask ); } if (!tool_tip_handled) { local_x = x; local_y = y; - tool_tip_handled = mRootView->handleToolTip(local_x, local_y, tool_tip_msg, screen_sticky_rect ); + tool_tip_handled = mRootView->handleToolTip(local_x, local_y, mask ); } LLTool* current_tool = LLToolMgr::getInstance()->getCurrentTool(); if (!tool_tip_handled && current_tool) { current_tool->screenPointToLocal(x, y, &local_x, &local_y); - tool_tip_handled = current_tool->handleToolTip(local_x, local_y, tool_tip_msg, screen_sticky_rect ); + tool_tip_handled = current_tool->handleToolTip(local_x, local_y, mask ); } } } @@ -2687,7 +2690,7 @@ void LLViewerWindow::updateLayout() && !suppress_toolbox // not override in third person && LLToolMgr::getInstance()->getCurrentToolset() != gFaceEditToolset // not special mode && LLToolMgr::getInstance()->getCurrentToolset() != gMouselookToolset - && (!captor || captor->isView()))) // not dragging + && (!captor || dynamic_cast<LLView*>(captor) != NULL))) // not dragging { // Force floater tools to be visible (unless minimized) if (!gFloaterTools->getVisible()) @@ -2787,6 +2790,14 @@ void LLViewerWindow::updateKeyboardFocus() } parent = parent->getParentUICtrl(); } + + // if we didn't find a better place to put focus, just release it + // hasFocus() will return true if and only if we didn't touch focus since we + // are only moving focus higher in the hierarchy + if (cur_focus->hasFocus()) + { + cur_focus->setFocus(FALSE); + } } else if (cur_focus->isFocusRoot()) { diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp index 79308f7e16..30736b1e03 100644 --- a/indra/newview/llworldmapview.cpp +++ b/indra/newview/llworldmapview.cpp @@ -1181,7 +1181,7 @@ LLVector3d LLWorldMapView::viewPosToGlobal( S32 x, S32 y ) } -BOOL LLWorldMapView::handleToolTip( S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen ) +BOOL LLWorldMapView::handleToolTip( S32 x, S32 y, MASK mask ) { LLVector3d pos_global = viewPosToGlobal(x, y); @@ -1236,9 +1236,10 @@ BOOL LLWorldMapView::handleToolTip( S32 x, S32 y, std::string& msg, LLRect& stic S32 screen_x, screen_y; localPointToScreen(x, y, &screen_x, &screen_y); + LLRect sticky_rect_screen; sticky_rect_screen.setCenterAndSize(screen_x, screen_y, SLOP, SLOP); - LLToolTipMgr::instance().show(LLToolTipParams() + LLToolTipMgr::instance().show(LLToolTip::Params() .message(tooltip_msg) .sticky_rect(sticky_rect_screen)); } diff --git a/indra/newview/llworldmapview.h b/indra/newview/llworldmapview.h index 8349d5399f..66793f0101 100644 --- a/indra/newview/llworldmapview.h +++ b/indra/newview/llworldmapview.h @@ -72,7 +72,7 @@ public: virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask ); virtual BOOL handleHover( S32 x, S32 y, MASK mask ); - virtual BOOL handleToolTip( S32 x, S32 y, std::string& msg, LLRect& sticky_rect_screen ); + virtual BOOL handleToolTip( S32 x, S32 y, MASK mask); bool checkItemHit(S32 x, S32 y, LLItemInfo& item, LLUUID* id, bool track); void handleClick(S32 x, S32 y, MASK mask, S32* hit_type, LLUUID* id); diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index 7e22e17188..eb62c442db 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -2,7 +2,6 @@ <colors> <!-- Named Colors --> - <color name="EmphasisColor" value="0.38 0.694 0.573 1" /> @@ -90,6 +89,9 @@ name="AlertCautionTextColor" reference="Black" /> <color + name="AgentLinkColor" + reference="White" /> + <color name="AlertTextColor" value="0.58 0.66 0.84 1" /> <color diff --git a/indra/newview/skins/default/textures/icons/Info_Small.png b/indra/newview/skins/default/textures/icons/Info_Small.png Binary files differnew file mode 100644 index 0000000000..81078c32dc --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Info_Small.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 8a6e9486a2..f106796cd9 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -137,6 +137,7 @@ <texture name="Icon_Undock_Press" file_name="windows/Icon_Undock_Press.png" preload="false" /> <texture name="Info" file_name="icons/Info.png" preload="false" /> + <texture name="Info_Small" file_name="icons/Info_Small.png" preload="false" /> <texture name="Info_Off" file_name="navbar/Info_Off.png" preload="false" /> <texture name="Info_Press" file_name="navbar/Info_Press.png" preload="false" /> diff --git a/indra/newview/skins/default/xui/en/floater_about.xml b/indra/newview/skins/default/xui/en/floater_about.xml index c9e143bf95..dc6af79db5 100644 --- a/indra/newview/skins/default/xui/en/floater_about.xml +++ b/indra/newview/skins/default/xui/en/floater_about.xml @@ -80,6 +80,7 @@ label="Support" name="support_panel"> <text_editor + allow_html="true" follows="top|left" font="SansSerif" height="350" @@ -127,7 +128,7 @@ Thank you to the following residents for helping to ensure that this is the best It is a rare mind indeed that can render the hitherto non-existent blindingly obvious. The cry 'I could have thought of that' is a very popular and misleading one, for the fact is that they didn't, and a very significant and revealing fact it is too. --- Douglas Adams + -- Douglas Adams </text_editor> </panel> <panel diff --git a/indra/newview/skins/default/xui/en/floater_about_land.xml b/indra/newview/skins/default/xui/en/floater_about_land.xml index 5a285cdcb0..524495d83d 100644 --- a/indra/newview/skins/default/xui/en/floater_about_land.xml +++ b/indra/newview/skins/default/xui/en/floater_about_land.xml @@ -132,9 +132,6 @@ Go to World menu > About Land or select another parcel to show its details. Description: </text> <text_editor - bevel_style="in" - border_style="line" - border_thickness="1" follows="left|top|right" height="52" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/floater_buy_land.xml b/indra/newview/skins/default/xui/en/floater_buy_land.xml index bd0dac5ca1..62d40bc45f 100644 --- a/indra/newview/skins/default/xui/en/floater_buy_land.xml +++ b/indra/newview/skins/default/xui/en/floater_buy_land.xml @@ -483,7 +483,8 @@ sold with objects name="error_message" right="435" top="208" - width="215"> + width="215" + word_wrap="true"> Something ain't right. </text> <button @@ -583,7 +584,8 @@ sold with objects left="72" name="land_use_reason" right="435" - width="215"> + width="215" + word_wrap="true"> You hold 1309 m² of land. This parcel is 512 m² of land. </text> diff --git a/indra/newview/skins/default/xui/en/floater_im_session.xml b/indra/newview/skins/default/xui/en/floater_im_session.xml index be1278e8cc..a233f7f0fa 100644 --- a/indra/newview/skins/default/xui/en/floater_im_session.xml +++ b/indra/newview/skins/default/xui/en/floater_im_session.xml @@ -56,6 +56,8 @@ layout="topleft" max_length="2147483647" name="im_text" + parse_highlights="true" + allow_html="true" track_bottom="true" width="195" word_wrap="true"> diff --git a/indra/newview/skins/default/xui/en/floater_incoming_call.xml b/indra/newview/skins/default/xui/en/floater_incoming_call.xml index 8b271eb41e..bdce8fa4fc 100644 --- a/indra/newview/skins/default/xui/en/floater_incoming_call.xml +++ b/indra/newview/skins/default/xui/en/floater_incoming_call.xml @@ -33,7 +33,7 @@ <text_editor font="SansSerif" height="64" - hide_border="true" + border_visible="false" hide_scrollbar="true" layout="topleft" left="77" diff --git a/indra/newview/skins/default/xui/en/floater_inspect.xml b/indra/newview/skins/default/xui/en/floater_inspect.xml index 00b9f850ad..339604e658 100644 --- a/indra/newview/skins/default/xui/en/floater_inspect.xml +++ b/indra/newview/skins/default/xui/en/floater_inspect.xml @@ -55,7 +55,7 @@ top_pad="5" width="150"> <button.commit_callback - function="Inspect.OwnerProfilet" /> + function="Inspect.OwnerProfile" /> </button> <button follows="left|bottom" diff --git a/indra/newview/skins/default/xui/en/floater_map.xml b/indra/newview/skins/default/xui/en/floater_map.xml index 70f9c19658..dc6a02efe1 100644 --- a/indra/newview/skins/default/xui/en/floater_map.xml +++ b/indra/newview/skins/default/xui/en/floater_map.xml @@ -81,7 +81,6 @@ left="0" name="floater_map_east" right="10" - text="E" text_color="1 1 1 0.7" top="215"> E @@ -95,7 +94,6 @@ left="0" name="floater_map_west" right="11" - text="W" text_color="1 1 1 0.7" top="215"> W @@ -109,7 +107,6 @@ left="0" name="floater_map_south" right="10" - text="S" text_color="1 1 1 0.7" top="215"> S @@ -123,7 +120,6 @@ left="0" name="floater_map_southeast" right="20" - text="SE" text_color="1 1 1 0.7" top="215"> SE @@ -137,7 +133,6 @@ left="0" name="floater_map_northeast" right="20" - text="NE" text_color="1 1 1 0.7" top="215"> NE @@ -151,7 +146,6 @@ left="0" name="floater_map_southwest" right="20" - text="SW" text_color="1 1 1 0.7" top="215"> SW @@ -165,7 +159,6 @@ left="0" name="floater_map_northwest" right="20" - text="NW" text_color="1 1 1 0.7" top="215"> NW diff --git a/indra/newview/skins/default/xui/en/floater_nearby_chat.xml b/indra/newview/skins/default/xui/en/floater_nearby_chat.xml index 78f795b0e2..e851710ad8 100644 --- a/indra/newview/skins/default/xui/en/floater_nearby_chat.xml +++ b/indra/newview/skins/default/xui/en/floater_nearby_chat.xml @@ -39,6 +39,7 @@ height="320" max_length="2147483647" name="Chat History Editor" + parse_highlights="true" read_only="true" text_color="ChatHistoryTextColor" text_readonly_color="ChatHistoryTextColor" diff --git a/indra/newview/skins/default/xui/en/floater_preview_notecard.xml b/indra/newview/skins/default/xui/en/floater_preview_notecard.xml index 1d7b7adca2..f90f2a81cf 100644 --- a/indra/newview/skins/default/xui/en/floater_preview_notecard.xml +++ b/indra/newview/skins/default/xui/en/floater_preview_notecard.xml @@ -74,6 +74,7 @@ left="4" max_length="65536" name="Notecard Editor" + allow_html="true" handle_edit_keys_directly="true" tab_group="1" top="46" diff --git a/indra/newview/skins/default/xui/en/floater_report_abuse.xml b/indra/newview/skins/default/xui/en/floater_report_abuse.xml index c7f5a1170f..abde4ba5fa 100644 --- a/indra/newview/skins/default/xui/en/floater_report_abuse.xml +++ b/indra/newview/skins/default/xui/en/floater_report_abuse.xml @@ -33,6 +33,7 @@ length="1" follows="left|top" height="16" + font.name="SansSerif" font.style="BOLD" layout="topleft" left="10" @@ -58,6 +59,7 @@ length="1" follows="left|top" height="16" + font.name="SansSerif" font.style="BOLD" layout="topleft" left="10" @@ -83,6 +85,7 @@ length="1" follows="left|top" height="16" + font.name="SansSerif" font.style="BOLD" layout="topleft" left="10" @@ -130,6 +133,7 @@ length="1" follows="left|top" height="16" + font.name="SansSerif" font.style="BOLD" layout="topleft" left="48" @@ -155,6 +159,7 @@ length="1" follows="left|top" height="16" + font.name="SansSerif" font.style="BOLD" layout="topleft" left="48" @@ -345,6 +350,7 @@ length="1" follows="left|top" height="16" + font.name="SansSerif" font.style="BOLD" layout="topleft" left_delta="0" @@ -389,6 +395,7 @@ length="1" follows="left|top" height="16" + font.name="SansSerif" font.style="BOLD" layout="topleft" left="10" @@ -413,6 +420,7 @@ length="1" follows="left|top" height="16" + font.name="SansSerif" font.style="BOLD" layout="topleft" left_delta="0" @@ -437,6 +445,7 @@ length="1" follows="left|top" height="16" + font.name="SansSerif" font.style="BOLD" layout="topleft" left_delta="0" @@ -473,6 +482,7 @@ height="50" layout="topleft" left="10" + font.name="SansSerif" font.style="BOLD" name="incomplete_title" top_pad="5" diff --git a/indra/newview/skins/default/xui/en/floater_script_debug_panel.xml b/indra/newview/skins/default/xui/en/floater_script_debug_panel.xml index c8b8c34f85..2085b74a55 100644 --- a/indra/newview/skins/default/xui/en/floater_script_debug_panel.xml +++ b/indra/newview/skins/default/xui/en/floater_script_debug_panel.xml @@ -16,6 +16,7 @@ layout="topleft" max_length="10000" name="Chat History Editor" + parse_highlights="true" width="420" word_wrap="true" /> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_telehub.xml b/indra/newview/skins/default/xui/en/floater_telehub.xml index b703b49b8b..95de27e0ea 100644 --- a/indra/newview/skins/default/xui/en/floater_telehub.xml +++ b/indra/newview/skins/default/xui/en/floater_telehub.xml @@ -88,7 +88,6 @@ </text> <scroll_list follows="left|top" - font="SansSerifSmall" height="60" layout="topleft" left_delta="0" diff --git a/indra/newview/skins/default/xui/en/floater_test_inspectors.xml b/indra/newview/skins/default/xui/en/floater_test_inspectors.xml index a1a271d0eb..2011f57d8b 100644 --- a/indra/newview/skins/default/xui/en/floater_test_inspectors.xml +++ b/indra/newview/skins/default/xui/en/floater_test_inspectors.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <floater - can_resize="true" + can_resize="false" height="400" layout="topleft" name="floater_test_inspectors" @@ -15,7 +15,6 @@ width="300"> Click to spawn an inspector: </text> - <!-- James Tester, 4 years old --> <button name="avatar_2d_btn1" label="Avatar 2D" @@ -25,7 +24,7 @@ width="100" commit_callback.function="ShowAvatarInspector" commit_callback.parameter="22df1dcb-810a-4975-aab9-0159958fe155" /> - <!-- DonkeyPuncher 2008-10-1 --> + <!-- InspectorA Tester --> <button name="avatar_2d_btn5" label="Avatar 2D" @@ -34,8 +33,8 @@ height="20" width="100" commit_callback.function="ShowAvatarInspector" - commit_callback.parameter="d5330e4e-391a-4f00-9352-b797ed2f9a97" /> - <!-- DonkeyPuncher 2009-01-15 --> + commit_callback.parameter="927e68e0-e52d-4bb8-b1a9-add97a57c86a" /> + <!-- InspectorB Tester --> <button name="avatar_2d_btn2" label="Avatar 2D" @@ -44,8 +43,8 @@ height="20" width="100" commit_callback.function="ShowAvatarInspector" - commit_callback.parameter="05511655-a58a-48b6-b645-966f69fc17a7" /> - <!-- 2009-06-14 --> + commit_callback.parameter="9a2300ca-e251-45dd-bb61-e33139f6e4eb" /> + <!-- InspectorC Tester --> <button name="avatar_2d_btn3" label="Avatar 2D" @@ -54,7 +53,7 @@ height="20" width="100" commit_callback.function="ShowAvatarInspector" - commit_callback.parameter="6a164b3d-7c2c-43eb-880a-0ebc0687b3ec" /> + commit_callback.parameter="8024f082-34cc-48a3-a42e-c42f345efd74" /> <!-- jarvtest Bombastic 2009-10-3 --> <button name="avatar_2d_btn4" @@ -85,7 +84,9 @@ top_pad="10" left_delta="0" height="20" - width="100"/> + width="100" + commit_callback.function="ShowObjectInspector" + commit_callback.parameter="" /> <button name="group_btn" label="Group" @@ -107,5 +108,17 @@ left_delta="0" height="20" width="100"/> + <text + follows="left|top" + font="SansSerif" + height="20" + layout="topleft" + left="0" + max_length="65536" + name="slurl" + top_pad="4" + width="100"> + secondlife:///app/agent/00000000-0000-0000-0000-000000000000/inspect + </text> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_test_textbox.xml b/indra/newview/skins/default/xui/en/floater_test_textbox.xml index 88c001b714..8305452c85 100644 --- a/indra/newview/skins/default/xui/en/floater_test_textbox.xml +++ b/indra/newview/skins/default/xui/en/floater_test_textbox.xml @@ -122,7 +122,6 @@ left="10" name="floater_map_north" right="30" - text="N" text_color="1 1 1 0.7" top="370"> N diff --git a/indra/newview/skins/default/xui/en/floater_test_widgets.xml b/indra/newview/skins/default/xui/en/floater_test_widgets.xml index 6ddaa63c1d..5a29c6a319 100644 --- a/indra/newview/skins/default/xui/en/floater_test_widgets.xml +++ b/indra/newview/skins/default/xui/en/floater_test_widgets.xml @@ -19,6 +19,8 @@ can_resize="true" title="Test Floater" height="500" + min_width="850" + min_height="500" layout="topleft" name="floater_test_widgets" help_topic="floater_test_widgets" @@ -39,6 +41,7 @@ <menu_bar height="18" layout="topleft" + follows="top|left" tool_tip="menu" left="2" name="test_menu_bar" @@ -74,7 +77,9 @@ <text bottom="55" layout="topleft" - left="10"> + follows="top|left" + left="10" + height="16"> For widget list see https://wiki.lindenlab.com/wiki/Viewer:UI/Widgets </text> @@ -82,6 +87,7 @@ <button height="20" + follows="top|left" label="Button" layout="topleft" left_delta="0" @@ -91,7 +97,7 @@ width="100" /> <!-- "flyout_button" is a button that can spawn a menu --> <flyout_button - follows="right|bottom" + follows="top|left" height="20" label="Flyout" layout="topleft" @@ -121,6 +127,7 @@ provide input that is not a list item. --> <combo_box bottom_delta="35" + follows="top|left" height="16" width="150" label="Combobox" @@ -150,6 +157,7 @@ the text. --> <line_editor height="20" + follows="top|left" layout="topleft" left_delta="0" name="test_line_editor" @@ -161,7 +169,7 @@ <!-- "filter_editor" is a specialized line_editor that shows read-only help text until the user clicks in the widget. --> <filter_editor - follows="left|top|right" + follows="left|top" height="20" label="Type here to search" layout="topleft" @@ -173,6 +181,7 @@ <!-- "progress_bar" percent completed gets set in C++ code --> <progress_bar height="16" + follows="top|left" layout="topleft" left_delta="0" name="test_progress_bar" @@ -227,6 +236,7 @@ <!-- "scroll_list" is a scrolling list of columnar data. --> <scroll_list bottom_delta="100" + follows="top|left" height="80" draw_heading="true" tool_tip="scroll list" @@ -239,10 +249,19 @@ dynamic_width="true" name="second_column" label="Column B"/> + <row> + <column column="first_column">short text</column> + <column column="second_column">more short text</column> + </row> + <row> + <column column="first_column">this is some longer text</column> + <column column="second_column">and here is some more long text</column> + </row> </scroll_list> <!-- "slider" is a horizontal input widget for numerical data. --> <slider bottom_delta="45" + follows="top|left" layout="topleft" min_val="0" max_val="100" @@ -255,22 +274,36 @@ change the value. --> <spinner bottom_delta="35" + follows="top|left" label="Spinner" layout="topleft" name="test_spinner" tool_tip="spinner"/> <text bottom_delta="50" + follows="top|left" + font.name="SansSerifSmall" font.style = "UNDERLINE" layout="topleft" name="test_text" tool_tip="text"> Text (underlined) </text> + <text + top_pad="10" + follows="top|left" + layout="topleft" + width="60" + use_ellipses="true" + name="test_text" + tool_tip="text"> + Truncated text here + </text> <!-- "text_editor" is a multi-line text input widget, similar to textarea in HTML. --> <text_editor - height="80" + height="40" + follows="top|left|bottom" layout="topleft" left_delta="0" name="test_text_editor" @@ -279,7 +312,18 @@ width="200"> Text Editor </text_editor> - + <text + height="40" + follows="top|left|bottom" + layout="topleft" + name="test_text_editor" + tool_tip="text box" + top_pad="5" + width="200"> + Text box +with +multiple lines + </text> <!-- And a third column --> <!-- "tab_container" is a holder for multiple panels of UI widgets. @@ -309,7 +353,6 @@ color="0.3 0.6 0.9 1" follows="left|top" height="90" - border="true" layout="topleft" left="10" label="Color Swatch 1" @@ -333,7 +376,7 @@ </panel> <!-- panels can also refer to other floaters or panels --> <panel - border="true" + border="true" filename="floater_test_checkbox.xml" height="225" label="Tab 2 - Checkbox" diff --git a/indra/newview/skins/default/xui/en/floater_ui_preview.xml b/indra/newview/skins/default/xui/en/floater_ui_preview.xml index 9a3d5aa183..4ed6787f53 100644 --- a/indra/newview/skins/default/xui/en/floater_ui_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_ui_preview.xml @@ -17,7 +17,7 @@ left="0" mouse_opaque="false" name="main_panel" - right="650" + right="750" top="0"> <text type="string" diff --git a/indra/newview/skins/default/xui/en/inspect_avatar.xml b/indra/newview/skins/default/xui/en/inspect_avatar.xml index 2042ffedbc..b44acebbcf 100644 --- a/indra/newview/skins/default/xui/en/inspect_avatar.xml +++ b/indra/newview/skins/default/xui/en/inspect_avatar.xml @@ -1,102 +1,142 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes"?> -<!-- All our XML is utf-8 encoded. --> - +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> <!-- Not can_close / no title to avoid window chrome Single instance - only have one at a time, recycle it each spawn --> <floater - background_opaque="false" - background_visible="true" - bevel_style="in" - bg_alpha_color="PanelDefaultBackgroundColor" - can_close="false" - can_minimize="false" - name="inspect_avatar" - help_topic="inspect_avatar" - single_instance="true" - sound_flags="0" - title="" - visible="true" - width="300" - height="200" - left="0" - top="200" - > + bevel_style="in" + bg_alpha_color="PanelDefaultBackgroundColor" + can_close="false" + can_minimize="false" + height="190" + layout="topleft" + name="inspect_avatar" + single_instance="true" + sound_flags="0" + visible="true" + width="300"> <!-- Allowed fields include: [BORN_ON] ("12/3/2008") [SL_PROFILE] (Second Life profile), - [RW_PROFILE] (real world profile), + [RW_PROFILE] (real world profile), [ACCTTYPE] ("Resident"), [PAYMENTINFO] ("Payment Info on File"), - [AGE] ("1 year 2 months") --> - <string name="Subtitle"> + [AGE] ("1 year 2 months") + --> + <string + name="Subtitle"> [AGE] </string> - <string name="Details"> -[ACCTTYPE], [PAYMENTINFO] -Profile: [SL_PROFILE] + <string + name="Details"> +[ACCTTYPE][COMMA] [PAYMENTINFO] + </string> + <string + name="Partner"> + Partner: [PARTNER] </string> <text - top="180" left="10" width="280" height="70" follows="left|top|right|bottom" - use_ellipses="true" word_wrap="true" - font="SansSerifHugeBold" text_color="white" - mouse_opaque="true" name="user_name" > - Test Name - </text> - <!-- General purpose subtitle area, not sure yet what goes here --> - <text - top="155" left="10" width="150" height="20" follows="left|top|right|bottom" - font="SansSerifBig" - text_color="white" - mouse_opaque="true" - name="user_subtitle" /> - <!-- Leave text fields blank so it doesn't flash when data arrives off the network --> - <text - top="115" left="10" width="290" height="50" follows="left|top|right|bottom" - font="SansSerifSmall" text_color="white" word_wrap="true" - mouse_opaque="true" name="user_details" /> - <avatar_icon - top="185" left="230" width="60" height="60" follows="left|top|right|bottom" - color="1 1 1 1" enabled="true" mouse_opaque="true" name="avatar_icon" - /> - <slider - bottom="35" left="45" width="250" height="30" follows="top|left" - name="volume_slider" - tool_tip="Voice Volume" - increment="0.05" initial_value="0.75" max_val="1" min_val="0" - show_text="false" - /> - <button - bottom="40" left="10" width="32" height="16" follows="left|top|right|bottom" - name="mute_btn" - label="" - image_unselected="icn_speaker_dark.tga" - image_disabled="icn_speaker_dark.tga" - image_selected="icn_speaker-muted_dark.tga" - image_hover_selected="icn_speaker-muted_dark.tga" - image_disabled_selected="icn_speaker-muted_dark.tga" - halign="center" - toggle="true" - /> - <button - bottom="10" left="10" width="110" height="20" follows="top|left" - name="add_friend_btn" - label="Add Friend" - font="SansSerif" - /> - <button - bottom="10" left="120" width="110" height="20" follows="top|left" - name="view_profile_btn" - label="View Profile" - font="SansSerif" - /> - <button - bottom="10" left="230" width="60" height="20" follows="top|left" - name="gear_btn" - label="" - image_overlay="Icon_Gear_Foreground" - image_overlay_alignment="center" - scale_image="true" - /> -</floater> + follows="left|top|right|bottom" + font="SansSerifHugeBold" + height="70" + left="10" + name="user_name" + text_color="white" + top="20" + use_ellipses="true" + value="Test Name" + width="280" + word_wrap="true" /> + <text + follows="left|top|right|bottom" + font="SansSerifBig" + height="20" + left="10" + name="user_subtitle" + text_color="white" + top="45" + width="150" /> + <!-- Leave text fields blank so it doesn't flash when data arrives off the network --> + <text + follows="left|top|right|bottom" + height="20" + left="10" + name="user_details" + text_color="white" + top="85" + width="290" + word_wrap="true" /> + <text + follows="left|top|right|bottom" + height="20" + left="10" + name="user_partner" + text_color="white" + top="105" + width="290" + word_wrap="true" /> + <avatar_icon + follows="left|top|right|bottom" + height="60" + left="230" + mouse_opaque="true" + name="avatar_icon" + top="15" + width="60" /> + <slider + follows="top|left" + height="30" + increment="0.05" + left="20" + max_val="0.95" + min_val="0.05" + name="volume_slider" + show_text="false" + tool_tip="Voice Volume" + top="125" + value="0.5" + width="240" /> + <button + follows="left|top|right|bottom" + height="16" + image_disabled="icn_speaker_dark.tga" + image_disabled_selected="icn_speaker-muted_dark.tga" + image_hover_selected="icn_speaker-muted_dark.tga" + image_selected="icn_speaker-muted_dark.tga" + image_unselected="icn_speaker_dark.tga" + is_toggle="true" + left="265" + name="mute_btn" + picture_style="true" + top="132" + width="32" /> + <button + follows="top|left" + font="SansSerif" + height="20" + label="Add Friend" + left="10" + name="add_friend_btn" + top_pad="10" + width="105" /> + <button + follows="top|left" + font="SansSerif" + height="20" + label="View Profile" + left_delta="110" + name="view_profile_btn" + top_delta="0" + width="105" /> + <menu_button + follows="top|left" + height="20" + image_overlay="windows\Icon_Gear_Foreground.png" + image_overlay_alignment="center" + menu_filename="menu_inspect_avatar_gear.xml" + name="gear_btn" + picture_style="true" + top_delta="0" + left_delta="110" + width="60"/> +</floater> diff --git a/indra/newview/skins/default/xui/en/inspect_object.xml b/indra/newview/skins/default/xui/en/inspect_object.xml new file mode 100644 index 0000000000..cc56f630b0 --- /dev/null +++ b/indra/newview/skins/default/xui/en/inspect_object.xml @@ -0,0 +1,162 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<!-- + Not can_close / no title to avoid window chrome + Single instance - only have one at a time, recycle it each spawn +--> +<floater + bevel_style="in" + bg_opaque_color="MouseGray" + can_close="false" + can_minimize="false" + height="145" + layout="topleft" + name="inspect_object" + single_instance="true" + sound_flags="0" + visible="true" + width="300"> + <string name="Creator">By [CREATOR]</string> + <string name="CreatorAndOwner"> +By [CREATOR] +Owned by [OWNER] + </string> + <!-- *TODO: Might need to change to [AMOUNT] if icon contains "L$" --> + <string name="Price">L$[AMOUNT]</string> + <string name="PriceFree">Free!</string> + <string name="Touch">Touch</string> + <string name="Sit">Sit</string> + <text + follows="all" + font="SansSerifLargeBold" + height="20" + left="8" + name="object_name" + text_color="white" + top="8" + use_ellipses="true" + value="Test Object Name That Is Really Long" + width="268" /> + <text + follows="all" + font="SansSerif" + height="30" + left="10" + name="object_creator" + top_pad="0" + width="280"> +By Longavatarname Johnsonlongstonnammer +Owned by James Linden + </text> + <text + follows="all" + height="45" + left="100" + name="object_description" + top_pad="4" + width="200" + word_wrap="true"> +This is a really long description for an object being as how it is at least 80 characters in length and maybe more like 120 at this point. Who knows, really? + </text> + <!-- *TODO: Replace this icon --> + <icon + name="price_icon" + image_name="Favorite_Star_Active" + left="5" + width="16" + height="16" + top="79" + follows="left|top" + /> + <text + follows="all" + font="SansSerifSmallBold" + height="45" + left="22" + name="price_text" + text_color="white" + top="80" + font_shadow="none" + width="80"> +L$300,000 + </text> + <!-- Overlapping buttons for all default actions. Show "Buy" if + for sale, "Sit" if can sit, etc. --> + <button + follows="top|left" + font="SansSerif" + height="23" + label="Buy" + left="10" + name="buy_btn" + top="116" + width="100" /> + <button + follows="top|left" + font="SansSerif" + height="23" + label="Pay" + left_delta="0" + name="pay_btn" + top_delta="0" + width="100" /> + <button + follows="top|left" + font="SansSerif" + height="23" + label="Take Copy" + left_delta="0" + name="take_free_copy_btn" + top_delta="0" + width="100" /> + <button + follows="top|left" + font="SansSerifSmall" + height="23" + label="Touch" + left_delta="0" + name="touch_btn" + top_delta="0" + width="100" /> + <button + follows="top|left" + font="SansSerif" + height="23" + label="Sit" + left_delta="0" + name="sit_btn" + top_delta="0" + width="100" /> + <button + follows="top|left" + font="SansSerifSmall" + height="23" + label="Open" + left_delta="0" + name="open_btn" + top_delta="0" + width="100" /> + <!-- non-overlapping buttons here --> + <menu_button + follows="top|left" + height="23" + image_overlay="Icon_Gear_Foreground" + image_overlay_alignment="center" + right="-8" + menu_filename="menu_inspect_object_gear.xml" + name="gear_btn" + picture_style="true" + top_delta="0" + width="30" /> + <button + follows="top|left" + height="22" + image_overlay="TabIcon_Close_Off" + layout="topleft" + name="more_info_btn" + picture_style="true" + right="-8" + top="7" + left_delta="110" + tab_stop="false" + width="20" /> +</floater> diff --git a/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml b/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml new file mode 100644 index 0000000000..9f5b7f3813 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml @@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="utf-8"?>
+<menu
+ create_jump_keys="true"
+ layout="topleft"
+ mouse_opaque="false"
+ visible="false"
+ name="Gear Menu">
+ <menu_item_call
+ label="View Profile"
+ layout="topleft"
+ enabled="true"
+ name="view_profile">
+ <menu_item_call.on_click
+ function="InspectAvatar.ViewProfile"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Add Friend"
+ layout="topleft"
+ name="add_friend">
+ <menu_item_call.on_click
+ function="InspectAvatar.AddFriend"/>
+ </menu_item_call>
+ <menu_item_call
+ label="IM"
+ layout="topleft"
+ name="im">
+ <menu_item_call.on_click
+ function="InspectAvatar.IM"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Call"
+ layout="topleft"
+ enabled="true"
+ name="call">
+ </menu_item_call>
+ <menu_item_call
+ label="Teleport"
+ layout="topleft"
+ name="teleport">
+ <menu_item_call.on_click
+ function="InspectAvatar.Teleport"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Invite to Group"
+ layout="topleft"
+ name="invite_to_group">
+ <menu_item_call.on_click
+ function="InspectAvatar.InviteToGroup"/>
+ </menu_item_call>
+ <menu_item_separator layout="topleft" />
+ <menu_item_call
+ label="Block"
+ layout="topleft"
+ name="block">
+ <menu_item_call.on_click
+ function="InspectAvatar.Block"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Report"
+ layout="topleft"
+ name="report">
+ <menu_item_call.on_click
+ function="InspectAvatar.Report"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Freeze"
+ layout="topleft"
+ name="freeze">
+ <menu_item_call.on_click
+ function="InspectAvatar.Freeze"/>
+ <menu_item_call.on_visible
+ function="InspectAvatar.VisibleGodMode"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Eject"
+ layout="topleft"
+ name="eject">
+ <menu_item_call.on_click
+ function="InspectAvatar.Eject"/>
+ <menu_item_call.on_visible
+ function="InspectAvatar.VisibleGodMode"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Debug"
+ layout="topleft"
+ name="debug">
+ <menu_item_call.on_click
+ function="InspectAvatar.Debug"/>
+ <menu_item_call.on_visible
+ function="InspectAvatar.VisibleGodMode"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Find On Map"
+ layout="topleft"
+ name="find_on_map">
+ <menu_item_call.on_click
+ function="InspectAvatar.FindOnMap"/>
+ <menu_item_call.on_visible
+ function="InspectAvatar.VisibleFindOnMap"/>
+ </menu_item_call>
+ <menu_item_call
+ enabled="false"
+ label="Zoom In"
+ layout="topleft"
+ name="zoom_in">
+ </menu_item_call>
+ <menu_item_call
+ label="Pay"
+ layout="topleft"
+ name="pay">
+ <menu_item_call.on_click
+ function="InspectAvatar.Pay"/>
+ </menu_item_call>
+</menu>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/menu_inspect_object_gear.xml b/indra/newview/skins/default/xui/en/menu_inspect_object_gear.xml new file mode 100644 index 0000000000..1bba8eb264 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_inspect_object_gear.xml @@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu + create_jump_keys="true" + layout="topleft" + mouse_opaque="false" + visible="false" + name="Gear Menu"> + <menu_item_call + label="Touch" + layout="topleft" + enabled="true" + name="touch"> + <menu_item_call.on_click + function="InspectObject.Touch"/> + <menu_item_call.on_visible + function="Object.VisibleTouch" /> + </menu_item_call> + <menu_item_call + label="Sit" + layout="topleft" + name="sit"> + <menu_item_call.on_click + function="InspectObject.Sit"/> + <menu_item_call.on_visible + function="Object.EnableSit" /> + </menu_item_call> + <menu_item_call + label="Pay" + layout="topleft" + name="pay"> + <menu_item_call.on_click + function="InspectObject.Pay"/> + <menu_item_call.on_visible + function="VisiblePayObject" /> + </menu_item_call> + <menu_item_call + label="Buy" + layout="topleft" + enabled="true" + name="buy"> + <menu_item_call.on_click + function="InspectObject.Buy"/> + <menu_item_call.on_visible + function="Object.VisibleBuy" /> + </menu_item_call> + <menu_item_call + label="Take" + layout="topleft" + name="take"> + <menu_item_call.on_click + function="Object.Take" /> + <menu_item_call.on_enable + function="Object.VisibleTake"/> + </menu_item_call> + <menu_item_call + label="Take Copy" + layout="topleft" + name="take_copy"> + <menu_item_call.on_click + function="InspectObject.TakeFreeCopy"/> + <menu_item_call.on_visible + function="Tools.VisibleTakeCopy"/> + </menu_item_call> + <menu_item_call + label="Open" + layout="topleft" + name="open"> + <menu_item_call.on_click + function="InspectObject.Open"/> + <menu_item_call.on_visible + function="Object.VisibleOpen" /> + </menu_item_call> + <menu_item_call + label="Edit" + layout="topleft" + name="report"> + <menu_item_call.on_click + function="Object.Edit" /> + <menu_item_call.on_enable + function="Object.VisibleEdit"/> + </menu_item_call> + <menu_item_call + label="Wear" + layout="topleft" + name="wear"> + <menu_item_call.on_click + function="Object.AttachToAvatar" /> + <menu_item_call.on_visible + function="Object.VisibleWear" /> + </menu_item_call> + <menu_item_call + label="Report" + layout="topleft" + name="report"> + <menu_item_call.on_click + function="Object.ReportAbuse" /> + </menu_item_call> + <menu_item_call + label="Block" + layout="topleft" + name="block"> + <menu_item_call.on_click + function="Object.Mute" /> + <menu_item_call.on_visible + function="Object.VisibleMute" /> + </menu_item_call> + <menu_item_call + enabled="false" + label="Zoom In" + layout="topleft" + name="zoom_in"> + </menu_item_call> + <menu_item_call + label="Remove" + layout="topleft" + name="remove"> + <menu_item_call.on_click + function="Object.Delete" /> + <menu_item_call.on_visible + function="Object.VisibleDelete" /> + </menu_item_call> + <menu_item_call + label="More Info" + layout="topleft" + name="more_info"> + <menu_item_call.on_click + function="InspectObject.MoreInfo"/> + </menu_item_call> +</menu> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index fe088b43be..829c2e02d8 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -447,7 +447,7 @@ Your default permissions may not work in older regions. icon="alertmodal.tga" name="ClickUnimplemented" type="alertmodal"> -This feature is yet to be implemented. +Sorry, not implemented yet. </notification> <notification @@ -761,7 +761,7 @@ You need an account to enter [SECOND_LIFE]. Would you like to create one now? name="url" openexternally = "1"> - http://secondlife.com/registration/ + http://join/secondlife.com/ </url> <usetemplate name="okcancelbuttons" diff --git a/indra/newview/skins/default/xui/en/panel_audio_device.xml b/indra/newview/skins/default/xui/en/panel_audio_device.xml index 5f495ef8ce..4329982209 100644 --- a/indra/newview/skins/default/xui/en/panel_audio_device.xml +++ b/indra/newview/skins/default/xui/en/panel_audio_device.xml @@ -80,7 +80,7 @@ bg_readonly_color="0 0 0 0" enabled="false" height="60" - hide_border="true" + border_visible="false" hide_scrollbar="true" layout="topleft" left_delta="10" diff --git a/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml b/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml index d5ed0c986d..c3ae2d953a 100644 --- a/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml +++ b/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml @@ -62,7 +62,6 @@ auto_update="true" follows="right" draw_border="false" - halign="left" height="16" layout="topleft" left_pad="3" diff --git a/indra/newview/skins/default/xui/en/panel_edit_profile.xml b/indra/newview/skins/default/xui/en/panel_edit_profile.xml index 0d6d8ba97d..b13058f40a 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_profile.xml @@ -201,7 +201,7 @@ type="string" follows="left|top" font="SansSerifSmall" - font.style="BOLD" + font.style="BOLD" height="15" layout="topleft" left="10" @@ -272,7 +272,7 @@ <text follows="left|top" font="SansSerifSmall" - font.style="BOLD" + font.style="BOLD" height="15" layout="topleft" left="10" @@ -305,7 +305,7 @@ <text follows="left|top" font="SansSerifSmall" - font.style="BOLD" + font.style="BOLD" height="15" layout="topleft" left="10" @@ -391,6 +391,7 @@ follows="left|top" height="10" layout="topleft" + font="SansSerifSmall" font.style="BOLD" left="12" mouse_opaque="false" diff --git a/indra/newview/skins/default/xui/en/panel_group_land_money.xml b/indra/newview/skins/default/xui/en/panel_group_land_money.xml index 069cf1d7bd..999aa814b1 100644 --- a/indra/newview/skins/default/xui/en/panel_group_land_money.xml +++ b/indra/newview/skins/default/xui/en/panel_group_land_money.xml @@ -55,7 +55,6 @@ <scroll_list draw_heading="true" follows="top" - font="SansSerifSmall" heading_height="14" height="100" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_login.xml b/indra/newview/skins/default/xui/en/panel_login.xml index c96c296057..aeeb884036 100644 --- a/indra/newview/skins/default/xui/en/panel_login.xml +++ b/indra/newview/skins/default/xui/en/panel_login.xml @@ -9,7 +9,7 @@ width="800"> <panel.string name="create_account_url"> - http://secondlife.com/registration/ + http://join.secondlife.com/ </panel.string> <panel.string name="real_url"> diff --git a/indra/newview/skins/default/xui/en/panel_profile.xml b/indra/newview/skins/default/xui/en/panel_profile.xml index 02d179d503..b4212aaa34 100644 --- a/indra/newview/skins/default/xui/en/panel_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_profile.xml @@ -17,6 +17,10 @@ http://www.secondlife.com/account/billing.php?lang=en </string> <string + name="partner_edit_link_url"> + http://www.secondlife.com/account/partners.php?lang=en + </string> + <string name="my_account_link_url" value="http://secondlife.com/account" /> <string diff --git a/indra/newview/skins/default/xui/en/panel_progress.xml b/indra/newview/skins/default/xui/en/panel_progress.xml index 9b2461db7c..9abaf29f57 100644 --- a/indra/newview/skins/default/xui/en/panel_progress.xml +++ b/indra/newview/skins/default/xui/en/panel_progress.xml @@ -82,7 +82,8 @@ name="progress_text" text_color="LoginProgressBoxTextColor" top_pad="5" - width="593" /> + width="593" + word_wrap="true"/> <progress_bar bottom="115" color_bar="1 1 1 0.96" @@ -100,11 +101,12 @@ height="100" layout="topleft" left="45" - line_spacing="2" + line_spacing.pixels="2" name="message_text" text_color="LoginProgressBoxTextColor" top="145" - width="550" /> + width="550" + word_wrap="true"/> </layout_panel> <layout_panel height="200" diff --git a/indra/newview/skins/default/xui/en/panel_script_ed.xml b/indra/newview/skins/default/xui/en/panel_script_ed.xml index a3c714ce72..765e2ae623 100644 --- a/indra/newview/skins/default/xui/en/panel_script_ed.xml +++ b/indra/newview/skins/default/xui/en/panel_script_ed.xml @@ -32,8 +32,6 @@ <text_editor type="string" length="1" - bevel_style="none" - border_style="line" bottom="393" follows="left|top|right|bottom" font="Monospace" diff --git a/indra/newview/skins/default/xui/en/panel_side_tray.xml b/indra/newview/skins/default/xui/en/panel_side_tray.xml index 418b88b7b5..d172154d49 100644 --- a/indra/newview/skins/default/xui/en/panel_side_tray.xml +++ b/indra/newview/skins/default/xui/en/panel_side_tray.xml @@ -21,7 +21,6 @@ filename="panel_sidetray_home_tab.xml" label="home" border="true" - font="SansSerifBold" /> </sidetray_tab> @@ -44,14 +43,12 @@ name="panel_people" filename="panel_people.xml" border="true" - font="SansSerifBold" /> <panel class="panel_profile_view" name="panel_profile_view" filename="panel_profile_view.xml" border="true" - font="SansSerifBold" /> <panel class="panel_group_info_sidetray" @@ -107,7 +104,6 @@ filename="panel_me_profile.xml" label="Me" border="true" - font="SansSerifBold" /> </sidetray_tab> diff --git a/indra/newview/skins/default/xui/en/panel_status_bar.xml b/indra/newview/skins/default/xui/en/panel_status_bar.xml index f6ffd2e4ee..07f0806ccb 100644 --- a/indra/newview/skins/default/xui/en/panel_status_bar.xml +++ b/indra/newview/skins/default/xui/en/panel_status_bar.xml @@ -58,7 +58,7 @@ <text type="string" length="1" - disabled_color="TimeTextColor" + text_readonly_color="TimeTextColor" follows="right|bottom" halign="right" height="16" @@ -100,9 +100,8 @@ visible="false" width="16" /> <text - type="string" - length="1" - disabled_color="HealthTextColor" + bg_visible="false" + text_readonly_color="HealthTextColor" follows="rsight|bottom" font_shadow="hard" height="16" diff --git a/indra/newview/skins/default/xui/en/panel_world_map.xml b/indra/newview/skins/default/xui/en/panel_world_map.xml index ee355fa95c..9f08d3a817 100644 --- a/indra/newview/skins/default/xui/en/panel_world_map.xml +++ b/indra/newview/skins/default/xui/en/panel_world_map.xml @@ -51,7 +51,6 @@ label="N" layout="topleft" name="floater_map_north" - text="N" text_color="1 1 1 0.7"> N </text> @@ -61,7 +60,6 @@ label="E" layout="topleft" name="floater_map_east" - text="E" text_color="1 1 1 0.7"> E </text> @@ -71,7 +69,6 @@ label="W" layout="topleft" name="floater_map_west" - text="W" text_color="1 1 1 0.7"> W </text> @@ -81,7 +78,6 @@ label="S" layout="topleft" name="floater_map_south" - text="S" text_color="1 1 1 0.7"> S </text> @@ -91,7 +87,6 @@ label="SE" layout="topleft" name="floater_map_southeast" - text="SE" text_color="1 1 1 0.7"> SE </text> @@ -101,7 +96,6 @@ label="NE" layout="topleft" name="floater_map_northeast" - text="NE" text_color="1 1 1 0.7"> NE </text> @@ -111,7 +105,6 @@ label="SW" layout="topleft" name="floater_map_southwest" - text="SW" text_color="1 1 1 0.7"> SW </text> @@ -121,7 +114,6 @@ label="NW" layout="topleft" name="floater_map_northwest" - text="NW" text_color="1 1 1 0.7"> NW </text> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index f398cc33b5..3a5347fe12 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -15,9 +15,6 @@ <string name="StartupDetectingHardware">Detecting hardware...</string> <string name="StartupLoading">Loading</string> <string name="Fullbright">Fullbright (Legacy)</string> - <string name="CacheWaiting">(Loading...)</string> - <string name="CacheNobody">(nobody)</string> - <string name="CacheNone">(none)</string> <!-- Login --> <string name="LoginInProgress">Logging in. [APP_NAME] may appear frozen. Please wait.</string> <string name="LoginInProgressNoFrozen">Logging in...</string> @@ -40,13 +37,14 @@ <string name="LoginDownloadingClothing">Downloading clothing...</string> <string name="LoginFailedNoNetwork">Network Error: Could not establish connection, please check your network connection.</string> <string name="Quit">Quit</string> - <string name="create_account_url">http://secondlife.com/registration/</string> + <string name="create_account_url">http://join.secondlife.com/</string> <!-- Disconnection --> <string name="AgentLostConnection">This region may be experiencing trouble. Please check your connection to the Internet.</string> <!-- Tooltip, lltooltipview.cpp --> + <!-- *TODO: Most of these are now unused, eliminate them --> <string name="TooltipPerson">Person</string><!-- Object under mouse pointer is an avatar --> <string name="TooltipNoName">(no name)</string> <!-- No name on an object --> <string name="TooltipOwner">Owner:</string> <!-- Owner name follows --> @@ -118,7 +116,10 @@ <!-- Avatar name: text shown while fetching name --> <string name="AvatarNameWaiting">(waiting)</string> - <!-- Avatar name: text shown as an alternative to AvatarNameFetching, easter egg. --> + <!-- Avatar name: More than one avatar is selected/used here --> + <string name="AvatarNameMultiple">(multiple)</string> + + <!-- Avatar name: text shown as an alternative to AvatarNameFetching, easter egg. --> <string name="AvatarNameHippos">(hippos)</string> <!-- Group name: text shown for LLUUID::null --> @@ -1931,21 +1932,24 @@ this texture in your inventory <string name="Left Pec">Left Pec</string> <string name="Right Pec">Right Pec</string> - <!-- Avatar age computation, see LLAvatarPropertiesProcessor::ageFromDate --> - <string name="YearsMonthsOld">[AGEYEARS][AGEMONTHS]old</string> - <string name="WeeksOld">[AGEWEEKS]old</string> - <string name="DaysOld">[AGEDAYS]old</string> + <!-- Avatar age computation, see LLDateUtil::ageFromDate --> + <string name="YearsMonthsOld">[AGEYEARS] [AGEMONTHS] old</string> + <string name="YearsOld">[AGEYEARS] old</string> + <string name="MonthsOld">[AGEMONTHS] old</string> + <string name="WeeksOld">[AGEWEEKS] old</string> + <string name="DaysOld">[AGEDAYS] old</string> <string name="TodayOld">Joined today</string> - <!-- Use value="" because of trailing spaces --> - <string name="AgeYears" value="[YEARS] years " /> - <string name="Age1Year" value="1 year "/> - <string name="AgeMonths" value="[MONTHS] months "/> - <string name="Age1Month" value="1 month "/> - <string name="AgeWeeks" value="[WEEKS] weeks "/> - <string name="Age1Week" value="1 week "/> - <string name="AgeDays" value="[DAYS] days "/> - <string name="Age1Day" value="1 day "/> + <!-- AgeYearsA = singular, AgeYearsB = plural, see logic in + LLTrans::getCountString() --> + <string name="AgeYearsA">[COUNT] year</string> + <string name="AgeYearsB">[COUNT] years</string> + <string name="AgeMonthsA">[COUNT] month</string> + <string name="AgeMonthsB">[COUNT] months</string> + <string name="AgeWeeksA">[COUNT] week</string> + <string name="AgeWeeksB">[COUNT] weeks</string> + <string name="AgeDaysA">[COUNT] day</string> + <string name="AgeDaysB">[COUNT] days</string> <!-- Account types, see LLAvatarPropertiesProcessor --> <string name="AcctTypeResident">Resident</string> @@ -1995,8 +1999,8 @@ this texture in your inventory <!-- groups --> <string name="GroupsNone">none</string> - <string name="Group" value=" (group)" /> - <string name="Unknown">(Unknown)</string> + <string name="Group" value=" (group)" /> + <string name="Unknown">(Unknown)</string> <string name="SummaryForTheWeek" value="Summary for this week, beginning on " /> <string name="NextStipendDay" value="The next stipend day is " /> <string name="GroupIndividualShare" value=" Group Individual Share" /> diff --git a/indra/newview/skins/default/xui/en/widgets/line_editor.xml b/indra/newview/skins/default/xui/en/widgets/line_editor.xml index 8b4126952e..546fbd9b47 100644 --- a/indra/newview/skins/default/xui/en/widgets/line_editor.xml +++ b/indra/newview/skins/default/xui/en/widgets/line_editor.xml @@ -1,8 +1,7 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<line_editor - background_image="TextField_Off" - background_image_disabled="TextField_Disabled" - background_image_focused="TextField_Active" +<line_editor background_image="TextField_Off" + background_image_disabled="TextField_Disabled" + background_image_focused="TextField_Active" select_on_focus="false" handle_edit_keys_directly="false" commit_on_focus_lost="true" diff --git a/indra/newview/skins/default/xui/en/widgets/simple_text_editor.xml b/indra/newview/skins/default/xui/en/widgets/simple_text_editor.xml index 4f2261c953..8ace7b96bc 100644 --- a/indra/newview/skins/default/xui/en/widgets/simple_text_editor.xml +++ b/indra/newview/skins/default/xui/en/widgets/simple_text_editor.xml @@ -1,22 +1,26 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <simple_text_editor + allow_html="false" mouse_opaque="true" font="SansSerifSmall" max_length="255" embedded_items="false" hide_scrollbar="false" - hide_border="true" + border_visible="false" word_wrap="false" ignore_tab="true" + line_spacing.pixels="1" track_bottom="false" cursor_color="TextCursorColor" default_color="TextDefaultColor" text_color="TextFgColor" text_readonly_color="TextFgReadOnlyColor" + h_pad="5" + v_pad="3" + bg_visible="true" bg_readonly_color="TextBgReadOnlyColor" bg_writeable_color="TextBgWriteableColor" - bg_focus_color="TextBgFocusColor" - link_color="HTMLLinkColor"> + bg_focus_color="TextBgFocusColor"> <simple_text_editor.border bevel_style="in" follows="all" /> diff --git a/indra/newview/skins/default/xui/en/widgets/text.xml b/indra/newview/skins/default/xui/en/widgets/text.xml index 3d98cd66f9..7d78a8fa20 100644 --- a/indra/newview/skins/default/xui/en/widgets/text.xml +++ b/indra/newview/skins/default/xui/en/widgets/text.xml @@ -1,16 +1,20 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<text name="text_box" +<text allow_html="true" + clip_to_rect="false" + name="text_box" font="SansSerifSmall" font_shadow="soft" tab_stop="false" halign="left" hover_color="LabelSelectedColor" - disabled_color="LabelDisabledColor" - background_color="FloaterDefaultBackgroundColor" + h_pad="-1" + hide_scrollbar="true" + text_readonly_color="LabelDisabledColor" + bg_writeable_color="FloaterDefaultBackgroundColor" border_color="DefaultHighlightLight" use_ellipses="false" bg_visible="false" - border_drop_shadow_visible="false" border_visible="false" hover="false" - text_color="LabelTextColor"/> + text_color="LabelTextColor" + v_pad="-1"/> diff --git a/indra/newview/skins/default/xui/en/widgets/text_editor.xml b/indra/newview/skins/default/xui/en/widgets/text_editor.xml index deaade04f8..23ca8ea338 100644 --- a/indra/newview/skins/default/xui/en/widgets/text_editor.xml +++ b/indra/newview/skins/default/xui/en/widgets/text_editor.xml @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <!-- Core parameters are in simple_text_editor.xml --> <text_editor - allow_html="false" /> + allow_html="false"/> diff --git a/indra/newview/skins/default/xui/en/widgets/textbase.xml b/indra/newview/skins/default/xui/en/widgets/textbase.xml new file mode 100644 index 0000000000..c352abca3b --- /dev/null +++ b/indra/newview/skins/default/xui/en/widgets/textbase.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<textbase clip_to_rect="true" + h_pad="4" + v_pad="4"/> diff --git a/indra/newview/skins/default/xui/nl/floater_telehub.xml b/indra/newview/skins/default/xui/nl/floater_telehub.xml index 5a8abc208a..8fe8e06c03 100644 --- a/indra/newview/skins/default/xui/nl/floater_telehub.xml +++ b/indra/newview/skins/default/xui/nl/floater_telehub.xml @@ -17,7 +17,7 @@ <text name="spawn_points_text"> Spawnpunten (posities, niet objecten): </text> - <scroll_list bottom_delta="-44" draw_border="true" follows="left|top" font="SansSerifSmall" + <scroll_list bottom_delta="-44" draw_border="true" follows="left|top" height="40" left="10" multi_select="false" name="spawn_points_list" width="230" /> diff --git a/indra/newview/tests/lldateutil_test.cpp b/indra/newview/tests/lldateutil_test.cpp new file mode 100644 index 0000000000..30e39a3bcf --- /dev/null +++ b/indra/newview/tests/lldateutil_test.cpp @@ -0,0 +1,159 @@ +/** + * @file lldateutil_test.cpp + * + * $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 "../test/lltut.h" + +#include "../lldateutil.h" + +#include "lldate.h" +#include "llstring.h" // LLStringUtil::format() +#include "lltrans.h" +#include "llui.h" + +#include <map> + + +// Baked-in return values for getString() +std::map< std::string, std::string > gString; + +// Baked-in return values for getCountString() +// map of pairs of input xml_desc and integer count +typedef std::pair< std::string, int > count_string_t; +std::map< count_string_t, std::string > gCountString; + +std::string LLTrans::getString(const std::string &xml_desc, const LLStringUtil::format_map_t& args) +{ + std::string text = gString[xml_desc]; + LLStringUtil::format(text, args); + return text; +} + +std::string LLTrans::getCountString(const std::string& language, const std::string& xml_desc, S32 count) +{ + return gCountString[ count_string_t(xml_desc, count) ]; +} + +std::string LLUI::getLanguage() +{ + return "en"; +} + +namespace tut +{ + struct dateutil + { + // Hard-code a "now" date so unit test doesn't change with + // current time. Because server strings are in Pacific time + // roll this forward 8 hours to compensate. This represents + // 2009-12-31T00:00:00Z UTC. + dateutil() + : mNow(std::string("2009-12-31T08:00:00Z")) + { + // copied from strings.xml + gString["YearsMonthsOld"] = "[AGEYEARS] [AGEMONTHS] old"; + gString["YearsOld"] = "[AGEYEARS] old"; + gString["MonthsOld"] = "[AGEMONTHS] old"; + gString["WeeksOld"] = "[AGEWEEKS] old"; + gString["DaysOld"] = "[AGEDAYS] old"; + gString["TodayOld"] = "Joined today"; + + gCountString[ count_string_t("AgeYears", 1) ] = "1 year"; + gCountString[ count_string_t("AgeYears", 2) ] = "2 years"; + gCountString[ count_string_t("AgeMonths", 1) ] = "1 month"; + gCountString[ count_string_t("AgeMonths", 2) ] = "2 months"; + gCountString[ count_string_t("AgeWeeks", 1) ] = "1 week"; + gCountString[ count_string_t("AgeWeeks", 2) ] = "2 weeks"; + gCountString[ count_string_t("AgeDays", 1) ] = "1 day"; + gCountString[ count_string_t("AgeDays", 2) ] = "2 days"; + } + LLDate mNow; + }; + + typedef test_group<dateutil> dateutil_t; + typedef dateutil_t::object dateutil_object_t; + tut::dateutil_t tut_dateutil("dateutil"); + + template<> template<> + void dateutil_object_t::test<1>() + { + set_test_name("Years"); + ensure_equals("years + months", + LLDateUtil::ageFromDate("10/30/2007", mNow), + "2 years 2 months old" ); + ensure_equals("years", + LLDateUtil::ageFromDate("12/31/2007", mNow), + "2 years old" ); + ensure_equals("single year", + LLDateUtil::ageFromDate("12/31/2008", mNow), + "1 year old" ); + ensure_equals("single year + a bit", + LLDateUtil::ageFromDate("12/12/2008", mNow), + "1 year old" ); + } + + template<> template<> + void dateutil_object_t::test<2>() + { + set_test_name("Months"); + ensure_equals("months", + LLDateUtil::ageFromDate("10/30/2009", mNow), + "2 months old" ); + ensure_equals("single month", + LLDateUtil::ageFromDate("11/30/2009", mNow), + "1 month old" ); + } + + template<> template<> + void dateutil_object_t::test<3>() + { + set_test_name("Weeks"); + ensure_equals("weeks", + LLDateUtil::ageFromDate("12/17/2009", mNow), + "2 weeks old" ); + ensure_equals("single week", + LLDateUtil::ageFromDate("12/24/2009", mNow), + "1 week old" ); + } + + template<> template<> + void dateutil_object_t::test<4>() + { + set_test_name("Days"); + ensure_equals("days", + LLDateUtil::ageFromDate("12/29/2009", mNow), + "2 days old" ); + ensure_equals("single day", + LLDateUtil::ageFromDate("12/30/2009", mNow), + "1 day old" ); + ensure_equals("today", + LLDateUtil::ageFromDate("12/31/2009", mNow), + "Joined today" ); + } +} diff --git a/indra/test/test.cpp b/indra/test/test.cpp index ba81c6e49e..2f50d872ee 100644 --- a/indra/test/test.cpp +++ b/indra/test/test.cpp @@ -136,12 +136,9 @@ public: run_completed_(*mStream); } run_completed_(std::cout); - - if (mFailedTests > 0) - { - exit(1); - } } + + int getFailedTests() const { return mFailedTests; } private: void run_completed_(std::ostream &stream) @@ -340,9 +337,11 @@ int main(int argc, char **argv) tut::runner.get().run_tests(test_group); } + bool success = (callback.getFailedTests() == 0); + if (wait_at_exit) { - std::cerr << "Waiting for input before exiting..." << std::endl; + std::cerr << "Press return to exit..." << std::endl; std::cin.get(); } @@ -352,7 +351,7 @@ int main(int argc, char **argv) delete output; } - if (touch) + if (touch && success) { std::ofstream s; s.open(touch); @@ -361,5 +360,7 @@ int main(int argc, char **argv) } apr_terminate(); - return 0; + + int retval = (success ? 0 : 1); + return retval; } |