aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llviewertexteditor.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/newview/llviewertexteditor.cpp
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/newview/llviewertexteditor.cpp')
-rw-r--r--linden/indra/newview/llviewertexteditor.cpp1490
1 files changed, 1490 insertions, 0 deletions
diff --git a/linden/indra/newview/llviewertexteditor.cpp b/linden/indra/newview/llviewertexteditor.cpp
new file mode 100644
index 0000000..00e25af
--- /dev/null
+++ b/linden/indra/newview/llviewertexteditor.cpp
@@ -0,0 +1,1490 @@
1/**
2 * @file llviewertexteditor.cpp
3 * @brief Text editor widget to let users enter a a multi-line ASCII document.
4 *
5 * Copyright (c) 2001-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "llviewerprecompiledheaders.h"
29
30#include "llfocusmgr.h"
31#include "audioengine.h"
32#include "llagent.h"
33#include "llinventory.h"
34#include "llinventorymodel.h"
35#include "llinventoryview.h"
36
37#include "llviewertexteditor.h"
38
39#include "llfloaterchat.h"
40#include "llfloaterworldmap.h"
41#include "llnotify.h"
42#include "llpreview.h"
43#include "llpreviewtexture.h"
44#include "llpreviewnotecard.h"
45#include "llpreviewlandmark.h"
46#include "llscrollbar.h"
47#include "lltooldraganddrop.h"
48#include "llviewercontrol.h"
49#include "llviewerimagelist.h"
50#include "llviewerwindow.h"
51#include "llviewerinventory.h"
52#include "llnotecard.h"
53#include "llmemorystream.h"
54
55extern BOOL gPacificDaylightTime;
56
57////////////////////////////////////////////////////////////
58// LLEmbeddedItems
59//
60// Embedded items are stored as:
61// * A global map of llwchar to LLInventoryItem
62// ** This is unique for each item embedded in any notecard
63// to support copy/paste across notecards
64// * A per-notecard set of embeded llwchars for easy removal
65// from the global list
66// * A per-notecard vector of embedded lwchars for mapping from
67// old style 0x80 + item format notechards
68
69class LLEmbeddedItems
70{
71public:
72 LLEmbeddedItems(const LLViewerTextEditor* editor);
73 ~LLEmbeddedItems();
74 void clear();
75
76 void bindEmbeddedChars(const LLFontGL* font);
77 void unbindEmbeddedChars(const LLFontGL* font);
78
79 BOOL insertEmbeddedItem(LLInventoryItem* item, llwchar* value, bool is_new);
80 BOOL removeEmbeddedItem( llwchar ext_char );
81
82 BOOL hasEmbeddedItem(llwchar ext_char); // returns TRUE if /this/ editor has an entry for this item
83
84 void getEmbeddedItemList( std::vector<LLPointer<LLInventoryItem> >& items );
85 void addItems(const std::vector<LLPointer<LLInventoryItem> >& items);
86
87 llwchar getEmbeddedCharFromIndex(S32 index);
88
89 void removeUnusedChars();
90 void copyUsedCharsToIndexed();
91 S32 getIndexFromEmbeddedChar(llwchar wch);
92
93 void markSaved();
94
95 static LLInventoryItem* getEmbeddedItem(llwchar ext_char); // returns item from static list
96 static BOOL getEmbeddedItemSaved(llwchar ext_char); // returns whether item from static list is saved
97
98 struct embedded_info_t
99 {
100 LLPointer<LLInventoryItem> mItem;
101 BOOL mSaved;
102 };
103private:
104 typedef std::map<llwchar, embedded_info_t > item_map_t;
105 static item_map_t sEntries;
106 static std::stack<llwchar> sFreeEntries;
107
108 std::set<llwchar> mEmbeddedUsedChars; // list of used llwchars
109 std::vector<llwchar> mEmbeddedIndexedChars; // index -> wchar for 0x80 + index format
110 const LLViewerTextEditor* mEditor;
111};
112
113//statics
114LLEmbeddedItems::item_map_t LLEmbeddedItems::sEntries;
115std::stack<llwchar> LLEmbeddedItems::sFreeEntries;
116
117LLEmbeddedItems::LLEmbeddedItems(const LLViewerTextEditor* editor)
118 : mEditor(editor)
119{
120}
121
122LLEmbeddedItems::~LLEmbeddedItems()
123{
124 clear();
125}
126
127void LLEmbeddedItems::clear()
128{
129 // Remove entries for this editor from static list
130 for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin();
131 iter != mEmbeddedUsedChars.end();)
132 {
133 std::set<llwchar>::iterator nextiter = iter++;
134 removeEmbeddedItem(*nextiter);
135 }
136 mEmbeddedUsedChars.clear();
137}
138
139// Inserts a new unique entry
140BOOL LLEmbeddedItems::insertEmbeddedItem( LLInventoryItem* item, llwchar* ext_char, bool is_new)
141{
142 // Now insert a new one
143 llwchar wc_emb;
144 if (!sFreeEntries.empty())
145 {
146 wc_emb = sFreeEntries.top();
147 sFreeEntries.pop();
148 }
149 else if (sEntries.empty())
150 {
151 wc_emb = FIRST_EMBEDDED_CHAR;
152 }
153 else
154 {
155 item_map_t::iterator last = sEntries.end();
156 --last;
157 wc_emb = last->first;
158 if (wc_emb >= LAST_EMBEDDED_CHAR)
159 {
160 return FALSE;
161 }
162 ++wc_emb;
163 }
164
165 sEntries[wc_emb].mItem = item;
166 sEntries[wc_emb].mSaved = is_new ? FALSE : TRUE;
167 *ext_char = wc_emb;
168 mEmbeddedUsedChars.insert(wc_emb);
169 return TRUE;
170}
171
172// Removes an entry (all entries are unique)
173BOOL LLEmbeddedItems::removeEmbeddedItem( llwchar ext_char )
174{
175 mEmbeddedUsedChars.erase(ext_char);
176 item_map_t::iterator iter = sEntries.find(ext_char);
177 if (iter != sEntries.end())
178 {
179 sEntries.erase(ext_char);
180 sFreeEntries.push(ext_char);
181 return TRUE;
182 }
183 return FALSE;
184}
185
186// static
187LLInventoryItem* LLEmbeddedItems::getEmbeddedItem(llwchar ext_char)
188{
189 if( ext_char >= FIRST_EMBEDDED_CHAR && ext_char <= LAST_EMBEDDED_CHAR )
190 {
191 item_map_t::iterator iter = sEntries.find(ext_char);
192 if (iter != sEntries.end())
193 {
194 return iter->second.mItem;
195 }
196 }
197 return NULL;
198}
199
200// static
201BOOL LLEmbeddedItems::getEmbeddedItemSaved(llwchar ext_char)
202{
203 if( ext_char >= FIRST_EMBEDDED_CHAR && ext_char <= LAST_EMBEDDED_CHAR )
204 {
205 item_map_t::iterator iter = sEntries.find(ext_char);
206 if (iter != sEntries.end())
207 {
208 return iter->second.mSaved;
209 }
210 }
211 return FALSE;
212}
213
214llwchar LLEmbeddedItems::getEmbeddedCharFromIndex(S32 index)
215{
216 if (index >= (S32)mEmbeddedIndexedChars.size())
217 {
218 llwarns << "No item for embedded char " << index << " using LL_UNKNOWN_CHAR" << llendl;
219 return LL_UNKNOWN_CHAR;
220 }
221 return mEmbeddedIndexedChars[index];
222}
223
224void LLEmbeddedItems::removeUnusedChars()
225{
226 std::set<llwchar> used = mEmbeddedUsedChars;
227 const LLWString& wtext = mEditor->getWText();
228 for (S32 i=0; i<(S32)wtext.size(); i++)
229 {
230 llwchar wc = wtext[i];
231 if( wc >= FIRST_EMBEDDED_CHAR && wc <= LAST_EMBEDDED_CHAR )
232 {
233 used.erase(wc);
234 }
235 }
236 // Remove chars not actually used
237 for (std::set<llwchar>::iterator iter = used.begin();
238 iter != used.end(); ++iter)
239 {
240 removeEmbeddedItem(*iter);
241 }
242}
243
244void LLEmbeddedItems::copyUsedCharsToIndexed()
245{
246 // Prune unused items
247 removeUnusedChars();
248
249 // Copy all used llwchars to mEmbeddedIndexedChars
250 mEmbeddedIndexedChars.clear();
251 for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin();
252 iter != mEmbeddedUsedChars.end(); ++iter)
253 {
254 mEmbeddedIndexedChars.push_back(*iter);
255 }
256}
257
258S32 LLEmbeddedItems::getIndexFromEmbeddedChar(llwchar wch)
259{
260 S32 idx = 0;
261 for (std::vector<llwchar>::iterator iter = mEmbeddedIndexedChars.begin();
262 iter != mEmbeddedIndexedChars.end(); ++iter)
263 {
264 if (wch == *iter)
265 break;
266 ++idx;
267 }
268 if (idx < (S32)mEmbeddedIndexedChars.size())
269 {
270 return idx;
271 }
272 else
273 {
274 llwarns << "Embedded char " << wch << " not found, using 0" << llendl;
275 return 0;
276 }
277}
278
279BOOL LLEmbeddedItems::hasEmbeddedItem(llwchar ext_char)
280{
281 std::set<llwchar>::iterator iter = mEmbeddedUsedChars.find(ext_char);
282 if (iter != mEmbeddedUsedChars.end())
283 {
284 return TRUE;
285 }
286 return FALSE;
287}
288
289void LLEmbeddedItems::bindEmbeddedChars( const LLFontGL* font )
290{
291 if( sEntries.empty() )
292 {
293 return;
294 }
295
296 for (std::set<llwchar>::iterator iter1 = mEmbeddedUsedChars.begin(); iter1 != mEmbeddedUsedChars.end(); ++iter1)
297 {
298 llwchar wch = *iter1;
299 item_map_t::iterator iter2 = sEntries.find(wch);
300 if (iter2 == sEntries.end())
301 {
302 continue;
303 }
304 LLInventoryItem* item = iter2->second.mItem;
305 if (!item)
306 {
307 continue;
308 }
309 const char* img_name;
310 switch( item->getType() )
311 {
312 case LLAssetType::AT_TEXTURE:
313 if(item->getInventoryType() == LLInventoryType::IT_SNAPSHOT)
314 {
315 img_name = "inv_item_snapshot.tga";
316 }
317 else
318 {
319 img_name = "inv_item_texture.tga";
320 }
321
322 break;
323 case LLAssetType::AT_SOUND: img_name = "inv_item_sound.tga"; break;
324 case LLAssetType::AT_LANDMARK:
325 if (item->getFlags() & LLInventoryItem::II_FLAGS_LANDMARK_VISITED)
326 {
327 img_name = "inv_item_landmark_visited.tga";
328 }
329 else
330 {
331 img_name = "inv_item_landmark.tga";
332 }
333 break;
334 case LLAssetType::AT_CLOTHING: img_name = "inv_item_clothing.tga"; break;
335 case LLAssetType::AT_OBJECT: img_name = "inv_item_object.tga"; break;
336 case LLAssetType::AT_NOTECARD: img_name = "inv_item_notecard.tga"; break;
337 case LLAssetType::AT_LSL_TEXT: img_name = "inv_item_script.tga"; break;
338 case LLAssetType::AT_BODYPART: img_name = "inv_item_bodypart.tga"; break;
339 case LLAssetType::AT_ANIMATION: img_name = "inv_item_animation.tga";break;
340 case LLAssetType::AT_GESTURE: img_name = "inv_item_gesture.tga"; break;
341 default: llassert(0); continue;
342 }
343
344 LLViewerImage* image = gImageList.getImage(LLUUID(gViewerArt.getString(img_name)), MIPMAP_FALSE, TRUE);
345
346 ((LLFontGL*)font)->addEmbeddedChar( wch, image, item->getName() );
347 }
348}
349
350void LLEmbeddedItems::unbindEmbeddedChars( const LLFontGL* font )
351{
352 if( sEntries.empty() )
353 {
354 return;
355 }
356
357 for (std::set<llwchar>::iterator iter1 = mEmbeddedUsedChars.begin(); iter1 != mEmbeddedUsedChars.end(); ++iter1)
358 {
359 ((LLFontGL*)font)->removeEmbeddedChar(*iter1);
360 }
361}
362
363void LLEmbeddedItems::addItems(const std::vector<LLPointer<LLInventoryItem> >& items)
364{
365 for (std::vector<LLPointer<LLInventoryItem> >::const_iterator iter = items.begin();
366 iter != items.end(); ++iter)
367 {
368 LLInventoryItem* item = *iter;
369 if (item)
370 {
371 llwchar wc;
372 if (!insertEmbeddedItem( item, &wc, false ))
373 {
374 break;
375 }
376 mEmbeddedIndexedChars.push_back(wc);
377 }
378 }
379}
380
381void LLEmbeddedItems::getEmbeddedItemList( std::vector<LLPointer<LLInventoryItem> >& items )
382{
383 for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin(); iter != mEmbeddedUsedChars.end(); ++iter)
384 {
385 llwchar wc = *iter;
386 LLPointer<LLInventoryItem> item = getEmbeddedItem(wc);
387 if (item)
388 {
389 items.push_back(item);
390 }
391 }
392}
393
394void LLEmbeddedItems::markSaved()
395{
396 for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin(); iter != mEmbeddedUsedChars.end(); ++iter)
397 {
398 llwchar wc = *iter;
399 sEntries[wc].mSaved = TRUE;
400 }
401}
402
403///////////////////////////////////////////////////////////////////
404
405class LLTextCmdInsertEmbeddedItem : public LLTextCmd
406{
407public:
408 LLTextCmdInsertEmbeddedItem( S32 pos, LLInventoryItem* item )
409 : LLTextCmd(pos, FALSE),
410 mExtCharValue(0)
411 {
412 mItem = item;
413 }
414
415 virtual BOOL execute( LLTextEditor* editor, S32* delta )
416 {
417 LLViewerTextEditor* viewer_editor = (LLViewerTextEditor*)editor;
418 // Take this opportunity to remove any unused embedded items from this editor
419 viewer_editor->mEmbeddedItemList->removeUnusedChars();
420 if(viewer_editor->mEmbeddedItemList->insertEmbeddedItem( mItem, &mExtCharValue, true ) )
421 {
422 LLWString ws;
423 ws.assign(1, mExtCharValue);
424 *delta = insert(editor, mPos, ws );
425 return (*delta != 0);
426 }
427 return FALSE;
428 }
429
430 virtual S32 undo( LLTextEditor* editor )
431 {
432 remove(editor, mPos, 1);
433 return mPos;
434 }
435
436 virtual S32 redo( LLTextEditor* editor )
437 {
438 LLWString ws;
439 ws += mExtCharValue;
440 insert(editor, mPos, ws );
441 return mPos + 1;
442 }
443 virtual BOOL hasExtCharValue( llwchar value )
444 {
445 return (value == mExtCharValue);
446 }
447
448private:
449 LLPointer<LLInventoryItem> mItem;
450 llwchar mExtCharValue;
451};
452
453struct LLNotecardCopyInfo
454{
455 LLNotecardCopyInfo(LLViewerTextEditor *ed, LLInventoryItem *item)
456 : mTextEd(ed)
457 {
458 mItem = item;
459 }
460
461 LLViewerTextEditor* mTextEd;
462 // need to make this be a copy (not a * here) because it isn't stable.
463 // I wish we had passed LLPointers all the way down, but we didn't
464 LLPointer<LLInventoryItem> mItem;
465};
466
467//----------------------------------------------------------------------------
468
469//
470// Member functions
471//
472
473LLViewerTextEditor::LLViewerTextEditor(const LLString& name,
474 const LLRect& rect,
475 S32 max_length,
476 const LLString& default_text,
477 const LLFontGL* font,
478 BOOL allow_embedded_items)
479 : LLTextEditor(name, rect, max_length, default_text, font, allow_embedded_items),
480 mDragItemSaved(FALSE)
481{
482 mEmbeddedItemList = new LLEmbeddedItems(this);
483}
484
485LLViewerTextEditor::~LLViewerTextEditor()
486{
487 delete mEmbeddedItemList;
488}
489
490///////////////////////////////////////////////////////////////////
491// virtual
492void LLViewerTextEditor::makePristine()
493{
494 mEmbeddedItemList->markSaved();
495 LLTextEditor::makePristine();
496}
497
498///////////////////////////////////////////////////////////////////
499
500BOOL LLViewerTextEditor::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen)
501{
502 if (pointInView(x, y) && getVisible())
503 {
504 for (child_list_const_iter_t child_iter = getChildList()->begin();
505 child_iter != getChildList()->end(); ++child_iter)
506 {
507 LLView *viewp = *child_iter;
508 S32 local_x = x - viewp->getRect().mLeft;
509 S32 local_y = y - viewp->getRect().mBottom;
510 if( viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen ) )
511 {
512 return TRUE;
513 }
514 }
515
516 if( mSegments.empty() )
517 {
518 return TRUE;
519 }
520
521 LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y );
522 if( cur_segment )
523 {
524 BOOL has_tool_tip = FALSE;
525 if( cur_segment->getStyle().getIsEmbeddedItem() )
526 {
527 LLWString wtip;
528 has_tool_tip = getEmbeddedItemToolTipAtPos(cur_segment->getStart(), wtip);
529 msg = wstring_to_utf8str(wtip);
530 }
531 else
532 {
533 has_tool_tip = cur_segment->getToolTip( msg );
534 }
535 if( has_tool_tip )
536 {
537 // Just use a slop area around the cursor
538 // Convert rect local to screen coordinates
539 S32 SLOP = 8;
540 localPointToScreen(
541 x - SLOP, y - SLOP,
542 &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
543 sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP;
544 sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP;
545 }
546 }
547 return TRUE;
548 }
549 return FALSE;
550}
551
552BOOL LLViewerTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
553{
554 BOOL handled = FALSE;
555
556 // Let scrollbar have first dibs
557 handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
558
559 // enable I Agree checkbox if the user scrolled through entire text
560 BOOL was_scrolled_to_bottom = (mScrollbar->getDocPos() == mScrollbar->getDocPosMax());
561 if (mOnScrollEndCallback && was_scrolled_to_bottom)
562 {
563 mOnScrollEndCallback(mOnScrollEndData);
564 }
565
566 if( !handled && mTakesNonScrollClicks)
567 {
568 if (!(mask & MASK_SHIFT))
569 {
570 deselect();
571 }
572
573 BOOL start_select = TRUE;
574 if( mAllowEmbeddedItems )
575 {
576 setCursorAtLocalPos( x, y, FALSE );
577 llwchar wc = 0;
578 if (mCursorPos < getLength())
579 {
580 wc = mWText[mCursorPos];
581 }
582 LLInventoryItem* item_at_pos = LLEmbeddedItems::getEmbeddedItem(wc);
583 if (item_at_pos)
584 {
585 mDragItem = item_at_pos;
586 mDragItemSaved = LLEmbeddedItems::getEmbeddedItemSaved(wc);
587 gFocusMgr.setMouseCapture( this, NULL );
588 mMouseDownX = x;
589 mMouseDownY = y;
590 S32 screen_x;
591 S32 screen_y;
592 localPointToScreen(x, y, &screen_x, &screen_y );
593 gToolDragAndDrop->setDragStart( screen_x, screen_y );
594
595 start_select = FALSE;
596 }
597 else
598 {
599 mDragItem = NULL;
600 }
601 }
602
603 if( start_select )
604 {
605 // If we're not scrolling (handled by child), then we're selecting
606 if (mask & MASK_SHIFT)
607 {
608 S32 old_cursor_pos = mCursorPos;
609 setCursorAtLocalPos( x, y, TRUE );
610
611 if (hasSelection())
612 {
613 /* Mac-like behavior - extend selection towards the cursor
614 if (mCursorPos < mSelectionStart
615 && mCursorPos < mSelectionEnd)
616 {
617 // ...left of selection
618 mSelectionStart = llmax(mSelectionStart, mSelectionEnd);
619 mSelectionEnd = mCursorPos;
620 }
621 else if (mCursorPos > mSelectionStart
622 && mCursorPos > mSelectionEnd)
623 {
624 // ...right of selection
625 mSelectionStart = llmin(mSelectionStart, mSelectionEnd);
626 mSelectionEnd = mCursorPos;
627 }
628 else
629 {
630 mSelectionEnd = mCursorPos;
631 }
632 */
633 // Windows behavior
634 mSelectionEnd = mCursorPos;
635 }
636 else
637 {
638 mSelectionStart = old_cursor_pos;
639 mSelectionEnd = mCursorPos;
640 }
641 // assume we're starting a drag select
642 mIsSelecting = TRUE;
643 }
644 else
645 {
646 setCursorAtLocalPos( x, y, TRUE );
647 startSelection();
648 }
649 gFocusMgr.setMouseCapture( this, &LLTextEditor::onMouseCaptureLost );
650 }
651
652 handled = TRUE;
653 }
654
655 if (mTakesFocus)
656 {
657 setFocus( TRUE );
658 handled = TRUE;
659 }
660
661 // Delay cursor flashing
662 mKeystrokeTimer.reset();
663
664 return handled;
665}
666
667
668BOOL LLViewerTextEditor::handleHover(S32 x, S32 y, MASK mask)
669{
670 BOOL handled = FALSE;
671
672 if (!mDragItem)
673 {
674 // leave hover segment active during drag and drop
675 mHoverSegment = NULL;
676 }
677 if( getVisible() )
678 {
679 if(gFocusMgr.getMouseCapture() == this )
680 {
681 if( mIsSelecting )
682 {
683 if (x != mLastSelectionX || y != mLastSelectionY)
684 {
685 mLastSelectionX = x;
686 mLastSelectionY = y;
687 }
688
689 if( y > mTextRect.mTop )
690 {
691 mScrollbar->setDocPos( mScrollbar->getDocPos() - 1 );
692 }
693 else
694 if( y < mTextRect.mBottom )
695 {
696 mScrollbar->setDocPos( mScrollbar->getDocPos() + 1 );
697 }
698
699 setCursorAtLocalPos( x, y, TRUE );
700 mSelectionEnd = mCursorPos;
701
702 updateScrollFromCursor();
703 getWindow()->setCursor(UI_CURSOR_IBEAM);
704 }
705 else if( mDragItem )
706 {
707 S32 screen_x;
708 S32 screen_y;
709 localPointToScreen(x, y, &screen_x, &screen_y );
710 if( gToolDragAndDrop->isOverThreshold( screen_x, screen_y ) )
711 {
712 gToolDragAndDrop->beginDrag(
713 LLAssetType::lookupDragAndDropType( mDragItem->getType() ),
714 mDragItem->getUUID(),
715 LLToolDragAndDrop::SOURCE_NOTECARD,
716 mSourceID, mObjectID);
717
718 return gToolDragAndDrop->handleHover( x, y, mask );
719 }
720 getWindow()->setCursor(UI_CURSOR_HAND);
721 }
722
723 lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
724 handled = TRUE;
725 }
726
727 if( !handled )
728 {
729 // Pass to children
730 handled = LLView::childrenHandleHover(x, y, mask) != NULL;
731 }
732
733 if( handled )
734 {
735 // Delay cursor flashing
736 mKeystrokeTimer.reset();
737 }
738
739 // Opaque
740 if( !handled && mTakesNonScrollClicks)
741 {
742 // Check to see if we're over an HTML-style link
743 if( !mSegments.empty() )
744 {
745 LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y );
746 if( cur_segment )
747 {
748 if(cur_segment->getStyle().isLink())
749 {
750 lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over link, inactive)" << llendl;
751 getWindow()->setCursor(UI_CURSOR_HAND);
752 handled = TRUE;
753 }
754 else
755 if(cur_segment->getStyle().getIsEmbeddedItem())
756 {
757 lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over embedded item, inactive)" << llendl;
758 getWindow()->setCursor(UI_CURSOR_HAND);
759 //getWindow()->setCursor(UI_CURSOR_ARROW);
760 handled = TRUE;
761 }
762 mHoverSegment = cur_segment;
763 }
764 }
765
766 if( !handled )
767 {
768 lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
769 if (!mScrollbar->getVisible() || x < mRect.getWidth() - SCROLLBAR_SIZE)
770 {
771 getWindow()->setCursor(UI_CURSOR_IBEAM);
772 }
773 else
774 {
775 getWindow()->setCursor(UI_CURSOR_ARROW);
776 }
777 handled = TRUE;
778 }
779 }
780 }
781
782 return handled;
783}
784
785
786BOOL LLViewerTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)
787{
788 BOOL handled = FALSE;
789
790 // let scrollbar have first dibs
791 handled = LLView::childrenHandleMouseUp(x, y, mask) != NULL;
792
793 // enable I Agree checkbox if the user scrolled through entire text
794 BOOL was_scrolled_to_bottom = (mScrollbar->getDocPos() == mScrollbar->getDocPosMax());
795 if (mOnScrollEndCallback && was_scrolled_to_bottom)
796 {
797 mOnScrollEndCallback(mOnScrollEndData);
798 }
799
800 if( !handled && mTakesNonScrollClicks)
801 {
802 if( mIsSelecting )
803 {
804 // Finish selection
805 if( y > mTextRect.mTop )
806 {
807 mScrollbar->setDocPos( mScrollbar->getDocPos() - 1 );
808 }
809 else
810 if( y < mTextRect.mBottom )
811 {
812 mScrollbar->setDocPos( mScrollbar->getDocPos() + 1 );
813 }
814
815 setCursorAtLocalPos( x, y, TRUE );
816 endSelection();
817
818 updateScrollFromCursor();
819 }
820
821 if( !hasSelection() )
822 {
823 handleMouseUpOverSegment( x, y, mask );
824 }
825
826 handled = TRUE;
827 }
828
829 // Delay cursor flashing
830 mKeystrokeTimer.reset();
831
832 if( gFocusMgr.getMouseCapture() == this )
833 {
834 if (mDragItem)
835 {
836 // mouse down was on an item
837 S32 dx = x - mMouseDownX;
838 S32 dy = y - mMouseDownY;
839 if (-2 < dx && dx < 2 && -2 < dy && dy < 2)
840 {
841 openEmbeddedItem(mDragItem, mDragItemSaved);
842 }
843 }
844 mDragItem = NULL;
845 gFocusMgr.setMouseCapture( NULL, NULL );
846 handled = TRUE;
847 }
848
849 return handled;
850}
851
852
853BOOL LLViewerTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
854{
855 BOOL handled = FALSE;
856
857 // let scrollbar have first dibs
858 handled = LLView::childrenHandleDoubleClick(x, y, mask) != NULL;
859
860 if( !handled && mTakesNonScrollClicks)
861 {
862 if( mAllowEmbeddedItems )
863 {
864 LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y );
865 if( cur_segment && cur_segment->getStyle().getIsEmbeddedItem() )
866 {
867 if( openEmbeddedItemAtPos( cur_segment->getStart() ) )
868 {
869 deselect();
870 setFocus( FALSE );
871 return TRUE;
872 }
873 }
874 }
875
876 if (mTakesFocus)
877 {
878 setFocus( TRUE );
879 }
880
881 setCursorAtLocalPos( x, y, FALSE );
882 deselect();
883
884 const LLWString &text = getWText();
885
886 if( isPartOfWord( text[mCursorPos] ) )
887 {
888 // Select word the cursor is over
889 while ((mCursorPos > 0) && isPartOfWord(text[mCursorPos-1]))
890 {
891 mCursorPos--;
892 }
893 startSelection();
894
895 while ((mCursorPos < (S32)text.length()) && isPartOfWord( text[mCursorPos] ) )
896 {
897 mCursorPos++;
898 }
899
900 mSelectionEnd = mCursorPos;
901 }
902 else if ((mCursorPos < (S32)text.length()) && !iswspace( text[mCursorPos]) )
903 {
904 // Select the character the cursor is over
905 startSelection();
906 mCursorPos++;
907 mSelectionEnd = mCursorPos;
908 }
909
910 // We don't want handleMouseUp() to "finish" the selection (and thereby
911 // set mSelectionEnd to where the mouse is), so we finish the selection here.
912 mIsSelecting = FALSE;
913
914 // delay cursor flashing
915 mKeystrokeTimer.reset();
916
917 handled = TRUE;
918 }
919 return handled;
920}
921
922
923// Allow calling cards to be dropped onto text fields. Append the name and
924// a carriage return.
925// virtual
926BOOL LLViewerTextEditor::handleDragAndDrop(S32 x, S32 y, MASK mask,
927 BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
928 EAcceptance *accept,
929 LLString& tooltip_msg)
930{
931 BOOL handled = FALSE;
932
933 if (mTakesNonScrollClicks)
934 {
935 if (getEnabled() && !mReadOnly)
936 {
937 switch( cargo_type )
938 {
939 case DAD_CALLINGCARD:
940 if(mAcceptCallingCardNames)
941 {
942 if (drop)
943 {
944 LLInventoryItem *item = (LLInventoryItem *)cargo_data;
945 LLString name = item->getName();
946 appendText(name, true, true);
947 }
948 *accept = ACCEPT_YES_COPY_SINGLE;
949 }
950 else
951 {
952 *accept = ACCEPT_NO;
953 }
954 break;
955
956 case DAD_TEXTURE:
957 case DAD_SOUND:
958 case DAD_LANDMARK:
959 case DAD_SCRIPT:
960 case DAD_CLOTHING:
961 case DAD_OBJECT:
962 case DAD_NOTECARD:
963 case DAD_BODYPART:
964 case DAD_ANIMATION:
965 case DAD_GESTURE:
966 {
967 LLInventoryItem *item = (LLInventoryItem *)cargo_data;
968 if( mAllowEmbeddedItems )
969 {
970 U32 mask_next = item->getPermissions().getMaskNextOwner();
971 if((mask_next & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)
972 {
973 if( drop )
974 {
975 deselect();
976 S32 old_cursor = mCursorPos;
977 setCursorAtLocalPos( x, y, TRUE );
978 S32 insert_pos = mCursorPos;
979 setCursorPos(old_cursor);
980 BOOL inserted = insertEmbeddedItem( insert_pos, item );
981 if( inserted && (old_cursor > mCursorPos) )
982 {
983 setCursorPos(mCursorPos + 1);
984 }
985
986 updateLineStartList();
987 }
988 *accept = ACCEPT_YES_COPY_MULTI;
989 }
990 else
991 {
992 *accept = ACCEPT_NO;
993 if (tooltip_msg.empty())
994 {
995 tooltip_msg.assign("Only items with unrestricted\n"
996 "'next owner' permissions \n"
997 "can be attached to notecards.");
998 }
999 }
1000 }
1001 else
1002 {
1003 *accept = ACCEPT_NO;
1004 }
1005 break;
1006 }
1007
1008 default:
1009 *accept = ACCEPT_NO;
1010 break;
1011 }
1012 }
1013 else
1014 {
1015 // Not enabled
1016 *accept = ACCEPT_NO;
1017 }
1018
1019 handled = TRUE;
1020 lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLViewerTextEditor " << getName() << llendl;
1021 }
1022
1023 return handled;
1024}
1025
1026void LLViewerTextEditor::setASCIIEmbeddedText(const LLString& instr)
1027{
1028 LLWString wtext;
1029 const U8* buffer = (U8*)(instr.c_str());
1030 while (*buffer)
1031 {
1032 llwchar wch;
1033 U8 c = *buffer++;
1034 if (c >= 0x80)
1035 {
1036 S32 index = (S32)(c - 0x80);
1037 wch = mEmbeddedItemList->getEmbeddedCharFromIndex(index);
1038 }
1039 else
1040 {
1041 wch = (llwchar)c;
1042 }
1043 wtext.push_back(wch);
1044 }
1045 setWText(wtext);
1046}
1047
1048void LLViewerTextEditor::setEmbeddedText(const LLString& instr)
1049{
1050 LLWString wtext = utf8str_to_wstring(instr);
1051 for (S32 i=0; i<(S32)wtext.size(); i++)
1052 {
1053 llwchar wch = wtext[i];
1054 if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR )
1055 {
1056 S32 index = wch - FIRST_EMBEDDED_CHAR;
1057 wtext[i] = mEmbeddedItemList->getEmbeddedCharFromIndex(index);
1058 }
1059 }
1060 setWText(wtext);
1061}
1062
1063LLString LLViewerTextEditor::getEmbeddedText()
1064{
1065#if 1
1066 // New version (Version 2)
1067 mEmbeddedItemList->copyUsedCharsToIndexed();
1068 LLWString outtextw;
1069 for (S32 i=0; i<(S32)mWText.size(); i++)
1070 {
1071 llwchar wch = mWText[i];
1072 if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR )
1073 {
1074 S32 index = mEmbeddedItemList->getIndexFromEmbeddedChar(wch);
1075 wch = FIRST_EMBEDDED_CHAR + index;
1076 }
1077 outtextw.push_back(wch);
1078 }
1079 LLString outtext = wstring_to_utf8str(outtextw);
1080 return outtext;
1081#else
1082 // Old version (Version 1)
1083 mEmbeddedItemList->copyUsedCharsToIndexed();
1084 LLString outtext;
1085 for (S32 i=0; i<(S32)mWText.size(); i++)
1086 {
1087 llwchar wch = mWText[i];
1088 if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR )
1089 {
1090 S32 index = mEmbeddedItemList->getIndexFromEmbeddedChar(wch);
1091 wch = 0x80 | index % 128;
1092 }
1093 else if (wch >= 0x80)
1094 {
1095 wch = LL_UNKNOWN_CHAR;
1096 }
1097 outtext.push_back((U8)wch);
1098 }
1099 return outtext;
1100#endif
1101}
1102
1103LLString LLViewerTextEditor::appendTime(bool prepend_newline)
1104{
1105 U32 utc_time;
1106 utc_time = time_corrected();
1107
1108 // There's only one internal tm buffer.
1109 struct tm* timep;
1110
1111 // Convert to Pacific, based on server's opinion of whether
1112 // it's daylight savings time there.
1113 timep = utc_to_pacific_time(utc_time, gPacificDaylightTime);
1114
1115 LLString text = llformat("[%d:%02d] ", timep->tm_hour, timep->tm_min);
1116 appendColoredText(text, false, prepend_newline, LLColor4::grey);
1117
1118 return text;
1119}
1120
1121//----------------------------------------------------------------------------
1122//----------------------------------------------------------------------------
1123
1124llwchar LLViewerTextEditor::pasteEmbeddedItem(llwchar ext_char)
1125{
1126 if (mEmbeddedItemList->hasEmbeddedItem(ext_char))
1127 {
1128 return ext_char; // already exists in my list
1129 }
1130 LLInventoryItem* item = LLEmbeddedItems::getEmbeddedItem(ext_char);
1131 if (item)
1132 {
1133 // Add item to my list and return new llwchar associated with it
1134 llwchar new_wc;
1135 if (mEmbeddedItemList->insertEmbeddedItem( item, &new_wc, true ))
1136 {
1137 return new_wc;
1138 }
1139 }
1140 return LL_UNKNOWN_CHAR; // item not found or list full
1141}
1142
1143void LLViewerTextEditor::bindEmbeddedChars(const LLFontGL* font)
1144{
1145 mEmbeddedItemList->bindEmbeddedChars( font );
1146}
1147
1148void LLViewerTextEditor::unbindEmbeddedChars(const LLFontGL* font)
1149{
1150 mEmbeddedItemList->unbindEmbeddedChars( font );
1151}
1152
1153BOOL LLViewerTextEditor::getEmbeddedItemToolTipAtPos(S32 pos, LLWString &msg)
1154{
1155 if (pos < getLength())
1156 {
1157 LLInventoryItem* item = LLEmbeddedItems::getEmbeddedItem(mWText[pos]);
1158 if( item )
1159 {
1160 msg = utf8str_to_wstring(item->getName());
1161 msg += '\n';
1162 msg += utf8str_to_wstring(item->getDescription());
1163 return TRUE;
1164 }
1165 }
1166 return FALSE;
1167}
1168
1169
1170BOOL LLViewerTextEditor::openEmbeddedItemAtPos(S32 pos)
1171{
1172 if( pos < getLength())
1173 {
1174 LLInventoryItem* item = LLEmbeddedItems::getEmbeddedItem( mWText[pos] );
1175 if( item )
1176 {
1177 BOOL saved = LLEmbeddedItems::getEmbeddedItemSaved( mWText[pos] );
1178 return openEmbeddedItem(item, saved);
1179 }
1180 }
1181 return FALSE;
1182}
1183
1184
1185BOOL LLViewerTextEditor::openEmbeddedItem(LLInventoryItem* item, BOOL saved)
1186{
1187 switch( item->getType() )
1188 {
1189 case LLAssetType::AT_TEXTURE:
1190 openEmbeddedTexture( item );
1191 return TRUE;
1192
1193 case LLAssetType::AT_SOUND:
1194 openEmbeddedSound( item );
1195 return TRUE;
1196
1197 case LLAssetType::AT_NOTECARD:
1198 openEmbeddedNotecard( item, saved );
1199 return TRUE;
1200
1201 case LLAssetType::AT_LANDMARK:
1202 showLandmarkDialog( item );
1203 return TRUE;
1204
1205 case LLAssetType::AT_LSL_TEXT:
1206 case LLAssetType::AT_CLOTHING:
1207 case LLAssetType::AT_OBJECT:
1208 case LLAssetType::AT_BODYPART:
1209 case LLAssetType::AT_ANIMATION:
1210 case LLAssetType::AT_GESTURE:
1211 showCopyToInvDialog( item );
1212 return TRUE;
1213 default:
1214 return FALSE;
1215 }
1216}
1217
1218
1219void LLViewerTextEditor::openEmbeddedTexture( LLInventoryItem* item )
1220{
1221 // See if we can bring an existing preview to the front
1222 if( !LLPreview::show( item->getUUID() ) )
1223 {
1224 // There isn't one, so make a new preview
1225 if(item)
1226 {
1227 S32 left, top;
1228 gFloaterView->getNewFloaterPosition(&left, &top);
1229 LLRect rect = gSavedSettings.getRect("PreviewTextureRect");
1230 rect.translate( left - rect.mLeft, top - rect.mTop );
1231
1232 LLPreviewTexture* preview = new LLPreviewTexture("preview texture",
1233 rect,
1234 item->getName(),
1235 item->getAssetUUID(),
1236 TRUE);
1237 preview->setAuxItem( item );
1238 preview->setNotecardInfo(mNotecardInventoryID, mObjectID);
1239 }
1240 }
1241}
1242
1243void LLViewerTextEditor::openEmbeddedSound( LLInventoryItem* item )
1244{
1245 // Play sound locally
1246 LLVector3d lpos_global = gAgent.getPositionGlobal();
1247 const F32 SOUND_GAIN = 1.0f;
1248 if(gAudiop)
1249 {
1250 gAudiop->triggerSound(
1251 item->getAssetUUID(), gAgentID, SOUND_GAIN, lpos_global);
1252 }
1253 showCopyToInvDialog( item );
1254}
1255
1256/*
1257void LLViewerTextEditor::openEmbeddedLandmark( LLInventoryItem* item )
1258{
1259 // See if we can bring an existing preview to the front
1260 if( !LLPreview::show( item->getUUID() ) )
1261 {
1262 // There isn't one, so make a new preview
1263 S32 left, top;
1264 gFloaterView->getNewFloaterPosition(&left, &top);
1265 LLRect rect = gSavedSettings.getRect("PreviewLandmarkRect");
1266 rect.translate( left - rect.mLeft, top - rect.mTop );
1267
1268 LLPreviewLandmark* preview = new LLPreviewLandmark(
1269 "preview landmark",
1270 rect,
1271 item->getName(),
1272 item->getUUID());
1273 preview->setAuxItem( item );
1274 preview->addCopyToInvButton();
1275 preview->open();
1276 }
1277}*/
1278
1279void LLViewerTextEditor::openEmbeddedNotecard( LLInventoryItem* item, BOOL saved )
1280{
1281 if (saved)
1282 {
1283 // Copy to inventory
1284 copyInventory(item);
1285 }
1286 else
1287 {
1288 LLNotecardCopyInfo *info = new LLNotecardCopyInfo(this, item);
1289 gViewerWindow->alertXml("ConfirmNotecardSave",
1290 LLViewerTextEditor::onNotecardDialog, (void*)info);
1291 }
1292}
1293
1294// static
1295void LLViewerTextEditor::onNotecardDialog( S32 option, void* userdata )
1296{
1297 LLNotecardCopyInfo *info = (LLNotecardCopyInfo *)userdata;
1298 if( option == 0 )
1299 {
1300 // itemptr is deleted by LLPreview::save
1301 LLPointer<LLInventoryItem>* itemptr = new LLPointer<LLInventoryItem>(info->mItem);
1302 LLPreview::save( info->mTextEd->mNotecardInventoryID, itemptr);
1303 }
1304}
1305
1306
1307void LLViewerTextEditor::showLandmarkDialog( LLInventoryItem* item )
1308{
1309 LLNotecardCopyInfo *info = new LLNotecardCopyInfo(this, item);
1310 gViewerWindow->alertXml("ConfirmLandmarkCopy",
1311 LLViewerTextEditor::onLandmarkDialog, (void*)info);
1312}
1313
1314// static
1315void LLViewerTextEditor::onLandmarkDialog( S32 option, void* userdata )
1316{
1317 LLNotecardCopyInfo *info = (LLNotecardCopyInfo *)userdata;
1318 if( option == 0 )
1319 {
1320 // Copy to inventory
1321 info->mTextEd->copyInventory(info->mItem);
1322 /*
1323 * XXXPAM
1324 *
1325 * Yes, this is broken. We don't show the map yet.
1326 *
1327 LLInventoryItem* orig_item = (LLInventoryItem*)userdata;
1328
1329 // Copy to inventory
1330 LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem;
1331 cloneInventoryItemToViewer(orig_item, new_item);
1332 U32 flags = new_item->getFlags();
1333 flags &= ~LLInventoryItem::II_FLAGS_LANDMARK_VISITED;
1334 new_item->setFlags(flags);
1335 new_item->updateServer(TRUE);
1336 gInventory.updateItem(new_item);
1337 gInventory.notifyObservers();
1338
1339 LLInventoryView* view = LLInventoryView::getActiveInventory();
1340 if(view)
1341 {
1342 view->getPanel()->setSelection(new_item->getUUID(), TAKE_FOCUS_NO);
1343 }
1344
1345 if( (0 == option) && gFloaterWorldMap )
1346 {
1347 // Note: there's a minor race condition here.
1348 // If the user immediately tries to teleport to the landmark, the dataserver may
1349 // not yet know that the user has the landmark in his inventory and so may
1350 // disallow the teleport. However, the user will need to be pretty fast to make
1351 // this happen, and, if it does, they haven't lost anything. Once the dataserver
1352 // knows about the new item, the user will be able to teleport to it successfully.
1353 gFloaterWorldMap->trackLandmark(new_item->getUUID());
1354 LLFloaterWorldMap::show(NULL, TRUE);
1355 }*/
1356 }
1357 delete info;
1358}
1359
1360
1361void LLViewerTextEditor::showCopyToInvDialog( LLInventoryItem* item )
1362{
1363 LLNotecardCopyInfo *info = new LLNotecardCopyInfo(this, item);
1364 gViewerWindow->alertXml( "ConfirmItemCopy",
1365 LLViewerTextEditor::onCopyToInvDialog, (void*)info);
1366}
1367
1368// static
1369void LLViewerTextEditor::onCopyToInvDialog( S32 option, void* userdata )
1370{
1371 LLNotecardCopyInfo *info = (LLNotecardCopyInfo *)userdata;
1372 if( 0 == option )
1373 {
1374 info->mTextEd->copyInventory(info->mItem);
1375 }
1376 delete info;
1377}
1378
1379
1380
1381// Returns change in number of characters in mWText
1382S32 LLViewerTextEditor::insertEmbeddedItem( S32 pos, LLInventoryItem* item )
1383{
1384 return execute( new LLTextCmdInsertEmbeddedItem( pos, item ) );
1385}
1386
1387bool LLViewerTextEditor::importStream(std::istream& str)
1388{
1389 LLNotecard nc(MAX_NOTECARD_SIZE);
1390 bool success = nc.importStream(str);
1391 if (success)
1392 {
1393 const std::vector<LLPointer<LLInventoryItem> >& items = nc.getItems();
1394 mEmbeddedItemList->addItems(items);
1395 // Actually set the text
1396 if (mAllowEmbeddedItems)
1397 {
1398 if (nc.getVersion() == 1)
1399 setASCIIEmbeddedText( nc.getText() );
1400 else
1401 setEmbeddedText( nc.getText() );
1402 }
1403 else
1404 {
1405 setText( nc.getText() );
1406 }
1407 }
1408 return success;
1409}
1410
1411void LLViewerTextEditor::copyInventory(LLInventoryItem* item)
1412{
1413 copy_inventory_from_notecard(mObjectID,
1414 mNotecardInventoryID,
1415 item);
1416}
1417
1418////////////////////////////////////////////////////////////////////////////
1419
1420BOOL LLViewerTextEditor::importBuffer( const LLString& buffer )
1421{
1422 LLMemoryStream str((U8*)buffer.c_str(), buffer.length());
1423 return importStream(str);
1424}
1425
1426BOOL LLViewerTextEditor::exportBuffer( LLString& buffer )
1427{
1428 LLNotecard nc(MAX_NOTECARD_SIZE);
1429
1430 std::vector<LLPointer<LLInventoryItem> > embedded_items;
1431 mEmbeddedItemList->getEmbeddedItemList(embedded_items);
1432
1433 nc.setItems(embedded_items);
1434 nc.setText(getEmbeddedText());
1435
1436 std::stringstream out_stream;
1437 nc.exportStream(out_stream);
1438
1439 buffer = out_stream.str();
1440
1441 return TRUE;
1442}
1443
1444LLView* LLViewerTextEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
1445{
1446 LLString name("text_editor");
1447 node->getAttributeString("name", name);
1448
1449 LLRect rect;
1450 createRect(node, rect, parent, LLRect());
1451
1452 U32 max_text_length = 255;
1453 node->getAttributeU32("max_length", max_text_length);
1454
1455 BOOL allow_embedded_items = FALSE;
1456 node->getAttributeBOOL("embedded_items", allow_embedded_items);
1457
1458 LLFontGL* font = LLView::selectFont(node);
1459
1460 LLString text = node->getValue();
1461
1462 if (text.size() > max_text_length)
1463 {
1464 // Erase everything from max_text_length on.
1465 text.erase(max_text_length);
1466 }
1467
1468 LLViewerTextEditor* text_editor = new LLViewerTextEditor(name,
1469 rect,
1470 max_text_length,
1471 text,
1472 font,
1473 allow_embedded_items);
1474
1475 BOOL ignore_tabs = text_editor->mTabToNextField;
1476 node->getAttributeBOOL("ignore_tab", ignore_tabs);
1477
1478 text_editor->setTabToNextField(ignore_tabs);
1479
1480
1481 text_editor->setTextEditorParameters(node);
1482
1483 BOOL hide_scrollbar = FALSE;
1484 node->getAttributeBOOL("hide_scrollbar",hide_scrollbar);
1485 text_editor->setHideScrollbarForShortDocs(hide_scrollbar);
1486
1487 text_editor->initFromXML(node, parent);
1488
1489 return text_editor;
1490}