diff options
24 files changed, 542 insertions, 74 deletions
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 772f173f17..b50ed2342d 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -34,6 +34,7 @@ set(llui_SOURCE_FILES llbadgeholder.cpp llbadgeowner.cpp llbutton.cpp + llchatentry.cpp llcheckboxctrl.cpp llclipboard.cpp llcombobox.cpp @@ -133,6 +134,7 @@ set(llui_HEADER_FILES llbadgeowner.h llbutton.h llcallbackmap.h + llchatentry.h llcheckboxctrl.h llclipboard.h llcombobox.h diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp index c025cd7939..43462bd244 100644 --- a/indra/llui/llaccordionctrltab.cpp +++ b/indra/llui/llaccordionctrltab.cpp @@ -184,7 +184,7 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitleFontStyle(std::string if (mHeaderTextbox) { std::string text = mHeaderTextbox->getText(); - mStyleParams.font(mHeaderTextbox->getDefaultFont()); + mStyleParams.font(mHeaderTextbox->getFont()); mStyleParams.font.style(style); mHeaderTextbox->setText(text, mStyleParams); } diff --git a/indra/llui/llchatentry.cpp b/indra/llui/llchatentry.cpp new file mode 100644 index 0000000000..1da7900f59 --- /dev/null +++ b/indra/llui/llchatentry.cpp @@ -0,0 +1,198 @@ +/** + * @file llchatentry.cpp + * @brief LLChatEntry implementation + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llchatentry.h" + +static LLDefaultChildRegistry::Register<LLChatEntry> r("text_editor"); + +LLChatEntry::Params::Params() +: has_history("has_history", true), + is_expandable("is_expandable", false), + expand_lines_count("expand_lines_count", 1) +{} + +LLChatEntry::LLChatEntry(const Params& p) +: LLTextEditor(p), + mTextExpandedSignal(NULL), + mHasHistory(p.has_history), + mIsExpandable(p.is_expandable), + mExpandLinesCount(p.expand_lines_count), + mPrevLinesCount(0) +{ + // Initialize current history line iterator + mCurrentHistoryLine = mLineHistory.begin(); + + mAutoIndent = false; +} + +LLChatEntry::~LLChatEntry() +{ + delete mTextExpandedSignal; +} + +void LLChatEntry::draw() +{ + LLTextEditor::draw(); + + if(mIsExpandable) + { + expandText(); + } +} + +void LLChatEntry::onCommit() +{ + updateHistory(); + LLTextEditor::onCommit(); +} + +boost::signals2::connection LLChatEntry::setTextExpandedCallback(const commit_signal_t::slot_type& cb) +{ + if (!mTextExpandedSignal) + { + mTextExpandedSignal = new commit_signal_t(); + } + return mTextExpandedSignal->connect(cb); +} + +void LLChatEntry::expandText() +{ + int visible_lines_count = llabs(getVisibleLines(true).first - getVisibleLines(true).second); + bool can_expand = getLineCount() <= mExpandLinesCount; + + // true if pasted text has more lines than expand height limit and expand limit is not reached yet + bool text_pasted = (getLineCount() > mExpandLinesCount) && (visible_lines_count < mExpandLinesCount); + + if (mIsExpandable && (can_expand || text_pasted) && getLineCount() != mPrevLinesCount) + { + int lines_height = 0; + if (text_pasted) + { + // text is pasted and now mLineInfoList.size() > mExpandLineCounts and mLineInfoList is not empty, + // so lines_height is the sum of the last 'mExpandLinesCount' lines height + lines_height = (mLineInfoList.end() - mExpandLinesCount)->mRect.mTop - mLineInfoList.back().mRect.mBottom; + } + else + { + lines_height = mLineInfoList.begin()->mRect.mTop - mLineInfoList.back().mRect.mBottom; + } + + int height = mVPad * 2 + lines_height; + + LLRect doc_rect = getRect(); + doc_rect.setOriginAndSize(doc_rect.mLeft, doc_rect.mBottom, doc_rect.getWidth(), height); + setShape(doc_rect); + + mPrevLinesCount = getLineCount(); + + if (mTextExpandedSignal) + { + (*mTextExpandedSignal)(this, LLSD() ); + } + } +} + +// line history support +void LLChatEntry::updateHistory() +{ + // On history enabled, remember committed line and + // reset current history line number. + // Be sure only to remember lines that are not empty and that are + // different from the last on the list. + if (mHasHistory && getLength()) + { + // Add text to history, ignoring duplicates + if (mLineHistory.empty() || getText() != mLineHistory.back()) + { + mLineHistory.push_back(getText()); + } + + mCurrentHistoryLine = mLineHistory.end(); + } +} + +BOOL LLChatEntry::handleSpecialKey(const KEY key, const MASK mask) +{ + BOOL handled = FALSE; + + LLTextEditor::handleSpecialKey(key, mask); + + switch(key) + { + case KEY_RETURN: + if (MASK_NONE == mask) + { + needsReflow(); + } + break; + + case KEY_UP: + if (mHasHistory && MASK_CONTROL == mask) + { + if (!mLineHistory.empty() && mCurrentHistoryLine > mLineHistory.begin()) + { + setText(*(--mCurrentHistoryLine)); + endOfDoc(); + } + else + { + LLUI::reportBadKeystroke(); + } + handled = TRUE; + } + break; + + case KEY_DOWN: + if (mHasHistory && MASK_CONTROL == mask) + { + if (!mLineHistory.empty() && ++mCurrentHistoryLine < mLineHistory.end()) + { + setText(*mCurrentHistoryLine); + endOfDoc(); + } + else if (!mLineHistory.empty() && mCurrentHistoryLine == mLineHistory.end()) + { + std::string empty(""); + setText(empty); + needsReflow(); + endOfDoc(); + } + else + { + LLUI::reportBadKeystroke(); + } + handled = TRUE; + } + break; + + default: + break; + } + + return handled; +} diff --git a/indra/llui/llchatentry.h b/indra/llui/llchatentry.h new file mode 100644 index 0000000000..10a4594e83 --- /dev/null +++ b/indra/llui/llchatentry.h @@ -0,0 +1,98 @@ +/** + * @file llchatentry.h + * @author Paul Guslisty + * @brief Text editor widget which is used for user input + * + * Features: + * Optional line history so previous entries can be recalled by CTRL UP/DOWN + * Optional auto-resize behavior on input chat field + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLCHATENTRY_H_ +#define LLCHATENTRY_H_ + +#include "lltexteditor.h" + +class LLChatEntry : public LLTextEditor +{ +public: + + struct Params : public LLInitParam::Block<Params, LLTextEditor::Params> + { + Optional<bool> has_history, + is_expandable; + + Optional<int> expand_lines_count; + + Params(); + }; + + virtual ~LLChatEntry(); + +protected: + + friend class LLUICtrlFactory; + LLChatEntry(const Params& p); + +public: + + virtual void draw(); + virtual void onCommit(); + + boost::signals2::connection setTextExpandedCallback(const commit_signal_t::slot_type& cb); + +private: + + /** + * Implements auto-resize behavior. + * When user's typing reaches the right edge of the chat field + * the chat field expands vertically by one line. The bottom of + * the chat field remains bottom-justified. The chat field does + * not expand beyond mExpandLinesCount. + */ + void expandText(); + + /** + * Implements line history so previous entries can be recalled by CTRL UP/DOWN + */ + void updateHistory(); + + BOOL handleSpecialKey(const KEY key, const MASK mask); + + + // Fired when text height expanded to mExpandLinesCount + commit_signal_t* mTextExpandedSignal; + + // line history support: + typedef std::vector<std::string> line_history_t; + line_history_t::iterator mCurrentHistoryLine; // currently browsed history line + line_history_t mLineHistory; // line history storage + bool mHasHistory; // flag for enabled/disabled line history + bool mIsExpandable; + + int mExpandLinesCount; + int mPrevLinesCount; +}; + +#endif /* LLCHATENTRY_H_ */ diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 7aeeae298f..c112a7e477 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -144,6 +144,7 @@ LLTextBase::Params::Params() : cursor_color("cursor_color"), text_color("text_color"), text_readonly_color("text_readonly_color"), + text_tentative_color("text_tentative_color"), bg_visible("bg_visible", false), border_visible("border_visible", false), bg_readonly_color("bg_readonly_color"), @@ -177,7 +178,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p) : LLUICtrl(p, LLTextViewModelPtr(new LLTextViewModel)), mURLClickSignal(NULL), mMaxTextByteLength( p.max_text_length ), - mDefaultFont(p.font), + mFont(p.font), mFontShadow(p.font_shadow), mPopupMenu(NULL), mReadOnly(p.read_only), @@ -185,6 +186,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p) mFgColor(p.text_color), mBorderVisible( p.border_visible ), mReadOnlyFgColor(p.text_readonly_color), + mTentativeFgColor(p.text_tentative_color()), mWriteableBgColor(p.bg_writeable_color), mReadOnlyBgColor(p.bg_readonly_color), mFocusBgColor(p.bg_focus_color), @@ -297,21 +299,21 @@ bool LLTextBase::truncate() return did_truncate; } -const LLStyle::Params& LLTextBase::getDefaultStyleParams() +const LLStyle::Params& LLTextBase::getStyleParams() { //FIXME: convert mDefaultStyle to a flyweight http://www.boost.org/doc/libs/1_40_0/libs/flyweight/doc/index.html //and eliminate color member values if (mStyleDirty) { - mDefaultStyle + mStyle .color(LLUIColor(&mFgColor)) // pass linked color instead of copy of mFGColor .readonly_color(LLUIColor(&mReadOnlyFgColor)) .selected_color(LLUIColor(&mTextSelectedColor)) - .font(mDefaultFont) + .font(mFont) .drop_shadow(mFontShadow); mStyleDirty = false; } - return mDefaultStyle; + return mStyle; } void LLTextBase::onValueChange(S32 start, S32 end) @@ -500,11 +502,17 @@ void LLTextBase::drawCursor() void LLTextBase::drawText() { - const S32 text_len = getLength(); - if( text_len <= 0 ) + S32 text_len = getLength(); + + if (text_len <= 0 && mLabel.empty()) { return; } + else if (text_len <= 0 && !mLabel.empty()) + { + text_len = mLabel.length(); + } + S32 selection_left = -1; S32 selection_right = -1; // Draw selection even if we don't have keyboard focus for search/replace @@ -624,7 +632,7 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s else { // create default editable segment to hold new text - LLStyleConstSP sp(new LLStyle(getDefaultStyleParams())); + LLStyleConstSP sp(new LLStyle(getStyleParams())); default_segment = new LLNormalTextSegment( sp, pos, pos + insert_len, *this); } @@ -749,7 +757,7 @@ void LLTextBase::createDefaultSegment() // ensures that there is always at least one segment if (mSegments.empty()) { - LLStyleConstSP sp(new LLStyle(getDefaultStyleParams())); + LLStyleConstSP sp(new LLStyle(getStyleParams())); LLTextSegmentPtr default_segment = new LLNormalTextSegment( sp, 0, getLength() + 1, *this); mSegments.insert(default_segment); default_segment->linkToDocument(this); @@ -1103,6 +1111,22 @@ void LLTextBase::deselect() mIsSelecting = FALSE; } +void LLTextBase::onFocusReceived() +{ + if (!getLength() && !mLabel.empty()) + { + // delete label which is LLLabelTextSegment + clearSegments(); + } +} + +void LLTextBase::onFocusLost() +{ + if (!getLength() && !mLabel.empty()) + { + resetLabel(); + } +} // Sets the scrollbar from the cursor position void LLTextBase::updateScrollFromCursor() @@ -1688,7 +1712,7 @@ static LLUIImagePtr image_from_icon_name(const std::string& icon_name) void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params) { LLStyle::Params style_params(input_params); - style_params.fillFrom(getDefaultStyleParams()); + style_params.fillFrom(getStyleParams()); S32 part = (S32)LLTextParser::WHOLE; if (mParseHTML && !style_params.is_link) // Don't search for URLs inside a link segment (STORM-358). @@ -1770,6 +1794,39 @@ void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, c appendTextImpl(new_text,input_params); } +void LLTextBase::setLabel(const LLStringExplicit& label) +{ + mLabel = label; + resetLabel(); +} + +BOOL LLTextBase::setLabelArg(const std::string& key, const LLStringExplicit& text ) +{ + mLabel.setArg(key, text); + return TRUE; +} + +void LLTextBase::resetLabel() +{ + if (!getLength() && !mLabel.empty() && !hasFocus()) + { + clearSegments(); + + LLStyle* style = new LLStyle(getStyleParams()); + style->setColor(mTentativeFgColor); + LLStyleConstSP sp(style); + + LLTextSegmentPtr label = new LLLabelTextSegment(sp, 0, getLabel().length() + 1, *this); + insertSegment(label); + } +} + +void LLTextBase::setFont(const LLFontGL* font) +{ + mFont = font; + mStyleDirty = true; +} + void LLTextBase::needsReflow(S32 index) { lldebugs << "reflow on object " << (void*)this << " index = " << mReflowIndex << ", new index = " << index << llendl; @@ -2160,7 +2217,7 @@ LLRect LLTextBase::getLocalRectFromDocIndex(S32 pos) const { // return default height rect in upper left local_rect = content_window_rect; - local_rect.mBottom = local_rect.mTop - mDefaultFont->getLineHeight(); + local_rect.mBottom = local_rect.mTop - mFont->getLineHeight(); return local_rect; } @@ -2665,7 +2722,7 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele { F32 alpha = LLViewDrawContext::getCurrentContext().mAlpha; - const LLWString &text = mEditor.getWText(); + const LLWString &text = getWText(); F32 right_x = rect.mLeft; if (!mStyle->isVisible()) @@ -2828,7 +2885,7 @@ bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& widt if (num_chars > 0) { height = mFontHeight; - const LLWString &text = mEditor.getWText(); + const LLWString &text = getWText(); // if last character is a newline, then return true, forcing line break width = mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars); } @@ -2837,7 +2894,7 @@ bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& widt S32 LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const { - const LLWString &text = mEditor.getWText(); + const LLWString &text = getWText(); return mStyle->getFont()->charFromPixelOffset(text.c_str(), mStart + start_offset, (F32)segment_local_x_coord, F32_MAX, @@ -2847,7 +2904,7 @@ S32 LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const { - const LLWString &text = mEditor.getWText(); + const LLWString &text = getWText(); LLUIImagePtr image = mStyle->getImage(); if( image.notNull()) @@ -2883,7 +2940,7 @@ S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin S32 last_char_in_run = mStart + segment_offset + num_chars; // check length first to avoid indexing off end of string if (last_char_in_run < mEnd - && (last_char_in_run >= mEditor.getLength() )) + && (last_char_in_run >= getLength())) { num_chars++; } @@ -2901,6 +2958,39 @@ void LLNormalTextSegment::dump() const llendl; } +/*virtual*/ +const LLWString& LLNormalTextSegment::getWText() const +{ + return mEditor.getWText(); +} + +/*virtual*/ +const S32 LLNormalTextSegment::getLength() const +{ + return mEditor.getLength(); +} + +LLLabelTextSegment::LLLabelTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ) +: LLNormalTextSegment(style, start, end, editor) +{ +} + +LLLabelTextSegment::LLLabelTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible) +: LLNormalTextSegment(color, start, end, editor, is_visible) +{ +} + +/*virtual*/ +const LLWString& LLLabelTextSegment::getWText() const +{ + return mEditor.getWlabel(); +} +/*virtual*/ +const S32 LLLabelTextSegment::getLength() const +{ + return mEditor.getLabel().length(); +} + // // LLOnHoverChangeableTextSegment // diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 0549141b72..412272b352 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -105,7 +105,7 @@ class LLNormalTextSegment : public LLTextSegment public: LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ); LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible = TRUE); - ~LLNormalTextSegment(); + virtual ~LLNormalTextSegment(); /*virtual*/ bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const; /*virtual*/ S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const; @@ -130,6 +130,9 @@ public: protected: F32 drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRect rect); + virtual const LLWString& getWText() const; + virtual const S32 getLength() const; + protected: class LLTextBase& mEditor; LLStyleConstSP mStyle; @@ -139,6 +142,21 @@ protected: boost::signals2::connection mImageLoadedConnection; }; +// This text segment is the same as LLNormalTextSegment, the only difference +// is that LLNormalTextSegment draws value of LLTextBase (LLTextBase::getWText()), +// but LLLabelTextSegment draws label of the LLTextBase (LLTextBase::mLabel) +class LLLabelTextSegment : public LLNormalTextSegment +{ +public: + LLLabelTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ); + LLLabelTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible = TRUE); + +protected: + + /*virtual*/ const LLWString& getWText() const; + /*virtual*/ const S32 getLength() const; +}; + // Text segment that changes it's style depending of mouse pointer position ( is it inside or outside segment) class LLOnHoverChangeableTextSegment : public LLNormalTextSegment { @@ -249,6 +267,7 @@ public: Optional<LLUIColor> cursor_color, text_color, text_readonly_color, + text_tentative_color, bg_readonly_color, bg_writeable_color, bg_focus_color, @@ -311,6 +330,9 @@ public: /*virtual*/ BOOL canDeselect() const; /*virtual*/ void deselect(); + virtual void onFocusReceived(); + virtual void onFocusLost(); + // used by LLTextSegment layout code bool getWordWrap() { return mWordWrap; } bool getUseEllipses() { return mUseEllipses; } @@ -330,6 +352,21 @@ public: const LLWString& getWText() const; void appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params = LLStyle::Params()); + + void setLabel(const LLStringExplicit& label); + virtual BOOL setLabelArg(const std::string& key, const LLStringExplicit& text ); + + const std::string& getLabel() { return mLabel.getString(); } + const LLWString& getWlabel() { return mLabel.getWString();} + + /** + * If label is set, draws text label (which is LLLabelTextSegment) + * that is visible when no user text provided and has no focus + */ + void resetLabel(); + + void setFont(const LLFontGL* font); + // force reflow of text void needsReflow(S32 index = 0); @@ -369,7 +406,7 @@ public: bool scrolledToStart(); bool scrolledToEnd(); - const LLFontGL* getDefaultFont() const { return mDefaultFont; } + const LLFontGL* getFont() const { return mFont; } virtual void appendLineBreakSegment(const LLStyle::Params& style_params); virtual void appendImageSegment(const LLStyle::Params& style_params); @@ -469,7 +506,7 @@ protected: void createDefaultSegment(); virtual void updateSegments(); void insertSegment(LLTextSegmentPtr segment_to_insert); - const LLStyle::Params& getDefaultStyleParams(); + const LLStyle::Params& getStyleParams(); // manage lines S32 getLineStart( S32 line ) const; @@ -514,15 +551,16 @@ protected: LLRect mTextBoundingRect; // default text style - LLStyle::Params mDefaultStyle; + LLStyle::Params mStyle; bool mStyleDirty; - const LLFontGL* const mDefaultFont; // font that is used when none specified, can only be set by constructor - const LLFontGL::ShadowType mFontShadow; // shadow style, can only be set by constructor + const LLFontGL* mFont; + const LLFontGL::ShadowType mFontShadow; // colors LLUIColor mCursorColor; LLUIColor mFgColor; LLUIColor mReadOnlyFgColor; + LLUIColor mTentativeFgColor; LLUIColor mWriteableBgColor; LLUIColor mReadOnlyBgColor; LLUIColor mFocusBgColor; @@ -558,6 +596,7 @@ protected: bool mClip; // clip text to widget rect bool mClipPartial; // false if we show lines that are partially inside bounding rect bool mPlainText; // didn't use Image or Icon segments + bool mAutoIndent; S32 mMaxTextByteLength; // Maximum length mText is allowed to be in bytes // support widgets @@ -573,6 +612,7 @@ protected: // Fired when a URL link is clicked commit_signal_t* mURLClickSignal; + LLUIString mLabel; // text label that is visible when no user text provided }; #endif diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 9720dded6c..4fa6ea085e 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -235,6 +235,7 @@ LLTextEditor::Params::Params() embedded_items("embedded_items", false), ignore_tab("ignore_tab", true), show_line_numbers("show_line_numbers", false), + auto_indent("auto_indent", true), default_color("default_color"), commit_on_focus_lost("commit_on_focus_lost", false), show_context_menu("show_context_menu") @@ -249,6 +250,7 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : mLastCmd( NULL ), mDefaultColor( p.default_color() ), mShowLineNumbers ( p.show_line_numbers ), + mAutoIndent(p.auto_indent), mCommitOnFocusLost( p.commit_on_focus_lost), mAllowEmbeddedItems( p.embedded_items ), mMouseDownX(0), @@ -256,7 +258,8 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : mTabsToNextField(p.ignore_tab), mPrevalidateFunc(p.prevalidate_callback()), mContextMenu(NULL), - mShowContextMenu(p.show_context_menu) + mShowContextMenu(p.show_context_menu), + mPassDelete(FALSE) { mSourceID.generate(); @@ -1606,7 +1609,10 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask) { deleteSelection(FALSE); } - autoIndent(); // TODO: make this optional + if (mAutoIndent) + { + autoIndent(); + } } else { @@ -1746,7 +1752,7 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char) // virtual BOOL LLTextEditor::canDoDelete() const { - return !mReadOnly && ( hasSelection() || (mCursorPos < getLength()) ); + return !mReadOnly && ( !mPassDelete || ( hasSelection() || (mCursorPos < getLength())) ); } void LLTextEditor::doDelete() @@ -1984,7 +1990,7 @@ void LLTextEditor::drawPreeditMarker() return; } - const S32 line_height = mDefaultFont->getLineHeight(); + const S32 line_height = mFont->getLineHeight(); S32 line_start = getLineStart(cur_line); S32 line_y = mVisibleTextRect.mTop - line_height; @@ -2023,16 +2029,16 @@ void LLTextEditor::drawPreeditMarker() S32 preedit_left = mVisibleTextRect.mLeft; if (left > line_start) { - preedit_left += mDefaultFont->getWidth(text, line_start, left - line_start); + preedit_left += mFont->getWidth(text, line_start, left - line_start); } S32 preedit_right = mVisibleTextRect.mLeft; if (right < line_end) { - preedit_right += mDefaultFont->getWidth(text, line_start, right - line_start); + preedit_right += mFont->getWidth(text, line_start, right - line_start); } else { - preedit_right += mDefaultFont->getWidth(text, line_start, line_end - line_start); + preedit_right += mFont->getWidth(text, line_start, line_end - line_start); } if (mPreeditStandouts[i]) @@ -2707,11 +2713,11 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect const LLWString textString(getWText()); const llwchar * const text = textString.c_str(); - const S32 line_height = mDefaultFont->getLineHeight(); + const S32 line_height = mFont->getLineHeight(); if (coord) { - const S32 query_x = mVisibleTextRect.mLeft + mDefaultFont->getWidth(text, current_line_start, query - current_line_start); + const S32 query_x = mVisibleTextRect.mLeft + mFont->getWidth(text, current_line_start, query - current_line_start); const S32 query_y = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height - line_height / 2; S32 query_screen_x, query_screen_y; localPointToScreen(query_x, query_y, &query_screen_x, &query_screen_y); @@ -2723,17 +2729,17 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect S32 preedit_left = mVisibleTextRect.mLeft; if (preedit_left_position > current_line_start) { - preedit_left += mDefaultFont->getWidth(text, current_line_start, preedit_left_position - current_line_start); + preedit_left += mFont->getWidth(text, current_line_start, preedit_left_position - current_line_start); } S32 preedit_right = mVisibleTextRect.mLeft; if (preedit_right_position < current_line_end) { - preedit_right += mDefaultFont->getWidth(text, current_line_start, preedit_right_position - current_line_start); + preedit_right += mFont->getWidth(text, current_line_start, preedit_right_position - current_line_start); } else { - preedit_right += mDefaultFont->getWidth(text, current_line_start, current_line_end - current_line_start); + preedit_right += mFont->getWidth(text, current_line_start, current_line_end - current_line_start); } const S32 preedit_top = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height; @@ -2810,7 +2816,7 @@ void LLTextEditor::markAsPreedit(S32 position, S32 length) S32 LLTextEditor::getPreeditFontSize() const { - return llround((F32)mDefaultFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]); + return llround((F32)mFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]); } BOOL LLTextEditor::isDirty() const diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 40821ae9fb..f8f636b876 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -64,7 +64,8 @@ public: ignore_tab, show_line_numbers, commit_on_focus_lost, - show_context_menu; + show_context_menu, + auto_indent; //colors Optional<LLUIColor> default_color; @@ -202,6 +203,8 @@ public: void setShowContextMenu(bool show) { mShowContextMenu = show; } bool getShowContextMenu() const { return mShowContextMenu; } + void setPassDelete(BOOL b) { mPassDelete = b; } + protected: void showContextMenu(S32 x, S32 y); void drawPreeditMarker(); @@ -214,8 +217,8 @@ protected: S32 indentLine( S32 pos, S32 spaces ); void unindentLineBeforeCloseBrace(); + virtual BOOL handleSpecialKey(const KEY key, const MASK mask); BOOL handleNavigationKey(const KEY key, const MASK mask); - BOOL handleSpecialKey(const KEY key, const MASK mask); BOOL handleSelectionKey(const KEY key, const MASK mask); BOOL handleControlKey(const KEY key, const MASK mask); @@ -279,6 +282,7 @@ protected: LLUIColor mDefaultColor; BOOL mShowLineNumbers; + bool mAutoIndent; /*virtual*/ void updateSegments(); void updateLinkSegments(); @@ -321,6 +325,7 @@ private: BOOL mAllowEmbeddedItems; bool mShowContextMenu; bool mParseOnTheFly; + bool mPassDelete; LLUUID mSourceID; diff --git a/indra/newview/llexpandabletextbox.cpp b/indra/newview/llexpandabletextbox.cpp index 935dcb74b0..a50184460b 100644 --- a/indra/newview/llexpandabletextbox.cpp +++ b/indra/newview/llexpandabletextbox.cpp @@ -150,7 +150,7 @@ void LLExpandableTextBox::LLTextBoxEx::showExpandText() std::pair<S32, S32> visible_lines = getVisibleLines(true); S32 last_line = visible_lines.second - 1; - LLStyle::Params expander_style(getDefaultStyleParams()); + LLStyle::Params expander_style(getStyleParams()); expander_style.font.style = "UNDERLINE"; expander_style.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); LLExpanderSegment* expanderp = new LLExpanderSegment(new LLStyle(expander_style), getLineStart(last_line), getLength() + 1, mExpanderLabel, *this); @@ -166,7 +166,7 @@ void LLExpandableTextBox::LLTextBoxEx::hideExpandText() if (mExpanderVisible) { // this will overwrite the expander segment and all text styling with a single style - LLStyleConstSP sp(new LLStyle(getDefaultStyleParams())); + LLStyleConstSP sp(new LLStyle(getStyleParams())); LLNormalTextSegment* segmentp = new LLNormalTextSegment(sp, 0, getLength() + 1, *this); insertSegment(segmentp); diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp index 2de891565c..aba3d74d87 100644 --- a/indra/newview/llgrouplist.cpp +++ b/indra/newview/llgrouplist.cpp @@ -406,7 +406,7 @@ void LLGroupListItem::setActive(bool active) // *BUG: setName() overrides the style params. // Active group should be bold. - LLFontDescriptor new_desc(mGroupNameBox->getDefaultFont()->getFontDesc()); + LLFontDescriptor new_desc(mGroupNameBox->getFont()->getFontDesc()); // *NOTE dzaporozhan // On Windows LLFontGL::NORMAL will not remove LLFontGL::BOLD if font diff --git a/indra/newview/llimconversation.cpp b/indra/newview/llimconversation.cpp index 3e2b208874..ead87e3ed5 100644 --- a/indra/newview/llimconversation.cpp +++ b/indra/newview/llimconversation.cpp @@ -236,6 +236,13 @@ void LLIMConversation::updateHeaderAndToolbar() mCloseBtn->setVisible(is_hosted); enableDisableCallBtn(); + + showTranslationCheckbox(); +} + +void LLIMConversation::showTranslationCheckbox(BOOL show) +{ + getChild<LLUICtrl>("translate_chat_checkbox_lp")->setVisible(mIsNearbyChat? show : FALSE); } // static diff --git a/indra/newview/llimconversation.h b/indra/newview/llimconversation.h index d31ae0808a..66401b9a3c 100644 --- a/indra/newview/llimconversation.h +++ b/indra/newview/llimconversation.h @@ -56,6 +56,9 @@ public: */ static bool isChatMultiTab(); + // show/hide the translation check box + void showTranslationCheckbox(const BOOL visible = FALSE); + // LLFloater overrides /*virtual*/ void onOpen(const LLSD& key); /*virtual*/ BOOL postBuild(); diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 14fcd602fc..882637151d 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -44,7 +44,7 @@ #include "llimfloatercontainer.h" // to replace separate IM Floaters with multifloater container #include "llinventoryfunctions.h" //#include "lllayoutstack.h" -#include "lllineeditor.h" +#include "llchatentry.h" #include "lllogchat.h" #include "llscreenchannel.h" #include "llsyswellwindow.h" @@ -64,6 +64,7 @@ LLIMFloater::LLIMFloater(const LLUUID& session_id) mLastMessageIndex(-1), mDialog(IM_NOTHING_SPECIAL), mInputEditor(NULL), + mInputEditorTopPad(0), mSavedTitle(), mTypingStart(), mShouldSendTypingState(false), @@ -200,7 +201,9 @@ void LLIMFloater::sendMsg() { if (mInputEditor) { - LLWString text = mInputEditor->getConvertedText(); + LLWString text = mInputEditor->getWText(); + LLWStringUtil::trim(text); + LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. if(!text.empty()) { // Truncate and convert to UTF8 for transport @@ -297,26 +300,26 @@ BOOL LLIMFloater::postBuild() { LLIMConversation::postBuild(); - mInputEditor = getChild<LLLineEditor>("chat_editor"); + mInputEditor = getChild<LLChatEntry>("chat_editor"); mInputEditor->setMaxTextLength(1023); // enable line history support for instant message bar - mInputEditor->setEnableLineHistory(TRUE); LLFontGL* font = LLViewerChat::getChatFont(); mInputEditor->setFont(font); mInputEditor->setFocusReceivedCallback( boost::bind(onInputEditorFocusReceived, _1, this) ); mInputEditor->setFocusLostCallback( boost::bind(onInputEditorFocusLost, _1, this) ); - mInputEditor->setKeystrokeCallback( onInputEditorKeystroke, this ); + mInputEditor->setKeystrokeCallback( boost::bind(onInputEditorKeystroke, _1, this) ); mInputEditor->setCommitOnFocusLost( FALSE ); - mInputEditor->setRevertOnEsc( FALSE ); - mInputEditor->setReplaceNewlinesWithSpaces( FALSE ); mInputEditor->setPassDelete( TRUE ); - childSetCommitCallback("chat_editor", onSendMsg, this); + mInputEditor->setCommitCallback(boost::bind(onSendMsg, _1, this)); + mInputEditor->setTextExpandedCallback(boost::bind(&LLIMFloater::reshapeChatHistory, this)); mChatHistory = getChild<LLChatHistory>("chat_history"); + mInputEditorTopPad = mChatHistory->getRect().mBottom - mInputEditor->getRect().mTop; + setDocked(true); mTypingStart = LLTrans::getString("IM_typing_start_string"); @@ -890,7 +893,7 @@ void LLIMFloater::onInputEditorFocusLost(LLFocusableElement* caller, void* userd } // static -void LLIMFloater::onInputEditorKeystroke(LLLineEditor* caller, void* userdata) +void LLIMFloater::onInputEditorKeystroke(LLTextEditor* caller, void* userdata) { LLIMFloater* self = (LLIMFloater*) userdata; std::string text = self->mInputEditor->getText(); @@ -1087,7 +1090,7 @@ private: BOOL LLIMFloater::inviteToSession(const uuid_vec_t& ids) { LLViewerRegion* region = gAgent.getRegion(); - bool is_region_exist = !!region; + bool is_region_exist = region != NULL; if (is_region_exist) { @@ -1168,6 +1171,17 @@ void LLIMFloater::removeTypingIndicator(const LLIMInfo* im_info) } } +void LLIMFloater::reshapeChatHistory() +{ + LLRect chat_rect = mChatHistory->getRect(); + LLRect input_rect = mInputEditor->getRect(); + + int delta_height = chat_rect.mBottom - (input_rect.mTop + mInputEditorTopPad); + + chat_rect.setLeftTopAndSize(chat_rect.mLeft, chat_rect.mTop, chat_rect.getWidth(), chat_rect.getHeight() + delta_height); + mChatHistory->setShape(chat_rect); +} + // static void LLIMFloater::closeHiddenIMToasts() { diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index 333340c696..1992bd930c 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -37,7 +37,9 @@ class LLAvatarName; class LLButton; -class LLLineEditor; +class LLChatEntry; +class LLTextEditor; +class LLPanelChatControlPanel; class LLChatHistory; class LLInventoryItem; class LLInventoryCategory; @@ -142,7 +144,7 @@ private: void appendMessage(const LLChat& chat, const LLSD &args = 0); static void onInputEditorFocusReceived( LLFocusableElement* caller,void* userdata ); static void onInputEditorFocusLost(LLFocusableElement* caller, void* userdata); - static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata); + static void onInputEditorKeystroke(LLTextEditor* caller, void* userdata); void setTyping(bool typing); void onAddButtonClicked(); void onAvatarPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName> names); @@ -161,6 +163,13 @@ private: // Remove the "User is typing..." indicator. void removeTypingIndicator(const LLIMInfo* im_info = NULL); + /** + * Adjusts chat history height to fit vertically with input chat field + * and avoid overlapping, since input chat field can be vertically expanded. + * Implementation: chat history bottom "follows" top+top_pad of input chat field + */ + void reshapeChatHistory(); + static void closeHiddenIMToasts(); static void confirmLeaveCallCallback(const LLSD& notification, const LLSD& response); @@ -171,9 +180,11 @@ private: LLChatHistory* mChatHistory; + int mInputEditorTopPad; // padding between input field and chat history + EInstantMessage mDialog; LLUUID mOtherParticipantUUID; - LLLineEditor* mInputEditor; + LLChatEntry* mInputEditor; bool mPositioned; std::string mSavedTitle; diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp index 04fb2f3b9a..10188febab 100644 --- a/indra/newview/llimfloatercontainer.cpp +++ b/indra/newview/llimfloatercontainer.cpp @@ -96,8 +96,7 @@ void LLIMFloaterContainer::onOpen(const LLSD& key) // If there's *no* conversation open so far, we force the opening of the nearby chat conversation // *TODO: find a way to move this to XML as a default panel or something like that LLSD name("chat_bar"); - LLSD key(""); - LLFloaterReg::toggleInstanceOrBringToFront(name,key); + LLFloaterReg::toggleInstanceOrBringToFront(name); } /* if (key.isDefined()) diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index fbaf451412..1b2d9b6801 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -130,6 +130,7 @@ LLNearbyChat::LLNearbyChat(const LLSD& key) mSpeakerMgr(NULL), mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT) { + mKey = LLSD(); mIsNearbyChat = true; mSpeakerMgr = LLLocalSpeakerMgr::getInstance(); } @@ -382,11 +383,6 @@ LLNearbyChat* LLNearbyChat::getInstance() return LLFloaterReg::getTypedInstance<LLNearbyChat>("chat_bar"); } -//static -//LLNearbyChat* LLNearbyChat::findInstance() -//{ -// return LLFloaterReg::findTypedInstance<LLNearbyChat>("chat_bar"); -//} void LLNearbyChat::showHistory() { @@ -397,10 +393,6 @@ void LLNearbyChat::showHistory() storeRectControl(); } -void LLNearbyChat::showTranslationCheckbox(BOOL show) -{ - getChild<LLUICtrl>("translate_chat_checkbox_lp")->setVisible(show); -} BOOL LLNearbyChat::tick() { diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index bc0e54de15..3dc94ce0da 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -87,7 +87,6 @@ public: static void sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate); void showHistory(); - void showTranslationCheckbox(BOOL show); protected: static BOOL matchChatTypeTrigger(const std::string& in_str, std::string* out_str); diff --git a/indra/newview/llpaneltopinfobar.cpp b/indra/newview/llpaneltopinfobar.cpp index 280cc11179..854deb00d0 100644 --- a/indra/newview/llpaneltopinfobar.cpp +++ b/indra/newview/llpaneltopinfobar.cpp @@ -230,7 +230,7 @@ void LLPanelTopInfoBar::buildLocationString(std::string& loc_str, bool show_coor void LLPanelTopInfoBar::setParcelInfoText(const std::string& new_text) { LLRect old_rect = getRect(); - const LLFontGL* font = mParcelInfoText->getDefaultFont(); + const LLFontGL* font = mParcelInfoText->getFont(); S32 new_text_width = font->getWidth(new_text); mParcelInfoText->setText(new_text); diff --git a/indra/newview/lltoastgroupnotifypanel.cpp b/indra/newview/lltoastgroupnotifypanel.cpp index 707d2d9765..ed350ea144 100644 --- a/indra/newview/lltoastgroupnotifypanel.cpp +++ b/indra/newview/lltoastgroupnotifypanel.cpp @@ -112,7 +112,7 @@ LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(const LLNotificationPtr& notifi style.font = date_font; pMessageText->appendText(timeStr + "\n", TRUE, style); - style.font = pMessageText->getDefaultFont(); + style.font = pMessageText->getFont(); pMessageText->appendText(message, TRUE, style); //attachment diff --git a/indra/newview/lltoastpanel.cpp b/indra/newview/lltoastpanel.cpp index e20d516392..09f8dcf83c 100644 --- a/indra/newview/lltoastpanel.cpp +++ b/indra/newview/lltoastpanel.cpp @@ -68,7 +68,7 @@ void LLToastPanel::snapToMessageHeight(LLTextBase* message, S32 maxLineCount) if (message->getVisible()) { S32 heightDelta = 0; - S32 maxTextHeight = message->getDefaultFont()->getLineHeight() * maxLineCount; + S32 maxTextHeight = message->getFont()->getLineHeight() * maxLineCount; LLRect messageRect = message->getRect(); S32 oldTextHeight = messageRect.getHeight(); diff --git a/indra/newview/lltoastscripttextbox.cpp b/indra/newview/lltoastscripttextbox.cpp index 2529ec865a..45fbabad59 100644 --- a/indra/newview/lltoastscripttextbox.cpp +++ b/indra/newview/lltoastscripttextbox.cpp @@ -65,7 +65,7 @@ LLToastScriptTextbox::LLToastScriptTextbox(const LLNotificationPtr& notification pMessageText->clear(); LLStyle::Params style; - style.font = pMessageText->getDefaultFont(); + style.font = pMessageText->getFont(); pMessageText->appendText(message, TRUE, style); //submit button diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 7f14e021fd..016da1d994 100755 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -2555,7 +2555,7 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) if ( gSavedSettings.getS32("LetterKeysFocusChatBar") && !gAgentCamera.cameraMouselook() && !keyboard_focus && key < 0x80 && (mask == MASK_NONE || mask == MASK_SHIFT) ) { - LLLineEditor* chat_editor = LLFloaterReg::getTypedInstance<LLNearbyChat>("chat_bar")->getChatBox(); + LLLineEditor* chat_editor = LLNearbyChat::getInstance()->getChatBox(); if (chat_editor) { // passing NULL here, character will be added later when it is handled by character handler. 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 a4695b8c09..f6d5b20a65 100644 --- a/indra/newview/skins/default/xui/en/floater_im_session.xml +++ b/indra/newview/skins/default/xui/en/floater_im_session.xml @@ -242,18 +242,21 @@ bottom="-1" follows="left|right|bottom" tab_group="1"> - <line_editor + <text_editor bottom="0" + expand_lines_count="5" follows="left|right|bottom" font="SansSerifSmall" visible="true" height="20" + is_expandable="true" label="To" layout="bottomleft" name="chat_editor" tab_group="3" - width="220"> - </line_editor> + width="240" + wrap="true"> + </text_editor> </panel> </layout_panel> </layout_stack> diff --git a/indra/newview/skins/default/xui/en/widgets/text.xml b/indra/newview/skins/default/xui/en/widgets/text.xml index 134f2d7522..2102074674 100644 --- a/indra/newview/skins/default/xui/en/widgets/text.xml +++ b/indra/newview/skins/default/xui/en/widgets/text.xml @@ -9,6 +9,7 @@ h_pad="0" allow_scroll="false" text_readonly_color="LabelTextColor" + text_tentative_color="TextFgTentativeColor" bg_writeable_color="FloaterDefaultBackgroundColor" use_ellipses="false" bg_visible="false" |