aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llui/llview.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/llui/llview.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/llui/llview.cpp')
-rw-r--r--linden/indra/llui/llview.cpp2987
1 files changed, 2987 insertions, 0 deletions
diff --git a/linden/indra/llui/llview.cpp b/linden/indra/llui/llview.cpp
new file mode 100644
index 0000000..ab06c0f
--- /dev/null
+++ b/linden/indra/llui/llview.cpp
@@ -0,0 +1,2987 @@
1/**
2 * @file llview.cpp
3 * @author James Cook
4 * @brief Container for other views, anything that draws.
5 *
6 * Copyright (c) 2001-2007, Linden Research, Inc.
7 *
8 * The source code in this file ("Source Code") is provided by Linden Lab
9 * to you under the terms of the GNU General Public License, version 2.0
10 * ("GPL"), unless you have obtained a separate licensing agreement
11 * ("Other License"), formally executed by you and Linden Lab. Terms of
12 * the GPL can be found in doc/GPL-license.txt in this distribution, or
13 * online at http://secondlife.com/developers/opensource/gplv2
14 *
15 * There are special exceptions to the terms and conditions of the GPL as
16 * it is applied to this Source Code. View the full text of the exception
17 * in the file doc/FLOSS-exception.txt in this software distribution, or
18 * online at http://secondlife.com/developers/opensource/flossexception
19 *
20 * By copying, modifying or distributing this software, you acknowledge
21 * that you have read and understood your obligations described above,
22 * and agree to abide by those obligations.
23 *
24 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
25 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
26 * COMPLETENESS OR PERFORMANCE.
27 */
28
29#include "linden_common.h"
30
31#include "llview.h"
32
33#include "llstring.h"
34#include "llrect.h"
35#include "llgl.h"
36#include "llevent.h"
37#include "llfontgl.h"
38#include "llfocusmgr.h"
39#include "llglheaders.h"
40#include "llwindow.h"
41#include "llstl.h"
42#include "lluictrl.h"
43#include "llui.h" // colors saved settings
44#include "v3color.h"
45#include "llstl.h"
46
47#include <boost/tokenizer.hpp>
48
49#include <assert.h>
50
51BOOL LLView::sDebugRects = FALSE;
52BOOL LLView::sDebugKeys = FALSE;
53S32 LLView::sDepth = 0;
54LLView* LLView::sFastFrameView = NULL;
55BOOL LLView::sDebugMouseHandling = FALSE;
56LLString LLView::sMouseHandlerMessage;
57S32 LLView::sSelectID = GL_NAME_UI_RESERVED;
58BOOL LLView::sEditingUI = FALSE;
59BOOL LLView::sForceReshape = FALSE;
60LLView* LLView::sEditingUIView = NULL;
61S32 LLView::sLastLeftXML = S32_MIN;
62S32 LLView::sLastBottomXML = S32_MIN;
63std::map<LLViewHandle,LLView*> LLView::sViewHandleMap;
64
65S32 LLViewHandle::sNextID = 0;
66LLViewHandle LLViewHandle::sDeadHandle;
67
68#if LL_DEBUG
69BOOL LLView::sIsDrawing = FALSE;
70#endif
71
72//static
73LLView* LLView::getViewByHandle(LLViewHandle handle)
74{
75 if (handle == LLViewHandle::sDeadHandle)
76 {
77 return NULL;
78 }
79 std::map<LLViewHandle,LLView*>::iterator iter = sViewHandleMap.find(handle);
80 if (iter != sViewHandleMap.end())
81 {
82 return iter->second;
83 }
84 else
85 {
86 return NULL;
87 }
88}
89
90//static
91BOOL LLView::deleteViewByHandle(LLViewHandle handle)
92{
93 std::map<LLViewHandle,LLView*>::iterator iter = sViewHandleMap.find(handle);
94 if (iter != sViewHandleMap.end())
95 {
96 delete iter->second; // will remove from map
97 return TRUE;
98 }
99 else
100 {
101 return FALSE;
102 }
103}
104
105LLView::LLView() :
106 mParentView(NULL),
107 mReshapeFlags(FOLLOWS_NONE),
108 mDefaultTabGroup(0),
109 mEnabled(TRUE),
110 mMouseOpaque(TRUE),
111 mSoundFlags(MOUSE_UP), // default to only make sound on mouse up
112 mSaveToXML(TRUE),
113 mIsFocusRoot(FALSE),
114 mLastVisible(TRUE),
115 mRenderInFastFrame(TRUE),
116 mSpanChildren(FALSE),
117 mVisible(TRUE),
118 mHidden(FALSE),
119 mNextInsertionOrdinal(0)
120{
121 mViewHandle.init();
122 sViewHandleMap[mViewHandle] = this;
123}
124
125LLView::LLView(const LLString& name, BOOL mouse_opaque) :
126 mParentView(NULL),
127 mName(name),
128 mReshapeFlags(FOLLOWS_NONE),
129 mDefaultTabGroup(0),
130 mEnabled(TRUE),
131 mMouseOpaque(mouse_opaque),
132 mSoundFlags(MOUSE_UP), // default to only make sound on mouse up
133 mSaveToXML(TRUE),
134 mIsFocusRoot(FALSE),
135 mLastVisible(TRUE),
136 mRenderInFastFrame(TRUE),
137 mSpanChildren(FALSE),
138 mVisible(TRUE),
139 mHidden(FALSE),
140 mNextInsertionOrdinal(0)
141{
142 mViewHandle.init();
143 sViewHandleMap[mViewHandle] = this;
144}
145
146
147LLView::LLView(
148 const LLString& name, const LLRect& rect, BOOL mouse_opaque, U32 reshape) :
149 mParentView(NULL),
150 mName(name),
151 mRect(rect),
152 mReshapeFlags(reshape),
153 mDefaultTabGroup(0),
154 mEnabled(TRUE),
155 mMouseOpaque(mouse_opaque),
156 mSoundFlags(MOUSE_UP), // default to only make sound on mouse up
157 mSaveToXML(TRUE),
158 mIsFocusRoot(FALSE),
159 mLastVisible(TRUE),
160 mRenderInFastFrame(TRUE),
161 mSpanChildren(FALSE),
162 mVisible(TRUE),
163 mHidden(FALSE),
164 mNextInsertionOrdinal(0)
165{
166 mViewHandle.init();
167 sViewHandleMap[mViewHandle] = this;
168}
169
170
171LLView::~LLView()
172{
173 //llinfos << "Deleting view " << mName << ":" << (void*) this << llendl;
174// llassert(LLView::sIsDrawing == FALSE);
175 if( gFocusMgr.getKeyboardFocus() == this )
176 {
177 llwarns << "View holding keyboard focus deleted: " << getName() << ". Keyboard focus removed." << llendl;
178 gFocusMgr.removeKeyboardFocusWithoutCallback( this );
179 }
180
181 if( gFocusMgr.getMouseCapture() == this )
182 {
183 llwarns << "View holding mouse capture deleted: " << getName() << ". Mouse capture removed." << llendl;
184 gFocusMgr.removeMouseCaptureWithoutCallback( this );
185 }
186
187 if( gFocusMgr.getTopView() == this )
188 {
189 llwarns << "View holding top view deleted: " << getName() << ". Top view removed." << llendl;
190 gFocusMgr.removeTopViewWithoutCallback( this );
191 }
192
193 sViewHandleMap.erase(mViewHandle);
194
195 deleteAllChildren();
196
197 if (mParentView != NULL)
198 {
199 mParentView->removeChild(this);
200 }
201
202 if(LLView::sFastFrameView == this)
203 {
204 LLView::sFastFrameView = NULL;
205 }
206
207 dispatch_list_t::iterator itor;
208 for (itor = mDispatchList.begin(); itor != mDispatchList.end(); ++itor)
209 {
210 (*itor).second->clearDispatchers();
211 delete (*itor).second;
212 }
213}
214
215// virtual
216BOOL LLView::isView()
217{
218 return TRUE;
219}
220
221// virtual
222BOOL LLView::isCtrl() const
223{
224 return FALSE;
225}
226
227// virtual
228BOOL LLView::isPanel()
229{
230 return FALSE;
231}
232
233void LLView::setMouseOpaque(BOOL b)
234{
235 mMouseOpaque = b;
236}
237
238void LLView::setToolTip(const LLString& msg)
239{
240 mToolTipMsg = msg;
241}
242
243// virtual
244void LLView::setRect(const LLRect& rect)
245{
246 mRect = rect;
247}
248
249
250void LLView::setFollows(U32 flags)
251{
252 mReshapeFlags = flags;
253}
254
255void LLView::setFollowsNone()
256{
257 mReshapeFlags = FOLLOWS_NONE;
258}
259
260void LLView::setFollowsLeft()
261{
262 mReshapeFlags |= FOLLOWS_LEFT;
263}
264
265void LLView::setFollowsTop()
266{
267 mReshapeFlags |= FOLLOWS_TOP;
268}
269
270void LLView::setFollowsRight()
271{
272 mReshapeFlags |= FOLLOWS_RIGHT;
273}
274
275void LLView::setFollowsBottom()
276{
277 mReshapeFlags |= FOLLOWS_BOTTOM;
278}
279
280void LLView::setFollowsAll()
281{
282 mReshapeFlags |= FOLLOWS_ALL;
283}
284
285void LLView::setSoundFlags(U8 flags)
286{
287 mSoundFlags = flags;
288}
289
290void LLView::setName(LLString name)
291{
292 mName = name;
293}
294
295void LLView::setSpanChildren( BOOL span_children )
296{
297 mSpanChildren = span_children; updateRect();
298}
299
300const LLString& LLView::getToolTip()
301{
302 return mToolTipMsg;
303}
304
305// virtual
306const LLString& LLView::getName() const
307{
308 static const LLString unnamed("(no name)");
309 return mName.empty() ? unnamed : mName;
310}
311
312void LLView::sendChildToFront(LLView* child)
313{
314 if (child->mParentView == this)
315 {
316 mChildList.remove( child );
317 mChildList.push_front(child);
318 }
319}
320
321void LLView::sendChildToBack(LLView* child)
322{
323 if (child->mParentView == this)
324 {
325 mChildList.remove( child );
326 mChildList.push_back(child);
327 }
328}
329
330void LLView::moveChildToFrontOfTabGroup(LLUICtrl* child)
331{
332 if(mCtrlOrder.find(child) != mCtrlOrder.end())
333 {
334 mCtrlOrder[child].second = -1 * mNextInsertionOrdinal++;
335 }
336}
337
338void LLView::addChild(LLView* child, S32 tab_group)
339{
340 // remove from current parent
341 if (child->mParentView)
342 {
343 child->mParentView->removeChild(child);
344 }
345
346 // add to front of child list, as normal
347 mChildList.push_front(child);
348
349 // add to ctrl list if is LLUICtrl
350 if (child->isCtrl())
351 {
352 // controls are stored in reverse order from render order
353 addCtrlAtEnd((LLUICtrl*) child, tab_group);
354 }
355
356 child->mParentView = this;
357 updateRect();
358}
359
360
361void LLView::addChildAtEnd(LLView* child, S32 tab_group)
362{
363 // remove from current parent
364 if (child->mParentView)
365 {
366 child->mParentView->removeChild(child);
367 }
368
369 // add to back of child list
370 mChildList.push_back(child);
371
372 // add to ctrl list if is LLUICtrl
373 if (child->isCtrl())
374 {
375 // controls are stored in reverse order from render order
376 addCtrl((LLUICtrl*) child, tab_group);
377 }
378
379 child->mParentView = this;
380 updateRect();
381}
382
383// remove the specified child from the view, and set it's parent to NULL.
384void LLView::removeChild( LLView* child )
385{
386 if (child->mParentView == this)
387 {
388 mChildList.remove( child );
389 child->mParentView = NULL;
390 }
391 else
392 {
393 llerrs << "LLView::removeChild called with non-child" << llendl;
394 }
395
396 if (child->isCtrl())
397 {
398 removeCtrl((LLUICtrl*)child);
399 }
400}
401
402void LLView::addCtrlAtEnd(LLUICtrl* ctrl, S32 tab_group)
403{
404 mCtrlOrder.insert(tab_order_pair_t(ctrl,
405 tab_order_t(tab_group, mNextInsertionOrdinal++)));
406}
407
408void LLView::addCtrl( LLUICtrl* ctrl, S32 tab_group)
409{
410 // add to front of list by using negative ordinal, which monotonically increases
411 mCtrlOrder.insert(tab_order_pair_t(ctrl,
412 tab_order_t(tab_group, -1 * mNextInsertionOrdinal++)));
413}
414
415void LLView::removeCtrl(LLUICtrl* ctrl)
416{
417 child_tab_order_t::iterator found = mCtrlOrder.find(ctrl);
418 if(found != mCtrlOrder.end())
419 {
420 mCtrlOrder.erase(found);
421 }
422}
423
424S32 LLView::getDefaultTabGroup() const { return mDefaultTabGroup; }
425
426LLView::ctrl_list_t LLView::getCtrlList() const
427{
428 ctrl_list_t controls;
429 for(child_list_const_iter_t iter = mChildList.begin();
430 iter != mChildList.end();
431 iter++)
432 {
433 if((*iter)->isCtrl())
434 {
435 controls.push_back(static_cast<LLUICtrl*>(*iter));
436 }
437 }
438 return controls;
439}
440
441LLView::ctrl_list_t LLView::getCtrlListSorted() const
442{
443 ctrl_list_t controls = getCtrlList();
444 std::sort(controls.begin(), controls.end(), LLCompareByTabOrder(mCtrlOrder));
445 return controls;
446}
447
448LLCompareByTabOrder::LLCompareByTabOrder(LLView::child_tab_order_t order): mTabOrder(order) {}
449
450bool LLCompareByTabOrder::compareTabOrders(const LLView::tab_order_t & a, const LLView::tab_order_t & b) const
451{
452 return a < b;
453}
454
455// This method compares two LLViews by the tab order specified in the comparator object. The
456// code for this is a little convoluted because each argument can have four states:
457// 1) not a control, 2) a control but not in the tab order, 3) a control in the tab order, 4) null
458bool LLCompareByTabOrder::operator() (const LLView* const a, const LLView* const b) const
459{
460 S32 a_score = 0, b_score = 0;
461 if(a) a_score--;
462 if(b) b_score--;
463 if(a && a->isCtrl()) a_score--;
464 if(b && b->isCtrl()) b_score--;
465 if(a_score == -2 && b_score == -2)
466 {
467 const LLUICtrl * const a_ctrl = static_cast<const LLUICtrl* const>(a);
468 const LLUICtrl * const b_ctrl = static_cast<const LLUICtrl* const>(b);
469 LLView::child_tab_order_const_iter_t a_found = mTabOrder.find(a_ctrl), b_found = mTabOrder.find(b_ctrl);
470 if(a_found != mTabOrder.end()) a_score--;
471 if(b_found != mTabOrder.end()) b_score--;
472 if(a_score == -3 && b_score == -3)
473 {
474 // whew! Once we're in here, they're both in the tab order, and we can compare based on that
475 return compareTabOrders(a_found->second, b_found->second);
476 }
477 }
478 return (a_score == b_score) ? a < b : a_score < b_score;
479}
480
481BOOL LLView::isInVisibleChain() const
482{
483 const LLView* cur_view = this;
484 while(cur_view)
485 {
486 if (!cur_view->getVisible())
487 {
488 return FALSE;
489 }
490 cur_view = cur_view->getParent();
491 }
492 return TRUE;
493}
494
495BOOL LLView::isInEnabledChain() const
496{
497 const LLView* cur_view = this;
498 while(cur_view)
499 {
500 if (!cur_view->getEnabled())
501 {
502 return FALSE;
503 }
504 cur_view = cur_view->getParent();
505 }
506 return TRUE;
507}
508
509BOOL LLView::isFocusRoot() const
510{
511 return mIsFocusRoot;
512}
513
514LLView* LLView::findRootMostFocusRoot()
515{
516 LLView* focus_root = NULL;
517 LLView* next_view = this;
518 while(next_view)
519 {
520 if (next_view->isFocusRoot())
521 {
522 focus_root = next_view;
523 }
524 next_view = next_view->getParent();
525 }
526 return focus_root;
527}
528
529BOOL LLView::canFocusChildren() const
530{
531 return TRUE;
532}
533
534BOOL LLView::focusNextRoot()
535{
536 LLView::child_list_t result = LLView::getFocusRootsQuery().run(this);
537 return LLView::focusNext(result);
538}
539
540BOOL LLView::focusPrevRoot()
541{
542 LLView::child_list_t result = LLView::getFocusRootsQuery().run(this);
543 return LLView::focusPrev(result);
544}
545
546BOOL LLView::focusNextItem(BOOL text_fields_only)
547{
548 // this assumes that this method is called on the focus root.
549 LLCtrlQuery query = LLView::getTabOrderQuery();
550 if(text_fields_only || LLUI::sConfigGroup->getBOOL("TabToTextFieldsOnly"))
551 {
552 query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance());
553 }
554 LLView::child_list_t result = query(this);
555 return LLView::focusNext(result);
556}
557
558BOOL LLView::focusPrevItem(BOOL text_fields_only)
559{
560 // this assumes that this method is called on the focus root.
561 LLCtrlQuery query = LLView::getTabOrderQuery();
562 if(text_fields_only || LLUI::sConfigGroup->getBOOL("TabToTextFieldsOnly"))
563 {
564 query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance());
565 }
566 LLView::child_list_t result = query(this);
567 return LLView::focusPrev(result);
568}
569
570
571// static
572BOOL LLView::focusNext(LLView::child_list_t & result)
573{
574 LLView::child_list_iter_t focused = result.end();
575 for(LLView::child_list_iter_t iter = result.begin();
576 iter != result.end();
577 ++iter)
578 {
579 if(gFocusMgr.childHasKeyboardFocus(*iter))
580 {
581 focused = iter;
582 break;
583 }
584 }
585 LLView::child_list_iter_t next = focused;
586 next = (next == result.end()) ? result.begin() : ++next;
587 while(next != focused)
588 {
589 // wrap around to beginning if necessary
590 if(next == result.end())
591 {
592 next = result.begin();
593 }
594 if((*next)->isCtrl())
595 {
596 LLUICtrl * ctrl = static_cast<LLUICtrl*>(*next);
597 ctrl->setFocus(TRUE);
598 ctrl->onTabInto();
599 gFocusMgr.triggerFocusFlash();
600 return TRUE;
601 }
602 ++next;
603 }
604 return FALSE;
605}
606
607// static
608BOOL LLView::focusPrev(LLView::child_list_t & result)
609{
610 LLView::child_list_reverse_iter_t focused = result.rend();
611 for(LLView::child_list_reverse_iter_t iter = result.rbegin();
612 iter != result.rend();
613 ++iter)
614 {
615 if(gFocusMgr.childHasKeyboardFocus(*iter))
616 {
617 focused = iter;
618 break;
619 }
620 }
621 LLView::child_list_reverse_iter_t next = focused;
622 next = (next == result.rend()) ? result.rbegin() : ++next;
623 while(next != focused)
624 {
625 // wrap around to beginning if necessary
626 if(next == result.rend())
627 {
628 next = result.rbegin();
629 }
630 if((*next)->isCtrl())
631 {
632 LLUICtrl * ctrl = static_cast<LLUICtrl*>(*next);
633 if (!ctrl->hasFocus())
634 {
635 ctrl->setFocus(TRUE);
636 ctrl->onTabInto();
637 gFocusMgr.triggerFocusFlash();
638 }
639 return TRUE;
640 }
641 ++next;
642 }
643 return FALSE;
644}
645
646BOOL LLView::focusFirstItem(BOOL prefer_text_fields)
647{
648 // search for text field first
649 if(prefer_text_fields)
650 {
651 LLCtrlQuery query = LLView::getTabOrderQuery();
652 query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance());
653 LLView::child_list_t result = query(this);
654 if(result.size() > 0)
655 {
656 LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.front());
657 if(!ctrl->hasFocus())
658 {
659 ctrl->setFocus(TRUE);
660 ctrl->onTabInto();
661 gFocusMgr.triggerFocusFlash();
662 }
663 return TRUE;
664 }
665 }
666 // no text field found, or we don't care about text fields
667 LLView::child_list_t result = LLView::getTabOrderQuery().run(this);
668 if(result.size() > 0)
669 {
670 LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.front());
671 if(!ctrl->hasFocus())
672 {
673 ctrl->setFocus(TRUE);
674 ctrl->onTabInto();
675 gFocusMgr.triggerFocusFlash();
676 }
677 return TRUE;
678 }
679 return FALSE;
680}
681
682BOOL LLView::focusLastItem(BOOL prefer_text_fields)
683{
684 // search for text field first
685 if(prefer_text_fields)
686 {
687 LLCtrlQuery query = LLView::getTabOrderQuery();
688 query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance());
689 LLView::child_list_t result = query(this);
690 if(result.size() > 0)
691 {
692 LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.back());
693 if(!ctrl->hasFocus())
694 {
695 ctrl->setFocus(TRUE);
696 ctrl->onTabInto();
697 gFocusMgr.triggerFocusFlash();
698 }
699 return TRUE;
700 }
701 }
702 // no text field found, or we don't care about text fields
703 LLView::child_list_t result = LLView::getTabOrderQuery().run(this);
704 if(result.size() > 0)
705 {
706 LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.back());
707 if(!ctrl->hasFocus())
708 {
709 ctrl->setFocus(TRUE);
710 ctrl->onTabInto();
711 gFocusMgr.triggerFocusFlash();
712 }
713 return TRUE;
714 }
715 return FALSE;
716}
717
718
719
720// delete all children. Override this function if you need to
721// perform any extra clean up such as cached pointers to selected
722// children, etc.
723void LLView::deleteAllChildren()
724{
725 // clear out the control ordering
726 mCtrlOrder.clear();
727
728 while (!mChildList.empty())
729 {
730 LLView* viewp = mChildList.front();
731 delete viewp; // will remove the child from mChildList
732 }
733}
734
735void LLView::setAllChildrenEnabled(BOOL b)
736{
737 for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
738 {
739 LLView* viewp = *child_it;
740 viewp->setEnabled(b);
741 }
742}
743
744// virtual
745void LLView::setTentative(BOOL b)
746{
747}
748
749// virtual
750BOOL LLView::getTentative() const
751{
752 return FALSE;
753}
754
755// virtual
756void LLView::setEnabled(BOOL enabled)
757{
758 mEnabled = enabled;
759}
760
761// virtual
762void LLView::setVisible(BOOL visible)
763{
764 if( !visible && (gFocusMgr.getTopView() == this) )
765 {
766 gFocusMgr.setTopView( NULL, NULL );
767 }
768
769 if ( mVisible != visible )
770 {
771 // tell all children of this view that the visibility may have changed
772 onVisibilityChange ( visible );
773 }
774
775 mVisible = visible;
776}
777
778// virtual
779void LLView::setHidden(BOOL hidden)
780{
781 mHidden = hidden;
782}
783
784// virtual
785BOOL LLView::setLabelArg(const LLString& key, const LLString& text)
786{
787 return FALSE;
788}
789
790void LLView::onVisibilityChange ( BOOL curVisibilityIn )
791{
792 for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
793 {
794 LLView* viewp = *child_it;
795 // only views that are themselves visible will have their overall visibility affected by their ancestors
796 if (viewp->getVisible())
797 {
798 viewp->onVisibilityChange ( curVisibilityIn );
799 }
800 }
801}
802
803// virtual
804void LLView::translate(S32 x, S32 y)
805{
806 mRect.translate(x, y);
807}
808
809// virtual
810BOOL LLView::canSnapTo(LLView* other_view)
811{
812 return other_view->getVisible();
813}
814
815// virtual
816void LLView::snappedTo(LLView* snap_view)
817{
818}
819
820BOOL LLView::handleHover(S32 x, S32 y, MASK mask)
821{
822 BOOL handled = childrenHandleHover( x, y, mask ) != NULL;
823 if( !handled && mMouseOpaque && pointInView( x, y ) )
824 {
825 LLUI::sWindow->setCursor(UI_CURSOR_ARROW);
826 lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl;
827 handled = TRUE;
828 }
829
830 return handled;
831}
832
833LLString LLView::getShowNamesToolTip()
834{
835 LLView* view = getParent();
836 LLString name;
837 LLString tool_tip = mName;
838
839 while (view)
840 {
841 name = view->getName();
842
843 if (name == "root") break;
844
845 if (view->getToolTip().find(".xml") != LLString::npos)
846 {
847 tool_tip = view->getToolTip() + "/" + tool_tip;
848 break;
849 }
850 else
851 {
852 tool_tip = view->getName() + "/" + tool_tip;
853 }
854
855 view = view->getParent();
856 }
857
858 return "/" + tool_tip;
859}
860
861
862BOOL LLView::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen)
863{
864 BOOL handled = FALSE;
865
866 LLString tool_tip;
867
868 if ( getVisible() && getEnabled())
869 {
870 for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
871 {
872 LLView* viewp = *child_it;
873 S32 local_x = x - viewp->mRect.mLeft;
874 S32 local_y = y - viewp->mRect.mBottom;
875 if( viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen ) )
876 {
877 handled = TRUE;
878 break;
879 }
880 }
881
882 if (LLUI::sShowXUINames && (mToolTipMsg.find(".xml", 0) == LLString::npos) &&
883 (mName.find("Drag", 0) == LLString::npos))
884 {
885 tool_tip = getShowNamesToolTip();
886 }
887 else
888 {
889 tool_tip = mToolTipMsg;
890 }
891
892
893
894 BOOL showNamesTextBox = LLUI::sShowXUINames && (getWidgetType() == WIDGET_TYPE_TEXT_BOX);
895
896 if( !handled && (mMouseOpaque || showNamesTextBox) && pointInView( x, y ) && !tool_tip.empty())
897 {
898
899 msg = tool_tip;
900
901 // Convert rect local to screen coordinates
902 localPointToScreen(
903 0, 0,
904 &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
905 localPointToScreen(
906 mRect.getWidth(), mRect.getHeight(),
907 &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) );
908
909 handled = TRUE;
910 }
911 }
912
913 return handled;
914}
915
916BOOL LLView::handleKey(KEY key, MASK mask, BOOL called_from_parent)
917{
918 BOOL handled = FALSE;
919
920 if( called_from_parent )
921 {
922 // Downward traversal
923 if (getVisible() && mEnabled)
924 {
925 handled = childrenHandleKey( key, mask ) != NULL;
926 }
927 }
928
929 // JC: Must pass to disabled views, since they could have
930 // keyboard focus, which requires the escape key to exit.
931 if (!handled && getVisible())
932 {
933 handled = handleKeyHere( key, mask, called_from_parent );
934 if (handled && LLView::sDebugKeys)
935 {
936 llinfos << "Key handled by " << getName() << llendl;
937 }
938 }
939
940 if( !handled && !called_from_parent)
941 {
942 if (mIsFocusRoot)
943 {
944 // stop processing at focus root
945 handled = FALSE;
946 }
947 else if (mParentView)
948 {
949 // Upward traversal
950 handled = mParentView->handleKey( key, mask, FALSE );
951 }
952 }
953 return handled;
954}
955
956// Called from handleKey()
957// Handles key in this object. Checking parents and children happens in handleKey()
958BOOL LLView::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
959{
960 return FALSE;
961}
962
963BOOL LLView::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent)
964{
965 BOOL handled = FALSE;
966
967 if( called_from_parent )
968 {
969 // Downward traversal
970 if (getVisible() && mEnabled)
971 {
972 handled = childrenHandleUnicodeChar( uni_char ) != NULL;
973 }
974 }
975
976 if (!handled && getVisible())
977 {
978 handled = handleUnicodeCharHere(uni_char, called_from_parent);
979 if (handled && LLView::sDebugKeys)
980 {
981 llinfos << "Unicode key handled by " << getName() << llendl;
982 }
983 }
984
985
986 if (!handled && !called_from_parent)
987 {
988 if (mIsFocusRoot)
989 {
990 // stop processing at focus root
991 handled = FALSE;
992 }
993 else if(mParentView)
994 {
995 // Upward traversal
996 handled = mParentView->handleUnicodeChar(uni_char, FALSE);
997 }
998 }
999
1000 return handled;
1001}
1002
1003
1004BOOL LLView::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent )
1005{
1006 return FALSE;
1007}
1008
1009
1010BOOL LLView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
1011 EDragAndDropType cargo_type, void* cargo_data,
1012 EAcceptance* accept,
1013 LLString& tooltip_msg)
1014{
1015 // CRO this is an experiment to allow drag and drop into object inventory based on the DragAndDrop tool's permissions rather than the parent
1016 BOOL handled = childrenHandleDragAndDrop( x, y, mask, drop,
1017 cargo_type,
1018 cargo_data,
1019 accept,
1020 tooltip_msg) != NULL;
1021 if( !handled && mMouseOpaque )
1022 {
1023 *accept = ACCEPT_NO;
1024 handled = TRUE;
1025 lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLView " << getName() << llendl;
1026 }
1027
1028 return handled;
1029}
1030
1031LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask,
1032 BOOL drop,
1033 EDragAndDropType cargo_type,
1034 void* cargo_data,
1035 EAcceptance* accept,
1036 LLString& tooltip_msg)
1037{
1038 LLView* handled_view = FALSE;
1039 // CRO this is an experiment to allow drag and drop into object inventory based on the DragAndDrop tool's permissions rather than the parent
1040 if( getVisible() )
1041// if( getVisible() && mEnabled )
1042 {
1043 for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
1044 {
1045 LLView* viewp = *child_it;
1046 S32 local_x = x - viewp->mRect.mLeft;
1047 S32 local_y = y - viewp->mRect.mBottom;
1048 if( viewp->pointInView(local_x, local_y) &&
1049 viewp->getVisible() &&
1050 viewp->mEnabled &&
1051 viewp->handleDragAndDrop(local_x, local_y, mask, drop,
1052 cargo_type,
1053 cargo_data,
1054 accept,
1055 tooltip_msg))
1056 {
1057 handled_view = viewp;
1058 break;
1059 }
1060 }
1061 }
1062 return handled_view;
1063}
1064
1065
1066
1067BOOL LLView::handleMouseUp(S32 x, S32 y, MASK mask)
1068{
1069 BOOL handled = childrenHandleMouseUp( x, y, mask ) != NULL;
1070 if( !handled && mMouseOpaque )
1071 {
1072 handled = TRUE;
1073 }
1074 return handled;
1075}
1076
1077BOOL LLView::handleMouseDown(S32 x, S32 y, MASK mask)
1078{
1079 LLView* handled_view = childrenHandleMouseDown( x, y, mask );
1080 BOOL handled = (handled_view != NULL);
1081 if( !handled && mMouseOpaque )
1082 {
1083 handled = TRUE;
1084 handled_view = this;
1085 }
1086
1087 // HACK If we're editing UI, select the leaf view that ate the click.
1088 if (sEditingUI && handled_view)
1089 {
1090 // need to find leaf views, big hack
1091 EWidgetType type = handled_view->getWidgetType();
1092 if (type == WIDGET_TYPE_BUTTON
1093 || type == WIDGET_TYPE_LINE_EDITOR
1094 || type == WIDGET_TYPE_TEXT_EDITOR
1095 || type == WIDGET_TYPE_TEXT_BOX)
1096 {
1097 sEditingUIView = handled_view;
1098 }
1099 }
1100
1101 return handled;
1102}
1103
1104BOOL LLView::handleDoubleClick(S32 x, S32 y, MASK mask)
1105{
1106 BOOL handled = childrenHandleDoubleClick( x, y, mask ) != NULL;
1107 if( !handled && mMouseOpaque )
1108 {
1109 handleMouseDown(x, y, mask);
1110 handled = TRUE;
1111 }
1112 return handled;
1113}
1114
1115BOOL LLView::handleScrollWheel(S32 x, S32 y, S32 clicks)
1116{
1117 BOOL handled = FALSE;
1118 if( getVisible() && mEnabled )
1119 {
1120 handled = childrenHandleScrollWheel( x, y, clicks ) != NULL;
1121 if( !handled && mMouseOpaque )
1122 {
1123 handled = TRUE;
1124 }
1125 }
1126 return handled;
1127}
1128
1129BOOL LLView::handleRightMouseDown(S32 x, S32 y, MASK mask)
1130{
1131 BOOL handled = childrenHandleRightMouseDown( x, y, mask ) != NULL;
1132 if( !handled && mMouseOpaque )
1133 {
1134 handled = TRUE;
1135 }
1136 return handled;
1137}
1138
1139BOOL LLView::handleRightMouseUp(S32 x, S32 y, MASK mask)
1140{
1141 BOOL handled = childrenHandleRightMouseUp( x, y, mask ) != NULL;
1142 if( !handled && mMouseOpaque )
1143 {
1144 handled = TRUE;
1145 }
1146 return handled;
1147}
1148
1149LLView* LLView::childrenHandleScrollWheel(S32 x, S32 y, S32 clicks)
1150{
1151 LLView* handled_view = NULL;
1152 if (getVisible() && mEnabled )
1153 {
1154 for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
1155 {
1156 LLView* viewp = *child_it;
1157 S32 local_x = x - viewp->mRect.mLeft;
1158 S32 local_y = y - viewp->mRect.mBottom;
1159 if (viewp->pointInView(local_x, local_y) &&
1160 viewp->handleScrollWheel( local_x, local_y, clicks ))
1161 {
1162 if (sDebugMouseHandling)
1163 {
1164 sMouseHandlerMessage = LLString("->") + viewp->mName.c_str() + sMouseHandlerMessage;
1165 }
1166
1167 handled_view = viewp;
1168 break;
1169 }
1170 }
1171 }
1172 return handled_view;
1173}
1174
1175LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask)
1176{
1177 LLView* handled_view = NULL;
1178 if (getVisible() && mEnabled )
1179 {
1180 for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
1181 {
1182 LLView* viewp = *child_it;
1183 S32 local_x = x - viewp->mRect.mLeft;
1184 S32 local_y = y - viewp->mRect.mBottom;
1185 if(viewp->pointInView(local_x, local_y) &&
1186 viewp->getVisible() &&
1187 viewp->getEnabled() &&
1188 viewp->handleHover(local_x, local_y, mask) )
1189 {
1190 if (sDebugMouseHandling)
1191 {
1192 sMouseHandlerMessage = LLString("->") + viewp->mName.c_str() + sMouseHandlerMessage;
1193 }
1194
1195 handled_view = viewp;
1196 break;
1197 }
1198 }
1199 }
1200 return handled_view;
1201}
1202
1203// Called during downward traversal
1204LLView* LLView::childrenHandleKey(KEY key, MASK mask)
1205{
1206 LLView* handled_view = NULL;
1207
1208 if ( getVisible() && mEnabled )
1209 {
1210 for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
1211 {
1212 LLView* viewp = *child_it;
1213 if (viewp->handleKey(key, mask, TRUE))
1214 {
1215 if (LLView::sDebugKeys)
1216 {
1217 llinfos << "Key handled by " << viewp->getName() << llendl;
1218 }
1219 handled_view = viewp;
1220 break;
1221 }
1222 }
1223 }
1224
1225 return handled_view;
1226}
1227
1228// Called during downward traversal
1229LLView* LLView::childrenHandleUnicodeChar(llwchar uni_char)
1230{
1231 LLView* handled_view = NULL;
1232
1233 if ( getVisible() && mEnabled )
1234 {
1235 for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
1236 {
1237 LLView* viewp = *child_it;
1238 if (viewp->handleUnicodeChar(uni_char, TRUE))
1239 {
1240 if (LLView::sDebugKeys)
1241 {
1242 llinfos << "Unicode character handled by " << viewp->getName() << llendl;
1243 }
1244 handled_view = viewp;
1245 break;
1246 }
1247 }
1248 }
1249
1250 return handled_view;
1251}
1252
1253LLView* LLView::childrenHandleMouseDown(S32 x, S32 y, MASK mask)
1254{
1255 LLView* handled_view = NULL;
1256
1257 for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
1258 {
1259 LLView* viewp = *child_it;
1260 S32 local_x = x - viewp->mRect.mLeft;
1261 S32 local_y = y - viewp->mRect.mBottom;
1262
1263 if (viewp->pointInView(local_x, local_y) &&
1264 viewp->getVisible() &&
1265 viewp->mEnabled &&
1266 viewp->handleMouseDown( local_x, local_y, mask ))
1267 {
1268 if (sDebugMouseHandling)
1269 {
1270 sMouseHandlerMessage = LLString("->") + viewp->mName.c_str() + sMouseHandlerMessage;
1271 }
1272 handled_view = viewp;
1273 break;
1274 }
1275 }
1276 return handled_view;
1277}
1278
1279LLView* LLView::childrenHandleRightMouseDown(S32 x, S32 y, MASK mask)
1280{
1281 LLView* handled_view = NULL;
1282
1283 if (getVisible() && mEnabled )
1284 {
1285 for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
1286 {
1287 LLView* viewp = *child_it;
1288 S32 local_x = x - viewp->mRect.mLeft;
1289 S32 local_y = y - viewp->mRect.mBottom;
1290 if (viewp->pointInView(local_x, local_y) &&
1291 viewp->getVisible() &&
1292 viewp->mEnabled &&
1293 viewp->handleRightMouseDown( local_x, local_y, mask ))
1294 {
1295 if (sDebugMouseHandling)
1296 {
1297 sMouseHandlerMessage = LLString("->") + viewp->mName.c_str() + sMouseHandlerMessage;
1298 }
1299 handled_view = viewp;
1300 break;
1301 }
1302 }
1303 }
1304 return handled_view;
1305}
1306
1307LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask)
1308{
1309 LLView* handled_view = NULL;
1310
1311 if (getVisible() && mEnabled )
1312 {
1313 for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
1314 {
1315 LLView* viewp = *child_it;
1316 S32 local_x = x - viewp->mRect.mLeft;
1317 S32 local_y = y - viewp->mRect.mBottom;
1318 if (viewp->pointInView(local_x, local_y) &&
1319 viewp->getVisible() &&
1320 viewp->mEnabled &&
1321 viewp->handleDoubleClick( local_x, local_y, mask ))
1322 {
1323 if (sDebugMouseHandling)
1324 {
1325 sMouseHandlerMessage = LLString("->") + viewp->mName.c_str() + sMouseHandlerMessage;
1326 }
1327 handled_view = viewp;
1328 break;
1329 }
1330 }
1331 }
1332 return handled_view;
1333}
1334
1335LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask)
1336{
1337 LLView* handled_view = NULL;
1338 if( getVisible() && mEnabled )
1339 {
1340 for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
1341 {
1342 LLView* viewp = *child_it;
1343 S32 local_x = x - viewp->mRect.mLeft;
1344 S32 local_y = y - viewp->mRect.mBottom;
1345 if (!viewp->pointInView(local_x, local_y))
1346 continue;
1347 if (!viewp->getVisible())
1348 continue;
1349 if (!viewp->mEnabled)
1350 continue;
1351 if (viewp->handleMouseUp( local_x, local_y, mask ))
1352 {
1353 if (sDebugMouseHandling)
1354 {
1355 sMouseHandlerMessage = LLString("->") + viewp->mName.c_str() + sMouseHandlerMessage;
1356 }
1357 handled_view = viewp;
1358 break;
1359 }
1360 }
1361 }
1362 return handled_view;
1363}
1364
1365LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask)
1366{
1367 LLView* handled_view = NULL;
1368 if( getVisible() && mEnabled )
1369 {
1370 for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
1371 {
1372 LLView* viewp = *child_it;
1373 S32 local_x = x - viewp->mRect.mLeft;
1374 S32 local_y = y - viewp->mRect.mBottom;
1375 if (viewp->pointInView(local_x, local_y) &&
1376 viewp->getVisible() &&
1377 viewp->mEnabled &&
1378 viewp->handleRightMouseUp( local_x, local_y, mask ))
1379 {
1380 if (sDebugMouseHandling)
1381 {
1382 sMouseHandlerMessage = LLString("->") + viewp->mName.c_str() + sMouseHandlerMessage;
1383 }
1384 handled_view = viewp;
1385 break;
1386 }
1387 }
1388 }
1389 return handled_view;
1390}
1391
1392
1393void LLView::draw()
1394{
1395 if (getVisible())
1396 {
1397 if (sDebugRects)
1398 {
1399 drawDebugRect();
1400
1401 // Check for bogus rectangle
1402 if (mRect.mRight <= mRect.mLeft
1403 || mRect.mTop <= mRect.mBottom)
1404 {
1405 llwarns << "Bogus rectangle for " << getName() << " with " << mRect << llendl;
1406 }
1407 }
1408
1409 LLRect rootRect = getRootView()->getRect();
1410 LLRect screenRect;
1411
1412 // draw focused control on top of everything else
1413 LLView* focus_view = gFocusMgr.getKeyboardFocus();
1414 if (focus_view && focus_view->getParent() != this)
1415 {
1416 focus_view = NULL;
1417 }
1418
1419 for (child_list_reverse_iter_t child_iter = mChildList.rbegin(); child_iter != mChildList.rend(); ++child_iter)
1420 {
1421 LLView *viewp = *child_iter;
1422 ++sDepth;
1423
1424 if (viewp->getVisible() && viewp != focus_view)
1425 {
1426 // Only draw views that are within the root view
1427 localRectToScreen(viewp->getRect(),&screenRect);
1428 if ( rootRect.rectInRect(&screenRect) )
1429 {
1430 glMatrixMode(GL_MODELVIEW);
1431 LLUI::pushMatrix();
1432 {
1433 LLUI::translate((F32)viewp->getRect().mLeft, (F32)viewp->getRect().mBottom, 0.f);
1434 viewp->draw();
1435 }
1436 LLUI::popMatrix();
1437 }
1438 }
1439
1440 --sDepth;
1441 }
1442
1443 if (focus_view && focus_view->getVisible())
1444 {
1445 drawChild(focus_view);
1446 }
1447
1448 // HACK
1449 if (sEditingUI && this == sEditingUIView)
1450 {
1451 drawDebugRect();
1452 }
1453 }
1454}
1455
1456//Draw a box for debugging.
1457void LLView::drawDebugRect()
1458{
1459 // drawing solids requires texturing be disabled
1460 LLGLSNoTexture no_texture;
1461
1462 // draw red rectangle for the border
1463 LLColor4 border_color(0.f, 0.f, 0.f, 1.f);
1464 if (sEditingUI)
1465 {
1466 border_color.mV[0] = 1.f;
1467 }
1468 else
1469 {
1470 border_color.mV[sDepth%3] = 1.f;
1471 }
1472
1473 glColor4fv( border_color.mV );
1474
1475 glBegin(GL_LINES);
1476 glVertex2i(0, mRect.getHeight() - 1);
1477 glVertex2i(0, 0);
1478
1479 glVertex2i(0, 0);
1480 glVertex2i(mRect.getWidth() - 1, 0);
1481
1482 glVertex2i(mRect.getWidth() - 1, 0);
1483 glVertex2i(mRect.getWidth() - 1, mRect.getHeight() - 1);
1484
1485 glVertex2i(mRect.getWidth() - 1, mRect.getHeight() - 1);
1486 glVertex2i(0, mRect.getHeight() - 1);
1487 glEnd();
1488
1489 // Draw the name if it's not a leaf node
1490 if (mChildList.size() && !sEditingUI)
1491 {
1492 //char temp[256];
1493 S32 x, y;
1494 glColor4fv( border_color.mV );
1495 x = mRect.getWidth()/2;
1496 y = mRect.getHeight()/2;
1497 LLString debug_text = llformat("%s (%d x %d)", getName().c_str(),
1498 mRect.getWidth(), mRect.getHeight());
1499 LLFontGL::sSansSerifSmall->renderUTF8(debug_text, 0, (F32)x, (F32)y, border_color,
1500 LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL,
1501 S32_MAX, S32_MAX, NULL, FALSE);
1502 }
1503}
1504
1505void LLView::drawChild(LLView* childp, S32 x_offset, S32 y_offset)
1506{
1507 if (childp && childp->getParent() == this)
1508 {
1509 ++sDepth;
1510
1511 if (childp->getVisible())
1512 {
1513 glMatrixMode(GL_MODELVIEW);
1514 LLUI::pushMatrix();
1515 {
1516 LLUI::translate((F32)childp->getRect().mLeft + x_offset, (F32)childp->getRect().mBottom + y_offset, 0.f);
1517 childp->draw();
1518 }
1519 LLUI::popMatrix();
1520 }
1521
1522 --sDepth;
1523 }
1524}
1525
1526
1527void LLView::reshape(S32 width, S32 height, BOOL called_from_parent)
1528{
1529 // make sure this view contains all its children
1530 updateRect();
1531
1532 // compute how much things changed and apply reshape logic to children
1533 S32 delta_width = width - mRect.getWidth();
1534 S32 delta_height = height - mRect.getHeight();
1535
1536 if (delta_width || delta_height || sForceReshape)
1537 {
1538 // adjust our rectangle
1539 mRect.mRight = mRect.mLeft + width;
1540 mRect.mTop = mRect.mBottom + height;
1541
1542 // move child views according to reshape flags
1543 for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
1544 {
1545 LLView* viewp = *child_it;
1546 LLRect child_rect( viewp->mRect );
1547
1548 if (viewp->followsRight() && viewp->followsLeft())
1549 {
1550 child_rect.mRight += delta_width;
1551 }
1552 else if (viewp->followsRight())
1553 {
1554 child_rect.mLeft += delta_width;
1555 child_rect.mRight += delta_width;
1556 }
1557 else if (viewp->followsLeft())
1558 {
1559 // left is 0, don't need to adjust coords
1560 }
1561 else
1562 {
1563 // BUG what to do when we don't follow anyone?
1564 // for now, same as followsLeft
1565 }
1566
1567 if (viewp->followsTop() && viewp->followsBottom())
1568 {
1569 child_rect.mTop += delta_height;
1570 }
1571 else if (viewp->followsTop())
1572 {
1573 child_rect.mTop += delta_height;
1574 child_rect.mBottom += delta_height;
1575 }
1576 else if (viewp->followsBottom())
1577 {
1578 // bottom is 0, so don't need to adjust coords
1579 }
1580 else
1581 {
1582 // BUG what to do when we don't follow?
1583 // for now, same as bottom
1584 }
1585
1586 S32 delta_x = child_rect.mLeft - viewp->mRect.mLeft;
1587 S32 delta_y = child_rect.mBottom - viewp->mRect.mBottom;
1588 viewp->translate( delta_x, delta_y );
1589 viewp->reshape(child_rect.getWidth(), child_rect.getHeight());
1590 }
1591 }
1592
1593 if (!called_from_parent)
1594 {
1595 if (mParentView)
1596 {
1597 mParentView->reshape(mParentView->getRect().getWidth(), mParentView->getRect().getHeight(), FALSE);
1598 }
1599 }
1600}
1601
1602LLRect LLView::getRequiredRect()
1603{
1604 return mRect;
1605}
1606
1607const LLRect LLView::getScreenRect() const
1608{
1609 // *FIX: check for one-off error
1610 LLRect screen_rect;
1611 localPointToScreen(0, 0, &screen_rect.mLeft, &screen_rect.mBottom);
1612 localPointToScreen(mRect.getWidth(), mRect.getHeight(), &screen_rect.mRight, &screen_rect.mTop);
1613 return screen_rect;
1614}
1615
1616const LLRect LLView::getLocalRect() const
1617{
1618 LLRect local_rect(0, mRect.getHeight(), mRect.getWidth(), 0);
1619 return local_rect;
1620}
1621
1622void LLView::updateRect()
1623{
1624 if (mSpanChildren && mChildList.size())
1625 {
1626 LLView* first_child = (*mChildList.begin());
1627 LLRect child_spanning_rect = first_child->mRect;
1628
1629 for ( child_list_iter_t child_it = ++mChildList.begin(); child_it != mChildList.end(); ++child_it)
1630 {
1631 LLView* viewp = *child_it;
1632 if (viewp->getVisible())
1633 {
1634 child_spanning_rect |= viewp->mRect;
1635 }
1636 }
1637
1638 S32 translate_x = llmin(0, child_spanning_rect.mLeft);
1639 S32 translate_y = llmin(0, child_spanning_rect.mBottom);
1640 S32 new_width = llmax(mRect.getWidth() + translate_x, child_spanning_rect.getWidth());
1641 S32 new_height = llmax(mRect.getHeight() + translate_y, child_spanning_rect.getHeight());
1642
1643 mRect.setOriginAndSize(mRect.mLeft + translate_x, mRect.mBottom + translate_y, new_width, new_height);
1644
1645 for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
1646 {
1647 LLView* viewp = *child_it;
1648 viewp->mRect.translate(-translate_x, -translate_y);
1649 }
1650 }
1651}
1652
1653BOOL LLView::hasAncestor(LLView* parentp)
1654{
1655 if (!parentp)
1656 {
1657 return FALSE;
1658 }
1659
1660 LLView* viewp = getParent();
1661 while(viewp)
1662 {
1663 if (viewp == parentp)
1664 {
1665 return TRUE;
1666 }
1667 viewp = viewp->getParent();
1668 }
1669
1670 return FALSE;
1671}
1672
1673//-----------------------------------------------------------------------------
1674
1675BOOL LLView::childHasKeyboardFocus( const LLString& childname ) const
1676{
1677 LLView *child = getChildByName(childname);
1678 if (child)
1679 {
1680 return gFocusMgr.childHasKeyboardFocus(child);
1681 }
1682 else
1683 {
1684 return FALSE;
1685 }
1686}
1687
1688//-----------------------------------------------------------------------------
1689
1690BOOL LLView::hasChild(const LLString& childname, BOOL recurse) const
1691{
1692 return getChildByName(childname, recurse) ? TRUE : FALSE;
1693}
1694
1695//-----------------------------------------------------------------------------
1696// getChildByName()
1697//-----------------------------------------------------------------------------
1698LLView* LLView::getChildByName(const LLString& name, BOOL recurse) const
1699{
1700 if(name.empty()) return NULL;
1701 child_list_const_iter_t child_it;
1702 // Look for direct children *first*
1703 for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
1704 {
1705 LLView* childp = *child_it;
1706 if (childp->getName() == name)
1707 {
1708 return childp;
1709 }
1710 }
1711 if (recurse)
1712 {
1713 // Look inside the child as well.
1714 for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
1715 {
1716 LLView* childp = *child_it;
1717 LLView* viewp = childp->getChildByName(name, recurse);
1718 if ( viewp )
1719 {
1720 return viewp;
1721 }
1722 }
1723 }
1724 return NULL;
1725}
1726
1727// virtual
1728void LLView::onFocusLost()
1729{
1730}
1731
1732// virtual
1733void LLView::onFocusReceived()
1734{
1735}
1736
1737// virtual
1738void LLView::screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const
1739{
1740 *local_x = screen_x - mRect.mLeft;
1741 *local_y = screen_y - mRect.mBottom;
1742
1743 const LLView* cur = this;
1744 while( cur->mParentView )
1745 {
1746 cur = cur->mParentView;
1747 *local_x -= cur->mRect.mLeft;
1748 *local_y -= cur->mRect.mBottom;
1749 }
1750}
1751
1752void LLView::localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const
1753{
1754 *screen_x = local_x + mRect.mLeft;
1755 *screen_y = local_y + mRect.mBottom;
1756
1757 const LLView* cur = this;
1758 while( cur->mParentView )
1759 {
1760 cur = cur->mParentView;
1761 *screen_x += cur->mRect.mLeft;
1762 *screen_y += cur->mRect.mBottom;
1763 }
1764}
1765
1766void LLView::screenRectToLocal(const LLRect& screen, LLRect* local) const
1767{
1768 *local = screen;
1769 local->translate( -mRect.mLeft, -mRect.mBottom );
1770
1771 const LLView* cur = this;
1772 while( cur->mParentView )
1773 {
1774 cur = cur->mParentView;
1775 local->translate( -cur->mRect.mLeft, -cur->mRect.mBottom );
1776 }
1777}
1778
1779void LLView::localRectToScreen(const LLRect& local, LLRect* screen) const
1780{
1781 *screen = local;
1782 screen->translate( mRect.mLeft, mRect.mBottom );
1783
1784 const LLView* cur = this;
1785 while( cur->mParentView )
1786 {
1787 cur = cur->mParentView;
1788 screen->translate( cur->mRect.mLeft, cur->mRect.mBottom );
1789 }
1790}
1791
1792LLView* LLView::getRootMostFastFrameView()
1793{
1794 if (gFocusMgr.getTopView() == this)
1795 {
1796 return this;
1797 }
1798
1799 if (getParent())
1800 {
1801 LLView* rootmost_view = getParent()->getRootMostFastFrameView();
1802 if (rootmost_view)
1803 {
1804 return rootmost_view;
1805 }
1806 }
1807
1808 return mRenderInFastFrame ? this : NULL;
1809}
1810
1811
1812LLView* LLView::getRootView()
1813{
1814 LLView* view = this;
1815 while( view->mParentView )
1816 {
1817 view = view->mParentView;
1818 }
1819 return view;
1820}
1821
1822//static
1823LLWindow* LLView::getWindow(void)
1824{
1825 return LLUI::sWindow;
1826}
1827
1828// Moves the view so that it is entirely inside of constraint.
1829// If the view will not fit because it's too big, aligns with the top and left.
1830// (Why top and left? That's where the drag bars are for floaters.)
1831BOOL LLView::translateIntoRect(const LLRect& constraint, BOOL allow_partial_outside )
1832{
1833 S32 delta_x = 0;
1834 S32 delta_y = 0;
1835
1836 if (allow_partial_outside)
1837 {
1838 const S32 KEEP_ONSCREEN_PIXELS = 16;
1839
1840 if( mRect.mRight - KEEP_ONSCREEN_PIXELS < constraint.mLeft )
1841 {
1842 delta_x = constraint.mLeft - (mRect.mRight - KEEP_ONSCREEN_PIXELS);
1843 }
1844 else
1845 if( mRect.mLeft + KEEP_ONSCREEN_PIXELS > constraint.mRight )
1846 {
1847 delta_x = constraint.mRight - (mRect.mLeft + KEEP_ONSCREEN_PIXELS);
1848 delta_x += llmax( 0, mRect.getWidth() - constraint.getWidth() );
1849 }
1850
1851 if( mRect.mTop > constraint.mTop )
1852 {
1853 delta_y = constraint.mTop - mRect.mTop;
1854 }
1855 else
1856 if( mRect.mTop - KEEP_ONSCREEN_PIXELS < constraint.mBottom )
1857 {
1858 delta_y = constraint.mBottom - (mRect.mTop - KEEP_ONSCREEN_PIXELS);
1859 delta_y -= llmax( 0, mRect.getHeight() - constraint.getHeight() );
1860 }
1861 }
1862 else
1863 {
1864 if( mRect.mLeft < constraint.mLeft )
1865 {
1866 delta_x = constraint.mLeft - mRect.mLeft;
1867 }
1868 else
1869 if( mRect.mRight > constraint.mRight )
1870 {
1871 delta_x = constraint.mRight - mRect.mRight;
1872 delta_x += llmax( 0, mRect.getWidth() - constraint.getWidth() );
1873 }
1874
1875 if( mRect.mTop > constraint.mTop )
1876 {
1877 delta_y = constraint.mTop - mRect.mTop;
1878 }
1879 else
1880 if( mRect.mBottom < constraint.mBottom )
1881 {
1882 delta_y = constraint.mBottom - mRect.mBottom;
1883 delta_y -= llmax( 0, mRect.getHeight() - constraint.getHeight() );
1884 }
1885 }
1886
1887 if (delta_x != 0 || delta_y != 0)
1888 {
1889 translate(delta_x, delta_y);
1890 return TRUE;
1891 }
1892 return FALSE;
1893}
1894
1895BOOL LLView::localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, LLView* other_view)
1896{
1897 LLView* cur_view = this;
1898 LLView* root_view = NULL;
1899
1900 while (cur_view)
1901 {
1902 if (cur_view == other_view)
1903 {
1904 *other_x = x;
1905 *other_y = y;
1906 return TRUE;
1907 }
1908
1909 x += cur_view->getRect().mLeft;
1910 y += cur_view->getRect().mBottom;
1911
1912 cur_view = cur_view->getParent();
1913 root_view = cur_view;
1914 }
1915
1916 // assuming common root between two views, chase other_view's parents up to root
1917 cur_view = other_view;
1918 while (cur_view)
1919 {
1920 x -= cur_view->getRect().mLeft;
1921 y -= cur_view->getRect().mBottom;
1922
1923 cur_view = cur_view->getParent();
1924
1925 if (cur_view == root_view)
1926 {
1927 *other_x = x;
1928 *other_y = y;
1929 return TRUE;
1930 }
1931 }
1932
1933 *other_x = x;
1934 *other_y = y;
1935 return FALSE;
1936}
1937
1938BOOL LLView::localRectToOtherView( const LLRect& local, LLRect* other, LLView* other_view ) const
1939{
1940 LLRect cur_rect = local;
1941 const LLView* cur_view = this;
1942 const LLView* root_view = NULL;
1943
1944 while (cur_view)
1945 {
1946 if (cur_view == other_view)
1947 {
1948 *other = cur_rect;
1949 return TRUE;
1950 }
1951
1952 cur_rect.translate(cur_view->getRect().mLeft, cur_view->getRect().mBottom);
1953
1954 cur_view = cur_view->getParent();
1955 root_view = cur_view;
1956 }
1957
1958 // assuming common root between two views, chase other_view's parents up to root
1959 cur_view = other_view;
1960 while (cur_view)
1961 {
1962 cur_rect.translate(-cur_view->getRect().mLeft, -cur_view->getRect().mBottom);
1963
1964 cur_view = cur_view->getParent();
1965
1966 if (cur_view == root_view)
1967 {
1968 *other = cur_rect;
1969 return TRUE;
1970 }
1971 }
1972
1973 *other = cur_rect;
1974 return FALSE;
1975}
1976
1977// virtual
1978LLXMLNodePtr LLView::getXML(bool save_children) const
1979{
1980 const LLString& type_name = getWidgetTag();
1981
1982 LLXMLNodePtr node = new LLXMLNode(type_name, FALSE);
1983
1984 node->createChild("name", TRUE)->setStringValue(getName());
1985 node->createChild("width", TRUE)->setIntValue(getRect().getWidth());
1986 node->createChild("height", TRUE)->setIntValue(getRect().getHeight());
1987
1988 LLView* parent = getParent();
1989 S32 left = mRect.mLeft;
1990 S32 bottom = mRect.mBottom;
1991 if (parent) bottom -= parent->getRect().getHeight();
1992
1993 node->createChild("left", TRUE)->setIntValue(left);
1994 node->createChild("bottom", TRUE)->setIntValue(bottom);
1995
1996 U32 follows_flags = getFollows();
1997 if (follows_flags)
1998 {
1999 std::stringstream buffer;
2000 bool pipe = false;
2001 if (followsLeft())
2002 {
2003 buffer << "left";
2004 pipe = true;
2005 }
2006 if (followsTop())
2007 {
2008 if (pipe) buffer << "|";
2009 buffer << "top";
2010 pipe = true;
2011 }
2012 if (followsRight())
2013 {
2014 if (pipe) buffer << "|";
2015 buffer << "right";
2016 pipe = true;
2017 }
2018 if (followsBottom())
2019 {
2020 if (pipe) buffer << "|";
2021 buffer << "bottom";
2022 }
2023 node->createChild("follows", TRUE)->setStringValue(buffer.str());
2024 }
2025 // Export all widgets as enabled and visible - code must disable.
2026 node->createChild("hidden", TRUE)->setBoolValue(mHidden);
2027 node->createChild("mouse_opaque", TRUE)->setBoolValue(mMouseOpaque );
2028 if (!mToolTipMsg.empty())
2029 {
2030 node->createChild("tool_tip", TRUE)->setStringValue(mToolTipMsg);
2031 }
2032 if (mSoundFlags != MOUSE_UP)
2033 {
2034 node->createChild("sound_flags", TRUE)->setIntValue((S32)mSoundFlags);
2035 }
2036
2037 node->createChild("enabled", TRUE)->setBoolValue(mEnabled);
2038
2039 if (!mControlName.empty())
2040 {
2041 node->createChild("control_name", TRUE)->setStringValue(mControlName);
2042 }
2043 return node;
2044}
2045
2046// static
2047void LLView::addColorXML(LLXMLNodePtr node, const LLColor4& color,
2048 const LLString& xml_name, const LLString& control_name)
2049{
2050 if (color != LLUI::sColorsGroup->getColor(control_name))
2051 {
2052 node->createChild(xml_name, TRUE)->setFloatValue(4, color.mV);
2053 }
2054}
2055
2056// static
2057void LLView::saveColorToXML(std::ostream& out, const LLColor4& color,
2058 const LLString& xml_name, const LLString& control_name,
2059 const LLString& indent)
2060{
2061 if (color != LLUI::sColorsGroup->getColor(control_name))
2062 {
2063 out << indent << xml_name << "=\""
2064 << color.mV[VRED] << ", "
2065 << color.mV[VGREEN] << ", "
2066 << color.mV[VBLUE] << ", "
2067 << color.mV[VALPHA] << "\"\n";
2068 }
2069}
2070
2071//static
2072LLString LLView::escapeXML(const LLString& xml, LLString& indent)
2073{
2074 LLString ret = indent + "\"" + LLXMLNode::escapeXML(xml);
2075
2076 //replace every newline with a close quote, new line, indent, open quote
2077 size_t index = ret.size()-1;
2078 size_t fnd;
2079
2080 while ((fnd = ret.rfind("\n", index)) != std::string::npos)
2081 {
2082 ret.replace(fnd, 1, "\"\n" + indent + "\"");
2083 index = fnd-1;
2084 }
2085
2086 //append close quote
2087 ret.append("\"");
2088
2089 return ret;
2090}
2091
2092// static
2093LLWString LLView::escapeXML(const LLWString& xml)
2094{
2095 LLWString out;
2096 for (LLWString::size_type i = 0; i < xml.size(); ++i)
2097 {
2098 llwchar c = xml[i];
2099 switch(c)
2100 {
2101 case '"': out.append(utf8string_to_wstring("&quot;")); break;
2102 case '\'': out.append(utf8string_to_wstring("&apos;")); break;
2103 case '&': out.append(utf8string_to_wstring("&amp;")); break;
2104 case '<': out.append(utf8string_to_wstring("&lt;")); break;
2105 case '>': out.append(utf8string_to_wstring("&gt;")); break;
2106 default: out.push_back(c); break;
2107 }
2108 }
2109 return out;
2110}
2111
2112// static
2113const LLCtrlQuery & LLView::getTabOrderQuery()
2114{
2115 static LLCtrlQuery query;
2116 if(query.getPreFilters().size() == 0) {
2117 query.addPreFilter(LLVisibleFilter::getInstance());
2118 query.addPreFilter(LLEnabledFilter::getInstance());
2119 query.addPreFilter(LLTabStopFilter::getInstance());
2120 query.addPostFilter(LLUICtrl::LLTabStopPostFilter::getInstance());
2121 }
2122 return query;
2123}
2124
2125// static
2126const LLCtrlQuery & LLView::getFocusRootsQuery()
2127{
2128 static LLCtrlQuery query;
2129 if(query.getPreFilters().size() == 0) {
2130 query.addPreFilter(LLVisibleFilter::getInstance());
2131 query.addPreFilter(LLEnabledFilter::getInstance());
2132 query.addPreFilter(LLView::LLFocusRootsFilter::getInstance());
2133 }
2134 return query;
2135}
2136
2137
2138LLView* LLView::findSnapRect(LLRect& new_rect, const LLCoordGL& mouse_dir,
2139 LLView::ESnapType snap_type, S32 threshold, S32 padding)
2140{
2141 LLView* snap_view = NULL;
2142
2143 if (!mParentView)
2144 {
2145 new_rect = mRect;
2146 return snap_view;
2147 }
2148
2149 // If the view is near the edge of its parent, snap it to
2150 // the edge.
2151 LLRect test_rect = getSnapRect();
2152 LLRect view_rect = getSnapRect();
2153 test_rect.stretch(padding);
2154 view_rect.stretch(padding);
2155
2156 BOOL snapped_x = FALSE;
2157 BOOL snapped_y = FALSE;
2158
2159 LLRect parent_local_snap_rect = mParentView->getSnapRect();
2160 parent_local_snap_rect.translate(-mParentView->getRect().mLeft, -mParentView->getRect().mBottom);
2161
2162 if (snap_type == SNAP_PARENT || snap_type == SNAP_PARENT_AND_SIBLINGS)
2163 {
2164 if (llabs(parent_local_snap_rect.mRight - test_rect.mRight) <= threshold && (parent_local_snap_rect.mRight - test_rect.mRight) * mouse_dir.mX >= 0)
2165 {
2166 view_rect.translate(parent_local_snap_rect.mRight - view_rect.mRight, 0);
2167 snap_view = mParentView;
2168 snapped_x = TRUE;
2169 }
2170
2171 if (llabs(test_rect.mLeft - parent_local_snap_rect.mLeft) <= threshold && test_rect.mLeft * mouse_dir.mX <= 0)
2172 {
2173 view_rect.translate(parent_local_snap_rect.mLeft - view_rect.mLeft, 0);
2174 snap_view = mParentView;
2175 snapped_x = TRUE;
2176 }
2177
2178 if (llabs(test_rect.mBottom - parent_local_snap_rect.mBottom) <= threshold && test_rect.mBottom * mouse_dir.mY <= 0)
2179 {
2180 view_rect.translate(0, parent_local_snap_rect.mBottom - view_rect.mBottom);
2181 snap_view = mParentView;
2182 snapped_y = TRUE;
2183 }
2184
2185 if (llabs(parent_local_snap_rect.mTop - test_rect.mTop) <= threshold && (parent_local_snap_rect.mTop - test_rect.mTop) * mouse_dir.mY >= 0)
2186 {
2187 view_rect.translate(0, parent_local_snap_rect.mTop - view_rect.mTop);
2188 snap_view = mParentView;
2189 snapped_y = TRUE;
2190 }
2191 }
2192 if (snap_type == SNAP_SIBLINGS || snap_type == SNAP_PARENT_AND_SIBLINGS)
2193 {
2194 for ( child_list_const_iter_t child_it = mParentView->getChildList()->begin();
2195 child_it != mParentView->getChildList()->end(); ++child_it)
2196 {
2197 LLView* siblingp = *child_it;
2198 // skip self
2199 if (siblingp == this || !siblingp->getVisible() || !canSnapTo(siblingp))
2200 {
2201 continue;
2202 }
2203
2204 LLRect sibling_rect = siblingp->getSnapRect();
2205
2206 if (!snapped_x && llabs(test_rect.mRight - sibling_rect.mLeft) <= threshold && (test_rect.mRight - sibling_rect.mLeft) * mouse_dir.mX <= 0)
2207 {
2208 view_rect.translate(sibling_rect.mLeft - view_rect.mRight, 0);
2209 if (!snapped_y)
2210 {
2211 if (llabs(test_rect.mTop - sibling_rect.mTop) <= threshold && (test_rect.mTop - sibling_rect.mTop) * mouse_dir.mY <= 0)
2212 {
2213 view_rect.translate(0, sibling_rect.mTop - test_rect.mTop);
2214 snapped_y = TRUE;
2215 }
2216 else if (llabs(test_rect.mBottom - sibling_rect.mBottom) <= threshold && (test_rect.mBottom - sibling_rect.mBottom) * mouse_dir.mY <= 0)
2217 {
2218 view_rect.translate(0, sibling_rect.mBottom - test_rect.mBottom);
2219 snapped_y = TRUE;
2220 }
2221 }
2222 snap_view = siblingp;
2223 snapped_x = TRUE;
2224 }
2225
2226 if (!snapped_x && llabs(test_rect.mLeft - sibling_rect.mRight) <= threshold && (test_rect.mLeft - sibling_rect.mRight) * mouse_dir.mX <= 0)
2227 {
2228 view_rect.translate(sibling_rect.mRight - view_rect.mLeft, 0);
2229 if (!snapped_y)
2230 {
2231 if (llabs(test_rect.mTop - sibling_rect.mTop) <= threshold && (test_rect.mTop - sibling_rect.mTop) * mouse_dir.mY <= 0)
2232 {
2233 view_rect.translate(0, sibling_rect.mTop - test_rect.mTop);
2234 snapped_y = TRUE;
2235 }
2236 else if (llabs(test_rect.mBottom - sibling_rect.mBottom) <= threshold && (test_rect.mBottom - sibling_rect.mBottom) * mouse_dir.mY <= 0)
2237 {
2238 view_rect.translate(0, sibling_rect.mBottom - test_rect.mBottom);
2239 snapped_y = TRUE;
2240 }
2241 }
2242 snap_view = siblingp;
2243 snapped_x = TRUE;
2244 }
2245
2246 if (!snapped_y && llabs(test_rect.mBottom - sibling_rect.mTop) <= threshold && (test_rect.mBottom - sibling_rect.mTop) * mouse_dir.mY <= 0)
2247 {
2248 view_rect.translate(0, sibling_rect.mTop - view_rect.mBottom);
2249 if (!snapped_x)
2250 {
2251 if (llabs(test_rect.mLeft - sibling_rect.mLeft) <= threshold && (test_rect.mLeft - sibling_rect.mLeft) * mouse_dir.mX <= 0)
2252 {
2253 view_rect.translate(sibling_rect.mLeft - test_rect.mLeft, 0);
2254 snapped_x = TRUE;
2255 }
2256 else if (llabs(test_rect.mRight - sibling_rect.mRight) <= threshold && (test_rect.mRight - sibling_rect.mRight) * mouse_dir.mX <= 0)
2257 {
2258 view_rect.translate(sibling_rect.mRight - test_rect.mRight, 0);
2259 snapped_x = TRUE;
2260 }
2261 }
2262 snap_view = siblingp;
2263 snapped_y = TRUE;
2264 }
2265
2266 if (!snapped_y && llabs(test_rect.mTop - sibling_rect.mBottom) <= threshold && (test_rect.mTop - sibling_rect.mBottom) * mouse_dir.mY <= 0)
2267 {
2268 view_rect.translate(0, sibling_rect.mBottom - view_rect.mTop);
2269 if (!snapped_x)
2270 {
2271 if (llabs(test_rect.mLeft - sibling_rect.mLeft) <= threshold && (test_rect.mLeft - sibling_rect.mLeft) * mouse_dir.mX <= 0)
2272 {
2273 view_rect.translate(sibling_rect.mLeft - test_rect.mLeft, 0);
2274 snapped_x = TRUE;
2275 }
2276 else if (llabs(test_rect.mRight - sibling_rect.mRight) <= threshold && (test_rect.mRight - sibling_rect.mRight) * mouse_dir.mX <= 0)
2277 {
2278 view_rect.translate(sibling_rect.mRight - test_rect.mRight, 0);
2279 snapped_x = TRUE;
2280 }
2281 }
2282 snap_view = siblingp;
2283 snapped_y = TRUE;
2284 }
2285
2286 if (snapped_x && snapped_y)
2287 {
2288 break;
2289 }
2290 }
2291 }
2292
2293 // shrink actual view rect back down
2294 view_rect.stretch(-padding);
2295 new_rect = view_rect;
2296 return snap_view;
2297}
2298
2299LLView* LLView::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding)
2300{
2301 LLRect snap_rect = getSnapRect();
2302 S32 snap_pos = 0;
2303 switch(snap_edge)
2304 {
2305 case SNAP_LEFT:
2306 snap_pos = snap_rect.mLeft;
2307 break;
2308 case SNAP_RIGHT:
2309 snap_pos = snap_rect.mRight;
2310 break;
2311 case SNAP_TOP:
2312 snap_pos = snap_rect.mTop;
2313 break;
2314 case SNAP_BOTTOM:
2315 snap_pos = snap_rect.mBottom;
2316 break;
2317 }
2318
2319 if (!mParentView)
2320 {
2321 new_edge_val = snap_pos;
2322 return NULL;
2323 }
2324
2325 LLView* snap_view = NULL;
2326
2327 // If the view is near the edge of its parent, snap it to
2328 // the edge.
2329 LLRect test_rect = snap_rect;
2330 test_rect.stretch(padding);
2331
2332 BOOL snapped_x = FALSE;
2333 BOOL snapped_y = FALSE;
2334
2335 LLRect parent_local_snap_rect = mParentView->getSnapRect();
2336 parent_local_snap_rect.translate(-mParentView->getRect().mLeft, -mParentView->getRect().mBottom);
2337
2338 if (snap_type == SNAP_PARENT || snap_type == SNAP_PARENT_AND_SIBLINGS)
2339 {
2340 switch(snap_edge)
2341 {
2342 case SNAP_RIGHT:
2343 if (llabs(parent_local_snap_rect.mRight - test_rect.mRight) <= threshold && (parent_local_snap_rect.mRight - test_rect.mRight) * mouse_dir.mX >= 0)
2344 {
2345 snap_pos = parent_local_snap_rect.mRight - padding;
2346 snap_view = mParentView;
2347 snapped_x = TRUE;
2348 }
2349 break;
2350 case SNAP_LEFT:
2351 if (llabs(test_rect.mLeft - parent_local_snap_rect.mLeft) <= threshold && test_rect.mLeft * mouse_dir.mX <= 0)
2352 {
2353 snap_pos = parent_local_snap_rect.mLeft + padding;
2354 snap_view = mParentView;
2355 snapped_x = TRUE;
2356 }
2357 break;
2358 case SNAP_BOTTOM:
2359 if (llabs(test_rect.mBottom - parent_local_snap_rect.mBottom) <= threshold && test_rect.mBottom * mouse_dir.mY <= 0)
2360 {
2361 snap_pos = parent_local_snap_rect.mBottom + padding;
2362 snap_view = mParentView;
2363 snapped_y = TRUE;
2364 }
2365 break;
2366 case SNAP_TOP:
2367 if (llabs(parent_local_snap_rect.mTop - test_rect.mTop) <= threshold && (parent_local_snap_rect.mTop - test_rect.mTop) * mouse_dir.mY >= 0)
2368 {
2369 snap_pos = parent_local_snap_rect.mTop - padding;
2370 snap_view = mParentView;
2371 snapped_y = TRUE;
2372 }
2373 break;
2374 default:
2375 llerrs << "Invalid snap edge" << llendl;
2376 }
2377 }
2378
2379 if (snap_type == SNAP_SIBLINGS || snap_type == SNAP_PARENT_AND_SIBLINGS)
2380 {
2381 for ( child_list_const_iter_t child_it = mParentView->getChildList()->begin();
2382 child_it != mParentView->getChildList()->end(); ++child_it)
2383 {
2384 LLView* siblingp = *child_it;
2385 // skip self
2386 if (siblingp == this || !siblingp->getVisible() || !canSnapTo(siblingp))
2387 {
2388 continue;
2389 }
2390
2391 LLRect sibling_rect = siblingp->getSnapRect();
2392
2393 switch(snap_edge)
2394 {
2395 case SNAP_RIGHT:
2396 if (!snapped_x)
2397 {
2398 if (llabs(test_rect.mRight - sibling_rect.mLeft) <= threshold && (test_rect.mRight - sibling_rect.mLeft) * mouse_dir.mX <= 0)
2399 {
2400 snap_pos = sibling_rect.mLeft - padding;
2401 snap_view = siblingp;
2402 snapped_x = TRUE;
2403 }
2404 // if snapped with sibling along other axis, check for shared edge
2405 else if (llabs(sibling_rect.mTop - (test_rect.mBottom - padding)) <= threshold ||
2406 llabs(sibling_rect.mBottom - (test_rect.mTop + padding)) <= threshold)
2407 {
2408 if (llabs(test_rect.mRight - sibling_rect.mRight) <= threshold && (test_rect.mRight - sibling_rect.mRight) * mouse_dir.mX <= 0)
2409 {
2410 snap_pos = sibling_rect.mRight;
2411 snap_view = siblingp;
2412 snapped_x = TRUE;
2413 }
2414 }
2415 }
2416 break;
2417 case SNAP_LEFT:
2418 if (!snapped_x)
2419 {
2420 if (llabs(test_rect.mLeft - sibling_rect.mRight) <= threshold && (test_rect.mLeft - sibling_rect.mRight) * mouse_dir.mX <= 0)
2421 {
2422 snap_pos = sibling_rect.mRight + padding;
2423 snap_view = siblingp;
2424 snapped_x = TRUE;
2425 }
2426 // if snapped with sibling along other axis, check for shared edge
2427 else if (llabs(sibling_rect.mTop - (test_rect.mBottom - padding)) <= threshold ||
2428 llabs(sibling_rect.mBottom - (test_rect.mTop + padding)) <= threshold)
2429 {
2430 if (llabs(test_rect.mLeft - sibling_rect.mLeft) <= threshold && (test_rect.mLeft - sibling_rect.mLeft) * mouse_dir.mX <= 0)
2431 {
2432 snap_pos = sibling_rect.mLeft;
2433 snap_view = siblingp;
2434 snapped_x = TRUE;
2435 }
2436 }
2437 }
2438 break;
2439 case SNAP_BOTTOM:
2440 if (!snapped_y)
2441 {
2442 if (llabs(test_rect.mBottom - sibling_rect.mTop) <= threshold && (test_rect.mBottom - sibling_rect.mTop) * mouse_dir.mY <= 0)
2443 {
2444 snap_pos = sibling_rect.mTop + padding;
2445 snap_view = siblingp;
2446 snapped_y = TRUE;
2447 }
2448 // if snapped with sibling along other axis, check for shared edge
2449 else if (llabs(sibling_rect.mRight - (test_rect.mLeft - padding)) <= threshold ||
2450 llabs(sibling_rect.mLeft - (test_rect.mRight + padding)) <= threshold)
2451 {
2452 if (llabs(test_rect.mBottom - sibling_rect.mBottom) <= threshold && (test_rect.mBottom - sibling_rect.mBottom) * mouse_dir.mY <= 0)
2453 {
2454 snap_pos = sibling_rect.mBottom;
2455 snap_view = siblingp;
2456 snapped_y = TRUE;
2457 }
2458 }
2459 }
2460 break;
2461 case SNAP_TOP:
2462 if (!snapped_y)
2463 {
2464 if (llabs(test_rect.mTop - sibling_rect.mBottom) <= threshold && (test_rect.mTop - sibling_rect.mBottom) * mouse_dir.mY <= 0)
2465 {
2466 snap_pos = sibling_rect.mBottom - padding;
2467 snap_view = siblingp;
2468 snapped_y = TRUE;
2469 }
2470 // if snapped with sibling along other axis, check for shared edge
2471 else if (llabs(sibling_rect.mRight - (test_rect.mLeft - padding)) <= threshold ||
2472 llabs(sibling_rect.mLeft - (test_rect.mRight + padding)) <= threshold)
2473 {
2474 if (llabs(test_rect.mTop - sibling_rect.mTop) <= threshold && (test_rect.mTop - sibling_rect.mTop) * mouse_dir.mY <= 0)
2475 {
2476 snap_pos = sibling_rect.mTop;
2477 snap_view = siblingp;
2478 snapped_y = TRUE;
2479 }
2480 }
2481 }
2482 break;
2483 default:
2484 llerrs << "Invalid snap edge" << llendl;
2485 }
2486 if (snapped_x && snapped_y)
2487 {
2488 break;
2489 }
2490 }
2491 }
2492
2493 new_edge_val = snap_pos;
2494 return snap_view;
2495}
2496
2497bool operator==(const LLViewHandle& lhs, const LLViewHandle& rhs)
2498{
2499 return lhs.mID == rhs.mID;
2500}
2501
2502bool operator!=(const LLViewHandle& lhs, const LLViewHandle& rhs)
2503{
2504 return lhs.mID != rhs.mID;
2505}
2506
2507bool operator<(const LLViewHandle &lhs, const LLViewHandle &rhs)
2508{
2509 return lhs.mID < rhs.mID;
2510}
2511
2512//-----------------------------------------------------------------------------
2513// Listener dispatch functions
2514//-----------------------------------------------------------------------------
2515
2516void LLView::registerEventListener(LLString name, LLSimpleListener* function)
2517{
2518 mDispatchList.insert(std::pair<LLString, LLSimpleListener*>(name, function));
2519}
2520
2521void LLView::deregisterEventListener(LLString name)
2522{
2523 dispatch_list_t::iterator itor = mDispatchList.find(name);
2524 if (itor != mDispatchList.end())
2525 {
2526 delete itor->second;
2527 mDispatchList.erase(itor);
2528 }
2529}
2530
2531LLString LLView::findEventListener(LLSimpleListener *listener) const
2532{
2533 dispatch_list_t::const_iterator itor;
2534 for (itor = mDispatchList.begin(); itor != mDispatchList.end(); ++itor)
2535 {
2536 if (itor->second == listener)
2537 {
2538 return itor->first;
2539 }
2540 }
2541 if (mParentView)
2542 {
2543 return mParentView->findEventListener(listener);
2544 }
2545 return LLString::null;
2546}
2547
2548LLSimpleListener* LLView::getListenerByName(const LLString& callback_name)
2549{
2550 LLSimpleListener* callback = NULL;
2551 dispatch_list_t::iterator itor = mDispatchList.find(callback_name);
2552 if (itor != mDispatchList.end())
2553 {
2554 callback = itor->second;
2555 }
2556 else if (mParentView)
2557 {
2558 callback = mParentView->getListenerByName(callback_name);
2559 }
2560 return callback;
2561}
2562
2563void LLView::addListenerToControl(LLEventDispatcher *dispatcher, const LLString& name, LLSD filter, LLSD userdata)
2564{
2565 LLSimpleListener* listener = getListenerByName(name);
2566 if (listener)
2567 {
2568 dispatcher->addListener(listener, filter, userdata);
2569 }
2570}
2571
2572LLControlBase *LLView::findControl(LLString name)
2573{
2574 control_map_t::iterator itor = mFloaterControls.find(name);
2575 if (itor != mFloaterControls.end())
2576 {
2577 return itor->second;
2578 }
2579 if (mParentView)
2580 {
2581 return mParentView->findControl(name);
2582 }
2583 return LLUI::sConfigGroup->getControl(name);
2584}
2585
2586const S32 FLOATER_H_MARGIN = 15;
2587const S32 MIN_WIDGET_HEIGHT = 10;
2588const S32 VPAD = 4;
2589
2590// static
2591U32 LLView::createRect(LLXMLNodePtr node, LLRect &rect, LLView* parent_view, const LLRect &required_rect)
2592{
2593 U32 follows = 0;
2594 S32 x = FLOATER_H_MARGIN;
2595 S32 y = 0;
2596 S32 w = 0;
2597 S32 h = 0;
2598
2599 U32 last_x = 0;
2600 U32 last_y = 0;
2601 if (parent_view)
2602 {
2603 last_y = parent_view->getRect().getHeight();
2604 child_list_t::const_iterator itor = parent_view->getChildList()->begin();
2605 if (itor != parent_view->getChildList()->end())
2606 {
2607 LLView *last_view = (*itor);
2608 if (last_view->getSaveToXML())
2609 {
2610 last_x = last_view->getRect().mLeft;
2611 last_y = last_view->getRect().mBottom;
2612 }
2613 }
2614 }
2615
2616 LLString rect_control;
2617 node->getAttributeString("rect_control", rect_control);
2618 if (! rect_control.empty())
2619 {
2620 LLRect rect = LLUI::sConfigGroup->getRect(rect_control);
2621 x = rect.mLeft;
2622 y = rect.mBottom;
2623 w = rect.getWidth();
2624 h = rect.getHeight();
2625 }
2626
2627 if (node->hasAttribute("left"))
2628 {
2629 node->getAttributeS32("left", x);
2630 }
2631 if (node->hasAttribute("bottom"))
2632 {
2633 node->getAttributeS32("bottom", y);
2634 }
2635
2636 // Make your width the width of the containing
2637 // view if you don't specify a width.
2638 if (parent_view)
2639 {
2640 w = llmax(required_rect.getWidth(), parent_view->getRect().getWidth() - (FLOATER_H_MARGIN) - x);
2641 h = llmax(MIN_WIDGET_HEIGHT, required_rect.getHeight());
2642 }
2643
2644 if (node->hasAttribute("width"))
2645 {
2646 node->getAttributeS32("width", w);
2647 }
2648 if (node->hasAttribute("height"))
2649 {
2650 node->getAttributeS32("height", h);
2651 }
2652
2653 if (parent_view)
2654 {
2655 if (node->hasAttribute("left_delta"))
2656 {
2657 S32 left_delta = 0;
2658 node->getAttributeS32("left_delta", left_delta);
2659 x = last_x + left_delta;
2660 }
2661 else if (node->hasAttribute("left") && node->hasAttribute("right"))
2662 {
2663 // compute width based on left and right
2664 S32 right = 0;
2665 node->getAttributeS32("right", right);
2666 if (right < 0)
2667 {
2668 right = parent_view->getRect().getWidth() + right;
2669 }
2670 w = right - x;
2671 }
2672 else if (node->hasAttribute("left"))
2673 {
2674 if (x < 0)
2675 {
2676 x = parent_view->getRect().getWidth() + x;
2677 follows |= FOLLOWS_RIGHT;
2678 }
2679 else
2680 {
2681 follows |= FOLLOWS_LEFT;
2682 }
2683 }
2684 else if (node->hasAttribute("width") && node->hasAttribute("right"))
2685 {
2686 S32 right = 0;
2687 node->getAttributeS32("right", right);
2688 if (right < 0)
2689 {
2690 right = parent_view->getRect().getWidth() + right;
2691 }
2692 x = right - w;
2693 }
2694 else
2695 {
2696 // left not specified, same as last
2697 x = last_x;
2698 }
2699
2700 if (node->hasAttribute("bottom_delta"))
2701 {
2702 S32 bottom_delta = 0;
2703 node->getAttributeS32("bottom_delta", bottom_delta);
2704 y = last_y + bottom_delta;
2705 }
2706 else if (node->hasAttribute("top"))
2707 {
2708 // compute height based on top
2709 S32 top = 0;
2710 node->getAttributeS32("top", top);
2711 if (top < 0)
2712 {
2713 top = parent_view->getRect().getHeight() + top;
2714 }
2715 h = top - y;
2716 }
2717 else if (node->hasAttribute("bottom"))
2718 {
2719 if (y < 0)
2720 {
2721 y = parent_view->getRect().getHeight() + y;
2722 follows |= FOLLOWS_TOP;
2723 }
2724 else
2725 {
2726 follows |= FOLLOWS_BOTTOM;
2727 }
2728 }
2729 else
2730 {
2731 // if bottom not specified, generate automatically
2732 if (last_y == 0)
2733 {
2734 // treat first child as "bottom"
2735 y = parent_view->getRect().getHeight() - (h + VPAD);
2736 follows |= FOLLOWS_TOP;
2737 }
2738 else
2739 {
2740 // treat subsequent children as "bottom_delta"
2741 y = last_y - (h + VPAD);
2742 }
2743 }
2744 }
2745 else
2746 {
2747 x = llmax(x, 0);
2748 y = llmax(y, 0);
2749 follows = FOLLOWS_LEFT | FOLLOWS_TOP;
2750 }
2751 rect.setOriginAndSize(x, y, w, h);
2752
2753 return follows;
2754}
2755
2756void LLView::initFromXML(LLXMLNodePtr node, LLView* parent)
2757{
2758 // create rect first, as this will supply initial follows flags
2759 LLRect view_rect;
2760 U32 follows_flags = createRect(node, view_rect, parent, getRequiredRect());
2761 // call reshape in case there are any child elements that need to be layed out
2762 reshape(view_rect.getWidth(), view_rect.getHeight());
2763 setRect(view_rect);
2764 setFollows(follows_flags);
2765
2766 if (node->hasAttribute("follows"))
2767 {
2768 setFollowsNone();
2769
2770 LLString follows;
2771 node->getAttributeString("follows", follows);
2772
2773 typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
2774 boost::char_separator<char> sep("|");
2775 tokenizer tokens(follows, sep);
2776 tokenizer::iterator token_iter = tokens.begin();
2777
2778 while(token_iter != tokens.end())
2779 {
2780 const std::string& token_str = *token_iter;
2781 if (token_str == "left")
2782 {
2783 setFollowsLeft();
2784 }
2785 else if (token_str == "right")
2786 {
2787 setFollowsRight();
2788 }
2789 else if (token_str == "top")
2790 {
2791 setFollowsTop();
2792 }
2793 else if (token_str == "bottom")
2794 {
2795 setFollowsBottom();
2796 }
2797 else if (token_str == "all")
2798 {
2799 setFollowsAll();
2800 }
2801 ++token_iter;
2802 }
2803 }
2804
2805 if (node->hasAttribute("control_name"))
2806 {
2807 LLString control_name;
2808 node->getAttributeString("control_name", control_name);
2809 setControlName(control_name, NULL);
2810 }
2811
2812 if (node->hasAttribute("tool_tip"))
2813 {
2814 LLString tool_tip_msg("");
2815 node->getAttributeString("tool_tip", tool_tip_msg);
2816 setToolTip(tool_tip_msg);
2817 }
2818
2819 if (node->hasAttribute("enabled"))
2820 {
2821 BOOL enabled;
2822 node->getAttributeBOOL("enabled", enabled);
2823 setEnabled(enabled);
2824 }
2825
2826 if (node->hasAttribute("visible"))
2827 {
2828 BOOL visible;
2829 node->getAttributeBOOL("visible", visible);
2830 setVisible(visible);
2831 }
2832
2833 if (node->hasAttribute("hidden"))
2834 {
2835 BOOL hidden;
2836 node->getAttributeBOOL("hidden", hidden);
2837 setHidden(hidden);
2838 }
2839
2840 node->getAttributeS32("default_tab_group", mDefaultTabGroup);
2841
2842 reshape(view_rect.getWidth(), view_rect.getHeight());
2843}
2844
2845// static
2846LLFontGL* LLView::selectFont(LLXMLNodePtr node)
2847{
2848 LLFontGL* gl_font = NULL;
2849
2850 if (node->hasAttribute("font"))
2851 {
2852 LLString font_name;
2853 node->getAttributeString("font", font_name);
2854
2855 gl_font = LLFontGL::fontFromName(font_name.c_str());
2856 }
2857 return gl_font;
2858}
2859
2860// static
2861LLFontGL::HAlign LLView::selectFontHAlign(LLXMLNodePtr node)
2862{
2863 LLFontGL::HAlign gl_hfont_align = LLFontGL::LEFT;
2864
2865 if (node->hasAttribute("halign"))
2866 {
2867 LLString horizontal_align_name;
2868 node->getAttributeString("halign", horizontal_align_name);
2869 gl_hfont_align = LLFontGL::hAlignFromName(horizontal_align_name);
2870 }
2871 return gl_hfont_align;
2872}
2873
2874// static
2875LLFontGL::VAlign LLView::selectFontVAlign(LLXMLNodePtr node)
2876{
2877 LLFontGL::VAlign gl_vfont_align = LLFontGL::BASELINE;
2878
2879 if (node->hasAttribute("valign"))
2880 {
2881 LLString vert_align_name;
2882 node->getAttributeString("valign", vert_align_name);
2883 gl_vfont_align = LLFontGL::vAlignFromName(vert_align_name);
2884 }
2885 return gl_vfont_align;
2886}
2887
2888// static
2889LLFontGL::StyleFlags LLView::selectFontStyle(LLXMLNodePtr node)
2890{
2891 LLFontGL::StyleFlags gl_font_style = LLFontGL::NORMAL;
2892
2893 if (node->hasAttribute("style"))
2894 {
2895 LLString style_flags_name;
2896 node->getAttributeString("style", style_flags_name);
2897
2898 if (style_flags_name == "normal")
2899 {
2900 gl_font_style = LLFontGL::NORMAL;
2901 }
2902 else if (style_flags_name == "bold")
2903 {
2904 gl_font_style = LLFontGL::BOLD;
2905 }
2906 else if (style_flags_name == "italic")
2907 {
2908 gl_font_style = LLFontGL::ITALIC;
2909 }
2910 else if (style_flags_name == "underline")
2911 {
2912 gl_font_style = LLFontGL::UNDERLINE;
2913 }
2914 //else leave left
2915 }
2916 return gl_font_style;
2917}
2918
2919void LLView::setControlValue(const LLSD& value)
2920{
2921 LLUI::sConfigGroup->setValue(getControlName(), value);
2922}
2923
2924//virtual
2925LLString LLView::getControlName() const
2926{
2927 return mControlName;
2928}
2929
2930//virtual
2931void LLView::setControlName(const LLString& control_name, LLView *context)
2932{
2933 if (context == NULL)
2934 {
2935 context = this;
2936 }
2937
2938 // Unregister from existing listeners
2939 if (!mControlName.empty())
2940 {
2941 clearDispatchers();
2942 }
2943
2944 // Register new listener
2945 if (!control_name.empty())
2946 {
2947 LLControlBase *control = context->findControl(control_name);
2948 if (control)
2949 {
2950 mControlName = control_name;
2951 LLSD state = control->registerListener(this, "DEFAULT");
2952 setValue(state);
2953 }
2954 }
2955}
2956
2957// virtual
2958bool LLView::handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
2959{
2960 if (userdata.asString() == "DEFAULT" && event->desc() == "value_changed")
2961 {
2962 LLSD state = event->getValue();
2963 setValue(state);
2964 return TRUE;
2965 }
2966 return FALSE;
2967}
2968
2969void LLView::setValue(const LLSD& value)
2970{
2971}
2972
2973
2974void LLView::addBoolControl(LLString name, bool initial_value)
2975{
2976 mFloaterControls[name] = new LLControl(name, TYPE_BOOLEAN, initial_value, "Internal floater control");
2977}
2978
2979LLControlBase *LLView::getControl(LLString name)
2980{
2981 control_map_t::iterator itor = mFloaterControls.find(name);
2982 if (itor != mFloaterControls.end())
2983 {
2984 return itor->second;
2985 }
2986 return NULL;
2987}