summaryrefslogtreecommitdiff
path: root/indra/llui
diff options
context:
space:
mode:
authorRichard Nelson <richard@lindenlab.com>2009-08-24 20:04:52 +0000
committerRichard Nelson <richard@lindenlab.com>2009-08-24 20:04:52 +0000
commit138bf1132262c479dbbd5c95195db46b1efd065f (patch)
treebe2286f245865008b4ca6d738194133542822d65 /indra/llui
parentc2619694fd2f94ad7da2d6e936494f4c16601212 (diff)
merge -r 130399-131510 skinning-21 -> viewer-2.0.0-3
DEV-11254 DEV-11254 DEV-2003: DEV-21567 DEV-37301 EXT-104 EXT-138 EXT-217 EXT-256 EXT-259 EXT-259 EXT-328 EXT-348 EXT-386 EXT-399 EXT-403 EXT-460 EXT-492 EXT-492 EXT-531 EXT-537 EXT-684 improved text editor (handles multiple fonts simultaneously as well as inline widgets)
Diffstat (limited to 'indra/llui')
-rw-r--r--indra/llui/llbutton.cpp25
-rw-r--r--indra/llui/llcombobox.cpp15
-rw-r--r--indra/llui/llcontainerview.cpp22
-rw-r--r--indra/llui/llfloater.cpp18
-rw-r--r--indra/llui/llkeywords.cpp31
-rw-r--r--indra/llui/llkeywords.h7
-rw-r--r--indra/llui/lllayoutstack.cpp2
-rw-r--r--indra/llui/lllineeditor.cpp14
-rw-r--r--indra/llui/llmenugl.cpp125
-rw-r--r--indra/llui/llmenugl.h23
-rw-r--r--indra/llui/llmodaldialog.cpp5
-rw-r--r--indra/llui/llmultifloater.cpp10
-rw-r--r--indra/llui/llpanel.cpp109
-rw-r--r--indra/llui/llpanel.h22
-rw-r--r--indra/llui/llradiogroup.cpp12
-rw-r--r--indra/llui/llradiogroup.h2
-rw-r--r--indra/llui/llresizehandle.h4
-rw-r--r--indra/llui/llscrollbar.cpp2
-rw-r--r--indra/llui/llscrollbar.h5
-rw-r--r--indra/llui/llscrollcontainer.cpp247
-rw-r--r--indra/llui/llscrollcontainer.h25
-rw-r--r--indra/llui/llscrolllistcell.cpp21
-rw-r--r--indra/llui/llscrolllistctrl.cpp47
-rw-r--r--indra/llui/llscrolllistctrl.h11
-rw-r--r--indra/llui/llstyle.cpp133
-rw-r--r--indra/llui/llstyle.h67
-rw-r--r--indra/llui/lltabcontainer.cpp36
-rw-r--r--indra/llui/lltabcontainer.h3
-rw-r--r--indra/llui/lltextbox.cpp4
-rw-r--r--indra/llui/lltexteditor.cpp2906
-rw-r--r--indra/llui/lltexteditor.h428
-rw-r--r--indra/llui/llui.cpp9
-rw-r--r--indra/llui/llui.h3
-rw-r--r--indra/llui/lluictrl.cpp30
-rw-r--r--indra/llui/lluictrl.h8
-rw-r--r--indra/llui/lluictrlfactory.cpp36
-rw-r--r--indra/llui/llview.cpp43
-rw-r--r--indra/llui/llview.h52
-rw-r--r--indra/llui/llviewborder.cpp4
39 files changed, 2427 insertions, 2139 deletions
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index c566282bef..b13b250c75 100644
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -419,8 +419,16 @@ BOOL LLButton::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
setFocus(TRUE);
}
- }
+// if (pointInView(x, y))
+// {
+// }
+ }
+ // send the mouse down signal
+ LLUICtrl::handleRightMouseDown(x,y,mask);
+ // *TODO: Return result of LLUICtrl call above? Should defer to base class
+ // but this might change the mouse handling of existing buttons in a bad way
+ // if they are not mouse opaque.
return TRUE;
}
@@ -432,15 +440,20 @@ BOOL LLButton::handleRightMouseUp(S32 x, S32 y, MASK mask)
// Always release the mouse
gFocusMgr.setMouseCapture( NULL );
- if (pointInView(x, y))
- {
- mRightClickSignal(this, x,y,mask);
- }
+// if (pointInView(x, y))
+// {
+// mRightMouseUpSignal(this, x,y,mask);
+// }
}
else
{
childrenHandleRightMouseUp(x, y, mask);
}
+ // send the mouse up signal
+ LLUICtrl::handleRightMouseUp(x,y,mask);
+ // *TODO: Return result of LLUICtrl call above? Should defer to base class
+ // but this might change the mouse handling of existing buttons in a bad way.
+ // if they are not mouse opaque.
return TRUE;
}
@@ -788,7 +801,7 @@ void LLButton::draw()
LLFontGL::NORMAL,
mDropShadowedText ? LLFontGL::DROP_SHADOW_SOFT : LLFontGL::NO_SHADOW,
S32_MAX, text_width,
- NULL, FALSE, mUseEllipses);
+ NULL, mUseEllipses);
}
LLUICtrl::draw();
diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp
index b3c3a2e698..ac56d15d1b 100644
--- a/indra/llui/llcombobox.cpp
+++ b/indra/llui/llcombobox.cpp
@@ -191,6 +191,7 @@ void LLComboBox::clear()
mButton->setLabelSelected(LLStringUtil::null);
mButton->setLabelUnselected(LLStringUtil::null);
mList->deselectAllItems();
+ mLastSelectedIndex = -1;
}
void LLComboBox::onCommit()
@@ -296,6 +297,7 @@ BOOL LLComboBox::setSimple(const LLStringExplicit& name)
if (found)
{
setLabel(name);
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
}
return found;
@@ -312,6 +314,7 @@ void LLComboBox::setValue(const LLSD& value)
{
setLabel( mList->getSelectedItemLabel() );
}
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
}
}
@@ -359,6 +362,7 @@ void LLComboBox::setLabel(const LLStringExplicit& name)
if (mList->selectItemByLabel(name, FALSE))
{
mTextEntry->setTentative(FALSE);
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
}
else
{
@@ -384,6 +388,7 @@ BOOL LLComboBox::remove(const std::string& name)
{
mList->deleteSingleItem(mList->getItemIndex(item));
}
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
}
return found;
@@ -436,6 +441,7 @@ BOOL LLComboBox::setCurrentByIndex( S32 index )
if (found)
{
setLabel(mList->getSelectedItemLabel());
+ mLastSelectedIndex = index;
}
return found;
}
@@ -607,9 +613,6 @@ void LLComboBox::showList()
mList->setVisible(TRUE);
setUseBoundingRect(TRUE);
-
- mList->sortItems();
- mLastSelectedIndex = mList->getFirstSelectedIndex();
}
void LLComboBox::hideList()
@@ -819,11 +822,13 @@ void LLComboBox::onTextEntry(LLLineEditor* line_editor)
if (mList->selectItemByLabel(line_editor->getText(), FALSE))
{
line_editor->setTentative(FALSE);
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
}
else
{
line_editor->setTentative(mTextEntryTentative);
mList->deselectAllItems();
+ mLastSelectedIndex = -1;
}
return;
}
@@ -890,6 +895,7 @@ void LLComboBox::updateSelection()
if (mList->selectItemByLabel(full_string, FALSE))
{
mTextEntry->setTentative(FALSE);
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
}
else if (mList->selectItemByPrefix(left_wstring, FALSE))
{
@@ -900,6 +906,7 @@ void LLComboBox::updateSelection()
mTextEntry->endSelection();
mTextEntry->setTentative(FALSE);
mHasAutocompletedText = TRUE;
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
}
else // no matching items found
{
@@ -907,6 +914,7 @@ void LLComboBox::updateSelection()
mTextEntry->setText(wstring_to_utf8str(user_wstring)); // removes text added by autocompletion
mTextEntry->setTentative(mTextEntryTentative);
mHasAutocompletedText = FALSE;
+ mLastSelectedIndex = -1;
}
}
@@ -994,6 +1002,7 @@ BOOL LLComboBox::setCurrentByID(const LLUUID& id)
if (found)
{
setLabel(mList->getSelectedItemLabel());
+ mLastSelectedIndex = mList->getFirstSelectedIndex();
}
return found;
diff --git a/indra/llui/llcontainerview.cpp b/indra/llui/llcontainerview.cpp
index 7e7d6ac111..51ef80f4b9 100644
--- a/indra/llui/llcontainerview.cpp
+++ b/indra/llui/llcontainerview.cpp
@@ -132,35 +132,31 @@ void LLContainerView::draw()
void LLContainerView::reshape(S32 width, S32 height, BOOL called_from_parent)
{
- S32 desired_width = width;
- S32 desired_height = height;
+ LLRect scroller_rect;
+ scroller_rect.setOriginAndSize(0, 0, width, height);
if (mScrollContainer)
{
- BOOL dum_bool;
- mScrollContainer->calcVisibleSize(&desired_width, &desired_height, &dum_bool, &dum_bool);
+ scroller_rect = mScrollContainer->getContentWindowRect();
}
else
{
// if we're uncontained - make height as small as possible
- desired_height = 0;
+ scroller_rect.mTop = 0;
}
- arrange(desired_width, desired_height, called_from_parent);
+ arrange(scroller_rect.getWidth(), scroller_rect.getHeight(), called_from_parent);
// sometimes, after layout, our container will change size (scrollbars popping in and out)
// if so, attempt another layout
if (mScrollContainer)
{
- S32 new_container_width;
- S32 new_container_height;
- BOOL dum_bool;
- mScrollContainer->calcVisibleSize(&new_container_width, &new_container_height, &dum_bool, &dum_bool);
+ LLRect new_container_rect = mScrollContainer->getContentWindowRect();
- if ((new_container_width != desired_width) ||
- (new_container_height != desired_height)) // the container size has changed, attempt to arrange again
+ if ((new_container_rect.getWidth() != scroller_rect.getWidth()) ||
+ (new_container_rect.getHeight() != scroller_rect.getHeight())) // the container size has changed, attempt to arrange again
{
- arrange(new_container_width, new_container_height, called_from_parent);
+ arrange(new_container_rect.getWidth(), new_container_rect.getHeight(), called_from_parent);
}
}
}
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index d420d1141e..2679143bbc 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -711,10 +711,7 @@ void LLFloater::releaseFocus()
gFocusMgr.setTopCtrl(NULL);
}
- if( gFocusMgr.childHasKeyboardFocus( this ) )
- {
- gFocusMgr.setKeyboardFocus(NULL);
- }
+ setFocus(FALSE);
if( gFocusMgr.childHasMouseCapture( this ) )
{
@@ -1074,7 +1071,7 @@ void LLFloater::setFocus( BOOL b )
}
LLUICtrl* last_focus = gFocusMgr.getLastFocusForGroup(this);
// a descendent already has focus
- BOOL child_had_focus = gFocusMgr.childHasKeyboardFocus(this);
+ BOOL child_had_focus = hasFocus();
// give focus to first valid descendent
LLPanel::setFocus(b);
@@ -1948,7 +1945,7 @@ LLRect LLFloaterView::findNeighboringPosition( LLFloater* reference_floater, LLF
if (sibling &&
sibling != neighbor &&
sibling->getVisible() &&
- expanded_base_rect.rectInRect(&sibling->getRect()))
+ expanded_base_rect.overlaps(sibling->getRect()))
{
base_rect.unionWith(sibling->getRect());
}
@@ -2593,6 +2590,8 @@ void LLFloater::initFromParams(const LLFloater::Params& p)
initCommitCallback(p.close_callback, mCloseSignal);
}
+LLFastTimer::DeclareTimer POST_BUILD("Floater Post Build");
+
void LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node)
{
Params params(LLUICtrlFactory::getDefaultParams<LLFloater>());
@@ -2626,7 +2625,12 @@ void LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr o
LLFloater::setFloaterHost(last_host);
}
- BOOL result = postBuild();
+ BOOL result;
+ {
+ LLFastTimer ft(POST_BUILD);
+
+ result = postBuild();
+ }
if (!result)
{
diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp
index 30796a5ab9..db1611abb5 100644
--- a/indra/llui/llkeywords.cpp
+++ b/indra/llui/llkeywords.cpp
@@ -231,11 +231,13 @@ LLColor3 LLKeywords::readColor( const std::string& s )
return LLColor3( r, g, b );
}
+LLFastTimer::DeclareTimer FTM_SYNTAX_COLORING("Syntax Coloring");
+
// Walk through a string, applying the rules specified by the keyword token list and
// create a list of color segments.
-void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWString& wtext, const LLColor4 &defaultColor)
+void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLWString& wtext, const LLColor4 &defaultColor, LLTextEditor& editor)
{
- std::for_each(seg_list->begin(), seg_list->end(), DeletePointer());
+ LLFastTimer ft(FTM_SYNTAX_COLORING);
seg_list->clear();
if( wtext.empty() )
@@ -245,7 +247,7 @@ void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWS
S32 text_len = wtext.size();
- seg_list->push_back( new LLTextSegment( LLColor3(defaultColor), 0, text_len ) );
+ seg_list->push_back( new LLNormalTextSegment( defaultColor, 0, text_len, editor ) );
const llwchar* base = wtext.c_str();
const llwchar* cur = base;
@@ -296,9 +298,9 @@ void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWS
}
S32 seg_end = cur - base;
- LLTextSegment* text_segment = new LLTextSegment( cur_token->getColor(), seg_start, seg_end );
+ LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_token->getColor(), seg_start, seg_end, editor );
text_segment->setToken( cur_token );
- insertSegment( seg_list, text_segment, text_len, defaultColor);
+ insertSegment( seg_list, text_segment, text_len, defaultColor, editor);
line_done = TRUE; // to break out of second loop.
break;
}
@@ -405,9 +407,9 @@ void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWS
}
- LLTextSegment* text_segment = new LLTextSegment( cur_delimiter->getColor(), seg_start, seg_end );
+ LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_delimiter->getColor(), seg_start, seg_end, editor );
text_segment->setToken( cur_delimiter );
- insertSegment( seg_list, text_segment, text_len, defaultColor);
+ insertSegment( seg_list, text_segment, text_len, defaultColor, editor);
// Note: we don't increment cur, since the end of one delimited seg may be immediately
// followed by the start of another one.
@@ -438,9 +440,9 @@ void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWS
// llinfos << "Seg: [" << word.c_str() << "]" << llendl;
- LLTextSegment* text_segment = new LLTextSegment( cur_token->getColor(), seg_start, seg_end );
+ LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_token->getColor(), seg_start, seg_end, editor );
text_segment->setToken( cur_token );
- insertSegment( seg_list, text_segment, text_len, defaultColor);
+ insertSegment( seg_list, text_segment, text_len, defaultColor, editor);
}
cur += seg_len;
continue;
@@ -455,25 +457,24 @@ void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWS
}
}
-void LLKeywords::insertSegment(std::vector<LLTextSegment*>* seg_list, LLTextSegment* new_segment, S32 text_len, const LLColor4 &defaultColor )
+void LLKeywords::insertSegment(std::vector<LLTextSegmentPtr>* seg_list, LLTextSegmentPtr new_segment, S32 text_len, const LLColor4 &defaultColor, LLTextEditor& editor )
{
- LLTextSegment* last = seg_list->back();
+ LLTextSegmentPtr last = seg_list->back();
S32 new_seg_end = new_segment->getEnd();
if( new_segment->getStart() == last->getStart() )
{
- *last = *new_segment;
- delete new_segment;
+ seg_list->pop_back();
}
else
{
last->setEnd( new_segment->getStart() );
- seg_list->push_back( new_segment );
}
+ seg_list->push_back( new_segment );
if( new_seg_end < text_len )
{
- seg_list->push_back( new LLTextSegment( defaultColor, new_seg_end, text_len ) );
+ seg_list->push_back( new LLNormalTextSegment( defaultColor, new_seg_end, text_len, editor ) );
}
}
diff --git a/indra/llui/llkeywords.h b/indra/llui/llkeywords.h
index 38f5e993c2..53377869ca 100644
--- a/indra/llui/llkeywords.h
+++ b/indra/llui/llkeywords.h
@@ -39,9 +39,10 @@
#include <map>
#include <list>
#include <deque>
+#include "llpointer.h"
class LLTextSegment;
-
+typedef LLPointer<LLTextSegment> LLTextSegmentPtr;
class LLKeywordToken
{
@@ -84,7 +85,7 @@ public:
BOOL loadFromFile(const std::string& filename);
BOOL isLoaded() const { return mLoaded; }
- void findSegments(std::vector<LLTextSegment *> *seg_list, const LLWString& text, const LLColor4 &defaultColor );
+ void findSegments(std::vector<LLTextSegmentPtr> *seg_list, const LLWString& text, const LLColor4 &defaultColor, class LLTextEditor& editor );
// Add the token as described
void addToken(LLKeywordToken::TOKEN_TYPE type,
@@ -103,7 +104,7 @@ public:
private:
LLColor3 readColor(const std::string& s);
- void insertSegment(std::vector<LLTextSegment *> *seg_list, LLTextSegment* new_segment, S32 text_len, const LLColor4 &defaultColor);
+ void insertSegment(std::vector<LLTextSegmentPtr> *seg_list, LLTextSegmentPtr new_segment, S32 text_len, const LLColor4 &defaultColor, class LLTextEditor& editor);
BOOL mLoaded;
word_token_map_t mWordTokenMap;
diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp
index 702d8e4a39..f98edec1f3 100644
--- a/indra/llui/lllayoutstack.cpp
+++ b/indra/llui/lllayoutstack.cpp
@@ -167,7 +167,7 @@ void LLLayoutStack::draw()
LLLocalClipRect clip(clip_rect, mClip);
// only force drawing invisible children if visible amount is non-zero
- drawChild(panelp, 0, 0, !clip_rect.isNull());
+ drawChild(panelp, 0, 0, !clip_rect.isEmpty());
}
}
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index 42a1b9ad9c..6926415c6d 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -494,19 +494,19 @@ BOOL LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
BOOL doSelectAll = TRUE;
// Select the word we're on
- if( LLTextEditor::isPartOfWord( wtext[mCursorPos] ) )
+ if( LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) )
{
S32 old_selection_start = mLastSelectionStart;
S32 old_selection_end = mLastSelectionEnd;
// Select word the cursor is over
- while ((mCursorPos > 0) && LLTextEditor::isPartOfWord( wtext[mCursorPos-1] ))
+ while ((mCursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[mCursorPos-1] ))
{ // Find the start of the word
mCursorPos--;
}
startSelection();
- while ((mCursorPos < (S32)wtext.length()) && LLTextEditor::isPartOfWord( wtext[mCursorPos] ) )
+ while ((mCursorPos < (S32)wtext.length()) && LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) )
{ // Find the end of the word
mCursorPos++;
}
@@ -840,7 +840,7 @@ S32 LLLineEditor::prevWordPos(S32 cursorPos) const
{
cursorPos--;
}
- while( (cursorPos > 0) && LLTextEditor::isPartOfWord( wtext[cursorPos-1] ) )
+ while( (cursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[cursorPos-1] ) )
{
cursorPos--;
}
@@ -850,7 +850,7 @@ S32 LLLineEditor::prevWordPos(S32 cursorPos) const
S32 LLLineEditor::nextWordPos(S32 cursorPos) const
{
const LLWString& wtext = mText.getWString();
- while( (cursorPos < getLength()) && LLTextEditor::isPartOfWord( wtext[cursorPos] ) )
+ while( (cursorPos < getLength()) && LLWStringUtil::isPartOfWord( wtext[cursorPos] ) )
{
cursorPos++;
}
@@ -1732,11 +1732,11 @@ void LLLineEditor::draw()
#endif
// If we're editing...
- if( gFocusMgr.getKeyboardFocus() == this)
+ if( hasFocus())
{
//mBorder->setVisible(TRUE); // ok, programmer art just this once.
// (Flash the cursor every half second)
- if (gShowTextEditCursor && !mReadOnly)
+ if (!mReadOnly && gFocusMgr.getAppHasFocus())
{
F32 elapsed = mKeystrokeTimer.getElapsedTimeF32();
if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) )
diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp
index 5be53e6afc..b6c3ffc61c 100644
--- a/indra/llui/llmenugl.cpp
+++ b/indra/llui/llmenugl.cpp
@@ -184,6 +184,13 @@ LLMenuItemGL::LLMenuItemGL(const LLMenuItemGL::Params& p)
<< LL_ENDL;
}
+//virtual
+void LLMenuItemGL::setValue(const LLSD& value)
+{
+ setLabel(value.asString());
+}
+
+//virtual
BOOL LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask)
{
if( getEnabled() && (!gKeyboard->getKeyRepeated(key) || mAllowKeyRepeat) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) )
@@ -201,6 +208,26 @@ BOOL LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask)
return TRUE;
}
+//virtual
+BOOL LLMenuItemGL::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ return LLUICtrl::handleRightMouseDown(x,y,mask);
+}
+
+//virtual
+BOOL LLMenuItemGL::handleRightMouseUp(S32 x, S32 y, MASK mask)
+{
+ // If this event came from a right-click context menu spawn,
+ // process as a left-click to allow menu items to be hit
+ if (LLMenuHolderGL::sContextMenuSpawnPos.mX != S32_MAX
+ || LLMenuHolderGL::sContextMenuSpawnPos.mY != S32_MAX)
+ {
+ BOOL handled = handleMouseUp(x, y, mask);
+ return handled;
+ }
+ return LLUICtrl::handleRightMouseUp(x,y,mask);
+}
+
// This function checks to see if the accelerator key is already in use;
// if not, it will be added to the list
BOOL LLMenuItemGL::addToAcceleratorList(std::list <LLKeyBinding*> *listp)
@@ -306,6 +333,17 @@ U32 LLMenuItemGL::getNominalHeight( void ) const
return llround(mFont->getLineHeight()) + MENU_ITEM_PADDING;
}
+//virtual
+void LLMenuItemGL::setBriefItem(BOOL brief)
+{
+ mBriefItem = brief;
+}
+
+//virtual
+BOOL LLMenuItemGL::isBriefItem() const
+{
+ return mBriefItem;
+}
// Get the parent menu for this item
LLMenuGL* LLMenuItemGL::getMenu() const
@@ -800,15 +838,8 @@ BOOL LLMenuItemCallGL::handleAcceleratorKey( KEY key, MASK mask )
return FALSE;
}
-BOOL LLMenuItemCallGL::handleRightMouseUp(S32 x, S32 y, MASK mask)
-{
- if (pointInView(x, y))
- {
- mRightClickSignal(this,x,y, mask);
- }
-
- return TRUE;
-}
+// handleRightMouseUp moved into base class LLMenuItemGL so clicks are
+// handled for all menu item types
///============================================================================
/// Class LLMenuItemCheckGL
@@ -898,24 +929,38 @@ LLMenuItemBranchGL::~LLMenuItemBranchGL()
}
// virtual
-LLView* LLMenuItemBranchGL::getChildView(const std::string& name, BOOL recurse, BOOL create_if_missing) const
+LLView* LLMenuItemBranchGL::getChildView(const std::string& name, BOOL recurse) const
{
LLMenuGL* branch = getBranch();
- if (!branch)
- return LLView::getChildView(name, recurse, create_if_missing);
-
- // richard: this is redundant with parent, remove
- if (branch->getName() == name)
+ if (branch)
{
- return branch;
+ if (branch->getName() == name)
+ {
+ return branch;
+ }
+
+ // Always recurse on branches
+ return branch->getChildView(name, recurse);
}
- // Always recurse on branches
- LLView* child = branch->getChildView(name, recurse, FALSE);
- if (!child)
+
+ return LLView::getChildView(name, recurse);
+}
+
+LLView* LLMenuItemBranchGL::findChildView(const std::string& name, BOOL recurse) const
+{
+ LLMenuGL* branch = getBranch();
+ if (branch)
{
- child = LLView::getChildView(name, recurse, create_if_missing);
+ if (branch->getName() == name)
+ {
+ return branch;
+ }
+
+ // Always recurse on branches
+ return branch->findChildView(name, recurse);
}
- return child;
+
+ return LLView::findChildView(name, recurse);
}
// virtual
@@ -1123,6 +1168,18 @@ BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask )
return LLMenuItemGL::handleKeyHere(key, mask);
}
+//virtual
+BOOL LLMenuItemBranchGL::isActive() const
+{
+ return isOpen() && getBranch() && getBranch()->getHighlightedItem();
+}
+
+//virtual
+BOOL LLMenuItemBranchGL::isOpen() const
+{
+ return getBranch() && getBranch()->isOpen();
+}
+
void LLMenuItemBranchGL::openMenu()
{
LLMenuGL* branch = getBranch();
@@ -1298,6 +1355,9 @@ void LLMenuItemBranchDownGL::setHighlight( BOOL highlight )
{
if (highlight == getHighlight()) return;
+ LLMenuGL* branch = getBranch();
+ if (!branch) return;
+
//NOTE: Purposely calling all the way to the base to bypass auto-open.
LLMenuItemGL::setHighlight(highlight);
if( !highlight)
@@ -2803,9 +2863,9 @@ void LLMenuGL::setVisible(BOOL visible)
}
}
-LLMenuGL* LLMenuGL::getChildMenuByName(const std::string& name, BOOL recurse) const
+LLMenuGL* LLMenuGL::findChildMenuByName(const std::string& name, BOOL recurse) const
{
- LLView* view = getChildView(name, recurse, FALSE);
+ LLView* view = findChildView(name, recurse);
if (view)
{
LLMenuItemBranchGL* branch = dynamic_cast<LLMenuItemBranchGL*>(view);
@@ -2844,9 +2904,19 @@ void hide_top_view( LLView* view )
}
+// x and y are the desired location for the popup, NOT necessarily the
+// mouse location
// static
void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y)
{
+ // 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;
+ LLUI::getCursorPositionLocal(menu->getParent(), &mouse_x, &mouse_y);
+ LLMenuHolderGL::sContextMenuSpawnPos.set(mouse_x,mouse_y);
+
const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect();
const S32 HPAD = 2;
@@ -2857,9 +2927,6 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y)
spawning_view->localPointToOtherView(left, top, &left, &top, menu->getParent());
rect.setLeftTopAndSize( left, top,
rect.getWidth(), rect.getHeight() );
-
-
- //rect.setLeftTopAndSize(x + HPAD, y, rect.getWidth(), rect.getHeight());
menu->setRect( rect );
// Resetting scrolling position
@@ -2871,18 +2938,16 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y)
menu->arrangeAndClear(); // Fix menu rect if needed.
rect = menu->getRect();
+ // Adjust context menu to fit onscreen
S32 bottom;
left = rect.mLeft;
bottom = rect.mBottom;
- //menu->getParent()->localPointToScreen( rect.mLeft, rect.mBottom,
- // &left, &bottom );
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 = menu_region_rect.mBottom - bottom;
delta_y = (rect.getHeight() + 2 * HPAD);
}
@@ -2890,7 +2955,6 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y)
{
// At this point, we need to move the context menu to the
// other side of the mouse.
- //delta_x = (window_width - rect.getWidth()) - x;
delta_x = -(rect.getWidth() + 2 * HPAD);
}
menu->translate( delta_x, delta_y );
@@ -3633,6 +3697,7 @@ void LLContextMenu::show(S32 x, S32 y)
// Save click point for detecting cursor moves before mouse-up.
// Must be in local coords to compare with mouseUp events.
// If the mouse doesn't move, the menu will stay open ala the Mac.
+ // See also LLMenuGL::showPopup()
LLMenuHolderGL::sContextMenuSpawnPos.set(x,y);
arrangeAndClear();
diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h
index d39a02da28..2e7f61c2dd 100644
--- a/indra/llui/llmenugl.h
+++ b/indra/llui/llmenugl.h
@@ -105,10 +105,15 @@ protected:
LLMenuItemGL(const Params&);
friend class LLUICtrlFactory;
public:
- virtual void setValue(const LLSD& value) { setLabel(value.asString()); }
+ // LLView overrides
/*virtual*/ void handleVisibilityChange(BOOL new_visibility);
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask);
+
+ // LLUICtrl overrides
+ /*virtual*/ void setValue(const LLSD& value);
- virtual BOOL handleHover(S32 x, S32 y, MASK mask);
virtual BOOL handleAcceleratorKey(KEY key, MASK mask);
LLColor4 getHighlightBgColor() { return mHighlightBackground.get(); }
@@ -124,8 +129,8 @@ public:
virtual U32 getNominalHeight( void ) const;
// Marks item as not needing space for check marks or accelerator keys
- virtual void setBriefItem(BOOL brief) { mBriefItem = brief; }
- virtual BOOL isBriefItem() const { return mBriefItem; }
+ virtual void setBriefItem(BOOL brief);
+ virtual BOOL isBriefItem() const;
virtual BOOL addToAcceleratorList(std::list<LLKeyBinding*> *listp);
void setAllowKeyRepeat(BOOL allow) { mAllowKeyRepeat = allow; }
@@ -283,7 +288,6 @@ public:
virtual BOOL handleAcceleratorKey(KEY key, MASK mask);
virtual BOOL handleKeyHere(KEY key, MASK mask);
- virtual BOOL handleRightMouseUp(S32 x, S32 y, MASK mask);
//virtual void draw();
@@ -427,7 +431,7 @@ public:
virtual BOOL handleAcceleratorKey(KEY key, MASK mask);
- LLMenuGL* getChildMenuByName(const std::string& name, BOOL recurse) const;
+ LLMenuGL* findChildMenuByName(const std::string& name, BOOL recurse) const;
BOOL clearHoverItem();
@@ -610,9 +614,9 @@ public:
virtual BOOL handleKeyHere(KEY key, MASK mask);
- virtual BOOL isActive() const { return isOpen() && getBranch() && getBranch()->getHighlightedItem(); }
+ virtual BOOL isActive() const;
- virtual BOOL isOpen() const { return getBranch() && getBranch()->isOpen(); }
+ virtual BOOL isOpen() const;
LLMenuGL* getBranch() const { return (LLMenuGL*)mBranchHandle.get(); }
@@ -627,7 +631,8 @@ public:
virtual void openMenu();
- virtual LLView* getChildView(const std::string& name, BOOL recurse = TRUE, BOOL create_if_missing = TRUE) const;
+ virtual LLView* getChildView(const std::string& name, BOOL recurse = TRUE) const;
+ virtual LLView* findChildView(const std::string& name, BOOL recurse = TRUE) const;
private:
LLHandle<LLView> mBranchHandle;
diff --git a/indra/llui/llmodaldialog.cpp b/indra/llui/llmodaldialog.cpp
index 11fa290de1..c8162fe466 100644
--- a/indra/llui/llmodaldialog.cpp
+++ b/indra/llui/llmodaldialog.cpp
@@ -279,10 +279,7 @@ void LLModalDialog::onAppFocusLost()
gFocusMgr.setMouseCapture( NULL );
}
- if( gFocusMgr.childHasKeyboardFocus( instance ) )
- {
- gFocusMgr.setKeyboardFocus( NULL );
- }
+ instance->setFocus(FALSE);
}
}
diff --git a/indra/llui/llmultifloater.cpp b/indra/llui/llmultifloater.cpp
index 9f9e3aecac..e8ce1a8d97 100644
--- a/indra/llui/llmultifloater.cpp
+++ b/indra/llui/llmultifloater.cpp
@@ -454,14 +454,8 @@ BOOL LLMultiFloater::postBuild()
return TRUE;
}
- requires<LLTabContainer>("Preview Tabs");
- if (checkRequirements())
- {
- mTabContainer = getChild<LLTabContainer>("Preview Tabs");
- return TRUE;
- }
-
- return FALSE;
+ mTabContainer = getChild<LLTabContainer>("Preview Tabs");
+ return TRUE;
}
void LLMultiFloater::updateResizeLimits()
diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp
index da55ababab..667a3e10c4 100644
--- a/indra/llui/llpanel.cpp
+++ b/indra/llui/llpanel.cpp
@@ -192,10 +192,6 @@ void LLPanel::draw()
void LLPanel::updateDefaultBtn()
{
- // This method does not call LLView::draw() so callers will need
- // to take care of that themselves at the appropriate place in
- // their rendering sequence
-
if( mDefaultBtn)
{
if (gFocusMgr.childHasKeyboardFocus( this ) && mDefaultBtn->getEnabled())
@@ -254,7 +250,7 @@ BOOL LLPanel::handleKeyHere( KEY key, MASK mask )
// handle user hitting ESC to defocus
if (key == KEY_ESCAPE)
{
- gFocusMgr.setKeyboardFocus(NULL);
+ setFocus(FALSE);
return TRUE;
}
else if( (mask == MASK_SHIFT) && (KEY_TAB == key))
@@ -315,53 +311,18 @@ void LLPanel::handleVisibilityChange ( BOOL new_visibility )
mVisibleSignal(this, LLSD(new_visibility) ); // Pass BOOL as LLSD
}
-BOOL LLPanel::checkRequirements()
-{
- if (!mRequirementsError.empty())
- {
- LLSD args;
- args["COMPONENTS"] = mRequirementsError;
- args["FLOATER"] = getName();
-
- llwarns << getName() << " failed requirements check on: \n"
- << mRequirementsError << llendl;
-
- LLNotifications::instance().add(LLNotification::Params("FailedRequirementsCheck").payload(args));
- mRequirementsError.clear();
- return FALSE;
- }
-
- return TRUE;
-}
-
void LLPanel::setFocus(BOOL b)
{
- if( b )
+ if( b && !hasFocus())
{
- if (!gFocusMgr.childHasKeyboardFocus(this))
- {
- // give ourselves focus preemptively, to avoid infinite loop
- LLUICtrl::setFocus(TRUE);
- // then try to pass to first valid child
- focusFirstItem();
- }
+ // give ourselves focus preemptively, to avoid infinite loop
+ LLUICtrl::setFocus(TRUE);
+ // then try to pass to first valid child
+ focusFirstItem();
}
else
{
- if( this == gFocusMgr.getKeyboardFocus() )
- {
- gFocusMgr.setKeyboardFocus( NULL );
- }
- else
- {
- //RN: why is this here?
- LLView::ctrl_list_t ctrls = getCtrlList();
- for (LLView::ctrl_list_t::iterator ctrl_it = ctrls.begin(); ctrl_it != ctrls.end(); ++ctrl_it)
- {
- LLUICtrl* ctrl = *ctrl_it;
- ctrl->setFocus( FALSE );
- }
- }
+ LLUICtrl::setFocus(b);
}
}
@@ -704,7 +665,6 @@ BOOL LLPanel::childHasFocus(const std::string& id)
}
else
{
- childNotFound(id);
return FALSE;
}
}
@@ -881,58 +841,3 @@ void LLPanel::childSetControlName(const std::string& id, const std::string& cont
view->setControlName(control_name, NULL);
}
}
-
-//virtual
-LLView* LLPanel::getChildView(const std::string& name, BOOL recurse, BOOL create_if_missing) const
-{
- // just get child, don't try to create a dummy one
- LLView* view = LLUICtrl::getChildView(name, recurse, FALSE);
- if (!view && !recurse)
- {
- childNotFound(name);
- }
- if (!view && create_if_missing)
- {
- view = getDefaultWidget<LLView>(name);
- if (!view)
- {
- // create LLViews explicitly, as they are not registered widget types
- view = LLUICtrlFactory::createDefaultWidget<LLView>(name);
- }
- }
- return view;
-}
-
-void LLPanel::childNotFound(const std::string& id) const
-{
- if (mExpectedMembers.find(id) == mExpectedMembers.end())
- {
- mNewExpectedMembers.insert(id);
- }
-}
-
-void LLPanel::childDisplayNotFound()
-{
- if (mNewExpectedMembers.empty())
- {
- return;
- }
- std::string msg;
- expected_members_list_t::iterator itor;
- for (itor=mNewExpectedMembers.begin(); itor!=mNewExpectedMembers.end(); ++itor)
- {
- msg.append(*itor);
- msg.append("\n");
- mExpectedMembers.insert(*itor);
- }
- mNewExpectedMembers.clear();
- LLSD args;
- args["CONTROLS"] = msg;
- LLNotifications::instance().add("FloaterNotFound", args);
-}
-
-void LLPanel::requires(const std::string& name)
-{
- requires<LLView>(name);
-}
-
diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h
index 2a70467ffc..3ee11483c3 100644
--- a/indra/llui/llpanel.h
+++ b/indra/llui/llpanel.h
@@ -115,9 +115,6 @@ public:
/*virtual*/ BOOL handleKeyHere( KEY key, MASK mask );
/*virtual*/ void handleVisibilityChange ( BOOL new_visibility );
- // Override to set not found list:
- /*virtual*/ LLView* getChildView(const std::string& name, BOOL recurse = TRUE, BOOL create_if_missing = TRUE) const;
-
// From LLFocusableElement
/*virtual*/ void setFocus( BOOL b );
@@ -132,19 +129,6 @@ public:
BOOL hasBorder() const { return mBorder != NULL; }
void setBorderVisible( BOOL b );
- template <class T> void requires(const std::string& name)
- {
- // check for widget with matching type and name
- if (LLView::getChild<T>(name) == NULL)
- {
- mRequirementsError += name + "\n";
- }
- }
-
- // requires LLView by default
- void requires(const std::string& name);
- BOOL checkRequirements();
-
void setBackgroundColor( const LLColor4& color ) { mBgColorOpaque = color; }
const LLColor4& getBackgroundColor() const { return mBgColorOpaque; }
void setTransparentColor(const LLColor4& color) { mBgColorAlpha = color; }
@@ -241,10 +225,6 @@ public:
void childSetControlName(const std::string& id, const std::string& control_name);
- // Error reporting
- void childNotFound(const std::string& id) const;
- void childDisplayNotFound();
-
static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node = NULL);
//call onOpen to let panel know when it's about to be shown or activated
@@ -279,8 +259,6 @@ private:
typedef std::map<std::string, std::string> ui_string_map_t;
ui_string_map_t mUIStrings;
- std::string mRequirementsError;
-
// for setting the xml filename when building panel in context dependent cases
std::string mXMLFilename;
diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp
index 30adbb023c..c66b9bde2b 100644
--- a/indra/llui/llradiogroup.cpp
+++ b/indra/llui/llradiogroup.cpp
@@ -90,18 +90,6 @@ BOOL LLRadioGroup::postBuild()
return TRUE;
}
-// virtual
-void LLRadioGroup::setEnabled(BOOL enabled)
-{
- for (child_list_const_iter_t child_iter = getChildList()->begin();
- child_iter != getChildList()->end(); ++child_iter)
- {
- LLView *child = *child_iter;
- child->setEnabled(enabled);
- }
- LLView::setEnabled(enabled);
-}
-
void LLRadioGroup::setIndexEnabled(S32 index, BOOL enabled)
{
S32 count = 0;
diff --git a/indra/llui/llradiogroup.h b/indra/llui/llradiogroup.h
index d04473fa44..b5516307fd 100644
--- a/indra/llui/llradiogroup.h
+++ b/indra/llui/llradiogroup.h
@@ -106,9 +106,7 @@ public:
virtual BOOL handleKeyHere(KEY key, MASK mask);
- virtual void setEnabled(BOOL enabled);
void setIndexEnabled(S32 index, BOOL enabled);
-
// return the index value of the selected item
S32 getSelectedIndex() const { return mSelectedIndex; }
diff --git a/indra/llui/llresizehandle.h b/indra/llui/llresizehandle.h
index 1560a03796..1c09cbb1bd 100644
--- a/indra/llui/llresizehandle.h
+++ b/indra/llui/llresizehandle.h
@@ -76,8 +76,8 @@ private:
const ECorner mCorner;
};
-const S32 RESIZE_HANDLE_HEIGHT = 16;
-const S32 RESIZE_HANDLE_WIDTH = 16;
+const S32 RESIZE_HANDLE_HEIGHT = 11;
+const S32 RESIZE_HANDLE_WIDTH = 11;
#endif // LL_RESIZEHANDLE_H
diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp
index 566825ff3b..b46915b379 100644
--- a/indra/llui/llscrollbar.cpp
+++ b/indra/llui/llscrollbar.cpp
@@ -394,7 +394,7 @@ BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask)
}
else
{
- handled = childrenHandleMouseUp( x, y, mask ) != NULL;
+ handled = childrenHandleHover( x, y, mask ) != NULL;
}
// Opaque
diff --git a/indra/llui/llscrollbar.h b/indra/llui/llscrollbar.h
index 5522e5d0fa..30d906e04c 100644
--- a/indra/llui/llscrollbar.h
+++ b/indra/llui/llscrollbar.h
@@ -127,11 +127,6 @@ public:
void onLineUpBtnPressed(const LLSD& data);
void onLineDownBtnPressed(const LLSD& data);
- void setTrackColor( const LLColor4& color ) { mTrackColor = color; }
- void setThumbColor( const LLColor4& color ) { mThumbColor = color; }
-
- void setOnScrollEndCallback(void (*callback)(void*), void* userdata) { mOnScrollEndCallback = callback; mOnScrollEndData = userdata;}
-
private:
void updateThumbRect();
void changeLine(S32 delta, BOOL update_thumb );
diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp
index 0b455f8e17..13f862f3af 100644
--- a/indra/llui/llscrollcontainer.cpp
+++ b/indra/llui/llscrollcontainer.cpp
@@ -55,8 +55,6 @@
static const S32 HORIZONTAL_MULTIPLE = 8;
static const S32 VERTICAL_MULTIPLE = 16;
-static const F32 MIN_AUTO_SCROLL_RATE = 120.f;
-static const F32 MAX_AUTO_SCROLL_RATE = 500.f;
static const F32 AUTO_SCROLL_RATE_ACCEL = 120.f;
///----------------------------------------------------------------------------
@@ -75,6 +73,9 @@ static ScrollContainerRegistry::Register<LLPanel> r3("panel", &LLPanel::fromXML)
LLScrollContainer::Params::Params()
: is_opaque("opaque"),
bg_color("color"),
+ border_visible("border_visible"),
+ 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)
{
name = "scroll_container";
@@ -91,6 +92,8 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)
mBackgroundColor(p.bg_color()),
mIsOpaque(p.is_opaque),
mReserveScrollCorner(p.reserve_scroll_corner),
+ mMinAutoScrollRate(p.min_auto_scroll_rate),
+ mMaxAutoScrollRate(p.max_auto_scroll_rate),
mScrolledView(NULL)
{
static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
@@ -98,6 +101,7 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)
LLViewBorder::Params params;
params.name("scroll border");
params.rect(border_rect);
+ params.visible(p.border_visible);
params.bevel_style(LLViewBorder::BEVEL_IN);
mBorder = LLUICtrlFactory::create<LLViewBorder> (params);
LLView::addChild( mBorder );
@@ -115,12 +119,11 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)
sbparams.doc_pos(0);
sbparams.page_size(mInnerRect.getHeight());
sbparams.step_size(VERTICAL_MULTIPLE);
+ sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
+ sbparams.visible(false);
+ sbparams.change_callback(p.scroll_callback);
mScrollbar[VERTICAL] = LLUICtrlFactory::create<LLScrollbar> (sbparams);
LLView::addChild( mScrollbar[VERTICAL] );
- mScrollbar[VERTICAL]->setVisible( FALSE );
- mScrollbar[VERTICAL]->setFollowsRight();
- mScrollbar[VERTICAL]->setFollowsTop();
- mScrollbar[VERTICAL]->setFollowsBottom();
LLRect horizontal_scroll_rect = mInnerRect;
horizontal_scroll_rect.mTop = horizontal_scroll_rect.mBottom + scrollbar_size;
@@ -131,11 +134,11 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p)
sbparams.doc_pos(0);
sbparams.page_size(mInnerRect.getWidth());
sbparams.step_size(VERTICAL_MULTIPLE);
+ sbparams.visible(false);
+ sbparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_RIGHT);
+ sbparams.change_callback(p.scroll_callback);
mScrollbar[HORIZONTAL] = LLUICtrlFactory::create<LLScrollbar> (sbparams);
LLView::addChild( mScrollbar[HORIZONTAL] );
- mScrollbar[HORIZONTAL]->setVisible( FALSE );
- mScrollbar[HORIZONTAL]->setFollowsLeft();
- mScrollbar[HORIZONTAL]->setFollowsRight();
}
// Destroys the object
@@ -181,7 +184,7 @@ void LLScrollContainer::reshape(S32 width, S32 height,
{
LLUICtrl::reshape( width, height, called_from_parent );
- mInnerRect.set( 0, getRect().getHeight(), getRect().getWidth(), 0 );
+ mInnerRect = getLocalRect();
mInnerRect.stretch( -mBorder->getBorderWidth() );
if (mScrolledView)
@@ -192,13 +195,14 @@ void LLScrollContainer::reshape(S32 width, S32 height,
S32 visible_height = 0;
BOOL show_v_scrollbar = FALSE;
BOOL show_h_scrollbar = FALSE;
- calcVisibleSize( scrolled_rect, &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
+ calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() );
mScrollbar[VERTICAL]->setPageSize( visible_height );
mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() );
mScrollbar[HORIZONTAL]->setPageSize( visible_width );
+ updateScroll();
}
}
@@ -217,6 +221,7 @@ BOOL LLScrollContainer::handleKeyHere(KEY key, MASK mask)
{
if( mScrollbar[i]->handleKeyHere(key, mask) )
{
+ updateScroll();
return TRUE;
}
}
@@ -233,6 +238,7 @@ BOOL LLScrollContainer::handleScrollWheel( S32 x, S32 y, S32 clicks )
// Pretend the mouse is over the scrollbar
if( mScrollbar[i]->handleScrollWheel( 0, 0, clicks ) )
{
+ updateScroll();
return TRUE;
}
}
@@ -241,28 +247,6 @@ BOOL LLScrollContainer::handleScrollWheel( S32 x, S32 y, S32 clicks )
return TRUE;
}
-BOOL LLScrollContainer::needsToScroll(S32 x, S32 y, LLScrollContainer::SCROLL_ORIENTATION axis) const
-{
- if(mScrollbar[axis]->getVisible())
- {
- LLRect inner_rect_local( 0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0 );
- const S32 AUTOSCROLL_SIZE = 10;
- if(mScrollbar[axis]->getVisible())
- {
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
- inner_rect_local.mRight -= scrollbar_size;
- inner_rect_local.mTop += AUTOSCROLL_SIZE;
- inner_rect_local.mBottom = inner_rect_local.mTop - AUTOSCROLL_SIZE;
- }
- if( inner_rect_local.pointInRect( x, y ) && (mScrollbar[axis]->getDocPos() > 0) )
- {
- return TRUE;
- }
-
- }
- return FALSE;
-}
-
BOOL LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask,
BOOL drop,
EDragAndDropType cargo_type,
@@ -273,12 +257,27 @@ BOOL LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask,
static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
// Scroll folder view if needed. Never accepts a drag or drop.
*accept = ACCEPT_NO;
- BOOL handled = FALSE;
+ BOOL handled = autoScroll(x, y);
+
+ if( !handled )
+ {
+ handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type,
+ cargo_data, accept, tooltip_msg) != NULL;
+ }
+
+ return TRUE;
+}
+
+bool LLScrollContainer::autoScroll(S32 x, S32 y)
+{
+ static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
+
+ bool scrolling = false;
if( mScrollbar[HORIZONTAL]->getVisible() || mScrollbar[VERTICAL]->getVisible() )
{
- const S32 AUTOSCROLL_SIZE = 10;
- S32 auto_scroll_speed = llround(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32());
-
+ LLRect screen_local_extents;
+ screenRectToLocal(getRootView()->getLocalRect(), &screen_local_extents);
+
LLRect inner_rect_local( 0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0 );
if( mScrollbar[HORIZONTAL]->getVisible() )
{
@@ -289,65 +288,61 @@ BOOL LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask,
inner_rect_local.mRight -= scrollbar_size;
}
+ // clip rect against root view
+ inner_rect_local.intersectWith(screen_local_extents);
+
+ S32 auto_scroll_speed = llround(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32());
+ // autoscroll region should take up no more than one third of visible scroller area
+ S32 auto_scroll_region_width = llmin(inner_rect_local.getWidth() / 3, 10);
+ S32 auto_scroll_region_height = llmin(inner_rect_local.getHeight() / 3, 10);
+
if( mScrollbar[HORIZONTAL]->getVisible() )
{
- LLRect left_scroll_rect = inner_rect_local;
- left_scroll_rect.mRight = AUTOSCROLL_SIZE;
+ LLRect left_scroll_rect = screen_local_extents;
+ left_scroll_rect.mRight = inner_rect_local.mLeft + auto_scroll_region_width;
if( left_scroll_rect.pointInRect( x, y ) && (mScrollbar[HORIZONTAL]->getDocPos() > 0) )
{
mScrollbar[HORIZONTAL]->setDocPos( mScrollbar[HORIZONTAL]->getDocPos() - auto_scroll_speed );
mAutoScrolling = TRUE;
- handled = TRUE;
+ scrolling = true;
}
- LLRect right_scroll_rect = inner_rect_local;
- right_scroll_rect.mLeft = inner_rect_local.mRight - AUTOSCROLL_SIZE;
+ LLRect right_scroll_rect = screen_local_extents;
+ right_scroll_rect.mLeft = inner_rect_local.mRight - auto_scroll_region_width;
if( right_scroll_rect.pointInRect( x, y ) && (mScrollbar[HORIZONTAL]->getDocPos() < mScrollbar[HORIZONTAL]->getDocPosMax()) )
{
mScrollbar[HORIZONTAL]->setDocPos( mScrollbar[HORIZONTAL]->getDocPos() + auto_scroll_speed );
mAutoScrolling = TRUE;
- handled = TRUE;
+ scrolling = true;
}
}
if( mScrollbar[VERTICAL]->getVisible() )
{
- LLRect bottom_scroll_rect = inner_rect_local;
- bottom_scroll_rect.mTop = AUTOSCROLL_SIZE + bottom_scroll_rect.mBottom;
+ LLRect bottom_scroll_rect = screen_local_extents;
+ bottom_scroll_rect.mTop = inner_rect_local.mBottom + auto_scroll_region_height;
if( bottom_scroll_rect.pointInRect( x, y ) && (mScrollbar[VERTICAL]->getDocPos() < mScrollbar[VERTICAL]->getDocPosMax()) )
{
mScrollbar[VERTICAL]->setDocPos( mScrollbar[VERTICAL]->getDocPos() + auto_scroll_speed );
mAutoScrolling = TRUE;
- handled = TRUE;
+ scrolling = true;
}
- LLRect top_scroll_rect = inner_rect_local;
- top_scroll_rect.mBottom = inner_rect_local.mTop - AUTOSCROLL_SIZE;
+ LLRect top_scroll_rect = screen_local_extents;
+ top_scroll_rect.mBottom = inner_rect_local.mTop - auto_scroll_region_height;
if( top_scroll_rect.pointInRect( x, y ) && (mScrollbar[VERTICAL]->getDocPos() > 0) )
{
mScrollbar[VERTICAL]->setDocPos( mScrollbar[VERTICAL]->getDocPos() - auto_scroll_speed );
mAutoScrolling = TRUE;
- handled = TRUE;
+ scrolling = true;
}
}
}
-
- if( !handled )
- {
- handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type,
- cargo_data, accept, tooltip_msg) != NULL;
- }
-
- return TRUE;
+ return scrolling;
}
void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const
{
- const LLRect& rect = getScrolledViewRect();
- calcVisibleSize(rect, visible_width, visible_height, show_h_scrollbar, show_v_scrollbar);
-}
-
-void LLScrollContainer::calcVisibleSize( const LLRect& doc_rect, S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const
-{
+ const LLRect& doc_rect = getScrolledViewRect();
static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
S32 doc_width = doc_rect.getWidth();
S32 doc_height = doc_rect.getHeight();
@@ -384,20 +379,20 @@ void LLScrollContainer::draw()
if (mAutoScrolling)
{
// add acceleration to autoscroll
- mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), MAX_AUTO_SCROLL_RATE);
+ mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), mMaxAutoScrollRate);
}
else
{
- // reset to minimum
- mAutoScrollRate = MIN_AUTO_SCROLL_RATE;
+ // reset to minimum for next time
+ mAutoScrollRate = mMinAutoScrollRate;
}
- // clear this flag to be set on next call to handleDragAndDrop
+ // clear this flag to be set on next call to autoScroll
mAutoScrolling = FALSE;
// auto-focus when scrollbar active
// this allows us to capture user intent (i.e. stop automatically scrolling the view/etc)
- if (!gFocusMgr.childHasKeyboardFocus(this) &&
- (mScrollbar[VERTICAL]->hasMouseCapture() || mScrollbar[HORIZONTAL]->hasMouseCapture()))
+ if (!hasFocus()
+ && (mScrollbar[VERTICAL]->hasMouseCapture() || mScrollbar[HORIZONTAL]->hasMouseCapture()))
{
focusFirstItem();
}
@@ -424,7 +419,7 @@ void LLScrollContainer::draw()
S32 visible_height = 0;
BOOL show_v_scrollbar = FALSE;
BOOL show_h_scrollbar = FALSE;
- calcVisibleSize( mScrolledView->getRect(), &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
+ calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
LLLocalClipRect clip(LLRect(mInnerRect.mLeft,
mInnerRect.mBottom + (show_h_scrollbar ? scrollbar_size : 0) + visible_height,
@@ -506,7 +501,7 @@ void LLScrollContainer::updateScroll()
S32 visible_height = 0;
BOOL show_v_scrollbar = FALSE;
BOOL show_h_scrollbar = FALSE;
- calcVisibleSize( doc_rect, &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
+ calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
S32 border_width = mBorder->getBorderWidth();
if( show_v_scrollbar )
@@ -584,71 +579,73 @@ void LLScrollContainer::setBorderVisible(BOOL b)
mBorder->setVisible( b );
}
-// Scroll so that as much of rect as possible is showing (where rect is defined in the space of scroller view, not scrolled)
-void LLScrollContainer::scrollToShowRect(const LLRect& rect, const LLCoordGL& desired_offset)
+LLRect LLScrollContainer::getVisibleContentRect()
{
- if (!mScrolledView)
- {
- llwarns << "LLScrollContainer::scrollToShowRect with no view!" << llendl;
- return;
- }
+ updateScroll();
+ LLRect visible_rect = getContentWindowRect();
+ LLRect contents_rect = mScrolledView->getRect();
+ visible_rect.translate(-contents_rect.mLeft, -contents_rect.mBottom);
+ return visible_rect;
+}
+LLRect LLScrollContainer::getContentWindowRect() const
+{
+ LLRect scroller_view_rect;
S32 visible_width = 0;
S32 visible_height = 0;
- BOOL show_v_scrollbar = FALSE;
BOOL show_h_scrollbar = FALSE;
- const LLRect& scrolled_rect = mScrolledView->getRect();
- calcVisibleSize( scrolled_rect, &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
-
- // can't be so far left that right side of rect goes off screen, or so far right that left side does
- S32 horiz_offset = llclamp(desired_offset.mX, llmin(0, -visible_width + rect.getWidth()), 0);
- // can't be so high that bottom of rect goes off screen, or so low that top does
- S32 vert_offset = llclamp(desired_offset.mY, 0, llmax(0, visible_height - rect.getHeight()));
-
- // Vertical
- // 1. First make sure the top is visible
- // 2. Then, if possible without hiding the top, make the bottom visible.
- S32 vert_pos = mScrollbar[VERTICAL]->getDocPos();
-
- // find scrollbar position to get top of rect on screen (scrolling up)
- S32 top_offset = scrolled_rect.mTop - rect.mTop - vert_offset;
- // find scrollbar position to get bottom of rect on screen (scrolling down)
- S32 bottom_offset = vert_offset == 0 ? scrolled_rect.mTop - rect.mBottom - visible_height : top_offset;
- // scroll up far enough to see top or scroll down just enough if item is bigger than visual area
- if( vert_pos >= top_offset || visible_height < rect.getHeight())
- {
- vert_pos = top_offset;
- }
- // else scroll down far enough to see bottom
- else
- if( vert_pos <= bottom_offset )
+ BOOL show_v_scrollbar = FALSE;
+ calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
+ S32 border_width = mBorder->getBorderWidth();
+ scroller_view_rect.setOriginAndSize(border_width,
+ show_h_scrollbar ? mScrollbar[HORIZONTAL]->getRect().mTop : border_width,
+ visible_width,
+ visible_height);
+ return scroller_view_rect;
+}
+
+// rect is in document coordinates, constraint is in display coordinates relative to content window rect
+void LLScrollContainer::scrollToShowRect(const LLRect& rect, const LLRect& constraint)
+{
+ if (!mScrolledView)
{
- vert_pos = bottom_offset;
+ llwarns << "LLScrollContainer::scrollToShowRect with no view!" << llendl;
+ return;
}
+ LLRect content_window_rect = getContentWindowRect();
+ // get document rect
+ LLRect scrolled_rect = mScrolledView->getRect();
+
+ // shrink target rect to fit within constraint region, biasing towards top left
+ LLRect rect_to_constrain = rect;
+ rect_to_constrain.mBottom = llmax(rect_to_constrain.mBottom, rect_to_constrain.mTop - constraint.getHeight());
+ rect_to_constrain.mRight = llmin(rect_to_constrain.mRight, rect_to_constrain.mLeft + constraint.getWidth());
+
+ // calculate allowable positions for scroller window in document coordinates
+ LLRect allowable_scroll_rect(rect_to_constrain.mRight - constraint.mRight,
+ rect_to_constrain.mBottom - constraint.mBottom,
+ rect_to_constrain.mLeft - constraint.mLeft,
+ rect_to_constrain.mTop - constraint.mTop);
+
+ // translate from allowable region for lower left corner to upper left corner
+ allowable_scroll_rect.translate(0, content_window_rect.getHeight());
+
+ S32 vert_pos = llclamp(mScrollbar[VERTICAL]->getDocPos(),
+ mScrollbar[VERTICAL]->getDocSize() - allowable_scroll_rect.mTop, // min vertical scroll
+ mScrollbar[VERTICAL]->getDocSize() - allowable_scroll_rect.mBottom); // max vertical scroll
+
mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() );
- mScrollbar[VERTICAL]->setPageSize( visible_height );
+ mScrollbar[VERTICAL]->setPageSize( content_window_rect.getHeight() );
mScrollbar[VERTICAL]->setDocPos( vert_pos );
- // Horizontal
- // 1. First make sure left side is visible
- // 2. Then, if possible without hiding the left side, make the right side visible.
- S32 horiz_pos = mScrollbar[HORIZONTAL]->getDocPos();
- S32 left_offset = rect.mLeft - scrolled_rect.mLeft + horiz_offset;
- S32 right_offset = horiz_offset == 0 ? rect.mRight - scrolled_rect.mLeft - visible_width : left_offset;
+ S32 horizontal_pos = llclamp(mScrollbar[HORIZONTAL]->getDocPos(),
+ allowable_scroll_rect.mLeft,
+ allowable_scroll_rect.mRight);
- if( horiz_pos >= left_offset || visible_width < rect.getWidth() )
- {
- horiz_pos = left_offset;
- }
- else if( horiz_pos <= right_offset )
- {
- horiz_pos = right_offset;
- }
-
mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() );
- mScrollbar[HORIZONTAL]->setPageSize( visible_width );
- mScrollbar[HORIZONTAL]->setDocPos( horiz_pos );
+ mScrollbar[HORIZONTAL]->setPageSize( content_window_rect.getWidth() );
+ mScrollbar[HORIZONTAL]->setDocPos( horizontal_pos );
// propagate scroll to document
updateScroll();
@@ -657,21 +654,25 @@ void LLScrollContainer::scrollToShowRect(const LLRect& rect, const LLCoordGL& de
void LLScrollContainer::pageUp(S32 overlap)
{
mScrollbar[VERTICAL]->pageUp(overlap);
+ updateScroll();
}
void LLScrollContainer::pageDown(S32 overlap)
{
mScrollbar[VERTICAL]->pageDown(overlap);
+ updateScroll();
}
void LLScrollContainer::goToTop()
{
mScrollbar[VERTICAL]->setDocPos(0);
+ updateScroll();
}
void LLScrollContainer::goToBottom()
{
mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocSize());
+ updateScroll();
}
S32 LLScrollContainer::getBorderWidth() const
diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h
index 912d126f23..8385bca02f 100644
--- a/indra/llui/llscrollcontainer.h
+++ b/indra/llui/llscrollcontainer.h
@@ -66,9 +66,13 @@ public:
struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
{
- Optional<bool> is_opaque;
+ Optional<bool> is_opaque,
+ reserve_scroll_corner,
+ border_visible;
+ Optional<F32> min_auto_scroll_rate,
+ max_auto_scroll_rate;
Optional<LLUIColor> bg_color;
- Optional<bool> reserve_scroll_corner;
+ Optional<LLScrollbar::callback_t> scroll_callback;
Params();
};
@@ -84,21 +88,23 @@ public:
virtual void setValue(const LLSD& value) { mInnerRect.setValue(value); }
- void calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const;
- void calcVisibleSize( const LLRect& doc_rect, S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const;
void setBorderVisible( BOOL b );
- void scrollToShowRect( const LLRect& rect, const LLCoordGL& desired_offset );
+ void scrollToShowRect( const LLRect& rect, const LLRect& constraint);
+ void scrollToShowRect( const LLRect& rect) { scrollToShowRect(rect, LLRect(0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0)); }
+
void setReserveScrollCorner( BOOL b ) { mReserveScrollCorner = b; }
+ LLRect getVisibleContentRect();
+ LLRect getContentWindowRect() const;
const LLRect& getScrolledViewRect() const { return mScrolledView ? mScrolledView->getRect() : LLRect::null; }
void pageUp(S32 overlap = 0);
void pageDown(S32 overlap = 0);
void goToTop();
void goToBottom();
+ bool isAtTop() { return mScrollbar[VERTICAL]->isAtBeginning(); }
+ bool isAtBottom() { return mScrollbar[VERTICAL]->isAtEnd(); }
S32 getBorderWidth() const;
- BOOL needsToScroll(S32 x, S32 y, SCROLL_ORIENTATION axis) const;
-
// LLView functionality
virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
virtual BOOL handleKeyHere(KEY key, MASK mask);
@@ -111,12 +117,15 @@ public:
virtual void draw();
virtual bool addChild(LLView* view, S32 tab_group = 0);
+
+ bool autoScroll(S32 x, S32 y);
private:
// internal scrollbar handlers
virtual void scrollHorizontal( S32 new_pos );
virtual void scrollVertical( S32 new_pos );
void updateScroll();
+ void calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const;
LLScrollbar* mScrollbar[SCROLLBAR_COUNT];
LLView* mScrolledView;
@@ -128,6 +137,8 @@ private:
BOOL mReserveScrollCorner;
BOOL mAutoScrolling;
F32 mAutoScrollRate;
+ F32 mMinAutoScrollRate;
+ F32 mMaxAutoScrollRate;
};
diff --git a/indra/llui/llscrolllistcell.cpp b/indra/llui/llscrolllistcell.cpp
index cd43e194d2..e28da91305 100644
--- a/indra/llui/llscrolllistcell.cpp
+++ b/indra/llui/llscrolllistcell.cpp
@@ -310,17 +310,16 @@ void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_col
break;
}
mFont->render(mText.getWString(), 0,
- start_x, 2.f,
- display_color,
- mFontAlignment,
- LLFontGL::BOTTOM,
- 0,
- LLFontGL::NO_SHADOW,
- string_chars,
- getWidth(),
- &right_x,
- FALSE,
- TRUE);
+ start_x, 2.f,
+ display_color,
+ mFontAlignment,
+ LLFontGL::BOTTOM,
+ 0,
+ LLFontGL::NO_SHADOW,
+ string_chars,
+ getWidth(),
+ &right_x,
+ TRUE);
}
//
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index 3041773fb2..637642cdcd 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -160,7 +160,7 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p)
mNumDynamicWidthColumns(0),
mTotalStaticColumnWidth(0),
mTotalColumnPadding(0),
- mSorted(FALSE),
+ mSorted(false),
mDirty(FALSE),
mOriginalSelection(-1),
mLastSelected(NULL),
@@ -374,6 +374,10 @@ std::vector<LLScrollListItem*> LLScrollListCtrl::getAllSelected() const
S32 LLScrollListCtrl::getFirstSelectedIndex() const
{
S32 CurSelectedIndex = 0;
+
+ // make sure sort is up to date before returning an index
+ updateSort();
+
item_list::const_iterator iter;
for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
{
@@ -507,7 +511,7 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, BOOL r
{
case ADD_TOP:
mItemList.push_front(item);
- setSorted(FALSE);
+ setNeedsSort();
break;
case ADD_SORTED:
@@ -524,18 +528,18 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, BOOL r
// ADD_SORTED just sorts by first column...
// this might not match user sort criteria, so flag list as being in unsorted state
- setSorted(FALSE);
+ setNeedsSort();
break;
}
case ADD_BOTTOM:
mItemList.push_back(item);
- setSorted(FALSE);
+ setNeedsSort();
break;
default:
llassert(0);
mItemList.push_back(item);
- setSorted(FALSE);
+ setNeedsSort();
break;
}
@@ -759,6 +763,9 @@ BOOL LLScrollListCtrl::selectItemRange( S32 first_index, S32 last_index )
return FALSE;
}
+ // make sure sort is up to date
+ updateSort();
+
S32 listlen = (S32)mItemList.size();
first_index = llclamp(first_index, 0, listlen-1);
@@ -812,6 +819,7 @@ void LLScrollListCtrl::swapWithNext(S32 index)
// At end of list, doesn't do anything
return;
}
+ updateSort();
LLScrollListItem *cur_itemp = mItemList[index];
mItemList[index] = mItemList[index + 1];
mItemList[index + 1] = cur_itemp;
@@ -825,6 +833,7 @@ void LLScrollListCtrl::swapWithPrevious(S32 index)
// At beginning of list, don't do anything
}
+ updateSort();
LLScrollListItem *cur_itemp = mItemList[index];
mItemList[index] = mItemList[index - 1];
mItemList[index - 1] = cur_itemp;
@@ -838,6 +847,8 @@ void LLScrollListCtrl::deleteSingleItem(S32 target_index)
return;
}
+ updateSort();
+
LLScrollListItem *itemp;
itemp = mItemList[target_index];
if (itemp == mLastSelected)
@@ -939,6 +950,8 @@ S32 LLScrollListCtrl::selectMultiple( std::vector<LLUUID> ids )
S32 LLScrollListCtrl::getItemIndex( LLScrollListItem* target_item ) const
{
+ updateSort();
+
S32 index = 0;
item_list::const_iterator iter;
for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
@@ -955,6 +968,8 @@ S32 LLScrollListCtrl::getItemIndex( LLScrollListItem* target_item ) const
S32 LLScrollListCtrl::getItemIndex( const LLUUID& target_id ) const
{
+ updateSort();
+
S32 index = 0;
item_list::const_iterator iter;
for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
@@ -980,6 +995,8 @@ void LLScrollListCtrl::selectPrevItem( BOOL extend_selection)
}
else
{
+ updateSort();
+
item_list::iterator iter;
for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
{
@@ -1022,6 +1039,8 @@ void LLScrollListCtrl::selectNextItem( BOOL extend_selection)
}
else
{
+ updateSort();
+
item_list::reverse_iterator iter;
for (iter = mItemList.rbegin(); iter != mItemList.rend(); iter++)
{
@@ -1436,7 +1455,7 @@ void LLScrollListCtrl::draw()
LLLocalClipRect clip(getLocalRect());
// if user specifies sort, make sure it is maintained
- sortItems();
+ updateSort();
if (mNeedsScroll)
{
@@ -1463,7 +1482,7 @@ void LLScrollListCtrl::draw()
if (mBorder)
{
- mBorder->setKeyboardFocusHighlight(gFocusMgr.getKeyboardFocus() == this);
+ mBorder->setKeyboardFocusHighlight(hasFocus());
}
LLUICtrl::draw();
@@ -1754,6 +1773,8 @@ LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y )
// Excludes disabled items.
LLScrollListItem* hit_item = NULL;
+ updateSort();
+
LLRect item_rect;
item_rect.setLeftTopAndSize(
mItemListRect.mLeft,
@@ -2199,7 +2220,7 @@ BOOL LLScrollListCtrl::setSort(S32 column_idx, BOOL ascending)
sort_column_t new_sort_column(column_idx, ascending);
- setSorted(FALSE);
+ setNeedsSort();
if (mSortColumns.empty())
{
@@ -2241,10 +2262,10 @@ void LLScrollListCtrl::sortByColumn(const std::string& name, BOOL ascending)
void LLScrollListCtrl::sortByColumnIndex(U32 column, BOOL ascending)
{
setSort(column, ascending);
- sortItems();
+ updateSort();
}
-void LLScrollListCtrl::sortItems()
+void LLScrollListCtrl::updateSort() const
{
if (hasSortOrder() && !isSorted())
{
@@ -2254,7 +2275,7 @@ void LLScrollListCtrl::sortItems()
mItemList.end(),
SortScrollListItem(mSortColumns));
- setSorted(TRUE);
+ mSorted = true;
}
}
@@ -2311,7 +2332,7 @@ void LLScrollListCtrl::scrollToShowSelected()
return;
}
- sortItems();
+ updateSort();
S32 index = getFirstSelectedIndex();
if (index < 0)
@@ -2552,7 +2573,7 @@ std::string LLScrollListCtrl::getSortColumnName()
else return "";
}
-BOOL LLScrollListCtrl::hasSortOrder()
+BOOL LLScrollListCtrl::hasSortOrder() const
{
return !mSortColumns.empty();
}
diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h
index e699711bd4..253a58ab73 100644
--- a/indra/llui/llscrolllistctrl.h
+++ b/indra/llui/llscrolllistctrl.h
@@ -329,15 +329,16 @@ public:
std::string getSortColumnName();
BOOL getSortAscending() { return mSortColumns.empty() ? TRUE : mSortColumns.back().second; }
- BOOL hasSortOrder();
+ BOOL hasSortOrder() const;
S32 selectMultiple( std::vector<LLUUID> ids );
- void sortItems();
+ // conceptually const, but mutates mItemList
+ void updateSort() const;
// sorts a list without affecting the permanent sort order (so further list insertions can be unsorted, for example)
void sortOnce(S32 column, BOOL ascending);
// manually call this whenever editing list items in place to flag need for resorting
- void setSorted(BOOL sorted) { mSorted = sorted; }
+ void setNeedsSort() { mSorted = false; }
void dirtyColumns(); // some operation has potentially affected column layout or ordering
protected:
@@ -390,7 +391,7 @@ private:
const BOOL mDisplayColumnHeaders;
BOOL mColumnsDirty;
- item_list mItemList;
+ mutable item_list mItemList;
LLScrollListItem *mLastSelected;
@@ -429,7 +430,7 @@ private:
S32 mTotalStaticColumnWidth;
S32 mTotalColumnPadding;
- BOOL mSorted;
+ mutable bool mSorted;
typedef std::map<std::string, LLScrollListColumn> column_map_t;
column_map_t mColumns;
diff --git a/indra/llui/llstyle.cpp b/indra/llui/llstyle.cpp
index 432d54dfee..929a809d88 100644
--- a/indra/llui/llstyle.cpp
+++ b/indra/llui/llstyle.cpp
@@ -38,119 +38,34 @@
#include "llstring.h"
#include "llui.h"
-
-LLStyle::LLStyle()
-{
- init(TRUE, LLColor4(0,0,0,1),LLStringUtil::null);
-}
-
-LLStyle::LLStyle(const LLStyle &style)
-{
- if (this != &style)
- {
- init(style.isVisible(),style.getColor(),style.getFontString());
- if (style.isLink())
- {
- setLinkHREF(style.getLinkHREF());
- }
- mItalic = style.mItalic;
- mBold = style.mBold;
- mUnderline = style.mUnderline;
- mDropShadow = style.mDropShadow;
- mImageHeight = style.mImageHeight;
- mImageWidth = style.mImageWidth;
- mImagep = style.mImagep;
- mIsEmbeddedItem = style.mIsEmbeddedItem;
- }
- else
- {
- init(TRUE, LLColor4(0,0,0,1),LLStringUtil::null);
- }
-}
-
-LLStyle::LLStyle(BOOL is_visible, const LLColor4 &color, const std::string& font_name)
-{
- init(is_visible, color, font_name);
-}
-
-void LLStyle::init(BOOL is_visible, const LLColor4 &color, const std::string& font_name)
+LLStyle::Params::Params()
+: visible("visible", true),
+ drop_shadow("drop_shadow", false),
+ color("color", LLColor4::black),
+ font("font", LLFontGL::getFontMonospace()),
+ image("image"),
+ link_href("href")
+{}
+
+
+LLStyle::LLStyle(const LLStyle::Params& p)
+: mVisible(p.visible),
+ mColor(p.color()),
+ mFont(p.font()),
+ mLink(p.link_href),
+ mDropShadow(p.drop_shadow),
+ mImageHeight(0),
+ mImageWidth(0),
+ mImagep(p.image())
+{}
+
+void LLStyle::setFont(const LLFontGL* font)
{
- mVisible = is_visible;
- mColor = color;
- setFontName(font_name);
- setLinkHREF(LLStringUtil::null);
- mItalic = FALSE;
- mBold = FALSE;
- mUnderline = FALSE;
- mDropShadow = FALSE;
- mImageHeight = 0;
- mImageWidth = 0;
- mIsEmbeddedItem = FALSE;
-}
-
-
-// Copy assignment
-LLStyle &LLStyle::operator=(const LLStyle &rhs)
-{
- if (this != &rhs)
- {
- setVisible(rhs.isVisible());
- setColor(rhs.getColor());
- this->mFontName = rhs.getFontString();
- this->mLink = rhs.getLinkHREF();
- mImagep = rhs.mImagep;
- mImageHeight = rhs.mImageHeight;
- mImageWidth = rhs.mImageWidth;
- mItalic = rhs.mItalic;
- mBold = rhs.mBold;
- mUnderline = rhs.mUnderline;
- mDropShadow = rhs.mDropShadow;
- mIsEmbeddedItem = rhs.mIsEmbeddedItem;
- }
-
- return *this;
+ mFont = font;
}
-//virtual
-const std::string& LLStyle::getFontString() const
-{
- return mFontName;
-}
-
-//virtual
-void LLStyle::setFontName(const std::string& fontname)
-{
- mFontName = fontname;
-
- std::string fontname_lc = fontname;
- LLStringUtil::toLower(fontname_lc);
-
- // cache the font pointer for speed when rendering text
- if ((fontname_lc == "sansserif") || (fontname_lc == "sans-serif"))
- {
- mFont = LLFontGL::getFontSansSerif();
- }
- else if ((fontname_lc == "serif"))
- {
- // *TODO: Do we have a real serif font?
- mFont = LLFontGL::getFontMonospace();
- }
- else if ((fontname_lc == "sansserifbig"))
- {
- mFont = LLFontGL::getFontSansSerifBig();
- }
- else if (fontname_lc == "small")
- {
- mFont = LLFontGL::getFontSansSerifSmall();
- }
- else
- {
- mFont = LLFontGL::getFontMonospace();
- }
-}
-//virtual
-LLFontGL* LLStyle::getFont() const
+const LLFontGL* LLStyle::getFont() const
{
return mFont;
}
diff --git a/indra/llui/llstyle.h b/indra/llui/llstyle.h
index 32ddded2c8..dcf274a651 100644
--- a/indra/llui/llstyle.h
+++ b/indra/llui/llstyle.h
@@ -35,59 +35,59 @@
#include "v4color.h"
#include "llui.h"
+#include "llinitparam.h"
class LLFontGL;
class LLStyle : public LLRefCount
{
public:
- LLStyle();
- LLStyle(const LLStyle &style);
- LLStyle(BOOL is_visible, const LLColor4 &color, const std::string& font_name);
-
- LLStyle &operator=(const LLStyle &rhs);
-
- virtual void init (BOOL is_visible, const LLColor4 &color, const std::string& font_name);
-
- virtual const LLColor4& getColor() const { return mColor; }
- virtual void setColor(const LLColor4 &color) { mColor = color; }
-
- virtual BOOL isVisible() const;
- virtual void setVisible(BOOL is_visible);
+ 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;
+ Params();
+ };
+ LLStyle(const Params& p = Params());
+public:
+ const LLColor4& getColor() const { return mColor; }
+ void setColor(const LLColor4 &color) { mColor = color; }
- virtual const std::string& getFontString() const;
- virtual void setFontName(const std::string& fontname);
- virtual LLFontGL* getFont() const;
+ BOOL isVisible() const;
+ void setVisible(BOOL is_visible);
- virtual const std::string& getLinkHREF() const { return mLink; }
- virtual void setLinkHREF(const std::string& href);
- virtual BOOL isLink() const;
+ void setFont(const LLFontGL* font);
+ const LLFontGL* getFont() const;
- virtual LLUIImagePtr getImage() const;
- virtual void setImage(const LLUUID& src);
+ const std::string& getLinkHREF() const { return mLink; }
+ void setLinkHREF(const std::string& href);
+ BOOL isLink() const;
- virtual BOOL isImage() const { return ((mImageWidth != 0) && (mImageHeight != 0)); }
- virtual void setImageSize(S32 width, S32 height);
+ LLUIImagePtr getImage() const;
+ void setImage(const LLUUID& src);
- BOOL getIsEmbeddedItem() const { return mIsEmbeddedItem; }
- void setIsEmbeddedItem( BOOL b ) { mIsEmbeddedItem = b; }
+ BOOL isImage() const { return ((mImageWidth != 0) && (mImageHeight != 0)); }
+ void setImageSize(S32 width, S32 height);
// inlined here to make it easier to compare to member data below. -MG
bool operator==(const LLStyle &rhs) const
{
return
- mVisible == rhs.isVisible()
- && mColor == rhs.getColor()
- && mFontName == rhs.getFontString()
- && mLink == rhs.getLinkHREF()
+ mVisible == rhs.mVisible
+ && mColor == rhs.mColor
+ && mFont == rhs.mFont
+ && mLink == rhs.mLink
&& mImagep == rhs.mImagep
&& mImageHeight == rhs.mImageHeight
&& mImageWidth == rhs.mImageWidth
&& mItalic == rhs.mItalic
&& mBold == rhs.mBold
&& mUnderline == rhs.mUnderline
- && mDropShadow == rhs.mDropShadow
- && mIsEmbeddedItem == rhs.mIsEmbeddedItem;
+ && mDropShadow == rhs.mDropShadow;
}
bool operator!=(const LLStyle& rhs) const { return !(*this == rhs); }
@@ -101,16 +101,15 @@ public:
S32 mImageHeight;
protected:
- virtual ~LLStyle() { }
+ ~LLStyle() { }
private:
BOOL mVisible;
LLUIColor mColor;
std::string mFontName;
- LLFontGL* mFont; // cached for performance
+ const LLFontGL* mFont; // cached for performance
std::string mLink;
LLUIImagePtr mImagep;
- BOOL mIsEmbeddedItem;
};
typedef LLPointer<LLStyle> LLStyleSP;
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index 29c30004ef..ee36318107 100644
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -190,7 +190,7 @@ void LLTabContainer::reshape(S32 width, S32 height, BOOL called_from_parent)
}
//virtual
-LLView* LLTabContainer::getChildView(const std::string& name, BOOL recurse, BOOL create_if_missing) const
+LLView* LLTabContainer::getChildView(const std::string& name, BOOL recurse) const
{
tuple_list_t::const_iterator itor;
for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
@@ -207,14 +207,42 @@ LLView* LLTabContainer::getChildView(const std::string& name, BOOL recurse, BOOL
for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
{
LLPanel *panel = (*itor)->mTabPanel;
- LLView *child = panel->getChildView(name, recurse, FALSE);
+ LLView *child = panel->getChildView(name, recurse);
if (child)
{
return child;
}
}
}
- return LLView::getChildView(name, recurse, create_if_missing);
+ return LLView::getChildView(name, recurse);
+}
+
+//virtual
+LLView* LLTabContainer::findChildView(const std::string& name, BOOL recurse) const
+{
+ tuple_list_t::const_iterator itor;
+ for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
+ {
+ LLPanel *panel = (*itor)->mTabPanel;
+ if (panel->getName() == name)
+ {
+ return panel;
+ }
+ }
+
+ if (recurse)
+ {
+ for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
+ {
+ LLPanel *panel = (*itor)->mTabPanel;
+ LLView *child = panel->findChildView(name, recurse);
+ if (child)
+ {
+ return child;
+ }
+ }
+ }
+ return LLView::findChildView(name, recurse);
}
bool LLTabContainer::addChild(LLView* view, S32 tab_group)
@@ -457,7 +485,7 @@ BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
index = llclamp(index, 0, tab_count-1);
LLButton* tab_button = getTab(index)->mButton;
gFocusMgr.setMouseCapture(this);
- gFocusMgr.setKeyboardFocus(tab_button);
+ tab_button->setFocus(TRUE);
}
}
return handled;
diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h
index 78592a0f9a..ebe76af966 100644
--- a/indra/llui/lltabcontainer.h
+++ b/indra/llui/lltabcontainer.h
@@ -104,7 +104,8 @@ public:
/*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
EDragAndDropType type, void* cargo_data,
EAcceptance* accept, std::string& tooltip);
- /*virtual*/ LLView* getChildView(const std::string& name, BOOL recurse = TRUE, BOOL create_if_missing = TRUE) const;
+ /*virtual*/ LLView* getChildView(const std::string& name, BOOL recurse = TRUE) const;
+ /*virtual*/ LLView* findChildView(const std::string& name, BOOL recurse = TRUE) const;
/*virtual*/ void initFromParams(const LLPanel::Params& p);
/*virtual*/ bool addChild(LLView* view, S32 tab_group = 0);
/*virtual*/ BOOL postBuild();
diff --git a/indra/llui/lltextbox.cpp b/indra/llui/lltextbox.cpp
index f9bcb685b8..96e72487b8 100644
--- a/indra/llui/lltextbox.cpp
+++ b/indra/llui/lltextbox.cpp
@@ -389,7 +389,7 @@ void LLTextBox::drawText( S32 x, S32 y, const LLColor4& color )
mHAlign, mVAlign,
0,
mShadowType,
- S32_MAX, getRect().getWidth(), NULL, TRUE, mUseEllipses);
+ S32_MAX, getRect().getWidth(), NULL, mUseEllipses);
}
else
{
@@ -402,7 +402,7 @@ void LLTextBox::drawText( S32 x, S32 y, const LLColor4& color )
mHAlign, mVAlign,
0,
mShadowType,
- line_length, getRect().getWidth(), NULL, TRUE, mUseEllipses );
+ line_length, getRect().getWidth(), NULL, mUseEllipses );
cur_pos += line_length + 1;
y -= llfloor(mFontGL->getLineHeight()) + mLineSpacing;
}
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index 48816e4b9e..7d13220c94 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -58,7 +58,10 @@
#include "llcontrol.h"
#include "llwindow.h"
#include "lltextparser.h"
+#include "llscrollcontainer.h"
+#include "llpanel.h"
#include <queue>
+#include "llcombobox.h"
//
// Globals
@@ -75,19 +78,73 @@ const S32 CURSOR_THICKNESS = 2;
const S32 SPACES_PER_TAB = 4;
-LLUIColor LLTextEditor::mLinkColor = LLColor4::blue;
-void (* LLTextEditor::mURLcallback)(const std::string&) = NULL;
-bool (* LLTextEditor::mSecondlifeURLcallback)(const std::string&) = NULL;
-bool (* LLTextEditor::mSecondlifeURLcallbackRightClick)(const std::string&) = NULL;
+void (* LLTextEditor::sURLcallback)(const std::string&) = NULL;
+bool (* LLTextEditor::sSecondlifeURLcallback)(const std::string&) = NULL;
+bool (* LLTextEditor::sSecondlifeURLcallbackRightClick)(const std::string&) = NULL;
+// 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
+ }
+};
+
+// 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
+ }
+};
+
+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);
+ }
+
+};
+
+//
+// DocumentPanel
+//
+
+class DocumentPanel : public LLPanel
+{
+public:
+ DocumentPanel(const Params&);
+};
+
+DocumentPanel::DocumentPanel(const Params& p)
+: LLPanel(p)
+{}
///////////////////////////////////////////////////////////////////
class LLTextEditor::LLTextCmdInsert : public LLTextEditor::LLTextCmd
{
public:
- LLTextCmdInsert(S32 pos, BOOL group_with_next, const LLWString &ws)
- : LLTextCmd(pos, group_with_next), mWString(ws)
+ LLTextCmdInsert(S32 pos, BOOL group_with_next, const LLWString &ws, LLTextSegmentPtr segment)
+ : LLTextCmd(pos, group_with_next, segment), mWString(ws)
{
}
virtual ~LLTextCmdInsert() {}
@@ -117,8 +174,8 @@ private:
class LLTextEditor::LLTextCmdAddChar : public LLTextEditor::LLTextCmd
{
public:
- LLTextCmdAddChar( S32 pos, BOOL group_with_next, llwchar wc)
- : LLTextCmd(pos, group_with_next), mWString(1, wc), mBlockExtensions(FALSE)
+ LLTextCmdAddChar( S32 pos, BOOL group_with_next, llwchar wc, LLTextSegmentPtr segment)
+ : LLTextCmd(pos, group_with_next, segment), mWString(1, wc), mBlockExtensions(FALSE)
{
}
virtual void blockExtensions()
@@ -127,6 +184,9 @@ public:
}
virtual BOOL canExtend(S32 pos) const
{
+ // cannot extend text with custom segments
+ if (!mSegments.empty()) return FALSE;
+
return !mBlockExtensions && (pos == getPosition() + (S32)mWString.length());
}
virtual BOOL execute( LLTextEditor* editor, S32* delta )
@@ -201,9 +261,10 @@ private:
class LLTextEditor::LLTextCmdRemove : public LLTextEditor::LLTextCmd
{
public:
- LLTextCmdRemove( S32 pos, BOOL group_with_next, S32 len ) :
+ LLTextCmdRemove( S32 pos, BOOL group_with_next, S32 len, segment_vec_t& segments ) :
LLTextCmd(pos, group_with_next), mLen(len)
{
+ std::swap(mSegments, segments);
}
virtual BOOL execute( LLTextEditor* editor, S32* delta )
{
@@ -213,7 +274,7 @@ public:
}
virtual S32 undo( LLTextEditor* editor )
{
- insert(editor, getPosition(), mWString );
+ insert(editor, getPosition(), mWString);
return getPosition() + mWString.length();
}
virtual S32 redo( LLTextEditor* editor )
@@ -233,12 +294,13 @@ LLTextEditor::Params::Params()
max_text_length("max_length", 255),
read_only("read_only", false),
embedded_items("embedded_items", false),
- hide_scrollbar("hide_scrollbar", 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),
- takes_non_scroll_clicks("takes_non_scroll_clicks", true),
+ 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"),
@@ -246,6 +308,8 @@ LLTextEditor::Params::Params()
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
@@ -261,8 +325,6 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p)
mIsSelecting( FALSE ),
mSelectionStart( 0 ),
mSelectionEnd( 0 ),
- mScrolledToBottom( TRUE ),
- mOnScrollEndCallback( NULL ),
mOnScrollEndData( NULL ),
mCursorColor( p.cursor_color() ),
mFgColor( p.text_color() ),
@@ -271,15 +333,14 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p)
mWriteableBgColor( p.bg_writeable_color() ),
mReadOnlyBgColor( p.bg_readonly_color() ),
mFocusBgColor( p.bg_focus_color() ),
+ mLinkColor( p.link_color() ),
mReadOnly(p.read_only),
mWordWrap( p.word_wrap ),
- mShowLineNumbers ( FALSE ),
- mCommitOnFocusLost( FALSE ),
- mHideScrollbarForShortDocs( FALSE ),
- mTakesNonScrollClicks( p.takes_non_scroll_clicks ),
+ mShowLineNumbers ( p.show_line_numbers ),
+ mCommitOnFocusLost( p.commit_on_focus_lost),
mTrackBottom( p.track_bottom ),
mAllowEmbeddedItems( p.embedded_items ),
- mHandleEditKeysDirectly( FALSE ),
+ mHandleEditKeysDirectly( p.handle_edit_keys_directly ),
mMouseDownX(0),
mMouseDownY(0),
mLastSelectionX(-1),
@@ -289,7 +350,8 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p)
mParseHTML(FALSE),
mParseHighlights(FALSE),
mTabsToNextField(p.ignore_tab),
- mGLFont(p.font)
+ mDefaultFont(p.font),
+ mScrollIndex(-1)
{
static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
@@ -298,44 +360,42 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p)
// reset desired x cursor position
mDesiredXPixel = -1;
- updateTextRect();
+ 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);
- S32 line_height = llround( mGLFont->getLineHeight() );
- S32 page_size = mTextRect.getHeight() / line_height;
-
- // Init the scrollbar
- LLRect scroll_rect;
- scroll_rect.setOriginAndSize(
- getRect().getWidth() - scrollbar_size,
- 1,
- scrollbar_size,
- getRect().getHeight() - 1);
- S32 lines_in_doc = getLineCount();
- LLScrollbar::Params sbparams;
- sbparams.name("Scrollbar");
- sbparams.rect(scroll_rect);
- sbparams.orientation(LLScrollbar::VERTICAL);
- sbparams.doc_size(lines_in_doc);
- sbparams.doc_pos(0);
- sbparams.page_size(page_size);
- sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
- mScrollbar = LLUICtrlFactory::create<LLScrollbar> (sbparams);
- mScrollbar->setOnScrollEndCallback(mOnScrollEndCallback, mOnScrollEndData);
- addChild(mScrollbar);
+ updateTextRect();
static LLUICachedControl<S32> text_editor_border ("UITextEditorBorder", 0);
LLViewBorder::Params params;
- params.name("text ed border");
- params.rect(getLocalRect());
- params.bevel_style(LLViewBorder::BEVEL_IN);
- params.border_thickness(text_editor_border);
+ params.name = "text ed border";
+ params.rect = getLocalRect();
+ params.bevel_style = LLViewBorder::BEVEL_IN;
+ params.border_thickness = text_editor_border;
mBorder = LLUICtrlFactory::create<LLViewBorder> (params);
addChild( mBorder );
mBorder->setVisible(!p.hide_border);
- appendText(p.default_text, FALSE, FALSE);
+ createDefaultSegment();
- setHideScrollbarForShortDocs(p.hide_scrollbar);
+ appendText(p.default_text, FALSE, FALSE);
mHTML.clear();
}
@@ -350,16 +410,23 @@ void LLTextEditor::initFromParams( const LLTextEditor::Params& p)
if (p.read_only.isProvided())
{
mReadOnly = p.read_only;
- updateSegments();
- updateAllowingLanguageInput();
}
+
+ 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()
{
- gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit()
+ gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() while LLTextEditor still valid
// Route menu back to the default
if( gEditMenuHandler == this )
@@ -369,8 +436,6 @@ LLTextEditor::~LLTextEditor()
// Scrollbar is deleted by LLView
mHoverSegment = NULL;
- std::for_each(mSegments.begin(), mSegments.end(), DeletePointer());
-
std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer());
}
@@ -379,117 +444,183 @@ LLTextViewModel* LLTextEditor::getViewModel() const
return (LLTextViewModel*)mViewModel.get();
}
-void LLTextEditor::setTrackColor( const LLColor4& color )
-{
- mScrollbar->setTrackColor(color);
-}
-
-void LLTextEditor::setThumbColor( const LLColor4& color )
-{
- mScrollbar->setThumbColor(color);
-}
-
-void LLTextEditor::updateLineStartList(S32 startpos)
+static LLFastTimer::DeclareTimer FTM_TEXT_REFLOW ("Text Reflow");
+void LLTextEditor::reflow(S32 start_index)
{
- updateSegments();
-
- bindEmbeddedChars(mGLFont);
+ if (!mReflowNeeded) return;
- S32 seg_num = mSegments.size();
- S32 seg_idx = 0;
- S32 seg_offset = 0;
+ LLFastTimer ft(FTM_TEXT_REFLOW);
+ static LLUICachedControl<S32> texteditor_vpad_top ("UITextEditorVPadTop", 0);
+ updateSegments();
- if (!mLineStartList.empty())
+ while(mReflowNeeded)
{
- getSegmentAndOffset(startpos, &seg_idx, &seg_offset);
- line_info t(seg_idx, seg_offset);
- line_list_t::iterator iter = std::upper_bound(mLineStartList.begin(), mLineStartList.end(), t, line_info_compare());
- if (iter != mLineStartList.begin()) --iter;
- seg_idx = iter->mSegment;
- seg_offset = iter->mOffset;
- mLineStartList.erase(iter, mLineStartList.end());
- }
+ bool scrolled_to_bottom = mScroller->isAtBottom();
+ mReflowNeeded = FALSE;
- LLWString text(getWText());
- while( seg_idx < seg_num )
- {
- mLineStartList.push_back(line_info(seg_idx,seg_offset));
- BOOL line_ended = FALSE;
- S32 start_x = mShowLineNumbers ? UI_TEXTEDITOR_LINE_NUMBER_MARGIN : 0;
- S32 line_width = start_x;
- while(!line_ended && seg_idx < seg_num)
+ 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())
{
- LLTextSegment* segment = mSegments[seg_idx];
- S32 start_idx = segment->getStart() + seg_offset;
- S32 end_idx = start_idx;
- while (end_idx < segment->getEnd() && text[end_idx] != '\n')
+ 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())
{
- end_idx++;
+ // 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());
}
- if (start_idx == end_idx)
+
+ // reserve enough space for line numbers
+ S32 line_height = mShowLineNumbers ? (S32)(LLFontGL::getFontMonospace()->getLineHeight()) : 0;
+
+ while(seg_iter != mSegments.end())
{
- if (end_idx >= segment->getEnd())
- {
- // empty segment
- seg_idx++;
- seg_offset = 0;
- }
- else
- {
- // empty line
- line_ended = TRUE;
- seg_offset++;
- }
- }
- else
- {
- const llwchar* str = text.c_str() + start_idx;
- S32 drawn = mGLFont->maxDrawableChars(str, (F32)abs(mTextRect.getWidth()) - line_width,
- end_idx - start_idx, mWordWrap, mAllowEmbeddedItems );
- if( 0 == drawn && line_width == start_x)
+ 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')
{
- // If at the beginning of a line, draw at least one character, even if it doesn't all fit.
- drawn = 1;
+ ++end_index;
}
- seg_offset += drawn;
- line_width += mGLFont->getWidth(str, 0, drawn, mAllowEmbeddedItems);
- end_idx = segment->getStart() + seg_offset;
- if (end_idx < segment->getEnd())
+
+ // 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())
{
- line_ended = TRUE;
- if (text[end_idx] == '\n')
+ // 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++; // skip newline
+ 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
{
- // finished with segment
- seg_idx++;
+ // subtract pixels used and increment segment
+ remaining_pixels -= segment->getWidth(seg_offset, character_count);
+ ++seg_iter;
seg_offset = 0;
}
}
}
- }
-
- unbindEmbeddedChars(mGLFont);
- mScrollbar->setDocSize( getLineCount() );
+ // 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);
- if (mHideScrollbarForShortDocs)
- {
- BOOL short_doc = (mScrollbar->getDocSize() <= mScrollbar->getPageSize());
- mScrollbar->setVisible(!short_doc);
- }
+ // 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;
+ }
+ }
- // if scrolled to bottom, stay at bottom
- // unless user is selecting text
- // do this after updating page size
- if (mScrolledToBottom && mTrackBottom && !hasMouseCapture())
- {
- endOfDoc();
+ // 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();
}
////////////////////////////////////////////////////////////
@@ -519,35 +650,53 @@ BOOL LLTextEditor::truncate()
return did_truncate;
}
+void LLTextEditor::clearSegments()
+{
+ mHoverSegment = NULL;
+ mSegments.clear();
+}
+
void LLTextEditor::setText(const LLStringExplicit &utf8str)
{
+ clearSegments();
+
// LLStringUtil::removeCRLF(utf8str);
- mViewModel->setValue(utf8str_removeCRLF(utf8str));
+ getViewModel()->setValue(utf8str_removeCRLF(utf8str));
truncate();
blockUndo();
- setCursorPos(0);
+ createDefaultSegment();
+
+ startOfDoc();
deselect();
needsReflow();
resetDirty();
+
+ onValueChange(0, getLength());
}
void LLTextEditor::setWText(const LLWString &wtext)
{
+ clearSegments();
+
getViewModel()->setDisplay(wtext);
truncate();
blockUndo();
- setCursorPos(0);
+ createDefaultSegment();
+
+ startOfDoc();
deselect();
needsReflow();
resetDirty();
+
+ onValueChange(0, getLength());
}
// virtual
@@ -562,39 +711,7 @@ std::string LLTextEditor::getText() const
{
llwarns << "getText() called on text with embedded items (not supported)" << llendl;
}
- return mViewModel->getValue().asString();
-}
-
-void LLTextEditor::setWordWrap(BOOL b)
-{
- mWordWrap = b;
-
- setCursorPos(0);
- deselect();
-
- needsReflow();
-}
-
-
-void LLTextEditor::setBorderVisible(BOOL b)
-{
- mBorder->setVisible(b);
-}
-
-BOOL LLTextEditor::isBorderVisible() const
-{
- return mBorder->getVisible();
-}
-
-void LLTextEditor::setHideScrollbarForShortDocs(BOOL b)
-{
- mHideScrollbarForShortDocs = b;
-
- if (mHideScrollbarForShortDocs)
- {
- BOOL short_doc = (mScrollbar->getDocSize() <= mScrollbar->getPageSize());
- mScrollbar->setVisible(!short_doc);
- }
+ return getViewModel()->getValue().asString();
}
void LLTextEditor::selectNext(const std::string& search_text_in, BOOL case_insensitive, BOOL wrap)
@@ -619,7 +736,7 @@ void LLTextEditor::selectNext(const std::string& search_text_in, BOOL case_insen
if (selected_text == search_text)
{
// We already have this word selected, we are searching for the next.
- mCursorPos += search_text.size();
+ setCursorPos(mCursorPos + search_text.size());
}
}
@@ -682,9 +799,7 @@ BOOL LLTextEditor::replaceText(const std::string& search_text_in, const std::str
void LLTextEditor::replaceTextAll(const std::string& search_text, const std::string& replace_text, BOOL case_insensitive)
{
- S32 cur_pos = mScrollbar->getDocPos();
-
- setCursorPos(0);
+ startOfDoc();
selectNext(search_text, case_insensitive, FALSE);
BOOL replaced = TRUE;
@@ -692,14 +807,12 @@ void LLTextEditor::replaceTextAll(const std::string& search_text, const std::str
{
replaced = replaceText(search_text,replace_text, case_insensitive, FALSE);
}
-
- mScrollbar->setDocPos(cur_pos);
}
// 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 )
+void LLTextEditor::setCursorAtLocalPos( S32 local_x, S32 local_y, bool round, bool keep_cursor_offset )
{
- setCursorPos(getCursorPosFromLocalCoord(local_x, local_y, round));
+ setCursorPos(getDocIndexFromLocalCoord(local_x, local_y, round), keep_cursor_offset);
}
S32 LLTextEditor::prevWordPos(S32 cursorPos) const
@@ -709,7 +822,7 @@ S32 LLTextEditor::prevWordPos(S32 cursorPos) const
{
cursorPos--;
}
- while( (cursorPos > 0) && isPartOfWord( wtext[cursorPos-1] ) )
+ while( (cursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[cursorPos-1] ) )
{
cursorPos--;
}
@@ -719,7 +832,7 @@ S32 LLTextEditor::prevWordPos(S32 cursorPos) const
S32 LLTextEditor::nextWordPos(S32 cursorPos) const
{
LLWString wtext(getWText());
- while( (cursorPos < getLength()) && isPartOfWord( wtext[cursorPos] ) )
+ while( (cursorPos < getLength()) && LLWStringUtil::isPartOfWord( wtext[cursorPos] ) )
{
cursorPos++;
}
@@ -739,156 +852,316 @@ S32 LLTextEditor::getLineStart( S32 line ) const
}
line = llclamp(line, 0, num_lines-1);
- S32 segidx = mLineStartList[line].mSegment;
- S32 segoffset = mLineStartList[line].mOffset;
- LLTextSegment* seg = mSegments[segidx];
- S32 res = seg->getStart() + segoffset;
- if (res > seg->getEnd()) llerrs << "wtf" << llendl;
- return res;
+ 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 ) const
+void LLTextEditor::getLineAndOffset( S32 startpos, S32* linep, S32* offsetp, bool include_wordwrap) const
{
- if (mLineStartList.empty())
+ if (mLineInfoList.empty())
{
*linep = 0;
*offsetp = startpos;
}
else
{
- S32 seg_idx, seg_offset;
- getSegmentAndOffset( startpos, &seg_idx, &seg_offset );
+ 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;
+ }
+}
- line_info tline(seg_idx, seg_offset);
- line_list_t::const_iterator iter = std::upper_bound(mLineStartList.begin(), mLineStartList.end(), tline, line_info_compare());
- if (iter != mLineStartList.begin()) --iter;
- *linep = iter - mLineStartList.begin();
- S32 line_start = mSegments[iter->mSegment]->getStart() + iter->mOffset;
- *offsetp = startpos - line_start;
+void LLTextEditor::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 LLTextEditor::getSegmentAndOffset( S32 startpos, S32* segidxp, S32* offsetp ) const
+void LLTextEditor::getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp )
{
- if (mSegments.empty())
+ *seg_iter = getSegIterContaining(startpos);
+ if (*seg_iter == mSegments.end())
{
- *segidxp = -1;
- *offsetp = startpos;
+ *offsetp = 0;
+ }
+ else
+ {
+ *offsetp = startpos - (**seg_iter)->getStart();
}
-
- LLTextSegment tseg(startpos);
- segment_list_t::const_iterator seg_iter;
- seg_iter = std::upper_bound(mSegments.begin(), mSegments.end(), &tseg, LLTextSegment::compare());
- if (seg_iter != mSegments.begin()) --seg_iter;
- *segidxp = seg_iter - mSegments.begin();
- *offsetp = startpos - (*seg_iter)->getStart();
}
-const LLTextSegment* LLTextEditor::getPreviousSegment() const
+const LLTextSegmentPtr LLTextEditor::getPreviousSegment() const
{
// find segment index at character to left of cursor (or rightmost edge of selection)
- S32 idx = llmax(0, getSegmentIdxAtOffset(mCursorPos) - 1);
- return idx >= 0 ? mSegments[idx] : NULL;
+ segment_set_t::const_iterator it = mSegments.lower_bound(new LLIndexSegment(mCursorPos));
+
+ if (it != mSegments.end())
+ {
+ return *it;
+ }
+ else
+ {
+ return LLTextSegmentPtr();
+ }
}
-void LLTextEditor::getSelectedSegments(std::vector<const LLTextSegment*>& segments) const
+void LLTextEditor::getSelectedSegments(LLTextEditor::segment_vec_t& segments) const
{
S32 left = hasSelection() ? llmin(mSelectionStart, mSelectionEnd) : mCursorPos;
S32 right = hasSelection() ? llmax(mSelectionStart, mSelectionEnd) : mCursorPos;
- S32 first_idx = llmax(0, getSegmentIdxAtOffset(left));
- S32 last_idx = llmax(0, first_idx, getSegmentIdxAtOffset(right));
- for (S32 idx = first_idx; idx <= last_idx; ++idx)
- {
- segments.push_back(mSegments[idx]);
- }
+ return getSegmentsInRange(segments, left, right, true);
}
-S32 LLTextEditor::getCursorPosFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const
+void LLTextEditor::getSegmentsInRange(LLTextEditor::segment_vec_t& segments_out, S32 start, S32 end, bool include_partial) const
{
- if(mShowLineNumbers)
+ segment_set_t::const_iterator first_it = getSegIterContaining(start);
+ segment_set_t::const_iterator end_it = getSegIterContaining(end - 1);
+ if (end_it != mSegments.end()) ++end_it;
+
+ for (segment_set_t::const_iterator it = first_it; it != end_it; ++it)
{
- local_x -= UI_TEXTEDITOR_LINE_NUMBER_MARGIN;
+ LLTextSegmentPtr segment = *it;
+ if (include_partial
+ || (segment->getStart() >= start
+ && segment->getEnd() <= end))
+ {
+ segments_out.push_back(segment);
+ }
}
+}
- // 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.
+// 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.
- S32 total_lines = getLineCount();
- S32 line_height = llround( mGLFont->getLineHeight() );
- S32 max_visible_lines = mTextRect.getHeight() / line_height;
- S32 scroll_lines = mScrollbar->getDocPos();
- S32 visible_lines = llmin( total_lines - scroll_lines, max_visible_lines ); // Lines currently visible
+ 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());
- //S32 line = S32( 0.5f + ((mTextRect.mTop - local_y) / mGLFont->getLineHeight()) );
- S32 line = (mTextRect.mTop - 1 - local_y) / line_height;
- if (line >= total_lines)
+ if (line_iter == mLineInfoList.end())
{
return getLength(); // past the end
}
- line = llclamp( line, 0, visible_lines ) + scroll_lines;
+ 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;
+ }
- S32 line_start = getLineStart(line);
- S32 next_start = getLineStart(line+1);
- S32 line_end = (next_start != line_start) ? next_start - 1 : getLength();
+ return pos;
+}
- if(line_start == -1)
- {
- return 0;
+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;
}
- else
+
+ // 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())
{
- S32 line_len = line_end - line_start;
- S32 pos;
- LLWString text(getWText());
+ const LLTextSegmentPtr segmentp = *line_seg_iter;
- if (mAllowEmbeddedItems)
+ if (line_seg_iter == cursor_seg_iter)
{
- // Figure out which character we're nearest to.
- bindEmbeddedChars(mGLFont);
- pos = mGLFont->charFromPixelOffset(text.c_str(), line_start,
- (F32)(local_x - mTextRect.mLeft),
- (F32)(mTextRect.getWidth()),
- line_len,
- round, TRUE);
- unbindEmbeddedChars(mGLFont);
+ // 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
{
- pos = mGLFont->charFromPixelOffset(text.c_str(), line_start,
- (F32)(local_x - mTextRect.mLeft),
- (F32)mTextRect.getWidth(),
- line_len,
- round);
+ // 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;
}
-
- return line_start + pos;
}
+
+ local_rect.mRight = local_rect.mLeft;
+
+ return local_rect;
+}
+
+void LLTextEditor::addDocumentChild(LLView* view)
+{
+ mDocumentPanel->addChild(view);
}
-void LLTextEditor::setCursor(S32 row, S32 column)
+void LLTextEditor::removeDocumentChild(LLView* view)
+{
+ mDocumentPanel->removeChild(view);
+}
+
+bool LLTextEditor::setCursor(S32 row, S32 column)
{
- LLWString text(getWText());
- const llwchar* doc = text.c_str();
- const char CR = 10;
- while(row--)
+ if (0 <= row && row < (S32)mLineInfoList.size())
{
- while (CR != *doc++);
+ 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);
}
- doc += column;
- setCursorPos(doc - text.c_str());
+ return false;
}
-void LLTextEditor::setCursorPos(S32 offset)
+bool LLTextEditor::setCursorPos(S32 cursor_pos, bool keep_cursor_offset)
{
- mCursorPos = llclamp(offset, 0, (S32)getLength());
+ 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 = -1;
+ mDesiredXPixel = getLocalRectFromDocIndex(mCursorPos).mLeft;
+}
+
+// constraint cursor to editable segments of document
+S32 LLTextEditor::getEditableIndex(S32 index, bool increasing_direction)
+{
+ //// always allow editable position at end of doc
+ //if (index == getLength())
+ //{
+ // return index;
+ //}
+
+ 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
@@ -1029,7 +1302,7 @@ void LLTextEditor::indentSelectedLines( S32 spaces )
}
right += delta_spaces;
- //text = mWText;
+ text = getWText();
// Find the next new line
while( (cur < right) && (text[cur] != '\n') )
@@ -1055,7 +1328,7 @@ void LLTextEditor::indentSelectedLines( S32 spaces )
mSelectionStart = right;
mSelectionEnd = left;
}
- mCursorPos = mSelectionEnd;
+ setCursorPos(mSelectionEnd);
}
}
@@ -1070,7 +1343,7 @@ void LLTextEditor::selectAll()
{
mSelectionStart = getLength();
mSelectionEnd = 0;
- mCursorPos = mSelectionEnd;
+ setCursorPos(mSelectionEnd);
}
@@ -1088,12 +1361,7 @@ BOOL LLTextEditor::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_
}
}
- if( mSegments.empty() )
- {
- return TRUE;
- }
-
- const LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y );
+ const LLTextSegmentPtr cur_segment = getSegmentAtLocalPos( x, y );
if( cur_segment )
{
BOOL has_tool_tip = FALSE;
@@ -1114,12 +1382,6 @@ BOOL LLTextEditor::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_
return TRUE;
}
-BOOL LLTextEditor::handleScrollWheel(S32 x, S32 y, S32 clicks)
-{
- // Pretend the mouse is over the scrollbar
- return mScrollbar->handleScrollWheel( 0, 0, clicks );
-}
-
BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
{
BOOL handled = FALSE;
@@ -1127,7 +1389,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
// Let scrollbar have first dibs
handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
- if( !handled && mTakesNonScrollClicks)
+ if( !handled )
{
if (!(mask & MASK_SHIFT))
{
@@ -1141,31 +1403,10 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
if (mask & MASK_SHIFT)
{
S32 old_cursor_pos = mCursorPos;
- setCursorAtLocalPos( x, y, TRUE );
+ setCursorAtLocalPos( x, y, true );
if (hasSelection())
{
- /* Mac-like behavior - extend selection towards the cursor
- if (mCursorPos < mSelectionStart
- && mCursorPos < mSelectionEnd)
- {
- // ...left of selection
- mSelectionStart = llmax(mSelectionStart, mSelectionEnd);
- mSelectionEnd = mCursorPos;
- }
- else if (mCursorPos > mSelectionStart
- && mCursorPos > mSelectionEnd)
- {
- // ...right of selection
- mSelectionStart = llmin(mSelectionStart, mSelectionEnd);
- mSelectionEnd = mCursorPos;
- }
- else
- {
- mSelectionEnd = mCursorPos;
- }
- */
- // Windows behavior
mSelectionEnd = mCursorPos;
}
else
@@ -1178,7 +1419,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
}
else
{
- setCursorAtLocalPos( x, y, TRUE );
+ setCursorAtLocalPos( x, y, true );
startSelection();
}
gFocusMgr.setMouseCapture( this );
@@ -1202,11 +1443,17 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
BOOL LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
{
- setFocus( TRUE );
- if( canPastePrimary() )
+ BOOL handled = FALSE;
+ handled = childrenHandleMiddleMouseDown(x, y, mask) != NULL;
+
+ if (!handled)
{
- setCursorAtLocalPos( x, y, TRUE );
- pastePrimary();
+ setFocus( TRUE );
+ if( canPastePrimary() )
+ {
+ setCursorAtLocalPos( x, y, true );
+ pastePrimary();
+ }
}
return TRUE;
}
@@ -1217,7 +1464,12 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask)
static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
BOOL handled = FALSE;
+ if (mHoverSegment)
+ {
+ mHoverSegment->setHasMouseHover(false);
+ }
mHoverSegment = NULL;
+
if(hasMouseCapture() )
{
if( mIsSelecting )
@@ -1228,17 +1480,11 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask)
mLastSelectionY = y;
}
- if( y > mTextRect.mTop )
- {
- mScrollbar->setDocPos( mScrollbar->getDocPos() - 1 );
- }
- else
- if( y < mTextRect.mBottom )
- {
- mScrollbar->setDocPos( mScrollbar->getDocPos() + 1 );
- }
+ mScroller->autoScroll(x, y);
- setCursorAtLocalPos( x, y, TRUE );
+ S32 clamped_x = llclamp(x, mTextRect.mLeft, mTextRect.mRight);
+ S32 clamped_y = llclamp(y, mTextRect.mBottom, mTextRect.mTop);
+ setCursorAtLocalPos( clamped_x, clamped_y, true );
mSelectionEnd = mCursorPos;
}
@@ -1260,51 +1506,43 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask)
}
// Opaque
- if( !handled && mTakesNonScrollClicks)
+ if( !handled )
{
// Check to see if we're over an HTML-style link
- if( !mSegments.empty() )
+ LLTextSegmentPtr cur_segment = getSegmentAtLocalPos( x, y );
+ if( cur_segment )
{
- const LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y );
- if( cur_segment )
+ if(cur_segment->getStyle()->isLink())
{
- if(cur_segment->getStyle()->isLink())
- {
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over link, inactive)" << llendl;
- getWindow()->setCursor(UI_CURSOR_HAND);
- handled = TRUE;
- }
- else
- if(cur_segment->getStyle()->getIsEmbeddedItem())
- {
- lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over embedded item, inactive)" << llendl;
- getWindow()->setCursor(UI_CURSOR_HAND);
- //getWindow()->setCursor(UI_CURSOR_ARROW);
- handled = TRUE;
- }
- mHoverSegment = cur_segment;
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over link, inactive)" << llendl;
+ getWindow()->setCursor(UI_CURSOR_HAND);
+ handled = TRUE;
+ }
+ //else
+ //if(cur_segment->getStyle()->getIsEmbeddedItem())
+ //{
+ // lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over embedded item, inactive)" << llendl;
+ // getWindow()->setCursor(UI_CURSOR_HAND);
+ // //getWindow()->setCursor(UI_CURSOR_ARROW);
+ // handled = TRUE;
+ //}
+ if (mHoverSegment)
+ {
+ mHoverSegment->setHasMouseHover(false);
}
+ cur_segment->setHasMouseHover(true);
+ mHoverSegment = cur_segment;
+ mHTML = mHoverSegment->getStyle()->getLinkHREF();
}
if( !handled )
{
lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
- if (!mScrollbar->getVisible() || x < getRect().getWidth() - scrollbar_size)
- {
- getWindow()->setCursor(UI_CURSOR_IBEAM);
- }
- else
- {
- getWindow()->setCursor(UI_CURSOR_ARROW);
- }
+ getWindow()->setCursor(UI_CURSOR_IBEAM);
handled = TRUE;
}
}
- if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()))
- {
- mOnScrollEndCallback(mOnScrollEndData);
- }
return handled;
}
@@ -1316,22 +1554,14 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)
// let scrollbar have first dibs
handled = LLView::childrenHandleMouseUp(x, y, mask) != NULL;
- if( !handled && mTakesNonScrollClicks)
+ if( !handled )
{
if( mIsSelecting )
{
- // Finish selection
- if( y > mTextRect.mTop )
- {
- mScrollbar->setDocPos( mScrollbar->getDocPos() - 1 );
- }
- else
- if( y < mTextRect.mBottom )
- {
- mScrollbar->setDocPos( mScrollbar->getDocPos() + 1 );
- }
-
- setCursorAtLocalPos( x, y, TRUE );
+ mScroller->autoScroll(x, y);
+ S32 clamped_x = llclamp(x, mTextRect.mLeft, mTextRect.mRight);
+ S32 clamped_y = llclamp(y, mTextRect.mBottom, mTextRect.mTop);
+ setCursorAtLocalPos( clamped_x, clamped_y, true );
endSelection();
}
@@ -1367,25 +1597,25 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
// let scrollbar have first dibs
handled = LLView::childrenHandleDoubleClick(x, y, mask) != NULL;
- if( !handled && mTakesNonScrollClicks)
+ if( !handled )
{
- setCursorAtLocalPos( x, y, FALSE );
+ setCursorAtLocalPos( x, y, false );
deselect();
LLWString text = getWText();
- if( isPartOfWord( text[mCursorPos] ) )
+ if( LLWStringUtil::isPartOfWord( text[mCursorPos] ) )
{
// Select word the cursor is over
- while ((mCursorPos > 0) && isPartOfWord(text[mCursorPos-1]))
+ while ((mCursorPos > 0) && LLWStringUtil::isPartOfWord(text[mCursorPos-1]))
{
- mCursorPos--;
+ if (!setCursorPos(mCursorPos - 1)) break;
}
startSelection();
- while ((mCursorPos < (S32)text.length()) && isPartOfWord( text[mCursorPos] ) )
+ while ((mCursorPos < (S32)text.length()) && LLWStringUtil::isPartOfWord( text[mCursorPos] ) )
{
- mCursorPos++;
+ if (!setCursorPos(mCursorPos + 1)) break;
}
mSelectionEnd = mCursorPos;
@@ -1394,7 +1624,7 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
{
// Select the character the cursor is over
startSelection();
- mCursorPos++;
+ setCursorPos(mCursorPos + 1);
mSelectionEnd = mCursorPos;
}
@@ -1457,19 +1687,25 @@ S32 LLTextEditor::execute( LLTextCmd* cmd )
return delta;
}
-S32 LLTextEditor::insert(const S32 pos, const LLWString &wstr, const BOOL group_with_next_op)
+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 ) );
+ return execute( new LLTextCmdInsert( pos, group_with_next_op, wstr, segment ) );
}
-S32 LLTextEditor::remove(const S32 pos, const S32 length, const BOOL group_with_next_op)
+S32 LLTextEditor::remove(S32 pos, S32 length, bool group_with_next_op)
{
- return execute( new LLTextCmdRemove( pos, group_with_next_op, length ) );
+ S32 end_pos = getEditableIndex(pos + length, true);
+
+ segment_vec_t segments_to_remove;
+ // 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, const BOOL group_with_next_op)
+S32 LLTextEditor::append(const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment)
{
- return insert(getLength(), wstr, group_with_next_op);
+ return insert(getLength(), wstr, group_with_next_op, segment);
}
S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc)
@@ -1575,7 +1811,7 @@ S32 LLTextEditor::addChar(S32 pos, llwchar wc)
}
else
{
- return execute(new LLTextCmdAddChar(pos, FALSE, wc));
+ return execute(new LLTextCmdAddChar(pos, FALSE, wc, LLTextSegmentPtr()));
}
}
@@ -1612,10 +1848,10 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask)
if( 0 < mCursorPos )
{
startSelection();
- mCursorPos--;
+ setCursorPos(mCursorPos - 1);
if( mask & MASK_CONTROL )
{
- mCursorPos = prevWordPos(mCursorPos);
+ setCursorPos(prevWordPos(mCursorPos));
}
mSelectionEnd = mCursorPos;
}
@@ -1625,10 +1861,10 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask)
if( mCursorPos < getLength() )
{
startSelection();
- mCursorPos++;
+ setCursorPos(mCursorPos + 1);
if( mask & MASK_CONTROL )
{
- mCursorPos = nextWordPos(mCursorPos);
+ setCursorPos(nextWordPos(mCursorPos));
}
mSelectionEnd = mCursorPos;
}
@@ -1650,7 +1886,7 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask)
startSelection();
if( mask & MASK_CONTROL )
{
- mCursorPos = 0;
+ setCursorPos(0);
}
else
{
@@ -1675,7 +1911,7 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask)
startSelection();
if( mask & MASK_CONTROL )
{
- mCursorPos = getLength();
+ setCursorPos(getLength());
}
else
{
@@ -1726,14 +1962,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
switch( key )
{
case KEY_UP:
- if (mReadOnly)
- {
- mScrollbar->setDocPos(mScrollbar->getDocPos() - 1);
- }
- else
- {
- changeLine( -1 );
- }
+ changeLine( -1 );
break;
case KEY_PAGE_UP:
@@ -1741,25 +1970,11 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
break;
case KEY_HOME:
- if (mReadOnly)
- {
- mScrollbar->setDocPos(0);
- }
- else
- {
- startOfLine();
- }
+ startOfLine();
break;
case KEY_DOWN:
- if (mReadOnly)
- {
- mScrollbar->setDocPos(mScrollbar->getDocPos() + 1);
- }
- else
- {
- changeLine( 1 );
- }
+ changeLine( 1 );
break;
case KEY_PAGE_DOWN:
@@ -1767,21 +1982,10 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
break;
case KEY_END:
- if (mReadOnly)
- {
- mScrollbar->setDocPos(mScrollbar->getDocPosMax());
- }
- else
- {
- endOfLine();
- }
+ endOfLine();
break;
case KEY_LEFT:
- if (mReadOnly)
- {
- break;
- }
if( hasSelection() )
{
setCursorPos(llmin( mCursorPos - 1, mSelectionStart, mSelectionEnd ));
@@ -1800,10 +2004,6 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
break;
case KEY_RIGHT:
- if (mReadOnly)
- {
- break;
- }
if( hasSelection() )
{
setCursorPos(llmax( mCursorPos + 1, mSelectionStart, mSelectionEnd ));
@@ -1827,10 +2027,6 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
}
}
- if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()))
- {
- mOnScrollEndCallback(mOnScrollEndData);
- }
return handled;
}
@@ -1967,7 +2163,7 @@ void LLTextEditor::pasteHelper(bool is_primary)
}
// Insert the new text into the existing text.
- setCursorPos(mCursorPos + insert(mCursorPos, clean_string, FALSE));
+ setCursorPos(mCursorPos + insert(mCursorPos, clean_string, FALSE, LLTextSegmentPtr()));
deselect();
needsReflow();
@@ -2014,7 +2210,7 @@ BOOL LLTextEditor::handleControlKey(const KEY key, const MASK mask)
if( mask & MASK_SHIFT )
{
startSelection();
- mCursorPos = 0;
+ setCursorPos(0);
mSelectionEnd = mCursorPos;
}
else
@@ -2022,7 +2218,7 @@ BOOL LLTextEditor::handleControlKey(const KEY key, const MASK mask)
// Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down
// all move the cursor as if clicking, so should deselect.
deselect();
- setCursorPos(0);
+ startOfDoc();
}
break;
@@ -2251,42 +2447,87 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask )
BOOL return_key_hit = FALSE;
BOOL text_may_have_changed = TRUE;
- if ( gFocusMgr.getKeyboardFocus() == this )
+ // Special case for TAB. If want to move to next field, report
+ // not handled and let the parent take care of field movement.
+ if (KEY_TAB == key && mTabsToNextField)
{
- // Special case for TAB. If want to move to next field, report
- // not handled and let the parent take care of field movement.
- if (KEY_TAB == key && mTabsToNextField)
- {
- return FALSE;
- }
+ return FALSE;
+ }
+ /*
+ if (KEY_F10 == key)
+ {
+ LLComboBox::Params cp;
+ cp.name = "combo box";
+ cp.label = "my combo";
+ cp.rect.width = 100;
+ cp.rect.height = 20;
+ cp.items.add().label = "item 1";
+ cp.items.add().label = "item 2";
+ cp.items.add().label = "item 3";
+
+ appendWidget(LLUICtrlFactory::create<LLComboBox>(cp), "combo", true, false);
+ }
+ if (KEY_F11 == key)
+ {
+ LLButton::Params bp;
+ bp.name = "text button";
+ bp.label = "Click me";
+ bp.rect.width = 100;
+ bp.rect.height = 20;
+ appendWidget(LLUICtrlFactory::create<LLButton>(bp), "button", true, false);
+ }
+ */
+ if (mReadOnly)
+ {
+ handled = mScroller->handleKeyHere( key, mask );
+ }
+ else
+ {
+ // handle navigation keys ourself
handled = handleNavigationKey( key, mask );
+ }
+
+
+ if( handled )
+ {
+ text_may_have_changed = FALSE;
+ }
+
+ if( !handled )
+ {
+ handled = handleSelectionKey( key, mask );
if( handled )
{
- text_may_have_changed = FALSE;
+ selection_modified = TRUE;
}
-
- if( !handled )
+ }
+
+ if( !handled )
+ {
+ handled = handleControlKey( key, mask );
+ if( handled )
{
- handled = handleSelectionKey( key, mask );
- if( handled )
- {
- selection_modified = TRUE;
- }
+ selection_modified = TRUE;
}
-
- if( !handled )
+ }
+
+ if( !handled && mHandleEditKeysDirectly )
+ {
+ handled = handleEditKey( key, mask );
+ if( handled )
{
- handled = handleControlKey( key, mask );
- if( handled )
- {
- selection_modified = TRUE;
- }
+ selection_modified = TRUE;
+ text_may_have_changed = TRUE;
}
+ }
- if( !handled && mHandleEditKeysDirectly )
+ // Handle most keys only if the text editor is writeable.
+ if( !mReadOnly )
+ {
+ if( !handled )
{
- handled = handleEditKey( key, mask );
+ handled = handleSpecialKey( key, mask, &return_key_hit );
if( handled )
{
selection_modified = TRUE;
@@ -2294,41 +2535,27 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask )
}
}
- // Handle most keys only if the text editor is writeable.
- if( !mReadOnly )
- {
- if( !handled )
- {
- handled = handleSpecialKey( key, mask, &return_key_hit );
- if( handled )
- {
- selection_modified = TRUE;
- text_may_have_changed = TRUE;
- }
- }
+ }
- }
+ if( handled )
+ {
+ resetKeystrokeTimer();
- if( handled )
+ // Most keystrokes will make the selection box go away, but not all will.
+ if( !selection_modified &&
+ KEY_SHIFT != key &&
+ KEY_CONTROL != key &&
+ KEY_ALT != key &&
+ KEY_CAPSLOCK )
{
- resetKeystrokeTimer();
-
- // Most keystrokes will make the selection box go away, but not all will.
- if( !selection_modified &&
- KEY_SHIFT != key &&
- KEY_CONTROL != key &&
- KEY_ALT != key &&
- KEY_CAPSLOCK )
- {
- deselect();
- }
+ deselect();
+ }
- if(text_may_have_changed)
- {
- needsReflow();
- }
- needsScroll();
+ if(text_may_have_changed)
+ {
+ needsReflow();
}
+ needsScroll();
}
return handled;
@@ -2344,34 +2571,31 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char)
BOOL handled = FALSE;
- if ( gFocusMgr.getKeyboardFocus() == this )
+ // Handle most keys only if the text editor is writeable.
+ if( !mReadOnly )
{
- // Handle most keys only if the text editor is writeable.
- if( !mReadOnly )
+ if( '}' == uni_char )
{
- if( '}' == uni_char )
- {
- unindentLineBeforeCloseBrace();
- }
+ unindentLineBeforeCloseBrace();
+ }
- // TODO: KLW Add auto show of tool tip on (
- addChar( uni_char );
+ // TODO: KLW Add auto show of tool tip on (
+ addChar( uni_char );
- // Keys that add characters temporarily hide the cursor
- getWindow()->hideCursorUntilMouseMove();
+ // Keys that add characters temporarily hide the cursor
+ getWindow()->hideCursorUntilMouseMove();
- handled = TRUE;
- }
+ handled = TRUE;
+ }
- if( handled )
- {
- resetKeystrokeTimer();
+ if( handled )
+ {
+ resetKeystrokeTimer();
- // Most keystrokes will make the selection box go away, but not all will.
- deselect();
+ // Most keystrokes will make the selection box go away, but not all will.
+ deselect();
- needsReflow();
- }
+ needsReflow();
}
return handled;
@@ -2544,6 +2768,12 @@ void LLTextEditor::onFocusLost()
LLUICtrl::onFocusLost();
}
+void LLTextEditor::onCommit()
+{
+ setControlValue(getValue());
+ LLUICtrl::onCommit();
+}
+
void LLTextEditor::setEnabled(BOOL enabled)
{
// just treat enabled as read-only flag
@@ -2560,300 +2790,187 @@ void LLTextEditor::drawBackground()
{
S32 left = 0;
S32 top = getRect().getHeight();
- S32 right = getRect().getWidth();
S32 bottom = 0;
LLColor4 bg_color = mReadOnly ? mReadOnlyBgColor.get()
- : gFocusMgr.getKeyboardFocus() == this ? mFocusBgColor.get() : mWriteableBgColor.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, right, bottom, bg_color); // body text area to the right of line numbers
gl_rect_2d(UI_TEXTEDITOR_LINE_NUMBER_MARGIN, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN-1, bottom, LLColor4::grey3); // separator
- } else {
- gl_rect_2d(left, top, right, bottom, bg_color); // body text area
- }
-
- LLView::draw();
+ }
}
// 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() )
+ if( hasSelection() && !mLineInfoList.empty())
{
LLWString text = getWText();
- const S32 text_len = getLength();
- std::queue<S32> line_endings;
-
- S32 line_height = llround( mGLFont->getLineHeight() );
+ std::vector<LLRect> selection_rects;
S32 selection_left = llmin( mSelectionStart, mSelectionEnd );
S32 selection_right = llmax( mSelectionStart, mSelectionEnd );
- S32 selection_left_x = mTextRect.mLeft;
- S32 selection_left_y = mTextRect.mTop - line_height;
- S32 selection_right_x = mTextRect.mRight;
- S32 selection_right_y = mTextRect.mBottom;
-
- BOOL selection_left_visible = FALSE;
- BOOL selection_right_visible = FALSE;
+ LLRect selection_rect = mTextRect;
// Skip through the lines we aren't drawing.
- S32 cur_line = mScrollbar->getDocPos();
+ LLRect content_display_rect = mScroller->getVisibleContentRect();
- S32 left_line_num = cur_line;
- S32 num_lines = getLineCount();
- S32 right_line_num = num_lines - 1;
+ // 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());
- S32 line_start = -1;
- if (cur_line >= num_lines)
- {
- return;
- }
-
- line_start = getLineStart(cur_line);
-
- S32 left_visible_pos = line_start;
- S32 right_visible_pos = line_start;
-
- S32 text_y = mTextRect.mTop - line_height;
+ bool done = false;
// Find the coordinates of the selected area
- while((cur_line < num_lines))
+ for (;line_iter != end_iter && !done; ++line_iter)
{
- S32 next_line = -1;
- S32 line_end = text_len;
-
- if ((cur_line + 1) < num_lines)
+ // is selection visible on this line?
+ if (line_iter->mDocIndexEnd > selection_left && line_iter->mDocIndexStart < selection_right)
{
- next_line = getLineStart(cur_line + 1);
- line_end = next_line;
-
- line_end = ( (line_end - line_start)==0 || text[next_line-1] == '\n' || text[next_line-1] == '\0' || text[next_line-1] == ' ' || text[next_line-1] == '\t' ) ? next_line-1 : next_line;
- }
+ 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;
- const llwchar* line = text.c_str() + line_start;
+ S32 segment_line_start = segmentp->getStart() + segment_offset;
+ S32 segment_line_end = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd);
- if( line_start <= selection_left && selection_left <= line_end )
- {
- left_line_num = cur_line;
- selection_left_visible = TRUE;
- selection_left_x = mTextRect.mLeft + mGLFont->getWidth(line, 0, selection_left - line_start, mAllowEmbeddedItems);
- selection_left_y = text_y;
- }
- if( line_start <= selection_right && selection_right <= line_end )
- {
- right_line_num = cur_line;
- selection_right_visible = TRUE;
- selection_right_x = mTextRect.mLeft + mGLFont->getWidth(line, 0, selection_right - line_start, mAllowEmbeddedItems);
- if (selection_right == line_end)
- {
- // add empty space for "newline"
- //selection_right_x += mGLFont->getWidth("n");
- }
- selection_right_y = text_y;
- }
-
- // if selection spans end of current line...
- if (selection_left <= line_end && line_end < selection_right && selection_left != selection_right)
- {
- // extend selection slightly beyond end of line
- // to indicate selection of newline character (use "n" character to determine width)
- const LLWString nstr(utf8str_to_wstring(std::string("n")));
- line_endings.push(mTextRect.mLeft + mGLFont->getWidth(line, 0, line_end - line_start, mAllowEmbeddedItems) + mGLFont->getWidth(nstr.c_str()));
- }
-
- // move down one line
- text_y -= line_height;
+ // 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);
+ }
- right_visible_pos = line_end;
- line_start = next_line;
- cur_line++;
+ // 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);
- if (selection_right_visible)
- {
- break;
+ 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).
- BOOL selection_visible = (left_visible_pos <= selection_right) && (selection_left <= right_visible_pos);
- if( selection_visible )
+ 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)
{
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- const LLColor4& color = mReadOnly ? mReadOnlyBgColor.get() : mWriteableBgColor.get();
- F32 alpha = hasFocus() ? 1.f : 0.5f;
- gGL.color4f( 1.f - color.mV[0], 1.f - color.mV[1], 1.f - color.mV[2], alpha );
- S32 margin_offset = mShowLineNumbers ? UI_TEXTEDITOR_LINE_NUMBER_MARGIN : 0;
-
- if( selection_left_y == selection_right_y )
- {
- // Draw from selection start to selection end
- gl_rect_2d( selection_left_x + margin_offset, selection_left_y + line_height + 1,
- selection_right_x + margin_offset, selection_right_y);
- }
- else
- {
- // Draw from selection start to the end of the first line
- if( mTextRect.mRight == selection_left_x )
- {
- selection_left_x -= CURSOR_THICKNESS;
- }
-
- S32 line_end = line_endings.front();
- line_endings.pop();
- gl_rect_2d( selection_left_x + margin_offset, selection_left_y + line_height + 1,
- line_end + margin_offset, selection_left_y );
-
- S32 line_num = left_line_num + 1;
- while(line_endings.size())
- {
- S32 vert_offset = -(line_num - left_line_num) * line_height;
- // Draw the block between the two lines
- gl_rect_2d( mTextRect.mLeft + margin_offset, selection_left_y + vert_offset + line_height + 1,
- line_endings.front() + margin_offset, selection_left_y + vert_offset);
- line_endings.pop();
- line_num++;
- }
-
- // Draw from the start of the last line to selection end
- if( mTextRect.mLeft == selection_right_x )
- {
- selection_right_x += CURSOR_THICKNESS;
- }
- gl_rect_2d( mTextRect.mLeft + margin_offset, selection_right_y + line_height + 1,
- selection_right_x + margin_offset, selection_right_y );
- }
+ 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( gFocusMgr.getKeyboardFocus() == this
- && gShowTextEditCursor && !mReadOnly)
+ if( hasFocus()
+ && gFocusMgr.getAppHasFocus()
+ && !mReadOnly)
{
- LLWString text = getWText();
- const S32 text_len = getLength();
+ LLWString wtext = getWText();
+ const llwchar* text = wtext.c_str();
- // Skip through the lines we aren't drawing.
- S32 cur_pos = mScrollbar->getDocPos();
+ 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;
- S32 num_lines = getLineCount();
- if (cur_pos >= num_lines)
+ if (seg_it != mSegments.end())
{
+ segmentp = *seg_it;
+ }
+ else
+ {
+ //segmentp = mSegments.back();
return;
}
- S32 line_start = getLineStart(cur_pos);
-
- F32 line_height = mGLFont->getLineHeight();
- F32 text_y = (F32)(mTextRect.mTop) - line_height;
- F32 cursor_left = 0.f;
- F32 next_char_left = 0.f;
- F32 cursor_bottom = 0.f;
- BOOL cursor_visible = FALSE;
-
- S32 line_end = 0;
- // Determine if the cursor is visible and if so what its coordinates are.
- while( (mTextRect.mBottom <= llround(text_y)) && (cur_pos < num_lines))
+ // 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) )
{
- line_end = text_len + 1;
- S32 next_line = -1;
- if ((cur_pos + 1) < num_lines)
+ if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
{
- next_line = getLineStart(cur_pos + 1);
- line_end = next_line - 1;
+ S32 width = llmax(CURSOR_THICKNESS, segmentp->getWidth(mCursorPos - segmentp->getStart(), 1));
+ cursor_rect.mRight = cursor_rect.mLeft + width;
}
-
- const llwchar* line = text.c_str() + line_start;
-
- // Find the cursor and selection bounds
- if( line_start <= mCursorPos && mCursorPos <= line_end )
+ else
{
- cursor_visible = TRUE;
- next_char_left = (F32)mTextRect.mLeft + mGLFont->getWidthF32(line, 0, mCursorPos - line_start, mAllowEmbeddedItems );
- cursor_left = next_char_left - 1.f;
- cursor_bottom = text_y;
- break;
+ cursor_rect.mRight = cursor_rect.mLeft + CURSOR_THICKNESS;
}
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- // move down one line
- text_y -= line_height;
- line_start = next_line;
- cur_pos++;
- }
-
- if(mShowLineNumbers)
- {
- cursor_left += UI_TEXTEDITOR_LINE_NUMBER_MARGIN;
- }
+ gGL.color4fv( mCursorColor.get().mV );
+
+ gl_rect_2d(cursor_rect);
- // Draw the cursor
- if( cursor_visible )
- {
- // (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() && text[mCursorPos] != '\n')
{
- F32 cursor_top = cursor_bottom + line_height + 1.f;
- F32 cursor_right = cursor_left + (F32)CURSOR_THICKNESS;
- if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
+ LLColor4 text_color;
+ const LLFontGL* fontp;
+ if (segmentp)
{
- cursor_left += CURSOR_THICKNESS;
- const LLWString space(utf8str_to_wstring(std::string(" ")));
- F32 spacew = mGLFont->getWidthF32(space.c_str());
- if (mCursorPos == line_end)
- {
- cursor_right = cursor_left + spacew;
- }
- else
- {
- F32 width = mGLFont->getWidthF32(text.c_str(), mCursorPos, 1, mAllowEmbeddedItems);
- cursor_right = cursor_left + llmax(spacew, width);
- }
+ text_color = segmentp->getColor();
+ fontp = segmentp->getStyle()->getFont();
}
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
- gGL.color4fv( mCursorColor.get().mV );
-
- gl_rect_2d(llfloor(cursor_left), llfloor(cursor_top),
- llfloor(cursor_right), llfloor(cursor_bottom));
-
- if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection() && text[mCursorPos] != '\n')
+ else if (mReadOnly)
{
- const LLTextSegment* segmentp = getSegmentAtOffset(mCursorPos);
- LLColor4 text_color;
- if (segmentp)
- {
- text_color = segmentp->getColor();
- }
- else if (mReadOnly)
- {
- text_color = mReadOnlyFgColor.get();
- }
- else
- {
- text_color = mFgColor.get();
- }
- mGLFont->render(text, mCursorPos, next_char_left, cursor_bottom + line_height,
- LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], 1.f),
- LLFontGL::LEFT, LLFontGL::TOP,
- LLFontGL::NORMAL,
- LLFontGL::NO_SHADOW,
- 1);
+ 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_left), screen_pos.mBottom + llfloor(cursor_top) );
+ // 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 );
- }
+ 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 );
}
}
}
@@ -2879,13 +2996,13 @@ void LLTextEditor::drawPreeditMarker()
const S32 text_len = getLength();
const S32 num_lines = getLineCount();
- S32 cur_line = mScrollbar->getDocPos();
+ S32 cur_line = getFirstVisibleLine();
if (cur_line >= num_lines)
{
return;
}
- const S32 line_height = llround( mGLFont->getLineHeight() );
+ const S32 line_height = llround( mDefaultFont->getLineHeight() );
S32 line_start = getLineStart(cur_line);
S32 line_y = mTextRect.mTop - line_height;
@@ -2924,16 +3041,16 @@ void LLTextEditor::drawPreeditMarker()
S32 preedit_left = mTextRect.mLeft;
if (left > line_start)
{
- preedit_left += mGLFont->getWidth(text, line_start, left - line_start, mAllowEmbeddedItems);
+ preedit_left += mDefaultFont->getWidth(text, line_start, left - line_start);
}
S32 preedit_right = mTextRect.mLeft;
if (right < line_end)
{
- preedit_right += mGLFont->getWidth(text, line_start, right - line_start, mAllowEmbeddedItems);
+ preedit_right += mDefaultFont->getWidth(text, line_start, right - line_start);
}
else
{
- preedit_right += mGLFont->getWidth(text, line_start, line_end - line_start, mAllowEmbeddedItems);
+ preedit_right += mDefaultFont->getWidth(text, line_start, line_end - line_start);
}
if (mPreeditStandouts[i])
@@ -2981,25 +3098,34 @@ void LLTextEditor::drawText()
}
LLGLSUIDefault gls_ui;
-
- S32 cur_line = mScrollbar->getDocPos();
+ LLRect scrolled_view_rect = mScroller->getVisibleContentRect();
+ LLRect content_rect = mScroller->getContentWindowRect();
+ S32 first_line = getFirstVisibleLine();
S32 num_lines = getLineCount();
- if (cur_line >= num_lines)
+ if (first_line >= num_lines)
{
return;
}
- S32 line_start = getLineStart(cur_line);
- LLTextSegment t(line_start);
- segment_list_t::iterator seg_iter;
- seg_iter = std::upper_bound(mSegments.begin(), mSegments.end(), &t, LLTextSegment::compare());
- if (seg_iter == mSegments.end() || (*seg_iter)->getStart() > line_start) --seg_iter;
- LLTextSegment* cur_segment = *seg_iter;
-
- S32 line_height = llround( mGLFont->getLineHeight() );
- F32 text_y = (F32)(mTextRect.mTop - line_height);
- while((mTextRect.mBottom <= text_y) && (cur_line < num_lines))
+ 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;
@@ -3012,9 +3138,13 @@ void LLTextEditor::drawText()
{
--line_end;
}
-
- F32 text_x = (F32)mTextRect.mLeft;
+ 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 )
{
@@ -3024,184 +3154,120 @@ void LLTextEditor::drawText()
if (seg_iter == mSegments.end())
{
llwarns << "Ran off the segmentation end!" << llendl;
+
return;
}
cur_segment = *seg_iter;
}
- // 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->isImage() && (cur_segment->getStart() >= seg_start) && (cur_segment->getStart() <= clipped_end))
- {
- S32 style_image_height = style->mImageHeight;
- S32 style_image_width = style->mImageWidth;
- LLUIImagePtr image = style->getImage();
- image->draw(llround(text_x), llround(text_y)+line_height-style_image_height,
- style_image_width, style_image_height);
- }
-
- if (cur_segment == mHoverSegment && style->getIsEmbeddedItem())
- {
- style->mUnderline = TRUE;
- }
-
- S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
-
- if ( (mParseHTML) && (left_pos > seg_start) && (left_pos < clipped_end) && mIsSelecting && (mSelectionStart == mSelectionEnd) )
- {
- mHTML = style->getLinkHREF();
- }
+ 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));
- drawClippedSegment( text, seg_start, clipped_end, text_x, text_y, selection_left, selection_right, style, &text_x );
-
- // Note: text_x is incremented by drawClippedSegment()
- seg_start += clipped_len;
- }
+ seg_start = clipped_end + cur_segment->getStart();
}
- // move down one line
- text_y -= (F32)line_height;
-
line_start = next_start;
- cur_line++;
}
}
-
-// Draws a single text segment, reversing the color for selection if needed.
-void LLTextEditor::drawClippedSegment(const LLWString &text, S32 seg_start, S32 seg_end, F32 x, F32 y, S32 selection_left, S32 selection_right, const LLStyleSP& style, F32* right_x )
+void LLTextEditor::drawLineNumbers()
{
- if (!style->isVisible())
- {
- return;
- }
-
- const LLFontGL* font = mGLFont;
-
- LLColor4 color = style->getColor();
+ LLGLSUIDefault gls_ui;
- if ( style->getFontString()[0] )
+ LLRect scrolled_view_rect = mScroller->getVisibleContentRect();
+ LLRect content_rect = mScroller->getContentWindowRect();
+ LLLocalClipRect clip(content_rect);
+ S32 first_line = getFirstVisibleLine();
+ S32 num_lines = getLineCount();
+ if (first_line >= num_lines)
{
- font = style->getFont();
+ return;
}
-
- U8 font_flags = LLFontGL::NORMAL;
- if (style->mBold)
- {
- font_flags |= LLFontGL::BOLD;
- }
- if (style->mItalic)
- {
- font_flags |= LLFontGL::ITALIC;
- }
- if (style->mUnderline)
- {
- font_flags |= LLFontGL::UNDERLINE;
- }
+ S32 cursor_line = getCurrentLine();
- if (style->getIsEmbeddedItem())
+ if (mShowLineNumbers)
{
- static LLUIColor text_embedded_item_readonly_color = LLUIColorTable::instance().getColor("TextEmbeddedItemReadOnlyColor");
- static LLUIColor text_embedded_item_color = LLUIColorTable::instance().getColor("TextEmbeddedItemColor");
- if (mReadOnly)
- {
- color = text_embedded_item_readonly_color;
- }
- else
- {
- color = text_embedded_item_color;
- }
- }
+ S32 last_line_num = -1;
- F32 y_top = y + (F32)llround(font->getLineHeight());
+ for (S32 cur_line = first_line; cur_line < num_lines; cur_line++)
+ {
+ line_info& line = mLineInfoList[cur_line];
- if( selection_left > seg_start )
- {
- // Draw normally
- S32 start = seg_start;
- S32 end = llmin( selection_left, seg_end );
- S32 length = end - start;
- font->render(text, start, x, y_top, color, LLFontGL::LEFT, LLFontGL::TOP, 0, LLFontGL::NO_SHADOW, length, S32_MAX, right_x, mAllowEmbeddedItems);
- }
- x = *right_x;
-
- if( (selection_left < seg_end) && (selection_right > seg_start) )
- {
- // Draw reversed
- S32 start = llmax( selection_left, seg_start );
- S32 end = llmin( selection_right, seg_end );
- S32 length = end - start;
+ if ((line.mTop - scrolled_view_rect.mBottom) < mTextRect.mBottom)
+ {
+ break;
+ }
- font->render(text, start, x, y_top,
- LLColor4( 1.f - color.mV[0], 1.f - color.mV[1], 1.f - color.mV[2], 1.f ),
- LLFontGL::LEFT, LLFontGL::TOP, 0, LLFontGL::NO_SHADOW, length, S32_MAX, right_x, mAllowEmbeddedItems);
- }
- x = *right_x;
- if( selection_right < seg_end )
- {
- // Draw normally
- S32 start = llmax( selection_right, seg_start );
- S32 end = seg_end;
- S32 length = end - start;
- font->render(text, start, x, y_top, color, LLFontGL::LEFT, LLFontGL::TOP, 0, LLFontGL::NO_SHADOW, length, S32_MAX, right_x, mAllowEmbeddedItems);
+ S32 line_bottom = line.mBottom - scrolled_view_rect.mBottom + mTextRect.mBottom;
+ // draw the line numbers
+ if(line.mLineNum != last_line_num && line.mTop <= scrolled_view_rect.mTop)
+ {
+ const LLFontGL *num_font = LLFontGL::getFontMonospace();
+ const LLWString ltext = utf8str_to_wstring(llformat("%d", line.mLineNum ));
+ BOOL is_cur_line = cursor_line == line.mLineNum;
+ const U8 style = is_cur_line ? LLFontGL::BOLD : LLFontGL::NORMAL;
+ const LLColor4 fg_color = is_cur_line ? mCursorColor : mReadOnlyFgColor;
+ num_font->render(
+ ltext, // string to draw
+ 0, // begin offset
+ UI_TEXTEDITOR_LINE_NUMBER_MARGIN - 2, // x
+ line_bottom, // y
+ fg_color,
+ LLFontGL::RIGHT, // horizontal alignment
+ LLFontGL::BOTTOM, // vertical alignment
+ style,
+ LLFontGL::NO_SHADOW,
+ S32_MAX, // max chars
+ UI_TEXTEDITOR_LINE_NUMBER_MARGIN - 2); // max pixels
+ last_line_num = line.mLineNum;
+ }
+ }
}
- }
-
+}
void LLTextEditor::draw()
{
- // do on-demand reflow
- if (mReflowNeeded)
- {
- updateLineStartList();
- mReflowNeeded = FALSE;
- }
+ // reflow if needed, on demand
+ reflow();
// then update scroll position, as cursor may have moved
- if (mScrollNeeded)
- {
- updateScrollFromCursor();
- mScrollNeeded = FALSE;
- }
+ updateScrollFromCursor();
- {
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
- LLLocalClipRect clip(LLRect(0, getRect().getHeight(), getRect().getWidth() - (mScrollbar->getVisible() ? scrollbar_size : 0), 0));
+ LLColor4 bg_color = mReadOnly
+ ? mReadOnlyBgColor.get()
+ : hasFocus()
+ ? mFocusBgColor.get()
+ : mWriteableBgColor.get();
- bindEmbeddedChars( mGLFont );
+ mDocumentPanel->setBackgroundColor(bg_color);
- drawBackground();
+ drawChildren();
+ drawBackground(); //overlays scrolling panel bg
+ drawLineNumbers();
+
+ {
+ LLLocalClipRect clip(mTextRect);
drawSelectionBackground();
drawPreeditMarker();
drawText();
drawCursor();
-
- unbindEmbeddedChars( mGLFont );
-
- //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( gFocusMgr.getKeyboardFocus() == this);// && !mReadOnly);
}
-
- LLView::draw(); // Draw children (scrollbar and border)
- // remember if we are supposed to be at the bottom of the buffer
- mScrolledToBottom = isScrolledToBottom();
+ //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);
}
-void LLTextEditor::onTabInto()
+S32 LLTextEditor::getFirstVisibleLine() const
{
- // selecting all on tabInto causes users to hit tab twice and replace their text with a tab character
- // theoretically, one could selectAll if mTabsToNextField is true, but we couldn't think of a use case
- // where you'd want to select all anyway
- // preserve insertion point when returning to the editor
- //selectAll();
+ 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
@@ -3270,103 +3336,62 @@ S32 LLTextEditor::getPos( S32 line, S32 offset )
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 );
- // get desired x position to remember previous position
- S32 desired_x_pixel = mDesiredXPixel;
+ LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos);
- // allow one line overlap
- S32 page_size = mScrollbar->getPageSize() - 1;
if( delta == -1 )
{
- line = llmax( line - page_size, 0);
- setCursorPos(getPos( line, offset ));
- mScrollbar->setDocPos( mScrollbar->getDocPos() - page_size );
+ mScroller->pageUp(PIXEL_OVERLAP_ON_PAGE_CHANGE);
}
else
if( delta == 1 )
{
- setCursorPos(getPos( line + page_size, offset ));
- mScrollbar->setDocPos( mScrollbar->getDocPos() + page_size );
+ mScroller->pageDown(PIXEL_OVERLAP_ON_PAGE_CHANGE);
}
- // put desired position into remember-buffer after setCursorPos()
- mDesiredXPixel = desired_x_pixel;
-
- if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()))
+ if (getLocalRectFromDocIndex(mCursorPos) == cursor_rect)
{
- mOnScrollEndCallback(mOnScrollEndData);
+ // 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 )
{
- bindEmbeddedChars(mGLFont);
-
S32 line, offset;
getLineAndOffset( mCursorPos, &line, &offset );
- S32 line_start = getLineStart(line);
-
- // set desired x position to remembered previous position
- S32 desired_x_pixel = mDesiredXPixel;
- // if remembered position was reset (thus -1), calculate new one here
- if( desired_x_pixel == -1 )
- {
- LLWString text(getWText());
- desired_x_pixel = mGLFont->getWidth(text.c_str(), line_start, offset, mAllowEmbeddedItems );
- }
-
- S32 new_line = 0;
+ S32 new_line = line;
if( (delta < 0) && (line > 0 ) )
{
new_line = line - 1;
}
- else
- if( (delta > 0) && (line < (getLineCount() - 1)) )
+ else if( (delta > 0) && (line < (getLineCount() - 1)) )
{
new_line = line + 1;
}
- else
- {
- unbindEmbeddedChars(mGLFont);
- return;
- }
-
- S32 num_lines = getLineCount();
- S32 new_line_start = getLineStart(new_line);
- S32 new_line_end = getLength();
- if (new_line + 1 < num_lines)
- {
- new_line_end = getLineStart(new_line + 1) - 1;
- }
-
- S32 new_line_len = new_line_end - new_line_start;
-
- S32 new_offset;
- LLWString text(getWText());
- new_offset = mGLFont->charFromPixelOffset(text.c_str(), new_line_start,
- (F32)desired_x_pixel,
- (F32)mTextRect.getWidth(),
- new_line_len,
- mAllowEmbeddedItems);
-
- setCursorPos (getPos( new_line, new_offset ));
- // put desired position into remember-buffer after setCursorPos()
- mDesiredXPixel = desired_x_pixel;
- unbindEmbeddedChars(mGLFont);
-}
+ LLRect visible_region = mScroller->getVisibleContentRect();
-BOOL LLTextEditor::isScrolledToTop()
-{
- return mScrollbar->isAtBeginning();
-}
-
-BOOL LLTextEditor::isScrolledToBottom()
-{
- return mScrollbar->isAtEnd();
+ S32 new_cursor_pos = getDocIndexFromLocalCoord(mDesiredXPixel, mLineInfoList[new_line].mBottom + mTextRect.mBottom - visible_region.mBottom, TRUE);
+ setCursorPos(new_cursor_pos, true);
}
@@ -3383,32 +3408,11 @@ void LLTextEditor::setCursorAndScrollToEnd()
{
deselect();
endOfDoc();
- needsScroll();
}
void LLTextEditor::getLineAndColumnForPosition( S32 position, S32* line, S32* col, BOOL include_wordwrap )
{
- if( include_wordwrap )
- {
- getLineAndOffset( mCursorPos, line, col );
- }
- else
- {
- LLWString text = getWText();
- S32 line_count = 0;
- S32 line_start = 0;
- S32 i;
- for( i = 0; text[i] && (i < position); i++ )
- {
- if( '\n' == text[i] )
- {
- line_start = i + 1;
- line_count++;
- }
- }
- *line = line_count;
- *col = i - line_start;
- }
+ getLineAndOffset( mCursorPos, line, col, include_wordwrap );
}
void LLTextEditor::getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wordwrap )
@@ -3444,64 +3448,39 @@ void LLTextEditor::endOfLine()
}
}
-void LLTextEditor::endOfDoc()
+void LLTextEditor::startOfDoc()
{
- mScrollbar->setDocPos(mScrollbar->getDocPosMax());
- mScrolledToBottom = true;
+ setCursorPos(0);
+}
- S32 len = getLength();
- if( len )
- {
- setCursorPos(len);
- }
- if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()))
- {
- mOnScrollEndCallback(mOnScrollEndData);
- }
+void LLTextEditor::endOfDoc()
+{
+ setCursorPos(getLength());
}
// Sets the scrollbar from the cursor position
void LLTextEditor::updateScrollFromCursor()
{
- mScrollbar->setDocSize( getLineCount() );
-
if (mReadOnly)
{
// no cursor in read only mode
return;
}
- S32 line, offset;
- getLineAndOffset( mCursorPos, &line, &offset );
-
- S32 page_size = mScrollbar->getPageSize();
-
- if( line < mScrollbar->getDocPos() )
- {
- // scroll so that the cursor is at the top of the page
- mScrollbar->setDocPos( line );
- }
- else if( line >= mScrollbar->getDocPos() + page_size - 1 )
+ if (!mScrollNeeded)
{
- S32 new_pos = 0;
- if( line < mScrollbar->getDocSize() - 1 )
- {
- // scroll so that the cursor is one line above the bottom of the page,
- new_pos = line - page_size + 1;
- }
- else
- {
- // if there is less than a page of text remaining, scroll so that the cursor is at the bottom
- new_pos = mScrollbar->getDocPosMax();
- }
- mScrollbar->setDocPos( new_pos );
+ return;
}
+ mScrollNeeded = FALSE;
- // Check if we've scrolled to bottom for callback if asked for callback
- if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()))
- {
- mOnScrollEndCallback(mOnScrollEndData);
- }
+ 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)
@@ -3513,13 +3492,6 @@ void LLTextEditor::reshape(S32 width, S32 height, BOOL called_from_parent)
updateTextRect();
needsReflow();
-
- // propagate shape information to scrollbar
- mScrollbar->setDocSize( getLineCount() );
-
- S32 line_height = llround( mGLFont->getLineHeight() );
- S32 page_lines = mTextRect.getHeight() / line_height;
- mScrollbar->setPageSize( page_lines );
}
void LLTextEditor::autoIndent()
@@ -3564,7 +3536,7 @@ void LLTextEditor::insertText(const std::string &new_text)
deleteSelection(TRUE);
}
- setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), FALSE ));
+ setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), FALSE, LLTextSegmentPtr() ));
needsReflow();
@@ -3585,17 +3557,23 @@ void LLTextEditor::appendColoredText(const std::string &new_text,
highlight->parseFullLineHighlights(new_text, &lcolor);
}
- LLStyleSP style(new LLStyle);
- style->setVisible(true);
- style->setColor(lcolor);
- style->setFontName(font_name);
- appendStyledText(new_text, allow_undo, prepend_newline, style);
+ 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,
- LLStyleSP stylep)
+ const LLStyle::Params& style_params)
{
S32 part = (S32)LLTextParser::WHOLE;
if(mParseHTML)
@@ -3605,14 +3583,10 @@ void LLTextEditor::appendStyledText(const std::string &new_text,
std::string text = new_text;
while ( findHTML(text, &start, &end) )
{
- LLStyleSP html(new LLStyle);
- html->setVisible(true);
- html->setColor(mLinkColor);
- if (stylep)
- {
- html->setFontName(stylep->getFontString());
- }
- html->mUnderline = TRUE;
+ LLStyle::Params link_params = style_params;
+ link_params.color = mLinkColor;
+ link_params.font.style = "UNDERLINE";
+ link_params.link_href = text.substr(start,end-start);
if (start > 0)
{
@@ -3626,11 +3600,10 @@ void LLTextEditor::appendStyledText(const std::string &new_text,
part = (S32)LLTextParser::MIDDLE;
}
std::string subtext=text.substr(0,start);
- appendHighlightedText(subtext,allow_undo, prepend_newline, part, stylep);
+ appendHighlightedText(subtext,allow_undo, prepend_newline, part, style_params);
}
- html->setLinkHREF(text.substr(start,end-start));
- appendText(text.substr(start, end-start),allow_undo, prepend_newline, html);
+ appendText(text.substr(start, end-start),allow_undo, prepend_newline, link_params);
if (end < (S32)text.length())
{
text = text.substr(end,text.length() - end);
@@ -3643,11 +3616,11 @@ void LLTextEditor::appendStyledText(const std::string &new_text,
}
}
if (part != (S32)LLTextParser::WHOLE) part=(S32)LLTextParser::END;
- if (end < (S32)text.length()) appendHighlightedText(text,allow_undo, prepend_newline, part, stylep);
+ if (end < (S32)text.length()) appendHighlightedText(text,allow_undo, prepend_newline, part, style_params);
}
else
{
- appendHighlightedText(new_text, allow_undo, prepend_newline, part, stylep);
+ appendHighlightedText(new_text, allow_undo, prepend_newline, part, style_params);
}
}
@@ -3655,38 +3628,40 @@ void LLTextEditor::appendHighlightedText(const std::string &new_text,
bool allow_undo,
bool prepend_newline,
S32 highlight_part,
- LLStyleSP stylep)
+ const LLStyle::Params& style_params)
{
if (mParseHighlights)
{
LLTextParser* highlight = LLTextParser::getInstance();
- if (highlight && stylep)
+ if (highlight && !style_params.isDefault())
{
- LLSD pieces = highlight->parsePartialLineHighlights(new_text, stylep->getColor(), highlight_part);
+ 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);
- LLStyleSP lstylep(new LLStyle(*stylep));
- lstylep->setColor(lcolor);
+ highlight_params.color = lcolor;
if (i != 0 && (pieces.size() > 1) ) lprepend=FALSE;
- appendText((std::string)pieces[i]["text"], allow_undo, lprepend, lstylep);
+ appendText((std::string)pieces[i]["text"], allow_undo, lprepend, highlight_params);
}
return;
}
}
- appendText(new_text, allow_undo, prepend_newline, stylep);
+ 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 LLStyleSP stylep)
+ const LLStyle::Params& stylep)
{
+ if (new_text.empty()) return;
+
// Save old state
- BOOL was_scrolled_to_bottom = (mScrollbar->getDocPos() == mScrollbar->getDocPosMax());
S32 selection_start = mSelectionStart;
S32 selection_end = mSelectionEnd;
BOOL was_selecting = mIsSelecting;
@@ -3698,50 +3673,94 @@ void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool
setCursorPos(old_length);
+ LLWString wide_text;
+
// Add carriage return if not first line
if (getLength() != 0
&& prepend_newline)
{
- std::string final_text = "\n";
- final_text += new_text;
- append(utf8str_to_wstring(final_text), TRUE);
+ wide_text = utf8str_to_wstring(std::string("\n") + new_text);
}
else
{
- append(utf8str_to_wstring(new_text), TRUE );
+ wide_text = utf8str_to_wstring(new_text);
}
- if (stylep)
+ LLTextSegmentPtr segmentp;
+ if (!stylep.isDefault())
{
S32 segment_start = old_length;
- S32 segment_end = getLength();
- LLTextSegment* segment = new LLTextSegment(stylep, segment_start, segment_end );
- mSegments.push_back(segment);
+ 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
- // Maintain the scroll position unless the scroll was at the end of the doc (in which
- // case, move it to the new end of the doc) or unless the user was doing actively selecting
- if( was_scrolled_to_bottom && !was_selecting )
- {
- if( selection_start != selection_end )
- {
- // maintain an existing non-active selection
- mSelectionStart = selection_start;
- mSelectionEnd = selection_end;
- }
- endOfDoc();
- }
- else if( selection_start != selection_end )
+ 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
+ S32 selection_start = mSelectionStart;
+ S32 selection_end = mSelectionEnd;
+ BOOL was_selecting = mIsSelecting;
+ S32 cursor_pos = mCursorPos;
+ S32 old_length = getLength();
+ BOOL cursor_was_at_end = (mCursorPos == old_length);
+
+ deselect();
+
+ setCursorPos(old_length);
+
+ LLWString widget_wide_text;
+
+ // Add carriage return if not first line
+ if (getLength() != 0
+ && prepend_newline)
+ {
+ widget_wide_text = utf8str_to_wstring(std::string("\n") + widget_text);
+ }
+ else
+ {
+ widget_wide_text = utf8str_to_wstring(widget_text);
+ }
+
+ LLTextSegmentPtr segment = new LLInlineViewSegment(widget, old_length, old_length + widget_text.size());
+ append(widget_wide_text, FALSE, segment);
+
+ needsReflow();
+
+ // Set the cursor and scroll position
+ if( selection_start != selection_end )
+ {
+ mSelectionStart = selection_start;
+ mSelectionEnd = selection_end;
+
mIsSelecting = was_selecting;
setCursorPos(cursor_pos);
}
@@ -3767,26 +3786,79 @@ void LLTextEditor::removeTextFromEnd(S32 num_chars)
remove(getLength() - num_chars, num_chars, FALSE);
S32 len = getLength();
- mCursorPos = llclamp(mCursorPos, 0, len);
+ setCursorPos (llclamp(mCursorPos, 0, len));
mSelectionStart = llclamp(mSelectionStart, 0, len);
mSelectionEnd = llclamp(mSelectionEnd, 0, len);
- pruneSegments();
-
- // pruneSegments will invalidate mLineStartList.
- updateLineStartList();
+ reflow();
needsScroll();
}
///////////////////////////////////////////////////////////////////
// Returns change in number of characters in mWText
-S32 LLTextEditor::insertStringNoUndo(const S32 pos, const LLWString &wstr)
+S32 LLTextEditor::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextEditor::segment_vec_t* segments )
{
- LLWString text(getWText());
+ 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);
@@ -3797,14 +3869,67 @@ S32 LLTextEditor::insertStringNoUndo(const S32 pos, const LLWString &wstr)
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
}
@@ -3817,6 +3942,9 @@ S32 LLTextEditor::overwriteCharNoUndo(S32 pos, llwchar wc)
LLWString text(getWText());
text[pos] = wc;
getViewModel()->setDisplay(text);
+
+ onValueChange(pos, pos + 1);
+
return 1;
}
@@ -3887,21 +4015,25 @@ void LLTextEditor::updateTextRect()
{
static LLUICachedControl<S32> texteditor_border ("UITextEditorBorder", 0);
static LLUICachedControl<S32> texteditor_h_pad ("UITextEditorHPad", 0);
- static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
- static LLUICachedControl<S32> texteditor_vpad_top ("UITextEditorVPadTop", 0);
-
- mTextRect.setOriginAndSize(
- texteditor_border + texteditor_h_pad,
- texteditor_border,
- getRect().getWidth() - scrollbar_size - 2 * (texteditor_border + texteditor_h_pad),
- getRect().getHeight() - 2 * texteditor_border - texteditor_vpad_top );
+
+ 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");
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);
if(mKeywords.loadFromFile(filename))
{
S32 count = llmin(funcs.size(), tooltips.size());
@@ -3910,133 +4042,115 @@ void LLTextEditor::loadKeywords(const std::string& filename,
std::string name = utf8str_trim(funcs[i]);
mKeywords.addToken(LLKeywordToken::WORD, name, color, tooltips[i] );
}
+ segment_vec_t segment_list;
+ mKeywords.findSegments(&segment_list, getWText(), mDefaultColor.get(), *this);
- mKeywords.findSegments( &mSegments, getWText(), mDefaultColor.get() );
-
- llassert( mSegments.front()->getStart() == 0 );
- llassert( mSegments.back()->getEnd() == getLength() );
+ mSegments.clear();
+ 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);
+ }
}
}
-void LLTextEditor::updateSegments()
+void LLTextEditor::createDefaultSegment()
{
- if (mKeywords.isLoaded())
- {
- // HACK: No non-ascii keywords for now
- mKeywords.findSegments(&mSegments, getWText(), mDefaultColor.get());
- }
- else if (mAllowEmbeddedItems)
- {
- findEmbeddedItemSegments();
- }
-
- // Make sure we have at least one segment
- if (mSegments.size() == 1 && mSegments[0]->getIsDefault())
- {
- delete mSegments[0];
- mSegments.clear(); // create default segment
- }
+ // ensures that there is always at least one segment
if (mSegments.empty())
{
- LLColor4 text_color = ( mReadOnly ? mReadOnlyFgColor.get() : mFgColor.get() );
- LLTextSegment* default_segment = new LLTextSegment( text_color, 0, getLength() );
- default_segment->setIsDefault(TRUE);
- mSegments.push_back(default_segment);
+ LLTextSegmentPtr default_segment = new LLNormalTextSegment( getDefaultStyle(), 0, getLength() + 1, *this);
+ mSegments.insert(default_segment);
+ default_segment->linkToDocument(this);
}
}
-// Only effective if text was removed from the end of the editor
-// *NOTE: Using this will invalidate references to mSegments from mLineStartList.
-void LLTextEditor::pruneSegments()
+LLStyleSP LLTextEditor::getDefaultStyle()
{
- S32 len = getLength();
- // Find and update the first valid segment
- segment_list_t::iterator iter = mSegments.end();
- while(iter != mSegments.begin())
- {
- --iter;
- LLTextSegment* seg = *iter;
- if (seg->getStart() < len)
- {
- // valid segment
- if (seg->getEnd() > len)
- {
- seg->setEnd(len);
- }
- break; // done
- }
- }
- if (iter != mSegments.end())
- {
- // erase invalid segments
- ++iter;
- std::for_each(iter, mSegments.end(), DeletePointer());
- mSegments.erase(iter, mSegments.end());
- }
- else
- {
- llwarns << "Tried to erase end of empty LLTextEditor" << llendl;
- }
+ LLColor4 text_color = ( mReadOnly ? mReadOnlyFgColor.get() : mFgColor.get() );
+ return LLStyleSP(new LLStyle(LLStyle::Params().color(text_color).font(mDefaultFont)));
}
-void LLTextEditor::findEmbeddedItemSegments()
+LLFastTimer::DeclareTimer FTM_UPDATE_TEXT_SEGMENTS("Update Text Segments");
+void LLTextEditor::updateSegments()
{
- mHoverSegment = NULL;
- std::for_each(mSegments.begin(), mSegments.end(), DeletePointer());
- mSegments.clear();
-
- BOOL found_embedded_items = FALSE;
- LLWString text = getWText();
- S32 idx = 0;
- while( text[idx] )
+ LLFastTimer ft(FTM_UPDATE_TEXT_SEGMENTS);
+ if (mKeywords.isLoaded())
{
- if( text[idx] >= FIRST_EMBEDDED_CHAR && text[idx] <= LAST_EMBEDDED_CHAR )
- {
- found_embedded_items = TRUE;
- break;
+ // HACK: No non-ascii keywords for now
+ segment_vec_t segment_list;
+ mKeywords.findSegments(&segment_list, getWText(), mDefaultColor.get(), *this);
+
+ mSegments.clear();
+ 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);
}
- ++idx;
}
- if( !found_embedded_items )
+ createDefaultSegment();
+
+}
+
+void LLTextEditor::insertSegment(LLTextSegmentPtr segment_to_insert)
+{
+ if (segment_to_insert.isNull())
{
return;
}
- S32 text_len = text.length();
+ segment_set_t::iterator cur_seg_iter = getSegIterContaining(segment_to_insert->getStart());
- BOOL in_text = FALSE;
-
- LLColor4 text_color = ( mReadOnly ? mReadOnlyFgColor.get() : mFgColor.get() );
-
- if( idx > 0 )
+ if (cur_seg_iter == mSegments.end())
{
- mSegments.push_back( new LLTextSegment( text_color, 0, text_len ) ); // text
- in_text = TRUE;
+ mSegments.insert(segment_to_insert);
+ segment_to_insert->linkToDocument(this);
}
-
- LLStyleSP embedded_style(new LLStyle);
- embedded_style->setIsEmbeddedItem( TRUE );
-
- // Start with i just after the first embedded item
- while ( text[idx] )
+ else
{
- if( text[idx] >= FIRST_EMBEDDED_CHAR && text[idx] <= LAST_EMBEDDED_CHAR )
+ LLTextSegmentPtr cur_segmentp = *cur_seg_iter;
+ if (cur_segmentp->getStart() < segment_to_insert->getStart())
{
- if( in_text )
- {
- mSegments.back()->setEnd( idx );
- }
- mSegments.push_back( new LLTextSegment( embedded_style, idx, idx + 1 ) ); // item
- in_text = FALSE;
+ 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
- if( !in_text )
{
- mSegments.push_back( new LLTextSegment( text_color, idx, text_len ) ); // text
- in_text = TRUE;
+ 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;
+ }
}
- ++idx;
}
}
@@ -4050,9 +4164,9 @@ BOOL LLTextEditor::handleMouseUpOverSegment(S32 x, S32 y, MASK mask)
if (mParseHTML && mHTML.length() > 0)
{
//Special handling for slurls
- if ( (mSecondlifeURLcallback!=NULL) && !(*mSecondlifeURLcallback)(mHTML) )
+ if ( (sSecondlifeURLcallback!=NULL) && !(*sSecondlifeURLcallback)(mHTML) )
{
- if (mURLcallback!=NULL) (*mURLcallback)(mHTML);
+ if (sURLcallback!=NULL) (*sURLcallback)(mHTML);
}
mHTML.clear();
}
@@ -4063,44 +4177,37 @@ BOOL LLTextEditor::handleMouseUpOverSegment(S32 x, S32 y, MASK mask)
// Finds the text segment (if any) at the give local screen position
-const LLTextSegment* LLTextEditor::getSegmentAtLocalPos( S32 x, S32 y ) const
+LLTextSegmentPtr LLTextEditor::getSegmentAtLocalPos( S32 x, S32 y )
{
// Find the cursor position at the requested local screen position
- S32 offset = getCursorPosFromLocalCoord( x, y, FALSE );
- S32 idx = getSegmentIdxAtOffset(offset);
- return idx >= 0 ? mSegments[idx] : NULL;
-}
-
-const LLTextSegment* LLTextEditor::getSegmentAtOffset(S32 offset) const
-{
- S32 idx = getSegmentIdxAtOffset(offset);
- return idx >= 0 ? mSegments[idx] : NULL;
-}
-
-S32 LLTextEditor::getSegmentIdxAtOffset(S32 offset) const
-{
- if (mSegments.empty() || offset < 0 || offset >= getLength())
+ S32 offset = getDocIndexFromLocalCoord( x, y, FALSE );
+ segment_set_t::iterator seg_iter = getSegIterContaining(offset);
+ if (seg_iter != mSegments.end())
{
- return -1;
+ return *seg_iter;
}
else
{
- S32 segidx, segoff;
- getSegmentAndOffset(offset, &segidx, &segoff);
- return segidx;
+ return LLTextSegmentPtr();
}
}
-void LLTextEditor::onMouseCaptureLost()
+LLTextEditor::segment_set_t::iterator LLTextEditor::getSegIterContaining(S32 index)
{
- endSelection();
+ segment_set_t::iterator it = mSegments.upper_bound(new LLIndexSegment(index));
+ return it;
+}
+
+LLTextEditor::segment_set_t::const_iterator LLTextEditor::getSegIterContaining(S32 index) const
+{
+ LLTextEditor::segment_set_t::const_iterator it = mSegments.upper_bound(new LLIndexSegment(index));
+ return it;
}
-void LLTextEditor::setOnScrollEndCallback(void (*callback)(void*), void* userdata)
+
+void LLTextEditor::onMouseCaptureLost()
{
- mOnScrollEndCallback = callback;
- mOnScrollEndData = userdata;
- mScrollbar->setOnScrollEndCallback(callback, userdata);
+ endSelection();
}
///////////////////////////////////////////////////////////////////
@@ -4186,7 +4293,7 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length )
delete[] text;
- setCursorPos(0);
+ startOfDoc();
deselect();
needsReflow();
@@ -4207,74 +4314,6 @@ BOOL LLTextEditor::exportBuffer(std::string &buffer )
return TRUE;
}
-//////////////////////////////////////////////////////////////////////////
-// LLTextSegment
-
-LLTextSegment::LLTextSegment(S32 start) :
- mStart(start),
- mEnd(0),
- mToken(NULL),
- mIsDefault(FALSE)
-{
-}
-LLTextSegment::LLTextSegment( const LLStyleSP& style, S32 start, S32 end ) :
- mStyle( style ),
- mStart( start),
- mEnd( end ),
- mToken(NULL),
- mIsDefault(FALSE)
-{
-}
-LLTextSegment::LLTextSegment( const LLColor4& color, S32 start, S32 end, BOOL is_visible) :
- mStyle(new LLStyle(is_visible,color,LLStringUtil::null)),
- mStart( start),
- mEnd( end ),
- mToken(NULL),
- mIsDefault(FALSE)
-{
-}
-LLTextSegment::LLTextSegment( const LLColor4& color, S32 start, S32 end ) :
- mStyle(new LLStyle(TRUE, color,LLStringUtil::null )),
- mStart( start),
- mEnd( end ),
- mToken(NULL),
- mIsDefault(FALSE)
-{
-}
-LLTextSegment::LLTextSegment( const LLColor3& color, S32 start, S32 end ) :
- mStyle(new LLStyle(TRUE, color,LLStringUtil::null )),
- mStart( start),
- mEnd( end ),
- mToken(NULL),
- mIsDefault(FALSE)
-{
-}
-
-BOOL LLTextSegment::getToolTip(std::string& msg) const
-{
- if (mToken && !mToken->getToolTip().empty())
- {
- const LLWString& wmsg = mToken->getToolTip();
- msg = wstring_to_utf8str(wmsg);
- return TRUE;
- }
- return FALSE;
-}
-
-
-
-void LLTextSegment::dump() const
-{
- llinfos << "Segment [" <<
-// mColor.mV[VX] << ", " <<
-// mColor.mV[VY] << ", " <<
-// mColor.mV[VZ] << "]\t[" <<
- mStart << ", " <<
- getEnd() << "]" <<
- llendl;
-
-}
-
///////////////////////////////////////////////////////////////////
// Refactoring note: We may eventually want to replace this with boost::regex or
// boost::tokenizer capabilities since we've already fixed at least two JIRAs
@@ -4473,7 +4512,7 @@ void LLTextEditor::resetPreedit()
deselect();
}
- mCursorPos = mPreeditPositions.front();
+ setCursorPos(mPreeditPositions.front());
removeStringNoUndo(mCursorPos, mPreeditPositions.back() - mCursorPos);
insertStringNoUndo(mCursorPos, mPreeditOverwrittenWString);
@@ -4557,7 +4596,7 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect
return FALSE;
}
- const S32 first_visible_line = mScrollbar->getDocPos();
+ const S32 first_visible_line = getFirstVisibleLine();
if (query < getLineStart(first_visible_line))
{
return FALSE;
@@ -4583,11 +4622,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 = llround(mGLFont->getLineHeight());
+ const S32 line_height = llround(mDefaultFont->getLineHeight());
if (coord)
{
- const S32 query_x = mTextRect.mLeft + mGLFont->getWidth(text, current_line_start, query - current_line_start, mAllowEmbeddedItems);
+ const S32 query_x = mTextRect.mLeft + mDefaultFont->getWidth(text, current_line_start, query - current_line_start);
const S32 query_y = mTextRect.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);
@@ -4599,17 +4638,17 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect
S32 preedit_left = mTextRect.mLeft;
if (preedit_left_position > current_line_start)
{
- preedit_left += mGLFont->getWidth(text, current_line_start, preedit_left_position - current_line_start, mAllowEmbeddedItems);
+ preedit_left += mDefaultFont->getWidth(text, current_line_start, preedit_left_position - current_line_start);
}
S32 preedit_right = mTextRect.mLeft;
if (preedit_right_position < current_line_end)
{
- preedit_right += mGLFont->getWidth(text, current_line_start, preedit_right_position - current_line_start, mAllowEmbeddedItems);
+ preedit_right += mDefaultFont->getWidth(text, current_line_start, preedit_right_position - current_line_start);
}
else
{
- preedit_right += mGLFont->getWidth(text, current_line_start, current_line_end - current_line_start, mAllowEmbeddedItems);
+ preedit_right += mDefaultFont->getWidth(text, current_line_start, current_line_end - current_line_start);
}
const S32 preedit_top = mTextRect.mTop - (current_line - first_visible_line) * line_height;
@@ -4686,10 +4725,267 @@ void LLTextEditor::markAsPreedit(S32 position, S32 length)
S32 LLTextEditor::getPreeditFontSize() const
{
- return llround(mGLFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]);
+ return llround(mDefaultFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]);
}
LLWString LLTextEditor::getWText() const
{
return getViewModel()->getDisplay();
}
+
+void LLTextEditor::onValueChange(S32 start, S32 end)
+{
+}
+
+//
+// LLTextSegment
+//
+
+LLTextSegment::~LLTextSegment()
+{}
+
+S32 LLTextSegment::getWidth(S32 first_char, S32 num_chars) const { return 0; }
+S32 LLTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const { return 0; }
+S32 LLTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const { return 0; }
+void LLTextSegment::updateLayout(const LLTextEditor& editor) {}
+F32 LLTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect) { return draw_rect.mLeft; }
+S32 LLTextSegment::getMaxHeight() const { return 0; }
+bool LLTextSegment::canEdit() const { return false; }
+void LLTextSegment::unlinkFromDocument(LLTextEditor*) {}
+void LLTextSegment::linkToDocument(LLTextEditor*) {}
+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::dump() const {}
+
+
+//
+// LLNormalTextSegment
+//
+
+LLNormalTextSegment::LLNormalTextSegment( const LLStyleSP& style, S32 start, S32 end, LLTextEditor& editor )
+: LLTextSegment(start, end),
+ mStyle( style ),
+ mToken(NULL),
+ mHasMouseHover(false),
+ mEditor(editor)
+{
+ mMaxHeight = llceil(mStyle->getFont()->getLineHeight());
+}
+
+LLNormalTextSegment::LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextEditor& editor, BOOL is_visible)
+: LLTextSegment(start, end),
+ mToken(NULL),
+ mHasMouseHover(false),
+ mEditor(editor)
+{
+ mStyle = new LLStyle(LLStyle::Params().visible(is_visible).color(color));
+
+ mMaxHeight = llceil(mStyle->getFont()->getLineHeight());
+}
+
+F32 LLNormalTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect)
+{
+ if( end - start > 0 )
+ {
+ if ( mStyle->isImage() && (start >= 0) && (end <= mEnd - mStart))
+ {
+ S32 style_image_height = mStyle->mImageHeight;
+ S32 style_image_width = mStyle->mImageWidth;
+ LLUIImagePtr image = mStyle->getImage();
+ image->draw(draw_rect.mLeft, draw_rect.mTop-style_image_height,
+ style_image_width, style_image_height);
+ }
+
+ return drawClippedSegment( getStart() + start, getStart() + end, selection_start, selection_end, draw_rect.mLeft, draw_rect.mBottom);
+ }
+ 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)
+{
+ const LLWString &text = mEditor.getWText();
+
+ F32 right_x = x;
+ if (!mStyle->isVisible())
+ {
+ return right_x;
+ }
+
+ const LLFontGL* font = mStyle->getFont();
+
+ LLColor4 color = mStyle->getColor();
+
+ font = mStyle->getFont();
+
+ if( selection_start > seg_start )
+ {
+ // Draw normally
+ S32 start = seg_start;
+ S32 end = llmin( selection_start, seg_end );
+ S32 length = end - start;
+ font->render(text, start, x, y, color, LLFontGL::LEFT, LLFontGL::BOTTOM, 0, LLFontGL::NO_SHADOW, length, S32_MAX, &right_x, mEditor.allowsEmbeddedItems());
+ }
+ x = right_x;
+
+ if( (selection_start < seg_end) && (selection_end > seg_start) )
+ {
+ // Draw reversed
+ S32 start = llmax( selection_start, seg_start );
+ S32 end = llmin( selection_end, seg_end );
+ S32 length = end - start;
+
+ font->render(text, start, x, y,
+ 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());
+ }
+ x = 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());
+ }
+ return right_x;
+}
+
+S32 LLNormalTextSegment::getMaxHeight() const
+{
+ return mMaxHeight;
+}
+
+BOOL LLNormalTextSegment::getToolTip(std::string& msg) const
+{
+ if (mToken && !mToken->getToolTip().empty())
+ {
+ const LLWString& wmsg = mToken->getToolTip();
+ msg = wstring_to_utf8str(wmsg);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+S32 LLNormalTextSegment::getWidth(S32 first_char, S32 num_chars) const
+{
+ LLWString text = mEditor.getWText();
+ return mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars);
+}
+
+S32 LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const
+{
+ LLWString text = mEditor.getWText();
+ return mStyle->getFont()->charFromPixelOffset(text.c_str(), mStart + start_offset,
+ (F32)segment_local_x_coord,
+ F32_MAX,
+ num_chars,
+ round);
+}
+
+S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const
+{
+ LLWString text = mEditor.getWText();
+ S32 num_chars = mStyle->getFont()->maxDrawableChars(text.c_str() + segment_offset + mStart,
+ (F32)num_pixels,
+ max_chars,
+ mEditor.getWordWrap());
+
+ if (num_chars == 0
+ && line_offset == 0
+ && max_chars > 0)
+ {
+ // If at the beginning of a line, and a single character won't fit, draw it anyway
+ num_chars = 1;
+ }
+ if (mStart + segment_offset + num_chars == mEditor.getLength())
+ {
+ // include terminating NULL
+ num_chars++;
+ }
+ return num_chars;
+}
+
+void LLNormalTextSegment::dump() const
+{
+ llinfos << "Segment [" <<
+// mColor.mV[VX] << ", " <<
+// mColor.mV[VY] << ", " <<
+// mColor.mV[VZ] << "]\t[" <<
+ mStart << ", " <<
+ getEnd() << "]" <<
+ llendl;
+}
+
+//
+// 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 LLTextEditor& editor)
+{
+ 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);
+}
+
+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(LLTextEditor* editor)
+{
+ editor->removeDocumentChild(mView);
+}
+
+void LLInlineViewSegment::linkToDocument(LLTextEditor* editor)
+{
+ editor->addDocumentChild(mView);
+}
diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h
index 0babd7ba58..67c67d0f67 100644
--- a/indra/llui/lltexteditor.h
+++ b/indra/llui/lltexteditor.h
@@ -53,6 +53,101 @@ class LLScrollbar;
class LLKeywordToken;
class LLTextCmd;
class LLUICtrlFactory;
+class LLScrollContainer;
+
+class LLTextSegment : public LLRefCount
+{
+public:
+ LLTextSegment(S32 start, S32 end) : mStart(start), mEnd(end){};
+ virtual ~LLTextSegment();
+
+ virtual S32 getWidth(S32 first_char, S32 num_chars) const;
+ virtual S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const;
+ virtual S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const;
+ virtual void updateLayout(const class LLTextEditor& 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;
+ virtual void unlinkFromDocument(class LLTextEditor* editor);
+ virtual void linkToDocument(class LLTextEditor* 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 dump() const;
+
+ S32 getStart() const { return mStart; }
+ void setStart(S32 start) { mStart = start; }
+ S32 getEnd() const { return mEnd; }
+ void setEnd( S32 end ) { mEnd = end; }
+
+protected:
+ S32 mStart;
+ S32 mEnd;
+};
+
+class LLNormalTextSegment : public LLTextSegment
+{
+public:
+ LLNormalTextSegment( const LLStyleSP& style, S32 start, S32 end, LLTextEditor& editor );
+ LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextEditor& editor, BOOL is_visible = TRUE);
+
+ /*virtual*/ S32 getWidth(S32 first_char, S32 num_chars) const;
+ /*virtual*/ S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const;
+ /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const;
+ /*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; }
+ /*virtual*/ void setStyle(const LLStyleSP &style) { mStyle = style; }
+ /*virtual*/ void setToken( LLKeywordToken* token ) { mToken = token; }
+ /*virtual*/ LLKeywordToken* getToken() const { return mToken; }
+ /*virtual*/ BOOL getToolTip( std::string& msg ) const;
+ /*virtual*/ void dump() const;
+
+protected:
+ F32 drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, F32 x, F32 y);
+
+ class LLTextEditor& mEditor;
+ LLStyleSP mStyle;
+ S32 mMaxHeight;
+ LLKeywordToken* mToken;
+ bool mHasMouseHover;
+};
+
+typedef LLPointer<LLTextSegment> LLTextSegmentPtr;
+
+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 LLTextEditor& 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 LLTextEditor* editor);
+ /*virtual*/ void linkToDocument(class LLTextEditor* editor);
+
+private:
+ LLView* mView;
+};
+
+class LLIndexSegment : public LLTextSegment
+{
+public:
+ LLIndexSegment(S32 pos) : LLTextSegment(pos, pos) {}
+};
class LLTextEditor : public LLUICtrl, LLEditMenuHandler, protected LLPreeditor
{
@@ -64,12 +159,13 @@ public:
Optional<bool> read_only,
embedded_items,
- hide_scrollbar,
word_wrap,
ignore_tab,
hide_border,
track_bottom,
- takes_non_scroll_clicks;
+ handle_edit_keys_directly,
+ show_line_numbers,
+ commit_on_focus_lost;
//colors
Optional<LLUIColor> cursor_color,
@@ -78,13 +174,15 @@ public:
text_readonly_color,
bg_readonly_color,
bg_writeable_color,
- bg_focus_color;
+ bg_focus_color,
+ link_color;
Optional<LLViewBorder::Params> border;
Ignored type,
length,
- is_unicode;
+ is_unicode,
+ hide_scrollbar;
Params();
};
@@ -101,6 +199,17 @@ 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();
+ }
+ };
+
+ typedef std::multiset<LLTextSegmentPtr, compare_segment_end> segment_set_t;
+
virtual ~LLTextEditor();
void setParseHTML(BOOL parsing) {mParseHTML=parsing;}
@@ -110,7 +219,6 @@ 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 handleScrollWheel(S32 x, S32 y, S32 clicks);
virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask );
virtual BOOL handleMiddleMouseDown(S32 x,S32 y,MASK mask);
@@ -128,14 +236,15 @@ public:
virtual void draw();
virtual void onFocusReceived();
virtual void onFocusLost();
+ virtual void onCommit();
virtual void setEnabled(BOOL enabled);
// uictrl overrides
- virtual void onTabInto();
virtual void clear();
virtual void setFocus( BOOL b );
virtual BOOL acceptsTextInput() const;
- virtual BOOL isDirty() const { return( mLastCmd != NULL || (mPristineCmd && (mPristineCmd != mLastCmd)) ); }
+ virtual BOOL isDirty() const { return isPristine(); }
+ virtual void setValue(const LLSD& value);
// LLEditMenuHandler interface
virtual void undo();
@@ -162,9 +271,12 @@ 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); }
// Undo/redo stack
void blockUndo();
@@ -173,12 +285,20 @@ public:
virtual void makePristine();
BOOL isPristine() const;
BOOL allowsEmbeddedItems() const { return mAllowEmbeddedItems; }
+ BOOL getWordWrap() { return mWordWrap; }
+ S32 getLength() const { return getWText().length(); }
+ void setReadOnly(bool read_only) { mReadOnly = read_only; }
+ bool getReadOnly() { return mReadOnly; }
+
+ //
+ // Text manipulation
+ //
// 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 LLStyleSP stylep = NULL);
+ const LLStyle::Params& style = LLStyle::Params());
void appendColoredText(const std::string &wtext, bool allow_undo,
bool prepend_newline,
@@ -187,19 +307,24 @@ public:
// 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,
- LLStyleSP stylep = NULL);
+ const LLStyle::Params& style);
void appendHighlightedText(const std::string &new_text, bool allow_undo,
bool prepend_newline, S32 highlight_part,
- LLStyleSP stylep);
-
+ 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
// Does not change highlight or cursor position.
void removeTextFromEnd(S32 num_chars);
BOOL tryToRevertToPristineState();
- void setCursor(S32 row, S32 column);
- void setCursorPos(S32 offset);
+ 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 );
@@ -214,90 +339,57 @@ public:
LLKeywords::keyword_iterator_t keywordsBegin() { return mKeywords.begin(); }
LLKeywords::keyword_iterator_t keywordsEnd() { return mKeywords.end(); }
- // Color support
- void setCursorColor(const LLColor4& c) { mCursorColor = c; }
- void setFgColor( const LLColor4& c ) { mFgColor = c; }
- void setTextDefaultColor( const LLColor4& c ) { mDefaultColor = c; }
- void setReadOnlyFgColor( const LLColor4& c ) { mReadOnlyFgColor = c; }
- void setWriteableBgColor( const LLColor4& c ) { mWriteableBgColor = c; }
- void setReadOnlyBgColor( const LLColor4& c ) { mReadOnlyBgColor = c; }
- void setTrackColor( const LLColor4& color );
- void setThumbColor( const LLColor4& color );
-
// Hacky methods to make it into a word-wrapping, potentially scrolling,
// read-only text box.
- void setBorderVisible(BOOL b);
- BOOL isBorderVisible() const;
- void setTakesNonScrollClicks(BOOL b) { mTakesNonScrollClicks = b; }
- void setHideScrollbarForShortDocs(BOOL b);
-
- void setWordWrap( BOOL b );
- void setTabsToNextField(BOOL b) { mTabsToNextField = b; }
- BOOL tabsToNextField() const { return mTabsToNextField; }
void setCommitOnFocusLost(BOOL b) { mCommitOnFocusLost = b; }
// Hack to handle Notecards
virtual BOOL importBuffer(const char* buffer, S32 length );
virtual BOOL exportBuffer(std::string& buffer );
- // If takes focus, will take keyboard focus on click.
- void setTakesFocus(BOOL b) { mTakesFocus = b; }
+ const class DocumentPanel* getDocumentPanel() const { return mDocumentPanel; }
- void setSourceID(const LLUUID& id) { mSourceID = id; }
const LLUUID& getSourceID() const { return mSourceID; }
- void setHandleEditKeysDirectly( BOOL b ) { mHandleEditKeysDirectly = b; }
-
// Callbacks
- static void setLinkColor(LLColor4 color) { mLinkColor = color; }
static void setURLCallbacks(void (*callback1) (const std::string& url),
bool (*callback2) (const std::string& url),
bool (*callback3) (const std::string& url) )
- { mURLcallback = callback1; mSecondlifeURLcallback = callback2; mSecondlifeURLcallbackRightClick = callback3;}
-
- void setOnScrollEndCallback(void (*callback)(void*), void* userdata);
-
- // new methods
- void setValue(const LLSD& value);
+ { sURLcallback = callback1; sSecondlifeURLcallback = callback2; sSecondlifeURLcallbackRightClick = callback3;}
std::string getText() const;
- // Non-undoable
- void setText(const LLStringExplicit &utf8str);
- void setWText(const LLWString &wtext);
-
- // Returns byte length limit
- S32 getMaxLength() const { return mMaxTextByteLength; }
-
- // Change cursor
- void startOfLine();
- void endOfLine();
- void endOfDoc();
-
- BOOL isScrolledToTop();
- BOOL isScrolledToBottom();
-
// Getters
LLWString getWText() const;
llwchar getWChar(S32 pos) const { return getWText()[pos]; }
LLWString getWSubString(S32 pos, S32 len) const { return getWText().substr(pos, len); }
- const LLTextSegment* getCurrentSegment() const { return getSegmentAtOffset(mCursorPos); }
- const LLTextSegment* getPreviousSegment() const;
- void getSelectedSegments(std::vector<const LLTextSegment*>& segments) const;
+ 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;
- static bool isPartOfWord(llwchar c) { return (c == '_') || LLStringOps::isAlnum((char)c); }
+ void addDocumentChild(LLView* view);
+ void removeDocumentChild(LLView* view);
protected:
- //
- // Methods
- //
+ // Change cursor
+ void startOfLine();
+ void endOfLine();
+ void startOfDoc();
+ void endOfDoc();
- S32 getLength() const { return getWText().length(); }
- void getSegmentAndOffset( S32 startpos, S32* segidxp, S32* offsetp ) const;
+ 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 ) ;
void drawPreeditMarker();
- void updateLineStartList(S32 startpos = 0);
+ void needsReflow() { mReflowNeeded = TRUE; }
+ void needsScroll() { mScrollNeeded = TRUE; }
+ void updateCursorXPos();
+
void updateScrollFromCursor();
void updateTextRect();
const LLRect& getTextRect() const { return mTextRect; }
@@ -306,16 +398,16 @@ protected:
BOOL truncate(); // Returns true if truncation occurs
void removeCharOrTab();
- void setCursorAtLocalPos(S32 x, S32 y, BOOL round);
- S32 getCursorPosFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const;
+ void setCursorAtLocalPos(S32 x, S32 y, bool round, bool keep_cursor_offset = false);
+ S32 getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const;
void indentSelectedLines( S32 spaces );
S32 indentLine( S32 pos, S32 spaces );
void unindentLineBeforeCloseBrace();
- S32 getSegmentIdxAtOffset(S32 offset) const;
- const LLTextSegment* getSegmentAtLocalPos(S32 x, S32 y) const;
- const LLTextSegment* getSegmentAtOffset(S32 offset) const;
+ LLTextSegmentPtr getSegmentAtLocalPos(S32 x, S32 y);
+ segment_set_t::iterator getSegIterContaining(S32 index);
+ segment_set_t::const_iterator getSegIterContaining(S32 index) const;
void reportBadKeystroke() { make_ui_sound("UISndBadKeystroke"); }
@@ -325,7 +417,6 @@ protected:
BOOL handleControlKey(const KEY key, const MASK mask);
BOOL handleEditKey(const KEY key, const MASK mask);
- BOOL hasSelection() const { return (mSelectionStart !=mSelectionEnd); }
BOOL selectionContainsLineBreaks();
void startSelection();
void endSelection();
@@ -334,9 +425,10 @@ protected:
S32 prevWordPos(S32 cursorPos) const;
S32 nextWordPos(S32 cursorPos) const;
- S32 getLineCount() const { return mLineStartList.size(); }
+ S32 getLineCount() const { return mLineInfoList.size(); }
S32 getLineStart( S32 line ) const;
- void getLineAndOffset(S32 pos, S32* linep, S32* offsetp) 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);
@@ -344,13 +436,13 @@ protected:
void autoIndent();
- void findEmbeddedItemSegments();
+ void findEmbeddedItemSegments(S32 start, S32 end);
+ void insertSegment(LLTextSegmentPtr segment_to_insert);
+
virtual BOOL handleMouseUpOverSegment(S32 x, S32 y, MASK mask);
virtual llwchar pasteEmbeddedItem(llwchar ext_char) { return ext_char; }
- virtual void bindEmbeddedChars(const LLFontGL* font) const {}
- virtual void unbindEmbeddedChars(const LLFontGL* font) const {}
S32 findHTMLToken(const std::string &line, S32 pos, BOOL reverse) const;
BOOL findHTML(const std::string &line, S32 *begin, S32 *end) const;
@@ -361,7 +453,15 @@ protected:
class LLTextCmd
{
public:
- LLTextCmd( S32 pos, BOOL group_with_next ) : mPos(pos), mGroupWithNext(group_with_next) {}
+ 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;
@@ -372,16 +472,17 @@ protected:
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 ); }
+ 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; }
- private:
- const S32 mPos;
- BOOL 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);
@@ -392,12 +493,12 @@ protected:
S32 overwriteChar(S32 pos, llwchar wc);
void removeChar();
S32 removeChar(S32 pos);
- S32 insert(const S32 pos, const LLWString &wstr, const BOOL group_with_next_op);
- S32 remove(const S32 pos, const S32 length, const BOOL group_with_next_op);
- S32 append(const LLWString &wstr, const BOOL group_with_next_op);
+ 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); // returns num of chars actually inserted
+ 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);
@@ -441,22 +542,38 @@ protected:
BOOL mParseHighlights;
std::string mHTML;
- typedef std::vector<LLTextSegment *> segment_list_t;
- segment_list_t mSegments;
- const LLTextSegment* mHoverSegment;
+ segment_set_t mSegments;
+ LLTextSegmentPtr mHoverSegment;
// Scrollbar data
- class LLScrollbar* mScrollbar;
- BOOL mHideScrollbarForShortDocs;
- BOOL mTakesNonScrollClicks;
- void (*mOnScrollEndCallback)(void*);
+ 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;
+
+ BOOL mReadOnly;
+ BOOL mWordWrap;
+ BOOL mShowLineNumbers;
+
+ void updateSegments();
+
private:
//
@@ -465,32 +582,28 @@ private:
void pasteHelper(bool is_primary);
virtual LLTextViewModel* getViewModel() const;
-
- void updateSegments();
- void pruneSegments();
+ void reflow(S32 startpos = 0);
+
+ void clearSegments();
+ void createDefaultSegment();
+ LLStyleSP getDefaultStyle();
+ S32 getEditableIndex(S32 index, bool increasing_direction);
void drawBackground();
void drawSelectionBackground();
void drawCursor();
void drawText();
- void drawClippedSegment(const LLWString &wtext, S32 seg_start, S32 seg_end, F32 x, F32 y, S32 selection_left, S32 selection_right, const LLStyleSP& color, F32* right_x);
-
- void needsReflow()
- {
- mReflowNeeded = TRUE;
- // cursor might have moved, need to scroll
- mScrollNeeded = TRUE;
- }
- void needsScroll() { mScrollNeeded = TRUE; }
+ void drawLineNumbers();
+
+ S32 getFirstVisibleLine() const;
//
// Data
//
LLKeywords mKeywords;
- static LLUIColor mLinkColor;
- static void (*mURLcallback) (const std::string& url);
- static bool (*mSecondlifeURLcallback) (const std::string& url);
- static bool (*mSecondlifeURLcallbackRightClick) (const std::string& url);
+ static void (*sURLcallback) (const std::string& url);
+ static bool (*sSecondlifeURLcallback) (const std::string& url);
+ static bool (*sSecondlifeURLcallbackRightClick) (const std::string& url);
// Concrete LLTextCmd sub-classes used by the LLTextEditor base class
class LLTextCmdInsert;
@@ -500,7 +613,7 @@ private:
S32 mMaxTextByteLength; // Maximum length mText is allowed to be in bytes
- const LLFontGL* mGLFont;
+ const LLFontGL* mDefaultFont;
class LLViewBorder* mBorder;
@@ -515,49 +628,35 @@ private:
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 pred;
struct line_info
{
- line_info(S32 segment, S32 offset) : mSegment(segment), mOffset(offset) {}
- S32 mSegment;
- S32 mOffset;
- };
- struct line_info_compare
- {
- bool operator()(const line_info& a, const line_info& b) const
- {
- if (a.mSegment < b.mSegment)
- return true;
- else if (a.mSegment > b.mSegment)
- return false;
- else
- return a.mOffset < b.mOffset;
- }
+ 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 mLineStartList;
+ line_list_t mLineInfoList;
BOOL mReflowNeeded;
BOOL mScrollNeeded;
LLFrameTimer mKeystrokeTimer;
- LLUIColor mCursorColor;
- LLUIColor mFgColor;
- LLUIColor mDefaultColor;
- LLUIColor mReadOnlyFgColor;
- LLUIColor mWriteableBgColor;
- LLUIColor mReadOnlyBgColor;
- LLUIColor mFocusBgColor;
-
- BOOL mReadOnly;
- BOOL mWordWrap;
- BOOL mShowLineNumbers;
-
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 mScrolledToBottom;
BOOL mAllowEmbeddedItems;
@@ -571,47 +670,4 @@ private:
}; // end class LLTextEditor
-
-class LLTextSegment
-{
-public:
- // for creating a compare value
- LLTextSegment(S32 start);
- LLTextSegment( const LLStyleSP& style, S32 start, S32 end );
- LLTextSegment( const LLColor4& color, S32 start, S32 end, BOOL is_visible);
- LLTextSegment( const LLColor4& color, S32 start, S32 end );
- LLTextSegment( const LLColor3& color, S32 start, S32 end );
-
- S32 getStart() const { return mStart; }
- S32 getEnd() const { return mEnd; }
- void setEnd( S32 end ) { mEnd = end; }
- const LLColor4& getColor() const { return mStyle->getColor(); }
- void setColor(const LLColor4 &color) { mStyle->setColor(color); }
- const LLStyleSP& getStyle() const { return mStyle; }
- void setStyle(const LLStyleSP &style) { mStyle = style; }
- void setIsDefault(BOOL b) { mIsDefault = b; }
- BOOL getIsDefault() const { return mIsDefault; }
- void setToken( LLKeywordToken* token ) { mToken = token; }
- LLKeywordToken* getToken() const { return mToken; }
- BOOL getToolTip( std::string& msg ) const;
-
- void dump() const;
-
- struct compare
- {
- bool operator()(const LLTextSegment* a, const LLTextSegment* b) const
- {
- return a->mStart < b->mStart;
- }
- };
-
-private:
- LLStyleSP mStyle;
- S32 mStart;
- S32 mEnd;
- LLKeywordToken* mToken;
- BOOL mIsDefault;
-};
-
-
#endif // LL_TEXTEDITOR_
diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp
index fab8f61356..7e4df892c4 100644
--- a/indra/llui/llui.cpp
+++ b/indra/llui/llui.cpp
@@ -71,9 +71,6 @@
//
const LLColor4 UI_VERTEX_COLOR(1.f, 1.f, 1.f, 1.f);
-// Used to hide the flashing text cursor when window doesn't have focus.
-BOOL gShowTextEditCursor = TRUE;
-
// Language for UI construction
std::map<std::string, std::string> gTranslation;
std::list<std::string> gUntranslated;
@@ -1898,6 +1895,12 @@ void LLScreenClipRect::pushClipRect(const LLRect& rect)
{
LLRect top = sClipRectStack.top();
combined_clip_rect.intersectWith(top);
+
+ if(combined_clip_rect.isEmpty())
+ {
+ // avoid artifacts where zero area rects show up as lines
+ combined_clip_rect = LLRect::null;
+ }
}
sClipRectStack.push(combined_clip_rect);
}
diff --git a/indra/llui/llui.h b/indra/llui/llui.h
index b1943a7b02..1f9b0b2dbc 100644
--- a/indra/llui/llui.h
+++ b/indra/llui/llui.h
@@ -151,9 +151,6 @@ inline void gl_rect_2d_offset_local( const LLRect& rect, S32 pixel_offset, BOOL
gl_rect_2d_offset_local( rect.mLeft, rect.mTop, rect.mRight, rect.mBottom, pixel_offset, filled );
}
-// Used to hide the flashing text cursor when window doesn't have focus.
-extern BOOL gShowTextEditCursor;
-
class LLImageProviderInterface;
typedef void (*LLUIAudioCallback)(const LLUUID& uuid);
diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp
index 8aa7540446..4a9fec3191 100644
--- a/indra/llui/lluictrl.cpp
+++ b/indra/llui/lluictrl.cpp
@@ -278,24 +278,46 @@ void LLUICtrl::onMouseLeave(S32 x, S32 y, MASK mask)
{
mMouseLeaveSignal(this, getValue());
}
+
//virtual
-BOOL LLUICtrl::handleMouseDown(S32 x, S32 y, MASK mask){
+BOOL LLUICtrl::handleMouseDown(S32 x, S32 y, MASK mask)
+{
BOOL handled = LLView::handleMouseDown(x,y,mask);
mMouseDownSignal(this,x,y,mask);
return handled;
}
+
//virtual
-BOOL LLUICtrl::handleMouseUp(S32 x, S32 y, MASK mask){
+BOOL LLUICtrl::handleMouseUp(S32 x, S32 y, MASK mask)
+{
BOOL handled = LLView::handleMouseUp(x,y,mask);
mMouseUpSignal(this,x,y,mask);
return handled;
}
+
+//virtual
+BOOL LLUICtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = LLView::handleRightMouseDown(x,y,mask);
+ mRightMouseDownSignal(this,x,y,mask);
+ return handled;
+}
+
//virtual
-BOOL LLUICtrl::handleRightMouseUp(S32 x, S32 y, MASK mask){
+BOOL LLUICtrl::handleRightMouseUp(S32 x, S32 y, MASK mask)
+{
BOOL handled = LLView::handleRightMouseUp(x,y,mask);
- mRightClickSignal(this,x,y,mask);
+ mRightMouseUpSignal(this,x,y,mask);
return handled;
}
+
+// can't tab to children of a non-tab-stop widget
+BOOL LLUICtrl::canFocusChildren() const
+{
+ return hasTabStop();
+}
+
+
void LLUICtrl::onCommit()
{
mCommitSignal(this, getValue());
diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h
index 6ba3b01fcb..a4f539af14 100644
--- a/indra/llui/lluictrl.h
+++ b/indra/llui/lluictrl.h
@@ -186,8 +186,10 @@ public:
/*virtual*/ BOOL getTentative() const;
/*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask);
/*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL canFocusChildren() const;
/*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 handleRightMouseUp(S32 x, S32 y, MASK mask);
// From LLFocusableElement
@@ -258,7 +260,8 @@ public:
boost::signals2::connection setMouseDownCallback( const mouse_signal_t::slot_type& cb ) { return mMouseDownSignal.connect(cb); }
boost::signals2::connection setMouseUpCallback( const mouse_signal_t::slot_type& cb ) { return mMouseUpSignal.connect(cb); }
- boost::signals2::connection setRightClickedCallback( const mouse_signal_t::slot_type& cb ) { return mRightClickSignal.connect(cb); }
+ boost::signals2::connection setRightMouseDownCallback( const mouse_signal_t::slot_type& cb ) { return mRightMouseDownSignal.connect(cb); }
+ boost::signals2::connection setRightMouseUpCallback( const mouse_signal_t::slot_type& cb ) { return mRightMouseUpSignal.connect(cb); }
// *TODO: Deprecate; for backwards compatability only:
boost::signals2::connection setCommitCallback( boost::function<void (LLUICtrl*,void*)> cb, void* data);
@@ -292,7 +295,8 @@ protected:
mouse_signal_t mMouseDownSignal;
mouse_signal_t mMouseUpSignal;
- mouse_signal_t mRightClickSignal;
+ mouse_signal_t mRightMouseDownSignal;
+ mouse_signal_t mRightMouseUpSignal;
LLViewModelPtr mViewModel;
diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp
index 2bbede8c13..1161101f90 100644
--- a/indra/llui/lluictrlfactory.cpp
+++ b/indra/llui/lluictrlfactory.cpp
@@ -45,31 +45,7 @@
#include "llquaternion.h"
// this library includes
-#include "llbutton.h"
-#include "llcheckboxctrl.h"
-//#include "llcolorswatch.h"
-#include "llcombobox.h"
-#include "llcontrol.h"
-#include "lldir.h"
-#include "llevent.h"
#include "llfloater.h"
-#include "lliconctrl.h"
-#include "lllineeditor.h"
-#include "llmenugl.h"
-#include "llradiogroup.h"
-#include "llscrollcontainer.h"
-#include "llscrollingpanellist.h"
-#include "llscrolllistctrl.h"
-#include "llslider.h"
-#include "llsliderctrl.h"
-#include "llmultislider.h"
-#include "llmultisliderctrl.h"
-#include "llspinctrl.h"
-#include "lltabcontainer.h"
-#include "lltextbox.h"
-#include "lltexteditor.h"
-#include "llui.h"
-#include "llviewborder.h"
LLFastTimer::DeclareTimer FTM_WIDGET_CONSTRUCTION("Widget Construction");
LLFastTimer::DeclareTimer FTM_INIT_FROM_PARAMS("Widget InitFromParams");
@@ -149,7 +125,7 @@ void LLUICtrlFactory::createChildren(LLView* viewp, LLXMLNodePtr node, const wid
}
-LLFastTimer::DeclareTimer FTM_XML_PARSE("XML Reading/Parsing");
+static LLFastTimer::DeclareTimer FTM_XML_PARSE("XML Reading/Parsing");
//-----------------------------------------------------------------------------
// getLayeredXMLNode()
//-----------------------------------------------------------------------------
@@ -177,14 +153,14 @@ bool LLUICtrlFactory::getLocalizedXMLNode(const std::string &xui_filename, LLXML
}
}
-static LLFastTimer::DeclareTimer BUILD_FLOATERS("Build Floaters");
+static LLFastTimer::DeclareTimer FTM_BUILD_FLOATERS("Build Floaters");
//-----------------------------------------------------------------------------
// buildFloater()
//-----------------------------------------------------------------------------
void LLUICtrlFactory::buildFloater(LLFloater* floaterp, const std::string& filename, LLXMLNodePtr output_node)
{
- LLFastTimer timer(BUILD_FLOATERS);
+ LLFastTimer timer(FTM_BUILD_FLOATERS);
LLXMLNodePtr root;
//if exporting, only load the language being exported,
@@ -248,14 +224,14 @@ S32 LLUICtrlFactory::saveToXML(LLView* viewp, const std::string& filename)
return 0;
}
-static LLFastTimer::DeclareTimer BUILD_PANELS("Build Panels");
+static LLFastTimer::DeclareTimer FTM_BUILD_PANELS("Build Panels");
//-----------------------------------------------------------------------------
// buildPanel()
//-----------------------------------------------------------------------------
BOOL LLUICtrlFactory::buildPanel(LLPanel* panelp, const std::string& filename, LLXMLNodePtr output_node)
{
- LLFastTimer timer(BUILD_PANELS);
+ LLFastTimer timer(FTM_BUILD_PANELS);
BOOL didPost = FALSE;
LLXMLNodePtr root;
@@ -317,7 +293,7 @@ BOOL LLUICtrlFactory::buildPanel(LLPanel* panelp, const std::string& filename, L
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
-LLFastTimer::DeclareTimer FTM_CREATE_FROM_XML("Create child widget");
+static LLFastTimer::DeclareTimer FTM_CREATE_FROM_XML("Create child widget");
LLView *LLUICtrlFactory::createFromXML(LLXMLNodePtr node, LLView* parent, const std::string& filename, const widget_registry_t& registry, LLXMLNodePtr output_node)
{
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index 0ba028e1c8..e2dc85d03f 100644
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -1301,6 +1301,11 @@ LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask)
void LLView::draw()
{
+ drawChildren();
+}
+
+void LLView::drawChildren()
+{
if (sDebugRects)
{
drawDebugRect();
@@ -1329,7 +1334,7 @@ void LLView::draw()
{
// Only draw views that are within the root view
localRectToScreen(viewp->getRect(),&screenRect);
- if ( rootRect.rectInRect(&screenRect) )
+ if ( rootRect.overlaps(screenRect) )
{
glMatrixMode(GL_MODELVIEW);
LLUI::pushMatrix();
@@ -1540,7 +1545,7 @@ void LLView::updateBoundingRect()
LLRect child_bounding_rect = childp->getBoundingRect();
- if (local_bounding_rect.isNull())
+ if (local_bounding_rect.isEmpty())
{
// start out with bounding rect equal to first visible child's bounding rect
local_bounding_rect = child_bounding_rect;
@@ -1548,7 +1553,7 @@ void LLView::updateBoundingRect()
else
{
// accumulate non-null children rectangles
- if (!child_bounding_rect.isNull())
+ if (!child_bounding_rect.isEmpty())
{
local_bounding_rect.unionWith(child_bounding_rect);
}
@@ -1639,7 +1644,7 @@ BOOL LLView::hasAncestor(const LLView* parentp) const
BOOL LLView::childHasKeyboardFocus( const std::string& childname ) const
{
- LLView *child = getChildView(childname, TRUE, FALSE);
+ LLView *child = findChildView(childname, TRUE);
if (child)
{
return gFocusMgr.childHasKeyboardFocus(child);
@@ -1654,13 +1659,27 @@ BOOL LLView::childHasKeyboardFocus( const std::string& childname ) const
BOOL LLView::hasChild(const std::string& childname, BOOL recurse) const
{
- return getChildView(childname, recurse, FALSE) != NULL;
+ return findChildView(childname, recurse) != NULL;
}
//-----------------------------------------------------------------------------
// getChildView()
//-----------------------------------------------------------------------------
-LLView* LLView::getChildView(const std::string& name, BOOL recurse, BOOL create_if_missing) const
+LLView* LLView::getChildView(const std::string& name, BOOL recurse) const
+{
+ LLView* child = findChildView(name, recurse);
+ if (!child)
+ {
+ child = getDefaultWidget<LLView>(name);
+ if (!child)
+ {
+ child = LLUICtrlFactory::createDefaultWidget<LLView>(name);
+ }
+ }
+ return child;
+}
+
+LLView* LLView::findChildView(const std::string& name, BOOL recurse) const
{
//richard: should we allow empty names?
//if(name.empty())
@@ -1681,23 +1700,13 @@ LLView* LLView::getChildView(const std::string& name, BOOL recurse, BOOL create_
for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
{
LLView* childp = *child_it;
- LLView* viewp = childp->getChildView(name, recurse, FALSE);
+ LLView* viewp = childp->findChildView(name, recurse);
if ( viewp )
{
return viewp;
}
}
}
-
- if (create_if_missing)
- {
- LLView* view = getDefaultWidget<LLView>(name);
- if (!view)
- {
- view = LLUICtrlFactory::createDefaultWidget<LLView>(name);
- }
- return view;
- }
return NULL;
}
diff --git a/indra/llui/llview.h b/indra/llui/llview.h
index ee49276139..6247bf036c 100644
--- a/indra/llui/llview.h
+++ b/indra/llui/llview.h
@@ -213,6 +213,9 @@ protected:
LLView(const LLView::Params&);
friend class LLUICtrlFactory;
+private:
+ // widgets in general are not copyable
+ LLView(const LLView& other) {};
public:
#if LL_DEBUG
static BOOL sIsDrawing;
@@ -421,6 +424,7 @@ public:
virtual std::string getShowNamesToolTip();
virtual void draw();
+ void drawChildren();
void parseFollowsFlags(const LLView::Params& params);
@@ -484,19 +488,20 @@ public:
template <class T> T* findChild(const std::string& name, BOOL recurse = TRUE) const
{
- LLView* child = getChildView(name, recurse, FALSE);
+ LLView* child = findChildView(name, recurse);
T* result = dynamic_cast<T*>(child);
return result;
}
- template <class T> T* getChild(const std::string& name, BOOL recurse = TRUE, BOOL create_if_missing = TRUE) const;
+ template <class T> T* getChild(const std::string& name, BOOL recurse = TRUE) const;
template <class T> T& getChildRef(const std::string& name, BOOL recurse = TRUE) const
{
- return *getChild<T>(name, recurse, TRUE);
+ return *getChild<T>(name, recurse);
}
- virtual LLView* getChildView(const std::string& name, BOOL recurse = TRUE, BOOL create_if_missing = TRUE) const;
+ virtual LLView* getChildView(const std::string& name, BOOL recurse = TRUE) const;
+ virtual LLView* findChildView(const std::string& name, BOOL recurse = TRUE) const;
template <class T> T* getDefaultWidget(const std::string& name) const
{
@@ -636,9 +641,9 @@ private:
LLView::child_tab_order_t mTabOrder;
};
-template <class T> T* LLView::getChild(const std::string& name, BOOL recurse, BOOL create_if_missing) const
+template <class T> T* LLView::getChild(const std::string& name, BOOL recurse) const
{
- LLView* child = getChildView(name, recurse, FALSE);
+ LLView* child = findChildView(name, recurse);
T* result = dynamic_cast<T*>(child);
if (!result)
{
@@ -647,28 +652,25 @@ template <class T> T* LLView::getChild(const std::string& name, BOOL recurse, BO
{
llwarns << "Found child named " << name << " but of wrong type " << typeid(child).name() << ", expecting " << typeid(T*).name() << llendl;
}
- if (create_if_missing)
+ result = getDefaultWidget<T>(name);
+ if (!result)
{
- result = getDefaultWidget<T>(name);
- if (!result)
+ result = LLUICtrlFactory::getDefaultWidget<T>(name);
+
+ if (result)
{
- result = LLUICtrlFactory::getDefaultWidget<T>(name);
-
- if (result)
- {
- // *NOTE: You cannot call mFoo = getChild<LLFoo>("bar")
- // in a floater or panel constructor. The widgets will not
- // be ready. Instead, put it in postBuild().
- llwarns << "Making dummy " << typeid(T).name() << " named \"" << name << "\" in " << getName() << llendl;
- }
- else
- {
- llwarns << "Failed to create dummy " << typeid(T).name() << llendl;
- return NULL;
- }
-
- getDefaultWidgetMap()[name] = result;
+ // *NOTE: You cannot call mFoo = getChild<LLFoo>("bar")
+ // in a floater or panel constructor. The widgets will not
+ // be ready. Instead, put it in postBuild().
+ llwarns << "Making dummy " << typeid(T).name() << " named \"" << name << "\" in " << getName() << llendl;
}
+ else
+ {
+ llwarns << "Failed to create dummy " << typeid(T).name() << llendl;
+ return NULL;
+ }
+
+ getDefaultWidgetMap()[name] = result;
}
}
return result;
diff --git a/indra/llui/llviewborder.cpp b/indra/llui/llviewborder.cpp
index 860aa3302e..f41c98f7b3 100644
--- a/indra/llui/llviewborder.cpp
+++ b/indra/llui/llviewborder.cpp
@@ -134,9 +134,7 @@ void LLViewBorder::draw()
}
}
- // draw the children
- LLView::draw();
-
+ drawChildren();
}
void LLViewBorder::drawOnePixelLines()