/** * @file llview.h * @brief Container for other views, anything that draws. * * $LicenseInfo:firstyear=2001&license=viewergpl$ * * Copyright (c) 2001-2009, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at * http://secondlifegrid.net/programs/open_source/licensing/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ #ifndef LL_LLVIEW_H #define LL_LLVIEW_H // A view is an area in a window that can draw. It might represent // the HUD or a dialog box or a button. It can also contain sub-views // and child widgets #include "stdtypes.h" #include "llcoord.h" #include "llfontgl.h" #include "llmortician.h" #include "llmousehandler.h" #include "llstring.h" #include "llrect.h" #include "llui.h" #include "lluistring.h" #include "llviewquery.h" #include "stdenums.h" #include "lluistring.h" #include "llcursortypes.h" #include "lluictrlfactory.h" #include "lltreeiterators.h" #include "llfocusmgr.h" #include class LLSD; const U32 FOLLOWS_NONE = 0x00; const U32 FOLLOWS_LEFT = 0x01; const U32 FOLLOWS_RIGHT = 0x02; const U32 FOLLOWS_TOP = 0x10; const U32 FOLLOWS_BOTTOM = 0x20; const U32 FOLLOWS_ALL = 0x33; const BOOL MOUSE_OPAQUE = TRUE; const BOOL NOT_MOUSE_OPAQUE = FALSE; const U32 GL_NAME_UI_RESERVED = 2; // maintains render state during traversal of UI tree class LLViewDrawContext { public: F32 mAlpha; LLViewDrawContext(F32 alpha = 1.f) : mAlpha(alpha) { if (!sDrawContextStack.empty()) { LLViewDrawContext* context_top = sDrawContextStack.back(); // merge with top of stack mAlpha *= context_top->mAlpha; } sDrawContextStack.push_back(this); } ~LLViewDrawContext() { sDrawContextStack.pop_back(); } static const LLViewDrawContext& getCurrentContext(); private: static std::vector sDrawContextStack; }; class LLViewWidgetRegistry : public LLChildRegistry {}; class LLView : public LLMouseHandler, public LLMortician, public LLFocusableElement { public: struct Follows : public LLInitParam::Choice { Alternative string; Alternative flags; Follows() : string(""), flags("flags", FOLLOWS_LEFT | FOLLOWS_TOP) {} }; struct Params : public LLInitParam::Block { Mandatory name; Optional enabled, visible, mouse_opaque, use_bounding_rect, from_xui; Optional tab_group, default_tab_group; Optional tool_tip; Optional sound_flags; Optional follows; Optional hover_cursor; Optional layout; Optional rect; // Historical bottom-left layout used bottom_delta and left_delta // for relative positioning. New layout "topleft" prefers specifying // based on top edge. Optional bottom_delta, // deprecated top_pad, // from last bottom to my top top_delta, // from last top to my top left_pad, // from last right to my left left_delta; // from last left to my left Optional center_horiz, center_vert; // these are nested attributes for LLLayoutPanel //FIXME: get parent context involved in parsing traversal Ignored user_resize, auto_resize, needs_translate, xmlns, xmlns_xsi, xsi_schemaLocation, xsi_type; Params(); }; typedef LLViewWidgetRegistry child_registry_t; void initFromParams(const LLView::Params&); protected: LLView(const LLView::Params&); friend class LLUICtrlFactory; private: // widgets in general are not copyable LLView(const LLView& other) {}; public: #if LL_DEBUG static BOOL sIsDrawing; #endif enum ESoundFlags { SILENT = 0, MOUSE_DOWN = 1, MOUSE_UP = 2 }; enum ESnapType { SNAP_PARENT, SNAP_SIBLINGS, SNAP_PARENT_AND_SIBLINGS }; enum ESnapEdge { SNAP_LEFT, SNAP_TOP, SNAP_RIGHT, SNAP_BOTTOM }; typedef std::list child_list_t; typedef child_list_t::iterator child_list_iter_t; typedef child_list_t::const_iterator child_list_const_iter_t; typedef child_list_t::reverse_iterator child_list_reverse_iter_t; typedef child_list_t::const_reverse_iterator child_list_const_reverse_iter_t; typedef std::vector ctrl_list_t; typedef std::pair tab_order_t; typedef std::pair tab_order_pair_t; // this structure primarily sorts by the tab group, secondarily by the insertion ordinal (lastly by the value of the pointer) typedef std::map child_tab_order_t; typedef child_tab_order_t::iterator child_tab_order_iter_t; typedef child_tab_order_t::const_iterator child_tab_order_const_iter_t; typedef child_tab_order_t::reverse_iterator child_tab_order_reverse_iter_t; typedef child_tab_order_t::const_reverse_iterator child_tab_order_const_reverse_iter_t; virtual ~LLView(); // Some UI widgets need to be added as controls. Others need to // be added as regular view children. isCtrl should return TRUE // if a widget needs to be added as a ctrl virtual BOOL isCtrl() const; virtual BOOL isPanel() const; // // MANIPULATORS // void setMouseOpaque( BOOL b ) { mMouseOpaque = b; } BOOL getMouseOpaque() const { return mMouseOpaque; } void setToolTip( const LLStringExplicit& msg ); BOOL setToolTipArg( const LLStringExplicit& key, const LLStringExplicit& text ); void setToolTipArgs( const LLStringUtil::format_map_t& args ); virtual void setRect(const LLRect &rect); void setFollows(U32 flags) { mReshapeFlags = flags; } // deprecated, use setFollows() with FOLLOWS_LEFT | FOLLOWS_TOP, etc. void setFollowsNone() { mReshapeFlags = FOLLOWS_NONE; } void setFollowsLeft() { mReshapeFlags |= FOLLOWS_LEFT; } void setFollowsTop() { mReshapeFlags |= FOLLOWS_TOP; } void setFollowsRight() { mReshapeFlags |= FOLLOWS_RIGHT; } void setFollowsBottom() { mReshapeFlags |= FOLLOWS_BOTTOM; } void setFollowsAll() { mReshapeFlags |= FOLLOWS_ALL; } void setSoundFlags(U8 flags) { mSoundFlags = flags; } void setName(std::string name) { mName = name; } void setUseBoundingRect( BOOL use_bounding_rect ); BOOL getUseBoundingRect(); ECursorType getHoverCursor() { return mHoverCursor; } const std::string& getToolTip() const { return mToolTipMsg.getString(); } void sendChildToFront(LLView* child); void sendChildToBack(LLView* child); void moveChildToFrontOfTabGroup(LLUICtrl* child); void moveChildToBackOfTabGroup(LLUICtrl* child); virtual bool addChild(LLView* view, S32 tab_group = 0); // implemented in terms of addChild() bool addChildInBack(LLView* view, S32 tab_group = 0); // remove the specified child from the view, and set it's parent to NULL. virtual void removeChild(LLView* view); // helper function for lluictrlfactory.h create<> template void setParent(LLView* parent) { if (parent) parent->addChild(this); } virtual BOOL postBuild() { return TRUE; } child_tab_order_t getCtrlOrder() const { return mCtrlOrder; } ctrl_list_t getCtrlList() const; ctrl_list_t getCtrlListSorted() const; void setDefaultTabGroup(S32 d) { mDefaultTabGroup = d; } S32 getDefaultTabGroup() const { return mDefaultTabGroup; } S32 getLastTabGroup() { return mLastTabGroup; } bool trueToRoot(const boost::function& predicate) const; BOOL isInVisibleChain() const; BOOL isInEnabledChain() const; void setFocusRoot(BOOL b) { mIsFocusRoot = b; } BOOL isFocusRoot() const { return mIsFocusRoot; } virtual BOOL canFocusChildren() const; BOOL focusNextRoot(); BOOL focusPrevRoot(); // delete all children. Override this function if you need to // perform any extra clean up such as cached pointers to selected // children, etc. virtual void deleteAllChildren(); virtual void setTentative(BOOL b); virtual BOOL getTentative() const; void setAllChildrenEnabled(BOOL b); virtual void setVisible(BOOL visible); BOOL getVisible() const { return mVisible; } virtual void setEnabled(BOOL enabled); BOOL getEnabled() const { return mEnabled; } U8 getSoundFlags() const { return mSoundFlags; } virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); virtual void handleVisibilityChange ( BOOL new_visibility ); void pushVisible(BOOL visible) { mLastVisible = mVisible; setVisible(visible); } void popVisible() { setVisible(mLastVisible); } LLHandle getHandle() { mHandle.bind(this); return mHandle; } U32 getFollows() const { return mReshapeFlags; } BOOL followsLeft() const { return mReshapeFlags & FOLLOWS_LEFT; } BOOL followsRight() const { return mReshapeFlags & FOLLOWS_RIGHT; } BOOL followsTop() const { return mReshapeFlags & FOLLOWS_TOP; } BOOL followsBottom() const { return mReshapeFlags & FOLLOWS_BOTTOM; } BOOL followsAll() const { return mReshapeFlags & FOLLOWS_ALL; } const LLRect& getRect() const { return mRect; } const LLRect& getBoundingRect() const { return mBoundingRect; } LLRect getLocalBoundingRect() const; LLRect calcScreenRect() const; LLRect calcScreenBoundingRect() const; LLRect getLocalRect() const; virtual LLRect getSnapRect() const; LLRect getLocalSnapRect() const; std::string getLayout() { return mLayout; } // Override and return required size for this object. 0 for width/height means don't care. virtual LLRect getRequiredRect(); LLRect calcBoundingRect(); void updateBoundingRect(); LLView* getRootView(); LLView* getParent() const { return mParentView; } LLView* getFirstChild() const { return (mChildList.empty()) ? NULL : *(mChildList.begin()); } LLView* findPrevSibling(LLView* child); LLView* findNextSibling(LLView* child); S32 getChildCount() const { return (S32)mChildList.size(); } template void sortChildren(_Pr3 _Pred) { mChildList.sort(_Pred); } BOOL hasAncestor(const LLView* parentp) const; BOOL hasChild(const std::string& childname, BOOL recurse = FALSE) const; BOOL childHasKeyboardFocus( const std::string& childname ) const; // these iterators are used for collapsing various tree traversals into for loops typedef LLTreeDFSIter tree_iterator_t; tree_iterator_t beginTreeDFS(); tree_iterator_t endTreeDFS(); typedef LLTreeDFSPostIter tree_post_iterator_t; tree_post_iterator_t beginTreeDFSPost(); tree_post_iterator_t endTreeDFSPost(); typedef LLTreeDownIter root_to_view_iterator_t; root_to_view_iterator_t beginRootToView(); root_to_view_iterator_t endRootToView(); // // UTILITIES // // Default behavior is to use reshape flags to resize child views virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); virtual void translate( S32 x, S32 y ); void setOrigin( S32 x, S32 y ) { mRect.translate( x - mRect.mLeft, y - mRect.mBottom ); } BOOL translateIntoRect( const LLRect& constraint, BOOL allow_partial_outside ); BOOL translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, BOOL allow_partial_outside ); void centerWithin(const LLRect& bounds); void setShape(const LLRect& new_rect, bool by_user = false); virtual LLView* findSnapRect(LLRect& new_rect, const LLCoordGL& mouse_dir, LLView::ESnapType snap_type, S32 threshold, S32 padding = 0); virtual LLView* findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding = 0); virtual BOOL canSnapTo(const LLView* other_view); virtual void setSnappedTo(const LLView* snap_view); // inherited from LLFocusableElement /* virtual */ BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); /* virtual */ BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent); virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, std::string& tooltip_msg); virtual void draw(); void parseFollowsFlags(const LLView::Params& params); // Some widgets, like close box buttons, don't need to be saved BOOL getSaveToXML() const { return mSaveToXML; } void setSaveToXML(BOOL b) { mSaveToXML = b; } typedef enum e_hit_test_type { HIT_TEST_USE_BOUNDING_RECT, HIT_TEST_IGNORE_BOUNDING_RECT }EHitTestType; BOOL parentPointInView(S32 x, S32 y, EHitTestType type = HIT_TEST_USE_BOUNDING_RECT) const; BOOL pointInView(S32 x, S32 y, EHitTestType type = HIT_TEST_USE_BOUNDING_RECT) const; BOOL blockMouseEvent(S32 x, S32 y) const; // See LLMouseHandler virtuals for screenPointToLocal and localPointToScreen BOOL localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, LLView* other_view) const; BOOL localRectToOtherView( const LLRect& local, LLRect* other, LLView* other_view ) const; void screenRectToLocal( const LLRect& screen, LLRect* local ) const; void localRectToScreen( const LLRect& local, LLRect* screen ) const; LLControlVariable *findControl(const std::string& name); // Moved setValue(), getValue(), setControlValue(), setControlName(), // controlListener() to LLUICtrl because an LLView is NOT assumed to // contain a value. If that's what you want, use LLUICtrl instead. // virtual bool handleEvent(LLPointer event, const LLSD& userdata); const child_list_t* getChildList() const { return &mChildList; } const child_list_const_iter_t beginChild() { return mChildList.begin(); } const child_list_const_iter_t endChild() { return mChildList.end(); } // LLMouseHandler functions // Default behavior is to pass events to children /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleMiddleMouseUp(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask); /*virtual*/ std::string getName() const; /*virtual*/ void onMouseCaptureLost(); /*virtual*/ BOOL hasMouseCapture(); /*virtual*/ void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const; /*virtual*/ void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const; virtual LLView* childFromPoint(S32 x, S32 y); // view-specific handlers virtual void onMouseEnter(S32 x, S32 y, MASK mask); virtual void onMouseLeave(S32 x, S32 y, MASK mask); template T* findChild(const std::string& name, BOOL recurse = TRUE) const { LLView* child = findChildView(name, recurse); T* result = dynamic_cast(child); return result; } template T* getChild(const std::string& name, BOOL recurse = TRUE) const; template T& getChildRef(const std::string& name, BOOL recurse = TRUE) const { return *getChild(name, recurse); } virtual LLView* getChildView(const std::string& name, BOOL recurse = TRUE) const; virtual LLView* findChildView(const std::string& name, BOOL recurse = TRUE) const; template T* getDefaultWidget(const std::string& name) const { default_widget_map_t::const_iterator found_it = getDefaultWidgetMap().find(name); if (found_it == getDefaultWidgetMap().end()) { return NULL; } return dynamic_cast(found_it->second); } ////////////////////////////////////////////// // statics ////////////////////////////////////////////// //static LLFontGL::HAlign selectFontHAlign(LLXMLNodePtr node); // focuses the item in the list after the currently-focused item, wrapping if necessary static BOOL focusNext(LLView::child_list_t & result); // focuses the item in the list before the currently-focused item, wrapping if necessary static BOOL focusPrev(LLView::child_list_t & result); // returns query for iterating over controls in tab order static const LLCtrlQuery & getTabOrderQuery(); // return query for iterating over focus roots in tab order static const LLCtrlQuery & getFocusRootsQuery(); static void deleteViewByHandle(LLHandle handle); static LLWindow* getWindow(void) { return LLUI::sWindow; } // Set up params after XML load before calling new(), // usually to adjust layout. static void setupParams(Params& p, LLView* parent); // For re-export of floaters and panels, convert the coordinate system // to be top-left based. static void setupParamsForExport(Params& p, LLView* parent); //virtual BOOL addChildFromParam(const LLInitParam::BaseBlock& params) { return TRUE; } virtual BOOL handleKeyHere(KEY key, MASK mask); virtual BOOL handleUnicodeCharHere(llwchar uni_char); virtual void handleReshape(const LLRect& rect, bool by_user); virtual void dirtyRect(); virtual void notifyParent(const LLSD& info); virtual void notifyChildren(const LLSD& info); static const LLViewDrawContext& getDrawContext(); protected: void drawDebugRect(); void drawChild(LLView* childp, S32 x_offset = 0, S32 y_offset = 0, BOOL force_draw = FALSE); void drawChildren(); LLView* childrenHandleKey(KEY key, MASK mask); LLView* childrenHandleUnicodeChar(llwchar uni_char); LLView* childrenHandleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType type, void* data, EAcceptance* accept, std::string& tooltip_msg); LLView* childrenHandleHover(S32 x, S32 y, MASK mask); LLView* childrenHandleMouseUp(S32 x, S32 y, MASK mask); LLView* childrenHandleMouseDown(S32 x, S32 y, MASK mask); LLView* childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask); LLView* childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask); LLView* childrenHandleDoubleClick(S32 x, S32 y, MASK mask); LLView* childrenHandleScrollWheel(S32 x, S32 y, S32 clicks); LLView* childrenHandleRightMouseDown(S32 x, S32 y, MASK mask); LLView* childrenHandleRightMouseUp(S32 x, S32 y, MASK mask); LLView* childrenHandleToolTip(S32 x, S32 y, MASK mask); ECursorType mHoverCursor; private: LLView* mParentView; child_list_t mChildList; std::string mName; // location in pixels, relative to surrounding structure, bottom,left=0,0 LLRect mRect; LLRect mBoundingRect; std::string mLayout; U32 mReshapeFlags; child_tab_order_t mCtrlOrder; S32 mDefaultTabGroup; S32 mLastTabGroup; BOOL mEnabled; // Enabled means "accepts input that has an effect on the state of the application." // A disabled view, for example, may still have a scrollbar that responds to mouse events. BOOL mMouseOpaque; // Opaque views handle all mouse events that are over their rect. LLUIString mToolTipMsg; // isNull() is true if none. U8 mSoundFlags; BOOL mSaveToXML; BOOL mIsFocusRoot; BOOL mUseBoundingRect; // hit test against bounding rectangle that includes all child elements LLRootHandle mHandle; BOOL mLastVisible; BOOL mVisible; S32 mNextInsertionOrdinal; static LLWindow* sWindow; // All root views must know about their window. typedef std::map default_widget_map_t; // allocate this map no demand, as it is rarely needed mutable default_widget_map_t* mDefaultWidgets; default_widget_map_t& getDefaultWidgetMap() const; public: // Depth in view hierarchy during rendering static S32 sDepth; // Draw debug rectangles around widgets to help with alignment and spacing static bool sDebugRects; // Draw widget names and sizes when drawing debug rectangles, turning this // off is useful to make the rectangles themselves easier to see. static bool sDebugRectsShowNames; static bool sDebugKeys; static bool sDebugMouseHandling; static std::string sMouseHandlerMessage; static S32 sSelectID; static std::set sPreviewHighlightedElements; // DEV-16869 static BOOL sHighlightingDiffs; // DEV-16869 static LLView* sPreviewClickedElement; // DEV-16869 static BOOL sDrawPreviewHighlights; static S32 sLastLeftXML; static S32 sLastBottomXML; static BOOL sForceReshape; }; class LLCompareByTabOrder { public: LLCompareByTabOrder(LLView::child_tab_order_t order) : mTabOrder(order) {} virtual ~LLCompareByTabOrder() {} bool operator() (const LLView* const a, const LLView* const b) const; private: virtual bool compareTabOrders(const LLView::tab_order_t & a, const LLView::tab_order_t & b) const { return a < b; } LLView::child_tab_order_t mTabOrder; }; template T* LLView::getChild(const std::string& name, BOOL recurse) const { LLView* child = findChildView(name, recurse); T* result = dynamic_cast(child); if (!result) { // did we find *something* with that name? if (child) { llwarns << "Found child named " << name << " but of wrong type " << typeid(*child).name() << ", expecting " << typeid(T*).name() << llendl; } result = getDefaultWidget(name); if (!result) { result = LLUICtrlFactory::getDefaultWidget(name); if (result) { // *NOTE: You cannot call mFoo = getChild("bar") // in a floater or panel constructor. The widgets will not // be ready. Instead, put it in postBuild(). llwarns << "Making dummy " << typeid(T).name() << " named \"" << name << "\" in " << getName() << llendl; } else { llwarns << "Failed to create dummy " << typeid(T).name() << llendl; return NULL; } getDefaultWidgetMap()[name] = result; } } return result; } #endif //LL_LLVIEW_H