/** * @file llchiclet.h * @brief LLChiclet class header file * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #ifndef LL_LLCHICLET_H #define LL_LLCHICLET_H #include "llavatariconctrl.h" #include "llbutton.h" #include "llnotifications.h" #include "lltextbox.h" class LLMenuGL; class LLFloaterIMSession; /** * Class for displaying amount of messages/notifications(unread). */ class LLChicletNotificationCounterCtrl : public LLTextBox { public: struct Params : public LLInitParam::Block<Params, LLTextBox::Params> { /** * Contains maximum displayed count of unread messages. Default value is 9. * * If count is less than "max_unread_count" will be displayed as is. * Otherwise 9+ will be shown (for default value). */ Optional<S32> max_displayed_count; Params(); }; /** * Sets number of notifications */ virtual void setCounter(S32 counter); /** * Returns number of notifications */ virtual S32 getCounter() const { return mCounter; } /** * Returns width, required to display amount of notifications in text form. * Width is the only valid value. */ /*virtual*/ LLRect getRequiredRect(); /** * Sets number of notifications using LLSD */ /*virtual*/ void setValue(const LLSD& value); /** * Returns number of notifications wrapped in LLSD */ /*virtual*/ LLSD getValue() const; protected: LLChicletNotificationCounterCtrl(const Params& p); friend class LLUICtrlFactory; private: S32 mCounter; S32 mInitialWidth; S32 mMaxDisplayedCount; }; /** * Class for displaying avatar's icon in P2P chiclet. */ class LLChicletAvatarIconCtrl : public LLAvatarIconCtrl { public: struct Params : public LLInitParam::Block<Params, LLAvatarIconCtrl::Params> { Params() { changeDefault(draw_tooltip, false); changeDefault(mouse_opaque, false); changeDefault(default_icon_name, "Generic_Person"); }; }; protected: LLChicletAvatarIconCtrl(const Params& p); friend class LLUICtrlFactory; }; /** * Class for displaying icon in inventory offer chiclet. */ class LLChicletInvOfferIconCtrl : public LLChicletAvatarIconCtrl { public: struct Params : public LLInitParam::Block<Params, LLChicletAvatarIconCtrl::Params> { Optional<std::string> default_icon; Params() : default_icon("default_icon", "Generic_Object_Small") { changeDefault(avatar_id, LLUUID::null); }; }; /** * Sets icon, if value is LLUUID::null - default icon will be set. */ virtual void setValue(const LLSD& value ); protected: LLChicletInvOfferIconCtrl(const Params& p); friend class LLUICtrlFactory; private: std::string mDefaultIcon; }; /** * Base class for all chiclets. */ class LLChiclet : public LLUICtrl { public: struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> { Optional<bool> show_counter, enable_counter; Params(); }; virtual ~LLChiclet() {} /** * Associates chat session id with chiclet. */ virtual void setSessionId(const LLUUID& session_id) { mSessionId = session_id; } /** * Returns associated chat session. */ virtual const LLUUID& getSessionId() const { return mSessionId; } /** * Sets show counter state. */ virtual void setShowCounter(bool show) { mShowCounter = show; } /** * Connects chiclet clicked event with callback. */ /*virtual*/ boost::signals2::connection setLeftButtonClickCallback( const commit_callback_t& cb); typedef boost::function<void (LLChiclet* ctrl, const LLSD& param)> chiclet_size_changed_callback_t; /** * Connects chiclets size changed event with callback. */ virtual boost::signals2::connection setChicletSizeChangedCallback( const chiclet_size_changed_callback_t& cb); /** * Sets IM Session id using LLSD */ /*virtual*/ LLSD getValue() const; /** * Returns IM Session id using LLSD */ /*virtual*/ void setValue(const LLSD& value); protected: friend class LLUICtrlFactory; LLChiclet(const Params& p); /** * Notifies subscribers about click on chiclet. */ /*virtual*/ bool handleMouseDown(S32 x, S32 y, MASK mask); /** * Notifies subscribers about chiclet size changed event. */ virtual void onChicletSizeChanged(); private: LLUUID mSessionId; bool mShowCounter; typedef boost::signals2::signal<void (LLChiclet* ctrl, const LLSD& param)> chiclet_size_changed_signal_t; chiclet_size_changed_signal_t mChicletSizeChangedSignal; }; /** * Base class for Instant Message chiclets. * IMChiclet displays icon, number of unread messages(optional) * and voice chat status(optional). */ class LLIMChiclet : public LLChiclet { public: enum EType { TYPE_UNKNOWN, TYPE_IM, TYPE_GROUP, TYPE_AD_HOC }; struct Params : public LLInitParam::Block<Params, LLChiclet::Params> {}; virtual ~LLIMChiclet(); /** * It is used for default setting up of chicklet:click handler, etc. */ bool postBuild(); /** * Sets IM session name. This name will be displayed in chiclet tooltip. */ virtual void setIMSessionName(const std::string& name) { setToolTip(name); } /** * Sets id of person/group user is chatting with. * Session id should be set before calling this */ virtual void setOtherParticipantId(const LLUUID& other_participant_id) { mOtherParticipantId = other_participant_id; } /** * Enables/disables the counter control for a chiclet. */ virtual void enableCounterControl(bool enable); /** * Sets required width for a chiclet according to visible controls. */ virtual void setRequiredWidth(); /** * Shows/hides overlay icon concerning new unread messages. */ virtual void setShowNewMessagesIcon(bool show); /** * Returns visibility of overlay icon concerning new unread messages. */ virtual bool getShowNewMessagesIcon(); /** * The action taken on mouse down event. * * Made public so that it can be triggered from outside * (more specifically, from the Active IM window). */ virtual void onMouseDown(); virtual void setToggleState(bool toggle); /** * Displays popup menu. */ virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask); void hidePopupMenu(); protected: LLIMChiclet(const LLIMChiclet::Params& p); protected: /** * Creates chiclet popup menu. */ virtual void createPopupMenu() = 0; /** * Enables/disables menus. */ virtual void updateMenuItems() {}; bool canCreateMenu(); LLHandle<LLUICtrl> mPopupMenuHandle; bool mShowSpeaker; bool mCounterEnabled; /* initial width of chiclet, should not include counter or speaker width */ S32 mDefaultWidth; LLIconCtrl* mNewMessagesIcon; LLButton* mChicletButton; /** the id of another participant, either an avatar id or a group id*/ LLUUID mOtherParticipantId; template<typename Container> struct CollectChicletCombiner { typedef Container result_type; template<typename InputIterator> Container operator()(InputIterator first, InputIterator last) const { Container c = Container(); for (InputIterator iter = first; iter != last; iter++) { if (*iter != NULL) { c.push_back(*iter); } } return c; } }; public: static boost::signals2::signal<LLChiclet* (const LLUUID&), CollectChicletCombiner<std::list<LLChiclet*> > > sFindChicletsSignal; }; /** * Chiclet for script floaters. */ class LLScriptChiclet : public LLIMChiclet { public: struct Params : public LLInitParam::Block<Params, LLIMChiclet::Params> { Optional<LLButton::Params> chiclet_button; Optional<LLIconCtrl::Params> icon; Optional<LLIconCtrl::Params> new_message_icon; Params(); }; /*virtual*/ void setSessionId(const LLUUID& session_id); /** * Toggle script floater */ /*virtual*/ void onMouseDown(); protected: LLScriptChiclet(const Params&); friend class LLUICtrlFactory; /** * Creates chiclet popup menu. */ virtual void createPopupMenu(); /** * Processes clicks on chiclet popup menu. */ virtual void onMenuItemClicked(const LLSD& user_data); private: LLIconCtrl* mChicletIconCtrl; }; /** * Chiclet for inventory offer script floaters. */ class LLInvOfferChiclet: public LLIMChiclet { public: struct Params : public LLInitParam::Block<Params, LLIMChiclet::Params> { Optional<LLButton::Params> chiclet_button; Optional<LLChicletInvOfferIconCtrl::Params> icon; Optional<LLIconCtrl::Params> new_message_icon; Params(); }; /*virtual*/ void setSessionId(const LLUUID& session_id); /** * Toggle script floater */ /*virtual*/ void onMouseDown(); protected: LLInvOfferChiclet(const Params&); friend class LLUICtrlFactory; /** * Creates chiclet popup menu. */ virtual void createPopupMenu(); /** * Processes clicks on chiclet popup menu. */ virtual void onMenuItemClicked(const LLSD& user_data); private: LLChicletInvOfferIconCtrl* mChicletIconCtrl; }; /** * Implements notification chiclet. Used to display total amount of unread messages * across all IM sessions, total amount of system notifications. See EXT-3147 for details */ class LLSysWellChiclet : public LLChiclet { public: struct Params : public LLInitParam::Block<Params, LLChiclet::Params> { Optional<LLButton::Params> button; Optional<LLChicletNotificationCounterCtrl::Params> unread_notifications; /** * Contains maximum displayed count of unread messages. Default value is 9. * * If count is less than "max_unread_count" will be displayed as is. * Otherwise 9+ will be shown (for default value). */ Optional<S32> max_displayed_count; Params(); }; /*virtual*/ void setCounter(S32 counter); // *TODO: mantipov: seems getCounter is not necessary for LLNotificationChiclet // but inherited interface requires it to implement. // Probably it can be safe removed. /*virtual*/S32 getCounter() { return mCounter; } boost::signals2::connection setClickCallback(const commit_callback_t& cb); /*virtual*/ ~LLSysWellChiclet(); void setToggleState(bool toggled); void setNewMessagesState(bool new_messages); //this method should change a widget according to state of the SysWellWindow virtual void updateWidget(bool is_window_empty); protected: LLSysWellChiclet(const Params& p); friend class LLUICtrlFactory; /** * Change Well 'Lit' state from 'Lit' to 'Unlit' and vice-versa. * * There is an assumption that it will be called 2*N times to do not change its start state. * @see FlashToLitTimer */ void changeLitState(bool blink); /** * Displays menu. */ virtual bool handleRightMouseDown(S32 x, S32 y, MASK mask); virtual void createMenu() = 0; protected: class FlashToLitTimer; LLButton* mButton; S32 mCounter; S32 mMaxDisplayedCount; bool mIsNewMessagesState; LLFlashTimer* mFlashToLitTimer; LLHandle<LLContextMenu> mContextMenuHandle; }; class LLNotificationChiclet : public LLSysWellChiclet { LOG_CLASS(LLNotificationChiclet); friend class LLUICtrlFactory; public: struct Params : public LLInitParam::Block<Params, LLSysWellChiclet::Params>{}; protected: class ChicletNotificationChannel : public LLNotificationChannel { public: ChicletNotificationChannel(LLNotificationChiclet* chiclet) : LLNotificationChannel(LLNotificationChannel::Params().filter(filterNotification).name(chiclet->getSessionId().asString())) , mChiclet(chiclet) { // connect counter handlers to the signals connectToChannel("Group Notifications"); connectToChannel("Offer"); connectToChannel("Notifications"); } virtual ~ChicletNotificationChannel() {} static bool filterNotification(LLNotificationPtr notify); // connect counter updaters to the corresponding signals /*virtual*/ void onAdd(LLNotificationPtr p) { mChiclet->setCounter(++mChiclet->mUreadSystemNotifications); } /*virtual*/ void onLoad(LLNotificationPtr p) { mChiclet->setCounter(++mChiclet->mUreadSystemNotifications); } /*virtual*/ void onDelete(LLNotificationPtr p) { mChiclet->setCounter(--mChiclet->mUreadSystemNotifications); } LLNotificationChiclet* const mChiclet; }; boost::scoped_ptr<ChicletNotificationChannel> mNotificationChannel; LLNotificationChiclet(const Params& p); ~LLNotificationChiclet(); /** * Processes clicks on chiclet menu. */ void onMenuItemClicked(const LLSD& user_data); /** * Enables chiclet menu items. */ bool enableMenuItem(const LLSD& user_data); /** * Creates menu. */ /*virtual*/ void createMenu(); /*virtual*/ void setCounter(S32 counter); S32 mUreadSystemNotifications; }; /** * Storage class for all IM chiclets. Provides mechanism to display, * scroll, create, remove chiclets. */ class LLChicletPanel : public LLPanel { public: struct Params : public LLInitParam::Block<Params, LLPanel::Params> { Optional<S32> chiclet_padding, scrolling_offset, scroll_button_hpad, scroll_ratio; Optional<S32> min_width; Params(); }; virtual ~LLChicletPanel(); /** * Creates chiclet and adds it to chiclet list at specified index. */ template<class T> T* createChiclet(const LLUUID& session_id, S32 index); /** * Creates chiclet and adds it to chiclet list at right. */ template<class T> T* createChiclet(const LLUUID& session_id); /** * Returns pointer to chiclet of specified type at specified index. */ template<class T> T* getChiclet(S32 index); /** * Returns pointer to LLChiclet at specified index. */ LLChiclet* getChiclet(S32 index) { return getChiclet<LLChiclet>(index); } /** * Searches a chiclet using IM session id. */ template<class T> T* findChiclet(const LLUUID& im_session_id); /** * Returns number of hosted chiclets. */ S32 getChicletCount() { return static_cast<S32>(mChicletList.size()); } /** * Returns index of chiclet in list. */ S32 getChicletIndex(const LLChiclet* chiclet); /** * Removes chiclet by index. */ void removeChiclet(S32 index); /** * Removes chiclet by pointer. */ void removeChiclet(LLChiclet* chiclet); /** * Removes chiclet by IM session id. */ void removeChiclet(const LLUUID& im_session_id); /** * Removes all chiclets. */ void removeAll(); /** * Scrolls the panel to the specified chiclet */ void scrollToChiclet(const LLChiclet* chiclet); boost::signals2::connection setChicletClickedCallback( const commit_callback_t& cb); /*virtual*/ bool postBuild(); /** * Handler for the Voice Client's signal. Finds a corresponding chiclet and toggles its SpeakerControl */ void onCurrentVoiceChannelChanged(const LLUUID& session_id); /** * Reshapes controls and rearranges chiclets if needed. */ /*virtual*/ void reshape(S32 width, S32 height, bool called_from_parent = true ); /*virtual*/ void draw(); S32 getMinWidth() const { return mMinWidth; } /*virtual*/ S32 notifyParent(const LLSD& info); /** * Toggle chiclet by session id ON and toggle OFF all other chiclets. */ void setChicletToggleState(const LLUUID& session_id, bool toggle); protected: LLChicletPanel(const Params&p); friend class LLUICtrlFactory; /** * Adds chiclet to list and rearranges all chiclets. * They should be right aligned, most recent right. See EXT-1293 * * It calculates position of the first chiclet in the list. Other chiclets are placed in arrange(). * * @see arrange() */ bool addChiclet(LLChiclet*, S32 index); /** * Arranges chiclets to have them in correct positions. * * Method bases on assumption that first chiclet has correct rect and starts from the its position. * * @see addChiclet() */ void arrange(); /** * Returns true if chiclets can be scrolled right. */ bool canScrollRight(); /** * Returns true if we need to show scroll buttons */ bool needShowScroll(); /** * Returns true if chiclets can be scrolled left. */ bool canScrollLeft(); /** * Shows or hides chiclet scroll buttons if chiclets can or can not be scrolled. */ void showScrollButtonsIfNeeded(); /** * Shifts chiclets left or right. */ void shiftChiclets(S32 offset, S32 start_index = 0); /** * Removes gaps between first chiclet and scroll area left side, * last chiclet and scroll area right side. */ void trimChiclets(); /** * Scrolls chiclets to right or left. */ void scroll(S32 offset); /** * Verifies that chiclets can be scrolled left, then calls scroll() */ void scrollLeft(); /** * Verifies that chiclets can be scrolled right, then calls scroll() */ void scrollRight(); /** * Callback for left scroll button clicked */ void onLeftScrollClick(); /** * Callback for right scroll button clicked */ void onRightScrollClick(); /** * Callback for right scroll button held down event */ void onLeftScrollHeldDown(); /** * Callback for left scroll button held down event */ void onRightScrollHeldDown(); /** * Callback for mouse wheel scrolled, calls scrollRight() or scrollLeft() */ bool handleScrollWheel(S32 x, S32 y, S32 clicks); /** * Notifies subscribers about click on chiclet. * Do not place any code here, instead subscribe on event (see setChicletClickedCallback). */ void onChicletClick(LLUICtrl*ctrl,const LLSD¶m); /** * Callback for chiclet size changed event, rearranges chiclets. */ void onChicletSizeChanged(LLChiclet* ctrl, const LLSD& param); void onMessageCountChanged(const LLSD& data); void objectChicletCallback(const LLSD& data); typedef std::vector<LLChiclet*> chiclet_list_t; /** * Removes chiclet from scroll area and chiclet list. */ void removeChiclet(chiclet_list_t::iterator it); S32 getChicletPadding() { return mChicletPadding; } S32 getScrollingOffset() { return mScrollingOffset; } bool isAnyIMFloaterDoked(); protected: chiclet_list_t mChicletList; LLButton* mLeftScrollButton; LLButton* mRightScrollButton; LLPanel* mScrollArea; S32 mChicletPadding; S32 mScrollingOffset; S32 mScrollButtonHPad; S32 mScrollRatio; S32 mMinWidth; bool mShowControls; static const S32 s_scroll_ratio; }; template<class T> T* LLChicletPanel::createChiclet(const LLUUID& session_id, S32 index) { typename T::Params params; T* chiclet = LLUICtrlFactory::create<T>(params); if(!chiclet) { LL_WARNS() << "Could not create chiclet" << LL_ENDL; return NULL; } if(!addChiclet(chiclet, index)) { delete chiclet; LL_WARNS() << "Could not add chiclet to chiclet panel" << LL_ENDL; return NULL; } if (!isAnyIMFloaterDoked()) { scrollToChiclet(chiclet); } chiclet->setSessionId(session_id); return chiclet; } template<class T> T* LLChicletPanel::createChiclet(const LLUUID& session_id) { return createChiclet<T>(session_id, static_cast<S32>(mChicletList.size())); } template<class T> T* LLChicletPanel::findChiclet(const LLUUID& im_session_id) { if(im_session_id.isNull()) { return NULL; } chiclet_list_t::const_iterator it = mChicletList.begin(); for( ; mChicletList.end() != it; ++it) { LLChiclet* chiclet = *it; llassert(chiclet); if (!chiclet) continue; if(chiclet->getSessionId() == im_session_id) { T* result = dynamic_cast<T*>(chiclet); if(!result) { LL_WARNS() << "Found chiclet but of wrong type " << LL_ENDL; continue; } return result; } } return NULL; } template<class T> T* LLChicletPanel::getChiclet(S32 index) { if(index < 0 || index >= getChicletCount()) { return NULL; } LLChiclet* chiclet = mChicletList[index]; T*result = dynamic_cast<T*>(chiclet); if(!result && chiclet) { LL_WARNS() << "Found chiclet but of wrong type " << LL_ENDL; } return result; } #endif // LL_LLCHICLET_H