From 606b381c9fbc43c214afd26fb2e2598eec656b66 Mon Sep 17 00:00:00 2001 From: Steven Bennetts Date: Tue, 29 Sep 2009 19:37:05 +0000 Subject: merge https://svn.aws.productengine.com/secondlife/export-from-ll/viewer-2-0@1830 https://svn.aws.productengine.com/secondlife/pe/stable-2@1839 -> viewer-2.0.0-3 JIRAS: EXT-96 EXT-204 EXT-312 EXT-334 EXT-479 EXT-498 EXT-514 EXT-637 EXT-647 EXT-746 EXT-748 EXT-749 EXT-757 EXT-789 EXT-794 EXT-808 EXT-817 EXT-823 EXT-831 EXT-834 EXT-837 EXT-844 EXT-848 EXT-862 EXT-876 EXT-896 EXT-897 EXT-898 EXT-899 EXT-910 EXT-912 EXT-918 EXT-921 EXT-925 EXT-926 EXT-928 EXT-930 EXT-931 EXT-935 EXT-938 EXT-939 EXT-952 EXT-985 EXT-986 EXT-992 EXT-994 EXT-995 EXT-996 EXT-997 EXT-998 EXT-1001 EXT-1004 EXT-1010 EXT-1012 EXT-1016 EXT-1018 EXT-1020 EXT-1028 EXT-1041 EXT-1044 EXT-1051 EXT-1052 EXT-1061 EXT-1069 EXT-1071 EXT-1074 EXT-1075 EXT-1076 EXT-1078 EXT-1080 EXT-1081 EXT-1082 EXT-1083 EXT-1085 EXT-1092 EXT-1093 EXT-1099 EXT-1100 EXT-1101 EXT-1104 EXT-1106 EXT-1111 EXT-1113 EXT-1114 EXT-1115 EXT-1116 EXT-1118 EXT-1119 EXT-1129 EXT-1132 EXT-1135 EXT-1138 EXT-1142 EXT-1161 EXT-1162 EXT-1178 EXT-1180 * NEW DEVELOPMENT: * EXT-898 - Add dock/undock support for camera and movement controls * Avatar list changes * Bottom bar changes: menu, docking, visibility * Camera changes * Camera & Movement Floaters * Dockable Floaters (LLDockableFloater) * Removed LLListCtrl * Toast / Notification changes: signal / destruction changes, ordering * Nearby chat input should display active voice indicator QA NOTES: * Message Well Window is ready to be tested for regression & matching the spec. * Verify Group List Item L&F * Verify All tabs in People Panel * Verify that Picks behavior is not changed --- indra/newview/CMakeLists.txt | 9 +- indra/newview/app_settings/settings.xml | 50 +- indra/newview/llavataractions.cpp | 59 +++ indra/newview/llavataractions.h | 16 + indra/newview/llavatarlist.cpp | 506 +++++--------------- indra/newview/llavatarlist.h | 89 ++-- indra/newview/llavatarlistitem.cpp | 50 +- indra/newview/llavatarlistitem.h | 18 +- indra/newview/llavatarpropertiesprocessor.cpp | 19 +- indra/newview/llavatarpropertiesprocessor.h | 2 +- indra/newview/llbottomtray.cpp | 78 ++-- indra/newview/llbottomtray.h | 13 +- indra/newview/llchannelmanager.cpp | 126 +++-- indra/newview/llchannelmanager.h | 39 +- indra/newview/llchiclet.cpp | 55 ++- indra/newview/llchiclet.h | 15 +- indra/newview/llexpandabletextbox.cpp | 508 +++++++++++++++++++++ indra/newview/llexpandabletextbox.h | 237 ++++++++++ indra/newview/llfavoritesbar.cpp | 125 ++++- indra/newview/llfavoritesbar.h | 18 +- indra/newview/llfloatercamera.cpp | 91 ++-- indra/newview/llfloatercamera.h | 24 +- indra/newview/llfloaterpreference.cpp | 4 + indra/newview/llgrouplist.cpp | 206 ++++++++- indra/newview/llgrouplist.h | 53 ++- indra/newview/llimfloater.cpp | 416 +++++++++++++++++ indra/newview/llimfloater.h | 112 +++++ indra/newview/llimhandler.cpp | 69 ++- indra/newview/llimpanel.cpp | 316 +------------ indra/newview/llimpanel.h | 64 --- indra/newview/llimview.cpp | 19 +- indra/newview/llimview.h | 3 +- indra/newview/llinventorybridge.cpp | 9 +- indra/newview/lllandmarkactions.cpp | 35 +- indra/newview/lllandmarkactions.h | 14 +- indra/newview/lllocationinputctrl.cpp | 14 +- indra/newview/llmoveview.cpp | 95 +++- indra/newview/llmoveview.h | 12 +- indra/newview/llnearbychat.cpp | 2 +- indra/newview/llnearbychatbar.cpp | 30 ++ indra/newview/llnearbychatbar.h | 2 + indra/newview/llnearbychathandler.cpp | 52 +-- indra/newview/llnearbychathandler.h | 6 +- indra/newview/llnotificationalerthandler.cpp | 53 ++- indra/newview/llnotificationgrouphandler.cpp | 77 ++-- indra/newview/llnotificationhandler.h | 113 +++-- indra/newview/llnotificationmanager.cpp | 26 +- indra/newview/llnotificationmanager.h | 6 +- indra/newview/llnotificationscripthandler.cpp | 135 ++++++ indra/newview/llnotificationtiphandler.cpp | 113 +++++ indra/newview/lloutputmonitorctrl.cpp | 10 +- indra/newview/lloutputmonitorctrl.h | 4 + indra/newview/llpanelavatar.cpp | 3 +- indra/newview/llpanelgroup.cpp | 154 +++++-- indra/newview/llpanelgroup.h | 8 + indra/newview/llpanelgroupgeneral.cpp | 58 +-- indra/newview/llpanelgroupgeneral.h | 1 - indra/newview/llpanelimcontrolpanel.cpp | 3 + indra/newview/llpanelpeople.cpp | 188 +++++--- indra/newview/llpanelpeople.h | 9 +- indra/newview/llpanelpeoplemenus.cpp | 148 ++++++ indra/newview/llpanelpeoplemenus.h | 82 ++++ indra/newview/llpanelpick.cpp | 122 ++++- indra/newview/llpanelpick.h | 21 +- indra/newview/llpanelpicks.cpp | 18 +- indra/newview/llpanelpicks.h | 7 +- indra/newview/llpanelplaceinfo.cpp | 81 ++-- indra/newview/llpanelplaceinfo.h | 8 +- indra/newview/llpanelplaces.cpp | 91 +++- indra/newview/llpanelplaces.h | 3 + indra/newview/llpanelprofile.cpp | 9 - indra/newview/llpanelprofile.h | 7 - indra/newview/llpanelprofileview.cpp | 21 + indra/newview/llpanelprofileview.h | 10 + indra/newview/llpanelteleporthistory.cpp | 245 +++++++--- indra/newview/llpanelteleporthistory.h | 17 +- indra/newview/llrecentpeople.cpp | 19 +- indra/newview/llrecentpeople.h | 4 +- indra/newview/llremoteparcelrequest.cpp | 3 +- indra/newview/llscreenchannel.cpp | 173 +++++-- indra/newview/llscreenchannel.h | 57 ++- indra/newview/llstatusbar.cpp | 40 -- indra/newview/llstatusbar.h | 3 - indra/newview/llsyswellitem.cpp | 11 +- indra/newview/llsyswellitem.h | 6 +- indra/newview/llsyswellwindow.cpp | 400 ++++++++++------ indra/newview/llsyswellwindow.h | 53 ++- indra/newview/lltoast.cpp | 36 +- indra/newview/lltoast.h | 38 +- indra/newview/lltoastimpanel.cpp | 15 +- indra/newview/lltoastimpanel.h | 2 +- indra/newview/lltoastnotifypanel.cpp | 1 + indra/newview/lltooldraganddrop.cpp | 2 + indra/newview/lltooldraganddrop.h | 6 + indra/newview/llviewercontrol.cpp | 37 ++ indra/newview/llviewerfloaterreg.cpp | 2 + indra/newview/llviewermenu.cpp | 1 + indra/newview/llviewermessage.cpp | 1 + indra/newview/llviewerwindow.cpp | 33 +- indra/newview/llviewerwindow.h | 5 + indra/newview/llvoiceclient.cpp | 1 + indra/newview/llvoiceclient.h | 2 +- indra/newview/skins/default/textures/textures.xml | 3 + .../skins/default/xui/en/favorites_bar_button.xml | 10 +- .../skins/default/xui/en/floater_camera.xml | 4 +- .../skins/default/xui/en/floater_im_session.xml | 2 +- .../skins/default/xui/en/floater_moveview.xml | 4 +- .../skins/default/xui/en/floater_sys_well.xml | 33 +- .../skins/default/xui/en/menu_bottomtray.xml | 44 ++ .../skins/default/xui/en/menu_hide_navbar.xml | 20 +- .../xui/en/menu_people_groups_view_sort.xml | 16 + .../skins/default/xui/en/menu_people_nearby.xml | 65 +++ .../xui/en/menu_people_recent_view_sort.xml | 4 +- indra/newview/skins/default/xui/en/menu_viewer.xml | 34 +- .../default/xui/en/panel_avatar_list_item.xml | 2 +- .../skins/default/xui/en/panel_bottomtray.xml | 62 +-- .../default/xui/en/panel_group_info_sidetray.xml | 43 +- .../skins/default/xui/en/panel_group_list_item.xml | 66 +++ .../skins/default/xui/en/panel_instant_message.xml | 12 +- .../skins/default/xui/en/panel_navigation_bar.xml | 1 + .../newview/skins/default/xui/en/panel_people.xml | 8 +- .../skins/default/xui/en/panel_pick_list_item.xml | 2 + indra/newview/skins/default/xui/en/panel_picks.xml | 14 +- .../newview/skins/default/xui/en/panel_profile.xml | 14 +- .../default/xui/en/panel_side_tray_tab_caption.xml | 1 + .../skins/default/xui/en/panel_sys_well_item.xml | 2 +- .../default/xui/en/panel_teleport_history.xml | 120 ++--- .../default/xui/en/panel_teleport_history_item.xml | 65 +++ indra/newview/skins/default/xui/en/strings.xml | 2 +- .../default/xui/en/widgets/expandable_text.xml | 27 ++ .../default/xui/en/widgets/flat_list_view.xml | 3 +- .../newview/skins/default/xui/en/widgets/list.xml | 10 - .../default/xui/en/widgets/location_input.xml | 3 +- .../skins/default/xui/en/widgets/scroll_list.xml | 4 +- 134 files changed, 4970 insertions(+), 2212 deletions(-) create mode 100644 indra/newview/llexpandabletextbox.cpp create mode 100644 indra/newview/llexpandabletextbox.h create mode 100644 indra/newview/llimfloater.cpp create mode 100644 indra/newview/llimfloater.h create mode 100644 indra/newview/llnotificationscripthandler.cpp create mode 100644 indra/newview/llnotificationtiphandler.cpp create mode 100644 indra/newview/llpanelpeoplemenus.cpp create mode 100644 indra/newview/llpanelpeoplemenus.h create mode 100644 indra/newview/skins/default/xui/en/menu_bottomtray.xml create mode 100644 indra/newview/skins/default/xui/en/menu_people_groups_view_sort.xml create mode 100644 indra/newview/skins/default/xui/en/menu_people_nearby.xml create mode 100644 indra/newview/skins/default/xui/en/panel_group_list_item.xml create mode 100644 indra/newview/skins/default/xui/en/panel_teleport_history_item.xml create mode 100644 indra/newview/skins/default/xui/en/widgets/expandable_text.xml delete mode 100644 indra/newview/skins/default/xui/en/widgets/list.xml (limited to 'indra/newview') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 8a59b34332..e4e4d8a2fa 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -128,6 +128,7 @@ set(viewer_SOURCE_FILES lleventinfo.cpp lleventnotifier.cpp lleventpoll.cpp + llexpandabletextbox.cpp llface.cpp llfasttimerview.cpp llfavoritesbar.cpp @@ -233,6 +234,7 @@ set(viewer_SOURCE_FILES llhudrender.cpp llhudtext.cpp llhudview.cpp + llimfloater.cpp llimhandler.cpp llimpanel.cpp llimview.cpp @@ -276,8 +278,9 @@ set(viewer_SOURCE_FILES llnetmap.cpp llnotificationalerthandler.cpp llnotificationgrouphandler.cpp - llnotificationinfohandler.cpp llnotificationmanager.cpp + llnotificationscripthandler.cpp + llnotificationtiphandler.cpp llnotify.cpp lloutputmonitorctrl.cpp lloverlaybar.cpp @@ -320,6 +323,7 @@ set(viewer_SOURCE_FILES llpanelmovetip.cpp llpanelobject.cpp llpanelpeople.cpp + llpanelpeoplemenus.cpp llpanelpermissions.cpp llpanelpick.cpp llpanelpicks.cpp @@ -587,6 +591,7 @@ set(viewer_HEADER_FILES lleventinfo.h lleventnotifier.h lleventpoll.h + llexpandabletextbox.h llface.h llfasttimerview.h llfavoritesbar.h @@ -692,6 +697,7 @@ set(viewer_HEADER_FILES llhudrender.h llhudtext.h llhudview.h + llimfloater.h llimpanel.h llimview.h llimcontrolpanel.h @@ -777,6 +783,7 @@ set(viewer_HEADER_FILES llpanelmovetip.h llpanelobject.h llpanelpeople.h + llpanelpeoplemenus.h llpanelpermissions.h llpanelpick.h llpanelpicks.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index c794d7d319..19d503390c 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4884,18 +4884,7 @@ Type S32 Value - 10 - - NavBarMargin - - Comment - Width of notification messages - Persist - 1 - Type - S32 - Value - 60 + 35 OverflowToastHeight @@ -7520,10 +7509,10 @@ Value 1 - ShowCameraAndMoveControls + ShowCameraButton Comment - Show/Hide Camera and Move controls in the bottom tray + Show/Hide Camera button in the bottom tray Persist 1 Type @@ -7531,6 +7520,28 @@ Value 1 + ShowMoveButton + + Comment + Show/Hide Move button in the bottom tray + Persist + 1 + Type + Boolean + Value + 1 + + ShowGestureButton + + Comment + Show/Hide Gesture button in the bottom tray + Persist + 1 + Type + Boolean + Value + 1 + ShowNavbarFavoritesPanel Comment @@ -7553,6 +7564,17 @@ Value 1 + GroupListShowIcons + + Comment + Show/hide group icons in the group list + Persist + 1 + Type + Boolean + Value + 1 + ShowPGSearchAll Comment diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 4819703e72..1676bb1d44 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -43,8 +43,11 @@ #include "llappviewer.h" // for gLastVersionChannel #include "llcachename.h" #include "llcallingcard.h" // for LLAvatarTracker +#include "llgivemoney.h" // foe LLFloaterPay #include "llinventorymodel.h" // for gInventory.findCategoryUUIDForType #include "llimview.h" // for gIMMgr +#include "llmutelist.h" +#include "llrecentpeople.h" #include "llsidetray.h" #include "llviewermessage.h" // for handle_lure #include "llviewerregion.h" @@ -206,6 +209,41 @@ void LLAvatarActions::showProfile(const LLUUID& id) } } +// static +void LLAvatarActions::pay(const LLUUID& id) +{ + LLNotification::Params params("BusyModePay"); + params.functor.function(boost::bind(&LLAvatarActions::handlePay, _1, _2, id)); + + if (gAgent.getBusy()) + { + // warn users of being in busy mode during a transaction + LLNotifications::instance().add(params); + } + else + { + LLNotifications::instance().forceResponse(params, 1); + } +} + +// static +void LLAvatarActions::toggleBlock(const LLUUID& id) +{ + std::string name; + + gCacheName->getFullName(id, name); + LLMute mute(id, name, LLMute::AGENT); + + if (LLMuteList::getInstance()->isMuted(mute.mID, mute.mName)) + { + LLMuteList::getInstance()->remove(mute); + } + else + { + LLMuteList::getInstance()->add(mute); + } +} + //== private methods ======================================================================================== // static @@ -242,6 +280,19 @@ bool LLAvatarActions::handleRemove(const LLSD& notification, const LLSD& respons return false; } +// static +bool LLAvatarActions::handlePay(const LLSD& notification, const LLSD& response, LLUUID avatar_id) +{ + S32 option = LLNotification::getSelectedOption(notification, response); + if (option == 0) + { + gAgent.clearBusy(); + } + + LLFloaterPay::payDirectly(&give_money, avatar_id, /*is_group=*/FALSE); + return false; +} + // static bool LLAvatarActions::callbackAddFriendWithMessage(const LLSD& notification, const LLSD& response) { @@ -290,3 +341,11 @@ bool LLAvatarActions::isFriend(const LLUUID& id) { return ( NULL != LLAvatarTracker::instance().getBuddyInfo(id) ); } + +// static +bool LLAvatarActions::isBlocked(const LLUUID& id) +{ + std::string name; + gCacheName->getFullName(id, name); + return LLMuteList::getInstance()->isMuted(id, name); +} diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h index f3c411e033..e911715c70 100644 --- a/indra/newview/llavataractions.h +++ b/indra/newview/llavataractions.h @@ -76,15 +76,31 @@ public: */ static void showProfile(const LLUUID& id); + /** + * Give money to the avatar. + */ + static void pay(const LLUUID& id); + + /** + * Block/unblock the avatar. + */ + static void toggleBlock(const LLUUID& id); + /** * Return true if avatar with "id" is a friend */ static bool isFriend(const LLUUID& id); + /** + * @return true if the avatar is blocked + */ + static bool isBlocked(const LLUUID& id); + private: static bool callbackAddFriend(const LLSD& notification, const LLSD& response); static bool callbackAddFriendWithMessage(const LLSD& notification, const LLSD& response); static bool handleRemove(const LLSD& notification, const LLSD& response); + static bool handlePay(const LLSD& notification, const LLSD& response, LLUUID avatar_id); // Just request friendship, no dialog. static void requestFriendship(const LLUUID& target_id, const std::string& target_name, const std::string& message); diff --git a/indra/newview/llavatarlist.cpp b/indra/newview/llavatarlist.cpp index 2e64c10bb2..ee14a2ff86 100644 --- a/indra/newview/llavatarlist.cpp +++ b/indra/newview/llavatarlist.cpp @@ -37,144 +37,9 @@ // newview #include "llcallingcard.h" // for LLAvatarTracker #include "llcachename.h" -#include "lloutputmonitorctrl.h" #include "llvoiceclient.h" static LLDefaultChildRegistry::Register r("avatar_list"); -static LLDefaultChildRegistry::Register r_tmp("avatar_list_tmp"); - -static const std::string COMMENT_TEXTBOX = "comment_text"; - -LLAvatarList::Params::Params() -: - volume_column_width("volume_column_width", 0) - , online_go_first("online_go_first", true) -{ - draw_heading = true; - draw_stripes = false; - multi_select = false; - column_padding = 0; - search_column = COL_NAME; - sort_column = COL_NAME; -} - -LLAvatarList::LLAvatarList(const Params& p) -: LLScrollListCtrl(p) - , mHaveVolumeColumn(p.volume_column_width > 0) - , mOnlineGoFirst(p.online_go_first) -{ - setCommitOnSelectionChange(TRUE); // there's no such param in LLScrollListCtrl::Params - - // display a context menu appropriate for a list of avatar names - setContextMenu(LLScrollListCtrl::MENU_AVATAR); - - // "volume" column - { - LLScrollListColumn::Params col_params; - col_params.name = "volume"; - col_params.header.label = "Volume"; // *TODO: localize or remove the header - col_params.width.pixel_width = p.volume_column_width; - addColumn(col_params); - } - - // "name" column - { - LLScrollListColumn::Params col_params; - col_params.name = "name"; - col_params.header.label = "Name"; // *TODO: localize or remove the header - col_params.width.dynamic_width = true; - addColumn(col_params); - } - - // "online status" column - { - LLScrollListColumn::Params col_params; - col_params.name = "online"; - col_params.header.label = "Online"; // *TODO: localize or remove the header - col_params.width.pixel_width = 0; // invisible column - addColumn(col_params); - } - - - // invisible "id" column - { - LLScrollListColumn::Params col_params; - col_params.name = "id"; - col_params.header.label = "ID"; // *TODO: localize or remove the header - col_params.width.pixel_width = 0; - addColumn(col_params); - } - - // Primary sort = online status, secondary sort = name - // The corresponding parameters don't work because we create columns dynamically. - sortByColumnIndex(COL_NAME, TRUE); - if (mOnlineGoFirst) - sortByColumnIndex(COL_ONLINE, FALSE); - setSearchColumn(COL_NAME); -} - -// virtual -void LLAvatarList::draw() -{ - LLScrollListCtrl::draw(); - if (mHaveVolumeColumn) - { - updateVolume(); - } -} - -std::vector LLAvatarList::getSelectedIDs() -{ - LLUUID selected_id; - std::vector avatar_ids; - std::vector selected = getAllSelected(); - for(std::vector::iterator itr = selected.begin(); itr != selected.end(); ++itr) - { - avatar_ids.push_back((*itr)->getUUID()); - } - return avatar_ids; -} - -void LLAvatarList::addItem(const LLUUID& id, const std::string& name, BOOL is_bold, EAddPosition pos) -{ - std::string fullname; - - // Populate list item. - LLSD element; - element["id"] = id; - - // Update volume column (if we have one) - { - std::string icon = mHaveVolumeColumn ? getVolumeIcon(id) : ""; - LLSD& volume_column = element["columns"][COL_VOLUME]; - volume_column["column"] = "volume"; - volume_column["type"] = "icon"; - volume_column["value"] = icon; - } - - LLSD& friend_column = element["columns"][COL_NAME]; - friend_column["column"] = "name"; - friend_column["value"] = name; - - LLSD& online_column = element["columns"][COL_ONLINE]; - online_column["column"] = "online"; - online_column["value"] = is_bold ? "1" : "0"; - - LLScrollListItem* new_itemp = addElement(element, pos); - - // Indicate buddy online status. - // (looks like parsing font parameters from LLSD is broken) - if (is_bold) - { - LLScrollListText* name_textp = dynamic_cast(new_itemp->getColumn(COL_NAME)); - if (name_textp) - name_textp->setFontStyle(LLFontGL::BOLD); - else - { - llwarns << "Name column not found" << llendl; - } - } -} static bool findInsensitive(std::string haystack, const std::string& needle_upper) { @@ -182,136 +47,12 @@ static bool findInsensitive(std::string haystack, const std::string& needle_uppe return haystack.find(needle_upper) != std::string::npos; } -BOOL LLAvatarList::update(const std::vector& all_buddies, const std::string& name_filter) -{ - BOOL have_names = TRUE; - - // Save selection. - std::vector selected_ids = getSelectedIDs(); - LLUUID current_id = getCurrentID(); - S32 pos = getScrollPos(); - - std::vector::const_iterator buddy_it = all_buddies.begin(); - deleteAllItems(); - for(; buddy_it != all_buddies.end(); ++buddy_it) - { - std::string name; - const LLUUID& buddy_id = *buddy_it; - have_names &= gCacheName->getFullName(buddy_id, name); - if (name_filter != LLStringUtil::null && !findInsensitive(name, name_filter)) - continue; - addItem(buddy_id, name, LLAvatarTracker::instance().isBuddyOnline(buddy_id)); - } - - // Changed item in place, need to request sort and update columns - // because we might have changed data in a column on which the user - // has already sorted. JC - updateSort(); - - // re-select items - selectMultiple(selected_ids); - setCurrentByID(current_id); -#if 0 - // Restore selection. - if(selected_ids.size() > 0) - { - // only non-null if friends was already found. This may fail, - // but we don't really care here, because refreshUI() will - // clean up the interface. - for(std::vector::iterator itr = selected_ids.begin(); itr != selected_ids.end(); ++itr) - { - setSelectedByValue(*itr, true); - } - } -#endif - setScrollPos(pos); - - updateLineHeight(); - LLRect rect = getRequiredRect(); - - LLSD params; - params["action"] = "size_changes"; - params["width"] = rect.getWidth(); - params["height"] = llmax(rect.getHeight(),20) + 5; - - getParent()->notifyParent(params); - - return have_names; -} -// static -std::string LLAvatarList::getVolumeIcon(const LLUUID& id) -{ - // - // Determine icon appropriate for the current avatar volume. - // - // *TODO: remove this in favor of LLOutputMonitorCtrl - // when ListView widget is implemented - // which is capable of containing arbitrary widgets. - // - static LLOutputMonitorCtrl::Params default_monitor_params(LLUICtrlFactory::getDefaultParams()); - bool muted = gVoiceClient->getIsModeratorMuted(id) || gVoiceClient->getOnMuteList(id); - F32 power = gVoiceClient->getCurrentPower(id); - std::string icon; - - if (muted) - { - icon = default_monitor_params.image_mute.name; - } - else if (power == 0.f) - { - icon = default_monitor_params.image_off.name; - } - else if (power < LLVoiceClient::OVERDRIVEN_POWER_LEVEL) - { - S32 icon_image_idx = llmin(2, llfloor((power / LLVoiceClient::OVERDRIVEN_POWER_LEVEL) * 3.f)); - switch(icon_image_idx) - { - default: - case 0: - icon = default_monitor_params.image_on.name; - break; - case 1: - icon = default_monitor_params.image_level_1.name; - break; - case 2: - icon = default_monitor_params.image_level_2.name; - break; - } - } - else - { - // overdriven - icon = default_monitor_params.image_level_3.name; - } - - return icon; -} - -// Update volume column for all list rows. -void LLAvatarList::updateVolume() -{ - item_list& items = getItemList(); +//comparators +static const LLAvatarItemNameComparator NAME_COMPARATOR; +static const LLFlatListView::ItemReverseComparator REVERSE_NAME_COMPARATOR(NAME_COMPARATOR); - for (item_list::iterator item_it = items.begin(); - item_it != items.end(); - ++item_it) - { - LLScrollListItem* itemp = (*item_it); - LLUUID speaker_id = itemp->getUUID(); - - LLScrollListCell* icon_cell = itemp->getColumn(COL_VOLUME); - if (icon_cell) - icon_cell->setValue(getVolumeIcon(speaker_id)); - } -} - - - - -#include "llavatarlistitem.h" - -LLAvatarListTmp::Params::Params() +LLAvatarList::Params::Params() : volume_column_width("volume_column_width", 0) , online_go_first("online_go_first", true) @@ -320,198 +61,163 @@ volume_column_width("volume_column_width", 0) -LLAvatarListTmp::LLAvatarListTmp(const Params& p) +LLAvatarList::LLAvatarList(const Params& p) : LLFlatListView(p) -, mHaveVolumeColumn(p.volume_column_width > 0) , mOnlineGoFirst(p.online_go_first) +, mContextMenu(NULL) { - LLRect item_list_rect = getLocalRect(); - item_list_rect.stretch( -getBorderWidth()); - - LLTextBox::Params text_p; - text_p.name(COMMENT_TEXTBOX); - text_p.border_visible(false); - text_p.rect(item_list_rect); - text_p.follows.flags(FOLLOWS_ALL); - addChild(LLUICtrlFactory::create(text_p)); -} + setCommitOnSelectionChange(true); -// virtual -void LLAvatarListTmp::draw() -{ - LLFlatListView::draw(); - if (mHaveVolumeColumn) - { - updateVolume(); - } + // Set default sort order. + setComparator(&NAME_COMPARATOR); } -std::vector LLAvatarListTmp::getSelectedIDs() +void LLAvatarList::computeDifference( + const std::vector& vnew_unsorted, + std::vector& vadded, + std::vector& vremoved) { - LLUUID selected_id; - std::vector avatar_ids; + std::vector vcur; + std::vector vnew = vnew_unsorted; - getSelectedUUIDs(avatar_ids); + // Convert LLSDs to LLUUIDs. + { + std::vector vcur_values; + getValues(vcur_values); - return avatar_ids; -} + for (size_t i=0; ishowStatus(true); - item->showInfoBtn(true); - item->showSpeakingIndicator(true); - item->setName(name); - item->setAvatarId(id); + std::sort(vcur.begin(), vcur.end()); + std::sort(vnew.begin(), vnew.end()); - item->childSetVisible("info_btn", false); + std::vector::iterator it; + size_t maxsize = llmax(vcur.size(), vnew.size()); + vadded.resize(maxsize); + vremoved.resize(maxsize); - addItem(item, id, pos); + // what to remove + it = set_difference(vcur.begin(), vcur.end(), vnew.begin(), vnew.end(), vremoved.begin()); + vremoved.erase(it, vremoved.end()); - setCommentVisible(false); + // what to add + it = set_difference(vnew.begin(), vnew.end(), vcur.begin(), vcur.end(), vadded.begin()); + vadded.erase(it, vadded.end()); } -BOOL LLAvatarListTmp::update(const std::vector& all_buddies, const std::string& name_filter) +BOOL LLAvatarList::update(const std::vector& all_buddies, const std::string& name_filter) { BOOL have_names = TRUE; + bool have_filter = name_filter != LLStringUtil::null; // Save selection. - std::vector selected_ids = getSelectedIDs(); + std::vector selected_ids; + getSelectedUUIDs(selected_ids); LLUUID current_id = getSelectedUUID(); - LLRect pos = getScrolledViewRect(); - std::vector::const_iterator buddy_it = all_buddies.begin(); - clear(); - for(; buddy_it != all_buddies.end(); ++buddy_it) + // Determine what to add and what to remove. + std::vector added, removed; + LLAvatarList::computeDifference(all_buddies, added, removed); + + // Handle added items. + for (std::vector::const_iterator it=added.begin(); it != added.end(); it++) { std::string name; - const LLUUID& buddy_id = *buddy_it; + const LLUUID& buddy_id = *it; have_names &= gCacheName->getFullName(buddy_id, name); - if (name_filter != LLStringUtil::null && !findInsensitive(name, name_filter)) - continue; + if (!have_filter || findInsensitive(name, name_filter)) addNewItem(buddy_id, name, LLAvatarTracker::instance().isBuddyOnline(buddy_id)); } + // Handle removed items. + for (std::vector::const_iterator it=removed.begin(); it != removed.end(); it++) + { + removeItemByUUID(*it); + } + + // Handle filter. + if (have_filter) + { + std::vector cur_values; + getValues(cur_values); + + for (std::vector::const_iterator it=cur_values.begin(); it != cur_values.end(); it++) + { + std::string name; + const LLUUID& buddy_id = it->asUUID(); + have_names &= gCacheName->getFullName(buddy_id, name); + if (!findInsensitive(name, name_filter)) + removeItemByUUID(buddy_id); + } + } + // Changed item in place, need to request sort and update columns // because we might have changed data in a column on which the user // has already sorted. JC - // updateSort(); // TODO: implement sorting + sort(); // re-select items // selectMultiple(selected_ids); // TODO: implement in LLFlatListView if need selectItemByUUID(current_id); - scrollToShowRect(pos); - - - setCommentVisible(false); - - return have_names; -} - - -const LLUUID LLAvatarListTmp::getCurrentID() const -{ - return getSelectedUUID(); + // If the name filter is specified and the names are incomplete, + // we need to re-update when the names are complete so that + // the filter can be applied correctly. + // + // Otherwise, if we have no filter then no need to update again + // because the items will update their names. + return !have_filter || have_names; } -void LLAvatarListTmp::setCommentText(const std::string& comment_text) +void LLAvatarList::sortByName() { - getChild(COMMENT_TEXTBOX)->setValue(comment_text); + setComparator(&NAME_COMPARATOR); + sort(); } - ////////////////////////////////////////////////////////////////////////// // PROTECTED SECTION ////////////////////////////////////////////////////////////////////////// - -// virtual overridden -bool LLAvatarListTmp::removeItemPair(item_pair_t* item_pair) +void LLAvatarList::addNewItem(const LLUUID& id, const std::string& name, BOOL is_bold, EAddPosition pos) { - bool removed = LLFlatListView::removeItemPair(item_pair); - setCommentVisible(size() == 0); - return removed; + LLAvatarListItem* item = new LLAvatarListItem(); + item->showStatus(false); + item->showInfoBtn(true); + item->showSpeakingIndicator(true); + item->setName(name); + item->setAvatarId(id); + item->setContextMenu(mContextMenu); + + item->childSetVisible("info_btn", false); + + addItem(item, id, pos); } -////////////////////////////////////////////////////////////////////////// -// PRIVATE SECTION -////////////////////////////////////////////////////////////////////////// -// static -std::string LLAvatarListTmp::getVolumeIcon(const LLUUID& id) -{ - // - // Determine icon appropriate for the current avatar volume. - // - // *TODO: remove this in favor of LLOutputMonitorCtrl - // when ListView widget is implemented - // which is capable of containing arbitrary widgets. - // - static LLOutputMonitorCtrl::Params default_monitor_params(LLUICtrlFactory::getDefaultParams()); - bool muted = gVoiceClient->getIsModeratorMuted(id) || gVoiceClient->getOnMuteList(id); - F32 power = gVoiceClient->getCurrentPower(id); - std::string icon; - if (muted) - { - icon = default_monitor_params.image_mute.name; - } - else if (power == 0.f) - { - icon = default_monitor_params.image_off.name; - } - else if (power < LLVoiceClient::OVERDRIVEN_POWER_LEVEL) - { - S32 icon_image_idx = llmin(2, llfloor((power / LLVoiceClient::OVERDRIVEN_POWER_LEVEL) * 3.f)); - switch(icon_image_idx) - { - default: - case 0: - icon = default_monitor_params.image_on.name; - break; - case 1: - icon = default_monitor_params.image_level_1.name; - break; - case 2: - icon = default_monitor_params.image_level_2.name; - break; - } - } - else +bool LLAvatarItemComparator::compare(const LLPanel* item1, const LLPanel* item2) const +{ + const LLAvatarListItem* avatar_item1 = dynamic_cast(item1); + const LLAvatarListItem* avatar_item2 = dynamic_cast(item2); + + if (!avatar_item1 || !avatar_item2) { - // overdriven - icon = default_monitor_params.image_level_3.name; + llerror("item1 and item2 cannot be null", 0); + return true; } - return icon; + return doCompare(avatar_item1, avatar_item2); } -// Update volume column for all list rows. -void LLAvatarListTmp::updateVolume() +bool LLAvatarItemNameComparator::doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const { - // TODO: implement via Listener - /* - item_list& items = getItemList(); + std::string name1 = avatar_item1->getAvatarName(); + std::string name2 = avatar_item2->getAvatarName(); - for (item_list::iterator item_it = items.begin(); - item_it != items.end(); - ++item_it) - { - LLScrollListItem* itemp = (*item_it); - LLUUID speaker_id = itemp->getUUID(); + LLStringUtil::toUpper(name1); + LLStringUtil::toUpper(name2); - LLScrollListCell* icon_cell = itemp->getColumn(COL_VOLUME); - if (icon_cell) - icon_cell->setValue(getVolumeIcon(speaker_id)); - } - */ + return name1 < name2; } - -void LLAvatarListTmp::setCommentVisible(bool visible) const -{ - getChildView(COMMENT_TEXTBOX)->setVisible(visible); -} - -// EOF diff --git a/indra/newview/llavatarlist.h b/indra/newview/llavatarlist.h index 639ed83ada..8d79e073d2 100644 --- a/indra/newview/llavatarlist.h +++ b/indra/newview/llavatarlist.h @@ -33,88 +33,77 @@ #ifndef LL_LLAVATARLIST_H #define LL_LLAVATARLIST_H -#include +#include "llflatlistview.h" + +#include "llavatarlistitem.h" -// *TODO: derive from ListView when it's ready. -class LLAvatarList : public LLScrollListCtrl +class LLAvatarList : public LLFlatListView { LOG_CLASS(LLAvatarList); public: - struct Params : public LLInitParam::Block + struct Params : public LLInitParam::Block { Optional volume_column_width; Optional online_go_first; Params(); }; - enum EColumnOrder - { - COL_VOLUME, - COL_NAME, - COL_ONLINE, - COL_ID, - }; - LLAvatarList(const Params&); virtual ~LLAvatarList() {} - /*virtual*/ void draw(); - BOOL update(const std::vector& all_buddies, const std::string& name_filter = LLStringUtil::null); + void setContextMenu(LLAvatarListItem::ContextMenu* menu) { mContextMenu = menu; } + + void sortByName(); + protected: - std::vector getSelectedIDs(); - void addItem(const LLUUID& id, const std::string& name, BOOL is_bold, EAddPosition pos = ADD_BOTTOM); + void addNewItem(const LLUUID& id, const std::string& name, BOOL is_bold, EAddPosition pos = ADD_BOTTOM); + void computeDifference( + const std::vector& vnew, + std::vector& vadded, + std::vector& vremoved); private: - static std::string getVolumeIcon(const LLUUID& id); /// determine volume icon from current avatar volume - void updateVolume(); // update volume for all avatars - bool mHaveVolumeColumn; bool mOnlineGoFirst; -}; - -#include "llflatlistview.h" + LLAvatarListItem::ContextMenu* mContextMenu; +}; -class LLAvatarListTmp : public LLFlatListView +/** Abstract comparator for avatar items */ +class LLAvatarItemComparator : public LLFlatListView::ItemComparator { - LOG_CLASS(LLAvatarListTmp); -public: - struct Params : public LLInitParam::Block - { - Optional volume_column_width; - Optional online_go_first; - Params(); - }; - - LLAvatarListTmp(const Params&); - virtual ~LLAvatarListTmp() {} - - /*virtual*/ void draw(); + LOG_CLASS(LLAvatarItemComparator); - BOOL update(const std::vector& all_buddies, - const std::string& name_filter = LLStringUtil::null); +public: + LLAvatarItemComparator() {}; + virtual ~LLAvatarItemComparator() {}; - const LLUUID getCurrentID() const; - void setCommentText( const std::string& comment_text); + virtual bool compare(const LLPanel* item1, const LLPanel* item2) const; protected: - std::vector getSelectedIDs(); - void addNewItem(const LLUUID& id, const std::string& name, BOOL is_bold, EAddPosition pos = ADD_BOTTOM); - /*virtual*/ bool removeItemPair(item_pair_t* item_pair); -private: - static std::string getVolumeIcon(const LLUUID& id); /// determine volume icon from current avatar volume - void updateVolume(); // update volume for all avatars - void setCommentVisible(bool visible) const; + /** + * Returns true if avatar_item1 < avatar_item2, false otherwise + * Implement this method in your particular comparator. + * In Linux a compiler failed to build it using the name "compare", so it was renamed to doCompare + */ + virtual bool doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const = 0; +}; - bool mHaveVolumeColumn; - bool mOnlineGoFirst; -}; +class LLAvatarItemNameComparator : public LLAvatarItemComparator +{ + LOG_CLASS(LLAvatarItemNameComparator); +public: + LLAvatarItemNameComparator() {}; + virtual ~LLAvatarItemNameComparator() {}; +protected: + virtual bool doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const; +}; #endif // LL_LLAVATARLIST_H diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp index feae8202bc..665dffc8c6 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -48,7 +48,9 @@ LLAvatarListItem::LLAvatarListItem() mAvatarName(NULL), mStatus(NULL), mSpeakingIndicator(NULL), - mInfoBtn(NULL) + mInfoBtn(NULL), + mContextMenu(NULL), + mAvatarId(LLUUID::null) { LLUICtrlFactory::getInstance()->buildPanel(this, "panel_avatar_list_item.xml"); } @@ -114,6 +116,15 @@ void LLAvatarListItem::onMouseLeave(S32 x, S32 y, MASK mask) LLPanel::onMouseLeave(x, y, mask); } +// virtual +BOOL LLAvatarListItem::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + if (mContextMenu) + mContextMenu->show(this, const_cast(mAvatarId), x, y); + + return LLPanel::handleRightMouseDown(x, y, mask); +} + void LLAvatarListItem::setStatus(const std::string& status) { mStatus->setValue(status); @@ -127,13 +138,17 @@ void LLAvatarListItem::setName(const std::string& name) void LLAvatarListItem::setAvatarId(const LLUUID& id) { + mAvatarId = id; mAvatarIcon->setValue(id); mSpeakingIndicator->setSpeakerId(id); + + // Set avatar name. + gCacheName->get(id, FALSE, boost::bind(&LLAvatarListItem::onNameCache, this, _2, _3)); } void LLAvatarListItem::onInfoBtnClick() { - LLFloaterReg::showInstance("inspect_avatar", mAvatarIcon->getValue()); + LLFloaterReg::showInstance("inspect_avatar", mAvatarId); /* TODO fix positioning of inspector localPointToScreen(mXPos, mYPos, &mXPos, &mYPos); @@ -156,6 +171,21 @@ void LLAvatarListItem::onInfoBtnClick() */ } +void LLAvatarListItem::showStatus(bool show_status) +{ + // *HACK: dirty hack until we can determine correct avatar status (EXT-1076). + + if (show_status) + return; + + LLRect name_rect = mAvatarName->getRect(); + LLRect status_rect = mStatus->getRect(); + + mStatus->setVisible(show_status); + name_rect.mRight += (status_rect.mRight - name_rect.mRight); + mAvatarName->setRect(name_rect); +} + void LLAvatarListItem::setValue( const LLSD& value ) { if (!value.isMap()) return;; @@ -163,3 +193,19 @@ void LLAvatarListItem::setValue( const LLSD& value ) childSetVisible("selected_icon", value["selected"]); } +const LLUUID& LLAvatarListItem::getAvatarId() const +{ + return mAvatarId; +} + +const std::string LLAvatarListItem::getAvatarName() const +{ + return mAvatarName->getValue(); +} + +void LLAvatarListItem::onNameCache(const std::string& first_name, const std::string& last_name) +{ + std::string name = first_name + " " + last_name; + mAvatarName->setValue(name); + mAvatarName->setToolTip(name); +} diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h index dc5606e4c2..bde9250e4a 100644 --- a/indra/newview/llavatarlistitem.h +++ b/indra/newview/llavatarlistitem.h @@ -43,32 +43,48 @@ class LLAvatarIconCtrl; class LLAvatarListItem : public LLPanel { public: + class ContextMenu + { + public: + virtual void show(LLView* spawning_view, const LLUUID& id, S32 x, S32 y) = 0; + }; + LLAvatarListItem(); virtual ~LLAvatarListItem() {}; virtual BOOL postBuild(); virtual void onMouseLeave(S32 x, S32 y, MASK mask); virtual void onMouseEnter(S32 x, S32 y, MASK mask); + virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); virtual void setValue(const LLSD& value); void setStatus(const std::string& status); void setName(const std::string& name); void setAvatarId(const LLUUID& id); + + const LLUUID& getAvatarId() const; + const std::string getAvatarName() const; void onInfoBtnClick(); void showSpeakingIndicator(bool show) { mSpeakingIndicator->setVisible(show); } void showInfoBtn(bool show_info_btn) {mInfoBtn->setVisible(show_info_btn); } - void showStatus(bool show_status) {mStatus->setVisible(show_status); } + void showStatus(bool show_status); + void setContextMenu(ContextMenu* menu) { mContextMenu = menu; } private: + void onNameCache(const std::string& first_name, const std::string& last_name); + LLAvatarIconCtrl*mAvatarIcon; LLTextBox* mAvatarName; LLTextBox* mStatus; LLOutputMonitorCtrl* mSpeakingIndicator; LLButton* mInfoBtn; + ContextMenu* mContextMenu; + + LLUUID mAvatarId; }; #endif //LL_LLAVATARLISTITEM_H diff --git a/indra/newview/llavatarpropertiesprocessor.cpp b/indra/newview/llavatarpropertiesprocessor.cpp index edf6e84b68..e568b9c526 100644 --- a/indra/newview/llavatarpropertiesprocessor.cpp +++ b/indra/newview/llavatarpropertiesprocessor.cpp @@ -428,27 +428,10 @@ void LLAvatarPropertiesProcessor::processPickInfoReply(LLMessageSystem* msg, voi msg->getString(_PREHASH_Data, _PREHASH_Desc, pick_data.desc); msg->getUUID(_PREHASH_Data, _PREHASH_SnapshotID, pick_data.snapshot_id); - // "Location text" is actually the owner name, the original - // name that owner gave the parcel, and the location. - msg->getString(_PREHASH_Data, _PREHASH_User, pick_data.location_text); - pick_data.location_text.append(", "); - + msg->getString(_PREHASH_Data, _PREHASH_User, pick_data.user_name); msg->getString(_PREHASH_Data, _PREHASH_OriginalName, pick_data.original_name); - if (!pick_data.original_name.empty()) - { - pick_data.location_text.append(pick_data.original_name); - pick_data.location_text.append(", "); - } - msg->getString(_PREHASH_Data, _PREHASH_SimName, pick_data.sim_name); - pick_data.location_text.append(pick_data.sim_name); - pick_data.location_text.append(" "); - msg->getVector3d(_PREHASH_Data, _PREHASH_PosGlobal, pick_data.pos_global); - S32 region_x = llround((F32)pick_data.pos_global.mdV[VX]) % REGION_WIDTH_UNITS; - S32 region_y = llround((F32)pick_data.pos_global.mdV[VY]) % REGION_WIDTH_UNITS; - S32 region_z = llround((F32)pick_data.pos_global.mdV[VZ]); - pick_data.location_text.append(llformat("(%d, %d, %d)", region_x, region_y, region_z)); msg->getS32(_PREHASH_Data, _PREHASH_SortOrder, pick_data.sort_order); msg->getBOOL(_PREHASH_Data, _PREHASH_Enabled, pick_data.enabled); diff --git a/indra/newview/llavatarpropertiesprocessor.h b/indra/newview/llavatarpropertiesprocessor.h index 24675c44c0..79d109f1db 100644 --- a/indra/newview/llavatarpropertiesprocessor.h +++ b/indra/newview/llavatarpropertiesprocessor.h @@ -96,7 +96,7 @@ struct LLPickData BOOL enabled; //used only in read requests - std::string location_text; + std::string user_name; std::string original_name; std::string sim_name; diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp index 06f9a86d8d..46151b469f 100644 --- a/indra/newview/llbottomtray.cpp +++ b/indra/newview/llbottomtray.cpp @@ -37,7 +37,7 @@ #include "llchiclet.h" #include "llfloaterreg.h" #include "llflyoutbutton.h" -#include "llimpanel.h" // for LLIMFloater +#include "llimfloater.h" // for LLIMFloater #include "lllayoutstack.h" #include "llnearbychatbar.h" #include "llsplitbutton.h" @@ -59,13 +59,13 @@ LLBottomTray::LLBottomTray(const LLSD&) mChicletPanel = getChild("chiclet_list"); mSysWell = getChild("sys_well"); - mSysWell->setNotificationChicletWindow(LLFloaterReg::getInstance("syswell_window")); + // init mSysWell + // set handler for a Click operation + mSysWell->setClickCallback(boost::bind(&LLSysWellWindow::onChicletClick, LLFloaterReg::getTypedInstance("syswell_window"))); mChicletPanel->setChicletClickedCallback(boost::bind(&LLBottomTray::onChicletClick,this,_1)); - LLSplitButton* presets = getChild("presets"); - presets->setSelectionCallback(LLFloaterCamera::onClickCameraPresets); - + LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("CameraPresets.ChangeView",&LLFloaterCameraPresets::onClickCameraPresets); LLIMMgr::getInstance()->addSessionObserver(this); //this is to fix a crash that occurs because LLBottomTray is a singleton @@ -79,16 +79,15 @@ LLBottomTray::LLBottomTray(const LLSD&) BOOL LLBottomTray::postBuild() { - mCommitCallbackRegistrar.add("ShowCamMoveCtrls.Action", boost::bind(&LLBottomTray::onShowCamMoveCtrlsContextMenuItemClicked, this, _2)); - mEnableCallbackRegistrar.add("ShowCamMoveCtrls.EnableMenuItem", boost::bind(&LLBottomTray::onShowCamMoveCtrlsContextMenuItemEnabled, this, _2)); - - mShowCamMoveCtrlsContextMenu = LLUICtrlFactory::getInstance()->createFromFile("menu_hide_camera_move_controls.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - gMenuHolder->addChild(mShowCamMoveCtrlsContextMenu); + mBottomTrayContextMenu = LLUICtrlFactory::getInstance()->createFromFile("menu_bottomtray.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + gMenuHolder->addChild(mBottomTrayContextMenu); mNearbyChatBar = getChild("chat_bar"); mToolbarStack = getChild("toolbar_stack"); mMovementPanel = getChild("movement_panel"); + mGestureCombo = getChild("Gesture"); mCamPanel = getChild("cam_panel"); + setRightMouseDownCallback(boost::bind(&LLBottomTray::showBottomTrayContextMenu,this, _2, _3,_4)); return TRUE; } @@ -222,54 +221,47 @@ void LLBottomTray::setVisible(BOOL visible) } } -BOOL LLBottomTray::handleRightMouseDown(S32 x, S32 y, MASK mask) +void LLBottomTray::showBottomTrayContextMenu(S32 x, S32 y, MASK mask) { - if (!LLPanel::handleRightMouseDown(x, y, mask)) + // We should show BottomTrayContextMenu in last turn + if (mBottomTrayContextMenu && !LLMenuGL::sMenuContainer->getVisibleMenu()) { - if (mShowCamMoveCtrlsContextMenu) - { - mShowCamMoveCtrlsContextMenu->buildDrawLabels(); - mShowCamMoveCtrlsContextMenu->updateParent(LLMenuGL::sMenuContainer); - LLMenuGL::showPopup(this, mShowCamMoveCtrlsContextMenu, x, y); + //there are no other context menu (IM chiclet etc ), so we can show BottomTrayContextMenu + mBottomTrayContextMenu->buildDrawLabels(); + mBottomTrayContextMenu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, mBottomTrayContextMenu, x, y); + } - } - - return TRUE; } -bool LLBottomTray::onShowCamMoveCtrlsContextMenuItemEnabled(const LLSD& userdata) +void LLBottomTray::showGestureButton(BOOL visible) { - std::string item = userdata.asString(); - - if (item == "show_camera_move_controls") + if (visible != mGestureCombo->getVisible()) { - return gSavedSettings.getBOOL("ShowCameraAndMoveControls"); - } + LLRect r = mNearbyChatBar->getRect(); - return FALSE; -} - -void LLBottomTray::onShowCamMoveCtrlsContextMenuItemClicked(const LLSD& userdata) -{ - std::string item = userdata.asString(); + mGestureCombo->setVisible(visible); - if (item == "show_camera_move_controls") + if (!visible) { - BOOL state = !gSavedSettings.getBOOL("ShowCameraAndMoveControls"); + LLFloaterReg::hideFloaterInstance("gestures"); + r.mRight -= mGestureCombo->getRect().getWidth(); + } + else + { + r.mRight += mGestureCombo->getRect().getWidth(); + } - showCameraAndMoveControls(state); - gSavedSettings.setBOOL("ShowCameraAndMoveControls", state); + mNearbyChatBar->setRect(r); } } -void LLBottomTray::showCameraAndMoveControls(BOOL visible) +void LLBottomTray::showMoveButton(BOOL visible) { - mCamPanel->setVisible(visible); mMovementPanel->setVisible(visible); +} - if (!visible) - { - LLFloaterReg::hideFloaterInstance("moveview"); - LLFloaterReg::hideFloaterInstance("camera"); - } +void LLBottomTray::showCameraButton(BOOL visible) +{ + mCamPanel->setVisible(visible); } diff --git a/indra/newview/llbottomtray.h b/indra/newview/llbottomtray.h index c3c840ede0..b25dec7b92 100644 --- a/indra/newview/llbottomtray.h +++ b/indra/newview/llbottomtray.h @@ -37,6 +37,7 @@ #include "llpanel.h" #include "llimview.h" +#include "llcombobox.h" class LLChicletPanel; class LLLineEditor; @@ -70,9 +71,11 @@ public: virtual void onFocusLost(); virtual void setVisible(BOOL visible); - virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + void showBottomTrayContextMenu(S32 x, S32 y, MASK mask); - void showCameraAndMoveControls(BOOL visible); + void showGestureButton(BOOL visible); + void showMoveButton(BOOL visible); + void showCameraButton(BOOL visible); private: @@ -82,9 +85,6 @@ protected: void onChicletClick(LLUICtrl* ctrl); - bool onShowCamMoveCtrlsContextMenuItemEnabled(const LLSD& userdata); - void onShowCamMoveCtrlsContextMenuItemClicked(const LLSD& userdata); - static void* createNearbyChatBar(void* userdata); /** @@ -97,9 +97,10 @@ protected: LLTalkButton* mTalkBtn; LLNearbyChatBar* mNearbyChatBar; LLLayoutStack* mToolbarStack; - LLMenuGL* mShowCamMoveCtrlsContextMenu; + LLMenuGL* mBottomTrayContextMenu; LLPanel* mMovementPanel; LLPanel* mCamPanel; + LLComboBox* mGestureCombo; }; #endif // LL_LLBOTTOMPANEL_H diff --git a/indra/newview/llchannelmanager.cpp b/indra/newview/llchannelmanager.cpp index a8373491cf..7ae9976338 100644 --- a/indra/newview/llchannelmanager.cpp +++ b/indra/newview/llchannelmanager.cpp @@ -37,6 +37,8 @@ #include "llappviewer.h" #include "llviewercontrol.h" #include "llimview.h" +#include "llbottomtray.h" +#include "llviewerwindow.h" #include @@ -48,12 +50,34 @@ LLChannelManager::LLChannelManager() LLAppViewer::instance()->setOnLoginCompletedCallback(boost::bind(&LLChannelManager::onLoginCompleted, this)); mChannelList.clear(); mStartUpChannel = NULL; + + if(!gViewerWindow) + { + llerrs << "LLChannelManager::LLChannelManager() - viwer window is not initialized yet" << llendl; + } } //-------------------------------------------------------------------------- LLChannelManager::~LLChannelManager() { - //All channels are being deleted by Parent View + for(std::vector::iterator it = mChannelList.begin(); it != mChannelList.end(); ++it) + { + delete (*it).channel; + } + + mChannelList.clear(); +} + +//-------------------------------------------------------------------------- +LLScreenChannel* LLChannelManager::createNotificationChannel() +{ + // creating params for a channel + LLChannelManager::Params p; + p.id = LLUUID(gSavedSettings.getString("NotificationChannelUUID")); + p.channel_align = CA_RIGHT; + + // Getting a Channel for our notifications + return LLChannelManager::getInstance()->getChannel(p); } //-------------------------------------------------------------------------- @@ -61,20 +85,22 @@ void LLChannelManager::onLoginCompleted() { S32 away_notifications = 0; + // calc a number of all offline notifications for(std::vector::iterator it = mChannelList.begin(); it != mChannelList.end(); ++it) { + // don't calc notifications for Nearby Chat if((*it).channel->getChannelID() == LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))) { continue; } + // don't calc notifications for channels that always show their notifications if(!(*it).channel->getDisplayToastsAlways()) { away_notifications +=(*it).channel->getNumberOfHiddenToasts(); } } - // *TODO: calculate IM notifications away_notifications += gIMMgr->getNumberOfUnreadIM(); if(!away_notifications) @@ -83,11 +109,11 @@ void LLChannelManager::onLoginCompleted() return; } + // create a channel for the StartUp Toast LLChannelManager::Params p; p.id = LLUUID(gSavedSettings.getString("StartUpChannelUUID")); - p.channel_right_bound = getRootView()->getRect().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); - p.channel_width = gSavedSettings.getS32("NotifyBoxWidth"); - mStartUpChannel = createChannel(p); + p.channel_align = CA_RIGHT; + mStartUpChannel = getChannel(p); if(!mStartUpChannel) { @@ -95,8 +121,13 @@ void LLChannelManager::onLoginCompleted() return; } + // init channel's position and size + S32 channel_right_bound = gViewerWindow->getWorldViewRect().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); + S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth"); + mStartUpChannel->init(channel_right_bound - channel_width, channel_right_bound); mStartUpChannel->setShowToasts(true); - static_cast(mStartUpChannel)->setCommitCallback(boost::bind(&LLChannelManager::onStartUpToastClose, this)); + + mStartUpChannel->setCommitCallback(boost::bind(&LLChannelManager::onStartUpToastClose, this)); mStartUpChannel->createStartUpToast(away_notifications, gSavedSettings.getS32("ChannelBottomPanelMargin"), gSavedSettings.getS32("StartUpToastTime")); } @@ -107,76 +138,56 @@ void LLChannelManager::onStartUpToastClose() { mStartUpChannel->setVisible(FALSE); mStartUpChannel->closeStartUpToast(); - getRootView()->removeChild(mStartUpChannel); removeChannelByID(LLUUID(gSavedSettings.getString("StartUpChannelUUID"))); delete mStartUpChannel; mStartUpChannel = NULL; } - // set StartUp Toast Flag + // set StartUp Toast Flag to allow all other channels to show incoming toasts LLScreenChannel::setStartUpToastShown(); - // allow all other channels to show incoming toasts - for(std::vector::iterator it = mChannelList.begin(); it != mChannelList.end(); ++it) - { - (*it).channel->setShowToasts(true); - } - // force NEARBY CHAT CHANNEL to repost all toasts if present - LLScreenChannel* nearby_channel = getChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))); + LLScreenChannel* nearby_channel = findChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))); nearby_channel->loadStoredToastsToChannel(); nearby_channel->setCanStoreToasts(false); } //-------------------------------------------------------------------------- -LLScreenChannel* LLChannelManager::createChannel(LLChannelManager::Params& p) +LLScreenChannel* LLChannelManager::getChannel(LLChannelManager::Params& p) { LLScreenChannel* new_channel = NULL; - if(!p.chiclet) - { - new_channel = getChannelByID(p.id); - } - else - { - new_channel = getChannelByChiclet(p.chiclet); - } + new_channel = findChannelByID(p.id); if(new_channel) return new_channel; new_channel = new LLScreenChannel(p.id); - getRootView()->addChild(new_channel); - new_channel->init(p.channel_right_bound - p.channel_width, p.channel_right_bound); - new_channel->setToastAlignment(p.align); + + if(!new_channel) + { + llerrs << "LLChannelManager::getChannel(LLChannelManager::Params& p) - can't create a channel!" << llendl; + } + else + { + new_channel->setToastAlignment(p.toast_align); + new_channel->setChannelAlignment(p.channel_align); new_channel->setDisplayToastsAlways(p.display_toasts_always); ChannelElem new_elem; new_elem.id = p.id; - new_elem.chiclet = p.chiclet; new_elem.channel = new_channel; - - mChannelList.push_back(new_elem); //TODO: remove chiclet from ScreenChannel? - - return new_channel; -} -//-------------------------------------------------------------------------- -LLScreenChannel* LLChannelManager::getChannelByID(const LLUUID id) -{ - std::vector::iterator it = find(mChannelList.begin(), mChannelList.end(), id); - if(it != mChannelList.end()) - { - return (*it).channel; + mChannelList.push_back(new_elem); } - return NULL; + return new_channel; } //-------------------------------------------------------------------------- -LLScreenChannel* LLChannelManager::getChannelByChiclet(const LLChiclet* chiclet) +LLScreenChannel* LLChannelManager::findChannelByID(const LLUUID id) { - std::vector::iterator it = find(mChannelList.begin(), mChannelList.end(), chiclet); + std::vector::iterator it = find(mChannelList.begin(), mChannelList.end(), id); if(it != mChannelList.end()) { return (*it).channel; @@ -185,22 +196,6 @@ LLScreenChannel* LLChannelManager::getChannelByChiclet(const LLChiclet* chiclet) return NULL; } -//-------------------------------------------------------------------------- -void LLChannelManager::reshape(S32 width, S32 height, BOOL called_from_parent) -{ - for(std::vector::iterator it = mChannelList.begin(); it != mChannelList.end(); ++it) - { - if((*it).channel->getToastAlignment() == NA_CENTRE) - { - LLRect channel_rect = (*it).channel->getRect(); - S32 screen_width = getRootView()->getRect().getWidth(); - channel_rect.setLeftTopAndSize(screen_width/2, channel_rect.mTop, channel_rect.getWidth(), channel_rect.getHeight()); - (*it).channel->setRect(channel_rect); - (*it).channel->showToasts(); - } - } -} - //-------------------------------------------------------------------------- void LLChannelManager::removeChannelByID(const LLUUID id) { @@ -212,18 +207,5 @@ void LLChannelManager::removeChannelByID(const LLUUID id) } //-------------------------------------------------------------------------- -void LLChannelManager::removeChannelByChiclet(const LLChiclet* chiclet) -{ - std::vector::iterator it = find(mChannelList.begin(), mChannelList.end(), chiclet); - if(it != mChannelList.end()) - { - mChannelList.erase(it); - } -} - -//-------------------------------------------------------------------------- - - - diff --git a/indra/newview/llchannelmanager.h b/indra/newview/llchannelmanager.h index e26c96b62e..811fa06d2b 100644 --- a/indra/newview/llchannelmanager.h +++ b/indra/newview/llchannelmanager.h @@ -34,7 +34,6 @@ #define LL_LLCHANNELMANAGER_H -#include "llchiclet.h" #include "llscreenchannel.h" #include "lluuid.h" @@ -48,36 +47,30 @@ namespace LLNotificationsUI * Manager for screen channels. * Responsible for instantiating and retrieving screen channels. */ -class LLChannelManager : public LLUICtrl, public LLSingleton +class LLChannelManager : public LLSingleton { public: - struct Params : public LLInitParam::Block + struct Params { LLUUID id; - LLChiclet* chiclet; - S32 channel_right_bound; - S32 channel_width; bool display_toasts_always; - EToastAlignment align; + EToastAlignment toast_align; + EChannelAlignment channel_align; - Params(): id(LLUUID("")), chiclet(NULL), - channel_right_bound(0), channel_width(0), - display_toasts_always(false), align(NA_BOTTOM) + Params(): id(LLUUID("")), display_toasts_always(false), toast_align(NA_BOTTOM), channel_align(CA_LEFT) {} }; struct ChannelElem { LLUUID id; - LLChiclet* chiclet; LLScreenChannel* channel; - ChannelElem() : id(LLUUID("")), chiclet(NULL), channel(NULL) { } + ChannelElem() : id(LLUUID("")), channel(NULL) { } ChannelElem(const ChannelElem &elem) { id = elem.id; - chiclet = elem.chiclet; channel = elem.channel; } @@ -85,12 +78,6 @@ public: { return (id == id_op); } - - bool operator == (const LLChiclet* chiclet_op) const - { - return (chiclet == chiclet_op); - } - }; LLChannelManager(); @@ -101,17 +88,17 @@ public: // removes a channel intended for the startup toast and allows other channels to show their toasts void onStartUpToastClose(); - //TODO: make protected? in order to be shure that channels are created only by notification handlers - LLScreenChannel* createChannel(LLChannelManager::Params& p); + // creates a new ScreenChannel according to the given parameters or returns existing if present + LLScreenChannel* getChannel(LLChannelManager::Params& p); + + // returns a channel by its ID + LLScreenChannel* findChannelByID(const LLUUID id); - LLScreenChannel* getChannelByID(const LLUUID id); - LLScreenChannel* getChannelByChiclet(const LLChiclet* chiclet); + // creator of the Notification channel, that is used in more than one handler + LLScreenChannel* createNotificationChannel(); // remove channel methods void removeChannelByID(const LLUUID id); - void removeChannelByChiclet(const LLChiclet* chiclet); - - void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); private: diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index a2dc97f7f5..20c44d5b11 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -38,6 +38,7 @@ #include "llgroupactions.h" #include "lliconctrl.h" #include "llimpanel.h" // LLFloaterIMPanel +#include "llimfloater.h" #include "llimview.h" #include "llfloaterreg.h" #include "lllocalcliprect.h" @@ -47,6 +48,7 @@ #include "llvoiceclient.h" #include "llvoicecontrolpanel.h" #include "llgroupmgr.h" +#include "llnotificationmanager.h" static LLDefaultChildRegistry::Register t1("chiclet_panel"); static LLDefaultChildRegistry::Register t2("chiclet_talk"); @@ -84,7 +86,6 @@ LLNotificationChiclet::LLNotificationChiclet(const Params& p) : LLChiclet(p) , mButton(NULL) , mCounterCtrl(NULL) -, mNotificationChicletWindow(NULL) { LLButton::Params button_params = p.button; button_params.rect(p.rect()); @@ -94,6 +95,11 @@ LLNotificationChiclet::LLNotificationChiclet(const Params& p) LLChicletNotificationCounterCtrl::Params unread_params = p.unread_notifications; mCounterCtrl = LLUICtrlFactory::create(unread_params); addChild(mCounterCtrl); + + // connect counter handlers to the signals + connectCounterUpdatersToSignal("notify"); + connectCounterUpdatersToSignal("groupnotify"); + connectCounterUpdatersToSignal("notifytoast"); } LLNotificationChiclet::~LLNotificationChiclet() @@ -101,6 +107,25 @@ LLNotificationChiclet::~LLNotificationChiclet() } +void LLNotificationChiclet::connectCounterUpdatersToSignal(std::string notification_type) +{ + LLNotificationsUI::LLNotificationManager* manager = LLNotificationsUI::LLNotificationManager::getInstance(); + LLNotificationsUI::LLEventHandler* n_handler = manager->getHandlerForNotification(notification_type); + if(n_handler) + { + if(notification_type == "notifytoast") + { + n_handler->setNewNotificationCallback(boost::bind(&LLNotificationChiclet::updateUreadIMNotifications, this)); + n_handler->setDelNotification(boost::bind(&LLNotificationChiclet::updateUreadIMNotifications, this)); + } + else + { + n_handler->setNewNotificationCallback(boost::bind(&LLNotificationChiclet::incUreadSystemNotifications, this)); + n_handler->setDelNotification(boost::bind(&LLNotificationChiclet::decUreadSystemNotifications, this)); + } + } +} + void LLNotificationChiclet::setCounter(S32 counter) { mCounterCtrl->setCounter(counter); @@ -259,7 +284,8 @@ LLIMP2PChiclet::Params::Params() rect(LLRect(0, 25, 45, 0)); avatar_icon.name("avatar_icon"); - avatar_icon.rect(LLRect(0, 25, 25, 0)); + avatar_icon.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP | FOLLOWS_BOTTOM); + avatar_icon.rect(LLRect(0, 24, 25, 0)); avatar_icon.mouse_opaque(false); unread_notifications.name("unread"); @@ -432,7 +458,7 @@ LLIMGroupChiclet::Params::Params() rect(LLRect(0, 25, 45, 0)); group_icon.name("group_icon"); - group_icon.rect(LLRect(0, 25, 25, 0)); + group_icon.rect(LLRect(0, 24, 25, 0)); unread_notifications.name("unread"); unread_notifications.rect(LLRect(25, 25, 45, 0)); @@ -846,6 +872,27 @@ void LLChicletPanel::removeAll() showScrollButtonsIfNeeded(); } +void LLChicletPanel::scrollToChiclet(const LLChiclet* chiclet) +{ + const LLRect& rect = chiclet->getRect(); + + if (rect.mLeft < 0) + { + scroll(llabs(rect.mLeft)); + showScrollButtonsIfNeeded(); + } + else + { + S32 scrollWidth = mScrollArea->getRect().getWidth(); + + if (rect.mRight > scrollWidth) + { + scroll(-llabs(rect.mRight - scrollWidth)); + showScrollButtonsIfNeeded(); + } + } +} + void LLChicletPanel::reshape(S32 width, S32 height, BOOL called_from_parent ) { LLPanel::reshape(width,height,called_from_parent); @@ -861,7 +908,7 @@ void LLChicletPanel::reshape(S32 width, S32 height, BOOL called_from_parent ) width, height - scroll_button_rect.getHeight())); mScrollArea->setRect(LLRect(scroll_button_rect.getWidth() + SCROLL_BUTTON_PAD, - height + 7, width - scroll_button_rect.getWidth() - SCROLL_BUTTON_PAD, 0)); + height, width - scroll_button_rect.getWidth() - SCROLL_BUTTON_PAD, 0)); mShowControls = width > mMinWidth; mScrollArea->setVisible(mShowControls); diff --git a/indra/newview/llchiclet.h b/indra/newview/llchiclet.h index 52bd7dbc31..316348cf1d 100644 --- a/indra/newview/llchiclet.h +++ b/indra/newview/llchiclet.h @@ -548,9 +548,6 @@ public: /*virtual*/ ~ LLNotificationChiclet(); - // Notification Chiclet Window - void setNotificationChicletWindow(LLFloater* wnd) { mNotificationChicletWindow = wnd; } - // methods for updating a number of unread System or IM notifications void incUreadSystemNotifications() { setCounter(++mUreadSystemNotifications + mUreadIMNotifications); } void decUreadSystemNotifications() { setCounter(--mUreadSystemNotifications + mUreadIMNotifications); } @@ -558,11 +555,12 @@ public: void setToggleState(BOOL toggled); protected: + // connect counter updaters to the corresponding signals + void connectCounterUpdatersToSignal(std::string notification_type); + LLNotificationChiclet(const Params& p); friend class LLUICtrlFactory; - LLFloater* mNotificationChicletWindow; - static S32 mUreadSystemNotifications; static S32 mUreadIMNotifications; @@ -644,6 +642,11 @@ public: */ void removeAll(); + /* + * Scrolls the panel to the specified chiclet + */ + void scrollToChiclet(const LLChiclet* chiclet); + boost::signals2::connection setChicletClickedCallback( const commit_callback_t& cb); @@ -814,6 +817,8 @@ T* LLChicletPanel::createChiclet(const LLUUID& session_id /*= LLUUID::null*/, S3 return NULL; } + scrollToChiclet(chiclet); + chiclet->setSessionId(session_id); return chiclet; diff --git a/indra/newview/llexpandabletextbox.cpp b/indra/newview/llexpandabletextbox.cpp new file mode 100644 index 0000000000..131f9ceaf0 --- /dev/null +++ b/indra/newview/llexpandabletextbox.cpp @@ -0,0 +1,508 @@ +/** + * @file llexpandabletextbox.cpp + * @brief LLExpandableTextBox and related class implementations + * + * $LicenseInfo:firstyear=2004&license=viewergpl$ + * + * Copyright (c) 2004-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$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llexpandabletextbox.h" + +#include "llscrollcontainer.h" + +static LLDefaultChildRegistry::Register t1("expandable_text"); + +LLExpandableTextBox::LLTextBoxEx::Params::Params() +: expand_textbox("expand_textbox") +{ +} + +LLExpandableTextBox::LLTextBoxEx::LLTextBoxEx(const Params& p) +: LLTextBox(p) +{ + setIsChrome(TRUE); + + LLTextBox::Params params = p.expand_textbox; + mExpandTextBox = LLUICtrlFactory::create(params); + addChild(mExpandTextBox); + + LLRect rc = getLocalRect(); + rc.mRight -= getHPad(); + rc.mLeft = rc.mRight - mExpandTextBox->getTextPixelWidth(); + rc.mTop = mExpandTextBox->getTextPixelHeight(); + mExpandTextBox->setRect(rc); +} + +BOOL LLExpandableTextBox::LLTextBoxEx::handleMouseUp(S32 x, S32 y, MASK mask) +{ + BOOL ret = LLTextBox::handleMouseUp(x, y, mask); + + if(mExpandTextBox->getRect().pointInRect(x, y)) + { + onCommit(); + } + + return ret; +} + +void LLExpandableTextBox::LLTextBoxEx::draw() +{ + // draw text box + LLTextBox::draw(); + // force text box to draw children + LLUICtrl::draw(); +} + +void LLExpandableTextBox::LLTextBoxEx::drawText( S32 x, S32 y, const LLWString &text, const LLColor4& color ) +{ + // *NOTE:dzaporozhan: + // Copy/paste from LLTextBox::drawText in order to modify last + // line width if needed and who "More" link + F32 alpha = getDrawContext().mAlpha; + if (mSegments.size() > 1) + { + // we have Urls (or other multi-styled segments) + drawTextSegments(x, y, text); + } + else if( mLineLengthList.empty() ) + { + // simple case of 1 line of text in one style + mDefaultFont->render(text, 0, (F32)x, (F32)y, color % alpha, + mHAlign, mVAlign, + 0, + mShadowType, + S32_MAX, getRect().getWidth(), NULL, mUseEllipses); + + mExpandTextBox->setVisible(FALSE); + } + else + { + // simple case of multiple lines of text, all in the same style + S32 cur_pos = 0; + for (std::vector::iterator iter = mLineLengthList.begin(); + iter != mLineLengthList.end(); ++iter) + { + S32 line_length = *iter; + S32 line_height = llfloor(mDefaultFont->getLineHeight()) + mLineSpacing; + S32 max_pixels = getRect().getWidth(); + + if(iter + 1 != mLineLengthList.end() + && y - line_height < line_height) + { + max_pixels = getCropTextWidth(); + } + + mDefaultFont->render(text, cur_pos, (F32)x, (F32)y, color % alpha, + mHAlign, mVAlign, + 0, + mShadowType, + line_length, max_pixels, NULL, mUseEllipses ); + + cur_pos += line_length + 1; + + y -= line_height; + if(y < line_height) + { + if( mLineLengthList.end() != iter + 1 ) + { + showExpandText(y); + } + else + { + hideExpandText(); + } + break; + } + } + } +} + +void LLExpandableTextBox::LLTextBoxEx::showExpandText(S32 y) +{ + LLRect rc = mExpandTextBox->getRect(); + rc.mTop = y + mExpandTextBox->getTextPixelHeight(); + rc.mBottom = y; + mExpandTextBox->setRect(rc); + mExpandTextBox->setVisible(TRUE); +} + +void LLExpandableTextBox::LLTextBoxEx::hideExpandText() +{ + mExpandTextBox->setVisible(FALSE); +} + +S32 LLExpandableTextBox::LLTextBoxEx::getCropTextWidth() +{ + return mExpandTextBox->getRect().mLeft - getHPad() * 2; +} + +void LLExpandableTextBox::LLTextBoxEx::drawTextSegments(S32 init_x, S32 init_y, const LLWString &text) +{ + // *NOTE:dzaporozhan: + // Copy/paste from LLTextBox::drawTextSegments in order to modify last + // line width if needed and who "More" link + F32 alpha = getDrawContext().mAlpha; + + const S32 text_len = text.length(); + if (text_len <= 0) + { + return; + } + + S32 cur_line = 0; + S32 num_lines = getLineCount(); + S32 line_start = getLineStart(cur_line); + S32 line_height = llround( mDefaultFont->getLineHeight() ) + mLineSpacing; + F32 text_y = (F32) init_y; + segment_set_t::iterator cur_seg = mSegments.begin(); + + // render a line of text at a time + const LLRect textRect = getLocalRect(); + while((textRect.mBottom <= text_y) && (cur_line < num_lines)) + { + S32 next_start = -1; + S32 line_end = text_len; + + if ((cur_line + 1) < num_lines) + { + next_start = getLineStart(cur_line + 1); + line_end = next_start; + } + if ( text[line_end-1] == '\n' ) + { + --line_end; + } + + // render all segments on this line + F32 text_x = init_x; + S32 seg_start = line_start; + while (seg_start < line_end && cur_seg != mSegments.end()) + { + // move to the next segment (or continue the previous one) + LLTextSegment *cur_segment = *cur_seg; + while (cur_segment->getEnd() <= seg_start) + { + if (++cur_seg == mSegments.end()) + { + return; + } + cur_segment = *cur_seg; + } + + // Draw a segment within the line + S32 clipped_end = llmin( line_end, cur_segment->getEnd() ); + S32 clipped_len = clipped_end - seg_start; + if( clipped_len > 0 ) + { + LLStyleSP style = cur_segment->getStyle(); + if (style && style->isVisible()) + { + // work out the color for the segment + LLColor4 color ; + if (getEnabled()) + { + color = style->isLink() ? mLinkColor.get() : mTextColor.get(); + } + else + { + color = mDisabledColor.get(); + } + color = color % alpha; + + S32 max_pixels = textRect.getWidth(); + + if(cur_line + 1 < num_lines + && text_y - line_height < line_height) + { + max_pixels = getCropTextWidth(); + } + + // render a single line worth for this segment + mDefaultFont->render(text, seg_start, text_x, text_y, color, + mHAlign, mVAlign, 0, mShadowType, clipped_len, + max_pixels, &text_x, mUseEllipses); + } + + seg_start += clipped_len; + } + } + + // move down one line + text_y -= (F32)line_height; + line_start = next_start; + cur_line++; + if(text_y < line_height) + { + if( cur_line < num_lines ) + { + showExpandText((S32)text_y); + } + else + { + hideExpandText(); + } + break; + } + } +} + +S32 LLExpandableTextBox::LLTextBoxEx::getVerticalTextDelta() +{ + S32 text_height = getTextPixelHeight(); + S32 textbox_height = getRect().getHeight(); + + return text_height - textbox_height; +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +LLExpandableTextBox::Params::Params() +: textbox("textbox") +, scroll("scroll") +, max_height("max_height", 0) +, bg_visible("bg_visible", false) +, expanded_bg_visible("expanded_bg_visible", true) +, bg_color("bg_color", LLColor4::black) +, expanded_bg_color("expanded_bg_color", LLColor4::black) +{ +} + +LLExpandableTextBox::LLExpandableTextBox(const Params& p) +: LLUICtrl(p) +, mMaxHeight(p.max_height) +, mBGVisible(p.bg_visible) +, mExpandedBGVisible(p.expanded_bg_visible) +, mBGColor(p.bg_color) +, mExpandedBGColor(p.expanded_bg_color) +, mExpanded(false) +{ + LLRect rc = getLocalRect(); + + LLScrollContainer::Params scroll_params = p.scroll; + scroll_params.rect(rc); + mScroll = LLUICtrlFactory::create(scroll_params); + addChild(mScroll); + + LLTextBoxEx::Params textbox_params = p.textbox; + textbox_params.rect(rc); + mTextBox = LLUICtrlFactory::create(textbox_params); + mScroll->addChild(mTextBox); + + updateTextBoxRect(); + + mTextBox->setCommitCallback(boost::bind(&LLExpandableTextBox::onExpandClicked, this)); +} + +void LLExpandableTextBox::draw() +{ + if(mBGVisible && !mExpanded) + { + gl_rect_2d(getLocalRect(), mBGColor.get(), TRUE); + } + if(mExpandedBGVisible && mExpanded) + { + gl_rect_2d(getLocalRect(), mExpandedBGColor.get(), TRUE); + } + + collapseIfPosChanged(); + + LLUICtrl::draw(); +} + +void LLExpandableTextBox::collapseIfPosChanged() +{ + if(mExpanded) + { + LLView* parentp = getParent(); + LLRect parent_rect = parentp->getRect(); + parentp->localRectToOtherView(parent_rect, &parent_rect, getRootView()); + + if(parent_rect.mLeft != mParentRect.mLeft + || parent_rect.mTop != mParentRect.mTop) + { + collapseTextBox(); + } + } +} + +void LLExpandableTextBox::onExpandClicked() +{ + expandTextBox(); +} + +void LLExpandableTextBox::updateTextBoxRect() +{ + LLRect rc = getLocalRect(); + + rc.mLeft += mScroll->getBorderWidth(); + rc.mRight -= mScroll->getBorderWidth(); + rc.mTop -= mScroll->getBorderWidth(); + rc.mBottom += mScroll->getBorderWidth(); + + mTextBox->reshape(rc.getWidth(), rc.getHeight()); + mTextBox->setRect(rc); +} + +S32 LLExpandableTextBox::recalculateTextDelta(S32 text_delta) +{ + LLRect expanded_rect = getLocalRect(); + LLView* root_view = getRootView(); + LLRect window_rect = root_view->getRect(); + + LLRect expanded_screen_rect; + localRectToOtherView(expanded_rect, &expanded_screen_rect, root_view); + + // don't allow expanded text box bottom go off screen + if(expanded_screen_rect.mBottom - text_delta < window_rect.mBottom) + { + text_delta = expanded_screen_rect.mBottom - window_rect.mBottom; + } + // show scroll bar if max_height is valid + // and expanded size is greater that max_height + else if(mMaxHeight > 0 && expanded_rect.getHeight() + text_delta > mMaxHeight) + { + text_delta = mMaxHeight - expanded_rect.getHeight(); + } + + return text_delta; +} + +void LLExpandableTextBox::expandTextBox() +{ + S32 text_delta = mTextBox->getVerticalTextDelta(); + text_delta += mTextBox->getVPad() * 2 + mScroll->getBorderWidth() * 2; + // no need to expand + if(text_delta <= 0) + { + return; + } + + saveCollapsedState(); + + LLRect expanded_rect = getLocalRect(); + LLRect expanded_screen_rect; + + S32 updated_text_delta = recalculateTextDelta(text_delta); + // actual expand + expanded_rect.mBottom -= updated_text_delta; + + LLRect text_box_rect = mTextBox->getRect(); + + // check if we need to show scrollbar + if(text_delta != updated_text_delta) + { + static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); + + // disable horizontal scrollbar + text_box_rect.mRight -= scrollbar_size; + // text box size has changed - redo text wrap + mTextBox->setWrappedText(mText, text_box_rect.getWidth()); + // recalculate text delta since text wrap changed text height + text_delta = mTextBox->getVerticalTextDelta() + mTextBox->getVPad() * 2; + } + + // expand text + text_box_rect.mBottom -= text_delta; + mTextBox->reshape(text_box_rect.getWidth(), text_box_rect.getHeight()); + mTextBox->setRect(text_box_rect); + + // expand text box + localRectToOtherView(expanded_rect, &expanded_screen_rect, getParent()); + reshape(expanded_screen_rect.getWidth(), expanded_screen_rect.getHeight(), FALSE); + setRect(expanded_screen_rect); + + setFocus(TRUE); + // this lets us receive top_lost event(needed to collapse text box) + // it also draws text box above all other ui elements + gFocusMgr.setTopCtrl(this); + + mExpanded = true; +} + +void LLExpandableTextBox::collapseTextBox() +{ + if(!mExpanded) + { + return; + } + + mExpanded = false; + + reshape(mCollapsedRect.getWidth(), mCollapsedRect.getHeight(), FALSE); + setRect(mCollapsedRect); + + updateTextBoxRect(); + + mTextBox->setWrappedText(mText); + if(gFocusMgr.getTopCtrl() == this) + { + gFocusMgr.setTopCtrl(NULL); + } +} + +void LLExpandableTextBox::onFocusLost() +{ + collapseTextBox(); + + LLUICtrl::onFocusLost(); +} + +void LLExpandableTextBox::onTopLost() +{ + collapseTextBox(); + + LLUICtrl::onTopLost(); +} + +void LLExpandableTextBox::setValue(const LLSD& value) +{ + collapseTextBox(); + + mText = value.asString(); + mTextBox->setValue(value); +} + +void LLExpandableTextBox::setText(const std::string& str) +{ + collapseTextBox(); + + mText = str; + mTextBox->setText(str); +} + +void LLExpandableTextBox::saveCollapsedState() +{ + mCollapsedRect = getRect(); + + mParentRect = getParent()->getRect(); + // convert parent rect to screen coordinates, + // this will allow to track parent's position change + getParent()->localRectToOtherView(mParentRect, &mParentRect, getRootView()); +} diff --git a/indra/newview/llexpandabletextbox.h b/indra/newview/llexpandabletextbox.h new file mode 100644 index 0000000000..0a5a4c8b75 --- /dev/null +++ b/indra/newview/llexpandabletextbox.h @@ -0,0 +1,237 @@ +/** + * @file llexpandabletextbox.h + * @brief LLExpandableTextBox and related class definitions + * + * $LicenseInfo:firstyear=2004&license=viewergpl$ + * + * Copyright (c) 2004-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_LLEXPANDABLETEXTBOX_H +#define LL_LLEXPANDABLETEXTBOX_H + +#include "lltextbox.h" +#include "llscrollcontainer.h" + +/** + * LLExpandableTextBox is a text box control that will show "More" link at end of text + * if text doesn't fit into text box. After pressing "More" the text box will expand to show + * all text. If text is still too big, a scroll bar will appear inside expanded text box. + */ +class LLExpandableTextBox : public LLUICtrl +{ +protected: + + /** + * Extended text box. "More" link will appear at end of text if + * text is too long to fit into text box size. + */ + class LLTextBoxEx : public LLTextBox + { + public: + struct Params : public LLInitParam::Block + { + Optional expand_textbox; + + Params(); + }; + + /** + * Draw text box and "More" link + */ + /*virtual*/ void draw(); + + /** + * Draws simple text(no urls) line by line, will show or hide "More" link + * if needed. + */ + /*virtual*/ void drawText( S32 x, S32 y, const LLWString &text, const LLColor4& color ); + + /** + * Draws segmented text(with urls) line by line. Will show or hide "More" link + * if needed + */ + void drawTextSegments(S32 x, S32 y, const LLWString &text); + + /** + * Returns difference between text box height and text height. + * Value is positive if text height is greater than text box height. + */ + virtual S32 getVerticalTextDelta(); + + /** + * Returns text vertical padding + */ + virtual S32 getVPad() { return mVPad; } + + /** + * Returns text horizontal padding + */ + virtual S32 getHPad() { return mHPad; } + + /** + * Broadcasts "commit" signal if user clicked "More" link + */ + /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); + + protected: + + LLTextBoxEx(const Params& p); + friend class LLUICtrlFactory; + + /** + * Shows "More" link + */ + void showExpandText(S32 y); + + /** + * Hides "More" link + */ + void hideExpandText(); + + /** + * Returns cropped line width + */ + S32 getCropTextWidth(); + + private: + + LLTextBox* mExpandTextBox; + }; + +public: + + struct Params : public LLInitParam::Block + { + Optional textbox; + + Optional scroll; + + Optional max_height; + + Optional bg_visible, + expanded_bg_visible; + + Optional bg_color, + expanded_bg_color; + + Params(); + }; + + /** + * Sets text + */ + virtual void setText(const std::string& str); + + /** + * Returns text + */ + virtual std::string getText() const { return mText; } + + /** + * Sets text + */ + /*virtual*/ void setValue(const LLSD& value); + + /** + * Returns text + */ + /*virtual*/ LLSD getValue() const { return mText; } + + /** + * Collapses text box on focus_lost event + */ + /*virtual*/ void onFocusLost(); + + /** + * Collapses text box on top_lost event + */ + /*virtual*/ void onTopLost(); + + /** + * Draws text box, collapses text box if its expanded and its parent's position changed + */ + /*virtual*/ void draw(); + +protected: + + LLExpandableTextBox(const Params& p); + friend class LLUICtrlFactory; + + /** + * Expands text box. + * A scroll bar will appear if expanded height is greater than max_height + */ + virtual void expandTextBox(); + + /** + * Collapses text box. + */ + virtual void collapseTextBox(); + + /** + * Collapses text box if it is expanded and its parent's position changed + */ + virtual void collapseIfPosChanged(); + + /** + * Updates text box rect to avoid horizontal scroll bar + */ + virtual void updateTextBoxRect(); + + /** + * User clicked on "More" link - expand text box + */ + virtual void onExpandClicked(); + + /** + * Saves collapsed text box's states(rect, parent rect...) + */ + virtual void saveCollapsedState(); + + /** + * Recalculate text delta considering min_height and window rect. + */ + virtual S32 recalculateTextDelta(S32 text_delta); + +protected: + + std::string mText; + LLTextBoxEx* mTextBox; + LLScrollContainer* mScroll; + + S32 mMaxHeight; + LLRect mCollapsedRect; + bool mExpanded; + LLRect mParentRect; + + bool mBGVisible; + bool mExpandedBGVisible; + LLUIColor mBGColor; + LLUIColor mExpandedBGColor; +}; + +#endif //LL_LLEXPANDABLETEXTBOX_H diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index ef71e35254..6b18984f88 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -144,6 +144,18 @@ public: void setLandmarkID(const LLUUID& id){ mUrlGetter.setLandmarkID(id); } const LLUUID& getLandmarkId() const { return mUrlGetter.getLandmarkId(); } + void onMouseEnter(S32 x, S32 y, MASK mask) + { + if (LLToolDragAndDrop::getInstance()->hasMouseCapture()) + { + LLUICtrl::onMouseEnter(x, y, mask); + } + else + { + LLButton::onMouseEnter(x, y, mask); + } + } + protected: LLFavoriteLandmarkButton(const LLButton::Params& p) : LLButton(p) {} friend class LLUICtrlFactory; @@ -278,7 +290,8 @@ struct LLFavoritesSort }; LLFavoritesBarCtrl::Params::Params() -: chevron_button_tool_tip("chevron_button_tool_tip") +: chevron_button_tool_tip("chevron_button_tool_tip"), + image_drag_indication("image_drag_indication") { } @@ -287,7 +300,12 @@ LLFavoritesBarCtrl::LLFavoritesBarCtrl(const LLFavoritesBarCtrl::Params& p) mFont(p.font.isProvided() ? p.font() : LLFontGL::getFontSansSerifSmall()), mPopupMenuHandle(), mInventoryItemsPopupMenuHandle(), - mChevronButtonToolTip(p.chevron_button_tool_tip) + mChevronButtonToolTip(p.chevron_button_tool_tip), + mImageDragIndication(p.image_drag_indication), + mShowDragMarker(FALSE), + mLandingTab(NULL), + mLastTab(NULL), + mTabsHighlightEnabled(TRUE) { // Register callback for menus with current registrar (will be parent panel's registrar) LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Favorites.DoToSelected", @@ -321,17 +339,49 @@ BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, case DAD_LANDMARK: { + /* + * add a callback to the end drag event. + * the callback will disconnet itself immediately after execution + * this is done because LLToolDragAndDrop is a common tool so it shouldn't + * be overloaded with redundant callbacks. + */ + if (!mEndDragConnection.connected()) + { + mEndDragConnection = LLToolDragAndDrop::getInstance()->setEndDragCallback(boost::bind(&LLFavoritesBarCtrl::onEndDrag, this)); + } + // Copy the item into the favorites folder (if it's not already there). LLInventoryItem *item = (LLInventoryItem *)cargo_data; + if (LLFavoriteLandmarkButton* dest = dynamic_cast(findChildByLocalCoords(x, y))) + { + setLandingTab(dest); + } + /* + * the condition dest == NULL can be satisfied not only in the case + * of dragging to the right from the last tab of the favbar. there is a + * small gap between each tab. if the user drags something exactly there + * then mLandingTab will be set to NULL and the dragged item will be pushed + * to the end of the favorites bar. this is incorrect behavior. that's why + * we need an additional check which excludes the case described previously + * making sure that the mouse pointer is beyond the last tab. + */ + else if (mLastTab && x >= mLastTab->getRect().mRight) + { + setLandingTab(NULL); + } + // check if we are dragging an existing item from the favorites bar if (item && mDragItemId == item->getUUID()) { *accept = ACCEPT_YES_SINGLE; + showDragMarker(TRUE); + if (drop) { handleExistingFavoriteDragAndDrop(x, y); + showDragMarker(FALSE); } } else @@ -343,11 +393,14 @@ BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, break; } - *accept = ACCEPT_YES_COPY_SINGLE; + *accept = ACCEPT_YES_COPY_MULTI; + + showDragMarker(TRUE); if (drop) { handleNewFavoriteDragAndDrop(item, favorites_id, x, y); + showDragMarker(FALSE); } } } @@ -361,7 +414,13 @@ BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, void LLFavoritesBarCtrl::handleExistingFavoriteDragAndDrop(S32 x, S32 y) { - LLFavoriteLandmarkButton* dest = dynamic_cast(findChildByLocalCoords(x, y)); + LLFavoriteLandmarkButton* dest = dynamic_cast(mLandingTab); + + // there is no need to handle if an item was dragged onto itself + if (dest && dest->getLandmarkId() == mDragItemId) + { + return; + } if (dest) { @@ -381,14 +440,17 @@ void LLFavoritesBarCtrl::handleExistingFavoriteDragAndDrop(S32 x, S32 y) menu->setVisible(FALSE); showDropDownMenu(); } - - mDragItemId = LLUUID::null; - getWindow()->setCursor(UI_CURSOR_ARROW); } void LLFavoritesBarCtrl::handleNewFavoriteDragAndDrop(LLInventoryItem *item, const LLUUID& favorites_id, S32 x, S32 y) { - LLFavoriteLandmarkButton* dest = dynamic_cast(findChildByLocalCoords(x, y)); + LLFavoriteLandmarkButton* dest = dynamic_cast(mLandingTab); + + // there is no need to handle if an item was dragged onto itself + if (dest && dest->getLandmarkId() == mDragItemId) + { + return; + } if (dest) { @@ -458,6 +520,30 @@ void LLFavoritesBarCtrl::reshape(S32 width, S32 height, BOOL called_from_parent) LLUICtrl::reshape(width, height, called_from_parent); } +void LLFavoritesBarCtrl::draw() +{ + LLUICtrl::draw(); + + if (mShowDragMarker) + { + S32 w = mImageDragIndication->getWidth() / 2; + S32 h = mImageDragIndication->getHeight() / 2; + + if (mLandingTab) + { + // mouse pointer hovers over an existing tab + LLRect rect = mLandingTab->getRect(); + mImageDragIndication->draw(rect.mLeft - w/2, rect.getHeight(), w, h); + } + else if (mLastTab) + { + // mouse pointer hovers over the favbar empty space (right to the last tab) + LLRect rect = mLastTab->getRect(); + mImageDragIndication->draw(rect.mRight, rect.getHeight(), w, h); + } + } +} + LLXMLNodePtr LLFavoritesBarCtrl::getButtonXMLNode() { LLXMLNodePtr buttonXMLNode = NULL; @@ -628,11 +714,15 @@ void LLFavoritesBarCtrl::createButtons(const LLInventoryModel::item_array_t &ite { S32 curr_x = buttonHGap; // Adding buttons + + LLFavoriteLandmarkButton* fav_btn = NULL; + mLandingTab = mLastTab = NULL; + for(S32 i = mFirstDropDownItem -1, j = 0; i >= 0; i--) { LLViewerInventoryItem* item = items.get(j++); - LLFavoriteLandmarkButton* fav_btn = LLUICtrlFactory::defaultBuilder(buttonXMLNode, this, NULL); + fav_btn = LLUICtrlFactory::defaultBuilder(buttonXMLNode, this, NULL); if (NULL == fav_btn) { llwarns << "Unable to create button for landmark: " << item->getName() << llendl; @@ -657,6 +747,8 @@ void LLFavoritesBarCtrl::createButtons(const LLInventoryModel::item_array_t &ite curr_x += buttonWidth + buttonHGap; } + + mLastTab = fav_btn; } @@ -784,6 +876,7 @@ void LLFavoritesBarCtrl::showDropDownMenu() menu_item->setRightMouseDownCallback(boost::bind(&LLFavoritesBarCtrl::onButtonRightClick, this,item->getUUID(),_1,_2,_3,_4)); menu_item->LLUICtrl::setMouseDownCallback(boost::bind(&LLFavoritesBarCtrl::onButtonMouseDown, this, item->getUUID(), _1, _2, _3, _4)); menu_item->LLUICtrl::setMouseUpCallback(boost::bind(&LLFavoritesBarCtrl::onButtonMouseUp, this, item->getUUID(), _1, _2, _3, _4)); + menu_item->setLandmarkID(item->getUUID()); // Check whether item name wider than menu if (menu_item->getNominalWidth() > max_width) @@ -968,7 +1061,6 @@ void LLFavoritesBarCtrl::pastFromClipboard() const void LLFavoritesBarCtrl::onButtonMouseDown(LLUUID id, LLUICtrl* ctrl, S32 x, S32 y, MASK mask) { mDragItemId = id; - mStartDrag = TRUE; S32 screenX, screenY; localPointToScreen(x, y, &screenX, &screenY); @@ -981,9 +1073,18 @@ void LLFavoritesBarCtrl::onButtonMouseUp(LLUUID id, LLUICtrl* ctrl, S32 x, S32 y mDragItemId = LLUUID::null; } +void LLFavoritesBarCtrl::onEndDrag() +{ + mEndDragConnection.disconnect(); + + showDragMarker(FALSE); + mDragItemId = LLUUID::null; + LLView::getWindow()->setCursor(UI_CURSOR_ARROW); +} + BOOL LLFavoritesBarCtrl::handleHover(S32 x, S32 y, MASK mask) { - if (mDragItemId != LLUUID::null && mStartDrag) + if (mDragItemId != LLUUID::null) { S32 screenX, screenY; localPointToScreen(x, y, &screenX, &screenY); @@ -994,8 +1095,6 @@ BOOL LLFavoritesBarCtrl::handleHover(S32 x, S32 y, MASK mask) DAD_LANDMARK, mDragItemId, LLToolDragAndDrop::SOURCE_LIBRARY); - mStartDrag = FALSE; - return LLToolDragAndDrop::getInstance()->handleHover(x, y, mask); } } diff --git a/indra/newview/llfavoritesbar.h b/indra/newview/llfavoritesbar.h index 4cd92d1a58..0be8de29a9 100644 --- a/indra/newview/llfavoritesbar.h +++ b/indra/newview/llfavoritesbar.h @@ -43,6 +43,7 @@ public: struct Params : public LLInitParam::Block { Optional chevron_button_tool_tip; + Optional image_drag_indication; Params(); }; @@ -65,6 +66,10 @@ public: // LLInventoryObserver observer trigger virtual void changed(U32 mask); virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + virtual void draw(); + + void showDragMarker(BOOL show) { mShowDragMarker = show; } + void setLandingTab(LLUICtrl* tab) { mLandingTab = tab; } protected: void updateButtons(U32 bar_width); @@ -78,6 +83,8 @@ protected: void onButtonMouseDown(LLUUID id, LLUICtrl* button, S32 x, S32 y, MASK mask); void onButtonMouseUp(LLUUID id, LLUICtrl* button, S32 x, S32 y, MASK mask); + void onEndDrag(); + void doToSelected(const LLSD& userdata); BOOL isClipboardPasteable() const; void pastFromClipboard() const; @@ -98,6 +105,7 @@ protected: LLRect mChevronRect; std::string mChevronButtonToolTip; + LLUIImage* mImageDragIndication; private: /* @@ -136,10 +144,16 @@ private: // finds an item by it's UUID in the items array LLInventoryModel::item_array_t::iterator findItemByUUID(LLInventoryModel::item_array_t& items, const LLUUID& id); - BOOL mSkipUpdate; - BOOL mStartDrag; + BOOL mShowDragMarker; + LLUICtrl* mLandingTab; + LLUICtrl* mLastTab; + LLUUID mDragItemId; LLInventoryModel::item_array_t mItems; + + BOOL mTabsHighlightEnabled; + + boost::signals2::connection mEndDragConnection; }; diff --git a/indra/newview/llfloatercamera.cpp b/indra/newview/llfloatercamera.cpp index f4c4f38008..0511ec1063 100644 --- a/indra/newview/llfloatercamera.cpp +++ b/indra/newview/llfloatercamera.cpp @@ -88,7 +88,6 @@ void LLFloaterCamera::update() { ECameraControlMode mode = determineMode(); if (mode != mCurrMode) setMode(mode); - updatePosition(); show_tip(mMode2TipType[mode], this); } @@ -122,48 +121,20 @@ LLFloaterCamera* LLFloaterCamera::findInstance() return LLFloaterReg::findTypedInstance("camera"); } -/*static*/ -void LLFloaterCamera::onClickCameraPresets(LLUICtrl* ctrl, const LLSD& param) -{ - std::string name = param.asString(); - - if ("rear_view" == name) - { - LLFirstTimeTipsManager::showTipsFor(LLFirstTimeTipsManager::FTT_CAMERA_PRESET_REAR, ctrl); - gAgent.switchCameraPreset(CAMERA_PRESET_REAR_VIEW); - } - else if ("group_view" == name) - { - LLFirstTimeTipsManager::showTipsFor(LLFirstTimeTipsManager::FTT_CAMERA_PRESET_GROUP); - gAgent.switchCameraPreset(CAMERA_PRESET_GROUP_VIEW); - } - else if ("front_view" == name) - { - LLFirstTimeTipsManager::showTipsFor(LLFirstTimeTipsManager::FTT_CAMERA_PRESET_FRONT); - gAgent.switchCameraPreset(CAMERA_PRESET_FRONT_VIEW); - } - -} - void LLFloaterCamera::onOpen(const LLSD& key) { - updatePosition(); -} - -void LLFloaterCamera::updatePosition() -{ - LLBottomTray* tray = LLBottomTray::getInstance(); - if (!tray) return; + LLButton *anchor_panel = LLBottomTray::getInstance()->getChild("camera_btn"); - LLButton* camera_button = tray->getChild("camera_btn"); + setDockControl(new LLDockControl( + anchor_panel, this, + getDockTongue(), LLDockControl::TOP)); - //align centers of a button and a floater - S32 x = camera_button->calcScreenRect().getCenterX() - getRect().getWidth()/2; - setOrigin(x, 0); + show_tip(mMode2TipType[mCurrMode], this); } + LLFloaterCamera::LLFloaterCamera(const LLSD& val) -: LLFloater(val), +: LLDockableFloater(NULL, false, val), mCurrMode(CAMERA_CTRL_MODE_ORBIT), mPrevMode(CAMERA_CTRL_MODE_ORBIT) { @@ -187,7 +158,7 @@ BOOL LLFloaterCamera::postBuild() update(); - return TRUE; + return LLDockableFloater::postBuild(); } ECameraControlMode LLFloaterCamera::determineMode() @@ -311,7 +282,8 @@ void LLFloaterCamera::updateState() LLRect controls_rect; if (childGetRect(CONTROLS, controls_rect)) { - static S32 height = controls_rect.getHeight(); + static LLUICachedControl floater_header_size ("UIFloaterHeaderSize", 0); + static S32 height = controls_rect.getHeight() - floater_header_size; S32 newHeight = rect.getHeight(); if (showControls) @@ -330,3 +302,46 @@ void LLFloaterCamera::updateState() } } +//-------------LLFloaterCameraPresets------------------------ + +LLFloaterCameraPresets::LLFloaterCameraPresets(const LLSD& key): +LLDockableFloater(NULL, false, key) +{} + +BOOL LLFloaterCameraPresets::postBuild() +{ + setIsChrome(TRUE); + + //build dockTongue + LLDockableFloater::postBuild(); + + LLButton *anchor_btn = LLBottomTray::getInstance()->getChild("camera_presets_btn"); + + setDockControl(new LLDockControl( + anchor_btn, this, + getDockTongue(), LLDockControl::TOP)); + return TRUE; +} + +/*static*/ +void LLFloaterCameraPresets::onClickCameraPresets(LLUICtrl* ctrl, const LLSD& param) +{ + std::string name = param.asString(); + + if ("rear_view" == name) + { + LLFirstTimeTipsManager::showTipsFor(LLFirstTimeTipsManager::FTT_CAMERA_PRESET_REAR, ctrl); + gAgent.switchCameraPreset(CAMERA_PRESET_REAR_VIEW); + } + else if ("group_view" == name) + { + LLFirstTimeTipsManager::showTipsFor(LLFirstTimeTipsManager::FTT_CAMERA_PRESET_GROUP); + gAgent.switchCameraPreset(CAMERA_PRESET_GROUP_VIEW); + } + else if ("front_view" == name) + { + LLFirstTimeTipsManager::showTipsFor(LLFirstTimeTipsManager::FTT_CAMERA_PRESET_FRONT); + gAgent.switchCameraPreset(CAMERA_PRESET_FRONT_VIEW); + } + +} diff --git a/indra/newview/llfloatercamera.h b/indra/newview/llfloatercamera.h index 1181c443bf..020cae7e82 100644 --- a/indra/newview/llfloatercamera.h +++ b/indra/newview/llfloatercamera.h @@ -33,7 +33,7 @@ #ifndef LLFLOATERCAMERA_H #define LLFLOATERCAMERA_H -#include "llfloater.h" +#include "lldockablefloater.h" #include "llfirsttimetipmanager.h" @@ -51,7 +51,7 @@ enum ECameraControlMode }; class LLFloaterCamera - : public LLFloater + : public LLDockableFloater { friend class LLFloaterReg; @@ -70,14 +70,8 @@ public: static void updateIfNotInAvatarViewMode(); - static void onClickCameraPresets(LLUICtrl* ctrl, const LLSD& param); - virtual void onOpen(const LLSD& key); - // *HACK: due to hard enough to have this control aligned with "Camera" button while resizing - // let update its position in each frame - /*virtual*/ void draw(){updatePosition(); LLFloater::draw();} - LLJoystickCameraRotate* mRotate; LLJoystickCameraZoom* mZoom; LLJoystickCameraTrack* mTrack; @@ -113,9 +107,6 @@ private: void assignButton2Mode(ECameraControlMode mode, const std::string& button_name); void initMode2TipTypeMap(); - /*Updates position of the floater to be center aligned with "Camera" button.*/ - void updatePosition(); - ECameraControlMode mPrevMode; ECameraControlMode mCurrMode; @@ -124,4 +115,15 @@ private: }; +class LLFloaterCameraPresets : public LLDockableFloater +{ + friend class LLFloaterReg; +public: + static void onClickCameraPresets(LLUICtrl* ctrl, const LLSD& param); +private: + LLFloaterCameraPresets(const LLSD&); + ~LLFloaterCameraPresets(){} + /*virtual*/ BOOL postBuild(); + +}; #endif diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index c197c78a41..761b5c9219 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -102,6 +102,7 @@ #include "llboost.h" #include "llviewermedia.h" #include "llpluginclassmedia.h" +#include "llteleporthistorystorage.h" #include @@ -221,6 +222,9 @@ bool callback_clear_browser_cache(const LLSD& notification, const LLSD& response LLSearchHistory::getInstance()->save(); LLSearchComboBox* search_ctrl = LLNavigationBar::getInstance()->getChild("search_combo_box"); search_ctrl->clearHistory(); + + LLTeleportHistoryStorage::getInstance()->purgeItems(); + LLTeleportHistoryStorage::getInstance()->save(); } return false; diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp index cddc67cb0a..d3b013237b 100644 --- a/indra/newview/llgrouplist.cpp +++ b/indra/newview/llgrouplist.cpp @@ -35,24 +35,53 @@ #include "llgrouplist.h" // libs +#include "llbutton.h" +#include "lliconctrl.h" +#include "lltextbox.h" #include "lltrans.h" // newview #include "llagent.h" +#include "llgroupactions.h" +#include "llviewercontrol.h" // for gSavedSettings static LLDefaultChildRegistry::Register r("group_list"); +S32 LLGroupListItem::sIconWidth = 0; + +class LLGroupComparator : public LLFlatListView::ItemComparator +{ +public: + /** Returns true if item1 < item2, false otherwise */ + /*virtual*/ bool compare(const LLPanel* item1, const LLPanel* item2) const + { + std::string name1 = static_cast(item1)->getGroupName(); + std::string name2 = static_cast(item2)->getGroupName(); + + LLStringUtil::toUpper(name1); + LLStringUtil::toUpper(name2); + + return name1 < name2; + } +}; + +static const LLGroupComparator GROUP_COMPARATOR; LLGroupList::Params::Params() { - // Prevent the active group from being always first in the list. - online_go_first = false; + } LLGroupList::LLGroupList(const Params& p) -: LLAvatarList(p) +: LLFlatListView(p) { + mShowIcons = gSavedSettings.getBOOL("GroupListShowIcons"); + setCommitOnSelectionChange(true); + // TODO: implement context menu // display a context menu appropriate for a list of group names - setContextMenu(LLScrollListCtrl::MENU_GROUP); +// setContextMenu(LLScrollListCtrl::MENU_GROUP); + + // Set default sort order. + setComparator(&GROUP_COMPARATOR); } static bool findInsensitive(std::string haystack, const std::string& needle_upper) @@ -63,36 +92,185 @@ static bool findInsensitive(std::string haystack, const std::string& needle_uppe BOOL LLGroupList::update(const std::string& name_filter) { - LLCtrlListInterface *group_list = getListInterface(); const LLUUID& highlight_id = gAgent.getGroupID(); S32 count = gAgent.mGroups.count(); LLUUID id; - group_list->operateOnAll(LLCtrlListInterface::OP_DELETE); + clear(); for(S32 i = 0; i < count; ++i) { - // *TODO: check powers mask? id = gAgent.mGroups.get(i).mID; const LLGroupData& group_data = gAgent.mGroups.get(i); if (name_filter != LLStringUtil::null && !findInsensitive(group_data.mName, name_filter)) continue; - addItem(id, group_data.mName, highlight_id == id, ADD_BOTTOM); // ADD_SORTED can only sort by first column anyway + addNewItem(id, group_data.mName, group_data.mInsigniaID, highlight_id == id, ADD_BOTTOM); } - // Force sorting the list. - updateSort(); + // Sort the list. + sort(); // add "none" to list at top { std::string loc_none = LLTrans::getString("GroupsNone"); if (name_filter == LLStringUtil::null || findInsensitive(loc_none, name_filter)) - addItem(LLUUID::null, loc_none, highlight_id.isNull(), ADD_TOP); + addNewItem(LLUUID::null, loc_none, LLUUID::null, highlight_id.isNull(), ADD_TOP); + } - // Prevent the "none" item from being sorted. - setNeedsSort(false); + selectItemByUUID(highlight_id); + + return TRUE; +} + +void LLGroupList::toggleIcons() +{ + // Save the new value for new items to use. + mShowIcons = !mShowIcons; + gSavedSettings.setBOOL("GroupListShowIcons", mShowIcons); + + // Show/hide icons for all existing items. + std::vector items; + getItems(items); + for( std::vector::const_iterator it = items.begin(); it != items.end(); it++) + { + static_cast(*it)->setGroupIconVisible(mShowIcons); } +} + +////////////////////////////////////////////////////////////////////////// +// PRIVATE Section +////////////////////////////////////////////////////////////////////////// + +void LLGroupList::addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, BOOL is_bold, EAddPosition pos) +{ + LLGroupListItem* item = new LLGroupListItem(); + + item->setName(name); + item->setGroupID(id); + item->setGroupIconID(icon_id); +// item->setContextMenu(mContextMenu); + + item->childSetVisible("info_btn", false); + item->setGroupIconVisible(mShowIcons); + + addItem(item, id, pos); + +// setCommentVisible(false); +} + + +/************************************************************************/ +/* LLGroupListItem implementation */ +/************************************************************************/ + +LLGroupListItem::LLGroupListItem() +: LLPanel(), +mGroupIcon(NULL), +mGroupNameBox(NULL), +mInfoBtn(NULL), +//mContextMenu(NULL), //TODO: +mGroupID(LLUUID::null) +{ + LLUICtrlFactory::getInstance()->buildPanel(this, "panel_group_list_item.xml"); + + // Remember group icon width including its padding from the name text box, + // so that we can hide and show the icon again later. + if (!sIconWidth) + { + sIconWidth = mGroupNameBox->getRect().mLeft - mGroupIcon->getRect().mLeft; + } +} + +//virtual +BOOL LLGroupListItem::postBuild() +{ + mGroupIcon = getChild("group_icon"); + mGroupNameBox = getChild("group_name"); + + mInfoBtn = getChild("info_btn"); + mInfoBtn->setClickedCallback(boost::bind(&LLGroupListItem::onInfoBtnClick, this)); - group_list->selectByValue(highlight_id); return TRUE; } + +//virtual +void LLGroupListItem::setValue( const LLSD& value ) +{ + if (!value.isMap()) return; + if (!value.has("selected")) return; + childSetVisible("selected_icon", value["selected"]); +} + +void LLGroupListItem::onMouseEnter(S32 x, S32 y, MASK mask) +{ + childSetVisible("hovered_icon", true); + if (mGroupID.notNull()) // don't show the info button for the "none" group + mInfoBtn->setVisible(true); + + LLPanel::onMouseEnter(x, y, mask); +} + +void LLGroupListItem::onMouseLeave(S32 x, S32 y, MASK mask) +{ + childSetVisible("hovered_icon", false); + mInfoBtn->setVisible(false); + + LLPanel::onMouseLeave(x, y, mask); +} + +void LLGroupListItem::setName(const std::string& name) +{ + mGroupName = name; + mGroupNameBox->setValue(name); + mGroupNameBox->setToolTip(name); +} + +void LLGroupListItem::setGroupID(const LLUUID& group_id) +{ + mGroupID = group_id; + setActive(group_id == gAgent.getGroupID()); +} + +void LLGroupListItem::setGroupIconID(const LLUUID& group_icon_id) +{ + if (group_icon_id.notNull()) + { + mGroupIcon->setValue(group_icon_id); + } +} + +void LLGroupListItem::setGroupIconVisible(bool visible) +{ + // Already done? Then do nothing. + if (mGroupIcon->getVisible() == (BOOL)visible) + return; + + // Show/hide the group icon. + mGroupIcon->setVisible(visible); + + // Move the group name horizontally by icon size + its distance from the group name. + LLRect name_rect = mGroupNameBox->getRect(); + name_rect.mLeft += visible ? sIconWidth : -sIconWidth; + mGroupNameBox->setRect(name_rect); +} + +////////////////////////////////////////////////////////////////////////// +// Private Section +////////////////////////////////////////////////////////////////////////// +void LLGroupListItem::setActive(bool active) +{ + // Active group should be bold. + LLFontDescriptor new_desc(mGroupNameBox->getFont()->getFontDesc()); + + // *NOTE dzaporozhan + // On Windows LLFontGL::NORMAL will not remove LLFontGL::BOLD if font + // is predefined as bold (SansSerifSmallBold, for example) + new_desc.setStyle(active ? LLFontGL::BOLD : LLFontGL::NORMAL); + mGroupNameBox->setFont(LLFontGL::getFont(new_desc)); +} + +void LLGroupListItem::onInfoBtnClick() +{ + LLGroupActions::show(mGroupID); +} +//EOF diff --git a/indra/newview/llgrouplist.h b/indra/newview/llgrouplist.h index e893313f4b..7708b58de6 100644 --- a/indra/newview/llgrouplist.h +++ b/indra/newview/llgrouplist.h @@ -33,22 +33,61 @@ #ifndef LL_LLGROUPLIST_H #define LL_LLGROUPLIST_H -#include +#include "llflatlistview.h" +#include "llpanel.h" -#include "llavatarlist.h" - -// *TODO: derive from ListView when it's ready. -class LLGroupList: public LLAvatarList +class LLGroupList: public LLFlatListView { LOG_CLASS(LLGroupList); public: - struct Params : public LLInitParam::Block + struct Params : public LLInitParam::Block { Params(); }; - LLGroupList(const Params&); + LLGroupList(const Params& p); BOOL update(const std::string& name_filter = LLStringUtil::null); + void toggleIcons(); + bool getIconsVisible() const { return mShowIcons; } + +private: + void addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, BOOL is_bold, EAddPosition pos = ADD_BOTTOM); + + bool mShowIcons; }; +class LLButton; +class LLIconCtrl; +class LLTextBox; + +class LLGroupListItem : public LLPanel +{ +public: + LLGroupListItem(); + /*virtual*/ BOOL postBuild(); + /*virtual*/ void setValue(const LLSD& value); + void onMouseEnter(S32 x, S32 y, MASK mask); + void onMouseLeave(S32 x, S32 y, MASK mask); + + const LLUUID& getGroupID() const { return mGroupID; } + const std::string& getGroupName() const { return mGroupName; } + + void setName(const std::string& name); + void setGroupID(const LLUUID& group_id); + void setGroupIconID(const LLUUID& group_icon_id); + void setGroupIconVisible(bool visible); + +private: + void setActive(bool active); + void onInfoBtnClick(); + + LLTextBox* mGroupNameBox; + LLUUID mGroupID; + LLIconCtrl* mGroupIcon; + LLButton* mInfoBtn; + + std::string mGroupName; + + static S32 sIconWidth; // icon width + padding +}; #endif // LL_LLGROUPLIST_H diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp new file mode 100644 index 0000000000..29102feb64 --- /dev/null +++ b/indra/newview/llimfloater.cpp @@ -0,0 +1,416 @@ +/** + * @file llimfloater.cpp + * @brief LLIMFloater class definition + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 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$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llimfloater.h" + +#include "llagent.h" +#include "llappviewer.h" +#include "llbutton.h" +#include "llbottomtray.h" +#include "llchannelmanager.h" +#include "llchiclet.h" +#include "llfloaterreg.h" +#include "llimview.h" +#include "lllineeditor.h" +#include "llpanelimcontrolpanel.h" +#include "llscreenchannel.h" +#include "lltrans.h" +#include "llviewertexteditor.h" +#include "llviewerwindow.h" + + + +LLIMFloater::LLIMFloater(const LLUUID& session_id) + : LLDockableFloater(NULL, session_id), + mControlPanel(NULL), + mSessionID(session_id), + mLastMessageIndex(-1), + mLastFromName(), + mDialog(IM_NOTHING_SPECIAL), + mHistoryEditor(NULL), + mInputEditor(NULL), + mPositioned(false) +{ + LLIMModel::LLIMSession* session = get_if_there(LLIMModel::instance().sSessionsMap, mSessionID, (LLIMModel::LLIMSession*)NULL); + if(session) + { + mDialog = session->mType; + } + + if (mDialog == IM_NOTHING_SPECIAL) + { + mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelIMControl, this); + } + else + { + mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this); + } +// LLUICtrlFactory::getInstance()->buildFloater(this, "floater_im_session.xml"); + + gFocusMgr.addFocusChangeCallback(boost::bind(&LLIMFloater::focusChangeCallback, this)); + + mCloseSignal.connect(boost::bind(&LLIMFloater::onClose, this)); +} + +void LLIMFloater::onClose() +{ + LLIMModel::instance().sendLeaveSession(mSessionID, mOtherParticipantUUID); + gIMMgr->removeSession(mSessionID); +} + +/* static */ +void LLIMFloater::newIMCallback(const LLSD& data){ + + if (data["num_unread"].asInteger() > 0) + { + LLUUID session_id = data["session_id"].asUUID(); + + LLIMFloater* floater = LLFloaterReg::findTypedInstance("impanel", session_id); + if (floater == NULL) + { + llwarns << "new_im_callback for non-existent session_id " << session_id << llendl; + return; + } + + // update if visible, otherwise will be updated when opened + if (floater->getVisible()) + { + floater->updateMessages(); + } + } +} + +void LLIMFloater::onSendMsg( LLUICtrl* ctrl, void* userdata ) +{ + LLIMFloater* self = (LLIMFloater*) userdata; + self->sendMsg(); +} + +void LLIMFloater::sendMsg() +{ + if (!gAgent.isGodlike() + && (mDialog == IM_NOTHING_SPECIAL) + && mOtherParticipantUUID.isNull()) + { + llinfos << "Cannot send IM to everyone unless you're a god." << llendl; + return; + } + + if (mInputEditor) + { + LLWString text = mInputEditor->getConvertedText(); + if(!text.empty()) + { + // Truncate and convert to UTF8 for transport + std::string utf8_text = wstring_to_utf8str(text); + utf8_text = utf8str_truncate(utf8_text, MAX_MSG_BUF_SIZE - 1); + + LLIMModel::sendMessage(utf8_text, + mSessionID, + mOtherParticipantUUID, + mDialog); + + mInputEditor->setText(LLStringUtil::null); + + updateMessages(); + } + } +} + + + +LLIMFloater::~LLIMFloater() +{ +} + +//virtual +BOOL LLIMFloater::postBuild() +{ + LLIMModel::LLIMSession* session = get_if_there(LLIMModel::instance().sSessionsMap, mSessionID, (LLIMModel::LLIMSession*)NULL); + if(session) + { + mOtherParticipantUUID = session->mOtherParticipantID; + mControlPanel->setID(session->mOtherParticipantID); + } + + LLButton* slide_left = getChild("slide_left_btn"); + slide_left->setVisible(mControlPanel->getVisible()); + slide_left->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); + + LLButton* slide_right = getChild("slide_right_btn"); + slide_right->setVisible(!mControlPanel->getVisible()); + slide_right->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); + + mInputEditor = getChild("chat_editor"); + mInputEditor->setMaxTextLength(1023); + // enable line history support for instant message bar + mInputEditor->setEnableLineHistory(TRUE); + + mInputEditor->setFocusReceivedCallback( onInputEditorFocusReceived, this ); + mInputEditor->setFocusLostCallback( onInputEditorFocusLost, this ); + mInputEditor->setKeystrokeCallback( onInputEditorKeystroke, this ); + mInputEditor->setCommitOnFocusLost( FALSE ); + mInputEditor->setRevertOnEsc( FALSE ); + mInputEditor->setReplaceNewlinesWithSpaces( FALSE ); + + childSetCommitCallback("chat_editor", onSendMsg, this); + + mHistoryEditor = getChild("im_text"); + mHistoryEditor->setParseHTML(TRUE); + + setTitle(LLIMModel::instance().getName(mSessionID)); + setDocked(true); + + return LLDockableFloater::postBuild(); +} + + + +// static +void* LLIMFloater::createPanelIMControl(void* userdata) +{ + LLIMFloater *self = (LLIMFloater*)userdata; + self->mControlPanel = new LLPanelIMControlPanel(); + self->mControlPanel->setXMLFilename("panel_im_control_panel.xml"); + return self->mControlPanel; +} + + +// static +void* LLIMFloater::createPanelGroupControl(void* userdata) +{ + LLIMFloater *self = (LLIMFloater*)userdata; + self->mControlPanel = new LLPanelGroupControlPanel(); + self->mControlPanel->setXMLFilename("panel_group_control_panel.xml"); + return self->mControlPanel; +} + + + +void LLIMFloater::focusChangeCallback() +{ + // hide docked floater if user clicked inside in-world area + if (isDocked() && gFocusMgr.getKeyboardFocus() == NULL) + { + setVisible(false); + } +} + +void LLIMFloater::onSlide() +{ + LLPanel* im_control_panel = getChild("panel_im_control_panel"); + im_control_panel->setVisible(!im_control_panel->getVisible()); + + getChild("slide_left_btn")->setVisible(im_control_panel->getVisible()); + getChild("slide_right_btn")->setVisible(!im_control_panel->getVisible()); +} + +//static +LLIMFloater* LLIMFloater::show(const LLUUID& session_id) +{ + //hide all + LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel"); + for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); + iter != inst_list.end(); ++iter) + { + LLIMFloater* floater = dynamic_cast(*iter); + if (floater && floater->isDocked()) + { + floater->setVisible(false); + } + } + + LLIMFloater* floater = LLFloaterReg::showTypedInstance("impanel", session_id); + + floater->updateMessages(); + floater->mInputEditor->setFocus(TRUE); + + if (floater->getDockControl() == NULL) + { + LLChiclet* chiclet = + LLBottomTray::getInstance()->getChicletPanel()->findChiclet( + session_id); + if (chiclet == NULL) + { + llerror("Dock chiclet for LLIMFloater doesn't exists", 0); + } + else + { + LLBottomTray::getInstance()->getChicletPanel()->scrollToChiclet(chiclet); + } + + floater->setDockControl(new LLDockControl(chiclet, floater, floater->getDockTongue(), + LLDockControl::TOP, boost::bind(&LLIMFloater::getEnabledRect, floater, _1))); + } + + return floater; +} + +void LLIMFloater::getEnabledRect(LLRect& rect) +{ + rect = gViewerWindow->getWorldViewRect(); +} + +void LLIMFloater::setDocked(bool docked, bool pop_on_undock) +{ + // update notification channel state + LLNotificationsUI::LLScreenChannel* channel = LLNotificationsUI::LLChannelManager::getInstance()-> + findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID"))); + LLDockableFloater::setDocked(docked, pop_on_undock); + + // update notification channel state + if(channel) + { + channel->updateShowToastsState(); + } +} + +void LLIMFloater::setVisible(BOOL visible) +{ + LLNotificationsUI::LLScreenChannel* channel = LLNotificationsUI::LLChannelManager::getInstance()-> + findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID"))); + LLDockableFloater::setVisible(visible); + + // update notification channel state + if(channel) + { + channel->updateShowToastsState(); + } +} + +//static +bool LLIMFloater::toggle(const LLUUID& session_id) +{ + LLIMFloater* floater = LLFloaterReg::findTypedInstance("impanel", session_id); + if (floater && floater->getVisible()) + { + // clicking on chiclet to close floater just hides it to maintain existing + // scroll/text entry state + floater->setVisible(false); + return false; + } + else + { + // ensure the list of messages is updated when floater is made visible + show(session_id); + // update number of unread notifications in the SysWell + LLBottomTray::getInstance()->getSysWell()->updateUreadIMNotifications(); + return true; + } +} + +void LLIMFloater::updateMessages() +{ + std::list messages = LLIMModel::instance().getMessages(mSessionID, mLastMessageIndex+1); + std::string agent_name; + + gCacheName->getFullName(gAgentID, agent_name); + + if (messages.size()) + { + LLUIColor divider_color = LLUIColorTable::instance().getColor("LtGray_50"); + LLUIColor chat_color = LLUIColorTable::instance().getColor("IMChatColor"); + + std::ostringstream message; + std::list::const_reverse_iterator iter = messages.rbegin(); + std::list::const_reverse_iterator iter_end = messages.rend(); + for (; iter != iter_end; ++iter) + { + LLSD msg = *iter; + + const bool prepend_newline = true; + std::string from = msg["from"].asString(); + if (from == agent_name) + from = LLTrans::getString("You"); + if (mLastFromName != from) + { + message << from << " ----- " << msg["time"].asString(); + mHistoryEditor->appendColoredText(message.str(), false, + prepend_newline, divider_color); + message.str(""); + mLastFromName = from; + } + + message << msg["message"].asString(); + mHistoryEditor->appendColoredText(message.str(), false, + prepend_newline, chat_color); + message.str(""); + + mLastMessageIndex = msg["index"].asInteger(); + } + + mHistoryEditor->setCursorAndScrollToEnd(); + } +} + +// static +void LLIMFloater::onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata ) +{ + LLIMFloater* self= (LLIMFloater*) userdata; + + //in disconnected state IM input editor should be disabled + self->mInputEditor->setEnabled(!gDisconnected); + + self->mHistoryEditor->setCursorAndScrollToEnd(); +} + +// static +void LLIMFloater::onInputEditorFocusLost(LLFocusableElement* caller, void* userdata) +{ + LLIMFloater* self = (LLIMFloater*) userdata; + self->setTyping(FALSE); +} + +// static +void LLIMFloater::onInputEditorKeystroke(LLLineEditor* caller, void* userdata) +{ + LLIMFloater* self = (LLIMFloater*)userdata; + std::string text = self->mInputEditor->getText(); + if (!text.empty()) + { + self->setTyping(TRUE); + } + else + { + // Deleting all text counts as stopping typing. + self->setTyping(FALSE); + } +} + + +//just a stub for now +void LLIMFloater::setTyping(BOOL typing) +{ +} + diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h new file mode 100644 index 0000000000..276f38e829 --- /dev/null +++ b/indra/newview/llimfloater.h @@ -0,0 +1,112 @@ +/** + * @file llimfloater.h + * @brief LLIMFloater class definition + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 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_IMFLOATER_H +#define LL_IMFLOATER_H + +#include "lldockablefloater.h" + +class LLLineEditor; +class LLPanelChatControlPanel; +class LLViewerTextEditor; + + +/** + * Individual IM window that appears at the bottom of the screen, + * optionally "docked" to the bottom tray. + */ +class LLIMFloater : public LLDockableFloater +{ +public: + LLIMFloater(const LLUUID& session_id); + + virtual ~LLIMFloater(); + + // LLView overrides + /*virtual*/ BOOL postBuild(); + /*virtual*/ void setVisible(BOOL visible); + + // LLFloater overrides + /*virtual*/ void setDocked(bool docked, bool pop_on_undock = true); + + // Make IM conversion visible and update the message history + static LLIMFloater* show(const LLUUID& session_id); + + // Toggle panel specified by session_id + // Returns true iff panel became visible + static bool toggle(const LLUUID& session_id); + + // get new messages from LLIMModel + void updateMessages(); + static void onSendMsg( LLUICtrl*, void*); + void sendMsg(); + + // callback for LLIMModel on new messages + // route to specific floater if it is visible + static void newIMCallback(const LLSD& data); + + // called when docked floater's position has been set by chiclet + void setPositioned(bool b) { mPositioned = b; }; + + // handler for a CLOSE signal + void onClose(); + + +private: + + static void onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata ); + static void onInputEditorFocusLost(LLFocusableElement* caller, void* userdata); + static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata); + void setTyping(BOOL typing); + void onSlide(); + static void* createPanelIMControl(void* userdata); + static void* createPanelGroupControl(void* userdata); + void focusChangeCallback(); + void getEnabledRect(LLRect& rect); + + LLPanelChatControlPanel* mControlPanel; + LLUUID mSessionID; + S32 mLastMessageIndex; + + // username of last user who added text to this conversation, used to + // suppress duplicate username divider bars + std::string mLastFromName; + + EInstantMessage mDialog; + LLUUID mOtherParticipantUUID; + LLViewerTextEditor* mHistoryEditor; + LLLineEditor* mInputEditor; + bool mPositioned; +}; + + +#endif // LL_IMFLOATER_H diff --git a/indra/newview/llimhandler.cpp b/indra/newview/llimhandler.cpp index a47477c446..46067c081f 100644 --- a/indra/newview/llimhandler.cpp +++ b/indra/newview/llimhandler.cpp @@ -36,29 +36,18 @@ #include "llnotificationhandler.h" #include "llagentdata.h" -#include "llbottomtray.h" -#include "llviewercontrol.h" #include "lltoastimpanel.h" +#include "llviewerwindow.h" using namespace LLNotificationsUI; //-------------------------------------------------------------------------- -LLIMHandler::LLIMHandler() +LLIMHandler::LLIMHandler(e_notification_type type, const LLSD& id) { - - // getting a Chiclet and creating params for a channel - LLBottomTray* tray = LLBottomTray::getInstance(); - mChiclet = tray->getSysWell(); - - LLChannelManager::Params p; - // *TODO: createNotificationChannel method - p.id = LLUUID(gSavedSettings.getString("NotificationChannelUUID")); - p.channel_right_bound = tray->getRect().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); - p.channel_width = gSavedSettings.getS32("NotifyBoxWidth"); + mType = type; // Getting a Channel for our notifications - mChannel = LLChannelManager::getInstance()->createChannel(p); - + mChannel = LLChannelManager::getInstance()->createNotificationChannel(); } //-------------------------------------------------------------------------- @@ -67,12 +56,31 @@ LLIMHandler::~LLIMHandler() } //-------------------------------------------------------------------------- -void LLIMHandler::processNotification(const LLSD& notify) +void LLIMHandler::initChannel() +{ + S32 channel_right_bound = gViewerWindow->getWorldViewRect().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); + S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth"); + mChannel->init(channel_right_bound - channel_width, channel_right_bound); +} + +//-------------------------------------------------------------------------- +bool LLIMHandler::processNotification(const LLSD& notify) { + if(!mChannel) + { + return false; + } + LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); if(!notification) - return; + return false; + + // arrange a channel on a screen + if(!mChannel->getVisible()) + { + initChannel(); + } if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") { @@ -95,40 +103,31 @@ void LLIMHandler::processNotification(const LLSD& notify) LLToastIMPanel* im_box = new LLToastIMPanel(im_p); LLToast::Params p; - p.id = notification->getID(); + p.notif_id = notification->getID(); + p.session_id = im_p.session_id; p.notification = notification; p.panel = im_box; p.can_be_stored = false; - p.on_toast_destroy = boost::bind(&LLIMHandler::onToastDestroy, this, _1); + p.on_delete_toast = boost::bind(&LLIMHandler::onDeleteToast, this, _1); mChannel->addToast(p); - - static_cast(mChiclet)->updateUreadIMNotifications(); + // send a signal to the counter manager; + mNewNotificationSignal(); } else if (notify["sigtype"].asString() == "delete") { mChannel->killToastByNotificationID(notification->getID()); } + return true; } //-------------------------------------------------------------------------- -void LLIMHandler::onToastDestroy(LLToast* toast) -{ - toast->closeFloater(); - static_cast(mChiclet)->updateUreadIMNotifications(); -} - -//-------------------------------------------------------------------------- -void LLIMHandler::onChicletClick(void) +void LLIMHandler::onDeleteToast(LLToast* toast) { + // send a signal to the counter manager + mDelNotificationSignal(); } //-------------------------------------------------------------------------- -void LLIMHandler::onChicletClose(void) -{ -} - -//-------------------------------------------------------------------------- - diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 0efe9b9849..de4faf72f5 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -48,6 +48,7 @@ #include "llbutton.h" #include "llbottomtray.h" #include "llcallingcard.h" +#include "llchannelmanager.h" #include "llchat.h" #include "llchiclet.h" #include "llconsole.h" @@ -81,6 +82,7 @@ #include "llhttpclient.h" #include "llmutelist.h" #include "llstylemap.h" +#include "llappviewer.h" // // Constants @@ -1986,318 +1988,4 @@ bool LLFloaterIMPanel::onConfirmForceCloseError(const LLSD& notification, const } return false; } - - -LLIMFloater::LLIMFloater(const LLUUID& session_id) - : LLDockableFloater(NULL, session_id), - mControlPanel(NULL), - mSessionID(session_id), - mLastMessageIndex(-1), - mLastFromName(), - mDialog(IM_NOTHING_SPECIAL), - mHistoryEditor(NULL), - mInputEditor(NULL), - mPositioned(false) -{ - LLIMModel::LLIMSession* session = get_if_there(LLIMModel::instance().sSessionsMap, mSessionID, (LLIMModel::LLIMSession*)NULL); - if(session) - { - mDialog = session->mType; - } - - if (mDialog == IM_NOTHING_SPECIAL) - { - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelIMControl, this); - } - else - { - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this); - } -// LLUICtrlFactory::getInstance()->buildFloater(this, "floater_im_session.xml"); -} - -/* static */ -void LLIMFloater::newIMCallback(const LLSD& data){ - - if (data["num_unread"].asInteger() > 0) - { - LLUUID session_id = data["session_id"].asUUID(); - - LLIMFloater* floater = LLFloaterReg::findTypedInstance("impanel", session_id); - if (floater == NULL) - { - llwarns << "new_im_callback for non-existent session_id " << session_id << llendl; - return; - } - - // update if visible, otherwise will be updated when opened - if (floater->getVisible()) - { - floater->updateMessages(); - } - } -} - -void LLIMFloater::onSendMsg( LLUICtrl* ctrl, void* userdata ) -{ - LLIMFloater* self = (LLIMFloater*) userdata; - self->sendMsg(); -} - -void LLIMFloater::sendMsg() -{ - if (!gAgent.isGodlike() - && (mDialog == IM_NOTHING_SPECIAL) - && mOtherParticipantUUID.isNull()) - { - llinfos << "Cannot send IM to everyone unless you're a god." << llendl; - return; - } - - if (mInputEditor) - { - LLWString text = mInputEditor->getConvertedText(); - if(!text.empty()) - { - // Truncate and convert to UTF8 for transport - std::string utf8_text = wstring_to_utf8str(text); - utf8_text = utf8str_truncate(utf8_text, MAX_MSG_BUF_SIZE - 1); - - LLIMModel::sendMessage(utf8_text, - mSessionID, - mOtherParticipantUUID, - mDialog); - - mInputEditor->setText(LLStringUtil::null); - - updateMessages(); - } - } -} - - - -LLIMFloater::~LLIMFloater() -{ -} - -//virtual -BOOL LLIMFloater::postBuild() -{ - LLIMModel::LLIMSession* session = get_if_there(LLIMModel::instance().sSessionsMap, mSessionID, (LLIMModel::LLIMSession*)NULL); - if(session) - { - mOtherParticipantUUID = session->mOtherParticipantID; - mControlPanel->setID(session->mOtherParticipantID); - } - - LLButton* slide_left = getChild("slide_left_btn"); - slide_left->setVisible(mControlPanel->getVisible()); - slide_left->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); - - LLButton* slide_right = getChild("slide_right_btn"); - slide_right->setVisible(!mControlPanel->getVisible()); - slide_right->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); - - mInputEditor = getChild("chat_editor"); - mInputEditor->setMaxTextLength(1023); - // enable line history support for instant message bar - mInputEditor->setEnableLineHistory(TRUE); - - mInputEditor->setFocusReceivedCallback( onInputEditorFocusReceived, this ); - mInputEditor->setFocusLostCallback( onInputEditorFocusLost, this ); - mInputEditor->setKeystrokeCallback( onInputEditorKeystroke, this ); - mInputEditor->setCommitOnFocusLost( FALSE ); - mInputEditor->setRevertOnEsc( FALSE ); - mInputEditor->setReplaceNewlinesWithSpaces( FALSE ); - - childSetCommitCallback("chat_editor", onSendMsg, this); - - mHistoryEditor = getChild("im_text"); - mHistoryEditor->setParseHTML(TRUE); - - setTitle(LLIMModel::instance().getName(mSessionID)); - setDocked(true); - - return LLDockableFloater::postBuild(); -} - - - -// static -void* LLIMFloater::createPanelIMControl(void* userdata) -{ - LLIMFloater *self = (LLIMFloater*)userdata; - self->mControlPanel = new LLPanelIMControlPanel(); - self->mControlPanel->setXMLFilename("panel_im_control_panel.xml"); - return self->mControlPanel; -} - - -// static -void* LLIMFloater::createPanelGroupControl(void* userdata) -{ - LLIMFloater *self = (LLIMFloater*)userdata; - self->mControlPanel = new LLPanelGroupControlPanel(); - self->mControlPanel->setXMLFilename("panel_group_control_panel.xml"); - return self->mControlPanel; -} - -const U32 DOCK_ICON_HEIGHT = 6; - -//virtual -void LLIMFloater::onFocusLost() -{ - // spec says close if docked to bottom tray and user has clicked away - // (hence we are no longer focused) - if (isDocked()) - { - LLIMFloater* floater = LLFloaterReg::getTypedInstance("impanel", mSessionID); - if (floater) - { - floater->setVisible(false); - } - } -} - -void LLIMFloater::onSlide() -{ - LLPanel* im_control_panel = getChild("panel_im_control_panel"); - im_control_panel->setVisible(!im_control_panel->getVisible()); - - getChild("slide_left_btn")->setVisible(im_control_panel->getVisible()); - getChild("slide_right_btn")->setVisible(!im_control_panel->getVisible()); -} - -//static -LLIMFloater* LLIMFloater::show(const LLUUID& session_id) -{ - //hide all - LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel"); - for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); - iter != inst_list.end(); ++iter) - { - LLIMFloater* floater = dynamic_cast(*iter); - if (floater && floater->isDocked()) - { - floater->setVisible(false); - } - } - - LLIMFloater* floater = LLFloaterReg::showTypedInstance("impanel", session_id); - - floater->updateMessages(); - floater->mInputEditor->setFocus(TRUE); - - if (floater->getDockControl() == NULL) - { - LLView* chiclet = - LLBottomTray::getInstance()->getChicletPanel()->findChiclet( - session_id); - if (chiclet == NULL) - { - llerror("Dock chiclet for LLIMFloater doesn't exists", 0); - } - floater->setDockControl(new LLDockControl(chiclet, floater, floater->getDockTongue(), - LLDockControl::TOP, floater->isDocked())); - } - - return floater; -} - -//static -bool LLIMFloater::toggle(const LLUUID& session_id) -{ - LLIMFloater* floater = LLFloaterReg::findTypedInstance("impanel", session_id); - if (floater && floater->getVisible()) - { - // clicking on chiclet to close floater just hides it to maintain existing - // scroll/text entry state - floater->setVisible(false); - return false; - } - else - { - // ensure the list of messages is updated when floater is made visible - show(session_id); - // update number of unread notifications in the SysWell - LLBottomTray::getInstance()->getSysWell()->updateUreadIMNotifications(); - return true; - } -} - -void LLIMFloater::updateMessages() -{ - std::list messages = LLIMModel::instance().getMessages(mSessionID, mLastMessageIndex+1); - - if (messages.size()) - { - LLUIColor divider_color = LLUIColorTable::instance().getColor("LtGray_50"); - LLUIColor chat_color = LLUIColorTable::instance().getColor("IMChatColor"); - - std::ostringstream message; - std::list::const_reverse_iterator iter = messages.rbegin(); - std::list::const_reverse_iterator iter_end = messages.rend(); - for (; iter != iter_end; ++iter) - { - LLSD msg = *iter; - - const bool prepend_newline = true; - std::string from = msg["from"].asString(); - if (mLastFromName != from) - { - message << from << " ----- " << msg["time"].asString(); - mHistoryEditor->appendColoredText(message.str(), false, - prepend_newline, divider_color); - message.str(""); - mLastFromName = from; - } - - message << msg["message"].asString(); - mHistoryEditor->appendColoredText(message.str(), false, - prepend_newline, chat_color); - message.str(""); - - mLastMessageIndex = msg["index"].asInteger(); - } - - mHistoryEditor->setCursorAndScrollToEnd(); - } -} - -// static -void LLIMFloater::onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata ) -{ - LLIMFloater* self= (LLIMFloater*) userdata; - self->mHistoryEditor->setCursorAndScrollToEnd(); -} - -// static -void LLIMFloater::onInputEditorFocusLost(LLFocusableElement* caller, void* userdata) -{ - LLIMFloater* self = (LLIMFloater*) userdata; - self->setTyping(FALSE); -} - -// static -void LLIMFloater::onInputEditorKeystroke(LLLineEditor* caller, void* userdata) -{ - LLIMFloater* self = (LLIMFloater*)userdata; - std::string text = self->mInputEditor->getText(); - if (!text.empty()) - { - self->setTyping(TRUE); - } - else - { - // Deleting all text counts as stopping typing. - self->setTyping(FALSE); - } -} - - -//just a stub for now -void LLIMFloater::setTyping(BOOL typing) -{ -} diff --git a/indra/newview/llimpanel.h b/indra/newview/llimpanel.h index 1d69f1567c..dbf5e1cb6a 100644 --- a/indra/newview/llimpanel.h +++ b/indra/newview/llimpanel.h @@ -360,68 +360,4 @@ private: void disableWhileSessionStarting(); }; - -// Individual IM window that appears at the bottom of the screen, -// optionally "docked" to the bottom tray. -class LLIMFloater : public LLDockableFloater -{ -public: - LLIMFloater(const LLUUID& session_id); - - virtual ~LLIMFloater(); - - // LLView overrides - /*virtual*/ BOOL postBuild(); - - // Floater should close when user clicks away to other UI area, - // hence causing focus loss. - /*virtual*/ void onFocusLost(); - - // Make IM conversion visible and update the message history - static LLIMFloater* show(const LLUUID& session_id); - - // Toggle panel specified by session_id - // Returns true iff panel became visible - static bool toggle(const LLUUID& session_id); - - // get new messages from LLIMModel - void updateMessages(); - static void onSendMsg( LLUICtrl*, void*); - void sendMsg(); - - // callback for LLIMModel on new messages - // route to specific floater if it is visible - static void newIMCallback(const LLSD& data); - - // called when docked floater's position has been set by chiclet - void setPositioned(bool b) { mPositioned = b; }; - - - -private: - - static void onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata ); - static void onInputEditorFocusLost(LLFocusableElement* caller, void* userdata); - static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata); - void setTyping(BOOL typing); - void onSlide(); - static void* createPanelIMControl(void* userdata); - static void* createPanelGroupControl(void* userdata); - - LLPanelChatControlPanel* mControlPanel; - LLUUID mSessionID; - S32 mLastMessageIndex; - // username of last user who added text to this conversation, used to - // suppress duplicate username divider bars - std::string mLastFromName; - EInstantMessage mDialog; - LLUUID mOtherParticipantUUID; - LLViewerTextEditor* mHistoryEditor; - LLLineEditor* mInputEditor; - bool mPositioned; -}; - - - - #endif // LL_IMPANEL_H diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 9ef98afe94..3cf78f957b 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -53,6 +53,7 @@ #include "llfloaterchatterbox.h" #include "llavataractions.h" #include "llhttpnode.h" +#include "llimfloater.h" #include "llimpanel.h" #include "llresizebar.h" #include "lltabcontainer.h" @@ -70,6 +71,7 @@ #include "llnotify.h" #include "llviewerregion.h" #include "lltrans.h" +#include "llrecentpeople.h" #include "llfirstuse.h" #include "llagentui.h" @@ -90,6 +92,11 @@ std::map LLIMModel::sSessionsMap; void toast_callback(const LLSD& msg){ + // do not show toast in busy mode + if (gAgent.getBusy()) + { + return; + } //we send notifications to reset counter also if (msg["num_unread"].asInteger()) @@ -101,8 +108,7 @@ void toast_callback(const LLSD& msg){ args["FROM_ID"] = msg["from_id"]; args["SESSION_ID"] = msg["session_id"]; - //LLNotifications::instance().add("IMToast", args, LLSD(), boost::bind(&LLFloaterChatterBox::onOpen, LLFloaterChatterBox::getInstance(), msg["session_id"].asUUID())); - LLNotifications::instance().add("IMToast", args, LLSD(), boost::bind(&LLIMFloater::toggle, msg["session_id"].asUUID())); + LLNotifications::instance().add("IMToast", args, LLSD(), boost::bind(&LLIMFloater::show, msg["session_id"].asUUID())); } } @@ -1345,8 +1351,15 @@ LLUUID LLIMMgr::addSession( // This removes the panel referenced by the uuid, and then restores // internal consistency. The internal pointer is not deleted? Did you mean // a pointer to the corresponding LLIMSession? Session data is cleared now. -void LLIMMgr::removeSession(const LLUUID& session_id) +// Put a copy of UUID to avoid problem when passed reference becames invalid +// if it has been come from the object removed in observer. +void LLIMMgr::removeSession(LLUUID session_id) { + if (mBeingRemovedSessionID == session_id) + { + return; + } + LLFloaterIMPanel* floater = findFloaterBySession(session_id); if(floater) { diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 4eb743b1ac..219af0705d 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -38,7 +38,6 @@ #include "llinstantmessage.h" #include "lluuid.h" #include "llmultifloater.h" -#include "llrecentpeople.h" class LLFloaterChatterBox; class LLUUID; @@ -159,7 +158,7 @@ public: // This removes the panel referenced by the uuid, and then // restores internal consistency. The internal pointer is not // deleted. - void removeSession(const LLUUID& session_id); + void removeSession(LLUUID session_id); void inviteToSession( const LLUUID& session_id, diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 40c5a243cc..e5cf8ccf66 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -3084,8 +3084,15 @@ void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags) items.push_back(std::string("Landmark Separator")); items.push_back(std::string("Teleport To Landmark")); - hideContextEntries(menu, items, disabled_items); + // Disable "About Landmark" menu item for + // multiple landmarks selected. Only one landmark + // info panel can be shown at a time. + if ((flags & FIRST_SELECTED_ITEM) == 0) + { + disabled_items.push_back(std::string("Teleport To Landmark")); + } + hideContextEntries(menu, items, disabled_items); } // Convenience function for the two functions below. diff --git a/indra/newview/lllandmarkactions.cpp b/indra/newview/lllandmarkactions.cpp index df9aa32d1b..2ad83c76b5 100644 --- a/indra/newview/lllandmarkactions.cpp +++ b/indra/newview/lllandmarkactions.cpp @@ -52,10 +52,10 @@ #include "llinventorymodel.h" #include "llagentui.h" -// Returns true if the given inventory item is a landmark pointing to the current parcel. -// Used to filter inventory items. -class LLIsAgentParcelLandmark : public LLInventoryCollectFunctor + +class LLFetchlLandmarkByAgentPos : public LLInventoryCollectFunctor { + public: /*virtual*/ bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) { @@ -69,8 +69,11 @@ public: LLVector3d landmark_global_pos; if (!landmark->getGlobalPos(landmark_global_pos)) return false; - - return LLViewerParcelMgr::getInstance()->inAgentParcel(landmark_global_pos); + LLVector3d a_pos = gAgent.getPositionGlobal(); + //we have to round off each coordinates to compare positions properly + return llround(a_pos.mdV[VX]) == llround(landmark_global_pos.mdV[VX]) + && llround(a_pos.mdV[VY]) == llround(landmark_global_pos.mdV[VY]) + && llround(a_pos.mdV[VZ]) == llround(landmark_global_pos.mdV[VZ]); } }; @@ -139,24 +142,22 @@ LLInventoryModel::item_array_t LLLandmarkActions::fetchLandmarksByName(std::stri bool LLLandmarkActions::landmarkAlreadyExists() { - // Determine whether there are landmarks pointing to the current parcel. - LLInventoryModel::item_array_t items; - collectParcelLandmark(items); - return !items.empty(); + // Determine whether there are landmarks pointing to the current global agent position. + return findLandmarkForAgentPos() != NULL; } -LLViewerInventoryItem* LLLandmarkActions::findLandmarkForAgentParcel() +LLViewerInventoryItem* LLLandmarkActions::findLandmarkForAgentPos() { // Determine whether there are landmarks pointing to the current parcel. LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; - LLIsAgentParcelLandmark is_current_parcel_landmark; + LLFetchlLandmarkByAgentPos is_current_pos_landmark; gInventory.collectDescendentsIf(gInventory.getRootFolderID(), cats, items, LLInventoryModel::EXCLUDE_TRASH, - is_current_parcel_landmark); + is_current_pos_landmark); if(items.empty()) { @@ -287,13 +288,3 @@ bool LLLandmarkActions::getLandmarkGlobalPos(const LLUUID& landmarkInventoryItem return landmark->getGlobalPos(posGlobal); } - -void LLLandmarkActions::collectParcelLandmark(LLInventoryModel::item_array_t& items){ - LLInventoryModel::cat_array_t cats; - LLIsAgentParcelLandmark is_current_parcel_landmark; - gInventory.collectDescendentsIf(gInventory.getRootFolderID(), - cats, - items, - LLInventoryModel::EXCLUDE_TRASH, - is_current_parcel_landmark); -} diff --git a/indra/newview/lllandmarkactions.h b/indra/newview/lllandmarkactions.h index c74072c0f4..ce3ed76090 100644 --- a/indra/newview/lllandmarkactions.h +++ b/indra/newview/lllandmarkactions.h @@ -53,12 +53,12 @@ public: static bool landmarkAlreadyExists(); /** - * @brief Searches landmark for parcel agent is currently in. - * @return Returns landmark for agent parcel or NULL. + * @brief Searches landmark for agent global position. + * @return Returns landmark or NULL. * * *TODO: dzaporozhan: There can be many landmarks for single parcel. */ - static LLViewerInventoryItem* findLandmarkForAgentParcel(); + static LLViewerInventoryItem* findLandmarkForAgentPos(); /** * @brief Checks whether agent has rights to create landmark for current parcel. @@ -77,14 +77,6 @@ public: const std::string& name, const std::string& desc, const LLUUID& folder_id); - - /** - * @brief Trying to find in inventory a landmark of the current parcel. - * Normally items should contain only one item, - * because we can create the only landmark per parcel according to Navigation spec. - */ - static void collectParcelLandmark(LLInventoryModel::item_array_t& items); - /** * @brief Creates SLURL for given global position. */ diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp index 1d9220cf3d..9d14a3fbdc 100644 --- a/indra/newview/lllocationinputctrl.cpp +++ b/indra/newview/lllocationinputctrl.cpp @@ -419,8 +419,7 @@ void LLLocationInputCtrl::onInfoButtonClicked() void LLLocationInputCtrl::onAddLandmarkButtonClicked() { - LLViewerInventoryItem* landmark = LLLandmarkActions::findLandmarkForAgentParcel(); - + LLViewerInventoryItem* landmark = LLLandmarkActions::findLandmarkForAgentPos(); // Landmark exists, open it for preview and edit if(landmark && landmark->getUUID().notNull()) { @@ -677,15 +676,16 @@ void LLLocationInputCtrl::onLocationContextMenuItemClicked(const LLSD& userdata) } else if (item == std::string("landmark")) { - LLInventoryModel::item_array_t items; - LLLandmarkActions::collectParcelLandmark(items); + LLViewerInventoryItem* landmark = LLLandmarkActions::findLandmarkForAgentPos(); - if(items.empty()) + if(!landmark) { LLSideTray::getInstance()->showPanel("panel_places", LLSD().insert("type", "create_landmark")); - }else{ + } + else + { LLSideTray::getInstance()->showPanel("panel_places", - LLSD().insert("type", "landmark").insert("id",items.get(0)->getUUID())); + LLSD().insert("type", "landmark").insert("id",landmark->getUUID())); } } else if (item == std::string("cut")) diff --git a/indra/newview/llmoveview.cpp b/indra/newview/llmoveview.cpp index 11c8b03f7f..d8f00ec370 100644 --- a/indra/newview/llmoveview.cpp +++ b/indra/newview/llmoveview.cpp @@ -71,7 +71,7 @@ const std::string BOTTOM_TRAY_BUTTON_NAME = "movement_btn"; // protected LLFloaterMove::LLFloaterMove(const LLSD& key) -: LLFloater(key), +: LLDockableFloater(NULL, false, key), mForwardButton(NULL), mBackwardButton(NULL), mTurnLeftButton(NULL), @@ -79,7 +79,8 @@ LLFloaterMove::LLFloaterMove(const LLSD& key) mMoveUpButton(NULL), mMoveDownButton(NULL), mStopFlyingButton(NULL), - mModeActionsPanel(NULL) + mModeActionsPanel(NULL), + mCurrentMode(MM_WALK) { } @@ -88,6 +89,7 @@ BOOL LLFloaterMove::postBuild() { setIsChrome(TRUE); + LLDockableFloater::postBuild(); mForwardButton = getChild("forward btn"); mForwardButton->setHeldDownDelay(MOVE_BUTTON_DELAY); @@ -134,8 +136,6 @@ BOOL LLFloaterMove::postBuild() initModeTooltips(); - updatePosition(); - initModeButtonMap(); initMovementMode(); @@ -145,6 +145,18 @@ BOOL LLFloaterMove::postBuild() return TRUE; } +// virtual +void LLFloaterMove::setEnabled(BOOL enabled) +{ + //we need to enable/disable only buttons, EXT-1061. + + // is called before postBuild() - use findChild here. + LLPanel *panel_actions = findChild("panel_actions"); + if (panel_actions) panel_actions->setEnabled(enabled); + + showModeButtons(enabled); +} + // static F32 LLFloaterMove::getYawRate( F32 time ) { @@ -266,6 +278,7 @@ void LLFloaterMove::onStopFlyingButtonClick() void LLFloaterMove::setMovementMode(const EMovementMode mode) { + mCurrentMode = mode; gAgent.setFlying(MM_FLY == mode); switch (mode) @@ -401,26 +414,49 @@ void LLFloaterMove::sUpdateFlyingStatus() void LLFloaterMove::showModeButtons(BOOL bShow) { - if (mModeActionsPanel->getVisible() == bShow) + // is called from setEnabled so can be called before postBuild(), check mModeActionsPanel agains to NULL + if (NULL == mModeActionsPanel || mModeActionsPanel->getVisible() == bShow) return; mModeActionsPanel->setVisible(bShow); + if (isDocked()) + { + return; + } + + updateHeight(bShow); +} + +void LLFloaterMove::updateHeight(bool show_mode_buttons) +{ + static bool size_changed = false; + static S32 origin_height = getRect().getHeight(); LLRect rect = getRect(); - static S32 height = mModeActionsPanel->getRect().getHeight(); + static S32 mode_panel_height = mModeActionsPanel->getRect().getHeight(); + S32 newHeight = getRect().getHeight(); - if (!bShow) + + if (!show_mode_buttons && origin_height == newHeight) { - newHeight -= height; + newHeight -= mode_panel_height; + size_changed = true; } - else + else if (show_mode_buttons && origin_height > newHeight) { - newHeight += height; + newHeight += mode_panel_height; + size_changed = true; } + + if (!size_changed) + return; + rect.setLeftTopAndSize(rect.mLeft, rect.mTop, rect.getWidth(), newHeight); reshape(rect.getWidth(), rect.getHeight()); setRect(rect); + size_changed = false; } + //static void LLFloaterMove::enableInstance(BOOL bEnable) { @@ -428,17 +464,42 @@ void LLFloaterMove::enableInstance(BOOL bEnable) if (instance) { instance->setEnabled(bEnable); - instance->showModeButtons(bEnable); } } void LLFloaterMove::onOpen(const LLSD& key) { - updatePosition(); + LLButton *anchor_panel = LLBottomTray::getInstance()->getChild("movement_btn"); + + if (gAgent.getFlying()) + { + setFlyingMode(TRUE); + showModeButtons(FALSE); + } + + if (gAgent.getAvatarObject() && gAgent.getAvatarObject()->isSitting()) + { + setSittingMode(TRUE); + showModeButtons(FALSE); + } + + setDockControl(new LLDockControl( + anchor_panel, this, + getDockTongue(), LLDockControl::TOP)); + + showQuickTips(mCurrentMode); sUpdateFlyingStatus(); } +//virtual +void LLFloaterMove::setDocked(bool docked, bool pop_on_undock/* = true*/) +{ + LLDockableFloater::setDocked(docked, pop_on_undock); + bool show_mode_buttons = isDocked() || !gAgent.getFlying(); + updateHeight(show_mode_buttons); +} + void LLFloaterMove::showQuickTips(const EMovementMode mode) { LLFirstTimeTipsManager::EFirstTimeTipType tipType = LLFirstTimeTipsManager::FTT_MOVE_WALK; @@ -543,6 +604,7 @@ void LLPanelStandStopFlying::setVisible(BOOL visible) if (visible) { updatePosition(); + getParent()->sendChildToFront(this); } LLPanel::setVisible(visible); @@ -600,6 +662,15 @@ void LLPanelStandStopFlying::updatePosition() S32 y = tray->getRect().getHeight(); + LLFloater *move_floater = LLFloaterReg::findInstance("moveview"); + if (move_floater) + { + if (move_floater->isDocked()) + { + y = move_floater->getRect().mBottom + getRect().getHeight(); + } + } + setOrigin(x, y); } diff --git a/indra/newview/llmoveview.h b/indra/newview/llmoveview.h index 6e6af9b693..584c7c494c 100644 --- a/indra/newview/llmoveview.h +++ b/indra/newview/llmoveview.h @@ -34,7 +34,7 @@ #define LL_LLMOVEVIEW_H // Library includes -#include "llfloater.h" +#include "lldockablefloater.h" class LLButton; class LLJoystickAgentTurn; @@ -44,7 +44,7 @@ class LLJoystickAgentSlide; // Classes // class LLFloaterMove -: public LLFloater +: public LLDockableFloater { friend class LLFloaterReg; @@ -54,6 +54,7 @@ private: public: /*virtual*/ BOOL postBuild(); + /*virtual*/ void setEnabled(BOOL enabled); static F32 getYawRate(F32 time); static void setFlyingMode(BOOL fly); void setFlyingModeImpl(BOOL fly); @@ -62,10 +63,7 @@ public: static void setSittingMode(BOOL bSitting); static void enableInstance(BOOL bEnable); /*virtual*/ void onOpen(const LLSD& key); - - // *HACK: due to hard enough to have this control aligned with "Move" button while resizing - // let update its position in each frame - /*virtual*/ void draw(){updatePosition(); LLFloater::draw();} + /*virtual*/ void setDocked(bool docked, bool pop_on_undock = true); static void sUpdateFlyingStatus(); @@ -98,6 +96,7 @@ private: void updateButtonsWithMovementMode(const EMovementMode newMode); void updatePosition(); void showModeButtons(BOOL bShow); + void updateHeight(bool show_mode_buttons); public: @@ -117,6 +116,7 @@ private: typedef std::map mode_control_button_map_t; mode_control_button_map_t mModeControlButtonMap; + EMovementMode mCurrentMode; }; diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 6150d5da37..b53bb586f3 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -482,7 +482,7 @@ BOOL LLNearbyChat::handleRightMouseDown(S32 x, S32 y, MASK mask) void LLNearbyChat::onOpen(const LLSD& key ) { - LLNotificationsUI::LLScreenChannel* chat_channel = LLNotificationsUI::LLChannelManager::getInstance()->getChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))); + LLNotificationsUI::LLScreenChannel* chat_channel = LLNotificationsUI::LLChannelManager::getInstance()->findChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))); if(chat_channel) { chat_channel->removeToastsFromChannel(); diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp index e348189ea9..cec4b9f7c7 100644 --- a/indra/newview/llnearbychatbar.cpp +++ b/indra/newview/llnearbychatbar.cpp @@ -201,6 +201,35 @@ BOOL LLNearbyChatBar::postBuild() mChatBox->setMaxTextLength(1023); mChatBox->setEnableLineHistory(TRUE); + // TODO: Initialization of the output monitor's params should be done via xml + const S32 MONITOR_RIGHT_PAD = 2; + + LLRect monitor_rect = LLRect(0, 18, 18, 0); + LLRect chatbox_rect = mChatBox->getRect(); + + S32 monitor_height = monitor_rect.getHeight(); + monitor_rect.mLeft = chatbox_rect.getWidth() - monitor_rect.getWidth() - MONITOR_RIGHT_PAD; + monitor_rect.mRight = chatbox_rect.getWidth() - MONITOR_RIGHT_PAD; + monitor_rect.mBottom = (chatbox_rect.getHeight() / 2) - (monitor_height / 2); + monitor_rect.mTop = monitor_rect.mBottom + monitor_height; + + LLOutputMonitorCtrl::Params monitor_params = LLOutputMonitorCtrl::Params(); + monitor_params.name = "output_monitor"; + monitor_params.draw_border(false); + monitor_params.rect(monitor_rect); + monitor_params.auto_update(true); + monitor_params.speaker_id(gAgentID); + + LLView::Follows follows = LLView::Follows(); + follows.flags = FOLLOWS_RIGHT; + monitor_params.follows = follows; + mOutputMonitor = LLUICtrlFactory::create(monitor_params); + mChatBox->addChild(mOutputMonitor); + + // never show "muted" because you can't mute yourself + mOutputMonitor->setIsMuted(false); + mOutputMonitor->setVisible(FALSE); + mTalkBtn = getChild("talk"); // Speak button should be initially disabled because @@ -593,6 +622,7 @@ LLWString LLNearbyChatBar::stripChannelNumber(const LLWString &mesg, S32* channe void LLNearbyChatBar::setPTTState(bool state) { mTalkBtn->setSpeakBtnToggleState(state); + mOutputMonitor->setVisible(state); } void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel) diff --git a/indra/newview/llnearbychatbar.h b/indra/newview/llnearbychatbar.h index f310740f42..1b71ad69f2 100644 --- a/indra/newview/llnearbychatbar.h +++ b/indra/newview/llnearbychatbar.h @@ -39,6 +39,7 @@ #include "llchat.h" #include "llchiclet.h" #include "llvoiceclient.h" +#include "lloutputmonitorctrl.h" class LLGestureComboBox : public LLComboBox @@ -114,6 +115,7 @@ protected: LLLineEditor* mChatBox; LLTalkButton* mTalkBtn; + LLOutputMonitorCtrl* mOutputMonitor; }; #endif diff --git a/indra/newview/llnearbychathandler.cpp b/indra/newview/llnearbychathandler.cpp index a1912655a3..7eb5d91e53 100644 --- a/indra/newview/llnearbychathandler.cpp +++ b/indra/newview/llnearbychathandler.cpp @@ -49,23 +49,25 @@ namespace LLNotificationsUI{ LLNearbyChatHandler::LLNearbyChatHandler(e_notification_type type, const LLSD& id) { mType = type; - LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat", LLSD()); - ///////////////////////////////////////////////////// - LLChannelManager::Params p; //TODO: check and correct + LLChannelManager::Params p; p.id = LLUUID(gSavedSettings.getString("NearByChatChannelUUID")); - p.channel_right_bound = nearby_chat->getRect().mRight; - p.channel_width = nearby_chat->getRect().mRight - 16; //HACK: 16 - ? - ///////////////////////////////////////////////////// - // Getting a Channel for our notifications - mChannel = LLChannelManager::getInstance()->createChannel(p); - mChannel->setFollows(FOLLOWS_LEFT | FOLLOWS_BOTTOM | FOLLOWS_TOP); + mChannel = LLChannelManager::getInstance()->getChannel(p); mChannel->setOverflowFormatString("You have %d unread nearby chat messages"); } LLNearbyChatHandler::~LLNearbyChatHandler() { } + +void LLNearbyChatHandler::initChannel() +{ + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat", LLSD()); + S32 channel_right_bound = nearby_chat->getRect().mRight; + S32 channel_width = nearby_chat->getRect().mRight - 16; //HACK: 16 - ? + mChannel->init(channel_right_bound - channel_width, channel_right_bound); +} + void LLNearbyChatHandler::processChat(const LLChat& chat_msg) { if(chat_msg.mMuted == TRUE) @@ -80,6 +82,12 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg) nearby_chat->addMessage(chat_msg); if(nearby_chat->getVisible()) return;//no need in toast if chat is visible + + // arrange a channel on a screen + if(!mChannel->getVisible()) + { + initChannel(); + } LLUUID id; id.generate(); @@ -95,34 +103,14 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg) item->setVisible(true); LLToast::Params p; - p.id = id; + p.notif_id = id; p.panel = item; - p.on_toast_destroy = boost::bind(&LLNearbyChatHandler::onToastDestroy, this, _1); - p.on_mouse_enter = boost::bind(&LLNearbyChatHandler::removeNearbyToastsAndShowChat, this); + p.on_delete_toast = boost::bind(&LLNearbyChatHandler::onDeleteToast, this, _1); mChannel->addToast(p); } -void LLNearbyChatHandler::onToastDestroy(LLToast* toast) +void LLNearbyChatHandler::onDeleteToast(LLToast* toast) { - if(toast) - toast->closeFloater(); -} - -void LLNearbyChatHandler::onChicletClick(void) -{ -} -void LLNearbyChatHandler::onChicletClose(void) -{ -} - -void LLNearbyChatHandler::removeNearbyToastsAndShowChat() -{ - /* - if(mChannel) - mChannel->removeToastsFromChannel(); - - LLFloaterReg::showTypedInstance("nearby_chat", LLSD()); - */ } } diff --git a/indra/newview/llnearbychathandler.h b/indra/newview/llnearbychathandler.h index 8fcd03689d..fb2abac6a4 100644 --- a/indra/newview/llnearbychathandler.h +++ b/indra/newview/llnearbychathandler.h @@ -46,12 +46,10 @@ public: virtual void processChat(const LLChat& chat_msg); - virtual void onToastDestroy(LLToast* toast); - virtual void onChicletClick(void); - virtual void onChicletClose(void); protected: - void removeNearbyToastsAndShowChat(); + virtual void onDeleteToast(LLToast* toast); + virtual void initChannel(); }; } diff --git a/indra/newview/llnotificationalerthandler.cpp b/indra/newview/llnotificationalerthandler.cpp index bd6c6b2308..3893eaa0d4 100644 --- a/indra/newview/llnotificationalerthandler.cpp +++ b/indra/newview/llnotificationalerthandler.cpp @@ -35,8 +35,8 @@ #include "llnotificationhandler.h" #include "lltoastnotifypanel.h" -#include "llbottomtray.h" #include "llviewercontrol.h" +#include "llviewerwindow.h" #include "lltoastalertpanel.h" @@ -47,17 +47,14 @@ LLAlertHandler::LLAlertHandler(e_notification_type type, const LLSD& id) : mIsMo { mType = type; - LLBottomTray* tray = LLBottomTray::getInstance(); LLChannelManager::Params p; p.id = LLUUID(gSavedSettings.getString("AlertChannelUUID")); - p.channel_right_bound = tray->getRect().getWidth() / 2; - p.channel_width = 0; p.display_toasts_always = true; - p.align = NA_CENTRE; + p.toast_align = NA_CENTRE; + p.channel_align = CA_CENTRE; // Getting a Channel for our notifications - mChannel = LLChannelManager::getInstance()->createChannel(p); - mChannel->setFollows(FOLLOWS_BOTTOM | FOLLOWS_TOP); + mChannel = LLChannelManager::getInstance()->getChannel(p); mChannel->setShowToasts(true); } @@ -67,21 +64,42 @@ LLAlertHandler::~LLAlertHandler() } //-------------------------------------------------------------------------- -void LLAlertHandler::processNotification(const LLSD& notify) +void LLAlertHandler::initChannel() { + S32 channel_right_bound = gViewerWindow->getWorldViewRect().getWidth() / 2; + mChannel->init(channel_right_bound, channel_right_bound); +} + +//-------------------------------------------------------------------------- +bool LLAlertHandler::processNotification(const LLSD& notify) +{ + if(!mChannel) + { + return false; + } + LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); + if(!notification) + return false; + + // arrange a channel on a screen + if(!mChannel->getVisible()) + { + initChannel(); + } + if (notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "load") { LLToastAlertPanel* alert_dialog = new LLToastAlertPanel(notification, mIsModal); LLToast::Params p; - p.id = notification->getID(); + p.notif_id = notification->getID(); p.notification = notification; p.panel = dynamic_cast(alert_dialog); p.enable_hide_btn = false; p.can_fade = false; p.is_modal = mIsModal; - p.on_toast_destroy = boost::bind(&LLAlertHandler::onToastDestroy, this, _1); + p.on_delete_toast = boost::bind(&LLAlertHandler::onDeleteToast, this, _1); mChannel->addToast(p); } else if (notify["sigtype"].asString() == "change") @@ -93,25 +111,14 @@ void LLAlertHandler::processNotification(const LLSD& notify) { mChannel->killToastByNotificationID(notification->getID()); } + return true; } //-------------------------------------------------------------------------- -void LLAlertHandler::onToastDestroy(LLToast* toast) -{ - toast->closeFloater(); -} - -//-------------------------------------------------------------------------- -void LLAlertHandler::onChicletClick(void) -{ -} - -//-------------------------------------------------------------------------- -void LLAlertHandler::onChicletClose(void) +void LLAlertHandler::onDeleteToast(LLToast* toast) { } //-------------------------------------------------------------------------- - diff --git a/indra/newview/llnotificationgrouphandler.cpp b/indra/newview/llnotificationgrouphandler.cpp index 31753efec9..c488d37ea5 100644 --- a/indra/newview/llnotificationgrouphandler.cpp +++ b/indra/newview/llnotificationgrouphandler.cpp @@ -34,11 +34,9 @@ #include "llnotificationhandler.h" #include "lltoastgroupnotifypanel.h" -#include "llbottomtray.h" #include "llgroupactions.h" #include "llviewercontrol.h" -#include "llfloaterreg.h" -#include "llsyswellwindow.h" +#include "llviewerwindow.h" using namespace LLNotificationsUI; @@ -47,16 +45,8 @@ LLGroupHandler::LLGroupHandler(e_notification_type type, const LLSD& id) { mType = type; - // getting a Chiclet and creating params for a channel - LLBottomTray* tray = LLBottomTray::getInstance(); - mChiclet = tray->getSysWell(); - LLChannelManager::Params p; - p.id = LLUUID(gSavedSettings.getString("NotificationChannelUUID")); - p.channel_right_bound = tray->getRect().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); - p.channel_width = gSavedSettings.getS32("NotifyBoxWidth"); - // Getting a Channel for our notifications - mChannel = LLChannelManager::getInstance()->createChannel(p); + mChannel = LLChannelManager::getInstance()->createNotificationChannel(); } //-------------------------------------------------------------------------- @@ -65,52 +55,63 @@ LLGroupHandler::~LLGroupHandler() } //-------------------------------------------------------------------------- -void LLGroupHandler::processNotification(const LLSD& notify) +void LLGroupHandler::initChannel() { + S32 channel_right_bound = gViewerWindow->getWorldViewRect().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); + S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth"); + mChannel->init(channel_right_bound - channel_width, channel_right_bound); +} + +//-------------------------------------------------------------------------- +bool LLGroupHandler::processNotification(const LLSD& notify) +{ + if(!mChannel) + { + return false; + } + LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); + + if(!notification) + return false; + + // arrange a channel on a screen + if(!mChannel->getVisible()) + { + initChannel(); + } + if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") { LLPanel* notify_box = new LLToastGroupNotifyPanel(notification); LLToast::Params p; - p.id = notification->getID(); + p.notif_id = notification->getID(); p.notification = notification; p.panel = notify_box; - p.on_toast_destroy = boost::bind(&LLGroupHandler::onToastDestroy, this, _1); + p.on_delete_toast = boost::bind(&LLGroupHandler::onDeleteToast, this, _1); mChannel->addToast(p); - static_cast(mChiclet)->incUreadSystemNotifications(); - - LLGroupActions::refresh_notices(); + // send a signal to the counter manager + mNewNotificationSignal(); + + LLGroupActions::refresh_notices(); } else if (notify["sigtype"].asString() == "delete") { mChannel->killToastByNotificationID(notification->getID()); } + return true; } //-------------------------------------------------------------------------- -void LLGroupHandler::onToastDestroy(LLToast* toast) -{ - static_cast(mChiclet)->decUreadSystemNotifications(); - - LLToastPanel* panel = dynamic_cast(toast->getPanel()); - LLFloaterReg::getTypedInstance("syswell_window")->removeItemByID(panel->getID()); - - // turning hovering off mannualy because onMouseLeave won't happen if a toast was closed using a keyboard - if(toast->hasFocus()) - mChannel->setHovering(false); - - toast->closeFloater(); -} - -//-------------------------------------------------------------------------- -void LLGroupHandler::onChicletClick(void) +void LLGroupHandler::onDeleteToast(LLToast* toast) { -} + // send a signal to the counter manager + mDelNotificationSignal(); -//-------------------------------------------------------------------------- -void LLGroupHandler::onChicletClose(void) -{ + // send a signal to a listener to let him perform some action + // in this case listener is a SysWellWindow and it will remove a corresponding item from its list + mNotificationIDSignal(toast->getNotificationID()); } //-------------------------------------------------------------------------- diff --git a/indra/newview/llnotificationhandler.h b/indra/newview/llnotificationhandler.h index 6982ab7096..90ff5fbaac 100644 --- a/indra/newview/llnotificationhandler.h +++ b/indra/newview/llnotificationhandler.h @@ -65,33 +65,54 @@ typedef enum e_notification_type // LLEventHandler is a base class that specifies a common interface for all // notification handlers. It states, that every handler must react on the follofing // events: -// - destroying of a toast; -// - clicking on a correspondet chiclet; -// - closing of a correspondent chiclet. +// - deleting of a toast; +// - initialization of a corresponding channel; // Also every handler must have the following attributes: // - type of the notification that this handler is responsible to; -// - pointer to a correspondent chiclet; // - pointer to a correspondent screen channel, in which all toasts of the handled notification's // type should be displayed +// This class also provides the following signald: +// - increment counter signal +// - decrement counter signal +// - update counter signal +// - signal, that emits ID of the notification that is being processed // class LLEventHandler { public: virtual ~LLEventHandler() {}; - virtual void onToastDestroy(LLToast* toast)=0; - virtual void onChicletClick(void)=0; - virtual void onChicletClose(void)=0; + // callbacks for counters + typedef boost::function notification_callback_t; + typedef boost::signals2::signal notification_signal_t; + notification_signal_t mNewNotificationSignal; + notification_signal_t mDelNotificationSignal; + boost::signals2::connection setNewNotificationCallback(notification_callback_t cb) { return mNewNotificationSignal.connect(cb); } + boost::signals2::connection setDelNotification(notification_callback_t cb) { return mDelNotificationSignal.connect(cb); } + // callback for notification/toast + typedef boost::function notification_id_callback_t; + typedef boost::signals2::signal notification_id_signal_t; + notification_id_signal_t mNotificationIDSignal; + boost::signals2::connection setNotificationIDCallback(notification_id_callback_t cb) { return mNotificationIDSignal.connect(cb); } + +protected: + virtual void onDeleteToast(LLToast* toast)=0; + + // arrange handler's channel on a screen + // is necessary to unbind a moment of creation of a channel and a moment of positioning of it + // it is useful when positioning depends on positions of other controls, that could not be created + // at the moment, when a handlers creates a channel. + virtual void initChannel()=0; LLScreenChannel* mChannel; - LLChiclet* mChiclet; e_notification_type mType; + }; // LLSysHandler and LLChatHandler are more specific base classes // that divide all notification handlers on to groups: -// - handlers for different system notifications (script dialogs, tips, group notices and alerts); -// - handlers for different messaging notifications (nearby chat, IM chat, group chat etc.) +// - handlers for different system notifications (script dialogs, tips, group notices, alerts and IMs); +// - handlers for different messaging notifications (nearby chat) /** * Handler for system notifications. */ @@ -100,7 +121,7 @@ class LLSysHandler : public LLEventHandler public: virtual ~LLSysHandler() {}; - virtual void processNotification(const LLSD& notify)=0; + virtual bool processNotification(const LLSD& notify)=0; }; /** @@ -116,43 +137,59 @@ public: /** * Handler for IM notifications. - * It manages life time of tip and script notices. + * It manages life time of IMs, group messages. */ class LLIMHandler : public LLSysHandler { public: - LLIMHandler(); + LLIMHandler(e_notification_type type, const LLSD& id); virtual ~LLIMHandler(); // base interface functions - virtual void processNotification(const LLSD& notify); - virtual void onToastDestroy(LLToast* toast); - virtual void onChicletClick(void); - virtual void onChicletClose(void); + virtual bool processNotification(const LLSD& notify); protected: + virtual void onDeleteToast(LLToast* toast); + virtual void initChannel(); }; /** * Handler for system informational notices. - * It manages life time of tip and script notices. + * It manages life time of tip notices. */ -class LLInfoHandler : public LLSysHandler +class LLTipHandler : public LLSysHandler { public: - LLInfoHandler(e_notification_type type, const LLSD& id); - virtual ~LLInfoHandler(); + LLTipHandler(e_notification_type type, const LLSD& id); + virtual ~LLTipHandler(); // base interface functions - virtual void processNotification(const LLSD& notify); - virtual void onToastDestroy(LLToast* toast); - virtual void onChicletClick(void); - virtual void onChicletClose(void); + virtual bool processNotification(const LLSD& notify); - // own handlers - void onStoreToast(LLPanel* info_panel, LLUUID id); - void onRejectToast(LLToast::Params p); protected: + virtual void onDeleteToast(LLToast* toast); + virtual void initChannel(); +}; + +/** + * Handler for system informational notices. + * It manages life time of script notices. + */ +class LLScriptHandler : public LLSysHandler +{ +public: + LLScriptHandler(e_notification_type type, const LLSD& id); + virtual ~LLScriptHandler(); + + // base interface functions + virtual bool processNotification(const LLSD& notify); + +protected: + virtual void onDeleteToast(LLToast* toast); + virtual void initChannel(); + + // own handlers + void onRejectToast(LLUUID& id); }; @@ -164,14 +201,13 @@ class LLGroupHandler : public LLSysHandler public: LLGroupHandler(e_notification_type type, const LLSD& id); virtual ~LLGroupHandler(); - - - virtual void processNotification(const LLSD& notify); - virtual void onToastDestroy(LLToast* toast); - virtual void onChicletClick(void); - virtual void onChicletClose(void); + + // base interface functions + virtual bool processNotification(const LLSD& notify); protected: + virtual void onDeleteToast(LLToast* toast); + virtual void initChannel(); }; /** @@ -185,12 +221,13 @@ public: void setAlertMode(bool is_modal) { mIsModal = is_modal; } - virtual void processNotification(const LLSD& notify); - virtual void onToastDestroy(LLToast* toast); - virtual void onChicletClick(void); - virtual void onChicletClose(void); + // base interface functions + virtual bool processNotification(const LLSD& notify); protected: + virtual void onDeleteToast(LLToast* toast); + virtual void initChannel(); + bool mIsModal; }; diff --git a/indra/newview/llnotificationmanager.cpp b/indra/newview/llnotificationmanager.cpp index 31266fdecf..81a6b32917 100644 --- a/indra/newview/llnotificationmanager.cpp +++ b/indra/newview/llnotificationmanager.cpp @@ -71,12 +71,13 @@ void LLNotificationManager::init() LLNotifications::instance().getChannel("AlertModal")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); LLNotifications::instance().getChannel("IM Notifications")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mNotifyHandlers["notify"] = boost::shared_ptr(new LLInfoHandler(NT_NOTIFY, LLSD())); - mNotifyHandlers["notifytip"] = mNotifyHandlers["notify"]; + mNotifyHandlers["notify"] = boost::shared_ptr(new LLScriptHandler(NT_NOTIFY, LLSD())); + mNotifyHandlers["notifytip"] = boost::shared_ptr(new LLTipHandler(NT_NOTIFY, LLSD())); mNotifyHandlers["groupnotify"] = boost::shared_ptr(new LLGroupHandler(NT_GROUPNOTIFY, LLSD())); mNotifyHandlers["alert"] = boost::shared_ptr(new LLAlertHandler(NT_ALERT, LLSD())); - mNotifyHandlers["alertmodal"] = mNotifyHandlers["alert"]; - mNotifyHandlers["notifytoast"] = boost::shared_ptr(new LLIMHandler()); + mNotifyHandlers["alertmodal"] = boost::shared_ptr(new LLAlertHandler(NT_ALERT, LLSD())); + static_cast(mNotifyHandlers["alertmodal"].get())->setAlertMode(true); + mNotifyHandlers["notifytoast"] = boost::shared_ptr(new LLIMHandler(NT_IMCHAT, LLSD())); mNotifyHandlers["nearbychat"] = boost::shared_ptr(new LLNearbyChatHandler(NT_NEARBYCHAT, LLSD())); } @@ -92,17 +93,12 @@ bool LLNotificationManager::onNotification(const LLSD& notify) return false; std::string notification_type = notification->getType(); - handle = dynamic_cast(mNotifyHandlers[notification_type].get()); + handle = static_cast(mNotifyHandlers[notification_type].get()); if(!handle) return false; - if( notification_type == "alertmodal" ) - dynamic_cast(handle)->setAlertMode(true); - - handle->processNotification(notify); - - return true; + return handle->processNotification(notify); } //-------------------------------------------------------------------------- @@ -124,7 +120,15 @@ void LLNotificationManager::onChat(const LLChat& msg,ENotificationType type) } //-------------------------------------------------------------------------- +LLEventHandler* LLNotificationManager::getHandlerForNotification(std::string notification_type) +{ + std::map >::iterator it = mNotifyHandlers.find(notification_type); + if(it != mNotifyHandlers.end()) + return (*it).second.get(); + return NULL; +} +//-------------------------------------------------------------------------- diff --git a/indra/newview/llnotificationmanager.h b/indra/newview/llnotificationmanager.h index 838a00ee11..072fc6f24c 100644 --- a/indra/newview/llnotificationmanager.h +++ b/indra/newview/llnotificationmanager.h @@ -50,7 +50,7 @@ class LLToast; /** * Responsible for registering notification handlers. */ -class LLNotificationManager : public LLUICtrl, public LLSingleton +class LLNotificationManager : public LLSingleton { typedef std::pair eventhandlers; public: @@ -68,6 +68,10 @@ public: // this method reacts on chat notifications and calls an appropriate handler void onChat(const LLChat& msg,ENotificationType type); + // get a handler for a certain type of notification + LLEventHandler* getHandlerForNotification(std::string notification_type); + + private: //TODO (*) std::map > mNotifyHandlers; diff --git a/indra/newview/llnotificationscripthandler.cpp b/indra/newview/llnotificationscripthandler.cpp new file mode 100644 index 0000000000..72855ac0fd --- /dev/null +++ b/indra/newview/llnotificationscripthandler.cpp @@ -0,0 +1,135 @@ +/** + * @file llnotificationscripthandler.cpp + * @brief Notification Handler Class for Simple Notifications and Notification Tips + * + * $LicenseInfo:firstyear=2000&license=viewergpl$ + * + * Copyright (c) 2000-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$ + */ + + +#include "llviewerprecompiledheaders.h" // must be first include + +#include "llnotificationhandler.h" +#include "lltoastnotifypanel.h" +#include "llviewercontrol.h" +#include "llviewerwindow.h" + +using namespace LLNotificationsUI; + +//-------------------------------------------------------------------------- +LLScriptHandler::LLScriptHandler(e_notification_type type, const LLSD& id) +{ + mType = type; + + // Getting a Channel for our notifications + mChannel = LLChannelManager::getInstance()->createNotificationChannel(); + mChannel->setControlHovering(true); + mChannel->setOnRejectToastCallback(boost::bind(&LLScriptHandler::onRejectToast, this, _1)); +} + +//-------------------------------------------------------------------------- +LLScriptHandler::~LLScriptHandler() +{ +} + +//-------------------------------------------------------------------------- +void LLScriptHandler::initChannel() +{ + S32 channel_right_bound = gViewerWindow->getWorldViewRect().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); + S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth"); + mChannel->init(channel_right_bound - channel_width, channel_right_bound); +} + +//-------------------------------------------------------------------------- +bool LLScriptHandler::processNotification(const LLSD& notify) +{ + if(!mChannel) + { + return false; + } + + LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); + + if(!notification) + return false; + + // arrange a channel on a screen + if(!mChannel->getVisible()) + { + initChannel(); + } + + if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") + { + LLToastNotifyPanel* notify_box = new LLToastNotifyPanel(notification); + + LLToast::Params p; + p.notif_id = notification->getID(); + p.notification = notification; + p.panel = notify_box; + p.on_delete_toast = boost::bind(&LLScriptHandler::onDeleteToast, this, _1); + mChannel->addToast(p); + + // send a signal to the counter manager + mNewNotificationSignal(); + + } + else if (notify["sigtype"].asString() == "delete") + { + mChannel->killToastByNotificationID(notification->getID()); + } + return true; +} + +//-------------------------------------------------------------------------- + +void LLScriptHandler::onDeleteToast(LLToast* toast) +{ + // send a signal to the counter manager + mDelNotificationSignal(); + + // send a signal to a listener to let him perform some action + // in this case listener is a SysWellWindow and it will remove a corresponding item from its list + mNotificationIDSignal(toast->getNotificationID()); +} + +//-------------------------------------------------------------------------- +void LLScriptHandler::onRejectToast(LLUUID& id) +{ + LLNotificationPtr notification = LLNotifications::instance().find(id); + + if(notification) + { + LLNotifications::instance().cancel(notification); + } +} + +//-------------------------------------------------------------------------- + + + + diff --git a/indra/newview/llnotificationtiphandler.cpp b/indra/newview/llnotificationtiphandler.cpp new file mode 100644 index 0000000000..740acb6365 --- /dev/null +++ b/indra/newview/llnotificationtiphandler.cpp @@ -0,0 +1,113 @@ +/** + * @file llnotificationtiphandler.cpp + * @brief Notification Handler Class for Notification Tips + * + * $LicenseInfo:firstyear=2000&license=viewergpl$ + * + * Copyright (c) 2000-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$ + */ + + +#include "llviewerprecompiledheaders.h" // must be first include + +#include "llnotificationhandler.h" +#include "lltoastnotifypanel.h" +#include "llviewercontrol.h" +#include "llviewerwindow.h" + +using namespace LLNotificationsUI; + + +//-------------------------------------------------------------------------- +LLTipHandler::LLTipHandler(e_notification_type type, const LLSD& id) +{ + mType = type; + + // Getting a Channel for our notifications + mChannel = LLChannelManager::getInstance()->createNotificationChannel(); +} + +//-------------------------------------------------------------------------- +LLTipHandler::~LLTipHandler() +{ +} + +//-------------------------------------------------------------------------- +void LLTipHandler::initChannel() +{ + S32 channel_right_bound = gViewerWindow->getWorldViewRect().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); + S32 channel_width = gSavedSettings.getS32("NotifyBoxWidth"); + mChannel->init(channel_right_bound - channel_width, channel_right_bound); +} + +//-------------------------------------------------------------------------- +bool LLTipHandler::processNotification(const LLSD& notify) +{ + if(!mChannel) + { + return false; + } + + LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); + + if(!notification) + return false; + + // arrange a channel on a screen + if(!mChannel->getVisible()) + { + initChannel(); + } + + if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") + { + LLToastNotifyPanel* notify_box = new LLToastNotifyPanel(notification); + + LLToast::Params p; + p.notif_id = notification->getID(); + p.notification = notification; + p.panel = notify_box; + p.is_tip = true; + p.can_be_stored = false; + + mChannel->addToast(p); + + } + else if (notify["sigtype"].asString() == "delete") + { + mChannel->killToastByNotificationID(notification->getID()); + } + return true; +} + +//-------------------------------------------------------------------------- +void LLTipHandler::onDeleteToast(LLToast* toast) +{ +} + +//-------------------------------------------------------------------------- + + diff --git a/indra/newview/lloutputmonitorctrl.cpp b/indra/newview/lloutputmonitorctrl.cpp index 49b48f5a8e..6e1dc6940e 100644 --- a/indra/newview/lloutputmonitorctrl.cpp +++ b/indra/newview/lloutputmonitorctrl.cpp @@ -129,10 +129,10 @@ void LLOutputMonitorCtrl::draw() const F32 LEVEL_1 = LLVoiceClient::OVERDRIVEN_POWER_LEVEL * 2.f / 3.f; const F32 LEVEL_2 = LLVoiceClient::OVERDRIVEN_POWER_LEVEL; - if (getVisible() && mAutoUpdate && !mIsMuted && mSpeakerId.notNull()) + if (mIsParentVisible && getVisible() && mAutoUpdate && !mIsMuted && mSpeakerId.notNull()) { setPower(gVoiceClient->getCurrentPower(mSpeakerId)); - setIsTalking(gVoiceClient->getUserPTTState()); + setIsTalking(gVoiceClient->getIsSpeaking(mSpeakerId)); } LLPointer icon; @@ -220,6 +220,12 @@ void LLOutputMonitorCtrl::draw() gl_rect_2d(0, monh, monw, 0, sColorBound, FALSE); } +void LLOutputMonitorCtrl::handleVisibilityChange(BOOL new_visibility) +{ + mIsParentVisible = new_visibility; + LLView::handleVisibilityChange(new_visibility); +} + void LLOutputMonitorCtrl::setSpeakerId(const LLUUID& speaker_id) { if (speaker_id.isNull()) return; diff --git a/indra/newview/lloutputmonitorctrl.h b/indra/newview/lloutputmonitorctrl.h index 7a7b8bc3a1..0e213c4326 100644 --- a/indra/newview/lloutputmonitorctrl.h +++ b/indra/newview/lloutputmonitorctrl.h @@ -72,6 +72,8 @@ public: // llview overrides virtual void draw(); + void handleVisibilityChange(BOOL new_visibility); + void setPower(F32 val); F32 getPower(F32 val) const { return mPower; } @@ -102,6 +104,8 @@ private: F32 mPower; bool mIsMuted; bool mIsTalking; + /** Stores flag whether parent is visible. If not it will not update indicator*/ + bool mIsParentVisible; LLPointer mImageMute; LLPointer mImageOff; LLPointer mImageOn; diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index b7f2f67a9a..649697e091 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -311,7 +311,8 @@ void LLPanelProfileTab::onOpen(const LLSD& key) void LLPanelProfileTab::scrollToTop() { - LLScrollContainer* scrollContainer = getChild("profile_scroll"); + LLScrollContainer* scrollContainer = findChild("profile_scroll"); + if (scrollContainer) scrollContainer->goToTop(); } diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp index d1ce6b14ed..490c845c94 100644 --- a/indra/newview/llpanelgroup.cpp +++ b/indra/newview/llpanelgroup.cpp @@ -44,6 +44,9 @@ #include "llfloaterreg.h" #include "llfloater.h" +#include "llagent.h" +#include "llstatusbar.h" // can_afford_transaction() + #include "llsidetraypanelcontainer.h" #include "llpanelgroupnotices.h" @@ -162,15 +165,25 @@ BOOL LLPanelGroup::postBuild() button->setEnabled(false); + button = getChild("btn_join"); + button->setVisible(false); + button->setEnabled(true); + + button = getChild("btn_cancel"); + button->setVisible(false); button->setEnabled(true); + button = getChild("btn_refresh"); button->setClickedCallback(onBtnRefresh, this); button->setVisible(mAllowEdit); getChild("btn_create")->setVisible(false); - childSetCommitCallback("btn_create",boost::bind(&LLPanelGroup::onBtnCreate,this),NULL); childSetCommitCallback("back",boost::bind(&LLPanelGroup::onBackBtnClick,this),NULL); + childSetCommitCallback("btn_create",boost::bind(&LLPanelGroup::onBtnCreate,this),NULL); + childSetCommitCallback("btn_join",boost::bind(&LLPanelGroup::onBtnJoin,this),NULL); + childSetCommitCallback("btn_cancel",boost::bind(&LLPanelGroup::onBtnCancel,this),NULL); + LLPanelGroupTab* panel_general = findChild("group_general_tab_panel"); LLPanelGroupTab* panel_roles = findChild("group_roles_tab_panel"); LLPanelGroupTab* panel_notices = findChild("group_notices_tab_panel"); @@ -181,41 +194,30 @@ BOOL LLPanelGroup::postBuild() if(panel_notices) mTabs.push_back(panel_notices); if(panel_land) mTabs.push_back(panel_land); + if(panel_general) panel_general->setupCtrls(this); return TRUE; } -void LLPanelGroup::reshape(S32 width, S32 height, BOOL called_from_parent ) +void LLPanelGroup::reposButton(const std::string& name) { - LLPanel::reshape(width, height, called_from_parent ); - - LLRect btn_rect; - - LLButton* button = findChild("btn_apply"); - if(button) - { - btn_rect = button->getRect(); - btn_rect.setLeftTopAndSize( btn_rect.mLeft, btn_rect.getHeight() + 2, btn_rect.getWidth(), btn_rect.getHeight()); - button->setRect(btn_rect); - } - - button = findChild("btn_create"); - if(button) - { - btn_rect = button->getRect(); + LLButton* button = findChild(name); + if(!button) + return; + LLRect btn_rect = button->getRect(); btn_rect.setLeftTopAndSize( btn_rect.mLeft, btn_rect.getHeight() + 2, btn_rect.getWidth(), btn_rect.getHeight()); button->setRect(btn_rect); - } +} +void LLPanelGroup::reshape(S32 width, S32 height, BOOL called_from_parent ) +{ + LLPanel::reshape(width, height, called_from_parent ); - button = findChild("btn_refresh"); - if(button) - { - btn_rect = button->getRect(); - btn_rect.setLeftTopAndSize( btn_rect.mLeft, btn_rect.getHeight() + 2, btn_rect.getWidth(), btn_rect.getHeight()); - button->setRect(btn_rect); - } + reposButton("btn_apply"); + reposButton("btn_create"); + reposButton("btn_refresh"); + reposButton("btn_cancel"); } void LLPanelGroup::onBackBtnClick() @@ -247,33 +249,94 @@ void LLPanelGroup::onBtnApply(void* user_data) LLPanelGroup* self = static_cast(user_data); self->apply(); } +void LLPanelGroup::onBtnJoin() +{ + lldebugs << "joining group: " << mID << llendl; + + LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mID); + + if (gdatap) + { + S32 cost = gdatap->mMembershipFee; + LLSD args; + args["COST"] = llformat("%d", cost); + LLSD payload; + payload["group_id"] = mID; + + if (can_afford_transaction(cost)) + { + LLNotifications::instance().add("JoinGroupCanAfford", args, payload, LLPanelGroup::joinDlgCB); + } + else + { + LLNotifications::instance().add("JoinGroupCannotAfford", args, payload); + } + } + else + { + llwarns << "LLGroupMgr::getInstance()->getGroupData(" << mID << ") was NULL" << llendl; + } +} +bool LLPanelGroup::joinDlgCB(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotification::getSelectedOption(notification, response); + + if (option == 1) + { + // user clicked cancel + return false; + } + + LLGroupMgr::getInstance()->sendGroupMemberJoin(notification["payload"]["group_id"].asUUID()); + return false; +} + +void LLPanelGroup::onBtnCancel() +{ + onBackBtnClick(); +} void LLPanelGroup::changed(LLGroupChange gc) { for(std::vector::iterator it = mTabs.begin();it!=mTabs.end();++it) (*it)->update(gc); - - LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mID); - if(gdatap) - childSetValue("group_name", gdatap->mName); + update(gc); } void LLPanelGroup::notifyObservers() { - for(std::vector::iterator it = mTabs.begin();it!=mTabs.end();++it) - (*it)->update(GC_ALL); + changed(GC_ALL); +} +void LLPanelGroup::update(LLGroupChange gc) +{ LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mID); if(gdatap) + { childSetValue("group_name", gdatap->mName); - -} - + LLGroupData agent_gdatap; + bool is_member = gAgent.getGroupData(mID,agent_gdatap); + LLButton* btn_join = getChild("btn_join"); + bool join_btn_visible = !is_member && gdatap->mOpenEnrollment; + btn_join->setVisible(join_btn_visible); + if(join_btn_visible) + { + LLStringUtil::format_map_t string_args; + string_args["[AMOUNT]"] = llformat("%d", gdatap->mMembershipFee); + std::string fee_buff = getString("group_join_btn", string_args); + btn_join->setLabelSelected(fee_buff); + btn_join->setLabelUnselected(fee_buff); + } + } +} void LLPanelGroup::setGroupID(const LLUUID& group_id) { + std::string str_group_id; + group_id.toString(str_group_id); + LLGroupMgr::getInstance()->removeObserver(this); mID = group_id; LLGroupMgr::getInstance()->addObserver(this); @@ -288,6 +351,8 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) LLButton* button_apply = findChild("btn_apply"); LLButton* button_refresh = findChild("btn_refresh"); LLButton* button_create = findChild("btn_create"); + LLButton* button_join = findChild("btn_join"); + LLButton* button_cancel = findChild("btn_cancel"); bool is_null_group_id = group_id == LLUUID::null; @@ -295,8 +360,11 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) button_apply->setVisible(!is_null_group_id); if(button_refresh) button_refresh->setVisible(!is_null_group_id); + if(button_create) button_create->setVisible(is_null_group_id); + if(button_cancel) + button_cancel->setVisible(!is_null_group_id); getChild("prepend_founded_by")->setVisible(!is_null_group_id); @@ -307,6 +375,9 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) if(!tab_general || !tab_roles || !tab_notices || !tab_land) return; + + if(button_join) + button_join->setVisible(false); if(is_null_group_id)//creating new group { @@ -323,6 +394,10 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) tab_roles->canOpenClose(false); tab_notices->canOpenClose(false); tab_land->canOpenClose(false); + + getChild("group_name")->setVisible(false); + getChild("group_name_editor")->setVisible(true); + } else { @@ -338,6 +413,9 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) tab_roles->canOpenClose(true); tab_notices->canOpenClose(true); tab_land->canOpenClose(true); + + getChild("group_name")->setVisible(true); + getChild("group_name_editor")->setVisible(false); } } @@ -395,11 +473,9 @@ void LLPanelGroup::draw() void LLPanelGroup::refreshData() { LLGroupMgr::getInstance()->clearGroupData(getID()); - - for(std::vector::iterator it = mTabs.begin();it!=mTabs.end();++it) - (*it)->activate(); - + setGroupID(getID()); + // 5 second timeout childDisable("btn_refresh"); mRefreshTimer.start(); diff --git a/indra/newview/llpanelgroup.h b/indra/newview/llpanelgroup.h index 6db6738d18..f2118a7244 100644 --- a/indra/newview/llpanelgroup.h +++ b/indra/newview/llpanelgroup.h @@ -91,12 +91,20 @@ public: protected: + virtual void update(LLGroupChange gc); + void onBtnCreate(); void onBackBtnClick(); + void onBtnJoin(); + void onBtnCancel(); static void onBtnApply(void*); static void onBtnRefresh(void*); + static bool joinDlgCB(const LLSD& notification, const LLSD& response); + + void reposButton(const std::string& name); + protected: bool apply(LLPanelGroupTab* tab); diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index f3893a104c..d63fd141b0 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -50,7 +50,6 @@ #include "llnamelistctrl.h" #include "llscrolllistitem.h" #include "llspinctrl.h" -#include "llstatusbar.h" // can_afford_transaction() #include "lltextbox.h" #include "lltexteditor.h" #include "lltexturectrl.h" @@ -96,9 +95,6 @@ BOOL LLPanelGroupGeneral::postBuild() { bool recurse = true; - // General info - mGroupNameEditor = getChild("group_name_editor", recurse); - mEditCharter = getChild("charter", recurse); if(mEditCharter) { @@ -194,7 +190,6 @@ BOOL LLPanelGroupGeneral::postBuild() // If the group_id is null, then we are creating a new group if (mGroupID.isNull()) { - mGroupNameEditor->setEnabled(TRUE); mEditCharter->setEnabled(TRUE); mCtrlShowInGroupList->setEnabled(TRUE); @@ -217,6 +212,7 @@ void LLPanelGroupGeneral::setupCtrls(LLPanel* panel_group) mDefaultIconID = mInsignia->getImageAssetID(); } mFounderName = panel_group->getChild("founder_name"); + mGroupNameEditor = panel_group->getChild("group_name_editor"); } // static @@ -297,56 +293,6 @@ void LLPanelGroupGeneral::onClickInfo(void *userdata) } -// static -void LLPanelGroupGeneral::onClickJoin(void *userdata) -{ - LLPanelGroupGeneral *self = (LLPanelGroupGeneral *)userdata; - - if ( !self ) return; - - lldebugs << "joining group: " << self->mGroupID << llendl; - - LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(self->mGroupID); - - if (gdatap) - { - S32 cost = gdatap->mMembershipFee; - LLSD args; - args["COST"] = llformat("%d", cost); - LLSD payload; - payload["group_id"] = self->mGroupID; - - if (can_afford_transaction(cost)) - { - LLNotifications::instance().add("JoinGroupCanAfford", args, payload, LLPanelGroupGeneral::joinDlgCB); - } - else - { - LLNotifications::instance().add("JoinGroupCannotAfford", args, payload); - } - } - else - { - llwarns << "LLGroupMgr::getInstance()->getGroupData(" << self->mGroupID - << ") was NULL" << llendl; - } -} - -// static -bool LLPanelGroupGeneral::joinDlgCB(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotification::getSelectedOption(notification, response); - - if (option == 1) - { - // user clicked cancel - return false; - } - - LLGroupMgr::getInstance()->sendGroupMemberJoin(notification["payload"]["group_id"].asUUID()); - return false; -} - // static void LLPanelGroupGeneral::openProfile(void* data) { @@ -883,6 +829,8 @@ void LLPanelGroupGeneral::reset() mComboActiveTitle->setVisible(false); mInsignia->setImageAssetID(LLUUID::null); + + mInsignia->setEnabled(true); { std::string empty_str = ""; diff --git a/indra/newview/llpanelgroupgeneral.h b/indra/newview/llpanelgroupgeneral.h index b828480a12..7e90e43cf9 100644 --- a/indra/newview/llpanelgroupgeneral.h +++ b/indra/newview/llpanelgroupgeneral.h @@ -79,7 +79,6 @@ private: static void onCommitUserOnly(LLUICtrl* ctrl, void* data); static void onCommitTitle(LLUICtrl* ctrl, void* data); static void onCommitEnrollment(LLUICtrl* ctrl, void* data); - static void onClickJoin(void* userdata); static void onClickInfo(void* userdata); static void onReceiveNotices(LLUICtrl* ctrl, void* data); static void openProfile(void* data); diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp index 51cdc5af93..a7590ac1dd 100644 --- a/indra/newview/llpanelimcontrolpanel.cpp +++ b/indra/newview/llpanelimcontrolpanel.cpp @@ -82,6 +82,9 @@ void LLPanelIMControlPanel::onShareButtonClicked() void LLPanelIMControlPanel::setID(const LLUUID& avatar_id) { + // Disable "Add friend" button for friends. + childSetEnabled("add_friend_btn", !LLAvatarActions::isFriend(avatar_id)); + getChild("avatar_icon")->setValue(avatar_id); } diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index 309a97a9f2..6a41b6feb9 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -42,15 +42,18 @@ #include "llpanelpeople.h" // newview +#include "llaccordionctrltab.h" #include "llagent.h" #include "llavataractions.h" #include "llavatarlist.h" +#include "llavatarlistitem.h" #include "llcallingcard.h" // for LLAvatarTracker #include "llfloateravatarpicker.h" //#include "llfloaterminiinspector.h" #include "llfriendcard.h" #include "llgroupactions.h" #include "llgrouplist.h" +#include "llpanelpeoplemenus.h" #include "llrecentpeople.h" #include "llviewercontrol.h" // for gSavedSettings #include "llviewermenu.h" // for gMenuHolder @@ -68,6 +71,27 @@ static const std::string FRIENDS_TAB_NAME = "friends_panel"; static const std::string GROUP_TAB_NAME = "groups_panel"; static const std::string RECENT_TAB_NAME = "recent_panel"; +/** Comparator for comparing avatar items by last interaction date */ +class LLAvatarItemRecentComparator : public LLAvatarItemComparator +{ +public: + LLAvatarItemRecentComparator() {}; + virtual ~LLAvatarItemRecentComparator() {}; + +protected: + virtual bool doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const + { + LLRecentPeople& people = LLRecentPeople::instance(); + const LLDate& date1 = people.getDate(avatar_item1->getAvatarId()); + const LLDate& date2 = people.getDate(avatar_item2->getAvatarId()); + + //older comes first + return date1 > date2; + } +}; + +static const LLAvatarItemRecentComparator RECENT_COMPARATOR; + static LLRegisterPanelClassWrapper t_people("panel_people"); //============================================================================= @@ -299,7 +323,7 @@ private: /** * Updates the recent people list (those the agent has recently interacted with). */ -class LLRecentListUpdater : public LLAvatarListUpdater +class LLRecentListUpdater : public LLAvatarListUpdater, public boost::signals2::trackable { LOG_CLASS(LLRecentListUpdater); @@ -398,21 +422,25 @@ LLPanelPeople::~LLPanelPeople() LLView::deleteViewByHandle(mGroupPlusMenuHandle); LLView::deleteViewByHandle(mNearbyViewSortMenuHandle); LLView::deleteViewByHandle(mFriendsViewSortMenuHandle); + LLView::deleteViewByHandle(mGroupsViewSortMenuHandle); LLView::deleteViewByHandle(mRecentViewSortMenuHandle); } -void onAvatarListTmpDoubleClicked(LLAvatarListTmp* list) -{ - LLUUID clicked_id = list->getCurrentID(); - if (clicked_id.isNull()) +void LLPanelPeople::onFriendsAccordionExpandedCollapsed(const LLSD& param, LLAvatarList* avatar_list) +{ + if(!avatar_list) + { + llerrs << "Bad parameter" << llendl; return; + } -#if 0 // SJB: Useful for testing, but not currently functional or to spec - LLAvatarActions::showProfile(clicked_id); -#else // spec says open IM window - LLAvatarActions::startIM(clicked_id); -#endif + bool expanded = param.asBoolean(); + + if(!expanded) + { + avatar_list->resetSelection(); + } } BOOL LLPanelPeople::postBuild() @@ -427,11 +455,22 @@ BOOL LLPanelPeople::postBuild() mOnlineFriendList = getChild(FRIENDS_TAB_NAME)->getChild("avatars_online"); mAllFriendList = getChild(FRIENDS_TAB_NAME)->getChild("avatars_all"); + mOnlineFriendList->setNoItemsCommentText(getString("no_friends_online")); + mAllFriendList->setNoItemsCommentText(getString("no_friends")); mNearbyList = getChild(NEARBY_TAB_NAME)->getChild("avatar_list"); + mNearbyList->setNoItemsCommentText(getString("no_one_near")); + + mRecentList = getChild(RECENT_TAB_NAME)->getChild("avatar_list"); + mRecentList->setNoItemsCommentText(getString("no_people")); - mRecentList = getChild(RECENT_TAB_NAME)->getChild("avatar_list"); mGroupList = getChild("group_list"); + mGroupList->setNoItemsCommentText(getString("no_groups")); + + mNearbyList->setContextMenu(&LLPanelPeopleMenus::gNearbyMenu); + mRecentList->setContextMenu(&LLPanelPeopleMenus::gNearbyMenu); + + mRecentList->setComparator(&RECENT_COMPARATOR); LLPanel* groups_panel = getChild(GROUP_TAB_NAME); groups_panel->childSetAction("activate_btn", boost::bind(&LLPanelPeople::onActivateButtonClicked, this)); @@ -445,15 +484,24 @@ BOOL LLPanelPeople::postBuild() mOnlineFriendList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, mOnlineFriendList)); mAllFriendList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, mAllFriendList)); mNearbyList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, mNearbyList)); - mRecentList->setDoubleClickCallback(boost::bind(onAvatarListTmpDoubleClicked, mRecentList)); + mRecentList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, mRecentList)); + mOnlineFriendList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mOnlineFriendList)); mAllFriendList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mAllFriendList)); mNearbyList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mNearbyList)); - mRecentList->setCommitCallback(boost::bind(&LLPanelPeople::updateButtons, this)); + mRecentList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mRecentList)); mGroupList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onGroupInfoButtonClicked, this)); mGroupList->setCommitCallback(boost::bind(&LLPanelPeople::updateButtons, this)); + LLAccordionCtrlTab* accordion_tab = getChild("tab_all"); + accordion_tab->setDropDownStateChangedCallback( + boost::bind(&LLPanelPeople::onFriendsAccordionExpandedCollapsed, this, _2, mAllFriendList)); + + accordion_tab = getChild("tab_online"); + accordion_tab->setDropDownStateChangedCallback( + boost::bind(&LLPanelPeople::onFriendsAccordionExpandedCollapsed, this, _2, mOnlineFriendList)); + buttonSetAction("view_profile_btn", boost::bind(&LLPanelPeople::onViewProfileButtonClicked, this)); buttonSetAction("add_friend_btn", boost::bind(&LLPanelPeople::onAddFriendButtonClicked, this)); buttonSetAction("group_info_btn", boost::bind(&LLPanelPeople::onGroupInfoButtonClicked, this)); @@ -466,6 +514,7 @@ BOOL LLPanelPeople::postBuild() getChild(NEARBY_TAB_NAME)->childSetAction("nearby_view_sort_btn",boost::bind(&LLPanelPeople::onNearbyViewSortButtonClicked, this)); getChild(RECENT_TAB_NAME)->childSetAction("recent_viewsort_btn",boost::bind(&LLPanelPeople::onRecentViewSortButtonClicked, this)); getChild(FRIENDS_TAB_NAME)->childSetAction("friends_viewsort_btn",boost::bind(&LLPanelPeople::onFriendsViewSortButtonClicked, this)); + getChild(GROUP_TAB_NAME)->childSetAction("groups_viewsort_btn",boost::bind(&LLPanelPeople::onGroupsViewSortButtonClicked, this)); // Must go after setting commit callback and initializing all pointers to children. mTabContainer->selectTabByName(FRIENDS_TAB_NAME); @@ -476,6 +525,7 @@ BOOL LLPanelPeople::postBuild() registrar.add("People.Group.Plus.Action", boost::bind(&LLPanelPeople::onGroupPlusMenuItemClicked, this, _2)); registrar.add("People.Friends.ViewSort.Action", boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemClicked, this, _2)); registrar.add("People.Nearby.ViewSort.Action", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemClicked, this, _2)); + registrar.add("People.Groups.ViewSort.Action", boost::bind(&LLPanelPeople::onGroupsViewSortMenuItemClicked, this, _2)); registrar.add("People.Recent.ViewSort.Action", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemClicked, this, _2)); LLMenuGL* plus_menu = LLUICtrlFactory::getInstance()->createFromFile("menu_group_plus.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); @@ -489,6 +539,10 @@ BOOL LLPanelPeople::postBuild() if(friend_view_sort) mFriendsViewSortMenuHandle = friend_view_sort->getHandle(); + LLMenuGL* group_view_sort = LLUICtrlFactory::getInstance()->createFromFile("menu_people_groups_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if(group_view_sort) + mGroupsViewSortMenuHandle = group_view_sort->getHandle(); + LLMenuGL* recent_view_sort = LLUICtrlFactory::getInstance()->createFromFile("menu_people_recent_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); if(recent_view_sort) mRecentViewSortMenuHandle = recent_view_sort->getHandle(); @@ -497,13 +551,28 @@ BOOL LLPanelPeople::postBuild() // Perform initial update. mFriendListUpdater->forceUpdate(); - mRecentListUpdater->forceUpdate(); + mNearbyListUpdater->forceUpdate(); mGroupListUpdater->forceUpdate(); mRecentListUpdater->forceUpdate(); + // call this method in case some list is empty and buttons can be in inconsistent state + updateButtons(); + return TRUE; } +void LLPanelPeople::applyFilterToTab(const std::string& tab_name) +{ + if (tab_name == FRIENDS_TAB_NAME) // this tab has two lists + filterFriendList(); + else if (tab_name == NEARBY_TAB_NAME) + filterNearbyList(); + else if (tab_name == RECENT_TAB_NAME) + filterRecentList(); + else if (tab_name == GROUP_TAB_NAME) + updateGroupList(); +} + bool LLPanelPeople::updateFriendList(U32 changed_mask) { // Refresh names. @@ -570,10 +639,7 @@ bool LLPanelPeople::updateGroupList() return true; // there's no point in further updates bool have_names = mGroupList->update(mFilterSubString); - - if (mGroupList->isEmpty()) - mGroupList->setCommentText(getString("no_groups")); - + updateButtons(); return have_names; } @@ -587,22 +653,15 @@ bool LLPanelPeople::filterFriendList() mOnlineFriendList->update(mOnlineFriendVec, mFilterSubString) & mAllFriendList->update(mAllFriendVec, mFilterSubString); - if (mOnlineFriendVec.size() == 0) - mOnlineFriendList->setCommentText(getString("no_friends_online")); - - if (mAllFriendVec.size() == 0) - mAllFriendList->setCommentText(getString("no_friends")); + updateButtons(); return have_names; } bool LLPanelPeople::filterNearbyList() { bool have_names = mNearbyList->update(mNearbyVec, mFilterSubString); - - if (mNearbyVec.size() == 0) - mNearbyList->setCommentText(getString("no_one_near")); - + updateButtons(); return have_names; } @@ -612,9 +671,11 @@ bool LLPanelPeople::filterRecentList() return true; if (mRecentVec.size() > 0) - return mRecentList->update(mRecentVec, mFilterSubString); - - mRecentList->setCommentText(getString("no_people")); + { + bool updated = mRecentList->update(mRecentVec, mFilterSubString); + updateButtons(); + return updated; + } return true; } @@ -664,12 +725,12 @@ void LLPanelPeople::updateButtons() if (group_tab_active) { - bool item_selected = mGroupList->getFirstSelected() != NULL; + bool item_selected = mGroupList->getSelectedItem() != NULL; bool cur_group_active = true; if (item_selected) { - selected_id = mGroupList->getCurrentID(); + selected_id = mGroupList->getSelectedUUID(); cur_group_active = (gAgent.getGroupID() == selected_id); } @@ -714,20 +775,20 @@ LLUUID LLPanelPeople::getCurrentItemID() const { LLUUID cur_online_friend; - if ((cur_online_friend = mOnlineFriendList->getCurrentID()).notNull()) + if ((cur_online_friend = mOnlineFriendList->getSelectedUUID()).notNull()) return cur_online_friend; - return mAllFriendList->getCurrentID(); + return mAllFriendList->getSelectedUUID(); } if (cur_tab == NEARBY_TAB_NAME) - return mNearbyList->getCurrentID(); + return mNearbyList->getSelectedUUID(); if (cur_tab == RECENT_TAB_NAME) - return mRecentList->getCurrentID(); + return mRecentList->getSelectedUUID(); if (cur_tab == GROUP_TAB_NAME) - return mGroupList->getCurrentID(); + return mGroupList->getSelectedUUID(); llassert(0 && "unknown tab selected"); return LLUUID::null; @@ -785,20 +846,16 @@ void LLPanelPeople::onFilterEdit(const std::string& search_string) LLStringUtil::toUpper(mFilterSubString); LLStringUtil::trimHead(mFilterSubString); - // Apply new filter to all tabs. - filterNearbyList(); - filterFriendList(); - filterRecentList(); - updateGroupList(); - - updateButtons(); + // Apply new filter to current tab. + applyFilterToTab(getActiveTabName()); } void LLPanelPeople::onTabSelected(const LLSD& param) { std::string tab_name = getChild(param.asString())->getName(); mNearbyListUpdater->setActive(tab_name == NEARBY_TAB_NAME); - updateButtons(); + applyFilterToTab(tab_name); + // No need to call updateButtons() because applyFilterToTab() does that. if (GROUP_TAB_NAME == tab_name) mFilterEditor->setLabel(getString("groups_filter_label")); @@ -808,7 +865,7 @@ void LLPanelPeople::onTabSelected(const LLSD& param) void LLPanelPeople::onAvatarListDoubleClicked(LLAvatarList* list) { - LLUUID clicked_id = list->getCurrentID(); + LLUUID clicked_id = list->getSelectedUUID(); if (clicked_id.isNull()) return; @@ -826,9 +883,9 @@ void LLPanelPeople::onAvatarListCommitted(LLAvatarList* list) if (getActiveTabName() == FRIENDS_TAB_NAME) { if (list == mOnlineFriendList) - mAllFriendList->deselectAllItems(TRUE); + mAllFriendList->resetSelection(true); else if (list == mAllFriendList) - mOnlineFriendList->deselectAllItems(TRUE); + mOnlineFriendList->resetSelection(true); else llassert(0 && "commit on unknown friends list"); } @@ -890,7 +947,7 @@ void LLPanelPeople::onImButtonClicked() void LLPanelPeople::onActivateButtonClicked() { - LLGroupActions::activate(mGroupList->getCurrentID()); + LLGroupActions::activate(mGroupList->getSelectedUUID()); } // static @@ -909,7 +966,7 @@ bool LLPanelPeople::onFriendListUpdate(U32 changed_mask) // Update online status in the Recent tab. // *TODO: isn't it too much to update the whole list? - updateRecentList(); +// updateRecentList(); // mantipov: seems online status should be supported by LLAvatarListItem itself. return have_names; } @@ -957,6 +1014,17 @@ void LLPanelPeople::onFriendsViewSortMenuItemClicked(const LLSD& userdata) { } } + +void LLPanelPeople::onGroupsViewSortMenuItemClicked(const LLSD& userdata) +{ + std::string chosen_item = userdata.asString(); + + if (chosen_item == "show_icons") + { + mGroupList->toggleIcons(); + } +} + void LLPanelPeople::onNearbyViewSortMenuItemClicked(const LLSD& userdata) { std::string chosen_item = userdata.asString(); @@ -966,6 +1034,7 @@ void LLPanelPeople::onNearbyViewSortMenuItemClicked(const LLSD& userdata) } else if (chosen_item == "sort_name") { + mNearbyList->sortByName(); } else if (chosen_item == "view_icons") { @@ -978,11 +1047,14 @@ void LLPanelPeople::onRecentViewSortMenuItemClicked(const LLSD& userdata) { std::string chosen_item = userdata.asString(); - if (chosen_item == "sort_most") + if (chosen_item == "sort_recent") { - } + mRecentList->setComparator(&RECENT_COMPARATOR); + mRecentList->sort(); + } else if (chosen_item == "sort_name") { + mRecentList->sortByName(); } else if (chosen_item == "view_icons") { @@ -1008,6 +1080,7 @@ void LLPanelPeople::onMoreButtonClicked() { // *TODO: not implemented yet } + void LLPanelPeople::onFriendsViewSortButtonClicked() { LLMenuGL* menu = (LLMenuGL*)mFriendsViewSortMenuHandle.get(); @@ -1015,6 +1088,15 @@ void LLPanelPeople::onFriendsViewSortButtonClicked() return; showGroupMenu(menu); } + +void LLPanelPeople::onGroupsViewSortButtonClicked() +{ + LLMenuGL* menu = (LLMenuGL*)mGroupsViewSortMenuHandle.get(); + if (!menu) + return; + showGroupMenu(menu); +} + void LLPanelPeople::onRecentViewSortButtonClicked() { LLMenuGL* menu = (LLMenuGL*)mRecentViewSortMenuHandle.get(); @@ -1022,6 +1104,7 @@ void LLPanelPeople::onRecentViewSortButtonClicked() return; showGroupMenu(menu); } + void LLPanelPeople::onNearbyViewSortButtonClicked() { LLMenuGL* menu = (LLMenuGL*)mNearbyViewSortMenuHandle.get(); @@ -1041,4 +1124,3 @@ void LLPanelPeople::onOpen(const LLSD& key) else reSelectedCurrentTab(); } - diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h index c0c2f70614..8cd3cc7feb 100644 --- a/indra/newview/llpanelpeople.h +++ b/indra/newview/llpanelpeople.h @@ -40,7 +40,6 @@ class LLFilterEditor; class LLTabContainer; class LLAvatarList; -class LLAvatarListTmp; class LLGroupList; class LLPanelPeople : public LLPanel @@ -67,6 +66,7 @@ private: bool filterFriendList(); bool filterNearbyList(); bool filterRecentList(); + void applyFilterToTab(const std::string& tab_name); void updateButtons(); const std::string& getActiveTabName() const; LLUUID getCurrentItemID() const; @@ -97,6 +97,7 @@ private: void onRecentViewSortButtonClicked(); void onNearbyViewSortButtonClicked(); void onFriendsViewSortButtonClicked(); + void onGroupsViewSortButtonClicked(); void onAvatarListDoubleClicked(LLAvatarList* list); void onAvatarListCommitted(LLAvatarList* list); void onGroupPlusButtonClicked(); @@ -105,6 +106,7 @@ private: void onFriendsViewSortMenuItemClicked(const LLSD& userdata); void onNearbyViewSortMenuItemClicked(const LLSD& userdata); + void onGroupsViewSortMenuItemClicked(const LLSD& userdata); void onRecentViewSortMenuItemClicked(const LLSD& userdata); // misc callbacks @@ -114,17 +116,20 @@ private: const std::vector& ids, void*); + void onFriendsAccordionExpandedCollapsed(const LLSD& param, LLAvatarList* avatar_list); + LLFilterEditor* mFilterEditor; LLTabContainer* mTabContainer; LLAvatarList* mOnlineFriendList; LLAvatarList* mAllFriendList; LLAvatarList* mNearbyList; - LLAvatarListTmp* mRecentList; + LLAvatarList* mRecentList; LLGroupList* mGroupList; LLHandle mGroupPlusMenuHandle; LLHandle mNearbyViewSortMenuHandle; LLHandle mFriendsViewSortMenuHandle; + LLHandle mGroupsViewSortMenuHandle; LLHandle mRecentViewSortMenuHandle; Updater* mFriendListUpdater; diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp new file mode 100644 index 0000000000..0e88058bb1 --- /dev/null +++ b/indra/newview/llpanelpeoplemenus.cpp @@ -0,0 +1,148 @@ +/** + * @file llpanelpeoplemenus.h + * @brief Menus used by the side tray "People" panel + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 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$ + */ + +#include "llviewerprecompiledheaders.h" + +// libs +#include "llmenugl.h" +#include "lluictrlfactory.h" + +#include "llpanelpeoplemenus.h" + +// newview +#include "llagentdata.h" // for gAgentID +#include "llavataractions.h" +#include "llviewermenu.h" // for gMenuHolder + +namespace LLPanelPeopleMenus +{ + +NearbyMenu gNearbyMenu; + +//== ContextMenu ============================================================== + +ContextMenu::ContextMenu() +: mMenu(NULL) +{ +} + +void ContextMenu::show(LLView* spawning_view, const LLUUID& id, S32 x, S32 y) +{ + if (mMenu) + { + //preventing parent (menu holder) from deleting already "dead" context menus on exit + LLView* parent = mMenu->getParent(); + if (parent) + { + parent->removeChild(mMenu); + mMenu->setParent(NULL); + } + delete mMenu; + } + + mID = id; + mMenu = createMenu(); + mMenu->show(x, y); + LLMenuGL::showPopup(spawning_view, mMenu, x, y); +} + +//== NearbyMenu =============================================================== + +LLContextMenu* NearbyMenu::createMenu() +{ + // set up the callbacks for all of the avatar menu items + // (N.B. callbacks don't take const refs as mID is local scope) + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + + registrar.add("Avatar.Profile", boost::bind(&LLAvatarActions::showProfile, mID)); + registrar.add("Avatar.AddFriend", boost::bind(&LLAvatarActions::requestFriendshipDialog, mID)); + registrar.add("Avatar.IM", boost::bind(&LLAvatarActions::startIM, mID)); + registrar.add("Avatar.Call", boost::bind(&LLAvatarActions::startIM, mID)); // *TODO: unimplemented + registrar.add("Avatar.OfferTeleport", boost::bind(&NearbyMenu::offerTeleport, this)); + registrar.add("Avatar.ShowOnMap", boost::bind(&LLAvatarActions::startIM, mID)); // *TODO: unimplemented + registrar.add("Avatar.Share", boost::bind(&LLAvatarActions::startIM, mID)); // *TODO: unimplemented + registrar.add("Avatar.Pay", boost::bind(&LLAvatarActions::pay, mID)); + registrar.add("Avatar.BlockUnblock", boost::bind(&LLAvatarActions::toggleBlock, mID)); + + enable_registrar.add("Avatar.EnableItem", boost::bind(&NearbyMenu::enableContextMenuItem, this, _2)); + enable_registrar.add("Avatar.CheckItem", boost::bind(&NearbyMenu::checkContextMenuItem, this, _2)); + + // create the context menu from the XUI + return LLUICtrlFactory::getInstance()->createFromFile( + "menu_people_nearby.xml", LLMenuGL::sMenuContainer, LLViewerMenuHolderGL::child_registry_t::instance()); +} + +bool NearbyMenu::enableContextMenuItem(const LLSD& userdata) +{ + std::string item = userdata.asString(); + + if (item == std::string("can_block")) + { + std::string firstname, lastname; + gCacheName->getName(mID, firstname, lastname); + bool is_linden = !LLStringUtil::compareStrings(lastname, "Linden"); + bool is_self = mID == gAgentID; + return !is_self && !is_linden; + } + else if (item == std::string("can_add")) + { + return !LLAvatarActions::isFriend(mID); + } + else if (item == std::string("can_delete")) + { + return LLAvatarActions::isFriend(mID); + } + + return false; +} + +bool NearbyMenu::checkContextMenuItem(const LLSD& userdata) +{ + std::string item = userdata.asString(); + + if (item == std::string("is_blocked")) + { + return LLAvatarActions::isBlocked(mID); + } + + return false; +} + +void NearbyMenu::offerTeleport() +{ + // boost::bind cannot recognize overloaded method LLAvatarActions::offerTeleport(), + // so we have to use a wrapper. + LLAvatarActions::offerTeleport(mID); +} + +} // namespace LLPanelPeopleMenus diff --git a/indra/newview/llpanelpeoplemenus.h b/indra/newview/llpanelpeoplemenus.h new file mode 100644 index 0000000000..0012ac38f8 --- /dev/null +++ b/indra/newview/llpanelpeoplemenus.h @@ -0,0 +1,82 @@ +/** + * @file llpanelpeoplemenus.h + * @brief Menus used by the side tray "People" panel + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 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_LLPANELPEOPLEMENUS_H +#define LL_LLPANELPEOPLEMENUS_H + +#include "llavatarlistitem.h" + +namespace LLPanelPeopleMenus +{ + +/** + * Base context menu. + */ +class ContextMenu : public LLAvatarListItem::ContextMenu +{ +public: + ContextMenu(); + virtual ~ContextMenu() {} + + /** + * Show the menu at specified coordinates. + * + * @param id either avatar or group id + */ + /*virtual*/ void show(LLView* spawning_view, const LLUUID& id, S32 x, S32 y); + +protected: + + virtual LLContextMenu* createMenu() = 0; + + LLUUID mID; + LLContextMenu* mMenu; +}; + +/** + * Menu used in the nearby people list. + */ +class NearbyMenu : public ContextMenu +{ +public: + /*virtual*/ LLContextMenu* createMenu(); +private: + bool enableContextMenuItem(const LLSD& userdata); + bool checkContextMenuItem(const LLSD& userdata); + void offerTeleport(); +}; + +extern NearbyMenu gNearbyMenu; + +} // namespace LLPanelPeopleMenus + +#endif // LL_LLPANELPEOPLEMENUS_H diff --git a/indra/newview/llpanelpick.cpp b/indra/newview/llpanelpick.cpp index 9ae58d1cb6..42185d28e5 100644 --- a/indra/newview/llpanelpick.cpp +++ b/indra/newview/llpanelpick.cpp @@ -64,6 +64,7 @@ #define LABEL_PICK = "Pick" #define LABEL_CHANGES = "Changes" +std::string SET_LOCATION_NOTICE("(will update after save)"); LLPanelPick::LLPanelPick(BOOL edit_mode/* = FALSE */) : LLPanel(), LLAvatarPropertiesObserver(), LLRemoteParcelInfoObserver(), @@ -71,7 +72,8 @@ LLPanelPick::LLPanelPick(BOOL edit_mode/* = FALSE */) mSnapshotCtrl(NULL), mPickId(LLUUID::null), mCreatorId(LLUUID::null), - mDataReceived(FALSE) + mDataReceived(FALSE), + mIsPickNew(false) { if (edit_mode) { @@ -171,7 +173,7 @@ void LLPanelPick::init(LLPickData *pick_data) setPickName(pick_data->name); setPickDesc(pick_data->desc); - setPickLocation(pick_data->location_text); + mSnapshotCtrl->setImageAssetID(pick_data->snapshot_id); //*HACK see reset() where the texture control was set to FALSE @@ -180,27 +182,45 @@ void LLPanelPick::init(LLPickData *pick_data) mPosGlobal = pick_data->pos_global; mSimName = pick_data->sim_name; mParcelId = pick_data->parcel_id; + + setPickLocation(createLocationText(pick_data->user_name, pick_data->original_name, + pick_data->sim_name, pick_data->pos_global)); } -// Fill in some reasonable defaults for a new pick. -void LLPanelPick::createNewPick() +void LLPanelPick::prepareNewPick(const LLVector3d pos_global, + const std::string& name, + const std::string& desc, + const LLUUID& snapshot_id, + const LLUUID& parcel_id) { mPickId.generate(); mCreatorId = gAgent.getID(); - mPosGlobal = gAgent.getPositionGlobal(); + mPosGlobal = pos_global; + setPickName(name); + setPickDesc(desc); + mSnapshotCtrl->setImageAssetID(snapshot_id); + mParcelId = parcel_id; + + setPickLocation(createLocationText(std::string(""), SET_LOCATION_NOTICE, name, pos_global)); + childSetLabelArg(XML_BTN_SAVE, SAVE_BTN_LABEL, std::string("Pick")); + + mIsPickNew = true; +} + +// Fill in some reasonable defaults for a new pick. +void LLPanelPick::prepareNewPick() +{ // Try to fill in the current parcel LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); if (parcel) { - setPickName(parcel->getName()); - setPickDesc(parcel->getDesc()); - mSnapshotCtrl->setImageAssetID(parcel->getSnapshotID()); + prepareNewPick(gAgent.getPositionGlobal(), + parcel->getName(), + parcel->getDesc(), + parcel->getSnapshotID(), + parcel->getID()); } - - sendUpdate(); - - childSetLabelArg(XML_BTN_SAVE, SAVE_BTN_LABEL, std::string("Pick")); } /*virtual*/ void LLPanelPick::processProperties(void* data, EAvatarProcessorType type) @@ -235,6 +255,15 @@ void LLPanelPick::setEditMode( BOOL edit_mode ) deleteAllChildren(); + // *WORKAROUND: for EXT-931. Children are created for both XML_PANEL_EDIT_PICK & XML_PANEL_PICK_INFO files + // The reason is in LLPanel::initPanelXML called from the LLUICtrlFactory::buildPanel(). + // It creates children from the xml file stored while previous initializing in the "mXMLFilename" member + // and then in creates children from the parameters passed from the LLUICtrlFactory::buildPanel(). + // Xml filename is stored after LLPanel::initPanelXML is called (added with export-from-ll/viewer-2-0, r1594 into LLUICtrlFactory::buildPanel & LLUICtrlFactory::buildFloater) + // In case panel creates children from the different xml files they appear from both files. + // So, let clear xml filename related to this instance. + setXMLFilename(""); + if (edit_mode) { LLUICtrlFactory::getInstance()->buildPanel(this, XML_PANEL_EDIT_PICK); @@ -258,6 +287,40 @@ void LLPanelPick::setEditMode( BOOL edit_mode ) updateButtons(); } +////////////////////////////////////////////////////////////////////////// +// PROTECTED AREA +////////////////////////////////////////////////////////////////////////// + +//static +std::string LLPanelPick::createLocationText(const std::string& owner_name, const std::string& original_name, + const std::string& sim_name, const LLVector3d& pos_global) +{ + std::string location_text; + location_text.append(owner_name); + if (!original_name.empty()) + { + if (!location_text.empty()) location_text.append(", "); + location_text.append(original_name); + + } + if (!sim_name.empty()) + { + if (!location_text.empty()) location_text.append(", "); + location_text.append(sim_name); + } + + if (!location_text.empty()) location_text.append(" "); + + if (!pos_global.isNull()) + { + S32 region_x = llround((F32)pos_global.mdV[VX]) % REGION_WIDTH_UNITS; + S32 region_y = llround((F32)pos_global.mdV[VY]) % REGION_WIDTH_UNITS; + S32 region_z = llround((F32)pos_global.mdV[VZ]); + location_text.append(llformat(" (%d, %d, %d)", region_x, region_y, region_z)); + } + return location_text; +} + void LLPanelPick::setPickName(std::string name) { if (mEditMode) @@ -288,7 +351,7 @@ void LLPanelPick::setPickDesc(std::string desc) mDesc = desc; } -void LLPanelPick::setPickLocation(std::string location) +void LLPanelPick::setPickLocation(const std::string& location) { childSetWrappedText(XML_LOCATION, location); @@ -375,6 +438,12 @@ void LLPanelPick::onClickCancel() { if (!mEditMode) return; + if (mIsPickNew) + { + mBackCb(this, LLSD()); + return; + } + LLUUID pick_id = mPickId; LLUUID creator_id = mCreatorId; reset(); @@ -385,29 +454,34 @@ void LLPanelPick::onClickCancel() void LLPanelPick::onClickSet() { if (!mEditMode) return; - if (!mDataReceived) return; + if (!mIsPickNew && !mDataReceived) return; // Save location for later. mPosGlobal = gAgent.getPositionGlobal(); - S32 region_x = llround((F32)mPosGlobal.mdV[VX]) % REGION_WIDTH_UNITS; - S32 region_y = llround((F32)mPosGlobal.mdV[VY]) % REGION_WIDTH_UNITS; - S32 region_z = llround((F32)mPosGlobal.mdV[VZ]); - - std::string location_text = "(will update after save), "; - location_text.append(mSimName); - location_text.append(llformat(" (%d, %d, %d)", region_x, region_y, region_z)); - - setPickLocation(location_text); + LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if (parcel) + { + mParcelId = parcel->getID(); + mSimName = parcel->getName(); + } + setPickLocation(createLocationText(std::string(""), SET_LOCATION_NOTICE, mSimName, mPosGlobal)); } // static void LLPanelPick::onClickSave() { if (!mEditMode) return; - if (!mDataReceived) return; + if (!mIsPickNew && !mDataReceived) return; sendUpdate(); + + if (mIsPickNew) + { + mBackCb(this, LLSD()); + return; + } + setEditMode(FALSE); } diff --git a/indra/newview/llpanelpick.h b/indra/newview/llpanelpick.h index 2cd4706dfe..7ce58b59af 100644 --- a/indra/newview/llpanelpick.h +++ b/indra/newview/llpanelpick.h @@ -56,9 +56,14 @@ public: /*virtual*/ BOOL postBuild(); - // Create a new pick, including creating an id, giving a sane - // initial position, etc. - void createNewPick(); + // Prepares a new pick, including creating an id, giving a sane + // initial position, etc (saved on clicking Save Pick button - onClickSave callback). + void prepareNewPick(); + void prepareNewPick(const LLVector3d pos_global, + const std::string& name, + const std::string& desc, + const LLUUID& snapshot_id, + const LLUUID& parcel_id); //initializes the panel with data of the pick with id = pick_id //owned by the avatar with id = creator_id @@ -87,9 +92,16 @@ public: protected: + /** + * "Location text" is actually the owner name, the original + * name that owner gave the parcel, and the location. + */ + static std::string createLocationText(const std::string& owner_name, const std::string& original_name, + const std::string& sim_name, const LLVector3d& pos_global); + void setPickName(std::string name); void setPickDesc(std::string desc); - void setPickLocation(std::string location); + void setPickLocation(const std::string& location); std::string getPickName(); std::string getPickDesc(); @@ -120,6 +132,7 @@ protected: BOOL mEditMode; LLTextureCtrl* mSnapshotCtrl; BOOL mDataReceived; + bool mIsPickNew; LLUUID mPickId; LLUUID mCreatorId; diff --git a/indra/newview/llpanelpicks.cpp b/indra/newview/llpanelpicks.cpp index d374d24316..93317e613f 100644 --- a/indra/newview/llpanelpicks.cpp +++ b/indra/newview/llpanelpicks.cpp @@ -34,6 +34,7 @@ #include "llagent.h" #include "llavatarconstants.h" +#include "llflatlistview.h" #include "lltexturectrl.h" #include "llviewergenericmessage.h" // send_generic_message #include "llmenugl.h" @@ -45,8 +46,6 @@ #include "llpanelavatar.h" #include "llpanelprofile.h" #include "llpanelpick.h" -#include "llscrollcontainer.h" -#include "lllistctrl.h" static const std::string XML_BTN_NEW = "new_btn"; static const std::string XML_BTN_DELETE = "trash_btn"; @@ -148,7 +147,7 @@ LLPickItem* LLPanelPicks::getSelectedPickItem() BOOL LLPanelPicks::postBuild() { - mPicksList = getChild("picks_list"); + mPicksList = getChild("picks_list"); childSetAction(XML_BTN_DELETE, boost::bind(&LLPanelPicks::onClickDelete, this)); @@ -199,6 +198,11 @@ void LLPanelPicks::onOpen(const LLSD& key) mPopupMenu->setItemVisible("pick_separator", TRUE); } + if(getAvatarId() != key.asUUID()) + { + mPicksList->goToTop(); + } + LLPanelProfileTab::onOpen(key); } @@ -314,7 +318,7 @@ void LLPanelPicks::onClickNew() { buildPickPanel(); mPickPanel->setEditMode(TRUE); - mPickPanel->createNewPick(); + mPickPanel->prepareNewPick(); getProfilePanel()->togglePanel(mPickPanel); } @@ -386,7 +390,6 @@ void LLPickItem::init(LLPickData* pick_data) setPickDesc(pick_data->desc); setSnapshotId(pick_data->snapshot_id); mPosGlobal = pick_data->pos_global; - mLocation = pick_data->location_text; LLTextureCtrl* picture = getChild("picture"); picture->setImageAssetID(pick_data->snapshot_id); @@ -434,11 +437,6 @@ const LLVector3d& LLPickItem::getPosGlobal() return mPosGlobal; } -const std::string& LLPickItem::getLocation() -{ - return mLocation; -} - const std::string LLPickItem::getDescription() { return childGetValue("picture_descr").asString(); diff --git a/indra/newview/llpanelpicks.h b/indra/newview/llpanelpicks.h index 97e8e607c8..27a21305b3 100644 --- a/indra/newview/llpanelpicks.h +++ b/indra/newview/llpanelpicks.h @@ -48,7 +48,7 @@ class LLPanelPick; class LLAgent; class LLMenuGL; class LLPickItem; -class LLListCtrl; +class LLFlatListView; class LLPanelPicks : public LLPanelProfileTab @@ -107,7 +107,7 @@ private: LLMenuGL* mPopupMenu; LLPanelProfile* mProfilePanel; LLPanelPick* mPickPanel; - LLListCtrl* mPicksList; + LLFlatListView* mPicksList; }; class LLPickItem : public LLPanel, public LLAvatarPropertiesObserver @@ -142,8 +142,6 @@ public: const LLVector3d& getPosGlobal(); - const std::string& getLocation(); - const std::string getDescription(); /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); @@ -167,7 +165,6 @@ protected: bool mNeedData; std::string mPickName; - std::string mLocation; }; #endif // LL_LLPANELPICKS_H diff --git a/indra/newview/llpanelplaceinfo.cpp b/indra/newview/llpanelplaceinfo.cpp index 7a19b8877e..eb269fabe3 100644 --- a/indra/newview/llpanelplaceinfo.cpp +++ b/indra/newview/llpanelplaceinfo.cpp @@ -44,6 +44,7 @@ #include "llqueryflags.h" #include "llbutton.h" +#include "lliconctrl.h" #include "lllineeditor.h" #include "llscrollcontainer.h" #include "lltextbox.h" @@ -55,6 +56,7 @@ #include "llfloaterworldmap.h" #include "llinventorymodel.h" #include "lllandmarkactions.h" +#include "llpanelpick.h" #include "lltexturectrl.h" #include "llviewerinventory.h" #include "llviewerparcelmgr.h" @@ -68,6 +70,7 @@ LLPanelPlaceInfo::LLPanelPlaceInfo() : LLPanel(), mParcelID(), mRequestedID(), + mPosRegion(), mLandmarkID(), mMinHeight(0), mScrollingPanel(NULL), @@ -88,6 +91,8 @@ BOOL LLPanelPlaceInfo::postBuild() mTitle = getChild("panel_title"); mCurrentTitle = mTitle->getText(); + mForSaleIcon = getChild("icon_for_sale"); + // Since this is only used in the directory browser, always // disable the snapshot control. Otherwise clicking on it will // open a texture picker. @@ -251,6 +256,8 @@ void LLPanelPlaceInfo::resetLocation() mParcelID.setNull(); mRequestedID.setNull(); mLandmarkID.setNull(); + mPosRegion.clearVec(); + mForSaleIcon->setVisible(FALSE); std::string not_available = getString("not_available"); mMaturityRatingText->setValue(not_available); mParcelOwner->setValue(not_available); @@ -449,12 +456,27 @@ void LLPanelPlaceInfo::processParcelInfo(const LLParcelData& parcel_data) //update for_sale banner, here we should use DFQ_FOR_SALE instead of PF_FOR_SALE //because we deal with remote parcel response format - bool isForSale = (parcel_data.flags & DFQ_FOR_SALE)? TRUE : FALSE; - getChild("icon_for_sale")->setVisible(isForSale); + bool isForSale = (parcel_data.flags & DFQ_FOR_SALE) && + mInfoType == AGENT ? TRUE : FALSE; + mForSaleIcon->setVisible(isForSale); + + S32 region_x; + S32 region_y; + S32 region_z; - S32 region_x = llround(parcel_data.global_x) % REGION_WIDTH_UNITS; - S32 region_y = llround(parcel_data.global_y) % REGION_WIDTH_UNITS; - S32 region_z = llround(parcel_data.global_z); + // If the region position is zero, grab position from the global + if(mPosRegion.isExactlyZero()) + { + region_x = llround(parcel_data.global_x) % REGION_WIDTH_UNITS; + region_y = llround(parcel_data.global_y) % REGION_WIDTH_UNITS; + region_z = llround(parcel_data.global_z); + } + else + { + region_x = llround(mPosRegion.mV[VX]); + region_y = llround(mPosRegion.mV[VY]); + region_z = llround(mPosRegion.mV[VZ]); + } std::string name = getString("not_available"); if (!parcel_data.sim_name.empty()) @@ -487,15 +509,15 @@ void LLPanelPlaceInfo::displayParcelInfo(const LLUUID& region_id, if (!region) return; + mPosRegion.setVec((F32)fmod(pos_global.mdV[VX], (F64)REGION_WIDTH_METERS), + (F32)fmod(pos_global.mdV[VY], (F64)REGION_WIDTH_METERS), + (F32)pos_global.mdV[VZ]); + LLSD body; std::string url = region->getCapability("RemoteParcelRequest"); if (!url.empty()) { - F32 region_x = (F32)fmod(pos_global.mdV[VX], (F64)REGION_WIDTH_METERS); - F32 region_y = (F32)fmod(pos_global.mdV[VY], (F64)REGION_WIDTH_METERS); - LLVector3 pos_region(region_x, region_y, (F32)pos_global.mdV[VZ]); - - body["location"] = ll_sd_from_vector3(pos_region); + body["location"] = ll_sd_from_vector3(mPosRegion); if (!region_id.isNull()) { body["region_id"] = region_id; @@ -536,20 +558,22 @@ void LLPanelPlaceInfo::displaySelectedParcelInfo(LLParcel* parcel, { case SIM_ACCESS_MATURE: parcel_data.flags = 0x1; + break; case SIM_ACCESS_ADULT: parcel_data.flags = 0x2; + break; default: parcel_data.flags = 0; } parcel_data.desc = parcel->getDesc(); parcel_data.name = parcel->getName(); - parcel_data.sim_name = gAgent.getRegion()->getName(); + parcel_data.sim_name = region->getName(); parcel_data.snapshot_id = parcel->getSnapshotID(); - parcel_data.global_x = pos_global.mdV[0]; - parcel_data.global_y = pos_global.mdV[1]; - parcel_data.global_z = pos_global.mdV[2]; + parcel_data.global_x = pos_global.mdV[VX]; + parcel_data.global_y = pos_global.mdV[VY]; + parcel_data.global_z = pos_global.mdV[VZ]; std::string on = getString("on"); std::string off = getString("off"); @@ -853,30 +877,19 @@ void LLPanelPlaceInfo::createLandmark(const LLUUID& folder_id) folder_id.notNull() ? folder_id : gInventory.findCategoryUUIDForType(LLAssetType::AT_LANDMARK)); } -void LLPanelPlaceInfo::createPick(const LLVector3d& global_pos) +void LLPanelPlaceInfo::createPick(const LLVector3d& pos_global, LLPanelPick* pick_panel) { - LLPickData pick_data; - - pick_data.agent_id = gAgent.getID(); - pick_data.session_id = gAgent.getSessionID(); - pick_data.pick_id = LLUUID::generateNewID(); - pick_data.creator_id = gAgentID; - - //legacy var needs to be deleted - pick_data.top_pick = FALSE; - pick_data.parcel_id = mParcelID; - pick_data.name = mParcelName->getText(); - if (pick_data.name.empty()) + std::string name = mParcelName->getText(); + if (name.empty()) { - pick_data.name = mRegionName->getText(); + name = mRegionName->getText(); } - pick_data.desc = mDescEditor->getText(); - pick_data.snapshot_id = mSnapshotCtrl->getImageAssetID(); - pick_data.pos_global = global_pos; - pick_data.sort_order = 0; - pick_data.enabled = TRUE; - LLAvatarPropertiesProcessor::instance().sendPickInfoUpdate(&pick_data); + pick_panel->prepareNewPick(pos_global, + name, + mDescEditor->getText(), + mSnapshotCtrl->getImageAssetID(), + mParcelID); } // virtual diff --git a/indra/newview/llpanelplaceinfo.h b/indra/newview/llpanelplaceinfo.h index 32ae4334aa..23a845bc20 100644 --- a/indra/newview/llpanelplaceinfo.h +++ b/indra/newview/llpanelplaceinfo.h @@ -38,15 +38,15 @@ #include "v3dmath.h" #include "lluuid.h" -#include "lliconctrl.h" - #include "llpanelmedia.h" #include "llremoteparcelrequest.h" class LLButton; class LLInventoryItem; class LLLineEditor; +class LLPanelPick; class LLParcel; +class LLIconCtrl; class LLTextBox; class LLTextEditor; class LLTextureCtrl; @@ -88,7 +88,7 @@ public: // Create a pick for the location specified // by global_pos. - void createPick(const LLVector3d& global_pos); + void createPick(const LLVector3d& pos_global, LLPanelPick* pick_panel); BOOL isMediaPanelVisible(); void toggleMediaPanel(BOOL visible); @@ -133,11 +133,13 @@ private: LLUUID mParcelID; LLUUID mRequestedID; LLUUID mLandmarkID; + LLVector3 mPosRegion; std::string mCurrentTitle; S32 mMinHeight; INFO_TYPE mInfoType; LLTextBox* mTitle; + LLIconCtrl* mForSaleIcon; LLTextureCtrl* mSnapshotCtrl; LLTextBox* mRegionName; LLTextBox* mParcelName; diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp index 11ddc3dd9a..4e070df7eb 100644 --- a/indra/newview/llpanelplaces.cpp +++ b/indra/newview/llpanelplaces.cpp @@ -48,14 +48,15 @@ #include "lluictrlfactory.h" #include "llagent.h" +#include "llavatarpropertiesprocessor.h" #include "llfloaterworldmap.h" #include "llinventorymodel.h" #include "lllandmarkactions.h" #include "lllandmarklist.h" #include "llpanelplaceinfo.h" #include "llpanellandmarks.h" +#include "llpanelpick.h" #include "llpanelteleporthistory.h" -#include "llsidetray.h" #include "llteleporthistorystorage.h" #include "lltoggleablemenu.h" #include "llviewerinventory.h" @@ -72,10 +73,12 @@ static const std::string REMOTE_PLACE_INFO_TYPE = "remote_place"; static const std::string TELEPORT_HISTORY_INFO_TYPE = "teleport_history"; // Helper functions +static bool is_agent_in_selected_parcel(LLParcel* parcel); static bool cmp_folders(const folder_pair_t& left, const folder_pair_t& right); static std::string getFullFolderName(const LLViewerInventoryCategory* cat); static void collectLandmarkFolders(LLInventoryModel::cat_array_t& cats); static void onSLURLBuilt(std::string& slurl); +static void setAllChildrenVisible(LLView* view, BOOL visible); //Observer classes class LLPlacesParcelObserver : public LLParcelObserver @@ -118,6 +121,7 @@ LLPanelPlaces::LLPanelPlaces() mActivePanel(NULL), mFilterEditor(NULL), mPlaceInfo(NULL), + mPickPanel(NULL), mItem(NULL), mPlaceMenu(NULL), mLandmarkMenu(NULL), @@ -332,6 +336,7 @@ void LLPanelPlaces::onFilterEdit(const std::string& search_string) LLStringUtil::toUpper(mFilterSubString); LLStringUtil::trimHead(mFilterSubString); + if (mActivePanel) mActivePanel->onSearchEdit(mFilterSubString); } } @@ -380,6 +385,7 @@ void LLPanelPlaces::onTeleportButtonClicked() } else { + if (mActivePanel) mActivePanel->onTeleport(); } } @@ -425,6 +431,7 @@ void LLPanelPlaces::onShowOnMapButtonClicked() } else { + if (mActivePanel) mActivePanel->onShowOnMap(); } } @@ -496,10 +503,23 @@ void LLPanelPlaces::onOverflowMenuItemClicked(const LLSD& param) { if (!mPlaceInfo) return; - - mPlaceInfo->createPick(mPosGlobal); - onBackButtonClicked(); + if (mPickPanel == NULL) + { + mPickPanel = new LLPanelPick(); + addChild(mPickPanel); + + mPickPanel->setExitCallback(boost::bind(&LLPanelPlaces::togglePickPanel, this, FALSE)); + } + + togglePickPanel(TRUE); + + LLRect rect = getRect(); + mPickPanel->reshape(rect.getWidth(), rect.getHeight()); + mPickPanel->setRect(rect); + mPickPanel->setEditMode(TRUE); + + mPlaceInfo->createPick(mPosGlobal, mPickPanel); } } @@ -508,6 +528,12 @@ void LLPanelPlaces::onCreateLandmarkButtonClicked(const LLUUID& folder_id) if (!mPlaceInfo) return; + // To prevent creating duplicate landmarks + // disable landmark creating buttons until + // the information on existing landmarks is reloaded. + mCreateLandmarkBtn->setEnabled(FALSE); + mFolderMenuBtn->setEnabled(FALSE); + mPlaceInfo->createLandmark(folder_id); } @@ -544,6 +570,14 @@ void LLPanelPlaces::toggleMediaPanel() onOpen(LLSD().insert("type", AGENT_INFO_TYPE)); } +void LLPanelPlaces::togglePickPanel(BOOL visible) +{ + setAllChildrenVisible(this, !visible); + + if (mPickPanel) + mPickPanel->setVisible(visible); +} + void LLPanelPlaces::togglePlaceInfoPanel(BOOL visible) { if (!mPlaceInfo) @@ -568,16 +602,16 @@ void LLPanelPlaces::changedParcelSelection() if (!mPlaceInfo) return; - mParcel = LLViewerParcelMgr::getInstance()->getFloatingParcelSelection(); + LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance(); + mParcel = parcel_mgr->getFloatingParcelSelection(); LLParcel* parcel = mParcel->getParcel(); - LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion(); + LLViewerRegion* region = parcel_mgr->getSelectionRegion(); if (!region || !parcel) return; // If agent is inside the selected parcel show agent's region, // otherwise show region of agent's selection point. - if (region == gAgent.getRegion() && - parcel->getLocalID() == LLViewerParcelMgr::getInstance()->getAgentParcel()->getLocalID()) + if (is_agent_in_selected_parcel(parcel)) { mPosGlobal = gAgent.getPositionGlobal(); } @@ -629,6 +663,10 @@ void LLPanelPlaces::changedInventory(U32 mask) mActivePanel = dynamic_cast(mTabContainer->getCurrentPanel()); + // Filter applied to show all items. + if (mActivePanel) + mActivePanel->onSearchEdit(mFilterSubString); + // we don't need to monitor inventory changes anymore, // so remove the observer gInventory.removeObserver(mInventoryObserver); @@ -658,7 +696,7 @@ void LLPanelPlaces::updateVerbs() bool is_agent_place_info_visible = mPlaceInfoType == AGENT_INFO_TYPE; bool is_create_landmark_visible = mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE; bool is_media_panel_visible = mPlaceInfo->isMediaPanelVisible(); - + mTeleportBtn->setVisible(!is_create_landmark_visible); mShareBtn->setVisible(!is_create_landmark_visible); mCreateLandmarkBtn->setVisible(is_create_landmark_visible); @@ -679,10 +717,11 @@ void LLPanelPlaces::updateVerbs() else if (is_create_landmark_visible) { // Enable "Create Landmark" only if there is no landmark - // for the current parcel. - bool no_landmark = !LLLandmarkActions::landmarkAlreadyExists(); - mCreateLandmarkBtn->setEnabled(no_landmark); - mFolderMenuBtn->setEnabled(no_landmark); + // for the current parcel and agent is inside it. + bool enable = !LLLandmarkActions::landmarkAlreadyExists() && + is_agent_in_selected_parcel(mParcel->getParcel()); + mCreateLandmarkBtn->setEnabled(enable); + mFolderMenuBtn->setEnabled(enable); } else if (mPlaceInfoType == LANDMARK_INFO_TYPE || mPlaceInfoType == REMOTE_PLACE_INFO_TYPE) { @@ -693,6 +732,7 @@ void LLPanelPlaces::updateVerbs() } else { + if (mActivePanel) mActivePanel->updateVerbs(); } } @@ -822,6 +862,18 @@ void LLPanelPlaces::showLandmarkFoldersMenu() LLMenuGL::showPopup(this, menu, btn_rect.mRight, btn_rect.mTop); } +static bool is_agent_in_selected_parcel(LLParcel* parcel) +{ + LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance(); + + LLViewerRegion* region = parcel_mgr->getSelectionRegion(); + if (!region || !parcel) + return false; + + return region == gAgent.getRegion() && + parcel->getLocalID() == parcel_mgr->getAgentParcel()->getLocalID(); +} + static bool cmp_folders(const folder_pair_t& left, const folder_pair_t& right) { return left.second < right.second; @@ -895,3 +947,16 @@ static void onSLURLBuilt(std::string& slurl) LLNotifications::instance().add("CopySLURL", args); } + +static void setAllChildrenVisible(LLView* view, BOOL visible) +{ + const LLView::child_list_t* children = view->getChildList(); + for (LLView::child_list_const_iter_t child_it = children->begin(); child_it != children->end(); ++child_it) + { + LLView* child = *child_it; + if (child->getParent() == view) + { + child->setVisible(visible); + } + } +} diff --git a/indra/newview/llpanelplaces.h b/indra/newview/llpanelplaces.h index 54bc2b9003..f208e91237 100644 --- a/indra/newview/llpanelplaces.h +++ b/indra/newview/llpanelplaces.h @@ -37,6 +37,7 @@ class LLInventoryItem; class LLFilterEditor; class LLLandmark; +class LLPanelPick; class LLPanelPlaceInfo; class LLPanelPlacesTab; class LLParcelSelection; @@ -77,6 +78,7 @@ private: void onBackButtonClicked(); void toggleMediaPanel(); + void togglePickPanel(BOOL visible); void togglePlaceInfoPanel(BOOL visible); void onAgentParcelChange(); @@ -88,6 +90,7 @@ private: LLPanelPlacesTab* mActivePanel; LLTabContainer* mTabContainer; LLPanelPlaceInfo* mPlaceInfo; + LLPanelPick* mPickPanel; LLToggleableMenu* mPlaceMenu; LLToggleableMenu* mLandmarkMenu; diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index ce01568e99..be28129451 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -104,10 +104,6 @@ void LLPanelProfile::onOpen(const LLSD& key) { getTabCtrl()->getCurrentPanel()->onOpen(getAvatarId()); } - - // Update the avatar name. - gCacheName->get(getAvatarId(), FALSE, - boost::bind(&LLPanelProfile::onAvatarNameCached, this, _1, _2, _3, _4)); } //*TODO redo panel toggling @@ -171,8 +167,3 @@ void LLPanelProfile::setAllChildrenVisible(BOOL visible) } } -void LLPanelProfile::onAvatarNameCached(const LLUUID& id, const std::string& first_name, const std::string& last_name, BOOL is_group) -{ - llassert(getAvatarId() == id); - getChild("user_name", FALSE)->setValue(first_name + " " + last_name); -} diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h index 0864ec1bc3..bb893f257a 100644 --- a/indra/newview/llpanelprofile.h +++ b/indra/newview/llpanelprofile.h @@ -72,13 +72,6 @@ protected: profile_tabs_t& getTabContainer() { return mTabContainer; } private: - // LLCacheName will call this function when avatar name is loaded from server. - // This is required to display names that have not been cached yet. - void onAvatarNameCached( - const LLUUID& id, - const std::string& first_name, - const std::string& last_name, - BOOL is_group); LLTabContainer* mTabCtrl; profile_tabs_t mTabContainer; diff --git a/indra/newview/llpanelprofileview.cpp b/indra/newview/llpanelprofileview.cpp index 0762bbeb87..f1db2416e4 100644 --- a/indra/newview/llpanelprofileview.cpp +++ b/indra/newview/llpanelprofileview.cpp @@ -70,6 +70,10 @@ void LLPanelProfileView::onOpen(const LLSD& key) setAvatarId(id); } + // Update the avatar name. + gCacheName->get(getAvatarId(), FALSE, + boost::bind(&LLPanelProfileView::onAvatarNameCached, this, _1, _2, _3, _4)); + // status should only show if viewer has permission to view online/offline. EXT-453 mStatusText->setVisible(isGrantedToSeeOnlineStatus()); updateOnlineStatus(); @@ -133,4 +137,21 @@ void LLPanelProfileView::updateOnlineStatus() mStatusText->setValue(status); } +void LLPanelProfileView::onAvatarNameCached(const LLUUID& id, const std::string& first_name, const std::string& last_name, BOOL is_group) +{ + llassert(getAvatarId() == id); + getChild("user_name", FALSE)->setValue(first_name + " " + last_name); +} + +void LLPanelProfileView::togglePanel(LLPanel* panel) +{ + LLPanelProfile::togglePanel(panel); + if(FALSE == panel->getVisible()) + { + // LLPanelProfile::togglePanel shows/hides all children, + // we don't want to display online status for non friends, so re-hide it here + mStatusText->setVisible(isGrantedToSeeOnlineStatus()); + } +} + // EOF diff --git a/indra/newview/llpanelprofileview.h b/indra/newview/llpanelprofileview.h index 533ab94cc3..07a6c3a9a0 100644 --- a/indra/newview/llpanelprofileview.h +++ b/indra/newview/llpanelprofileview.h @@ -60,6 +60,8 @@ public: /*virtual*/ BOOL postBuild(); + /*virtual*/ void togglePanel(LLPanel* panel); + protected: void onBackBtnClick(); @@ -67,6 +69,14 @@ protected: void updateOnlineStatus(); private: + // LLCacheName will call this function when avatar name is loaded from server. + // This is required to display names that have not been cached yet. + void onAvatarNameCached( + const LLUUID& id, + const std::string& first_name, + const std::string& last_name, + BOOL is_group); + LLTextBox* mStatusText; }; diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp index 1fd7928bfc..f6672d9c8b 100644 --- a/indra/newview/llpanelteleporthistory.cpp +++ b/indra/newview/llpanelteleporthistory.cpp @@ -40,6 +40,83 @@ #include "llteleporthistorystorage.h" #include "llaccordionctrl.h" #include "llaccordionctrltab.h" +#include "llflatlistview.h" +#include "lltextbox.h" + +class LLTeleportHistoryFlatItem : public LLPanel +{ +public: + LLTeleportHistoryFlatItem(S32 index, const std::string ®ion_name); + virtual ~LLTeleportHistoryFlatItem() {}; + + virtual BOOL postBuild(); + + S32 getIndex() { return mIndex; } + + /*virtual*/ void setValue(const LLSD& value); + + void onMouseEnter(S32 x, S32 y, MASK mask); + void onMouseLeave(S32 x, S32 y, MASK mask); +private: + void onInfoBtnClick(); + + LLButton* mInfoBtn; + + S32 mIndex; + std::string mRegionName; +}; + +LLTeleportHistoryFlatItem::LLTeleportHistoryFlatItem(S32 index, const std::string ®ion_name) +: LLPanel(), + mIndex(index), + mRegionName(region_name) +{ + LLUICtrlFactory::getInstance()->buildPanel(this, "panel_teleport_history_item.xml"); +} + +//virtual +BOOL LLTeleportHistoryFlatItem::postBuild() +{ + LLTextBox *region = getChild("region"); + region->setValue(mRegionName); + + mInfoBtn = getChild("info_btn"); + mInfoBtn->setClickedCallback(boost::bind(&LLTeleportHistoryFlatItem::onInfoBtnClick, this)); + + return true; +} + +void LLTeleportHistoryFlatItem::setValue(const LLSD& value) +{ + if (!value.isMap()) return;; + if (!value.has("selected")) return; + childSetVisible("selected_icon", value["selected"]); +} + +void LLTeleportHistoryFlatItem::onMouseEnter(S32 x, S32 y, MASK mask) +{ + childSetVisible("hovered_icon", true); + mInfoBtn->setVisible(true); + + LLPanel::onMouseEnter(x, y, mask); +} + +void LLTeleportHistoryFlatItem::onMouseLeave(S32 x, S32 y, MASK mask) +{ + childSetVisible("hovered_icon", false); + mInfoBtn->setVisible(false); + + LLPanel::onMouseLeave(x, y, mask); +} + +void LLTeleportHistoryFlatItem::onInfoBtnClick() +{ + LLSD params; + params["id"] = mIndex; + params["type"] = "teleport_history"; + + LLSideTray::getInstance()->showPanel("panel_places", params); +} // Not yet implemented; need to remove buildPanel() from constructor when we switch //static LLRegisterPanelClassWrapper t_teleport_history("panel_teleport_history"); @@ -48,7 +125,7 @@ LLTeleportHistoryPanel::LLTeleportHistoryPanel() : LLPanelPlacesTab(), mFilterSubString(LLStringUtil::null), mTeleportHistory(NULL), - mHistoryAccordeon(NULL), + mHistoryAccordion(NULL), mLastSelectedScrollList(NULL) { LLUICtrlFactory::getInstance()->buildPanel(this, "panel_teleport_history.xml"); @@ -66,26 +143,25 @@ BOOL LLTeleportHistoryPanel::postBuild() mTeleportHistory->setHistoryChangedCallback(boost::bind(&LLTeleportHistoryPanel::showTeleportHistory, this)); } - mHistoryAccordeon = getChild("history_accordion"); + mHistoryAccordion = getChild("history_accordion"); - if (mHistoryAccordeon) + if (mHistoryAccordion) { - for (child_list_const_iter_t iter = mHistoryAccordeon->beginChild(); iter != mHistoryAccordeon->endChild(); iter++) + for (child_list_const_iter_t iter = mHistoryAccordion->beginChild(); iter != mHistoryAccordion->endChild(); iter++) { if (dynamic_cast(*iter)) { LLAccordionCtrlTab* tab = (LLAccordionCtrlTab*)*iter; mItemContainers.put(tab); - LLScrollListCtrl* sl = getScrollListFromTab(tab); - if (sl) + LLFlatListView* fl = getFlatListViewFromTab(tab); + if (fl) { - sl->setDoubleClickCallback(onDoubleClickItem, this); - sl->setCommitOnSelectionChange(FALSE); - sl->setCommitCallback(boost::bind(&LLTeleportHistoryPanel::handleItemSelect, this, sl)); + fl->setCommitOnSelectionChange(true); + //fl->setDoubleClickCallback(onDoubleClickItem, this); + fl->setCommitCallback(boost::bind(&LLTeleportHistoryPanel::handleItemSelect, this, fl)); } - } } } @@ -109,13 +185,12 @@ void LLTeleportHistoryPanel::onShowOnMap() if (!mLastSelectedScrollList) return; - LLScrollListItem* itemp = mLastSelectedScrollList->getFirstSelected(); + LLTeleportHistoryFlatItem* itemp = dynamic_cast (mLastSelectedScrollList->getSelectedItem()); + if(!itemp) return; - S32 index = itemp->getColumn(LIST_INDEX)->getValue().asInteger(); - - LLVector3d global_pos = mTeleportHistory->getItems()[mTeleportHistory->getItems().size() - 1 - index].mGlobalPos; + LLVector3d global_pos = mTeleportHistory->getItems()[itemp->getIndex()].mGlobalPos; if (!global_pos.isExactlyZero()) { @@ -130,14 +205,12 @@ void LLTeleportHistoryPanel::onTeleport() if (!mLastSelectedScrollList) return; - LLScrollListItem* itemp = mLastSelectedScrollList->getFirstSelected(); + LLTeleportHistoryFlatItem* itemp = dynamic_cast (mLastSelectedScrollList->getSelectedItem()); if(!itemp) return; - S32 index = itemp->getColumn(LIST_INDEX)->getValue().asInteger(); - // teleport to existing item in history, so we don't add it again - mTeleportHistory->goToItem(mTeleportHistory->getItems().size() - 1 - index); + mTeleportHistory->goToItem(itemp->getIndex()); } /* @@ -177,15 +250,15 @@ void LLTeleportHistoryPanel::updateVerbs() return; } - LLScrollListItem* itemp = mLastSelectedScrollList->getFirstSelected(); + LLTeleportHistoryFlatItem* itemp = dynamic_cast (mLastSelectedScrollList->getSelectedItem()); - mTeleportBtn->setEnabled(NULL != itemp && 0 < itemp->getColumn(LIST_INDEX)->getValue().asInteger()); + mTeleportBtn->setEnabled(NULL != itemp && 0 < itemp->getIndex()); mShowOnMapBtn->setEnabled(NULL != itemp); } void LLTeleportHistoryPanel::showTeleportHistory() { - if (!mHistoryAccordeon) + if (!mHistoryAccordion) return; const LLTeleportHistoryStorage::slurl_list_t& hist_items = mTeleportHistory->getItems(); @@ -193,15 +266,17 @@ void LLTeleportHistoryPanel::showTeleportHistory() const U32 seconds_in_day = 24 * 60 * 60; LLDate curr_date = LLDate::now(); - curr_date.secondsSinceEpoch(curr_date.secondsSinceEpoch() + seconds_in_day); - S32 curr_tab = -1; S32 tabs_cnt = mItemContainers.size(); S32 curr_year = 0, curr_month = 0, curr_day = 0; + + curr_date.split(&curr_year, &curr_month, &curr_day); + curr_date.fromYMDHMS(curr_year, curr_month, curr_day); // Set hour, min, and sec to 0 + curr_date.secondsSinceEpoch(curr_date.secondsSinceEpoch() + seconds_in_day); - LLScrollListCtrl *curr_scroll_list = NULL; + LLFlatListView* curr_flat_view = NULL; - S32 index = 0; + S32 index = hist_items.size() - 1; for (LLTeleportHistoryStorage::slurl_list_t::const_reverse_iterator iter = hist_items.rbegin(); iter != hist_items.rend(); ++iter) @@ -219,71 +294,104 @@ void LLTeleportHistoryPanel::showTeleportHistory() { const LLDate &date = (*iter).mDate; - S32 year, month, day; - if (!date.split(&year, &month, &day)) - { - llwarns << "Failed to split date: " << date << llendl; - continue; - } - - if (day != curr_day || month != curr_month || year != curr_year) + if (date < curr_date) { LLAccordionCtrlTab* tab = NULL; - while (curr_tab < tabs_cnt - 1 && (day != curr_day || month != curr_month || year != curr_year)) + while (curr_tab < tabs_cnt - 1 && date < curr_date) { curr_tab++; tab = mItemContainers.get(mItemContainers.size() - 1 - curr_tab); tab->setVisible(false); - + + if (curr_tab <= tabs_cnt - 4) + { curr_date.secondsSinceEpoch(curr_date.secondsSinceEpoch() - seconds_in_day); + } + else if (curr_tab == tabs_cnt - 3) // 6 day and older, low boundary is 1 month + { + curr_date = LLDate::now(); curr_date.split(&curr_year, &curr_month, &curr_day); - } + curr_month--; + if (0 == curr_month) + { + curr_month = 12; + curr_year--; + } + curr_date.fromYMDHMS(curr_year, curr_month, curr_day); + } + else if (curr_tab == tabs_cnt - 2) // 1 month and older, low boundary is 6 months + { + curr_date = LLDate::now(); + curr_date.split(&curr_year, &curr_month, &curr_day); + if (curr_month > 6) + { + curr_month -= 6; + } + else + { + curr_month += 6; + curr_year--; + } + curr_date.fromYMDHMS(curr_year, curr_month, curr_day); + + } + else // 6 months and older + { + curr_date.secondsSinceEpoch(0); + } + } tab->setVisible(true); - curr_scroll_list = getScrollListFromTab(tab); - if (curr_scroll_list) + curr_flat_view = getFlatListViewFromTab(tab); + if (curr_flat_view) { - curr_scroll_list->deleteAllItems(); + curr_flat_view->clear(); } } } - LLSD row; - row["id"] = index; - - if (curr_scroll_list) - { - LLSD& icon_column = row["columns"][LIST_ICON]; - icon_column["column"] = "landmark_icon"; - icon_column["type"] = "icon"; - icon_column["value"] = "inv_item_landmark.tga"; - - LLSD& region_column = row["columns"][LIST_ITEM_TITLE]; - region_column["column"] = "region"; - region_column["type"] = "text"; - region_column["value"] = (*iter).mTitle; - - LLSD& index_column = row["columns"][LIST_INDEX]; - index_column["column"] = "index"; - index_column["type"] = "text"; - index_column["value"] = index; - - index++; - - curr_scroll_list->addElement(row); + if (curr_flat_view) + { + curr_flat_view->addItem(new LLTeleportHistoryFlatItem(index, (*iter).mTitle)); } + + index--; } - mHistoryAccordeon->arrange(); + // Hide empty tabs from current to bottom + for (curr_tab++; curr_tab < tabs_cnt; curr_tab++) + mItemContainers.get(mItemContainers.size() - 1 - curr_tab)->setVisible(false); + + mHistoryAccordion->arrange(); updateVerbs(); } -void LLTeleportHistoryPanel::handleItemSelect(LLScrollListCtrl* sl) +void LLTeleportHistoryPanel::handleItemSelect(LLFlatListView* selected) { - mLastSelectedScrollList = sl; + mLastSelectedScrollList = selected; + + S32 tabs_cnt = mItemContainers.size(); + + for (S32 n = 0; n < tabs_cnt; n++) + { + LLAccordionCtrlTab* tab = mItemContainers.get(n); + + if (!tab->getVisible()) + continue; + + LLFlatListView *flv = getFlatListViewFromTab(tab); + if (!flv) + continue; + + if (flv == selected) + continue; + + flv->resetSelection(true); + } + updateVerbs(); } @@ -303,15 +411,16 @@ void LLTeleportHistoryPanel::onDoubleClickItem(void* user_data) LLSideTray::getInstance()->showPanel("panel_places", key);*/ } -LLScrollListCtrl* LLTeleportHistoryPanel::getScrollListFromTab(LLAccordionCtrlTab *tab) +LLFlatListView* LLTeleportHistoryPanel::getFlatListViewFromTab(LLAccordionCtrlTab *tab) { for (child_list_const_iter_t iter = tab->beginChild(); iter != tab->endChild(); iter++) { - if (dynamic_cast(*iter)) + if (dynamic_cast(*iter)) { - return (LLScrollListCtrl*)*iter; // There should be one scroll list per tab. + return (LLFlatListView*)*iter; // There should be one scroll list per tab. } } return NULL; } + diff --git a/indra/newview/llpanelteleporthistory.h b/indra/newview/llpanelteleporthistory.h index a1c15d087b..66187e69c6 100644 --- a/indra/newview/llpanelteleporthistory.h +++ b/indra/newview/llpanelteleporthistory.h @@ -34,7 +34,6 @@ #define LL_LLPANELTELEPORTHISTORY_H #include "lluictrlfactory.h" -#include "llscrolllistctrl.h" #include "llpanelplacestab.h" #include "llteleporthistory.h" @@ -42,6 +41,7 @@ class LLTeleportHistoryStorage; class LLAccordionCtrl; class LLAccordionCtrlTab; +class LLFlatListView; class LLTeleportHistoryPanel : public LLPanelPlacesTab { @@ -60,19 +60,12 @@ private: static void onDoubleClickItem(void* user_data); void showTeleportHistory(); - void handleItemSelect(LLScrollListCtrl* ); - LLScrollListCtrl* getScrollListFromTab(LLAccordionCtrlTab *); - - enum TELEPORT_HISTORY_COLUMN_ORDER - { - LIST_ICON, - LIST_ITEM_TITLE, - LIST_INDEX - }; + void handleItemSelect(LLFlatListView* ); + LLFlatListView* getFlatListViewFromTab(LLAccordionCtrlTab *); LLTeleportHistoryStorage* mTeleportHistory; - LLAccordionCtrl* mHistoryAccordeon; - LLScrollListCtrl* mLastSelectedScrollList; + LLAccordionCtrl* mHistoryAccordion; + LLFlatListView* mLastSelectedScrollList; std::string mFilterSubString; typedef LLDynamicArray item_containers_t; diff --git a/indra/newview/llrecentpeople.cpp b/indra/newview/llrecentpeople.cpp index 04abe36878..b491c7e109 100644 --- a/indra/newview/llrecentpeople.cpp +++ b/indra/newview/llrecentpeople.cpp @@ -40,27 +40,38 @@ using namespace LLOldEvents; bool LLRecentPeople::add(const LLUUID& id) { - if (contains(id) || id == gAgent.getID()) + if (id == gAgent.getID()) return false; LLDate date_added = LLDate::now(); - mList.insert(std::make_pair(id, date_added)); + + //[] instead of insert to replace existing id->date with new date value + mPeople[id] = date_added; mChangedSignal(); return true; } bool LLRecentPeople::contains(const LLUUID& id) const { - return mList.find(id) != mList.end(); + return mPeople.find(id) != mPeople.end(); } void LLRecentPeople::get(std::vector& result) const { result.clear(); - for (recent_people_t::const_iterator pos = mList.begin(); pos != mList.end(); ++pos) + for (recent_people_t::const_iterator pos = mPeople.begin(); pos != mPeople.end(); ++pos) result.push_back((*pos).first); } +const LLDate& LLRecentPeople::getDate(const LLUUID& id) const +{ + recent_people_t::const_iterator it = mPeople.find(id); + if (it!= mPeople.end()) return (*it).second; + + static LLDate no_date = LLDate(); + return no_date; +} + // virtual bool LLRecentPeople::handleEvent(LLPointer event, const LLSD& userdata) { diff --git a/indra/newview/llrecentpeople.h b/indra/newview/llrecentpeople.h index c18116b4e5..e0f2faaec5 100644 --- a/indra/newview/llrecentpeople.h +++ b/indra/newview/llrecentpeople.h @@ -81,6 +81,8 @@ public: */ void get(std::vector& result) const; + const LLDate& getDate(const LLUUID& id) const; + /** * Set callback to be called when the list changed. * @@ -97,7 +99,7 @@ public: private: typedef std::map recent_people_t; - recent_people_t mList; + recent_people_t mPeople; signal_t mChangedSignal; }; diff --git a/indra/newview/llremoteparcelrequest.cpp b/indra/newview/llremoteparcelrequest.cpp index 1ac0175b83..95e3dd6d78 100644 --- a/indra/newview/llremoteparcelrequest.cpp +++ b/indra/newview/llremoteparcelrequest.cpp @@ -84,7 +84,7 @@ void LLRemoteParcelRequestResponder::error(U32 status, const std::string& reason void LLRemoteParcelInfoProcessor::addObserver(const LLUUID& parcel_id, LLRemoteParcelInfoObserver* observer) { - // Check if the observer is alredy in observsrs list for this UUID + // Check if the observer is already in observers list for this UUID observer_multimap_t::iterator it; it = mObservers.find(parcel_id); @@ -155,7 +155,6 @@ void LLRemoteParcelInfoProcessor::processParcelInfoReply(LLMessageSystem* msg, v for (; oi != end; ++oi) { oi->second->processParcelInfo(parcel_data); - LLRemoteParcelInfoProcessor::getInstance()->removeObserver(parcel_data.parcel_id, oi->second); } } diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp index 2157f1af74..082bba027f 100644 --- a/indra/newview/llscreenchannel.cpp +++ b/indra/newview/llscreenchannel.cpp @@ -39,9 +39,15 @@ #include "lltoastpanel.h" #include "llviewercontrol.h" +#include "llviewerwindow.h" #include "llfloaterreg.h" #include "lltrans.h" +#include "lldockablefloater.h" +#include "llimpanel.h" +#include "llsyswellwindow.h" +#include "llimfloater.h" + #include using namespace LLNotificationsUI; @@ -53,37 +59,52 @@ LLScreenChannel::LLScreenChannel(LLUUID& id): mOverflowToastPanel(NULL), mStartU mToastAlignment(NA_BOTTOM), mCanStoreToasts(true), mHiddenToastsNum(0), mOverflowToastHidden(false), mIsHovering(false), mControlHovering(false), - mShowToasts(false) + mShowToasts(true) { mID = id; - - setFollows(FOLLOWS_RIGHT | FOLLOWS_BOTTOM | FOLLOWS_TOP); - mOverflowFormatString = LLTrans::getString("OverflowInfoChannelString"); - + mWorldViewRectConnection = gViewerWindow->setOnWorldViewRectUpdated(boost::bind(&LLScreenChannel::updatePositionAndSize, this, _1, _2)); setMouseOpaque( false ); + setVisible(FALSE); } //-------------------------------------------------------------------------- void LLScreenChannel::init(S32 channel_left, S32 channel_right) { - S32 channel_top = getRootView()->getRect().getHeight() - gSavedSettings.getS32("NavBarMargin"); - S32 channel_bottom = getRootView()->getRect().mBottom + gSavedSettings.getS32("ChannelBottomPanelMargin"); + S32 channel_top = gViewerWindow->getWorldViewRect().getHeight(); + S32 channel_bottom = gViewerWindow->getWorldViewRect().mBottom + gSavedSettings.getS32("ChannelBottomPanelMargin"); setRect(LLRect(channel_left, channel_top, channel_right, channel_bottom)); - + setVisible(TRUE); } //-------------------------------------------------------------------------- LLScreenChannel::~LLScreenChannel() { + mWorldViewRectConnection.disconnect(); } //-------------------------------------------------------------------------- -void LLScreenChannel::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLScreenChannel::updatePositionAndSize(LLRect old_world_rect, LLRect new_world_rect) { - LLUICtrl::reshape(width, height, called_from_parent); - if(mToastAlignment != NA_CENTRE) - showToasts(); + S32 top_delta = old_world_rect.mTop - new_world_rect.mTop; + S32 right_delta = old_world_rect.mRight - new_world_rect.mRight; + + LLRect this_rect = getRect(); + + this_rect.mTop -= top_delta; + switch(mChannelAlignment) + { + case CA_LEFT : + break; + case CA_CENTRE : + this_rect.setCenterAndSize(new_world_rect.getWidth() / 2, new_world_rect.getHeight() / 2, this_rect.getWidth(), this_rect.getHeight()); + break; + case CA_RIGHT : + this_rect.mLeft -= right_delta; + this_rect.mRight -= right_delta; + } + setRect(this_rect); + redrawToasts(); } //-------------------------------------------------------------------------- @@ -91,12 +112,12 @@ void LLScreenChannel::addToast(LLToast::Params p) { bool store_toast = false, show_toast = false; - show_toast = mShowToasts || p.force_show; + show_toast = mWasStartUpToastShown && (mShowToasts || p.force_show); store_toast = !show_toast && p.can_be_stored && mCanStoreToasts; if(!show_toast && !store_toast) { - mOnRejectToast(p); + mRejectToastSignal(p.notif_id); return; } @@ -104,7 +125,8 @@ void LLScreenChannel::addToast(LLToast::Params p) mOverflowToastHidden = false; - new_toast_elem.toast->setOnFadeCallback(boost::bind(&LLScreenChannel::onToastFade, this, new_toast_elem.toast)); + new_toast_elem.toast->setOnFadeCallback(boost::bind(&LLScreenChannel::onToastFade, this, _1)); + new_toast_elem.toast->setOnToastDestroyedCallback(boost::bind(&LLScreenChannel::onToastDestroyed, this, _1)); if(mControlHovering) { new_toast_elem.toast->setOnToastHoverCallback(boost::bind(&LLScreenChannel::onToastHover, this, _1, _2)); @@ -113,7 +135,7 @@ void LLScreenChannel::addToast(LLToast::Params p) if(show_toast) { mToastList.push_back(new_toast_elem); - showToasts(); + redrawToasts(); } else // store_toast { @@ -122,16 +144,28 @@ void LLScreenChannel::addToast(LLToast::Params p) } } +//-------------------------------------------------------------------------- +void LLScreenChannel::onToastDestroyed(LLToast* toast) +{ + std::vector::iterator it = find(mToastList.begin(), mToastList.end(), static_cast(toast)); + + if(it != mToastList.end()) + { + mToastList.erase(it); + } +} + + //-------------------------------------------------------------------------- void LLScreenChannel::onToastFade(LLToast* toast) { std::vector::iterator it = find(mToastList.begin(), mToastList.end(), static_cast(toast)); - bool destroy_toast = !mCanStoreToasts || !toast->getCanBeStored(); - if(destroy_toast) + bool delete_toast = !mCanStoreToasts || !toast->getCanBeStored(); + if(delete_toast) { mToastList.erase(it); - toast->mOnToastDestroy(toast); + deleteToast(toast); } else { @@ -139,7 +173,22 @@ void LLScreenChannel::onToastFade(LLToast* toast) mToastList.erase(it); } - showToasts(); + redrawToasts(); +} + +//-------------------------------------------------------------------------- +void LLScreenChannel::deleteToast(LLToast* toast) +{ + // send signal to observers about destroying of a toast + toast->mOnDeleteToastSignal(toast); + + // update channel's Hovering state + // turning hovering off mannualy because onMouseLeave won't happen if a toast was closed using a keyboard + if(toast->hasFocus()) + setHovering(false); + + // close the toast + toast->closeFloater(); } //-------------------------------------------------------------------------- @@ -173,11 +222,11 @@ void LLScreenChannel::loadStoredToastsToChannel() } mStoredToastList.clear(); - showToasts(); + redrawToasts(); } //-------------------------------------------------------------------------- -void LLScreenChannel::loadStoredToastByIDToChannel(LLUUID id) +void LLScreenChannel::loadStoredToastByNotificationIDToChannel(LLUUID id) { std::vector::iterator it = find(mStoredToastList.begin(), mStoredToastList.end(), id); @@ -191,11 +240,11 @@ void LLScreenChannel::loadStoredToastByIDToChannel(LLUUID id) mToastList.push_back((*it)); mStoredToastList.erase(it); - showToasts(); + redrawToasts(); } //-------------------------------------------------------------------------- -void LLScreenChannel::removeStoredToastByID(LLUUID id) +void LLScreenChannel::removeStoredToastByNotificationID(LLUUID id) { // *TODO: may be remove this function std::vector::iterator it = find(mStoredToastList.begin(), mStoredToastList.end(), id); @@ -205,7 +254,7 @@ void LLScreenChannel::removeStoredToastByID(LLUUID id) LLToast* toast = (*it).toast; mStoredToastList.erase(it); - toast->discardNotification(); + mRejectToastSignal(toast->getNotificationID()); } //-------------------------------------------------------------------------- @@ -223,15 +272,15 @@ void LLScreenChannel::killToastByNotificationID(LLUUID id) // NOTE: if a notification is unresponded this function will be called twice for the same toast. // At first, the notification will be discarded, at second (it will be caused by discarding), // the toast will be destroyed. - if(toast->getIsNotificationUnResponded()) + if(toast->isNotificationValid()) { - toast->discardNotification(); + mRejectToastSignal(toast->getNotificationID()); } else { mToastList.erase(it); - toast->mOnToastDestroy(toast); - showToasts(); + deleteToast(toast); + redrawToasts(); } return; } @@ -243,8 +292,9 @@ void LLScreenChannel::killToastByNotificationID(LLUUID id) { LLToast* toast = (*it).toast; mStoredToastList.erase(it); - toast->discardNotification(); - toast->mOnToastDestroy(toast); + // send signal to a listener to let him perform some action on toast rejecting + mRejectToastSignal(toast->getNotificationID()); + deleteToast(toast); } } @@ -261,12 +311,12 @@ void LLScreenChannel::modifyToastByNotificationID(LLUUID id, LLPanel* panel) delete old_panel; toast->insertPanel(panel); toast->resetTimer(); - showToasts(); + redrawToasts(); } } //-------------------------------------------------------------------------- -void LLScreenChannel::showToasts() +void LLScreenChannel::redrawToasts() { if(mToastList.size() == 0 || mIsHovering) return; @@ -487,31 +537,55 @@ void LLScreenChannel::removeToastsFromChannel() hideToastsFromScreen(); for(std::vector::iterator it = mToastList.begin(); it != mToastList.end(); it++) { - // *TODO: ivestigate mOnToastDestroy callback - change name or/and place - (*it).toast->mOnToastDestroy((*it).toast); + deleteToast((*it).toast); } mToastList.clear(); } //-------------------------------------------------------------------------- -void LLScreenChannel::removeAndStoreAllVisibleToasts() +void LLScreenChannel::removeAndStoreAllStorableToasts() { if(mToastList.size() == 0) return; hideToastsFromScreen(); - for(std::vector::iterator it = mToastList.begin(); it != mToastList.end(); it++) + for(std::vector::iterator it = mToastList.begin(); it != mToastList.end();) { if((*it).toast->getCanBeStored()) { mStoredToastList.push_back(*it); mOnStoreToast((*it).toast->getPanel(), (*it).id); (*it).toast->stopTimer(); + it = mToastList.erase(it); } - (*it).toast->setVisible(FALSE); + else + { + ++it; } + } + redrawToasts(); +} - mToastList.clear(); +//-------------------------------------------------------------------------- +void LLScreenChannel::removeToastsBySessionID(LLUUID id) +{ + if(mToastList.size() == 0) + return; + + hideToastsFromScreen(); + for(std::vector::iterator it = mToastList.begin(); it != mToastList.end();) + { + if((*it).toast->getSessionID() == id) + { + deleteToast((*it).toast); + it = mToastList.erase(it); + } + else + { + ++it; + } + } + redrawToasts(); } //-------------------------------------------------------------------------- @@ -546,11 +620,30 @@ void LLScreenChannel::onToastHover(LLToast* toast, bool mouse_enter) } if(!mIsHovering) - showToasts(); + redrawToasts(); } //-------------------------------------------------------------------------- +void LLScreenChannel::updateShowToastsState() +{ + LLFloater* floater = LLDockableFloater::getInstanceHandle().get(); + if(!floater) + { + setShowToasts(true); + return; + } + if(dynamic_cast(floater) || dynamic_cast(floater)) + { + setShowToasts(!(floater->getVisible() && floater->isDocked())); + if (!getShowToasts()) + { + removeAndStoreAllStorableToasts(); + } + + } +} +//-------------------------------------------------------------------------- diff --git a/indra/newview/llscreenchannel.h b/indra/newview/llscreenchannel.h index 746580b574..f1ef6bd64d 100644 --- a/indra/newview/llscreenchannel.h +++ b/indra/newview/llscreenchannel.h @@ -48,6 +48,12 @@ typedef enum e_notification_toast_alignment NA_BOTTOM, } EToastAlignment; +typedef enum e_channel_alignment +{ + CA_LEFT, + CA_CENTRE, + CA_RIGHT, +} EChannelAlignment; /** * Screen channel manages toasts visibility and positioning on the screen. @@ -60,12 +66,14 @@ public: virtual ~LLScreenChannel(); // Channel's outfit-functions - // classic reshape - void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + // update channel's size and position in the World View + void updatePositionAndSize(LLRect old_world_rect, LLRect new_world_rect); // initialization of channel's shape and position void init(S32 channel_left, S32 channel_right); // set allignment of toasts inside a channel - void setToastAlignment(e_notification_toast_alignment align) {mToastAlignment = align;} + void setToastAlignment(EToastAlignment align) {mToastAlignment = align;} + // set allignment of channel inside a world view + void setChannelAlignment(EChannelAlignment align) {mChannelAlignment = align;} // set a template for a string in the OverflowToast void setOverflowFormatString ( std::string str) { mOverflowFormatString = str; } @@ -80,15 +88,17 @@ public: // removes all toasts from a channel void removeToastsFromChannel(); // show all toasts in a channel - void showToasts(); + void redrawToasts(); // void loadStoredToastsToChannel(); - // finds a toast among stored by its ID and throws it on a screen to a channel - void loadStoredToastByIDToChannel(LLUUID id); - // removes a toast from stored finding it by its ID - void removeStoredToastByID(LLUUID id); - // remove all toasts from screen and store them - void removeAndStoreAllVisibleToasts(); + // finds a toast among stored by its Notification ID and throws it on a screen to a channel + void loadStoredToastByNotificationIDToChannel(LLUUID id); + // removes a toast from stored finding it by its Notification ID + void removeStoredToastByNotificationID(LLUUID id); + // removes from channel all toasts that belongs to the certain IM session + void removeToastsBySessionID(LLUUID id); + // remove all storable toasts from screen and store them + void removeAndStoreAllStorableToasts(); // close the Overflow Toast void closeOverflowToastPanel(); // close the StartUp Toast @@ -113,6 +123,8 @@ public: void setShowToasts(bool show) { mShowToasts = show; } // determine whether channel shows toasts or not bool getShowToasts() { return mShowToasts; } + // let a channel update its ShowToast flag + void updateShowToastsState(); // Channel's other interface functions functions // get number of hidden notifications from a channel @@ -124,17 +136,17 @@ public: // get ID of a channel LLUUID getChannelID() { return mID; } - // Channel's callbacks - // callback for storing of faded toasts + // Channel's signals + // signal on storing of faded toasts event typedef boost::function store_tost_callback_t; typedef boost::signals2::signal store_tost_signal_t; store_tost_signal_t mOnStoreToast; boost::signals2::connection setOnStoreToastCallback(store_tost_callback_t cb) { return mOnStoreToast.connect(cb); } - // callback for discarding of a rejected toast - typedef boost::function reject_tost_callback_t; - typedef boost::signals2::signal reject_tost_signal_t; - reject_tost_signal_t mOnRejectToast; - boost::signals2::connection setOnRejectToastCallback(reject_tost_callback_t cb) { return mOnRejectToast.connect(cb); } + // signal on rejecting of a toast event + typedef boost::function reject_tost_callback_t; + typedef boost::signals2::signal reject_tost_signal_t; + reject_tost_signal_t mRejectToastSignal; + boost::signals2::connection setOnRejectToastCallback(reject_tost_callback_t cb) { return mRejectToastSignal.connect(cb); } private: struct ToastElem @@ -142,7 +154,7 @@ private: LLUUID id; LLToast* toast; - ToastElem(LLToast::Params p) : id(p.id) + ToastElem(LLToast::Params p) : id(p.notif_id) { toast = new LLToast(p); } @@ -167,11 +179,14 @@ private: // Channel's handlers void onToastHover(LLToast* toast, bool mouse_enter); void onToastFade(LLToast* toast); + void onToastDestroyed(LLToast* toast); void onOverflowToastHide(); void onStartUpToastHide(); // void storeToast(ToastElem& toast_elem); + // send signal to observers about destroying of a toast, update channel's Hovering state, close the toast + void deleteToast(LLToast* toast); // show-functions depending on allignment of toasts void showToastsBottom(); @@ -194,7 +209,8 @@ private: // controls whether a channel shows toasts or not bool mShowToasts; // - e_notification_toast_alignment mToastAlignment; + EToastAlignment mToastAlignment; + EChannelAlignment mChannelAlignment; // attributes for the Overflow Toast S32 mHiddenToastsNum; @@ -207,6 +223,9 @@ private: // channel's ID LLUUID mID; + // store a connection to prevent futher crash that is caused by sending a signal to a destroyed channel + boost::signals2::connection mWorldViewRectConnection; + std::vector mToastList; std::vector mStoredToastList; std::map mToastEventStack; diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp index 40b8445dcf..d7df258750 100644 --- a/indra/newview/llstatusbar.cpp +++ b/indra/newview/llstatusbar.cpp @@ -237,9 +237,6 @@ BOOL LLStatusBar::handleRightMouseDown(S32 x, S32 y, MASK mask) BOOL LLStatusBar::postBuild() { - mCommitCallbackRegistrar.add("HideNavbarMenu.Action", boost::bind(&LLStatusBar::onHideNavbarContextMenuItemClicked, this, _2)); - mEnableCallbackRegistrar.add("HideNavbarMenu.EnableMenuItem", boost::bind(&LLStatusBar::onHideNavbarContextMenuItemEnabled, this, _2)); - mHideNavbarContextMenu = LLUICtrlFactory::getInstance()->createFromFile("menu_hide_navbar.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); gMenuHolder->addChild(mHideNavbarContextMenu); @@ -563,43 +560,6 @@ void LLStatusBar::setupDate() } } -bool LLStatusBar::onHideNavbarContextMenuItemEnabled(const LLSD& userdata) -{ - std::string item = userdata.asString(); - - if (item == "show_navbar_navigation_panel") - { - return gSavedSettings.getBOOL("ShowNavbarNavigationPanel"); - } - else if (item == "show_navbar_favorites_panel") - { - return gSavedSettings.getBOOL("ShowNavbarFavoritesPanel"); - } - - return FALSE; -} - -void LLStatusBar::onHideNavbarContextMenuItemClicked(const LLSD& userdata) -{ - std::string item = userdata.asString(); - - if (item == "show_navbar_navigation_panel") - { - BOOL state = !gSavedSettings.getBOOL("ShowNavbarNavigationPanel"); - - LLNavigationBar::getInstance()->showNavigationPanel(state); - gSavedSettings.setBOOL("ShowNavbarNavigationPanel", state); - } - else if (item == "show_navbar_favorites_panel") - { - BOOL state = !gSavedSettings.getBOOL("ShowNavbarFavoritesPanel"); - - LLNavigationBar::getInstance()->showFavoritesPanel(state); - gSavedSettings.setBOOL("ShowNavbarFavoritesPanel", state); - } -} - - void LLStatusBar::onMainMenuRightClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask) { handleRightMouseDown(x, y, mask); diff --git a/indra/newview/llstatusbar.h b/indra/newview/llstatusbar.h index b77db2c525..81a69e9590 100644 --- a/indra/newview/llstatusbar.h +++ b/indra/newview/llstatusbar.h @@ -91,9 +91,6 @@ private: // simple method to setup the part that holds the date void setupDate(); - bool onHideNavbarContextMenuItemEnabled(const LLSD& userdata); - void onHideNavbarContextMenuItemClicked(const LLSD& userdata); - void onMainMenuRightClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask); static void onCommitSearch(LLUICtrl*, void* data); static void onClickSearch(void* data); diff --git a/indra/newview/llsyswellitem.cpp b/indra/newview/llsyswellitem.cpp index 7d9ebc7208..eef8435006 100644 --- a/indra/newview/llsyswellitem.cpp +++ b/indra/newview/llsyswellitem.cpp @@ -40,7 +40,7 @@ #include "lluicolortable.h" //--------------------------------------------------------------------------------- -LLSysWellItem::LLSysWellItem(const Params& p) : LLScrollingPanel(p), +LLSysWellItem::LLSysWellItem(const Params& p) : LLPanel(p), mTitle(NULL), mCloseBtn(NULL), mIcon(NULL) @@ -74,15 +74,6 @@ void LLSysWellItem::onClickCloseBtn() mOnItemClose(this); } -//--------------------------------------------------------------------------------- -void LLSysWellItem::updatePanel(BOOL allow_modify) -{ - S32 parent_width = getParent()->getRect().getWidth(); - S32 panel_height = getRect().getHeight(); - - reshape(parent_width, panel_height, TRUE); -} - //--------------------------------------------------------------------------------- BOOL LLSysWellItem::handleMouseDown(S32 x, S32 y, MASK mask) { diff --git a/indra/newview/llsyswellitem.h b/indra/newview/llsyswellitem.h index b0761f2790..b9b00e972a 100644 --- a/indra/newview/llsyswellitem.h +++ b/indra/newview/llsyswellitem.h @@ -33,14 +33,14 @@ #ifndef LL_LLSYSWELLITEM_H #define LL_LLSYSWELLITEM_H -#include "llscrollingpanellist.h" +#include "llpanel.h" #include "lltextbox.h" #include "llbutton.h" #include "lliconctrl.h" #include -class LLSysWellItem : public LLScrollingPanel +class LLSysWellItem : public LLPanel { public: struct Params : public LLInitParam::Block @@ -54,8 +54,6 @@ public: LLSysWellItem(const Params& p); virtual ~LLSysWellItem(); - void updatePanel(BOOL allow_modify); - // title void setTitle( std::string title ); diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index 98428bf0f7..c1eecf4c12 100644 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -32,6 +32,8 @@ #include "llviewerprecompiledheaders.h" // must be first include +#include "llflatlistview.h" + #include "llsyswellwindow.h" #include "llbottomtray.h" @@ -39,29 +41,101 @@ #include "llviewerwindow.h" #include "llchiclet.h" +#include "lltoastpanel.h" +#include "llnotificationmanager.h" + + +// IM session ID can be the same as Avatar UUID. (See LLIMMgr::computeSessionID) +// Probably notification ID also can be the same as Avatar UUID. +// In case when session ID & notification ID are the same it will be impossible to add both +// appropriate Items into Flat List. +// Functions below are intended to wrap passed LLUUID into LLSD value with different "type". +// Use them anywhere you need to add, get, remove items via the list +inline +LLSD get_notification_value(const LLUUID& notification_id) +{ + return LLSD() + .insert("type", "notification") + .insert("uuid", notification_id); +} + +inline +LLSD get_session_value(const LLUUID& session_id) +{ + return LLSD() + .insert("type", "im_chiclet") + .insert("uuid", session_id); +} + + //--------------------------------------------------------------------------------- LLSysWellWindow::LLSysWellWindow(const LLSD& key) : LLDockableFloater(NULL, key), mChannel(NULL), - mScrollContainer(NULL), - mNotificationList(NULL) + mMessageList(NULL), + mSeparator(NULL) { LLIMMgr::getInstance()->addSessionObserver(this); LLIMChiclet::sFindChicletsSignal.connect(boost::bind(&LLSysWellWindow::findIMChiclet, this, _1)); + + mTypedItemsCount[IT_NOTIFICATION] = 0; + mTypedItemsCount[IT_INSTANT_MESSAGE] = 0; } //--------------------------------------------------------------------------------- BOOL LLSysWellWindow::postBuild() { - mScrollContainer = getChild("notification_list_container"); - mTwinListPanel = getChild("twin_list_panel"); - mNotificationList = getChild("notification_list"); - mIMRowList = getChild("im_row_panel_list"); + mMessageList = getChild("notification_list"); + + // init connections to the list's update events + connectListUpdaterToSignal("notify"); + connectListUpdaterToSignal("groupnotify"); + + // get a corresponding channel + initChannel(); - mScrollContainer->setBorderVisible(FALSE); + LLPanel::Params params; + mSeparator = LLUICtrlFactory::create(params); + LLUICtrlFactory::instance().buildPanel(mSeparator, "panel_separator.xml"); + + LLRect rc = mSeparator->getRect(); + rc.setOriginAndSize(0, 0, mMessageList->getItemsRect().getWidth(), rc.getHeight()); + mSeparator->setRect(rc); + mSeparator->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_TOP); + mSeparator->setVisible(FALSE); + + mMessageList->addItem(mSeparator); return LLDockableFloater::postBuild(); } +//--------------------------------------------------------------------------------- +void LLSysWellWindow::connectListUpdaterToSignal(std::string notification_type) +{ + LLNotificationsUI::LLNotificationManager* manager = LLNotificationsUI::LLNotificationManager::getInstance(); + LLNotificationsUI::LLEventHandler* n_handler = manager->getHandlerForNotification(notification_type); + if(n_handler) + { + n_handler->setNotificationIDCallback(boost::bind(&LLSysWellWindow::removeItemByID, this, _1)); + } + else + { + llwarns << "LLSysWellWindow::connectListUpdaterToSignal() - could not get a handler for '" << notification_type <<"' type of notifications" << llendl; + } +} + +//--------------------------------------------------------------------------------- +void LLSysWellWindow::onChicletClick() +{ + // 1 - remove StartUp toast and channel if present + if(!LLNotificationsUI::LLScreenChannel::getStartUpToastShown()) + { + LLNotificationsUI::LLChannelManager::getInstance()->onStartUpToastClose(); + } + + // 2 - toggle instance of SysWell's chiclet-window + toggleWindow(); +} + //--------------------------------------------------------------------------------- LLSysWellWindow::~LLSysWellWindow() { @@ -71,57 +145,56 @@ LLSysWellWindow::~LLSysWellWindow() //--------------------------------------------------------------------------------- void LLSysWellWindow::addItem(LLSysWellItem::Params p) { + LLSD value = get_notification_value(p.notification_id); // do not add clones - if( findItemByID(p.notification_id) >= 0 ) + if( mMessageList->getItemByValue(value)) return; LLSysWellItem* new_item = new LLSysWellItem(p); - mNotificationList->addPanel(dynamic_cast(new_item)); + if (mMessageList->addItem(new_item, value, ADD_TOP)) + { + handleItemAdded(IT_NOTIFICATION); + reshapeWindow(); new_item->setOnItemCloseCallback(boost::bind(&LLSysWellWindow::onItemClose, this, _1)); new_item->setOnItemClickCallback(boost::bind(&LLSysWellWindow::onItemClick, this, _1)); + } + else + { + llwarns << "Unable to add Notification into the list, notification ID: " << p.notification_id + << ", title: " << p.title + << llendl; + + new_item->die(); + } } //--------------------------------------------------------------------------------- void LLSysWellWindow::clear() { - // *TODO: fill later + mMessageList->clear(); } //--------------------------------------------------------------------------------- -S32 LLSysWellWindow::findItemByID(const LLUUID& id) +void LLSysWellWindow::removeItemByID(const LLUUID& id) { - const LLScrollingPanelList::panel_list_t list = mNotificationList->getPanelList(); - if(list.size() == 0) - return -1; - - LLScrollingPanelList::panel_list_t::const_iterator it; - S32 index = 0; - for(it = list.begin(); it != list.end(); ++it, ++index) + if(mMessageList->removeItemByValue(get_notification_value(id))) { - if( dynamic_cast(*it)->getID() == id ) - break; + handleItemRemoved(IT_NOTIFICATION); + reshapeWindow(); } - - if(it == list.end()) - return -1; else - return index; - -} - -//--------------------------------------------------------------------------------- -void LLSysWellWindow::removeItemByID(const LLUUID& id) -{ - S32 index = findItemByID(id); - - if(index >= 0) - mNotificationList->removePanel(index); - else - return; + { + llwarns << "Unable to remove notification from the list, ID: " << id + << llendl; + } - reshapeWindow(); + // hide chiclet window if there are no items left + if(isWindowEmpty()) + { + setVisible(FALSE); + } } //--------------------------------------------------------------------------------- @@ -129,7 +202,7 @@ void LLSysWellWindow::onItemClick(LLSysWellItem* item) { LLUUID id = item->getID(); if(mChannel) - mChannel->loadStoredToastByIDToChannel(id); + mChannel->loadStoredToastByNotificationIDToChannel(id); } //--------------------------------------------------------------------------------- @@ -139,9 +212,37 @@ void LLSysWellWindow::onItemClose(LLSysWellItem* item) removeItemByID(id); if(mChannel) mChannel->killToastByNotificationID(id); +} - // hide chiclet window if there are no items left - setVisible(!isWindowEmpty()); +//-------------------------------------------------------------------------- +void LLSysWellWindow::onStoreToast(LLPanel* info_panel, LLUUID id) +{ + LLSysWellItem::Params p; + p.notification_id = id; + p.title = static_cast(info_panel)->getTitle(); + addItem(p); +} + +//--------------------------------------------------------------------------------- +void LLSysWellWindow::initChannel() +{ + LLNotificationsUI::LLScreenChannel* channel = LLNotificationsUI::LLChannelManager::getInstance()->findChannelByID( + LLUUID(gSavedSettings.getString("NotificationChannelUUID"))); + if(channel) + { + mChannel = channel; + mChannel->setOnStoreToastCallback(boost::bind(&LLSysWellWindow::onStoreToast, this, _1, _2)); + } + else + { + llwarns << "LLSysWellWindow::initChannel() - could not get a requested screen channel" << llendl; + } +} + +//--------------------------------------------------------------------------------- +void LLSysWellWindow::getEnabledRect(LLRect& rect) +{ + rect = gViewerWindow->getWorldViewRect(); } //--------------------------------------------------------------------------------- @@ -151,9 +252,26 @@ void LLSysWellWindow::toggleWindow() { setDockControl(new LLDockControl( LLBottomTray::getInstance()->getSysWell(), this, - getDockTongue(), LLDockControl::TOP, isDocked())); + getDockTongue(), LLDockControl::TOP, boost::bind(&LLSysWellWindow::getEnabledRect, this, _1))); + } + + if(!getVisible()) + { + if(mChannel) + { + mChannel->removeAndStoreAllStorableToasts(); + } + if(isWindowEmpty()) + { + return; + } + + setVisible(TRUE); + } + else + { + setVisible(FALSE); } - setVisible(!getVisible()); //set window in foreground setFocus(getVisible()); } @@ -161,15 +279,12 @@ void LLSysWellWindow::toggleWindow() //--------------------------------------------------------------------------------- void LLSysWellWindow::setVisible(BOOL visible) { - // on Show adjust position of SysWell chiclet's window if(visible) { if (LLBottomTray::instanceExists()) { LLBottomTray::getInstance()->getSysWell()->setToggleState(TRUE); } - if(mChannel) - mChannel->removeAndStoreAllVisibleToasts(); } else { @@ -178,86 +293,56 @@ void LLSysWellWindow::setVisible(BOOL visible) LLBottomTray::getInstance()->getSysWell()->setToggleState(FALSE); } } - if(mChannel) - mChannel->setShowToasts(!visible); LLDockableFloater::setVisible(visible); + + // update notification channel state + if(mChannel) + { + mChannel->updateShowToastsState(); + } } //--------------------------------------------------------------------------------- -void LLSysWellWindow::reshapeWindow() +void LLSysWellWindow::setDocked(bool docked, bool pop_on_undock) { - // Get size for scrollbar and floater's header - const LLUICachedControl SCROLLBAR_SIZE("UIScrollbarSize", 0); - const LLUICachedControl HEADER_SIZE("UIFloaterHeaderSize", 0); - - LLRect notif_list_rect = mNotificationList->getRect(); - LLRect im_list_rect = mIMRowList->getRect(); - LLRect panel_rect = mTwinListPanel->getRect(); - - S32 notif_list_height = notif_list_rect.getHeight(); - S32 im_list_height = im_list_rect.getHeight(); + LLDockableFloater::setDocked(docked, pop_on_undock); - S32 new_panel_height = notif_list_height + LLScrollingPanelList::GAP_BETWEEN_PANELS + im_list_height; - S32 new_window_height = new_panel_height + LLScrollingPanelList::GAP_BETWEEN_PANELS + HEADER_SIZE; - - U32 twinListWidth = 0; - - if (new_window_height > MAX_WINDOW_HEIGHT) - { - twinListWidth = MIN_PANELLIST_WIDTH - SCROLLBAR_SIZE; - new_window_height = MAX_WINDOW_HEIGHT; - } - else + // update notification channel state + if(mChannel) { - twinListWidth = MIN_PANELLIST_WIDTH; + mChannel->updateShowToastsState(); } - - reshape(MIN_WINDOW_WIDTH, new_window_height, FALSE); - mTwinListPanel->reshape(twinListWidth, new_panel_height, TRUE); - mNotificationList->reshape(twinListWidth, notif_list_height, TRUE); - mIMRowList->reshape(twinListWidth, im_list_height, TRUE); - - // arrange panel and lists - // move panel - panel_rect.setLeftTopAndSize(1, new_panel_height, twinListWidth, new_panel_height); - mTwinListPanel->setRect(panel_rect); - // move notif list panel - notif_list_rect.setLeftTopAndSize(notif_list_rect.mLeft, new_panel_height, twinListWidth, notif_list_height); - mNotificationList->setRect(notif_list_rect); - // move IM list panel - im_list_rect.setLeftTopAndSize(im_list_rect.mLeft, notif_list_rect.mBottom - LLScrollingPanelList::GAP_BETWEEN_PANELS, twinListWidth, im_list_height); - mIMRowList->setRect(im_list_rect); - - mNotificationList->updatePanels(TRUE); - mIMRowList->updatePanels(TRUE); } //--------------------------------------------------------------------------------- -LLSysWellWindow::RowPanel * LLSysWellWindow::findIMRow(const LLUUID& sessionId) +void LLSysWellWindow::reshapeWindow() { - RowPanel * res = NULL; - const LLScrollingPanelList::panel_list_t &list = mIMRowList->getPanelList(); - if (!list.empty()) + // save difference between floater height and the list height to take it into account while calculating new window height + // it includes height from floater top to list top and from floater bottom and list bottom + static S32 parent_list_delta_height = getRect().getHeight() - mMessageList->getRect().getHeight(); + + S32 notif_list_height = mMessageList->getItemsRect().getHeight() + 2 * mMessageList->getBorderWidth(); + + LLRect curRect = getRect(); + + S32 new_window_height = notif_list_height + parent_list_delta_height; + + if (new_window_height > MAX_WINDOW_HEIGHT) { - for (LLScrollingPanelList::panel_list_t::const_iterator iter = list.begin(); iter != list.end(); ++iter) - { - RowPanel *panel = static_cast (*iter); - if (panel->mChiclet->getSessionId() == sessionId) - { - res = panel; - break; - } - } + new_window_height = MAX_WINDOW_HEIGHT; } - return res; + S32 newY = curRect.mTop + new_window_height - curRect.getHeight(); + curRect.setLeftTopAndSize(curRect.mLeft, newY, MIN_WINDOW_WIDTH, new_window_height); + reshape(curRect.getWidth(), curRect.getHeight(), TRUE); + setRect(curRect); } //--------------------------------------------------------------------------------- LLChiclet* LLSysWellWindow::findIMChiclet(const LLUUID& sessionId) { LLChiclet* res = NULL; - RowPanel* panel = findIMRow(sessionId); + RowPanel* panel = mMessageList->getTypedItemByValue(get_session_value(sessionId)); if (panel != NULL) { res = panel->mChiclet; @@ -270,34 +355,51 @@ LLChiclet* LLSysWellWindow::findIMChiclet(const LLUUID& sessionId) void LLSysWellWindow::addIMRow(const LLUUID& sessionId, S32 chicletCounter, const std::string& name, const LLUUID& otherParticipantId) { + RowPanel* item = new RowPanel(this, sessionId, chicletCounter, name, otherParticipantId); + if (mMessageList->insertItemAfter(mSeparator, item, get_session_value(sessionId))) + { + handleItemAdded(IT_INSTANT_MESSAGE); + } + else + { + llwarns << "Unable to add IM Row into the list, sessionID: " << sessionId + << ", name: " << name + << ", other participant ID: " << otherParticipantId + << llendl; - mIMRowList->addPanel(new RowPanel(this, sessionId, chicletCounter, name, otherParticipantId)); + item->die(); + } } //--------------------------------------------------------------------------------- void LLSysWellWindow::delIMRow(const LLUUID& sessionId) { - RowPanel *panel = findIMRow(sessionId); - if (panel != NULL) + if (mMessageList->removeItemByValue(get_session_value(sessionId))) + { + handleItemRemoved(IT_INSTANT_MESSAGE); + } + else { - mIMRowList->removePanel(panel); + llwarns << "Unable to remove IM Row from the list, sessionID: " << sessionId + << llendl; } + // remove all toasts that belong to this session from a screen + if(mChannel) + mChannel->removeToastsBySessionID(sessionId); + // hide chiclet window if there are no items left - setVisible(!isWindowEmpty()); + if(isWindowEmpty()) + { + setVisible(FALSE); + } } //--------------------------------------------------------------------------------- bool LLSysWellWindow::isWindowEmpty() { - if(mIMRowList->getPanelList().size() == 0 && LLBottomTray::getInstance()->getSysWell()->getCounter() == 0) - { - return true; - } - else - { - return false; - } + // keep in mind, mSeparator is always in the list + return mMessageList->size() == 1; } //--------------------------------------------------------------------------------- @@ -305,7 +407,7 @@ bool LLSysWellWindow::isWindowEmpty() void LLSysWellWindow::sessionAdded(const LLUUID& sessionId, const std::string& name, const LLUUID& otherParticipantId) { - if (findIMRow(sessionId) == NULL) + if (mMessageList->getItemByValue(get_session_value(sessionId)) == NULL) { S32 chicletCounter = 0; LLIMModel::LLIMSession* session = get_if_there(LLIMModel::sSessionsMap, @@ -328,10 +430,57 @@ void LLSysWellWindow::sessionRemoved(const LLUUID& sessionId) LLBottomTray::getInstance()->getSysWell()->updateUreadIMNotifications(); } +void LLSysWellWindow::handleItemAdded(EItemType added_item_type) +{ + bool should_be_shown = ++mTypedItemsCount[added_item_type] == 1 && anotherTypeExists(added_item_type); + + if (should_be_shown && !mSeparator->getVisible()) + { + mSeparator->setVisible(TRUE); + + // refresh list to recalculate mSeparator position + mMessageList->reshape(mMessageList->getRect().getWidth(), mMessageList->getRect().getHeight()); + } +} + +void LLSysWellWindow::handleItemRemoved(EItemType removed_item_type) +{ + bool should_be_hidden = --mTypedItemsCount[removed_item_type] == 0; + + if (should_be_hidden && mSeparator->getVisible()) + { + mSeparator->setVisible(FALSE); + + // refresh list to recalculate mSeparator position + mMessageList->reshape(mMessageList->getRect().getWidth(), mMessageList->getRect().getHeight()); + } +} + +bool LLSysWellWindow::anotherTypeExists(EItemType item_type) +{ + bool exists = false; + switch(item_type) + { + case IT_INSTANT_MESSAGE: + if (mTypedItemsCount[IT_NOTIFICATION] > 0) + { + exists = true; + } + break; + case IT_NOTIFICATION: + if (mTypedItemsCount[IT_INSTANT_MESSAGE] > 0) + { + exists = true; + } + break; + } + return exists; +} + //--------------------------------------------------------------------------------- LLSysWellWindow::RowPanel::RowPanel(const LLSysWellWindow* parent, const LLUUID& sessionId, S32 chicletCounter, const std::string& name, const LLUUID& otherParticipantId) : - LLScrollingPanel(LLPanel::Params()), mChiclet(NULL), mParent(parent) + LLPanel(LLPanel::Params()), mChiclet(NULL), mParent(parent) { LLUICtrlFactory::getInstance()->buildPanel(this, "panel_activeim_row.xml", NULL); @@ -373,8 +522,8 @@ LLSysWellWindow::RowPanel::~RowPanel() //--------------------------------------------------------------------------------- void LLSysWellWindow::RowPanel::onClose() { - mParent->mIMRowList->removePanel(this); gIMMgr->removeSession(mChiclet->getSessionId()); + // This row panel will be removed from the list in LLSysWellWindow::sessionRemoved(). } //--------------------------------------------------------------------------------- @@ -400,13 +549,4 @@ BOOL LLSysWellWindow::RowPanel::handleMouseDown(S32 x, S32 y, MASK mask) return LLPanel::handleMouseDown(x, y, mask); } -//--------------------------------------------------------------------------------- -void LLSysWellWindow::RowPanel::updatePanel(BOOL allow_modify) -{ - S32 parent_width = getParent()->getRect().getWidth(); - S32 panel_height = getRect().getHeight(); - - reshape(parent_width, panel_height, TRUE); -} - -//--------------------------------------------------------------------------------- +// EOF diff --git a/indra/newview/llsyswellwindow.h b/indra/newview/llsyswellwindow.h index d76147b489..37a2690a82 100644 --- a/indra/newview/llsyswellwindow.h +++ b/indra/newview/llsyswellwindow.h @@ -44,7 +44,7 @@ #include "boost/shared_ptr.hpp" - +class LLFlatListView; class LLSysWellWindow : public LLDockableFloater, LLIMSessionObserver { @@ -54,36 +54,52 @@ public: BOOL postBuild(); // other interface functions + // check is window empty bool isWindowEmpty(); - // change attributes - void setChannel(LLNotificationsUI::LLScreenChannel* channel) {mChannel = channel;} - // Operating with items void addItem(LLSysWellItem::Params p); void clear( void ); void removeItemByID(const LLUUID& id); - S32 findItemByID(const LLUUID& id); // Operating with outfit virtual void setVisible(BOOL visible); void adjustWindowPosition(); void toggleWindow(); - /*virtua*/BOOL canClose() { return FALSE; } + /*virtual*/ BOOL canClose() { return FALSE; } + /*virtual*/ void setDocked(bool docked, bool pop_on_undock = true); // Handlers void onItemClick(LLSysWellItem* item); void onItemClose(LLSysWellItem* item); + void onStoreToast(LLPanel* info_panel, LLUUID id); + void onChicletClick(); // size constants for the window and for its elements static const S32 MAX_WINDOW_HEIGHT = 200; - static const S32 MIN_WINDOW_WIDTH = 320; - static const S32 MIN_PANELLIST_WIDTH = 318; + static const S32 MIN_WINDOW_WIDTH = 318; private: + + typedef enum{ + IT_NOTIFICATION, + IT_INSTANT_MESSAGE + }EItemType; + + // gets a rect valid for SysWellWindow's position on a screen (EXT-1111) + void getEnabledRect(LLRect& rect); + // connect counter and list updaters to the corresponding signals + void connectListUpdaterToSignal(std::string notification_type); + // init Window's channel + void initChannel(); + void handleItemAdded(EItemType added_item_type); + void handleItemRemoved(EItemType removed_item_type); + bool anotherTypeExists(EItemType item_type) ; + + + class RowPanel; void reshapeWindow(); - RowPanel * findIMRow(const LLUUID& sessionId); LLChiclet * findIMChiclet(const LLUUID& sessionId); void addIMRow(const LLUUID& sessionId, S32 chicletCounter, const std::string& name, const LLUUID& otherParticipantId); void delIMRow(const LLUUID& sessionId); @@ -93,23 +109,28 @@ private: // pointer to a corresponding channel's instance LLNotificationsUI::LLScreenChannel* mChannel; - LLPanel* mTwinListPanel; - LLScrollContainer* mScrollContainer; - LLScrollingPanelList* mIMRowList; - LLScrollingPanelList* mNotificationList; + LLFlatListView* mMessageList; + + /** + * Special panel which is used as separator of Notifications & IM Rows. + * It is always presents in the list and shown when it is necessary. + * It should be taken into account when reshaping and checking list size + */ + LLPanel* mSeparator; + + typedef std::map typed_items_count_t; + typed_items_count_t mTypedItemsCount; private: /** * Scrolling row panel. */ - class RowPanel: public LLScrollingPanel + class RowPanel: public LLPanel { public: RowPanel(const LLSysWellWindow* parent, const LLUUID& sessionId, S32 chicletCounter, const std::string& name, const LLUUID& otherParticipantId); virtual ~RowPanel(); - /*virtual*/ - void updatePanel(BOOL allow_modify); void onMouseEnter(S32 x, S32 y, MASK mask); void onMouseLeave(S32 x, S32 y, MASK mask); BOOL handleMouseDown(S32 x, S32 y, MASK mask); diff --git a/indra/newview/lltoast.cpp b/indra/newview/lltoast.cpp index fb7574d68b..97a15759bf 100644 --- a/indra/newview/lltoast.cpp +++ b/indra/newview/lltoast.cpp @@ -44,7 +44,8 @@ using namespace LLNotificationsUI; LLToast::LLToast(LLToast::Params p) : LLFloater(LLSD()), mPanel(p.panel), mTimerValue(p.timer_period), - mID(p.id), + mNotificationID(p.notif_id), + mSessionID(p.session_id), mCanFade(p.can_fade), mCanBeStored(p.can_be_stored), mHideBtnEnabled(p.enable_hide_btn), @@ -73,12 +74,12 @@ LLToast::LLToast(LLToast::Params p) : LLFloater(LLSD()), setFocus(TRUE); } - - if(!p.on_toast_destroy.empty()) - mOnToastDestroy.connect(p.on_toast_destroy); + // init callbacks if present + if(!p.on_delete_toast.empty()) + mOnDeleteToastSignal.connect(p.on_delete_toast); if(!p.on_mouse_enter.empty()) - mOnMousEnter.connect(p.on_mouse_enter); + mOnMouseEnterSignal.connect(p.on_mouse_enter); } //-------------------------------------------------------------------------- @@ -102,6 +103,7 @@ void LLToast::setHideButtonEnabled(bool enabled) //-------------------------------------------------------------------------- LLToast::~LLToast() { + mOnToastDestroyedSignal(this); if(mIsModal) { gFocusMgr.unlockFocus(); @@ -142,7 +144,7 @@ void LLToast::hide() { setVisible(FALSE); mTimer.stop(); - mOnFade(this); + mOnFadeSignal(this); } //-------------------------------------------------------------------------- @@ -160,7 +162,7 @@ void LLToast::tick() { setVisible(FALSE); mTimer.stop(); - mOnFade(this); + mOnFadeSignal(this); } } @@ -223,7 +225,7 @@ void LLToast::setVisible(BOOL show) //-------------------------------------------------------------------------- void LLToast::onMouseEnter(S32 x, S32 y, MASK mask) { - mOnToastHover(this, MOUSE_ENTER); + mOnToastHoverSignal(this, MOUSE_ENTER); setBackgroundOpaque(TRUE); if(mCanFade) @@ -234,13 +236,13 @@ void LLToast::onMouseEnter(S32 x, S32 y, MASK mask) sendChildToFront(mHideBtn); if(mHideBtn && mHideBtn->getEnabled()) mHideBtn->setVisible(TRUE); - mOnMousEnter(this); + mOnMouseEnterSignal(this); } //-------------------------------------------------------------------------- void LLToast::onMouseLeave(S32 x, S32 y, MASK mask) { - mOnToastHover(this, MOUSE_LEAVE); + mOnToastHoverSignal(this, MOUSE_LEAVE); if(mCanFade) { @@ -270,21 +272,11 @@ BOOL LLToast::handleMouseDown(S32 x, S32 y, MASK mask) } //-------------------------------------------------------------------------- -void LLToast::discardNotification() -{ - if(mNotification) - { - mNotification->setIgnored(TRUE); - mNotification->respond(mNotification->getResponseTemplate()); - } -} - -//-------------------------------------------------------------------------- -bool LLToast::getIsNotificationUnResponded() +bool LLToast::isNotificationValid() { if(mNotification) { - return !mNotification->isRespondedTo(); + return !mNotification->isCancelled(); } return false; } diff --git a/indra/newview/lltoast.h b/indra/newview/lltoast.h index 05e63a60c5..9248747c43 100644 --- a/indra/newview/lltoast.h +++ b/indra/newview/lltoast.h @@ -57,13 +57,14 @@ public: typedef boost::function toast_callback_t; typedef boost::signals2::signal toast_signal_t; - struct Params : public LLInitParam::Block + struct Params { LLPanel* panel; - LLUUID id; //notification or message ID + LLUUID notif_id; //notification ID + LLUUID session_id; //im session ID LLNotificationPtr notification; F32 timer_period; - toast_callback_t on_toast_destroy; + toast_callback_t on_delete_toast; toast_callback_t on_mouse_enter; bool can_fade; bool can_be_stored; @@ -100,8 +101,6 @@ public: void insertPanel(LLPanel* panel); // get toast's panel LLPanel* getPanel() { return mPanel; } - // discard notification - void discardNotification(); // enable/disable Toast's Hide button void setHideButtonEnabled(bool enabled); // initialize and start Toast's timer @@ -120,8 +119,12 @@ public: // get/set Toast's flags or states - // get information whether the notification corresponding to the toast is responded or not - bool getIsNotificationUnResponded(); + // get information whether the notification corresponding to the toast is valid or not + bool isNotificationValid(); + // get toast's Notification ID + const LLUUID getNotificationID() { return mNotificationID;} + // get toast's Session ID + const LLUUID getSessionID() { return mSessionID;} // void setCanFade(bool can_fade); // @@ -132,18 +135,18 @@ public: void setModal(bool modal); - // Registers callbacks for events - toast_signal_t mOnFade; - toast_signal_t mOnMousEnter; - toast_signal_t mOnToastDestroy; - boost::signals2::connection setOnFadeCallback(toast_callback_t cb) { return mOnFade.connect(cb); } - boost::signals2::connection setOnMouseEnterCallback(toast_callback_t cb) { return mOnMousEnter.connect(cb); } - boost::signals2::connection setOnToastDestroyCallback(toast_callback_t cb) { return mOnToastDestroy.connect(cb); } + // Registers signals/callbacks for events + toast_signal_t mOnFadeSignal; + toast_signal_t mOnMouseEnterSignal; + toast_signal_t mOnDeleteToastSignal; + toast_signal_t mOnToastDestroyedSignal; + boost::signals2::connection setOnFadeCallback(toast_callback_t cb) { return mOnFadeSignal.connect(cb); } + boost::signals2::connection setOnToastDestroyedCallback(toast_callback_t cb) { return mOnToastDestroyedSignal.connect(cb); } typedef boost::function toast_hover_check_callback_t; typedef boost::signals2::signal toast_hover_check_signal_t; - toast_hover_check_signal_t mOnToastHover; - boost::signals2::connection setOnToastHoverCallback(toast_hover_check_callback_t cb) { return mOnToastHover.connect(cb); } + toast_hover_check_signal_t mOnToastHoverSignal; + boost::signals2::connection setOnToastHoverCallback(toast_hover_check_callback_t cb) { return mOnToastHoverSignal.connect(cb); } private: @@ -153,7 +156,8 @@ private: // on timer finished function void tick(); - LLUUID mID; + LLUUID mNotificationID; + LLUUID mSessionID; LLNotificationPtr mNotification; LLTimer mTimer; diff --git a/indra/newview/lltoastimpanel.cpp b/indra/newview/lltoastimpanel.cpp index d401943020..fe1492d937 100644 --- a/indra/newview/lltoastimpanel.cpp +++ b/indra/newview/lltoastimpanel.cpp @@ -44,14 +44,14 @@ LLToastIMPanel::LLToastIMPanel(LLToastIMPanel::Params &p) : LLToastPanel(p.notif { LLUICtrlFactory::getInstance()->buildPanel(this, "panel_instant_message.xml"); - mAvatar = getChild("avatar"); + LLIconCtrl* sys_msg_icon = getChild("sys_msg_icon"); + mAvatar = getChild("avatar_icon"); mUserName = getChild("user_name"); mTime = getChild("time_box"); mMessage = getChild("message"); mReplyBtn = getChild("reply"); mMessage->setValue(p.message); - mAvatar->setValue(p.avatar_id); mUserName->setValue(p.from); mTime->setValue(p.time); mSessionID = p.session_id; @@ -60,6 +60,9 @@ LLToastIMPanel::LLToastIMPanel(LLToastIMPanel::Params &p) : LLToastPanel(p.notif // if message comes from the system - there shouldn't be a reply btn if(p.from == "Second Life") { + mAvatar->setVisible(FALSE); + sys_msg_icon->setVisible(TRUE); + mReplyBtn->setVisible(FALSE); S32 btn_height = mReplyBtn->getRect().getHeight(); LLRect msg_rect = mMessage->getRect(); @@ -68,6 +71,10 @@ LLToastIMPanel::LLToastIMPanel(LLToastIMPanel::Params &p) : LLToastPanel(p.notif } else { + mAvatar->setVisible(TRUE); + sys_msg_icon->setVisible(FALSE); + + mAvatar->setValue(p.avatar_id); mReplyBtn->setClickedCallback(boost::bind(&LLToastIMPanel::onClickReplyBtn, this)); } @@ -88,9 +95,7 @@ LLToastIMPanel::~LLToastIMPanel() //-------------------------------------------------------------------------- void LLToastIMPanel::onClickReplyBtn() { - LLSD response = mNotification->getResponseTemplate(); - response["respondbutton"] = true; - mNotification->respond(response); + mNotification->respond(mNotification->getResponseTemplate()); } //-------------------------------------------------------------------------- diff --git a/indra/newview/lltoastimpanel.h b/indra/newview/lltoastimpanel.h index b51ce23364..af21b07a3d 100644 --- a/indra/newview/lltoastimpanel.h +++ b/indra/newview/lltoastimpanel.h @@ -43,7 +43,7 @@ class LLToastIMPanel: public LLToastPanel { public: - struct Params : public LLInitParam::Block + struct Params { LLNotificationPtr notification; LLUUID avatar_id; diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp index 844c54da6a..9761a45d83 100644 --- a/indra/newview/lltoastnotifypanel.cpp +++ b/indra/newview/lltoastnotifypanel.cpp @@ -171,6 +171,7 @@ LLToastNotifyPanel::LLToastNotifyPanel(LLNotificationPtr& notification) : LLToas params.name("box"); params.rect(LLRect(x, y, getRect().getWidth()-2, mIsTip ? BOTTOM : BTN_TOP+16)); params.max_text_length(MAX_LENGTH); + params.read_only(true); params.default_text(mMessage); params.font(sFont); params.embedded_items(false); diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index 08040cfaa5..c3064ffa43 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -54,6 +54,7 @@ #include "llmutelist.h" #include "llnotify.h" #include "llpreviewnotecard.h" +#include "llrecentpeople.h" #include "llselectmgr.h" #include "lltoolmgr.h" #include "lltooltip.h" @@ -646,6 +647,7 @@ void LLToolDragAndDrop::beginMultiDrag( void LLToolDragAndDrop::endDrag() { + mEndDragSignal(); LLSelectMgr::getInstance()->unhighlightAll(); setMouseCapture(FALSE); } diff --git a/indra/newview/lltooldraganddrop.h b/indra/newview/lltooldraganddrop.h index c9fef26b58..acf01869e7 100644 --- a/indra/newview/lltooldraganddrop.h +++ b/indra/newview/lltooldraganddrop.h @@ -51,6 +51,8 @@ class LLPickInfo; class LLToolDragAndDrop : public LLTool, public LLSingleton { public: + typedef boost::signals2::signal enddrag_signal_t; + LLToolDragAndDrop(); // overridden from LLTool @@ -87,6 +89,8 @@ public: const LLUUID& getObjectID() const { return mObjectID; } EAcceptance getLastAccept() { return mLastAccept; } + boost::signals2::connection setEndDragCallback( const enddrag_signal_t::slot_type& cb ) { return mEndDragSignal.connect(cb); } + protected: enum EDropTarget { @@ -131,6 +135,8 @@ protected: S32 mCurItemIndex; std::string mToolTipMsg; + enddrag_signal_t mEndDragSignal; + // array of pointers to functions that implement the logic to // dragging and dropping into the simulator. static dragOrDrop3dImpl sDragAndDrop3d[DAD_COUNT][DT_COUNT]; diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index b888560fc7..24f4745c18 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -72,6 +72,8 @@ #include "llvosurfacepatch.h" #include "llvowlsky.h" #include "llrender.h" +#include "llbottomtray.h" +#include "llnavigationbar.h" #ifdef TOGGLE_HACKED_GODLIKE_VIEWER BOOL gHackGodmode = FALSE; @@ -483,6 +485,36 @@ bool toggle_agent_pause(const LLSD& newvalue) return true; } +bool toggle_show_gesture_button(const LLSD& newvalue) +{ + LLBottomTray::getInstance()->showGestureButton(newvalue.asBoolean()); + return true; +} + +bool toggle_show_move_button(const LLSD& newvalue) +{ + LLBottomTray::getInstance()->showMoveButton(newvalue.asBoolean()); + return true; +} + +bool toggle_show_camera_button(const LLSD& newvalue) +{ + LLBottomTray::getInstance()->showCameraButton(newvalue.asBoolean()); + return true; +} + +bool toggle_show_navigation_panel(const LLSD& newvalue) +{ + LLNavigationBar::getInstance()->showNavigationPanel(newvalue.asBoolean()); + return true; +} + +bool toggle_show_favorites_panel(const LLSD& newvalue) +{ + LLNavigationBar::getInstance()->showFavoritesPanel(newvalue.asBoolean()); + return true; +} + //////////////////////////////////////////////////////////////////////////// void settings_setup_listeners() @@ -619,6 +651,11 @@ void settings_setup_listeners() gSavedSettings.getControl("QAMode")->getSignal()->connect(boost::bind(&show_debug_menus)); gSavedSettings.getControl("UseDebugMenus")->getSignal()->connect(boost::bind(&show_debug_menus)); gSavedSettings.getControl("AgentPause")->getSignal()->connect(boost::bind(&toggle_agent_pause, _2)); + gSavedSettings.getControl("ShowGestureButton")->getSignal()->connect(boost::bind(&toggle_show_gesture_button, _2)); + gSavedSettings.getControl("ShowMoveButton")->getSignal()->connect(boost::bind(&toggle_show_move_button, _2)); + gSavedSettings.getControl("ShowCameraButton")->getSignal()->connect(boost::bind(&toggle_show_camera_button, _2)); + gSavedSettings.getControl("ShowNavbarNavigationPanel")->getSignal()->connect(boost::bind(&toggle_show_navigation_panel, _2)); + gSavedSettings.getControl("ShowNavbarFavoritesPanel")->getSignal()->connect(boost::bind(&toggle_show_favorites_panel, _2)); } #if TEST_CACHED_CONTROL diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 441d0ebeaa..51d699c0f7 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -69,6 +69,7 @@ #include "llfloatermediabrowser.h" #include "llfloaterhud.h" #include "llfloaterimagepreview.h" +#include "llimfloater.h" #include "llimpanel.h" #include "llfloaterinspect.h" #include "llfloaterinventory.h" @@ -143,6 +144,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("bumps", "floater_bumps.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("camera", "floater_camera.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("camera_presets", "floater_camera_presets.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("chat", "floater_chat_history.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("nearby_chat", "floater_nearby_chat.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("communicate", "floater_chatterbox.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 9d278e6a6c..a0bd5f301b 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -3830,6 +3830,7 @@ void handle_reset_view() } else { + gAgent.switchCameraPreset(CAMERA_PRESET_REAR_VIEW); reset_view_final( TRUE ); LLFloaterCamera::resetCameraMode(); } diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index e37bad6b0e..390e1fe032 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -98,6 +98,7 @@ #include "llnotifications.h" #include "llnotify.h" #include "llpanelgrouplandmoney.h" +#include "llrecentpeople.h" #include "llselectmgr.h" #include "llsidetray.h" #include "llstartup.h" diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 5fd5397970..d23f10f880 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1559,9 +1559,19 @@ void LLViewerWindow::initWorldUI() navbar->showFavoritesPanel(FALSE); } - if (!gSavedSettings.getBOOL("ShowCameraAndMoveControls")) + if (!gSavedSettings.getBOOL("ShowCameraButton")) { - LLBottomTray::getInstance()->showCameraAndMoveControls(FALSE); + LLBottomTray::getInstance()->showCameraButton(FALSE); + } + + if (!gSavedSettings.getBOOL("ShowMoveButton")) + { + LLBottomTray::getInstance()->showMoveButton(FALSE); + } + + if (!gSavedSettings.getBOOL("ShowGestureButton")) + { + LLBottomTray::getInstance()->showGestureButton(FALSE); } getRootView()->addChild(gStatusBar); @@ -1577,13 +1587,6 @@ void LLViewerWindow::initWorldUI() // menu holder appears on top to get first pass at all mouse events getRootView()->sendChildToFront(gMenuHolder); - //Channel Manager - LLNotificationsUI::LLChannelManager* channel_manager = LLNotificationsUI::LLChannelManager::getInstance(); - getRootView()->addChild(channel_manager); - //Notification Manager - LLNotificationsUI::LLNotificationManager* notify_manager = LLNotificationsUI::LLNotificationManager::getInstance(); - getRootView()->addChild(notify_manager); - if ( gHUDView == NULL ) { LLRect hud_rect = full_window; @@ -2859,6 +2862,9 @@ void LLViewerWindow::updateWorldViewRect(bool use_full_window) if (mWorldViewRect != new_world_rect) { + // sending a signal with a new WorldView rect + mOnWorldViewRectUpdated(mWorldViewRect, new_world_rect); + mWorldViewRect = new_world_rect; gResizeScreenTexture = TRUE; LLViewerCamera::getInstance()->setViewHeightInPixels( mWorldViewRect.getHeight() ); @@ -3215,9 +3221,18 @@ LLPickInfo LLViewerWindow::pickImmediate(S32 x, S32 y_from_bot, BOOL pick_trans pickAsync(x, y_from_bot, gKeyboard->currentMask(TRUE), NULL, pick_transparent); // assume that pickAsync put the results in the back of the mPicks list + if(mPicks.size() != 0) + { mLastPick = mPicks.back(); mLastPick.fetchResults(); mPicks.pop_back(); + } + else + { + llwarns << "List of last picks is empty" << llendl; + llwarns << "Using stub pick" << llendl; + mLastPick = LLPickInfo(); + } return mLastPick; } diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index e4f6240fc7..231b857d1f 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -196,6 +196,11 @@ public: typedef boost::signals2::signal bottom_tray_signal_t; bottom_tray_signal_t mOnBottomTrayWidthChanged; boost::signals2::connection setOnBottomTrayWidthChanged(bottom_tray_callback_t cb) { return mOnBottomTrayWidthChanged.connect(cb); } + // signal on update of WorldView rect + typedef boost::function world_rect_callback_t; + typedef boost::signals2::signal world_rect_signal_t; + world_rect_signal_t mOnWorldViewRectUpdated; + boost::signals2::connection setOnWorldViewRectUpdated(world_rect_callback_t cb) { return mOnWorldViewRectUpdated.connect(cb); } // // ACCESSORS diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 2304571cf1..6401389c8f 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -46,6 +46,7 @@ # include "expat/expat.h" #endif #include "llcallbacklist.h" +#include "llcallingcard.h" // for LLFriendObserver #include "llviewerregion.h" #include "llviewernetwork.h" // for gGridChoice #include "llbase64.h" diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index fe99e787da..bddd18dee8 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -42,9 +42,9 @@ class LLVivoxProtocolParser; #include "v3math.h" #include "llframetimer.h" #include "llviewerregion.h" -#include "llcallingcard.h" // for LLFriendObserver #include "m3math.h" // LLMatrix3 +class LLFriendObserver; class LLVoiceClientParticipantObserver { public: diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 8440022844..46c294768d 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -514,6 +514,9 @@ + + + diff --git a/indra/newview/skins/default/xui/en/favorites_bar_button.xml b/indra/newview/skins/default/xui/en/favorites_bar_button.xml index e2f81168fa..dcd85e1f58 100644 --- a/indra/newview/skins/default/xui/en/favorites_bar_button.xml +++ b/indra/newview/skins/default/xui/en/favorites_bar_button.xml @@ -6,9 +6,13 @@ halign="center" height="23" image_disabled="transparent.j2c" - image_disabled_selected="PushButton_Selected" - image_selected="PushButton_Selected" - image_unselected="PushButton_Off" + image_disabled_selected="transparent.j2c" + image_selected="transparent.j2c" + image_unselected="transparent.j2c" + image_hover_selected="PushButton_Selected" + image_hover_unselected="PushButton_Off" + image_pressed="PushButton_Press" + image_pressed_selected="PushButton_Selected_Press" hover_glow_amount="0.15" layout="topleft" left="2" diff --git a/indra/newview/skins/default/xui/en/floater_camera.xml b/indra/newview/skins/default/xui/en/floater_camera.xml index 1592ed4aa4..0784b88944 100644 --- a/indra/newview/skins/default/xui/en/floater_camera.xml +++ b/indra/newview/skins/default/xui/en/floater_camera.xml @@ -1,5 +1,6 @@ @@ -95,7 +97,7 @@ scale_image="false" tool_tip="Fly Down, Press "C"" top_delta="0" - width="25" /> + width="20" /> - - - - - - diff --git a/indra/newview/skins/default/xui/en/menu_bottomtray.xml b/indra/newview/skins/default/xui/en/menu_bottomtray.xml new file mode 100644 index 0000000000..e98920f8c2 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_bottomtray.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_hide_navbar.xml b/indra/newview/skins/default/xui/en/menu_hide_navbar.xml index 1ad10abbeb..a175b3103f 100644 --- a/indra/newview/skins/default/xui/en/menu_hide_navbar.xml +++ b/indra/newview/skins/default/xui/en/menu_hide_navbar.xml @@ -11,23 +11,23 @@ + name="ShowNavbarNavigationPanel"> + function="ToggleControl" + parameter="ShowNavbarNavigationPanel" /> + function="CheckControl" + parameter="ShowNavbarNavigationPanel" /> + name="ShowNavbarFavoritesPanel"> + function="ToggleControl" + parameter="ShowNavbarFavoritesPanel" /> + function="CheckControl" + parameter="ShowNavbarFavoritesPanel" /> diff --git a/indra/newview/skins/default/xui/en/menu_people_groups_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_groups_view_sort.xml new file mode 100644 index 0000000000..ec8582f5b5 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_groups_view_sort.xml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_people_nearby.xml b/indra/newview/skins/default/xui/en/menu_people_nearby.xml new file mode 100644 index 0000000000..643336cf6c --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_nearby.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_people_recent_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_recent_view_sort.xml index 88b0528e53..d9606de90d 100644 --- a/indra/newview/skins/default/xui/en/menu_people_recent_view_sort.xml +++ b/indra/newview/skins/default/xui/en/menu_people_recent_view_sort.xml @@ -2,8 +2,8 @@ - - + + diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index b430de2b7b..bc51ba5e6a 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -127,12 +127,14 @@ + + function="World.SetBusy"/> - + + layout="topleft" /> + + + + + + + + + - - - - - - - + diff --git a/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml b/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml index 311caa4866..c4c8aa24c4 100644 --- a/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml +++ b/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml @@ -16,7 +16,10 @@ name="want_apply_text"> Do you want to apply these changes? - + + Join (L$[AMOUNT]) +