diff options
Diffstat (limited to 'indra/newview')
231 files changed, 12637 insertions, 16594 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 753dbd7438..70493b1214 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -107,6 +107,8 @@ set(viewer_SOURCE_FILES llavatarlist.cpp llavatarlistitem.cpp llavatarpropertiesprocessor.cpp + llblockedlistitem.cpp + llblocklist.cpp llbox.cpp llbreadcrumbview.cpp llbrowsernotification.cpp @@ -132,6 +134,10 @@ set(viewer_SOURCE_FILES llcommandlineparser.cpp llcompilequeue.cpp llconfirmationmanager.cpp + llconversationlog.cpp + llconversationloglist.cpp + llconversationloglistitem.cpp + llconversationmodel.cpp llcurrencyuimanager.cpp llcylinder.cpp lldateutil.cpp @@ -188,6 +194,8 @@ set(viewer_SOURCE_FILES llfloaterbuyland.cpp llfloatercamera.cpp llfloatercolorpicker.cpp + llfloaterconversationlog.cpp + llfloaterconversationpreview.cpp llfloaterdeleteenvpreset.cpp llfloaterdestinations.cpp llfloaterdisplayname.cpp @@ -255,13 +263,13 @@ set(viewer_SOURCE_FILES llfloateruipreview.cpp llfloaterurlentry.cpp llfloatervoiceeffect.cpp + llfloatervoicevolume.cpp llfloaterwebcontent.cpp llfloaterwebprofile.cpp llfloaterwhitelistentry.cpp llfloaterwindowsize.cpp llfloaterworldmap.cpp - llfolderview.cpp - llfolderviewitem.cpp + llfolderviewmodelinventory.cpp llfollowcam.cpp llfriendcard.cpp llgesturelistener.cpp @@ -287,6 +295,7 @@ set(viewer_SOURCE_FILES llhudrender.cpp llhudtext.cpp llhudview.cpp + llimconversation.cpp llimfloater.cpp llimfloatercontainer.cpp llimhandler.cpp @@ -341,7 +350,6 @@ set(viewer_SOURCE_FILES llnamelistctrl.cpp llnavigationbar.cpp llnearbychat.cpp - llnearbychatbar.cpp llnearbychathandler.cpp llnearbychatbarlistener.cpp llnetmap.cpp @@ -373,7 +381,6 @@ set(viewer_SOURCE_FILES llpanelgroupnotices.cpp llpanelgrouproles.cpp llpanelhome.cpp - llpanelimcontrolpanel.cpp llpanelland.cpp llpanellandaudio.cpp llpanellandmarkinfo.cpp @@ -384,7 +391,6 @@ set(viewer_SOURCE_FILES llpanelmaininventory.cpp llpanelmarketplaceinbox.cpp llpanelmarketplaceinboxinventory.cpp - llpanelmarketplaceoutboxinventory.cpp llpanelmediasettingsgeneral.cpp llpanelmediasettingspermissions.cpp llpanelmediasettingssecurity.cpp @@ -683,6 +689,8 @@ set(viewer_HEADER_FILES llavatarlist.h llavatarlistitem.h llavatarpropertiesprocessor.h + llblockedlistitem.h + llblocklist.h llbox.h llbreadcrumbview.h llbuycurrencyhtml.h @@ -708,6 +716,10 @@ set(viewer_HEADER_FILES llcommandlineparser.h llcompilequeue.h llconfirmationmanager.h + llconversationlog.h + llconversationloglist.h + llconversationloglistitem.h + llconversationmodel.h llcurrencyuimanager.h llcylinder.h lldateutil.h @@ -764,6 +776,8 @@ set(viewer_HEADER_FILES llfloaterbuyland.h llfloatercamera.h llfloatercolorpicker.h + llfloaterconversationlog.h + llfloaterconversationpreview.h llfloaterdeleteenvpreset.h llfloaterdestinations.h llfloaterdisplayname.h @@ -831,14 +845,13 @@ set(viewer_HEADER_FILES llfloateruipreview.h llfloaterurlentry.h llfloatervoiceeffect.h + llfloatervoicevolume.h llfloaterwebcontent.h llfloaterwebprofile.h llfloaterwhitelistentry.h llfloaterwindowsize.h llfloaterworldmap.h - llfolderview.h - llfoldervieweventlistener.h - llfolderviewitem.h + llfolderviewmodelinventory.h llfollowcam.h llfriendcard.h llgesturelistener.h @@ -863,6 +876,7 @@ set(viewer_HEADER_FILES llhudrender.h llhudtext.h llhudview.h + llimconversation.h llimfloater.h llimfloatercontainer.h llimview.h @@ -917,7 +931,6 @@ set(viewer_HEADER_FILES llnamelistctrl.h llnavigationbar.h llnearbychat.h - llnearbychatbar.h llnearbychathandler.h llnearbychatbarlistener.h llnetmap.h @@ -943,7 +956,6 @@ set(viewer_HEADER_FILES llpanelgroupnotices.h llpanelgrouproles.h llpanelhome.h - llpanelimcontrolpanel.h llpanelland.h llpanellandaudio.h llpanellandmarkinfo.h @@ -954,7 +966,6 @@ set(viewer_HEADER_FILES llpanelmaininventory.h llpanelmarketplaceinbox.h llpanelmarketplaceinboxinventory.h - llpanelmarketplaceoutboxinventory.h llpanelmediasettingsgeneral.h llpanelmediasettingspermissions.h llpanelmediasettingssecurity.h diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml index 73df064ab2..51211a8ce5 100644 --- a/indra/newview/app_settings/commands.xml +++ b/indra/newview/app_settings/commands.xml @@ -46,11 +46,11 @@ available_in_toybox="true" icon="Command_Chat_Icon" label_ref="Command_Chat_Label" - tooltip_ref="Command_Chat_Tooltip" + tooltip_ref="Command_Conversations_Tooltip" execute_function="Floater.ToggleOrBringToFront" - execute_parameters="chat_bar" + execute_parameters="im_container" is_running_function="Floater.IsOpen" - is_running_parameters="chat_bar" + is_running_parameters="im_container" /> <command name="compass" available_in_toybox="false" diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 2af71d98b8..e4337cf8b6 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2,6 +2,28 @@ <llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> <map> + <key>IMShowTime</key> + <map> + <key>Comment</key> + <string>Enable(disable) timestamp showing in the chat.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> + <key>IMShowNamesForP2PConv</key> + <map> + <key>Comment</key> + <string>Enable(disable) showing of a names in the chat.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>CrashHostUrl</key> <map> <key>Comment</key> @@ -1595,17 +1617,6 @@ <key>Value</key> <integer>1</integer> </map> - <key>ChatWindow</key> - <map> - <key>Comment</key> - <string>Show chat in multiple windows(by default) or in one multi-tabbed window(requires restart)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>S32</string> - <key>Value</key> - <integer>0</integer> - </map> <key>CheesyBeacon</key> <map> <key>Comment</key> @@ -1628,6 +1639,17 @@ <key>Value</key> <string /> </map> + <key>NearbyChatIsNotTornOff</key> + <map> + <key>Comment</key> + <string>saving torn-off state of the nearby chat between sessions</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>CloseChatOnReturn</key> <map> <key>Comment</key> @@ -9835,7 +9857,7 @@ <key>ShowScriptErrorsLocation</key> <map> <key>Comment</key> - <string>Show script error in chat or window</string> + <string>Show script error in chat (0) or window (1).</string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -10019,6 +10041,39 @@ <key>Value</key> <integer>2</integer> </map> + <key>BlockPeopleSortOrder</key> + <map> + <key>Comment</key> + <string>Specifies sort order for recent people (0 = by name, 1 = by type)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>2</integer> + </map> + <key>CallLogSortOrder</key> + <map> + <key>Comment</key> + <string>Specifies sort order for Call Log (0 = by name, 1 = by date)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>2</integer> + </map> + <key>SortFriendsFirst</key> + <map> + <key>Comment</key> + <string>Specifies whether friends will be sorted first in Call Log</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>ShowPGSearchAll</key> <map> <key>Comment</key> diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml index 143126b334..1f637ef3ff 100644 --- a/indra/newview/app_settings/settings_per_account.xml +++ b/indra/newview/app_settings/settings_per_account.xml @@ -22,6 +22,61 @@ <key>Value</key> <string>The Resident you messaged is in 'busy mode' which means they have requested not to be disturbed. Your message will still be shown in their IM panel for later viewing.</string> </map> + <key>ConversationsExpandMessagePaneFirst</key> + <map> + <key>Comment</key> + <string>Expand either messages or conversations list pane from Conversations compact mode.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> + <key>ConversationsListPaneCollapsed</key> + <map> + <key>Comment</key> + <string>Stores the expanded/collapsed state of the conversations list pane in Conversations floater.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>ConversationsListPaneWidth</key> + <map> + <key>Comment</key> + <string>Conversations floater list pane width.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>268</integer> + </map> + <key>ConversationsMessagePaneCollapsed</key> + <map> + <key>Comment</key> + <string>Stores the expanded/collapsed state of Conversations floater message pane.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>ConversationsMessagePaneWidth</key> + <map> + <key>Comment</key> + <string>Conversations floater message pane width.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>412</integer> + </map> <key>InstantMessageLogPath</key> <map> <key>Comment</key> diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 447836910d..f187318c0f 100755 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -54,7 +54,7 @@ #include "llmorphview.h" #include "llmoveview.h" #include "llnavigationbar.h" // to show/hide navigation bar when changing mouse look state -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llnotificationsutil.h" #include "llpanelpathfindingrebakenavmesh.h" #include "llpaneltopinfobar.h" @@ -1911,7 +1911,7 @@ void LLAgent::startTyping() { sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_START); } - LLNearbyChatBar::getInstance()->sendChatFromViewer("", CHAT_TYPE_START, FALSE); + LLNearbyChat::getInstance()->sendChatFromViewer("", CHAT_TYPE_START, FALSE); } //----------------------------------------------------------------------------- @@ -1923,7 +1923,7 @@ void LLAgent::stopTyping() { clearRenderState(AGENT_STATE_TYPING); sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_STOP); - LLNearbyChatBar::getInstance()->sendChatFromViewer("", CHAT_TYPE_STOP, FALSE); + LLNearbyChat::getInstance()->sendChatFromViewer("", CHAT_TYPE_STOP, FALSE); } } diff --git a/indra/newview/llagentwearablesfetch.cpp b/indra/newview/llagentwearablesfetch.cpp index e2417cdddb..e31e39dca2 100644 --- a/indra/newview/llagentwearablesfetch.cpp +++ b/indra/newview/llagentwearablesfetch.cpp @@ -342,7 +342,7 @@ void LLLibraryOutfitsFetch::folderDone() } mClothingID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING); - mLibraryClothingID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING, false, true); + mLibraryClothingID = gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_CLOTHING, false); // If Library->Clothing->Initial Outfits exists, use that. LLNameCategoryCollector matchFolderFunctor("Initial Outfits"); diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 6d67e098a6..510abf198a 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -2330,7 +2330,7 @@ void LLAppearanceMgr::copyLibraryGestures() // Copy gestures LLUUID lib_gesture_cat_id = - gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE,false,true); + gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_GESTURE,false); if (lib_gesture_cat_id.isNull()) { llwarns << "Unable to copy gestures, source category not found" << llendl; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 63737c78d0..1c81459912 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -59,6 +59,7 @@ #include "llares.h" #include "llcurl.h" #include "llcalc.h" +#include "llconversationlog.h" #include "lltexturestats.h" #include "lltexturestats.h" #include "llviewerwindow.h" @@ -1832,6 +1833,9 @@ bool LLAppViewer::cleanup() // save mute list. gMuteList used to also be deleted here too. LLMuteList::getInstance()->cache(gAgent.getID()); + //save call log list + LLConversationLog::instance().cache(); + if (mPurgeOnExit) { llinfos << "Purging all cache files on exit" << llendflush; diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index fdd4565e50..93e8b9ca40 100755 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -235,7 +235,7 @@ void LLAvatarActions::startCall(const LLUUID& id) } // static -void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids) +void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids, const LLUUID& floater_id) { if (ids.size() == 0) { @@ -252,7 +252,7 @@ void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids) // create the new ad hoc voice session const std::string title = LLTrans::getString("conference-title"); LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START, - ids[0], id_array, true); + ids[0], id_array, true, floater_id); if (session_id == LLUUID::null) { return; @@ -285,7 +285,7 @@ bool LLAvatarActions::canCall() } // static -void LLAvatarActions::startConference(const uuid_vec_t& ids) +void LLAvatarActions::startConference(const uuid_vec_t& ids, const LLUUID& floater_id) { // *HACK: Copy into dynamic array LLDynamicArray<LLUUID> id_array; @@ -294,7 +294,7 @@ void LLAvatarActions::startConference(const uuid_vec_t& ids) id_array.push_back(*it); } const std::string title = LLTrans::getString("conference-title"); - LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START, ids[0], id_array); + LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START, ids[0], id_array, false, floater_id); if (session_id != LLUUID::null) { LLIMFloater::show(session_id); @@ -529,23 +529,6 @@ namespace action_give_inventory return acceptable; } - static void build_residents_string(const std::vector<LLAvatarName> avatar_names, std::string& residents_string) - { - llassert(avatar_names.size() > 0); - - const std::string& separator = LLTrans::getString("words_separator"); - for (std::vector<LLAvatarName>::const_iterator it = avatar_names.begin(); ; ) - { - LLAvatarName av_name = *it; - residents_string.append(av_name.mDisplayName); - if (++it == avatar_names.end()) - { - break; - } - residents_string.append(separator); - } - } - static void build_items_string(const std::set<LLUUID>& inventory_selected_uuids , std::string& items_string) { llassert(inventory_selected_uuids.size() > 0); @@ -681,7 +664,7 @@ namespace action_give_inventory } std::string residents; - build_residents_string(avatar_names, residents); + LLAvatarActions::buildResidentsString(avatar_names, residents); std::string items; build_items_string(inventory_selected_uuids, items); @@ -712,28 +695,51 @@ namespace action_give_inventory } } +// static +void LLAvatarActions::buildResidentsString(const std::vector<LLAvatarName> avatar_names, std::string& residents_string) +{ + llassert(avatar_names.size() > 0); + const std::string& separator = LLTrans::getString("words_separator"); + for (std::vector<LLAvatarName>::const_iterator it = avatar_names.begin(); ; ) + { + LLAvatarName av_name = *it; + residents_string.append(av_name.mDisplayName); + if (++it == avatar_names.end()) + { + break; + } + residents_string.append(separator); + } +} //static std::set<LLUUID> LLAvatarActions::getInventorySelectedUUIDs() { - std::set<LLUUID> inventory_selected_uuids; + std::set<LLFolderViewItem*> inventory_selected; LLInventoryPanel* active_panel = action_give_inventory::get_active_inventory_panel(); if (active_panel) { - inventory_selected_uuids = active_panel->getRootFolder()->getSelectionList(); + inventory_selected= active_panel->getRootFolder()->getSelectionList(); } - if (inventory_selected_uuids.empty()) + if (inventory_selected.empty()) { LLSidepanelInventory *sidepanel_inventory = LLFloaterSidePanelContainer::getPanel<LLSidepanelInventory>("inventory"); if (sidepanel_inventory) { - inventory_selected_uuids = sidepanel_inventory->getInboxSelectionList(); + inventory_selected= sidepanel_inventory->getInboxSelectionList(); } } + std::set<LLUUID> inventory_selected_uuids; + for (std::set<LLFolderViewItem*>::iterator it = inventory_selected.begin(), end_it = inventory_selected.end(); + it != end_it; + ++it) + { + inventory_selected_uuids.insert(static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID()); + } return inventory_selected_uuids; } @@ -769,15 +775,15 @@ bool LLAvatarActions::canShareSelectedItems(LLInventoryPanel* inv_panel /* = NUL // check selection in the panel LLFolderView* root_folder = inv_panel->getRootFolder(); - const std::set<LLUUID> inventory_selected_uuids = root_folder->getSelectionList(); - if (inventory_selected_uuids.empty()) return false; // nothing selected + const std::set<LLFolderViewItem*> inventory_selected = root_folder->getSelectionList(); + if (inventory_selected.empty()) return false; // nothing selected bool can_share = true; - std::set<LLUUID>::const_iterator it = inventory_selected_uuids.begin(); - const std::set<LLUUID>::const_iterator it_end = inventory_selected_uuids.end(); + std::set<LLFolderViewItem*>::const_iterator it = inventory_selected.begin(); + const std::set<LLFolderViewItem*>::const_iterator it_end = inventory_selected.end(); for (; it != it_end; ++it) { - LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it); + LLViewerInventoryCategory* inv_cat = gInventory.getCategory(static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID()); // any category can be offered. if (inv_cat) { @@ -785,9 +791,9 @@ bool LLAvatarActions::canShareSelectedItems(LLInventoryPanel* inv_panel /* = NUL } // check if inventory item can be given - LLFolderViewItem* item = root_folder->getItemByID(*it); + LLFolderViewItem* item = *it; if (!item) return false; - LLInvFVBridge* bridge = dynamic_cast<LLInvFVBridge*>(item->getListener()); + LLInvFVBridge* bridge = dynamic_cast<LLInvFVBridge*>(item->getViewModelItem()); if (bridge && bridge->canShare()) { continue; @@ -820,6 +826,26 @@ void LLAvatarActions::toggleBlock(const LLUUID& id) } // static +void LLAvatarActions::toggleMuteVoice(const LLUUID& id) +{ + std::string name; + gCacheName->getFullName(id, name); // needed for mute + + LLMuteList* mute_list = LLMuteList::getInstance(); + bool is_muted = mute_list->isMuted(id, LLMute::flagVoiceChat); + + LLMute mute(id, name, LLMute::AGENT); + if (!is_muted) + { + mute_list->add(mute, LLMute::flagVoiceChat); + } + else + { + mute_list->remove(mute, LLMute::flagVoiceChat); + } +} + +// static bool LLAvatarActions::canOfferTeleport(const LLUUID& id) { // First use LLAvatarTracker::isBuddy() @@ -1015,7 +1041,6 @@ void LLAvatarActions::requestFriendship(const LLUUID& target_id, const std::stri LLSD payload; payload["from_id"] = target_id; - payload["SUPPRESS_TOAST"] = true; LLNotificationsUtil::add("FriendshipOffered", args, payload); } @@ -1034,6 +1059,12 @@ bool LLAvatarActions::isBlocked(const LLUUID& id) } // static +bool LLAvatarActions::isVoiceMuted(const LLUUID& id) +{ + return LLMuteList::getInstance()->isMuted(id, LLMute::flagVoiceChat); +} + +// static bool LLAvatarActions::canBlock(const LLUUID& id) { std::string full_name; diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h index 748b7cb3d1..259e87c336 100644 --- a/indra/newview/llavataractions.h +++ b/indra/newview/llavataractions.h @@ -34,6 +34,7 @@ #include <string> #include <vector> +class LLAvatarName; class LLInventoryPanel; class LLFloater; @@ -81,14 +82,14 @@ public: static void startCall(const LLUUID& id); /** - * Start an ad-hoc conference voice call with multiple users + * Start an ad-hoc conference voice call with multiple users in a specific IM floater. */ - static void startAdhocCall(const uuid_vec_t& ids); + static void startAdhocCall(const uuid_vec_t& ids, const LLUUID& floater_id = LLUUID::null); /** - * Start conference chat with the given avatars. + * Start conference chat with the given avatars in a specific IM floater. */ - static void startConference(const uuid_vec_t& ids); + static void startConference(const uuid_vec_t& ids, const LLUUID& floater_id = LLUUID::null); /** * Show avatar profile. @@ -124,6 +125,11 @@ public: static void toggleBlock(const LLUUID& id); /** + * Block/unblock the avatar voice. + */ + static void toggleMuteVoice(const LLUUID& id); + + /** * Return true if avatar with "id" is a friend */ static bool isFriend(const LLUUID& id); @@ -134,6 +140,11 @@ public: static bool isBlocked(const LLUUID& id); /** + * @return true if the avatar voice is blocked + */ + static bool isVoiceMuted(const LLUUID& id); + + /** * @return true if you can block the avatar */ static bool canBlock(const LLUUID& id); @@ -198,6 +209,14 @@ public: */ static bool canShareSelectedItems(LLInventoryPanel* inv_panel = NULL); + /** + * Builds a string of residents' display names separated by "words_separator" string. + * + * @param avatar_names - a vector of given avatar names from which resulting string is built + * @param residents_string - the resulting string + */ + static void buildResidentsString(const std::vector<LLAvatarName> avatar_names, std::string& residents_string); + static std::set<LLUUID> getInventorySelectedUUIDs(); private: diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp index 30eecfe323..7b5229b5e6 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -38,6 +38,7 @@ #include "llavatarnamecache.h" #include "llavatariconctrl.h" #include "lloutputmonitorctrl.h" +#include "lltooldraganddrop.h" bool LLAvatarListItem::sStaticInitialized = false; S32 LLAvatarListItem::sLeftPadding = 0; @@ -334,6 +335,61 @@ BOOL LLAvatarListItem::handleDoubleClick(S32 x, S32 y, MASK mask) return LLPanel::handleDoubleClick(x, y, mask); } +BOOL LLAvatarListItem::handleMouseDown(S32 x, S32 y, MASK mask) +{ + if (LLUICtrl::handleMouseDown(x, y, mask)) + { + return TRUE; + } + + gFocusMgr.setMouseCapture(this); + + S32 screen_x; + S32 screen_y; + localPointToScreen(x, y, &screen_x, &screen_y); + LLToolDragAndDrop::getInstance()->setDragStart(screen_x, screen_y); + + return TRUE; +} + +BOOL LLAvatarListItem::handleMouseUp( S32 x, S32 y, MASK mask ) +{ + if (LLUICtrl::childrenHandleMouseUp(x, y, mask)) + { + return TRUE; + } + + if(hasMouseCapture()) + { + gFocusMgr.setMouseCapture(NULL); + } + return TRUE; +} + +BOOL LLAvatarListItem::handleHover(S32 x, S32 y, MASK mask) +{ + bool handled = hasMouseCapture(); + if(handled) + { + S32 screen_x; + S32 screen_y; + localPointToScreen(x, y, &screen_x, &screen_y); + + if(LLToolDragAndDrop::getInstance()->isOverThreshold(screen_x, screen_y)) + { + // First, create the global drag and drop object + std::vector<EDragAndDropType> types; + uuid_vec_t cargo_ids; + types.push_back(DAD_PERSON); + cargo_ids.push_back(mAvatarId); + LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_PEOPLE; + LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, src); + } + } + + return handled; +} + void LLAvatarListItem::setValue( const LLSD& value ) { if (!value.isMap()) return;; diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h index c95ac39696..28a50870d4 100644 --- a/indra/newview/llavatarlistitem.h +++ b/indra/newview/llavatarlistitem.h @@ -112,6 +112,9 @@ public: void onProfileBtnClick(); /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask ); + /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); protected: /** diff --git a/indra/newview/llblockedlistitem.cpp b/indra/newview/llblockedlistitem.cpp new file mode 100644 index 0000000000..d9afd2b629 --- /dev/null +++ b/indra/newview/llblockedlistitem.cpp @@ -0,0 +1,114 @@ +/** + * @file llviewerobjectlistitem.cpp + * @brief viewer object list item implementation + * + * Class LLPanelInventoryListItemBase displays inventory item as an element + * of LLInventoryItemsList. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llblockedlistitem.h" + +// llui +#include "lliconctrl.h" +#include "lltextbox.h" +#include "lltextutil.h" + +// newview +#include "llavatariconctrl.h" +#include "llgroupiconctrl.h" +#include "llinventoryicon.h" +#include "llviewerobject.h" + +LLBlockedListItem::LLBlockedListItem(const LLMute* item) +: LLPanel(), + mItemID(item->mID), + mItemName(item->mName), + mMuteType(item->mType) +{ + buildFromFile("panel_blocked_list_item.xml"); +} + +BOOL LLBlockedListItem::postBuild() +{ + mTitleCtrl = getChild<LLTextBox>("item_name"); + mTitleCtrl->setValue(mItemName); + + switch (mMuteType) + { + case LLMute::AGENT: + case LLMute::EXTERNAL: + { + LLAvatarIconCtrl* avatar_icon = getChild<LLAvatarIconCtrl>("avatar_icon"); + avatar_icon->setVisible(TRUE); + avatar_icon->setValue(mItemID); + } + break; + case LLMute::GROUP: + { + LLGroupIconCtrl* group_icon = getChild<LLGroupIconCtrl>("group_icon"); + group_icon->setVisible(TRUE); + group_icon->setValue(mItemID); + } + break; + case LLMute::OBJECT: + case LLMute::BY_NAME: + getChild<LLUICtrl>("object_icon")->setVisible(TRUE); + break; + + default: + break; + } + + return TRUE; +} + +void LLBlockedListItem::onMouseEnter(S32 x, S32 y, MASK mask) +{ + getChildView("hovered_icon")->setVisible(true); + LLPanel::onMouseEnter(x, y, mask); +} + +void LLBlockedListItem::onMouseLeave(S32 x, S32 y, MASK mask) +{ + getChildView("hovered_icon")->setVisible(false); + LLPanel::onMouseLeave(x, y, mask); +} + +void LLBlockedListItem::setValue(const LLSD& value) +{ + if (!value.isMap() || !value.has("selected")) + { + return; + } + + getChildView("selected_icon")->setVisible(value["selected"]); +} + +void LLBlockedListItem::highlightName(const std::string& highlited_text) +{ + LLStyle::Params params; + LLTextUtil::textboxSetHighlightedVal(mTitleCtrl, params, mItemName, highlited_text); +} diff --git a/indra/newview/llblockedlistitem.h b/indra/newview/llblockedlistitem.h new file mode 100644 index 0000000000..05409e8a3b --- /dev/null +++ b/indra/newview/llblockedlistitem.h @@ -0,0 +1,73 @@ +/** + * @file llviewerobjectlistitem.h + * @brief viewer object list item header file + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef LLVIEWEROBJECTLISTITEM_H_ +#define LLVIEWEROBJECTLISTITEM_H_ + +#include "llmutelist.h" +#include "llpanel.h" +#include "llstyle.h" +#include "lltextbox.h" +#include "lliconctrl.h" + +/** + * This class represents items of LLBlockList, which represents + * contents of LLMuteList. LLMuteList "consists" of LLMute items. + * Each LLMute represents either blocked avatar or object and + * stores info about mute type (avatar or object) + * + * Each item consists if object/avatar icon and object/avatar name + * + * To create a blocked list item just need to pass LLMute pointer + * and appropriate block list item will be created depending on + * LLMute type (LLMute::EType) and other LLMute's info + */ +class LLBlockedListItem : public LLPanel +{ +public: + + LLBlockedListItem(const LLMute* item); + virtual BOOL postBuild(); + + void onMouseEnter(S32 x, S32 y, MASK mask); + void onMouseLeave(S32 x, S32 y, MASK mask); + + virtual void setValue(const LLSD& value); + + void highlightName(const std::string& highlited_text); + const std::string& getName() const { return mItemName; } + const LLMute::EType& getType() const { return mMuteType; } + const LLUUID& getUUID() const { return mItemID; } + +private: + + LLTextBox* mTitleCtrl; + const LLUUID mItemID; + std::string mItemName; + LLMute::EType mMuteType; + +}; + +#endif /* LLVIEWEROBJECTLISTITEM_H_ */ diff --git a/indra/newview/llblocklist.cpp b/indra/newview/llblocklist.cpp new file mode 100644 index 0000000000..066cb71677 --- /dev/null +++ b/indra/newview/llblocklist.cpp @@ -0,0 +1,284 @@ +/** + * @file llblocklist.cpp + * @brief List of the blocked avatars and objects. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llblocklist.h" + +#include "llavataractions.h" +#include "llblockedlistitem.h" +#include "llfloatersidepanelcontainer.h" +#include "llviewermenu.h" + +static LLDefaultChildRegistry::Register<LLBlockList> r("block_list"); + +static const LLBlockListNameComparator NAME_COMPARATOR; +static const LLBlockListNameTypeComparator NAME_TYPE_COMPARATOR; + +LLBlockList::LLBlockList(const Params& p) +: LLFlatListViewEx(p), + mSelectedItem(NULL), + mDirty(true) +{ + + LLMuteList::getInstance()->addObserver(this); + + // Set up context menu. + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + + registrar.add ("Block.Action", boost::bind(&LLBlockList::onCustomAction, this, _2)); + enable_registrar.add("Block.Enable", boost::bind(&LLBlockList::isActionEnabled, this, _2)); + + LLToggleableMenu* context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>( + "menu_people_blocked_gear.xml", + gMenuHolder, + LLViewerMenuHolderGL::child_registry_t::instance()); + if(context_menu) + { + mContextMenu = context_menu->getHandle(); + } +} + +LLBlockList::~LLBlockList() +{ + if (mContextMenu.get()) + { + mContextMenu.get()->die(); + } + + LLMuteList::getInstance()->removeObserver(this); +} + +BOOL LLBlockList::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + BOOL handled = LLUICtrl::handleRightMouseDown(x, y, mask); + + LLToggleableMenu* context_menu = mContextMenu.get(); + if (context_menu && size()) + { + context_menu->buildDrawLabels(); + context_menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, context_menu, x, y); + } + + return handled; +} + +void LLBlockList::setNameFilter(const std::string& filter) +{ + std::string filter_upper = filter; + LLStringUtil::toUpper(filter_upper); + if (mNameFilter != filter_upper) + { + mNameFilter = filter_upper; + setDirty(); + } +} + +void LLBlockList::sortByName() +{ + setComparator(&NAME_COMPARATOR); + sort(); +} + +void LLBlockList::sortByType() +{ + setComparator(&NAME_TYPE_COMPARATOR); + sort(); +} + +void LLBlockList::draw() +{ + if (mDirty) + { + refresh(); + } + + LLFlatListView::draw(); +} + +void LLBlockList::addNewItem(const LLMute* mute) +{ + LLBlockedListItem* item = new LLBlockedListItem(mute); + if (!mNameFilter.empty()) + { + item->highlightName(mNameFilter); + } + addItem(item, item->getUUID(), ADD_BOTTOM); +} + +void LLBlockList::refresh() +{ + bool have_filter = !mNameFilter.empty(); + + // save selection to restore it after list rebuilt + LLUUID selected = getSelectedUUID(); + + // calling refresh may be initiated by removing currently selected item + // so select next item and save the selection to restore it after list rebuilt + if (!selectNextItemPair(false, true)) + { + selectNextItemPair(true, true); + } + LLUUID next_selected = getSelectedUUID(); + + clear(); + + std::vector<LLMute> mutes = LLMuteList::instance().getMutes(); + std::vector<LLMute>::const_iterator mute_it = mutes.begin(); + + for (; mute_it != mutes.end(); ++mute_it) + { + if (have_filter && !findInsensitive(mute_it->mName, mNameFilter)) + continue; + + addNewItem(&*mute_it); + } + + if (getItemPair(selected)) + { + // restore previously selected item + selectItemPair(getItemPair(selected), true); + } + else if (getItemPair(next_selected)) + { + // previously selected item was removed, so select next item + selectItemPair(getItemPair(next_selected), true); + } + + // Sort the list. + sort(); + + setDirty(false); +} + +bool LLBlockList::findInsensitive(std::string haystack, const std::string& needle_upper) +{ + LLStringUtil::toUpper(haystack); + return haystack.find(needle_upper) != std::string::npos; +} + +LLBlockedListItem* LLBlockList::getBlockedItem() const +{ + LLPanel* panel = LLFlatListView::getSelectedItem(); + LLBlockedListItem* item = dynamic_cast<LLBlockedListItem*>(panel); + return item; +} + +bool LLBlockList::isActionEnabled(const LLSD& userdata) +{ + bool action_enabled = true; + + const std::string command_name = userdata.asString(); + + if ("profile_item" == command_name) + { + LLBlockedListItem* item = getBlockedItem(); + action_enabled = item && (LLMute::AGENT == item->getType()); + } + + if ("unblock_item" == command_name) + { + action_enabled = getSelectedItem() != NULL; + } + + return action_enabled; +} + +void LLBlockList::onCustomAction(const LLSD& userdata) +{ + if (!isActionEnabled(userdata)) + { + return; + } + + LLBlockedListItem* item = getBlockedItem(); + const std::string command_name = userdata.asString(); + + if ("unblock_item" == command_name) + { + LLMute mute(item->getUUID(), item->getName()); + LLMuteList::getInstance()->remove(mute); + } + else if ("profile_item" == command_name) + { + switch(item->getType()) + { + + case LLMute::AGENT: + LLAvatarActions::showProfile(item->getUUID()); + break; + + default: + break; + } + } +} + +bool LLBlockListItemComparator::compare(const LLPanel* item1, const LLPanel* item2) const +{ + const LLBlockedListItem* blocked_item1 = dynamic_cast<const LLBlockedListItem*>(item1); + const LLBlockedListItem* blocked_item2 = dynamic_cast<const LLBlockedListItem*>(item2); + + if (!blocked_item1 || !blocked_item2) + { + llerror("blocked_item1 and blocked_item2 cannot be null", 0); + return true; + } + + return doCompare(blocked_item1, blocked_item2); +} + +bool LLBlockListNameComparator::doCompare(const LLBlockedListItem* blocked_item1, const LLBlockedListItem* blocked_item2) const +{ + std::string name1 = blocked_item1->getName(); + std::string name2 = blocked_item2->getName(); + + LLStringUtil::toUpper(name1); + LLStringUtil::toUpper(name2); + + return name1 < name2; +} + +bool LLBlockListNameTypeComparator::doCompare(const LLBlockedListItem* blocked_item1, const LLBlockedListItem* blocked_item2) const +{ + LLMute::EType type1 = blocked_item1->getType(); + LLMute::EType type2 = blocked_item2->getType(); + + // if mute type is LLMute::BY_NAME or LLMute::OBJECT it means that this mute is an object + bool both_mutes_are_objects = (LLMute::OBJECT == type1 || LLMute::BY_NAME == type1) && (LLMute::OBJECT == type2 || LLMute::BY_NAME == type2); + + // mute types may be different, but since both LLMute::BY_NAME and LLMute::OBJECT types represent objects + // it's needed to perform additional checking of both_mutes_are_objects variable + if (type1 != type2 && !both_mutes_are_objects) + { + // objects in block list go first, so return true if mute type is not an avatar + return LLMute::AGENT != type1; + } + + return NAME_COMPARATOR.compare(blocked_item1, blocked_item2); +} diff --git a/indra/newview/llblocklist.h b/indra/newview/llblocklist.h new file mode 100644 index 0000000000..1a215710f4 --- /dev/null +++ b/indra/newview/llblocklist.h @@ -0,0 +1,138 @@ +/** + * @file llblocklist.h + * @brief List of the blocked avatars and objects. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef LLBLOCKLIST_H_ +#define LLBLOCKLIST_H_ + +#include "llflatlistview.h" +#include "lllistcontextmenu.h" +#include "llmutelist.h" +#include "lltoggleablemenu.h" + +class LLBlockedListItem; +class LLMute; + +/** + * List of blocked avatars and objects. + * This list represents contents of the LLMuteList. + * Each change in LLMuteList leads to rebuilding this list, so + * it's always in actual state. + */ +class LLBlockList: public LLFlatListViewEx, public LLMuteListObserver +{ + LOG_CLASS(LLBlockList); +public: + struct Params : public LLInitParam::Block<Params, LLFlatListViewEx::Params> + { + Params(){}; + }; + + LLBlockList(const Params& p); + virtual ~LLBlockList(); + + virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + LLToggleableMenu* getContextMenu() const { return mContextMenu.get(); } + LLBlockedListItem* getBlockedItem() const; + + virtual void onChange() { refresh(); } + virtual void draw(); + + void setNameFilter(const std::string& filter); + void sortByName(); + void sortByType(); + void refresh(); + +private: + + void addNewItem(const LLMute* mute); + void setDirty(bool dirty = true) { mDirty = dirty; } + bool findInsensitive(std::string haystack, const std::string& needle_upper); + + bool isActionEnabled(const LLSD& userdata); + void onCustomAction (const LLSD& userdata); + + + LLHandle<LLToggleableMenu> mContextMenu; + + LLBlockedListItem* mSelectedItem; + std::string mNameFilter; + bool mDirty; + +}; + + +/* + * Abstract comparator for blocked items + */ +class LLBlockListItemComparator : public LLFlatListView::ItemComparator +{ + LOG_CLASS(LLBlockListItemComparator); + +public: + LLBlockListItemComparator() {}; + virtual ~LLBlockListItemComparator() {}; + + virtual bool compare(const LLPanel* item1, const LLPanel* item2) const; + +protected: + + virtual bool doCompare(const LLBlockedListItem* blocked_item1, const LLBlockedListItem* blocked_item2) const = 0; +}; + + +/* + * Compares items by name + */ +class LLBlockListNameComparator : public LLBlockListItemComparator +{ + LOG_CLASS(LLBlockListNameComparator); + +public: + LLBlockListNameComparator() {}; + virtual ~LLBlockListNameComparator() {}; + +protected: + + virtual bool doCompare(const LLBlockedListItem* blocked_item1, const LLBlockedListItem* blocked_item2) const; +}; + +/* + * Compares items by type and then by name within type + * Objects come first then avatars + */ +class LLBlockListNameTypeComparator : public LLBlockListItemComparator +{ + LOG_CLASS(LLBlockListNameTypeComparator); + +public: + LLBlockListNameTypeComparator() {}; + virtual ~LLBlockListNameTypeComparator() {}; + +protected: + + virtual bool doCompare(const LLBlockedListItem* blocked_item1, const LLBlockedListItem* blocked_item2) const; +}; + +#endif /* LLBLOCKLIST_H_ */ diff --git a/indra/newview/llbrowsernotification.cpp b/indra/newview/llbrowsernotification.cpp index 6e77d1e336..9e608d2c8b 100644 --- a/indra/newview/llbrowsernotification.cpp +++ b/indra/newview/llbrowsernotification.cpp @@ -35,11 +35,8 @@ using namespace LLNotificationsUI; -bool LLBrowserNotification::processNotification(const LLSD& notify) +bool LLBrowserNotification::processNotification(const LLNotificationPtr& notification) { - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - if (!notification) return false; - LLUUID media_id = notification->getPayload()["media_id"].asUUID(); LLMediaCtrl* media_instance = LLMediaCtrl::getInstance(media_id); if (media_instance) diff --git a/indra/newview/llcallingcard.cpp b/indra/newview/llcallingcard.cpp index 0d55c4429a..60d60abd45 100644 --- a/indra/newview/llcallingcard.cpp +++ b/indra/newview/llcallingcard.cpp @@ -54,6 +54,7 @@ #include "llresmgr.h" #include "llslurl.h" #include "llimview.h" +#include "lltrans.h" #include "llviewercontrol.h" #include "llviewernetwork.h" #include "llviewerobjectlist.h" @@ -723,12 +724,13 @@ static void on_avatar_name_cache_notify(const LLUUID& agent_id, // Use display name only because this user is your friend LLSD args; args["NAME"] = av_name.mDisplayName; + args["STATUS"] = online ? LLTrans::getString("OnlineStatus") : LLTrans::getString("OfflineStatus"); LLNotificationPtr notification; if (online) { notification = - LLNotificationsUtil::add("FriendOnline", + LLNotificationsUtil::add("FriendOnlineOffline", args, payload.with("respond_on_mousedown", TRUE), boost::bind(&LLAvatarActions::startIM, agent_id)); @@ -736,7 +738,7 @@ static void on_avatar_name_cache_notify(const LLUUID& agent_id, else { notification = - LLNotificationsUtil::add("FriendOffline", args, payload); + LLNotificationsUtil::add("FriendOnlineOffline", args, payload); } // If there's an open IM session with this agent, send a notification there too. diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 84e73e96fa..e3d57ab7ae 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -143,7 +143,8 @@ public: { LLMuteList::getInstance()->add(LLMute(getAvatarId(), mFrom, LLMute::OBJECT)); - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD().with("blocked_to_select", getAvatarId())); + LLFloaterSidePanelContainer::showPanel("people", "panel_people", + LLSD().with("people_panel_tab_name", "blocked_panel").with("blocked_to_select", getAvatarId())); } } @@ -695,6 +696,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL { LLFastTimer _(FTM_APPEND_MESSAGE); bool use_plain_text_chat_history = args["use_plain_text_chat_history"].asBoolean(); + bool square_brackets = false; // square brackets necessary for a system messages llassert(mEditor); if (!mEditor) @@ -702,9 +704,10 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL return; } + bool from_me = chat.mFromID == gAgent.getID(); mEditor->setPlainText(use_plain_text_chat_history); - if (!mEditor->scrolledToEnd() && chat.mFromID != gAgent.getID() && !chat.mFromName.empty()) + if (!mEditor->scrolledToEnd() && !from_me && !chat.mFromName.empty()) { mUnreadChatSources.insert(chat.mFromName); mMoreChatPanel->setVisible(TRUE); @@ -734,16 +737,23 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL } LLColor4 txt_color = LLUIColorTable::instance().getColor("White"); + LLColor4 name_color(txt_color); + LLViewerChat::getChatColor(chat,txt_color); LLFontGL* fontp = LLViewerChat::getChatFont(); std::string font_name = LLFontGL::nameFromFont(fontp); std::string font_size = LLFontGL::sizeFromFont(fontp); - LLStyle::Params style_params; - style_params.color(txt_color); - style_params.readonly_color(txt_color); - style_params.font.name(font_name); - style_params.font.size(font_size); - style_params.font.style(input_append_params.font.style); + + LLStyle::Params body_message_params; + body_message_params.color(txt_color); + body_message_params.readonly_color(txt_color); + body_message_params.font.name(font_name); + body_message_params.font.size(font_size); + body_message_params.font.style(input_append_params.font.style); + + LLStyle::Params name_params(body_message_params); + name_params.color(name_color); + name_params.readonly_color(name_color); std::string prefix = chat.mText.substr(0, 4); @@ -766,29 +776,51 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL if (irc_me || chat.mChatStyle == CHAT_STYLE_IRC) { delimiter = LLStringUtil::null; - style_params.font.style = "ITALIC"; + name_params.font.style = "ITALIC"; } bool message_from_log = chat.mChatStyle == CHAT_STYLE_HISTORY; // We graying out chat history by graying out messages that contains full date in a time string if (message_from_log) { - style_params.color(LLColor4::grey); - style_params.readonly_color(LLColor4::grey); + txt_color = LLColor4::grey; + body_message_params.color(txt_color); + body_message_params.readonly_color(txt_color); + name_params.color(txt_color); + name_params.readonly_color(txt_color); } + bool prependNewLineState = mEditor->getText().size() != 0; + + // compact mode: show a timestamp and name if (use_plain_text_chat_history) { - LLStyle::Params timestamp_style(style_params); + square_brackets = chat.mFromName == SYSTEM_FROM; + + LLStyle::Params timestamp_style(body_message_params); + + // out of the timestamp + if (args["show_time"].asBoolean()) + { if (!message_from_log) { LLColor4 timestamp_color = LLUIColorTable::instance().getColor("ChatTimestampColor"); timestamp_style.color(timestamp_color); timestamp_style.readonly_color(timestamp_color); } - mEditor->appendText("[" + chat.mTimeStr + "] ", mEditor->getLength() != 0, timestamp_style); + mEditor->appendText("[" + chat.mTimeStr + "] ", prependNewLineState, timestamp_style); + prependNewLineState = false; + } + + // out the opening square bracket (if need) + if (square_brackets) + { + mEditor->appendText("[", prependNewLineState, body_message_params); + prependNewLineState = false; + } - if (utf8str_trim(chat.mFromName).size() != 0) + // names showing + if (args["show_names_for_p2p_conv"].asBoolean() && utf8str_trim(chat.mFromName).size() != 0) { // Don't hotlink any messages from the system (e.g. "Second Life:"), so just add those in plain text. if ( chat.mSourceType == CHAT_SOURCE_OBJECT && chat.mFromID.notNull()) @@ -798,32 +830,47 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL // set the link for the object name to be the objectim SLapp // (don't let object names with hyperlinks override our objectim Url) - LLStyle::Params link_params(style_params); + LLStyle::Params link_params(body_message_params); LLColor4 link_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); link_params.color = link_color; link_params.readonly_color = link_color; link_params.is_link = true; link_params.link_href = url; - mEditor->appendText(chat.mFromName + delimiter, - false, link_params); + mEditor->appendText(chat.mFromName + delimiter, prependNewLineState, link_params); + prependNewLineState = false; } else if ( chat.mFromName != SYSTEM_FROM && chat.mFromID.notNull() && !message_from_log) { - LLStyle::Params link_params(style_params); + LLStyle::Params link_params(body_message_params); link_params.overwriteFrom(LLStyleMap::instance().lookupAgent(chat.mFromID)); + if (from_me) + { std::string localized_name; + bool is_localized = LLTrans::findString(localized_name, "AgentNameSubst"); + mEditor->appendText((is_localized? localized_name:"(You)") + delimiter, + prependNewLineState, link_params); + prependNewLineState = false; + } + else + { // Add link to avatar's inspector and delimiter to message. - mEditor->appendText(std::string(link_params.link_href) + delimiter, false, link_params); + mEditor->appendText(std::string(link_params.link_href) + delimiter, + prependNewLineState, link_params); + prependNewLineState = false; + } } else { - mEditor->appendText("<nolink>" + chat.mFromName + "</nolink>" + delimiter, false, style_params); + mEditor->appendText("<nolink>" + chat.mFromName + "</nolink>" + delimiter, + prependNewLineState, body_message_params); + prependNewLineState = false; } } } - else + else // showing timestamp and name in the expanded mode { + prependNewLineState = false; LLView* view = NULL; LLInlineViewSegment::Params p; p.force_newline = true; @@ -844,7 +891,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL } else { - view = getHeader(chat, style_params, args); + view = getHeader(chat, name_params, args); if (mEditor->getLength() == 0) p.top_pad = 0; else @@ -873,41 +920,16 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL mIsLastMessageFromLog = message_from_log; } + // body of the message processing + + // notify processing if (chat.mNotifId.notNull()) { LLNotificationPtr notification = LLNotificationsUtil::find(chat.mNotifId); if (notification != NULL) { LLIMToastNotifyPanel* notify_box = new LLIMToastNotifyPanel( - notification, chat.mSessionID, LLRect::null, !use_plain_text_chat_history); - //we can't set follows in xml since it broke toasts behavior - notify_box->setFollowsLeft(); - notify_box->setFollowsRight(); - notify_box->setFollowsTop(); - - ctrl_list_t ctrls = notify_box->getControlPanel()->getCtrlList(); - S32 offset = 0; - // Children were added by addChild() which uses push_front to insert them into list, - // so to get buttons in correct order reverse iterator is used (EXT-5906) - for (ctrl_list_t::reverse_iterator it = ctrls.rbegin(); it != ctrls.rend(); it++) - { - LLButton * button = dynamic_cast<LLButton*> (*it); - if (button != NULL) - { - button->setOrigin( offset, - button->getRect().mBottom); - button->setLeftHPad(2 * HPAD); - button->setRightHPad(2 * HPAD); - // set zero width before perform autoResize() - button->setRect(LLRect(button->getRect().mLeft, - button->getRect().mTop, button->getRect().mLeft, - button->getRect().mBottom)); - button->setAutoResize(true); - button->autoResize(); - offset += HPAD + button->getRect().getWidth(); - button->setFollowsNone(); - } - } + notification, chat.mSessionID, LLRect::null, !use_plain_text_chat_history, mEditor); //Prepare the rect for the view LLRect target_rect = mEditor->getDocumentView()->getRect(); @@ -924,6 +946,8 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL mEditor->appendWidget(params, "\n", false); } } + + // usual messages showing else { std::string message = irc_me ? chat.mText.substr(3) : chat.mText; @@ -931,7 +955,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL //MESSAGE TEXT PROCESSING //*HACK getting rid of redundant sender names in system notifications sent using sender name (see EXT-5010) - if (use_plain_text_chat_history && gAgentID != chat.mFromID && chat.mFromID.notNull()) + if (use_plain_text_chat_history && !from_me && chat.mFromID.notNull()) { std::string slurl_about = SLURL_APP_AGENT + chat.mFromID.asString() + SLURL_ABOUT; if (message.length() > slurl_about.length() && @@ -946,13 +970,19 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL message = chat.mFromName + message; } - mEditor->appendText(message, FALSE, style_params); + if (square_brackets) + { + message += "]"; + } + + mEditor->appendText(message, prependNewLineState, body_message_params); + prependNewLineState = false; } mEditor->blockUndo(); // automatically scroll to end when receiving chat from myself - if (chat.mFromID == gAgentID) + if (from_me) { mEditor->setCursorAndScrollToEnd(); } diff --git a/indra/newview/llchatitemscontainerctrl.cpp b/indra/newview/llchatitemscontainerctrl.cpp index 9a84280f25..61772b4bb7 100644 --- a/indra/newview/llchatitemscontainerctrl.cpp +++ b/indra/newview/llchatitemscontainerctrl.cpp @@ -35,7 +35,7 @@ #include "llfloaterreg.h" #include "lllocalcliprect.h" #include "lltrans.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llviewercontrol.h" #include "llagentdata.h" @@ -96,8 +96,15 @@ void LLNearbyChatToastPanel::reshape (S32 width, S32 height, BOOL called_from_p { LLPanel::reshape(width, height,called_from_parent); - LLUICtrl* msg_text = getChild<LLUICtrl>("msg_text", false); - LLUICtrl* icon = getChild<LLUICtrl>("avatar_icon", false); + // reshape() may be called from LLView::initFromParams() before the children are created. + // We call findChild() instead of getChild() here to avoid creating dummy controls. + LLUICtrl* msg_text = findChild<LLUICtrl>("msg_text", false); + LLUICtrl* icon = findChild<LLUICtrl>("avatar_icon", false); + + if (!msg_text || !icon) + { + return; + } LLRect msg_text_rect = msg_text->getRect(); LLRect avatar_rect = icon->getRect(); @@ -316,12 +323,12 @@ BOOL LLNearbyChatToastPanel::handleMouseUp (S32 x, S32 y, MASK mask) return TRUE; else { - LLNearbyChatBar::getInstance()->showHistory(); + LLNearbyChat::getInstance()->showHistory(); return FALSE; } } - LLNearbyChatBar::getInstance()->showHistory(); + LLNearbyChat::getInstance()->showHistory(); return LLPanel::handleMouseUp(x,y,mask); } @@ -355,6 +362,8 @@ BOOL LLNearbyChatToastPanel::handleRightMouseDown(S32 x, S32 y, MASK mask) } void LLNearbyChatToastPanel::draw() { + LLPanel::draw(); + if(mIsDirty) { LLAvatarIconCtrl* icon = getChild<LLAvatarIconCtrl>("avatar_icon", false); @@ -372,7 +381,6 @@ void LLNearbyChatToastPanel::draw() } mIsDirty = false; } - LLToastPanelBase::draw(); } diff --git a/indra/newview/llchatitemscontainerctrl.h b/indra/newview/llchatitemscontainerctrl.h index 1d700dcede..89b0c4f37a 100644 --- a/indra/newview/llchatitemscontainerctrl.h +++ b/indra/newview/llchatitemscontainerctrl.h @@ -40,7 +40,7 @@ typedef enum e_show_item_header CHATITEMHEADER_SHOW_BOTH } EShowItemHeader; -class LLNearbyChatToastPanel: public LLToastPanelBase +class LLNearbyChatToastPanel : public LLPanel { protected: LLNearbyChatToastPanel() diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index a661808d1f..17181edffc 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -335,30 +335,15 @@ void LLIMWellChiclet::messageCountChanged(const LLSD& session_data) /* LLNotificationChiclet implementation */ /************************************************************************/ LLNotificationChiclet::LLNotificationChiclet(const Params& p) -: LLSysWellChiclet(p) -, mUreadSystemNotifications(0) +: LLSysWellChiclet(p), + mUreadSystemNotifications(0) { - // connect counter handlers to the signals - connectCounterUpdatersToSignal("notify"); - connectCounterUpdatersToSignal("groupnotify"); - connectCounterUpdatersToSignal("offer"); - + mNotificationChannel.reset(new ChicletNotificationChannel(this)); // ensure that notification well window exists, to synchronously // handle toast add/delete events. LLNotificationWellWindow::getInstance()->setSysWellChiclet(this); } -void LLNotificationChiclet::connectCounterUpdatersToSignal(const std::string& notification_type) -{ - LLNotificationsUI::LLNotificationManager* manager = LLNotificationsUI::LLNotificationManager::getInstance(); - LLNotificationsUI::LLEventHandler* n_handler = manager->getHandlerForNotification(notification_type); - if(n_handler) - { - n_handler->setNewNotificationCallback(boost::bind(&LLNotificationChiclet::incUreadSystemNotifications, this)); - n_handler->setDelNotification(boost::bind(&LLNotificationChiclet::decUreadSystemNotifications, this)); - } -} - void LLNotificationChiclet::onMenuItemClicked(const LLSD& user_data) { std::string action = user_data.asString(); @@ -407,6 +392,18 @@ void LLNotificationChiclet::setCounter(S32 counter) updateWidget(getCounter() == 0); } + +bool LLNotificationChiclet::ChicletNotificationChannel::filterNotification( LLNotificationPtr notification ) +{ + if( !(notification->canLogToIM() && notification->hasFormElements()) + && (!notification->getPayload().has("give_inventory_notification") + || notification->getPayload()["give_inventory_notification"])) + { + return true; + } + return false; +} + ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llchiclet.h b/indra/newview/llchiclet.h index 19683492c2..f51d7b622c 100644 --- a/indra/newview/llchiclet.h +++ b/indra/newview/llchiclet.h @@ -34,6 +34,7 @@ #include "lloutputmonitorctrl.h" #include "llgroupmgr.h" #include "llimview.h" +#include "llnotifications.h" class LLMenuGL; class LLIMFloater; @@ -872,9 +873,10 @@ class LLIMWellChiclet : public LLSysWellChiclet, LLIMSessionObserver { friend class LLUICtrlFactory; public: - virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) {} - virtual void sessionRemoved(const LLUUID& session_id) { messageCountChanged(LLSD()); } - virtual void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) {} + /*virtual*/ void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) {} + /*virtual*/ void sessionVoiceOrIMStarted(const LLUUID& session_id) {}; + /*virtual*/ void sessionRemoved(const LLUUID& session_id) { messageCountChanged(LLSD()); } + /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) {} ~LLIMWellChiclet(); protected: @@ -911,11 +913,35 @@ protected: class LLNotificationChiclet : public LLSysWellChiclet { + LOG_CLASS(LLNotificationChiclet); + friend class LLUICtrlFactory; public: struct Params : public LLInitParam::Block<Params, LLSysWellChiclet::Params>{}; protected: + struct ChicletNotificationChannel : public LLNotificationChannel + { + ChicletNotificationChannel(LLNotificationChiclet* chiclet) + : LLNotificationChannel(LLNotificationChannel::Params().filter(filterNotification).name(chiclet->getSessionId().asString())), + mChiclet(chiclet) + { + // connect counter handlers to the signals + connectToChannel("Group Notifications"); + connectToChannel("Offer"); + connectToChannel("Notifications"); + } + + static bool filterNotification(LLNotificationPtr notify); + // connect counter updaters to the corresponding signals + /*virtual*/ void onAdd(LLNotificationPtr p) { mChiclet->setCounter(++mChiclet->mUreadSystemNotifications); } + /*virtual*/ void onDelete(LLNotificationPtr p) { mChiclet->setCounter(--mChiclet->mUreadSystemNotifications); } + + LLNotificationChiclet* const mChiclet; + }; + + boost::scoped_ptr<ChicletNotificationChannel> mNotificationChannel; + LLNotificationChiclet(const Params& p); /** @@ -933,12 +959,6 @@ protected: */ /*virtual*/ void createMenu(); - // connect counter updaters to the corresponding signals - void connectCounterUpdatersToSignal(const std::string& notification_type); - - // methods for updating a number of unread System notifications - void incUreadSystemNotifications() { setCounter(++mUreadSystemNotifications); } - void decUreadSystemNotifications() { setCounter(--mUreadSystemNotifications); } /*virtual*/ void setCounter(S32 counter); S32 mUreadSystemNotifications; }; diff --git a/indra/newview/llchicletbar.cpp b/indra/newview/llchicletbar.cpp index f1bc51fbe7..39f5d0b8f6 100644 --- a/indra/newview/llchicletbar.cpp +++ b/indra/newview/llchicletbar.cpp @@ -57,19 +57,11 @@ LLChicletBar::LLChicletBar(const LLSD&) : mChicletPanel(NULL), mToolbarStack(NULL) { - // Firstly add our self to IMSession observers, so we catch session events - // before chiclets do that. - LLIMMgr::getInstance()->addSessionObserver(this); - buildFromFile("panel_chiclet_bar.xml"); } LLChicletBar::~LLChicletBar() { - if (!LLSingleton<LLIMMgr>::destroyed()) - { - LLIMMgr::getInstance()->removeSessionObserver(this); - } } LLIMChiclet* LLChicletBar::createIMChiclet(const LLUUID& session_id) @@ -102,6 +94,13 @@ void LLChicletBar::sessionAdded(const LLUUID& session_id, const std::string& nam // no need to spawn chiclets for participants in P2P calls called through Avaline if (session->isP2P() && session->isOtherParticipantAvaline()) return; + // Do not spawn chiclet when using the new multitab conversation UI + if (LLIMConversation::isChatMultiTab()) + { + LLIMFloater::addToHost(session_id); + return; + } + if (getChicletPanel()->findChiclet<LLChiclet>(session_id)) return; LLIMChiclet* chiclet = createIMChiclet(session_id); @@ -109,7 +108,7 @@ void LLChicletBar::sessionAdded(const LLUUID& session_id, const std::string& nam { chiclet->setIMSessionName(name); chiclet->setOtherParticipantId(other_participant_id); - + LLIMFloater::onIMChicletCreated(session_id); } @@ -125,10 +124,12 @@ void LLChicletBar::sessionRemoved(const LLUUID& session_id) if(getChicletPanel()) { // IM floater should be closed when session removed and associated chiclet closed - LLIMFloater* iMfloater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); - if (iMfloater != NULL) + LLIMFloater* im_floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); + if (im_floater != NULL && !im_floater->getStartConferenceInSameFloater()) { - iMfloater->closeFloater(); + // Close the IM floater only if we are not planning to close the P2P chat + // and start a new conference in the same floater. + im_floater->closeFloater(); } getChicletPanel()->removeChiclet(session_id); diff --git a/indra/newview/llchicletbar.h b/indra/newview/llchicletbar.h index 1427bf95e0..7d0d904810 100644 --- a/indra/newview/llchicletbar.h +++ b/indra/newview/llchicletbar.h @@ -50,9 +50,10 @@ public: LLChicletPanel* getChicletPanel() { return mChicletPanel; } // LLIMSessionObserver observe triggers - virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); - virtual void sessionRemoved(const LLUUID& session_id); - void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); + /*virtual*/ void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + /*virtual*/ void sessionVoiceOrIMStarted(const LLUUID& session_id) {}; + /*virtual*/ void sessionRemoved(const LLUUID& session_id); + /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); S32 getTotalUnreadIMCount(); diff --git a/indra/newview/llconversationlog.cpp b/indra/newview/llconversationlog.cpp new file mode 100644 index 0000000000..137b97326c --- /dev/null +++ b/indra/newview/llconversationlog.cpp @@ -0,0 +1,354 @@ +/** + * @file llconversationlog.h + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llagent.h" +#include "llconversationlog.h" +#include "lltrans.h" + +struct Conversation_params +{ + Conversation_params(time_t time) + : mTime(time), + mTimestamp(LLConversation::createTimestamp(time)) + {} + + time_t mTime; + std::string mTimestamp; + SessionType mConversationType; + std::string mConversationName; + std::string mHistoryFileName; + LLUUID mSessionID; + LLUUID mParticipantID; + bool mIsVoice; + bool mHasOfflineIMs; +}; + +/************************************************************************/ +/* LLConversation implementation */ +/************************************************************************/ + +LLConversation::LLConversation(const Conversation_params& params) +: mTime(params.mTime), + mTimestamp(params.mTimestamp), + mConversationType(params.mConversationType), + mConversationName(params.mConversationName), + mHistoryFileName(params.mHistoryFileName), + mSessionID(params.mSessionID), + mParticipantID(params.mParticipantID), + mIsVoice(params.mIsVoice), + mHasOfflineIMs(params.mHasOfflineIMs) +{ + setListenIMFloaterOpened(); +} + +LLConversation::LLConversation(const LLIMModel::LLIMSession& session) +: mTime(time_corrected()), + mTimestamp(createTimestamp(mTime)), + mConversationType(session.mSessionType), + mConversationName(session.mName), + mHistoryFileName(session.mHistoryFileName), + mSessionID(session.mSessionID), + mParticipantID(session.mOtherParticipantID), + mIsVoice(session.mStartedAsIMCall), + mHasOfflineIMs(session.mHasOfflineMessage) +{ + setListenIMFloaterOpened(); +} + +LLConversation::LLConversation(const LLConversation& conversation) +{ + mTime = conversation.getTime(); + mTimestamp = conversation.getTimestamp(); + mConversationType = conversation.getConversationType(); + mConversationName = conversation.getConversationName(); + mHistoryFileName = conversation.getHistoryFileName(); + mSessionID = conversation.getSessionID(); + mParticipantID = conversation.getParticipantID(); + mIsVoice = conversation.isVoice(); + mHasOfflineIMs = conversation.hasOfflineMessages(); + + setListenIMFloaterOpened(); +} + +LLConversation::~LLConversation() +{ + mIMFloaterShowedConnection.disconnect(); +} + +void LLConversation::onIMFloaterShown(const LLUUID& session_id) +{ + if (mSessionID == session_id) + { + mHasOfflineIMs = false; + } +} + +// static +const std::string LLConversation::createTimestamp(const time_t& utc_time) +{ + std::string timeStr; + LLSD substitution; + substitution["datetime"] = (S32) utc_time; + + timeStr = "["+LLTrans::getString ("TimeMonth")+"]/[" + +LLTrans::getString ("TimeDay")+"]/[" + +LLTrans::getString ("TimeYear")+"] [" + +LLTrans::getString ("TimeHour")+"]:[" + +LLTrans::getString ("TimeMin")+"]"; + + + LLStringUtil::format (timeStr, substitution); + return timeStr; +} + +void LLConversation::setListenIMFloaterOpened() +{ + LLIMFloater* floater = LLIMFloater::findInstance(mSessionID); + + bool has_offline_ims = !mIsVoice && mHasOfflineIMs; + bool ims_are_read = LLIMFloater::isVisible(floater) && floater->hasFocus(); + + // we don't need to listen for im floater with this conversation is opened + // if floater is already opened or this conversation doesn't have unread offline messages + if (has_offline_ims && !ims_are_read) + { + mIMFloaterShowedConnection = LLIMFloater::setIMFloaterShowedCallback(boost::bind(&LLConversation::onIMFloaterShown, this, _1)); + } +} +/************************************************************************/ +/* LLConversationLogFriendObserver implementation */ +/************************************************************************/ + +// Note : An LLSingleton like LLConversationLog cannot be an LLFriendObserver +// at the same time. +// This is because avatar observers are deleted by the observed object which +// conflicts with the way LLSingleton are deleted. + +class LLConversationLogFriendObserver : public LLFriendObserver +{ +public: + LLConversationLogFriendObserver() {} + virtual ~LLConversationLogFriendObserver() {} + virtual void changed(U32 mask); +}; + +void LLConversationLogFriendObserver::changed(U32 mask) +{ + if (mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE)) + { + LLConversationLog::instance().notifyObservers(); + } +} + +/************************************************************************/ +/* LLConversationLog implementation */ +/************************************************************************/ + +LLConversationLog::LLConversationLog() +{ + loadFromFile(getFileName()); + + LLIMMgr::instance().addSessionObserver(this); + + mFriendObserver = new LLConversationLogFriendObserver; + LLAvatarTracker::instance().addObserver(mFriendObserver); +} +void LLConversationLog::logConversation(const LLConversation& conversation) +{ + mConversations.push_back(conversation); + notifyObservers(); +} + +void LLConversationLog::removeConversation(const LLConversation& conversation) +{ + conversations_vec_t::iterator conv_it = mConversations.begin(); + for(; conv_it != mConversations.end(); ++conv_it) + { + if (conv_it->getSessionID() == conversation.getSessionID() && conv_it->getTime() == conversation.getTime()) + { + mConversations.erase(conv_it); + notifyObservers(); + return; + } + } +} + +const LLConversation* LLConversationLog::getConversation(const LLUUID& session_id) +{ + conversations_vec_t::const_iterator conv_it = mConversations.begin(); + for(; conv_it != mConversations.end(); ++conv_it) + { + if (conv_it->getSessionID() == session_id) + { + return &*conv_it; + } + } + + return NULL; +} + +void LLConversationLog::addObserver(LLConversationLogObserver* observer) +{ + mObservers.insert(observer); +} + +void LLConversationLog::removeObserver(LLConversationLogObserver* observer) +{ + mObservers.erase(observer); +} + +void LLConversationLog::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) +{ + LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id); + if (session) + { + LLConversation conversation(*session); + LLConversationLog::instance().logConversation(conversation); + } +} + +void LLConversationLog::cache() +{ + saveToFile(getFileName()); +} + +std::string LLConversationLog::getFileName() +{ + std::string agent_id_string; + gAgent.getID().toString(agent_id_string); + + return gDirUtilp->getExpandedFilename(LL_PATH_CACHE, agent_id_string) + ".call_log"; +} + +bool LLConversationLog::saveToFile(const std::string& filename) +{ + if(!filename.size()) + { + llwarns << "Call log list filename is empty!" << llendl; + return false; + } + + LLFILE* fp = LLFile::fopen(filename, "wb"); + if (!fp) + { + llwarns << "Couldn't open call log list" << filename << llendl; + return false; + } + + std::string participant_id; + std::string conversation_id; + + conversations_vec_t::const_iterator conv_it = mConversations.begin(); + for (; conv_it != mConversations.end(); ++conv_it) + { + conv_it->getSessionID().toString(conversation_id); + conv_it->getParticipantID().toString(participant_id); + + // examples of two file entries + // [1343221177] 0 1 0 John Doe| 7e4ec5be-783f-49f5-71dz-16c58c64c145 4ec62a74-c246-0d25-2af6-846beac2aa55 john.doe| + // [1343222639] 2 0 0 Ad-hoc Conference| c3g67c89-c479-4c97-b21d-32869bcfe8rc 68f1c33e-4135-3e3e-a897-8c9b23115c09 Ad-hoc Conference hash597394a0-9982-766d-27b8-c75560213b9a| + + fprintf(fp, "[%d] %d %d %d %s| %s %s %s|\n", + (S32)conv_it->getTime(), + (S32)conv_it->getConversationType(), + (S32)conv_it->isVoice(), + (S32)conv_it->hasOfflineMessages(), + conv_it->getConversationName().c_str(), + participant_id.c_str(), + conversation_id.c_str(), + conv_it->getHistoryFileName().c_str()); + } + fclose(fp); + return true; +} +bool LLConversationLog::loadFromFile(const std::string& filename) +{ + if(!filename.size()) + { + llwarns << "Call log list filename is empty!" << llendl; + return false; + } + + LLFILE* fp = LLFile::fopen(filename, "rb"); + if (!fp) + { + llwarns << "Couldn't open call log list" << filename << llendl; + return false; + } + + char buffer[MAX_STRING]; + char conv_name_buffer[MAX_STRING]; + char part_id_buffer[MAX_STRING]; + char conv_id_buffer[MAX_STRING]; + char history_file_name[MAX_STRING]; + int is_voice; + int has_offline_ims; + int stype; + S32 time; + + while (!feof(fp) && fgets(buffer, MAX_STRING, fp)) + { + conv_name_buffer[0] = '\0'; + part_id_buffer[0] = '\0'; + conv_id_buffer[0] = '\0'; + + sscanf(buffer, "[%d] %d %d %d %[^|]| %s %s %[^|]|", + &time, + &stype, + &is_voice, + &has_offline_ims, + conv_name_buffer, + part_id_buffer, + conv_id_buffer, + history_file_name); + + Conversation_params params(time); + params.mConversationType = (SessionType)stype; + params.mIsVoice = is_voice; + params.mHasOfflineIMs = has_offline_ims; + params.mConversationName = std::string(conv_name_buffer); + params.mParticipantID = LLUUID(part_id_buffer); + params.mSessionID = LLUUID(conv_id_buffer); + params.mHistoryFileName = std::string(history_file_name); + + LLConversation conversation(params); + mConversations.push_back(conversation); + } + fclose(fp); + + notifyObservers(); + return true; +} + +void LLConversationLog::notifyObservers() +{ + std::set<LLConversationLogObserver*>::const_iterator iter = mObservers.begin(); + for (; iter != mObservers.end(); ++iter) + { + (*iter)->changed(); + } +} diff --git a/indra/newview/llconversationlog.h b/indra/newview/llconversationlog.h new file mode 100644 index 0000000000..a7457d55e3 --- /dev/null +++ b/indra/newview/llconversationlog.h @@ -0,0 +1,165 @@ +/** + * @file llconversationlog.h + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLCONVERSATIONLOG_H_ +#define LLCONVERSATIONLOG_H_ + +#include "llcallingcard.h" +#include "llimfloater.h" +#include "llimview.h" + +class LLConversationLogObserver; +struct Conversation_params; + +typedef LLIMModel::LLIMSession::SType SessionType; + +/* + * This class represents a particular session(conversation) of any type(im/voice/p2p/group/...) by storing some of session's data. + * Each LLConversation object has a corresponding visual representation in a form of LLConversationLogListItem. + */ +class LLConversation +{ +public: + + LLConversation(const Conversation_params& params); + LLConversation(const LLIMModel::LLIMSession& session); + LLConversation(const LLConversation& conversation); + + ~LLConversation(); + + const SessionType& getConversationType() const { return mConversationType; } + const std::string& getConversationName() const { return mConversationName; } + const std::string& getHistoryFileName() const { return mHistoryFileName; } + const LLUUID& getSessionID() const { return mSessionID; } + const LLUUID& getParticipantID() const { return mParticipantID; } + const std::string& getTimestamp() const { return mTimestamp; } + const time_t& getTime() const { return mTime; } + bool isVoice() const { return mIsVoice; } + bool hasOfflineMessages() const { return mHasOfflineIMs; } + + /* + * Resets flag of unread offline message to false when im floater with this conversation is opened. + */ + void onIMFloaterShown(const LLUUID& session_id); + + /* + * returns string representation(in form of: mm/dd/yyyy hh:mm) of time when conversation was started + */ + static const std::string createTimestamp(const time_t& utc_time); + +private: + + /* + * If conversation has unread offline messages sets callback for opening LLIMFloater + * with this conversation. + */ + void setListenIMFloaterOpened(); + + boost::signals2::connection mIMFloaterShowedConnection; + + time_t mTime; // start time of conversation + SessionType mConversationType; + std::string mConversationName; + std::string mHistoryFileName; + LLUUID mSessionID; + LLUUID mParticipantID; + bool mIsVoice; + bool mHasOfflineIMs; + std::string mTimestamp; // conversation start time in form of: mm/dd/yyyy hh:mm +}; + +/** + * LLConversationLog stores all agent's conversations. + * This class is responsible for creating and storing LLConversation objects when im or voice session starts. + * Also this class saves/retrieves conversations to/from file. + * + * Also please note that it may be several conversations with the same sessionID stored in the conversation log. + * To distinguish two conversations with the same sessionID it's also needed to compare their creation date. + */ + +class LLConversationLog : public LLSingleton<LLConversationLog>, LLIMSessionObserver +{ + friend class LLSingleton<LLConversationLog>; +public: + + /** + * adds conversation to the conversation list and notifies observers + */ + void logConversation(const LLConversation& conversation); + void removeConversation(const LLConversation& conversation); + + /** + * Returns first conversation with matched session_id + */ + const LLConversation* getConversation(const LLUUID& session_id); + + void addObserver(LLConversationLogObserver* observer); + void removeObserver(LLConversationLogObserver* observer); + + const std::vector<LLConversation>& getConversations() { return mConversations; } + + // LLIMSessionObserver triggers + virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + virtual void sessionVoiceOrIMStarted(const LLUUID& session_id){} // Stub + virtual void sessionRemoved(const LLUUID& session_id){} // Stub + virtual void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id){} // Stub + + // Triggered by LLFriendObserver change + void notifyObservers(); + + /** + * public method which is called on viewer exit to save conversation log + */ + void cache(); + +private: + + LLConversationLog(); + + /** + * constructs file name in which conversations log will be saved + * file name template: agentID.call_log. + * For example: a086icaa-782d-88d0-ae29-987a55c99sss.call_log + */ + std::string getFileName(); + + bool saveToFile(const std::string& filename); + bool loadFromFile(const std::string& filename); + + typedef std::vector<LLConversation> conversations_vec_t; + std::vector<LLConversation> mConversations; + std::set<LLConversationLogObserver*> mObservers; + + LLFriendObserver* mFriendObserver; // Observer of the LLAvatarTracker instance +}; + +class LLConversationLogObserver +{ +public: + virtual ~LLConversationLogObserver(){} + virtual void changed() = 0; +}; + +#endif /* LLCONVERSATIONLOG_H_ */ diff --git a/indra/newview/llconversationloglist.cpp b/indra/newview/llconversationloglist.cpp new file mode 100644 index 0000000000..0433719a89 --- /dev/null +++ b/indra/newview/llconversationloglist.cpp @@ -0,0 +1,422 @@ +/** + * @file llconversationloglist.cpp + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llavataractions.h" +#include "llagent.h" +#include "llfloaterreg.h" +#include "llfloaterconversationpreview.h" +#include "llgroupactions.h" +#include "llconversationloglist.h" +#include "llconversationloglistitem.h" +#include "llviewermenu.h" + +static LLDefaultChildRegistry::Register<LLConversationLogList> r("conversation_log_list"); + +static LLConversationLogListNameComparator NAME_COMPARATOR; +static LLConversationLogListDateComparator DATE_COMPARATOR; + +LLConversationLogList::LLConversationLogList(const Params& p) +: LLFlatListViewEx(p), + mIsDirty(true) +{ + LLConversationLog::instance().addObserver(this); + + // Set up context menu. + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar check_registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + + registrar.add ("Calllog.Action", boost::bind(&LLConversationLogList::onCustomAction, this, _2)); + check_registrar.add ("Calllog.Check", boost::bind(&LLConversationLogList::isActionChecked,this, _2)); + enable_registrar.add("Calllog.Enable", boost::bind(&LLConversationLogList::isActionEnabled,this, _2)); + + LLToggleableMenu* context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>( + "menu_conversation_log_gear.xml", + gMenuHolder, + LLViewerMenuHolderGL::child_registry_t::instance()); + if(context_menu) + { + mContextMenu = context_menu->getHandle(); + } + + mIsFriendsOnTop = gSavedSettings.getBOOL("SortFriendsFirst"); +} + +LLConversationLogList::~LLConversationLogList() +{ + if (mContextMenu.get()) + { + mContextMenu.get()->die(); + } + + LLConversationLog::instance().removeObserver(this); +} + +void LLConversationLogList::draw() +{ + if (mIsDirty) + { + refresh(); + } + LLFlatListViewEx::draw(); +} + +BOOL LLConversationLogList::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + BOOL handled = LLUICtrl::handleRightMouseDown(x, y, mask); + + LLToggleableMenu* context_menu = mContextMenu.get(); + { + context_menu->buildDrawLabels(); + if (context_menu && size()) + context_menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, context_menu, x, y); + } + + return handled; +} + +void LLConversationLogList::setNameFilter(const std::string& filter) +{ + std::string filter_upper = filter; + LLStringUtil::toUpper(filter_upper); + if (mNameFilter != filter_upper) + { + mNameFilter = filter_upper; + setDirty(); + } +} + +bool LLConversationLogList::findInsensitive(std::string haystack, const std::string& needle_upper) +{ + LLStringUtil::toUpper(haystack); + return haystack.find(needle_upper) != std::string::npos; +} + +void LLConversationLogList::sortByName() +{ + setComparator(&NAME_COMPARATOR); + sort(); +} + +void LLConversationLogList::sortByDate() +{ + setComparator(&DATE_COMPARATOR); + sort(); +} + +void LLConversationLogList::toggleSortFriendsOnTop() +{ + mIsFriendsOnTop = !mIsFriendsOnTop; + gSavedSettings.setBOOL("SortFriendsFirst", mIsFriendsOnTop); + sort(); +} + +void LLConversationLogList::changed() +{ + refresh(); +} + +void LLConversationLogList::addNewItem(const LLConversation* conversation) +{ + LLConversationLogListItem* item = new LLConversationLogListItem(&*conversation); + if (!mNameFilter.empty()) + { + item->highlightNameDate(mNameFilter); + } + addItem(item, conversation->getSessionID(), ADD_TOP); +} + +void LLConversationLogList::refresh() +{ + rebuildList(); + sort(); + + mIsDirty = false; +} + +void LLConversationLogList::rebuildList() +{ + clear(); + + bool have_filter = !mNameFilter.empty(); + + const std::vector<LLConversation>& conversations = LLConversationLog::instance().getConversations(); + std::vector<LLConversation>::const_iterator iter = conversations.begin(); + + for (; iter != conversations.end(); ++iter) + { + bool not_found = have_filter && !findInsensitive(iter->getConversationName(), mNameFilter) && !findInsensitive(iter->getTimestamp(), mNameFilter); + if (not_found) + continue; + + addNewItem(&*iter); + } +} + +void LLConversationLogList::onCustomAction(const LLSD& userdata) +{ + const std::string command_name = userdata.asString(); + const LLUUID& selected_id = getSelectedConversation()->getParticipantID(); + LLIMModel::LLIMSession::SType stype = getSelectedSessionType(); + + if ("im" == command_name) + { + switch (stype) + { + case LLIMModel::LLIMSession::P2P_SESSION: + LLAvatarActions::startIM(selected_id); + break; + + case LLIMModel::LLIMSession::GROUP_SESSION: + LLGroupActions::startIM(selected_id); + break; + + default: + break; + } + } + else if ("call" == command_name) + { + switch (stype) + { + case LLIMModel::LLIMSession::P2P_SESSION: + LLAvatarActions::startCall(selected_id); + break; + + case LLIMModel::LLIMSession::GROUP_SESSION: + LLGroupActions::startCall(selected_id); + break; + + default: + break; + } + } + else if ("view_profile" == command_name) + { + switch (stype) + { + case LLIMModel::LLIMSession::P2P_SESSION: + LLAvatarActions::showProfile(selected_id); + break; + + case LLIMModel::LLIMSession::GROUP_SESSION: + LLGroupActions::show(selected_id); + break; + + default: + break; + } + } + else if ("chat_history" == command_name) + { + const LLUUID& session_id = getSelectedConversation()->getSessionID(); + LLFloaterReg::showInstance("preview_conversation", session_id, true); + } + else if ("offer_teleport" == command_name) + { + LLAvatarActions::offerTeleport(selected_id); + } + else if("add_rem_friend" == command_name) + { + if (LLAvatarActions::isFriend(selected_id)) + { + LLAvatarActions::removeFriendDialog(selected_id); + } + else + { + LLAvatarActions::requestFriendshipDialog(selected_id); + } + } + else if ("invite_to_group" == command_name) + { + LLAvatarActions::inviteToGroup(selected_id); + } + else if ("show_on_map" == command_name) + { + LLAvatarActions::showOnMap(selected_id); + } + else if ("share" == command_name) + { + LLAvatarActions::share(selected_id); + } + else if ("pay" == command_name) + { + LLAvatarActions::pay(selected_id); + } + else if ("block" == command_name) + { + LLAvatarActions::toggleBlock(selected_id); + } +} + +bool LLConversationLogList::isActionEnabled(const LLSD& userdata) +{ + if (numSelected() != 1) + { + return false; + } + + const std::string command_name = userdata.asString(); + + LLIMModel::LLIMSession::SType stype = getSelectedSessionType(); + const LLUUID& selected_id = getSelectedConversation()->getParticipantID(); + + bool is_p2p = LLIMModel::LLIMSession::P2P_SESSION == stype; + bool is_group = LLIMModel::LLIMSession::GROUP_SESSION == stype; + + if ("can_im" == command_name || "can_view_profile" == command_name) + { + return is_p2p || is_group; + } + else if ("can_view_chat_history" == command_name) + { + return true; + } + else if ("can_call" == command_name) + { + return (is_p2p || is_group) && LLAvatarActions::canCall(); + } + else if ("add_rem_friend" == command_name || + "can_invite_to_group" == command_name || + "can_share" == command_name || + "can_block" == command_name || + "can_pay" == command_name) + { + return is_p2p; + } + else if("can_offer_teleport" == command_name) + { + return is_p2p && LLAvatarActions::canOfferTeleport(selected_id); + } + else if ("can_show_on_map") + { + return is_p2p && ((LLAvatarTracker::instance().isBuddyOnline(selected_id) && is_agent_mappable(selected_id)) || gAgent.isGodlike()); + } + + return false; +} + +bool LLConversationLogList::isActionChecked(const LLSD& userdata) +{ + const std::string command_name = userdata.asString(); + + const LLUUID& selected_id = getSelectedConversation()->getParticipantID(); + bool is_p2p = LLIMModel::LLIMSession::P2P_SESSION == getSelectedSessionType(); + + if ("is_blocked" == command_name) + { + return is_p2p && LLAvatarActions::isBlocked(selected_id); + } + else if ("is_friend" == command_name) + { + return is_p2p && LLAvatarActions::isFriend(selected_id); + } + + return false; +} + +LLIMModel::LLIMSession::SType LLConversationLogList::getSelectedSessionType() +{ + const LLConversationLogListItem* item = getSelectedConversationPanel(); + + if (item) + { + return item->getConversation()->getConversationType(); + } + + return LLIMModel::LLIMSession::NONE_SESSION; +} + +const LLConversationLogListItem* LLConversationLogList::getSelectedConversationPanel() +{ + LLPanel* panel = LLFlatListViewEx::getSelectedItem(); + LLConversationLogListItem* conv_panel = dynamic_cast<LLConversationLogListItem*>(panel); + + return conv_panel; +} + +const LLConversation* LLConversationLogList::getSelectedConversation() +{ + const LLConversationLogListItem* panel = getSelectedConversationPanel(); + + if (panel) + { + return panel->getConversation(); + } + + return NULL; +} + +bool LLConversationLogListItemComparator::compare(const LLPanel* item1, const LLPanel* item2) const +{ + const LLConversationLogListItem* conversation_item1 = dynamic_cast<const LLConversationLogListItem*>(item1); + const LLConversationLogListItem* conversation_item2 = dynamic_cast<const LLConversationLogListItem*>(item2); + + if (!conversation_item1 || !conversation_item2) + { + llerror("conversation_item1 and conversation_item2 cannot be null", 0); + return true; + } + + return doCompare(conversation_item1, conversation_item2); +} + +bool LLConversationLogListNameComparator::doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const +{ + std::string name1 = conversation1->getConversation()->getConversationName(); + std::string name2 = conversation2->getConversation()->getConversationName(); + const LLUUID& id1 = conversation1->getConversation()->getParticipantID(); + const LLUUID& id2 = conversation2->getConversation()->getParticipantID(); + + LLStringUtil::toUpper(name1); + LLStringUtil::toUpper(name2); + + bool friends_first = gSavedSettings.getBOOL("SortFriendsFirst"); + if (friends_first && (LLAvatarActions::isFriend(id1) ^ LLAvatarActions::isFriend(id2))) + { + return LLAvatarActions::isFriend(id1); + } + + return name1 < name2; +} + +bool LLConversationLogListDateComparator::doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const +{ + time_t date1 = conversation1->getConversation()->getTime(); + time_t date2 = conversation2->getConversation()->getTime(); + const LLUUID& id1 = conversation1->getConversation()->getParticipantID(); + const LLUUID& id2 = conversation2->getConversation()->getParticipantID(); + + bool friends_first = gSavedSettings.getBOOL("SortFriendsFirst"); + if (friends_first && (LLAvatarActions::isFriend(id1) ^ LLAvatarActions::isFriend(id2))) + { + return LLAvatarActions::isFriend(id1); + } + + return date1 > date2; +} diff --git a/indra/newview/llconversationloglist.h b/indra/newview/llconversationloglist.h new file mode 100644 index 0000000000..dff34a74c6 --- /dev/null +++ b/indra/newview/llconversationloglist.h @@ -0,0 +1,143 @@ +/** + * @file llconversationloglist.h + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLCONVERSATIONLOGLIST_H_ +#define LLCONVERSATIONLOGLIST_H_ + +#include "llconversationlog.h" +#include "llflatlistview.h" +#include "lltoggleablemenu.h" + +class LLConversationLogListItem; + +/** + * List of all agent's conversations. I.e. history of conversations. + * This list represents contents of the LLConversationLog. + * Each change in LLConversationLog leads to rebuilding this list, so + * it's always in actual state. + */ + +class LLConversationLogList: public LLFlatListViewEx, public LLConversationLogObserver +{ + LOG_CLASS(LLConversationLogList); +public: + struct Params : public LLInitParam::Block<Params, LLFlatListViewEx::Params> + { + Params(){}; + }; + + LLConversationLogList(const Params& p); + virtual ~LLConversationLogList(); + + virtual void draw(); + + virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + + LLToggleableMenu* getContextMenu() const { return mContextMenu.get(); } + + void addNewItem(const LLConversation* conversation); + void setNameFilter(const std::string& filter); + void sortByName(); + void sortByDate(); + void toggleSortFriendsOnTop(); + bool getSortFriendsOnTop() const { return mIsFriendsOnTop; } + + /** + * Changes from LLConversationLogObserver + */ + virtual void changed(); + +private: + + void setDirty(bool dirty = true) { mIsDirty = dirty; } + void refresh(); + + /** + * Clears list and re-adds items from LLConverstationLog + * If filter is not empty re-adds items which match the filter + */ + void rebuildList(); + + bool findInsensitive(std::string haystack, const std::string& needle_upper); + + void onCustomAction (const LLSD& userdata); + bool isActionEnabled(const LLSD& userdata); + bool isActionChecked(const LLSD& userdata); + + LLIMModel::LLIMSession::SType getSelectedSessionType(); + const LLConversationLogListItem* getSelectedConversationPanel(); + const LLConversation* getSelectedConversation(); + + LLHandle<LLToggleableMenu> mContextMenu; + bool mIsDirty; + bool mIsFriendsOnTop; + std::string mNameFilter; +}; + +/** + * Abstract comparator for ConversationLogList items + */ +class LLConversationLogListItemComparator : public LLFlatListView::ItemComparator +{ + LOG_CLASS(LLConversationLogListItemComparator); + +public: + LLConversationLogListItemComparator() {}; + virtual ~LLConversationLogListItemComparator() {}; + + virtual bool compare(const LLPanel* item1, const LLPanel* item2) const; + +protected: + + virtual bool doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const = 0; +}; + +class LLConversationLogListNameComparator : public LLConversationLogListItemComparator +{ + LOG_CLASS(LLConversationLogListNameComparator); + +public: + LLConversationLogListNameComparator() {}; + virtual ~LLConversationLogListNameComparator() {}; + +protected: + + virtual bool doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const; +}; + +class LLConversationLogListDateComparator : public LLConversationLogListItemComparator +{ + LOG_CLASS(LLConversationLogListDateComparator); + +public: + LLConversationLogListDateComparator() {}; + virtual ~LLConversationLogListDateComparator() {}; + +protected: + + virtual bool doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const; +}; + +#endif /* LLCONVERSATIONLOGLIST_H_ */ diff --git a/indra/newview/llconversationloglistitem.cpp b/indra/newview/llconversationloglistitem.cpp new file mode 100644 index 0000000000..fc2e757864 --- /dev/null +++ b/indra/newview/llconversationloglistitem.cpp @@ -0,0 +1,157 @@ +/** + * @file llconversationloglistitem.cpp + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +// llui +#include "lliconctrl.h" +#include "lltextbox.h" +#include "lltextutil.h" + +// newview +#include "llavatariconctrl.h" +#include "llconversationlog.h" +#include "llconversationloglistitem.h" +#include "llgroupiconctrl.h" +#include "llinventoryicon.h" + +LLConversationLogListItem::LLConversationLogListItem(const LLConversation* conversation) +: LLPanel(), + mConversation(conversation), + mConversationName(NULL), + mConversationDate(NULL) +{ + buildFromFile("panel_conversation_log_list_item.xml"); + + LLIMFloater* floater = LLIMFloater::findInstance(mConversation->getSessionID()); + + bool has_offline_ims = !mConversation->isVoice() && mConversation->hasOfflineMessages(); + bool ims_are_read = LLIMFloater::isVisible(floater) && floater->hasFocus(); + + if (has_offline_ims && !ims_are_read) + { + mIMFloaterShowedConnection = LLIMFloater::setIMFloaterShowedCallback(boost::bind(&LLConversationLogListItem::onIMFloaterShown, this, _1)); + } +} + +LLConversationLogListItem::~LLConversationLogListItem() +{ + mIMFloaterShowedConnection.disconnect(); +} + +BOOL LLConversationLogListItem::postBuild() +{ + initIcons(); + + // set conversation name + mConversationName = getChild<LLTextBox>("conversation_name"); + mConversationName->setValue(mConversation->getConversationName()); + + // set conversation date and time + mConversationDate = getChild<LLTextBox>("date_time"); + mConversationDate->setValue(mConversation->getTimestamp()); + + getChild<LLButton>("delete_btn")->setClickedCallback(boost::bind(&LLConversationLogListItem::onRemoveBtnClicked, this)); + + return TRUE; +} + +void LLConversationLogListItem::initIcons() +{ + switch (mConversation->getConversationType()) + { + case LLIMModel::LLIMSession::P2P_SESSION: + case LLIMModel::LLIMSession::ADHOC_SESSION: + { + LLAvatarIconCtrl* avatar_icon = getChild<LLAvatarIconCtrl>("avatar_icon"); + avatar_icon->setVisible(TRUE); + avatar_icon->setValue(mConversation->getParticipantID()); + break; + } + case LLIMModel::LLIMSession::GROUP_SESSION: + { + LLGroupIconCtrl* group_icon = getChild<LLGroupIconCtrl>("group_icon"); + group_icon->setVisible(TRUE); + group_icon->setValue(mConversation->getSessionID()); + break; + } + default: + break; + } + + if (mConversation->isVoice()) + { + getChild<LLIconCtrl>("voice_session_icon")->setVisible(TRUE); + } + else + { + if (mConversation->hasOfflineMessages()) + { + getChild<LLIconCtrl>("unread_ims_icon")->setVisible(TRUE); + } + } +} + +void LLConversationLogListItem::onMouseEnter(S32 x, S32 y, MASK mask) +{ + getChildView("hovered_icon")->setVisible(true); + LLPanel::onMouseEnter(x, y, mask); +} + +void LLConversationLogListItem::onMouseLeave(S32 x, S32 y, MASK mask) +{ + getChildView("hovered_icon")->setVisible(false); + LLPanel::onMouseLeave(x, y, mask); +} + +void LLConversationLogListItem::setValue(const LLSD& value) +{ + if (!value.isMap() || !value.has("selected")) + { + return; + } + + getChildView("selected_icon")->setVisible(value["selected"]); +} + +void LLConversationLogListItem::onIMFloaterShown(const LLUUID& session_id) +{ + if (mConversation->getSessionID() == session_id) + { + getChild<LLIconCtrl>("unread_ims_icon")->setVisible(FALSE); + } +} + +void LLConversationLogListItem::onRemoveBtnClicked() +{ + LLConversationLog::instance().removeConversation(*mConversation); +} + +void LLConversationLogListItem::highlightNameDate(const std::string& highlited_text) +{ + LLStyle::Params params; + LLTextUtil::textboxSetHighlightedVal(mConversationName, params, mConversation->getConversationName(), highlited_text); + LLTextUtil::textboxSetHighlightedVal(mConversationDate, params, mConversation->getTimestamp(), highlited_text); +} diff --git a/indra/newview/llconversationloglistitem.h b/indra/newview/llconversationloglistitem.h new file mode 100644 index 0000000000..deba7d4563 --- /dev/null +++ b/indra/newview/llconversationloglistitem.h @@ -0,0 +1,77 @@ +/** + * @file llconversationloglistitem.h + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLCONVERSATIONLOGLISTITEM_H_ +#define LLCONVERSATIONLOGLISTITEM_H_ + +#include "llimfloater.h" +#include "llpanel.h" + +class LLTextBox; +class LLConversation; + +/** + * This class is a visual representation of LLConversation, each of which is LLConversationLog entry. + * LLConversationLogList consists of these LLConversationLogListItems. + * LLConversationLogListItem consists of: + * conversaion_type_icon + * conversaion_name + * conversaion_date + * Also LLConversationLogListItem holds pointer to its LLConversationLog. + */ + +class LLConversationLogListItem : public LLPanel +{ +public: + LLConversationLogListItem(const LLConversation* conversation); + virtual ~LLConversationLogListItem(); + + void onMouseEnter(S32 x, S32 y, MASK mask); + void onMouseLeave(S32 x, S32 y, MASK mask); + + virtual void setValue(const LLSD& value); + + virtual BOOL postBuild(); + + void onIMFloaterShown(const LLUUID& session_id); + void onRemoveBtnClicked(); + + const LLConversation* getConversation() const { return mConversation; } + + void highlightNameDate(const std::string& highlited_text); + +private: + + void initIcons(); + + const LLConversation* mConversation; + + LLTextBox* mConversationName; + LLTextBox* mConversationDate; + + boost::signals2::connection mIMFloaterShowedConnection; +}; + +#endif /* LLCONVERSATIONLOGITEM_H_ */ diff --git a/indra/newview/llconversationmodel.cpp b/indra/newview/llconversationmodel.cpp new file mode 100644 index 0000000000..bd314588a0 --- /dev/null +++ b/indra/newview/llconversationmodel.cpp @@ -0,0 +1,106 @@ +/** + * @file llconversationmodel.cpp + * @brief Implementation of conversations list + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +#include "llviewerprecompiledheaders.h" + +#include "llconversationmodel.h" +#include "llimfloatercontainer.h" + +// Conversation items +LLConversationItem::LLConversationItem(std::string name, const LLUUID& uuid, LLFloater* floaterp, LLIMFloaterContainer* containerp) : + LLFolderViewModelItemCommon(containerp->getRootViewModel()), + mName(name), + mUUID(uuid), + mFloater(floaterp), + mContainer(containerp) +{ +} + +LLConversationItem::LLConversationItem(LLIMFloaterContainer* containerp) : + LLFolderViewModelItemCommon(containerp->getRootViewModel()), + mName(""), + mUUID(), + mFloater(NULL), + mContainer(NULL) +{ +} + + +// Virtual action callbacks +void LLConversationItem::selectItem(void) +{ + LLMultiFloater* host_floater = mFloater->getHost(); + if (host_floater == mContainer) + { + // Always expand the message pane if the panel is hosted by the container + mContainer->collapseMessagesPane(false); + // Switch to the conversation floater that is being selected + mContainer->selectFloater(mFloater); + } + // Set the focus on the selected floater + mFloater->setFocus(TRUE); +} + +void LLConversationItem::setVisibleIfDetached(BOOL visible) +{ + // Do this only if the conversation floater has been torn off (i.e. no multi floater host) and is not minimized + // Note: minimized dockable floaters are brought to front hence unminimized when made visible and we don't want that here + if (!mFloater->getHost() && !mFloater->isMinimized()) + { + mFloater->setVisible(visible); + } +} + +void LLConversationItem::performAction(LLInventoryModel* model, std::string action) +{ +} + +void LLConversationItem::openItem( void ) +{ +} + +void LLConversationItem::closeItem( void ) +{ +} + +void LLConversationItem::previewItem( void ) +{ +} + +void LLConversationItem::showProperties(void) +{ +} + +bool LLConversationSort::operator()(const LLConversationItem* const& a, const LLConversationItem* const& b) const +{ + // We compare only by name for the moment + // *TODO : Implement the sorting by date + S32 compare = LLStringUtil::compareDict(a->getDisplayName(), b->getDisplayName()); + return (compare < 0); +} + +// EOF diff --git a/indra/newview/llconversationmodel.h b/indra/newview/llconversationmodel.h new file mode 100644 index 0000000000..85760eb1a8 --- /dev/null +++ b/indra/newview/llconversationmodel.h @@ -0,0 +1,202 @@ +/** + * @file llconversationmodel.h + * @brief Implementation of conversations list + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLCONVERSATIONMODEL_H +#define LL_LLCONVERSATIONMODEL_H + +//#include <map> +//#include <vector> + +#include "llfolderviewitem.h" +#include "llfolderviewmodel.h" + +class LLButton; +class LLFloater; +class LLLayoutPanel; +class LLLayoutStack; +class LLTabContainer; +class LLIMFloaterContainer; + +// Implementation of conversations list + +class LLConversationItem; + +typedef std::map<LLFloater*, LLConversationItem*> conversations_items_map; +typedef std::map<LLFloater*, LLFolderViewItem*> conversations_widgets_map; + +// Conversation items: we hold a list of those and create an LLFolderViewItem widget for each +// that we tuck into the mConversationsListPanel. +class LLConversationItem : public LLFolderViewModelItemCommon +{ +public: + LLConversationItem(std::string name, const LLUUID& uuid, LLFloater* floaterp, LLIMFloaterContainer* containerp); + LLConversationItem(LLIMFloaterContainer* containerp); + virtual ~LLConversationItem() {} + + // Stub those things we won't really be using in this conversation context + virtual const std::string& getName() const { return mName; } + virtual const std::string& getDisplayName() const { return mName; } + virtual const std::string& getSearchableName() const { return mName; } + virtual const LLUUID& getUUID() const { return mUUID; } + virtual time_t getCreationDate() const { return 0; } + virtual LLPointer<LLUIImage> getIcon() const { return NULL; } + virtual LLPointer<LLUIImage> getOpenIcon() const { return getIcon(); } + virtual LLFontGL::StyleFlags getLabelStyle() const { return LLFontGL::NORMAL; } + virtual std::string getLabelSuffix() const { return LLStringUtil::null; } + virtual BOOL isItemRenameable() const { return FALSE; } + virtual BOOL renameItem(const std::string& new_name) { return FALSE; } + virtual BOOL isItemMovable( void ) const { return FALSE; } + virtual BOOL isItemRemovable( void ) const { return FALSE; } + virtual BOOL isItemInTrash( void) const { return FALSE; } + virtual BOOL removeItem() { return FALSE; } + virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch) { } + virtual void move( LLFolderViewModelItem* parent_listener ) { } + virtual BOOL isItemCopyable() const { return FALSE; } + virtual BOOL copyToClipboard() const { return FALSE; } + virtual BOOL cutToClipboard() const { return FALSE; } + virtual BOOL isClipboardPasteable() const { return FALSE; } + virtual void pasteFromClipboard() { } + virtual void pasteLinkFromClipboard() { } + virtual void buildContextMenu(LLMenuGL& menu, U32 flags) { } + virtual BOOL isUpToDate() const { return TRUE; } + virtual bool hasChildren() const { return FALSE; } + + virtual bool potentiallyVisible() { return true; } + virtual bool filter( LLFolderViewFilter& filter) { return false; } + virtual bool descendantsPassedFilter(S32 filter_generation = -1) { return true; } + virtual void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0) { } + virtual bool passedFilter(S32 filter_generation = -1) { return true; } + + // The action callbacks + virtual void performAction(LLInventoryModel* model, std::string action); + virtual void openItem( void ); + virtual void closeItem( void ); + virtual void previewItem( void ); + virtual void selectItem(void); + virtual void showProperties(void); + + void setVisibleIfDetached(BOOL visible); + + // This method will be called to determine if a drop can be + // performed, and will set drop to TRUE if a drop is + // requested. + // Returns TRUE if a drop is possible/happened, FALSE otherwise. + virtual BOOL dragOrDrop(MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + std::string& tooltip_msg) { return FALSE; } + + bool hasSameValues(std::string name, const LLUUID& uuid) { return ((name == mName) && (uuid == mUUID)); } + bool hasSameValue(const LLUUID& uuid) { return (uuid == mUUID); } +private: + std::string mName; + const LLUUID mUUID; + LLFloater* mFloater; + LLIMFloaterContainer* mContainer; +}; + +// We don't want to ever filter conversations but we need to declare that class to create a conversation view model. +// We just stubb everything for the moment. +class LLConversationFilter : public LLFolderViewFilter +{ +public: + + enum ESortOrderType + { + SO_NAME = 0, // Sort inventory by name + SO_DATE = 0x1, // Sort inventory by date + }; + + LLConversationFilter() { mEmpty = ""; } + ~LLConversationFilter() {} + + bool check(const LLFolderViewModelItem* item) { return true; } + bool checkFolder(const LLFolderViewModelItem* folder) const { return true; } + void setEmptyLookupMessage(const std::string& message) { } + std::string getEmptyLookupMessage() const { return mEmpty; } + bool showAllResults() const { return true; } + std::string::size_type getStringMatchOffset(LLFolderViewModelItem* item) const { return std::string::npos; } + std::string::size_type getFilterStringSize() const { return 0; } + + bool isActive() const { return false; } + bool isModified() const { return false; } + void clearModified() { } + const std::string& getName() const { return mEmpty; } + const std::string& getFilterText() { return mEmpty; } + void setModified(EFilterModified behavior = FILTER_RESTART) { } + + void setFilterCount(S32 count) { } + S32 getFilterCount() const { return 0; } + void decrementFilterCount() { } + + bool isDefault() const { return true; } + bool isNotDefault() const { return false; } + void markDefault() { } + void resetDefault() { } + + S32 getCurrentGeneration() const { return 0; } + S32 getFirstSuccessGeneration() const { return 0; } + S32 getFirstRequiredGeneration() const { return 0; } +private: + std::string mEmpty; +}; + +class LLConversationSort +{ +public: + LLConversationSort(U32 order = 0) + : mSortOrder(order), + mByDate(false), + mByName(false) + { + mByDate = (order & LLConversationFilter::SO_DATE); + mByName = (order & LLConversationFilter::SO_NAME); + } + + bool isByDate() const { return mByDate; } + U32 getSortOrder() const { return mSortOrder; } + + bool operator()(const LLConversationItem* const& a, const LLConversationItem* const& b) const; +private: + U32 mSortOrder; + bool mByDate; + bool mByName; +}; + +class LLConversationViewModel +: public LLFolderViewModel<LLConversationSort, LLConversationItem, LLConversationItem, LLConversationFilter> +{ +public: + typedef LLFolderViewModel<LLConversationSort, LLConversationItem, LLConversationItem, LLConversationFilter> base_t; + + void sort(LLFolderViewFolder* folder) { } // *TODO : implement conversation sort + bool contentsReady() { return true; } // *TODO : we need to check that participants names are available somewhat + bool startDrag(std::vector<LLFolderViewModelItem*>& items) { return false; } // We do not allow drag of conversation items + +private: +}; + +#endif // LL_LLCONVERSATIONMODEL_H diff --git a/indra/newview/llexpandabletextbox.cpp b/indra/newview/llexpandabletextbox.cpp index 935dcb74b0..a50184460b 100644 --- a/indra/newview/llexpandabletextbox.cpp +++ b/indra/newview/llexpandabletextbox.cpp @@ -150,7 +150,7 @@ void LLExpandableTextBox::LLTextBoxEx::showExpandText() std::pair<S32, S32> visible_lines = getVisibleLines(true); S32 last_line = visible_lines.second - 1; - LLStyle::Params expander_style(getDefaultStyleParams()); + LLStyle::Params expander_style(getStyleParams()); expander_style.font.style = "UNDERLINE"; expander_style.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); LLExpanderSegment* expanderp = new LLExpanderSegment(new LLStyle(expander_style), getLineStart(last_line), getLength() + 1, mExpanderLabel, *this); @@ -166,7 +166,7 @@ void LLExpandableTextBox::LLTextBoxEx::hideExpandText() if (mExpanderVisible) { // this will overwrite the expander segment and all text styling with a single style - LLStyleConstSP sp(new LLStyle(getDefaultStyleParams())); + LLStyleConstSP sp(new LLStyle(getStyleParams())); LLNormalTextSegment* segmentp = new LLNormalTextSegment(sp, 0, getLength() + 1, *this); insertSegment(segmentp); diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index 4cbc9cab4a..c68577db75 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -38,6 +38,7 @@ #include "lltooltip.h" #include "llagent.h" +#include "llavatarnamecache.h" #include "llclipboard.h" #include "llclipboard.h" #include "llinventorybridge.h" @@ -45,12 +46,14 @@ #include "llfloatersidepanelcontainer.h" #include "llfloaterworldmap.h" #include "lllandmarkactions.h" +#include "lllogininstance.h" #include "llnotificationsutil.h" #include "lltoggleablemenu.h" #include "llviewerinventory.h" #include "llviewermenu.h" #include "llviewermenu.h" #include "lltooldraganddrop.h" +#include "llsdserialize.h" static LLDefaultChildRegistry::Register<LLFavoritesBarCtrl> r("favorites_bar"); @@ -317,7 +320,8 @@ public: if (item) { - item->setSortField(mSortField); + LLFavoritesOrderStorage::instance().setSortIndex(item, mSortField); + item->setComplete(TRUE); item->updateServer(FALSE); @@ -339,8 +343,8 @@ struct LLFavoritesSort // TODO - made it customizible using gSavedSettings bool operator()(const LLViewerInventoryItem* const& a, const LLViewerInventoryItem* const& b) { - S32 sortField1 = a->getSortField(); - S32 sortField2 = b->getSortField(); + S32 sortField1 = LLFavoritesOrderStorage::instance().getSortIndex(a->getUUID()); + S32 sortField2 = LLFavoritesOrderStorage::instance().getSortIndex(b->getUUID()); if (!(sortField1 < 0 && sortField2 < 0)) { @@ -528,7 +532,7 @@ void LLFavoritesBarCtrl::handleExistingFavoriteDragAndDrop(S32 x, S32 y) mItems.push_back(gInventory.getItem(mDragItemId)); } - gInventory.saveItemsOrder(mItems); + LLFavoritesOrderStorage::instance().saveItemsOrder(mItems); LLToggleableMenu* menu = (LLToggleableMenu*) mOverflowMenuHandle.get(); @@ -587,7 +591,8 @@ void LLFavoritesBarCtrl::handleNewFavoriteDragAndDrop(LLInventoryItem *item, con } else { - currItem->setSortField(++sortField); + LLFavoritesOrderStorage::instance().setSortIndex(currItem, ++sortField); + currItem->setComplete(TRUE); currItem->updateServer(FALSE); @@ -640,7 +645,7 @@ void LLFavoritesBarCtrl::changed(U32 mask) for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) { - (*i)->getSLURL(); + LLFavoritesOrderStorage::instance().getSLURL((*i)->getAssetUUID()); } updateButtons(); } @@ -909,7 +914,7 @@ BOOL LLFavoritesBarCtrl::collectFavoriteItems(LLInventoryModel::item_array_t &it S32 sortField = 0; for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) { - (*i)->setSortField(++sortField); + LLFavoritesOrderStorage::instance().setSortIndex((*i), ++sortField); } } @@ -1355,7 +1360,7 @@ BOOL LLFavoritesBarCtrl::needToSaveItemsOrder(const LLInventoryModel::item_array // if there is an item without sort order field set, we need to save items order for (LLInventoryModel::item_array_t::const_iterator i = items.begin(); i != items.end(); ++i) { - if ((*i)->getSortField() < 0) + if (LLFavoritesOrderStorage::instance().getSortIndex((*i)->getUUID()) < 0) { result = TRUE; break; @@ -1390,4 +1395,291 @@ void LLFavoritesBarCtrl::insertItem(LLInventoryModel::item_array_t& items, const } } +const std::string LLFavoritesOrderStorage::SORTING_DATA_FILE_NAME = "landmarks_sorting.xml"; +const S32 LLFavoritesOrderStorage::NO_INDEX = -1; + +void LLFavoritesOrderStorage::setSortIndex(const LLViewerInventoryItem* inv_item, S32 sort_index) +{ + mSortIndexes[inv_item->getUUID()] = sort_index; + mIsDirty = true; + getSLURL(inv_item->getAssetUUID()); +} + +S32 LLFavoritesOrderStorage::getSortIndex(const LLUUID& inv_item_id) +{ + sort_index_map_t::const_iterator it = mSortIndexes.find(inv_item_id); + if (it != mSortIndexes.end()) + { + return it->second; + } + return NO_INDEX; +} + +void LLFavoritesOrderStorage::removeSortIndex(const LLUUID& inv_item_id) +{ + mSortIndexes.erase(inv_item_id); + mIsDirty = true; +} + +void LLFavoritesOrderStorage::getSLURL(const LLUUID& asset_id) +{ + slurls_map_t::iterator slurl_iter = mSLURLs.find(asset_id); + if (slurl_iter != mSLURLs.end()) return; // SLURL for current landmark is already cached + + LLLandmark* lm = gLandmarkList.getAsset(asset_id, + boost::bind(&LLFavoritesOrderStorage::onLandmarkLoaded, this, asset_id, _1)); + if (lm) + { + onLandmarkLoaded(asset_id, lm); + } +} + +// static +void LLFavoritesOrderStorage::destroyClass() +{ + LLFavoritesOrderStorage::instance().cleanup(); + if (gSavedPerAccountSettings.getBOOL("ShowFavoritesOnLogin")) + { + LLFavoritesOrderStorage::instance().saveFavoritesSLURLs(); + } + else + { + LLFavoritesOrderStorage::instance().removeFavoritesRecordOfUser(); + } +} + +void LLFavoritesOrderStorage::load() +{ + // load per-resident sorting information + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SORTING_DATA_FILE_NAME); + + LLSD settings_llsd; + llifstream file; + file.open(filename); + if (file.is_open()) + { + LLSDSerialize::fromXML(settings_llsd, file); + } + + for (LLSD::map_const_iterator iter = settings_llsd.beginMap(); + iter != settings_llsd.endMap(); ++iter) + { + mSortIndexes.insert(std::make_pair(LLUUID(iter->first), (S32)iter->second.asInteger())); + } +} + +void LLFavoritesOrderStorage::saveFavoritesSLURLs() +{ + // Do not change the file if we are not logged in yet. + if (!LLLoginInstance::getInstance()->authSuccess()) + { + llwarns << "Cannot save favorites: not logged in" << llendl; + return; + } + + std::string user_dir = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""); + if (user_dir.empty()) + { + llwarns << "Cannot save favorites: empty user dir name" << llendl; + return; + } + + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); + llifstream in_file; + in_file.open(filename); + LLSD fav_llsd; + if (in_file.is_open()) + { + LLSDSerialize::fromXML(fav_llsd, in_file); + } + + const LLUUID fav_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(fav_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); + + LLSD user_llsd; + for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); it++) + { + LLSD value; + value["name"] = (*it)->getName(); + value["asset_id"] = (*it)->getAssetUUID(); + + slurls_map_t::iterator slurl_iter = mSLURLs.find(value["asset_id"]); + if (slurl_iter != mSLURLs.end()) + { + lldebugs << "Saving favorite: idx=" << LLFavoritesOrderStorage::instance().getSortIndex((*it)->getUUID()) << ", SLURL=" << slurl_iter->second << ", value=" << value << llendl; + value["slurl"] = slurl_iter->second; + user_llsd[LLFavoritesOrderStorage::instance().getSortIndex((*it)->getUUID())] = value; + } + else + { + llwarns << "Not saving favorite " << value["name"] << ": no matching SLURL" << llendl; + } + } + + LLAvatarName av_name; + LLAvatarNameCache::get( gAgentID, &av_name ); + lldebugs << "Saved favorites for " << av_name.getLegacyName() << llendl; + fav_llsd[av_name.getLegacyName()] = user_llsd; + + llofstream file; + file.open(filename); + LLSDSerialize::toPrettyXML(fav_llsd, file); +} + +void LLFavoritesOrderStorage::removeFavoritesRecordOfUser() +{ + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); + LLSD fav_llsd; + llifstream file; + file.open(filename); + if (!file.is_open()) return; + LLSDSerialize::fromXML(fav_llsd, file); + + LLAvatarName av_name; + LLAvatarNameCache::get( gAgentID, &av_name ); + lldebugs << "Removed favorites for " << av_name.getLegacyName() << llendl; + if (fav_llsd.has(av_name.getLegacyName())) + { + fav_llsd.erase(av_name.getLegacyName()); + } + + llofstream out_file; + out_file.open(filename); + LLSDSerialize::toPrettyXML(fav_llsd, out_file); + +} + +void LLFavoritesOrderStorage::onLandmarkLoaded(const LLUUID& asset_id, LLLandmark* landmark) +{ + if (!landmark) return; + + LLVector3d pos_global; + if (!landmark->getGlobalPos(pos_global)) + { + // If global position was unknown on first getGlobalPos() call + // it should be set for the subsequent calls. + landmark->getGlobalPos(pos_global); + } + + if (!pos_global.isExactlyZero()) + { + LLLandmarkActions::getSLURLfromPosGlobal(pos_global, + boost::bind(&LLFavoritesOrderStorage::storeFavoriteSLURL, this, asset_id, _1)); + } +} + +void LLFavoritesOrderStorage::storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl) +{ + lldebugs << "Saving landmark SLURL: " << slurl << llendl; + mSLURLs[asset_id] = slurl; +} + +void LLFavoritesOrderStorage::save() +{ + // nothing to save if clean + if (!mIsDirty) return; + + // If we quit from the login screen we will not have an SL account + // name. Don't try to save, otherwise we'll dump a file in + // C:\Program Files\SecondLife\ or similar. JC + std::string user_dir = gDirUtilp->getLindenUserDir(); + if (!user_dir.empty()) + { + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SORTING_DATA_FILE_NAME); + LLSD settings_llsd; + + for(sort_index_map_t::const_iterator iter = mSortIndexes.begin(); iter != mSortIndexes.end(); ++iter) + { + settings_llsd[iter->first.asString()] = iter->second; + } + + llofstream file; + file.open(filename); + LLSDSerialize::toPrettyXML(settings_llsd, file); + } +} + +void LLFavoritesOrderStorage::cleanup() +{ + // nothing to clean + if (!mIsDirty) return; + + const LLUUID fav_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(fav_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); + + IsNotInFavorites is_not_in_fav(items); + + sort_index_map_t aTempMap; + //copy unremoved values from mSortIndexes to aTempMap + std::remove_copy_if(mSortIndexes.begin(), mSortIndexes.end(), + inserter(aTempMap, aTempMap.begin()), + is_not_in_fav); + + //Swap the contents of mSortIndexes and aTempMap + mSortIndexes.swap(aTempMap); +} + +void LLFavoritesOrderStorage::saveItemsOrder( const LLInventoryModel::item_array_t& items ) +{ + int sortField = 0; + + // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field + for (LLInventoryModel::item_array_t::const_iterator i = items.begin(); i != items.end(); ++i) + { + LLViewerInventoryItem* item = *i; + + setSortIndex(item, ++sortField); + + item->setComplete(TRUE); + item->updateServer(FALSE); + + gInventory.updateItem(item); + + // Tell the parent folder to refresh its sort order. + gInventory.addChangedMask(LLInventoryObserver::SORT, item->getParentUUID()); + } + + gInventory.notifyObservers(); +} + +// See also LLInventorySort where landmarks in the Favorites folder are sorted. +class LLViewerInventoryItemSort +{ +public: + bool operator()(const LLPointer<LLViewerInventoryItem>& a, const LLPointer<LLViewerInventoryItem>& b) + { + return LLFavoritesOrderStorage::instance().getSortIndex(a->getUUID()) + < LLFavoritesOrderStorage::instance().getSortIndex(b->getUUID()); + } +}; + +// * @param source_item_id - LLUUID of the source item to be moved into new position +// * @param target_item_id - LLUUID of the target item before which source item should be placed. +void LLFavoritesOrderStorage::rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLIsType is_type(LLAssetType::AT_LANDMARK); + LLUUID favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + gInventory.collectDescendentsIf(favorites_id, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type); + + // ensure items are sorted properly before changing order. EXT-3498 + std::sort(items.begin(), items.end(), LLViewerInventoryItemSort()); + + // update order + gInventory.updateItemsOrder(items, source_item_id, target_item_id); + + saveItemsOrder(items); +} + +void AddFavoriteLandmarkCallback::fire(const LLUUID& inv_item_id) +{ + if (mTargetLandmarkId.isNull()) return; + + LLFavoritesOrderStorage::instance().rearrangeFavoriteLandmarks(inv_item_id, mTargetLandmarkId); +} // EOF diff --git a/indra/newview/llfavoritesbar.h b/indra/newview/llfavoritesbar.h index 447d30f1f4..e000adc4aa 100644 --- a/indra/newview/llfavoritesbar.h +++ b/indra/newview/llfavoritesbar.h @@ -33,6 +33,7 @@ #include "llinventoryobserver.h" #include "llinventorymodel.h" +#include "llviewerinventory.h" class LLMenuItemCallGL; class LLToggleableMenu; @@ -161,5 +162,115 @@ private: boost::signals2::connection mEndDragConnection; }; +class AddFavoriteLandmarkCallback : public LLInventoryCallback +{ +public: + AddFavoriteLandmarkCallback() : mTargetLandmarkId(LLUUID::null) {} + void setTargetLandmarkId(const LLUUID& target_uuid) { mTargetLandmarkId = target_uuid; } + +private: + void fire(const LLUUID& inv_item); + + LLUUID mTargetLandmarkId; +}; + +/** + * Class to store sorting order of favorites landmarks in a local file. EXT-3985. + * It replaced previously implemented solution to store sort index in landmark's name as a "<N>@" prefix. + * Data are stored in user home directory. + */ +class LLFavoritesOrderStorage : public LLSingleton<LLFavoritesOrderStorage> + , public LLDestroyClass<LLFavoritesOrderStorage> +{ + LOG_CLASS(LLFavoritesOrderStorage); +public: + /** + * Sets sort index for specified with LLUUID favorite landmark + */ + void setSortIndex(const LLViewerInventoryItem* inv_item, S32 sort_index); + + /** + * Gets sort index for specified with LLUUID favorite landmark + */ + S32 getSortIndex(const LLUUID& inv_item_id); + void removeSortIndex(const LLUUID& inv_item_id); + + void getSLURL(const LLUUID& asset_id); + + // Saves current order of the passed items using inventory item sort field. + // Resets 'items' sort fields and saves them on server. + // Is used to save order for Favorites folder. + void saveItemsOrder(const LLInventoryModel::item_array_t& items); + + void rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id); + + /** + * Implementation of LLDestroyClass. Calls cleanup() instance method. + * + * It is important this callback is called before gInventory is cleaned. + * For now it is called from LLAppViewer::cleanup() -> LLAppViewer::disconnectViewer(), + * Inventory is cleaned later from LLAppViewer::cleanup() after LLAppViewer::disconnectViewer() is called. + * @see cleanup() + */ + static void destroyClass(); + + const static S32 NO_INDEX; +private: + friend class LLSingleton<LLFavoritesOrderStorage>; + LLFavoritesOrderStorage() : mIsDirty(false) { load(); } + ~LLFavoritesOrderStorage() { save(); } + + /** + * Removes sort indexes for items which are not in Favorites bar for now. + */ + void cleanup(); + + const static std::string SORTING_DATA_FILE_NAME; + + void load(); + void save(); + + void saveFavoritesSLURLs(); + + // Remove record of current user's favorites from file on disk. + void removeFavoritesRecordOfUser(); + + void onLandmarkLoaded(const LLUUID& asset_id, class LLLandmark* landmark); + void storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl); + + typedef std::map<LLUUID, S32> sort_index_map_t; + sort_index_map_t mSortIndexes; + + typedef std::map<LLUUID, std::string> slurls_map_t; + slurls_map_t mSLURLs; + + bool mIsDirty; + + struct IsNotInFavorites + { + IsNotInFavorites(const LLInventoryModel::item_array_t& items) + : mFavoriteItems(items) + { + + } + + /** + * Returns true if specified item is not found among inventory items + */ + bool operator()(const sort_index_map_t::value_type& id_index_pair) const + { + LLPointer<LLViewerInventoryItem> item = gInventory.getItem(id_index_pair.first); + if (item.isNull()) return true; + + LLInventoryModel::item_array_t::const_iterator found_it = + std::find(mFavoriteItems.begin(), mFavoriteItems.end(), item); + + return found_it == mFavoriteItems.end(); + } + private: + LLInventoryModel::item_array_t mFavoriteItems; + }; + +}; #endif // LL_LLFAVORITESBARCTRL_H diff --git a/indra/newview/llfloaterconversationlog.cpp b/indra/newview/llfloaterconversationlog.cpp new file mode 100644 index 0000000000..c77a9e74bb --- /dev/null +++ b/indra/newview/llfloaterconversationlog.cpp @@ -0,0 +1,127 @@ +/** + * @file llfloaterconversationlog.cpp + * @brief Functionality of the "conversation log" floater + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#include "llviewerprecompiledheaders.h" + +#include "llconversationloglist.h" +#include "llfiltereditor.h" +#include "llfloaterconversationlog.h" +#include "llmenubutton.h" + +LLFloaterConversationLog::LLFloaterConversationLog(const LLSD& key) +: LLFloater(key), + mConversationLogList(NULL) +{ + mCommitCallbackRegistrar.add("CallLog.Action", boost::bind(&LLFloaterConversationLog::onCustomAction, this, _2)); + mEnableCallbackRegistrar.add("CallLog.Check", boost::bind(&LLFloaterConversationLog::isActionChecked, this, _2)); +} + +BOOL LLFloaterConversationLog::postBuild() +{ + mConversationLogList = getChild<LLConversationLogList>("conversation_log_list"); + + switch (gSavedSettings.getU32("CallLogSortOrder")) + { + case E_SORT_BY_NAME: + mConversationLogList->sortByName(); + break; + + case E_SORT_BY_DATE: + mConversationLogList->sortByDate(); + break; + } + + // Use the context menu of the Conversation list for the Conversation tab gear menu. + LLToggleableMenu* conversations_gear_menu = mConversationLogList->getContextMenu(); + if (conversations_gear_menu) + { + getChild<LLMenuButton>("conversations_gear_btn")->setMenu(conversations_gear_menu, LLMenuButton::MP_BOTTOM_LEFT); + } + + getChild<LLFilterEditor>("people_filter_input")->setCommitCallback(boost::bind(&LLFloaterConversationLog::onFilterEdit, this, _2)); + + return LLFloater::postBuild(); +} + +void LLFloaterConversationLog::draw() +{ + LLFloater::draw(); +} + +void LLFloaterConversationLog::onFilterEdit(const std::string& search_string) +{ + std::string filter = search_string; + LLStringUtil::trimHead(filter); + + mConversationLogList->setNameFilter(filter); +} + + +void LLFloaterConversationLog::onCustomAction (const LLSD& userdata) +{ + const std::string command_name = userdata.asString(); + + if ("sort_by_name" == command_name) + { + mConversationLogList->sortByName(); + gSavedSettings.setU32("CallLogSortOrder", E_SORT_BY_NAME); + } + else if ("sort_by_date" == command_name) + { + mConversationLogList->sortByDate(); + gSavedSettings.setU32("CallLogSortOrder", E_SORT_BY_DATE); + } + else if ("sort_friends_on_top" == command_name) + { + mConversationLogList->toggleSortFriendsOnTop(); + } +} + +bool LLFloaterConversationLog::isActionEnabled(const LLSD& userdata) +{ + return true; +} + +bool LLFloaterConversationLog::isActionChecked(const LLSD& userdata) +{ + const std::string command_name = userdata.asString(); + + U32 sort_order = gSavedSettings.getU32("CallLogSortOrder"); + + if ("sort_by_name" == command_name) + { + return sort_order == E_SORT_BY_NAME; + } + else if ("sort_by_date" == command_name) + { + return sort_order == E_SORT_BY_DATE; + } + else if ("sort_friends_on_top" == command_name) + { + return gSavedSettings.getBOOL("SortFriendsFirst"); + } + + return false; +} diff --git a/indra/newview/llfloaterconversationlog.h b/indra/newview/llfloaterconversationlog.h new file mode 100644 index 0000000000..7d788c0290 --- /dev/null +++ b/indra/newview/llfloaterconversationlog.h @@ -0,0 +1,61 @@ +/** + * @file llfloaterconversationlog.h + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERCONVERSATIONLOG_H_ +#define LL_LLFLOATERCONVERSATIONLOG_H_ + +#include "llfloater.h" + +class LLConversationLogList; + +class LLFloaterConversationLog : public LLFloater +{ +public: + + typedef enum e_sort_oder{ + E_SORT_BY_NAME = 0, + E_SORT_BY_DATE = 1, + } ESortOrder; + + LLFloaterConversationLog(const LLSD& key); + virtual ~LLFloaterConversationLog(){}; + + virtual BOOL postBuild(); + + virtual void draw(); + + void onFilterEdit(const std::string& search_string); + +private: + + void onCustomAction (const LLSD& userdata); + bool isActionEnabled(const LLSD& userdata); + bool isActionChecked(const LLSD& userdata); + + LLConversationLogList* mConversationLogList; +}; + + +#endif /* LLFLOATERCONVERSATIONLOG_H_ */ diff --git a/indra/newview/llfloaterconversationpreview.cpp b/indra/newview/llfloaterconversationpreview.cpp new file mode 100644 index 0000000000..e8554bb066 --- /dev/null +++ b/indra/newview/llfloaterconversationpreview.cpp @@ -0,0 +1,112 @@ +/** + * @file llfloaterconversationpreview.cpp + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llconversationlog.h" +#include "llfloaterconversationpreview.h" +#include "llimview.h" +#include "lllineeditor.h" + +LLFloaterConversationPreview::LLFloaterConversationPreview(const LLSD& session_id) +: LLFloater(session_id), + mChatHistory(NULL), + mSessionID(session_id.asUUID()) +{} + +BOOL LLFloaterConversationPreview::postBuild() +{ + mChatHistory = getChild<LLChatHistory>("chat_history"); + + const LLConversation* conv = LLConversationLog::instance().getConversation(mSessionID); + if (conv) + { + std::string name = conv->getConversationName(); + LLStringUtil::format_map_t args; + args["[NAME]"] = name; + std::string title = getString("Title", args); + setTitle(title); + + getChild<LLLineEditor>("description")->setValue(name); + } + + return LLFloater::postBuild(); +} + +void LLFloaterConversationPreview::draw() +{ + LLFloater::draw(); +} + +void LLFloaterConversationPreview::onOpen(const LLSD& session_id) +{ + const LLConversation* conv = LLConversationLog::instance().getConversation(session_id); + if (!conv) + { + return; + } + std::list<LLSD> messages; + std::string file = conv->getHistoryFileName(); + LLLogChat::loadAllHistory(file, messages); + + if (messages.size()) + { + std::ostringstream message; + std::list<LLSD>::const_iterator iter = messages.begin(); + for (; iter != messages.end(); ++iter) + { + LLSD msg = *iter; + + std::string time = msg["time"].asString(); + LLUUID from_id = msg["from_id"].asUUID(); + std::string from = msg["from"].asString(); + std::string message = msg["message"].asString(); + bool is_history = msg["is_history"].asBoolean(); + + LLChat chat; + chat.mFromID = from_id; + chat.mSessionID = session_id; + chat.mFromName = from; + chat.mTimeStr = time; + chat.mChatStyle = is_history ? CHAT_STYLE_HISTORY : chat.mChatStyle; + chat.mText = message; + + appendMessage(chat); + } + } +} + +void LLFloaterConversationPreview::appendMessage(const LLChat& chat) +{ + if (!chat.mMuted) + { + LLSD args; + args["use_plain_text_chat_history"] = true; + args["show_time"] = true; + args["show_names_for_p2p_conv"] = true; + + mChatHistory->appendMessage(chat); + } +} diff --git a/indra/newview/llfloaterconversationpreview.h b/indra/newview/llfloaterconversationpreview.h new file mode 100644 index 0000000000..cfc7c34485 --- /dev/null +++ b/indra/newview/llfloaterconversationpreview.h @@ -0,0 +1,51 @@ +/** + * @file llfloaterconversationpreview.h + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLFLOATERCONVERSATIONPREVIEW_H_ +#define LLFLOATERCONVERSATIONPREVIEW_H_ + +#include "llchathistory.h" +#include "llfloater.h" + +class LLFloaterConversationPreview : public LLFloater +{ +public: + + LLFloaterConversationPreview(const LLSD& session_id); + virtual ~LLFloaterConversationPreview(){}; + + virtual BOOL postBuild(); + + virtual void draw(); + virtual void onOpen(const LLSD& session_id); + +private: + void appendMessage(const LLChat& chat); + + LLChatHistory* mChatHistory; + LLUUID mSessionID; +}; + +#endif /* LLFLOATERCONVERSATIONPREVIEW_H_ */ diff --git a/indra/newview/llfloaternotificationsconsole.cpp b/indra/newview/llfloaternotificationsconsole.cpp index 2681d4b34d..4f35c325a8 100644 --- a/indra/newview/llfloaternotificationsconsole.cpp +++ b/indra/newview/llfloaternotificationsconsole.cpp @@ -44,21 +44,16 @@ public: BOOL postBuild(); private: - bool update(const LLSD& payload, bool passed_filter); + bool update(const LLSD& payload); static void toggleClick(void* user_data); static void onClickNotification(void* user_data); - static void onClickNotificationReject(void* user_data); LLNotificationChannelPtr mChannelPtr; - LLNotificationChannelPtr mChannelRejectsPtr; }; LLNotificationChannelPanel::LLNotificationChannelPanel(const LLNotificationChannelPanel::Params& p) : LLLayoutPanel(p) { mChannelPtr = LLNotifications::instance().getChannel(p.name); - mChannelRejectsPtr = LLNotificationChannelPtr( - LLNotificationChannel::buildChannel(p.name() + "rejects", mChannelPtr->getParentChannelName(), - !boost::bind(mChannelPtr->getFilter(), _1))); buildFromFile( "panel_notifications_channel.xml"); } @@ -68,15 +63,11 @@ BOOL LLNotificationChannelPanel::postBuild() header_button->setLabel(mChannelPtr->getName()); header_button->setClickedCallback(toggleClick, this); - mChannelPtr->connectChanged(boost::bind(&LLNotificationChannelPanel::update, this, _1, true)); - mChannelRejectsPtr->connectChanged(boost::bind(&LLNotificationChannelPanel::update, this, _1, false)); + mChannelPtr->connectChanged(boost::bind(&LLNotificationChannelPanel::update, this, _1)); LLScrollListCtrl* scroll = getChild<LLScrollListCtrl>("notifications_list"); scroll->setDoubleClickCallback(onClickNotification, this); scroll->setRect(LLRect( getRect().mLeft, getRect().mTop, getRect().mRight, 0)); - scroll = getChild<LLScrollListCtrl>("notification_rejects_list"); - scroll->setDoubleClickCallback(onClickNotificationReject, this); - scroll->setRect(LLRect( getRect().mLeft, getRect().mTop, getRect().mRight, 0)); return TRUE; } @@ -97,8 +88,6 @@ void LLNotificationChannelPanel::toggleClick(void *user_data) // turn off tab stop for collapsed panel self->getChild<LLScrollListCtrl>("notifications_list")->setTabStop(!header_button->getToggleState()); self->getChild<LLScrollListCtrl>("notifications_list")->setVisible(!header_button->getToggleState()); - self->getChild<LLScrollListCtrl>("notification_rejects_list")->setTabStop(!header_button->getToggleState()); - self->getChild<LLScrollListCtrl>("notification_rejects_list")->setVisible(!header_button->getToggleState()); } /*static*/ @@ -118,24 +107,7 @@ void LLNotificationChannelPanel::onClickNotification(void* user_data) } } -/*static*/ -void LLNotificationChannelPanel::onClickNotificationReject(void* user_data) -{ - LLNotificationChannelPanel* self = (LLNotificationChannelPanel*)user_data; - if (!self) return; - LLScrollListItem* firstselected = self->getChild<LLScrollListCtrl>("notification_rejects_list")->getFirstSelected(); - llassert(firstselected); - if (firstselected) - { - void* data = firstselected->getUserdata(); - if (data) - { - gFloaterView->getParentFloater(self)->addDependentFloater(new LLFloaterNotification((LLNotification*)data), TRUE); - } - } -} - -bool LLNotificationChannelPanel::update(const LLSD& payload, bool passed_filter) +bool LLNotificationChannelPanel::update(const LLSD& payload) { LLNotificationPtr notification = LLNotifications::instance().find(payload["id"].asUUID()); if (notification) @@ -151,9 +123,7 @@ bool LLNotificationChannelPanel::update(const LLSD& payload, bool passed_filter) row["columns"][2]["column"] = "date"; row["columns"][2]["type"] = "date"; - LLScrollListItem* sli = passed_filter ? - getChild<LLScrollListCtrl>("notifications_list")->addElement(row) : - getChild<LLScrollListCtrl>("notification_rejects_list")->addElement(row); + LLScrollListItem* sli = getChild<LLScrollListCtrl>("notifications_list")->addElement(row); sli->setUserdata(&(*notification)); } diff --git a/indra/newview/llfloateroutbox.cpp b/indra/newview/llfloateroutbox.cpp index 540f977305..18ed36d0f3 100644 --- a/indra/newview/llfloateroutbox.cpp +++ b/indra/newview/llfloateroutbox.cpp @@ -44,14 +44,12 @@ #include "llviewernetwork.h" #include "llwindowshade.h" -#define USE_WINDOWSHADE_DIALOGS 0 - ///---------------------------------------------------------------------------- /// LLOutboxNotification class ///---------------------------------------------------------------------------- -bool LLNotificationsUI::LLOutboxNotification::processNotification(const LLSD& notify) +bool LLNotificationsUI::LLOutboxNotification::processNotification(const LLNotificationPtr& notify) { LLFloaterOutbox* outbox_floater = LLFloaterReg::getTypedInstance<LLFloaterOutbox>("outbox"); @@ -60,6 +58,14 @@ bool LLNotificationsUI::LLOutboxNotification::processNotification(const LLSD& no return false; } +void LLNotificationsUI::LLOutboxNotification::onDelete(LLNotificationPtr p) +{ + LLNotificationsUI::LLSysHandler * sys_handler = dynamic_cast<LLNotificationsUI::LLSysHandler*>(LLNotifications::instance().getChannel("AlertModal").get()); + if (sys_handler) + { + sys_handler->onDelete(p); + } +} ///---------------------------------------------------------------------------- /// LLOutboxAddedObserver helper class @@ -168,9 +174,8 @@ void LLFloaterOutbox::onOpen(const LLSD& key) if (mOutboxId.isNull()) { const bool do_not_create_folder = false; - const bool do_not_find_in_library = false; - const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, do_not_create_folder, do_not_find_in_library); + const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, do_not_create_folder); if (outbox_id.isNull()) { @@ -244,8 +249,9 @@ void LLFloaterOutbox::setupOutbox(const LLUUID& outboxId) mOutboxInventoryPanel->setShape(inventory_placeholder_rect); // Set the sort order newest to oldest - mOutboxInventoryPanel->setSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME); - mOutboxInventoryPanel->getFilter()->markDefault(); + + mOutboxInventoryPanel->getFolderViewModel()->setSorter(LLInventoryFilter::SO_FOLDERS_BY_NAME); + mOutboxInventoryPanel->getFilter().markDefault(); fetchOutboxContents(); @@ -380,7 +386,7 @@ BOOL LLFloaterOutbox::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, // Determine if the mouse is inside the inventory panel itself or just within the floater bool pointInInventoryPanel = false; bool pointInInventoryPanelChild = false; - LLFolderView * root_folder = mOutboxInventoryPanel->getRootFolder(); + LLFolderView* root_folder = mOutboxInventoryPanel->getRootFolder(); if (mOutboxInventoryPanel->getVisible()) { S32 inv_x, inv_y; @@ -437,10 +443,10 @@ void LLFloaterOutbox::onOutboxChanged() { llassert(!mOutboxId.isNull()); - if (mOutboxInventoryPanel) - { - mOutboxInventoryPanel->requestSort(); - } + //if (mOutboxInventoryPanel) + //{ + // mOutboxInventoryPanel->requestSort(); + //} fetchOutboxContents(); @@ -516,52 +522,11 @@ void LLFloaterOutbox::initializationReportError(U32 status, const LLSD& content) updateView(); } -void LLFloaterOutbox::showNotification(const LLSD& notify) +void LLFloaterOutbox::showNotification(const LLNotificationPtr& notification) { - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if (!notification) - { - llerrs << "Unable to find outbox notification!" << notify.asString() << llendl; - - return; - } - -#if USE_WINDOWSHADE_DIALOGS - - if (mWindowShade) - { - delete mWindowShade; - } - - LLRect floater_rect = getLocalRect(); - floater_rect.mTop -= getHeaderHeight(); - floater_rect.stretch(-5, 0); - - LLWindowShade::Params params; - params.name = "notification_shade"; - params.rect = floater_rect; - params.follows.flags = FOLLOWS_ALL; - params.modal = true; - params.can_close = false; - params.shade_color = LLColor4::white % 0.25f; - params.text_color = LLColor4::white; - - mWindowShade = LLUICtrlFactory::create<LLWindowShade>(params); - - addChild(mWindowShade); - mWindowShade->show(notification); - -#else - - LLNotificationsUI::LLEventHandler * handler = - LLNotificationsUI::LLNotificationManager::instance().getHandlerForNotification("alertmodal"); - - LLNotificationsUI::LLSysHandler * sys_handler = dynamic_cast<LLNotificationsUI::LLSysHandler *>(handler); + LLNotificationsUI::LLSysHandler * sys_handler = dynamic_cast<LLNotificationsUI::LLSysHandler*>(LLNotifications::instance().getChannel("AlertModal").get()); llassert(sys_handler); - sys_handler->processNotification(notify); - -#endif + sys_handler->processNotification(notification); } diff --git a/indra/newview/llfloateroutbox.h b/indra/newview/llfloateroutbox.h index 18baccf1c9..a91d8c1139 100644 --- a/indra/newview/llfloateroutbox.h +++ b/indra/newview/llfloateroutbox.h @@ -64,7 +64,7 @@ public: EAcceptance* accept, std::string& tooltip_msg); - void showNotification(const LLSD& notify); + void showNotification(const LLNotificationPtr& notification); BOOL handleHover(S32 x, S32 y, MASK mask); void onMouseLeave(S32 x, S32 y, MASK mask); diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 60556147a9..797aba2b98 100755 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -425,13 +425,7 @@ void LLFloaterPreference::saveAvatarProperties( void ) BOOL LLFloaterPreference::postBuild() { - gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLIMFloater::processChatHistoryStyleUpdate, _2)); - - gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLNearbyChat::processChatHistoryStyleUpdate, _2)); - - gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLIMFloater::processChatHistoryStyleUpdate, _2)); - - gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLNearbyChat::processChatHistoryStyleUpdate, _2)); + gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLIMConversation::processChatHistoryStyleUpdate)); gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLViewerChat::signalChatFontChanged)); @@ -461,14 +455,11 @@ BOOL LLFloaterPreference::postBuild() void LLFloaterPreference::onBusyResponseChanged() { // set "BusyResponseChanged" TRUE if user edited message differs from default, FALSE otherwise - if (LLTrans::getString("BusyModeResponseDefault") != getChild<LLUICtrl>("busy_response")->getValue().asString()) - { - gSavedPerAccountSettings.setBOOL("BusyResponseChanged", TRUE ); - } - else - { - gSavedPerAccountSettings.setBOOL("BusyResponseChanged", FALSE ); - } + bool busy_flag = + LLTrans::getString("BusyModeResponseDefault") + != getChild<LLUICtrl>("busy_response")->getValue().asString(); + + gSavedPerAccountSettings.setBOOL("BusyResponseChanged", busy_flag ); } LLFloaterPreference::~LLFloaterPreference() @@ -553,8 +544,6 @@ void LLFloaterPreference::apply() // LLWString busy_response = utf8str_to_wstring(getChild<LLUICtrl>("busy_response")->getValue().asString()); // LLWStringUtil::replaceTabsWithSpaces(busy_response, 4); - - gSavedSettings.setBOOL("PlainTextChatHistory", getChild<LLUICtrl>("plain_text_chat_history")->getValue().asBoolean()); if (mGotPersonalInfo) { @@ -1425,8 +1414,6 @@ void LLFloaterPreference::setPersonalInfo(const std::string& visibility, bool im getChild<LLUICtrl>("online_visibility")->setLabelArg("[DIR_VIS]", mDirectoryVisibility); getChildView("send_im_to_email")->setEnabled(TRUE); getChild<LLUICtrl>("send_im_to_email")->setValue(im_via_email); - getChildView("plain_text_chat_history")->setEnabled(TRUE); - getChild<LLUICtrl>("plain_text_chat_history")->setValue(gSavedSettings.getBOOL("PlainTextChatHistory")); getChildView("log_instant_messages")->setEnabled(TRUE); // getChildView("log_chat")->setEnabled(TRUE); // getChildView("busy_response")->setEnabled(TRUE); @@ -1508,7 +1495,8 @@ void LLFloaterPreference::onChangeMaturity() // but the UI for this will still be enabled void LLFloaterPreference::onClickBlockList() { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); + LLFloaterSidePanelContainer::showPanel("people", "panel_people", + LLSD().with("people_panel_tab_name", "blocked_panel")); } void LLFloaterPreference::onClickProxySettings() diff --git a/indra/newview/llfloatersidepanelcontainer.cpp b/indra/newview/llfloatersidepanelcontainer.cpp index 5385977d95..96b5c6b09b 100644 --- a/indra/newview/llfloatersidepanelcontainer.cpp +++ b/indra/newview/llfloatersidepanelcontainer.cpp @@ -61,7 +61,7 @@ LLPanel* LLFloaterSidePanelContainer::openChildPanel(const std::string& panel_na if (!getVisible()) { - openFloater(); + openFloater(); } LLPanel* panel = NULL; @@ -69,10 +69,7 @@ LLPanel* LLFloaterSidePanelContainer::openChildPanel(const std::string& panel_na LLSideTrayPanelContainer* container = dynamic_cast<LLSideTrayPanelContainer*>(view->getParent()); if (container) { - LLSD new_params = params; - new_params[LLSideTrayPanelContainer::PARAM_SUB_PANEL_NAME] = panel_name; - container->onOpen(new_params); - + container->openPanel(panel_name, params); panel = container->getCurrentPanel(); } else if ((panel = dynamic_cast<LLPanel*>(view)) != NULL) diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp index 99ebb0eb34..4d38f5834e 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -658,8 +658,8 @@ void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask) mBtnEdit ->setToggleState( edit_visible ); mRadioGroupEdit->setVisible( edit_visible ); - bool linked_parts = gSavedSettings.getBOOL("EditLinkedParts"); - getChildView("RenderingCost")->setVisible( !linked_parts && (edit_visible || focus_visible || move_visible) && sShowObjectCost); + //bool linked_parts = gSavedSettings.getBOOL("EditLinkedParts"); + //getChildView("RenderingCost")->setVisible( !linked_parts && (edit_visible || focus_visible || move_visible) && sShowObjectCost); mBtnLink->setVisible(edit_visible); mBtnUnlink->setVisible(edit_visible); diff --git a/indra/newview/llfloatertranslationsettings.cpp b/indra/newview/llfloatertranslationsettings.cpp index 1a17183efd..bb01ce5a7e 100644 --- a/indra/newview/llfloatertranslationsettings.cpp +++ b/indra/newview/llfloatertranslationsettings.cpp @@ -29,7 +29,7 @@ #include "llfloatertranslationsettings.h" // Viewer includes -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "lltranslate.h" #include "llviewercontrol.h" // for gSavedSettings @@ -293,6 +293,6 @@ void LLFloaterTranslationSettings::onBtnOK() gSavedSettings.setString("TranslationService", getSelectedService()); gSavedSettings.setString("BingTranslateAPIKey", getEnteredBingKey()); gSavedSettings.setString("GoogleTranslateAPIKey", getEnteredGoogleKey()); - LLNearbyChatBar::getInstance()->showTranslationCheckbox(LLTranslate::isTranslationConfigured()); + LLNearbyChat::getInstance()->showTranslationCheckbox(LLTranslate::isTranslationConfigured()); closeFloater(false); } diff --git a/indra/newview/llfloatervoicevolume.cpp b/indra/newview/llfloatervoicevolume.cpp new file mode 100644 index 0000000000..87b388b30a --- /dev/null +++ b/indra/newview/llfloatervoicevolume.cpp @@ -0,0 +1,209 @@ +/** + * @file llfloatervoicevolume.cpp + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloatervoicevolume.h" + +// Linden libraries +#include "llavatarname.h" +#include "llavatarnamecache.h" +#include "llfloater.h" +#include "llfloaterreg.h" +#include "lltextbox.h" + +// viewer files +#include "llagent.h" +#include "llavataractions.h" +#include "llinspect.h" +#include "lltransientfloatermgr.h" +#include "llvoiceclient.h" + +class LLAvatarName; + +////////////////////////////////////////////////////////////////////////////// +// LLFloaterVoiceVolume +////////////////////////////////////////////////////////////////////////////// + +// Avatar Inspector, a small information window used when clicking +// on avatar names in the 2D UI and in the ambient inspector widget for +// the 3D world. +class LLFloaterVoiceVolume : public LLInspect, LLTransientFloater +{ + friend class LLFloaterReg; + +public: + // avatar_id - Avatar ID for which to show information + // Inspector will be positioned relative to current mouse position + LLFloaterVoiceVolume(const LLSD& avatar_id); + virtual ~LLFloaterVoiceVolume(); + + /*virtual*/ BOOL postBuild(void); + + // Because floater is single instance, need to re-parse data on each spawn + // (for example, inspector about same avatar but in different position) + /*virtual*/ void onOpen(const LLSD& avatar_id); + + /*virtual*/ LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::GLOBAL; } + +private: + // Set the volume slider to this user's current client-side volume setting, + // hiding/disabling if the user is not nearby. + void updateVolumeControls(); + + void onClickMuteVolume(); + void onVolumeChange(const LLSD& data); + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); + +private: + LLUUID mAvatarID; + // Need avatar name information to spawn friend add request + LLAvatarName mAvatarName; +}; + +LLFloaterVoiceVolume::LLFloaterVoiceVolume(const LLSD& sd) +: LLInspect(LLSD()) // single_instance, doesn't really need key +, mAvatarID() // set in onOpen() *Note: we used to show partner's name but we dont anymore --angela 3rd Dec* +, mAvatarName() +{ + LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::GLOBAL, this); + LLTransientFloater::init(this); +} + +LLFloaterVoiceVolume::~LLFloaterVoiceVolume() +{ + LLTransientFloaterMgr::getInstance()->removeControlView(this); +} + +/*virtual*/ +BOOL LLFloaterVoiceVolume::postBuild(void) +{ + getChild<LLUICtrl>("mute_btn")->setCommitCallback( + boost::bind(&LLFloaterVoiceVolume::onClickMuteVolume, this) ); + + getChild<LLUICtrl>("volume_slider")->setCommitCallback( + boost::bind(&LLFloaterVoiceVolume::onVolumeChange, this, _2)); + + return TRUE; +} + + +// Multiple calls to showInstance("floater_voice_volume", foo) will provide different +// LLSD for foo, which we will catch here. +//virtual +void LLFloaterVoiceVolume::onOpen(const LLSD& data) +{ + // Start open animation + LLInspect::onOpen(data); + + // Extract appropriate avatar id + mAvatarID = data["avatar_id"]; + + LLUI::positionViewNearMouse(this); + + getChild<LLUICtrl>("avatar_name")->setValue(""); + updateVolumeControls(); + + LLAvatarNameCache::get(mAvatarID, + boost::bind(&LLFloaterVoiceVolume::onAvatarNameCache, this, _1, _2)); +} + +void LLFloaterVoiceVolume::updateVolumeControls() +{ + bool voice_enabled = LLVoiceClient::getInstance()->getVoiceEnabled(mAvatarID); + + LLUICtrl* mute_btn = getChild<LLUICtrl>("mute_btn"); + LLUICtrl* volume_slider = getChild<LLUICtrl>("volume_slider"); + + // Do not display volume slider and mute button if it + // is ourself or we are not in a voice channel together + if (!voice_enabled || (mAvatarID == gAgent.getID())) + { + mute_btn->setVisible(false); + volume_slider->setVisible(false); + } + else + { + mute_btn->setVisible(true); + volume_slider->setVisible(true); + + // By convention, we only display and toggle voice mutes, not all mutes + bool is_muted = LLAvatarActions::isVoiceMuted(mAvatarID); + bool is_linden = LLStringUtil::endsWith(mAvatarName.getLegacyName(), " Linden"); + + mute_btn->setEnabled(!is_linden); + mute_btn->setValue(is_muted); + + volume_slider->setEnabled(!is_muted); + + F32 volume; + if (is_muted) + { + // it's clearer to display their volume as zero + volume = 0.f; + } + else + { + // actual volume + volume = LLVoiceClient::getInstance()->getUserVolume(mAvatarID); + } + volume_slider->setValue((F64)volume); + } + +} + +void LLFloaterVoiceVolume::onClickMuteVolume() +{ + LLAvatarActions::toggleMuteVoice(mAvatarID); + updateVolumeControls(); +} + +void LLFloaterVoiceVolume::onVolumeChange(const LLSD& data) +{ + F32 volume = (F32)data.asReal(); + LLVoiceClient::getInstance()->setUserVolume(mAvatarID, volume); +} + +void LLFloaterVoiceVolume::onAvatarNameCache( + const LLUUID& agent_id, + const LLAvatarName& av_name) +{ + if (agent_id != mAvatarID) + { + return; + } + + getChild<LLUICtrl>("avatar_name")->setValue(av_name.getCompleteName()); + mAvatarName = av_name; +} + +////////////////////////////////////////////////////////////////////////////// +// LLFloaterVoiceVolumeUtil +////////////////////////////////////////////////////////////////////////////// +void LLFloaterVoiceVolumeUtil::registerFloater() +{ + LLFloaterReg::add("floater_voice_volume", "floater_voice_volume.xml", + &LLFloaterReg::build<LLFloaterVoiceVolume>); +} diff --git a/indra/newview/llfloatervoicevolume.h b/indra/newview/llfloatervoicevolume.h new file mode 100644 index 0000000000..8fcf7f250b --- /dev/null +++ b/indra/newview/llfloatervoicevolume.h @@ -0,0 +1,35 @@ +/** + * @file llfloatervoicevolume.h + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERVOICEVOLUME_H +#define LL_LLFLOATERVOICEVOLUME_H + +namespace LLFloaterVoiceVolumeUtil +{ + // Register with LLFloaterReg + void registerFloater(); +} + +#endif // LL_LLFLOATERVOICEVOLUME_H diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp deleted file mode 100644 index 7d047ec67e..0000000000 --- a/indra/newview/llfolderview.cpp +++ /dev/null @@ -1,2627 +0,0 @@ -/** - * @file llfolderview.cpp - * @brief Implementation of the folder view collection of classes. - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llfolderview.h" - -#include "llcallbacklist.h" -#include "llinventorybridge.h" -#include "llclipboard.h" // *TODO: remove this once hack below gone. -#include "llinventoryfilter.h" -#include "llinventoryfunctions.h" -#include "llinventorymodelbackgroundfetch.h" -#include "llinventorypanel.h" -#include "llfoldertype.h" -#include "llfloaterinventory.h"// hacked in for the bonus context menu items. -#include "llkeyboard.h" -#include "lllineeditor.h" -#include "llmenugl.h" -#include "llpanel.h" -#include "llpreview.h" -#include "llscrollcontainer.h" // hack to allow scrolling -#include "lltooldraganddrop.h" -#include "lltrans.h" -#include "llui.h" -#include "llviewertexture.h" -#include "llviewertexturelist.h" -#include "llviewerjointattachment.h" -#include "llviewermenu.h" -#include "lluictrlfactory.h" -#include "llviewercontrol.h" -#include "llviewerfoldertype.h" -#include "llviewerwindow.h" -#include "llvoavatar.h" -#include "llfloaterproperties.h" -#include "llnotificationsutil.h" - -// Linden library includes -#include "lldbstrings.h" -#include "llfocusmgr.h" -#include "llfontgl.h" -#include "llgl.h" -#include "llrender.h" -#include "llinventory.h" - -// Third-party library includes -#include <algorithm> - -///---------------------------------------------------------------------------- -/// Local function declarations, constants, enums, and typedefs -///---------------------------------------------------------------------------- - -const S32 RENAME_WIDTH_PAD = 4; -const S32 RENAME_HEIGHT_PAD = 1; -const S32 AUTO_OPEN_STACK_DEPTH = 16; -const S32 MIN_ITEM_WIDTH_VISIBLE = LLFolderViewItem::ICON_WIDTH - + LLFolderViewItem::ICON_PAD - + LLFolderViewItem::ARROW_SIZE - + LLFolderViewItem::TEXT_PAD - + /*first few characters*/ 40; -const S32 MINIMUM_RENAMER_WIDTH = 80; - -// *TODO: move in params in xml if necessary. Requires modification of LLFolderView & LLInventoryPanel Params. -const S32 STATUS_TEXT_HPAD = 6; -const S32 STATUS_TEXT_VPAD = 8; - -enum { - SIGNAL_NO_KEYBOARD_FOCUS = 1, - SIGNAL_KEYBOARD_FOCUS = 2 -}; - -F32 LLFolderView::sAutoOpenTime = 1.f; - -void delete_selected_item(void* user_data); -void copy_selected_item(void* user_data); -void open_selected_items(void* user_data); -void properties_selected_items(void* user_data); -void paste_items(void* user_data); - - -//--------------------------------------------------------------------------- - -// Tells all folders in a folderview to sort their items -// (and only their items, not folders) by a certain function. -class LLSetItemSortFunction : public LLFolderViewFunctor -{ -public: - LLSetItemSortFunction(U32 ordering) - : mSortOrder(ordering) {} - virtual ~LLSetItemSortFunction() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); - - U32 mSortOrder; -}; - - -// Set the sort order. -void LLSetItemSortFunction::doFolder(LLFolderViewFolder* folder) -{ - folder->setItemSortOrder(mSortOrder); -} - -// Do nothing. -void LLSetItemSortFunction::doItem(LLFolderViewItem* item) -{ - return; -} - -//--------------------------------------------------------------------------- - -// Tells all folders in a folderview to close themselves -// For efficiency, calls setOpenArrangeRecursively(). -// The calling function must then call: -// LLFolderView* root = getRoot(); -// if( root ) -// { -// root->arrange( NULL, NULL ); -// root->scrollToShowSelection(); -// } -// to patch things up. -class LLCloseAllFoldersFunctor : public LLFolderViewFunctor -{ -public: - LLCloseAllFoldersFunctor(BOOL close) { mOpen = !close; } - virtual ~LLCloseAllFoldersFunctor() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); - - BOOL mOpen; -}; - - -// Set the sort order. -void LLCloseAllFoldersFunctor::doFolder(LLFolderViewFolder* folder) -{ - folder->setOpenArrangeRecursively(mOpen); -} - -// Do nothing. -void LLCloseAllFoldersFunctor::doItem(LLFolderViewItem* item) -{ } - -///---------------------------------------------------------------------------- -/// Class LLFolderViewScrollContainer -///---------------------------------------------------------------------------- - -// virtual -const LLRect LLFolderViewScrollContainer::getScrolledViewRect() const -{ - LLRect rect = LLRect::null; - if (mScrolledView) - { - LLFolderView* folder_view = dynamic_cast<LLFolderView*>(mScrolledView); - if (folder_view) - { - S32 height = folder_view->mRunningHeight; - - rect = mScrolledView->getRect(); - rect.setLeftTopAndSize(rect.mLeft, rect.mTop, rect.getWidth(), height); - } - } - - return rect; -} - -LLFolderViewScrollContainer::LLFolderViewScrollContainer(const LLScrollContainer::Params& p) -: LLScrollContainer(p) -{} - -///---------------------------------------------------------------------------- -/// Class LLFolderView -///---------------------------------------------------------------------------- -LLFolderView::Params::Params() -: task_id("task_id"), - title("title"), - use_label_suffix("use_label_suffix"), - allow_multiselect("allow_multiselect", true), - show_empty_message("show_empty_message", true), - show_load_status("show_load_status", true), - use_ellipses("use_ellipses", false) -{ -} - - -// Default constructor -LLFolderView::LLFolderView(const Params& p) -: LLFolderViewFolder(p), - mRunningHeight(0), - mScrollContainer( NULL ), - mPopupMenuHandle(), - mAllowMultiSelect(p.allow_multiselect), - mShowEmptyMessage(p.show_empty_message), - mShowFolderHierarchy(FALSE), - mSourceID(p.task_id), - mRenameItem( NULL ), - mNeedsScroll( FALSE ), - mUseLabelSuffix(p.use_label_suffix), - mPinningSelectedItem(FALSE), - mNeedsAutoSelect( FALSE ), - mAutoSelectOverride(FALSE), - mNeedsAutoRename(FALSE), - mDebugFilters(FALSE), - mSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME), // This gets overridden by a pref immediately - mFilter( new LLInventoryFilter(p.title) ), - mShowSelectionContext(FALSE), - mShowSingleSelection(FALSE), - mArrangeGeneration(0), - mSignalSelectCallback(0), - mMinWidth(0), - mDragAndDropThisFrame(FALSE), - mCallbackRegistrar(NULL), - mParentPanel(p.parent_panel), - mUseEllipses(p.use_ellipses), - mDraggingOverItem(NULL), - mStatusTextBox(NULL) -{ - mRoot = this; - - mShowLoadStatus = p.show_load_status(); - - LLRect rect = p.rect; - LLRect new_rect(rect.mLeft, rect.mBottom + getRect().getHeight(), rect.mLeft + getRect().getWidth(), rect.mBottom); - setRect( rect ); - reshape(rect.getWidth(), rect.getHeight()); - mIsOpen = TRUE; // this view is always open. - mAutoOpenItems.setDepth(AUTO_OPEN_STACK_DEPTH); - mAutoOpenCandidate = NULL; - mAutoOpenTimer.stop(); - mKeyboardSelection = FALSE; - const LLFolderViewItem::Params& item_params = - LLUICtrlFactory::getDefaultParams<LLFolderViewItem>(); - S32 indentation = item_params.folder_indentation(); - mIndentation = -indentation; // children start at indentation 0 - gIdleCallbacks.addFunction(idle, this); - - //clear label - // go ahead and render root folder as usual - // just make sure the label ("Inventory Folder") never shows up - mLabel = LLStringUtil::null; - - //mRenamer->setWriteableBgColor(LLColor4::white); - // Escape is handled by reverting the rename, not commiting it (default behavior) - LLLineEditor::Params params; - params.name("ren"); - params.rect(rect); - params.font(getLabelFontForStyle(LLFontGL::NORMAL)); - params.max_length.bytes(DB_INV_ITEM_NAME_STR_LEN); - params.commit_callback.function(boost::bind(&LLFolderView::commitRename, this, _2)); - params.prevalidate_callback(&LLTextValidate::validateASCIIPrintableNoPipe); - params.commit_on_focus_lost(true); - params.visible(false); - mRenamer = LLUICtrlFactory::create<LLLineEditor> (params); - addChild(mRenamer); - - // Textbox - LLTextBox::Params text_p; - LLFontGL* font = getLabelFontForStyle(mLabelStyle); - LLRect new_r = LLRect(rect.mLeft + ICON_PAD, - rect.mTop - TEXT_PAD, - rect.mRight, - rect.mTop - TEXT_PAD - font->getLineHeight()); - text_p.rect(new_r); - text_p.name(std::string(p.name)); - text_p.font(font); - text_p.visible(false); - text_p.parse_urls(true); - text_p.wrap(true); // allow multiline text. See EXT-7564, EXT-7047 - // set text padding the same as in People panel. EXT-7047, EXT-4837 - text_p.h_pad(STATUS_TEXT_HPAD); - text_p.v_pad(STATUS_TEXT_VPAD); - mStatusTextBox = LLUICtrlFactory::create<LLTextBox> (text_p); - mStatusTextBox->setFollowsLeft(); - mStatusTextBox->setFollowsTop(); - //addChild(mStatusTextBox); - - - // make the popup menu available - LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_inventory.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if (!menu) - { - menu = LLUICtrlFactory::getDefaultWidget<LLMenuGL>("inventory_menu"); - } - menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor")); - mPopupMenuHandle = menu->getHandle(); - - mListener->openItem(); -} - -// Destroys the object -LLFolderView::~LLFolderView( void ) -{ - closeRenamer(); - - // The release focus call can potentially call the - // scrollcontainer, which can potentially be called with a partly - // destroyed scollcontainer. Just null it out here, and no worries - // about calling into the invalid scroll container. - // Same with the renamer. - mScrollContainer = NULL; - mRenameItem = NULL; - mRenamer = NULL; - mStatusTextBox = NULL; - - mAutoOpenItems.removeAllNodes(); - gIdleCallbacks.deleteFunction(idle, this); - - if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die(); - - mAutoOpenItems.removeAllNodes(); - clearSelection(); - mItems.clear(); - mFolders.clear(); - - mItemMap.clear(); - - delete mFilter; - mFilter = NULL; -} - -BOOL LLFolderView::canFocusChildren() const -{ - return FALSE; -} - -static LLFastTimer::DeclareTimer FTM_SORT("Sort Inventory"); - -void LLFolderView::setSortOrder(U32 order) -{ - if (order != mSortOrder) - { - LLFastTimer t(FTM_SORT); - - mSortOrder = order; - - sortBy(order); - arrangeAll(); - } -} - - -U32 LLFolderView::getSortOrder() const -{ - return mSortOrder; -} - -BOOL LLFolderView::addFolder( LLFolderViewFolder* folder) -{ - // enforce sort order of My Inventory followed by Library - if (folder->getListener()->getUUID() == gInventory.getLibraryRootFolderID()) - { - mFolders.push_back(folder); - } - else - { - mFolders.insert(mFolders.begin(), folder); - } - folder->setShowLoadStatus(mShowLoadStatus); - folder->setOrigin(0, 0); - folder->reshape(getRect().getWidth(), 0); - folder->setVisible(FALSE); - addChild( folder ); - folder->dirtyFilter(); - folder->requestArrange(); - return TRUE; -} - -void LLFolderView::closeAllFolders() -{ - // Close all the folders - setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_DOWN); - arrangeAll(); -} - -void LLFolderView::openTopLevelFolders() -{ - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->setOpen(TRUE); - } -} - -void LLFolderView::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse) -{ - // call base class to do proper recursion - LLFolderViewFolder::setOpenArrangeRecursively(openitem, recurse); - // make sure root folder is always open - mIsOpen = TRUE; -} - -static LLFastTimer::DeclareTimer FTM_ARRANGE("Arrange"); - -// This view grows and shrinks to enclose all of its children items and folders. -S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_generation ) -{ - if (getListener()->getUUID().notNull()) - { - if (mNeedsSort) - { - mFolders.sort(mSortFunction); - mItems.sort(mSortFunction); - mNeedsSort = false; - } - } - - LLFastTimer t2(FTM_ARRANGE); - - filter_generation = mFilter->getMinRequiredGeneration(); - mMinWidth = 0; - - mHasVisibleChildren = hasFilteredDescendants(filter_generation); - // arrange always finishes, so optimistically set the arrange generation to the most current - mLastArrangeGeneration = getRoot()->getArrangeGeneration(); - - LLInventoryFilter::EFolderShow show_folder_state = - getRoot()->getFilter()->getShowFolderState(); - - S32 total_width = LEFT_PAD; - S32 running_height = mDebugFilters ? LLFontGL::getFontMonospace()->getLineHeight() : 0; - S32 target_height = running_height; - S32 parent_item_height = getRect().getHeight(); - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - LLFolderViewFolder* folderp = (*fit); - if (getDebugFilters()) - { - folderp->setVisible(TRUE); - } - else - { - folderp->setVisible((show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders? - (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation)))); - } - - if (folderp->getVisible()) - { - S32 child_height = 0; - S32 child_width = 0; - S32 child_top = parent_item_height - running_height; - - target_height += folderp->arrange( &child_width, &child_height, filter_generation ); - - mMinWidth = llmax(mMinWidth, child_width); - total_width = llmax( total_width, child_width ); - running_height += child_height; - folderp->setOrigin( ICON_PAD, child_top - (*fit)->getRect().getHeight() ); - } - } - - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - LLFolderViewItem* itemp = (*iit); - itemp->setVisible(itemp->getFiltered(filter_generation)); - - if (itemp->getVisible()) - { - S32 child_width = 0; - S32 child_height = 0; - S32 child_top = parent_item_height - running_height; - - target_height += itemp->arrange( &child_width, &child_height, filter_generation ); - itemp->reshape(itemp->getRect().getWidth(), child_height); - - mMinWidth = llmax(mMinWidth, child_width); - total_width = llmax( total_width, child_width ); - running_height += child_height; - itemp->setOrigin( ICON_PAD, child_top - itemp->getRect().getHeight() ); - } - } - - if(!mHasVisibleChildren)// is there any filtered items ? - { - //Nope. We need to display status textbox, let's reserve some place for it - running_height = mStatusTextBox->getTextPixelHeight(); - target_height = running_height; - } - - mRunningHeight = running_height; - LLRect scroll_rect = mScrollContainer->getContentWindowRect(); - reshape( llmax(scroll_rect.getWidth(), total_width), running_height ); - - LLRect new_scroll_rect = mScrollContainer->getContentWindowRect(); - if (new_scroll_rect.getWidth() != scroll_rect.getWidth()) - { - reshape( llmax(scroll_rect.getWidth(), total_width), running_height ); - } - - // move item renamer text field to item's new position - updateRenamerPosition(); - - mTargetHeight = (F32)target_height; - return llround(mTargetHeight); -} - -const std::string LLFolderView::getFilterSubString(BOOL trim) -{ - return mFilter->getFilterSubString(trim); -} - -static LLFastTimer::DeclareTimer FTM_FILTER("Filter Inventory"); - -void LLFolderView::filter( LLInventoryFilter& filter ) -{ - LLFastTimer t2(FTM_FILTER); - filter.setFilterCount(llclamp(gSavedSettings.getS32("FilterItemsPerFrame"), 1, 5000)); - - if (getCompletedFilterGeneration() < filter.getCurrentGeneration()) - { - mPassedFilter = FALSE; - mMinWidth = 0; - LLFolderViewFolder::filter(filter); - } - else - { - mPassedFilter = TRUE; - } -} - -void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent) -{ - LLRect scroll_rect; - if (mScrollContainer) - { - LLView::reshape(width, height, called_from_parent); - scroll_rect = mScrollContainer->getContentWindowRect(); - } - width = llmax(mMinWidth, scroll_rect.getWidth()); - height = llmax(mRunningHeight, scroll_rect.getHeight()); - - // Restrict width within scroll container's width - if (mUseEllipses && mScrollContainer) - { - width = scroll_rect.getWidth(); - } - LLView::reshape(width, height, called_from_parent); - mReshapeSignal(mSelectedItems, FALSE); -} - -void LLFolderView::addToSelectionList(LLFolderViewItem* item) -{ - if (item->isSelected()) - { - removeFromSelectionList(item); - } - if (mSelectedItems.size()) - { - mSelectedItems.back()->setIsCurSelection(FALSE); - } - item->setIsCurSelection(TRUE); - mSelectedItems.push_back(item); -} - -void LLFolderView::removeFromSelectionList(LLFolderViewItem* item) -{ - if (mSelectedItems.size()) - { - mSelectedItems.back()->setIsCurSelection(FALSE); - } - - selected_items_t::iterator item_iter; - for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end();) - { - if (*item_iter == item) - { - item_iter = mSelectedItems.erase(item_iter); - } - else - { - ++item_iter; - } - } - if (mSelectedItems.size()) - { - mSelectedItems.back()->setIsCurSelection(TRUE); - } -} - -LLFolderViewItem* LLFolderView::getCurSelectedItem( void ) -{ - if(mSelectedItems.size()) - { - LLFolderViewItem* itemp = mSelectedItems.back(); - llassert(itemp->getIsCurSelection()); - return itemp; - } - return NULL; -} - - -// Record the selected item and pass it down the hierachy. -BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL openitem, - BOOL take_keyboard_focus) -{ - mSignalSelectCallback = take_keyboard_focus ? SIGNAL_KEYBOARD_FOCUS : SIGNAL_NO_KEYBOARD_FOCUS; - - if( selection == this ) - { - return FALSE; - } - - if( selection && take_keyboard_focus) - { - mParentPanel->setFocus(TRUE); - } - - // clear selection down here because change of keyboard focus can potentially - // affect selection - clearSelection(); - - if(selection) - { - addToSelectionList(selection); - } - - BOOL rv = LLFolderViewFolder::setSelection(selection, openitem, take_keyboard_focus); - if(openitem && selection) - { - selection->getParentFolder()->requestArrange(); - } - - llassert(mSelectedItems.size() <= 1); - - return rv; -} - -void LLFolderView::setSelectionByID(const LLUUID& obj_id, BOOL take_keyboard_focus) -{ - LLFolderViewItem* itemp = getItemByID(obj_id); - if(itemp && itemp->getListener()) - { - itemp->arrangeAndSet(TRUE, take_keyboard_focus); - mSelectThisID.setNull(); - return; - } - else - { - // save the desired item to be selected later (if/when ready) - mSelectThisID = obj_id; - } -} - -void LLFolderView::updateSelection() -{ - if (mSelectThisID.notNull()) - { - setSelectionByID(mSelectThisID, false); - } -} - -BOOL LLFolderView::changeSelection(LLFolderViewItem* selection, BOOL selected) -{ - BOOL rv = FALSE; - - // can't select root folder - if(!selection || selection == this) - { - return FALSE; - } - - if (!mAllowMultiSelect) - { - clearSelection(); - } - - selected_items_t::iterator item_iter; - for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter) - { - if (*item_iter == selection) - { - break; - } - } - - BOOL on_list = (item_iter != mSelectedItems.end()); - - if(selected && !on_list) - { - addToSelectionList(selection); - } - if(!selected && on_list) - { - removeFromSelectionList(selection); - } - - rv = LLFolderViewFolder::changeSelection(selection, selected); - - mSignalSelectCallback = SIGNAL_KEYBOARD_FOCUS; - - return rv; -} - -static LLFastTimer::DeclareTimer FTM_SANITIZE_SELECTION("Sanitize Selection"); -void LLFolderView::sanitizeSelection() -{ - LLFastTimer _(FTM_SANITIZE_SELECTION); - // store off current item in case it is automatically deselected - // and we want to preserve context - LLFolderViewItem* original_selected_item = getCurSelectedItem(); - - // Cache "Show all folders" filter setting - BOOL show_all_folders = (getRoot()->getFilter()->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS); - - std::vector<LLFolderViewItem*> items_to_remove; - selected_items_t::iterator item_iter; - for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter) - { - LLFolderViewItem* item = *item_iter; - - // ensure that each ancestor is open and potentially passes filtering - BOOL visible = item->potentiallyVisible(); // initialize from filter state for this item - // modify with parent open and filters states - LLFolderViewFolder* parent_folder = item->getParentFolder(); - if ( parent_folder ) - { - if ( show_all_folders ) - { // "Show all folders" is on, so this folder is visible - visible = TRUE; - } - else - { // Move up through parent folders and see what's visible - while(parent_folder) - { - visible = visible && parent_folder->isOpen() && parent_folder->potentiallyVisible(); - parent_folder = parent_folder->getParentFolder(); - } - } - } - - // deselect item if any ancestor is closed or didn't pass filter requirements. - if (!visible) - { - items_to_remove.push_back(item); - } - - // disallow nested selections (i.e. folder items plus one or more ancestors) - // could check cached mum selections count and only iterate if there are any - // but that may be a premature optimization. - selected_items_t::iterator other_item_iter; - for (other_item_iter = mSelectedItems.begin(); other_item_iter != mSelectedItems.end(); ++other_item_iter) - { - LLFolderViewItem* other_item = *other_item_iter; - for( parent_folder = other_item->getParentFolder(); parent_folder; parent_folder = parent_folder->getParentFolder()) - { - if (parent_folder == item) - { - // this is a descendent of the current folder, remove from list - items_to_remove.push_back(other_item); - break; - } - } - } - - // Don't allow invisible items (such as root folders) to be selected. - if (item == getRoot()) - { - items_to_remove.push_back(item); - } - } - - std::vector<LLFolderViewItem*>::iterator item_it; - for (item_it = items_to_remove.begin(); item_it != items_to_remove.end(); ++item_it ) - { - changeSelection(*item_it, FALSE); // toggle selection (also removes from list) - } - - // if nothing selected after prior constraints... - if (mSelectedItems.empty()) - { - // ...select first available parent of original selection - LLFolderViewItem* new_selection = NULL; - if (original_selected_item) - { - for(LLFolderViewFolder* parent_folder = original_selected_item->getParentFolder(); - parent_folder; - parent_folder = parent_folder->getParentFolder()) - { - if (parent_folder->potentiallyVisible()) - { - // give initial selection to first ancestor folder that potentially passes the filter - if (!new_selection) - { - new_selection = parent_folder; - } - - // if any ancestor folder of original item is closed, move the selection up - // to the highest closed - if (!parent_folder->isOpen()) - { - new_selection = parent_folder; - } - } - } - } - else - { - new_selection = NULL; - } - - if (new_selection) - { - setSelection(new_selection, FALSE, FALSE); - } - } -} - -void LLFolderView::clearSelection() -{ - for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); - item_it != mSelectedItems.end(); - ++item_it) - { - (*item_it)->setUnselected(); - } - - mSelectedItems.clear(); - mSelectThisID.setNull(); -} - -std::set<LLUUID> LLFolderView::getSelectionList() const -{ - std::set<LLUUID> selection; - for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); - item_it != mSelectedItems.end(); - ++item_it) - { - selection.insert((*item_it)->getListener()->getUUID()); - } - return selection; -} - -BOOL LLFolderView::startDrag(LLToolDragAndDrop::ESource source) -{ - std::vector<EDragAndDropType> types; - uuid_vec_t cargo_ids; - selected_items_t::iterator item_it; - BOOL can_drag = TRUE; - if (!mSelectedItems.empty()) - { - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - EDragAndDropType type = DAD_NONE; - LLUUID id = LLUUID::null; - can_drag = can_drag && (*item_it)->getListener()->startDrag(&type, &id); - - types.push_back(type); - cargo_ids.push_back(id); - } - - LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, source, mSourceID); - } - return can_drag; -} - -void LLFolderView::commitRename( const LLSD& data ) -{ - finishRenamingItem(); -} - -void LLFolderView::draw() -{ - static LLUIColor sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", LLColor4::white); - if (mDebugFilters) - { - std::string current_filter_string = llformat("Current Filter: %d, Least Filter: %d, Auto-accept Filter: %d", - mFilter->getCurrentGeneration(), mFilter->getMinRequiredGeneration(), mFilter->getMustPassGeneration()); - LLFontGL::getFontMonospace()->renderUTF8(current_filter_string, 0, 2, - getRect().getHeight() - LLFontGL::getFontMonospace()->getLineHeight(), LLColor4(0.5f, 0.5f, 0.8f, 1.f), - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); - } - - //LLFontGL* font = getLabelFontForStyle(mLabelStyle); - - // if cursor has moved off of me during drag and drop - // close all auto opened folders - if (!mDragAndDropThisFrame) - { - closeAutoOpenedFolders(); - } - - // while dragging, update selection rendering to reflect single/multi drag status - if (LLToolDragAndDrop::getInstance()->hasMouseCapture()) - { - EAcceptance last_accept = LLToolDragAndDrop::getInstance()->getLastAccept(); - if (last_accept == ACCEPT_YES_SINGLE || last_accept == ACCEPT_YES_COPY_SINGLE) - { - setShowSingleSelection(TRUE); - } - else - { - setShowSingleSelection(FALSE); - } - } - else - { - setShowSingleSelection(FALSE); - } - - - if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout") || !mSearchString.size()) - { - mSearchString.clear(); - } - - if (hasVisibleChildren() - || mFilter->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS) - { - mStatusText.clear(); - mStatusTextBox->setVisible( FALSE ); - } - else if (mShowEmptyMessage) - { - if (LLInventoryModelBackgroundFetch::instance().folderFetchActive() || mCompletedFilterGeneration < mFilter->getMinRequiredGeneration()) - { - mStatusText = LLTrans::getString("Searching"); - } - else - { - if (getFilter()) - { - LLStringUtil::format_map_t args; - args["[SEARCH_TERM]"] = LLURI::escape(getFilter()->getFilterSubStringOrig()); - mStatusText = LLTrans::getString(getFilter()->getEmptyLookupMessage(), args); - } - } - mStatusTextBox->setValue(mStatusText); - mStatusTextBox->setVisible( TRUE ); - - // firstly reshape message textbox with current size. This is necessary to - // LLTextBox::getTextPixelHeight works properly - const LLRect local_rect = getLocalRect(); - mStatusTextBox->setShape(local_rect); - - // get preferable text height... - S32 pixel_height = mStatusTextBox->getTextPixelHeight(); - bool height_changed = local_rect.getHeight() != pixel_height; - if (height_changed) - { - // ... if it does not match current height, lets rearrange current view. - // This will indirectly call ::arrange and reshape of the status textbox. - // We should call this method to also notify parent about required rect. - // See EXT-7564, EXT-7047. - arrangeFromRoot(); - LLUI::popMatrix(); - LLUI::pushMatrix(); - LLUI::translate((F32)getRect().mLeft, (F32)getRect().mBottom); - } - } - - // skip over LLFolderViewFolder::draw since we don't want the folder icon, label, - // and arrow for the root folder - LLView::draw(); - - mDragAndDropThisFrame = FALSE; -} - -void LLFolderView::finishRenamingItem( void ) -{ - if(!mRenamer) - { - return; - } - if( mRenameItem ) - { - mRenameItem->rename( mRenamer->getText() ); - } - - closeRenamer(); - - // List is re-sorted alphabeticly, so scroll to make sure the selected item is visible. - scrollToShowSelection(); -} - -void LLFolderView::closeRenamer( void ) -{ - if (mRenamer && mRenamer->getVisible()) - { - // Triggers onRenamerLost() that actually closes the renamer. - gViewerWindow->removePopup(mRenamer); - } -} - -void LLFolderView::removeSelectedItems( void ) -{ - if (mSelectedItems.empty()) return; - LLSD args; - args["QUESTION"] = LLTrans::getString(mSelectedItems.size() > 1 ? "DeleteItems" : "DeleteItem"); - LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&LLFolderView::onItemsRemovalConfirmation, this, _1, _2)); -} - -bool isDescendantOfASelectedItem(LLFolderViewItem* item, const std::vector<LLFolderViewItem*>& selectedItems) -{ - LLFolderViewItem* item_parent = dynamic_cast<LLFolderViewItem*>(item->getParent()); - - if (item_parent) - { - for(std::vector<LLFolderViewItem*>::const_iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) - { - const LLFolderViewItem* const selected_item = (*it); - - LLFolderViewItem* parent = item_parent; - - while (parent) - { - if (selected_item == parent) - { - return true; - } - - parent = dynamic_cast<LLFolderViewItem*>(parent->getParent()); - } - } - } - - return false; -} - -// static -void LLFolderView::removeCutItems() -{ - // There's no item in "cut" mode on the clipboard -> exit - if (!LLClipboard::instance().isCutMode()) - return; - - // Get the list of clipboard item uuids and iterate through them - LLDynamicArray<LLUUID> objects; - LLClipboard::instance().pasteFromClipboard(objects); - for (LLDynamicArray<LLUUID>::const_iterator iter = objects.begin(); - iter != objects.end(); - ++iter) - { - gInventory.removeObject(*iter); - } -} - -void LLFolderView::onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (option != 0) return; // canceled - - if(getVisible() && getEnabled()) - { - // just in case we're removing the renaming item. - mRenameItem = NULL; - - // create a temporary structure which we will use to remove - // items, since the removal will futz with internal data - // structures. - std::vector<LLFolderViewItem*> items; - S32 count = mSelectedItems.size(); - if(count == 0) return; - LLFolderViewItem* item = NULL; - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - item = *item_it; - if (item && item->isRemovable()) - { - items.push_back(item); - } - else - { - llinfos << "Cannot delete " << item->getName() << llendl; - return; - } - } - - // iterate through the new container. - count = items.size(); - LLUUID new_selection_id; - if(count == 1) - { - LLFolderViewItem* item_to_delete = items[0]; - LLFolderViewFolder* parent = item_to_delete->getParentFolder(); - LLFolderViewItem* new_selection = item_to_delete->getNextOpenNode(FALSE); - if (!new_selection) - { - new_selection = item_to_delete->getPreviousOpenNode(FALSE); - } - if(parent) - { - if (parent->removeItem(item_to_delete)) - { - // change selection on successful delete - if (new_selection) - { - setSelectionFromRoot(new_selection, new_selection->isOpen(), mParentPanel->hasFocus()); - } - else - { - setSelectionFromRoot(NULL, mParentPanel->hasFocus()); - } - } - } - arrangeAll(); - } - else if (count > 1) - { - LLDynamicArray<LLFolderViewEventListener*> listeners; - LLFolderViewEventListener* listener; - LLFolderViewItem* last_item = items[count - 1]; - LLFolderViewItem* new_selection = last_item->getNextOpenNode(FALSE); - while(new_selection && new_selection->isSelected()) - { - new_selection = new_selection->getNextOpenNode(FALSE); - } - if (!new_selection) - { - new_selection = last_item->getPreviousOpenNode(FALSE); - while (new_selection && (new_selection->isSelected() || isDescendantOfASelectedItem(new_selection, items))) - { - new_selection = new_selection->getPreviousOpenNode(FALSE); - } - } - if (new_selection) - { - setSelectionFromRoot(new_selection, new_selection->isOpen(), mParentPanel->hasFocus()); - } - else - { - setSelectionFromRoot(NULL, mParentPanel->hasFocus()); - } - - for(S32 i = 0; i < count; ++i) - { - listener = items[i]->getListener(); - if(listener && (listeners.find(listener) == LLDynamicArray<LLFolderViewEventListener*>::FAIL)) - { - listeners.put(listener); - } - } - listener = listeners.get(0); - if(listener) - { - listener->removeBatch(listeners); - } - } - arrangeAll(); - scrollToShowSelection(); - } -} - -// open the selected item. -void LLFolderView::openSelectedItems( void ) -{ - if(getVisible() && getEnabled()) - { - if (mSelectedItems.size() == 1) - { - mSelectedItems.front()->openItem(); - } - else - { - LLMultiPreview* multi_previewp = new LLMultiPreview(); - LLMultiProperties* multi_propertiesp = new LLMultiProperties(); - - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - // IT_{OBJECT,ATTACHMENT} creates LLProperties - // floaters; others create LLPreviews. Put - // each one in the right type of container. - LLFolderViewEventListener* listener = (*item_it)->getListener(); - bool is_prop = listener && (listener->getInventoryType() == LLInventoryType::IT_OBJECT || listener->getInventoryType() == LLInventoryType::IT_ATTACHMENT); - if (is_prop) - LLFloater::setFloaterHost(multi_propertiesp); - else - LLFloater::setFloaterHost(multi_previewp); - (*item_it)->openItem(); - } - - LLFloater::setFloaterHost(NULL); - // *NOTE: LLMulti* will safely auto-delete when open'd - // without any children. - multi_previewp->openFloater(LLSD()); - multi_propertiesp->openFloater(LLSD()); - } - } -} - -void LLFolderView::propertiesSelectedItems( void ) -{ - if(getVisible() && getEnabled()) - { - if (mSelectedItems.size() == 1) - { - LLFolderViewItem* folder_item = mSelectedItems.front(); - if(!folder_item) return; - folder_item->getListener()->showProperties(); - } - else - { - LLMultiProperties* multi_propertiesp = new LLMultiProperties(); - - LLFloater::setFloaterHost(multi_propertiesp); - - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - (*item_it)->getListener()->showProperties(); - } - - LLFloater::setFloaterHost(NULL); - multi_propertiesp->openFloater(LLSD()); - } - } -} - -void LLFolderView::changeType(LLInventoryModel *model, LLFolderType::EType new_folder_type) -{ - LLFolderBridge *folder_bridge = LLFolderBridge::sSelf.get(); - - if (!folder_bridge) return; - LLViewerInventoryCategory *cat = folder_bridge->getCategory(); - if (!cat) return; - cat->changeType(new_folder_type); -} - -void LLFolderView::autoOpenItem( LLFolderViewFolder* item ) -{ - if ((mAutoOpenItems.check() == item) || - (mAutoOpenItems.getDepth() >= (U32)AUTO_OPEN_STACK_DEPTH) || - item->isOpen()) - { - return; - } - - // close auto-opened folders - LLFolderViewFolder* close_item = mAutoOpenItems.check(); - while (close_item && close_item != item->getParentFolder()) - { - mAutoOpenItems.pop(); - close_item->setOpenArrangeRecursively(FALSE); - close_item = mAutoOpenItems.check(); - } - - item->requestArrange(); - - mAutoOpenItems.push(item); - - item->setOpen(TRUE); - LLRect content_rect = mScrollContainer->getContentWindowRect(); - LLRect constraint_rect(0,content_rect.getHeight(), content_rect.getWidth(), 0); - scrollToShowItem(item, constraint_rect); -} - -void LLFolderView::closeAutoOpenedFolders() -{ - while (mAutoOpenItems.check()) - { - LLFolderViewFolder* close_item = mAutoOpenItems.pop(); - close_item->setOpen(FALSE); - } - - if (mAutoOpenCandidate) - { - mAutoOpenCandidate->setAutoOpenCountdown(0.f); - } - mAutoOpenCandidate = NULL; - mAutoOpenTimer.stop(); -} - -BOOL LLFolderView::autoOpenTest(LLFolderViewFolder* folder) -{ - if (folder && mAutoOpenCandidate == folder) - { - if (mAutoOpenTimer.getStarted()) - { - if (!mAutoOpenCandidate->isOpen()) - { - mAutoOpenCandidate->setAutoOpenCountdown(clamp_rescale(mAutoOpenTimer.getElapsedTimeF32(), 0.f, sAutoOpenTime, 0.f, 1.f)); - } - if (mAutoOpenTimer.getElapsedTimeF32() > sAutoOpenTime) - { - autoOpenItem(folder); - mAutoOpenTimer.stop(); - return TRUE; - } - } - return FALSE; - } - - // otherwise new candidate, restart timer - if (mAutoOpenCandidate) - { - mAutoOpenCandidate->setAutoOpenCountdown(0.f); - } - mAutoOpenCandidate = folder; - mAutoOpenTimer.start(); - return FALSE; -} - -BOOL LLFolderView::canCopy() const -{ - if (!(getVisible() && getEnabled() && (mSelectedItems.size() > 0))) - { - return FALSE; - } - - for (selected_items_t::const_iterator selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it) - { - const LLFolderViewItem* item = *selected_it; - if (!item->getListener()->isItemCopyable()) - { - return FALSE; - } - } - return TRUE; -} - -// copy selected item -void LLFolderView::copy() -{ - // *NOTE: total hack to clear the inventory clipboard - LLClipboard::instance().reset(); - S32 count = mSelectedItems.size(); - if(getVisible() && getEnabled() && (count > 0)) - { - LLFolderViewEventListener* listener = NULL; - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - listener = (*item_it)->getListener(); - if(listener) - { - listener->copyToClipboard(); - } - } - } - mSearchString.clear(); -} - -BOOL LLFolderView::canCut() const -{ - if (!(getVisible() && getEnabled() && (mSelectedItems.size() > 0))) - { - return FALSE; - } - - for (selected_items_t::const_iterator selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it) - { - const LLFolderViewItem* item = *selected_it; - const LLFolderViewEventListener* listener = item->getListener(); - - if (!listener || !listener->isItemRemovable()) - { - return FALSE; - } - } - return TRUE; -} - -void LLFolderView::cut() -{ - // clear the inventory clipboard - LLClipboard::instance().reset(); - S32 count = mSelectedItems.size(); - if(getVisible() && getEnabled() && (count > 0)) - { - LLFolderViewEventListener* listener = NULL; - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - listener = (*item_it)->getListener(); - if(listener) - { - listener->cutToClipboard(); - } - } - LLFolderView::removeCutItems(); - } - mSearchString.clear(); -} - -BOOL LLFolderView::canPaste() const -{ - if (mSelectedItems.empty()) - { - return FALSE; - } - - if(getVisible() && getEnabled()) - { - for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); - item_it != mSelectedItems.end(); ++item_it) - { - // *TODO: only check folders and parent folders of items - const LLFolderViewItem* item = (*item_it); - const LLFolderViewEventListener* listener = item->getListener(); - if(!listener || !listener->isClipboardPasteable()) - { - const LLFolderViewFolder* folderp = item->getParentFolder(); - listener = folderp->getListener(); - if (!listener || !listener->isClipboardPasteable()) - { - return FALSE; - } - } - } - return TRUE; - } - return FALSE; -} - -// paste selected item -void LLFolderView::paste() -{ - if(getVisible() && getEnabled()) - { - // find set of unique folders to paste into - std::set<LLFolderViewItem*> folder_set; - - selected_items_t::iterator selected_it; - for (selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it) - { - LLFolderViewItem* item = *selected_it; - LLFolderViewEventListener* listener = item->getListener(); - if (listener->getInventoryType() != LLInventoryType::IT_CATEGORY) - { - item = item->getParentFolder(); - } - folder_set.insert(item); - } - - std::set<LLFolderViewItem*>::iterator set_iter; - for(set_iter = folder_set.begin(); set_iter != folder_set.end(); ++set_iter) - { - LLFolderViewEventListener* listener = (*set_iter)->getListener(); - if(listener && listener->isClipboardPasteable()) - { - listener->pasteFromClipboard(); - } - } - } - mSearchString.clear(); -} - -// public rename functionality - can only start the process -void LLFolderView::startRenamingSelectedItem( void ) -{ - // make sure selection is visible - scrollToShowSelection(); - - S32 count = mSelectedItems.size(); - LLFolderViewItem* item = NULL; - if(count > 0) - { - item = mSelectedItems.front(); - } - if(getVisible() && getEnabled() && (count == 1) && item && item->getListener() && - item->getListener()->isItemRenameable()) - { - mRenameItem = item; - - updateRenamerPosition(); - - - mRenamer->setText(item->getName()); - mRenamer->selectAll(); - mRenamer->setVisible( TRUE ); - // set focus will fail unless item is visible - mRenamer->setFocus( TRUE ); - mRenamer->setTopLostCallback(boost::bind(&LLFolderView::onRenamerLost, this)); - gViewerWindow->addPopup(mRenamer); - } -} - -BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) -{ - BOOL handled = FALSE; - - // SL-51858: Key presses are not being passed to the Popup menu. - // A proper fix is non-trivial so instead just close the menu. - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); - if (menu && menu->isOpen()) - { - LLMenuGL::sMenuContainer->hideMenus(); - } - - LLView *item = NULL; - if (getChildCount() > 0) - { - item = *(getChildList()->begin()); - } - - switch( key ) - { - case KEY_F2: - mSearchString.clear(); - startRenamingSelectedItem(); - handled = TRUE; - break; - - case KEY_RETURN: - if (mask == MASK_NONE) - { - if( mRenameItem && mRenamer->getVisible() ) - { - finishRenamingItem(); - mSearchString.clear(); - handled = TRUE; - } - else - { - LLFolderView::openSelectedItems(); - handled = TRUE; - } - } - break; - - case KEY_ESCAPE: - if( mRenameItem && mRenamer->getVisible() ) - { - closeRenamer(); - handled = TRUE; - } - mSearchString.clear(); - break; - - case KEY_PAGE_UP: - mSearchString.clear(); - mScrollContainer->pageUp(30); - handled = TRUE; - break; - - case KEY_PAGE_DOWN: - mSearchString.clear(); - mScrollContainer->pageDown(30); - handled = TRUE; - break; - - case KEY_HOME: - mSearchString.clear(); - mScrollContainer->goToTop(); - handled = TRUE; - break; - - case KEY_END: - mSearchString.clear(); - mScrollContainer->goToBottom(); - break; - - case KEY_DOWN: - if((mSelectedItems.size() > 0) && mScrollContainer) - { - LLFolderViewItem* last_selected = getCurSelectedItem(); - - if (!mKeyboardSelection) - { - setSelection(last_selected, FALSE, TRUE); - mKeyboardSelection = TRUE; - } - - LLFolderViewItem* next = NULL; - if (mask & MASK_SHIFT) - { - // don't shift select down to children of folders (they are implicitly selected through parent) - next = last_selected->getNextOpenNode(FALSE); - if (next) - { - if (next->isSelected()) - { - // shrink selection - changeSelectionFromRoot(last_selected, FALSE); - } - else if (last_selected->getParentFolder() == next->getParentFolder()) - { - // grow selection - changeSelectionFromRoot(next, TRUE); - } - } - } - else - { - next = last_selected->getNextOpenNode(); - if( next ) - { - if (next == last_selected) - { - //special case for LLAccordionCtrl - if(notifyParent(LLSD().with("action","select_next")) > 0 )//message was processed - { - clearSelection(); - return TRUE; - } - return FALSE; - } - setSelection( next, FALSE, TRUE ); - } - else - { - //special case for LLAccordionCtrl - if(notifyParent(LLSD().with("action","select_next")) > 0 )//message was processed - { - clearSelection(); - return TRUE; - } - return FALSE; - } - } - scrollToShowSelection(); - mSearchString.clear(); - handled = TRUE; - } - break; - - case KEY_UP: - if((mSelectedItems.size() > 0) && mScrollContainer) - { - LLFolderViewItem* last_selected = mSelectedItems.back(); - - if (!mKeyboardSelection) - { - setSelection(last_selected, FALSE, TRUE); - mKeyboardSelection = TRUE; - } - - LLFolderViewItem* prev = NULL; - if (mask & MASK_SHIFT) - { - // don't shift select down to children of folders (they are implicitly selected through parent) - prev = last_selected->getPreviousOpenNode(FALSE); - if (prev) - { - if (prev->isSelected()) - { - // shrink selection - changeSelectionFromRoot(last_selected, FALSE); - } - else if (last_selected->getParentFolder() == prev->getParentFolder()) - { - // grow selection - changeSelectionFromRoot(prev, TRUE); - } - } - } - else - { - prev = last_selected->getPreviousOpenNode(); - if( prev ) - { - if (prev == this) - { - // If case we are in accordion tab notify parent to go to the previous accordion - if(notifyParent(LLSD().with("action","select_prev")) > 0 )//message was processed - { - clearSelection(); - return TRUE; - } - - return FALSE; - } - setSelection( prev, FALSE, TRUE ); - } - } - scrollToShowSelection(); - mSearchString.clear(); - - handled = TRUE; - } - break; - - case KEY_RIGHT: - if(mSelectedItems.size()) - { - LLFolderViewItem* last_selected = getCurSelectedItem(); - last_selected->setOpen( TRUE ); - mSearchString.clear(); - handled = TRUE; - } - break; - - case KEY_LEFT: - if(mSelectedItems.size()) - { - LLFolderViewItem* last_selected = getCurSelectedItem(); - LLFolderViewItem* parent_folder = last_selected->getParentFolder(); - if (!last_selected->isOpen() && parent_folder && parent_folder->getParentFolder()) - { - setSelection(parent_folder, FALSE, TRUE); - } - else - { - last_selected->setOpen( FALSE ); - } - mSearchString.clear(); - scrollToShowSelection(); - handled = TRUE; - } - break; - } - - if (!handled && mParentPanel->hasFocus()) - { - if (key == KEY_BACKSPACE) - { - mSearchTimer.reset(); - if (mSearchString.size()) - { - mSearchString.erase(mSearchString.size() - 1, 1); - } - search(getCurSelectedItem(), mSearchString, FALSE); - handled = TRUE; - } - } - - return handled; -} - - -BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char) -{ - if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL - { - return FALSE; - } - - if (uni_char > 0x7f) - { - llwarns << "LLFolderView::handleUnicodeCharHere - Don't handle non-ascii yet, aborting" << llendl; - return FALSE; - } - - BOOL handled = FALSE; - if (mParentPanel->hasFocus()) - { - // SL-51858: Key presses are not being passed to the Popup menu. - // A proper fix is non-trivial so instead just close the menu. - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); - if (menu && menu->isOpen()) - { - LLMenuGL::sMenuContainer->hideMenus(); - } - - //do text search - if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout")) - { - mSearchString.clear(); - } - mSearchTimer.reset(); - if (mSearchString.size() < 128) - { - mSearchString += uni_char; - } - search(getCurSelectedItem(), mSearchString, FALSE); - - handled = TRUE; - } - - return handled; -} - - -BOOL LLFolderView::canDoDelete() const -{ - if (mSelectedItems.size() == 0) return FALSE; - - for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - if (!(*item_it)->getListener()->isItemRemovable()) - { - return FALSE; - } - } - return TRUE; -} - -void LLFolderView::doDelete() -{ - if(mSelectedItems.size() > 0) - { - removeSelectedItems(); - } -} - - -BOOL LLFolderView::handleMouseDown( S32 x, S32 y, MASK mask ) -{ - mKeyboardSelection = FALSE; - mSearchString.clear(); - - mParentPanel->setFocus(TRUE); - - LLEditMenuHandler::gEditMenuHandler = this; - - return LLView::handleMouseDown( x, y, mask ); -} - -BOOL LLFolderView::search(LLFolderViewItem* first_item, const std::string &search_string, BOOL backward) -{ - // get first selected item - LLFolderViewItem* search_item = first_item; - - // make sure search string is upper case - std::string upper_case_string = search_string; - LLStringUtil::toUpper(upper_case_string); - - // if nothing selected, select first item in folder - if (!search_item) - { - // start from first item - search_item = getNextFromChild(NULL); - } - - // search over all open nodes for first substring match (with wrapping) - BOOL found = FALSE; - LLFolderViewItem* original_search_item = search_item; - do - { - // wrap at end - if (!search_item) - { - if (backward) - { - search_item = getPreviousFromChild(NULL); - } - else - { - search_item = getNextFromChild(NULL); - } - if (!search_item || search_item == original_search_item) - { - break; - } - } - - const std::string current_item_label(search_item->getSearchableLabel()); - S32 search_string_length = llmin(upper_case_string.size(), current_item_label.size()); - if (!current_item_label.compare(0, search_string_length, upper_case_string)) - { - found = TRUE; - break; - } - if (backward) - { - search_item = search_item->getPreviousOpenNode(); - } - else - { - search_item = search_item->getNextOpenNode(); - } - - } while(search_item != original_search_item); - - - if (found) - { - setSelection(search_item, FALSE, TRUE); - scrollToShowSelection(); - } - - return found; -} - -BOOL LLFolderView::handleDoubleClick( S32 x, S32 y, MASK mask ) -{ - // skip LLFolderViewFolder::handleDoubleClick() - return LLView::handleDoubleClick( x, y, mask ); -} - -BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask ) -{ - // all user operations move keyboard focus to inventory - // this way, we know when to stop auto-updating a search - mParentPanel->setFocus(TRUE); - - BOOL handled = childrenHandleRightMouseDown(x, y, mask) != NULL; - S32 count = mSelectedItems.size(); - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); - if ( handled - && ( count > 0 && (hasVisibleChildren() || mFilter->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS) ) // show menu only if selected items are visible - && menu ) - { - if (mCallbackRegistrar) - mCallbackRegistrar->pushScope(); - - updateMenuOptions(menu); - - menu->updateParent(LLMenuGL::sMenuContainer); - LLMenuGL::showPopup(this, menu, x, y); - if (mCallbackRegistrar) - mCallbackRegistrar->popScope(); - } - else - { - if (menu && menu->getVisible()) - { - menu->setVisible(FALSE); - } - setSelection(NULL, FALSE, TRUE); - } - return handled; -} - -// Add "--no options--" if the menu is completely blank. -BOOL LLFolderView::addNoOptions(LLMenuGL* menu) const -{ - const std::string nooptions_str = "--no options--"; - LLView *nooptions_item = NULL; - - const LLView::child_list_t *list = menu->getChildList(); - for (LLView::child_list_t::const_iterator itor = list->begin(); - itor != list->end(); - ++itor) - { - LLView *menu_item = (*itor); - if (menu_item->getVisible()) - { - return FALSE; - } - std::string name = menu_item->getName(); - if (menu_item->getName() == nooptions_str) - { - nooptions_item = menu_item; - } - } - if (nooptions_item) - { - nooptions_item->setVisible(TRUE); - nooptions_item->setEnabled(FALSE); - return TRUE; - } - return FALSE; -} - -BOOL LLFolderView::handleHover( S32 x, S32 y, MASK mask ) -{ - return LLView::handleHover( x, y, mask ); -} - -BOOL LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - mDragAndDropThisFrame = TRUE; - // have children handle it first - BOOL handled = LLView::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, - accept, tooltip_msg); - - // when drop is not handled by child, it should be handled - // by the folder which is the hierarchy root. - if (!handled) - { - if (getListener()->getUUID().notNull()) - { - handled = LLFolderViewFolder::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); - } - else - { - if (!mFolders.empty()) - { - // dispatch to last folder as a hack to support "Contents" folder in object inventory - handled = mFolders.back()->handleDragAndDropFromChild(mask,drop,cargo_type,cargo_data,accept,tooltip_msg); - } - } - } - - if (handled) - { - lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderView" << llendl; - } - - return handled; -} - -void LLFolderView::deleteAllChildren() -{ - closeRenamer(); - if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die(); - mPopupMenuHandle = LLHandle<LLView>(); - mScrollContainer = NULL; - mRenameItem = NULL; - mRenamer = NULL; - mStatusTextBox = NULL; - - clearSelection(); - LLView::deleteAllChildren(); -} - -void LLFolderView::scrollToShowSelection() -{ - if ( mSelectedItems.size() ) - { - mNeedsScroll = TRUE; - } -} - -// If the parent is scroll container, scroll it to make the selection -// is maximally visible. -void LLFolderView::scrollToShowItem(LLFolderViewItem* item, const LLRect& constraint_rect) -{ - if (!mScrollContainer) return; - - // don't scroll to items when mouse is being used to scroll/drag and drop - if (gFocusMgr.childHasMouseCapture(mScrollContainer)) - { - mNeedsScroll = FALSE; - return; - } - - // if item exists and is in visible portion of parent folder... - if(item) - { - LLRect local_rect = item->getLocalRect(); - LLRect item_scrolled_rect; // item position relative to display area of scroller - LLRect visible_doc_rect = mScrollContainer->getVisibleContentRect(); - - S32 icon_height = mIcon.isNull() ? 0 : mIcon->getHeight(); - S32 label_height = getLabelFontForStyle(mLabelStyle)->getLineHeight(); - // when navigating with keyboard, only move top of opened folder on screen, otherwise show whole folder - S32 max_height_to_show = item->isOpen() && mScrollContainer->hasFocus() ? (llmax( icon_height, label_height ) + ICON_PAD) : local_rect.getHeight(); - - // get portion of item that we want to see... - LLRect item_local_rect = LLRect(item->getIndentation(), - local_rect.getHeight(), - llmin(MIN_ITEM_WIDTH_VISIBLE, local_rect.getWidth()), - llmax(0, local_rect.getHeight() - max_height_to_show)); - - LLRect item_doc_rect; - - item->localRectToOtherView(item_local_rect, &item_doc_rect, this); - - mScrollContainer->scrollToShowRect( item_doc_rect, constraint_rect ); - - } -} - -LLRect LLFolderView::getVisibleRect() -{ - S32 visible_height = mScrollContainer->getRect().getHeight(); - S32 visible_width = mScrollContainer->getRect().getWidth(); - LLRect visible_rect; - visible_rect.setLeftTopAndSize(-getRect().mLeft, visible_height - getRect().mBottom, visible_width, visible_height); - return visible_rect; -} - -BOOL LLFolderView::getShowSelectionContext() -{ - if (mShowSelectionContext) - { - return TRUE; - } - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); - if (menu && menu->getVisible()) - { - return TRUE; - } - return FALSE; -} - -void LLFolderView::setShowSingleSelection(BOOL show) -{ - if (show != mShowSingleSelection) - { - mMultiSelectionFadeTimer.reset(); - mShowSingleSelection = show; - } -} - -void LLFolderView::addItemID(const LLUUID& id, LLFolderViewItem* itemp) -{ - mItemMap[id] = itemp; -} - -void LLFolderView::removeItemID(const LLUUID& id) -{ - mItemMap.erase(id); -} - -LLFastTimer::DeclareTimer FTM_GET_ITEM_BY_ID("Get FolderViewItem by ID"); -LLFolderViewItem* LLFolderView::getItemByID(const LLUUID& id) -{ - LLFastTimer _(FTM_GET_ITEM_BY_ID); - if (id == getListener()->getUUID()) - { - return this; - } - - std::map<LLUUID, LLFolderViewItem*>::iterator map_it; - map_it = mItemMap.find(id); - if (map_it != mItemMap.end()) - { - return map_it->second; - } - - return NULL; -} - -LLFolderViewFolder* LLFolderView::getFolderByID(const LLUUID& id) -{ - if (id == getListener()->getUUID()) - { - return this; - } - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end(); - ++iter) - { - LLFolderViewFolder *folder = (*iter); - if (folder->getListener()->getUUID() == id) - { - return folder; - } - } - return NULL; -} - -bool LLFolderView::doToSelected(LLInventoryModel* model, const LLSD& userdata) -{ - std::string action = userdata.asString(); - - if ("rename" == action) - { - startRenamingSelectedItem(); - return true; - } - if ("delete" == action) - { - removeSelectedItems(); - return true; - } - if (("copy" == action) || ("cut" == action)) - { - // Clear the clipboard before we start adding things on it - LLClipboard::instance().reset(); - } - - static const std::string change_folder_string = "change_folder_type_"; - if (action.length() > change_folder_string.length() && - (action.compare(0,change_folder_string.length(),"change_folder_type_") == 0)) - { - LLFolderType::EType new_folder_type = LLViewerFolderType::lookupTypeFromXUIName(action.substr(change_folder_string.length())); - changeType(model, new_folder_type); - return true; - } - - - std::set<LLUUID> selected_items = getSelectionList(); - - LLMultiPreview* multi_previewp = NULL; - LLMultiProperties* multi_propertiesp = NULL; - - if (("task_open" == action || "open" == action) && selected_items.size() > 1) - { - multi_previewp = new LLMultiPreview(); - gFloaterView->addChild(multi_previewp); - - LLFloater::setFloaterHost(multi_previewp); - - } - else if (("task_properties" == action || "properties" == action) && selected_items.size() > 1) - { - multi_propertiesp = new LLMultiProperties(); - gFloaterView->addChild(multi_propertiesp); - - LLFloater::setFloaterHost(multi_propertiesp); - } - - std::set<LLUUID>::iterator set_iter; - - for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) - { - LLFolderViewItem* folder_item = getItemByID(*set_iter); - if(!folder_item) continue; - LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getListener(); - if(!bridge) continue; - bridge->performAction(model, action); - } - - LLFloater::setFloaterHost(NULL); - if (multi_previewp) - { - multi_previewp->openFloater(LLSD()); - } - else if (multi_propertiesp) - { - multi_propertiesp->openFloater(LLSD()); - } - - return true; -} - -static LLFastTimer::DeclareTimer FTM_AUTO_SELECT("Open and Select"); -static LLFastTimer::DeclareTimer FTM_INVENTORY("Inventory"); - -// Main idle routine -void LLFolderView::doIdle() -{ - // If this is associated with the user's inventory, don't do anything - // until that inventory is loaded up. - const LLInventoryPanel *inventory_panel = dynamic_cast<LLInventoryPanel*>(mParentPanel); - if (inventory_panel && !inventory_panel->getIsViewsInitialized()) - { - return; - } - - LLFastTimer t2(FTM_INVENTORY); - - BOOL debug_filters = gSavedSettings.getBOOL("DebugInventoryFilters"); - if (debug_filters != getDebugFilters()) - { - mDebugFilters = debug_filters; - arrangeAll(); - } - - mNeedsAutoSelect = mFilter->hasFilterString() && - !(gFocusMgr.childHasKeyboardFocus(this) || gFocusMgr.getMouseCapture()); - - - if (mFilter->isModified() && mFilter->isNotDefault()) - { - mNeedsAutoSelect = TRUE; - } - mFilter->clearModified(); - - // filter to determine visibility before arranging - filterFromRoot(); - - // automatically show matching items, and select first one if we had a selection - if (mNeedsAutoSelect) - { - LLFastTimer t3(FTM_AUTO_SELECT); - // select new item only if a filtered item not currently selected - LLFolderViewItem* selected_itemp = mSelectedItems.empty() ? NULL : mSelectedItems.back(); - if (!mAutoSelectOverride && (!selected_itemp || !selected_itemp->potentiallyFiltered())) - { - // these are named variables to get around gcc not binding non-const references to rvalues - // and functor application is inherently non-const to allow for stateful functors - LLSelectFirstFilteredItem functor; - applyFunctorRecursively(functor); - } - - // Open filtered folders for folder views with mAutoSelectOverride=TRUE. - // Used by LLPlacesFolderView. - if (mAutoSelectOverride && !mFilter->getFilterSubString().empty()) - { - // these are named variables to get around gcc not binding non-const references to rvalues - // and functor application is inherently non-const to allow for stateful functors - LLOpenFilteredFolders functor; - applyFunctorRecursively(functor); - } - - scrollToShowSelection(); - } - - BOOL filter_finished = mCompletedFilterGeneration >= mFilter->getCurrentGeneration() - && !LLInventoryModelBackgroundFetch::instance().folderFetchActive(); - if (filter_finished - || gFocusMgr.childHasKeyboardFocus(inventory_panel) - || gFocusMgr.childHasMouseCapture(inventory_panel)) - { - // finishing the filter process, giving focus to the folder view, or dragging the scrollbar all stop the auto select process - mNeedsAutoSelect = FALSE; - } - - - // during filtering process, try to pin selected item's location on screen - // this will happen when searching your inventory and when new items arrive - if (!filter_finished) - { - // calculate rectangle to pin item to at start of animated rearrange - if (!mPinningSelectedItem && !mSelectedItems.empty()) - { - // lets pin it! - mPinningSelectedItem = TRUE; - - LLRect visible_content_rect = mScrollContainer->getVisibleContentRect(); - LLFolderViewItem* selected_item = mSelectedItems.back(); - - LLRect item_rect; - selected_item->localRectToOtherView(selected_item->getLocalRect(), &item_rect, this); - // if item is visible in scrolled region - if (visible_content_rect.overlaps(item_rect)) - { - // then attempt to keep it in same place on screen - mScrollConstraintRect = item_rect; - mScrollConstraintRect.translate(-visible_content_rect.mLeft, -visible_content_rect.mBottom); - } - else - { - // otherwise we just want it onscreen somewhere - LLRect content_rect = mScrollContainer->getContentWindowRect(); - mScrollConstraintRect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight()); - } - } - } - else - { - // stop pinning selected item after folders stop rearranging - if (!needsArrange()) - { - mPinningSelectedItem = FALSE; - } - } - - LLRect constraint_rect; - if (mPinningSelectedItem) - { - // use last known constraint rect for pinned item - constraint_rect = mScrollConstraintRect; - } - else - { - // during normal use (page up/page down, etc), just try to fit item on screen - LLRect content_rect = mScrollContainer->getContentWindowRect(); - constraint_rect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight()); - } - - - BOOL is_visible = isInVisibleChain(); - - if ( is_visible ) - { - sanitizeSelection(); - if( needsArrange() ) - { - arrangeFromRoot(); - } - } - - if (mSelectedItems.size() && mNeedsScroll) - { - scrollToShowItem(mSelectedItems.back(), constraint_rect); - // continue scrolling until animated layout change is done - if (filter_finished - && (!needsArrange() || !is_visible)) - { - mNeedsScroll = FALSE; - } - } - - if (mSignalSelectCallback) - { - //RN: we use keyboard focus as a proxy for user-explicit actions - BOOL take_keyboard_focus = (mSignalSelectCallback == SIGNAL_KEYBOARD_FOCUS); - mSelectSignal(mSelectedItems, take_keyboard_focus); - } - mSignalSelectCallback = FALSE; -} - - -//static -void LLFolderView::idle(void* user_data) -{ - LLFolderView* self = (LLFolderView*)user_data; - if ( self ) - { // Do the real idle - self->doIdle(); - } -} - -void LLFolderView::dumpSelectionInformation() -{ - llinfos << "LLFolderView::dumpSelectionInformation()" << llendl; - llinfos << "****************************************" << llendl; - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - llinfos << " " << (*item_it)->getName() << llendl; - } - llinfos << "****************************************" << llendl; -} - -void LLFolderView::updateRenamerPosition() -{ - if(mRenameItem) - { - // See also LLFolderViewItem::draw() - S32 x = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mRenameItem->getIndentation(); - S32 y = mRenameItem->getRect().getHeight() - mRenameItem->getItemHeight() - RENAME_HEIGHT_PAD; - mRenameItem->localPointToScreen( x, y, &x, &y ); - screenPointToLocal( x, y, &x, &y ); - mRenamer->setOrigin( x, y ); - - LLRect scroller_rect(0, 0, gViewerWindow->getWindowWidthScaled(), 0); - if (mScrollContainer) - { - scroller_rect = mScrollContainer->getContentWindowRect(); - } - - S32 width = llmax(llmin(mRenameItem->getRect().getWidth() - x, scroller_rect.getWidth() - x - getRect().mLeft), MINIMUM_RENAMER_WIDTH); - S32 height = mRenameItem->getItemHeight() - RENAME_HEIGHT_PAD; - mRenamer->reshape( width, height, TRUE ); - } -} - -// Update visibility and availability (i.e. enabled/disabled) of context menu items. -void LLFolderView::updateMenuOptions(LLMenuGL* menu) -{ - const LLView::child_list_t *list = menu->getChildList(); - - LLView::child_list_t::const_iterator menu_itor; - for (menu_itor = list->begin(); menu_itor != list->end(); ++menu_itor) - { - (*menu_itor)->setVisible(FALSE); - (*menu_itor)->pushVisible(TRUE); - (*menu_itor)->setEnabled(TRUE); - } - - // Successively filter out invalid options - - U32 flags = FIRST_SELECTED_ITEM; - for (selected_items_t::iterator item_itor = mSelectedItems.begin(); - item_itor != mSelectedItems.end(); - ++item_itor) - { - LLFolderViewItem* selected_item = (*item_itor); - selected_item->buildContextMenu(*menu, flags); - flags = 0x0; - } - - addNoOptions(menu); -} - -// Refresh the context menu (that is already shown). -void LLFolderView::updateMenu() -{ - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); - if (menu && menu->getVisible()) - { - updateMenuOptions(menu); - menu->needsArrange(); // update menu height if needed - } -} - -bool LLFolderView::selectFirstItem() -{ - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();++iter) - { - LLFolderViewFolder* folder = (*iter ); - if (folder->getVisible()) - { - LLFolderViewItem* itemp = folder->getNextFromChild(0,true); - if(itemp) - setSelection(itemp,FALSE,TRUE); - return true; - } - - } - for(items_t::iterator iit = mItems.begin(); - iit != mItems.end(); ++iit) - { - LLFolderViewItem* itemp = (*iit); - if (itemp->getVisible()) - { - setSelection(itemp,FALSE,TRUE); - return true; - } - } - return false; -} -bool LLFolderView::selectLastItem() -{ - for(items_t::reverse_iterator iit = mItems.rbegin(); - iit != mItems.rend(); ++iit) - { - LLFolderViewItem* itemp = (*iit); - if (itemp->getVisible()) - { - setSelection(itemp,FALSE,TRUE); - return true; - } - } - for (folders_t::reverse_iterator iter = mFolders.rbegin(); - iter != mFolders.rend();++iter) - { - LLFolderViewFolder* folder = (*iter); - if (folder->getVisible()) - { - LLFolderViewItem* itemp = folder->getPreviousFromChild(0,true); - if(itemp) - setSelection(itemp,FALSE,TRUE); - return true; - } - } - return false; -} - - -S32 LLFolderView::notify(const LLSD& info) -{ - if(info.has("action")) - { - std::string str_action = info["action"]; - if(str_action == "select_first") - { - setFocus(true); - selectFirstItem(); - scrollToShowSelection(); - return 1; - - } - else if(str_action == "select_last") - { - setFocus(true); - selectLastItem(); - scrollToShowSelection(); - return 1; - } - } - return 0; -} - - -///---------------------------------------------------------------------------- -/// Local function definitions -///---------------------------------------------------------------------------- - -void LLFolderView::onRenamerLost() -{ - if (mRenamer && mRenamer->getVisible()) - { - mRenamer->setVisible(FALSE); - - // will commit current name (which could be same as original name) - mRenamer->setFocus(FALSE); - } - - if( mRenameItem ) - { - setSelectionFromRoot( mRenameItem, TRUE ); - mRenameItem = NULL; - } -} - -LLInventoryFilter* LLFolderView::getFilter() -{ - return mFilter; -} - -void LLFolderView::setFilterPermMask( PermissionMask filter_perm_mask ) -{ - mFilter->setFilterPermissions(filter_perm_mask); -} - -U32 LLFolderView::getFilterObjectTypes() const -{ - return mFilter->getFilterObjectTypes(); -} - -PermissionMask LLFolderView::getFilterPermissions() const -{ - return mFilter->getFilterPermissions(); -} - -BOOL LLFolderView::isFilterModified() -{ - return mFilter->isNotDefault(); -} - -void delete_selected_item(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data); - fv->removeSelectedItems(); - } -} - -void copy_selected_item(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data); - fv->copy(); - } -} - -void paste_items(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data); - fv->paste(); - } -} - -void open_selected_items(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data); - fv->openSelectedItems(); - } -} - -void properties_selected_items(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data); - fv->propertiesSelectedItems(); - } -} diff --git a/indra/newview/llfolderview.h b/indra/newview/llfolderview.h deleted file mode 100644 index da8bb15f8e..0000000000 --- a/indra/newview/llfolderview.h +++ /dev/null @@ -1,375 +0,0 @@ -/** - * @file llfolderview.h - * @brief Definition of the folder view collection of classes. - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -/** - * - * The folder view collection of classes provides an interface for - * making a 'folder view' similar to the way the a single pane file - * folder interface works. - * - */ - -#ifndef LL_LLFOLDERVIEW_H -#define LL_LLFOLDERVIEW_H - -#include "llfolderviewitem.h" // because LLFolderView is-a LLFolderViewFolder - -#include "lluictrl.h" -#include "v4color.h" -#include "lldarray.h" -#include "stdenums.h" -#include "lldepthstack.h" -#include "lleditmenuhandler.h" -#include "llfontgl.h" -#include "llscrollcontainer.h" -#include "lltooldraganddrop.h" -#include "llviewertexture.h" - -class LLFolderViewEventListener; -class LLFolderViewFolder; -class LLFolderViewItem; -class LLInventoryModel; -class LLPanel; -class LLLineEditor; -class LLMenuGL; -class LLUICtrl; -class LLTextBox; - -/** - * Class LLFolderViewScrollContainer - * - * A scroll container which provides the information about the height - * of currently displayed folder view contents. - * Used for updating vertical scroll bar visibility in inventory panel. - * See LLScrollContainer::calcVisibleSize(). - */ -class LLFolderViewScrollContainer : public LLScrollContainer -{ -public: - /*virtual*/ ~LLFolderViewScrollContainer() {}; - /*virtual*/ const LLRect getScrolledViewRect() const; - -protected: - LLFolderViewScrollContainer(const LLScrollContainer::Params& p); - friend class LLUICtrlFactory; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLFolderView -// -// The LLFolderView represents the root level folder view object. -// It manages the screen region of the folder view. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLFolderView : public LLFolderViewFolder, public LLEditMenuHandler -{ -public: - struct Params : public LLInitParam::Block<Params, LLFolderViewFolder::Params> - { - Mandatory<LLPanel*> parent_panel; - Optional<LLUUID> task_id; - Optional<std::string> title; - Optional<bool> use_label_suffix, - allow_multiselect, - show_empty_message, - show_load_status, - use_ellipses; - - Params(); - }; - - friend class LLFolderViewScrollContainer; - - LLFolderView(const Params&); - virtual ~LLFolderView( void ); - - virtual BOOL canFocusChildren() const; - - virtual LLFolderView* getRoot() { return this; } - - // FolderViews default to sort by name. This will change that, - // and resort the items if necessary. - void setSortOrder(U32 order); - void setFilterPermMask(PermissionMask filter_perm_mask); - - typedef boost::signals2::signal<void (const std::deque<LLFolderViewItem*>& items, BOOL user_action)> signal_t; - void setSelectCallback(const signal_t::slot_type& cb) { mSelectSignal.connect(cb); } - void setReshapeCallback(const signal_t::slot_type& cb) { mReshapeSignal.connect(cb); } - - // filter is never null - LLInventoryFilter* getFilter(); - const std::string getFilterSubString(BOOL trim = FALSE); - U32 getFilterObjectTypes() const; - PermissionMask getFilterPermissions() const; - // *NOTE: use getFilter()->getShowFolderState(); - //LLInventoryFilter::EFolderShow getShowFolderState(); - U32 getSortOrder() const; - BOOL isFilterModified(); - - bool getAllowMultiSelect() { return mAllowMultiSelect; } - - // Close all folders in the view - void closeAllFolders(); - void openTopLevelFolders(); - - virtual void toggleOpen() {}; - virtual void setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse); - virtual BOOL addFolder( LLFolderViewFolder* folder); - - // Find width and height of this object and its children. Also - // makes sure that this view and its children are the right size. - virtual S32 arrange( S32* width, S32* height, S32 filter_generation ); - - void arrangeAll() { mArrangeGeneration++; } - S32 getArrangeGeneration() { return mArrangeGeneration; } - - // Apply filters to control visibility of inventory items - virtual void filter( LLInventoryFilter& filter); - - // Get the last selected item - virtual LLFolderViewItem* getCurSelectedItem( void ); - - // Record the selected item and pass it down the hierarchy. - virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem, - BOOL take_keyboard_focus); - - // Used by menu callbacks - void setSelectionByID(const LLUUID& obj_id, BOOL take_keyboard_focus); - - // Called once a frame to update the selection if mSelectThisID has been set - void updateSelection(); - - // This method is used to toggle the selection of an item. - // Walks children and keeps track of selected objects. - virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected); - - virtual std::set<LLUUID> getSelectionList() const; - - // Make sure if ancestor is selected, descendents are not - void sanitizeSelection(); - void clearSelection(); - void addToSelectionList(LLFolderViewItem* item); - void removeFromSelectionList(LLFolderViewItem* item); - - BOOL startDrag(LLToolDragAndDrop::ESource source); - void setDragAndDropThisFrame() { mDragAndDropThisFrame = TRUE; } - void setDraggingOverItem(LLFolderViewItem* item) { mDraggingOverItem = item; } - LLFolderViewItem* getDraggingOverItem() { return mDraggingOverItem; } - - // Deletion functionality - void removeSelectedItems(); - static void removeCutItems(); - - // Open the selected item - void openSelectedItems( void ); - void propertiesSelectedItems( void ); - - // Change the folder type - void changeType(LLInventoryModel *model, LLFolderType::EType new_folder_type); - - void autoOpenItem(LLFolderViewFolder* item); - void closeAutoOpenedFolders(); - BOOL autoOpenTest(LLFolderViewFolder* item); - - // Copy & paste - virtual void copy(); - virtual BOOL canCopy() const; - - virtual void cut(); - virtual BOOL canCut() const; - - virtual void paste(); - virtual BOOL canPaste() const; - - virtual void doDelete(); - virtual BOOL canDoDelete() const; - - // Public rename functionality - can only start the process - void startRenamingSelectedItem( void ); - - // These functions were used when there was only one folderview, - // and relied on that concept. This functionality is now handled - // by the listeners and the lldraganddroptool. - //LLFolderViewItem* getMovingItem() { return mMovingItem; } - //void setMovingItem( LLFolderViewItem* item ) { mMovingItem = item; } - //void dragItemIntoFolder( LLFolderViewItem* moving_item, LLFolderViewFolder* dst_folder, BOOL drop, BOOL* accept ); - //void dragFolderIntoFolder( LLFolderViewFolder* moving_folder, LLFolderViewFolder* dst_folder, BOOL drop, BOOL* accept ); - - // LLView functionality - ///*virtual*/ BOOL handleKey( KEY key, MASK mask, BOOL called_from_parent ); - /*virtual*/ BOOL handleKeyHere( KEY key, MASK mask ); - /*virtual*/ BOOL handleUnicodeCharHere(llwchar uni_char); - /*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask ); - /*virtual*/ BOOL handleDoubleClick( S32 x, S32 y, MASK mask ); - /*virtual*/ BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); - /*virtual*/ BOOL handleHover( S32 x, S32 y, MASK mask ); - /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); - /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); - /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) { setShowSelectionContext(FALSE); } - virtual void draw(); - virtual void deleteAllChildren(); - - void scrollToShowSelection(); - void scrollToShowItem(LLFolderViewItem* item, const LLRect& constraint_rect); - void setScrollContainer( LLScrollContainer* parent ) { mScrollContainer = parent; } - LLRect getVisibleRect(); - - BOOL search(LLFolderViewItem* first_item, const std::string &search_string, BOOL backward); - void setShowSelectionContext(BOOL show) { mShowSelectionContext = show; } - BOOL getShowSelectionContext(); - void setShowSingleSelection(BOOL show); - BOOL getShowSingleSelection() { return mShowSingleSelection; } - F32 getSelectionFadeElapsedTime() { return mMultiSelectionFadeTimer.getElapsedTimeF32(); } - bool getUseEllipses() { return mUseEllipses; } - - void addItemID(const LLUUID& id, LLFolderViewItem* itemp); - void removeItemID(const LLUUID& id); - LLFolderViewItem* getItemByID(const LLUUID& id); - LLFolderViewFolder* getFolderByID(const LLUUID& id); - - bool doToSelected(LLInventoryModel* model, const LLSD& userdata); - - void doIdle(); // Real idle routine - static void idle(void* user_data); // static glue to doIdle() - - BOOL needsAutoSelect() { return mNeedsAutoSelect && !mAutoSelectOverride; } - BOOL needsAutoRename() { return mNeedsAutoRename; } - void setNeedsAutoRename(BOOL val) { mNeedsAutoRename = val; } - void setPinningSelectedItem(BOOL val) { mPinningSelectedItem = val; } - void setAutoSelectOverride(BOOL val) { mAutoSelectOverride = val; } - - void setCallbackRegistrar(LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* registrar) { mCallbackRegistrar = registrar; } - - BOOL getDebugFilters() { return mDebugFilters; } - - LLPanel* getParentPanel() { return mParentPanel; } - // DEBUG only - void dumpSelectionInformation(); - - virtual S32 notify(const LLSD& info) ; - - bool useLabelSuffix() { return mUseLabelSuffix; } - void updateMenu(); - -private: - void updateMenuOptions(LLMenuGL* menu); - void updateRenamerPosition(); - -protected: - LLScrollContainer* mScrollContainer; // NULL if this is not a child of a scroll container. - - void commitRename( const LLSD& data ); - void onRenamerLost(); - - void finishRenamingItem( void ); - void closeRenamer( void ); - - bool selectFirstItem(); - bool selectLastItem(); - - BOOL addNoOptions(LLMenuGL* menu) const; - - void onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response); - -protected: - LLHandle<LLView> mPopupMenuHandle; - - typedef std::deque<LLFolderViewItem*> selected_items_t; - selected_items_t mSelectedItems; - BOOL mKeyboardSelection; - BOOL mAllowMultiSelect; - BOOL mShowEmptyMessage; - BOOL mShowFolderHierarchy; - LLUUID mSourceID; - - // Renaming variables and methods - LLFolderViewItem* mRenameItem; // The item currently being renamed - LLLineEditor* mRenamer; - - BOOL mNeedsScroll; - BOOL mPinningSelectedItem; - LLRect mScrollConstraintRect; - BOOL mNeedsAutoSelect; - BOOL mAutoSelectOverride; - BOOL mNeedsAutoRename; - bool mUseLabelSuffix; - - BOOL mDebugFilters; - U32 mSortOrder; - LLDepthStack<LLFolderViewFolder> mAutoOpenItems; - LLFolderViewFolder* mAutoOpenCandidate; - LLFrameTimer mAutoOpenTimer; - LLFrameTimer mSearchTimer; - std::string mSearchString; - LLInventoryFilter* mFilter; - BOOL mShowSelectionContext; - BOOL mShowSingleSelection; - LLFrameTimer mMultiSelectionFadeTimer; - S32 mArrangeGeneration; - - signal_t mSelectSignal; - signal_t mReshapeSignal; - S32 mSignalSelectCallback; - S32 mMinWidth; - S32 mRunningHeight; - std::map<LLUUID, LLFolderViewItem*> mItemMap; - BOOL mDragAndDropThisFrame; - - LLUUID mSelectThisID; // if non null, select this item - - LLPanel* mParentPanel; - - /** - * Is used to determine if we need to cut text In LLFolderViewItem to avoid horizontal scroll. - * NOTE: For now it's used only to cut LLFolderViewItem::mLabel text for Landmarks in Places Panel. - */ - bool mUseEllipses; // See EXT-719 - - /** - * Contains item under mouse pointer while dragging - */ - LLFolderViewItem* mDraggingOverItem; // See EXT-719 - - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* mCallbackRegistrar; - -public: - static F32 sAutoOpenTime; - LLTextBox* mStatusTextBox; - -}; - -bool sort_item_name(LLFolderViewItem* a, LLFolderViewItem* b); -bool sort_item_date(LLFolderViewItem* a, LLFolderViewItem* b); - -// Flags for buildContextMenu() -const U32 SUPPRESS_OPEN_ITEM = 0x1; -const U32 FIRST_SELECTED_ITEM = 0x2; - -#endif // LL_LLFOLDERVIEW_H diff --git a/indra/newview/llfoldervieweventlistener.h b/indra/newview/llfoldervieweventlistener.h deleted file mode 100644 index 06682dcbf1..0000000000 --- a/indra/newview/llfoldervieweventlistener.h +++ /dev/null @@ -1,103 +0,0 @@ -/** - * @file llfoldervieweventlistener.h - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ -#ifndef LLFOLDERVIEWEVENTLISTENER_H -#define LLFOLDERVIEWEVENTLISTENER_H - -#include "lldarray.h" // *TODO: convert to std::vector -#include "llfoldertype.h" -#include "llfontgl.h" // just for StyleFlags enum -#include "llinventorytype.h" -#include "llpermissionsflags.h" -#include "llpointer.h" -#include "llwearabletype.h" - - -class LLFolderViewItem; -class LLFolderView; -class LLFontGL; -class LLInventoryModel; -class LLMenuGL; -class LLScrollContainer; -class LLUIImage; -class LLUUID; - -// This is an abstract base class that users of the folderview classes -// would use to catch the useful events emitted from the folder -// views. -class LLFolderViewEventListener -{ -public: - virtual ~LLFolderViewEventListener( void ) {} - virtual const std::string& getName() const = 0; - virtual const std::string& getDisplayName() const = 0; - virtual const LLUUID& getUUID() const = 0; - virtual time_t getCreationDate() const = 0; // UTC seconds - virtual PermissionMask getPermissionMask() const = 0; - virtual LLFolderType::EType getPreferredType() const = 0; - virtual LLPointer<LLUIImage> getIcon() const = 0; - virtual LLPointer<LLUIImage> getOpenIcon() const { return getIcon(); } - virtual LLFontGL::StyleFlags getLabelStyle() const = 0; - virtual std::string getLabelSuffix() const = 0; - virtual void openItem( void ) = 0; - virtual void closeItem( void ) = 0; - virtual void previewItem( void ) = 0; - virtual void selectItem(void) = 0; - virtual void showProperties(void) = 0; - virtual BOOL isItemRenameable() const = 0; - virtual BOOL renameItem(const std::string& new_name) = 0; - virtual BOOL isItemMovable( void ) const = 0; // Can be moved to another folder - virtual BOOL isItemRemovable( void ) const = 0; // Can be destroyed - virtual BOOL isItemInTrash( void) const { return FALSE; } // TODO: make into pure virtual. - virtual BOOL removeItem() = 0; - virtual void removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch) = 0; - virtual void move( LLFolderViewEventListener* parent_listener ) = 0; - virtual BOOL isItemCopyable() const = 0; - virtual BOOL copyToClipboard() const = 0; - virtual BOOL cutToClipboard() const = 0; - virtual BOOL isClipboardPasteable() const = 0; - virtual void pasteFromClipboard() = 0; - virtual void pasteLinkFromClipboard() = 0; - virtual void buildContextMenu(LLMenuGL& menu, U32 flags) = 0; - virtual BOOL isUpToDate() const = 0; - virtual BOOL hasChildren() const = 0; - virtual LLInventoryType::EType getInventoryType() const = 0; - virtual void performAction(LLInventoryModel* model, std::string action) = 0; - virtual LLWearableType::EType getWearableType() const = 0; - - // This method should be called when a drag begins. returns TRUE - // if the drag can begin, otherwise FALSE. - virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const = 0; - - // This method will be called to determine if a drop can be - // performed, and will set drop to TRUE if a drop is - // requested. Returns TRUE if a drop is possible/happened, - // otherwise FALSE. - virtual BOOL dragOrDrop(MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - std::string& tooltip_msg) = 0; -}; - -#endif diff --git a/indra/newview/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp deleted file mode 100644 index 515e544452..0000000000 --- a/indra/newview/llfolderviewitem.cpp +++ /dev/null @@ -1,2897 +0,0 @@ -/** -* @file llfolderviewitem.cpp -* @brief Items and folders that can appear in a hierarchical folder view -* -* $LicenseInfo:firstyear=2001&license=viewerlgpl$ -* Second Life Viewer Source Code -* Copyright (C) 2010, Linden Research, Inc. -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public -* License as published by the Free Software Foundation; -* version 2.1 of the License only. -* -* This library is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this library; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -* -* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -* $/LicenseInfo$ -*/ -#include "llviewerprecompiledheaders.h" - -#include "llfolderviewitem.h" - -// viewer includes -#include "llfolderview.h" // Items depend extensively on LLFolderViews -#include "llfoldervieweventlistener.h" -#include "llviewerfoldertype.h" -#include "llinventorybridge.h" // for LLItemBridge in LLInventorySort::operator() -#include "llinventoryfilter.h" -#include "llinventoryfunctions.h" -#include "llinventorymodelbackgroundfetch.h" -#include "llpanel.h" -#include "llviewercontrol.h" // gSavedSettings -#include "llviewerwindow.h" // Argh, only for setCursor() - -// linden library includes -#include "llclipboard.h" -#include "llfocusmgr.h" // gFocusMgr -#include "lltrans.h" - -///---------------------------------------------------------------------------- -/// Class LLFolderViewItem -///---------------------------------------------------------------------------- - -static LLDefaultChildRegistry::Register<LLFolderViewItem> r("folder_view_item"); - -// statics -std::map<U8, LLFontGL*> LLFolderViewItem::sFonts; // map of styles to fonts - -// only integers can be initialized in header -const F32 LLFolderViewItem::FOLDER_CLOSE_TIME_CONSTANT = 0.02f; -const F32 LLFolderViewItem::FOLDER_OPEN_TIME_CONSTANT = 0.03f; - -const LLColor4U DEFAULT_WHITE(255, 255, 255); - - -//static -LLFontGL* LLFolderViewItem::getLabelFontForStyle(U8 style) -{ - LLFontGL* rtn = sFonts[style]; - if (!rtn) // grab label font with this style, lazily - { - LLFontDescriptor labelfontdesc("SansSerif", "Small", style); - rtn = LLFontGL::getFont(labelfontdesc); - if (!rtn) - { - rtn = LLFontGL::getFontDefault(); - } - sFonts[style] = rtn; - } - return rtn; -} - -//static -void LLFolderViewItem::initClass() -{ -} - -//static -void LLFolderViewItem::cleanupClass() -{ - sFonts.clear(); -} - - -// NOTE: Optimize this, we call it a *lot* when opening a large inventory -LLFolderViewItem::Params::Params() -: icon(), - icon_open(), - icon_overlay(), - root(), - listener(), - folder_arrow_image("folder_arrow_image"), - folder_indentation("folder_indentation"), - selection_image("selection_image"), - item_height("item_height"), - item_top_pad("item_top_pad"), - creation_date() -{} - -// Default constructor -LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) -: LLView(p), - mLabelWidth(0), - mLabelWidthDirty(false), - mParentFolder( NULL ), - mIsSelected( FALSE ), - mIsCurSelection( FALSE ), - mSelectPending(FALSE), - mLabelStyle( LLFontGL::NORMAL ), - mHasVisibleChildren(FALSE), - mIndentation(0), - mItemHeight(p.item_height), - mPassedFilter(FALSE), - mLastFilterGeneration(-1), - mStringMatchOffset(std::string::npos), - mControlLabelRotation(0.f), - mDragAndDropTarget(FALSE), - mIsLoading(FALSE), - mLabel(p.name), - mRoot(p.root), - mCreationDate(p.creation_date), - mIcon(p.icon), - mIconOpen(p.icon_open), - mIconOverlay(p.icon_overlay), - mListener(p.listener), - mShowLoadStatus(false), - mIsMouseOverTitle(false) -{ -} - -BOOL LLFolderViewItem::postBuild() -{ - refresh(); - return TRUE; -} - -// Destroys the object -LLFolderViewItem::~LLFolderViewItem( void ) -{ - delete mListener; - mListener = NULL; -} - -LLFolderView* LLFolderViewItem::getRoot() -{ - return mRoot; -} - -// Returns true if this object is a child (or grandchild, etc.) of potential_ancestor. -BOOL LLFolderViewItem::isDescendantOf( const LLFolderViewFolder* potential_ancestor ) -{ - LLFolderViewItem* root = this; - while( root->mParentFolder ) - { - if( root->mParentFolder == potential_ancestor ) - { - return TRUE; - } - root = root->mParentFolder; - } - return FALSE; -} - -LLFolderViewItem* LLFolderViewItem::getNextOpenNode(BOOL include_children) -{ - if (!mParentFolder) - { - return NULL; - } - - LLFolderViewItem* itemp = mParentFolder->getNextFromChild( this, include_children ); - while(itemp && !itemp->getVisible()) - { - LLFolderViewItem* next_itemp = itemp->mParentFolder->getNextFromChild( itemp, include_children ); - if (itemp == next_itemp) - { - // hit last item - return itemp->getVisible() ? itemp : this; - } - itemp = next_itemp; - } - - return itemp; -} - -LLFolderViewItem* LLFolderViewItem::getPreviousOpenNode(BOOL include_children) -{ - if (!mParentFolder) - { - return NULL; - } - - LLFolderViewItem* itemp = mParentFolder->getPreviousFromChild( this, include_children ); - - // Skip over items that are invisible or are hidden from the UI. - while(itemp && !itemp->getVisible()) - { - LLFolderViewItem* next_itemp = itemp->mParentFolder->getPreviousFromChild( itemp, include_children ); - if (itemp == next_itemp) - { - // hit first item - return itemp->getVisible() ? itemp : this; - } - itemp = next_itemp; - } - - return itemp; -} - -// is this item something we think we should be showing? -// for example, if we haven't gotten around to filtering it yet, then the answer is yes -// until we find out otherwise -BOOL LLFolderViewItem::potentiallyVisible() -{ - // we haven't been checked against min required filter - // or we have and we passed - return potentiallyFiltered(); -} - -BOOL LLFolderViewItem::potentiallyFiltered() -{ - return getLastFilterGeneration() < getRoot()->getFilter()->getMinRequiredGeneration() || getFiltered(); -} - -BOOL LLFolderViewItem::getFiltered() -{ - return mPassedFilter && mLastFilterGeneration >= getRoot()->getFilter()->getMinRequiredGeneration(); -} - -BOOL LLFolderViewItem::getFiltered(S32 filter_generation) -{ - return mPassedFilter && mLastFilterGeneration >= filter_generation; -} - -void LLFolderViewItem::setFiltered(BOOL filtered, S32 filter_generation) -{ - mPassedFilter = filtered; - mLastFilterGeneration = filter_generation; -} - -void LLFolderViewItem::setIcon(LLUIImagePtr icon) -{ - mIcon = icon; -} - -// refresh information from the listener -void LLFolderViewItem::refreshFromListener() -{ - if(mListener) - { - mLabel = mListener->getDisplayName(); - LLFolderType::EType preferred_type = mListener->getPreferredType(); - - // *TODO: to be removed when database supports multi language. This is a - // temporary attempt to display the inventory folder in the user locale. - // mantipov: *NOTE: be sure this code is synchronized with LLFriendCardsManager::findChildFolderUUID - // it uses the same way to find localized string - - // HACK: EXT - 6028 ([HARD CODED]? Inventory > Library > "Accessories" folder) - // Translation of Accessories folder in Library inventory folder - bool accessories = false; - if(mLabel == std::string("Accessories")) - { - //To ensure that Accessories folder is in Library we have to check its parent folder. - //Due to parent LLFolderViewFloder is not set to this item yet we have to check its parent via Inventory Model - LLInventoryCategory* cat = gInventory.getCategory(mListener->getUUID()); - if(cat) - { - const LLUUID& parent_folder_id = cat->getParentUUID(); - accessories = (parent_folder_id == gInventory.getLibraryRootFolderID()); - } - } - - //"Accessories" inventory category has folder type FT_NONE. So, this folder - //can not be detected as protected with LLFolderType::lookupIsProtectedType - if (accessories || LLFolderType::lookupIsProtectedType(preferred_type)) - { - LLTrans::findString(mLabel, "InvFolder " + mLabel); - }; - - setToolTip(mLabel); - setIcon(mListener->getIcon()); - time_t creation_date = mListener->getCreationDate(); - if ((creation_date > 0) && (mCreationDate != creation_date)) - { - setCreationDate(creation_date); - dirtyFilter(); - } - if (mRoot->useLabelSuffix()) - { - mLabelStyle = mListener->getLabelStyle(); - mLabelSuffix = mListener->getLabelSuffix(); - } - } -} - -void LLFolderViewItem::refresh() -{ - refreshFromListener(); - - std::string searchable_label(mLabel); - searchable_label.append(mLabelSuffix); - LLStringUtil::toUpper(searchable_label); - - if (mSearchableLabel.compare(searchable_label)) - { - mSearchableLabel.assign(searchable_label); - dirtyFilter(); - // some part of label has changed, so overall width has potentially changed, and sort order too - if (mParentFolder) - { - mParentFolder->requestSort(); - mParentFolder->requestArrange(); - } - } - - mLabelWidthDirty = true; -} - -void LLFolderViewItem::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor) -{ - functor(mListener); -} - -// This function is called when items are added or view filters change. It's -// implemented here but called by derived classes when folding the -// views. -void LLFolderViewItem::filterFromRoot( void ) -{ - LLFolderViewItem* root = getRoot(); - - root->filter(*((LLFolderView*)root)->getFilter()); -} - -// This function is called when the folder view is dirty. It's -// implemented here but called by derived classes when folding the -// views. -void LLFolderViewItem::arrangeFromRoot() -{ - LLFolderViewItem* root = getRoot(); - - S32 height = 0; - S32 width = 0; - S32 total_height = root->arrange( &width, &height, 0 ); - - LLSD params; - params["action"] = "size_changes"; - params["height"] = total_height; - getParent()->notifyParent(params); -} - -// Utility function for LLFolderView -void LLFolderViewItem::arrangeAndSet(BOOL set_selection, - BOOL take_keyboard_focus) -{ - LLFolderView* root = getRoot(); - if (getParentFolder()) - { - getParentFolder()->requestArrange(); - } - if(set_selection) - { - setSelectionFromRoot(this, TRUE, take_keyboard_focus); - if(root) - { - root->scrollToShowSelection(); - } - } -} - -// This function clears the currently selected item, and records the -// specified selected item appropriately for display and use in the -// UI. If open is TRUE, then folders are opened up along the way to -// the selection. -void LLFolderViewItem::setSelectionFromRoot(LLFolderViewItem* selection, - BOOL openitem, - BOOL take_keyboard_focus) -{ - getRoot()->setSelection(selection, openitem, take_keyboard_focus); -} - -// helper function to change the selection from the root. -void LLFolderViewItem::changeSelectionFromRoot(LLFolderViewItem* selection, BOOL selected) -{ - getRoot()->changeSelection(selection, selected); -} - -std::set<LLUUID> LLFolderViewItem::getSelectionList() const -{ - std::set<LLUUID> selection; - return selection; -} - -EInventorySortGroup LLFolderViewItem::getSortGroup() const -{ - return SG_ITEM; -} - -// addToFolder() returns TRUE if it succeeds. FALSE otherwise -BOOL LLFolderViewItem::addToFolder(LLFolderViewFolder* folder, LLFolderView* root) -{ - if (!folder) - { - return FALSE; - } - mParentFolder = folder; - root->addItemID(getListener()->getUUID(), this); - return folder->addItem(this); -} - - -// Finds width and height of this object and its children. Also -// makes sure that this view and its children are the right size. -S32 LLFolderViewItem::arrange( S32* width, S32* height, S32 filter_generation) -{ - const Params& p = LLUICtrlFactory::getDefaultParams<LLFolderViewItem>(); - S32 indentation = p.folder_indentation(); - // Only indent deeper items in hierarchy - mIndentation = (getParentFolder() - && getParentFolder()->getParentFolder() ) - ? mParentFolder->getIndentation() + indentation - : 0; - if (mLabelWidthDirty) - { - mLabelWidth = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + getLabelFontForStyle(mLabelStyle)->getWidth(mLabel) + getLabelFontForStyle(mLabelStyle)->getWidth(mLabelSuffix) + TEXT_PAD_RIGHT; - mLabelWidthDirty = false; - } - - *width = llmax(*width, mLabelWidth + mIndentation); - - // determine if we need to use ellipses to avoid horizontal scroll. EXT-719 - bool use_ellipses = getRoot()->getUseEllipses(); - if (use_ellipses) - { - // limit to set rect to avoid horizontal scrollbar - *width = llmin(*width, getRoot()->getRect().getWidth()); - } - *height = getItemHeight(); - return *height; -} - -S32 LLFolderViewItem::getItemHeight() -{ - return mItemHeight; -} - -void LLFolderViewItem::filter( LLInventoryFilter& filter) -{ - const BOOL previous_passed_filter = mPassedFilter; - const BOOL passed_filter = filter.check(this); - - // If our visibility will change as a result of this filter, then - // we need to be rearranged in our parent folder - if (mParentFolder) - { - if (getVisible() != passed_filter - || previous_passed_filter != passed_filter ) - mParentFolder->requestArrange(); - } - - setFiltered(passed_filter, filter.getCurrentGeneration()); - mStringMatchOffset = filter.getStringMatchOffset(); - filter.decrementFilterCount(); - - if (getRoot()->getDebugFilters()) - { - mStatusText = llformat("%d", mLastFilterGeneration); - } -} - -void LLFolderViewItem::dirtyFilter() -{ - mLastFilterGeneration = -1; - // bubble up dirty flag all the way to root - if (getParentFolder()) - { - getParentFolder()->setCompletedFilterGeneration(-1, TRUE); - } -} - -// *TODO: This can be optimized a lot by simply recording that it is -// selected in the appropriate places, and assuming that set selection -// means 'deselect' for a leaf item. Do this optimization after -// multiple selection is implemented to make sure it all plays nice -// together. -BOOL LLFolderViewItem::setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus) -{ - if (selection == this && !mIsSelected) - { - selectItem(); - } - else if (mIsSelected) // Deselect everything else. - { - deselectItem(); - } - return mIsSelected; -} - -BOOL LLFolderViewItem::changeSelection(LLFolderViewItem* selection, BOOL selected) -{ - if (selection == this) - { - if (mIsSelected) - { - deselectItem(); - } - else - { - selectItem(); - } - return TRUE; - } - return FALSE; -} - -void LLFolderViewItem::deselectItem(void) -{ - mIsSelected = FALSE; -} - -void LLFolderViewItem::selectItem(void) -{ - if (mIsSelected == FALSE) - { - if (mListener) - { - mListener->selectItem(); - } - mIsSelected = TRUE; - } -} - -BOOL LLFolderViewItem::isMovable() -{ - if( mListener ) - { - return mListener->isItemMovable(); - } - else - { - return TRUE; - } -} - -BOOL LLFolderViewItem::isRemovable() -{ - if( mListener ) - { - return mListener->isItemRemovable(); - } - else - { - return TRUE; - } -} - -void LLFolderViewItem::destroyView() -{ - if (mParentFolder) - { - // removeView deletes me - mParentFolder->removeView(this); - } -} - -// Call through to the viewed object and return true if it can be -// removed. -//BOOL LLFolderViewItem::removeRecursively(BOOL single_item) -BOOL LLFolderViewItem::remove() -{ - if(!isRemovable()) - { - return FALSE; - } - if(mListener) - { - return mListener->removeItem(); - } - return TRUE; -} - -// Build an appropriate context menu for the item. -void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags) -{ - if(mListener) - { - mListener->buildContextMenu(menu, flags); - } -} - -void LLFolderViewItem::openItem( void ) -{ - if( mListener ) - { - mListener->openItem(); - } -} - -void LLFolderViewItem::preview( void ) -{ - if (mListener) - { - mListener->previewItem(); - } -} - -void LLFolderViewItem::rename(const std::string& new_name) -{ - if( !new_name.empty() ) - { - if( mListener ) - { - mListener->renameItem(new_name); - - if(mParentFolder) - { - mParentFolder->requestSort(); - } - } - } -} - -const std::string& LLFolderViewItem::getSearchableLabel() const -{ - return mSearchableLabel; -} - -LLViewerInventoryItem * LLFolderViewItem::getInventoryItem(void) -{ - if (!getListener()) return NULL; - return gInventory.getItem(getListener()->getUUID()); -} - -const std::string& LLFolderViewItem::getName( void ) const -{ - if(mListener) - { - return mListener->getName(); - } - return mLabel; -} - -// LLView functionality -BOOL LLFolderViewItem::handleRightMouseDown( S32 x, S32 y, MASK mask ) -{ - if(!mIsSelected) - { - setSelectionFromRoot(this, FALSE); - } - make_ui_sound("UISndClick"); - return TRUE; -} - -BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask ) -{ - if (LLView::childrenHandleMouseDown(x, y, mask)) - { - return TRUE; - } - - // No handler needed for focus lost since this class has no - // state that depends on it. - gFocusMgr.setMouseCapture( this ); - - if (!mIsSelected) - { - if(mask & MASK_CONTROL) - { - changeSelectionFromRoot(this, !mIsSelected); - } - else if (mask & MASK_SHIFT) - { - getParentFolder()->extendSelectionTo(this); - } - else - { - setSelectionFromRoot(this, FALSE); - } - make_ui_sound("UISndClick"); - } - else - { - mSelectPending = TRUE; - } - - if( isMovable() ) - { - S32 screen_x; - S32 screen_y; - localPointToScreen(x, y, &screen_x, &screen_y ); - LLToolDragAndDrop::getInstance()->setDragStart( screen_x, screen_y ); - } - return TRUE; -} - -BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask ) -{ - mIsMouseOverTitle = (y > (getRect().getHeight() - mItemHeight)); - - if( hasMouseCapture() && isMovable() ) - { - S32 screen_x; - S32 screen_y; - localPointToScreen(x, y, &screen_x, &screen_y ); - BOOL can_drag = TRUE; - if( LLToolDragAndDrop::getInstance()->isOverThreshold( screen_x, screen_y ) ) - { - LLFolderView* root = getRoot(); - - if(root->getCurSelectedItem()) - { - LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_WORLD; - - // *TODO: push this into listener and remove - // dependency on llagent - if (mListener - && gInventory.isObjectDescendentOf(mListener->getUUID(), gInventory.getRootFolderID())) - { - src = LLToolDragAndDrop::SOURCE_AGENT; - } - else if (mListener - && gInventory.isObjectDescendentOf(mListener->getUUID(), gInventory.getLibraryRootFolderID())) - { - src = LLToolDragAndDrop::SOURCE_LIBRARY; - } - - can_drag = root->startDrag(src); - if (can_drag) - { - // if (mListener) mListener->startDrag(); - // RN: when starting drag and drop, clear out last auto-open - root->autoOpenTest(NULL); - root->setShowSelectionContext(TRUE); - - // Release keyboard focus, so that if stuff is dropped into the - // world, pressing the delete key won't blow away the inventory - // item. - gFocusMgr.setKeyboardFocus(NULL); - - return LLToolDragAndDrop::getInstance()->handleHover( x, y, mask ); - } - } - } - - if (can_drag) - { - gViewerWindow->setCursor(UI_CURSOR_ARROW); - } - else - { - gViewerWindow->setCursor(UI_CURSOR_NOLOCKED); - } - return TRUE; - } - else - { - getRoot()->setShowSelectionContext(FALSE); - gViewerWindow->setCursor(UI_CURSOR_ARROW); - // let parent handle this then... - return FALSE; - } -} - - -BOOL LLFolderViewItem::handleDoubleClick( S32 x, S32 y, MASK mask ) -{ - preview(); - return TRUE; -} - -BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask ) -{ - if (LLView::childrenHandleMouseUp(x, y, mask)) - { - return TRUE; - } - - // if mouse hasn't moved since mouse down... - if ( pointInView(x, y) && mSelectPending ) - { - //...then select - if(mask & MASK_CONTROL) - { - changeSelectionFromRoot(this, !mIsSelected); - } - else if (mask & MASK_SHIFT) - { - getParentFolder()->extendSelectionTo(this); - } - else - { - setSelectionFromRoot(this, FALSE); - } - } - - mSelectPending = FALSE; - - if( hasMouseCapture() ) - { - getRoot()->setShowSelectionContext(FALSE); - gFocusMgr.setMouseCapture( NULL ); - } - return TRUE; -} - -void LLFolderViewItem::onMouseLeave(S32 x, S32 y, MASK mask) -{ - mIsMouseOverTitle = false; -} - -BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - BOOL accepted = FALSE; - BOOL handled = FALSE; - if(mListener) - { - accepted = mListener->dragOrDrop(mask,drop,cargo_type,cargo_data, tooltip_msg); - handled = accepted; - if (accepted) - { - mDragAndDropTarget = TRUE; - *accept = ACCEPT_YES_MULTI; - } - else - { - *accept = ACCEPT_NO; - } - } - if(mParentFolder && !handled) - { - // store this item to get it in LLFolderBridge::dragItemIntoFolder on drop event. - mRoot->setDraggingOverItem(this); - handled = mParentFolder->handleDragAndDropFromChild(mask,drop,cargo_type,cargo_data,accept,tooltip_msg); - mRoot->setDraggingOverItem(NULL); - } - if (handled) - { - lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewItem" << llendl; - } - - return handled; -} - -void LLFolderViewItem::draw() -{ - static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); - static LLUIColor sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE); - static LLUIColor sHighlightFgColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE); - static LLUIColor sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE); - static LLUIColor sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE); - static LLUIColor sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE); - static LLUIColor sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemColor", DEFAULT_WHITE); - static LLUIColor sLibraryColor = LLUIColorTable::instance().getColor("InventoryItemLibraryColor", DEFAULT_WHITE); - static LLUIColor sLinkColor = LLUIColorTable::instance().getColor("InventoryItemLinkColor", DEFAULT_WHITE); - static LLUIColor sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE); - static LLUIColor sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE); - - const Params& default_params = LLUICtrlFactory::getDefaultParams<LLFolderViewItem>(); - const S32 TOP_PAD = default_params.item_top_pad; - const S32 FOCUS_LEFT = 1; - const LLFontGL* font = getLabelFontForStyle(mLabelStyle); - - const BOOL in_inventory = getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(), gInventory.getRootFolderID()); - const BOOL in_library = getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(), gInventory.getLibraryRootFolderID()); - - //--------------------------------------------------------------------------------// - // Draw open folder arrow - // - const bool up_to_date = mListener && mListener->isUpToDate(); - const bool possibly_has_children = ((up_to_date && hasVisibleChildren()) // we fetched our children and some of them have passed the filter... - || (!up_to_date && mListener && mListener->hasChildren())); // ...or we know we have children but haven't fetched them (doesn't obey filter) - if (possibly_has_children) - { - LLUIImage* arrow_image = default_params.folder_arrow_image; - gl_draw_scaled_rotated_image( - mIndentation, getRect().getHeight() - ARROW_SIZE - TEXT_PAD - TOP_PAD, - ARROW_SIZE, ARROW_SIZE, mControlLabelRotation, arrow_image->getImage(), sFgColor); - } - - - //--------------------------------------------------------------------------------// - // Draw highlight for selected items - // - const BOOL show_context = getRoot()->getShowSelectionContext(); - const BOOL filled = show_context || (getRoot()->getParentPanel()->hasFocus()); // If we have keyboard focus, draw selection filled - const S32 focus_top = getRect().getHeight(); - const S32 focus_bottom = getRect().getHeight() - mItemHeight; - const bool folder_open = (getRect().getHeight() > mItemHeight + 4); - if (mIsSelected) // always render "current" item. Only render other selected items if mShowSingleSelection is FALSE - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - LLColor4 bg_color = sHighlightBgColor; - if (!mIsCurSelection) - { - // do time-based fade of extra objects - F32 fade_time = getRoot()->getSelectionFadeElapsedTime(); - if (getRoot()->getShowSingleSelection()) - { - // fading out - bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, bg_color.mV[VALPHA], 0.f); - } - else - { - // fading in - bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, 0.f, bg_color.mV[VALPHA]); - } - } - gl_rect_2d(FOCUS_LEFT, - focus_top, - getRect().getWidth() - 2, - focus_bottom, - bg_color, filled); - if (mIsCurSelection) - { - gl_rect_2d(FOCUS_LEFT, - focus_top, - getRect().getWidth() - 2, - focus_bottom, - sFocusOutlineColor, FALSE); - } - if (folder_open) - { - gl_rect_2d(FOCUS_LEFT, - focus_bottom + 1, // overlap with bottom edge of above rect - getRect().getWidth() - 2, - 0, - sFocusOutlineColor, FALSE); - if (show_context) - { - gl_rect_2d(FOCUS_LEFT, - focus_bottom + 1, - getRect().getWidth() - 2, - 0, - sHighlightBgColor, TRUE); - } - } - } - else if (mIsMouseOverTitle) - { - gl_rect_2d(FOCUS_LEFT, - focus_top, - getRect().getWidth() - 2, - focus_bottom, - sMouseOverColor, FALSE); - } - - //--------------------------------------------------------------------------------// - // Draw DragNDrop highlight - // - if (mDragAndDropTarget) - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gl_rect_2d(FOCUS_LEFT, - focus_top, - getRect().getWidth() - 2, - focus_bottom, - sHighlightBgColor, FALSE); - if (folder_open) - { - gl_rect_2d(FOCUS_LEFT, - focus_bottom + 1, // overlap with bottom edge of above rect - getRect().getWidth() - 2, - 0, - sHighlightBgColor, FALSE); - } - mDragAndDropTarget = FALSE; - } - - const LLViewerInventoryItem *item = getInventoryItem(); - const BOOL highlight_link = mIconOverlay && item && item->getIsLinkType(); - //--------------------------------------------------------------------------------// - // Draw open icon - // - const S32 icon_x = mIndentation + ARROW_SIZE + TEXT_PAD; - if (!mIconOpen.isNull() && (llabs(mControlLabelRotation) > 80)) // For open folders - { - mIconOpen->draw(icon_x, getRect().getHeight() - mIconOpen->getHeight() - TOP_PAD + 1); - } - else if (mIcon) - { - mIcon->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1); - } - - if (highlight_link) - { - mIconOverlay->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1); - } - - //--------------------------------------------------------------------------------// - // Exit if no label to draw - // - if (mLabel.empty()) - { - return; - } - - LLColor4 color = (mIsSelected && filled) ? sHighlightFgColor : sFgColor; - if (highlight_link) color = sLinkColor; - if (in_library) color = sLibraryColor; - - F32 right_x = 0; - F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD; - F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation); - - //--------------------------------------------------------------------------------// - // Highlight filtered text - // - if (getRoot()->getDebugFilters()) - { - if (!getFiltered() && !possibly_has_children) - { - color.mV[VALPHA] *= 0.5f; - } - LLColor4 filter_color = mLastFilterGeneration >= getRoot()->getFilter()->getCurrentGeneration() ? - LLColor4(0.5f, 0.8f, 0.5f, 1.f) : - LLColor4(0.8f, 0.5f, 0.5f, 1.f); - LLFontGL::getFontMonospace()->renderUTF8(mStatusText, 0, text_left, y, filter_color, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x, FALSE ); - text_left = right_x; - } - //--------------------------------------------------------------------------------// - // Draw the actual label text - // - font->renderUTF8(mLabel, 0, text_left, y, color, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - S32_MAX, getRect().getWidth() - (S32) text_left, &right_x, TRUE); - - //--------------------------------------------------------------------------------// - // Draw "Loading..." text - // - bool root_is_loading = false; - if (in_inventory) - { - root_is_loading = LLInventoryModelBackgroundFetch::instance().inventoryFetchInProgress(); - } - if (in_library) - { - root_is_loading = LLInventoryModelBackgroundFetch::instance().libraryFetchInProgress(); - } - if ((mIsLoading - && mTimeSinceRequestStart.getElapsedTimeF32() >= gSavedSettings.getF32("FolderLoadingMessageWaitTime")) - || (LLInventoryModelBackgroundFetch::instance().folderFetchActive() - && root_is_loading - && mShowLoadStatus)) - { - std::string load_string = " ( " + LLTrans::getString("LoadingData") + " ) "; - font->renderUTF8(load_string, 0, right_x, y, sSearchStatusColor, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x, FALSE); - } - - //--------------------------------------------------------------------------------// - // Draw label suffix - // - if (!mLabelSuffix.empty()) - { - font->renderUTF8( mLabelSuffix, 0, right_x, y, sSuffixColor, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x, FALSE ); - } - - //--------------------------------------------------------------------------------// - // Highlight string match - // - if (mStringMatchOffset != std::string::npos) - { - // don't draw backgrounds for zero-length strings - S32 filter_string_length = getRoot()->getFilterSubString().size(); - if (filter_string_length > 0) - { - std::string combined_string = mLabel + mLabelSuffix; - S32 left = llround(text_left) + font->getWidth(combined_string, 0, mStringMatchOffset) - 1; - S32 right = left + font->getWidth(combined_string, mStringMatchOffset, filter_string_length) + 2; - S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD); - S32 top = getRect().getHeight() - TOP_PAD; - - LLUIImage* box_image = default_params.selection_image; - LLRect box_rect(left, top, right, bottom); - box_image->draw(box_rect, sFilterBGColor); - F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, mStringMatchOffset); - F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD; - font->renderUTF8( combined_string, mStringMatchOffset, match_string_left, yy, - sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - filter_string_length, S32_MAX, &right_x, FALSE ); - } - } -} - - -///---------------------------------------------------------------------------- -/// Class LLFolderViewFolder -///---------------------------------------------------------------------------- - -LLFolderViewFolder::LLFolderViewFolder( const LLFolderViewItem::Params& p ): - LLFolderViewItem( p ), // 0 = no create time - mIsOpen(FALSE), - mExpanderHighlighted(FALSE), - mCurHeight(0.f), - mTargetHeight(0.f), - mAutoOpenCountdown(0.f), - mSubtreeCreationDate(0), - mAmTrash(LLFolderViewFolder::UNKNOWN), - mLastArrangeGeneration( -1 ), - mLastCalculatedWidth(0), - mCompletedFilterGeneration(-1), - mMostFilteredDescendantGeneration(-1), - mNeedsSort(false), - mPassedFolderFilter(FALSE) -{ -} - -// Destroys the object -LLFolderViewFolder::~LLFolderViewFolder( void ) -{ - // The LLView base class takes care of object destruction. make sure that we - // don't have mouse or keyboard focus - gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() -} - -void LLFolderViewFolder::setFilteredFolder(bool filtered, S32 filter_generation) -{ - mPassedFolderFilter = filtered; - mLastFilterGeneration = filter_generation; -} - -bool LLFolderViewFolder::getFilteredFolder(S32 filter_generation) -{ - return mPassedFolderFilter && mLastFilterGeneration >= getRoot()->getFilter()->getMinRequiredGeneration(); -} - -// addToFolder() returns TRUE if it succeeds. FALSE otherwise -BOOL LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder, LLFolderView* root) -{ - if (!folder) - { - return FALSE; - } - mParentFolder = folder; - root->addItemID(getListener()->getUUID(), this); - return folder->addFolder(this); -} - -// Finds width and height of this object and its children. Also -// makes sure that this view and its children are the right size. -S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) -{ - // sort before laying out contents - if (mNeedsSort) - { - mFolders.sort(mSortFunction); - mItems.sort(mSortFunction); - mNeedsSort = false; - } - - // evaluate mHasVisibleChildren - mHasVisibleChildren = false; - if (hasFilteredDescendants(filter_generation)) - { - // We have to verify that there's at least one child that's not filtered out - bool found = false; - // Try the items first - for (items_t::iterator iit = mItems.begin(); iit != mItems.end(); ++iit) - { - LLFolderViewItem* itemp = (*iit); - found = (itemp->getFiltered(filter_generation)); - if (found) - break; - } - if (!found) - { - // If no item found, try the folders - for (folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit) - { - LLFolderViewFolder* folderp = (*fit); - found = ( folderp->getListener() - && (folderp->getFiltered(filter_generation) - || (folderp->getFilteredFolder(filter_generation) - && folderp->hasFilteredDescendants(filter_generation)))); - if (found) - break; - } - } - - mHasVisibleChildren = found; - } - - // calculate height as a single item (without any children), and reshapes rectangle to match - LLFolderViewItem::arrange( width, height, filter_generation ); - - // clamp existing animated height so as to never get smaller than a single item - mCurHeight = llmax((F32)*height, mCurHeight); - - // initialize running height value as height of single item in case we have no children - *height = getItemHeight(); - F32 running_height = (F32)*height; - F32 target_height = (F32)*height; - - // are my children visible? - if (needsArrange()) - { - // set last arrange generation first, in case children are animating - // and need to be arranged again - mLastArrangeGeneration = getRoot()->getArrangeGeneration(); - if (mIsOpen) - { - // Add sizes of children - S32 parent_item_height = getRect().getHeight(); - - for(folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit) - { - LLFolderViewFolder* folderp = (*fit); - if (getRoot()->getDebugFilters()) - { - folderp->setVisible(TRUE); - } - else - { - folderp->setVisible( folderp->getListener() - && (folderp->getFiltered(filter_generation) - || (folderp->getFilteredFolder(filter_generation) - && folderp->hasFilteredDescendants(filter_generation)))); // passed filter or has descendants that passed filter - } - - if (folderp->getVisible()) - { - S32 child_width = *width; - S32 child_height = 0; - S32 child_top = parent_item_height - llround(running_height); - - target_height += folderp->arrange( &child_width, &child_height, filter_generation ); - - running_height += (F32)child_height; - *width = llmax(*width, child_width); - folderp->setOrigin( 0, child_top - folderp->getRect().getHeight() ); - } - } - for(items_t::iterator iit = mItems.begin(); - iit != mItems.end(); ++iit) - { - LLFolderViewItem* itemp = (*iit); - if (getRoot()->getDebugFilters()) - { - itemp->setVisible(TRUE); - } - else - { - itemp->setVisible(itemp->getFiltered(filter_generation)); - } - - if (itemp->getVisible()) - { - S32 child_width = *width; - S32 child_height = 0; - S32 child_top = parent_item_height - llround(running_height); - - target_height += itemp->arrange( &child_width, &child_height, filter_generation ); - // don't change width, as this item is as wide as its parent folder by construction - itemp->reshape( itemp->getRect().getWidth(), child_height); - - running_height += (F32)child_height; - *width = llmax(*width, child_width); - itemp->setOrigin( 0, child_top - itemp->getRect().getHeight() ); - } - } - } - - mTargetHeight = target_height; - // cache this width so next time we can just return it - mLastCalculatedWidth = *width; - } - else - { - // just use existing width - *width = mLastCalculatedWidth; - } - - // animate current height towards target height - if (llabs(mCurHeight - mTargetHeight) > 1.f) - { - mCurHeight = lerp(mCurHeight, mTargetHeight, LLCriticalDamp::getInterpolant(mIsOpen ? FOLDER_OPEN_TIME_CONSTANT : FOLDER_CLOSE_TIME_CONSTANT)); - - requestArrange(); - - // hide child elements that fall out of current animated height - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - // number of pixels that bottom of folder label is from top of parent folder - if (getRect().getHeight() - (*fit)->getRect().mTop + (*fit)->getItemHeight() - > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP) - { - // hide if beyond current folder height - (*fit)->setVisible(FALSE); - } - } - - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - // number of pixels that bottom of item label is from top of parent folder - if (getRect().getHeight() - (*iit)->getRect().mBottom - > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP) - { - (*iit)->setVisible(FALSE); - } - } - } - else - { - mCurHeight = mTargetHeight; - } - - // don't change width as this item is already as wide as its parent folder - reshape(getRect().getWidth(),llround(mCurHeight)); - - // pass current height value back to parent - *height = llround(mCurHeight); - - return llround(mTargetHeight); -} - -BOOL LLFolderViewFolder::needsArrange() -{ - return mLastArrangeGeneration < getRoot()->getArrangeGeneration(); -} - -void LLFolderViewFolder::requestSort() -{ - mNeedsSort = true; - // whenever item order changes, we need to lay things out again - requestArrange(); -} - -void LLFolderViewFolder::setCompletedFilterGeneration(S32 generation, BOOL recurse_up) -{ - //mMostFilteredDescendantGeneration = llmin(mMostFilteredDescendantGeneration, generation); - mCompletedFilterGeneration = generation; - // only aggregate up if we are a lower (older) value - if (recurse_up - && mParentFolder - && generation < mParentFolder->getCompletedFilterGeneration()) - { - mParentFolder->setCompletedFilterGeneration(generation, TRUE); - } -} - -void LLFolderViewFolder::filter( LLInventoryFilter& filter) -{ - S32 filter_generation = filter.getCurrentGeneration(); - // if failed to pass filter newer than must_pass_generation - // you will automatically fail this time, so we only - // check against items that have passed the filter - S32 must_pass_generation = filter.getMustPassGeneration(); - - bool autoopen_folders = (filter.hasFilterString()); - - // if we have already been filtered against this generation, skip out - if (getCompletedFilterGeneration() >= filter_generation) - { - return; - } - - // filter folder itself - if (getLastFilterGeneration() < filter_generation) - { - if (getLastFilterGeneration() >= must_pass_generation // folder has been compared to a valid precursor filter - && !mPassedFilter) // and did not pass the filter - { - // go ahead and flag this folder as done - mLastFilterGeneration = filter_generation; - mStringMatchOffset = std::string::npos; - } - else // filter self only on first pass through - { - // filter against folder rules - filterFolder(filter); - // and then item rules - LLFolderViewItem::filter( filter ); - } - } - - if (getRoot()->getDebugFilters()) - { - mStatusText = llformat("%d", mLastFilterGeneration); - mStatusText += llformat("(%d)", mCompletedFilterGeneration); - mStatusText += llformat("+%d", mMostFilteredDescendantGeneration); - } - - // all descendants have been filtered later than must pass generation - // but none passed - if(getCompletedFilterGeneration() >= must_pass_generation && !hasFilteredDescendants(must_pass_generation)) - { - // don't traverse children if we've already filtered them since must_pass_generation - // and came back with nothing - return; - } - - // we entered here with at least one filter iteration left - // check to see if we have any more before continuing on to children - if (filter.getFilterCount() < 0) - { - return; - } - - // when applying a filter, matching folders get their contents downloaded first - if (filter.isNotDefault() - && getFiltered(filter.getMinRequiredGeneration()) - && (mListener - && !gInventory.isCategoryComplete(mListener->getUUID()))) - { - LLInventoryModelBackgroundFetch::instance().start(mListener->getUUID()); - } - - // now query children - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end(); - ++iter) - { - LLFolderViewFolder* folder = (*iter); - // have we run out of iterations this frame? - if (filter.getFilterCount() < 0) - { - break; - } - - // mMostFilteredDescendantGeneration might have been reset - // in which case we need to update it even for folders that - // don't need to be filtered anymore - if (folder->getCompletedFilterGeneration() >= filter_generation) - { - // track latest generation to pass any child items - if (folder->getFiltered() || folder->hasFilteredDescendants(filter.getMinRequiredGeneration())) - { - mMostFilteredDescendantGeneration = filter_generation; - requestArrange(); - } - // just skip it, it has already been filtered - continue; - } - - // update this folders filter status (and children) - folder->filter( filter ); - - // track latest generation to pass any child items - if (folder->getFiltered() || folder->hasFilteredDescendants(filter_generation)) - { - mMostFilteredDescendantGeneration = filter_generation; - requestArrange(); - if (getRoot()->needsAutoSelect() && autoopen_folders) - { - folder->setOpenArrangeRecursively(TRUE); - } - } - } - - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end(); - ++iter) - { - LLFolderViewItem* item = (*iter); - if (filter.getFilterCount() < 0) - { - break; - } - if (item->getLastFilterGeneration() >= filter_generation) - { - if (item->getFiltered()) - { - mMostFilteredDescendantGeneration = filter_generation; - requestArrange(); - } - continue; - } - - if (item->getLastFilterGeneration() >= must_pass_generation && - !item->getFiltered(must_pass_generation)) - { - // failed to pass an earlier filter that was a subset of the current one - // go ahead and flag this item as done - item->setFiltered(FALSE, filter_generation); - continue; - } - - item->filter( filter ); - - if (item->getFiltered(filter.getMinRequiredGeneration())) - { - mMostFilteredDescendantGeneration = filter_generation; - requestArrange(); - } - } - - // if we didn't use all filter iterations - // that means we filtered all of our descendants - // instead of exhausting the filter count for this frame - if (filter.getFilterCount() > 0) - { - // flag this folder as having completed filter pass for all descendants - setCompletedFilterGeneration(filter_generation, FALSE/*dont recurse up to root*/); - } -} - -void LLFolderViewFolder::filterFolder(LLInventoryFilter& filter) -{ - const BOOL previous_passed_filter = mPassedFolderFilter; - const BOOL passed_filter = filter.checkFolder(this); - - // If our visibility will change as a result of this filter, then - // we need to be rearranged in our parent folder - if (mParentFolder) - { - if (getVisible() != passed_filter - || previous_passed_filter != passed_filter ) - { - mParentFolder->requestArrange(); - } - } - - setFilteredFolder(passed_filter, filter.getCurrentGeneration()); - filter.decrementFilterCount(); - - if (getRoot()->getDebugFilters()) - { - mStatusText = llformat("%d", mLastFilterGeneration); - } -} - -void LLFolderViewFolder::setFiltered(BOOL filtered, S32 filter_generation) -{ - // if this folder is now filtered, but wasn't before - // (it just passed) - if (filtered && !mPassedFilter) - { - // reset current height, because last time we drew it - // it might have had more visible items than now - mCurHeight = 0.f; - } - - LLFolderViewItem::setFiltered(filtered, filter_generation); -} - -void LLFolderViewFolder::dirtyFilter() -{ - // we're a folder, so invalidate our completed generation - setCompletedFilterGeneration(-1, FALSE); - LLFolderViewItem::dirtyFilter(); -} - -BOOL LLFolderViewFolder::getFiltered() -{ - return getFilteredFolder(getRoot()->getFilter()->getMinRequiredGeneration()) - && LLFolderViewItem::getFiltered(); -} - -BOOL LLFolderViewFolder::getFiltered(S32 filter_generation) -{ - return getFilteredFolder(filter_generation) && LLFolderViewItem::getFiltered(filter_generation); -} - -BOOL LLFolderViewFolder::hasFilteredDescendants(S32 filter_generation) -{ - return mMostFilteredDescendantGeneration >= filter_generation; -} - - -BOOL LLFolderViewFolder::hasFilteredDescendants() -{ - return mMostFilteredDescendantGeneration >= getRoot()->getFilter()->getCurrentGeneration(); -} - -// Passes selection information on to children and record selection -// information if necessary. -BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL openitem, - BOOL take_keyboard_focus) -{ - BOOL rv = FALSE; - if (selection == this) - { - if (!isSelected()) - { - selectItem(); - } - rv = TRUE; - } - else - { - if (isSelected()) - { - deselectItem(); - } - rv = FALSE; - } - BOOL child_selected = FALSE; - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - if((*fit)->setSelection(selection, openitem, take_keyboard_focus)) - { - rv = TRUE; - child_selected = TRUE; - } - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - if((*iit)->setSelection(selection, openitem, take_keyboard_focus)) - { - rv = TRUE; - child_selected = TRUE; - } - } - if(openitem && child_selected) - { - setOpenArrangeRecursively(TRUE); - } - return rv; -} - -// This method is used to change the selection of an item. -// Recursively traverse all children; if 'selection' is 'this' then change -// the select status if necessary. -// Returns TRUE if the selection state of this folder, or of a child, was changed. -BOOL LLFolderViewFolder::changeSelection(LLFolderViewItem* selection, BOOL selected) -{ - BOOL rv = FALSE; - if(selection == this) - { - if (isSelected() != selected) - { - rv = TRUE; - if (selected) - { - selectItem(); - } - else - { - deselectItem(); - } - } - } - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - if((*fit)->changeSelection(selection, selected)) - { - rv = TRUE; - } - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - if((*iit)->changeSelection(selection, selected)) - { - rv = TRUE; - } - } - return rv; -} - -LLFolderViewFolder* LLFolderViewFolder::getCommonAncestor(LLFolderViewItem* item_a, LLFolderViewItem* item_b, bool& reverse) -{ - if (!item_a->getParentFolder() || !item_b->getParentFolder()) return NULL; - - std::deque<LLFolderViewFolder*> item_a_ancestors; - - LLFolderViewFolder* parent = item_a->getParentFolder(); - while(parent) - { - item_a_ancestors.push_back(parent); - parent = parent->getParentFolder(); - } - - std::deque<LLFolderViewFolder*> item_b_ancestors; - - parent = item_b->getParentFolder(); - while(parent) - { - item_b_ancestors.push_back(parent); - parent = parent->getParentFolder(); - } - - LLFolderViewFolder* common_ancestor = item_a->getRoot(); - - while(item_a_ancestors.size() > item_b_ancestors.size()) - { - item_a = item_a_ancestors.front(); - item_a_ancestors.pop_front(); - } - - while(item_b_ancestors.size() > item_a_ancestors.size()) - { - item_b = item_b_ancestors.front(); - item_b_ancestors.pop_front(); - } - - while(item_a_ancestors.size()) - { - common_ancestor = item_a_ancestors.front(); - - if (item_a_ancestors.front() == item_b_ancestors.front()) - { - // which came first, sibling a or sibling b? - for (folders_t::iterator it = common_ancestor->mFolders.begin(), end_it = common_ancestor->mFolders.end(); - it != end_it; - ++it) - { - LLFolderViewItem* item = *it; - - if (item == item_a) - { - reverse = false; - return common_ancestor; - } - if (item == item_b) - { - reverse = true; - return common_ancestor; - } - } - - for (items_t::iterator it = common_ancestor->mItems.begin(), end_it = common_ancestor->mItems.end(); - it != end_it; - ++it) - { - LLFolderViewItem* item = *it; - - if (item == item_a) - { - reverse = false; - return common_ancestor; - } - if (item == item_b) - { - reverse = true; - return common_ancestor; - } - } - break; - } - - item_a = item_a_ancestors.front(); - item_a_ancestors.pop_front(); - item_b = item_b_ancestors.front(); - item_b_ancestors.pop_front(); - } - - return NULL; -} - -void LLFolderViewFolder::gatherChildRangeExclusive(LLFolderViewItem* start, LLFolderViewItem* end, bool reverse, std::vector<LLFolderViewItem*>& items) -{ - bool selecting = start == NULL; - if (reverse) - { - for (items_t::reverse_iterator it = mItems.rbegin(), end_it = mItems.rend(); - it != end_it; - ++it) - { - if (*it == end) - { - return; - } - if (selecting) - { - items.push_back(*it); - } - - if (*it == start) - { - selecting = true; - } - } - for (folders_t::reverse_iterator it = mFolders.rbegin(), end_it = mFolders.rend(); - it != end_it; - ++it) - { - if (*it == end) - { - return; - } - - if (selecting) - { - items.push_back(*it); - } - - if (*it == start) - { - selecting = true; - } - } - } - else - { - for (folders_t::iterator it = mFolders.begin(), end_it = mFolders.end(); - it != end_it; - ++it) - { - if (*it == end) - { - return; - } - - if (selecting) - { - items.push_back(*it); - } - - if (*it == start) - { - selecting = true; - } - } - for (items_t::iterator it = mItems.begin(), end_it = mItems.end(); - it != end_it; - ++it) - { - if (*it == end) - { - return; - } - - if (selecting) - { - items.push_back(*it); - } - - if (*it == start) - { - selecting = true; - } - } - } -} - -void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection) -{ - if (getRoot()->getAllowMultiSelect() == FALSE) return; - - LLFolderViewItem* cur_selected_item = getRoot()->getCurSelectedItem(); - if (cur_selected_item == NULL) - { - cur_selected_item = new_selection; - } - - - bool reverse = false; - LLFolderViewFolder* common_ancestor = getCommonAncestor(cur_selected_item, new_selection, reverse); - if (!common_ancestor) return; - - LLFolderViewItem* last_selected_item_from_cur = cur_selected_item; - LLFolderViewFolder* cur_folder = cur_selected_item->getParentFolder(); - - std::vector<LLFolderViewItem*> items_to_select_forward; - - while(cur_folder != common_ancestor) - { - cur_folder->gatherChildRangeExclusive(last_selected_item_from_cur, NULL, reverse, items_to_select_forward); - - last_selected_item_from_cur = cur_folder; - cur_folder = cur_folder->getParentFolder(); - } - - std::vector<LLFolderViewItem*> items_to_select_reverse; - - LLFolderViewItem* last_selected_item_from_new = new_selection; - cur_folder = new_selection->getParentFolder(); - while(cur_folder != common_ancestor) - { - cur_folder->gatherChildRangeExclusive(last_selected_item_from_new, NULL, !reverse, items_to_select_reverse); - - last_selected_item_from_new = cur_folder; - cur_folder = cur_folder->getParentFolder(); - } - - common_ancestor->gatherChildRangeExclusive(last_selected_item_from_cur, last_selected_item_from_new, reverse, items_to_select_forward); - - for (std::vector<LLFolderViewItem*>::reverse_iterator it = items_to_select_reverse.rbegin(), end_it = items_to_select_reverse.rend(); - it != end_it; - ++it) - { - items_to_select_forward.push_back(*it); - } - - LLFolderView* root = getRoot(); - - for (std::vector<LLFolderViewItem*>::iterator it = items_to_select_forward.begin(), end_it = items_to_select_forward.end(); - it != end_it; - ++it) - { - LLFolderViewItem* item = *it; - if (item->isSelected()) - { - root->removeFromSelectionList(item); - } - else - { - item->selectItem(); - } - root->addToSelectionList(item); - } - - if (new_selection->isSelected()) - { - root->removeFromSelectionList(new_selection); - } - else - { - new_selection->selectItem(); - } - root->addToSelectionList(new_selection); -} - - -void LLFolderViewFolder::destroyView() -{ - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - LLFolderViewItem* item = (*iit); - getRoot()->removeItemID(item->getListener()->getUUID()); - } - - std::for_each(mItems.begin(), mItems.end(), DeletePointer()); - mItems.clear(); - - while (!mFolders.empty()) - { - LLFolderViewFolder *folderp = mFolders.back(); - folderp->destroyView(); // removes entry from mFolders - } - - //deleteAllChildren(); - - if (mParentFolder) - { - mParentFolder->removeView(this); - } -} - -// remove the specified item (and any children) if possible. Return -// TRUE if the item was deleted. -BOOL LLFolderViewFolder::removeItem(LLFolderViewItem* item) -{ - if(item->remove()) - { - return TRUE; - } - return FALSE; -} - -// simply remove the view (and any children) Don't bother telling the -// listeners. -void LLFolderViewFolder::removeView(LLFolderViewItem* item) -{ - if (!item || item->getParentFolder() != this) - { - return; - } - // deselect without traversing hierarchy - if (item->isSelected()) - { - item->deselectItem(); - } - getRoot()->removeFromSelectionList(item); - extractItem(item); - delete item; -} - -// extractItem() removes the specified item from the folder, but -// doesn't delete it. -void LLFolderViewFolder::extractItem( LLFolderViewItem* item ) -{ - items_t::iterator it = std::find(mItems.begin(), mItems.end(), item); - if(it == mItems.end()) - { - // This is an evil downcast. However, it's only doing - // pointer comparison to find if (which it should be ) the - // item is in the container, so it's pretty safe. - LLFolderViewFolder* f = static_cast<LLFolderViewFolder*>(item); - folders_t::iterator ft; - ft = std::find(mFolders.begin(), mFolders.end(), f); - if (ft != mFolders.end()) - { - mFolders.erase(ft); - } - } - else - { - mItems.erase(it); - } - //item has been removed, need to update filter - dirtyFilter(); - //because an item is going away regardless of filter status, force rearrange - requestArrange(); - getRoot()->removeItemID(item->getListener()->getUUID()); - removeChild(item); -} - -bool LLFolderViewFolder::isTrash() const -{ - if (mAmTrash == LLFolderViewFolder::UNKNOWN) - { - mAmTrash = mListener->getUUID() == gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH, false) ? LLFolderViewFolder::TRASH : LLFolderViewFolder::NOT_TRASH; - } - return mAmTrash == LLFolderViewFolder::TRASH; -} - -void LLFolderViewFolder::sortBy(U32 order) -{ - if (!mSortFunction.updateSort(order)) - { - // No changes. - return; - } - - // Propagate this change to sub folders - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->sortBy(order); - } - - // Don't sort the topmost folders (My Inventory and Library) - if (mListener->getUUID().notNull()) - { - mFolders.sort(mSortFunction); - mItems.sort(mSortFunction); - } - - if (order & LLInventoryFilter::SO_DATE) - { - time_t latest = 0; - - if (!mItems.empty()) - { - LLFolderViewItem* item = *(mItems.begin()); - latest = item->getCreationDate(); - } - - if (!mFolders.empty()) - { - LLFolderViewFolder* folder = *(mFolders.begin()); - if (folder->getCreationDate() > latest) - { - latest = folder->getCreationDate(); - } - } - mSubtreeCreationDate = latest; - } -} - -void LLFolderViewFolder::setItemSortOrder(U32 ordering) -{ - if (mSortFunction.updateSort(ordering)) - { - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->setItemSortOrder(ordering); - } - - mFolders.sort(mSortFunction); - mItems.sort(mSortFunction); - } -} - -EInventorySortGroup LLFolderViewFolder::getSortGroup() const -{ - if (isTrash()) - { - return SG_TRASH_FOLDER; - } - - if( mListener ) - { - if(LLFolderType::lookupIsProtectedType(mListener->getPreferredType())) - { - return SG_SYSTEM_FOLDER; - } - } - - return SG_NORMAL_FOLDER; -} - -BOOL LLFolderViewFolder::isMovable() -{ - if( mListener ) - { - if( !(mListener->isItemMovable()) ) - { - return FALSE; - } - - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - if(!(*iit)->isMovable()) - { - return FALSE; - } - } - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - if(!(*fit)->isMovable()) - { - return FALSE; - } - } - } - return TRUE; -} - - -BOOL LLFolderViewFolder::isRemovable() -{ - if( mListener ) - { - if( !(mListener->isItemRemovable()) ) - { - return FALSE; - } - - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - if(!(*iit)->isRemovable()) - { - return FALSE; - } - } - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - if(!(*fit)->isRemovable()) - { - return FALSE; - } - } - } - return TRUE; -} - -// this is an internal method used for adding items to folders. -BOOL LLFolderViewFolder::addItem(LLFolderViewItem* item) -{ - mItems.push_back(item); - - item->setRect(LLRect(0, 0, getRect().getWidth(), 0)); - item->setVisible(FALSE); - - addChild(item); - - item->dirtyFilter(); - - // Update the folder creation date if the child is newer than our current date - setCreationDate(llmax<time_t>(mCreationDate, item->getCreationDate())); - - // Handle sorting - requestArrange(); - requestSort(); - - // Traverse parent folders and update creation date and resort, if necessary - LLFolderViewFolder* parentp = getParentFolder(); - while (parentp) - { - // Update the folder creation date if the child is newer than our current date - parentp->setCreationDate(llmax<time_t>(parentp->mCreationDate, item->getCreationDate())); - - if (parentp->mSortFunction.isByDate()) - { - // parent folder doesn't have a time stamp yet, so get it from us - parentp->requestSort(); - } - - parentp = parentp->getParentFolder(); - } - - return TRUE; -} - -// this is an internal method used for adding items to folders. -BOOL LLFolderViewFolder::addFolder(LLFolderViewFolder* folder) -{ - mFolders.push_back(folder); - folder->setOrigin(0, 0); - folder->reshape(getRect().getWidth(), 0); - folder->setVisible(FALSE); - addChild( folder ); - folder->dirtyFilter(); - // rearrange all descendants too, as our indentation level might have changed - folder->requestArrange(TRUE); - requestSort(); - LLFolderViewFolder* parentp = getParentFolder(); - while (parentp && !parentp->mSortFunction.isByDate()) - { - // parent folder doesn't have a time stamp yet, so get it from us - parentp->requestSort(); - parentp = parentp->getParentFolder(); - } - return TRUE; -} - -void LLFolderViewFolder::requestArrange(BOOL include_descendants) -{ - mLastArrangeGeneration = -1; - // flag all items up to root - if (mParentFolder) - { - mParentFolder->requestArrange(); - } - - if (include_descendants) - { - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end(); - ++iter) - { - (*iter)->requestArrange(TRUE); - } - } -} - -void LLFolderViewFolder::toggleOpen() -{ - setOpen(!mIsOpen); -} - -// Force a folder open or closed -void LLFolderViewFolder::setOpen(BOOL openitem) -{ - setOpenArrangeRecursively(openitem); -} - -void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse) -{ - BOOL was_open = mIsOpen; - mIsOpen = openitem; - if (mListener) - { - if(!was_open && openitem) - { - mListener->openItem(); - } - else if(was_open && !openitem) - { - mListener->closeItem(); - } - } - - if (recurse == RECURSE_DOWN || recurse == RECURSE_UP_DOWN) - { - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->setOpenArrangeRecursively(openitem, RECURSE_DOWN); /* Flawfinder: ignore */ - } - } - if (mParentFolder - && (recurse == RECURSE_UP - || recurse == RECURSE_UP_DOWN)) - { - mParentFolder->setOpenArrangeRecursively(openitem, RECURSE_UP); - } - - if (was_open != mIsOpen) - { - requestArrange(); - } -} - -BOOL LLFolderViewFolder::handleDragAndDropFromChild(MASK mask, - BOOL drop, - EDragAndDropType c_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - BOOL accepted = mListener && mListener->dragOrDrop(mask,drop,c_type,cargo_data, tooltip_msg); - if (accepted) - { - mDragAndDropTarget = TRUE; - *accept = ACCEPT_YES_MULTI; - } - else - { - *accept = ACCEPT_NO; - } - - // drag and drop to child item, so clear pending auto-opens - getRoot()->autoOpenTest(NULL); - - return TRUE; -} - -void LLFolderViewFolder::openItem( void ) -{ - toggleOpen(); -} - -void LLFolderViewFolder::applyFunctorToChildren(LLFolderViewFunctor& functor) -{ - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - functor.doItem((*fit)); - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - functor.doItem((*iit)); - } -} - -void LLFolderViewFolder::applyFunctorRecursively(LLFolderViewFunctor& functor) -{ - functor.doFolder(this); - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->applyFunctorRecursively(functor); - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - functor.doItem((*iit)); - } -} - -void LLFolderViewFolder::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor) -{ - functor(mListener); - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->applyListenerFunctorRecursively(functor); - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - (*iit)->applyListenerFunctorRecursively(functor); - } -} - -// LLView functionality -BOOL LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - BOOL handled = FALSE; - - if (mIsOpen) - { - handled = (childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL); - } - - if (!handled) - { - handleDragAndDropToThisFolder(mask, drop, cargo_type, cargo_data, accept, tooltip_msg); - - lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewFolder" << llendl; - } - - return TRUE; -} - -BOOL LLFolderViewFolder::handleDragAndDropToThisFolder(MASK mask, - BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - BOOL accepted = mListener && mListener->dragOrDrop(mask, drop, cargo_type, cargo_data, tooltip_msg); - - if (accepted) - { - mDragAndDropTarget = TRUE; - *accept = ACCEPT_YES_MULTI; - } - else - { - *accept = ACCEPT_NO; - } - - if (!drop && accepted) - { - getRoot()->autoOpenTest(this); - } - - return TRUE; -} - - -BOOL LLFolderViewFolder::handleRightMouseDown( S32 x, S32 y, MASK mask ) -{ - BOOL handled = FALSE; - // fetch contents of this folder, as context menu can depend on contents - // still, user would have to open context menu again to see the changes - gInventory.fetchDescendentsOf(mListener->getUUID()); - - if( mIsOpen ) - { - handled = childrenHandleRightMouseDown( x, y, mask ) != NULL; - } - if (!handled) - { - handled = LLFolderViewItem::handleRightMouseDown( x, y, mask ); - } - return handled; -} - - -BOOL LLFolderViewFolder::handleHover(S32 x, S32 y, MASK mask) -{ - mIsMouseOverTitle = (y > (getRect().getHeight() - mItemHeight)); - - BOOL handled = LLView::handleHover(x, y, mask); - - if (!handled) - { - // this doesn't do child processing - handled = LLFolderViewItem::handleHover(x, y, mask); - } - - return handled; -} - -BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask ) -{ - BOOL handled = FALSE; - if( mIsOpen ) - { - handled = childrenHandleMouseDown(x,y,mask) != NULL; - } - if( !handled ) - { - if(mIndentation < x && x < mIndentation + ARROW_SIZE + TEXT_PAD) - { - toggleOpen(); - handled = TRUE; - } - else - { - // do normal selection logic - handled = LLFolderViewItem::handleMouseDown(x, y, mask); - } - } - - return handled; -} - -BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask ) -{ - /* Disable outfit double click to wear - const LLUUID &cat_uuid = getListener()->getUUID(); - const LLViewerInventoryCategory *cat = gInventory.getCategory(cat_uuid); - if (cat && cat->getPreferredType() == LLFolderType::FT_OUTFIT) - { - getListener()->performAction(NULL, NULL,"replaceoutfit"); - return TRUE; - } - */ - - BOOL handled = FALSE; - if( mIsOpen ) - { - handled = childrenHandleDoubleClick( x, y, mask ) != NULL; - } - if( !handled ) - { - if(mIndentation < x && x < mIndentation + ARROW_SIZE + TEXT_PAD) - { - // don't select when user double-clicks plus sign - // so as not to contradict single-click behavior - toggleOpen(); - } - else - { - setSelectionFromRoot(this, FALSE); - toggleOpen(); - } - handled = TRUE; - } - return handled; -} - -void LLFolderViewFolder::draw() -{ - if (mAutoOpenCountdown != 0.f) - { - mControlLabelRotation = mAutoOpenCountdown * -90.f; - } - else if (mIsOpen) - { - mControlLabelRotation = lerp(mControlLabelRotation, -90.f, LLCriticalDamp::getInterpolant(0.04f)); - } - else - { - mControlLabelRotation = lerp(mControlLabelRotation, 0.f, LLCriticalDamp::getInterpolant(0.025f)); - } - - bool possibly_has_children = false; - bool up_to_date = mListener && mListener->isUpToDate(); - if(!up_to_date - && mListener->hasChildren()) // we know we have children but haven't fetched them (doesn't obey filter) - { - possibly_has_children = true; - } - - - BOOL loading = (mIsOpen - && possibly_has_children - && !up_to_date ); - - if ( loading && !mIsLoading ) - { - // Measure how long we've been in the loading state - mTimeSinceRequestStart.reset(); - } - - mIsLoading = loading; - - LLFolderViewItem::draw(); - - // draw children if root folder, or any other folder that is open or animating to closed state - if( getRoot() == this || (mIsOpen || mCurHeight != mTargetHeight )) - { - LLView::draw(); - } - - mExpanderHighlighted = FALSE; -} - -time_t LLFolderViewFolder::getCreationDate() const -{ - return llmax<time_t>(mCreationDate, mSubtreeCreationDate); -} - - -BOOL LLFolderViewFolder::potentiallyVisible() -{ - // folder should be visible by it's own filter status - return LLFolderViewItem::potentiallyVisible() - // or one or more of its descendants have passed the minimum filter requirement - || hasFilteredDescendants(getRoot()->getFilter()->getMinRequiredGeneration()) - // or not all of its descendants have been checked against minimum filter requirement - || getCompletedFilterGeneration() < getRoot()->getFilter()->getMinRequiredGeneration(); -} - -// this does prefix traversal, as folders are listed above their contents -LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, BOOL include_children ) -{ - BOOL found_item = FALSE; - - LLFolderViewItem* result = NULL; - // when not starting from a given item, start at beginning - if(item == NULL) - { - found_item = TRUE; - } - - // find current item among children - folders_t::iterator fit = mFolders.begin(); - folders_t::iterator fend = mFolders.end(); - - items_t::iterator iit = mItems.begin(); - items_t::iterator iend = mItems.end(); - - // if not trivially starting at the beginning, we have to find the current item - if (!found_item) - { - // first, look among folders, since they are always above items - for(; fit != fend; ++fit) - { - if(item == (*fit)) - { - found_item = TRUE; - // if we are on downwards traversal - if (include_children && (*fit)->isOpen()) - { - // look for first descendant - return (*fit)->getNextFromChild(NULL, TRUE); - } - // otherwise advance to next folder - ++fit; - include_children = TRUE; - break; - } - } - - // didn't find in folders? Check items... - if (!found_item) - { - for(; iit != iend; ++iit) - { - if(item == (*iit)) - { - found_item = TRUE; - // point to next item - ++iit; - break; - } - } - } - } - - if (!found_item) - { - // you should never call this method with an item that isn't a child - // so we should always find something - llassert(FALSE); - return NULL; - } - - // at this point, either iit or fit point to a candidate "next" item - // if both are out of range, we need to punt up to our parent - - // now, starting from found folder, continue through folders - // searching for next visible folder - while(fit != fend && !(*fit)->getVisible()) - { - // turn on downwards traversal for next folder - ++fit; - } - - if (fit != fend) - { - result = (*fit); - } - else - { - // otherwise, scan for next visible item - while(iit != iend && !(*iit)->getVisible()) - { - ++iit; - } - - // check to see if we have a valid item - if (iit != iend) - { - result = (*iit); - } - } - - if( !result && mParentFolder ) - { - // If there are no siblings or children to go to, recurse up one level in the tree - // and skip children for this folder, as we've already discounted them - result = mParentFolder->getNextFromChild(this, FALSE); - } - - return result; -} - -// this does postfix traversal, as folders are listed above their contents -LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* item, BOOL include_children ) -{ - BOOL found_item = FALSE; - - LLFolderViewItem* result = NULL; - // when not starting from a given item, start at end - if(item == NULL) - { - found_item = TRUE; - } - - // find current item among children - folders_t::reverse_iterator fit = mFolders.rbegin(); - folders_t::reverse_iterator fend = mFolders.rend(); - - items_t::reverse_iterator iit = mItems.rbegin(); - items_t::reverse_iterator iend = mItems.rend(); - - // if not trivially starting at the end, we have to find the current item - if (!found_item) - { - // first, look among items, since they are always below the folders - for(; iit != iend; ++iit) - { - if(item == (*iit)) - { - found_item = TRUE; - // point to next item - ++iit; - break; - } - } - - // didn't find in items? Check folders... - if (!found_item) - { - for(; fit != fend; ++fit) - { - if(item == (*fit)) - { - found_item = TRUE; - // point to next folder - ++fit; - break; - } - } - } - } - - if (!found_item) - { - // you should never call this method with an item that isn't a child - // so we should always find something - llassert(FALSE); - return NULL; - } - - // at this point, either iit or fit point to a candidate "next" item - // if both are out of range, we need to punt up to our parent - - // now, starting from found item, continue through items - // searching for next visible item - while(iit != iend && !(*iit)->getVisible()) - { - ++iit; - } - - if (iit != iend) - { - // we found an appropriate item - result = (*iit); - } - else - { - // otherwise, scan for next visible folder - while(fit != fend && !(*fit)->getVisible()) - { - ++fit; - } - - // check to see if we have a valid folder - if (fit != fend) - { - // try selecting child element of this folder - if ((*fit)->isOpen()) - { - result = (*fit)->getPreviousFromChild(NULL); - } - else - { - result = (*fit); - } - } - } - - if( !result ) - { - // If there are no siblings or children to go to, recurse up one level in the tree - // which gets back to this folder, which will only be visited if it is a valid, visible item - result = this; - } - - return result; -} - - -bool LLInventorySort::updateSort(U32 order) -{ - if (order != mSortOrder) - { - mSortOrder = order; - mByDate = (order & LLInventoryFilter::SO_DATE); - mSystemToTop = (order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP); - mFoldersByName = (order & LLInventoryFilter::SO_FOLDERS_BY_NAME); - return true; - } - return false; -} - -bool LLInventorySort::operator()(const LLFolderViewItem* const& a, const LLFolderViewItem* const& b) -{ - // ignore sort order for landmarks in the Favorites folder. - // they should be always sorted as in Favorites bar. See EXT-719 - if (a->getSortGroup() == SG_ITEM - && b->getSortGroup() == SG_ITEM - && a->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK - && b->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK) - { - - static const LLUUID& favorites_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); - - LLUUID a_uuid = a->getParentFolder()->getListener()->getUUID(); - LLUUID b_uuid = b->getParentFolder()->getListener()->getUUID(); - - if ((a_uuid == favorites_folder_id && b_uuid == favorites_folder_id)) - { - // *TODO: mantipov: probably it is better to add an appropriate method to LLFolderViewItem - // or to LLInvFVBridge - LLViewerInventoryItem* aitem = (static_cast<const LLItemBridge*>(a->getListener()))->getItem(); - LLViewerInventoryItem* bitem = (static_cast<const LLItemBridge*>(b->getListener()))->getItem(); - if (!aitem || !bitem) - return false; - S32 a_sort = aitem->getSortField(); - S32 b_sort = bitem->getSortField(); - return a_sort < b_sort; - } - } - - // We sort by name if we aren't sorting by date - // OR if these are folders and we are sorting folders by name. - bool by_name = (!mByDate - || (mFoldersByName - && (a->getSortGroup() != SG_ITEM))); - - if (a->getSortGroup() != b->getSortGroup()) - { - if (mSystemToTop) - { - // Group order is System Folders, Trash, Normal Folders, Items - return (a->getSortGroup() < b->getSortGroup()); - } - else if (mByDate) - { - // Trash needs to go to the bottom if we are sorting by date - if ( (a->getSortGroup() == SG_TRASH_FOLDER) - || (b->getSortGroup() == SG_TRASH_FOLDER)) - { - return (b->getSortGroup() == SG_TRASH_FOLDER); - } - } - } - - if (by_name) - { - S32 compare = LLStringUtil::compareDict(a->getLabel(), b->getLabel()); - if (0 == compare) - { - return (a->getCreationDate() > b->getCreationDate()); - } - else - { - return (compare < 0); - } - } - else - { - // BUG: This is very very slow. The getCreationDate() is log n in number - // of inventory items. - time_t first_create = a->getCreationDate(); - time_t second_create = b->getCreationDate(); - if (first_create == second_create) - { - return (LLStringUtil::compareDict(a->getLabel(), b->getLabel()) < 0); - } - else - { - return (first_create > second_create); - } - } -} diff --git a/indra/newview/llfolderviewitem.h b/indra/newview/llfolderviewitem.h deleted file mode 100644 index 3c7592046a..0000000000 --- a/indra/newview/llfolderviewitem.h +++ /dev/null @@ -1,576 +0,0 @@ -/** -* @file llfolderviewitem.h -* @brief Items and folders that can appear in a hierarchical folder view -* -* $LicenseInfo:firstyear=2001&license=viewerlgpl$ -* Second Life Viewer Source Code -* Copyright (C) 2010, Linden Research, Inc. -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public -* License as published by the Free Software Foundation; -* version 2.1 of the License only. -* -* This library is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this library; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -* -* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -* $/LicenseInfo$ -*/ -#ifndef LLFOLDERVIEWITEM_H -#define LLFOLDERVIEWITEM_H - -#include "llview.h" -#include "lldarray.h" // *TODO: Eliminate, forward declare -#include "lluiimage.h" - -class LLFontGL; -class LLFolderView; -class LLFolderViewEventListener; -class LLFolderViewFolder; -class LLFolderViewFunctor; -class LLFolderViewItem; -class LLFolderViewListenerFunctor; -class LLInventoryFilter; -class LLMenuGL; -class LLUIImage; -class LLViewerInventoryItem; - -// These are grouping of inventory types. -// Order matters when sorting system folders to the top. -enum EInventorySortGroup -{ - SG_SYSTEM_FOLDER, - SG_TRASH_FOLDER, - SG_NORMAL_FOLDER, - SG_ITEM -}; - -// *TODO: do we really need one sort object per folder? -// can we just have one of these per LLFolderView ? -class LLInventorySort -{ -public: - LLInventorySort() - : mSortOrder(0), - mByDate(false), - mSystemToTop(false), - mFoldersByName(false) { } - - // Returns true if order has changed - bool updateSort(U32 order); - U32 getSort() { return mSortOrder; } - bool isByDate() { return mByDate; } - - bool operator()(const LLFolderViewItem* const& a, const LLFolderViewItem* const& b); -private: - U32 mSortOrder; - bool mByDate; - bool mSystemToTop; - bool mFoldersByName; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLFolderViewItem -// -// An instance of this class represents a single item in a folder view -// such as an inventory item or a file. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLFolderViewItem : public LLView -{ -public: - static void initClass(); - static void cleanupClass(); - - struct Params : public LLInitParam::Block<Params, LLView::Params> - { - Optional<LLUIImage*> icon; - Optional<LLUIImage*> icon_open; // used for folders - Optional<LLUIImage*> icon_overlay; // for links - Optional<LLFolderView*> root; - Mandatory<LLFolderViewEventListener*> listener; - - Optional<LLUIImage*> folder_arrow_image; - Optional<S32> folder_indentation; // pixels - Optional<LLUIImage*> selection_image; - Optional<S32> item_height; // pixels - Optional<S32> item_top_pad; // pixels - - Optional<S32> creation_date; //UTC seconds - - Params(); - }; - - // layout constants - static const S32 LEFT_PAD = 5; - // LEFT_INDENTATION is set via folder_indentation above - static const S32 ICON_PAD = 2; - static const S32 ICON_WIDTH = 16; - static const S32 TEXT_PAD = 1; - static const S32 TEXT_PAD_RIGHT = 4; - static const S32 ARROW_SIZE = 12; - static const S32 MAX_FOLDER_ITEM_OVERLAP = 2; - // animation parameters - static const F32 FOLDER_CLOSE_TIME_CONSTANT; - static const F32 FOLDER_OPEN_TIME_CONSTANT; - - // Mostly for debugging printout purposes. - const std::string& getSearchableLabel() { return mSearchableLabel; } - - BOOL isLoading() const { return mIsLoading; } - -private: - BOOL mIsSelected; - -protected: - friend class LLUICtrlFactory; - friend class LLFolderViewEventListener; - - LLFolderViewItem(const Params& p); - - std::string mLabel; - std::string mSearchableLabel; - S32 mLabelWidth; - bool mLabelWidthDirty; - time_t mCreationDate; - LLFolderViewFolder* mParentFolder; - LLFolderViewEventListener* mListener; - BOOL mIsCurSelection; - BOOL mSelectPending; - LLFontGL::StyleFlags mLabelStyle; - std::string mLabelSuffix; - LLUIImagePtr mIcon; - std::string mStatusText; - LLUIImagePtr mIconOpen; - LLUIImagePtr mIconOverlay; - BOOL mHasVisibleChildren; - S32 mIndentation; - S32 mItemHeight; - BOOL mPassedFilter; - S32 mLastFilterGeneration; - std::string::size_type mStringMatchOffset; - F32 mControlLabelRotation; - LLFolderView* mRoot; - BOOL mDragAndDropTarget; - BOOL mIsLoading; - LLTimer mTimeSinceRequestStart; - bool mShowLoadStatus; - bool mIsMouseOverTitle; - - // helper function to change the selection from the root. - void changeSelectionFromRoot(LLFolderViewItem* selection, BOOL selected); - - // this is an internal method used for adding items to folders. A - // no-op at this level, but reimplemented in derived classes. - virtual BOOL addItem(LLFolderViewItem*) { return FALSE; } - virtual BOOL addFolder(LLFolderViewFolder*) { return FALSE; } - - static LLFontGL* getLabelFontForStyle(U8 style); - - virtual void setCreationDate(time_t creation_date_utc) { mCreationDate = creation_date_utc; } - -public: - BOOL postBuild(); - - // This function clears the currently selected item, and records - // the specified selected item appropriately for display and use - // in the UI. If open is TRUE, then folders are opened up along - // the way to the selection. - void setSelectionFromRoot(LLFolderViewItem* selection, BOOL openitem, - BOOL take_keyboard_focus = TRUE); - - // This function is called when the folder view is dirty. It's - // implemented here but called by derived classes when folding the - // views. - void arrangeFromRoot(); - void filterFromRoot( void ); - - void arrangeAndSet(BOOL set_selection, BOOL take_keyboard_focus); - - virtual ~LLFolderViewItem( void ); - - // addToFolder() returns TRUE if it succeeds. FALSE otherwise - enum { ARRANGE = TRUE, DO_NOT_ARRANGE = FALSE }; - virtual BOOL addToFolder(LLFolderViewFolder* folder, LLFolderView* root); - - virtual EInventorySortGroup getSortGroup() const; - - // Finds width and height of this object and it's children. Also - // makes sure that this view and it's children are the right size. - virtual S32 arrange( S32* width, S32* height, S32 filter_generation ); - virtual S32 getItemHeight(); - - // applies filters to control visibility of inventory items - virtual void filter( LLInventoryFilter& filter); - - // updates filter serial number and optionally propagated value up to root - S32 getLastFilterGeneration() { return mLastFilterGeneration; } - - virtual void dirtyFilter(); - - // If 'selection' is 'this' then note that otherwise ignore. - // Returns TRUE if this item ends up being selected. - virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus); - - // This method is used to set the selection state of an item. - // If 'selection' is 'this' then note selection. - // Returns TRUE if the selection state of this item was changed. - virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected); - - // this method is used to deselect this element - void deselectItem(); - - // this method is used to select this element - virtual void selectItem(); - - // gets multiple-element selection - virtual std::set<LLUUID> getSelectionList() const; - - // Returns true is this object and all of its children can be removed (deleted by user) - virtual BOOL isRemovable(); - - // Returns true is this object and all of its children can be moved - virtual BOOL isMovable(); - - // destroys this item recursively - virtual void destroyView(); - - BOOL isSelected() const { return mIsSelected; } - - void setUnselected() { mIsSelected = FALSE; } - - void setIsCurSelection(BOOL select) { mIsCurSelection = select; } - - BOOL getIsCurSelection() { return mIsCurSelection; } - - BOOL hasVisibleChildren() { return mHasVisibleChildren; } - - void setShowLoadStatus(bool status) { mShowLoadStatus = status; } - - // Call through to the viewed object and return true if it can be - // removed. Returns true if it's removed. - //virtual BOOL removeRecursively(BOOL single_item); - BOOL remove(); - - // Build an appropriate context menu for the item. Flags unused. - void buildContextMenu(LLMenuGL& menu, U32 flags); - - // This method returns the actual name of the thing being - // viewed. This method will ask the viewed object itself. - const std::string& getName( void ) const; - - const std::string& getSearchableLabel( void ) const; - - // This method returns the label displayed on the view. This - // method was primarily added to allow sorting on the folder - // contents possible before the entire view has been constructed. - const std::string& getLabel() const { return mLabel; } - - // Used for sorting, like getLabel() above. - virtual time_t getCreationDate() const { return mCreationDate; } - - LLFolderViewFolder* getParentFolder( void ) { return mParentFolder; } - const LLFolderViewFolder* getParentFolder( void ) const { return mParentFolder; } - - LLFolderViewItem* getNextOpenNode( BOOL include_children = TRUE ); - LLFolderViewItem* getPreviousOpenNode( BOOL include_children = TRUE ); - - const LLFolderViewEventListener* getListener( void ) const { return mListener; } - LLFolderViewEventListener* getListener( void ) { return mListener; } - - // Gets the inventory item if it exists (null otherwise) - LLViewerInventoryItem * getInventoryItem(void); - - // just rename the object. - void rename(const std::string& new_name); - - // open - virtual void openItem( void ); - virtual void preview(void); - - // Show children (unfortunate that this is called "open") - virtual void setOpen(BOOL open = TRUE) {}; - - virtual BOOL isOpen() const { return FALSE; } - - virtual LLFolderView* getRoot(); - BOOL isDescendantOf( const LLFolderViewFolder* potential_ancestor ); - S32 getIndentation() { return mIndentation; } - - virtual BOOL potentiallyVisible(); // do we know for a fact that this item won't be displayed? - virtual BOOL potentiallyFiltered(); // do we know for a fact that this item has been filtered out? - - virtual BOOL getFiltered(); - virtual BOOL getFiltered(S32 filter_generation); - virtual void setFiltered(BOOL filtered, S32 filter_generation); - - // change the icon - void setIcon(LLUIImagePtr icon); - - // refresh information from the object being viewed. - void refreshFromListener(); - virtual void refresh(); - - virtual void applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor); - - // LLView functionality - virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); - virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask ); - virtual BOOL handleHover( S32 x, S32 y, MASK mask ); - virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask ); - virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask ); - - virtual void onMouseLeave(S32 x, S32 y, MASK mask); - - virtual LLView* findChildView(const std::string& name, BOOL recurse) const { return NULL; } - - // virtual void handleDropped(); - virtual void draw(); - virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); - -private: - static std::map<U8, LLFontGL*> sFonts; // map of styles to fonts -}; - - -// function used for sorting. -typedef bool (*sort_order_f)(LLFolderViewItem* a, LLFolderViewItem* b); - - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLFolderViewFolder -// -// An instance of an LLFolderViewFolder represents a collection of -// more folders and items. This is used to build the hierarchy of -// items found in the folder view. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLFolderViewFolder : public LLFolderViewItem -{ -protected: - LLFolderViewFolder( const LLFolderViewItem::Params& ); - friend class LLUICtrlFactory; - -public: - typedef enum e_trash - { - UNKNOWN, TRASH, NOT_TRASH - } ETrash; - - typedef std::list<LLFolderViewItem*> items_t; - typedef std::list<LLFolderViewFolder*> folders_t; - -protected: - items_t mItems; - folders_t mFolders; - LLInventorySort mSortFunction; - - BOOL mIsOpen; - BOOL mExpanderHighlighted; - F32 mCurHeight; - F32 mTargetHeight; - F32 mAutoOpenCountdown; - time_t mSubtreeCreationDate; - mutable ETrash mAmTrash; - S32 mLastArrangeGeneration; - S32 mLastCalculatedWidth; - S32 mCompletedFilterGeneration; - S32 mMostFilteredDescendantGeneration; - bool mNeedsSort; - bool mPassedFolderFilter; - -public: - typedef enum e_recurse_type - { - RECURSE_NO, - RECURSE_UP, - RECURSE_DOWN, - RECURSE_UP_DOWN - } ERecurseType; - - - virtual ~LLFolderViewFolder( void ); - - virtual BOOL potentiallyVisible(); - - LLFolderViewItem* getNextFromChild( LLFolderViewItem*, BOOL include_children = TRUE ); - LLFolderViewItem* getPreviousFromChild( LLFolderViewItem*, BOOL include_children = TRUE ); - - // addToFolder() returns TRUE if it succeeds. FALSE otherwise - virtual BOOL addToFolder(LLFolderViewFolder* folder, LLFolderView* root); - - // Finds width and height of this object and it's children. Also - // makes sure that this view and it's children are the right size. - virtual S32 arrange( S32* width, S32* height, S32 filter_generation ); - - BOOL needsArrange(); - void requestSort(); - - // Returns the sort group (system, trash, folder) for this folder. - virtual EInventorySortGroup getSortGroup() const; - - virtual void setCompletedFilterGeneration(S32 generation, BOOL recurse_up); - virtual S32 getCompletedFilterGeneration() { return mCompletedFilterGeneration; } - - BOOL hasFilteredDescendants(S32 filter_generation); - BOOL hasFilteredDescendants(); - - // applies filters to control visibility of inventory items - virtual void filter( LLInventoryFilter& filter); - virtual void setFiltered(BOOL filtered, S32 filter_generation); - virtual BOOL getFiltered(); - virtual BOOL getFiltered(S32 filter_generation); - - virtual void dirtyFilter(); - - // folder-specific filtering (filter status propagates top down instead of bottom up) - void filterFolder(LLInventoryFilter& filter); - void setFilteredFolder(bool filtered, S32 filter_generation); - bool getFilteredFolder(S32 filter_generation); - - // Passes selection information on to children and record - // selection information if necessary. - // Returns TRUE if this object (or a child) ends up being selected. - // If 'openitem' is TRUE then folders are opened up along the way to the selection. - virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus); - - // This method is used to change the selection of an item. - // Recursively traverse all children; if 'selection' is 'this' then change - // the select status if necessary. - // Returns TRUE if the selection state of this folder, or of a child, was changed. - virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected); - - // this method is used to group select items - void extendSelectionTo(LLFolderViewItem* selection); - - // Returns true is this object and all of its children can be removed. - virtual BOOL isRemovable(); - - // Returns true is this object and all of its children can be moved - virtual BOOL isMovable(); - - // destroys this folder, and all children - virtual void destroyView(); - - // If this folder can be removed, remove all children that can be - // removed, return TRUE if this is empty after the operation and - // it's viewed folder object can be removed. - //virtual BOOL removeRecursively(BOOL single_item); - //virtual BOOL remove(); - - // remove the specified item (and any children) if - // possible. Return TRUE if the item was deleted. - BOOL removeItem(LLFolderViewItem* item); - - // simply remove the view (and any children) Don't bother telling - // the listeners. - void removeView(LLFolderViewItem* item); - - // extractItem() removes the specified item from the folder, but - // doesn't delete it. - void extractItem( LLFolderViewItem* item ); - - // This function is called by a child that needs to be resorted. - void resort(LLFolderViewItem* item); - - void setItemSortOrder(U32 ordering); - void sortBy(U32); - //BOOL (*func)(LLFolderViewItem* a, LLFolderViewItem* b)); - - void setAutoOpenCountdown(F32 countdown) { mAutoOpenCountdown = countdown; } - - // folders can be opened. This will usually be called by internal - // methods. - virtual void toggleOpen(); - - // Force a folder open or closed - virtual void setOpen(BOOL openitem = TRUE); - - // Called when a child is refreshed. - // don't rearrange child folder contents unless explicitly requested - virtual void requestArrange(BOOL include_descendants = FALSE); - - // internal method which doesn't update the entire view. This - // method was written because the list iterators destroy the state - // of other iterations, thus, we can't arrange while iterating - // through the children (such as when setting which is selected. - virtual void setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse = RECURSE_NO); - - // Get the current state of the folder. - virtual BOOL isOpen() const { return mIsOpen; } - - // special case if an object is dropped on the child. - BOOL handleDragAndDropFromChild(MASK mask, - BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); - - void applyFunctorRecursively(LLFolderViewFunctor& functor); - virtual void applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor); - - // Just apply this functor to the folder's immediate children. - void applyFunctorToChildren(LLFolderViewFunctor& functor); - - virtual void openItem( void ); - virtual BOOL addItem(LLFolderViewItem* item); - virtual BOOL addFolder( LLFolderViewFolder* folder); - - // LLView functionality - virtual BOOL handleHover(S32 x, S32 y, MASK mask); - virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); - virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask ); - virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask ); - virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); - BOOL handleDragAndDropToThisFolder(MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); - virtual void draw(); - - time_t getCreationDate() const; - bool isTrash() const; - - folders_t::const_iterator getFoldersBegin() const { return mFolders.begin(); } - folders_t::const_iterator getFoldersEnd() const { return mFolders.end(); } - folders_t::size_type getFoldersCount() const { return mFolders.size(); } - - items_t::const_iterator getItemsBegin() const { return mItems.begin(); } - items_t::const_iterator getItemsEnd() const { return mItems.end(); } - items_t::size_type getItemsCount() const { return mItems.size(); } - LLFolderViewFolder* getCommonAncestor(LLFolderViewItem* item_a, LLFolderViewItem* item_b, bool& reverse); - void gatherChildRangeExclusive(LLFolderViewItem* start, LLFolderViewItem* end, bool reverse, std::vector<LLFolderViewItem*>& items); -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLFolderViewListenerFunctor -// -// This simple abstract base class can be used to applied to all -// listeners in a hierarchy. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLFolderViewListenerFunctor -{ -public: - virtual ~LLFolderViewListenerFunctor() {} - virtual void operator()(LLFolderViewEventListener* listener) = 0; -}; - -#endif // LLFOLDERVIEWITEM_H diff --git a/indra/newview/llfolderviewmodelinventory.cpp b/indra/newview/llfolderviewmodelinventory.cpp new file mode 100644 index 0000000000..8a4b4bae84 --- /dev/null +++ b/indra/newview/llfolderviewmodelinventory.cpp @@ -0,0 +1,322 @@ +/* + * @file llfolderviewmodelinventory.cpp + * @brief Implementation of the inventory-specific view model + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llfolderviewmodelinventory.h" +#include "llinventorymodelbackgroundfetch.h" +#include "llinventorypanel.h" +#include "lltooldraganddrop.h" + +// +// class LLFolderViewModelInventory +// +static LLFastTimer::DeclareTimer FTM_INVENTORY_SORT("Sort"); + +bool LLFolderViewModelInventory::startDrag(std::vector<LLFolderViewModelItem*>& items) +{ + std::vector<EDragAndDropType> types; + uuid_vec_t cargo_ids; + std::vector<LLFolderViewModelItem*>::iterator item_it; + bool can_drag = true; + if (!items.empty()) + { + for (item_it = items.begin(); item_it != items.end(); ++item_it) + { + EDragAndDropType type = DAD_NONE; + LLUUID id = LLUUID::null; + can_drag = can_drag && static_cast<LLFolderViewModelItemInventory*>(*item_it)->startDrag(&type, &id); + + types.push_back(type); + cargo_ids.push_back(id); + } + + LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, + static_cast<LLFolderViewModelItemInventory*>(items.front())->getDragSource(), mTaskID); + } + return can_drag; +} + + +void LLFolderViewModelInventory::sort( LLFolderViewFolder* folder ) +{ + LLFastTimer _(FTM_INVENTORY_SORT); + + if (!needsSort(folder->getViewModelItem())) return; + + LLFolderViewModelItemInventory* modelp = static_cast<LLFolderViewModelItemInventory*>(folder->getViewModelItem()); + if (modelp->getUUID().isNull()) return; + + for (std::list<LLFolderViewFolder*>::iterator it = folder->getFoldersBegin(), end_it = folder->getFoldersEnd(); + it != end_it; + ++it) + { + LLFolderViewFolder* child_folderp = *it; + sort(child_folderp); + + if (child_folderp->getFoldersCount() > 0) + { + time_t most_recent_folder_time = + static_cast<LLFolderViewModelItemInventory*>((*child_folderp->getFoldersBegin())->getViewModelItem())->getCreationDate(); + LLFolderViewModelItemInventory* modelp = static_cast<LLFolderViewModelItemInventory*>(child_folderp->getViewModelItem()); + if (most_recent_folder_time > modelp->getCreationDate()) + { + modelp->setCreationDate(most_recent_folder_time); + } + } + if (child_folderp->getItemsCount() > 0) + { + time_t most_recent_item_time = + static_cast<LLFolderViewModelItemInventory*>((*child_folderp->getItemsBegin())->getViewModelItem())->getCreationDate(); + + LLFolderViewModelItemInventory* modelp = static_cast<LLFolderViewModelItemInventory*>(child_folderp->getViewModelItem()); + if (most_recent_item_time > modelp->getCreationDate()) + { + modelp->setCreationDate(most_recent_item_time); + } + } + } + base_t::sort(folder); +} + +bool LLFolderViewModelInventory::contentsReady() +{ + return !LLInventoryModelBackgroundFetch::instance().folderFetchActive(); +} + +void LLFolderViewModelItemInventory::requestSort() +{ + LLFolderViewModelItemCommon::requestSort(); + LLFolderViewFolder* folderp = dynamic_cast<LLFolderViewFolder*>(mFolderViewItem); + if (folderp) + { + folderp->requestArrange(); + } + if (static_cast<LLFolderViewModelInventory&>(mRootViewModel).getSorter().isByDate()) + { + // sort by date potentially affects parent folders which use a date + // derived from newest item in them + if (mParent) + { + mParent->requestSort(); + } + } +} + +void LLFolderViewModelItemInventory::setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset, std::string::size_type string_size) +{ + LLFolderViewModelItemCommon::setPassedFilter(passed, filter_generation, string_offset, string_size); + + bool passed_filter_before = mPrevPassedAllFilters; + mPrevPassedAllFilters = passedFilter(filter_generation); + + if (passed_filter_before != mPrevPassedAllFilters) + { + //TODO RN: ensure this still happens, but without dependency on folderview + LLFolderViewFolder* parent_folder = mFolderViewItem->getParentFolder(); + if (parent_folder) + { + parent_folder->requestArrange(); + } + } +} + +bool LLFolderViewModelItemInventory::filterChildItem( LLFolderViewModelItem* item, LLFolderViewFilter& filter ) +{ + S32 filter_generation = filter.getCurrentGeneration(); + + bool continue_filtering = true; + if (item->getLastFilterGeneration() < filter_generation) + { + // recursive application of the filter for child items + continue_filtering = item->filter( filter ); + } + + // track latest generation to pass any child items, for each folder up to root + if (item->passedFilter()) + { + LLFolderViewModelItemInventory* view_model = this; + + while(view_model && view_model->mMostFilteredDescendantGeneration < filter_generation) + { + view_model->mMostFilteredDescendantGeneration = filter_generation; + view_model = static_cast<LLFolderViewModelItemInventory*>(view_model->mParent); + } + } + + return continue_filtering; +} + +bool LLFolderViewModelItemInventory::filter( LLFolderViewFilter& filter) +{ + const S32 filter_generation = filter.getCurrentGeneration(); + const S32 must_pass_generation = filter.getFirstRequiredGeneration(); + + if (getLastFilterGeneration() >= must_pass_generation + && getLastFolderFilterGeneration() >= must_pass_generation + && !passedFilter(must_pass_generation)) + { + // failed to pass an earlier filter that was a subset of the current one + // go ahead and flag this item as done + setPassedFilter(false, filter_generation); + setPassedFolderFilter(false, filter_generation); + return true; + } + + const bool passed_filter_folder = (getInventoryType() == LLInventoryType::IT_CATEGORY) + ? filter.checkFolder(this) + : true; + setPassedFolderFilter(passed_filter_folder, filter_generation); + + if(!mChildren.empty() + && (getLastFilterGeneration() < must_pass_generation // haven't checked descendants against minimum required generation to pass + || descendantsPassedFilter(must_pass_generation))) // or at least one descendant has passed the minimum requirement + { + // now query children + for (child_list_t::iterator iter = mChildren.begin(), end_iter = mChildren.end(); + iter != end_iter && filter.getFilterCount() > 0; + ++iter) + { + if (!filterChildItem((*iter), filter)) + { + break; + } + } + } + + // if we didn't use all filter iterations + // that means we filtered all of our descendants + // so filter ourselves now + if (filter.getFilterCount() > 0) + { + filter.decrementFilterCount(); + + const bool passed_filter = filter.check(this); + setPassedFilter(passed_filter, filter_generation, filter.getStringMatchOffset(this), filter.getFilterStringSize()); + return true; + } + else + { + return false; + } +} + +LLFolderViewModelInventory* LLInventoryPanel::getFolderViewModel() +{ + return &mInventoryViewModel; +} + + +const LLFolderViewModelInventory* LLInventoryPanel::getFolderViewModel() const +{ + return &mInventoryViewModel; +} + +bool LLInventorySort::operator()(const LLFolderViewModelItemInventory* const& a, const LLFolderViewModelItemInventory* const& b) const +{ + // ignore sort order for landmarks in the Favorites folder. + // they should be always sorted as in Favorites bar. See EXT-719 + //TODO RN: fix sorting in favorites folder + //if (a->getSortGroup() == SG_ITEM + // && b->getSortGroup() == SG_ITEM + // && a->getInventoryType() == LLInventoryType::IT_LANDMARK + // && b->getInventoryType() == LLInventoryType::IT_LANDMARK) + //{ + + // static const LLUUID& favorites_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + + // LLUUID a_uuid = a->getParentFolder()->getUUID(); + // LLUUID b_uuid = b->getParentFolder()->getUUID(); + + // if ((a_uuid == favorites_folder_id && b_uuid == favorites_folder_id)) + // { + // // *TODO: mantipov: probably it is better to add an appropriate method to LLFolderViewItem + // // or to LLInvFVBridge + // LLViewerInventoryItem* aitem = (static_cast<const LLItemBridge*>(a))->getItem(); + // LLViewerInventoryItem* bitem = (static_cast<const LLItemBridge*>(b))->getItem(); + // if (!aitem || !bitem) + // return false; + // S32 a_sort = aitem->getSortField(); + // S32 b_sort = bitem->getSortField(); + // return a_sort < b_sort; + // } + //} + + // We sort by name if we aren't sorting by date + // OR if these are folders and we are sorting folders by name. + bool by_name = (!mByDate + || (mFoldersByName + && (a->getSortGroup() != SG_ITEM))); + + if (a->getSortGroup() != b->getSortGroup()) + { + if (mSystemToTop) + { + // Group order is System Folders, Trash, Normal Folders, Items + return (a->getSortGroup() < b->getSortGroup()); + } + else if (mByDate) + { + // Trash needs to go to the bottom if we are sorting by date + if ( (a->getSortGroup() == SG_TRASH_FOLDER) + || (b->getSortGroup() == SG_TRASH_FOLDER)) + { + return (b->getSortGroup() == SG_TRASH_FOLDER); + } + } + } + + if (by_name) + { + S32 compare = LLStringUtil::compareDict(a->getDisplayName(), b->getDisplayName()); + if (0 == compare) + { + return (a->getCreationDate() > b->getCreationDate()); + } + else + { + return (compare < 0); + } + } + else + { + time_t first_create = a->getCreationDate(); + time_t second_create = b->getCreationDate(); + if (first_create == second_create) + { + return (LLStringUtil::compareDict(a->getDisplayName(), b->getDisplayName()) < 0); + } + else + { + return (first_create > second_create); + } + } +} + +LLFolderViewModelItemInventory::LLFolderViewModelItemInventory( class LLFolderViewModelInventory& root_view_model ) + : LLFolderViewModelItemCommon(root_view_model), + mPrevPassedAllFilters(false) +{ +} diff --git a/indra/newview/llfolderviewmodelinventory.h b/indra/newview/llfolderviewmodelinventory.h new file mode 100644 index 0000000000..890d03d1c9 --- /dev/null +++ b/indra/newview/llfolderviewmodelinventory.h @@ -0,0 +1,118 @@ +/** + * @file llfolderviewmodelinventory.h + * @brief view model implementation specific to inventory + * class definition + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFOLDERVIEWMODELINVENTORY_H +#define LL_LLFOLDERVIEWMODELINVENTORY_H + +#include "llinventoryfilter.h" +#include "llinventory.h" +#include "llwearabletype.h" +#include "lltooldraganddrop.h" + +class LLFolderViewModelItemInventory + : public LLFolderViewModelItemCommon +{ +public: + LLFolderViewModelItemInventory(class LLFolderViewModelInventory& root_view_model); + virtual const LLUUID& getUUID() const = 0; + virtual time_t getCreationDate() const = 0; // UTC seconds + virtual void setCreationDate(time_t creation_date_utc) = 0; + virtual PermissionMask getPermissionMask() const = 0; + virtual LLFolderType::EType getPreferredType() const = 0; + virtual void showProperties(void) = 0; + virtual BOOL isItemInTrash( void) const { return FALSE; } // TODO: make into pure virtual. + virtual BOOL isUpToDate() const = 0; + virtual bool hasChildren() const = 0; + virtual LLInventoryType::EType getInventoryType() const = 0; + virtual void performAction(LLInventoryModel* model, std::string action) = 0; + virtual LLWearableType::EType getWearableType() const = 0; + virtual EInventorySortGroup getSortGroup() const = 0; + virtual LLInventoryObject* getInventoryObject() const = 0; + virtual void requestSort(); + virtual void setPassedFilter(bool filtered, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0); + virtual bool filter( LLFolderViewFilter& filter); + virtual bool filterChildItem( LLFolderViewModelItem* item, LLFolderViewFilter& filter); + + virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const = 0; + virtual LLToolDragAndDrop::ESource getDragSource() const = 0; + +protected: + bool mPrevPassedAllFilters; +}; + +class LLInventorySort +{ +public: + struct Params : public LLInitParam::Block<Params> + { + Optional<S32> order; + + Params() + : order("order", 0) + {} + }; + + LLInventorySort(S32 order = 0) + { + fromParams(Params().order(order)); + } + + bool isByDate() const { return mByDate; } + U32 getSortOrder() const { return mSortOrder; } + void toParams(Params& p) { p.order(mSortOrder);} + void fromParams(Params& p) + { + mSortOrder = p.order; + mByDate = (mSortOrder & LLInventoryFilter::SO_DATE); + mSystemToTop = (mSortOrder & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP); + mFoldersByName = (mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_NAME); + } + + bool operator()(const LLFolderViewModelItemInventory* const& a, const LLFolderViewModelItemInventory* const& b) const; +private: + U32 mSortOrder; + bool mByDate; + bool mSystemToTop; + bool mFoldersByName; +}; + +class LLFolderViewModelInventory + : public LLFolderViewModel<LLInventorySort, LLFolderViewModelItemInventory, LLFolderViewModelItemInventory, LLInventoryFilter> +{ +public: + typedef LLFolderViewModel<LLInventorySort, LLFolderViewModelItemInventory, LLFolderViewModelItemInventory, LLInventoryFilter> base_t; + + void setTaskID(const LLUUID& id) {mTaskID = id;} + + void sort(LLFolderViewFolder* folder); + bool contentsReady(); + bool startDrag(std::vector<LLFolderViewModelItem*>& items); + +private: + LLUUID mTaskID; +}; +#endif // LL_LLFOLDERVIEWMODELINVENTORY_H diff --git a/indra/newview/llfollowcam.cpp b/indra/newview/llfollowcam.cpp index b670af1782..47612fe25c 100644 --- a/indra/newview/llfollowcam.cpp +++ b/indra/newview/llfollowcam.cpp @@ -38,7 +38,6 @@ std::vector<LLFollowCamParams*> LLFollowCamMgr::sParamStack; //------------------------------------------------------- // constants //------------------------------------------------------- -const F32 ONE_HALF = 0.5; const F32 FOLLOW_CAM_ZOOM_FACTOR = 0.1f; const F32 FOLLOW_CAM_MIN_ZOOM_AMOUNT = 0.1f; const F32 DISTANCE_EPSILON = 0.0001f; diff --git a/indra/newview/llfriendcard.cpp b/indra/newview/llfriendcard.cpp index 11401d6c68..a64ddd185d 100644 --- a/indra/newview/llfriendcard.cpp +++ b/indra/newview/llfriendcard.cpp @@ -47,13 +47,13 @@ static const std::string INVENTORY_STRING_FRIENDS_ALL_SUBFOLDER = "All"; // helper functions // NOTE: For now Friends & All folders are created as protected folders of the LLFolderType::FT_CALLINGCARD type. -// So, their names will be processed in the LLFolderViewItem::refreshFromListener() to be localized +// So, their names will be processed in the LLFolderViewItem::refresh() to be localized // using "InvFolder LABEL_NAME" as LLTrans::findString argument. // We must use in this file their hard-coded names to ensure found them on different locales. EXT-5829. // These hard-coded names will be stored in InventoryItems but shown localized in FolderViewItems -// If hack in the LLFolderViewItem::refreshFromListener() to localize protected folder is removed +// If hack in the LLFolderViewItem::refresh() to localize protected folder is removed // or these folders are not protected these names should be localized in another place/way. inline const std::string get_friend_folder_name() { diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp index 66ca76bfb0..26b63bdacb 100644 --- a/indra/newview/llgesturemgr.cpp +++ b/indra/newview/llgesturemgr.cpp @@ -51,7 +51,7 @@ #include "llviewermessage.h" #include "llvoavatarself.h" #include "llviewerstats.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llappearancemgr.h" #include "llgesturelistener.h" @@ -997,7 +997,7 @@ void LLGestureMgr::runStep(LLMultiGesture* gesture, LLGestureStep* step) const BOOL animate = FALSE; - LLNearbyChatBar::getInstance()->sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate); + LLNearbyChat::getInstance()->sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate); gesture->mCurrentStep++; break; diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp index 129cddda45..aba3d74d87 100644 --- a/indra/newview/llgrouplist.cpp +++ b/indra/newview/llgrouplist.cpp @@ -86,7 +86,7 @@ LLGroupList::LLGroupList(const Params& p) registrar.add("People.Groups.Action", boost::bind(&LLGroupList::onContextMenuItemClick, this, _2)); enable_registrar.add("People.Groups.Enable", boost::bind(&LLGroupList::onContextMenuItemEnable, this, _2)); - LLMenuGL* context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_people_groups.xml", + LLToggleableMenu* context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_groups.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); if(context_menu) mContextMenuHandle = context_menu->getHandle(); @@ -112,7 +112,7 @@ BOOL LLGroupList::handleRightMouseDown(S32 x, S32 y, MASK mask) { BOOL handled = LLUICtrl::handleRightMouseDown(x, y, mask); - LLMenuGL* context_menu = (LLMenuGL*)mContextMenuHandle.get(); + LLToggleableMenu* context_menu = mContextMenuHandle.get(); if (context_menu && size() > 0) { context_menu->buildDrawLabels(); @@ -406,7 +406,7 @@ void LLGroupListItem::setActive(bool active) // *BUG: setName() overrides the style params. // Active group should be bold. - LLFontDescriptor new_desc(mGroupNameBox->getDefaultFont()->getFontDesc()); + LLFontDescriptor new_desc(mGroupNameBox->getFont()->getFontDesc()); // *NOTE dzaporozhan // On Windows LLFontGL::NORMAL will not remove LLFontGL::BOLD if font diff --git a/indra/newview/llgrouplist.h b/indra/newview/llgrouplist.h index 8abf14b3d0..e96a720886 100644 --- a/indra/newview/llgrouplist.h +++ b/indra/newview/llgrouplist.h @@ -28,10 +28,13 @@ #define LL_LLGROUPLIST_H #include "llevent.h" +#include "llpointer.h" + #include "llflatlistview.h" #include "llpanel.h" -#include "llpointer.h" #include "llstyle.h" +#include "lltoggleablemenu.h" + #include "llgroupmgr.h" /** @@ -45,6 +48,10 @@ class LLGroupList: public LLFlatListViewEx, public LLOldEvents::LLSimpleListener { LOG_CLASS(LLGroupList); public: + struct Params : public LLInitParam::Block<Params, LLFlatListViewEx::Params> + { + Params(){}; + }; LLGroupList(const Params& p); virtual ~LLGroupList(); @@ -57,6 +64,8 @@ public: void toggleIcons(); bool getIconsVisible() const { return mShowIcons; } + LLToggleableMenu* getContextMenu() const { return mContextMenuHandle.get(); } + private: void setDirty(bool val = true) { mDirty = val; } void refresh(); @@ -66,7 +75,7 @@ private: bool onContextMenuItemClick(const LLSD& userdata); bool onContextMenuItemEnable(const LLSD& userdata); - LLHandle<LLView> mContextMenuHandle; + LLHandle<LLToggleableMenu> mContextMenuHandle; bool mShowIcons; bool mDirty; diff --git a/indra/newview/llhudnametag.cpp b/indra/newview/llhudnametag.cpp index 482294c8a6..3336097955 100644 --- a/indra/newview/llhudnametag.cpp +++ b/indra/newview/llhudnametag.cpp @@ -166,7 +166,6 @@ BOOL LLHUDNameTag::lineSegmentIntersect(const LLVector3& start, const LLVector3& } // scale screen size of borders down - //RN: for now, text on hud objects is never occluded LLVector3 x_pixel_vec; LLVector3 y_pixel_vec; @@ -187,45 +186,29 @@ BOOL LLHUDNameTag::lineSegmentIntersect(const LLVector3& start, const LLVector3& + (y_pixel_vec * screen_offset.mV[VY]); - //if (mUseBubble) + LLVector3 bg_pos = render_position + + (F32)mOffsetY * y_pixel_vec + - (width_vec / 2.f) + - (height_vec); + + LLVector3 v[] = { - LLVector3 bg_pos = render_position - + (F32)mOffsetY * y_pixel_vec - - (width_vec / 2.f) - - (height_vec); - //LLUI::translate(bg_pos.mV[VX], bg_pos.mV[VY], bg_pos.mV[VZ]); + bg_pos, + bg_pos + width_vec, + bg_pos + width_vec + height_vec, + bg_pos + height_vec, + }; - LLVector3 v[] = - { - bg_pos, - bg_pos + width_vec, - bg_pos + width_vec + height_vec, - bg_pos + height_vec, - }; + LLVector3 dir = end-start; + F32 a, b, t; - if (debug_render) + if (LLTriangleRayIntersect(v[0], v[1], v[2], start, dir, a, b, t, FALSE) || + LLTriangleRayIntersect(v[2], v[3], v[0], start, dir, a, b, t, FALSE) ) + { + if (t <= 1.f) { - gGL.begin(LLRender::LINE_STRIP); - gGL.vertex3fv(v[0].mV); - gGL.vertex3fv(v[1].mV); - gGL.vertex3fv(v[2].mV); - gGL.vertex3fv(v[3].mV); - gGL.vertex3fv(v[0].mV); - gGL.vertex3fv(v[2].mV); - gGL.end(); - } - - LLVector3 dir = end-start; - F32 a, b, t; - - if (LLTriangleRayIntersect(v[0], v[1], v[2], start, dir, a, b, t, FALSE) || - LLTriangleRayIntersect(v[2], v[3], v[0], start, dir, a, b, t, FALSE) ) - { - if (t <= 1.f) - { - intersection = start + dir*t; - return TRUE; - } + intersection = start + dir*t; + return TRUE; } } @@ -241,12 +224,6 @@ void LLHUDNameTag::render() } } -void LLHUDNameTag::renderForSelect() -{ - LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); - renderText(TRUE); -} - void LLHUDNameTag::renderText(BOOL for_select) { if (!mVisible || mHidden) @@ -299,24 +276,6 @@ void LLHUDNameTag::renderText(BOOL for_select) LLColor4 bg_color = LLUIColorTable::instance().getColor("NameTagBackground"); bg_color.setAlpha(gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor); - // maybe a no-op? - //const S32 border_height = 16; - //const S32 border_width = 16; - const S32 border_height = 8; - const S32 border_width = 8; - - // *TODO move this into helper function - F32 border_scale = 1.f; - - if (border_height * 2 > mHeight) - { - border_scale = (F32)mHeight / ((F32)border_height * 2.f); - } - if (border_width * 2 > mWidth) - { - border_scale = llmin(border_scale, (F32)mWidth / ((F32)border_width * 2.f)); - } - // scale screen size of borders down //RN: for now, text on hud objects is never occluded @@ -325,152 +284,34 @@ void LLHUDNameTag::renderText(BOOL for_select) LLViewerCamera::getInstance()->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec); - LLVector2 border_scale_vec((F32)border_width / (F32)imagep->getTextureWidth(), (F32)border_height / (F32)imagep->getTextureHeight()); LLVector3 width_vec = mWidth * x_pixel_vec; LLVector3 height_vec = mHeight * y_pixel_vec; - LLVector3 scaled_border_width = (F32)llfloor(border_scale * (F32)border_width) * x_pixel_vec; - LLVector3 scaled_border_height = (F32)llfloor(border_scale * (F32)border_height) * y_pixel_vec; mRadius = (width_vec + height_vec).magVec() * 0.5f; LLCoordGL screen_pos; LLViewerCamera::getInstance()->projectPosAgentToScreen(mPositionAgent, screen_pos, FALSE); - LLVector2 screen_offset; -// if (!mUseBubble) -// { -// screen_offset = mPositionOffset; -// } -// else -// { - screen_offset = updateScreenPos(mPositionOffset); -// } + LLVector2 screen_offset = updateScreenPos(mPositionOffset); LLVector3 render_position = mPositionAgent + (x_pixel_vec * screen_offset.mV[VX]) + (y_pixel_vec * screen_offset.mV[VY]); -// if (mUseBubble) + LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); + LLRect screen_rect; + screen_rect.setCenterAndSize(0, static_cast<S32>(lltrunc(-mHeight / 2 + mOffsetY)), static_cast<S32>(lltrunc(mWidth)), static_cast<S32>(lltrunc(mHeight))); + imagep->draw3D(render_position, x_pixel_vec, y_pixel_vec, screen_rect, bg_color); + if (mLabelSegments.size()) { - LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); - LLUI::pushMatrix(); - { - LLVector3 bg_pos = render_position - + (F32)mOffsetY * y_pixel_vec - - (width_vec / 2.f) - - (height_vec); - LLUI::translate(bg_pos.mV[VX], bg_pos.mV[VY], bg_pos.mV[VZ]); - - if (for_select) - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - S32 name = mSourceObject->mGLName; - LLColor4U coloru((U8)(name >> 16), (U8)(name >> 8), (U8)name); - gGL.color4ubv(coloru.mV); - gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, height_vec); - LLUI::popMatrix(); - return; - } - else - { - gGL.getTexUnit(0)->bind(imagep->getImage()); - - gGL.color4fv(bg_color.mV); - gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, height_vec); - - if ( mLabelSegments.size()) - { - LLUI::pushMatrix(); - { - gGL.color4f(text_color.mV[VX], text_color.mV[VY], text_color.mV[VZ], gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor); - LLVector3 label_height = (mFontp->getLineHeight() * mLabelSegments.size() + (VERTICAL_PADDING / 3.f)) * y_pixel_vec; - LLVector3 label_offset = height_vec - label_height; - LLUI::translate(label_offset.mV[VX], label_offset.mV[VY], label_offset.mV[VZ]); - gl_segmented_rect_3d_tex_top(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, label_height); - } - LLUI::popMatrix(); - } - } - - BOOL outside_width = llabs(mPositionOffset.mV[VX]) > mWidth * 0.5f; - BOOL outside_height = llabs(mPositionOffset.mV[VY] + (mVertAlignment == ALIGN_VERT_TOP ? mHeight * 0.5f : 0.f)) > mHeight * (mVertAlignment == ALIGN_VERT_TOP ? mHeight * 0.75f : 0.5f); + LLUIImagePtr rect_top_image = LLUI::getUIImage("Rounded_Rect_Top"); + LLRect label_top_rect = screen_rect; + const S32 label_height = llround((mFontp->getLineHeight() * (F32)mLabelSegments.size() + (VERTICAL_PADDING / 3.f))); + label_top_rect.mBottom = label_top_rect.mTop - label_height; + LLColor4 label_top_color = text_color; + label_top_color.mV[VALPHA] = gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor; - // draw line segments pointing to parent object - if (!mOffscreen && (outside_width || outside_height)) - { - LLUI::pushMatrix(); - { - gGL.color4fv(bg_color.mV); - LLVector3 target_pos = -1.f * (mPositionOffset.mV[VX] * x_pixel_vec + mPositionOffset.mV[VY] * y_pixel_vec); - target_pos += (width_vec / 2.f); - target_pos += mVertAlignment == ALIGN_VERT_CENTER ? (height_vec * 0.5f) : LLVector3::zero; - target_pos -= 3.f * x_pixel_vec; - target_pos -= 6.f * y_pixel_vec; - LLUI::translate(target_pos.mV[VX], target_pos.mV[VY], target_pos.mV[VZ]); - gl_segmented_rect_3d_tex(border_scale_vec, 3.f * x_pixel_vec, 3.f * y_pixel_vec, 6.f * x_pixel_vec, 6.f * y_pixel_vec); - } - LLUI::popMatrix(); - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - LLGLDepthTest gls_depth(mZCompare ? GL_TRUE : GL_FALSE, GL_FALSE); - - LLVector3 box_center_offset; - box_center_offset = (width_vec * 0.5f) + (height_vec * 0.5f); - LLUI::translate(box_center_offset.mV[VX], box_center_offset.mV[VY], box_center_offset.mV[VZ]); - gGL.color4fv(bg_color.mV); - LLUI::setLineWidth(2.0); - gGL.begin(LLRender::LINES); - { - if (outside_width) - { - LLVector3 vert; - // draw line in x then y - if (mPositionOffset.mV[VX] < 0.f) - { - // start at right edge - vert = width_vec * 0.5f; - gGL.vertex3fv(vert.mV); - } - else - { - // start at left edge - vert = width_vec * -0.5f; - gGL.vertex3fv(vert.mV); - } - vert = -mPositionOffset.mV[VX] * x_pixel_vec; - gGL.vertex3fv(vert.mV); - gGL.vertex3fv(vert.mV); - vert -= mPositionOffset.mV[VY] * y_pixel_vec; - vert -= ((mVertAlignment == ALIGN_VERT_TOP) ? (height_vec * 0.5f) : LLVector3::zero); - gGL.vertex3fv(vert.mV); - } - else - { - LLVector3 vert; - // draw line in y then x - if (mPositionOffset.mV[VY] < 0.f) - { - // start at top edge - vert = (height_vec * 0.5f) - (mPositionOffset.mV[VX] * x_pixel_vec); - gGL.vertex3fv(vert.mV); - } - else - { - // start at bottom edge - vert = (height_vec * -0.5f) - (mPositionOffset.mV[VX] * x_pixel_vec); - gGL.vertex3fv(vert.mV); - } - vert = -mPositionOffset.mV[VY] * y_pixel_vec - mPositionOffset.mV[VX] * x_pixel_vec; - vert -= ((mVertAlignment == ALIGN_VERT_TOP) ? (height_vec * 0.5f) : LLVector3::zero); - gGL.vertex3fv(vert.mV); - } - } - gGL.end(); - LLUI::setLineWidth(1.0); - - } - } - LLUI::popMatrix(); + rect_top_image->draw3D(render_position, x_pixel_vec, y_pixel_vec, label_top_rect, label_top_color); } F32 y_offset = (F32)mOffsetY; @@ -874,29 +715,26 @@ void LLHUDNameTag::updateAll() for (r_it = sVisibleTextObjects.rbegin(); r_it != sVisibleTextObjects.rend(); ++r_it) { LLHUDNameTag* textp = (*r_it); -// if (textp->mUseBubble) -// { - if (current_screen_area / screen_area > LOD_2_SCREEN_COVERAGE) - { - textp->setLOD(3); - } - else if (current_screen_area / screen_area > LOD_1_SCREEN_COVERAGE) - { - textp->setLOD(2); - } - else if (current_screen_area / screen_area > LOD_0_SCREEN_COVERAGE) - { - textp->setLOD(1); - } - else - { - textp->setLOD(0); - } - textp->updateSize(); - // find on-screen position and initialize collision rectangle - textp->mTargetPositionOffset = textp->updateScreenPos(LLVector2::zero); - current_screen_area += (F32)(textp->mSoftScreenRect.getWidth() * textp->mSoftScreenRect.getHeight()); -// } + if (current_screen_area / screen_area > LOD_2_SCREEN_COVERAGE) + { + textp->setLOD(3); + } + else if (current_screen_area / screen_area > LOD_1_SCREEN_COVERAGE) + { + textp->setLOD(2); + } + else if (current_screen_area / screen_area > LOD_0_SCREEN_COVERAGE) + { + textp->setLOD(1); + } + else + { + textp->setLOD(0); + } + textp->updateSize(); + // find on-screen position and initialize collision rectangle + textp->mTargetPositionOffset = textp->updateScreenPos(LLVector2::zero); + current_screen_area += (F32)(textp->mSoftScreenRect.getWidth() * textp->mSoftScreenRect.getHeight()); } LLStat* camera_vel_stat = LLViewerCamera::getInstance()->getVelocityStat(); @@ -914,20 +752,12 @@ void LLHUDNameTag::updateAll() { LLHUDNameTag* src_textp = (*src_it); -// if (!src_textp->mUseBubble) -// { -// continue; -// } VisibleTextObjectIterator dst_it = src_it; ++dst_it; for (; dst_it != sVisibleTextObjects.end(); ++dst_it) { LLHUDNameTag* dst_textp = (*dst_it); -// if (!dst_textp->mUseBubble) -// { -// continue; -// } if (src_textp->mSoftScreenRect.overlaps(dst_textp->mSoftScreenRect)) { LLRectf intersect_rect = src_textp->mSoftScreenRect; @@ -976,10 +806,6 @@ void LLHUDNameTag::updateAll() VisibleTextObjectIterator this_object_it; for (this_object_it = sVisibleTextObjects.begin(); this_object_it != sVisibleTextObjects.end(); ++this_object_it) { -// if (!(*this_object_it)->mUseBubble) -// { -// continue; -// } (*this_object_it)->mPositionOffset = lerp((*this_object_it)->mPositionOffset, (*this_object_it)->mTargetPositionOffset, LLCriticalDamp::getInterpolant(POSITION_DAMPING_TC)); } } @@ -1037,10 +863,6 @@ void LLHUDNameTag::addPickable(std::set<LLViewerObject*> &pick_list) VisibleTextObjectIterator text_it; for (text_it = sVisibleTextObjects.begin(); text_it != sVisibleTextObjects.end(); ++text_it) { -// if (!(*text_it)->mUseBubble) -// { -// continue; -// } pick_list.insert((*text_it)->mSourceObject); } } diff --git a/indra/newview/llhudnametag.h b/indra/newview/llhudnametag.h index 3325c22def..72647d5b26 100644 --- a/indra/newview/llhudnametag.h +++ b/indra/newview/llhudnametag.h @@ -118,7 +118,6 @@ public: /*virtual*/ void markDead(); friend class LLHUDObject; /*virtual*/ F32 getDistance() const { return mLastDistance; } - //void setUseBubble(BOOL use_bubble) { mUseBubble = use_bubble; } S32 getLOD() { return mLOD; } BOOL getVisible() { return mVisible; } BOOL getHidden() const { return mHidden; } @@ -136,7 +135,6 @@ protected: LLHUDNameTag(const U8 type); /*virtual*/ void render(); - /*virtual*/ void renderForSelect(); void renderText(BOOL for_select); static void updateAll(); void setLOD(S32 lod); diff --git a/indra/newview/llhudobject.cpp b/indra/newview/llhudobject.cpp index 95d57d08d8..0960846510 100644 --- a/indra/newview/llhudobject.cpp +++ b/indra/newview/llhudobject.cpp @@ -232,9 +232,11 @@ LLHUDEffect *LLHUDObject::addHUDEffect(const U8 type) case LL_HUD_EFFECT_LOOKAT: hud_objectp = new LLHUDEffectLookAt(type); break; +#ifdef XXX_STINSON_CHUI_REWORK case LL_HUD_EFFECT_VOICE_VISUALIZER: hud_objectp = new LLVoiceVisualizer(type); break; +#endif // XXX_STINSON_CHUI_REWORK case LL_HUD_EFFECT_POINTAT: hud_objectp = new LLHUDEffectPointAt(type); break; diff --git a/indra/newview/llhudobject.h b/indra/newview/llhudobject.h index 2f7a98c86c..32cffe6839 100644 --- a/indra/newview/llhudobject.h +++ b/indra/newview/llhudobject.h @@ -39,6 +39,8 @@ #include "lldrawpool.h" // TODO: eliminate, unused below #include <list> +#define XXX_STINSON_CHUI_REWORK // temporarily re-enabling the in-world voice-dot + class LLViewerCamera; class LLFontGL; class LLFace; @@ -94,7 +96,9 @@ public: LL_HUD_EFFECT_EDIT, LL_HUD_EFFECT_LOOKAT, LL_HUD_EFFECT_POINTAT, +#ifdef XXX_STINSON_CHUI_REWORK LL_HUD_EFFECT_VOICE_VISUALIZER, // Ventrella +#endif // XXX_STINSON_CHUI_REWORK LL_HUD_NAME_TAG, LL_HUD_EFFECT_BLOB }; diff --git a/indra/newview/llimconversation.cpp b/indra/newview/llimconversation.cpp new file mode 100644 index 0000000000..b56f30312a --- /dev/null +++ b/indra/newview/llimconversation.cpp @@ -0,0 +1,406 @@ +/** + * @file llimconversation.cpp + * @brief LLIMConversation class implements the common behavior of LNearbyChatBar + * @brief and LLIMFloater for hosting both in LLIMContainer + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llimconversation.h" + +#include "llchatentry.h" +#include "llchathistory.h" +#include "lldraghandle.h" +#include "llfloaterreg.h" +#include "llimfloater.h" +#include "llimfloatercontainer.h" // to replace separate IM Floaters with multifloater container +#include "lllayoutstack.h" +#include "llnearbychat.h" + +const F32 REFRESH_INTERVAL = 0.2f; + +LLIMConversation::LLIMConversation(const LLUUID& session_id) + : LLTransientDockableFloater(NULL, true, session_id) + , mIsP2PChat(false) + , mExpandCollapseBtn(NULL) + , mTearOffBtn(NULL) + , mCloseBtn(NULL) + , mSessionID(session_id) + , mParticipantList(NULL) + , mChatHistory(NULL) + , mInputEditor(NULL) + , mInputEditorTopPad(0) + , mRefreshTimer(new LLTimer()) +{ + mCommitCallbackRegistrar.add("IMSession.Menu.Action", + boost::bind(&LLIMConversation::onIMSessionMenuItemClicked, this, _2)); + mEnableCallbackRegistrar.add("IMSession.Menu.CompactExpandedModes.CheckItem", + boost::bind(&LLIMConversation::onIMCompactExpandedMenuItemCheck, this, _2)); + mEnableCallbackRegistrar.add("IMSession.Menu.ShowModes.CheckItem", + boost::bind(&LLIMConversation::onIMShowModesMenuItemCheck, this, _2)); + mEnableCallbackRegistrar.add("IMSession.Menu.ShowModes.Enable", + boost::bind(&LLIMConversation::onIMShowModesMenuItemEnable, this, _2)); + + // Zero expiry time is set only once to allow initial update. + mRefreshTimer->setTimerExpirySec(0); + mRefreshTimer->start(); +} + +LLIMConversation::~LLIMConversation() +{ + if (mParticipantList) + { + delete mParticipantList; + mParticipantList = NULL; + } + + delete mRefreshTimer; +} + +BOOL LLIMConversation::postBuild() +{ + BOOL result; + + mCloseBtn = getChild<LLButton>("close_btn"); + mCloseBtn->setCommitCallback(boost::bind(&LLFloater::onClickClose, this)); + + mExpandCollapseBtn = getChild<LLButton>("expand_collapse_btn"); + mExpandCollapseBtn->setClickedCallback(boost::bind(&LLIMConversation::onSlide, this)); + + mParticipantListPanel = getChild<LLLayoutPanel>("speakers_list_panel"); + + mTearOffBtn = getChild<LLButton>("tear_off_btn"); + mTearOffBtn->setCommitCallback(boost::bind(&LLIMConversation::onTearOffClicked, this)); + + mChatHistory = getChild<LLChatHistory>("chat_history"); + + mInputEditor = getChild<LLChatEntry>("chat_editor"); + mInputEditor->setTextExpandedCallback(boost::bind(&LLIMConversation::reshapeChatHistory, this)); + mInputEditor->setCommitOnFocusLost( FALSE ); + mInputEditor->setPassDelete(TRUE); + mInputEditor->setFont(LLViewerChat::getChatFont()); + + mInputEditorTopPad = mChatHistory->getRect().mBottom - mInputEditor->getRect().mTop; + + setOpenPositioning(LLFloaterEnums::POSITIONING_RELATIVE); + + buildParticipantList(); + + updateHeaderAndToolbar(); + + mSaveRect = isTornOff(); + initRectControl(); + + if (isChatMultiTab()) + { + if (mIsNearbyChat) + { + setCanClose(FALSE); + } + result = LLFloater::postBuild(); + } + else + { + result = LLDockableFloater::postBuild(); + } + + return result; +} + +void LLIMConversation::draw() +{ + LLTransientDockableFloater::draw(); + + if (mRefreshTimer->hasExpired()) + { + if (mParticipantList) + { + mParticipantList->update(); + } + + refresh(); + + // Restart the refresh timer + mRefreshTimer->setTimerExpirySec(REFRESH_INTERVAL); + } +} + +void LLIMConversation::buildParticipantList() +{ + if (mIsNearbyChat) + { + LLLocalSpeakerMgr* speaker_manager = LLLocalSpeakerMgr::getInstance(); + mParticipantList = new LLParticipantList(speaker_manager, getChild<LLAvatarList>("speakers_list"), true, false); + } + else + { + LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + // for group and ad-hoc chat we need to include agent into list + if(!mIsP2PChat && mSessionID.notNull() && speaker_manager) + { + delete mParticipantList; // remove the old list and create a new one if the session id has changed + mParticipantList = new LLParticipantList(speaker_manager, getChild<LLAvatarList>("speakers_list"), true, false); + } + } +} + +void LLIMConversation::onSortMenuItemClicked(const LLSD& userdata) +{ + // TODO: Check this code when sort order menu will be added. (EM) + if (!mParticipantList) + { + return; + } + + std::string chosen_item = userdata.asString(); + + if (chosen_item == "sort_name") + { + mParticipantList->setSortOrder(LLParticipantList::E_SORT_BY_NAME); + } + +} + +void LLIMConversation::onIMSessionMenuItemClicked(const LLSD& userdata) +{ + std::string item = userdata.asString(); + + if (item == "compact_view" || item == "expanded_view") + { + gSavedSettings.setBOOL("PlainTextChatHistory", item == "compact_view"); + } + else + { + bool prev_value = gSavedSettings.getBOOL(item); + gSavedSettings.setBOOL(item, !prev_value); + } + + LLIMConversation::processChatHistoryStyleUpdate(); +} + + +bool LLIMConversation::onIMCompactExpandedMenuItemCheck(const LLSD& userdata) +{ + std::string item = userdata.asString(); + bool is_plain_text_mode = gSavedSettings.getBOOL("PlainTextChatHistory"); + + return is_plain_text_mode? item == "compact_view" : item == "expanded_view"; +} + + +bool LLIMConversation::onIMShowModesMenuItemCheck(const LLSD& userdata) +{ + return gSavedSettings.getBOOL(userdata.asString()); +} + +// enable/disable states for the "show time" and "show names" items of the show-modes menu +bool LLIMConversation::onIMShowModesMenuItemEnable(const LLSD& userdata) +{ + std::string item = userdata.asString(); + bool plain_text = gSavedSettings.getBOOL("PlainTextChatHistory"); + bool is_not_names = (item != "IMShowNamesForP2PConv"); + return (plain_text && (is_not_names || mIsP2PChat)); +} + +void LLIMConversation::hideOrShowTitle() +{ + const LLFloater::Params& default_params = LLFloater::getDefaultParams(); + S32 floater_header_size = default_params.header_height; + LLView* floater_contents = getChild<LLView>("contents_view"); + + LLRect floater_rect = getLocalRect(); + S32 top_border_of_contents = floater_rect.mTop - (isTornOff()? floater_header_size : 0); + LLRect handle_rect (0, floater_rect.mTop, floater_rect.mRight, top_border_of_contents); + LLRect contents_rect (0, top_border_of_contents, floater_rect.mRight, floater_rect.mBottom); + mDragHandle->setShape(handle_rect); + mDragHandle->setVisible(isTornOff()); + floater_contents->setShape(contents_rect); +} + +void LLIMConversation::hideAllStandardButtons() +{ + for (S32 i = 0; i < BUTTON_COUNT; i++) + { + if (mButtons[i]) + { + // Hide the standard header buttons in a docked IM floater. + mButtons[i]->setVisible(false); + } + } +} + +void LLIMConversation::updateHeaderAndToolbar() +{ + bool is_torn_off = isTornOff(); + if (!is_torn_off) + { + hideAllStandardButtons(); + } + + hideOrShowTitle(); + + // Participant list should be visible only in torn off floaters. + bool is_participant_list_visible = + is_torn_off + && gSavedSettings.getBOOL("IMShowControlPanel") + && !mIsP2PChat; + + mParticipantListPanel->setVisible(is_participant_list_visible); + + // Display collapse image (<<) if the floater is hosted + // or if it is torn off but has an open control panel. + bool is_expanded = !is_torn_off || is_participant_list_visible; + mExpandCollapseBtn->setImageOverlay(getString(is_expanded ? "collapse_icon" : "expand_icon")); + + // toggle floater's drag handle and title visibility + if (mDragHandle) + { + mDragHandle->setTitleVisible(is_torn_off); + } + + // The button (>>) should be disabled for torn off P2P conversations. + mExpandCollapseBtn->setEnabled(!is_torn_off || !mIsP2PChat); + + mTearOffBtn->setImageOverlay(getString(is_torn_off? "return_icon" : "tear_off_icon")); + + mCloseBtn->setVisible(!is_torn_off && !mIsNearbyChat); + + enableDisableCallBtn(); + + showTranslationCheckbox(); +} + +void LLIMConversation::reshapeChatHistory() +{ + LLRect chat_rect = mChatHistory->getRect(); + LLRect input_rect = mInputEditor->getRect(); + + int delta_height = chat_rect.mBottom - (input_rect.mTop + mInputEditorTopPad); + + chat_rect.setLeftTopAndSize(chat_rect.mLeft, chat_rect.mTop, chat_rect.getWidth(), chat_rect.getHeight() + delta_height); + mChatHistory->setShape(chat_rect); +} + +void LLIMConversation::showTranslationCheckbox(BOOL show) +{ + getChild<LLUICtrl>("translate_chat_checkbox_lp")->setVisible(mIsNearbyChat? show : FALSE); +} + +// static +void LLIMConversation::processChatHistoryStyleUpdate() +{ + 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<LLIMFloater*>(*iter); + if (floater) + { + floater->reloadMessages(); + } + } + + LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); + if (nearby_chat) + { + nearby_chat->reloadMessages(); + } +} + +void LLIMConversation::updateCallBtnState(bool callIsActive) +{ + getChild<LLButton>("voice_call_btn")->setImageOverlay( + callIsActive? getString("call_btn_stop") : getString("call_btn_start")); + enableDisableCallBtn(); + +} + +void LLIMConversation::onSlide(LLIMConversation* self) +{ + LLIMFloaterContainer* host_floater = dynamic_cast<LLIMFloaterContainer*>(self->getHost()); + if (host_floater) + { + // Hide the messages pane if a floater is hosted in the Conversations + host_floater->collapseMessagesPane(true); + } + else ///< floater is torn off + { + if (!self->mIsP2PChat) + { + bool expand = !self->mParticipantListPanel->getVisible(); + + // Expand/collapse the IM control panel + self->mParticipantListPanel->setVisible(expand); + + gSavedSettings.setBOOL("IMShowControlPanel", expand); + + self->mExpandCollapseBtn->setImageOverlay(self->getString(expand ? "collapse_icon" : "expand_icon")); + } + } +} + +/*virtual*/ +void LLIMConversation::onOpen(const LLSD& key) +{ + LLIMFloaterContainer* host_floater = dynamic_cast<LLIMFloaterContainer*>(getHost()); + bool is_hosted = !!host_floater; + if (is_hosted) + { + // Show the messages pane when opening a floater hosted in the Conversations + host_floater->collapseMessagesPane(false); + } + + setTornOff(!is_hosted); + updateHeaderAndToolbar(); +} + +// virtual +void LLIMConversation::onClose(bool app_quitting) +{ + // Always suppress the IM from the conversations list on close if present for any reason + if (LLIMConversation::isChatMultiTab()) + { + LLIMFloaterContainer* im_box = LLIMFloaterContainer::findInstance(); + if (im_box) + { + im_box->removeConversationListItem(this); + } + } +} + +void LLIMConversation::onTearOffClicked() +{ + setFollows(isTornOff()? FOLLOWS_ALL : FOLLOWS_NONE); + mSaveRect = isTornOff(); + initRectControl(); + LLFloater::onClickTearOff(this); + updateHeaderAndToolbar(); +} + +// static +bool LLIMConversation::isChatMultiTab() +{ + // Restart is required in order to change chat window type. + return true; +} diff --git a/indra/newview/llimconversation.h b/indra/newview/llimconversation.h new file mode 100644 index 0000000000..649c200899 --- /dev/null +++ b/indra/newview/llimconversation.h @@ -0,0 +1,128 @@ +/** + * @file llimconversation.h + * @brief LLIMConversation class implements the common behavior of LNearbyChatBar + * @brief and LLIMFloater for hosting both in LLIMContainer + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_IMCONVERSATION_H +#define LL_IMCONVERSATION_H + +#include "lllayoutstack.h" +#include "llparticipantlist.h" +#include "lltransientdockablefloater.h" +#include "llviewercontrol.h" +#include "lleventtimer.h" + +class LLPanelChatControlPanel; +class LLChatEntry; +class LLChatHistory; + +class LLIMConversation + : public LLTransientDockableFloater +{ + +public: + LOG_CLASS(LLIMConversation); + + LLIMConversation(const LLUUID& session_id); + ~LLIMConversation(); + + // reload all message with new settings of visual modes + static void processChatHistoryStyleUpdate(); + + /** + * Returns true if chat is displayed in multi tabbed floater + * false if chat is displayed in multiple windows + */ + static bool isChatMultiTab(); + + // show/hide the translation check box + void showTranslationCheckbox(const BOOL visible = FALSE); + + // LLFloater overrides + /*virtual*/ void onOpen(const LLSD& key); + /*virtual*/ void onClose(bool app_quitting); + /*virtual*/ BOOL postBuild(); + /*virtual*/ void draw(); + +protected: + + // callback for click on any items of the visual states menu + void onIMSessionMenuItemClicked(const LLSD& userdata); + + // callback for check/uncheck of the expanded/collapse mode's switcher + bool onIMCompactExpandedMenuItemCheck(const LLSD& userdata); + + // + bool onIMShowModesMenuItemCheck(const LLSD& userdata); + bool onIMShowModesMenuItemEnable(const LLSD& userdata); + static void onSlide(LLIMConversation *self); + virtual void onTearOffClicked(); + + // refresh a visual state of the Call button + void updateCallBtnState(bool callIsActive); + + // set the enable/disable state for the Call button + virtual void enableDisableCallBtn() = 0; + + void buildParticipantList(); + void onSortMenuItemClicked(const LLSD& userdata); + + void hideOrShowTitle(); // toggle the floater's drag handle + void hideAllStandardButtons(); + + bool mIsNearbyChat; + bool mIsP2PChat; + + LLLayoutPanel* mParticipantListPanel; + LLParticipantList* mParticipantList; + LLUUID mSessionID; + + LLChatHistory* mChatHistory; + LLChatEntry* mInputEditor; + int mInputEditorTopPad; // padding between input field and chat history + + LLButton* mExpandCollapseBtn; + LLButton* mTearOffBtn; + LLButton* mCloseBtn; + +private: + /// Refreshes the floater at a constant rate. + virtual void refresh() = 0; + + /// Update floater header and toolbar buttons when hosted/torn off state is toggled. + void updateHeaderAndToolbar(); + + /** + * Adjusts chat history height to fit vertically with input chat field + * and avoid overlapping, since input chat field can be vertically expanded. + * Implementation: chat history bottom "follows" top+top_pad of input chat field + */ + void reshapeChatHistory(); + + LLTimer* mRefreshTimer; ///< Defines the rate at which refresh() is called. +}; + + +#endif // LL_IMCONVERSATION_H diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 63eedcdfea..3399a88c9e 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -28,44 +28,44 @@ #include "llimfloater.h" +#include "lldraghandle.h" #include "llnotificationsutil.h" #include "llagent.h" #include "llappviewer.h" +#include "llavataractions.h" #include "llavatarnamecache.h" #include "llbutton.h" #include "llchannelmanager.h" #include "llchiclet.h" #include "llchicletbar.h" #include "llfloaterreg.h" +#include "llfloateravatarpicker.h" #include "llimfloatercontainer.h" // to replace separate IM Floaters with multifloater container #include "llinventoryfunctions.h" -#include "lllayoutstack.h" -#include "lllineeditor.h" +//#include "lllayoutstack.h" +#include "llchatentry.h" #include "lllogchat.h" -#include "llpanelimcontrolpanel.h" #include "llscreenchannel.h" #include "llsyswellwindow.h" #include "lltrans.h" #include "llchathistory.h" #include "llnotifications.h" #include "llviewerwindow.h" -#include "llvoicechannel.h" #include "lltransientfloatermgr.h" #include "llinventorymodel.h" #include "llrootview.h" #include "llspeakers.h" #include "llviewerchat.h" +#include "llnotificationmanager.h" #include "llautoreplace.h" +floater_showed_signal_t LLIMFloater::sIMFloaterShowedSignal; + LLIMFloater::LLIMFloater(const LLUUID& session_id) - : LLTransientDockableFloater(NULL, true, session_id), - mControlPanel(NULL), - mSessionID(session_id), + : LLIMConversation(session_id), mLastMessageIndex(-1), mDialog(IM_NOTHING_SPECIAL), - mChatHistory(NULL), - mInputEditor(NULL), mSavedTitle(), mTypingStart(), mShouldSendTypingState(false), @@ -74,38 +74,13 @@ LLIMFloater::LLIMFloater(const LLUUID& session_id) mTypingTimer(), mTypingTimeoutTimer(), mPositioned(false), - mSessionInitialized(false) + mSessionInitialized(false), + mStartConferenceInSameFloater(false) { - LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(mSessionID); - if (im_session) - { - mSessionInitialized = im_session->mSessionInitialized; + mIsNearbyChat = false; + + initIMSession(session_id); - mDialog = im_session->mType; - switch(mDialog){ - case IM_NOTHING_SPECIAL: - case IM_SESSION_P2P_INVITE: - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelIMControl, this); - break; - case IM_SESSION_CONFERENCE_START: - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelAdHocControl, this); - break; - case IM_SESSION_GROUP_START: - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this); - break; - case IM_SESSION_INVITE: - if (gAgent.isInGroup(mSessionID)) - { - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this); - } - else - { - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelAdHocControl, this); - } - break; - default: break; - } - } setOverlapsScreenChannel(true); LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this); @@ -133,30 +108,58 @@ void LLIMFloater::onFocusReceived() } // virtual -void LLIMFloater::onClose(bool app_quitting) +void LLIMFloater::refresh() +{ + if (mMeTyping) { + // Time out if user hasn't typed for a while. + if (mTypingTimeoutTimer.getElapsedTimeF32() > LLAgent::TYPING_TIMEOUT_SECS) + { setTyping(false); + } + } +} - // The source of much argument and design thrashing - // Should the window hide or the session close when the X is clicked? - // - // Last change: - // EXT-3516 X Button should end IM session, _ button should hide - gIMMgr->leaveSession(mSessionID); +// virtual +void LLIMFloater::onClickCloseBtn() +{ + LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession( + mSessionID); + + if (session == NULL) + { + llwarns << "Empty session." << llendl; + return; +} + + bool is_call_with_chat = session->isGroupSessionType() + || session->isAdHocSessionType() || session->isP2PSessionType(); + + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); + + if (is_call_with_chat && voice_channel != NULL + && voice_channel->isActive()) + { + LLSD payload; + payload["session_id"] = mSessionID; + LLNotificationsUtil::add("ConfirmLeaveCall", LLSD(), payload, confirmLeaveCallCallback); + return; + } + + LLIMConversation::onClickCloseBtn(); } /* static */ -void LLIMFloater::newIMCallback(const LLSD& data){ - +void LLIMFloater::newIMCallback(const LLSD& data) +{ if (data["num_unread"].asInteger() > 0 || data["from_id"].asUUID().isNull()) { LLUUID session_id = data["session_id"].asUUID(); LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); - if (floater == NULL) return; // update if visible, otherwise will be updated when opened - if (floater->getVisible()) + if (floater && floater->getVisible()) { floater->updateMessages(); } @@ -189,17 +192,15 @@ void LLIMFloater::onSendMsg( LLUICtrl* ctrl, void* userdata ) void LLIMFloater::sendMsg() { - if (!gAgent.isGodlike() - && (mDialog == IM_NOTHING_SPECIAL) - && mOtherParticipantUUID.isNull()) + 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(); + LLWString text = mInputEditor->getWText(); + LLWStringUtil::trim(text); + LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. if(!text.empty()) { // Truncate and convert to UTF8 for transport @@ -208,8 +209,7 @@ void LLIMFloater::sendMsg() if (mSessionInitialized) { - LLIMModel::sendMessage(utf8_text, mSessionID, - mOtherParticipantUUID,mDialog); + LLIMModel::sendMessage(utf8_text, mSessionID, mOtherParticipantUUID, mDialog); } else { @@ -223,95 +223,307 @@ void LLIMFloater::sendMsg() } } } - - + else + { + llinfos << "Cannot send IM to everyone unless you're a god." << llendl; + } +} LLIMFloater::~LLIMFloater() { + mParticipantsListRefreshConnection.disconnect(); + mVoiceChannelStateChangeConnection.disconnect(); + if(LLVoiceClient::instanceExists()) + { + LLVoiceClient::getInstance()->removeObserver(this); + } + LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this); } -//virtual -BOOL LLIMFloater::postBuild() +void LLIMFloater::initIMSession(const LLUUID& session_id) +{ + // Change the floater key to bind it to a new session. + setKey(session_id); + + mSessionID = session_id; + mSession = LLIMModel::getInstance()->findIMSession(mSessionID); + + if (mSession) { - const LLUUID& other_party_id = LLIMModel::getInstance()->getOtherParticipantID(mSessionID); + mIsP2PChat = mSession->isP2PSessionType(); + mSessionInitialized = mSession->mSessionInitialized; + + mDialog = mSession->mType; + } +} + +void LLIMFloater::initIMFloater() +{ + const LLUUID& other_party_id = + LLIMModel::getInstance()->getOtherParticipantID(mSessionID); if (other_party_id.notNull()) { mOtherParticipantUUID = other_party_id; } - mControlPanel->setSessionId(mSessionID); - mControlPanel->getParent()->setVisible(gSavedSettings.getBOOL("IMShowControlPanel")); + boundVoiceChannel(); + + mTypingStart = LLTrans::getString("IM_typing_start_string"); + + // Show control panel in torn off floaters only. + mParticipantListPanel->setVisible(!getHost() && gSavedSettings.getBOOL("IMShowControlPanel")); + + // Disable input editor if session cannot accept text + if ( mSession && !mSession->mTextIMPossible ) + { + mInputEditor->setEnabled(FALSE); + mInputEditor->setLabel(LLTrans::getString("IM_unavailable_text_label")); + } - LLButton* slide_left = getChild<LLButton>("slide_left_btn"); - slide_left->setVisible(mControlPanel->getParent()->getVisible()); - slide_left->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); + if (mIsP2PChat) + { + // look up display name for window title + LLAvatarNameCache::get(mSession->mOtherParticipantID, + boost::bind(&LLIMFloater::onAvatarNameCache, + this, _1, _2)); + } + else + { + std::string session_name(LLIMModel::instance().getName(mSessionID)); + updateSessionName(session_name, session_name); - LLButton* slide_right = getChild<LLButton>("slide_right_btn"); - slide_right->setVisible(!mControlPanel->getParent()->getVisible()); - slide_right->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); + // For ad hoc conferences we should update the title with participants names. + if ((IM_SESSION_INVITE == mDialog && !gAgent.isInGroup(mSessionID)) + || mDialog == IM_SESSION_CONFERENCE_START) + { + if (mParticipantsListRefreshConnection.connected()) + { + mParticipantsListRefreshConnection.disconnect(); + } + + LLAvatarList* avatar_list = getChild<LLAvatarList>("speakers_list"); + mParticipantsListRefreshConnection = avatar_list->setRefreshCompleteCallback( + boost::bind(&LLIMFloater::onParticipantsListChanged, this, _1)); + } + } +} + +//virtual +BOOL LLIMFloater::postBuild() +{ + BOOL result = LLIMConversation::postBuild(); - mInputEditor = getChild<LLLineEditor>("chat_editor"); mInputEditor->setMaxTextLength(1023); // enable line history support for instant message bar - mInputEditor->setEnableLineHistory(TRUE); + // XXX stinson TODO : resolve merge by adding autoreplace to text editors +#if 0 // *TODO Establish LineEditor with autoreplace callback mInputEditor->setAutoreplaceCallback(boost::bind(&LLAutoReplace::autoreplaceCallback, LLAutoReplace::getInstance(), _1, _2)); - - LLFontGL* font = LLViewerChat::getChatFont(); - mInputEditor->setFont(font); +#endif mInputEditor->setFocusReceivedCallback( boost::bind(onInputEditorFocusReceived, _1, this) ); mInputEditor->setFocusLostCallback( boost::bind(onInputEditorFocusLost, _1, this) ); - mInputEditor->setKeystrokeCallback( onInputEditorKeystroke, this ); - mInputEditor->setCommitOnFocusLost( FALSE ); - mInputEditor->setRevertOnEsc( FALSE ); - mInputEditor->setReplaceNewlinesWithSpaces( FALSE ); - mInputEditor->setPassDelete( TRUE ); + mInputEditor->setKeystrokeCallback( boost::bind(onInputEditorKeystroke, _1, this) ); + mInputEditor->setCommitCallback(boost::bind(onSendMsg, _1, this)); + + setDocked(true); + + LLButton* add_btn = getChild<LLButton>("add_btn"); + + // Allow to add chat participants depending on the session type + add_btn->setEnabled(isInviteAllowed()); + add_btn->setClickedCallback(boost::bind(&LLIMFloater::onAddButtonClicked, this)); + + childSetAction("voice_call_btn", boost::bind(&LLIMFloater::onCallButtonClicked, this)); - childSetCommitCallback("chat_editor", onSendMsg, this); + LLVoiceClient::getInstance()->addObserver(this); - mChatHistory = getChild<LLChatHistory>("chat_history"); + //*TODO if session is not initialized yet, add some sort of a warning message like "starting session...blablabla" + //see LLFloaterIMPanel for how it is done (IB) - setDocked(true); + initIMFloater(); - mTypingStart = LLTrans::getString("IM_typing_start_string"); + // Add a conversation list item in the left pane + LLIMFloaterContainer* im_box = LLIMFloaterContainer::getInstance(); + im_box->addConversationListItem(getTitle(), getKey(), this); - // Disable input editor if session cannot accept text - LLIMModel::LLIMSession* im_session = - LLIMModel::instance().findIMSession(mSessionID); - if( im_session && !im_session->mTextIMPossible ) + return result; +} + +void LLIMFloater::onAddButtonClicked() { - mInputEditor->setEnabled(FALSE); - mInputEditor->setLabel(LLTrans::getString("IM_unavailable_text_label")); + LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLIMFloater::addSessionParticipants, this, _1), TRUE, TRUE); + if (!picker) + { + return; } - if ( im_session && im_session->isP2PSessionType()) + // Need to disable 'ok' button when selected users are already in conversation. + picker->setOkBtnEnableCb(boost::bind(&LLIMFloater::canAddSelectedToChat, this, _1)); + LLFloater* root_floater = gFloaterView->getParentFloater(this); + if (root_floater) { - // look up display name for window title - LLAvatarNameCache::get(im_session->mOtherParticipantID, - boost::bind(&LLIMFloater::onAvatarNameCache, - this, _1, _2)); + root_floater->addDependentFloater(picker); + } +} + +bool LLIMFloater::canAddSelectedToChat(const uuid_vec_t& uuids) +{ + if (!mSession + || mDialog == IM_SESSION_GROUP_START + || mDialog == IM_SESSION_INVITE && gAgent.isInGroup(mSessionID)) + { + return false; + } + + if (mIsP2PChat) + { + // For a P2P session just check if we are not adding the other participant. + + for (uuid_vec_t::const_iterator id = uuids.begin(); + id != uuids.end(); ++id) + { + if (*id == mOtherParticipantUUID) + { + return false; + } + } } else { - std::string session_name(LLIMModel::instance().getName(mSessionID)); - updateSessionName(session_name, session_name); + // For a conference session we need to check against the list from LLSpeakerMgr, + // because this list may change when participants join or leave the session. + + LLSpeakerMgr::speaker_list_t speaker_list; + LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if (speaker_mgr) + { + speaker_mgr->getSpeakerList(&speaker_list, true); } - //*TODO if session is not initialized yet, add some sort of a warning message like "starting session...blablabla" - //see LLFloaterIMPanel for how it is done (IB) + for (uuid_vec_t::const_iterator id = uuids.begin(); + id != uuids.end(); ++id) + { + for (LLSpeakerMgr::speaker_list_t::const_iterator it = speaker_list.begin(); + it != speaker_list.end(); ++it) + { + const LLPointer<LLSpeaker>& speaker = *it; + if (*id == speaker->mID) + { + return false; + } + } + } + } - if(isChatMultiTab()) + return true; +} + +void LLIMFloater::addSessionParticipants(const uuid_vec_t& uuids) + { + if (mIsP2PChat) { - return LLFloater::postBuild(); + mStartConferenceInSameFloater = true; + + uuid_vec_t temp_ids; + + // Add the initial participant of a P2P session + temp_ids.push_back(mOtherParticipantUUID); + temp_ids.insert(temp_ids.end(), uuids.begin(), uuids.end()); + + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); + + // first check whether this is a voice session + bool is_voice_call = voice_channel != NULL && voice_channel->isActive(); + + // then we can close the current session + onClose(false); + + // Start a new ad hoc voice call if we invite new participants to a P2P call, + // or start a text chat otherwise. + if (is_voice_call) + { + LLAvatarActions::startAdhocCall(temp_ids, mSessionID); + } + else + { + LLAvatarActions::startConference(temp_ids, mSessionID); } +} else { - return LLDockableFloater::postBuild(); + inviteToSession(uuids); + } +} + +void LLIMFloater::boundVoiceChannel() +{ + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); + if(voice_channel) + { + mVoiceChannelStateChangeConnection = voice_channel->setStateChangedCallback( + boost::bind(&LLIMFloater::onVoiceChannelStateChanged, this, _1, _2)); + + //call (either p2p, group or ad-hoc) can be already in started state + bool callIsActive = voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED; + updateCallBtnState(callIsActive); + } +} + +void LLIMFloater::enableDisableCallBtn() +{ + bool voice_enabled = LLVoiceClient::getInstance()->voiceEnabled() + && LLVoiceClient::getInstance()->isVoiceWorking(); + + if (mSession) + { + bool session_initialized = mSession->mSessionInitialized; + bool callback_enabled = mSession->mCallBackEnabled; + + BOOL enable_connect = + session_initialized && voice_enabled && callback_enabled; + getChildView("voice_call_btn")->setEnabled(enable_connect); + } + else + { + getChildView("voice_call_btn")->setEnabled(false); + } +} + + +void LLIMFloater::onCallButtonClicked() +{ + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); + if (voice_channel) + { + bool is_call_active = voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED; + if (is_call_active) + { + gIMMgr->endCall(mSessionID); + } + else + { + gIMMgr->startCall(mSessionID); + } + } +} + +void LLIMFloater::onChange(EStatusType status, const std::string &channelURI, bool proximal) +{ + if(status != STATUS_JOINING && status != STATUS_LEFT_CHANNEL) + { + enableDisableCallBtn(); } } +void LLIMFloater::onVoiceChannelStateChanged( + const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state) +{ + bool callIsActive = new_state >= LLVoiceChannel::STATE_CALL_STARTED; + updateCallBtnState(callIsActive); +} + void LLIMFloater::updateSessionName(const std::string& ui_title, const std::string& ui_label) { @@ -329,58 +541,59 @@ void LLIMFloater::onAvatarNameCache(const LLUUID& agent_id, mTypingStart.setArg("[NAME]", ui_title); } -// virtual -void LLIMFloater::draw() +void LLIMFloater::onParticipantsListChanged(LLUICtrl* ctrl) { - if ( mMeTyping ) + LLAvatarList* avatar_list = dynamic_cast<LLAvatarList*>(ctrl); + if (!avatar_list) { - // Time out if user hasn't typed for a while. - if ( mTypingTimeoutTimer.getElapsedTimeF32() > LLAgent::TYPING_TIMEOUT_SECS ) - { - setTyping(false); + return; } - } - LLTransientDockableFloater::draw(); -} + bool all_names_resolved = true; + std::vector<LLSD> participants_uuids; + avatar_list->getValues(participants_uuids); -// static -void* LLIMFloater::createPanelIMControl(void* userdata) + // Check whether we have all participants names in LLAvatarNameCache + for (std::vector<LLSD>::const_iterator it = participants_uuids.begin(); it != participants_uuids.end(); ++it) { - LLIMFloater *self = (LLIMFloater*)userdata; - self->mControlPanel = new LLPanelIMControlPanel(); - self->mControlPanel->setXMLFilename("panel_im_control_panel.xml"); - return self->mControlPanel; -} - + const LLUUID& id = it->asUUID(); + LLAvatarName av_name; + if (!LLAvatarNameCache::get(id, &av_name)) + { + all_names_resolved = false; -// static -void* LLIMFloater::createPanelGroupControl(void* userdata) -{ - LLIMFloater *self = (LLIMFloater*)userdata; - self->mControlPanel = new LLPanelGroupControlPanel(self->mSessionID); - self->mControlPanel->setXMLFilename("panel_group_control_panel.xml"); - return self->mControlPanel; + // If a name is not found in cache, request it and continue the process recursively + // until all ids are resolved into names. + LLAvatarNameCache::get(id, + boost::bind(&LLIMFloater::onParticipantsListChanged, this, avatar_list)); + break; + } } -// static -void* LLIMFloater::createPanelAdHocControl(void* userdata) + if (all_names_resolved) + { + std::vector<LLAvatarName> avatar_names; + std::vector<LLSD>::const_iterator it = participants_uuids.begin(); + for (; it != participants_uuids.end(); ++it) + { + const LLUUID& id = it->asUUID(); + LLAvatarName av_name; + if (LLAvatarNameCache::get(id, &av_name)) { - LLIMFloater *self = (LLIMFloater*)userdata; - self->mControlPanel = new LLPanelAdHocControlPanel(self->mSessionID); - self->mControlPanel->setXMLFilename("panel_adhoc_control_panel.xml"); - return self->mControlPanel; + avatar_names.push_back(av_name); + } } -void LLIMFloater::onSlide() + // We should check whether the vector is not empty to pass the assertion + // that avatar_names.size() > 0 in LLAvatarActions::buildResidentsString. + if (!avatar_names.empty()) { - mControlPanel->getParent()->setVisible(!mControlPanel->getParent()->getVisible()); - - gSavedSettings.setBOOL("IMShowControlPanel", mControlPanel->getParent()->getVisible()); - - getChild<LLButton>("slide_left_btn")->setVisible(mControlPanel->getParent()->getVisible()); - getChild<LLButton>("slide_right_btn")->setVisible(!mControlPanel->getParent()->getVisible()); + std::string ui_title; + LLAvatarActions::buildResidentsString(avatar_names, ui_title); + updateSessionName(ui_title, ui_title); + } +} } //static @@ -388,7 +601,8 @@ LLIMFloater* LLIMFloater::show(const LLUUID& session_id) { closeHiddenIMToasts(); - if (!gIMMgr->hasSession(session_id)) return NULL; + if (!gIMMgr->hasSession(session_id)) + return NULL; if(!isChatMultiTab()) { @@ -405,22 +619,24 @@ LLIMFloater* LLIMFloater::show(const LLUUID& session_id) } } + // Test the existence of the floater before we try to create it bool exist = findInstance(session_id); + // Get the floater: this will create the instance if it didn't exist LLIMFloater* floater = getInstance(session_id); - if (!floater) return NULL; + if (!floater) + return NULL; if(isChatMultiTab()) { LLIMFloaterContainer* floater_container = LLIMFloaterContainer::getInstance(); - // do not add existed floaters to avoid adding torn off instances + // Do not add again existing floaters if (!exist) { // LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END; // TODO: mantipov: use LLTabContainer::RIGHT_OF_CURRENT if it exists LLTabContainer::eInsertionPoint i_pt = LLTabContainer::END; - if (floater_container) { floater_container->addFloater(floater, TRUE, i_pt); @@ -458,6 +674,37 @@ LLIMFloater* LLIMFloater::show(const LLUUID& session_id) return floater; } +//static +LLIMFloater* LLIMFloater::findInstance(const LLUUID& session_id) +{ + LLIMFloater* conversation = + LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); + + return conversation; +} + +LLIMFloater* LLIMFloater::getInstance(const LLUUID& session_id) +{ + LLIMFloater* conversation = + LLFloaterReg::getTypedInstance<LLIMFloater>("impanel", session_id); + + return conversation; +} + +void LLIMFloater::onClose(bool app_quitting) +{ + setTyping(false); + + // The source of much argument and design thrashing + // Should the window hide or the session close when the X is clicked? + // + // Last change: + // EXT-3516 X Button should end IM session, _ button should hide + gIMMgr->leaveSession(mSessionID); + + // Clean up the conversation *after* the session has been ended + LLIMConversation::onClose(app_quitting); +} void LLIMFloater::setDocked(bool docked, bool pop_on_undock) { @@ -516,13 +763,21 @@ void LLIMFloater::setVisible(BOOL visible) chiclet->setToggleState(false); } } + + if (visible) + { + sIMFloaterShowedSignal(mSessionID); + } } BOOL LLIMFloater::getVisible() { + bool visible; + if(isChatMultiTab()) { - LLIMFloaterContainer* im_container = LLIMFloaterContainer::getInstance(); + LLIMFloaterContainer* im_container = + LLIMFloaterContainer::getInstance(); // Treat inactive floater as invisible. bool is_active = im_container->getActiveFloater() == this; @@ -530,16 +785,21 @@ BOOL LLIMFloater::getVisible() //torn off floater is always inactive if (!is_active && getHost() != im_container) { - return LLTransientDockableFloater::getVisible(); + visible = LLTransientDockableFloater::getVisible(); } - + else + { // getVisible() returns TRUE when Tabbed IM window is minimized. - return is_active && !im_container->isMinimized() && im_container->getVisible(); + visible = is_active && !im_container->isMinimized() + && im_container->getVisible(); + } } else { - return LLTransientDockableFloater::getVisible(); + visible = LLTransientDockableFloater::getVisible(); } + + return visible; } //static @@ -547,7 +807,8 @@ bool LLIMFloater::toggle(const LLUUID& session_id) { if(!isChatMultiTab()) { - LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); + LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>( + "impanel", session_id); if (floater && floater->getVisible() && floater->hasFocus()) { // clicking on chiclet to close floater just hides it to maintain existing @@ -568,17 +829,6 @@ bool LLIMFloater::toggle(const LLUUID& session_id) return true; } -//static -LLIMFloater* LLIMFloater::findInstance(const LLUUID& session_id) -{ - return LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); -} - -LLIMFloater* LLIMFloater::getInstance(const LLUUID& session_id) -{ - return LLFloaterReg::getTypedInstance<LLIMFloater>("impanel", session_id); -} - void LLIMFloater::sessionInitReplyReceived(const LLUUID& im_session_id) { mSessionInitialized = true; @@ -586,53 +836,57 @@ void LLIMFloater::sessionInitReplyReceived(const LLUUID& im_session_id) //will be different only for an ad-hoc im session if (mSessionID != im_session_id) { - mSessionID = im_session_id; - setKey(im_session_id); - mControlPanel->setSessionId(im_session_id); - } + initIMSession(im_session_id); - // updating "Call" button from group control panel here to enable it without placing into draw() (EXT-4796) - if(gAgent.isInGroup(im_session_id)) - { - mControlPanel->updateCallButton(); + buildParticipantList(); } + + initIMFloater(); //*TODO here we should remove "starting session..." warning message if we added it in postBuild() (IB) - //need to send delayed messaged collected while waiting for session initialization - if (!mQueuedMsgsForInit.size()) return; + if (mQueuedMsgsForInit.size()) + { LLSD::array_iterator iter; for ( iter = mQueuedMsgsForInit.beginArray(); - iter != mQueuedMsgsForInit.endArray(); - ++iter) + iter != mQueuedMsgsForInit.endArray(); ++iter) { LLIMModel::sendMessage(iter->asString(), mSessionID, mOtherParticipantUUID, mDialog); } } +} -void LLIMFloater::updateMessages() +void LLIMFloater::appendMessage(const LLChat& chat, const LLSD &args) { - bool use_plain_text_chat_history = gSavedSettings.getBOOL("PlainTextChatHistory"); + LLChat& tmp_chat = const_cast<LLChat&>(chat); + if (!chat.mMuted) + { + tmp_chat.mFromName = chat.mFromName; + LLSD chat_args; + if (args) chat_args = args; + chat_args["use_plain_text_chat_history"] = + gSavedSettings.getBOOL("PlainTextChatHistory"); + chat_args["show_time"] = gSavedSettings.getBOOL("IMShowTime"); + chat_args["show_names_for_p2p_conv"] = !mIsP2PChat + || gSavedSettings.getBOOL("IMShowNamesForP2PConv"); + + mChatHistory->appendMessage(chat, chat_args); + } +} + +void LLIMFloater::updateMessages() +{ std::list<LLSD> messages; // we shouldn't reset unread message counters if IM floater doesn't have focus - if (hasFocus()) - { - LLIMModel::instance().getMessages(mSessionID, messages, mLastMessageIndex+1); - } - else - { - LLIMModel::instance().getMessagesSilently(mSessionID, messages, mLastMessageIndex+1); - } + LLIMModel::instance().getMessages( + mSessionID, messages, mLastMessageIndex + 1, hasFocus()); if (messages.size()) { - LLSD chat_args; - chat_args["use_plain_text_chat_history"] = use_plain_text_chat_history; - std::ostringstream message; std::list<LLSD>::const_reverse_iterator iter = messages.rbegin(); std::list<LLSD>::const_reverse_iterator iter_end = messages.rend(); @@ -682,7 +936,7 @@ void LLIMFloater::updateMessages() chat.mText = message; } - mChatHistory->appendMessage(chat, chat_args); + appendMessage(chat); mLastMessageIndex = msg["index"].asInteger(); // if it is a notification - next message is a notification history log, so skip it @@ -706,6 +960,7 @@ void LLIMFloater::reloadMessages() mChatHistory->clear(); mLastMessageIndex = -1; updateMessages(); + mInputEditor->setFont(LLViewerChat::getChatFont()); } // static @@ -732,19 +987,13 @@ void LLIMFloater::onInputEditorFocusLost(LLFocusableElement* caller, void* userd } // static -void LLIMFloater::onInputEditorKeystroke(LLLineEditor* caller, void* userdata) +void LLIMFloater::onInputEditorKeystroke(LLTextEditor* 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); - } + self->setTyping(!text.empty()); } void LLIMFloater::setTyping(bool typing) @@ -769,27 +1018,25 @@ void LLIMFloater::setTyping(bool typing) // much network traffic. Only send in person-to-person IMs. if ( mShouldSendTypingState && mDialog == IM_NOTHING_SPECIAL ) { - if ( mMeTyping ) + // Still typing, send 'start typing' notification or + // send 'stop typing' notification immediately + if (!mMeTyping || mTypingTimer.getElapsedTimeF32() > 1.f) { - if ( mTypingTimer.getElapsedTimeF32() > 1.f ) - { - // Still typing, send 'start typing' notification - LLIMModel::instance().sendTypingState(mSessionID, mOtherParticipantUUID, TRUE); + LLIMModel::instance().sendTypingState(mSessionID, + mOtherParticipantUUID, mMeTyping); mShouldSendTypingState = false; + } } - else - { - // Send 'stop typing' notification immediately - LLIMModel::instance().sendTypingState(mSessionID, mOtherParticipantUUID, FALSE); - mShouldSendTypingState = false; - } - } + if (!mIsNearbyChat) + { LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); if (speaker_mgr) + { speaker_mgr->setSpeakerTyping(gAgent.getID(), FALSE); - + } +} } void LLIMFloater::processIMTyping(const LLIMInfo* im_info, BOOL typing) @@ -808,9 +1055,7 @@ void LLIMFloater::processIMTyping(const LLIMInfo* im_info, BOOL typing) void LLIMFloater::processAgentListUpdates(const LLSD& body) { - if ( !body.isMap() ) return; - - if ( body.has("agent_updates") && body["agent_updates"].isMap() ) + if (body.isMap() && body.has("agent_updates") && body["agent_updates"].isMap()) { LLSD agent_data = body["agent_updates"].get(gAgentID.asString()); if (agent_data.isMap() && agent_data.has("info")) @@ -835,30 +1080,6 @@ void LLIMFloater::processAgentListUpdates(const LLSD& body) } } -void LLIMFloater::updateChatHistoryStyle() -{ - mChatHistory->clear(); - mLastMessageIndex = -1; - updateMessages(); -} - -void LLIMFloater::processChatHistoryStyleUpdate(const LLSD& newvalue) -{ - LLFontGL* font = LLViewerChat::getChatFont(); - 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<LLIMFloater*>(*iter); - if (floater) - { - floater->updateChatHistoryStyle(); - floater->mInputEditor->setFont(font); - } - } - -} - void LLIMFloater::processSessionUpdate(const LLSD& session_update) { // *TODO : verify following code when moderated mode will be implemented @@ -870,7 +1091,8 @@ void LLIMFloater::processSessionUpdate(const LLSD& session_update) if (voice_moderated) { - setTitle(session_label + std::string(" ") + LLTrans::getString("IM_moderated_chat_label")); + setTitle(session_label + std::string(" ") + + LLTrans::getString("IM_moderated_chat_label")); } else { @@ -883,98 +1105,56 @@ void LLIMFloater::processSessionUpdate(const LLSD& session_update) } } -BOOL LLIMFloater::handleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, EDragAndDropType cargo_type, - void *cargo_data, EAcceptance *accept, +// virtual +BOOL LLIMFloater::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, std::string& tooltip_msg) { - - if (mDialog == IM_NOTHING_SPECIAL) + if (cargo_type == DAD_PERSON) { - LLToolDragAndDrop::handleGiveDragAndDrop(mOtherParticipantUUID, mSessionID, drop, - cargo_type, cargo_data, accept); + if (dropPerson(static_cast<LLUUID*>(cargo_data), drop)) + { + *accept = ACCEPT_YES_MULTI; } - - // handle case for dropping calling cards (and folders of calling cards) onto invitation panel for invites - else if (isInviteAllowed()) + else { *accept = ACCEPT_NO; - - if (cargo_type == DAD_CALLINGCARD) - { - if (dropCallingCard((LLInventoryItem*)cargo_data, drop)) - { - *accept = ACCEPT_YES_MULTI; } } - else if (cargo_type == DAD_CATEGORY) + else if (mDialog == IM_NOTHING_SPECIAL) { - if (dropCategory((LLInventoryCategory*)cargo_data, drop)) - { - *accept = ACCEPT_YES_MULTI; + LLToolDragAndDrop::handleGiveDragAndDrop(mOtherParticipantUUID, mSessionID, drop, + cargo_type, cargo_data, accept); } - } - } + return TRUE; } -BOOL LLIMFloater::dropCallingCard(LLInventoryItem* item, BOOL drop) +bool LLIMFloater::dropPerson(LLUUID* person_id, bool drop) { - BOOL rv = isInviteAllowed(); - if(rv && item && item->getCreatorUUID().notNull()) + bool res = person_id && person_id->notNull(); + if(res) { - if(drop) - { uuid_vec_t ids; - ids.push_back(item->getCreatorUUID()); - inviteToSession(ids); - } - } - else + ids.push_back(*person_id); + + res = canAddSelectedToChat(ids); + if(res && drop) { - // set to false if creator uuid is null. - rv = FALSE; + addSessionParticipants(ids); } - return rv; } -BOOL LLIMFloater::dropCategory(LLInventoryCategory* category, BOOL drop) -{ - BOOL rv = isInviteAllowed(); - if(rv && category) - { - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - LLUniqueBuddyCollector buddies; - gInventory.collectDescendentsIf(category->getUUID(), - cats, - items, - LLInventoryModel::EXCLUDE_TRASH, - buddies); - S32 count = items.count(); - if(count == 0) - { - rv = FALSE; + return res; } - else if(drop) - { - uuid_vec_t ids; - ids.reserve(count); - for(S32 i = 0; i < count; ++i) - { - ids.push_back(items.get(i)->getCreatorUUID()); - } - inviteToSession(ids); - } - } - return rv; -} BOOL LLIMFloater::isInviteAllowed() const { - return ( (IM_SESSION_CONFERENCE_START == mDialog) - || (IM_SESSION_INVITE == mDialog) ); + || (IM_SESSION_INVITE == mDialog && !gAgent.isInGroup(mSessionID)) + || mIsP2PChat); } class LLSessionInviteResponder : public LLHTTPClient::Responder @@ -998,11 +1178,10 @@ private: BOOL LLIMFloater::inviteToSession(const uuid_vec_t& ids) { LLViewerRegion* region = gAgent.getRegion(); - if (!region) - { - return FALSE; - } + bool is_region_exist = region != NULL; + if (is_region_exist) + { S32 count = ids.size(); if( isInviteAllowed() && (count > 0) ) @@ -1024,8 +1203,7 @@ BOOL LLIMFloater::inviteToSession(const uuid_vec_t& ids) LLHTTPClient::post( url, data, - new LLSessionInviteResponder( - mSessionID)); + new LLSessionInviteResponder(mSessionID)); } else { @@ -1035,8 +1213,9 @@ BOOL LLIMFloater::inviteToSession(const uuid_vec_t& ids) // successful add, because everyone that needed to get added // was added. } + } - return TRUE; + return is_region_exist; } void LLIMFloater::addTypingIndicator(const LLIMInfo* im_info) @@ -1077,7 +1256,6 @@ void LLIMFloater::removeTypingIndicator(const LLIMInfo* im_info) speaker_mgr->setSpeakerTyping(im_info->mFromID, FALSE); } } - } } @@ -1094,7 +1272,8 @@ void LLIMFloater::closeHiddenIMToasts() } }; - LLNotificationsUI::LLScreenChannel* channel = LLNotificationsUI::LLChannelManager::getNotificationScreenChannel(); + LLNotificationsUI::LLScreenChannel* channel = + LLNotificationsUI::LLChannelManager::getNotificationScreenChannel(); if (channel != NULL) { channel->closeHiddenToasts(IMToastMatcher()); @@ -1107,7 +1286,7 @@ void LLIMFloater::confirmLeaveCallCallback(const LLSD& notification, const LLSD& const LLSD& payload = notification["payload"]; LLUUID session_id = payload["session_id"]; - LLFloater* im_floater = LLFloaterReg::findInstance("impanel", session_id); + LLFloater* im_floater = findInstance(session_id); if (option == 0 && im_floater != NULL) { im_floater->closeFloater(); @@ -1117,79 +1296,49 @@ void LLIMFloater::confirmLeaveCallCallback(const LLSD& notification, const LLSD& } // static -bool LLIMFloater::isChatMultiTab() -{ - // Restart is required in order to change chat window type. - static bool is_single_window = gSavedSettings.getS32("ChatWindow") == 1; - return is_single_window; -} - -// static -void LLIMFloater::initIMFloater() -{ - // This is called on viewer start up - // init chat window type before user changed it in preferences - isChatMultiTab(); -} - -//static void LLIMFloater::sRemoveTypingIndicator(const LLSD& data) { LLUUID session_id = data["session_id"]; - if (session_id.isNull()) return; + if (session_id.isNull()) + return; LLUUID from_id = data["from_id"]; - if (gAgentID == from_id || LLUUID::null == from_id) return; + if (gAgentID == from_id || LLUUID::null == from_id) + return; LLIMFloater* floater = LLIMFloater::findInstance(session_id); - if (!floater) return; + if (!floater) + return; - if (IM_NOTHING_SPECIAL != floater->mDialog) return; + if (IM_NOTHING_SPECIAL != floater->mDialog) + return; floater->removeTypingIndicator(); } void LLIMFloater::onIMChicletCreated( const LLUUID& session_id ) { - - if (isChatMultiTab()) - { - LLIMFloaterContainer* im_box = LLIMFloaterContainer::getInstance(); - if (!im_box) return; - - if (LLIMFloater::findInstance(session_id)) return; - - LLIMFloater* new_tab = LLIMFloater::getInstance(session_id); - - im_box->addFloater(new_tab, FALSE, LLTabContainer::END); - } - + LLIMFloater::addToHost(session_id); } - -void LLIMFloater::onClickCloseBtn() +void LLIMFloater::addToHost(const LLUUID& session_id) + { + if (LLIMConversation::isChatMultiTab()) { - - LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession( - mSessionID); - - if (session == NULL) + LLIMFloaterContainer* im_box = LLIMFloaterContainer::findInstance(); + if (!im_box) { - llwarns << "Empty session." << llendl; - return; + im_box = LLIMFloaterContainer::getInstance(); } - bool is_call_with_chat = session->isGroupSessionType() - || session->isAdHocSessionType() || session->isP2PSessionType(); - - LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); - - if (is_call_with_chat && voice_channel != NULL && voice_channel->isActive()) + if (im_box && !LLIMFloater::findInstance(session_id)) { - LLSD payload; - payload["session_id"] = mSessionID; - LLNotificationsUtil::add("ConfirmLeaveCall", LLSD(), payload, confirmLeaveCallCallback); - return; + LLIMFloater* new_tab = LLIMFloater::getInstance(session_id); + im_box->addFloater(new_tab, FALSE, LLTabContainer::END); } + } +} - LLFloater::onClickCloseBtn(); +boost::signals2::connection LLIMFloater::setIMFloaterShowedCallback(const floater_showed_signal_t::slot_type& cb) +{ + return LLIMFloater::sIMFloaterShowedSignal.connect(cb); } diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index f7cd35b5eb..434613ff43 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -27,41 +27,55 @@ #ifndef LL_IMFLOATER_H #define LL_IMFLOATER_H +#include "llimview.h" +#include "llimconversation.h" #include "llinstantmessage.h" #include "lllogchat.h" #include "lltooldraganddrop.h" -#include "lltransientdockablefloater.h" +#include "llvoicechannel.h" +#include "llvoiceclient.h" class LLAvatarName; -class LLLineEditor; +class LLButton; +class LLChatEntry; +class LLTextEditor; class LLPanelChatControlPanel; class LLChatHistory; class LLInventoryItem; class LLInventoryCategory; +typedef boost::signals2::signal<void(const LLUUID& session_id)> floater_showed_signal_t; + /** * Individual IM window that appears at the bottom of the screen, * optionally "docked" to the bottom tray. */ -class LLIMFloater : public LLTransientDockableFloater +class LLIMFloater + : public LLVoiceClientStatusObserver + , public LLIMConversation { LOG_CLASS(LLIMFloater); public: LLIMFloater(const LLUUID& session_id); virtual ~LLIMFloater(); - + + void initIMSession(const LLUUID& session_id); + void initIMFloater(); + // LLView overrides /*virtual*/ BOOL postBuild(); /*virtual*/ void setVisible(BOOL visible); /*virtual*/ BOOL getVisible(); // Check typing timeout timer. - /*virtual*/ void draw(); + + static LLIMFloater* findInstance(const LLUUID& session_id); + static LLIMFloater* getInstance(const LLUUID& session_id); + static void addToHost(const LLUUID& session_id); // LLFloater overrides /*virtual*/ void onClose(bool app_quitting); /*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); @@ -69,16 +83,12 @@ public: // Returns true iff panel became visible static bool toggle(const LLUUID& session_id); - static LLIMFloater* findInstance(const LLUUID& session_id); - - static LLIMFloater* getInstance(const LLUUID& session_id); - void sessionInitReplyReceived(const LLUUID& im_session_id); // get new messages from LLIMModel void updateMessages(); void reloadMessages(); - static void onSendMsg( LLUICtrl*, void*); + static void onSendMsg(LLUICtrl*, void*); void sendMsg(); // callback for LLIMModel on new messages @@ -89,62 +99,76 @@ public: void setPositioned(bool b) { mPositioned = b; }; void onVisibilityChange(const LLSD& new_visibility); + + // Implements LLVoiceClientStatusObserver::onChange() to enable the call + // button when voice is available + void onChange(EStatusType status, const std::string &channelURI, + bool proximal); + + virtual LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; } + virtual void onVoiceChannelStateChanged( + const LLVoiceChannel::EState& old_state, + const LLVoiceChannel::EState& new_state); + void processIMTyping(const LLIMInfo* im_info, BOOL typing); void processAgentListUpdates(const LLSD& body); void processSessionUpdate(const LLSD& session_update); - void updateChatHistoryStyle(); - static void processChatHistoryStyleUpdate(const LLSD& newvalue); - - BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, EDragAndDropType cargo_type, - void *cargo_data, EAcceptance *accept, - std::string& tooltip_msg); - - /** - * Returns true if chat is displayed in multi tabbed floater - * false if chat is displayed in multiple windows - */ - static bool isChatMultiTab(); + /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg); - static void initIMFloater(); //used as a callback on receiving new IM message static void sRemoveTypingIndicator(const LLSD& data); - static void onIMChicletCreated(const LLUUID& session_id); - virtual LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; } + bool getStartConferenceInSameFloater() const { return mStartConferenceInSameFloater; } -protected: - /* virtual */ - void onClickCloseBtn(); + static boost::signals2::connection setIMFloaterShowedCallback(const floater_showed_signal_t::slot_type& cb); + static floater_showed_signal_t sIMFloaterShowedSignal; private: + // process focus events to set a currently active session /* virtual */ void onFocusLost(); /* virtual */ void onFocusReceived(); + /*virtual*/ void refresh(); + + /*virtual*/ void onClickCloseBtn(); + // Update the window title, input field help text, etc. void updateSessionName(const std::string& ui_title, const std::string& ui_label); - + // For display name lookups for IM window titles void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); - - BOOL dropCallingCard(LLInventoryItem* item, BOOL drop); - BOOL dropCategory(LLInventoryCategory* category, BOOL drop); + + /// Updates the list of ad hoc conference participants + /// in an IM floater title. + void onParticipantsListChanged(LLUICtrl* ctrl); + + bool dropPerson(LLUUID* person_id, bool drop); BOOL isInviteAllowed() const; BOOL inviteToSession(const uuid_vec_t& agent_ids); - - 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); - static void* createPanelAdHocControl(void* userdata); + void appendMessage(const LLChat& chat, const LLSD &args = 0); + static void onInputEditorFocusReceived( LLFocusableElement* caller,void* userdata ); + static void onInputEditorFocusLost(LLFocusableElement* caller, void* userdata); + static void onInputEditorKeystroke(LLTextEditor* caller, void* userdata); + void setTyping(bool typing); + void onAddButtonClicked(); + void addSessionParticipants(const uuid_vec_t& uuids); + bool canAddSelectedToChat(const uuid_vec_t& uuids); + + void onCallButtonClicked(); + + // set the enable/disable state for the Call button + virtual void enableDisableCallBtn(); + + void boundVoiceChannel(); // Add the "User is typing..." indicator. void addTypingIndicator(const LLIMInfo* im_info); @@ -156,14 +180,12 @@ private: static void confirmLeaveCallCallback(const LLSD& notification, const LLSD& response); - LLPanelChatControlPanel* mControlPanel; - LLUUID mSessionID; + + LLIMModel::LLIMSession* mSession; S32 mLastMessageIndex; EInstantMessage mDialog; LLUUID mOtherParticipantUUID; - LLChatHistory* mChatHistory; - LLLineEditor* mInputEditor; bool mPositioned; std::string mSavedTitle; @@ -176,7 +198,13 @@ private: bool mSessionInitialized; LLSD mQueuedMsgsForInit; -}; + bool mStartConferenceInSameFloater; + + // connection to voice channel state change signal + boost::signals2::connection mVoiceChannelStateChangeConnection; + + boost::signals2::connection mParticipantsListRefreshConnection; +}; #endif // LL_IMFLOATER_H diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp index 0f0ae896a2..c2c0ddddea 100644 --- a/indra/newview/llimfloatercontainer.cpp +++ b/indra/newview/llimfloatercontainer.cpp @@ -27,20 +27,34 @@ #include "llviewerprecompiledheaders.h" +#include "llimfloater.h" #include "llimfloatercontainer.h" + #include "llfloaterreg.h" -#include "llimview.h" +#include "lllayoutstack.h" +#include "llnearbychat.h" + +#include "llagent.h" +#include "llavataractions.h" #include "llavatariconctrl.h" +#include "llavatarnamecache.h" #include "llgroupiconctrl.h" -#include "llagent.h" +#include "llfloateravatarpicker.h" +#include "llimview.h" #include "lltransientfloatermgr.h" +#include "llviewercontrol.h" // // LLIMFloaterContainer // LLIMFloaterContainer::LLIMFloaterContainer(const LLSD& seed) -: LLMultiFloater(seed) +: LLMultiFloater(seed), + mExpandCollapseBtn(NULL), + mConversationsRoot(NULL) { + // Firstly add our self to IMSession observers, so we catch session events + LLIMMgr::getInstance()->addSessionObserver(this); + mAutoResize = FALSE; LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this); } @@ -49,6 +63,26 @@ LLIMFloaterContainer::~LLIMFloaterContainer() { mNewMessageConnection.disconnect(); LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this); + + gSavedPerAccountSettings.setBOOL("ConversationsListPaneCollapsed", mConversationsPane->isCollapsed()); + gSavedPerAccountSettings.setBOOL("ConversationsMessagePaneCollapsed", mMessagesPane->isCollapsed()); + + if (!LLSingleton<LLIMMgr>::destroyed()) + { + LLIMMgr::getInstance()->removeSessionObserver(this); + } +} + +void LLIMFloaterContainer::sessionVoiceOrIMStarted(const LLUUID& session_id) +{ + LLIMFloater::show(session_id); +} + +void LLIMFloaterContainer::sessionRemoved(const LLUUID& session_id) +{ + LLIMFloater* floaterp = LLIMFloater::findInstance(session_id); + LLFloater::onClickClose(floaterp); + removeConversationListItem(floaterp); } BOOL LLIMFloaterContainer::postBuild() @@ -56,24 +90,47 @@ BOOL LLIMFloaterContainer::postBuild() mNewMessageConnection = LLIMModel::instance().mNewMsgSignal.connect(boost::bind(&LLIMFloaterContainer::onNewMessageReceived, this, _1)); // Do not call base postBuild to not connect to mCloseSignal to not close all floaters via Close button // mTabContainer will be initialized in LLMultiFloater::addChild() + + setTabContainer(getChild<LLTabContainer>("im_box_tab_container")); + + mConversationsStack = getChild<LLLayoutStack>("conversations_stack"); + mConversationsPane = getChild<LLLayoutPanel>("conversations_layout_panel"); + mMessagesPane = getChild<LLLayoutPanel>("messages_layout_panel"); + + mConversationsListPanel = getChild<LLPanel>("conversations_list_panel"); + + // CHUI-98 : View Model for conversations + LLConversationItem* base_item = new LLConversationItem(this); + LLFolderView::Params p; + p.view_model = &mConversationViewModel; + p.parent_panel = mConversationsListPanel; + p.rect = mConversationsListPanel->getLocalRect(); + p.follows.flags = FOLLOWS_ALL; + p.listener = base_item; + p.root = NULL; + + mConversationsRoot = LLUICtrlFactory::create<LLFolderView>(p); + mConversationsListPanel->addChild(mConversationsRoot); + + mExpandCollapseBtn = getChild<LLButton>("expand_collapse_btn"); + mExpandCollapseBtn->setClickedCallback(boost::bind(&LLIMFloaterContainer::onExpandCollapseButtonClicked, this)); + + childSetAction("add_btn", boost::bind(&LLIMFloaterContainer::onAddButtonClicked, this)); + + collapseMessagesPane(gSavedPerAccountSettings.getBOOL("ConversationsMessagePaneCollapsed")); + collapseConversationsPane(gSavedPerAccountSettings.getBOOL("ConversationsListPaneCollapsed")); + LLAvatarNameCache::addUseDisplayNamesCallback( + boost::bind(&LLIMConversation::processChatHistoryStyleUpdate)); + return TRUE; } void LLIMFloaterContainer::onOpen(const LLSD& key) { LLMultiFloater::onOpen(key); -/* - if (key.isDefined()) - { - LLIMFloater* im_floater = LLIMFloater::findInstance(key.asUUID()); - if (im_floater) - { - im_floater->openFloater(); - } - } -*/ } +// virtual void LLIMFloaterContainer::addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point) @@ -86,11 +143,15 @@ void LLIMFloaterContainer::addFloater(LLFloater* floaterp, openFloater(floaterp->getKey()); return; } + + // Make sure the message panel is open when adding a floater or it stays mysteriously hidden + collapseMessagesPane(false); + // Add the floater LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point); LLUUID session_id = floaterp->getKey(); - + LLIconCtrl* icon = 0; if(gAgent.isInGroup(session_id, TRUE)) @@ -113,15 +174,52 @@ void LLIMFloaterContainer::addFloater(LLFloater* floaterp, mSessions[session_id] = floaterp; floaterp->mCloseSignal.connect(boost::bind(&LLIMFloaterContainer::onCloseFloater, this, session_id)); } + + // forced resize of the floater + LLRect wrapper_rect = this->mTabContainer->getLocalRect(); + floaterp->setRect(wrapper_rect); + mTabContainer->setTabImage(floaterp, icon); } + void LLIMFloaterContainer::onCloseFloater(LLUUID& id) { mSessions.erase(id); setFocus(TRUE); } +// virtual +void LLIMFloaterContainer::computeResizeLimits(S32& new_min_width, S32& new_min_height) +{ + bool is_left_pane_expanded = !mConversationsPane->isCollapsed(); + bool is_right_pane_expanded = !mMessagesPane->isCollapsed(); + + S32 conversations_pane_min_dim = mConversationsPane->getMinDim(); + + if (is_right_pane_expanded) + { + S32 conversations_pane_width = + (is_left_pane_expanded ? gSavedPerAccountSettings.getS32("ConversationsListPaneWidth") : conversations_pane_min_dim); + + // possibly increase minimum size constraint due to children's minimums. + for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) + { + LLFloater* floaterp = dynamic_cast<LLFloater*>(mTabContainer->getPanelByIndex(tab_idx)); + if (floaterp) + { + new_min_width = llmax(new_min_width, + floaterp->getMinWidth() + conversations_pane_width + LLPANEL_BORDER_WIDTH * 2); + new_min_height = llmax(new_min_height, floaterp->getMinHeight()); + } + } + } + else + { + new_min_width = conversations_pane_min_dim; + } +} + void LLIMFloaterContainer::onNewMessageReceived(const LLSD& data) { LLUUID session_id = data["session_id"].asUUID(); @@ -136,6 +234,21 @@ void LLIMFloaterContainer::onNewMessageReceived(const LLSD& data) } } +void LLIMFloaterContainer::onExpandCollapseButtonClicked() +{ + if (mConversationsPane->isCollapsed() && mMessagesPane->isCollapsed() + && gSavedPerAccountSettings.getBOOL("ConversationsExpandMessagePaneFirst")) + { + // Expand the messages pane from ultra minimized state + // if it was collapsed last in order. + collapseMessagesPane(false); + } + else + { + collapseConversationsPane(!mConversationsPane->isCollapsed()); + } +} + LLIMFloaterContainer* LLIMFloaterContainer::findInstance() { return LLFloaterReg::findTypedInstance<LLIMFloaterContainer>("im_container"); @@ -151,8 +264,6 @@ void LLIMFloaterContainer::setMinimized(BOOL b) if (isMinimized() == b) return; LLMultiFloater::setMinimized(b); - // Hide minimized floater (see EXT-5315) - setVisible(!b); if (isMinimized()) return; @@ -162,4 +273,267 @@ void LLIMFloaterContainer::setMinimized(BOOL b) } } +void LLIMFloaterContainer::draw() +{ + if (mTabContainer->getTabCount() == 0) + { + // Do not close the container when every conversation is torn off because the user + // still needs the conversation list. Simply collapse the message pane in that case. + collapseMessagesPane(true); + } + LLFloater::draw(); + + repositioningWidgets(); +} + +void LLIMFloaterContainer::tabClose() +{ + if (mTabContainer->getTabCount() == 0) + { + // Do not close the container when every conversation is torn off because the user + // still needs the conversation list. Simply collapse the message pane in that case. + collapseMessagesPane(true); + } +} + +void LLIMFloaterContainer::setVisible(BOOL visible) +{ + if (visible) + { + // Make sure we have the Nearby Chat present when showing the conversation container + LLFloater* nearby_chat = LLFloaterReg::findInstance("chat_bar"); + if (nearby_chat == NULL) + { + // If not found, force the creation of the nearby chat conversation panel + // *TODO: find a way to move this to XML as a default panel or something like that + LLSD name("chat_bar"); + LLFloaterReg::toggleInstanceOrBringToFront(name); + } + } + + // We need to show/hide all the associated conversations that have been torn off + // (and therefore, are not longer managed by the multifloater), + // so that they show/hide with the conversations manager. + conversations_items_map::iterator item_it = mConversationsItems.begin(); + for (;item_it != mConversationsItems.end(); ++item_it) + { + LLConversationItem* item = item_it->second; + item->setVisibleIfDetached(visible); + } + + // Now, do the normal multifloater show/hide + LLMultiFloater::setVisible(visible); + +} + +void LLIMFloaterContainer::collapseMessagesPane(bool collapse) +{ + if (mMessagesPane->isCollapsed() == collapse) + { + return; + } + + if (collapse) + { + // Save the messages pane width before collapsing it. + gSavedPerAccountSettings.setS32("ConversationsMessagePaneWidth", mMessagesPane->getRect().getWidth()); + + // Save the order in which the panels are closed to reverse user's last action. + gSavedPerAccountSettings.setBOOL("ConversationsExpandMessagePaneFirst", mConversationsPane->isCollapsed()); + } + + // Show/hide the messages pane. + mConversationsStack->collapsePanel(mMessagesPane, collapse); + + updateState(collapse, gSavedPerAccountSettings.getS32("ConversationsMessagePaneWidth")); +} + +void LLIMFloaterContainer::collapseConversationsPane(bool collapse) +{ + if (mConversationsPane->isCollapsed() == collapse) + { + return; + } + + LLView* button_panel = getChild<LLView>("conversations_pane_buttons_expanded"); + button_panel->setVisible(!collapse); + mExpandCollapseBtn->setImageOverlay(getString(collapse ? "expand_icon" : "collapse_icon")); + + if (collapse) + { + // Save the conversations pane width before collapsing it. + gSavedPerAccountSettings.setS32("ConversationsListPaneWidth", mConversationsPane->getRect().getWidth()); + + // Save the order in which the panels are closed to reverse user's last action. + gSavedPerAccountSettings.setBOOL("ConversationsExpandMessagePaneFirst", !mMessagesPane->isCollapsed()); + } + + mConversationsStack->collapsePanel(mConversationsPane, collapse); + + S32 collapsed_width = mConversationsPane->getMinDim(); + updateState(collapse, gSavedPerAccountSettings.getS32("ConversationsListPaneWidth") - collapsed_width); +} + +void LLIMFloaterContainer::updateState(bool collapse, S32 delta_width) +{ + LLRect floater_rect = getRect(); + floater_rect.mRight += ((collapse ? -1 : 1) * delta_width); + + // Set by_user = true so that reshaped rect is saved in user_settings. + setShape(floater_rect, true); + + updateResizeLimits(); + + bool is_left_pane_expanded = !mConversationsPane->isCollapsed(); + bool is_right_pane_expanded = !mMessagesPane->isCollapsed(); + + setCanResize(is_left_pane_expanded || is_right_pane_expanded); + setCanMinimize(is_left_pane_expanded || is_right_pane_expanded); +} + +void LLIMFloaterContainer::onAddButtonClicked() +{ + LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLIMFloaterContainer::onAvatarPicked, this, _1), TRUE, TRUE); + LLFloater* root_floater = gFloaterView->getParentFloater(this); + if (picker && root_floater) + { + root_floater->addDependentFloater(picker); + } +} + +void LLIMFloaterContainer::onAvatarPicked(const uuid_vec_t& ids) +{ + if (ids.size() == 1) + { + LLAvatarActions::startIM(ids.back()); + } + else + { + LLAvatarActions::startConference(ids); + } +} + +void LLIMFloaterContainer::repositioningWidgets() +{ + LLRect panel_rect = mConversationsListPanel->getRect(); + S32 item_height = 16; + int index = 0; + for (conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin(); + widget_it != mConversationsWidgets.end(); + widget_it++, ++index) + { + LLFolderViewItem* widget = widget_it->second; + widget->setVisible(TRUE); + widget->setRect(LLRect(0, + panel_rect.getHeight() - item_height*index, + panel_rect.getWidth(), + panel_rect.getHeight() - item_height*(index+1))); + } +} + +// CHUI-137 : Temporary implementation of conversations list +void LLIMFloaterContainer::addConversationListItem(std::string name, const LLUUID& uuid, LLFloater* floaterp) +{ + // Check if the item is not already in the list, exit if it is and has the same name and uuid (nothing to do) + // Note: this happens often, when reattaching a torn off conversation for instance + conversations_items_map::iterator item_it = mConversationsItems.find(floaterp); + if (item_it != mConversationsItems.end()) + { + LLConversationItem* item = item_it->second; + // Check if the item has changed + if (item->hasSameValues(name,uuid)) + { + // If it hasn't changed, nothing to do -> exit + return; + } + } + + // Remove the conversation item that might exist already: it'll be recreated anew further down anyway + // and nothing wrong will happen removing it if it doesn't exist + removeConversationListItem(floaterp,false); + + // Create a conversation item + LLConversationItem* item = new LLConversationItem(name, uuid, floaterp, this); + mConversationsItems[floaterp] = item; + + // Create a widget from it + LLFolderViewItem* widget = createConversationItemWidget(item); + mConversationsWidgets[floaterp] = widget; + + // Add a new conversation widget to the root folder of a folder view. + widget->addToFolder(mConversationsRoot); + + // Add it to the UI + widget->setVisible(TRUE); + + repositioningWidgets(); + + mConversationsListPanel->addChild(widget); + + return; +} + +void LLIMFloaterContainer::removeConversationListItem(LLFloater* floaterp, bool change_focus) +{ + // Delete the widget and the associated conversation item + // Note : since the mConversationsItems is also the listener to the widget, deleting + // the widget will also delete its listener + conversations_widgets_map::iterator widget_it = mConversationsWidgets.find(floaterp); + if (widget_it != mConversationsWidgets.end()) + { + LLFolderViewItem* widget = widget_it->second; + widget->destroyView(); + } + + // Suppress the conversation items and widgets from their respective maps + mConversationsItems.erase(floaterp); + mConversationsWidgets.erase(floaterp); + + repositioningWidgets(); + + // Don't let the focus fall IW, select and refocus on the first conversation in the list + if (change_focus) + { + setFocus(TRUE); + conversations_items_map::iterator item_it = mConversationsItems.begin(); + if (item_it != mConversationsItems.end()) + { + LLConversationItem* item = item_it->second; + item->selectItem(); + } + } + return; +} + +LLFloater* LLIMFloaterContainer::findConversationItem(LLUUID& uuid) +{ + LLFloater* floaterp = NULL; + for (conversations_items_map::iterator item_it = mConversationsItems.begin(); item_it != mConversationsItems.end(); ++item_it) + { + LLConversationItem* item = item_it->second; + if (item->hasSameValue(uuid)) + { + floaterp = item_it->first; + break; + } + } + return floaterp; +} + +LLFolderViewItem* LLIMFloaterContainer::createConversationItemWidget(LLConversationItem* item) +{ + LLFolderViewItem::Params params; + + params.name = item->getDisplayName(); + //params.icon = bridge->getIcon(); + //params.icon_open = bridge->getOpenIcon(); + //params.creation_date = bridge->getCreationDate(); + params.root = mConversationsRoot; + params.listener = item; + params.rect = LLRect (0, 0, 0, 0); + params.tool_tip = params.name; + + return LLUICtrlFactory::create<LLFolderViewItem>(params); +} + // EOF diff --git a/indra/newview/llimfloatercontainer.h b/indra/newview/llimfloatercontainer.h index 892ecef48d..161c6d9806 100644 --- a/indra/newview/llimfloatercontainer.h +++ b/indra/newview/llimfloatercontainer.h @@ -30,14 +30,22 @@ #include <map> #include <vector> +#include "llimview.h" #include "llfloater.h" #include "llmultifloater.h" #include "llavatarpropertiesprocessor.h" #include "llgroupmgr.h" +#include "llconversationmodel.h" +class LLButton; +class LLLayoutPanel; +class LLLayoutStack; class LLTabContainer; +class LLIMFloaterContainer; -class LLIMFloaterContainer : public LLMultiFloater +class LLIMFloaterContainer + : public LLMultiFloater + , public LLIMSessionObserver { public: LLIMFloaterContainer(const LLSD& seed); @@ -45,12 +53,16 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); + /*virtual*/ void draw(); + /*virtual*/ void setVisible(BOOL visible); void onCloseFloater(LLUUID& id); /*virtual*/ void addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END); + /*virtual*/ void tabClose(); + static LLFloater* getCurrentVoiceFloater(); static LLIMFloaterContainer* findInstance(); @@ -59,12 +71,54 @@ public: virtual void setMinimized(BOOL b); + void collapseMessagesPane(bool collapse); + + + // LLIMSessionObserver observe triggers + /*virtual*/ void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) {}; + /*virtual*/ void sessionVoiceOrIMStarted(const LLUUID& session_id); + /*virtual*/ void sessionRemoved(const LLUUID& session_id); + /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) {}; + LLConversationViewModel& getRootViewModel() { return mConversationViewModel; } + private: typedef std::map<LLUUID,LLFloater*> avatarID_panel_map_t; avatarID_panel_map_t mSessions; boost::signals2::connection mNewMessageConnection; + /*virtual*/ void computeResizeLimits(S32& new_min_width, S32& new_min_height); + void onNewMessageReceived(const LLSD& data); + + void onExpandCollapseButtonClicked(); + + void collapseConversationsPane(bool collapse); + + void updateState(bool collapse, S32 delta_width); + void repositioningWidgets(); + + void onAddButtonClicked(); + void onAvatarPicked(const uuid_vec_t& ids); + + LLButton* mExpandCollapseBtn; + LLLayoutPanel* mMessagesPane; + LLLayoutPanel* mConversationsPane; + LLLayoutStack* mConversationsStack; + + // Conversation list implementation +public: + void removeConversationListItem(LLFloater* floaterp, bool change_focus = true); + void addConversationListItem(std::string name, const LLUUID& uuid, LLFloater* floaterp); + LLFloater* findConversationItem(LLUUID& uuid); +private: + LLFolderViewItem* createConversationItemWidget(LLConversationItem* item); + + // Conversation list data + LLPanel* mConversationsListPanel; // This is the main widget we add conversation widget to + conversations_items_map mConversationsItems; + conversations_widgets_map mConversationsWidgets; + LLConversationViewModel mConversationViewModel; + LLFolderView* mConversationsRoot; }; #endif // LL_LLIMFLOATERCONTAINER_H diff --git a/indra/newview/llimhandler.cpp b/indra/newview/llimhandler.cpp index 07d73c8c66..047472a282 100644 --- a/indra/newview/llimhandler.cpp +++ b/indra/newview/llimhandler.cpp @@ -37,10 +37,9 @@ using namespace LLNotificationsUI; //-------------------------------------------------------------------------- -LLIMHandler::LLIMHandler(e_notification_type type, const LLSD& id) +LLIMHandler::LLIMHandler() +: LLSysHandler("IM Notifications", "notifytoast") { - mType = type; - // Getting a Channel for our notifications mChannel = LLChannelManager::getInstance()->createNotificationChannel()->getHandle(); } @@ -59,72 +58,49 @@ void LLIMHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLIMHandler::processNotification(const LLSD& notify) +bool LLIMHandler::processNotification(const LLNotificationPtr& notification) { if(mChannel.isDead()) { return false; } - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if(!notification) - return false; - // arrange a channel on a screen if(!mChannel.get()->getVisible()) { initChannel(); } - if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") - { - LLSD substitutions = notification->getSubstitutions(); - - // According to comments in LLIMMgr::addMessage(), if we get message - // from ourselves, the sender id is set to null. This fixes EXT-875. - LLUUID avatar_id = substitutions["FROM_ID"].asUUID(); - if (avatar_id.isNull()) - avatar_id = gAgentID; - - LLToastIMPanel::Params im_p; - im_p.notification = notification; - im_p.avatar_id = avatar_id; - im_p.from = substitutions["FROM"].asString(); - im_p.time = substitutions["TIME"].asString(); - im_p.message = substitutions["MESSAGE"].asString(); - im_p.session_id = substitutions["SESSION_ID"].asUUID(); - - LLToastIMPanel* im_box = new LLToastIMPanel(im_p); - - LLToast::Params p; - 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_delete_toast = boost::bind(&LLIMHandler::onDeleteToast, this, _1); - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->addToast(p); - - // send a signal to the counter manager; - mNewNotificationSignal(); - } - else if (notify["sigtype"].asString() == "delete") - { - mChannel.get()->killToastByNotificationID(notification->getID()); - } - return false; -} + LLSD substitutions = notification->getSubstitutions(); + + // According to comments in LLIMMgr::addMessage(), if we get message + // from ourselves, the sender id is set to null. This fixes EXT-875. + LLUUID avatar_id = substitutions["FROM_ID"].asUUID(); + if (avatar_id.isNull()) + avatar_id = gAgentID; + + LLToastIMPanel::Params im_p; + im_p.notification = notification; + im_p.avatar_id = avatar_id; + im_p.from = substitutions["FROM"].asString(); + im_p.time = substitutions["TIME"].asString(); + im_p.message = substitutions["MESSAGE"].asString(); + im_p.session_id = substitutions["SESSION_ID"].asUUID(); + + LLToastIMPanel* im_box = new LLToastIMPanel(im_p); + + LLToast::Params p; + p.notif_id = notification->getID(); + p.session_id = im_p.session_id; + p.notification = notification; + p.panel = im_box; + p.can_be_stored = false; + LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); + if(channel) + channel->addToast(p); -//-------------------------------------------------------------------------- -void LLIMHandler::onDeleteToast(LLToast* toast) -{ - // send a signal to the counter manager - mDelNotificationSignal(); + return false; } -//-------------------------------------------------------------------------- diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 4000570872..d88a558125 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -41,7 +41,7 @@ #include "lltextutil.h" #include "lltrans.h" #include "lluictrlfactory.h" - +#include "llimconversation.h" #include "llagent.h" #include "llagentui.h" #include "llappviewer.h" @@ -175,10 +175,11 @@ LLIMModel::LLIMModel() addNewMsgCallback(boost::bind(&toast_callback, _1)); } -LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice) +LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg) : mSessionID(session_id), mName(name), mType(type), + mHasOfflineMessage(has_offline_msg), mParticipantUnreadMessageCount(0), mNumUnread(0), mOtherParticipantID(other_participant_id), @@ -375,6 +376,8 @@ void LLIMModel::LLIMSession::onVoiceChannelStateChanged(const LLVoiceChannel::ES break; } } + default: + break; } // Update speakers list when connected if (LLVoiceChannel::STATE_CONNECTED == new_state) @@ -676,7 +679,7 @@ void LLIMModel::testMessages() //session name should not be empty bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, - const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice) + const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg) { if (name.empty()) { @@ -690,7 +693,7 @@ bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, co return false; } - LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids, voice); + LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids, voice, has_offline_msg); mId2SessionMap[session_id] = session; // When notifying observer, name of session is used instead of "name", because they may not be the @@ -702,10 +705,10 @@ bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, co } -bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, bool voice) +bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, bool voice, bool has_offline_msg) { uuid_vec_t no_ids; - return newSession(session_id, name, type, other_participant_id, no_ids, voice); + return newSession(session_id, name, type, other_participant_id, no_ids, voice, has_offline_msg); } bool LLIMModel::clearSession(const LLUUID& session_id) @@ -716,6 +719,16 @@ bool LLIMModel::clearSession(const LLUUID& session_id) return true; } +void LLIMModel::getMessages(const LLUUID& session_id, std::list<LLSD>& messages, int start_index, const bool sendNoUnreadMsgs) +{ + getMessagesSilently(session_id, messages, start_index); + + if (sendNoUnreadMsgs) + { + sendNoUnreadMessages(session_id); + } +} + void LLIMModel::getMessagesSilently(const LLUUID& session_id, std::list<LLSD>& messages, int start_index) { LLIMSession* session = findIMSession(session_id); @@ -757,13 +770,6 @@ void LLIMModel::sendNoUnreadMessages(const LLUUID& session_id) mNoUnreadMsgsSignal(arg); } -void LLIMModel::getMessages(const LLUUID& session_id, std::list<LLSD>& messages, int start_index) -{ - getMessagesSilently(session_id, messages, start_index); - - sendNoUnreadMessages(session_id); -} - bool LLIMModel::addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text) { LLIMSession* session = findIMSession(session_id); @@ -904,7 +910,7 @@ const LLUUID& LLIMModel::getOtherParticipantID(const LLUUID& session_id) const LLIMSession* session = findIMSession(session_id); if (!session) { - llwarns << "session " << session_id << "does not exist " << llendl; + llwarns << "session " << session_id << " does not exist " << llendl; return LLUUID::null; } @@ -2395,6 +2401,7 @@ void LLIMMgr::addMessage( const LLUUID& target_id, const std::string& from, const std::string& msg, + bool is_offline_msg, const std::string& session_name, EInstantMessage dialog, U32 parent_estate_id, @@ -2420,7 +2427,7 @@ void LLIMMgr::addMessage( bool new_session = !hasSession(new_session_id); if (new_session) { - LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id); + LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id, false, is_offline_msg); // When we get a new IM, and if you are a god, display a bit // of information about the source. This is to help liaisons @@ -2480,8 +2487,7 @@ void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& mess LLChat chat(message); chat.mSourceType = CHAT_SOURCE_SYSTEM; - LLFloater* chat_bar = LLFloaterReg::getInstance("chat_bar"); - LLNearbyChat* nearby_chat = chat_bar->findChild<LLNearbyChat>("nearby_chat"); + LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); if(nearby_chat) { @@ -2497,6 +2503,7 @@ void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& mess gIMMgr->addMessage(session_id, LLUUID::null, SYSTEM_FROM, message.getString()); } // log message to file + else { std::string session_name; @@ -2579,7 +2586,8 @@ LLUUID LLIMMgr::addSession( { LLDynamicArray<LLUUID> ids; ids.put(other_participant_id); - return addSession(name, dialog, other_participant_id, ids, voice); + LLUUID session_id = addSession(name, dialog, other_participant_id, ids, voice); + return session_id; } // Adds a session using the given session_id. If the session already exists @@ -2588,7 +2596,8 @@ LLUUID LLIMMgr::addSession( const std::string& name, EInstantMessage dialog, const LLUUID& other_participant_id, - const LLDynamicArray<LLUUID>& ids, bool voice) + const LLDynamicArray<LLUUID>& ids, bool voice, + const LLUUID& floater_id) { if (0 == ids.getLength()) { @@ -2603,6 +2612,19 @@ LLUUID LLIMMgr::addSession( LLUUID session_id = computeSessionID(dialog,other_participant_id); + if (floater_id.notNull()) + { + LLIMFloater* im_floater = LLIMFloater::findInstance(floater_id); + + if (im_floater && im_floater->getStartConferenceInSameFloater()) + { + // The IM floater should be initialized with a new session_id + // so that it is found by that id when creating a chiclet in LLIMFloater::onIMChicletCreated, + // and a new floater is not created. + im_floater->initIMSession(session_id); + } + } + bool new_session = !LLIMModel::getInstance()->findIMSession(session_id); //works only for outgoing ad-hoc sessions @@ -2634,6 +2656,8 @@ LLUUID LLIMMgr::addSession( noteMutedUsers(session_id, ids); } + notifyObserverSessionVoiceOrIMStarted(session_id); + return session_id; } @@ -2920,6 +2944,14 @@ void LLIMMgr::notifyObserverSessionAdded(const LLUUID& session_id, const std::st } } +void LLIMMgr::notifyObserverSessionVoiceOrIMStarted(const LLUUID& session_id) +{ + for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++) + { + (*it)->sessionVoiceOrIMStarted(session_id); + } +} + void LLIMMgr::notifyObserverSessionRemoved(const LLUUID& session_id) { for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++) @@ -3279,6 +3311,7 @@ public: from_id, name, buffer, + IM_OFFLINE == offline, std::string((char*)&bin_bucket[0]), IM_SESSION_INVITE, message_params["parent_estate_id"].asInteger(), diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 7c2cd03d97..fa9d20ca53 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -70,10 +70,11 @@ public: GROUP_SESSION, ADHOC_SESSION, AVALINE_SESSION, + NONE_SESSION, } SType; LLIMSession(const LLUUID& session_id, const std::string& name, - const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice); + const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg); virtual ~LLIMSession(); void sessionInitReplyReceived(const LLUUID& new_session_id); @@ -133,6 +134,8 @@ public: //if IM session is created for a voice call bool mStartedAsIMCall; + bool mHasOfflineMessage; + private: void onAdHocNameCache(const LLAvatarName& av_name); @@ -181,10 +184,10 @@ public: * @param name session name should not be empty, will return false if empty */ bool newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, - const uuid_vec_t& ids, bool voice = false); + const uuid_vec_t& ids, bool voice = false, bool has_offline_msg = false); bool newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, - const LLUUID& other_participant_id, bool voice = false); + const LLUUID& other_participant_id, bool voice = false, bool has_offline_msg = false); /** * Remove all session data associated with a session specified by session_id @@ -192,12 +195,6 @@ public: bool clearSession(const LLUUID& session_id); /** - * Populate supplied std::list with messages starting from index specified by start_index without - * emitting no unread messages signal. - */ - void getMessagesSilently(const LLUUID& session_id, std::list<LLSD>& messages, int start_index = 0); - - /** * Sends no unread messages signal. */ void sendNoUnreadMessages(const LLUUID& session_id); @@ -205,7 +202,7 @@ public: /** * Populate supplied std::list with messages starting from index specified by start_index */ - void getMessages(const LLUUID& session_id, std::list<LLSD>& messages, int start_index = 0); + void getMessages(const LLUUID& session_id, std::list<LLSD>& messages, int start_index = 0, const bool sendNoUnreadMsgs = true); /** * Add a message to an IM Model - the message is saved in a message store associated with a session specified by session_id @@ -288,6 +285,12 @@ public: private: /** + * Populate supplied std::list with messages starting from index specified by start_index without + * emitting no unread messages signal. + */ + void getMessagesSilently(const LLUUID& session_id, std::list<LLSD>& messages, int start_index = 0); + + /** * Add message to a list of message associated with session specified by session_id */ bool addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text); @@ -298,6 +301,7 @@ class LLIMSessionObserver public: virtual ~LLIMSessionObserver() {} virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) = 0; + virtual void sessionVoiceOrIMStarted(const LLUUID& session_id) = 0; virtual void sessionRemoved(const LLUUID& session_id) = 0; virtual void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) = 0; }; @@ -324,6 +328,7 @@ public: const LLUUID& target_id, const std::string& from, const std::string& msg, + bool is_offline_msg = false, const std::string& session_name = LLStringUtil::null, EInstantMessage dialog = IM_NOTHING_SPECIAL, U32 parent_estate_id = 0, @@ -347,10 +352,12 @@ public: // Adds a session using a specific group of starting agents // the dialog type is assumed correct. Returns the uuid of the session. + // A session can be added to a floater specified by floater_id. LLUUID addSession(const std::string& name, EInstantMessage dialog, const LLUUID& other_participant_id, - const LLDynamicArray<LLUUID>& ids, bool voice = false); + const LLDynamicArray<LLUUID>& ids, bool voice = false, + const LLUUID& floater_id = LLUUID::null); /** * Creates a P2P session with the requisite handle for responding to voice calls. @@ -460,6 +467,7 @@ private: static void onInviteNameLookup(LLSD payload, const LLUUID& id, const std::string& name, bool is_group); void notifyObserverSessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + void notifyObserverSessionVoiceOrIMStarted(const LLUUID& session_id); void notifyObserverSessionRemoved(const LLUUID& session_id); void notifyObserverSessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp index 17d0b0ffbb..8a15cd279f 100644 --- a/indra/newview/llinspectavatar.cpp +++ b/indra/newview/llinspectavatar.cpp @@ -28,38 +28,21 @@ #include "llinspectavatar.h" // viewer files -#include "llagent.h" -#include "llagentdata.h" -#include "llavataractions.h" +#include "llavatariconctrl.h" #include "llavatarnamecache.h" #include "llavatarpropertiesprocessor.h" -#include "llcallingcard.h" #include "lldateutil.h" -#include "llfloaterreporter.h" -#include "llfloaterworldmap.h" -#include "llimview.h" #include "llinspect.h" -#include "llmutelist.h" -#include "llpanelblockedlist.h" +#include "llslurl.h" #include "llstartup.h" -#include "llspeakers.h" -#include "llviewermenu.h" -#include "llvoiceclient.h" -#include "llviewerobjectlist.h" #include "lltransientfloatermgr.h" -#include "llnotificationsutil.h" // Linden libraries #include "llfloater.h" #include "llfloaterreg.h" -#include "llmenubutton.h" #include "lltextbox.h" -#include "lltoggleablemenu.h" #include "lltooltip.h" // positionViewNearMouse() #include "lltrans.h" -#include "lluictrl.h" - -#include "llavatariconctrl.h" class LLFetchAvatarData; @@ -81,22 +64,13 @@ public: LLInspectAvatar(const LLSD& avatar_id); virtual ~LLInspectAvatar(); - /*virtual*/ BOOL postBuild(void); - // Because floater is single instance, need to re-parse data on each spawn // (for example, inspector about same avatar but in different position) /*virtual*/ void onOpen(const LLSD& avatar_id); - // When closing they should close their gear menu - /*virtual*/ void onClose(bool app_quitting); - // Update view based on information from avatar properties processor void processAvatarData(LLAvatarData* data); - // override the inspector mouse leave so timer is only paused if - // gear menu is not open - /* virtual */ void onMouseLeave(S32 x, S32 y, MASK mask); - virtual LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::GLOBAL; } private: @@ -104,47 +78,6 @@ private: // Used on construction and if avatar id changes. void requestUpdate(); - // Set the volume slider to this user's current client-side volume setting, - // hiding/disabling if the user is not nearby. - void updateVolumeSlider(); - - // Shows/hides moderator panel depending on voice state - void updateModeratorPanel(); - - // Moderator ability to enable/disable voice chat for avatar - void toggleSelectedVoice(bool enabled); - - // Button callbacks - void onClickAddFriend(); - void onClickViewProfile(); - void onClickIM(); - void onClickCall(); - void onClickTeleport(); - void onClickInviteToGroup(); - void onClickPay(); - void onClickShare(); - void onToggleMute(); - void onClickReport(); - void onClickFreeze(); - void onClickEject(); - void onClickKick(); - void onClickCSR(); - void onClickZoomIn(); - void onClickFindOnMap(); - bool onVisibleFindOnMap(); - bool onVisibleEject(); - bool onVisibleFreeze(); - bool onVisibleZoomIn(); - void onClickMuteVolume(); - void onVolumeChange(const LLSD& data); - bool enableMute(); - bool enableUnmute(); - bool enableTeleportOffer(); - bool godModeEnabled(); - - // Is used to determine if "Add friend" option should be enabled in gear menu - bool isNotFriend(); - void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); @@ -209,39 +142,8 @@ LLInspectAvatar::LLInspectAvatar(const LLSD& sd) mAvatarName(), mPropertiesRequest(NULL) { - mCommitCallbackRegistrar.add("InspectAvatar.ViewProfile", boost::bind(&LLInspectAvatar::onClickViewProfile, this)); - mCommitCallbackRegistrar.add("InspectAvatar.AddFriend", boost::bind(&LLInspectAvatar::onClickAddFriend, this)); - mCommitCallbackRegistrar.add("InspectAvatar.IM", - boost::bind(&LLInspectAvatar::onClickIM, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Call", boost::bind(&LLInspectAvatar::onClickCall, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Teleport", boost::bind(&LLInspectAvatar::onClickTeleport, this)); - mCommitCallbackRegistrar.add("InspectAvatar.InviteToGroup", boost::bind(&LLInspectAvatar::onClickInviteToGroup, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Pay", boost::bind(&LLInspectAvatar::onClickPay, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Share", boost::bind(&LLInspectAvatar::onClickShare, this)); - mCommitCallbackRegistrar.add("InspectAvatar.ToggleMute", boost::bind(&LLInspectAvatar::onToggleMute, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Freeze", boost::bind(&LLInspectAvatar::onClickFreeze, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Eject", boost::bind(&LLInspectAvatar::onClickEject, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Kick", boost::bind(&LLInspectAvatar::onClickKick, this)); - mCommitCallbackRegistrar.add("InspectAvatar.CSR", boost::bind(&LLInspectAvatar::onClickCSR, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Report", boost::bind(&LLInspectAvatar::onClickReport, this)); - mCommitCallbackRegistrar.add("InspectAvatar.FindOnMap", boost::bind(&LLInspectAvatar::onClickFindOnMap, this)); - mCommitCallbackRegistrar.add("InspectAvatar.ZoomIn", boost::bind(&LLInspectAvatar::onClickZoomIn, this)); - mCommitCallbackRegistrar.add("InspectAvatar.DisableVoice", boost::bind(&LLInspectAvatar::toggleSelectedVoice, this, false)); - mCommitCallbackRegistrar.add("InspectAvatar.EnableVoice", boost::bind(&LLInspectAvatar::toggleSelectedVoice, this, true)); - - mEnableCallbackRegistrar.add("InspectAvatar.EnableGod", boost::bind(&LLInspectAvatar::godModeEnabled, this)); - mEnableCallbackRegistrar.add("InspectAvatar.VisibleFindOnMap", boost::bind(&LLInspectAvatar::onVisibleFindOnMap, this)); - mEnableCallbackRegistrar.add("InspectAvatar.VisibleEject", boost::bind(&LLInspectAvatar::onVisibleEject, this)); - mEnableCallbackRegistrar.add("InspectAvatar.VisibleFreeze", boost::bind(&LLInspectAvatar::onVisibleFreeze, this)); - mEnableCallbackRegistrar.add("InspectAvatar.VisibleZoomIn", boost::bind(&LLInspectAvatar::onVisibleZoomIn, this)); - mEnableCallbackRegistrar.add("InspectAvatar.Gear.Enable", boost::bind(&LLInspectAvatar::isNotFriend, this)); - mEnableCallbackRegistrar.add("InspectAvatar.Gear.EnableCall", boost::bind(&LLAvatarActions::canCall)); - mEnableCallbackRegistrar.add("InspectAvatar.Gear.EnableTeleportOffer", boost::bind(&LLInspectAvatar::enableTeleportOffer, this)); - mEnableCallbackRegistrar.add("InspectAvatar.EnableMute", boost::bind(&LLInspectAvatar::enableMute, this)); - mEnableCallbackRegistrar.add("InspectAvatar.EnableUnmute", boost::bind(&LLInspectAvatar::enableUnmute, this)); - // can't make the properties request until the widgets are constructed - // as it might return immediately, so do it in postBuild. + // as it might return immediately, so do it in onOpen. LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::GLOBAL, this); LLTransientFloater::init(this); @@ -257,25 +159,6 @@ LLInspectAvatar::~LLInspectAvatar() LLTransientFloaterMgr::getInstance()->removeControlView(this); } -/*virtual*/ -BOOL LLInspectAvatar::postBuild(void) -{ - getChild<LLUICtrl>("add_friend_btn")->setCommitCallback( - boost::bind(&LLInspectAvatar::onClickAddFriend, this) ); - - getChild<LLUICtrl>("view_profile_btn")->setCommitCallback( - boost::bind(&LLInspectAvatar::onClickViewProfile, this) ); - - getChild<LLUICtrl>("mute_btn")->setCommitCallback( - boost::bind(&LLInspectAvatar::onClickMuteVolume, this) ); - - getChild<LLUICtrl>("volume_slider")->setCommitCallback( - boost::bind(&LLInspectAvatar::onVolumeChange, this, _2)); - - return TRUE; -} - - // Multiple calls to showInstance("inspect_avatar", foo) will provide different // LLSD for foo, which we will catch here. //virtual @@ -287,11 +170,6 @@ void LLInspectAvatar::onOpen(const LLSD& data) // Extract appropriate avatar id mAvatarID = data["avatar_id"]; - BOOL self = mAvatarID == gAgent.getID(); - - getChild<LLUICtrl>("gear_self_btn")->setVisible(self); - getChild<LLUICtrl>("gear_btn")->setVisible(!self); - // Position the inspector relative to the mouse cursor // Similar to how tooltips are positioned // See LLToolTipMgr::createToolTip @@ -304,20 +182,13 @@ void LLInspectAvatar::onOpen(const LLSD& data) LLUI::positionViewNearMouse(this); } + // Generate link to avatar profile. + getChild<LLUICtrl>("avatar_profile_link")->setTextArg("[LINK]", LLSLURL("agent", mAvatarID, "about").getSLURLString()); + // can't call from constructor as widgets are not built yet requestUpdate(); - - updateVolumeSlider(); - - updateModeratorPanel(); } -// virtual -void LLInspectAvatar::onClose(bool app_quitting) -{ - getChild<LLMenuButton>("gear_btn")->hideMenu(); -} - void LLInspectAvatar::requestUpdate() { // Don't make network requests when spawning from the debug menu at the @@ -344,25 +215,6 @@ void LLInspectAvatar::requestUpdate() delete mPropertiesRequest; mPropertiesRequest = new LLFetchAvatarData(mAvatarID, this); - // You can't re-add someone as a friend if they are already your friend - bool is_friend = LLAvatarTracker::instance().getBuddyInfo(mAvatarID) != NULL; - bool is_self = (mAvatarID == gAgentID); - if (is_self) - { - getChild<LLUICtrl>("add_friend_btn")->setVisible(false); - getChild<LLUICtrl>("im_btn")->setVisible(false); - } - else if (is_friend) - { - getChild<LLUICtrl>("add_friend_btn")->setVisible(false); - getChild<LLUICtrl>("im_btn")->setVisible(true); - } - else - { - getChild<LLUICtrl>("add_friend_btn")->setVisible(true); - getChild<LLUICtrl>("im_btn")->setVisible(false); - } - // Use an avatar_icon even though the image id will come down with the // avatar properties because the avatar_icon code maintains a cache of icons // and this may result in the image being visible sooner. @@ -405,214 +257,6 @@ void LLInspectAvatar::processAvatarData(LLAvatarData* data) mPropertiesRequest = NULL; } -// For the avatar inspector, we only want to unpause the fade timer -// if neither the gear menu or self gear menu are open -void LLInspectAvatar::onMouseLeave(S32 x, S32 y, MASK mask) -{ - LLToggleableMenu* gear_menu = getChild<LLMenuButton>("gear_btn")->getMenu(); - LLToggleableMenu* gear_menu_self = getChild<LLMenuButton>("gear_self_btn")->getMenu(); - if ( gear_menu && gear_menu->getVisible() && - gear_menu_self && gear_menu_self->getVisible() ) - { - return; - } - - if(childHasVisiblePopupMenu()) - { - return; - } - - mOpenTimer.unpause(); -} - -void LLInspectAvatar::updateModeratorPanel() -{ - bool enable_moderator_panel = false; - - if (LLVoiceChannel::getCurrentVoiceChannel() && - mAvatarID != gAgent.getID()) - { - LLUUID session_id = LLVoiceChannel::getCurrentVoiceChannel()->getSessionID(); - - if (session_id != LLUUID::null) - { - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id); - - if (speaker_mgr) - { - LLPointer<LLSpeaker> self_speakerp = speaker_mgr->findSpeaker(gAgent.getID()); - LLPointer<LLSpeaker> selected_speakerp = speaker_mgr->findSpeaker(mAvatarID); - - if(speaker_mgr->isVoiceActive() && selected_speakerp && - selected_speakerp->isInVoiceChannel() && - ((self_speakerp && self_speakerp->mIsModerator) || gAgent.isGodlike())) - { - getChild<LLUICtrl>("enable_voice")->setVisible(selected_speakerp->mModeratorMutedVoice); - getChild<LLUICtrl>("disable_voice")->setVisible(!selected_speakerp->mModeratorMutedVoice); - - enable_moderator_panel = true; - } - } - } - } - - if (enable_moderator_panel) - { - if (!getChild<LLUICtrl>("moderator_panel")->getVisible()) - { - getChild<LLUICtrl>("moderator_panel")->setVisible(true); - // stretch the floater so it can accommodate the moderator panel - reshape(getRect().getWidth(), getRect().getHeight() + getChild<LLUICtrl>("moderator_panel")->getRect().getHeight()); - } - } - else if (getChild<LLUICtrl>("moderator_panel")->getVisible()) - { - getChild<LLUICtrl>("moderator_panel")->setVisible(false); - // shrink the inspector floater back to original size - reshape(getRect().getWidth(), getRect().getHeight() - getChild<LLUICtrl>("moderator_panel")->getRect().getHeight()); - } -} - -void LLInspectAvatar::toggleSelectedVoice(bool enabled) -{ - LLUUID session_id = LLVoiceChannel::getCurrentVoiceChannel()->getSessionID(); - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id); - - if (speaker_mgr) - { - if (!gAgent.getRegion()) - return; - - std::string url = gAgent.getRegion()->getCapability("ChatSessionRequest"); - LLSD data; - data["method"] = "mute update"; - data["session-id"] = session_id; - data["params"] = LLSD::emptyMap(); - data["params"]["agent_id"] = mAvatarID; - data["params"]["mute_info"] = LLSD::emptyMap(); - // ctrl value represents ability to type, so invert - data["params"]["mute_info"]["voice"] = !enabled; - - class MuteVoiceResponder : public LLHTTPClient::Responder - { - public: - MuteVoiceResponder(const LLUUID& session_id) - { - mSessionID = session_id; - } - - virtual void error(U32 status, const std::string& reason) - { - llwarns << status << ": " << reason << llendl; - - if ( gIMMgr ) - { - //403 == you're not a mod - //should be disabled if you're not a moderator - if ( 403 == status ) - { - gIMMgr->showSessionEventError( - "mute", - "not_a_moderator", - mSessionID); - } - else - { - gIMMgr->showSessionEventError( - "mute", - "generic", - mSessionID); - } - } - } - - private: - LLUUID mSessionID; - }; - - LLHTTPClient::post( - url, - data, - new MuteVoiceResponder(speaker_mgr->getSessionID())); - } - - closeFloater(); - -} - -void LLInspectAvatar::updateVolumeSlider() -{ - bool voice_enabled = LLVoiceClient::getInstance()->getVoiceEnabled(mAvatarID); - - // Do not display volume slider and mute button if it - // is ourself or we are not in a voice channel together - if (!voice_enabled || (mAvatarID == gAgent.getID())) - { - getChild<LLUICtrl>("mute_btn")->setVisible(false); - getChild<LLUICtrl>("volume_slider")->setVisible(false); - } - - else - { - getChild<LLUICtrl>("mute_btn")->setVisible(true); - getChild<LLUICtrl>("volume_slider")->setVisible(true); - - // By convention, we only display and toggle voice mutes, not all mutes - bool is_muted = LLMuteList::getInstance()-> - isMuted(mAvatarID, LLMute::flagVoiceChat); - - LLUICtrl* mute_btn = getChild<LLUICtrl>("mute_btn"); - - bool is_linden = LLStringUtil::endsWith(mAvatarName.getLegacyName(), " Linden"); - - mute_btn->setEnabled( !is_linden); - mute_btn->setValue( is_muted ); - - LLUICtrl* volume_slider = getChild<LLUICtrl>("volume_slider"); - volume_slider->setEnabled( !is_muted ); - - F32 volume; - - if (is_muted) - { - // it's clearer to display their volume as zero - volume = 0.f; - } - else - { - // actual volume - volume = LLVoiceClient::getInstance()->getUserVolume(mAvatarID); - } - volume_slider->setValue( (F64)volume ); - } - -} - -void LLInspectAvatar::onClickMuteVolume() -{ - // By convention, we only display and toggle voice mutes, not all mutes - LLMuteList* mute_list = LLMuteList::getInstance(); - bool is_muted = mute_list->isMuted(mAvatarID, LLMute::flagVoiceChat); - - LLMute mute(mAvatarID, mAvatarName.getLegacyName(), LLMute::AGENT); - if (!is_muted) - { - mute_list->add(mute, LLMute::flagVoiceChat); - } - else - { - mute_list->remove(mute, LLMute::flagVoiceChat); - } - - updateVolumeSlider(); -} - -void LLInspectAvatar::onVolumeChange(const LLSD& data) -{ - F32 volume = (F32)data.asReal(); - LLVoiceClient::getInstance()->setUserVolume(mAvatarID, volume); -} - void LLInspectAvatar::onAvatarNameCache( const LLUUID& agent_id, const LLAvatarName& av_name) @@ -640,215 +284,6 @@ void LLInspectAvatar::onAvatarNameCache( } } -void LLInspectAvatar::onClickAddFriend() -{ - LLAvatarActions::requestFriendshipDialog(mAvatarID, mAvatarName.getLegacyName()); - closeFloater(); -} - -void LLInspectAvatar::onClickViewProfile() -{ - LLAvatarActions::showProfile(mAvatarID); - closeFloater(); -} - -bool LLInspectAvatar::isNotFriend() -{ - return !LLAvatarActions::isFriend(mAvatarID); -} - -bool LLInspectAvatar::onVisibleFindOnMap() -{ - return gAgent.isGodlike() || is_agent_mappable(mAvatarID); -} - -bool LLInspectAvatar::onVisibleEject() -{ - return enable_freeze_eject( LLSD(mAvatarID) ); -} - -bool LLInspectAvatar::onVisibleFreeze() -{ - // either user is a god and can do long distance freeze - // or check for target proximity and permissions - return gAgent.isGodlike() || enable_freeze_eject(LLSD(mAvatarID)); -} - -bool LLInspectAvatar::onVisibleZoomIn() -{ - return gObjectList.findObject(mAvatarID); -} - -void LLInspectAvatar::onClickIM() -{ - LLAvatarActions::startIM(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickCall() -{ - LLAvatarActions::startCall(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickTeleport() -{ - LLAvatarActions::offerTeleport(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickInviteToGroup() -{ - LLAvatarActions::inviteToGroup(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickPay() -{ - LLAvatarActions::pay(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickShare() -{ - LLAvatarActions::share(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onToggleMute() -{ - LLMute mute(mAvatarID, mAvatarName.mDisplayName, LLMute::AGENT); - - if (LLMuteList::getInstance()->isMuted(mute.mID, mute.mName)) - { - LLMuteList::getInstance()->remove(mute); - } - else - { - LLMuteList::getInstance()->add(mute); - } - - LLPanelBlockedList::showPanelAndSelect(mute.mID); - closeFloater(); -} - -void LLInspectAvatar::onClickReport() -{ - LLFloaterReporter::showFromAvatar(mAvatarID, mAvatarName.getCompleteName()); - closeFloater(); -} - -bool godlike_freeze(const LLSD& notification, const LLSD& response) -{ - LLUUID avatar_id = notification["payload"]["avatar_id"].asUUID(); - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - - switch (option) - { - case 0: - LLAvatarActions::freeze(avatar_id); - break; - case 1: - LLAvatarActions::unfreeze(avatar_id); - break; - default: - break; - } - - return false; -} - -void LLInspectAvatar::onClickFreeze() -{ - if (gAgent.isGodlike()) - { - // use godlike freeze-at-a-distance, with confirmation - LLNotificationsUtil::add("FreezeAvatar", - LLSD(), - LLSD().with("avatar_id", mAvatarID), - godlike_freeze); - } - else - { - // use default "local" version of freezing that requires avatar to be in range - handle_avatar_freeze( LLSD(mAvatarID) ); - } - closeFloater(); -} - -void LLInspectAvatar::onClickEject() -{ - handle_avatar_eject( LLSD(mAvatarID) ); - closeFloater(); -} - -void LLInspectAvatar::onClickKick() -{ - LLAvatarActions::kick(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickCSR() -{ - std::string name; - gCacheName->getFullName(mAvatarID, name); - LLAvatarActions::csr(mAvatarID, name); - closeFloater(); -} - -void LLInspectAvatar::onClickZoomIn() -{ - handle_zoom_to_object(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickFindOnMap() -{ - gFloaterWorldMap->trackAvatar(mAvatarID, mAvatarName.mDisplayName); - LLFloaterReg::showInstance("world_map"); -} - - -bool LLInspectAvatar::enableMute() -{ - bool is_linden = LLStringUtil::endsWith(mAvatarName.getLegacyName(), " Linden"); - bool is_self = mAvatarID == gAgent.getID(); - - if (!is_linden && !is_self && !LLMuteList::getInstance()->isMuted(mAvatarID, mAvatarName.getLegacyName())) - { - return true; - } - else - { - return false; - } -} - -bool LLInspectAvatar::enableUnmute() -{ - bool is_linden = LLStringUtil::endsWith(mAvatarName.getLegacyName(), " Linden"); - bool is_self = mAvatarID == gAgent.getID(); - - if (!is_linden && !is_self && LLMuteList::getInstance()->isMuted(mAvatarID, mAvatarName.getLegacyName())) - { - return true; - } - else - { - return false; - } -} - -bool LLInspectAvatar::enableTeleportOffer() -{ - return LLAvatarActions::canOfferTeleport(mAvatarID); -} - -bool LLInspectAvatar::godModeEnabled() -{ - return gAgent.isGodlike(); -} - ////////////////////////////////////////////////////////////////////////////// // LLInspectAvatarUtil ////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index b86c453d61..43c4ce1278 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -37,6 +37,7 @@ #include "llappearancemgr.h" #include "llattachmentsmgr.h" #include "llavataractions.h" +#include "llfavoritesbar.h" // management of favorites folder #include "llfloateropenobject.h" #include "llfloaterreg.h" #include "llfloatersidepanelcontainer.h" @@ -115,10 +116,10 @@ void teleport_via_landmark(const LLUUID& asset_id); static BOOL can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit); static bool check_category(LLInventoryModel* model, const LLUUID& cat_id, - LLFolderView* active_folder_view, + LLInventoryPanel* active_panel, LLInventoryFilter* filter); static bool check_item(const LLUUID& item_id, - LLFolderView* active_folder_view, + LLInventoryPanel* active_panel, LLInventoryFilter* filter); // Helper functions @@ -189,7 +190,8 @@ LLInvFVBridge::LLInvFVBridge(LLInventoryPanel* inventory, mUUID(uuid), mRoot(root), mInvType(LLInventoryType::IT_NONE), - mIsLink(FALSE) + mIsLink(FALSE), + LLFolderViewModelItemInventory(inventory->getRootViewModel()) { mInventoryPanel = inventory->getInventoryPanelHandle(); const LLInventoryObject* obj = getInventoryObject(); @@ -208,7 +210,11 @@ const std::string& LLInvFVBridge::getName() const const std::string& LLInvFVBridge::getDisplayName() const { - return getName(); + if(mDisplayName.empty()) + { + buildDisplayName(); + } + return mDisplayName; } // Folders have full perms @@ -227,9 +233,24 @@ LLFolderType::EType LLInvFVBridge::getPreferredType() const // Folders don't have creation dates. time_t LLInvFVBridge::getCreationDate() const { - return 0; + LLInventoryObject* objectp = getInventoryObject(); + if (objectp) + { + return objectp->getCreationDate(); + } + return (time_t)0; +} + +void LLInvFVBridge::setCreationDate(time_t creation_date_utc) +{ + LLInventoryObject* objectp = getInventoryObject(); + if (objectp) + { + objectp->setCreationDate(creation_date_utc); + } } + // Can be destroyed (or moved to trash) BOOL LLInvFVBridge::isItemRemovable() const { @@ -283,7 +304,7 @@ void LLInvFVBridge::showProperties() */ } -void LLInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch) +void LLInvFVBridge::removeBatch(std::vector<LLFolderViewModelItem*>& batch) { // Deactivate gestures when moving them into Trash LLInvFVBridge* bridge; @@ -292,11 +313,11 @@ void LLInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batc LLViewerInventoryCategory* cat = NULL; LLInventoryModel::cat_array_t descendent_categories; LLInventoryModel::item_array_t descendent_items; - S32 count = batch.count(); + S32 count = batch.size(); S32 i,j; for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.get(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); if (item) @@ -309,7 +330,7 @@ void LLInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batc } for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.get(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID()); if (cat) @@ -327,7 +348,7 @@ void LLInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batc removeBatchNoCheck(batch); } -void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener*>& batch) +void LLInvFVBridge::removeBatchNoCheck(std::vector<LLFolderViewModelItem*>& batch) { // this method moves a bunch of items and folders to the trash. As // per design guidelines for the inventory model, the message is @@ -343,14 +364,14 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener* uuid_vec_t move_ids; LLInventoryModel::update_map_t update; bool start_new_message = true; - S32 count = batch.count(); + S32 count = batch.size(); S32 i; // first, hide any 'preview' floaters that correspond to the items // being deleted. for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.get(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); if(item) @@ -363,7 +384,7 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener* for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.get(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); if(item) @@ -404,7 +425,7 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener* for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.get(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID()); if(cat) @@ -876,6 +897,12 @@ LLInventoryModel* LLInvFVBridge::getInventoryModel() const return panel ? panel->getModel() : NULL; } +LLInventoryFilter* LLInvFVBridge::getInventoryFilter() const +{ + LLInventoryPanel* panel = mInventoryPanel.get(); + return panel ? &(panel->getFilter()) : NULL; +} + BOOL LLInvFVBridge::isItemInTrash() const { LLInventoryModel* model = getInventoryModel(); @@ -928,7 +955,7 @@ BOOL LLInvFVBridge::isCOFFolder() const BOOL LLInvFVBridge::isInboxFolder() const { - const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false, false); + const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false); if (inbox_id.isNull()) { @@ -968,7 +995,7 @@ BOOL LLInvFVBridge::isOutboxFolderDirectParent() const const LLUUID LLInvFVBridge::getOutboxFolder() const { - const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false); + const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); return outbox_id; } @@ -1000,6 +1027,7 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags) @@ -1125,7 +1153,7 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, default: llinfos << "Unhandled asset type (llassetstorage.h): " << (S32)asset_type << " (" << LLAssetType::lookup(asset_type) << ")" << llendl; - break; + break; } if (new_listener) @@ -1250,10 +1278,10 @@ bool LLInvFVBridge::canListOnMarketplaceNow() const if (can_list) { - LLFolderViewFolder * object_folderp = mRoot->getFolderByID(object_id); + LLFolderViewFolder * object_folderp = mInventoryPanel.get() ? mInventoryPanel.get()->getFolderByID(object_id) : NULL; if (object_folderp) { - can_list = !object_folderp->isLoading(); + can_list = !static_cast<LLFolderBridge*>(object_folderp->getViewModelItem())->isLoading(); } } @@ -1261,7 +1289,7 @@ bool LLInvFVBridge::canListOnMarketplaceNow() const { // Get outbox id const LLUUID & outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); - LLFolderViewItem * outbox_itemp = mRoot->getItemByID(outbox_id); + LLFolderViewItem * outbox_itemp = mInventoryPanel.get() ? mInventoryPanel.get()->getItemByID(outbox_id) : NULL; if (outbox_itemp) { @@ -1271,7 +1299,7 @@ bool LLInvFVBridge::canListOnMarketplaceNow() const void * cargo_data = (void *) obj; std::string tooltip_msg; - can_list = outbox_itemp->getListener()->dragOrDrop(mask, drop, cargo_type, cargo_data, tooltip_msg); + can_list = outbox_itemp->getViewModelItem()->dragOrDrop(mask, drop, cargo_type, cargo_data, tooltip_msg); } } } @@ -1283,14 +1311,30 @@ bool LLInvFVBridge::canListOnMarketplaceNow() const #endif } +LLToolDragAndDrop::ESource LLInvFVBridge::getDragSource() const +{ + if (gInventory.isObjectDescendentOf(getUUID(), gInventory.getRootFolderID())) + { + return LLToolDragAndDrop::SOURCE_AGENT; + } + else if (gInventory.isObjectDescendentOf(getUUID(), gInventory.getLibraryRootFolderID())) + { + return LLToolDragAndDrop::SOURCE_LIBRARY; + } + + return LLToolDragAndDrop::SOURCE_VIEWER; +} + + // +=================================================+ // | InventoryFVBridgeBuilder | // +=================================================+ -LLInvFVBridge* LLInventoryFVBridgeBuilder::createBridge(LLAssetType::EType asset_type, +LLInvFVBridge* LLInventoryFolderViewModelBuilder::createBridge(LLAssetType::EType asset_type, LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags /* = 0x00 */) const @@ -1299,6 +1343,7 @@ LLInvFVBridge* LLInventoryFVBridgeBuilder::createBridge(LLAssetType::EType asset actual_asset_type, inv_type, inventory, + view_model, root, uuid, flags); @@ -1355,7 +1400,7 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) else if ("cut" == action) { cutToClipboard(); - LLFolderView::removeCutItems(); + gInventory.removeObject(mUUID); return; } else if ("copy" == action) @@ -1368,10 +1413,10 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) LLInventoryItem* itemp = model->getItem(mUUID); if (!itemp) return; - LLFolderViewItem* folder_view_itemp = mRoot->getItemByID(itemp->getParentUUID()); + LLFolderViewItem* folder_view_itemp = mInventoryPanel.get()->getItemByID(itemp->getParentUUID()); if (!folder_view_itemp) return; - folder_view_itemp->getListener()->pasteFromClipboard(); + folder_view_itemp->getViewModelItem()->pasteFromClipboard(); return; } else if ("paste_link" == action) @@ -1380,10 +1425,10 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) LLInventoryItem* itemp = model->getItem(mUUID); if (!itemp) return; - LLFolderViewItem* folder_view_itemp = mRoot->getItemByID(itemp->getParentUUID()); + LLFolderViewItem* folder_view_itemp = mInventoryPanel.get()->getItemByID(itemp->getParentUUID()); if (!folder_view_itemp) return; - folder_view_itemp->getListener()->pasteLinkFromClipboard(); + folder_view_itemp->getViewModelItem()->pasteLinkFromClipboard(); return; } else if (isMarketplaceCopyAction(action)) @@ -1393,7 +1438,7 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) LLInventoryItem* itemp = model->getItem(mUUID); if (!itemp) return; - const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false); + const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); copy_item_to_outbox(itemp, outbox_id, LLUUID::null, LLToolDragAndDrop::getOperationId()); } } @@ -1494,25 +1539,20 @@ PermissionMask LLItemBridge::getPermissionMask() const return perm_mask; } -const std::string& LLItemBridge::getDisplayName() const -{ - if(mDisplayName.empty()) - { - buildDisplayName(getItem(), mDisplayName); - } - return mDisplayName; -} - -void LLItemBridge::buildDisplayName(LLInventoryItem* item, std::string& name) +void LLItemBridge::buildDisplayName() const { - if(item) + if(getItem()) { - name.assign(item->getName()); + mDisplayName.assign(getItem()->getName()); } else { - name.assign(LLStringUtil::null); + mDisplayName.assign(LLStringUtil::null); } + + mSearchableName.assign(mDisplayName); + mSearchableName.append(getLabelSuffix()); + LLStringUtil::toUpper(mSearchableName); } LLFontGL::StyleFlags LLItemBridge::getLabelStyle() const @@ -1629,25 +1669,23 @@ BOOL LLItemBridge::renameItem(const std::string& new_name) { LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); new_item->rename(new_name); - buildDisplayName(new_item, mDisplayName); new_item->updateServer(FALSE); model->updateItem(new_item); model->notifyObservers(); + buildDisplayName(); } // return FALSE because we either notified observers (& therefore // rebuilt) or we didn't update. return FALSE; } - BOOL LLItemBridge::removeItem() { if(!isItemRemovable()) { return FALSE; } - // move it to the trash LLPreview::hide(mUUID, TRUE); @@ -1786,6 +1824,93 @@ void LLFolderBridge::selectItem() LLInventoryModelBackgroundFetch::instance().start(getUUID(), true); } +void LLFolderBridge::buildDisplayName() const +{ + LLFolderType::EType preferred_type = getPreferredType(); + + // *TODO: to be removed when database supports multi language. This is a + // temporary attempt to display the inventory folder in the user locale. + // mantipov: *NOTE: be sure this code is synchronized with LLFriendCardsManager::findChildFolderUUID + // it uses the same way to find localized string + + // HACK: EXT - 6028 ([HARD CODED]? Inventory > Library > "Accessories" folder) + // Translation of Accessories folder in Library inventory folder + bool accessories = false; + if(getName() == "Accessories") + { + //To ensure that Accessories folder is in Library we have to check its parent folder. + //Due to parent LLFolderViewFloder is not set to this item yet we have to check its parent via Inventory Model + LLInventoryCategory* cat = gInventory.getCategory(getUUID()); + if(cat) + { + const LLUUID& parent_folder_id = cat->getParentUUID(); + accessories = (parent_folder_id == gInventory.getLibraryRootFolderID()); + } + } + + //"Accessories" inventory category has folder type FT_NONE. So, this folder + //can not be detected as protected with LLFolderType::lookupIsProtectedType + mDisplayName.assign(getName()); + if (accessories || LLFolderType::lookupIsProtectedType(preferred_type)) + { + LLTrans::findString(mDisplayName, std::string("InvFolder ") + getName(), LLSD()); + } + + mSearchableName.assign(mDisplayName); + mSearchableName.append(getLabelSuffix()); + LLStringUtil::toUpper(mSearchableName); +} + + +void LLFolderBridge::update() +{ + bool possibly_has_children = false; + bool up_to_date = isUpToDate(); + if(!up_to_date && hasChildren()) // we know we have children but haven't fetched them (doesn't obey filter) + { + possibly_has_children = true; + } + + bool loading = (possibly_has_children + && !up_to_date ); + + if (loading != mIsLoading) + { + if ( loading && !mIsLoading ) + { + // Measure how long we've been in the loading state + mTimeSinceRequestStart.reset(); + } + + const BOOL in_inventory = gInventory.isObjectDescendentOf(getUUID(), gInventory.getRootFolderID()); + const BOOL in_library = gInventory.isObjectDescendentOf(getUUID(), gInventory.getLibraryRootFolderID()); + + bool root_is_loading = false; + if (in_inventory) + { + root_is_loading = LLInventoryModelBackgroundFetch::instance().inventoryFetchInProgress(); + } + if (in_library) + { + root_is_loading = LLInventoryModelBackgroundFetch::instance().libraryFetchInProgress(); + } + if ((mIsLoading + && mTimeSinceRequestStart.getElapsedTimeF32() >= gSavedSettings.getF32("FolderLoadingMessageWaitTime")) + || (LLInventoryModelBackgroundFetch::instance().folderFetchActive() + && root_is_loading)) + { + mDisplayName = LLInvFVBridge::getDisplayName() + " ( " + LLTrans::getString("LoadingData") + " ) "; + mIsLoading = true; + } + else + { + mDisplayName = LLInvFVBridge::getDisplayName(); + mIsLoading = false; + } + } +} + + // Iterate through a folder's children to determine if // all the children are removable. class LLIsItemRemovable : public LLFolderViewFunctor @@ -1794,11 +1919,11 @@ public: LLIsItemRemovable() : mPassed(TRUE) {} virtual void doFolder(LLFolderViewFolder* folder) { - mPassed &= folder->getListener()->isItemRemovable(); + mPassed &= folder->getViewModelItem()->isItemRemovable(); } virtual void doItem(LLFolderViewItem* item) { - mPassed &= item->getListener()->isItemRemovable(); + mPassed &= item->getViewModelItem()->isItemRemovable(); } BOOL mPassed; }; @@ -1812,7 +1937,7 @@ BOOL LLFolderBridge::isItemRemovable() const } LLInventoryPanel* panel = mInventoryPanel.get(); - LLFolderViewFolder* folderp = dynamic_cast<LLFolderViewFolder*>(panel ? panel->getRootFolder()->getItemByID(mUUID) : NULL); + LLFolderViewFolder* folderp = dynamic_cast<LLFolderViewFolder*>(panel ? panel->getItemByID(mUUID) : NULL); if (folderp) { LLIsItemRemovable folder_test; @@ -2051,7 +2176,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, LLInventoryPanel* destination_panel = mInventoryPanel.get(); if (!destination_panel) return false; - LLInventoryFilter* filter = destination_panel->getFilter(); + LLInventoryFilter* filter = getInventoryFilter(); if (!filter) return false; const LLUUID &cat_id = inv_cat->getUUID(); @@ -2270,7 +2395,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, { // Check whether the folder being dragged from active inventory panel // passes the filter of the destination panel. - is_movable = check_category(model, cat_id, active_folder_view, filter); + is_movable = check_category(model, cat_id, active_panel, filter); } } } @@ -2344,7 +2469,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } else { - if (model->isObjectDescendentOf(cat_id, model->findCategoryUUIDForType(LLFolderType::FT_INBOX, false, false))) + if (model->isObjectDescendentOf(cat_id, model->findCategoryUUIDForType(LLFolderType::FT_INBOX, false))) { set_dad_inbox_object(cat_id); } @@ -2696,7 +2821,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) { if ("open" == action) { - LLFolderViewFolder *f = dynamic_cast<LLFolderViewFolder *>(mRoot->getItemByID(mUUID)); + LLFolderViewFolder *f = dynamic_cast<LLFolderViewFolder *>(mInventoryPanel.get()->getItemByID(mUUID)); if (f) { f->setOpen(TRUE); @@ -2743,7 +2868,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) else if ("cut" == action) { cutToClipboard(); - LLFolderView::removeCutItems(); + gInventory.removeObject(mUUID); return; } else if ("copy" == action) @@ -2784,7 +2909,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) LLInventoryCategory * cat = gInventory.getCategory(mUUID); if (!cat) return; - const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false); + const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); copy_folder_to_outbox(cat, outbox_id, cat->getUUID(), LLToolDragAndDrop::getOperationId()); } #if ENABLE_MERCHANT_SEND_TO_MARKETPLACE_CONTEXT_MENU @@ -2880,17 +3005,24 @@ LLUIImagePtr LLFolderBridge::getIcon() const LLUIImagePtr LLFolderBridge::getIcon(LLFolderType::EType preferred_type) { return LLUI::getUIImage(LLViewerFolderType::lookupIconName(preferred_type, FALSE)); - /*case LLAssetType::AT_MESH: - control = "inv_folder_mesh.tga"; - break;*/ } -LLUIImagePtr LLFolderBridge::getOpenIcon() const +LLUIImagePtr LLFolderBridge::getIconOpen() const { return LLUI::getUIImage(LLViewerFolderType::lookupIconName(getPreferredType(), TRUE)); } +LLUIImagePtr LLFolderBridge::getIconOverlay() const +{ + if (getInventoryObject() && getInventoryObject()->getIsLinkType()) + { + return LLUI::getUIImage("Inv_Link"); + } + return NULL; +} + + BOOL LLFolderBridge::renameItem(const std::string& new_name) { rename_category(getInventoryModel(), mUUID, new_name); @@ -2971,7 +3103,7 @@ void LLFolderBridge::pasteFromClipboard() if (move_is_into_outbox) { - LLFolderViewItem * outbox_itemp = mRoot->getItemByID(mUUID); + LLFolderViewItem * outbox_itemp = mInventoryPanel.get()->getItemByID(mUUID); if (outbox_itemp) { @@ -2994,7 +3126,7 @@ void LLFolderBridge::pasteFromClipboard() void * cargo_data = (void *) item; std::string tooltip_msg; - can_list = outbox_itemp->getListener()->dragOrDrop(mask, drop, cargo_type, cargo_data, tooltip_msg); + can_list = outbox_itemp->getViewModelItem()->dragOrDrop(mask, drop, cargo_type, cargo_data, tooltip_msg); } } @@ -3166,7 +3298,7 @@ BOOL LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInv return ((item_array.count() > 0) ? TRUE : FALSE ); } -void LLFolderBridge::buildContextMenuBaseOptions(U32 flags) +void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items) { LLInventoryModel* model = getInventoryModel(); llassert(model != NULL); @@ -3177,30 +3309,30 @@ void LLFolderBridge::buildContextMenuBaseOptions(U32 flags) if (lost_and_found_id == mUUID) { // This is the lost+found folder. - mItems.push_back(std::string("Empty Lost And Found")); + items.push_back(std::string("Empty Lost And Found")); - mDisabledItems.push_back(std::string("New Folder")); - mDisabledItems.push_back(std::string("New Script")); - mDisabledItems.push_back(std::string("New Note")); - mDisabledItems.push_back(std::string("New Gesture")); - mDisabledItems.push_back(std::string("New Clothes")); - mDisabledItems.push_back(std::string("New Body Parts")); + disabled_items.push_back(std::string("New Folder")); + disabled_items.push_back(std::string("New Script")); + disabled_items.push_back(std::string("New Note")); + disabled_items.push_back(std::string("New Gesture")); + disabled_items.push_back(std::string("New Clothes")); + disabled_items.push_back(std::string("New Body Parts")); } if(trash_id == mUUID) { // This is the trash. - mItems.push_back(std::string("Empty Trash")); + items.push_back(std::string("Empty Trash")); } else if(isItemInTrash()) { // This is a folder in the trash. - mItems.clear(); // clear any items that used to exist - addTrashContextMenuOptions(mItems, mDisabledItems); + items.clear(); // clear any items that used to exist + addTrashContextMenuOptions(items, disabled_items); } else if(isOutboxFolder()) { - addOutboxContextMenuOptions(flags, mItems, mDisabledItems); + addOutboxContextMenuOptions(flags, items, disabled_items); } else if(isAgentInventory()) // do not allow creating in library { @@ -3214,40 +3346,40 @@ void LLFolderBridge::buildContextMenuBaseOptions(U32 flags) // Do not allow to create 2-level subfolder in the Calling Card/Friends folder. EXT-694. if (!LLFriendCardsManager::instance().isCategoryInFriendFolder(cat)) { - mItems.push_back(std::string("New Folder")); + items.push_back(std::string("New Folder")); } - mItems.push_back(std::string("New Script")); - mItems.push_back(std::string("New Note")); - mItems.push_back(std::string("New Gesture")); - mItems.push_back(std::string("New Clothes")); - mItems.push_back(std::string("New Body Parts")); + items.push_back(std::string("New Script")); + items.push_back(std::string("New Note")); + items.push_back(std::string("New Gesture")); + items.push_back(std::string("New Clothes")); + items.push_back(std::string("New Body Parts")); } #if SUPPORT_ENSEMBLES // Changing folder types is an unfinished unsupported feature // and can lead to unexpected behavior if enabled. - mItems.push_back(std::string("Change Type")); + items.push_back(std::string("Change Type")); const LLViewerInventoryCategory *cat = getCategory(); if (cat && LLFolderType::lookupIsProtectedType(cat->getPreferredType())) { - mDisabledItems.push_back(std::string("Change Type")); + disabled_items.push_back(std::string("Change Type")); } #endif - getClipboardEntries(false, mItems, mDisabledItems, flags); + getClipboardEntries(false, items, disabled_items, flags); } else { // Want some but not all of the items from getClipboardEntries for outfits. if (cat && (cat->getPreferredType() == LLFolderType::FT_OUTFIT)) { - mItems.push_back(std::string("Rename")); + items.push_back(std::string("Rename")); - addDeleteContextMenuOptions(mItems, mDisabledItems); + addDeleteContextMenuOptions(items, disabled_items); // EXT-4030: disallow deletion of currently worn outfit const LLViewerInventoryItem *base_outfit_link = LLAppearanceMgr::instance().getBaseOutfitLink(); if (base_outfit_link && (cat == base_outfit_link->getLinkedCategory())) { - mDisabledItems.push_back(std::string("Delete")); + disabled_items.push_back(std::string("Delete")); } } } @@ -3276,20 +3408,44 @@ void LLFolderBridge::buildContextMenuBaseOptions(U32 flags) // Preemptively disable system folder removal if more than one item selected. if ((flags & FIRST_SELECTED_ITEM) == 0) { - mDisabledItems.push_back(std::string("Delete System Folder")); + disabled_items.push_back(std::string("Delete System Folder")); } if (!isOutboxFolder()) { - mItems.push_back(std::string("Share")); + items.push_back(std::string("Share")); if (!canShare()) { - mDisabledItems.push_back(std::string("Share")); + disabled_items.push_back(std::string("Share")); + } + } + // Add menu items that are dependent on the contents of the folder. + LLViewerInventoryCategory* category = (LLViewerInventoryCategory *) model->getCategory(mUUID); + if (category) + { + uuid_vec_t folders; + folders.push_back(category->getUUID()); + + sSelf = getHandle(); + LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(folders); + fetch->startFetch(); + if (fetch->isFinished()) + { + // Do not call execute() or done() here as if the folder is here, there's likely no point drilling down + // This saves lots of time as buildContextMenu() is called a lot + delete fetch; + buildContextMenuFolderOptions(flags, items, disabled_items); + } + else + { + // it's all on its way - add an observer, and the inventory will call done for us when everything is here. + inc_busy_count(); + gInventory.addObserver(fetch); } } } -void LLFolderBridge::buildContextMenuFolderOptions(U32 flags) +void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items) { // Build folder specific options back up LLInventoryModel* model = getInventoryModel(); @@ -3316,21 +3472,21 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags) LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD); if (mCallingCards || checkFolderForContentsOfType(model, is_callingcard)) { - mItems.push_back(std::string("Calling Card Separator")); - mItems.push_back(std::string("Conference Chat Folder")); - mItems.push_back(std::string("IM All Contacts In Folder")); + items.push_back(std::string("Calling Card Separator")); + items.push_back(std::string("Conference Chat Folder")); + items.push_back(std::string("IM All Contacts In Folder")); } } if (!isItemRemovable()) { - mDisabledItems.push_back(std::string("Delete")); + disabled_items.push_back(std::string("Delete")); } #ifndef LL_RELEASE_FOR_DOWNLOAD if (LLFolderType::lookupIsProtectedType(type)) { - mItems.push_back(std::string("Delete System Folder")); + items.push_back(std::string("Delete System Folder")); } #endif @@ -3345,7 +3501,7 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags) checkFolderForContentsOfType(model, is_object) || checkFolderForContentsOfType(model, is_gesture) ) { - mItems.push_back(std::string("Folder Wearables Separator")); + items.push_back(std::string("Folder Wearables Separator")); // Only enable add/replace outfit for non-system folders. if (!is_system_folder) @@ -3353,25 +3509,25 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags) // Adding an outfit onto another (versus replacing) doesn't make sense. if (type != LLFolderType::FT_OUTFIT) { - mItems.push_back(std::string("Add To Outfit")); + items.push_back(std::string("Add To Outfit")); } - mItems.push_back(std::string("Replace Outfit")); + items.push_back(std::string("Replace Outfit")); } if (is_ensemble) { - mItems.push_back(std::string("Wear As Ensemble")); + items.push_back(std::string("Wear As Ensemble")); } - mItems.push_back(std::string("Remove From Outfit")); + items.push_back(std::string("Remove From Outfit")); if (!LLAppearanceMgr::getCanRemoveFromCOF(mUUID)) { - mDisabledItems.push_back(std::string("Remove From Outfit")); + disabled_items.push_back(std::string("Remove From Outfit")); } if (!LLAppearanceMgr::instance().getCanReplaceCOF(mUUID)) { - mDisabledItems.push_back(std::string("Replace Outfit")); + disabled_items.push_back(std::string("Replace Outfit")); } - mItems.push_back(std::string("Outfit Separator")); + items.push_back(std::string("Outfit Separator")); } } @@ -3380,49 +3536,28 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { sSelf.markDead(); - mItems.clear(); - mDisabledItems.clear(); + // fetch contents of this folder, as context menu can depend on contents + // still, user would have to open context menu again to see the changes + gInventory.fetchDescendentsOf(getUUID()); + + + menuentry_vec_t items; + menuentry_vec_t disabled_items; lldebugs << "LLFolderBridge::buildContextMenu()" << llendl; LLInventoryModel* model = getInventoryModel(); if(!model) return; - buildContextMenuBaseOptions(flags); - - // Add menu items that are dependent on the contents of the folder. - LLViewerInventoryCategory* category = (LLViewerInventoryCategory *) model->getCategory(mUUID); - if (category) - { - uuid_vec_t folders; - folders.push_back(category->getUUID()); - - sSelf = getHandle(); - LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(folders); - fetch->startFetch(); - if (fetch->isFinished()) - { - // Do not call execute() or done() here as if the folder is here, there's likely no point drilling down - // This saves lots of time as buildContextMenu() is called a lot - delete fetch; - buildContextMenuFolderOptions(flags); - } - else - { - // it's all on its way - add an observer, and the inventory will call done for us when everything is here. - inc_busy_count(); - gInventory.addObserver(fetch); - } - } - - hide_context_entries(menu, mItems, mDisabledItems); + buildContextMenuOptions(flags, items, disabled_items); + hide_context_entries(menu, items, disabled_items); // Reposition the menu, in case we're adding items to an existing menu. menu.needsArrange(); menu.arrangeAndClear(); } -BOOL LLFolderBridge::hasChildren() const +bool LLFolderBridge::hasChildren() const { LLInventoryModel* model = getInventoryModel(); if(!model) return FALSE; @@ -3512,25 +3647,6 @@ void LLFolderBridge::pasteClipboard(void* user_data) if(self) self->pasteFromClipboard(); } -void LLFolderBridge::createNewCategory(void* user_data) -{ - LLFolderBridge* bridge = (LLFolderBridge*)user_data; - if(!bridge) return; - LLInventoryPanel* panel = bridge->mInventoryPanel.get(); - if (!panel) return; - LLInventoryModel* model = panel->getModel(); - if(!model) return; - LLUUID id; - id = model->createNewCategory(bridge->getUUID(), - LLFolderType::FT_NONE, - LLStringUtil::null); - model->notifyObservers(); - - // At this point, the bridge has probably been deleted, but the - // view is still there. - panel->setSelection(id, TAKE_FOCUS_YES); -} - void LLFolderBridge::createNewShirt(void* user_data) { LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_SHIRT); @@ -3596,6 +3712,24 @@ void LLFolderBridge::createNewEyes(void* user_data) LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_EYES); } +EInventorySortGroup LLFolderBridge::getSortGroup() const +{ + LLFolderType::EType preferred_type = getPreferredType(); + + if (preferred_type == LLFolderType::FT_TRASH) + { + return SG_TRASH_FOLDER; + } + + if(LLFolderType::lookupIsProtectedType(preferred_type)) + { + return SG_SYSTEM_FOLDER; + } + + return SG_NORMAL_FOLDER; +} + + // static void LLFolderBridge::createWearable(LLFolderBridge* bridge, LLWearableType::EType type) { @@ -3698,9 +3832,10 @@ void LLFolderBridge::dropToFavorites(LLInventoryItem* inv_item) LLPointer<AddFavoriteLandmarkCallback> cb = new AddFavoriteLandmarkCallback(); LLInventoryPanel* panel = mInventoryPanel.get(); LLFolderViewItem* drag_over_item = panel ? panel->getRootFolder()->getDraggingOverItem() : NULL; - if (drag_over_item && drag_over_item->getListener()) + LLFolderViewModelItemInventory* view_model = drag_over_item ? static_cast<LLFolderViewModelItemInventory*>(drag_over_item->getViewModelItem()) : NULL; + if (view_model) { - cb.get()->setTargetLandmarkId(drag_over_item->getListener()->getUUID()); + cb.get()->setTargetLandmarkId(view_model->getUUID()); } copy_inventory_item( @@ -3749,7 +3884,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, LLInventoryPanel* destination_panel = mInventoryPanel.get(); if (!destination_panel) return false; - LLInventoryFilter* filter = destination_panel->getFilter(); + LLInventoryFilter* filter = getInventoryFilter(); if (!filter) return false; const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); @@ -3866,13 +4001,10 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // passes the filter of the destination panel. if (accept && active_panel) { - LLFolderView* active_folder_view = active_panel->getRootFolder(); - if (!active_folder_view) return false; - - LLFolderViewItem* fv_item = active_folder_view->getItemByID(inv_item->getUUID()); + LLFolderViewItem* fv_item = active_panel->getItemByID(inv_item->getUUID()); if (!fv_item) return false; - accept = filter->check(fv_item); + accept = filter->check(fv_item->getViewModelItem()); } if (accept && drop) @@ -3884,6 +4016,8 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, } // If an item is being dragged between windows, unselect everything in the active window // so that we don't follow the selection to its new location (which is very annoying). + // RN: a better solution would be to deselect automatically when an item is moved + // and then select any item that is dropped only in the panel that it is dropped in if (active_panel && (destination_panel != active_panel)) { active_panel->unSelectAll(); @@ -3901,8 +4035,8 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, if (itemp) { LLUUID srcItemId = inv_item->getUUID(); - LLUUID destItemId = itemp->getListener()->getUUID(); - gInventory.rearrangeFavoriteLandmarks(srcItemId, destItemId); + LLUUID destItemId = static_cast<LLFolderViewModelItemInventory*>(itemp->getViewModelItem())->getUUID(); + LLFavoritesOrderStorage::instance().rearrangeFavoriteLandmarks(srcItemId, destItemId); } } @@ -3934,7 +4068,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, else { // set up observer to select item once drag and drop from inbox is complete - if (gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false, false))) + if (gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false))) { set_dad_inbox_object(inv_item->getUUID()); } @@ -4089,13 +4223,10 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // passes the filter of the destination panel. if (accept && active_panel) { - LLFolderView* active_folder_view = active_panel->getRootFolder(); - if (!active_folder_view) return false; - - LLFolderViewItem* fv_item = active_folder_view->getItemByID(inv_item->getUUID()); + LLFolderViewItem* fv_item = active_panel->getItemByID(inv_item->getUUID()); if (!fv_item) return false; - accept = filter->check(fv_item); + accept = filter->check(fv_item->getViewModelItem()); } if (accept && drop) @@ -4135,10 +4266,10 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // static bool check_category(LLInventoryModel* model, const LLUUID& cat_id, - LLFolderView* active_folder_view, + LLInventoryPanel* active_panel, LLInventoryFilter* filter) { - if (!model || !active_folder_view || !filter) + if (!model || !active_panel || !filter) return false; if (!filter->checkFolder(cat_id)) @@ -4158,13 +4289,13 @@ bool check_category(LLInventoryModel* model, // Empty folder should be checked as any other folder view item. // If we are filtering by date the folder should not pass because // it doesn't have its own creation date. See LLInvFVBridge::getCreationDate(). - return check_item(cat_id, active_folder_view, filter); + return check_item(cat_id, active_panel, filter); } for (S32 i = 0; i < num_descendent_categories; ++i) { LLInventoryCategory* category = descendent_categories[i]; - if(!check_category(model, category->getUUID(), active_folder_view, filter)) + if(!check_category(model, category->getUUID(), active_panel, filter)) { return false; } @@ -4173,7 +4304,7 @@ bool check_category(LLInventoryModel* model, for (S32 i = 0; i < num_descendent_items; ++i) { LLViewerInventoryItem* item = descendent_items[i]; - if(!check_item(item->getUUID(), active_folder_view, filter)) + if(!check_item(item->getUUID(), active_panel, filter)) { return false; } @@ -4184,15 +4315,15 @@ bool check_category(LLInventoryModel* model, // static bool check_item(const LLUUID& item_id, - LLFolderView* active_folder_view, + LLInventoryPanel* active_panel, LLInventoryFilter* filter) { - if (!active_folder_view || !filter) return false; + if (!active_panel || !filter) return false; - LLFolderViewItem* fv_item = active_folder_view->getItemByID(item_id); + LLFolderViewItem* fv_item = active_panel->getItemByID(item_id); if (!fv_item) return false; - return filter->check(fv_item); + return filter->check(fv_item->getViewModelItem()); } // +=================================================+ @@ -4294,15 +4425,6 @@ void LLSoundBridge::openItem() } } -void LLSoundBridge::previewItem() -{ - LLViewerInventoryItem* item = getItem(); - if(item) - { - send_sound_trigger(item->getAssetUUID(), 1.0); - } -} - void LLSoundBridge::openSoundPreview(void* which) { LLSoundBridge *me = (LLSoundBridge *)which; @@ -4518,7 +4640,7 @@ LLCallingCardBridge::~LLCallingCardBridge() void LLCallingCardBridge::refreshFolderViewItem() { LLInventoryPanel* panel = mInventoryPanel.get(); - LLFolderViewItem* itemp = panel ? panel->getRootFolder()->getItemByID(mUUID) : NULL; + LLFolderViewItem* itemp = panel ? panel->getItemByID(mUUID) : NULL; if (itemp) { itemp->refresh(); @@ -5309,11 +5431,10 @@ BOOL LLObjectBridge::renameItem(const std::string& new_name) { LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); new_item->rename(new_name); - buildDisplayName(new_item, mDisplayName); new_item->updateServer(FALSE); model->updateItem(new_item); - model->notifyObservers(); + buildDisplayName(); if (isAgentAvatarValid()) { @@ -5913,16 +6034,6 @@ void LLMeshBridge::openItem() } } -void LLMeshBridge::previewItem() -{ - LLViewerInventoryItem* item = getItem(); - if(item) - { - // preview mesh - } -} - - void LLMeshBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { lldebugs << "LLMeshBridge::buildContextMenu()" << llendl; @@ -6011,14 +6122,15 @@ void LLLinkFolderBridge::gotoItem() const LLUUID &cat_uuid = getFolderID(); if (!cat_uuid.isNull()) { - if (LLFolderViewItem *base_folder = mRoot->getItemByID(cat_uuid)) + LLFolderViewItem *base_folder = mInventoryPanel.get()->getItemByID(cat_uuid); + if (base_folder) { if (LLInventoryModel* model = getInventoryModel()) { model->fetchDescendentsOf(cat_uuid); } base_folder->setOpen(TRUE); - mRoot->setSelectionFromRoot(base_folder,TRUE); + mRoot->setSelection(base_folder,TRUE); mRoot->scrollToShowSelection(); } } @@ -6349,9 +6461,8 @@ LLInvFVBridgeAction* LLInvFVBridgeAction::createAction(LLAssetType::EType asset_ /************************************************************************/ void LLRecentItemsFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { - LLFolderBridge::buildContextMenu(menu, flags); - - menuentry_vec_t disabled_items, items = getMenuItems(); + menuentry_vec_t disabled_items, items; + buildContextMenuOptions(flags, items, disabled_items); items.erase(std::remove(items.begin(), items.end(), std::string("New Folder")), items.end()); @@ -6363,42 +6474,29 @@ LLInvFVBridge* LLRecentInventoryBridgeBuilder::createBridge( LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags /*= 0x00*/ ) const { LLInvFVBridge* new_listener = NULL; - switch(asset_type) + if (asset_type == LLAssetType::AT_CATEGORY + && actual_asset_type != LLAssetType::AT_LINK_FOLDER) { - case LLAssetType::AT_CATEGORY: - if (actual_asset_type == LLAssetType::AT_LINK_FOLDER) - { - // *TODO: Create a link folder handler instead if it is necessary - new_listener = LLInventoryFVBridgeBuilder::createBridge( - asset_type, - actual_asset_type, - inv_type, - inventory, - root, - uuid, - flags); - break; - } new_listener = new LLRecentItemsFolderBridge(inv_type, inventory, root, uuid); - break; - default: - new_listener = LLInventoryFVBridgeBuilder::createBridge( - asset_type, - actual_asset_type, - inv_type, - inventory, - root, - uuid, - flags); + } + else + { + new_listener = LLInventoryFolderViewModelBuilder::createBridge(asset_type, + actual_asset_type, + inv_type, + inventory, + view_model, + root, + uuid, + flags); } return new_listener; - } - // EOF diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index dc9e88d54d..fc0b15acad 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -29,11 +29,13 @@ #include "llcallingcard.h" #include "llfloaterproperties.h" -#include "llfoldervieweventlistener.h" +#include "llfolderviewmodel.h" #include "llinventorymodel.h" #include "llinventoryobserver.h" +#include "llinventorypanel.h" #include "llviewercontrol.h" #include "llwearable.h" +#include "lltooldraganddrop.h" class LLInventoryFilter; class LLInventoryPanel; @@ -41,7 +43,7 @@ class LLInventoryModel; class LLMenuGL; class LLCallingCardObserver; class LLViewerJointAttachment; - +class LLFolderView; typedef std::vector<std::string> menuentry_vec_t; @@ -56,7 +58,7 @@ typedef std::vector<std::string> menuentry_vec_t; // functionality a bit. (except for folders, you can create those // manually...) //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLInvFVBridge : public LLFolderViewEventListener +class LLInvFVBridge : public LLFolderViewModelItemInventory { public: // This method is a convenience function which creates the correct @@ -65,6 +67,7 @@ public: LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags = 0x00); @@ -78,23 +81,25 @@ public: // LLInvFVBridge functionality //-------------------------------------------------------------------- virtual const LLUUID& getUUID() const { return mUUID; } - virtual void clearDisplayName() {} + virtual void clearDisplayName() { mDisplayName.clear(); } virtual void restoreItem() {} virtual void restoreToWorld() {} //-------------------------------------------------------------------- - // Inherited LLFolderViewEventListener functions + // Inherited LLFolderViewModelItemInventory functions //-------------------------------------------------------------------- virtual const std::string& getName() const; virtual const std::string& getDisplayName() const; + const std::string& getSearchableName() const { return mSearchableName; } + virtual PermissionMask getPermissionMask() const; virtual LLFolderType::EType getPreferredType() const; virtual time_t getCreationDate() const; + virtual void setCreationDate(time_t creation_date_utc); virtual LLFontGL::StyleFlags getLabelStyle() const { return LLFontGL::NORMAL; } virtual std::string getLabelSuffix() const { return LLStringUtil::null; } virtual void openItem() {} virtual void closeItem() {} - virtual void previewItem() {openItem();} virtual void showProperties(); virtual BOOL isItemRenameable() const { return TRUE; } //virtual BOOL renameItem(const std::string& new_name) {} @@ -103,8 +108,8 @@ public: virtual BOOL isItemInTrash() const; virtual BOOL isLink() const; //virtual BOOL removeItem() = 0; - virtual void removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch); - virtual void move(LLFolderViewEventListener* new_parent_bridge) {} + virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch); + virtual void move(LLFolderViewModelItem* new_parent_bridge) {} virtual BOOL isItemCopyable() const { return FALSE; } virtual BOOL copyToClipboard() const; virtual BOOL cutToClipboard() const; @@ -115,6 +120,7 @@ public: void getClipboardEntries(bool show_asset_id, menuentry_vec_t &items, menuentry_vec_t &disabled_items, U32 flags); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); + virtual LLToolDragAndDrop::ESource getDragSource() const; virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const; virtual BOOL dragOrDrop(MASK mask, BOOL drop, EDragAndDropType cargo_type, @@ -122,6 +128,9 @@ public: std::string& tooltip_msg) { return FALSE; } virtual LLInventoryType::EType getInventoryType() const { return mInvType; } virtual LLWearableType::EType getWearableType() const { return LLWearableType::WT_NONE; } + EInventorySortGroup getSortGroup() const { return SG_ITEM; } + virtual LLInventoryObject* getInventoryObject() const; + //-------------------------------------------------------------------- // Convenience functions for adding various common menu options. @@ -138,16 +147,16 @@ protected: protected: LLInvFVBridge(LLInventoryPanel* inventory, LLFolderView* root, const LLUUID& uuid); - LLInventoryObject* getInventoryObject() const; LLInventoryModel* getInventoryModel() const; + LLInventoryFilter* getInventoryFilter() const; BOOL isLinkedObjectInTrash() const; // Is this obj or its baseobj in the trash? BOOL isLinkedObjectMissing() const; // Is this a linked obj whose baseobj is not in inventory? BOOL isAgentInventory() const; // false if lost or in the inventory library - BOOL isCOFFolder() const; // true if COF or descendent of - BOOL isInboxFolder() const; // true if COF or descendent of marketplace inbox - BOOL isOutboxFolder() const; // true if COF or descendent of marketplace outbox + BOOL isCOFFolder() const; // true if COF or descendant of + BOOL isInboxFolder() const; // true if COF or descendant of marketplace inbox + BOOL isOutboxFolder() const; // true if COF or descendant of marketplace outbox BOOL isOutboxFolderDirectParent() const; const LLUUID getOutboxFolder() const; @@ -160,30 +169,36 @@ protected: LLViewerInventoryCategory* item, const LLUUID& new_parent, BOOL restamp); - void removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener*>& batch); + void removeBatchNoCheck(std::vector<LLFolderViewModelItem*>& batch); protected: - LLHandle<LLInventoryPanel> mInventoryPanel; - LLFolderView* mRoot; - const LLUUID mUUID; // item id - LLInventoryType::EType mInvType; - BOOL mIsLink; + LLHandle<LLInventoryPanel> mInventoryPanel; + LLFolderView* mRoot; + const LLUUID mUUID; // item id + LLInventoryType::EType mInvType; + bool mIsLink; + LLTimer mTimeSinceRequestStart; + mutable std::string mDisplayName; + mutable std::string mSearchableName; + void purgeItem(LLInventoryModel *model, const LLUUID &uuid); + virtual void buildDisplayName() const {} }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInvFVBridgeBuilder +// Class LLInventoryFolderViewModelBuilder // -// This class intended to build Folder View Bridge via LLInvFVBridge::createBridge. -// It can be overridden with another way of creation necessary Inventory-Folder-View-Bridge. +// This class intended to build Folder View Model via LLInvFVBridge::createBridge. +// It can be overridden with another way of creation necessary Inventory Folder View Models. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLInventoryFVBridgeBuilder +class LLInventoryFolderViewModelBuilder { public: - virtual ~LLInventoryFVBridgeBuilder() {} + virtual ~LLInventoryFolderViewModelBuilder() {} virtual LLInvFVBridge* createBridge(LLAssetType::EType asset_type, LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags = 0x00) const; @@ -203,7 +218,6 @@ public: virtual void restoreToWorld(); virtual void gotoItem(); virtual LLUIImagePtr getIcon() const; - virtual const std::string& getDisplayName() const; virtual std::string getLabelSuffix() const; virtual LLFontGL::StyleFlags getLabelStyle() const; virtual PermissionMask getPermissionMask() const; @@ -212,19 +226,16 @@ public: virtual BOOL renameItem(const std::string& new_name); virtual BOOL removeItem(); virtual BOOL isItemCopyable() const; - virtual BOOL hasChildren() const { return FALSE; } + virtual bool hasChildren() const { return FALSE; } virtual BOOL isUpToDate() const { return TRUE; } - /*virtual*/ void clearDisplayName() { mDisplayName.clear(); } - LLViewerInventoryItem* getItem() const; protected: BOOL confirmRemoveItem(const LLSD& notification, const LLSD& response); virtual BOOL isItemPermissive() const; - static void buildDisplayName(LLInventoryItem* item, std::string& name); + virtual void buildDisplayName() const; - mutable std::string mDisplayName; }; class LLFolderBridge : public LLInvFVBridge @@ -232,15 +243,18 @@ class LLFolderBridge : public LLInvFVBridge public: LLFolderBridge(LLInventoryPanel* inventory, LLFolderView* root, - const LLUUID& uuid) : - LLInvFVBridge(inventory, root, uuid), + const LLUUID& uuid) + : LLInvFVBridge(inventory, root, uuid), mCallingCards(FALSE), - mWearables(FALSE) + mWearables(FALSE), + mIsLoading(false) {} BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop, std::string& tooltip_msg); BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop, std::string& tooltip_msg); + virtual void buildDisplayName() const; + virtual void performAction(LLInventoryModel* model, std::string action); virtual void openItem(); virtual void closeItem(); @@ -250,7 +264,9 @@ public: virtual LLFolderType::EType getPreferredType() const; virtual LLUIImagePtr getIcon() const; - virtual LLUIImagePtr getOpenIcon() const; + virtual LLUIImagePtr getIconOpen() const; + virtual LLUIImagePtr getIconOverlay() const; + static LLUIImagePtr getIcon(LLFolderType::EType preferred_type); virtual BOOL renameItem(const std::string& new_name); @@ -262,7 +278,7 @@ public: virtual void pasteFromClipboard(); virtual void pasteLinkFromClipboard(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); - virtual BOOL hasChildren() const; + virtual bool hasChildren() const; virtual BOOL dragOrDrop(MASK mask, BOOL drop, EDragAndDropType cargo_type, void* cargo_data, @@ -275,20 +291,24 @@ public: virtual BOOL isClipboardPasteable() const; virtual BOOL isClipboardPasteableAsLink() const; + EInventorySortGroup getSortGroup() const; + virtual void update(); + static void createWearable(LLFolderBridge* bridge, LLWearableType::EType type); LLViewerInventoryCategory* getCategory() const; LLHandle<LLFolderBridge> getHandle() { mHandle.bind(this); return mHandle; } + bool isLoading() { return mIsLoading; } + protected: - void buildContextMenuBaseOptions(U32 flags); - void buildContextMenuFolderOptions(U32 flags); + void buildContextMenuOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items); + void buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items); //-------------------------------------------------------------------- // Menu callbacks //-------------------------------------------------------------------- static void pasteClipboard(void* user_data); - static void createNewCategory(void* user_data); static void createNewShirt(void* user_data); static void createNewPants(void* user_data); static void createNewShoes(void* user_data); @@ -308,8 +328,6 @@ protected: void modifyOutfit(BOOL append); void determineFolderType(); - menuentry_vec_t getMenuItems() { return mItems; } // returns a copy of current menu items - void dropToFavorites(LLInventoryItem* inv_item); void dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit); @@ -321,11 +339,12 @@ public: static void staticFolderOptionsMenu(); private: - BOOL mCallingCards; - BOOL mWearables; - menuentry_vec_t mItems; - menuentry_vec_t mDisabledItems; - LLRootHandle<LLFolderBridge> mHandle; + + bool mCallingCards; + bool mWearables; + bool mIsLoading; + LLTimer mTimeSinceRequestStart; + LLRootHandle<LLFolderBridge> mHandle; }; class LLTextureBridge : public LLItemBridge @@ -354,7 +373,6 @@ public: const LLUUID& uuid) : LLItemBridge(inventory, root, uuid) {} virtual void openItem(); - virtual void previewItem(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); static void openSoundPreview(void*); }; @@ -544,7 +562,6 @@ class LLMeshBridge : public LLItemBridge public: virtual LLUIImagePtr getIcon() const; virtual void openItem(); - virtual void previewItem(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); protected: @@ -620,7 +637,7 @@ public: }; // Bridge builder to create Inventory-Folder-View-Bridge for Recent Inventory Panel -class LLRecentInventoryBridgeBuilder : public LLInventoryFVBridgeBuilder +class LLRecentInventoryBridgeBuilder : public LLInventoryFolderViewModelBuilder { public: // Overrides FolderBridge for Recent Inventory Panel. @@ -629,6 +646,7 @@ public: LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags = 0x00) const; diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index 4573074c73..c913269aad 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -29,7 +29,7 @@ #include "llinventoryfilter.h" // viewer includes -#include "llfoldervieweventlistener.h" +#include "llfolderviewmodel.h" #include "llfolderviewitem.h" #include "llinventorymodel.h" #include "llinventorymodelbackgroundfetch.h" @@ -42,109 +42,90 @@ #include "llclipboard.h" #include "lltrans.h" +//TODO RN: fix use of static cast as much as possible + LLFastTimer::DeclareTimer FT_FILTER_CLIPBOARD("Filter Clipboard"); -LLInventoryFilter::FilterOps::FilterOps() : - mFilterObjectTypes(0xffffffffffffffffULL), - mFilterCategoryTypes(0xffffffffffffffffULL), - mFilterWearableTypes(0xffffffffffffffffULL), - mMinDate(time_min()), - mMaxDate(time_max()), - mHoursAgo(0), - mShowFolderState(SHOW_NON_EMPTY_FOLDERS), - mPermissions(PERM_NONE), - mFilterTypes(FILTERTYPE_OBJECT), - mFilterUUID(LLUUID::null), - mFilterLinks(FILTERLINK_INCLUDE_LINKS) +LLInventoryFilter::FilterOps::FilterOps(const Params& p) +: mFilterObjectTypes(p.object_types), + mFilterCategoryTypes(p.category_types), + mFilterWearableTypes(p.wearable_types), + mMinDate(p.date_range.min_date), + mMaxDate(p.date_range.max_date), + mHoursAgo(p.hours_ago), + mShowFolderState(p.show_folder_state), + mPermissions(p.permissions), + mFilterTypes(p.types), + mFilterUUID(p.uuid), + mFilterLinks(p.links) { } ///---------------------------------------------------------------------------- /// Class LLInventoryFilter ///---------------------------------------------------------------------------- -LLInventoryFilter::LLInventoryFilter(const std::string& name) -: mName(name), - mModified(FALSE), - mNeedTextRebuild(TRUE), - mEmptyLookupMessage("InventoryNoMatchingItems") +LLInventoryFilter::LLInventoryFilter(const Params& p) +: mName(p.name), + mFilterModified(FILTER_NONE), + mEmptyLookupMessage("InventoryNoMatchingItems"), + mFilterOps(p.filter_ops), + mFilterSubString(p.substring), + mCurrentGeneration(0), + mFirstRequiredGeneration(0), + mFirstSuccessGeneration(0), + mFilterCount(0) { - mOrder = SO_FOLDERS_BY_NAME; // This gets overridden by a pref immediately - - mSubStringMatchOffset = 0; - mFilterSubString.clear(); - mFilterGeneration = 0; - mMustPassGeneration = S32_MAX; - mMinRequiredGeneration = 0; - mFilterCount = 0; - mNextFilterGeneration = mFilterGeneration + 1; - - mLastLogoff = gSavedPerAccountSettings.getU32("LastLogoff"); - mFilterBehavior = FILTER_NONE; + mNextFilterGeneration = mCurrentGeneration + 1; // copy mFilterOps into mDefaultFilterOps markDefault(); } -LLInventoryFilter::~LLInventoryFilter() -{ -} - -BOOL LLInventoryFilter::check(const LLFolderViewItem* item) +bool LLInventoryFilter::check(const LLFolderViewModelItem* item) { + const LLFolderViewModelItemInventory* listener = static_cast<const LLFolderViewModelItemInventory*>(item); // Clipboard cut items are *always* filtered so we need this value upfront - const LLFolderViewEventListener* listener = item->getListener(); const BOOL passed_clipboard = (listener ? checkAgainstClipboard(listener->getUUID()) : TRUE); // If it's a folder and we're showing all folders, return automatically. - const BOOL is_folder = (dynamic_cast<const LLFolderViewFolder*>(item) != NULL); + const BOOL is_folder = listener->getInventoryType() == LLInventoryType::IT_CATEGORY; if (is_folder && (mFilterOps.mShowFolderState == LLInventoryFilter::SHOW_ALL_FOLDERS)) { return passed_clipboard; } - mSubStringMatchOffset = mFilterSubString.size() ? item->getSearchableLabel().find(mFilterSubString) : std::string::npos; + std::string::size_type string_offset = mFilterSubString.size() ? listener->getSearchableName().find(mFilterSubString) : std::string::npos; - const BOOL passed_filtertype = checkAgainstFilterType(item); - const BOOL passed_permissions = checkAgainstPermissions(item); - const BOOL passed_filterlink = checkAgainstFilterLinks(item); - const BOOL passed = (passed_filtertype && - passed_permissions && - passed_filterlink && - passed_clipboard && - (mFilterSubString.size() == 0 || mSubStringMatchOffset != std::string::npos)); + BOOL passed = (mFilterSubString.size() == 0 || string_offset != std::string::npos); + passed = passed && checkAgainstFilterType(listener); + passed = passed && checkAgainstPermissions(listener); + passed = passed && checkAgainstFilterLinks(listener); + passed = passed && passed_clipboard; return passed; } bool LLInventoryFilter::check(const LLInventoryItem* item) { - mSubStringMatchOffset = mFilterSubString.size() ? item->getName().find(mFilterSubString) : std::string::npos; + std::string::size_type string_offset = mFilterSubString.size() ? item->getName().find(mFilterSubString) : std::string::npos; const bool passed_filtertype = checkAgainstFilterType(item); const bool passed_permissions = checkAgainstPermissions(item); const BOOL passed_clipboard = checkAgainstClipboard(item->getUUID()); - const bool passed = (passed_filtertype && - passed_permissions && - passed_clipboard && - (mFilterSubString.size() == 0 || mSubStringMatchOffset != std::string::npos)); + const bool passed = (passed_filtertype + && passed_permissions + && passed_clipboard + && (mFilterSubString.size() == 0 || string_offset != std::string::npos)); return passed; } -bool LLInventoryFilter::checkFolder(const LLFolderViewFolder* folder) const +bool LLInventoryFilter::checkFolder(const LLFolderViewModelItem* item) const { - if (!folder) - { - llwarns << "The filter can not be checked on an invalid folder." << llendl; - llassert(false); // crash in development builds - return false; - } - - const LLFolderViewEventListener* listener = folder->getListener(); + const LLFolderViewModelItemInventory* listener = static_cast<const LLFolderViewModelItemInventory*>(item); if (!listener) { - llwarns << "Folder view event listener not found." << llendl; - llassert(false); // crash in development builds + llerrs << "Folder view event listener not found." << llendl; return false; } @@ -155,6 +136,13 @@ bool LLInventoryFilter::checkFolder(const LLFolderViewFolder* folder) const bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const { + // when applying a filter, matching folders get their contents downloaded first + if (isNotDefault() + && !gInventory.isCategoryComplete(folder_id)) + { + LLInventoryModelBackgroundFetch::instance().start(folder_id); + } + // Always check against the clipboard const BOOL passed_clipboard = checkAgainstClipboard(folder_id); @@ -163,14 +151,14 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const { return passed_clipboard; } - + if (mFilterOps.mFilterTypes & FILTERTYPE_CATEGORY) { // Can only filter categories for items in your inventory // (e.g. versus in-world object contents). const LLViewerInventoryCategory *cat = gInventory.getCategory(folder_id); if (!cat) - return false; + return folder_id.isNull(); LLFolderType::EType cat_type = cat->getPreferredType(); if (cat_type != LLFolderType::FT_NONE && (1LL << cat_type & mFilterOps.mFilterCategoryTypes) == U64(0)) return false; @@ -179,9 +167,8 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const return passed_clipboard; } -BOOL LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) const +bool LLInventoryFilter::checkAgainstFilterType(const LLFolderViewModelItemInventory* listener) const { - const LLFolderViewEventListener* listener = item->getListener(); if (!listener) return FALSE; LLInventoryType::EType object_type = listener->getInventoryType(); @@ -268,7 +255,7 @@ BOOL LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) con } } } - + return TRUE; } @@ -347,13 +334,12 @@ bool LLInventoryFilter::checkAgainstClipboard(const LLUUID& object_id) const return true; } -BOOL LLInventoryFilter::checkAgainstPermissions(const LLFolderViewItem* item) const +bool LLInventoryFilter::checkAgainstPermissions(const LLFolderViewModelItemInventory* listener) const { - const LLFolderViewEventListener* listener = item->getListener(); if (!listener) return FALSE; PermissionMask perm = listener->getPermissionMask(); - const LLInvFVBridge *bridge = dynamic_cast<const LLInvFVBridge *>(item->getListener()); + const LLInvFVBridge *bridge = dynamic_cast<const LLInvFVBridge *>(listener); if (bridge && bridge->isLink()) { const LLUUID& linked_uuid = gInventory.getLinkedItemID(bridge->getUUID()); @@ -375,9 +361,8 @@ bool LLInventoryFilter::checkAgainstPermissions(const LLInventoryItem* item) con return (perm & mFilterOps.mPermissions) == mFilterOps.mPermissions; } -BOOL LLInventoryFilter::checkAgainstFilterLinks(const LLFolderViewItem* item) const +bool LLInventoryFilter::checkAgainstFilterLinks(const LLFolderViewModelItemInventory* listener) const { - const LLFolderViewEventListener* listener = item->getListener(); if (!listener) return TRUE; const LLUUID object_id = listener->getUUID(); @@ -397,20 +382,21 @@ const std::string& LLInventoryFilter::getFilterSubString(BOOL trim) const return mFilterSubString; } -std::string::size_type LLInventoryFilter::getStringMatchOffset() const +std::string::size_type LLInventoryFilter::getStringMatchOffset(LLFolderViewModelItem* item) const { - return mSubStringMatchOffset; + const LLFolderViewModelItemInventory* listener = static_cast<const LLFolderViewModelItemInventory*>(item); + return mFilterSubString.size() ? listener->getSearchableName().find(mFilterSubString) : std::string::npos; } -BOOL LLInventoryFilter::isDefault() const +bool LLInventoryFilter::isDefault() const { return !isNotDefault(); } // has user modified default filter params? -BOOL LLInventoryFilter::isNotDefault() const +bool LLInventoryFilter::isNotDefault() const { - BOOL not_default = FALSE; + S32 not_default = 0; not_default |= (mFilterOps.mFilterObjectTypes != mDefaultFilterOps.mFilterObjectTypes); not_default |= (mFilterOps.mFilterCategoryTypes != mDefaultFilterOps.mFilterCategoryTypes); @@ -422,11 +408,11 @@ BOOL LLInventoryFilter::isNotDefault() const not_default |= (mFilterOps.mMinDate != mDefaultFilterOps.mMinDate); not_default |= (mFilterOps.mMaxDate != mDefaultFilterOps.mMaxDate); not_default |= (mFilterOps.mHoursAgo != mDefaultFilterOps.mHoursAgo); - - return not_default; + + return not_default != 0; } -BOOL LLInventoryFilter::isActive() const +bool LLInventoryFilter::isActive() const { return mFilterOps.mFilterObjectTypes != 0xffffffffffffffffULL || mFilterOps.mFilterCategoryTypes != 0xffffffffffffffffULL @@ -440,16 +426,9 @@ BOOL LLInventoryFilter::isActive() const || mFilterOps.mHoursAgo != 0; } -BOOL LLInventoryFilter::isModified() const +bool LLInventoryFilter::isModified() const { - return mModified; -} - -BOOL LLInventoryFilter::isModifiedAndClear() -{ - BOOL ret = mModified; - mModified = FALSE; - return ret; + return mFilterModified != FILTER_NONE; } void LLInventoryFilter::updateFilterTypes(U64 types, U64& current_types) @@ -613,9 +592,10 @@ void LLInventoryFilter::setDateRange(time_t min_date, time_t max_date) void LLInventoryFilter::setDateRangeLastLogoff(BOOL sl) { + static LLCachedControl<U32> s_last_logoff(gSavedPerAccountSettings, "LastLogoff", 0); if (sl && !isSinceLogoff()) { - setDateRange(mLastLogoff, time_max()); + setDateRange(s_last_logoff(), time_max()); setModified(); } if (!sl && isSinceLogoff()) @@ -634,17 +614,18 @@ void LLInventoryFilter::setDateRangeLastLogoff(BOOL sl) } } -BOOL LLInventoryFilter::isSinceLogoff() const +bool LLInventoryFilter::isSinceLogoff() const { - return (mFilterOps.mMinDate == (time_t)mLastLogoff) && + static LLCachedControl<U32> s_last_logoff(gSavedSettings, "LastLogoff", 0); + + return (mFilterOps.mMinDate == (time_t)s_last_logoff()) && (mFilterOps.mMaxDate == time_max()) && (mFilterOps.mFilterTypes & FILTERTYPE_DATE); } void LLInventoryFilter::clearModified() { - mModified = FALSE; - mFilterBehavior = FILTER_NONE; + mFilterModified = FILTER_NONE; } void LLInventoryFilter::setHoursAgo(U32 hours) @@ -722,15 +703,6 @@ void LLInventoryFilter::setShowFolderState(EFolderShow state) } } -void LLInventoryFilter::setSortOrder(U32 order) -{ - if (mOrder != order) - { - mOrder = order; - setModified(); - } -} - void LLInventoryFilter::markDefault() { mDefaultFilterOps = mFilterOps; @@ -742,83 +714,68 @@ void LLInventoryFilter::resetDefault() setModified(); } -void LLInventoryFilter::setModified(EFilterBehavior behavior) +void LLInventoryFilter::setModified(EFilterModified behavior) { - mModified = TRUE; - mNeedTextRebuild = TRUE; - mFilterGeneration = mNextFilterGeneration++; + mFilterText.clear(); + mCurrentGeneration = mNextFilterGeneration++; - if (mFilterBehavior == FILTER_NONE) + if (mFilterModified == FILTER_NONE) { - mFilterBehavior = behavior; + mFilterModified = behavior; } - else if (mFilterBehavior != behavior) + else if (mFilterModified != behavior) { // trying to do both less restrictive and more restrictive filter // basically means restart from scratch - mFilterBehavior = FILTER_RESTART; + mFilterModified = FILTER_RESTART; } - if (isNotDefault()) + // if not keeping current filter results, update last valid as well + switch(mFilterModified) { - // if not keeping current filter results, update last valid as well - switch(mFilterBehavior) - { - case FILTER_RESTART: - mMustPassGeneration = mFilterGeneration; - mMinRequiredGeneration = mFilterGeneration; - break; - case FILTER_LESS_RESTRICTIVE: - mMustPassGeneration = mFilterGeneration; - break; - case FILTER_MORE_RESTRICTIVE: - mMinRequiredGeneration = mFilterGeneration; - // must have passed either current filter generation (meaningless, as it hasn't been run yet) - // or some older generation, so keep the value - mMustPassGeneration = llmin(mMustPassGeneration, mFilterGeneration); - break; - default: - llerrs << "Bad filter behavior specified" << llendl; - } - } - else - { - // shortcut disabled filters to show everything immediately - mMinRequiredGeneration = 0; - mMustPassGeneration = S32_MAX; + case FILTER_RESTART: + mFirstRequiredGeneration = mCurrentGeneration; + mFirstSuccessGeneration = mCurrentGeneration; + break; + case FILTER_LESS_RESTRICTIVE: + mFirstRequiredGeneration = mCurrentGeneration; + break; + case FILTER_MORE_RESTRICTIVE: + mFirstSuccessGeneration = mCurrentGeneration; + break; + default: + llerrs << "Bad filter behavior specified" << llendl; } } -BOOL LLInventoryFilter::isFilterObjectTypesWith(LLInventoryType::EType t) const +bool LLInventoryFilter::isFilterObjectTypesWith(LLInventoryType::EType t) const { return mFilterOps.mFilterObjectTypes & (1LL << t); } const std::string& LLInventoryFilter::getFilterText() { - if (!mNeedTextRebuild) + if (!mFilterText.empty()) { return mFilterText; } - mNeedTextRebuild = FALSE; std::string filtered_types; std::string not_filtered_types; BOOL filtered_by_type = FALSE; BOOL filtered_by_all_types = TRUE; S32 num_filter_types = 0; + mFilterText.clear(); if (isFilterObjectTypesWith(LLInventoryType::IT_ANIMATION)) { - //filtered_types += " Animations,"; filtered_types += LLTrans::getString("Animations"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Animations,"; not_filtered_types += LLTrans::getString("Animations"); filtered_by_all_types = FALSE; @@ -826,140 +783,120 @@ const std::string& LLInventoryFilter::getFilterText() if (isFilterObjectTypesWith(LLInventoryType::IT_CALLINGCARD)) { - //filtered_types += " Calling Cards,"; filtered_types += LLTrans::getString("Calling Cards"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Calling Cards,"; not_filtered_types += LLTrans::getString("Calling Cards"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_WEARABLE)) { - //filtered_types += " Clothing,"; filtered_types += LLTrans::getString("Clothing"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Clothing,"; not_filtered_types += LLTrans::getString("Clothing"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_GESTURE)) { - //filtered_types += " Gestures,"; filtered_types += LLTrans::getString("Gestures"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Gestures,"; not_filtered_types += LLTrans::getString("Gestures"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_LANDMARK)) { - //filtered_types += " Landmarks,"; filtered_types += LLTrans::getString("Landmarks"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Landmarks,"; not_filtered_types += LLTrans::getString("Landmarks"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_NOTECARD)) { - //filtered_types += " Notecards,"; filtered_types += LLTrans::getString("Notecards"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Notecards,"; not_filtered_types += LLTrans::getString("Notecards"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_OBJECT) && isFilterObjectTypesWith(LLInventoryType::IT_ATTACHMENT)) { - //filtered_types += " Objects,"; filtered_types += LLTrans::getString("Objects"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Objects,"; not_filtered_types += LLTrans::getString("Objects"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_LSL)) { - //filtered_types += " Scripts,"; filtered_types += LLTrans::getString("Scripts"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Scripts,"; not_filtered_types += LLTrans::getString("Scripts"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_SOUND)) { - //filtered_types += " Sounds,"; filtered_types += LLTrans::getString("Sounds"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Sounds,"; not_filtered_types += LLTrans::getString("Sounds"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_TEXTURE)) { - //filtered_types += " Textures,"; filtered_types += LLTrans::getString("Textures"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Textures,"; not_filtered_types += LLTrans::getString("Textures"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_SNAPSHOT)) { - //filtered_types += " Snapshots,"; filtered_types += LLTrans::getString("Snapshots"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Snapshots,"; not_filtered_types += LLTrans::getString("Snapshots"); filtered_by_all_types = FALSE; } @@ -975,7 +912,6 @@ const std::string& LLInventoryFilter::getFilterText() } else { - //mFilterText += "No "; mFilterText += LLTrans::getString("No Filters"); mFilterText += not_filtered_types; } @@ -985,66 +921,55 @@ const std::string& LLInventoryFilter::getFilterText() if (isSinceLogoff()) { - //mFilterText += " - Since Logoff"; mFilterText += LLTrans::getString("Since Logoff"); } return mFilterText; } -void LLInventoryFilter::toLLSD(LLSD& data) const -{ - data["filter_types"] = (LLSD::Integer)getFilterObjectTypes(); - data["min_date"] = (LLSD::Integer)getMinDate(); - data["max_date"] = (LLSD::Integer)getMaxDate(); - data["hours_ago"] = (LLSD::Integer)getHoursAgo(); - data["show_folder_state"] = (LLSD::Integer)getShowFolderState(); - data["permissions"] = (LLSD::Integer)getFilterPermissions(); - data["substring"] = (LLSD::String)getFilterSubString(); - data["sort_order"] = (LLSD::Integer)getSortOrder(); - data["since_logoff"] = (LLSD::Boolean)isSinceLogoff(); -} -void LLInventoryFilter::fromLLSD(LLSD& data) +LLInventoryFilter& LLInventoryFilter::operator=( const LLInventoryFilter& other ) { - if(data.has("filter_types")) - { - setFilterObjectTypes((U64)data["filter_types"].asInteger()); - } - - if(data.has("min_date") && data.has("max_date")) - { - setDateRange(data["min_date"].asInteger(), data["max_date"].asInteger()); - } - - if(data.has("hours_ago")) - { - setHoursAgo((U32)data["hours_ago"].asInteger()); - } - - if(data.has("show_folder_state")) - { - setShowFolderState((EFolderShow)data["show_folder_state"].asInteger()); - } + setFilterObjectTypes(other.getFilterObjectTypes()); + setDateRange(other.getMinDate(), other.getMaxDate()); + setHoursAgo(other.getHoursAgo()); + setShowFolderState(other.getShowFolderState()); + setFilterPermissions(other.getFilterPermissions()); + setFilterSubString(other.getFilterSubString()); + setDateRangeLastLogoff(other.isSinceLogoff()); + return *this; +} - if(data.has("permissions")) - { - setFilterPermissions((PermissionMask)data["permissions"].asInteger()); - } - if(data.has("substring")) - { - setFilterSubString(std::string(data["substring"].asString())); - } +void LLInventoryFilter::toParams(Params& params) const +{ + params.filter_ops.types = getFilterObjectTypes(); + params.filter_ops.category_types = getFilterCategoryTypes(); + params.filter_ops.wearable_types = getFilterWearableTypes(); + params.filter_ops.date_range.min_date = getMinDate(); + params.filter_ops.date_range.max_date = getMaxDate(); + params.filter_ops.hours_ago = getHoursAgo(); + params.filter_ops.show_folder_state = getShowFolderState(); + params.filter_ops.permissions = getFilterPermissions(); + params.substring = getFilterSubString(); + params.since_logoff = isSinceLogoff(); +} - if(data.has("sort_order")) +void LLInventoryFilter::fromParams(const Params& params) +{ + if (!params.validateBlock()) { - setSortOrder((U32)data["sort_order"].asInteger()); + return; } - if(data.has("since_logoff")) - { - setDateRangeLastLogoff((bool)data["since_logoff"].asBoolean()); - } + setFilterObjectTypes(params.filter_ops.types); + setFilterCategoryTypes(params.filter_ops.category_types); + setFilterWearableTypes(params.filter_ops.wearable_types); + setDateRange(params.filter_ops.date_range.min_date, params.filter_ops.date_range.max_date); + setHoursAgo(params.filter_ops.hours_ago); + setShowFolderState(params.filter_ops.show_folder_state); + setFilterPermissions(params.filter_ops.permissions); + setFilterSubString(params.substring); + setDateRangeLastLogoff(params.since_logoff); } U64 LLInventoryFilter::getFilterObjectTypes() const @@ -1057,11 +982,21 @@ U64 LLInventoryFilter::getFilterCategoryTypes() const return mFilterOps.mFilterCategoryTypes; } -BOOL LLInventoryFilter::hasFilterString() const +U64 LLInventoryFilter::getFilterWearableTypes() const +{ + return mFilterOps.mFilterWearableTypes; +} + +bool LLInventoryFilter::hasFilterString() const { return mFilterSubString.size() > 0; } +std::string::size_type LLInventoryFilter::getFilterStringSize() const +{ + return mFilterSubString.size(); +} + PermissionMask LLInventoryFilter::getFilterPermissions() const { return mFilterOps.mPermissions; @@ -1088,14 +1023,6 @@ LLInventoryFilter::EFolderShow LLInventoryFilter::getShowFolderState() const { return mFilterOps.mShowFolderState; } -U32 LLInventoryFilter::getSortOrder() const -{ - return mOrder; -} -const std::string& LLInventoryFilter::getName() const -{ - return mName; -} void LLInventoryFilter::setFilterCount(S32 count) { @@ -1113,15 +1040,15 @@ void LLInventoryFilter::decrementFilterCount() S32 LLInventoryFilter::getCurrentGeneration() const { - return mFilterGeneration; + return mCurrentGeneration; } -S32 LLInventoryFilter::getMinRequiredGeneration() const +S32 LLInventoryFilter::getFirstSuccessGeneration() const { - return mMinRequiredGeneration; + return mFirstSuccessGeneration; } -S32 LLInventoryFilter::getMustPassGeneration() const +S32 LLInventoryFilter::getFirstRequiredGeneration() const { - return mMustPassGeneration; + return mFirstRequiredGeneration; } void LLInventoryFilter::setEmptyLookupMessage(const std::string& message) @@ -1129,9 +1056,12 @@ void LLInventoryFilter::setEmptyLookupMessage(const std::string& message) mEmptyLookupMessage = message; } -const std::string& LLInventoryFilter::getEmptyLookupMessage() const +std::string LLInventoryFilter::getEmptyLookupMessage() const { - return mEmptyLookupMessage; + LLStringUtil::format_map_t args; + args["[SEARCH_TERM]"] = LLURI::escape(getFilterSubStringOrig()); + + return LLTrans::getString(mEmptyLookupMessage, args); } @@ -1141,3 +1071,27 @@ bool LLInventoryFilter::areDateLimitsSet() || mFilterOps.mMaxDate != time_max() || mFilterOps.mHoursAgo != 0; } + +bool LLInventoryFilter::showAllResults() const +{ + return hasFilterString(); +} + + + +bool LLInventoryFilter::FilterOps::DateRange::validateBlock( bool emit_errors /*= true*/ ) const +{ + bool valid = LLInitParam::Block<DateRange>::validateBlock(emit_errors); + if (valid) + { + if (max_date() < min_date()) + { + if (emit_errors) + { + llwarns << "max_date should be greater or equal to min_date" << llendl; + } + valid = false; + } + } + return valid; +} diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h index 9e600c036f..4912b5ca91 100644 --- a/indra/newview/llinventoryfilter.h +++ b/indra/newview/llinventoryfilter.h @@ -29,12 +29,13 @@ #include "llinventorytype.h" #include "llpermissionsflags.h" +#include "llfolderviewmodel.h" class LLFolderViewItem; class LLFolderViewFolder; class LLInventoryItem; -class LLInventoryFilter +class LLInventoryFilter : public LLFolderViewFilter { public: enum EFolderShow @@ -44,14 +45,6 @@ public: SHOW_NO_FOLDERS }; - enum EFilterBehavior - { - FILTER_NONE, // nothing to do, already filtered - FILTER_RESTART, // restart filtering from scratch - FILTER_LESS_RESTRICTIVE, // existing filtered items will certainly pass this filter - FILTER_MORE_RESTRICTIVE // if you didn't pass the previous filter, you definitely won't pass this one - }; - enum EFilterType { FILTERTYPE_NONE = 0, FILTERTYPE_OBJECT = 0x1 << 0, // normal default search-by-object-type @@ -59,7 +52,7 @@ public: FILTERTYPE_UUID = 0x1 << 2, // find the object with UUID and any links to it FILTERTYPE_DATE = 0x1 << 3, // search by date range FILTERTYPE_WEARABLE = 0x1 << 4, // search by wearable type - FILTERTYPE_EMPTYFOLDERS = 0x1 << 5 // pass if folder is not a system folder to be hidden if empty + FILTERTYPE_EMPTYFOLDERS = 0x1 << 5 // pass if folder is not a system folder to be hidden if }; enum EFilterLink @@ -77,16 +70,92 @@ public: SO_SYSTEM_FOLDERS_TO_TOP = 0x1 << 2 // Force system folders to be on top }; - LLInventoryFilter(const std::string& name); - virtual ~LLInventoryFilter(); + struct FilterOps + { + struct DateRange : public LLInitParam::Block<DateRange> + { + Optional<time_t> min_date, + max_date; + + DateRange() + : min_date("min_date", time_min()), + max_date("max_date", time_max()) + {} + + bool validateBlock(bool emit_errors = true) const; + }; + + struct Params : public LLInitParam::Block<Params> + { + Optional<U32> types; + Optional<U64> object_types, + wearable_types, + category_types; + Optional<EFilterLink> links; + Optional<LLUUID> uuid; + Optional<DateRange> date_range; + Optional<S32> hours_ago; + Optional<EFolderShow> show_folder_state; + Optional<PermissionMask> permissions; + + Params() + : types("filter_types", FILTERTYPE_OBJECT), + object_types("object_types", 0xffffFFFFffffFFFFULL), + wearable_types("wearable_types", 0xffffFFFFffffFFFFULL), + category_types("category_types", 0xffffFFFFffffFFFFULL), + links("links", FILTERLINK_INCLUDE_LINKS), + uuid("uuid"), + date_range("date_range"), + hours_ago("hours_ago", 0), + show_folder_state("show_folder_state", SHOW_NON_EMPTY_FOLDERS), + permissions("permissions", PERM_NONE) + {} + }; + + FilterOps(const Params& = Params()); + + U32 mFilterTypes; + U64 mFilterObjectTypes, // For _OBJECT + mFilterWearableTypes, + mFilterLinks, + mFilterCategoryTypes; // For _CATEGORY + LLUUID mFilterUUID; // for UUID + + time_t mMinDate, + mMaxDate; + U32 mHoursAgo; + + EFolderShow mShowFolderState; + PermissionMask mPermissions; + }; + + struct Params : public LLInitParam::Block<Params> + { + Optional<std::string> name; + Optional<FilterOps::Params> filter_ops; + Optional<std::string> substring; + Optional<bool> since_logoff; + + Params() + : name("name"), + filter_ops(""), + substring("substring"), + since_logoff("since_logoff") + {} + }; + + LLInventoryFilter(const Params& p = Params()); + LLInventoryFilter(const LLInventoryFilter& other) { *this = other; } + virtual ~LLInventoryFilter() {} // +-------------------------------------------------------------------+ // + Parameters // +-------------------------------------------------------------------+ - void setFilterObjectTypes(U64 types); U64 getFilterObjectTypes() const; U64 getFilterCategoryTypes() const; - BOOL isFilterObjectTypesWith(LLInventoryType::EType t) const; + U64 getFilterWearableTypes() const; + bool isFilterObjectTypesWith(LLInventoryType::EType t) const; + void setFilterObjectTypes(U64 types); void setFilterCategoryTypes(U64 types); void setFilterUUID(const LLUUID &object_id); void setFilterWearableTypes(U64 types); @@ -96,7 +165,7 @@ public: void setFilterSubString(const std::string& string); const std::string& getFilterSubString(BOOL trim = FALSE) const; const std::string& getFilterSubStringOrig() const { return mFilterSubStringOrig; } - BOOL hasFilterString() const; + bool hasFilterString() const; void setFilterPermissions(PermissionMask perms); PermissionMask getFilterPermissions() const; @@ -115,43 +184,35 @@ public: // +-------------------------------------------------------------------+ // + Execution And Results // +-------------------------------------------------------------------+ - BOOL check(const LLFolderViewItem* item); + bool check(const LLFolderViewModelItem* listener); bool check(const LLInventoryItem* item); - bool checkFolder(const LLFolderViewFolder* folder) const; + bool checkFolder(const LLFolderViewModelItem* listener) const; bool checkFolder(const LLUUID& folder_id) const; - BOOL checkAgainstFilterType(const LLFolderViewItem* item) const; - bool checkAgainstFilterType(const LLInventoryItem* item) const; - BOOL checkAgainstPermissions(const LLFolderViewItem* item) const; - bool checkAgainstPermissions(const LLInventoryItem* item) const; - BOOL checkAgainstFilterLinks(const LLFolderViewItem* item) const; - bool checkAgainstClipboard(const LLUUID& object_id) const; - std::string::size_type getStringMatchOffset() const; + bool showAllResults() const; + std::string::size_type getStringMatchOffset(LLFolderViewModelItem* item) const; + std::string::size_type getFilterStringSize() const; // +-------------------------------------------------------------------+ // + Presentation // +-------------------------------------------------------------------+ void setShowFolderState( EFolderShow state); EFolderShow getShowFolderState() const; - void setSortOrder(U32 order); - U32 getSortOrder() const; - void setEmptyLookupMessage(const std::string& message); - const std::string& getEmptyLookupMessage() const; + std::string getEmptyLookupMessage() const; // +-------------------------------------------------------------------+ // + Status // +-------------------------------------------------------------------+ - BOOL isActive() const; - BOOL isModified() const; - BOOL isModifiedAndClear(); - BOOL isSinceLogoff() const; + bool isActive() const; + bool isModified() const; + bool isSinceLogoff() const; void clearModified(); - const std::string& getName() const; + const std::string& getName() const { return mName; } const std::string& getFilterText(); //RN: this is public to allow system to externally force a global refilter - void setModified(EFilterBehavior behavior = FILTER_RESTART); + void setModified(EFilterModified behavior = FILTER_RESTART); // +-------------------------------------------------------------------+ // + Count @@ -163,8 +224,8 @@ public: // +-------------------------------------------------------------------+ // + Default // +-------------------------------------------------------------------+ - BOOL isDefault() const; - BOOL isNotDefault() const; + bool isDefault() const; + bool isNotDefault() const; void markDefault(); void resetDefault(); @@ -172,57 +233,42 @@ public: // + Generation // +-------------------------------------------------------------------+ S32 getCurrentGeneration() const; - S32 getMinRequiredGeneration() const; - S32 getMustPassGeneration() const; + S32 getFirstSuccessGeneration() const; + S32 getFirstRequiredGeneration() const; + // +-------------------------------------------------------------------+ // + Conversion // +-------------------------------------------------------------------+ - void toLLSD(LLSD& data) const; - void fromLLSD(LLSD& data); + void toParams(Params& params) const; + void fromParams(const Params& p); + + LLInventoryFilter& operator =(const LLInventoryFilter& other); private: bool areDateLimitsSet(); - - struct FilterOps - { - FilterOps(); - U32 mFilterTypes; - - U64 mFilterObjectTypes; // For _OBJECT - U64 mFilterWearableTypes; - U64 mFilterCategoryTypes; // For _CATEGORY - LLUUID mFilterUUID; // for UUID - - time_t mMinDate; - time_t mMaxDate; - U32 mHoursAgo; - EFolderShow mShowFolderState; - PermissionMask mPermissions; - U64 mFilterLinks; - }; - - U32 mOrder; - U32 mLastLogoff; + bool checkAgainstFilterType(const class LLFolderViewModelItemInventory* listener) const; + bool checkAgainstFilterType(const LLInventoryItem* item) const; + bool checkAgainstPermissions(const class LLFolderViewModelItemInventory* listener) const; + bool checkAgainstPermissions(const LLInventoryItem* item) const; + bool checkAgainstFilterLinks(const class LLFolderViewModelItemInventory* listener) const; + bool checkAgainstClipboard(const LLUUID& object_id) const; FilterOps mFilterOps; FilterOps mDefaultFilterOps; - std::string::size_type mSubStringMatchOffset; std::string mFilterSubString; std::string mFilterSubStringOrig; const std::string mName; - S32 mFilterGeneration; - S32 mMustPassGeneration; - S32 mMinRequiredGeneration; + S32 mCurrentGeneration; + S32 mFirstRequiredGeneration; + S32 mFirstSuccessGeneration; S32 mNextFilterGeneration; S32 mFilterCount; - EFilterBehavior mFilterBehavior; + EFilterModified mFilterModified; - BOOL mModified; - BOOL mNeedTextRebuild; std::string mFilterText; std::string mEmptyLookupMessage; }; diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index ab5b082915..07f3dd8ffb 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -45,7 +45,7 @@ // newview includes #include "llappearancemgr.h" #include "llappviewer.h" -//#include "llfirstuse.h" +#include "llclipboard.h" #include "llfloaterinventory.h" #include "llfloatersidepanelcontainer.h" #include "llfocusmgr.h" @@ -74,8 +74,10 @@ #include "llsidepanelinventory.h" #include "lltabcontainer.h" #include "lltooldraganddrop.h" +#include "lltrans.h" #include "lluictrlfactory.h" #include "llviewermessage.h" +#include "llviewerfoldertype.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" #include "llviewerwindow.h" @@ -948,7 +950,7 @@ void LLSaveFolderState::setApply(BOOL apply) void LLSaveFolderState::doFolder(LLFolderViewFolder* folder) { LLMemType mt(LLMemType::MTYPE_INVENTORY_DO_FOLDER); - LLInvFVBridge* bridge = (LLInvFVBridge*)folder->getListener(); + LLInvFVBridge* bridge = (LLInvFVBridge*)folder->getViewModelItem(); if(!bridge) return; if(mApply) @@ -983,7 +985,7 @@ void LLSaveFolderState::doFolder(LLFolderViewFolder* folder) void LLOpenFilteredFolders::doItem(LLFolderViewItem *item) { - if (item->getFiltered()) + if (item->passedFilter()) { item->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP); } @@ -991,12 +993,12 @@ void LLOpenFilteredFolders::doItem(LLFolderViewItem *item) void LLOpenFilteredFolders::doFolder(LLFolderViewFolder* folder) { - if (folder->getFiltered() && folder->getParentFolder()) + if (folder->LLFolderViewItem::passedFilter() && folder->getParentFolder()) { folder->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP); } // if this folder didn't pass the filter, and none of its descendants did - else if (!folder->getFiltered() && !folder->hasFilteredDescendants()) + else if (!folder->getViewModelItem()->passedFilter() && !folder->getViewModelItem()->descendantsPassedFilter()) { folder->setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_NO); } @@ -1004,7 +1006,7 @@ void LLOpenFilteredFolders::doFolder(LLFolderViewFolder* folder) void LLSelectFirstFilteredItem::doItem(LLFolderViewItem *item) { - if (item->getFiltered() && !mItemSelected) + if (item->passedFilter() && !mItemSelected) { item->getRoot()->setSelection(item, FALSE, FALSE); if (item->getParentFolder()) @@ -1017,7 +1019,7 @@ void LLSelectFirstFilteredItem::doItem(LLFolderViewItem *item) void LLSelectFirstFilteredItem::doFolder(LLFolderViewFolder* folder) { - if (folder->getFiltered() && !mItemSelected) + if (folder->LLFolderViewItem::passedFilter() && !mItemSelected) { folder->getRoot()->setSelection(folder, FALSE, FALSE); if (folder->getParentFolder()) @@ -1044,3 +1046,87 @@ void LLOpenFoldersWithSelection::doFolder(LLFolderViewFolder* folder) } } +void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root, const std::string& action) +{ + if ("rename" == action) + { + root->startRenamingSelectedItem(); + return; + } + if ("delete" == action) + { + LLSD args; + args["QUESTION"] = LLTrans::getString(root->getNumSelectedItems() > 1 ? "DeleteItems" : "DeleteItem"); + LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&LLInventoryAction::onItemsRemovalConfirmation, _1, _2, root)); + return; + } + if (("copy" == action) || ("cut" == action)) + { + // Clear the clipboard before we start adding things on it + LLClipboard::instance().reset(); + } + + static const std::string change_folder_string = "change_folder_type_"; + if (action.length() > change_folder_string.length() && + (action.compare(0,change_folder_string.length(),"change_folder_type_") == 0)) + { + LLFolderType::EType new_folder_type = LLViewerFolderType::lookupTypeFromXUIName(action.substr(change_folder_string.length())); + LLFolderViewModelItemInventory* inventory_item = static_cast<LLFolderViewModelItemInventory*>(root->getViewModelItem()); + LLViewerInventoryCategory *cat = model->getCategory(inventory_item->getUUID()); + if (!cat) return; + cat->changeType(new_folder_type); + return; + } + + + std::set<LLFolderViewItem*> selected_items = root->getSelectionList(); + + LLMultiPreview* multi_previewp = NULL; + LLMultiProperties* multi_propertiesp = NULL; + + if (("task_open" == action || "open" == action) && selected_items.size() > 1) + { + multi_previewp = new LLMultiPreview(); + gFloaterView->addChild(multi_previewp); + + LLFloater::setFloaterHost(multi_previewp); + + } + else if (("task_properties" == action || "properties" == action) && selected_items.size() > 1) + { + multi_propertiesp = new LLMultiProperties(); + gFloaterView->addChild(multi_propertiesp); + + LLFloater::setFloaterHost(multi_propertiesp); + } + + std::set<LLFolderViewItem*>::iterator set_iter; + + for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) + { + LLFolderViewItem* folder_item = *set_iter; + if(!folder_item) continue; + LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem(); + if(!bridge) continue; + bridge->performAction(model, action); + } + + LLFloater::setFloaterHost(NULL); + if (multi_previewp) + { + multi_previewp->openFloater(LLSD()); + } + else if (multi_propertiesp) + { + multi_propertiesp->openFloater(LLSD()); + } +} + +void LLInventoryAction::onItemsRemovalConfirmation( const LLSD& notification, const LLSD& response, LLFolderView* root ) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) + { + root->removeSelectedItems(); + } +} diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 5cf9c528b0..d8d3d9bbbb 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -418,21 +418,6 @@ public: class LLFolderViewItem; class LLFolderViewFolder; -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLFolderViewFunctor -// -// Simple abstract base class for applying a functor to folders and -// items in a folder view hierarchy. This is suboptimal for algorithms -// that only work folders or only work on items, but I'll worry about -// that later when it's determined to be too slow. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLFolderViewFunctor -{ -public: - virtual ~LLFolderViewFunctor() {} - virtual void doFolder(LLFolderViewFolder* folder) = 0; - virtual void doItem(LLFolderViewItem* item) = 0; -}; class LLInventoryState { @@ -442,49 +427,13 @@ public: static LLUUID sWearNewClothingTransactionID; // wear all clothing in this transaction }; -class LLSelectFirstFilteredItem : public LLFolderViewFunctor +struct LLInventoryAction { -public: - LLSelectFirstFilteredItem() : mItemSelected(FALSE) {} - virtual ~LLSelectFirstFilteredItem() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); - BOOL wasItemSelected() { return mItemSelected; } -protected: - BOOL mItemSelected; -}; + static void doToSelected(class LLInventoryModel* model, class LLFolderView* root, const std::string& action); -class LLOpenFilteredFolders : public LLFolderViewFunctor -{ -public: - LLOpenFilteredFolders() {} - virtual ~LLOpenFilteredFolders() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); + static void onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response, LLFolderView* root); }; -class LLSaveFolderState : public LLFolderViewFunctor -{ -public: - LLSaveFolderState() : mApply(FALSE) {} - virtual ~LLSaveFolderState() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item) {} - void setApply(BOOL apply); - void clearOpenFolders() { mOpenFolders.clear(); } -protected: - std::set<LLUUID> mOpenFolders; - BOOL mApply; -}; - -class LLOpenFoldersWithSelection : public LLFolderViewFunctor -{ -public: - LLOpenFoldersWithSelection() {} - virtual ~LLOpenFoldersWithSelection() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); -}; #endif // LL_LLINVENTORYFUNCTIONS_H diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 6e23d7c701..0673970d89 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -373,13 +373,12 @@ void LLInventoryModel::unlockDirectDescendentArrays(const LLUUID& cat_id) // specifies 'type' as what it defaults to containing. The category is // not necessarily only for that type. *NOTE: This will create a new // inventory category on the fly if one does not exist. -const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType preferred_type, - bool create_folder, - bool find_in_library) +const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType preferred_type, bool create_folder/*, + bool find_in_library*/) { LLUUID rv = LLUUID::null; - const LLUUID &root_id = (find_in_library) ? gInventory.getLibraryRootFolderID() : gInventory.getRootFolderID(); + const LLUUID &root_id = /*(find_in_library) ? gInventory.getLibraryRootFolderID() :*/ gInventory.getRootFolderID(); if(LLFolderType::FT_ROOT_INVENTORY == preferred_type) { rv = root_id; @@ -402,7 +401,44 @@ const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType prefe } } - if(rv.isNull() && isInventoryUsable() && (create_folder && !find_in_library)) + if(rv.isNull() && isInventoryUsable() && (create_folder && true/*!find_in_library*/)) + { + if(root_id.notNull()) + { + return createNewCategory(root_id, preferred_type, LLStringUtil::null); + } + } + return rv; +} + +const LLUUID LLInventoryModel::findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type, bool create_folder) +{ + LLUUID rv = LLUUID::null; + + const LLUUID &root_id = gInventory.getLibraryRootFolderID(); + if(LLFolderType::FT_ROOT_INVENTORY == preferred_type) + { + rv = root_id; + } + else if (root_id.notNull()) + { + cat_array_t* cats = NULL; + cats = get_ptr_in_map(mParentChildCategoryTree, root_id); + if(cats) + { + S32 count = cats->count(); + for(S32 i = 0; i < count; ++i) + { + if(cats->get(i)->getPreferredType() == preferred_type) + { + rv = cats->get(i)->getUUID(); + break; + } + } + } + } + + if(rv.isNull() && isInventoryUsable() && (create_folder && true/*!find_in_library*/)) { if(root_id.notNull()) { @@ -1245,14 +1281,33 @@ void LLInventoryModel::purgeDescendentsOf(const LLUUID& id) items, INCLUDE_TRASH); S32 count = items.count(); + + item_map_t::iterator item_map_end = mItemMap.end(); + cat_map_t::iterator cat_map_end = mCategoryMap.end(); + LLUUID uu_id; + for(S32 i = 0; i < count; ++i) { - deleteObject(items.get(i)->getUUID()); + uu_id = items.get(i)->getUUID(); + + // This check prevents the deletion of a previously deleted item. + // This is necessary because deletion is not done in a hierarchical + // order. The current item may have been already deleted as a child + // of its deleted parent. + if (mItemMap.find(uu_id) != item_map_end) + { + deleteObject(uu_id); + } } + count = categories.count(); for(S32 i = 0; i < count; ++i) { - deleteObject(categories.get(i)->getUUID()); + uu_id = categories.get(i)->getUUID(); + if (mCategoryMap.find(uu_id) != cat_map_end) + { + deleteObject(uu_id); + } } } } @@ -3239,68 +3294,7 @@ void LLInventoryModel::updateItemsOrder(LLInventoryModel::item_array_t& items, c } } -//* @param[in] items vector of items in order to be saved. -void LLInventoryModel::saveItemsOrder(const LLInventoryModel::item_array_t& items) -{ - int sortField = 0; - - // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field - for (item_array_t::const_iterator i = items.begin(); i != items.end(); ++i) - { - LLViewerInventoryItem* item = *i; - - item->setSortField(++sortField); - item->setComplete(TRUE); - item->updateServer(FALSE); - - updateItem(item); - - // Tell the parent folder to refresh its sort order. - addChangedMask(LLInventoryObserver::SORT, item->getParentUUID()); - } - - notifyObservers(); -} - -// See also LLInventorySort where landmarks in the Favorites folder are sorted. -class LLViewerInventoryItemSort -{ -public: - bool operator()(const LLPointer<LLViewerInventoryItem>& a, const LLPointer<LLViewerInventoryItem>& b) - { - return a->getSortField() < b->getSortField(); - } -}; -/** - * Sorts passed items by LLViewerInventoryItem sort field. - * - * @param[in, out] items - array of items, not sorted. - */ -static void rearrange_item_order_by_sort_field(LLInventoryModel::item_array_t& items) -{ - static LLViewerInventoryItemSort sort_functor; - std::sort(items.begin(), items.end(), sort_functor); -} - -// * @param source_item_id - LLUUID of the source item to be moved into new position -// * @param target_item_id - LLUUID of the target item before which source item should be placed. -void LLInventoryModel::rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id) -{ - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - LLIsType is_type(LLAssetType::AT_LANDMARK); - LLUUID favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); - gInventory.collectDescendentsIf(favorites_id, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type); - - // ensure items are sorted properly before changing order. EXT-3498 - rearrange_item_order_by_sort_field(items); - - // update order - updateItemsOrder(items, source_item_id, target_item_id); - - saveItemsOrder(items); -} //---------------------------------------------------------------------------- diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index 8382e875b4..503de627a0 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -231,11 +231,12 @@ public: // Returns the uuid of the category that specifies 'type' as what it // defaults to containing. The category is not necessarily only for that type. // NOTE: If create_folder is true, this will create a new inventory category - // on the fly if one does not exist. *NOTE: if find_in_library is true it - // will search in the user's library folder instead of "My Inventory" + // on the fly if one does not exist. const LLUUID findCategoryUUIDForType(LLFolderType::EType preferred_type, - bool create_folder = true, - bool find_in_library = false); + bool create_folder = true); + // will search in the user's library folder instead of "My Inventory" + const LLUUID findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type, + bool create_folder = true); // Get whatever special folder this object is a child of, if any. const LLViewerInventoryCategory *getFirstNondefaultParent(const LLUUID& obj_id) const; @@ -362,15 +363,6 @@ public: // Returns end() of the vector if not found. static LLInventoryModel::item_array_t::iterator findItemIterByUUID(LLInventoryModel::item_array_t& items, const LLUUID& id); - // Saves current order of the passed items using inventory item sort field. - // Resets 'items' sort fields and saves them on server. - // Is used to save order for Favorites folder. - void saveItemsOrder(const LLInventoryModel::item_array_t& items); - - // Rearranges Landmarks inside Favorites folder. - // Moves source landmark before target one. - void rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id); - //-------------------------------------------------------------------- // Creation //-------------------------------------------------------------------- diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 71dd963f28..7fba52d47f 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -38,11 +38,13 @@ #include "llfloaterreg.h" #include "llfloatersidepanelcontainer.h" #include "llfolderview.h" +#include "llfolderviewitem.h" #include "llimfloater.h" #include "llimview.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" #include "llinventorymodelbackgroundfetch.h" +#include "llpreview.h" #include "llsidepanelinventory.h" #include "llviewerattachmenu.h" #include "llviewerfoldertype.h" @@ -53,8 +55,7 @@ static LLDefaultChildRegistry::Register<LLInventoryPanel> r("inventory_panel"); const std::string LLInventoryPanel::DEFAULT_SORT_ORDER = std::string("InventorySortOrder"); const std::string LLInventoryPanel::RECENTITEMS_SORT_ORDER = std::string("RecentItemsSortOrder"); const std::string LLInventoryPanel::INHERIT_SORT_ORDER = std::string(""); -static const LLInventoryFVBridgeBuilder INVENTORY_BRIDGE_BUILDER; - +static const LLInventoryFolderViewModelBuilder INVENTORY_BRIDGE_BUILDER; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLInventoryPanelObserver @@ -134,13 +135,12 @@ LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) : mAllowMultiSelect(p.allow_multi_select), mShowItemLinkOverlays(p.show_item_link_overlays), mShowEmptyMessage(p.show_empty_message), - mShowLoadStatus(p.show_load_status), mViewsInitialized(false), mInvFVBridgeBuilder(NULL) { mInvFVBridgeBuilder = &INVENTORY_BRIDGE_BUILDER; - // contex menu callbacks + // context menu callbacks mCommitCallbackRegistrar.add("Inventory.DoToSelected", boost::bind(&LLInventoryPanel::doToSelected, this, _2)); mCommitCallbackRegistrar.add("Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH)); mCommitCallbackRegistrar.add("Inventory.EmptyLostAndFound", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND)); @@ -151,69 +151,67 @@ LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) : } -void LLInventoryPanel::buildFolderView(const LLInventoryPanel::Params& params) -{ - // Determine the root folder in case specified, and - // build the views starting with that folder. - - std::string start_folder_name(params.start_folder()); - - const LLFolderType::EType preferred_type = LLViewerFolderType::lookupTypeFromNewCategoryName(start_folder_name); - - LLUUID root_id; - if ("LIBRARY" == params.start_folder()) - { - root_id = gInventory.getLibraryRootFolderID(); - } - else - { - root_id = (preferred_type != LLFolderType::FT_NONE) - ? gInventory.findCategoryUUIDForType(preferred_type, false, false) - : LLUUID::null; - } - - if ((root_id == LLUUID::null) && !start_folder_name.empty()) +void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) +{ + // save off copy of params + mParams = params; + // Clear up the root view + // Note: This needs to be done *before* we build the new folder view + LLUUID root_id = getRootFolderID(); + if (mFolderRoot) { - llwarns << "No category found that matches start_folder: " << start_folder_name << llendl; - root_id = LLUUID::generateNewID(); + removeItemID(root_id); + mFolderRoot->destroyView(); + mFolderRoot = NULL; } - - LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(LLAssetType::AT_CATEGORY, - LLAssetType::AT_CATEGORY, - LLInventoryType::IT_CATEGORY, - this, - NULL, - root_id); - - mFolderRoot = createFolderView(new_listener, params.use_label_suffix()); -} -void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) -{ LLMemType mt(LLMemType::MTYPE_INVENTORY_POST_BUILD); - - mCommitCallbackRegistrar.pushScope(); // registered as a widget; need to push callback scope ourselves - buildFolderView(params); - + mCommitCallbackRegistrar.pushScope(); // registered as a widget; need to push callback scope ourselves + { + // Determine the root folder in case specified, and + // build the views starting with that folder. + + + LLFolderView::Params p(mParams.folder_view); + p.name = getName(); + p.title = getLabel(); + p.rect = LLRect(0, 0, getRect().getWidth(), 0); + p.parent_panel = this; + p.tool_tip = p.name; + p.listener = mInvFVBridgeBuilder->createBridge( LLAssetType::AT_CATEGORY, + LLAssetType::AT_CATEGORY, + LLInventoryType::IT_CATEGORY, + this, + &mInventoryViewModel, + NULL, + root_id); + p.view_model = &mInventoryViewModel; + p.use_label_suffix = mParams.use_label_suffix; + p.allow_multiselect = mAllowMultiSelect; + p.show_empty_message = mShowEmptyMessage; + p.show_item_link_overlays = mShowItemLinkOverlays; + p.root = NULL; + + mFolderRoot = LLUICtrlFactory::create<LLFolderView>(p); + + addItemID(root_id, mFolderRoot); + } mCommitCallbackRegistrar.popScope(); - mFolderRoot->setCallbackRegistrar(&mCommitCallbackRegistrar); // Scroller - { - LLRect scroller_view_rect = getRect(); - scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); - LLScrollContainer::Params scroller_params(params.scroll()); - scroller_params.rect(scroller_view_rect); - mScroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params); - addChild(mScroller); - mScroller->addChild(mFolderRoot); - mFolderRoot->setScrollContainer(mScroller); - mFolderRoot->setFollowsAll(); - mFolderRoot->addChild(mFolderRoot->mStatusTextBox); - } + LLRect scroller_view_rect = getRect(); + scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); + LLScrollContainer::Params scroller_params(mParams.scroll()); + scroller_params.rect(scroller_view_rect); + mScroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params); + addChild(mScroller); + mScroller->addChild(mFolderRoot); + mFolderRoot->setScrollContainer(mScroller); + mFolderRoot->setFollowsAll(); + mFolderRoot->addChild(mFolderRoot->mStatusTextBox); // Set up the callbacks from the inventory we're viewing, and then build everything. mInventoryObserver = new LLInventoryPanelObserver(this); @@ -228,6 +226,7 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) { initializeViews(); } + gIdleCallbacks.addFunction(onIdle, (void*)this); if (mSortOrderSetting != INHERIT_SORT_ORDER) @@ -240,31 +239,30 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) } // hide inbox - getFilter()->setFilterCategoryTypes(getFilter()->getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_INBOX)); - getFilter()->setFilterCategoryTypes(getFilter()->getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_OUTBOX)); + getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_INBOX)); + getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_OUTBOX)); // set the filter for the empty folder if the debug setting is on if (gSavedSettings.getBOOL("DebugHideEmptySystemFolders")) { - getFilter()->setFilterEmptySystemFolders(); + getFilter().setFilterEmptySystemFolders(); } // keep track of the clipboard state so that we avoid filtering too much mClipboardState = LLClipboard::instance().getGeneration(); // Initialize base class params. - LLPanel::initFromParams(params); + LLPanel::initFromParams(mParams); } LLInventoryPanel::~LLInventoryPanel() { - if (mFolderRoot) + gIdleCallbacks.deleteFunction(idle, this); + + U32 sort_order = getFolderViewModel()->getSorter().getSortOrder(); + if (mSortOrderSetting != INHERIT_SORT_ORDER) { - U32 sort_order = mFolderRoot->getSortOrder(); - if (mSortOrderSetting != INHERIT_SORT_ORDER) - { - gSavedSettings.setU32(mSortOrderSetting, sort_order); - } + gSavedSettings.setU32(mSortOrderSetting, sort_order); } gIdleCallbacks.deleteFunction(onIdle, this); @@ -281,82 +279,68 @@ LLInventoryPanel::~LLInventoryPanel() void LLInventoryPanel::draw() { // Select the desired item (in case it wasn't loaded when the selection was requested) - mFolderRoot->updateSelection(); - - // Nudge the filter if the clipboard state changed - if (mClipboardState != LLClipboard::instance().getGeneration()) - { - mClipboardState = LLClipboard::instance().getGeneration(); - getFilter()->setModified(LLClipboard::instance().isCutMode() ? LLInventoryFilter::FILTER_MORE_RESTRICTIVE : LLInventoryFilter::FILTER_LESS_RESTRICTIVE); - } + updateSelection(); LLPanel::draw(); } -LLInventoryFilter* LLInventoryPanel::getFilter() +const LLInventoryFilter& LLInventoryPanel::getFilter() const { - if (mFolderRoot) - { - return mFolderRoot->getFilter(); - } - return NULL; + return getFolderViewModel()->getFilter(); } -const LLInventoryFilter* LLInventoryPanel::getFilter() const +LLInventoryFilter& LLInventoryPanel::getFilter() { - if (mFolderRoot) - { - return mFolderRoot->getFilter(); - } - return NULL; + return getFolderViewModel()->getFilter(); } void LLInventoryPanel::setFilterTypes(U64 types, LLInventoryFilter::EFilterType filter_type) { if (filter_type == LLInventoryFilter::FILTERTYPE_OBJECT) - getFilter()->setFilterObjectTypes(types); + getFilter().setFilterObjectTypes(types); if (filter_type == LLInventoryFilter::FILTERTYPE_CATEGORY) - getFilter()->setFilterCategoryTypes(types); + getFilter().setFilterCategoryTypes(types); } U32 LLInventoryPanel::getFilterObjectTypes() const { - return mFolderRoot->getFilterObjectTypes(); + return getFilter().getFilterObjectTypes(); } U32 LLInventoryPanel::getFilterPermMask() const { - return mFolderRoot->getFilterPermissions(); + return getFilter().getFilterPermissions(); } void LLInventoryPanel::setFilterPermMask(PermissionMask filter_perm_mask) { - getFilter()->setFilterPermissions(filter_perm_mask); + getFilter().setFilterPermissions(filter_perm_mask); } void LLInventoryPanel::setFilterWearableTypes(U64 types) { - getFilter()->setFilterWearableTypes(types); + getFilter().setFilterWearableTypes(types); } void LLInventoryPanel::setFilterSubString(const std::string& string) { - getFilter()->setFilterSubString(string); + getFilter().setFilterSubString(string); } const std::string LLInventoryPanel::getFilterSubString() { - return mFolderRoot->getFilterSubString(); + return getFilter().getFilterSubString(); } void LLInventoryPanel::setSortOrder(U32 order) { - getFilter()->setSortOrder(order); - if (getFilter()->isModified()) + LLInventorySort sorter(order); + if (order != getFolderViewModel()->getSorter().getSortOrder()) { - mFolderRoot->setSortOrder(order); + getFolderViewModel()->setSorter(sorter); + mFolderRoot->arrangeAll(); // try to keep selection onscreen, even if it wasn't to start with mFolderRoot->scrollToShowSelection(); } @@ -364,37 +348,32 @@ void LLInventoryPanel::setSortOrder(U32 order) U32 LLInventoryPanel::getSortOrder() const { - return mFolderRoot->getSortOrder(); -} - -void LLInventoryPanel::requestSort() -{ - mFolderRoot->requestSort(); + return getFolderViewModel()->getSorter().getSortOrder(); } void LLInventoryPanel::setSinceLogoff(BOOL sl) { - getFilter()->setDateRangeLastLogoff(sl); + getFilter().setDateRangeLastLogoff(sl); } void LLInventoryPanel::setHoursAgo(U32 hours) { - getFilter()->setHoursAgo(hours); + getFilter().setHoursAgo(hours); } void LLInventoryPanel::setFilterLinks(U64 filter_links) { - getFilter()->setFilterLinks(filter_links); + getFilter().setFilterLinks(filter_links); } void LLInventoryPanel::setShowFolderState(LLInventoryFilter::EFolderShow show) { - getFilter()->setShowFolderState(show); + getFilter().setShowFolderState(show); } LLInventoryFilter::EFolderShow LLInventoryPanel::getShowFolderState() { - return getFilter()->getShowFolderState(); + return getFilter().getShowFolderState(); } void LLInventoryPanel::modelChanged(U32 mask) @@ -418,11 +397,20 @@ void LLInventoryPanel::modelChanged(U32 mask) { const LLUUID& item_id = (*items_iter); const LLInventoryObject* model_item = model->getObject(item_id); - LLFolderViewItem* view_item = mFolderRoot->getItemByID(item_id); + LLFolderViewItem* view_item = getItemByID(item_id); + LLFolderViewModelItemInventory* viewmodel_item = + static_cast<LLFolderViewModelItemInventory*>(view_item ? view_item->getViewModelItem() : NULL); // LLFolderViewFolder is derived from LLFolderViewItem so dynamic_cast from item // to folder is the fast way to get a folder without searching through folders tree. - LLFolderViewFolder* view_folder = dynamic_cast<LLFolderViewFolder*>(view_item); + LLFolderViewFolder* view_folder = NULL; + + // Check requires as this item might have already been deleted + // as a child of its deleted parent. + if (model_item && view_item) + { + view_folder = dynamic_cast<LLFolderViewFolder*>(view_item); + } ////////////////////////////// // LABEL Operation @@ -433,7 +421,7 @@ void LLInventoryPanel::modelChanged(U32 mask) if (view_item) { // Request refresh on this item (also flags for filtering) - LLInvFVBridge* bridge = (LLInvFVBridge*)view_item->getListener(); + LLInvFVBridge* bridge = (LLInvFVBridge*)view_item->getViewModelItem(); if(bridge) { // Clear the display name first, so it gets properly re-built during refresh() bridge->clearDisplayName(); @@ -449,11 +437,14 @@ void LLInventoryPanel::modelChanged(U32 mask) if (mask & LLInventoryObserver::REBUILD) { handled = true; - if (model_item && view_item) + if (model_item && view_item && viewmodel_item) { view_item->destroyView(); + removeItemID(viewmodel_item->getUUID()); } view_item = buildNewViews(item_id); + viewmodel_item = + static_cast<LLFolderViewModelItemInventory*>(view_item ? view_item->getViewModelItem() : NULL); view_folder = dynamic_cast<LLFolderViewFolder *>(view_item); } @@ -475,7 +466,7 @@ void LLInventoryPanel::modelChanged(U32 mask) { if (view_folder) { - view_folder->requestSort(); + view_folder->getViewModelItem()->requestSort(); } } @@ -510,20 +501,24 @@ void LLInventoryPanel::modelChanged(U32 mask) else if (model_item && view_item) { // Don't process the item if it is the root - if (view_item->getRoot() != view_item) + if (view_item->getParentFolder()) { - LLFolderViewFolder* new_parent = (LLFolderViewFolder*)mFolderRoot->getItemByID(model_item->getParentUUID()); + LLFolderViewFolder* new_parent = (LLFolderViewFolder*)getItemByID(model_item->getParentUUID()); // Item has been moved. if (view_item->getParentFolder() != new_parent) { if (new_parent != NULL) { // Item is to be moved and we found its new parent in the panel's directory, so move the item's UI. - view_item->getParentFolder()->extractItem(view_item); - view_item->addToFolder(new_parent, mFolderRoot); + view_item->addToFolder(new_parent); + addItemID(viewmodel_item->getUUID(), view_item); } else { + // Remove the item ID before destroying the view because the view-model-item gets + // destroyed when the view is destroyed + removeItemID(viewmodel_item->getUUID()); + // Item is to be moved outside the panel's directory (e.g. moved to trash for a panel that // doesn't include trash). Just remove the item's UI. view_item->destroyView(); @@ -535,9 +530,10 @@ void LLInventoryPanel::modelChanged(U32 mask) ////////////////////////////// // REMOVE Operation // This item has been removed from memory, but its associated UI element still exists. - else if (!model_item && view_item) + else if (!model_item && view_item && viewmodel_item) { // Remove the item's UI. + removeItemID(viewmodel_item->getUUID()); view_item->destroyView(); } } @@ -549,6 +545,43 @@ LLFolderView* LLInventoryPanel::getRootFolder() return mFolderRoot; } +LLUUID LLInventoryPanel::getRootFolderID() +{ + if (mFolderRoot && mFolderRoot->getViewModelItem()) + { + return static_cast<LLFolderViewModelItemInventory*>(mFolderRoot->getViewModelItem())->getUUID(); + + } + else + { + LLUUID root_id; + if (mParams.start_folder.id.isChosen()) + { + root_id = mParams.start_folder.id; + } + else + { + const LLFolderType::EType preferred_type = mParams.start_folder.type.isChosen() + ? mParams.start_folder.type + : LLViewerFolderType::lookupTypeFromNewCategoryName(mParams.start_folder.name); + + if ("LIBRARY" == mParams.start_folder.name()) + { + root_id = gInventory.getLibraryRootFolderID(); + } + else if (preferred_type != LLFolderType::FT_NONE) + { + root_id = gInventory.findCategoryUUIDForType(preferred_type, false); + if (root_id.isNull()) + { + llwarns << "Could not find folder of type " << preferred_type << llendl; + root_id.generateNewID(); + } + } + } + return root_id; + } +} // static void LLInventoryPanel::onIdle(void *userdata) @@ -568,16 +601,72 @@ void LLInventoryPanel::onIdle(void *userdata) } } -const LLUUID& LLInventoryPanel::getRootFolderID() const +struct DirtyFilterFunctor : public LLFolderViewFunctor +{ + /*virtual*/ void doFolder(LLFolderViewFolder* folder) + { + folder->getViewModelItem()->dirtyFilter(); + } + /*virtual*/ void doItem(LLFolderViewItem* item) + { + item->getViewModelItem()->dirtyFilter(); + } +}; + +void LLInventoryPanel::idle(void* user_data) { - return mFolderRoot->getListener()->getUUID(); + LLInventoryPanel* panel = (LLInventoryPanel*)user_data; + // Nudge the filter if the clipboard state changed + if (panel->mClipboardState != LLClipboard::instance().getGeneration()) + { + panel->mClipboardState = LLClipboard::instance().getGeneration(); + const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); + LLFolderViewFolder* trash_folder = panel->getFolderByID(trash_id); + if (trash_folder) + { + DirtyFilterFunctor dirtyFilterFunctor; + trash_folder->applyFunctorToChildren(dirtyFilterFunctor); + } + + } + + panel->mFolderRoot->update(); + // while dragging, update selection rendering to reflect single/multi drag status + if (LLToolDragAndDrop::getInstance()->hasMouseCapture()) + { + EAcceptance last_accept = LLToolDragAndDrop::getInstance()->getLastAccept(); + if (last_accept == ACCEPT_YES_SINGLE || last_accept == ACCEPT_YES_COPY_SINGLE) + { + panel->mFolderRoot->setShowSingleSelection(TRUE); + } + else + { + panel->mFolderRoot->setShowSingleSelection(FALSE); + } + } + else + { + panel->mFolderRoot->setShowSingleSelection(FALSE); + } } + void LLInventoryPanel::initializeViews() { if (!gInventory.isInventoryUsable()) return; - rebuildViewsFor(getRootFolderID()); + LLUUID root_id = getRootFolderID(); + if (root_id.notNull()) + { + buildNewViews(getRootFolderID()); + } + else + { + buildNewViews(gInventory.getRootFolderID()); + buildNewViews(gInventory.getLibraryRootFolderID()); + } + + gIdleCallbacks.addFunction(idle, this); mViewsInitialized = true; @@ -587,14 +676,14 @@ void LLInventoryPanel::initializeViews() if (gAgent.isFirstLogin()) { // Auto open the user's library - LLFolderViewFolder* lib_folder = mFolderRoot->getFolderByID(gInventory.getLibraryRootFolderID()); + LLFolderViewFolder* lib_folder = getFolderByID(gInventory.getLibraryRootFolderID()); if (lib_folder) { lib_folder->setOpen(TRUE); } // Auto close the user's my inventory folder - LLFolderViewFolder* my_inv_folder = mFolderRoot->getFolderByID(gInventory.getRootFolderID()); + LLFolderViewFolder* my_inv_folder = getFolderByID(gInventory.getRootFolderID()); if (my_inv_folder) { my_inv_folder->setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_DOWN); @@ -602,54 +691,12 @@ void LLInventoryPanel::initializeViews() } } -LLFolderViewItem* LLInventoryPanel::rebuildViewsFor(const LLUUID& id) -{ - // Destroy the old view for this ID so we can rebuild it. - LLFolderViewItem* old_view = mFolderRoot->getItemByID(id); - if (old_view) - { - old_view->destroyView(); - } - - return buildNewViews(id); -} - -LLFolderView * LLInventoryPanel::createFolderView(LLInvFVBridge * bridge, bool useLabelSuffix) -{ - LLRect folder_rect(0, - 0, - getRect().getWidth(), - 0); - - LLFolderView::Params p; - - p.name = getName(); - p.title = getLabel(); - p.rect = folder_rect; - p.parent_panel = this; - p.tool_tip = p.name; - p.listener = bridge; - p.use_label_suffix = useLabelSuffix; - p.allow_multiselect = mAllowMultiSelect; - p.show_empty_message = mShowEmptyMessage; - p.show_load_status = mShowLoadStatus; - - return LLUICtrlFactory::create<LLFolderView>(p); -} LLFolderViewFolder * LLInventoryPanel::createFolderViewFolder(LLInvFVBridge * bridge) { - LLFolderViewFolder::Params params; + LLFolderViewFolder::Params params(mParams.folder); params.name = bridge->getDisplayName(); - params.icon = bridge->getIcon(); - params.icon_open = bridge->getOpenIcon(); - - if (mShowItemLinkOverlays) // if false, then links show up just like normal items - { - params.icon_overlay = LLUI::getUIImage("Inv_Link"); - } - params.root = mFolderRoot; params.listener = bridge; params.tool_tip = params.name; @@ -659,17 +706,9 @@ LLFolderViewFolder * LLInventoryPanel::createFolderViewFolder(LLInvFVBridge * br LLFolderViewItem * LLInventoryPanel::createFolderViewItem(LLInvFVBridge * bridge) { - LLFolderViewItem::Params params; + LLFolderViewItem::Params params(mParams.item); params.name = bridge->getDisplayName(); - params.icon = bridge->getIcon(); - params.icon_open = bridge->getOpenIcon(); - - if (mShowItemLinkOverlays) // if false, then links show up just like normal items - { - params.icon_overlay = LLUI::getUIImage("Inv_Link"); - } - params.creation_date = bridge->getCreationDate(); params.root = mFolderRoot; params.listener = bridge; @@ -682,79 +721,69 @@ LLFolderViewItem * LLInventoryPanel::createFolderViewItem(LLInvFVBridge * bridge LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) { LLInventoryObject const* objectp = gInventory.getObject(id); - LLUUID root_id = mFolderRoot->getListener()->getUUID(); - LLFolderViewFolder* parent_folder = NULL; - LLFolderViewItem* itemp = NULL; - if (id == root_id) - { - parent_folder = mFolderRoot; - } - else if (objectp) - { - const LLUUID &parent_id = objectp->getParentUUID(); - parent_folder = (LLFolderViewFolder*)mFolderRoot->getItemByID(parent_id); - - if (parent_folder) + if (!objectp) return NULL; + + LLFolderViewItem* folder_view_item = getItemByID(id); + + const LLUUID &parent_id = objectp->getParentUUID(); + LLFolderViewFolder* parent_folder = (LLFolderViewFolder*)getItemByID(parent_id); + + if (!folder_view_item && parent_folder) + { + if (objectp->getType() <= LLAssetType::AT_NONE || + objectp->getType() >= LLAssetType::AT_COUNT) { - if (objectp->getType() <= LLAssetType::AT_NONE || - objectp->getType() >= LLAssetType::AT_COUNT) - { - llwarns << "LLInventoryPanel::buildNewViews called with invalid objectp->mType : " - << ((S32) objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID() - << llendl; - return NULL; - } + llwarns << "LLInventoryPanel::buildNewViews called with invalid objectp->mType : " + << ((S32) objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID() + << llendl; + return NULL; + } - if ((objectp->getType() == LLAssetType::AT_CATEGORY) && - (objectp->getActualType() != LLAssetType::AT_LINK_FOLDER)) + if ((objectp->getType() == LLAssetType::AT_CATEGORY) && + (objectp->getActualType() != LLAssetType::AT_LINK_FOLDER)) + { + LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(objectp->getType(), + objectp->getType(), + LLInventoryType::IT_CATEGORY, + this, + &mInventoryViewModel, + mFolderRoot, + objectp->getUUID()); + if (new_listener) { - LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(objectp->getType(), - objectp->getType(), - LLInventoryType::IT_CATEGORY, - this, - mFolderRoot, - objectp->getUUID()); - if (new_listener) - { - LLFolderViewFolder* folderp = createFolderViewFolder(new_listener); - if (folderp) - { - folderp->setItemSortOrder(mFolderRoot->getSortOrder()); - } - itemp = folderp; - } + folder_view_item = createFolderViewFolder(new_listener); } - else - { - // Build new view for item. - LLInventoryItem* item = (LLInventoryItem*)objectp; - LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(item->getType(), - item->getActualType(), - item->getInventoryType(), - this, - mFolderRoot, - item->getUUID(), - item->getFlags()); + } + else + { + // Build new view for item. + LLInventoryItem* item = (LLInventoryItem*)objectp; + LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(item->getType(), + item->getActualType(), + item->getInventoryType(), + this, + &mInventoryViewModel, + mFolderRoot, + item->getUUID(), + item->getFlags()); - if (new_listener) - { - itemp = createFolderViewItem(new_listener); - } + if (new_listener) + { + folder_view_item = createFolderViewItem(new_listener); } + } - if (itemp) - { - itemp->addToFolder(parent_folder, mFolderRoot); - } - } + if (folder_view_item) + { + folder_view_item->addToFolder(parent_folder); + addItemID(id, folder_view_item); + } } // If this is a folder, add the children of the folder and recursively add any // child folders. - if (id.isNull() - || (objectp - && objectp->getType() == LLAssetType::AT_CATEGORY)) + if (folder_view_item && objectp->getType() == LLAssetType::AT_CATEGORY) { LLViewerInventoryCategory::cat_array_t* categories; LLViewerInventoryItem::item_array_t* items; @@ -771,7 +800,7 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) } } - if(items && parent_folder) + if(items) { for (LLViewerInventoryItem::item_array_t::const_iterator item_iter = items->begin(); item_iter != items->end(); @@ -784,7 +813,7 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) mInventory->unlockDirectDescendentArrays(id); } - return itemp; + return folder_view_item; } // bit of a hack to make sure the inventory is open. @@ -795,8 +824,8 @@ void LLInventoryPanel::openStartFolderOrMyInventory() { LLFolderViewFolder *fchild = dynamic_cast<LLFolderViewFolder*>(child); if (fchild - && fchild->getListener() - && fchild->getListener()->getUUID() == gInventory.getRootFolderID()) + && fchild->getViewModelItem() + && fchild->getViewModelItem()->getName() == "My Inventory") { fchild->setOpen(TRUE); break; @@ -813,7 +842,7 @@ void LLInventoryPanel::openSelected() { LLFolderViewItem* folder_item = mFolderRoot->getCurSelectedItem(); if(!folder_item) return; - LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getListener(); + LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem(); if(!bridge) return; bridge->openItem(); } @@ -917,7 +946,7 @@ void LLInventoryPanel::setSelection(const LLUUID& obj_id, BOOL take_keyboard_foc { return; } - mFolderRoot->setSelectionByID(obj_id, take_keyboard_focus); + setSelectionByID(obj_id, take_keyboard_focus); } void LLInventoryPanel::setSelectCallback(const boost::function<void (const std::deque<LLFolderViewItem*>& items, BOOL user_action)>& cb) @@ -930,7 +959,7 @@ void LLInventoryPanel::setSelectCallback(const boost::function<void (const std:: void LLInventoryPanel::clearSelection() { - mFolderRoot->clearSelection(); + mSelectThisID.setNull(); } void LLInventoryPanel::onSelectionChange(const std::deque<LLFolderViewItem*>& items, BOOL user_action) @@ -939,7 +968,7 @@ void LLInventoryPanel::onSelectionChange(const std::deque<LLFolderViewItem*>& it mCompletionObserver->reset(); for (std::deque<LLFolderViewItem*>::const_iterator it = items.begin(); it != items.end(); ++it) { - LLUUID id = (*it)->getListener()->getUUID(); + LLUUID id = static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID(); LLViewerInventoryItem* inv_item = mInventory->getItem(id); if (inv_item && !inv_item->isFinished()) @@ -959,19 +988,14 @@ void LLInventoryPanel::onSelectionChange(const std::deque<LLFolderViewItem*>& it } } -void LLInventoryPanel::doToSelected(const LLSD& userdata) -{ - mFolderRoot->doToSelected(&gInventory, userdata); -} - void LLInventoryPanel::doCreate(const LLSD& userdata) { - menu_create_inventory_item(mFolderRoot, LLFolderBridge::sSelf.get(), userdata); + menu_create_inventory_item(this, LLFolderBridge::sSelf.get(), userdata); } bool LLInventoryPanel::beginIMSession() { - std::set<LLUUID> selected_items = mFolderRoot->getSelectionList(); + std::set<LLFolderViewItem*> selected_items = mFolderRoot->getSelectionList(); std::string name; static int session_num = 1; @@ -979,20 +1003,19 @@ bool LLInventoryPanel::beginIMSession() LLDynamicArray<LLUUID> members; EInstantMessage type = IM_SESSION_CONFERENCE_START; - std::set<LLUUID>::const_iterator iter; + std::set<LLFolderViewItem*>::const_iterator iter; for (iter = selected_items.begin(); iter != selected_items.end(); iter++) { - LLUUID item = *iter; - LLFolderViewItem* folder_item = mFolderRoot->getItemByID(item); + LLFolderViewItem* folder_item = (*iter); if(folder_item) { - LLFolderViewEventListener* fve_listener = folder_item->getListener(); + LLFolderViewModelItemInventory* fve_listener = static_cast<LLFolderViewModelItemInventory*>(folder_item->getViewModelItem()); if (fve_listener && (fve_listener->getInventoryType() == LLInventoryType::IT_CATEGORY)) { - LLFolderBridge* bridge = (LLFolderBridge*)folder_item->getListener(); + LLFolderBridge* bridge = (LLFolderBridge*)folder_item->getViewModelItem(); if(!bridge) return true; LLViewerInventoryCategory* cat = bridge->getCategory(); if(!cat) return true; @@ -1026,9 +1049,7 @@ bool LLInventoryPanel::beginIMSession() } else { - LLFolderViewItem* folder_item = mFolderRoot->getItemByID(item); - if(!folder_item) return true; - LLInvFVBridge* listenerp = (LLInvFVBridge*)folder_item->getListener(); + LLInvFVBridge* listenerp = (LLInvFVBridge*)folder_item->getViewModelItem(); if (listenerp->getInventoryType() == LLInventoryType::IT_CALLINGCARD) { @@ -1069,13 +1090,13 @@ bool LLInventoryPanel::beginIMSession() bool LLInventoryPanel::attachObject(const LLSD& userdata) { // Copy selected item UUIDs to a vector. - std::set<LLUUID> selected_items = mFolderRoot->getSelectionList(); + std::set<LLFolderViewItem*> selected_items = mFolderRoot->getSelectionList(); uuid_vec_t items; - for (std::set<LLUUID>::const_iterator set_iter = selected_items.begin(); + for (std::set<LLFolderViewItem*>::const_iterator set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) { - items.push_back(*set_iter); + items.push_back(static_cast<LLFolderViewModelItemInventory*>((*set_iter)->getViewModelItem())->getUUID()); } // Attach selected items. @@ -1088,7 +1109,7 @@ bool LLInventoryPanel::attachObject(const LLSD& userdata) BOOL LLInventoryPanel::getSinceLogoff() { - return getFilter()->isSinceLogoff(); + return getFilter().isSinceLogoff(); } // DEBUG ONLY @@ -1214,12 +1235,93 @@ void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const L void LLInventoryPanel::addHideFolderType(LLFolderType::EType folder_type) { - getFilter()->setFilterCategoryTypes(getFilter()->getFilterCategoryTypes() & ~(1ULL << folder_type)); + getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << folder_type)); } BOOL LLInventoryPanel::getIsHiddenFolderType(LLFolderType::EType folder_type) const { - return !(getFilter()->getFilterCategoryTypes() & (1ULL << folder_type)); + return !(getFilter().getFilterCategoryTypes() & (1ULL << folder_type)); +} + +void LLInventoryPanel::addItemID( const LLUUID& id, LLFolderViewItem* itemp ) +{ + mItemMap[id] = itemp; +} + +void LLInventoryPanel::removeItemID(const LLUUID& id) +{ + LLInventoryModel::cat_array_t categories; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(id, categories, items, TRUE); + + mItemMap.erase(id); + + for (LLInventoryModel::cat_array_t::iterator it = categories.begin(), end_it = categories.end(); + it != end_it; + ++it) + { + mItemMap.erase((*it)->getUUID()); + } + + for (LLInventoryModel::item_array_t::iterator it = items.begin(), end_it = items.end(); + it != end_it; + ++it) + { + mItemMap.erase((*it)->getUUID()); + } +} + +LLFastTimer::DeclareTimer FTM_GET_ITEM_BY_ID("Get FolderViewItem by ID"); +LLFolderViewItem* LLInventoryPanel::getItemByID(const LLUUID& id) +{ + LLFastTimer _(FTM_GET_ITEM_BY_ID); + + std::map<LLUUID, LLFolderViewItem*>::iterator map_it; + map_it = mItemMap.find(id); + if (map_it != mItemMap.end()) + { + return map_it->second; + } + + return NULL; +} + +LLFolderViewFolder* LLInventoryPanel::getFolderByID(const LLUUID& id) +{ + LLFolderViewItem* item = getItemByID(id); + return dynamic_cast<LLFolderViewFolder*>(item); +} + + +void LLInventoryPanel::setSelectionByID( const LLUUID& obj_id, BOOL take_keyboard_focus ) +{ + LLFolderViewItem* itemp = getItemByID(obj_id); + if(itemp && itemp->getViewModelItem()) + { + itemp->arrangeAndSet(TRUE, take_keyboard_focus); + mSelectThisID.setNull(); + return; + } + else + { + // save the desired item to be selected later (if/when ready) + mSelectThisID = obj_id; + } +} + +void LLInventoryPanel::updateSelection() +{ + if (mSelectThisID.notNull()) + { + setSelectionByID(mSelectThisID, false); + } +} + +void LLInventoryPanel::doToSelected(const LLSD& userdata) +{ + LLInventoryAction::doToSelected(mInventory, mFolderRoot, userdata.asString()); + + return; } @@ -1240,7 +1342,7 @@ public: { LLInventoryPanel::initFromParams(p); // turn on inbox for recent items - getFilter()->setFilterCategoryTypes(getFilter()->getFilterCategoryTypes() | (1ULL << LLFolderType::FT_INBOX)); + getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() | (1ULL << LLFolderType::FT_INBOX)); } protected: @@ -1255,3 +1357,34 @@ LLInventoryRecentItemsPanel::LLInventoryRecentItemsPanel( const Params& params) mInvFVBridgeBuilder = &RECENT_ITEMS_BUILDER; } +namespace LLInitParam +{ + void TypeValues<LLFolderType::EType>::declareValues() + { + declare(LLFolderType::lookup(LLFolderType::FT_TEXTURE) , LLFolderType::FT_TEXTURE); + declare(LLFolderType::lookup(LLFolderType::FT_SOUND) , LLFolderType::FT_SOUND); + declare(LLFolderType::lookup(LLFolderType::FT_CALLINGCARD) , LLFolderType::FT_CALLINGCARD); + declare(LLFolderType::lookup(LLFolderType::FT_LANDMARK) , LLFolderType::FT_LANDMARK); + declare(LLFolderType::lookup(LLFolderType::FT_CLOTHING) , LLFolderType::FT_CLOTHING); + declare(LLFolderType::lookup(LLFolderType::FT_OBJECT) , LLFolderType::FT_OBJECT); + declare(LLFolderType::lookup(LLFolderType::FT_NOTECARD) , LLFolderType::FT_NOTECARD); + declare(LLFolderType::lookup(LLFolderType::FT_ROOT_INVENTORY) , LLFolderType::FT_ROOT_INVENTORY); + declare(LLFolderType::lookup(LLFolderType::FT_LSL_TEXT) , LLFolderType::FT_LSL_TEXT); + declare(LLFolderType::lookup(LLFolderType::FT_BODYPART) , LLFolderType::FT_BODYPART); + declare(LLFolderType::lookup(LLFolderType::FT_TRASH) , LLFolderType::FT_TRASH); + declare(LLFolderType::lookup(LLFolderType::FT_SNAPSHOT_CATEGORY), LLFolderType::FT_SNAPSHOT_CATEGORY); + declare(LLFolderType::lookup(LLFolderType::FT_LOST_AND_FOUND) , LLFolderType::FT_LOST_AND_FOUND); + declare(LLFolderType::lookup(LLFolderType::FT_ANIMATION) , LLFolderType::FT_ANIMATION); + declare(LLFolderType::lookup(LLFolderType::FT_GESTURE) , LLFolderType::FT_GESTURE); + declare(LLFolderType::lookup(LLFolderType::FT_FAVORITE) , LLFolderType::FT_FAVORITE); + declare(LLFolderType::lookup(LLFolderType::FT_ENSEMBLE_START) , LLFolderType::FT_ENSEMBLE_START); + declare(LLFolderType::lookup(LLFolderType::FT_ENSEMBLE_END) , LLFolderType::FT_ENSEMBLE_END); + declare(LLFolderType::lookup(LLFolderType::FT_CURRENT_OUTFIT) , LLFolderType::FT_CURRENT_OUTFIT); + declare(LLFolderType::lookup(LLFolderType::FT_OUTFIT) , LLFolderType::FT_OUTFIT); + declare(LLFolderType::lookup(LLFolderType::FT_MY_OUTFITS) , LLFolderType::FT_MY_OUTFITS); + declare(LLFolderType::lookup(LLFolderType::FT_MESH ) , LLFolderType::FT_MESH ); + declare(LLFolderType::lookup(LLFolderType::FT_INBOX) , LLFolderType::FT_INBOX); + declare(LLFolderType::lookup(LLFolderType::FT_OUTBOX) , LLFolderType::FT_OUTBOX); + declare(LLFolderType::lookup(LLFolderType::FT_BASIC_ROOT) , LLFolderType::FT_BASIC_ROOT); + } +} diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index 6db59afb9b..e9bfcb0ccf 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -30,6 +30,8 @@ #include "llassetstorage.h" #include "lldarray.h" +#include "llfolderviewitem.h" +#include "llfolderviewmodelinventory.h" #include "llfloater.h" #include "llinventory.h" #include "llinventoryfilter.h" @@ -38,22 +40,19 @@ #include "lluictrlfactory.h" #include <set> -class LLFolderView; -class LLFolderViewFolder; -class LLFolderViewItem; -class LLInventoryFilter; -class LLInventoryModel; class LLInvFVBridge; -class LLInventoryFVBridgeBuilder; -class LLMenuBarGL; -class LLCheckBoxCtrl; -class LLSpinCtrl; -class LLTextBox; -class LLIconCtrl; -class LLSaveFolderState; -class LLFilterEditor; -class LLTabContainer; +class LLInventoryFolderViewModelBuilder; class LLInvPanelComplObserver; +class LLFolderViewModelInventory; + +namespace LLInitParam +{ + template<> + struct TypeValues<LLFolderType::EType> : public TypeValuesHelper<LLFolderType::EType> + { + static void declareValues(); + }; +} class LLInventoryPanel : public LLPanel { @@ -74,6 +73,19 @@ public: {} }; + struct StartFolder : public LLInitParam::ChoiceBlock<StartFolder> + { + Alternative<std::string> name; + Alternative<LLUUID> id; + Alternative<LLFolderType::EType> type; + + StartFolder() + : name("name"), + id("id"), + type("type") + {} + }; + struct Params : public LLInitParam::Block<Params, LLPanel::Params> { @@ -82,12 +94,14 @@ public: Optional<bool> allow_multi_select; Optional<bool> show_item_link_overlays; Optional<Filter> filter; - Optional<std::string> start_folder; + Optional<StartFolder> start_folder; Optional<bool> use_label_suffix; Optional<bool> show_empty_message; - Optional<bool> show_load_status; Optional<LLScrollContainer::Params> scroll; Optional<bool> accepts_drag_and_drop; + Optional<LLFolderView::Params> folder_view; + Optional<LLFolderViewFolder::Params> folder; + Optional<LLFolderViewItem::Params> item; Params() : sort_order_setting("sort_order_setting"), @@ -98,24 +112,34 @@ public: start_folder("start_folder"), use_label_suffix("use_label_suffix", true), show_empty_message("show_empty_message", true), - show_load_status("show_load_status"), scroll("scroll"), - accepts_drag_and_drop("accepts_drag_and_drop") + accepts_drag_and_drop("accepts_drag_and_drop"), + folder_view("folder_view"), + folder("folder"), + item("item") {} }; + struct InventoryState : public LLInitParam::Block<InventoryState> + { + Mandatory<LLInventoryFilter::Params> filter; + Mandatory<LLInventorySort::Params> sort; + }; + //-------------------------------------------------------------------- // Initialization //-------------------------------------------------------------------- protected: LLInventoryPanel(const Params&); void initFromParams(const Params&); + friend class LLUICtrlFactory; public: virtual ~LLInventoryPanel(); public: LLInventoryModel* getModel() { return mInventory; } + LLFolderViewModelInventory& getRootViewModel() { return mInventoryViewModel; } // LLView methods void draw(); @@ -137,8 +161,8 @@ public: void setSelection(const LLUUID& obj_id, BOOL take_keyboard_focus); void setSelectCallback(const boost::function<void (const std::deque<LLFolderViewItem*>& items, BOOL user_action)>& cb); void clearSelection(); - LLInventoryFilter* getFilter(); - const LLInventoryFilter* getFilter() const; + LLInventoryFilter& getFilter(); + const LLInventoryFilter& getFilter() const; void setFilterTypes(U64 filter, LLInventoryFilter::EFilterType = LLInventoryFilter::FILTERTYPE_OBJECT); U32 getFilterObjectTypes() const; void setFilterPermMask(PermissionMask filter_perm_mask); @@ -156,6 +180,7 @@ public: // This method is called when something has changed about the inventory. void modelChanged(U32 mask); LLFolderView* getRootFolder(); + LLUUID getRootFolderID(); LLScrollContainer* getScrollableContainer() { return mScroller; } void onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action); @@ -167,7 +192,8 @@ public: void doCreate(const LLSD& userdata); bool beginIMSession(); bool attachObject(const LLSD& userdata); - + static void idle(void* user_data); + // DEBUG ONLY: static void dumpSelectionInformation(void* user_data); @@ -182,30 +208,44 @@ public: static void openInventoryPanelAndSetSelection(BOOL auto_open, const LLUUID& obj_id); + void addItemID(const LLUUID& id, LLFolderViewItem* itemp); + void removeItemID(const LLUUID& id); + LLFolderViewItem* getItemByID(const LLUUID& id); + LLFolderViewFolder* getFolderByID(const LLUUID& id); + void setSelectionByID(const LLUUID& obj_id, BOOL take_keyboard_focus); + void updateSelection(); + + LLFolderViewModelInventory* getFolderViewModel(); + const LLFolderViewModelInventory* getFolderViewModel() const; + protected: void openStartFolderOrMyInventory(); // open the first level of inventory void onItemsCompletion(); // called when selected items are complete + LLUUID mSelectThisID; LLInventoryModel* mInventory; LLInventoryObserver* mInventoryObserver; LLInvPanelComplObserver* mCompletionObserver; - BOOL mAcceptsDragAndDrop; - BOOL mAllowMultiSelect; - BOOL mShowItemLinkOverlays; // Shows link graphic over inventory item icons - BOOL mShowEmptyMessage; - BOOL mShowLoadStatus; + bool mAcceptsDragAndDrop; + bool mAllowMultiSelect; + bool mShowItemLinkOverlays; // Shows link graphic over inventory item icons + bool mShowEmptyMessage; LLFolderView* mFolderRoot; LLScrollContainer* mScroller; + LLFolderViewModelInventory mInventoryViewModel; + Params mParams; // stored copy of parameter block + + std::map<LLUUID, LLFolderViewItem*> mItemMap; /** - * Pointer to LLInventoryFVBridgeBuilder. + * Pointer to LLInventoryFolderViewModelBuilder. * * It is set in LLInventoryPanel's constructor and can be overridden in derived classes with * another implementation. * Take into account it will not be deleted by LLInventoryPanel itself. */ - const LLInventoryFVBridgeBuilder* mInvFVBridgeBuilder; + const LLInventoryFolderViewModelBuilder* mInvFVBridgeBuilder; //-------------------------------------------------------------------- @@ -218,7 +258,6 @@ public: void setSortOrder(U32 order); U32 getSortOrder() const; - void requestSort(); private: std::string mSortOrderSetting; @@ -231,26 +270,19 @@ public: void addHideFolderType(LLFolderType::EType folder_type); public: - BOOL getIsViewsInitialized() const { return mViewsInitialized; } - const LLUUID& getRootFolderID() const; + BOOL getIsViewsInitialized() const { return mViewsInitialized; } protected: // Builds the UI. Call this once the inventory is usable. void initializeViews(); - LLFolderViewItem* rebuildViewsFor(const LLUUID& id); // Given the id and the parent, build all of the folder views. - virtual void buildFolderView(const LLInventoryPanel::Params& params); LLFolderViewItem* buildNewViews(const LLUUID& id); BOOL getIsHiddenFolderType(LLFolderType::EType folder_type) const; - virtual LLFolderView* createFolderView(LLInvFVBridge * bridge, bool useLabelSuffix); virtual LLFolderViewFolder* createFolderViewFolder(LLInvFVBridge * bridge); virtual LLFolderViewItem* createFolderViewItem(LLInvFVBridge * bridge); private: - BOOL mBuildDefaultHierarchy; // default inventory hierarchy should be created in postBuild() - BOOL mViewsInitialized; // Views have been generated - // UUID of category from which hierarchy should be built. Set with the - // "start_folder" xml property. Default is LLUUID::null that means total Inventory hierarchy. - LLUUID mStartFolderID; + bool mBuildDefaultHierarchy; // default inventory hierarchy should be created in postBuild() + bool mViewsInitialized; // Views have been generated }; #endif // LL_LLINVENTORYPANEL_H diff --git a/indra/newview/lllistcontextmenu.h b/indra/newview/lllistcontextmenu.h index fabd68ee20..04d3314829 100644 --- a/indra/newview/lllistcontextmenu.h +++ b/indra/newview/lllistcontextmenu.h @@ -37,7 +37,7 @@ class LLContextMenu; /** * Context menu for single or multiple list items. * - * Derived classes must implement contextMenu(). + * Derived classes must implement createMenu(). * * Typical usage: * <code> diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index a7303ad035..8f0e6b4c83 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -1,8 +1,8 @@ /** * @file LLNearbyChat.cpp - * @brief Nearby chat history scrolling panel implementation + * @brief LLNearbyChat class implementation * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * @@ -26,68 +26,67 @@ #include "llviewerprecompiledheaders.h" -#include "llnearbychat.h" -#include "llviewercontrol.h" -#include "llviewerwindow.h" -#include "llrootview.h" -//#include "llchatitemscontainerctrl.h" +#include "message.h" + #include "lliconctrl.h" +#include "llappviewer.h" +#include "llchatentry.h" +#include "llfloaterreg.h" +#include "lltrans.h" +#include "llimfloatercontainer.h" #include "llfloatersidepanelcontainer.h" #include "llfocusmgr.h" #include "lllogchat.h" #include "llresizebar.h" #include "llresizehandle.h" +#include "lldraghandle.h" #include "llmenugl.h" -#include "llviewermenu.h"//for gMenuHolder - +#include "llviewermenu.h" // for gMenuHolder #include "llnearbychathandler.h" #include "llchannelmanager.h" - -#include "llagent.h" // gAgent #include "llchathistory.h" #include "llstylemap.h" - #include "llavatarnamecache.h" - -#include "lldraghandle.h" - -#include "llnearbychatbar.h" #include "llfloaterreg.h" #include "lltrans.h" -static const S32 RESIZE_BAR_THICKNESS = 3; - - -static LLRegisterPanelClassWrapper<LLNearbyChat> t_panel_nearby_chat("panel_nearby_chat"); +#include "llfirstuse.h" +#include "llnearbychat.h" +#include "llagent.h" // gAgent +#include "llgesturemgr.h" +#include "llmultigesture.h" +#include "llkeyboard.h" +#include "llanimationstates.h" +#include "llviewerstats.h" +#include "llcommandhandler.h" +#include "llviewercontrol.h" +#include "llnavigationbar.h" +#include "llwindow.h" +#include "llviewerwindow.h" +#include "llrootview.h" +#include "llviewerchat.h" +#include "lltranslate.h" -LLNearbyChat::LLNearbyChat(const LLNearbyChat::Params& p) -: LLPanel(p), - mChatHistory(NULL) -{ -} +S32 LLNearbyChat::sLastSpecialChatChannel = 0; -BOOL LLNearbyChat::postBuild() +// --- 2 functions in the global namespace :( --- +bool isWordsName(const std::string& name) { - //menu - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; - - enable_registrar.add("NearbyChat.Check", boost::bind(&LLNearbyChat::onNearbyChatCheckContextMenuItem, this, _2)); - registrar.add("NearbyChat.Action", boost::bind(&LLNearbyChat::onNearbyChatContextMenuItemClicked, this, _2)); - - - LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_nearby_chat.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if(menu) - mPopupMenuHandle = menu->getHandle(); - - gSavedSettings.declareS32("nearbychat_showicons_and_names",2,"NearByChat header settings",true); - - mChatHistory = getChild<LLChatHistory>("chat_history"); + // checking to see if it's display name plus username in parentheses + S32 open_paren = name.find(" (", 0); + S32 close_paren = name.find(')', 0); - if(!LLPanel::postBuild()) - return false; - - return true; + if (open_paren != std::string::npos && + close_paren == name.length()-1) + { + return true; + } + else + { + //checking for a single space + S32 pos = name.find(' ', 0); + return std::string::npos != pos && name.rfind(' ', name.length()) == pos && 0 != pos && name.length()-1 != pos; + } } std::string appendTime() @@ -106,52 +105,97 @@ std::string appendTime() } -void LLNearbyChat::addMessage(const LLChat& chat,bool archive,const LLSD &args) +const S32 EXPANDED_HEIGHT = 266; +const S32 COLLAPSED_HEIGHT = 60; +const S32 EXPANDED_MIN_HEIGHT = 150; + +// legacy callback glue +void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel); + +struct LLChatTypeTrigger { + std::string name; + EChatType type; +}; + +static LLChatTypeTrigger sChatTypeTriggers[] = { + { "/whisper" , CHAT_TYPE_WHISPER}, + { "/shout" , CHAT_TYPE_SHOUT} +}; + + +LLNearbyChat::LLNearbyChat(const LLSD& key) +: LLIMConversation(key), + //mOutputMonitor(NULL), + mSpeakerMgr(NULL), + mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT) { - LLChat& tmp_chat = const_cast<LLChat&>(chat); + mIsNearbyChat = true; + setIsChrome(TRUE); + mKey = LLSD(); + mSpeakerMgr = LLLocalSpeakerMgr::getInstance(); +} - if(tmp_chat.mTimeStr.empty()) - tmp_chat.mTimeStr = appendTime(); +//virtual +BOOL LLNearbyChat::postBuild() +{ + BOOL result = LLIMConversation::postBuild(); + mInputEditor->setCommitCallback(boost::bind(&LLNearbyChat::onChatBoxCommit, this)); + mInputEditor->setKeystrokeCallback(boost::bind(&onChatBoxKeystroke, _1, this)); + mInputEditor->setFocusLostCallback(boost::bind(&onChatBoxFocusLost, _1, this)); + mInputEditor->setFocusReceivedCallback(boost::bind(&LLNearbyChat::onChatBoxFocusReceived, this)); - bool use_plain_text_chat_history = gSavedSettings.getBOOL("PlainTextChatHistory"); - - if (!chat.mMuted) - { - tmp_chat.mFromName = chat.mFromName; - LLSD chat_args = args; - chat_args["use_plain_text_chat_history"] = use_plain_text_chat_history; - mChatHistory->appendMessage(chat, chat_args); - } +// mOutputMonitor = getChild<LLOutputMonitorCtrl>("chat_zone_indicator"); +// mOutputMonitor->setVisible(FALSE); - if(archive) + // Register for font change notifications + LLViewerChat::setFontChangedCallback(boost::bind(&LLNearbyChat::onChatFontChange, this, _1)); + + // title must be defined BEFORE call addConversationListItem() because + // it is used for show the item's name in the conversations list + setTitle(getString("NearbyChatTitle")); + + addToHost(); + + //for menu + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + + enable_registrar.add("NearbyChat.Check", boost::bind(&LLNearbyChat::onNearbyChatCheckContextMenuItem, this, _2)); + registrar.add("NearbyChat.Action", boost::bind(&LLNearbyChat::onNearbyChatContextMenuItemClicked, this, _2)); + + LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_nearby_chat.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if(menu) { - mMessageArchive.push_back(chat); - if(mMessageArchive.size()>200) - mMessageArchive.erase(mMessageArchive.begin()); + mPopupMenuHandle = menu->getHandle(); } - if (args["do_not_log"].asBoolean()) + // obsolete, but may be needed for backward compatibility? + gSavedSettings.declareS32("nearbychat_showicons_and_names", 2, "NearByChat header settings", true); + + if (gSavedPerAccountSettings.getBOOL("LogShowHistory")) { - return; + loadHistory(); } - if (gSavedPerAccountSettings.getBOOL("LogNearbyChat")) - { - std::string from_name = chat.mFromName; + // added row in the conversations list when nearby chat is tear-off + LLIMFloaterContainer* im_box = LLIMFloaterContainer::getInstance(); + im_box->addConversationListItem(getTitle(), LLSD(), this); - if (chat.mSourceType == CHAT_SOURCE_AGENT) - { - // if the chat is coming from an agent, log the complete name - LLAvatarName av_name; - LLAvatarNameCache::get(chat.mFromID, &av_name); + return result; +} - if (!av_name.mIsDisplayNameDefault) - { - from_name = av_name.getCompleteName(); - } - } +// virtual +void LLNearbyChat::refresh() +{ + displaySpeakingIndicator(); + updateCallBtnState(LLVoiceClient::getInstance()->getUserPTTState()); - LLLogChat::saveHistory("chat", from_name, chat.mFromID, chat.mText); + // *HACK: Update transparency type depending on whether our children have focus. + // This is needed because this floater is chrome and thus cannot accept focus, so + // the transparency type setting code from LLFloater::setFocus() isn't reached. + if (getTransparencyType() != TT_DEFAULT) + { + setTransparencyType(hasFocus() ? TT_ACTIVE : TT_INACTIVE); } } @@ -162,44 +206,47 @@ void LLNearbyChat::onNearbySpeakers() LLFloaterSidePanelContainer::showPanel("people", "panel_people", param); } - void LLNearbyChat::onNearbyChatContextMenuItemClicked(const LLSD& userdata) { } + bool LLNearbyChat::onNearbyChatCheckContextMenuItem(const LLSD& userdata) { std::string str = userdata.asString(); if(str == "nearby_people") - onNearbySpeakers(); + onNearbySpeakers(); return false; } -void LLNearbyChat::removeScreenChat() +//////////////////////////////////////////////////////////////////////////////// +// +void LLNearbyChat::onFocusReceived() { - LLNotificationsUI::LLScreenChannelBase* chat_channel = LLNotificationsUI::LLChannelManager::getInstance()->findChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))); - if(chat_channel) - { - chat_channel->removeToastsFromChannel(); - } + setBackgroundOpaque(true); + LLIMConversation::onFocusReceived(); } -void LLNearbyChat::setVisible(BOOL visible) +//////////////////////////////////////////////////////////////////////////////// +// +void LLNearbyChat::onFocusLost() { - if(visible) - { - removeScreenChat(); - } - - LLPanel::setVisible(visible); + setBackgroundOpaque(false); + LLIMConversation::onFocusLost(); } - -void LLNearbyChat::getAllowedRect(LLRect& rect) +BOOL LLNearbyChat::handleMouseDown(S32 x, S32 y, MASK mask) { - rect = gViewerWindow->getWorldViewRectScaled(); + //fix for EXT-6625 + //highlight NearbyChat history whenever mouseclick happen in NearbyChat + //setting focus to eidtor will force onFocusLost() call that in its turn will change + //background opaque. This all happenn since NearByChat is "chrome" and didn't process focus change. + + if(mChatHistory) + mChatHistory->setFocus(TRUE); + return LLPanel::handleMouseDown(x, y, mask); } -void LLNearbyChat::updateChatHistoryStyle() +void LLNearbyChat::reloadMessages() { mChatHistory->clear(); @@ -212,34 +259,6 @@ void LLNearbyChat::updateChatHistoryStyle() } } -//static -void LLNearbyChat::processChatHistoryStyleUpdate(const LLSD& newvalue) -{ - LLFloater* chat_bar = LLFloaterReg::getInstance("chat_bar"); - LLNearbyChat* nearby_chat = chat_bar->findChild<LLNearbyChat>("nearby_chat"); - if(nearby_chat) - nearby_chat->updateChatHistoryStyle(); -} - -bool isWordsName(const std::string& name) -{ - // checking to see if it's display name plus username in parentheses - S32 open_paren = name.find(" (", 0); - S32 close_paren = name.find(')', 0); - - if (open_paren != std::string::npos && - close_paren == name.length()-1) - { - return true; - } - else - { - //checking for a single space - S32 pos = name.find(' ', 0); - return std::string::npos != pos && name.rfind(' ', name.length()) == pos && 0 != pos && name.length()-1 != pos; - } -} - void LLNearbyChat::loadHistory() { LLSD do_not_log; @@ -274,9 +293,9 @@ void LLNearbyChat::loadHistory() chat.mSourceType = CHAT_SOURCE_AGENT; if (from_id.isNull() && SYSTEM_FROM == from) - { + { chat.mSourceType = CHAT_SOURCE_SYSTEM; - + } else if (from_id.isNull()) { @@ -289,50 +308,620 @@ void LLNearbyChat::loadHistory() } } +void LLNearbyChat::removeScreenChat() +{ + LLNotificationsUI::LLScreenChannelBase* chat_channel = LLNotificationsUI::LLChannelManager::getInstance()->findChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))); + if(chat_channel) + { + chat_channel->removeToastsFromChannel(); + } +} + +void LLNearbyChat::setVisible(BOOL visible) +{ + if(visible) + { + removeScreenChat(); + } + + LLIMConversation::setVisible(visible); +} + + +void LLNearbyChat::enableDisableCallBtn() +{ + // bool btn_enabled = LLAgent::isActionAllowed("speak"); + + getChildView("voice_call_btn")->setEnabled(false /*btn_enabled*/); +} + +void LLNearbyChat::onTearOffClicked() +{ + LLIMConversation::onTearOffClicked(); + + // see CHUI-170: Save torn-off state of the nearby chat between sessions + BOOL in_the_multifloater = !isTornOff(); + gSavedSettings.setBOOL("NearbyChatIsNotTornOff", in_the_multifloater); +} + +void LLNearbyChat::addToHost() +{ + if ( LLIMConversation::isChatMultiTab()) + { + LLIMFloaterContainer* im_box = LLIMFloaterContainer::getInstance(); + if (im_box) + { + if (gSavedSettings.getBOOL("NearbyChatIsNotTornOff")) + { + im_box->addFloater(this, TRUE, LLTabContainer::END); + } + else + { + // setting of the "potential" host: this sequence sets + // LLFloater::mHostHandle = NULL (a current host), but + // LLFloater::mLastHostHandle = im_box (a "future" host) + setHost(im_box); + setHost(NULL); + } + } + } +} + +// virtual +void LLNearbyChat::onOpen(const LLSD& key) +{ + LLIMConversation::onOpen(key); + showTranslationCheckbox(LLTranslate::isTranslationConfigured()); +} + +void LLNearbyChat::onChatFontChange(LLFontGL* fontp) +{ + // Update things with the new font whohoo + if (mInputEditor) + { + mInputEditor->setFont(fontp); + } +} + //static LLNearbyChat* LLNearbyChat::getInstance() { - LLFloater* chat_bar = LLFloaterReg::getInstance("chat_bar"); - return chat_bar->findChild<LLNearbyChat>("nearby_chat"); + return LLFloaterReg::getTypedInstance<LLNearbyChat>("chat_bar"); } -//////////////////////////////////////////////////////////////////////////////// -// -void LLNearbyChat::onFocusReceived() +void LLNearbyChat::show() { - setBackgroundOpaque(true); - LLPanel::onFocusReceived(); + if (isChatMultiTab()) + { + openFloater(getKey()); + } } -//////////////////////////////////////////////////////////////////////////////// -// -void LLNearbyChat::onFocusLost() +bool LLNearbyChat::isChatVisible() const { - setBackgroundOpaque(false); - LLPanel::onFocusLost(); + bool isVisible = false; + LLIMFloaterContainer* im_box = LLIMFloaterContainer::getInstance(); + // Is the IM floater container ever null? + llassert(im_box != NULL); + if (im_box != NULL) + { + isVisible = + isChatMultiTab() && gSavedSettings.getBOOL("NearbyChatIsNotTornOff")? + im_box->getVisible() && !im_box->isMinimized() : + getVisible() && !isMinimized(); + } + + return isVisible; } -BOOL LLNearbyChat::handleMouseDown(S32 x, S32 y, MASK mask) +void LLNearbyChat::showHistory() { - //fix for EXT-6625 - //highlight NearbyChat history whenever mouseclick happen in NearbyChat - //setting focus to eidtor will force onFocusLost() call that in its turn will change - //background opaque. This all happenn since NearByChat is "chrome" and didn't process focus change. + openFloater(); + setResizeLimits(getMinWidth(), EXPANDED_MIN_HEIGHT); +} + +std::string LLNearbyChat::getCurrentChat() +{ + return mInputEditor ? mInputEditor->getText() : LLStringUtil::null; +} + +// virtual +BOOL LLNearbyChat::handleKeyHere( KEY key, MASK mask ) +{ + BOOL handled = FALSE; + + if( KEY_RETURN == key && mask == MASK_CONTROL) + { + // shout + sendChat(CHAT_TYPE_SHOUT); + handled = TRUE; + } + + return handled; +} + +BOOL LLNearbyChat::matchChatTypeTrigger(const std::string& in_str, std::string* out_str) +{ + U32 in_len = in_str.length(); + S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); - if(mChatHistory) - mChatHistory->setFocus(TRUE); - return LLPanel::handleMouseDown(x, y, mask); + bool string_was_found = false; + + for (S32 n = 0; n < cnt && !string_was_found; n++) + { + if (in_len <= sChatTypeTriggers[n].name.length()) + { + std::string trigger_trunc = sChatTypeTriggers[n].name; + LLStringUtil::truncate(trigger_trunc, in_len); + + if (!LLStringUtil::compareInsensitive(in_str, trigger_trunc)) + { + *out_str = sChatTypeTriggers[n].name; + string_was_found = true; + } + } + } + + return string_was_found; } -void LLNearbyChat::draw() +void LLNearbyChat::onChatBoxKeystroke(LLTextEditor* caller, void* userdata) { - // *HACK: Update transparency type depending on whether our children have focus. - // This is needed because this floater is chrome and thus cannot accept focus, so - // the transparency type setting code from LLFloater::setFocus() isn't reached. - if (getTransparencyType() != TT_DEFAULT) + LLFirstUse::otherAvatarChatFirst(false); + + LLNearbyChat* self = (LLNearbyChat *)userdata; + + LLWString raw_text = self->mInputEditor->getWText(); + + // Can't trim the end, because that will cause autocompletion + // to eat trailing spaces that might be part of a gesture. + LLWStringUtil::trimHead(raw_text); + + S32 length = raw_text.length(); + + if( (length > 0) && (raw_text[0] != '/') ) // forward slash is used for escape (eg. emote) sequences { - setTransparencyType(hasFocus() ? TT_ACTIVE : TT_INACTIVE); + gAgent.startTyping(); + } + else + { + gAgent.stopTyping(); + } + + /* Doesn't work -- can't tell the difference between a backspace + that killed the selection vs. backspace at the end of line. + if (length > 1 + && text[0] == '/' + && key == KEY_BACKSPACE) + { + // the selection will already be deleted, but we need to trim + // off the character before + std::string new_text = raw_text.substr(0, length-1); + self->mInputEditor->setText( new_text ); + self->mInputEditor->setCursorToEnd(); + length = length - 1; + } + */ + + KEY key = gKeyboard->currentKey(); + + // Ignore "special" keys, like backspace, arrows, etc. + if (length > 1 + && raw_text[0] == '/' + && key < KEY_SPECIAL) + { + // we're starting a gesture, attempt to autocomplete + + std::string utf8_trigger = wstring_to_utf8str(raw_text); + std::string utf8_out_str(utf8_trigger); + + if (LLGestureMgr::instance().matchPrefix(utf8_trigger, &utf8_out_str)) + { + std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); + self->mInputEditor->setText(utf8_trigger + rest_of_match); // keep original capitalization for user-entered part + + // Select to end of line, starting from the character + // after the last one the user typed. + self->mInputEditor->selectNext(rest_of_match, false); + } + else if (matchChatTypeTrigger(utf8_trigger, &utf8_out_str)) + { + std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); + self->mInputEditor->setText(utf8_trigger + rest_of_match + " "); // keep original capitalization for user-entered part + self->mInputEditor->endOfDoc(); + } + + //llinfos << "GESTUREDEBUG " << trigger + // << " len " << length + // << " outlen " << out_str.getLength() + // << llendl; + } +} + +// static +void LLNearbyChat::onChatBoxFocusLost(LLFocusableElement* caller, void* userdata) +{ + // stop typing animation + gAgent.stopTyping(); +} + +void LLNearbyChat::onChatBoxFocusReceived() +{ + mInputEditor->setEnabled(!gDisconnected); +} + +EChatType LLNearbyChat::processChatTypeTriggers(EChatType type, std::string &str) +{ + U32 length = str.length(); + S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); + + for (S32 n = 0; n < cnt; n++) + { + if (length >= sChatTypeTriggers[n].name.length()) + { + std::string trigger = str.substr(0, sChatTypeTriggers[n].name.length()); + + if (!LLStringUtil::compareInsensitive(trigger, sChatTypeTriggers[n].name)) + { + U32 trigger_length = sChatTypeTriggers[n].name.length(); + + // It's to remove space after trigger name + if (length > trigger_length && str[trigger_length] == ' ') + trigger_length++; + + str = str.substr(trigger_length, length); + + if (CHAT_TYPE_NORMAL == type) + return sChatTypeTriggers[n].type; + else + break; + } + } + } + + return type; +} + +void LLNearbyChat::sendChat( EChatType type ) +{ + if (mInputEditor) + { + LLWString text = mInputEditor->getWText(); + LLWStringUtil::trim(text); + LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. + if (!text.empty()) + { + // Check if this is destined for another channel + S32 channel = 0; + stripChannelNumber(text, &channel); + + std::string utf8text = wstring_to_utf8str(text); + // Try to trigger a gesture, if not chat to a script. + std::string utf8_revised_text; + if (0 == channel) + { + // discard returned "found" boolean + LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text); + } + else + { + utf8_revised_text = utf8text; + } + + utf8_revised_text = utf8str_trim(utf8_revised_text); + + type = processChatTypeTriggers(type, utf8_revised_text); + + if (!utf8_revised_text.empty()) + { + // Chat with animation + sendChatFromViewer(utf8_revised_text, type, TRUE); + } + } + + mInputEditor->setText(LLStringExplicit("")); + } + + gAgent.stopTyping(); + + // If the user wants to stop chatting on hitting return, lose focus + // and go out of chat mode. + if (gSavedSettings.getBOOL("CloseChatOnReturn")) + { + stopChat(); + } +} + + +void LLNearbyChat::appendMessage(const LLChat& chat, const LLSD &args) +{ + LLChat& tmp_chat = const_cast<LLChat&>(chat); + + if(tmp_chat.mTimeStr.empty()) + tmp_chat.mTimeStr = appendTime(); + + if (!chat.mMuted) + { + tmp_chat.mFromName = chat.mFromName; + LLSD chat_args; + if (args) chat_args = args; + chat_args["use_plain_text_chat_history"] = + gSavedSettings.getBOOL("PlainTextChatHistory"); + chat_args["show_time"] = gSavedSettings.getBOOL("IMShowTime"); + chat_args["show_names_for_p2p_conv"] = true; + + mChatHistory->appendMessage(chat, chat_args); + } +} + +void LLNearbyChat::addMessage(const LLChat& chat,bool archive,const LLSD &args) +{ + appendMessage(chat, args); + + if(archive) + { + mMessageArchive.push_back(chat); + if(mMessageArchive.size()>200) + mMessageArchive.erase(mMessageArchive.begin()); } - LLPanel::draw(); + // logging + if (!args["do_not_log"].asBoolean() + && gSavedPerAccountSettings.getBOOL("LogNearbyChat")) + { + std::string from_name = chat.mFromName; + + if (chat.mSourceType == CHAT_SOURCE_AGENT) + { + // if the chat is coming from an agent, log the complete name + LLAvatarName av_name; + LLAvatarNameCache::get(chat.mFromID, &av_name); + + if (!av_name.mIsDisplayNameDefault) + { + from_name = av_name.getCompleteName(); + } + } + + LLLogChat::saveHistory("chat", from_name, chat.mFromID, chat.mText); + } +} + + +void LLNearbyChat::onChatBoxCommit() +{ + if (mInputEditor->getText().length() > 0) + { + sendChat(CHAT_TYPE_NORMAL); + } + + gAgent.stopTyping(); +} + +void LLNearbyChat::displaySpeakingIndicator() +{ + LLSpeakerMgr::speaker_list_t speaker_list; + LLUUID id; + + id.setNull(); + mSpeakerMgr->update(TRUE); + mSpeakerMgr->getSpeakerList(&speaker_list, FALSE); + + for (LLSpeakerMgr::speaker_list_t::iterator i = speaker_list.begin(); i != speaker_list.end(); ++i) + { + LLPointer<LLSpeaker> s = *i; + if (s->mSpeechVolume > 0 || s->mStatus == LLSpeaker::STATUS_SPEAKING) + { + id = s->mID; + break; + } + } + + if (!id.isNull()) + { + //mOutputMonitor->setVisible(TRUE); + //mOutputMonitor->setSpeakerId(id); + } + else + { + //mOutputMonitor->setVisible(FALSE); + } } + +void LLNearbyChat::sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate) +{ + sendChatFromViewer(utf8str_to_wstring(utf8text), type, animate); +} + +void LLNearbyChat::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate) +{ + // Look for "/20 foo" channel chats. + S32 channel = 0; + LLWString out_text = stripChannelNumber(wtext, &channel); + std::string utf8_out_text = wstring_to_utf8str(out_text); + std::string utf8_text = wstring_to_utf8str(wtext); + + utf8_text = utf8str_trim(utf8_text); + if (!utf8_text.empty()) + { + utf8_text = utf8str_truncate(utf8_text, MAX_STRING - 1); + } + + // Don't animate for chats people can't hear (chat to scripts) + if (animate && (channel == 0)) + { + if (type == CHAT_TYPE_WHISPER) + { + lldebugs << "You whisper " << utf8_text << llendl; + gAgent.sendAnimationRequest(ANIM_AGENT_WHISPER, ANIM_REQUEST_START); + } + else if (type == CHAT_TYPE_NORMAL) + { + lldebugs << "You say " << utf8_text << llendl; + gAgent.sendAnimationRequest(ANIM_AGENT_TALK, ANIM_REQUEST_START); + } + else if (type == CHAT_TYPE_SHOUT) + { + lldebugs << "You shout " << utf8_text << llendl; + gAgent.sendAnimationRequest(ANIM_AGENT_SHOUT, ANIM_REQUEST_START); + } + else + { + llinfos << "send_chat_from_viewer() - invalid volume" << llendl; + return; + } + } + else + { + if (type != CHAT_TYPE_START && type != CHAT_TYPE_STOP) + { + lldebugs << "Channel chat: " << utf8_text << llendl; + } + } + + send_chat_from_viewer(utf8_out_text, type, channel); +} + +// static +void LLNearbyChat::startChat(const char* line) +{ + LLNearbyChat* cb = LLNearbyChat::getInstance(); + + if (cb ) + { + cb->show(); + cb->setVisible(TRUE); + cb->setFocus(TRUE); + cb->mInputEditor->setFocus(TRUE); + + if (line) + { + std::string line_string(line); + cb->mInputEditor->setText(line_string); + } + + cb->mInputEditor->endOfDoc(); + } +} + +// Exit "chat mode" and do the appropriate focus changes +// static +void LLNearbyChat::stopChat() +{ + LLNearbyChat* cb = LLNearbyChat::getInstance(); + + if (cb) + { + cb->mInputEditor->setFocus(FALSE); + + // stop typing animation + gAgent.stopTyping(); + } +} + +// If input of the form "/20foo" or "/20 foo", returns "foo" and channel 20. +// Otherwise returns input and channel 0. +LLWString LLNearbyChat::stripChannelNumber(const LLWString &mesg, S32* channel) +{ + if (mesg[0] == '/' + && mesg[1] == '/') + { + // This is a "repeat channel send" + *channel = sLastSpecialChatChannel; + return mesg.substr(2, mesg.length() - 2); + } + else if (mesg[0] == '/' + && mesg[1] + && LLStringOps::isDigit(mesg[1])) + { + // This a special "/20" speak on a channel + S32 pos = 0; + + // Copy the channel number into a string + LLWString channel_string; + llwchar c; + do + { + c = mesg[pos+1]; + channel_string.push_back(c); + pos++; + } + while(c && pos < 64 && LLStringOps::isDigit(c)); + + // Move the pointer forward to the first non-whitespace char + // Check isspace before looping, so we can handle "/33foo" + // as well as "/33 foo" + while(c && iswspace(c)) + { + c = mesg[pos+1]; + pos++; + } + + sLastSpecialChatChannel = strtol(wstring_to_utf8str(channel_string).c_str(), NULL, 10); + *channel = sLastSpecialChatChannel; + return mesg.substr(pos, mesg.length() - pos); + } + else + { + // This is normal chat. + *channel = 0; + return mesg; + } +} + +void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel) +{ + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_ChatFromViewer); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_ChatData); + msg->addStringFast(_PREHASH_Message, utf8_out_text); + msg->addU8Fast(_PREHASH_Type, type); + msg->addS32("Channel", channel); + + gAgent.sendReliableMessage(); + + LLViewerStats::getInstance()->incStat(LLViewerStats::ST_CHAT_COUNT); +} + +class LLChatCommandHandler : public LLCommandHandler +{ +public: + // not allowed from outside the app + LLChatCommandHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { } + + // Your code here + bool handle(const LLSD& tokens, const LLSD& query_map, + LLMediaCtrl* web) + { + bool retval = false; + // Need at least 2 tokens to have a valid message. + if (tokens.size() < 2) + { + retval = false; + } + else + { + S32 channel = tokens[0].asInteger(); + // VWR-19499 Restrict function to chat channels greater than 0. + if ((channel > 0) && (channel < CHAT_CHANNEL_DEBUG)) + { + retval = true; + // Send unescaped message, see EXT-6353. + std::string unescaped_mesg (LLURI::unescape(tokens[1].asString())); + send_chat_from_viewer(unescaped_mesg, CHAT_TYPE_NORMAL, channel); + } + else + { + retval = false; + // Tell us this is an unsupported SLurl. + } + } + return retval; + } +}; + +// Creating the object registers with the dispatcher. +LLChatCommandHandler gChatHandler; diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index 7c5975cbc5..7c58e3037e 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -1,8 +1,8 @@ - /** +/** * @file llnearbychat.h - * @brief nearby chat history scrolling panel implementation + * @brief LLNearbyChat class definition * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * @@ -24,60 +24,108 @@ * $/LicenseInfo$ */ -#ifndef LL_LLNEARBYCHAT_H_ -#define LL_LLNEARBYCHAT_H_ +#ifndef LL_LLNEARBYCHAT_H +#define LL_LLNEARBYCHAT_H +#include "llimconversation.h" +#include "llcombobox.h" +#include "llgesturemgr.h" +#include "llchat.h" +#include "llvoiceclient.h" +#include "lloutputmonitorctrl.h" +#include "llspeakers.h" #include "llscrollbar.h" #include "llviewerchat.h" -#include "llfloater.h" +#include "llpanel.h" class LLResizeBar; -class LLChatHistory; -class LLNearbyChat: public LLPanel +class LLNearbyChat + : public LLIMConversation { public: - LLNearbyChat(const Params& p = LLPanel::getDefaultParams()); + // constructor for inline chat-bars (e.g. hosted in chat history window) + LLNearbyChat(const LLSD& key); + ~LLNearbyChat() {} - BOOL postBuild (); + /*virtual*/ BOOL postBuild(); + /*virtual*/ void onOpen(const LLSD& key); + + // focus overrides + /*virtual*/ void onFocusLost(); + /*virtual*/ void onFocusReceived(); + + /*virtual*/ void setVisible(BOOL visible); + + void loadHistory(); + void reloadMessages(); + void removeScreenChat(); + + static LLNearbyChat* getInstance(); + + void addToHost(); + void show(); + bool isChatVisible() const; /** @param archive true - to save a message to the chat history log */ - void addMessage (const LLChat& message,bool archive = true, const LLSD &args = LLSD()); + void addMessage (const LLChat& message,bool archive = true, const LLSD &args = LLSD()); void onNearbyChatContextMenuItemClicked(const LLSD& userdata); bool onNearbyChatCheckContextMenuItem(const LLSD& userdata); + LLChatEntry* getChatBox() { return mInputEditor; } + + std::string getCurrentChat(); + + virtual BOOL handleKeyHere( KEY key, MASK mask ); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); - virtual void draw(); - // focus overrides - /*virtual*/ void onFocusLost(); - /*virtual*/ void onFocusReceived(); - - /*virtual*/ void setVisible(BOOL visible); - - virtual void updateChatHistoryStyle(); + static void startChat(const char* line); + static void stopChat(); - static void processChatHistoryStyleUpdate(const LLSD& newvalue); + static void sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate); + static void sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate); - void loadHistory(); + void showHistory(); - static LLNearbyChat* getInstance(); - void removeScreenChat(); +protected: + static BOOL matchChatTypeTrigger(const std::string& in_str, std::string* out_str); + static void onChatBoxKeystroke(LLTextEditor* caller, void* userdata); + static void onChatBoxFocusLost(LLFocusableElement* caller, void* userdata); + void onChatBoxFocusReceived(); -private: + void sendChat( EChatType type ); + void onChatBoxCommit(); + void onChatFontChange(LLFontGL* fontp); - void getAllowedRect (LLRect& rect); + /*virtual*/ void onTearOffClicked(); - void onNearbySpeakers (); + static LLWString stripChannelNumber(const LLWString &mesg, S32* channel); + EChatType processChatTypeTriggers(EChatType type, std::string &str); + + void displaySpeakingIndicator(); + + // set the enable/disable state for the Call button + virtual void enableDisableCallBtn(); + + // Which non-zero channel did we last chat on? + static S32 sLastSpecialChatChannel; + + LLOutputMonitorCtrl* mOutputMonitor; + LLLocalSpeakerMgr* mSpeakerMgr; + S32 mExpandedHeight; private: - LLHandle<LLView> mPopupMenuHandle; - LLChatHistory* mChatHistory; + // prepare chat's params and out one message to chatHistory + void appendMessage(const LLChat& chat, const LLSD &args = 0); + void onNearbySpeakers (); + + /*virtual*/ void refresh(); + + LLHandle<LLView> mPopupMenuHandle; std::vector<LLChat> mMessageArchive; + }; #endif - - diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp deleted file mode 100644 index f8f0f7d243..0000000000 --- a/indra/newview/llnearbychatbar.cpp +++ /dev/null @@ -1,680 +0,0 @@ -/** - * @file llnearbychatbar.cpp - * @brief LLNearbyChatBar class implementation - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "message.h" - -#include "llappviewer.h" -#include "llfloaterreg.h" -#include "lltrans.h" - -#include "llfirstuse.h" -#include "llnearbychatbar.h" -#include "llnearbychatbarlistener.h" -#include "llagent.h" -#include "llgesturemgr.h" -#include "llmultigesture.h" -#include "llkeyboard.h" -#include "llanimationstates.h" -#include "llviewerstats.h" -#include "llcommandhandler.h" -#include "llviewercontrol.h" -#include "llnavigationbar.h" -#include "llwindow.h" -#include "llviewerwindow.h" -#include "llrootview.h" -#include "llviewerchat.h" -#include "llnearbychat.h" -#include "lltranslate.h" - -#include "llresizehandle.h" -#include "llautoreplace.h" - -S32 LLNearbyChatBar::sLastSpecialChatChannel = 0; - -const S32 EXPANDED_HEIGHT = 300; -const S32 COLLAPSED_HEIGHT = 60; -const S32 EXPANDED_MIN_HEIGHT = 150; - -// legacy callback glue -void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel); - -struct LLChatTypeTrigger { - std::string name; - EChatType type; -}; - -static LLChatTypeTrigger sChatTypeTriggers[] = { - { "/whisper" , CHAT_TYPE_WHISPER}, - { "/shout" , CHAT_TYPE_SHOUT} -}; - -LLNearbyChatBar::LLNearbyChatBar(const LLSD& key) -: LLFloater(key), - mChatBox(NULL), - mNearbyChat(NULL), - mOutputMonitor(NULL), - mSpeakerMgr(NULL), - mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT) -{ - mSpeakerMgr = LLLocalSpeakerMgr::getInstance(); - mListener.reset(new LLNearbyChatBarListener(*this)); -} - -//virtual -BOOL LLNearbyChatBar::postBuild() -{ - mChatBox = getChild<LLLineEditor>("chat_box"); - - mChatBox->setAutoreplaceCallback(boost::bind(&LLAutoReplace::autoreplaceCallback, LLAutoReplace::getInstance(), _1, _2)); - mChatBox->setCommitCallback(boost::bind(&LLNearbyChatBar::onChatBoxCommit, this)); - mChatBox->setKeystrokeCallback(&onChatBoxKeystroke, this); - mChatBox->setFocusLostCallback(boost::bind(&onChatBoxFocusLost, _1, this)); - mChatBox->setFocusReceivedCallback(boost::bind(&LLNearbyChatBar::onChatBoxFocusReceived, this)); - - mChatBox->setIgnoreArrowKeys( FALSE ); - mChatBox->setCommitOnFocusLost( FALSE ); - mChatBox->setRevertOnEsc( FALSE ); - mChatBox->setIgnoreTab(TRUE); - mChatBox->setPassDelete(TRUE); - mChatBox->setReplaceNewlinesWithSpaces(FALSE); - mChatBox->setEnableLineHistory(TRUE); - mChatBox->setFont(LLViewerChat::getChatFont()); - - mNearbyChat = getChildView("nearby_chat"); - - gSavedSettings.declareBOOL("nearbychat_history_visibility", mNearbyChat->getVisible(), "Visibility state of nearby chat history", TRUE); - BOOL show_nearby_chat = gSavedSettings.getBOOL("nearbychat_history_visibility"); - - LLButton* show_btn = getChild<LLButton>("show_nearby_chat"); - show_btn->setCommitCallback(boost::bind(&LLNearbyChatBar::onToggleNearbyChatPanel, this)); - show_btn->setToggleState(show_nearby_chat); - - mOutputMonitor = getChild<LLOutputMonitorCtrl>("chat_zone_indicator"); - mOutputMonitor->setVisible(FALSE); - - showNearbyChatPanel(show_nearby_chat); - - // Register for font change notifications - LLViewerChat::setFontChangedCallback(boost::bind(&LLNearbyChatBar::onChatFontChange, this, _1)); - - enableResizeCtrls(true, true, false); - - return TRUE; -} - -// virtual -void LLNearbyChatBar::onOpen(const LLSD& key) -{ - showTranslationCheckbox(LLTranslate::isTranslationConfigured()); -} - -bool LLNearbyChatBar::applyRectControl() -{ - bool rect_controlled = LLFloater::applyRectControl(); - - if (!mNearbyChat->getVisible()) - { - reshape(getRect().getWidth(), getMinHeight()); - enableResizeCtrls(true, true, false); - } - else - { - enableResizeCtrls(true); - setResizeLimits(getMinWidth(), EXPANDED_MIN_HEIGHT); - } - - return rect_controlled; -} - -void LLNearbyChatBar::onChatFontChange(LLFontGL* fontp) -{ - // Update things with the new font whohoo - if (mChatBox) - { - mChatBox->setFont(fontp); - } -} - -//static -LLNearbyChatBar* LLNearbyChatBar::getInstance() -{ - return LLFloaterReg::getTypedInstance<LLNearbyChatBar>("chat_bar"); -} - -void LLNearbyChatBar::showHistory() -{ - openFloater(); - - if (!getChildView("nearby_chat")->getVisible()) - { - onToggleNearbyChatPanel(); - } -} - -void LLNearbyChatBar::showTranslationCheckbox(BOOL show) -{ - getChild<LLUICtrl>("translate_chat_checkbox_lp")->setVisible(show); -} - -void LLNearbyChatBar::draw() -{ - displaySpeakingIndicator(); - LLFloater::draw(); -} - -std::string LLNearbyChatBar::getCurrentChat() -{ - return mChatBox ? mChatBox->getText() : LLStringUtil::null; -} - -// virtual -BOOL LLNearbyChatBar::handleKeyHere( KEY key, MASK mask ) -{ - BOOL handled = FALSE; - - if( KEY_RETURN == key && mask == MASK_CONTROL) - { - // shout - sendChat(CHAT_TYPE_SHOUT); - handled = TRUE; - } - - return handled; -} - -BOOL LLNearbyChatBar::matchChatTypeTrigger(const std::string& in_str, std::string* out_str) -{ - U32 in_len = in_str.length(); - S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); - - for (S32 n = 0; n < cnt; n++) - { - if (in_len > sChatTypeTriggers[n].name.length()) - continue; - - std::string trigger_trunc = sChatTypeTriggers[n].name; - LLStringUtil::truncate(trigger_trunc, in_len); - - if (!LLStringUtil::compareInsensitive(in_str, trigger_trunc)) - { - *out_str = sChatTypeTriggers[n].name; - return TRUE; - } - } - - return FALSE; -} - -void LLNearbyChatBar::onChatBoxKeystroke(LLLineEditor* caller, void* userdata) -{ - LLFirstUse::otherAvatarChatFirst(false); - - LLNearbyChatBar* self = (LLNearbyChatBar *)userdata; - - LLWString raw_text = self->mChatBox->getWText(); - - // Can't trim the end, because that will cause autocompletion - // to eat trailing spaces that might be part of a gesture. - LLWStringUtil::trimHead(raw_text); - - S32 length = raw_text.length(); - - if( (length > 0) && (raw_text[0] != '/') ) // forward slash is used for escape (eg. emote) sequences - { - gAgent.startTyping(); - } - else - { - gAgent.stopTyping(); - } - - /* Doesn't work -- can't tell the difference between a backspace - that killed the selection vs. backspace at the end of line. - if (length > 1 - && text[0] == '/' - && key == KEY_BACKSPACE) - { - // the selection will already be deleted, but we need to trim - // off the character before - std::string new_text = raw_text.substr(0, length-1); - self->mInputEditor->setText( new_text ); - self->mInputEditor->setCursorToEnd(); - length = length - 1; - } - */ - - KEY key = gKeyboard->currentKey(); - - // Ignore "special" keys, like backspace, arrows, etc. - if (length > 1 - && raw_text[0] == '/' - && key < KEY_SPECIAL) - { - // we're starting a gesture, attempt to autocomplete - - std::string utf8_trigger = wstring_to_utf8str(raw_text); - std::string utf8_out_str(utf8_trigger); - - if (LLGestureMgr::instance().matchPrefix(utf8_trigger, &utf8_out_str)) - { - std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); - self->mChatBox->setText(utf8_trigger + rest_of_match); // keep original capitalization for user-entered part - S32 outlength = self->mChatBox->getLength(); // in characters - - // Select to end of line, starting from the character - // after the last one the user typed. - self->mChatBox->setSelection(length, outlength); - } - else if (matchChatTypeTrigger(utf8_trigger, &utf8_out_str)) - { - std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); - self->mChatBox->setText(utf8_trigger + rest_of_match + " "); // keep original capitalization for user-entered part - self->mChatBox->setCursorToEnd(); - } - - //llinfos << "GESTUREDEBUG " << trigger - // << " len " << length - // << " outlen " << out_str.getLength() - // << llendl; - } -} - -// static -void LLNearbyChatBar::onChatBoxFocusLost(LLFocusableElement* caller, void* userdata) -{ - // stop typing animation - gAgent.stopTyping(); -} - -void LLNearbyChatBar::onChatBoxFocusReceived() -{ - mChatBox->setEnabled(!gDisconnected); -} - -EChatType LLNearbyChatBar::processChatTypeTriggers(EChatType type, std::string &str) -{ - U32 length = str.length(); - S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); - - for (S32 n = 0; n < cnt; n++) - { - if (length >= sChatTypeTriggers[n].name.length()) - { - std::string trigger = str.substr(0, sChatTypeTriggers[n].name.length()); - - if (!LLStringUtil::compareInsensitive(trigger, sChatTypeTriggers[n].name)) - { - U32 trigger_length = sChatTypeTriggers[n].name.length(); - - // It's to remove space after trigger name - if (length > trigger_length && str[trigger_length] == ' ') - trigger_length++; - - str = str.substr(trigger_length, length); - - if (CHAT_TYPE_NORMAL == type) - return sChatTypeTriggers[n].type; - else - break; - } - } - } - - return type; -} - -void LLNearbyChatBar::sendChat( EChatType type ) -{ - if (mChatBox) - { - LLWString text = mChatBox->getConvertedText(); - if (!text.empty()) - { - // store sent line in history, duplicates will get filtered - mChatBox->updateHistory(); - // Check if this is destined for another channel - S32 channel = 0; - stripChannelNumber(text, &channel); - - std::string utf8text = wstring_to_utf8str(text); - // Try to trigger a gesture, if not chat to a script. - std::string utf8_revised_text; - if (0 == channel) - { - // discard returned "found" boolean - LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text); - } - else - { - utf8_revised_text = utf8text; - } - - utf8_revised_text = utf8str_trim(utf8_revised_text); - - type = processChatTypeTriggers(type, utf8_revised_text); - - if (!utf8_revised_text.empty()) - { - // Chat with animation - sendChatFromViewer(utf8_revised_text, type, TRUE); - } - } - - mChatBox->setText(LLStringExplicit("")); - } - - gAgent.stopTyping(); - - // If the user wants to stop chatting on hitting return, lose focus - // and go out of chat mode. - if (gSavedSettings.getBOOL("CloseChatOnReturn")) - { - stopChat(); - } -} - -void LLNearbyChatBar::showNearbyChatPanel(bool show) -{ - if (!show) - { - if (mNearbyChat->getVisible() && !isMinimized()) - { - mExpandedHeight = getRect().getHeight(); - } - setResizeLimits(getMinWidth(), COLLAPSED_HEIGHT); - mNearbyChat->setVisible(FALSE); - reshape(getRect().getWidth(), COLLAPSED_HEIGHT); - enableResizeCtrls(true, true, false); - storeRectControl(); - } - else - { - mNearbyChat->setVisible(TRUE); - setResizeLimits(getMinWidth(), EXPANDED_MIN_HEIGHT); - reshape(getRect().getWidth(), mExpandedHeight); - enableResizeCtrls(true); - storeRectControl(); - } - - gSavedSettings.setBOOL("nearbychat_history_visibility", mNearbyChat->getVisible()); -} - -void LLNearbyChatBar::onToggleNearbyChatPanel() -{ - showNearbyChatPanel(!mNearbyChat->getVisible()); -} - -void LLNearbyChatBar::setMinimized(BOOL b) -{ - LLNearbyChat* nearby_chat = getChild<LLNearbyChat>("nearby_chat"); - // when unminimizing with nearby chat visible, go ahead and kill off screen chats - if (!b && nearby_chat->getVisible()) - { - nearby_chat->removeScreenChat(); - } - LLFloater::setMinimized(b); -} - -void LLNearbyChatBar::onChatBoxCommit() -{ - if (mChatBox->getText().length() > 0) - { - sendChat(CHAT_TYPE_NORMAL); - } - - gAgent.stopTyping(); -} - -void LLNearbyChatBar::displaySpeakingIndicator() -{ - LLSpeakerMgr::speaker_list_t speaker_list; - LLUUID id; - - id.setNull(); - mSpeakerMgr->update(TRUE); - mSpeakerMgr->getSpeakerList(&speaker_list, FALSE); - - for (LLSpeakerMgr::speaker_list_t::iterator i = speaker_list.begin(); i != speaker_list.end(); ++i) - { - LLPointer<LLSpeaker> s = *i; - if (s->mSpeechVolume > 0 || s->mStatus == LLSpeaker::STATUS_SPEAKING) - { - id = s->mID; - break; - } - } - - if (!id.isNull()) - { - mOutputMonitor->setVisible(TRUE); - mOutputMonitor->setSpeakerId(id); - } - else - { - mOutputMonitor->setVisible(FALSE); - } -} - -void LLNearbyChatBar::sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate) -{ - sendChatFromViewer(utf8str_to_wstring(utf8text), type, animate); -} - -void LLNearbyChatBar::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate) -{ - // Look for "/20 foo" channel chats. - S32 channel = 0; - LLWString out_text = stripChannelNumber(wtext, &channel); - std::string utf8_out_text = wstring_to_utf8str(out_text); - std::string utf8_text = wstring_to_utf8str(wtext); - - utf8_text = utf8str_trim(utf8_text); - if (!utf8_text.empty()) - { - utf8_text = utf8str_truncate(utf8_text, MAX_STRING - 1); - } - - // Don't animate for chats people can't hear (chat to scripts) - if (animate && (channel == 0)) - { - if (type == CHAT_TYPE_WHISPER) - { - lldebugs << "You whisper " << utf8_text << llendl; - gAgent.sendAnimationRequest(ANIM_AGENT_WHISPER, ANIM_REQUEST_START); - } - else if (type == CHAT_TYPE_NORMAL) - { - lldebugs << "You say " << utf8_text << llendl; - gAgent.sendAnimationRequest(ANIM_AGENT_TALK, ANIM_REQUEST_START); - } - else if (type == CHAT_TYPE_SHOUT) - { - lldebugs << "You shout " << utf8_text << llendl; - gAgent.sendAnimationRequest(ANIM_AGENT_SHOUT, ANIM_REQUEST_START); - } - else - { - llinfos << "send_chat_from_viewer() - invalid volume" << llendl; - return; - } - } - else - { - if (type != CHAT_TYPE_START && type != CHAT_TYPE_STOP) - { - lldebugs << "Channel chat: " << utf8_text << llendl; - } - } - - send_chat_from_viewer(utf8_out_text, type, channel); -} - -// static -void LLNearbyChatBar::startChat(const char* line) -{ - LLNearbyChatBar* cb = LLNearbyChatBar::getInstance(); - - if (!cb ) - return; - - cb->setVisible(TRUE); - cb->setFocus(TRUE); - cb->mChatBox->setFocus(TRUE); - - if (line) - { - std::string line_string(line); - cb->mChatBox->setText(line_string); - } - - cb->mChatBox->setCursorToEnd(); -} - -// Exit "chat mode" and do the appropriate focus changes -// static -void LLNearbyChatBar::stopChat() -{ - LLNearbyChatBar* cb = LLNearbyChatBar::getInstance(); - - if (!cb) - return; - - cb->mChatBox->setFocus(FALSE); - - // stop typing animation - gAgent.stopTyping(); -} - -// If input of the form "/20foo" or "/20 foo", returns "foo" and channel 20. -// Otherwise returns input and channel 0. -LLWString LLNearbyChatBar::stripChannelNumber(const LLWString &mesg, S32* channel) -{ - if (mesg[0] == '/' - && mesg[1] == '/') - { - // This is a "repeat channel send" - *channel = sLastSpecialChatChannel; - return mesg.substr(2, mesg.length() - 2); - } - else if (mesg[0] == '/' - && mesg[1] - && LLStringOps::isDigit(mesg[1])) - { - // This a special "/20" speak on a channel - S32 pos = 0; - - // Copy the channel number into a string - LLWString channel_string; - llwchar c; - do - { - c = mesg[pos+1]; - channel_string.push_back(c); - pos++; - } - while(c && pos < 64 && LLStringOps::isDigit(c)); - - // Move the pointer forward to the first non-whitespace char - // Check isspace before looping, so we can handle "/33foo" - // as well as "/33 foo" - while(c && iswspace(c)) - { - c = mesg[pos+1]; - pos++; - } - - sLastSpecialChatChannel = strtol(wstring_to_utf8str(channel_string).c_str(), NULL, 10); - *channel = sLastSpecialChatChannel; - return mesg.substr(pos, mesg.length() - pos); - } - else - { - // This is normal chat. - *channel = 0; - return mesg; - } -} - -void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel) -{ - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_ChatFromViewer); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_ChatData); - msg->addStringFast(_PREHASH_Message, utf8_out_text); - msg->addU8Fast(_PREHASH_Type, type); - msg->addS32("Channel", channel); - - gAgent.sendReliableMessage(); - - LLViewerStats::getInstance()->incStat(LLViewerStats::ST_CHAT_COUNT); -} - -class LLChatCommandHandler : public LLCommandHandler -{ -public: - // not allowed from outside the app - LLChatCommandHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { } - - // Your code here - bool handle(const LLSD& tokens, const LLSD& query_map, - LLMediaCtrl* web) - { - bool retval = false; - // Need at least 2 tokens to have a valid message. - if (tokens.size() < 2) - { - retval = false; - } - else - { - S32 channel = tokens[0].asInteger(); - // VWR-19499 Restrict function to chat channels greater than 0. - if ((channel > 0) && (channel < CHAT_CHANNEL_DEBUG)) - { - retval = true; - // Send unescaped message, see EXT-6353. - std::string unescaped_mesg (LLURI::unescape(tokens[1].asString())); - send_chat_from_viewer(unescaped_mesg, CHAT_TYPE_NORMAL, channel); - } - else - { - retval = false; - // Tell us this is an unsupported SLurl. - } - } - return retval; - } -}; - -// Creating the object registers with the dispatcher. -LLChatCommandHandler gChatHandler; - - diff --git a/indra/newview/llnearbychatbar.h b/indra/newview/llnearbychatbar.h deleted file mode 100644 index 662496d338..0000000000 --- a/indra/newview/llnearbychatbar.h +++ /dev/null @@ -1,104 +0,0 @@ -/** - * @file llnearbychatbar.h - * @brief LLNearbyChatBar class definition - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLNEARBYCHATBAR_H -#define LL_LLNEARBYCHATBAR_H - -#include "llfloater.h" -#include "llcombobox.h" -#include "llgesturemgr.h" -#include "llchat.h" -#include "llvoiceclient.h" -#include "lloutputmonitorctrl.h" -#include "llspeakers.h" - -class LLNearbyChatBarListener; - -class LLNearbyChatBar : public LLFloater -{ - LOG_CLASS(LLNearbyChatBar); - -public: - // constructor for inline chat-bars (e.g. hosted in chat history window) - LLNearbyChatBar(const LLSD& key); - ~LLNearbyChatBar() {} - - virtual BOOL postBuild(); - /*virtual*/ void onOpen(const LLSD& key); - - static LLNearbyChatBar* getInstance(); - - LLLineEditor* getChatBox() { return mChatBox; } - - virtual void draw(); - - std::string getCurrentChat(); - virtual BOOL handleKeyHere( KEY key, MASK mask ); - - static void startChat(const char* line); - static void stopChat(); - - static void sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate); - static void sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate); - - void showHistory(); - void showTranslationCheckbox(BOOL show); - /*virtual*/void setMinimized(BOOL b); - -protected: - static BOOL matchChatTypeTrigger(const std::string& in_str, std::string* out_str); - static void onChatBoxKeystroke(LLLineEditor* caller, void* userdata); - static void onChatBoxFocusLost(LLFocusableElement* caller, void* userdata); - void onChatBoxFocusReceived(); - - void sendChat( EChatType type ); - void onChatBoxCommit(); - void onChatFontChange(LLFontGL* fontp); - - /* virtual */ bool applyRectControl(); - - void showNearbyChatPanel(bool show); - void onToggleNearbyChatPanel(); - - static LLWString stripChannelNumber(const LLWString &mesg, S32* channel); - EChatType processChatTypeTriggers(EChatType type, std::string &str); - - void displaySpeakingIndicator(); - - // Which non-zero channel did we last chat on? - static S32 sLastSpecialChatChannel; - - LLLineEditor* mChatBox; - LLView* mNearbyChat; - LLOutputMonitorCtrl* mOutputMonitor; - LLLocalSpeakerMgr* mSpeakerMgr; - - S32 mExpandedHeight; - - boost::shared_ptr<LLNearbyChatBarListener> mListener; -}; - -#endif diff --git a/indra/newview/llnearbychatbarlistener.cpp b/indra/newview/llnearbychatbarlistener.cpp index a63e1fb76e..61815d1864 100644 --- a/indra/newview/llnearbychatbarlistener.cpp +++ b/indra/newview/llnearbychatbarlistener.cpp @@ -29,14 +29,14 @@ #include "llviewerprecompiledheaders.h" #include "llnearbychatbarlistener.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llagent.h" #include "llchat.h" -LLNearbyChatBarListener::LLNearbyChatBarListener(LLNearbyChatBar & chatbar) +LLNearbyChatBarListener::LLNearbyChatBarListener(LLNearbyChat & chatbar) : LLEventAPI("LLChatBar", "LLChatBar listener to (e.g.) sendChat, etc."), mChatbar(chatbar) diff --git a/indra/newview/llnearbychatbarlistener.h b/indra/newview/llnearbychatbarlistener.h index 9af9bc1f7b..0537275424 100644 --- a/indra/newview/llnearbychatbarlistener.h +++ b/indra/newview/llnearbychatbarlistener.h @@ -33,17 +33,17 @@ #include "lleventapi.h" class LLSD; -class LLNearbyChatBar; +class LLNearbyChat; class LLNearbyChatBarListener : public LLEventAPI { public: - LLNearbyChatBarListener(LLNearbyChatBar & chatbar); + LLNearbyChatBarListener(LLNearbyChat & chatbar); private: void sendChat(LLSD const & chat_data) const; - LLNearbyChatBar & mChatbar; + LLNearbyChat & mChatbar; }; #endif // LL_LLNEARBYCHATBARLISTENER_H diff --git a/indra/newview/llnearbychathandler.cpp b/indra/newview/llnearbychathandler.cpp index 600fd395fb..c97e3585e1 100644 --- a/indra/newview/llnearbychathandler.cpp +++ b/indra/newview/llnearbychathandler.cpp @@ -1,6 +1,6 @@ /** * @file LLNearbyChatHandler.cpp - * @brief Nearby chat notification managment + * @brief Nearby chat chat managment * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code @@ -40,22 +40,24 @@ #include "llfloaterreg.h"//for LLFloaterReg::getTypedInstance #include "llviewerwindow.h"//for screen channel position -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llrootview.h" #include "lllayoutstack.h" //add LLNearbyChatHandler to LLNotificationsUI namespace using namespace LLNotificationsUI; -//----------------------------------------------------------------------------------------------- -//LLNearbyChatScreenChannel -//----------------------------------------------------------------------------------------------- -LLToastPanelBase* createToastPanel() +static LLNearbyChatToastPanel* createToastPanel() { LLNearbyChatToastPanel* item = LLNearbyChatToastPanel::createInstance(); return item; } + +//----------------------------------------------------------------------------------------------- +//LLNearbyChatScreenChannel +//----------------------------------------------------------------------------------------------- + class LLNearbyChatScreenChannel: public LLScreenChannelBase { LOG_CLASS(LLNearbyChatScreenChannel); @@ -81,10 +83,10 @@ public: } } - void addNotification (LLSD& notification); + void addChat (LLSD& chat); void arrangeToasts (); - typedef boost::function<LLToastPanelBase* (void )> create_toast_panel_callback_t; + typedef boost::function<LLNearbyChatToastPanel* (void )> create_toast_panel_callback_t; void setCreatePanelCallback(create_toast_panel_callback_t value) { m_create_toast_panel_callback_t = value;} void onToastDestroyed (LLToast* toast, bool app_quitting); @@ -152,6 +154,8 @@ protected: bool mChannelRect; }; + + //----------------------------------------------------------------------------------------------- // LLNearbyChatToast //----------------------------------------------------------------------------------------------- @@ -255,7 +259,7 @@ void LLNearbyChatScreenChannel::updateToastFadingTime() bool LLNearbyChatScreenChannel::createPoolToast() { - LLToastPanelBase* panel= m_create_toast_panel_callback_t(); + LLNearbyChatToastPanel* panel= m_create_toast_panel_callback_t(); if(!panel) return false; @@ -277,7 +281,7 @@ bool LLNearbyChatScreenChannel::createPoolToast() return true; } -void LLNearbyChatScreenChannel::addNotification(LLSD& notification) +void LLNearbyChatScreenChannel::addChat(LLSD& chat) { //look in pool. if there is any message if(mStopProcessing) @@ -289,8 +293,8 @@ void LLNearbyChatScreenChannel::addNotification(LLSD& notification) if(m_active_toasts.size()) { - LLUUID fromID = notification["from_id"].asUUID(); // agent id or object id - std::string from = notification["from"].asString(); + LLUUID fromID = chat["from_id"].asUUID(); // agent id or object id + std::string from = chat["from"].asString(); LLToast* toast = m_active_toasts[0].get(); if (toast) { @@ -298,7 +302,7 @@ void LLNearbyChatScreenChannel::addNotification(LLSD& notification) if(panel && panel->messageID() == fromID && panel->getFromName() == from && panel->canAddText()) { - panel->addMessage(notification); + panel->addMessage(chat); toast->reshapeToPanel(); toast->startTimer(); @@ -316,11 +320,11 @@ void LLNearbyChatScreenChannel::addNotification(LLSD& notification) LL_DEBUGS("NearbyChat") << "Empty pool" << llendl; if(!createPoolToast())//created toast will go to pool. so next call will find it return; - addNotification(notification); + addChat(chat); return; } - int chat_type = notification["chat_type"].asInteger(); + int chat_type = chat["chat_type"].asInteger(); if( ((EChatType)chat_type == CHAT_TYPE_DEBUG_MSG)) { @@ -339,10 +343,10 @@ void LLNearbyChatScreenChannel::addNotification(LLSD& notification) m_toast_pool.pop_back(); - LLToastPanelBase* panel = dynamic_cast<LLToastPanelBase*>(toast->getPanel()); + LLNearbyChatToastPanel* panel = dynamic_cast<LLNearbyChatToastPanel*>(toast->getPanel()); if(!panel) return; - panel->init(notification); + panel->init(chat); toast->reshapeToPanel(); toast->startTimer(); @@ -445,10 +449,8 @@ void LLNearbyChatScreenChannel::arrangeToasts() //----------------------------------------------------------------------------------------------- boost::scoped_ptr<LLEventPump> LLNearbyChatHandler::sChatWatcher(new LLEventStream("LLChat")); -LLNearbyChatHandler::LLNearbyChatHandler(e_notification_type type, const LLSD& id) +LLNearbyChatHandler::LLNearbyChatHandler() { - mType = type; - // Getting a Channel for our notifications LLNearbyChatScreenChannel::Params p; p.id = LLUUID(gSavedSettings.getString("NearByChatChannelUUID")); @@ -485,28 +487,26 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, if(chat_msg.mText.empty()) return;//don't process empty messages - LLFloater* chat_bar = LLFloaterReg::getInstance("chat_bar"); - - LLNearbyChat* nearby_chat = chat_bar->findChild<LLNearbyChat>("nearby_chat"); + LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); // Build notification data - LLSD notification; - notification["message"] = chat_msg.mText; - notification["from"] = chat_msg.mFromName; - notification["from_id"] = chat_msg.mFromID; - notification["time"] = chat_msg.mTime; - notification["source"] = (S32)chat_msg.mSourceType; - notification["chat_type"] = (S32)chat_msg.mChatType; - notification["chat_style"] = (S32)chat_msg.mChatStyle; + LLSD chat; + chat["message"] = chat_msg.mText; + chat["from"] = chat_msg.mFromName; + chat["from_id"] = chat_msg.mFromID; + chat["time"] = chat_msg.mTime; + chat["source"] = (S32)chat_msg.mSourceType; + chat["chat_type"] = (S32)chat_msg.mChatType; + chat["chat_style"] = (S32)chat_msg.mChatStyle; // Pass sender info so that it can be rendered properly (STORM-1021). - notification["sender_slurl"] = LLViewerChat::getSenderSLURL(chat_msg, args); + chat["sender_slurl"] = LLViewerChat::getSenderSLURL(chat_msg, args); if (chat_msg.mChatType == CHAT_TYPE_DIRECT && chat_msg.mText.length() > 0 && chat_msg.mText[0] == '@') { // Send event on to LLEventStream and exit - sChatWatcher->post(notification); + sChatWatcher->post(chat); return; } @@ -553,11 +553,9 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, } // Send event on to LLEventStream - sChatWatcher->post(notification); + sChatWatcher->post(chat); - - if( !chat_bar->isMinimized() - && nearby_chat->isInVisibleChain() + if( nearby_chat->isInVisibleChain() || ( chat_msg.mSourceType == CHAT_SOURCE_AGENT && gSavedSettings.getBOOL("UseChatBubbles") ) || mChannel.isDead() @@ -604,23 +602,19 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, // Add a nearby chat toast. LLUUID id; id.generate(); - notification["id"] = id; + chat["id"] = id; std::string r_color_name = "White"; F32 r_color_alpha = 1.0f; LLViewerChat::getChatColor( chat_msg, r_color_name, r_color_alpha); - notification["text_color"] = r_color_name; - notification["color_alpha"] = r_color_alpha; - notification["font_size"] = (S32)LLViewerChat::getChatFontSize() ; - notification["message"] = toast_msg; - channel->addNotification(notification); + chat["text_color"] = r_color_name; + chat["color_alpha"] = r_color_alpha; + chat["font_size"] = (S32)LLViewerChat::getChatFontSize() ; + chat["message"] = toast_msg; + channel->addChat(chat); } } -void LLNearbyChatHandler::onDeleteToast(LLToast* toast) -{ -} - //----------------------------------------------------------------------------------------------- // LLNearbyChatToast diff --git a/indra/newview/llnearbychathandler.h b/indra/newview/llnearbychathandler.h index b0e4f62d51..a5034ac1cb 100644 --- a/indra/newview/llnearbychathandler.h +++ b/indra/newview/llnearbychathandler.h @@ -37,14 +37,13 @@ namespace LLNotificationsUI{ class LLNearbyChatHandler : public LLChatHandler { public: - LLNearbyChatHandler(e_notification_type type,const LLSD& id); + LLNearbyChatHandler(); virtual ~LLNearbyChatHandler(); virtual void processChat(const LLChat& chat_msg, const LLSD &args); protected: - virtual void onDeleteToast(LLToast* toast); virtual void initChannel(); static boost::scoped_ptr<LLEventPump> sChatWatcher; diff --git a/indra/newview/llnotificationalerthandler.cpp b/indra/newview/llnotificationalerthandler.cpp index 89fe7bb3c2..2bc9cdd3c1 100644 --- a/indra/newview/llnotificationalerthandler.cpp +++ b/indra/newview/llnotificationalerthandler.cpp @@ -40,10 +40,10 @@ using namespace LLNotificationsUI; //-------------------------------------------------------------------------- -LLAlertHandler::LLAlertHandler(e_notification_type type, const LLSD& id) : mIsModal(false) +LLAlertHandler::LLAlertHandler(const std::string& name, const std::string& notification_type, bool is_modal) +: LLSysHandler(name, notification_type), + mIsModal(is_modal) { - mType = type; - LLScreenChannelBase::Params p; p.id = LLUUID(gSavedSettings.getString("AlertChannelUUID")); p.display_toasts_always = true; @@ -68,79 +68,58 @@ void LLAlertHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLAlertHandler::processNotification(const LLSD& notify) +bool LLAlertHandler::processNotification(const LLNotificationPtr& notification) { if(mChannel.isDead()) { return false; } - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if(!notification) - return false; - // arrange a channel on a screen if(!mChannel.get()->getVisible()) { initChannel(); } - if (notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "load") + if (notification->canLogToIM() && notification->hasFormElements()) { - if (LLHandlerUtil::canSpawnSessionAndLogToIM(notification)) - { - const std::string name = LLHandlerUtil::getSubstitutionName(notification); - - LLUUID from_id = notification->getPayload()["from_id"]; - - // firstly create session... - LLHandlerUtil::spawnIMSession(name, from_id); - - // ...then log message to have IM Well notified about new message - LLHandlerUtil::logToIMP2P(notification); - } - - LLToastAlertPanel* alert_dialog = new LLToastAlertPanel(notification, mIsModal); - LLToast::Params p; - p.notif_id = notification->getID(); - p.notification = notification; - p.panel = dynamic_cast<LLToastPanel*>(alert_dialog); - p.enable_hide_btn = false; - p.can_fade = false; - p.is_modal = mIsModal; - p.on_delete_toast = boost::bind(&LLAlertHandler::onDeleteToast, this, _1); - - // Show alert in middle of progress view (during teleport) (EXT-1093) - LLProgressView* progress = gViewerWindow->getProgressView(); - LLRect rc = progress && progress->getVisible() ? progress->getRect() : gViewerWindow->getWorldViewRectScaled(); - mChannel.get()->updatePositionAndSize(rc); - - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->addToast(p); - } - else if (notify["sigtype"].asString() == "change") - { - LLToastAlertPanel* alert_dialog = new LLToastAlertPanel(notification, mIsModal); - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->modifyToastByNotificationID(notification->getID(), (LLToastPanel*)alert_dialog); - } - else - { - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->killToastByNotificationID(notification->getID()); + const std::string name = LLHandlerUtil::getSubstitutionName(notification); + + LLUUID from_id = notification->getPayload()["from_id"]; + + // firstly create session... + LLHandlerUtil::spawnIMSession(name, from_id); + + // ...then log message to have IM Well notified about new message + LLHandlerUtil::logToIMP2P(notification); } + + LLToastAlertPanel* alert_dialog = new LLToastAlertPanel(notification, mIsModal); + LLToast::Params p; + p.notif_id = notification->getID(); + p.notification = notification; + p.panel = dynamic_cast<LLToastPanel*>(alert_dialog); + p.enable_hide_btn = false; + p.can_fade = false; + p.is_modal = mIsModal; + p.on_delete_toast = boost::bind(&LLAlertHandler::onDeleteToast, this, _1); + + // Show alert in middle of progress view (during teleport) (EXT-1093) + LLProgressView* progress = gViewerWindow->getProgressView(); + LLRect rc = progress && progress->getVisible() ? progress->getRect() : gViewerWindow->getWorldViewRectScaled(); + mChannel.get()->updatePositionAndSize(rc); + + LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); + if(channel) + channel->addToast(p); + return false; } -//-------------------------------------------------------------------------- - -void LLAlertHandler::onDeleteToast(LLToast* toast) +void LLAlertHandler::onChange( LLNotificationPtr notification ) { + LLToastAlertPanel* alert_dialog = new LLToastAlertPanel(notification, mIsModal); + LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); + if(channel) + channel->modifyToastByNotificationID(notification->getID(), (LLToastPanel*)alert_dialog); } - -//-------------------------------------------------------------------------- - diff --git a/indra/newview/llnotificationgrouphandler.cpp b/indra/newview/llnotificationgrouphandler.cpp index ad51389241..18cd94e685 100644 --- a/indra/newview/llnotificationgrouphandler.cpp +++ b/indra/newview/llnotificationgrouphandler.cpp @@ -37,15 +37,13 @@ using namespace LLNotificationsUI; //-------------------------------------------------------------------------- -LLGroupHandler::LLGroupHandler(e_notification_type type, const LLSD& id) +LLGroupHandler::LLGroupHandler() +: LLSysHandler("Group Notifications", "groupnotify") { - mType = type; - // Getting a Channel for our notifications LLScreenChannel* channel = LLChannelManager::getInstance()->createNotificationChannel(); if(channel) { - channel->setOnRejectToastCallback(boost::bind(&LLGroupHandler::onRejectToast, this, _1)); mChannel = channel->getHandle(); } } @@ -64,72 +62,37 @@ void LLGroupHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLGroupHandler::processNotification(const LLSD& notify) +bool LLGroupHandler::processNotification(const LLNotificationPtr& notification) { if(mChannel.isDead()) { return false; } - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if(!notification) - return false; - // arrange a channel on a screen if(!mChannel.get()->getVisible()) { initChannel(); } - if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") - { - LLHandlerUtil::logGroupNoticeToIMGroup(notification); + LLHandlerUtil::logGroupNoticeToIMGroup(notification); - LLPanel* notify_box = new LLToastGroupNotifyPanel(notification); - LLToast::Params p; - p.notif_id = notification->getID(); - p.notification = notification; - p.panel = notify_box; - p.on_delete_toast = boost::bind(&LLGroupHandler::onDeleteToast, this, _1); + LLPanel* notify_box = new LLToastGroupNotifyPanel(notification); + LLToast::Params p; + p.notif_id = notification->getID(); + p.notification = notification; + p.panel = notify_box; + p.on_delete_toast = boost::bind(&LLGroupHandler::onDeleteToast, this, _1); - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->addToast(p); + LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); + if(channel) + channel->addToast(p); - // send a signal to the counter manager - mNewNotificationSignal(); + LLGroupActions::refresh_notices(); - LLGroupActions::refresh_notices(); - } - else if (notify["sigtype"].asString() == "delete") - { - mChannel.get()->killToastByNotificationID(notification->getID()); - } return false; } -//-------------------------------------------------------------------------- -void LLGroupHandler::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 LLGroupHandler::onRejectToast(LLUUID& id) -{ - LLNotificationPtr notification = LLNotifications::instance().find(id); - - if (notification && LLNotificationManager::getInstance()->getHandlerForNotification(notification->getType()) == this) - { - LLNotifications::instance().cancel(notification); - } -} //-------------------------------------------------------------------------- diff --git a/indra/newview/llnotificationhandler.h b/indra/newview/llnotificationhandler.h index 3569ad6447..0899625242 100644 --- a/indra/newview/llnotificationhandler.h +++ b/indra/newview/llnotificationhandler.h @@ -30,7 +30,7 @@ #include "llwindow.h" -//#include "llnotificationsutil.h" +#include "llnotifications.h" #include "llchannelmanager.h" #include "llchat.h" #include "llinstantmessage.h" @@ -40,20 +40,6 @@ class LLIMFloater; namespace LLNotificationsUI { -// ENotificationType enumerates all possible types of notifications that could be met -// -typedef enum e_notification_type -{ - NT_NOTIFY, - NT_NOTIFYTIP, - NT_GROUPNOTIFY, - NT_IMCHAT, - NT_GROUPCHAT, - NT_NEARBYCHAT, - NT_ALERT, - NT_ALERTMODAL, - NT_OFFER -} ENotificationType; /** * Handler of notification events. @@ -81,21 +67,8 @@ class LLEventHandler public: virtual ~LLEventHandler() {}; - // callbacks for counters - typedef boost::function<void (void)> notification_callback_t; - typedef boost::signals2::signal<void (void)> 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<void (const LLUUID id)> notification_id_callback_t; - typedef boost::signals2::signal<void (const LLUUID id)> 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; + virtual void onDeleteToast(LLToast* toast) {} // 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 @@ -104,8 +77,6 @@ protected: virtual void initChannel()=0; LLHandle<LLScreenChannelBase> mChannel; - e_notification_type mType; - }; // LLSysHandler and LLChatHandler are more specific base classes @@ -115,20 +86,18 @@ protected: /** * Handler for system notifications. */ -class LLSysHandler : public LLEventHandler +class LLSysHandler : public LLEventHandler, public LLNotificationChannel { public: - LLSysHandler(); + LLSysHandler(const std::string& name, const std::string& notification_type); virtual ~LLSysHandler() {}; - virtual bool processNotification(const LLSD& notify)=0; - -protected : - static void init(); - void removeExclusiveNotifications(const LLNotificationPtr& notif); + // base interface functions + /*virtual*/ void onAdd(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ void onLoad(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ void onDelete(LLNotificationPtr p) { if (mChannel.get()) mChannel.get()->removeToastByNotificationID(p->getID());} - typedef std::list< std::set<std::string> > exclusive_notif_sets; - static exclusive_notif_sets sExclusiveNotificationGroups; + virtual bool processNotification(const LLNotificationPtr& notify)=0; }; /** @@ -149,15 +118,12 @@ public: class LLIMHandler : public LLSysHandler { public: - LLIMHandler(e_notification_type type, const LLSD& id); + LLIMHandler(); virtual ~LLIMHandler(); - // base interface functions - virtual bool processNotification(const LLSD& notify); - protected: - virtual void onDeleteToast(LLToast* toast); - virtual void initChannel(); + bool processNotification(const LLNotificationPtr& p); + /*virtual*/ void initChannel(); }; /** @@ -167,16 +133,15 @@ protected: class LLTipHandler : public LLSysHandler { public: - LLTipHandler(e_notification_type type, const LLSD& id); + LLTipHandler(); virtual ~LLTipHandler(); // base interface functions - virtual bool processNotification(const LLSD& notify); + /*virtual*/ void onChange(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ bool processNotification(const LLNotificationPtr& p); protected: - virtual void onDeleteToast(LLToast* toast); - virtual void onRejectToast(const LLUUID& id); - virtual void initChannel(); + /*virtual*/ void initChannel(); }; /** @@ -186,18 +151,16 @@ protected: class LLScriptHandler : public LLSysHandler { public: - LLScriptHandler(e_notification_type type, const LLSD& id); + LLScriptHandler(); virtual ~LLScriptHandler(); + /*virtual*/ void onDelete(LLNotificationPtr p); // base interface functions - virtual bool processNotification(const LLSD& notify); + /*virtual*/ bool processNotification(const LLNotificationPtr& p); protected: - virtual void onDeleteToast(LLToast* toast); - virtual void initChannel(); - - // own handlers - void onRejectToast(LLUUID& id); + /*virtual*/ void onDeleteToast(LLToast* toast); + /*virtual*/ void initChannel(); }; @@ -207,18 +170,15 @@ protected: class LLGroupHandler : public LLSysHandler { public: - LLGroupHandler(e_notification_type type, const LLSD& id); + LLGroupHandler(); virtual ~LLGroupHandler(); // base interface functions - virtual bool processNotification(const LLSD& notify); + /*virtual*/ void onChange(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ bool processNotification(const LLNotificationPtr& p); protected: - virtual void onDeleteToast(LLToast* toast); virtual void initChannel(); - - // own handlers - void onRejectToast(LLUUID& id); }; /** @@ -227,16 +187,14 @@ protected: class LLAlertHandler : public LLSysHandler { public: - LLAlertHandler(e_notification_type type, const LLSD& id); + LLAlertHandler(const std::string& name, const std::string& notification_type, bool is_modal); virtual ~LLAlertHandler(); - void setAlertMode(bool is_modal) { mIsModal = is_modal; } - - // base interface functions - virtual bool processNotification(const LLSD& notify); + /*virtual*/ void onChange(LLNotificationPtr p); + /*virtual*/ void onLoad(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ bool processNotification(const LLNotificationPtr& p); protected: - virtual void onDeleteToast(LLToast* toast); virtual void initChannel(); bool mIsModal; @@ -249,102 +207,71 @@ protected: class LLOfferHandler : public LLSysHandler { public: - LLOfferHandler(e_notification_type type, const LLSD& id); + LLOfferHandler(); virtual ~LLOfferHandler(); // base interface functions - virtual bool processNotification(const LLSD& notify); + /*virtual*/ void onChange(LLNotificationPtr p); + /*virtual*/ void onDelete(LLNotificationPtr notification); + /*virtual*/ bool processNotification(const LLNotificationPtr& p); protected: - virtual void onDeleteToast(LLToast* toast); - virtual void initChannel(); - - // own handlers - void onRejectToast(LLUUID& id); + /*virtual*/ void initChannel(); }; /** * Handler for UI hints. */ -class LLHintHandler : public LLSingleton<LLHintHandler> +class LLHintHandler : public LLNotificationChannel { public: - LLHintHandler(); - virtual ~LLHintHandler(); + LLHintHandler() : LLNotificationChannel("Hints", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "hint")) + {} + virtual ~LLHintHandler() {} - // base interface functions - virtual bool processNotification(const LLSD& notify); + /*virtual*/ void onAdd(LLNotificationPtr p); + /*virtual*/ void onLoad(LLNotificationPtr p); + /*virtual*/ void onDelete(LLNotificationPtr p); }; /** * Handler for browser notifications */ -class LLBrowserNotification : public LLSingleton<LLBrowserNotification> +class LLBrowserNotification : public LLNotificationChannel { public: - virtual bool processNotification(const LLSD& notify); + LLBrowserNotification() + : LLNotificationChannel("Browser", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "browser")) + {} + /*virtual*/ void onAdd(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ void onChange(LLNotificationPtr p) { processNotification(p); } + bool processNotification(const LLNotificationPtr& p); }; /** * Handler for outbox notifications */ -class LLOutboxNotification : public LLSingleton<LLOutboxNotification> +class LLOutboxNotification : public LLNotificationChannel { public: - virtual bool processNotification(const LLSD& notify); + LLOutboxNotification() + : LLNotificationChannel("Outbox", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "outbox")) + {} + /*virtual*/ void onAdd(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ void onChange(LLNotificationPtr p) { } + /*virtual*/ void onDelete(LLNotificationPtr p); + bool processNotification(const LLNotificationPtr& p); }; class LLHandlerUtil { public: /** - * Checks sufficient conditions to log notification message to IM session. - */ - static bool canLogToIM(const LLNotificationPtr& notification); - - /** - * Checks sufficient conditions to log notification message to nearby chat session. - */ - static bool canLogToNearbyChat(const LLNotificationPtr& notification); - - /** - * Checks sufficient conditions to spawn IM session. - */ - static bool canSpawnIMSession(const LLNotificationPtr& notification); - - /** - * Checks sufficient conditions to add notification toast panel IM floater. - */ - static bool canAddNotifPanelToIM(const LLNotificationPtr& notification); - - /** - * Checks whether notification can be used multiple times or not. - */ - static bool isNotificationReusable(const LLNotificationPtr& notification); - - /** - * Checks if passed notification can create IM session and be written into it. - * - * This method uses canLogToIM() & canSpawnIMSession(). - */ - static bool canSpawnSessionAndLogToIM(const LLNotificationPtr& notification); - - /** - * Checks if passed notification can create toast. - */ - static bool canSpawnToast(const LLNotificationPtr& notification); - - /** * Determines whether IM floater is opened. */ static bool isIMFloaterOpened(const LLNotificationPtr& notification); /** - * Determines whether IM floater is focused. - */ - static bool isIMFloaterFocused(const LLNotificationPtr& notification); - - /** * Writes notification message to IM session. */ static void logToIM(const EInstantMessage& session_type, @@ -355,12 +282,7 @@ public: /** * Writes notification message to IM p2p session. */ - static void logToIMP2P(const LLNotificationPtr& notification); - - /** - * Writes notification message to IM p2p session. - */ - static void logToIMP2P(const LLNotificationPtr& notification, bool to_file_only); + static void logToIMP2P(const LLNotificationPtr& notification, bool to_file_only = false); /** * Writes group notice notification message to IM group session. @@ -406,13 +328,6 @@ public: */ static void decIMMesageCounter(const LLNotificationPtr& notification); -private: - - /** - * Find IM floater based on "from_id" - */ - static LLIMFloater* findIMFloater(const LLNotificationPtr& notification); - }; } diff --git a/indra/newview/llnotificationhandlerutil.cpp b/indra/newview/llnotificationhandlerutil.cpp index 16c51138a9..cba22b233b 100644 --- a/indra/newview/llnotificationhandlerutil.cpp +++ b/indra/newview/llnotificationhandlerutil.cpp @@ -41,212 +41,19 @@ using namespace LLNotificationsUI; -// static -std::list< std::set<std::string> > LLSysHandler::sExclusiveNotificationGroups; - -// static -void LLSysHandler::init() -{ - std::set<std::string> online_offline_group; - online_offline_group.insert("FriendOnline"); - online_offline_group.insert("FriendOffline"); - - sExclusiveNotificationGroups.push_back(online_offline_group); -} - -LLSysHandler::LLSysHandler() -{ - if(sExclusiveNotificationGroups.empty()) - { - init(); - } -} - -void LLSysHandler::removeExclusiveNotifications(const LLNotificationPtr& notif) -{ - LLScreenChannel* channel = dynamic_cast<LLScreenChannel *>(mChannel.get()); - if (channel == NULL) - { - return; - } - - class ExclusiveMatcher: public LLScreenChannel::Matcher - { - public: - ExclusiveMatcher(const std::set<std::string>& excl_group, - const std::string& from_name) : - mExclGroup(excl_group), mFromName(from_name) - { - } - bool matches(const LLNotificationPtr notification) const - { - for (std::set<std::string>::const_iterator it = mExclGroup.begin(); it - != mExclGroup.end(); it++) - { - std::string from_name = LLHandlerUtil::getSubstitutionName(notification); - if (notification->getName() == *it && from_name == mFromName) - { - return true; - } - } - return false; - } - private: - const std::set<std::string>& mExclGroup; - const std::string& mFromName; - }; - - - for (exclusive_notif_sets::iterator it = sExclusiveNotificationGroups.begin(); it - != sExclusiveNotificationGroups.end(); it++) - { - std::set<std::string> group = *it; - std::set<std::string>::iterator g_it = group.find(notif->getName()); - if (g_it != group.end()) - { - channel->killMatchedToasts(ExclusiveMatcher(group, - LLHandlerUtil::getSubstitutionName(notif))); - } - } -} - -const static std::string GRANTED_MODIFY_RIGHTS("GrantedModifyRights"), - REVOKED_MODIFY_RIGHTS("RevokedModifyRights"), - OBJECT_GIVE_ITEM("ObjectGiveItem"), - OBJECT_GIVE_ITEM_UNKNOWN_USER("ObjectGiveItemUnknownUser"), - PAYMENT_RECEIVED("PaymentReceived"), - PAYMENT_SENT("PaymentSent"), - ADD_FRIEND_WITH_MESSAGE("AddFriendWithMessage"), - USER_GIVE_ITEM("UserGiveItem"), - INVENTORY_ACCEPTED("InventoryAccepted"), - INVENTORY_DECLINED("InventoryDeclined"), - OFFER_FRIENDSHIP("OfferFriendship"), - FRIENDSHIP_ACCEPTED("FriendshipAccepted"), - FRIENDSHIP_OFFERED("FriendshipOffered"), - FRIENDSHIP_ACCEPTED_BYME("FriendshipAcceptedByMe"), - FRIENDSHIP_DECLINED_BYME("FriendshipDeclinedByMe"), - FRIEND_ONLINE("FriendOnline"), FRIEND_OFFLINE("FriendOffline"), - SERVER_OBJECT_MESSAGE("ServerObjectMessage"), - TELEPORT_OFFERED("TeleportOffered"), - TELEPORT_OFFERED_MATURITY_EXCEEDED("TeleportOffered_MaturityExceeded"), - TELEPORT_OFFERED_MATURITY_BLOCKED("TeleportOffered_MaturityBlocked"), - TELEPORT_OFFER_SENT("TeleportOfferSent"), - IM_SYSTEM_MESSAGE_TIP("IMSystemMessageTip"); - +LLSysHandler::LLSysHandler(const std::string& name, const std::string& notification_type) +: LLNotificationChannel(name, "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, notification_type)) +{} // static -bool LLHandlerUtil::canLogToIM(const LLNotificationPtr& notification) -{ - return GRANTED_MODIFY_RIGHTS == notification->getName() - || REVOKED_MODIFY_RIGHTS == notification->getName() - || PAYMENT_RECEIVED == notification->getName() - || PAYMENT_SENT == notification->getName() - || OFFER_FRIENDSHIP == notification->getName() - || FRIENDSHIP_OFFERED == notification->getName() - || FRIENDSHIP_ACCEPTED == notification->getName() - || FRIENDSHIP_ACCEPTED_BYME == notification->getName() - || FRIENDSHIP_DECLINED_BYME == notification->getName() - || SERVER_OBJECT_MESSAGE == notification->getName() - || INVENTORY_ACCEPTED == notification->getName() - || INVENTORY_DECLINED == notification->getName() - || USER_GIVE_ITEM == notification->getName() - || TELEPORT_OFFERED == notification->getName() - || TELEPORT_OFFERED_MATURITY_EXCEEDED == notification->getName() - || TELEPORT_OFFERED_MATURITY_BLOCKED == notification->getName() - || TELEPORT_OFFER_SENT == notification->getName() - || IM_SYSTEM_MESSAGE_TIP == notification->getName(); -} - -// static -bool LLHandlerUtil::canLogToNearbyChat(const LLNotificationPtr& notification) -{ - return notification->getType() == "notifytip" - && FRIEND_ONLINE != notification->getName() - && FRIEND_OFFLINE != notification->getName() - && INVENTORY_ACCEPTED != notification->getName() - && INVENTORY_DECLINED != notification->getName() - && IM_SYSTEM_MESSAGE_TIP != notification->getName(); -} - -// static -bool LLHandlerUtil::canSpawnIMSession(const LLNotificationPtr& notification) -{ - return OFFER_FRIENDSHIP == notification->getName() - || USER_GIVE_ITEM == notification->getName() - || TELEPORT_OFFERED == notification->getName() - || TELEPORT_OFFERED_MATURITY_EXCEEDED == notification->getName() - || TELEPORT_OFFERED_MATURITY_BLOCKED == notification->getName(); -} - -// static -bool LLHandlerUtil::canAddNotifPanelToIM(const LLNotificationPtr& notification) -{ - return OFFER_FRIENDSHIP == notification->getName() - || USER_GIVE_ITEM == notification->getName() - || TELEPORT_OFFERED == notification->getName() - || TELEPORT_OFFERED_MATURITY_EXCEEDED == notification->getName() - || TELEPORT_OFFERED_MATURITY_BLOCKED == notification->getName(); -} - -// static -bool LLHandlerUtil::isNotificationReusable(const LLNotificationPtr& notification) -{ - return OFFER_FRIENDSHIP == notification->getName() - || USER_GIVE_ITEM == notification->getName() - || TELEPORT_OFFERED == notification->getName() - || TELEPORT_OFFERED_MATURITY_EXCEEDED == notification->getName() - || TELEPORT_OFFERED_MATURITY_BLOCKED == notification->getName(); -} - -// static -bool LLHandlerUtil::canSpawnSessionAndLogToIM(const LLNotificationPtr& notification) -{ - return canLogToIM(notification) && canSpawnIMSession(notification); -} - -// static -bool LLHandlerUtil::canSpawnToast(const LLNotificationPtr& notification) +bool LLHandlerUtil::isIMFloaterOpened(const LLNotificationPtr& notification) { - if(INVENTORY_DECLINED == notification->getName() - || INVENTORY_ACCEPTED == notification->getName()) - { - // return false for inventory accepted/declined notifications if respective IM window is open (EXT-5909) - return ! isIMFloaterOpened(notification); - } - - if(FRIENDSHIP_ACCEPTED == notification->getName()) - { - // don't show FRIENDSHIP_ACCEPTED if IM window is opened and focused - EXT-6441 - return ! isIMFloaterFocused(notification); - } - - if(OFFER_FRIENDSHIP == notification->getName() - || USER_GIVE_ITEM == notification->getName() - || TELEPORT_OFFERED == notification->getName() - || TELEPORT_OFFERED_MATURITY_EXCEEDED == notification->getName() - || TELEPORT_OFFERED_MATURITY_BLOCKED == notification->getName()) - { - // When ANY offer arrives, show toast, unless IM window is already open - EXT-5904 - return ! isIMFloaterOpened(notification); - } - - return true; -} + bool res = false; -// static -LLIMFloater* LLHandlerUtil::findIMFloater(const LLNotificationPtr& notification) -{ LLUUID from_id = notification->getPayload()["from_id"]; LLUUID session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, from_id); - return LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); -} - -// static -bool LLHandlerUtil::isIMFloaterOpened(const LLNotificationPtr& notification) -{ - bool res = false; + LLIMFloater* im_floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); - LLIMFloater* im_floater = findIMFloater(notification); if (im_floater != NULL) { res = im_floater->getVisible() == TRUE; @@ -255,19 +62,6 @@ bool LLHandlerUtil::isIMFloaterOpened(const LLNotificationPtr& notification) return res; } -bool LLHandlerUtil::isIMFloaterFocused(const LLNotificationPtr& notification) -{ - bool res = false; - - LLIMFloater* im_floater = findIMFloater(notification); - if (im_floater != NULL) - { - res = im_floater->hasFocus() == TRUE; - } - - return res; -} - // static void LLHandlerUtil::logToIM(const EInstantMessage& session_type, const std::string& session_name, const std::string& from_name, @@ -329,12 +123,6 @@ void LLHandlerUtil::logToIM(const EInstantMessage& session_type, } } -// static -void LLHandlerUtil::logToIMP2P(const LLNotificationPtr& notification) -{ - logToIMP2P(notification, false); -} - void log_name_callback(const std::string& full_name, const std::string& from_name, const std::string& message, const LLUUID& from_id) @@ -346,9 +134,6 @@ void log_name_callback(const std::string& full_name, const std::string& from_nam // static void LLHandlerUtil::logToIMP2P(const LLNotificationPtr& notification, bool to_file_only) { - // don't create IM p2p session with objects, it's necessary condition to log - if (notification->getName() != OBJECT_GIVE_ITEM) - { LLUUID from_id = notification->getPayload()["from_id"]; if (from_id.isNull()) @@ -366,7 +151,6 @@ void LLHandlerUtil::logToIMP2P(const LLNotificationPtr& notification, bool to_fi gCacheName->get(from_id, false, boost::bind(&log_name_callback, _2, INTERACTIVE_SYSTEM_FROM, notification->getMessage(), from_id)); } } -} // static void LLHandlerUtil::logGroupNoticeToIMGroup( @@ -501,14 +285,10 @@ void LLHandlerUtil::decIMMesageCounter(const LLNotificationPtr& notification) LLUUID from_id = notification->getPayload()["from_id"]; LLUUID session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, from_id); - LLIMModel::LLIMSession * session = LLIMModel::getInstance()->findIMSession( - session_id); + LLIMModel::LLIMSession * session = LLIMModel::getInstance()->findIMSession(session_id); - if (session == NULL) + if (session) { - return; - } - LLSD arg; arg["session_id"] = session_id; session->mNumUnread--; @@ -517,3 +297,5 @@ void LLHandlerUtil::decIMMesageCounter(const LLNotificationPtr& notification) arg["participant_unread"] = session->mParticipantUnreadMessageCount; LLIMModel::getInstance()->mNewMsgSignal(arg); } +} + diff --git a/indra/newview/llnotificationhinthandler.cpp b/indra/newview/llnotificationhinthandler.cpp index f7163cb04f..271f418507 100644 --- a/indra/newview/llnotificationhinthandler.cpp +++ b/indra/newview/llnotificationhinthandler.cpp @@ -33,26 +33,6 @@ using namespace LLNotificationsUI; -LLHintHandler::LLHintHandler() -{ -} - -LLHintHandler::~LLHintHandler() -{ -} - -bool LLHintHandler::processNotification(const LLSD& notify) -{ - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - std::string sigtype = notify["sigtype"].asString(); - if (sigtype == "add" || sigtype == "load") - { - LLHints::show(notification); - } - else if (sigtype == "delete") - { - LLHints::hide(notification); - } - return false; -} +void LLHintHandler::onAdd(LLNotificationPtr p) { LLHints::show(p); } +void LLHintHandler::onLoad(LLNotificationPtr p) { LLHints::show(p); } +void LLHintHandler::onDelete(LLNotificationPtr p) { LLHints::hide(p); } diff --git a/indra/newview/llnotificationmanager.cpp b/indra/newview/llnotificationmanager.cpp index f792f53ac5..2862ad6962 100644 --- a/indra/newview/llnotificationmanager.cpp +++ b/indra/newview/llnotificationmanager.cpp @@ -42,107 +42,35 @@ using namespace LLNotificationsUI; //-------------------------------------------------------------------------- LLNotificationManager::LLNotificationManager() { - mNotifyHandlers.clear(); init(); } //-------------------------------------------------------------------------- LLNotificationManager::~LLNotificationManager() { - BOOST_FOREACH(listener_pair_t& pair, mChannelListeners) - { - pair.second.disconnect(); - } } //-------------------------------------------------------------------------- void LLNotificationManager::init() { - LLNotificationChannel::buildChannel("Notifications", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "notify")); - LLNotificationChannel::buildChannel("NotificationTips", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "notifytip")); - LLNotificationChannel::buildChannel("Group Notifications", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "groupnotify")); - LLNotificationChannel::buildChannel("Alerts", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alert")); - LLNotificationChannel::buildChannel("AlertModal", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alertmodal")); - LLNotificationChannel::buildChannel("IM Notifications", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "notifytoast")); - LLNotificationChannel::buildChannel("Offer", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "offer")); - LLNotificationChannel::buildChannel("Hints", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "hint")); - LLNotificationChannel::buildChannel("Browser", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "browser")); - LLNotificationChannel::buildChannel("Outbox", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "outbox")); + mChannels.push_back(new LLScriptHandler()); + mChannels.push_back(new LLTipHandler()); + mChannels.push_back(new LLGroupHandler()); + mChannels.push_back(new LLAlertHandler("Alerts", "alert", false)); + mChannels.push_back(new LLAlertHandler("AlertModal", "alertmodal", true)); + mChannels.push_back(new LLOfferHandler()); + mChannels.push_back(new LLHintHandler()); + mChannels.push_back(new LLBrowserNotification()); + mChannels.push_back(new LLOutboxNotification()); + mChannels.push_back(new LLIMHandler()); - mChannelListeners["Notifications"] = LLNotifications::instance().getChannel("Notifications")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["NotificationTips"] = LLNotifications::instance().getChannel("NotificationTips")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["Group Notifications"] = LLNotifications::instance().getChannel("Group Notifications")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["Alerts"] = LLNotifications::instance().getChannel("Alerts")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["AlertModal"] = LLNotifications::instance().getChannel("AlertModal")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["IM Notifications"] = LLNotifications::instance().getChannel("IM Notifications")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["Offer"] = LLNotifications::instance().getChannel("Offer")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["Hints"] = LLNotifications::instance().getChannel("Hints")->connectChanged(boost::bind(&LLHintHandler::processNotification, LLHintHandler::getInstance(), _1)); - mChannelListeners["Browser"] = LLNotifications::instance().getChannel("Browser")->connectChanged(boost::bind(&LLBrowserNotification::processNotification, LLBrowserNotification::getInstance(), _1)); - mChannelListeners["Outbox"] = LLNotifications::instance().getChannel("Outbox")->connectChanged(boost::bind(&LLOutboxNotification::processNotification, LLOutboxNotification::getInstance(), _1)); - - mNotifyHandlers["notify"] = boost::shared_ptr<LLEventHandler>(new LLScriptHandler(NT_NOTIFY, LLSD())); - mNotifyHandlers["notifytip"] = boost::shared_ptr<LLEventHandler>(new LLTipHandler(NT_NOTIFY, LLSD())); - mNotifyHandlers["groupnotify"] = boost::shared_ptr<LLEventHandler>(new LLGroupHandler(NT_GROUPNOTIFY, LLSD())); - mNotifyHandlers["alert"] = boost::shared_ptr<LLEventHandler>(new LLAlertHandler(NT_ALERT, LLSD())); - mNotifyHandlers["alertmodal"] = boost::shared_ptr<LLEventHandler>(new LLAlertHandler(NT_ALERT, LLSD())); - static_cast<LLAlertHandler*>(mNotifyHandlers["alertmodal"].get())->setAlertMode(true); - mNotifyHandlers["notifytoast"] = boost::shared_ptr<LLEventHandler>(new LLIMHandler(NT_IMCHAT, LLSD())); - - mNotifyHandlers["nearbychat"] = boost::shared_ptr<LLEventHandler>(new LLNearbyChatHandler(NT_NEARBYCHAT, LLSD())); - mNotifyHandlers["offer"] = boost::shared_ptr<LLEventHandler>(new LLOfferHandler(NT_OFFER, LLSD())); -} - -//-------------------------------------------------------------------------- -bool LLNotificationManager::onNotification(const LLSD& notify) -{ - LLSysHandler* handle = NULL; - - if (LLNotifications::destroyed()) - return false; - - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if (!notification) - return false; - - std::string notification_type = notification->getType(); - handle = static_cast<LLSysHandler*>(mNotifyHandlers[notification_type].get()); - - if(!handle) - return false; - - return handle->processNotification(notify); + mChatHandler = boost::shared_ptr<LLNearbyChatHandler>(new LLNearbyChatHandler()); } //-------------------------------------------------------------------------- void LLNotificationManager::onChat(const LLChat& msg, const LLSD &args) { - // check ENotificationType argument - switch(args["type"].asInteger()) - { - case NT_NEARBYCHAT: - { - LLNearbyChatHandler* handle = dynamic_cast<LLNearbyChatHandler*>(mNotifyHandlers["nearbychat"].get()); - - if(handle) - handle->processChat(msg, args); - } - break; - default: //no need to handle all enum types - break; - } -} - -//-------------------------------------------------------------------------- -LLEventHandler* LLNotificationManager::getHandlerForNotification(std::string notification_type) -{ - std::map<std::string, boost::shared_ptr<LLEventHandler> >::iterator it = mNotifyHandlers.find(notification_type); - - if(it != mNotifyHandlers.end()) - return (*it).second.get(); - - return NULL; + if(mChatHandler) + mChatHandler->processChat(msg, args); } -//-------------------------------------------------------------------------- - diff --git a/indra/newview/llnotificationmanager.h b/indra/newview/llnotificationmanager.h index 27b6ba1c71..c8afdf9e46 100644 --- a/indra/newview/llnotificationmanager.h +++ b/indra/newview/llnotificationmanager.h @@ -28,8 +28,6 @@ #ifndef LL_LLNOTIFICATIONMANAGER_H #define LL_LLNOTIFICATIONMANAGER_H -#include "llevents.h" - #include "lluictrl.h" #include "llnotificationhandler.h" @@ -49,7 +47,6 @@ class LLToast; class LLNotificationManager : public LLSingleton<LLNotificationManager> { typedef std::pair<std::string, LLEventHandler*> eventhandlers; - typedef std::pair<const std::string, LLBoundListener> listener_pair_t; public: LLNotificationManager(); virtual ~LLNotificationManager(); @@ -59,22 +56,12 @@ public: void init(void); //TODO: combine processing and storage (*) - // this method reacts on system notifications and calls an appropriate handler - bool onNotification(const LLSD& notification); - // this method reacts on chat notifications and calls an appropriate handler void onChat(const LLChat& msg, const LLSD &args); - // get a handler for a certain type of notification - LLEventHandler* getHandlerForNotification(std::string notification_type); - - private: - //TODO (*) - std::map<std::string, boost::shared_ptr<LLEventHandler> > mNotifyHandlers; - // cruft std::map<std::string, LLChatHandler*> mChatHandlers; - - std::map<std::string, LLBoundListener> mChannelListeners; + boost::shared_ptr<class LLNearbyChatHandler> mChatHandler; + std::vector<LLNotificationChannelPtr> mChannels; }; } diff --git a/indra/newview/llnotificationofferhandler.cpp b/indra/newview/llnotificationofferhandler.cpp index 1552ed3346..6e641575fa 100644 --- a/indra/newview/llnotificationofferhandler.cpp +++ b/indra/newview/llnotificationofferhandler.cpp @@ -40,16 +40,14 @@ using namespace LLNotificationsUI; //-------------------------------------------------------------------------- -LLOfferHandler::LLOfferHandler(e_notification_type type, const LLSD& id) +LLOfferHandler::LLOfferHandler() +: LLSysHandler("Offer", "offer") { - mType = type; - // Getting a Channel for our notifications LLScreenChannel* channel = LLChannelManager::getInstance()->createNotificationChannel(); if(channel) { channel->setControlHovering(true); - channel->setOnRejectToastCallback(boost::bind(&LLOfferHandler::onRejectToast, this, _1)); mChannel = channel->getHandle(); } } @@ -68,147 +66,109 @@ void LLOfferHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLOfferHandler::processNotification(const LLSD& notify) +bool LLOfferHandler::processNotification(const LLNotificationPtr& notification) { if(mChannel.isDead()) { return false; } - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if(!notification) - return false; - // arrange a channel on a screen if(!mChannel.get()->getVisible()) { initChannel(); } - if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") - { + if( notification->getPayload().has("give_inventory_notification") + && notification->getPayload()["give_inventory_notification"].asBoolean() == false) + { + // This is an original inventory offer, so add a script floater + LLScriptFloaterManager::instance().onAddNotification(notification->getID()); + } + else + { + bool add_notif_to_im = notification->canLogToIM() && notification->hasFormElements(); - if( notification->getPayload().has("give_inventory_notification") - && !notification->getPayload()["give_inventory_notification"] ) + if (add_notif_to_im) { - // This is an original inventory offer, so add a script floater - LLScriptFloaterManager::instance().onAddNotification(notification->getID()); + const std::string name = LLHandlerUtil::getSubstitutionName(notification); + + LLUUID from_id = notification->getPayload()["from_id"]; + + LLHandlerUtil::spawnIMSession(name, from_id); + LLHandlerUtil::addNotifPanelToIM(notification); } - else + + if (!notification->canShowToast()) { - notification->setReusable(LLHandlerUtil::isNotificationReusable(notification)); - - LLUUID session_id; - if (LLHandlerUtil::canSpawnIMSession(notification)) - { - const std::string name = LLHandlerUtil::getSubstitutionName(notification); - - LLUUID from_id = notification->getPayload()["from_id"]; - - session_id = LLHandlerUtil::spawnIMSession(name, from_id); - } - - bool show_toast = LLHandlerUtil::canSpawnToast(notification); - bool add_notid_to_im = LLHandlerUtil::canAddNotifPanelToIM(notification); - if (add_notid_to_im) - { - LLHandlerUtil::addNotifPanelToIM(notification); - } - - if (notification->getPayload().has("SUPPRESS_TOAST") - && notification->getPayload()["SUPPRESS_TOAST"]) - { - LLNotificationsUtil::cancel(notification); - } - else if(show_toast) - { - LLToastNotifyPanel* notify_box = new LLToastNotifyPanel(notification); - // don't close notification on panel destroy since it will be used by IM floater - notify_box->setCloseNotificationOnDestroy(!add_notid_to_im); - LLToast::Params p; - p.notif_id = notification->getID(); - p.notification = notification; - p.panel = notify_box; - p.on_delete_toast = boost::bind(&LLOfferHandler::onDeleteToast, this, _1); - // we not save offer notifications to the syswell floater that should be added to the IM floater - p.can_be_stored = !add_notid_to_im; - - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->addToast(p); - - // if we not add notification to IM - add it to notification well - if (!add_notid_to_im) - { - // send a signal to the counter manager - mNewNotificationSignal(); - } - } - - if (LLHandlerUtil::canLogToIM(notification)) - { - // log only to file if notif panel can be embedded to IM and IM is opened - if (add_notid_to_im && LLHandlerUtil::isIMFloaterOpened(notification)) - { - LLHandlerUtil::logToIMP2P(notification, true); - } - else - { - LLHandlerUtil::logToIMP2P(notification); - } - } + LLNotificationsUtil::cancel(notification); } - } - else if (notify["sigtype"].asString() == "delete") - { - if( notification->getPayload().has("give_inventory_notification") - && !notification->getPayload()["give_inventory_notification"] ) + else if(!notification->canLogToIM() || !LLHandlerUtil::isIMFloaterOpened(notification)) { - // Remove original inventory offer script floater - LLScriptFloaterManager::instance().onRemoveNotification(notification->getID()); + LLToastNotifyPanel* notify_box = new LLToastNotifyPanel(notification); + LLToast::Params p; + p.notif_id = notification->getID(); + p.notification = notification; + p.panel = notify_box; + // we not save offer notifications to the syswell floater that should be added to the IM floater + p.can_be_stored = !add_notif_to_im; + + LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); + if(channel) + channel->addToast(p); } - else + + if (notification->canLogToIM()) { - if (LLHandlerUtil::canAddNotifPanelToIM(notification) - && !LLHandlerUtil::isIMFloaterOpened(notification)) - { - LLHandlerUtil::decIMMesageCounter(notification); - } - mChannel.get()->killToastByNotificationID(notification->getID()); + // log only to file if notif panel can be embedded to IM and IM is opened + bool file_only = add_notif_to_im && LLHandlerUtil::isIMFloaterOpened(notification); + LLHandlerUtil::logToIMP2P(notification, file_only); } } return false; } -//-------------------------------------------------------------------------- - -void LLOfferHandler::onDeleteToast(LLToast* toast) +/*virtual*/ void LLOfferHandler::onChange(LLNotificationPtr p) { - if (!LLHandlerUtil::canAddNotifPanelToIM(toast->getNotification())) + LLToastNotifyPanel* panelp = LLToastNotifyPanel::getInstance(p->getID()); + if (panelp) { - // send a signal to the counter manager - mDelNotificationSignal(); + // + // HACK: if we're dealing with a notification embedded in IM, update it + // otherwise remove its toast + // + if (dynamic_cast<LLIMToastNotifyPanel*>(panelp)) + { + panelp->updateNotification(); + } + else + { + // if notification has changed, hide it + mChannel.get()->removeToastByNotificationID(p->getID()); + } } - - // 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 LLOfferHandler::onRejectToast(LLUUID& id) -{ - LLNotificationPtr notification = LLNotifications::instance().find(id); - if (notification - && LLNotificationManager::getInstance()->getHandlerForNotification( - notification->getType()) == this - // don't delete notification since it may be used by IM floater - && !LLHandlerUtil::canAddNotifPanelToIM(notification)) +/*virtual*/ void LLOfferHandler::onDelete(LLNotificationPtr notification) +{ + if( notification->getPayload().has("give_inventory_notification") + && !notification->getPayload()["give_inventory_notification"] ) + { + // Remove original inventory offer script floater + LLScriptFloaterManager::instance().onRemoveNotification(notification->getID()); + } + else { - LLNotifications::instance().cancel(notification); + if (notification->canLogToIM() + && notification->hasFormElements() + && !LLHandlerUtil::isIMFloaterOpened(notification)) + { + LLHandlerUtil::decIMMesageCounter(notification); + } + mChannel.get()->removeToastByNotificationID(notification->getID()); } } + diff --git a/indra/newview/llnotificationscripthandler.cpp b/indra/newview/llnotificationscripthandler.cpp index 398f54c6f7..7e9c0d4f4b 100644 --- a/indra/newview/llnotificationscripthandler.cpp +++ b/indra/newview/llnotificationscripthandler.cpp @@ -37,21 +37,15 @@ using namespace LLNotificationsUI; -static const std::string SCRIPT_DIALOG ("ScriptDialog"); -static const std::string SCRIPT_DIALOG_GROUP ("ScriptDialogGroup"); -static const std::string SCRIPT_LOAD_URL ("LoadWebPage"); - //-------------------------------------------------------------------------- -LLScriptHandler::LLScriptHandler(e_notification_type type, const LLSD& id) +LLScriptHandler::LLScriptHandler() +: LLSysHandler("Notifications", "notify") { - mType = type; - // Getting a Channel for our notifications LLScreenChannel* channel = LLChannelManager::getInstance()->createNotificationChannel(); if(channel) { channel->setControlHovering(true); - channel->setOnRejectToastCallback(boost::bind(&LLScriptHandler::onRejectToast, this, _1)); mChannel = channel->getHandle(); } } @@ -70,32 +64,25 @@ void LLScriptHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLScriptHandler::processNotification(const LLSD& notify) +bool LLScriptHandler::processNotification(const LLNotificationPtr& notification) { if(mChannel.isDead()) { return false; } - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if(!notification) - return false; - // arrange a channel on a screen if(!mChannel.get()->getVisible()) { initChannel(); } - if(notify["sigtype"].asString() == "add") - { - if (LLHandlerUtil::canLogToIM(notification)) + if (notification->canLogToIM()) { LLHandlerUtil::logToIMP2P(notification); } - if(SCRIPT_DIALOG == notification->getName() || SCRIPT_DIALOG_GROUP == notification->getName() || SCRIPT_LOAD_URL == notification->getName()) + if(notification->hasFormElements()) { LLScriptFloaterManager::getInstance()->onAddNotification(notification->getID()); } @@ -114,60 +101,39 @@ bool LLScriptHandler::processNotification(const LLSD& notify) { channel->addToast(p); } + } - // send a signal to the counter manager - mNewNotificationSignal(); + return false; } - } - else if (notify["sigtype"].asString() == "delete") + + +void LLScriptHandler::onDelete( LLNotificationPtr notification ) { - if(SCRIPT_DIALOG == notification->getName() || SCRIPT_DIALOG_GROUP == notification->getName() || SCRIPT_LOAD_URL == notification->getName()) + if(notification->hasFormElements()) { LLScriptFloaterManager::getInstance()->onRemoveNotification(notification->getID()); } else { - mChannel.get()->killToastByNotificationID(notification->getID()); + mChannel.get()->removeToastByNotificationID(notification->getID()); } } - return false; -} + //-------------------------------------------------------------------------- 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()); - LLNotificationPtr notification = LLNotifications::getInstance()->find(toast->getNotificationID()); - if( notification && - (SCRIPT_DIALOG == notification->getName() || SCRIPT_DIALOG_GROUP == notification->getName()) ) + if( notification && notification->hasFormElements()) { LLScriptFloaterManager::getInstance()->onRemoveNotification(notification->getID()); } } -//-------------------------------------------------------------------------- -void LLScriptHandler::onRejectToast(LLUUID& id) -{ - LLNotificationPtr notification = LLNotifications::instance().find(id); - - if (notification - && LLNotificationManager::getInstance()->getHandlerForNotification( - notification->getType()) == this) - { - LLNotifications::instance().cancel(notification); - } -} - -//-------------------------------------------------------------------------- - diff --git a/indra/newview/llnotificationstorage.cpp b/indra/newview/llnotificationstorage.cpp index fb1adc7ddf..a31b95811e 100644 --- a/indra/newview/llnotificationstorage.cpp +++ b/indra/newview/llnotificationstorage.cpp @@ -84,9 +84,11 @@ bool LLPersistentNotificationStorage::onPersistentChannelChanged(const LLSD& pay return false; } +static LLFastTimer::DeclareTimer FTM_SAVE_NOTIFICATIONS("Save Notifications"); + void LLPersistentNotificationStorage::saveNotifications() { - // TODO - think about save optimization. + LLFastTimer _(FTM_SAVE_NOTIFICATIONS); llofstream notify_file(mFileName.c_str()); if (!notify_file.is_open()) @@ -98,10 +100,15 @@ void LLPersistentNotificationStorage::saveNotifications() LLSD output; LLSD& data = output["data"]; - LLNotificationChannelPtr history_channel = LLNotifications::instance().getChannel("Persistent"); - LLNotificationSet::iterator it = history_channel->begin(); + boost::intrusive_ptr<LLPersistentNotificationChannel> history_channel = boost::dynamic_pointer_cast<LLPersistentNotificationChannel>(LLNotifications::instance().getChannel("Persistent")); + if (!history_channel) + { + return; + } - for ( ; history_channel->end() != it; ++it) + for ( std::vector<LLNotificationPtr>::iterator it = history_channel->beginHistory(), end_it = history_channel->endHistory(); + it != end_it; + ++it) { LLNotificationPtr notification = *it; @@ -120,8 +127,11 @@ void LLPersistentNotificationStorage::saveNotifications() formatter->format(output, notify_file, LLSDFormatter::OPTIONS_PRETTY); } +static LLFastTimer::DeclareTimer FTM_LOAD_NOTIFICATIONS("Load Notifications"); + void LLPersistentNotificationStorage::loadNotifications() { + LLFastTimer _(FTM_LOAD_NOTIFICATIONS); LLResponderRegistry::registerResponders(); LLNotifications::instance().getChannel("Persistent")-> diff --git a/indra/newview/llnotificationtiphandler.cpp b/indra/newview/llnotificationtiphandler.cpp index e397cfa046..a420c0d2ed 100644 --- a/indra/newview/llnotificationtiphandler.cpp +++ b/indra/newview/llnotificationtiphandler.cpp @@ -29,7 +29,7 @@ #include "llfloaterreg.h" #include "llnearbychat.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llnotificationhandler.h" #include "llnotifications.h" #include "lltoastnotifypanel.h" @@ -41,15 +41,13 @@ using namespace LLNotificationsUI; //-------------------------------------------------------------------------- -LLTipHandler::LLTipHandler(e_notification_type type, const LLSD& id) +LLTipHandler::LLTipHandler() +: LLSysHandler("NotificationTips", "notifytip") { - mType = type; - // Getting a Channel for our notifications LLScreenChannel* channel = LLChannelManager::getInstance()->createNotificationChannel(); if(channel) { - channel->setOnRejectToastCallback(boost::bind(&LLTipHandler::onRejectToast, this, _1)); mChannel = channel->getHandle(); } } @@ -68,102 +66,67 @@ void LLTipHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLTipHandler::processNotification(const LLSD& notify) +bool LLTipHandler::processNotification(const LLNotificationPtr& notification) { if(mChannel.isDead()) { return false; } - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if(!notification) - return false; - // arrange a channel on a screen if(!mChannel.get()->getVisible()) { initChannel(); } - if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") - { // archive message in nearby chat - if (LLHandlerUtil::canLogToNearbyChat(notification)) - { - LLHandlerUtil::logToNearbyChat(notification, CHAT_SOURCE_SYSTEM); - - // don't show toast if Nearby Chat is opened - LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); - LLNearbyChatBar* nearby_chat_bar = LLNearbyChatBar::getInstance(); - if (!nearby_chat_bar->isMinimized() && nearby_chat_bar->getVisible() && nearby_chat->getVisible()) - { - return false; - } - } - - std::string session_name = notification->getPayload()["SESSION_NAME"]; - const std::string name = notification->getSubstitutions()["NAME"]; - if (session_name.empty()) - { - session_name = name; - } - LLUUID from_id = notification->getPayload()["from_id"]; - if (LLHandlerUtil::canLogToIM(notification)) - { - LLHandlerUtil::logToIM(IM_NOTHING_SPECIAL, session_name, name, - notification->getMessage(), from_id, from_id); - } - - if (LLHandlerUtil::canSpawnIMSession(notification)) - { - LLHandlerUtil::spawnIMSession(name, from_id); - } + if (notification->canLogToChat()) + { + LLHandlerUtil::logToNearbyChat(notification, CHAT_SOURCE_SYSTEM); - // don't spawn toast for inventory accepted/declined offers if respective IM window is open (EXT-5909) - if (!LLHandlerUtil::canSpawnToast(notification)) + // don't show toast if Nearby Chat is opened + LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); + if (nearby_chat->isChatVisible()) { return false; } + } - LLToastPanel* notify_box = LLToastPanel::buidPanelFromNotification(notification); - - LLToast::Params p; - p.notif_id = notification->getID(); - p.notification = notification; - p.lifetime_secs = gSavedSettings.getS32("NotificationTipToastLifeTime"); - p.panel = notify_box; - p.is_tip = true; - p.can_be_stored = false; - - removeExclusiveNotifications(notification); - - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->addToast(p); + std::string session_name = notification->getPayload()["SESSION_NAME"]; + const std::string name = notification->getSubstitutions()["NAME"]; + if (session_name.empty()) + { + session_name = name; } - else if (notify["sigtype"].asString() == "delete") + LLUUID from_id = notification->getPayload()["from_id"]; + if (notification->canLogToIM()) { - mChannel.get()->killToastByNotificationID(notification->getID()); + LLHandlerUtil::logToIM(IM_NOTHING_SPECIAL, session_name, name, + notification->getMessage(), from_id, from_id); } - return false; -} - -//-------------------------------------------------------------------------- -void LLTipHandler::onDeleteToast(LLToast* toast) -{ -} - -//-------------------------------------------------------------------------- -void LLTipHandler::onRejectToast(const LLUUID& id) -{ - LLNotificationPtr notification = LLNotifications::instance().find(id); + if (notification->canLogToIM() && notification->hasFormElements()) + { + LLHandlerUtil::spawnIMSession(name, from_id); + } - if (notification - && LLNotificationManager::getInstance()->getHandlerForNotification( - notification->getType()) == this) + if (notification->canLogToIM() && LLHandlerUtil::isIMFloaterOpened(notification)) { - LLNotifications::instance().cancel(notification); + return false; } + + LLToastPanel* notify_box = LLToastPanel::buidPanelFromNotification(notification); + + LLToast::Params p; + p.notif_id = notification->getID(); + p.notification = notification; + p.lifetime_secs = gSavedSettings.getS32("NotificationTipToastLifeTime"); + p.panel = notify_box; + p.is_tip = true; + p.can_be_stored = false; + + LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); + if(channel) + channel->addToast(p); + return false; } diff --git a/indra/newview/lloutputmonitorctrl.cpp b/indra/newview/lloutputmonitorctrl.cpp index 85626d8783..096e714981 100644 --- a/indra/newview/lloutputmonitorctrl.cpp +++ b/indra/newview/lloutputmonitorctrl.cpp @@ -28,6 +28,7 @@ #include "lloutputmonitorctrl.h" // library includes +#include "llfloaterreg.h" #include "llui.h" // viewer includes @@ -241,6 +242,17 @@ void LLOutputMonitorCtrl::draw() gl_rect_2d(0, monh, monw, 0, sColorBound, FALSE); } +// virtual +BOOL LLOutputMonitorCtrl::handleMouseUp(S32 x, S32 y, MASK mask) +{ + if (mSpeakerId != gAgentID) + { + LLFloaterReg::showInstance("floater_voice_volume", LLSD().with("avatar_id", mSpeakerId)); + } + + return TRUE; +} + void LLOutputMonitorCtrl::setSpeakerId(const LLUUID& speaker_id, const LLUUID& session_id/* = LLUUID::null*/) { if (speaker_id.isNull() && mSpeakerId.notNull()) diff --git a/indra/newview/lloutputmonitorctrl.h b/indra/newview/lloutputmonitorctrl.h index 2d23753d46..7b02e84744 100644 --- a/indra/newview/lloutputmonitorctrl.h +++ b/indra/newview/lloutputmonitorctrl.h @@ -68,6 +68,7 @@ public: // llview overrides virtual void draw(); + virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); void setPower(F32 val); F32 getPower(F32 val) const { return mPower; } diff --git a/indra/newview/llpanelblockedlist.cpp b/indra/newview/llpanelblockedlist.cpp index 5c85ec438c..35cda14f8d 100644 --- a/indra/newview/llpanelblockedlist.cpp +++ b/indra/newview/llpanelblockedlist.cpp @@ -30,15 +30,23 @@ // library include #include "llavatarname.h" +#include "llfiltereditor.h" #include "llfloater.h" #include "llfloaterreg.h" #include "llnotificationsutil.h" #include "llscrolllistctrl.h" +#include "llmenubutton.h" // project include +#include "llavatarlistitem.h" +#include "llblocklist.h" +#include "llblockedlistitem.h" #include "llfloateravatarpicker.h" #include "llfloatersidepanelcontainer.h" +#include "llinventorylistitem.h" +#include "llinventorymodel.h" #include "llsidetraypanelcontainer.h" +#include "llviewercontrol.h" static LLRegisterPanelClassWrapper<LLPanelBlockedList> t_panel_blocked_list("panel_block_list_sidetray"); @@ -54,26 +62,35 @@ const std::string BLOCKED_PARAM_NAME = "blocked_to_select"; LLPanelBlockedList::LLPanelBlockedList() : LLPanel() { - mCommitCallbackRegistrar.add("Block.ClickPick", boost::bind(&LLPanelBlockedList::onPickBtnClick, this)); - mCommitCallbackRegistrar.add("Block.ClickBlockByName", boost::bind(&LLPanelBlockedList::onBlockByNameClick, this)); - mCommitCallbackRegistrar.add("Block.ClickRemove", boost::bind(&LLPanelBlockedList::onRemoveBtnClick, this)); -} - -LLPanelBlockedList::~LLPanelBlockedList() -{ - LLMuteList::getInstance()->removeObserver(this); + mCommitCallbackRegistrar.add("Block.Action", boost::bind(&LLPanelBlockedList::onCustomAction, this, _2)); + mEnableCallbackRegistrar.add("Block.Check", boost::bind(&LLPanelBlockedList::isActionChecked, this, _2)); } BOOL LLPanelBlockedList::postBuild() { - mBlockedList = getChild<LLScrollListCtrl>("blocked"); + mBlockedList = getChild<LLBlockList>("blocked"); mBlockedList->setCommitOnSelectionChange(TRUE); - childSetCommitCallback("back", boost::bind(&LLPanelBlockedList::onBackBtnClick, this), NULL); + switch (gSavedSettings.getU32("BlockPeopleSortOrder")) + { + case E_SORT_BY_NAME: + mBlockedList->sortByName(); + break; + + case E_SORT_BY_TYPE: + mBlockedList->sortByType(); + break; + } + + // Use the context menu of the Block list for the Block tab gear menu. + LLToggleableMenu* blocked_gear_menu = mBlockedList->getContextMenu(); + if (blocked_gear_menu) + { + getChild<LLMenuButton>("blocked_gear_btn")->setMenu(blocked_gear_menu, LLMenuButton::MP_BOTTOM_LEFT); + } - LLMuteList::getInstance()->addObserver(this); - - refreshBlockedList(); + getChild<LLButton>("unblock_btn")->setCommitCallback(boost::bind(&LLPanelBlockedList::unblockItem, this)); + getChild<LLFilterEditor>("blocked_filter_input")->setCommitCallback(boost::bind(&LLPanelBlockedList::onFilterEdit, this, _2)); return LLPanel::postBuild(); } @@ -94,78 +111,77 @@ void LLPanelBlockedList::onOpen(const LLSD& key) void LLPanelBlockedList::selectBlocked(const LLUUID& mute_id) { - mBlockedList->selectByID(mute_id); + mBlockedList->selectItemByUUID(mute_id); } void LLPanelBlockedList::showPanelAndSelect(const LLUUID& idToSelect) { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD().with(BLOCKED_PARAM_NAME, idToSelect)); + LLFloaterSidePanelContainer::showPanel("people", "panel_people", + LLSD().with("people_panel_tab_name", "blocked_panel").with(BLOCKED_PARAM_NAME, idToSelect)); } ////////////////////////////////////////////////////////////////////////// // Private Section ////////////////////////////////////////////////////////////////////////// -void LLPanelBlockedList::refreshBlockedList() +void LLPanelBlockedList::updateButtons() { - mBlockedList->deleteAllItems(); + bool hasSelected = NULL != mBlockedList->getSelectedItem(); + getChildView("unblock_btn")->setEnabled(hasSelected); +} - std::vector<LLMute> mutes = LLMuteList::getInstance()->getMutes(); - std::vector<LLMute>::iterator it; - for (it = mutes.begin(); it != mutes.end(); ++it) +void LLPanelBlockedList::unblockItem() +{ + LLBlockedListItem* item = mBlockedList->getBlockedItem(); + if (item) { - LLScrollListItem::Params item_p; - item_p.enabled(TRUE); - item_p.value(it->mID); // link UUID of blocked item with ScrollListItem - item_p.columns.add().column("item_name").value(it->mName);//.type("text"); - item_p.columns.add().column("item_type").value(it->getDisplayType());//.type("text").width(111); - - mBlockedList->addRow(item_p, ADD_BOTTOM); + LLMute mute(item->getUUID(), item->getName()); + LLMuteList::instance().remove(mute); } } -void LLPanelBlockedList::updateButtons() +void LLPanelBlockedList::onCustomAction(const LLSD& userdata) { - bool hasSelected = NULL != mBlockedList->getFirstSelected(); - getChildView("Unblock")->setEnabled(hasSelected); -} - + const std::string command_name = userdata.asString(); - -void LLPanelBlockedList::onBackBtnClick() -{ - LLSideTrayPanelContainer* parent = dynamic_cast<LLSideTrayPanelContainer*>(getParent()); - if(parent) + if ("block_obj_by_name" == command_name) + { + blockObjectByName(); + } + else if ("block_res_by_name" == command_name) + { + blockResidentByName(); + } + else if ("sort_by_name" == command_name) + { + mBlockedList->sortByName(); + gSavedSettings.setU32("BlockPeopleSortOrder", E_SORT_BY_NAME); + } + else if ("sort_by_type" == command_name) { - parent->openPreviousPanel(); + mBlockedList->sortByType(); + gSavedSettings.setU32("BlockPeopleSortOrder", E_SORT_BY_TYPE); } } -void LLPanelBlockedList::onRemoveBtnClick() +BOOL LLPanelBlockedList::isActionChecked(const LLSD& userdata) { - std::string name = mBlockedList->getSelectedItemLabel(); - LLUUID id = mBlockedList->getStringUUIDSelectedItem(); - LLMute mute(id, name); - - S32 last_selected = mBlockedList->getFirstSelectedIndex(); - if (LLMuteList::getInstance()->remove(mute)) + std::string item = userdata.asString(); + U32 sort_order = gSavedSettings.getU32("BlockPeopleSortOrder"); + + if ("sort_by_name" == item) + { + return E_SORT_BY_NAME == sort_order; + } + else if ("sort_by_type" == item) { - // Above removals may rebuild this dialog. - - if (last_selected == mBlockedList->getItemCount()) - { - // we were on the last item, so select the last item again - mBlockedList->selectNthItem(last_selected - 1); - } - else - { - // else select the item after the last item previously selected - mBlockedList->selectNthItem(last_selected); - } + return E_SORT_BY_TYPE == sort_order; } + + return false; } -void LLPanelBlockedList::onPickBtnClick() +void LLPanelBlockedList::blockResidentByName() { const BOOL allow_multiple = FALSE; const BOOL close_on_select = TRUE; @@ -176,11 +192,19 @@ void LLPanelBlockedList::onPickBtnClick() // addDependentFloater(picker); } -void LLPanelBlockedList::onBlockByNameClick() +void LLPanelBlockedList::blockObjectByName() { LLFloaterGetBlockedObjectName::show(&LLPanelBlockedList::callbackBlockByName); } +void LLPanelBlockedList::onFilterEdit(const std::string& search_string) +{ + std::string filter = search_string; + LLStringUtil::trimHead(filter); + + mBlockedList->setNameFilter(filter); +} + void LLPanelBlockedList::callbackBlockPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName> names) { if (names.empty() || ids.empty()) return; diff --git a/indra/newview/llpanelblockedlist.h b/indra/newview/llpanelblockedlist.h index 74ad82e32d..332349dfc0 100644 --- a/indra/newview/llpanelblockedlist.h +++ b/indra/newview/llpanelblockedlist.h @@ -30,21 +30,15 @@ #include "llpanel.h" #include "llmutelist.h" #include "llfloater.h" -// #include <vector> -// class LLButton; -// class LLLineEditor; -// class LLMessageSystem; -// class LLUUID; class LLAvatarName; -class LLScrollListCtrl; +class LLBlockList; -class LLPanelBlockedList - : public LLPanel, public LLMuteListObserver +class LLPanelBlockedList : public LLPanel { public: LLPanelBlockedList(); - ~LLPanelBlockedList(); + ~LLPanelBlockedList(){}; virtual BOOL postBuild(); virtual void draw(); @@ -59,25 +53,31 @@ public: * If it is LLUUID::null, nothing will be selected. */ static void showPanelAndSelect(const LLUUID& idToSelect); - - // LLMuteListObserver callback interface implementation. - /* virtual */ void onChange() { refreshBlockedList();} private: - void refreshBlockedList(); + + typedef enum e_sort_oder{ + E_SORT_BY_NAME = 0, + E_SORT_BY_TYPE = 1, + } ESortOrder; + void updateButtons(); // UI callbacks - void onBackBtnClick(); - void onRemoveBtnClick(); - void onPickBtnClick(); - void onBlockByNameClick(); + void unblockItem(); + void blockResidentByName(); + void blockObjectByName(); + void onFilterEdit(const std::string& search_string); + + // List commnads + void onCustomAction(const LLSD& userdata); + BOOL isActionChecked(const LLSD& userdata); void callbackBlockPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName> names); static void callbackBlockByName(const std::string& text); private: - LLScrollListCtrl* mBlockedList; + LLBlockList* mBlockedList; }; //----------------------------------------------------------------------------- diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp index eda0749cdb..389baa86cd 100644 --- a/indra/newview/llpanelimcontrolpanel.cpp +++ b/indra/newview/llpanelimcontrolpanel.cpp @@ -1,31 +1,30 @@ -/** +/** * @file llpanelavatar.cpp * @brief LLPanelAvatar and related class implementations * * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" - #include "llfloaterreg.h" #include "llpanelimcontrolpanel.h" @@ -39,393 +38,7 @@ #include "llavatarlist.h" #include "llparticipantlist.h" #include "llimview.h" -#include "llvoicechannel.h" #include "llspeakers.h" #include "lltrans.h" -void LLPanelChatControlPanel::onCallButtonClicked() -{ - gIMMgr->startCall(mSessionId); -} - -void LLPanelChatControlPanel::onEndCallButtonClicked() -{ - gIMMgr->endCall(mSessionId); -} - -void LLPanelChatControlPanel::onOpenVoiceControlsClicked() -{ - LLFloaterReg::showInstance("voice_controls"); -} - -void LLPanelChatControlPanel::onChange(EStatusType status, const std::string &channelURI, bool proximal) -{ - if(status == STATUS_JOINING || status == STATUS_LEFT_CHANNEL) - { - return; - } - - updateCallButton(); -} - -void LLPanelChatControlPanel::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state) -{ - updateButtons(new_state); -} - -void LLPanelChatControlPanel::updateCallButton() -{ - // hide/show call button - bool voice_enabled = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); - - LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(mSessionId); - - if (!session) - { - getChildView("call_btn")->setEnabled(false); - return; - } - - bool session_initialized = session->mSessionInitialized; - bool callback_enabled = session->mCallBackEnabled; - - BOOL enable_connect = session_initialized - && voice_enabled - && callback_enabled; - getChildView("call_btn")->setEnabled(enable_connect); -} - -void LLPanelChatControlPanel::updateButtons(LLVoiceChannel::EState state) -{ - bool is_call_started = state >= LLVoiceChannel::STATE_CALL_STARTED; - getChildView("end_call_btn_panel")->setVisible( is_call_started); - getChildView("voice_ctrls_btn_panel")->setVisible( is_call_started && findChild<LLView>("voice_ctrls_btn_panel")); - getChildView("call_btn_panel")->setVisible( ! is_call_started); - - getChildView("volume_ctrl_panel")->setVisible(state == LLVoiceChannel::STATE_CONNECTED); - - updateCallButton(); - -} - -LLPanelChatControlPanel::~LLPanelChatControlPanel() -{ - mVoiceChannelStateChangeConnection.disconnect(); - if(LLVoiceClient::instanceExists()) - { - LLVoiceClient::getInstance()->removeObserver(this); - } -} - -BOOL LLPanelChatControlPanel::postBuild() -{ - childSetAction("call_btn", boost::bind(&LLPanelChatControlPanel::onCallButtonClicked, this)); - childSetAction("end_call_btn", boost::bind(&LLPanelChatControlPanel::onEndCallButtonClicked, this)); - childSetAction("voice_ctrls_btn", boost::bind(&LLPanelChatControlPanel::onOpenVoiceControlsClicked, this)); - - LLVoiceClient::getInstance()->addObserver(this); - - return TRUE; -} - -void LLPanelChatControlPanel::setSessionId(const LLUUID& session_id) -{ - //Method is called twice for AdHoc and Group chat. Second time when server init reply received - mSessionId = session_id; - LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionId); - if(voice_channel) - { - mVoiceChannelStateChangeConnection = voice_channel->setStateChangedCallback(boost::bind(&LLPanelChatControlPanel::onVoiceChannelStateChanged, this, _1, _2)); - - //call (either p2p, group or ad-hoc) can be already in started state - updateButtons(voice_channel->getState()); - } -} - -LLPanelIMControlPanel::LLPanelIMControlPanel() -{ -} - -LLPanelIMControlPanel::~LLPanelIMControlPanel() -{ - LLAvatarTracker::instance().removeParticularFriendObserver(mAvatarID, this); -} - -BOOL LLPanelIMControlPanel::postBuild() -{ - childSetAction("view_profile_btn", boost::bind(&LLPanelIMControlPanel::onViewProfileButtonClicked, this)); - childSetAction("add_friend_btn", boost::bind(&LLPanelIMControlPanel::onAddFriendButtonClicked, this)); - - childSetAction("share_btn", boost::bind(&LLPanelIMControlPanel::onShareButtonClicked, this)); - childSetAction("teleport_btn", boost::bind(&LLPanelIMControlPanel::onTeleportButtonClicked, this)); - childSetAction("pay_btn", boost::bind(&LLPanelIMControlPanel::onPayButtonClicked, this)); - - childSetAction("mute_btn", boost::bind(&LLPanelIMControlPanel::onClickMuteVolume, this)); - childSetAction("block_btn", boost::bind(&LLPanelIMControlPanel::onClickBlock, this)); - childSetAction("unblock_btn", boost::bind(&LLPanelIMControlPanel::onClickUnblock, this)); - - getChild<LLUICtrl>("volume_slider")->setCommitCallback(boost::bind(&LLPanelIMControlPanel::onVolumeChange, this, _2)); - - getChildView("add_friend_btn")->setEnabled(!LLAvatarActions::isFriend(getChild<LLAvatarIconCtrl>("avatar_icon")->getAvatarId())); - - setFocusReceivedCallback(boost::bind(&LLPanelIMControlPanel::onFocusReceived, this)); - - return LLPanelChatControlPanel::postBuild(); -} - -void LLPanelIMControlPanel::draw() -{ - bool is_muted = LLMuteList::getInstance()->isMuted(mAvatarID); - - getChild<LLUICtrl>("block_btn_panel")->setVisible(!is_muted); - getChild<LLUICtrl>("unblock_btn_panel")->setVisible(is_muted); - - if (getChildView("volume_ctrl_panel")->getVisible()) - { - - bool is_muted_voice = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat); - - LLUICtrl* mute_btn = getChild<LLUICtrl>("mute_btn"); - mute_btn->setValue( is_muted_voice ); - - LLUICtrl* volume_slider = getChild<LLUICtrl>("volume_slider"); - volume_slider->setEnabled( !is_muted_voice ); - - F32 volume; - - if (is_muted_voice) - { - // it's clearer to display their volume as zero - volume = 0.f; - } - else - { - // actual volume - volume = LLVoiceClient::getInstance()->getUserVolume(mAvatarID); - } - volume_slider->setValue( (F64)volume ); - } - - LLPanelChatControlPanel::draw(); -} - -void LLPanelIMControlPanel::onClickMuteVolume() -{ - // By convention, we only display and toggle voice mutes, not all mutes - LLMuteList* mute_list = LLMuteList::getInstance(); - bool is_muted = mute_list->isMuted(mAvatarID, LLMute::flagVoiceChat); - - LLMute mute(mAvatarID, getChild<LLTextBox>("avatar_name")->getText(), LLMute::AGENT); - if (!is_muted) - { - mute_list->add(mute, LLMute::flagVoiceChat); - } - else - { - mute_list->remove(mute, LLMute::flagVoiceChat); - } -} - -void LLPanelIMControlPanel::onClickBlock() -{ - LLMute mute(mAvatarID, getChild<LLTextBox>("avatar_name")->getText(), LLMute::AGENT); - - LLMuteList::getInstance()->add(mute); -} - -void LLPanelIMControlPanel::onClickUnblock() -{ - LLMute mute(mAvatarID, getChild<LLTextBox>("avatar_name")->getText(), LLMute::AGENT); - - LLMuteList::getInstance()->remove(mute); -} - -void LLPanelIMControlPanel::onVolumeChange(const LLSD& data) -{ - F32 volume = (F32)data.asReal(); - LLVoiceClient::getInstance()->setUserVolume(mAvatarID, volume); -} - -void LLPanelIMControlPanel::onTeleportButtonClicked() -{ - LLAvatarActions::offerTeleport(mAvatarID); -} -void LLPanelIMControlPanel::onPayButtonClicked() -{ - LLAvatarActions::pay(mAvatarID); -} - -void LLPanelIMControlPanel::onViewProfileButtonClicked() -{ - LLAvatarActions::showProfile(mAvatarID); -} - -void LLPanelIMControlPanel::onAddFriendButtonClicked() -{ - LLAvatarIconCtrl* avatar_icon = getChild<LLAvatarIconCtrl>("avatar_icon"); - std::string full_name = avatar_icon->getFullName(); - LLAvatarActions::requestFriendshipDialog(mAvatarID, full_name); -} - -void LLPanelIMControlPanel::onShareButtonClicked() -{ - LLAvatarActions::share(mAvatarID); -} - -void LLPanelIMControlPanel::onFocusReceived() -{ - // Disable all the buttons (Call, Teleport, etc) if disconnected. - if (gDisconnected) - { - setAllChildrenEnabled(FALSE); - } -} - -void LLPanelIMControlPanel::setSessionId(const LLUUID& session_id) -{ - LLPanelChatControlPanel::setSessionId(session_id); - - LLIMModel& im_model = LLIMModel::instance(); - - LLAvatarTracker::instance().removeParticularFriendObserver(mAvatarID, this); - mAvatarID = im_model.getOtherParticipantID(session_id); - LLAvatarTracker::instance().addParticularFriendObserver(mAvatarID, this); - - // Disable "Add friend" button for friends. - getChildView("add_friend_btn")->setEnabled(!LLAvatarActions::isFriend(mAvatarID)); - - // Disable "Teleport" button if friend is offline - if(LLAvatarActions::isFriend(mAvatarID)) - { - getChildView("teleport_btn")->setEnabled(LLAvatarTracker::instance().isBuddyOnline(mAvatarID)); - } - - getChild<LLAvatarIconCtrl>("avatar_icon")->setValue(mAvatarID); - - // Disable most profile buttons if the participant is - // not really an SL avatar (e.g., an Avaline caller). - LLIMModel::LLIMSession* im_session = - im_model.findIMSession(session_id); - if( im_session && !im_session->mOtherParticipantIsAvatar ) - { - getChildView("view_profile_btn")->setEnabled(FALSE); - getChildView("add_friend_btn")->setEnabled(FALSE); - - getChildView("share_btn")->setEnabled(FALSE); - getChildView("teleport_btn")->setEnabled(FALSE); - getChildView("pay_btn")->setEnabled(FALSE); - - getChild<LLTextBox>("avatar_name")->setValue(im_session->mName); - getChild<LLTextBox>("avatar_name")->setToolTip(im_session->mName); - } - else - { - // If the participant is an avatar, fetch the currect name - gCacheName->get(mAvatarID, false, - boost::bind(&LLPanelIMControlPanel::onNameCache, this, _1, _2, _3)); - } -} - -//virtual -void LLPanelIMControlPanel::changed(U32 mask) -{ - getChildView("add_friend_btn")->setEnabled(!LLAvatarActions::isFriend(mAvatarID)); - - // Disable "Teleport" button if friend is offline - if(LLAvatarActions::isFriend(mAvatarID)) - { - getChildView("teleport_btn")->setEnabled(LLAvatarTracker::instance().isBuddyOnline(mAvatarID)); - } -} - -void LLPanelIMControlPanel::onNameCache(const LLUUID& id, const std::string& full_name, bool is_group) -{ - if ( id == mAvatarID ) - { - std::string avatar_name = full_name; - getChild<LLTextBox>("avatar_name")->setValue(avatar_name); - getChild<LLTextBox>("avatar_name")->setToolTip(avatar_name); - - bool is_linden = LLStringUtil::endsWith(full_name, " Linden"); - getChild<LLUICtrl>("mute_btn")->setEnabled( !is_linden); - } -} - -LLPanelGroupControlPanel::LLPanelGroupControlPanel(const LLUUID& session_id): -mParticipantList(NULL) -{ -} - -BOOL LLPanelGroupControlPanel::postBuild() -{ - childSetAction("group_info_btn", boost::bind(&LLPanelGroupControlPanel::onGroupInfoButtonClicked, this)); - - return LLPanelChatControlPanel::postBuild(); -} - -LLPanelGroupControlPanel::~LLPanelGroupControlPanel() -{ - delete mParticipantList; - mParticipantList = NULL; -} - -// virtual -void LLPanelGroupControlPanel::draw() -{ - // Need to resort the participant list if it's in sort by recent speaker order. - if (mParticipantList) - mParticipantList->update(); - LLPanelChatControlPanel::draw(); -} - -void LLPanelGroupControlPanel::onGroupInfoButtonClicked() -{ - LLGroupActions::show(mGroupID); -} - -void LLPanelGroupControlPanel::onSortMenuItemClicked(const LLSD& userdata) -{ - // TODO: Check this code when when sort order menu will be added. (EM) - if (false && !mParticipantList) - return; - - std::string chosen_item = userdata.asString(); - - if (chosen_item == "sort_name") - { - mParticipantList->setSortOrder(LLParticipantList::E_SORT_BY_NAME); - } - -} - -void LLPanelGroupControlPanel::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state) -{ - LLPanelChatControlPanel::onVoiceChannelStateChanged(old_state, new_state); - mParticipantList->setSpeakingIndicatorsVisible(new_state >= LLVoiceChannel::STATE_CALL_STARTED); -} - -void LLPanelGroupControlPanel::setSessionId(const LLUUID& session_id) -{ - LLPanelChatControlPanel::setSessionId(session_id); - - mGroupID = session_id; - - // for group and Ad-hoc chat we need to include agent into list - if(!mParticipantList) - { - LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(session_id); - mParticipantList = new LLParticipantList(speaker_manager, getChild<LLAvatarList>("speakers_list"), true,false); - } -} - - -LLPanelAdHocControlPanel::LLPanelAdHocControlPanel(const LLUUID& session_id):LLPanelGroupControlPanel(session_id) -{ -} - -BOOL LLPanelAdHocControlPanel::postBuild() -{ - //We don't need LLPanelGroupControlPanel::postBuild() to be executed as there is no group_info_btn at AdHoc chat - return LLPanelChatControlPanel::postBuild(); -} diff --git a/indra/newview/llpanelimcontrolpanel.h b/indra/newview/llpanelimcontrolpanel.h index bba847b5d4..02915ec4bb 100644 --- a/indra/newview/llpanelimcontrolpanel.h +++ b/indra/newview/llpanelimcontrolpanel.h @@ -28,14 +28,12 @@ #define LL_LLPANELIMCONTROLPANEL_H #include "llpanel.h" -#include "llvoicechannel.h" #include "llcallingcard.h" class LLParticipantList; -class LLPanelChatControlPanel +class LLPanelChatControlPanel : public LLPanel - , public LLVoiceClientStatusObserver { public: LLPanelChatControlPanel() : @@ -44,21 +42,6 @@ public: virtual BOOL postBuild(); - void onCallButtonClicked(); - void onEndCallButtonClicked(); - void onOpenVoiceControlsClicked(); - - // Implements LLVoiceClientStatusObserver::onChange() to enable the call - // button when voice is available - /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); - - virtual void onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state); - - void updateButtons(LLVoiceChannel::EState state); - - // Enables/disables call button depending on voice availability - void updateCallButton(); - virtual void setSessionId(const LLUUID& session_id); const LLUUID& getSessionId() { return mSessionId; } @@ -69,41 +52,6 @@ private: boost::signals2::connection mVoiceChannelStateChangeConnection; }; - -class LLPanelIMControlPanel : public LLPanelChatControlPanel, LLFriendObserver -{ -public: - LLPanelIMControlPanel(); - ~LLPanelIMControlPanel(); - - BOOL postBuild(); - - void setSessionId(const LLUUID& session_id); - - // LLFriendObserver trigger - virtual void changed(U32 mask); - -protected: - void onNameCache(const LLUUID& id, const std::string& full_name, bool is_group); - -private: - void onViewProfileButtonClicked(); - void onAddFriendButtonClicked(); - void onShareButtonClicked(); - void onTeleportButtonClicked(); - void onPayButtonClicked(); - void onFocusReceived(); - - void onClickMuteVolume(); - void onClickBlock(); - void onClickUnblock(); - /*virtual*/ void draw(); - void onVolumeChange(const LLSD& data); - - LLUUID mAvatarID; -}; - - class LLPanelGroupControlPanel : public LLPanelChatControlPanel { public: @@ -121,9 +69,7 @@ protected: LLParticipantList* mParticipantList; private: - void onGroupInfoButtonClicked(); void onSortMenuItemClicked(const LLSD& userdata); - /*virtual*/ void onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state); }; class LLPanelAdHocControlPanel : public LLPanelGroupControlPanel diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index 68a3b6d1cd..9225ea3d53 100644 --- a/indra/newview/llpanellandmarks.cpp +++ b/indra/newview/llpanellandmarks.cpp @@ -102,7 +102,7 @@ void LLCheckFolderState::doFolder(LLFolderViewFolder* folder) // Counting only folders that pass the filter. // The listener check allow us to avoid counting the folder view // object itself because it has no listener assigned. - if (folder->hasFilteredDescendants() && folder->getListener()) + if (folder->getViewModelItem()->descendantsPassedFilter()) { if (folder->isOpen()) { @@ -138,7 +138,7 @@ private: // virtual void LLOpenFolderByID::doFolder(LLFolderViewFolder* folder) { - if (folder->getListener() && folder->getListener()->getUUID() == mFolderID) + if (folder->getViewModelItem() && static_cast<LLFolderViewModelItemInventory*>(folder->getViewModelItem())->getUUID() == mFolderID) { if (!folder->isOpen()) { @@ -177,7 +177,7 @@ void LLLandmarksPanelObserver::changed(U32 mask) if (!mIsLibraryLandmarksOpen && library) { // Search for "Landmarks" folder in the Library and open it once on start up. See EXT-4827. - const LLUUID &landmarks_cat = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false, true); + const LLUUID &landmarks_cat = gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_LANDMARK, false); if (landmarks_cat.notNull()) { LLOpenFolderByID opener(landmarks_cat); @@ -247,10 +247,7 @@ void LLLandmarksPanel::onSearchEdit(const std::string& string) LLPlacesInventoryPanel* inventory_list = dynamic_cast<LLPlacesInventoryPanel*>(tab->getAccordionView()); if (NULL == inventory_list) continue; - if (inventory_list->getFilter()) - { - filter_list(inventory_list, string); - } + filter_list(inventory_list, string); } if (sFilterSubString != string) @@ -281,28 +278,21 @@ void LLLandmarksPanel::onShowOnMap() //virtual void LLLandmarksPanel::onShowProfile() { - LLFolderViewItem* cur_item = getCurSelectedItem(); + LLFolderViewModelItemInventory* cur_item = getCurSelectedViewModelItem(); if(!cur_item) return; - cur_item->getListener()->performAction(mCurrentSelectedList->getModel(),"about"); + cur_item->performAction(mCurrentSelectedList->getModel(),"about"); } // virtual void LLLandmarksPanel::onTeleport() { - LLFolderViewItem* current_item = getCurSelectedItem(); - if (!current_item) - { - llwarns << "There are no selected list. No actions are performed." << llendl; - return; - } - - LLFolderViewEventListener* listenerp = current_item->getListener(); - if (listenerp && listenerp->getInventoryType() == LLInventoryType::IT_LANDMARK) + LLFolderViewModelItemInventory* view_model_item = getCurSelectedViewModelItem(); + if (view_model_item && view_model_item->getInventoryType() == LLInventoryType::IT_LANDMARK) { - listenerp->openItem(); + view_model_item->openItem(); } } @@ -313,8 +303,7 @@ bool LLLandmarksPanel::isSingleItemSelected() if (mCurrentSelectedList != NULL) { - LLPlacesFolderView* root_view = - static_cast<LLPlacesFolderView*>(mCurrentSelectedList->getRootFolder()); + LLFolderView* root_view = mCurrentSelectedList->getRootFolder(); if (root_view->getSelectedCount() == 1) { @@ -360,7 +349,7 @@ void LLLandmarksPanel::onSelectorButtonClicked() LLFolderViewItem* cur_item = mFavoritesInventoryPanel->getRootFolder()->getCurSelectedItem(); if (!cur_item) return; - LLFolderViewEventListener* listenerp = cur_item->getListener(); + LLFolderViewModelItemInventory* listenerp = static_cast<LLFolderViewModelItemInventory*>(cur_item->getViewModelItem()); if (listenerp->getInventoryType() == LLInventoryType::IT_LANDMARK) { LLSD key; @@ -373,10 +362,7 @@ void LLLandmarksPanel::onSelectorButtonClicked() void LLLandmarksPanel::updateShowFolderState() { - if (!mLandmarksInventoryPanel->getFilter()) - return; - - bool show_all_folders = mLandmarksInventoryPanel->getRootFolder()->getFilterSubString().empty(); + bool show_all_folders = mLandmarksInventoryPanel->getFilterSubString().empty(); if (show_all_folders) { show_all_folders = category_has_descendents(mLandmarksInventoryPanel); @@ -417,8 +403,9 @@ void LLLandmarksPanel::setItemSelected(const LLUUID& obj_id, BOOL take_keyboard_ bool LLLandmarksPanel::isLandmarkSelected() const { - LLFolderViewItem* current_item = getCurSelectedItem(); - if(current_item && current_item->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK) + LLFolderViewModelItemInventory* current_item = getCurSelectedViewModelItem(); + + if(current_item && current_item->getInventoryType() == LLInventoryType::IT_LANDMARK) { return true; } @@ -440,10 +427,10 @@ bool LLLandmarksPanel::isReceivedFolderSelected() const void LLLandmarksPanel::doActionOnCurSelectedLandmark(LLLandmarkList::loaded_callback_t cb) { - LLFolderViewItem* cur_item = getCurSelectedItem(); - if(cur_item && cur_item->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK) + LLFolderViewModelItemInventory* cur_item = getCurSelectedViewModelItem(); + if(cur_item && cur_item->getInventoryType() == LLInventoryType::IT_LANDMARK) { - LLLandmark* landmark = LLLandmarkActions::getLandmark(cur_item->getListener()->getUUID(), cb); + LLLandmark* landmark = LLLandmarkActions::getLandmark(cur_item->getUUID(), cb); if (landmark) { cb(landmark); @@ -456,6 +443,17 @@ LLFolderViewItem* LLLandmarksPanel::getCurSelectedItem() const return mCurrentSelectedList ? mCurrentSelectedList->getRootFolder()->getCurSelectedItem() : NULL; } +LLFolderViewModelItemInventory* LLLandmarksPanel::getCurSelectedViewModelItem() const +{ + LLFolderViewItem* cur_item = getCurSelectedItem(); + if (cur_item) + { + return static_cast<LLFolderViewModelItemInventory*>(cur_item->getViewModelItem()); + } + return NULL; +} + + LLFolderViewItem* LLLandmarksPanel::selectItemInAccordionTab(LLPlacesInventoryPanel* inventory_list, const std::string& tab_name, const LLUUID& obj_id, @@ -466,7 +464,7 @@ LLFolderViewItem* LLLandmarksPanel::selectItemInAccordionTab(LLPlacesInventoryPa LLFolderView* root = inventory_list->getRootFolder(); - LLFolderViewItem* item = root->getItemByID(obj_id); + LLFolderViewItem* item = inventory_list->getItemByID(obj_id); if (!item) return NULL; @@ -508,12 +506,12 @@ void LLLandmarksPanel::processParcelInfo(const LLParcelData& parcel_data) // We have to make request to sever to get parcel_id and snaption_id. if(isLandmarkSelected()) { - LLFolderViewItem* cur_item = getCurSelectedItem(); + LLFolderViewModelItemInventory* cur_item = getCurSelectedViewModelItem(); if (!cur_item) return; - LLUUID id = cur_item->getListener()->getUUID(); + LLUUID id = cur_item->getUUID(); LLInventoryItem* inv_item = mCurrentSelectedList->getModel()->getItem(id); doActionOnCurSelectedLandmark(boost::bind( - &LLLandmarksPanel::doProcessParcelInfo, this, _1, cur_item, inv_item, parcel_data)); + &LLLandmarksPanel::doProcessParcelInfo, this, _1, getCurSelectedItem(), inv_item, parcel_data)); } } @@ -543,7 +541,7 @@ void LLLandmarksPanel::initFavoritesInventoryPanel() mFavoritesInventoryPanel = getChild<LLPlacesInventoryPanel>("favorites_list"); initLandmarksPanel(mFavoritesInventoryPanel); - mFavoritesInventoryPanel->getFilter()->setEmptyLookupMessage("FavoritesNoMatchingItems"); + mFavoritesInventoryPanel->getFilter().setEmptyLookupMessage("FavoritesNoMatchingItems"); initAccordion("tab_favorites", mFavoritesInventoryPanel, true); } @@ -554,12 +552,7 @@ void LLLandmarksPanel::initLandmarksInventoryPanel() initLandmarksPanel(mLandmarksInventoryPanel); - // Check if mLandmarksInventoryPanel is properly initialized and has a Filter created. - // In case of a dummy widget getFilter() will return NULL. - if (mLandmarksInventoryPanel->getFilter()) - { - mLandmarksInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS); - } + mLandmarksInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS); // subscribe to have auto-rename functionality while creating New Folder mLandmarksInventoryPanel->setSelectCallback(boost::bind(&LLInventoryPanel::onSelectionChange, mLandmarksInventoryPanel, _1, _2)); @@ -583,7 +576,7 @@ void LLLandmarksPanel::initLibraryInventoryPanel() initLandmarksPanel(mLibraryInventoryPanel); // We want to fetch only "Landmarks" category from the library. - const LLUUID &landmarks_cat = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false, true); + const LLUUID &landmarks_cat = gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_LANDMARK, false); if (landmarks_cat.notNull()) { LLInventoryModelBackgroundFetch::instance().start(landmarks_cat); @@ -595,12 +588,7 @@ void LLLandmarksPanel::initLibraryInventoryPanel() void LLLandmarksPanel::initLandmarksPanel(LLPlacesInventoryPanel* inventory_list) { - // In case of a dummy widget further we have no Folder View widget and no Filter, - // so further initialization leads to crash. - if (!inventory_list->getFilter()) - return; - - inventory_list->getFilter()->setEmptyLookupMessage("PlacesNoMatchingItems"); + inventory_list->getFilter().setEmptyLookupMessage("PlacesNoMatchingItems"); inventory_list->setFilterTypes(0x1 << LLInventoryType::IT_LANDMARK); inventory_list->setSelectCallback(boost::bind(&LLLandmarksPanel::onSelectionChange, this, inventory_list, _1, _2)); @@ -665,20 +653,20 @@ void LLLandmarksPanel::deselectOtherThan(const LLPlacesInventoryPanel* inventory { if (inventory_list != mFavoritesInventoryPanel) { - mFavoritesInventoryPanel->getRootFolder()->clearSelection(); + mFavoritesInventoryPanel->clearSelection(); } if (inventory_list != mLandmarksInventoryPanel) { - mLandmarksInventoryPanel->getRootFolder()->clearSelection(); + mLandmarksInventoryPanel->clearSelection(); } if (inventory_list != mMyInventoryPanel) { - mMyInventoryPanel->getRootFolder()->clearSelection(); + mMyInventoryPanel->clearSelection(); } if (inventory_list != mLibraryInventoryPanel) { - mLibraryInventoryPanel->getRootFolder()->clearSelection(); + mLibraryInventoryPanel->clearSelection(); } } @@ -731,14 +719,9 @@ void LLLandmarksPanel::onActionsButtonClick() { LLToggleableMenu* menu = mGearFolderMenu; - LLFolderViewItem* cur_item = NULL; if(mCurrentSelectedList) { - cur_item = mCurrentSelectedList->getRootFolder()->getCurSelectedItem(); - if(!cur_item) - return; - - LLFolderViewEventListener* listenerp = cur_item->getListener(); + LLFolderViewModelItemInventory* listenerp = getCurSelectedViewModelItem(); if(!listenerp) return; @@ -776,6 +759,9 @@ void LLLandmarksPanel::onTrashButtonClick() const void LLLandmarksPanel::onAddAction(const LLSD& userdata) const { + LLFolderViewModelItemInventory* view_model = getCurSelectedViewModelItem(); + LLFolderViewItem* item = getCurSelectedItem(); + std::string command_name = userdata.asString(); if("add_landmark" == command_name) { @@ -791,24 +777,24 @@ void LLLandmarksPanel::onAddAction(const LLSD& userdata) const } else if ("category" == command_name) { - LLFolderViewItem* item = getCurSelectedItem(); if (item && mCurrentSelectedList == mLandmarksInventoryPanel) { - LLFolderViewEventListener* folder_bridge = NULL; - if (item-> getListener()->getInventoryType() + LLFolderViewModelItem* folder_bridge = NULL; + + if (view_model->getInventoryType() == LLInventoryType::IT_LANDMARK) { // for a landmark get parent folder bridge - folder_bridge = item->getParentFolder()->getListener(); + folder_bridge = item->getParentFolder()->getViewModelItem(); } - else if (item-> getListener()->getInventoryType() + else if (view_model->getInventoryType() == LLInventoryType::IT_CATEGORY) { // for a folder get its own bridge - folder_bridge = item->getListener(); + folder_bridge = view_model; } - menu_create_inventory_item(mCurrentSelectedList->getRootFolder(), + menu_create_inventory_item(mCurrentSelectedList, dynamic_cast<LLFolderBridge*> (folder_bridge), LLSD( "category"), gInventory.findCategoryUUIDForType( LLFolderType::FT_LANDMARK)); @@ -816,7 +802,7 @@ void LLLandmarksPanel::onAddAction(const LLSD& userdata) const else { //in case My Landmarks tab is completely empty (thus cannot be determined as being selected) - menu_create_inventory_item(mLandmarksInventoryPanel->getRootFolder(), NULL, LLSD("category"), + menu_create_inventory_item(mLandmarksInventoryPanel, NULL, LLSD("category"), gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK)); if (mMyLandmarksAccordionTab) @@ -834,9 +820,9 @@ void LLLandmarksPanel::onClipboardAction(const LLSD& userdata) const std::string command_name = userdata.asString(); if("copy_slurl" == command_name) { - LLFolderViewItem* cur_item = getCurSelectedItem(); + LLFolderViewModelItemInventory* cur_item = getCurSelectedViewModelItem(); if(cur_item) - LLLandmarkActions::copySLURLtoClipboard(cur_item->getListener()->getUUID()); + LLLandmarkActions::copySLURLtoClipboard(cur_item->getUUID()); } else if ( "paste" == command_name) { @@ -848,7 +834,7 @@ void LLLandmarksPanel::onClipboardAction(const LLSD& userdata) const } else { - mCurrentSelectedList->getRootFolder()->doToSelected(mCurrentSelectedList->getModel(),command_name); + mCurrentSelectedList->doToSelected(command_name); } } @@ -893,7 +879,7 @@ void LLLandmarksPanel::onFoldingAction(const LLSD& userdata) { if(mCurrentSelectedList) { - mCurrentSelectedList->getRootFolder()->doToSelected(&gInventory, userdata); + mCurrentSelectedList->doToSelected(userdata); } } } @@ -915,8 +901,9 @@ bool LLLandmarksPanel::isActionEnabled(const LLSD& userdata) const { std::string command_name = userdata.asString(); - LLPlacesFolderView* root_folder_view = mCurrentSelectedList ? - static_cast<LLPlacesFolderView*>(mCurrentSelectedList->getRootFolder()) : NULL; + LLFolderView* root_folder_view = mCurrentSelectedList + ? mCurrentSelectedList->getRootFolder() + : NULL; if ("collapse_all" == command_name) { @@ -977,18 +964,13 @@ bool LLLandmarksPanel::isActionEnabled(const LLSD& userdata) const { if (!root_folder_view) return false; - std::set<LLUUID> selected_uuids = root_folder_view->getSelectionList(); + std::set<LLFolderViewItem*> selected_uuids = root_folder_view->getSelectionList(); // Allow to execute the command only if it can be applied to all selected items. - for (std::set<LLUUID>::const_iterator iter = selected_uuids.begin(); iter != selected_uuids.end(); ++iter) + for (std::set<LLFolderViewItem*>::const_iterator iter = selected_uuids.begin(); iter != selected_uuids.end(); ++iter) { - LLFolderViewItem* item = root_folder_view->getItemByID(*iter); + LLFolderViewItem* item = *iter; - // If no item is found it might be a folder id. - if (!item) - { - item = root_folder_view->getFolderByID(*iter); - } if (!item) return false; if (!canItemBeModified(command_name, item)) return false; @@ -1012,10 +994,10 @@ bool LLLandmarksPanel::isActionEnabled(const LLSD& userdata) const if ("show_on_map" == command_name) { - LLFolderViewItem* cur_item = root_folder_view->getCurSelectedItem(); + LLFolderViewModelItemInventory* cur_item = getCurSelectedViewModelItem(); if (!cur_item) return false; - LLViewerInventoryItem* inv_item = cur_item->getInventoryItem(); + LLViewerInventoryItem* inv_item = dynamic_cast<LLViewerInventoryItem*>(cur_item->getInventoryObject()); if (!inv_item) return false; LLUUID asset_uuid = inv_item->getAssetUUID(); @@ -1049,7 +1031,7 @@ bool LLLandmarksPanel::isActionEnabled(const LLSD& userdata) const { if (mCurrentSelectedList) { - std::set<LLUUID> selection = mCurrentSelectedList->getRootFolder()->getSelectionList(); + std::set<LLFolderViewItem*> selection = mCurrentSelectedList->getRootFolder()->getSelectionList(); if (!selection.empty()) { return ( 1 == selection.size() && !LLAgentPicksInfo::getInstance()->isPickLimitReached() ); @@ -1105,27 +1087,23 @@ void LLLandmarksPanel::onMenuVisibilityChange(LLUICtrl* ctrl, const LLSD& param) { const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); - std::set<LLUUID> selected_uuids = root_folder_view->getSelectionList(); + std::set<LLFolderViewItem*> selected_items = root_folder_view->getSelectionList(); // Iterate through selected items to find out if any of these items are in Trash // or all the items are in Trash category. - for (std::set<LLUUID>::const_iterator iter = selected_uuids.begin(); iter != selected_uuids.end(); ++iter) + for (std::set<LLFolderViewItem*>::const_iterator iter = selected_items.begin(); iter != selected_items.end(); ++iter) { - LLFolderViewItem* item = root_folder_view->getItemByID(*iter); + LLFolderViewItem* item = *iter; // If no item is found it might be a folder id. - if (!item) - { - item = root_folder_view->getFolderByID(*iter); - } if (!item) continue; - LLFolderViewEventListener* listenerp = item->getListener(); + LLFolderViewModelItemInventory* listenerp = static_cast<LLFolderViewModelItemInventory*>(item->getViewModelItem()); if(!listenerp) continue; // Trash category itself should not be included because it can't be // actually restored from trash. - are_all_items_in_trash &= listenerp->isItemInTrash() && *iter != trash_id; + are_all_items_in_trash &= listenerp->isItemInTrash() && listenerp->getUUID() != trash_id; // If there are any selected items in Trash including the Trash category itself // we show "Restore Item" in context menu and hide other irrelevant items. @@ -1164,7 +1142,7 @@ bool LLLandmarksPanel::canItemBeModified(const std::string& command_name, LLFold bool can_be_modified = false; // landmarks can be modified in any other accordion... - if (item->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK) + if (static_cast<LLFolderViewModelItemInventory*>(item->getViewModelItem())->getInventoryType() == LLInventoryType::IT_LANDMARK) { can_be_modified = true; @@ -1202,7 +1180,7 @@ bool LLLandmarksPanel::canItemBeModified(const std::string& command_name, LLFold if (can_be_modified) { - LLFolderViewEventListener* listenerp = item->getListener(); + LLFolderViewModelItemInventory* listenerp = static_cast<LLFolderViewModelItemInventory*>(item->getViewModelItem()); if ("cut" == command_name) { @@ -1262,8 +1240,9 @@ bool LLLandmarksPanel::handleDragAndDropToTrash(BOOL drop, EDragAndDropType carg LLInventoryItem* item = static_cast<LLInventoryItem*>(cargo_data); if (item) { - LLFolderViewItem* fv_item = (mCurrentSelectedList && mCurrentSelectedList->getRootFolder()) ? - mCurrentSelectedList->getRootFolder()->getItemByID(item->getUUID()) : NULL; + LLFolderViewItem* fv_item = mCurrentSelectedList + ? mCurrentSelectedList->getItemByID(item->getUUID()) + : NULL; if (fv_item) { @@ -1391,7 +1370,7 @@ void LLLandmarksPanel::doCreatePick(LLLandmark* landmark) static void filter_list(LLPlacesInventoryPanel* inventory_list, const std::string& string) { // When search is cleared, restore the old folder state. - if (!inventory_list->getRootFolder()->getFilterSubString().empty() && string == "") + if (!inventory_list->getFilterSubString().empty() && string == "") { inventory_list->setFilterSubString(LLStringUtil::null); // Re-open folders that were open before @@ -1405,7 +1384,7 @@ static void filter_list(LLPlacesInventoryPanel* inventory_list, const std::strin } // save current folder open state if no filter currently applied - if (inventory_list->getRootFolder()->getFilterSubString().empty()) + if (inventory_list->getFilterSubString().empty()) { inventory_list->saveFolderState(); } diff --git a/indra/newview/llpanellandmarks.h b/indra/newview/llpanellandmarks.h index b2f4e92473..aa5f69739d 100644 --- a/indra/newview/llpanellandmarks.h +++ b/indra/newview/llpanellandmarks.h @@ -44,6 +44,7 @@ class LLMenuGL; class LLToggleableMenu; class LLInventoryPanel; class LLPlacesInventoryPanel; +class LLFolderViewModelItemInventory; class LLLandmarksPanel : public LLPanelPlacesTab, LLRemoteParcelInfoObserver { @@ -87,6 +88,7 @@ protected: bool isReceivedFolderSelected() const; void doActionOnCurSelectedLandmark(LLLandmarkList::loaded_callback_t cb); LLFolderViewItem* getCurSelectedItem() const; + LLFolderViewModelItemInventory* getCurSelectedViewModelItem() const; /** * Selects item with "obj_id" in "inventory_list" and scrolls accordion diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index c11597f532..e1aa70cc4a 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -47,12 +47,14 @@ #include "llresmgr.h" #include "llscrollcontainer.h" #include "llsdserialize.h" +#include "llsdparam.h" #include "llspinctrl.h" #include "lltoggleablemenu.h" #include "lltooldraganddrop.h" #include "llviewermenu.h" #include "llviewertexturelist.h" #include "llsidepanelinventory.h" +#include "llfolderview.h" const std::string FILTERS_FILENAME("filters.xml"); @@ -87,9 +89,9 @@ public: static void selectNoTypes(void* user_data); private: LLPanelMainInventory* mPanelMainInventory; - LLSpinCtrl* mSpinSinceDays; - LLSpinCtrl* mSpinSinceHours; - LLInventoryFilter* mFilter; + LLSpinCtrl* mSpinSinceDays; + LLSpinCtrl* mSpinSinceHours; + LLInventoryFilter* mFilter; }; ///---------------------------------------------------------------------------- @@ -129,7 +131,7 @@ BOOL LLPanelMainInventory::postBuild() mFilterTabs = getChild<LLTabContainer>("inventory filter tabs"); mFilterTabs->setCommitCallback(boost::bind(&LLPanelMainInventory::onFilterSelected, this)); - //panel->getFilter()->markDefault(); + //panel->getFilter().markDefault(); // Set up the default inv. panel/filter settings. mActivePanel = getChild<LLInventoryPanel>("All Items"); @@ -137,7 +139,7 @@ BOOL LLPanelMainInventory::postBuild() { // "All Items" is the previous only view, so it gets the InventorySortOrder mActivePanel->setSortOrder(gSavedSettings.getU32(LLInventoryPanel::DEFAULT_SORT_ORDER)); - mActivePanel->getFilter()->markDefault(); + mActivePanel->getFilter().markDefault(); mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); mActivePanel->setSelectCallback(boost::bind(&LLPanelMainInventory::onSelectionChange, this, mActivePanel, _1, _2)); mResortActivePanel = true; @@ -148,7 +150,7 @@ BOOL LLPanelMainInventory::postBuild() recent_items_panel->setSinceLogoff(TRUE); recent_items_panel->setSortOrder(LLInventoryFilter::SO_DATE); recent_items_panel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); - recent_items_panel->getFilter()->markDefault(); + recent_items_panel->getFilter().markDefault(); recent_items_panel->setSelectCallback(boost::bind(&LLPanelMainInventory::onSelectionChange, this, recent_items_panel, _1, _2)); } @@ -167,11 +169,14 @@ BOOL LLPanelMainInventory::postBuild() // Note that the "All Items" settings do not persist. if(recent_items_panel) { - if(savedFilterState.has(recent_items_panel->getFilter()->getName())) + if(savedFilterState.has(recent_items_panel->getFilter().getName())) { LLSD recent_items = savedFilterState.get( - recent_items_panel->getFilter()->getName()); - recent_items_panel->getFilter()->fromLLSD(recent_items); + recent_items_panel->getFilter().getName()); + LLInventoryFilter::Params p; + LLParamSDParser parser; + parser.readSD(recent_items, p); + recent_items_panel->getFilter().fromParams(p); } } @@ -208,24 +213,28 @@ LLPanelMainInventory::~LLPanelMainInventory( void ) LLInventoryPanel* all_items_panel = getChild<LLInventoryPanel>("All Items"); if (all_items_panel) { - LLInventoryFilter* filter = all_items_panel->getFilter(); - if (filter) + LLSD filterState; + LLInventoryPanel::InventoryState p; + all_items_panel->getFilter().toParams(p.filter); + all_items_panel->getRootViewModel().getSorter().toParams(p.sort); + if (p.validateBlock(false)) { - LLSD filterState; - filter->toLLSD(filterState); - filterRoot[filter->getName()] = filterState; + LLParamSDParser().writeSD(filterState, p); + filterRoot[all_items_panel->getName()] = filterState; } } - LLInventoryPanel* recent_items_panel = getChild<LLInventoryPanel>("Recent Items"); - if (recent_items_panel) + LLInventoryPanel* panel = findChild<LLInventoryPanel>("Recent Items"); + if (panel) { - LLInventoryFilter* filter = recent_items_panel->getFilter(); - if (filter) + LLSD filterState; + LLInventoryPanel::InventoryState p; + panel->getFilter().toParams(p.filter); + panel->getRootViewModel().getSorter().toParams(p.sort); + if (p.validateBlock(false)) { - LLSD filterState; - filter->toLLSD(filterState); - filterRoot[filter->getName()] = filterState; + LLParamSDParser().writeSD(filterState, p); + filterRoot[panel->getName()] = filterState; } } @@ -285,7 +294,7 @@ BOOL LLPanelMainInventory::handleKeyHere(KEY key, MASK mask) void LLPanelMainInventory::doToSelected(const LLSD& userdata) { - getPanel()->getRootFolder()->doToSelected(&gInventory, userdata); + getPanel()->doToSelected(userdata); } void LLPanelMainInventory::closeAllFolders() @@ -306,13 +315,13 @@ void LLPanelMainInventory::newWindow() void LLPanelMainInventory::doCreate(const LLSD& userdata) { - menu_create_inventory_item(getPanel()->getRootFolder(), NULL, userdata); + menu_create_inventory_item(getPanel(), NULL, userdata); } void LLPanelMainInventory::resetFilters() { LLFloaterInventoryFinder *finder = getFinder(); - getActivePanel()->getFilter()->resetDefault(); + getActivePanel()->getFilter().resetDefault(); if (finder) { finder->updateElementsFromFilter(); @@ -417,7 +426,7 @@ void LLPanelMainInventory::onFilterEdit(const std::string& search_string ) } // save current folder open state if no filter currently applied - if (!mActivePanel->getRootFolder()->isFilterModified()) + if (!mActivePanel->getFilter().isNotDefault()) { mSavedFolderState->setApply(FALSE); mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); @@ -479,13 +488,13 @@ void LLPanelMainInventory::onFilterSelected() } setFilterSubString(mFilterSubString); - LLInventoryFilter* filter = mActivePanel->getFilter(); + LLInventoryFilter& filter = mActivePanel->getFilter(); LLFloaterInventoryFinder *finder = getFinder(); if (finder) { - finder->changeFilter(filter); + finder->changeFilter(&filter); } - if (filter->isActive()) + if (filter.isActive()) { // If our filter is active we may be the first thing requiring a fetch so we better start it here. LLInventoryModelBackgroundFetch::instance().start(); @@ -598,7 +607,7 @@ void LLPanelMainInventory::onFocusReceived() void LLPanelMainInventory::setFilterTextFromFilter() { - mFilterText = mActivePanel->getFilter()->getFilterText(); + mFilterText = mActivePanel->getFilter().getFilterText(); } void LLPanelMainInventory::toggleFindOptions() @@ -648,7 +657,7 @@ LLFloaterInventoryFinder* LLPanelMainInventory::getFinder() LLFloaterInventoryFinder::LLFloaterInventoryFinder(LLPanelMainInventory* inventory_view) : LLFloater(LLSD()), mPanelMainInventory(inventory_view), - mFilter(inventory_view->getPanel()->getFilter()) + mFilter(&inventory_view->getPanel()->getFilter()) { buildFromFile("floater_inventory_view_finder.xml"); updateElementsFromFilter(); @@ -961,7 +970,7 @@ void LLPanelMainInventory::onTrashButtonClick() void LLPanelMainInventory::onClipboardAction(const LLSD& userdata) { std::string command_name = userdata.asString(); - getActivePanel()->getRootFolder()->doToSelected(getActivePanel()->getModel(),command_name); + getActivePanel()->doToSelected(command_name); } void LLPanelMainInventory::saveTexture(const LLSD& userdata) @@ -972,7 +981,7 @@ void LLPanelMainInventory::saveTexture(const LLSD& userdata) return; } - const LLUUID& item_id = current_item->getListener()->getUUID(); + const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); LLPreviewTexture* preview_texture = LLFloaterReg::showTypedInstance<LLPreviewTexture>("preview_texture", LLSD(item_id), TAKE_FOCUS_YES); if (preview_texture) { @@ -1045,7 +1054,7 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata) { return; } - const LLUUID item_id = current_item->getListener()->getUUID(); + const LLUUID item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); LLViewerInventoryItem *item = gInventory.getItem(item_id); if (item) { @@ -1060,7 +1069,7 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata) { return; } - current_item->getListener()->performAction(getActivePanel()->getModel(), "goto"); + static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->performAction(getActivePanel()->getModel(), "goto"); } if (command_name == "find_links") @@ -1070,17 +1079,17 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata) { return; } - const LLUUID& item_id = current_item->getListener()->getUUID(); - const std::string &item_name = current_item->getListener()->getName(); + const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); + const std::string &item_name = current_item->getViewModelItem()->getName(); mFilterSubString = item_name; - LLInventoryFilter *filter = mActivePanel->getFilter(); - filter->setFilterSubString(item_name); + LLInventoryFilter &filter = mActivePanel->getFilter(); + filter.setFilterSubString(item_name); mFilterEditor->setText(item_name); mFilterEditor->setFocus(TRUE); - filter->setFilterUUID(item_id); - filter->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); - filter->setFilterLinks(LLInventoryFilter::FILTERLINK_ONLY_LINKS); + filter.setFilterUUID(item_id); + filter.setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); + filter.setFilterLinks(LLInventoryFilter::FILTERLINK_ONLY_LINKS); } } @@ -1089,11 +1098,11 @@ bool LLPanelMainInventory::isSaveTextureEnabled(const LLSD& userdata) LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); if (current_item) { - LLViewerInventoryItem *inv_item = current_item->getInventoryItem(); + LLViewerInventoryItem *inv_item = dynamic_cast<LLViewerInventoryItem*>(static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getInventoryObject()); if(inv_item) { bool can_save = inv_item->checkPermissionsSet(PERM_ITEM_UNRESTRICTED); - LLInventoryType::EType curr_type = current_item->getListener()->getInventoryType(); + LLInventoryType::EType curr_type = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getInventoryType(); return can_save && (curr_type == LLInventoryType::IT_TEXTURE || curr_type == LLInventoryType::IT_SNAPSHOT); } } @@ -1110,15 +1119,14 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata) if (root) { can_delete = TRUE; - std::set<LLUUID> selection_set = root->getSelectionList(); + std::set<LLFolderViewItem*> selection_set = root->getSelectionList(); if (selection_set.empty()) return FALSE; - for (std::set<LLUUID>::iterator iter = selection_set.begin(); + for (std::set<LLFolderViewItem*>::iterator iter = selection_set.begin(); iter != selection_set.end(); ++iter) { - const LLUUID &item_id = (*iter); - LLFolderViewItem *item = root->getItemByID(item_id); - const LLFolderViewEventListener *listener = item->getListener(); + LLFolderViewItem *item = *iter; + const LLFolderViewModelItemInventory *listener = static_cast<const LLFolderViewModelItemInventory*>(item->getViewModelItem()); llassert(listener); if (!listener) return FALSE; can_delete &= listener->isItemRemovable(); @@ -1136,7 +1144,7 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata) { LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); if (!current_item) return FALSE; - const LLUUID& item_id = current_item->getListener()->getUUID(); + const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); const LLViewerInventoryItem *item = gInventory.getItem(item_id); if (item && item->getIsLinkType() && !item->getIsBrokenLink()) { @@ -1148,11 +1156,11 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata) if (command_name == "find_links") { LLFolderView* root = getActivePanel()->getRootFolder(); - std::set<LLUUID> selection_set = root->getSelectionList(); + std::set<LLFolderViewItem*> selection_set = root->getSelectionList(); if (selection_set.size() != 1) return FALSE; LLFolderViewItem* current_item = root->getCurSelectedItem(); if (!current_item) return FALSE; - const LLUUID& item_id = current_item->getListener()->getUUID(); + const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); const LLInventoryObject *obj = gInventory.getObject(item_id); if (obj && !obj->getIsLinkType() && LLAssetType::lookupCanLink(obj->getType())) { @@ -1165,7 +1173,7 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata) { LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); if (!current_item) return FALSE; - const LLUUID& item_id = current_item->getListener()->getUUID(); + const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); const LLViewerInventoryItem *item = gInventory.getItem(item_id); if (item && item->getIsBrokenLink()) { diff --git a/indra/newview/llpanelmarketplaceinbox.cpp b/indra/newview/llpanelmarketplaceinbox.cpp index 66c9c323cb..ea0521e3a7 100644 --- a/indra/newview/llpanelmarketplaceinbox.cpp +++ b/indra/newview/llpanelmarketplaceinbox.cpp @@ -94,14 +94,14 @@ LLInventoryPanel * LLPanelMarketplaceInbox::setupInventoryPanel() mInventoryPanel->setShape(inventory_placeholder_rect); // Set the sort order newest to oldest - mInventoryPanel->setSortOrder(LLInventoryFilter::SO_DATE); - mInventoryPanel->getFilter()->markDefault(); + mInventoryPanel->getFolderViewModel()->setSorter(LLInventoryFilter::SO_DATE); + mInventoryPanel->getFilter().markDefault(); // Set selection callback for proper update of inventory status buttons mInventoryPanel->setSelectCallback(boost::bind(&LLPanelMarketplaceInbox::onSelectionChange, this)); // Set up the note to display when the inbox is empty - mInventoryPanel->getFilter()->setEmptyLookupMessage("InventoryInboxNoItems"); + mInventoryPanel->getFilter().setEmptyLookupMessage("InventoryInboxNoItems"); // Hide the placeholder text inbox_inventory_placeholder->setVisible(FALSE); @@ -139,7 +139,7 @@ U32 LLPanelMarketplaceInbox::getFreshItemCount() const if (mInventoryPanel) { - const LLFolderViewFolder * inbox_folder = mInventoryPanel->getRootFolder(); + LLFolderViewFolder * inbox_folder = mInventoryPanel->getRootFolder(); if (inbox_folder) { diff --git a/indra/newview/llpanelmarketplaceinboxinventory.cpp b/indra/newview/llpanelmarketplaceinboxinventory.cpp index 678e4f2843..0d3fbe66d7 100644 --- a/indra/newview/llpanelmarketplaceinboxinventory.cpp +++ b/indra/newview/llpanelmarketplaceinboxinventory.cpp @@ -29,7 +29,8 @@ #include "llpanelmarketplaceinboxinventory.h" #include "llfolderview.h" -#include "llfoldervieweventlistener.h" +#include "llfolderviewitem.h" +#include "llfolderviewmodel.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" #include "llpanellandmarks.h" @@ -53,82 +54,17 @@ static LLDefaultChildRegistry::Register<LLInboxFolderViewItem> r3("inbox_folder_ // LLInboxInventoryPanel::LLInboxInventoryPanel(const LLInboxInventoryPanel::Params& p) - : LLInventoryPanel(p) -{ -} +: LLInventoryPanel(p) +{} LLInboxInventoryPanel::~LLInboxInventoryPanel() -{ -} - -// virtual -void LLInboxInventoryPanel::buildFolderView(const LLInventoryPanel::Params& params) -{ - // Determine the root folder in case specified, and - // build the views starting with that folder. - - LLUUID root_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false, false); - - // leslie -- temporary HACK to work around sim not creating inbox with proper system folder type - if (root_id.isNull()) - { - std::string start_folder_name(params.start_folder()); - - LLInventoryModel::cat_array_t* cats; - LLInventoryModel::item_array_t* items; - - gInventory.getDirectDescendentsOf(gInventory.getRootFolderID(), cats, items); - - if (cats) - { - for (LLInventoryModel::cat_array_t::const_iterator cat_it = cats->begin(); cat_it != cats->end(); ++cat_it) - { - LLInventoryCategory* cat = *cat_it; - - if (cat->getName() == start_folder_name) - { - root_id = cat->getUUID(); - break; - } - } - } - - if (root_id == LLUUID::null) - { - llwarns << "No category found that matches inbox inventory panel start_folder: " << start_folder_name << llendl; - } - } - // leslie -- end temporary HACK - - if (root_id == LLUUID::null) - { - llwarns << "Inbox inventory panel has no root folder!" << llendl; - root_id = LLUUID::generateNewID(); - } - - LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(LLAssetType::AT_CATEGORY, - LLAssetType::AT_CATEGORY, - LLInventoryType::IT_CATEGORY, - this, - NULL, - root_id); - - mFolderRoot = createFolderView(new_listener, params.use_label_suffix()); -} +{} LLFolderViewFolder * LLInboxInventoryPanel::createFolderViewFolder(LLInvFVBridge * bridge) { LLInboxFolderViewFolder::Params params; params.name = bridge->getDisplayName(); - params.icon = bridge->getIcon(); - params.icon_open = bridge->getOpenIcon(); - - if (mShowItemLinkOverlays) // if false, then links show up just like normal items - { - params.icon_overlay = LLUI::getUIImage("Inv_Link"); - } - params.root = mFolderRoot; params.listener = bridge; params.tool_tip = params.name; @@ -141,14 +77,6 @@ LLFolderViewItem * LLInboxInventoryPanel::createFolderViewItem(LLInvFVBridge * b LLInboxFolderViewItem::Params params; params.name = bridge->getDisplayName(); - params.icon = bridge->getIcon(); - params.icon_open = bridge->getOpenIcon(); - - if (mShowItemLinkOverlays) // if false, then links show up just like normal items - { - params.icon_overlay = LLUI::getUIImage("Inv_Link"); - } - params.creation_date = bridge->getCreationDate(); params.root = mFolderRoot; params.listener = bridge; @@ -163,9 +91,9 @@ LLFolderViewItem * LLInboxInventoryPanel::createFolderViewItem(LLInvFVBridge * b // LLInboxFolderViewFolder::LLInboxFolderViewFolder(const Params& p) - : LLFolderViewFolder(p) - , LLBadgeOwner(getHandle()) - , mFresh(false) +: LLFolderViewFolder(p), + LLBadgeOwner(getHandle()), + mFresh(false) { #if SUPPORTING_FRESH_ITEM_COUNT initBadgeParams(p.new_badge()); @@ -207,7 +135,7 @@ void LLInboxFolderViewFolder::computeFreshness() if (last_expansion_utc > 0) { - mFresh = (mCreationDate > last_expansion_utc); + mFresh = (static_cast<LLFolderViewModelItemInventory*>(getViewModelItem())->getCreationDate() > last_expansion_utc); #if DEBUGGING_FRESHNESS if (mFresh) @@ -229,15 +157,16 @@ void LLInboxFolderViewFolder::deFreshify() gSavedPerAccountSettings.setU32("LastInventoryInboxActivity", time_corrected()); } -void LLInboxFolderViewFolder::setCreationDate(time_t creation_date_utc) -{ - mCreationDate = creation_date_utc; - - if (mParentFolder == mRoot) - { - computeFreshness(); - } -} +// TODO RN: move this behavior to modelview? +//void LLInboxFolderViewFolder::setCreationDate(time_t creation_date_utc) +//{ +// mCreationDate = creation_date_utc; +// +// if (LLFolderViewItem::mParentFolder == mRoot) +// { +// computeFreshness(); +// } +//} // // LLInboxFolderViewItem Implementation @@ -253,9 +182,9 @@ LLInboxFolderViewItem::LLInboxFolderViewItem(const Params& p) #endif } -BOOL LLInboxFolderViewItem::addToFolder(LLFolderViewFolder* folder, LLFolderView* root) +void LLInboxFolderViewItem::addToFolder(LLFolderViewFolder* folder) { - BOOL retval = LLFolderViewItem::addToFolder(folder, root); + LLFolderViewItem::addToFolder(folder); #if SUPPORTING_FRESH_ITEM_COUNT // Compute freshness if our parent is the root folder for the inbox @@ -264,8 +193,6 @@ BOOL LLInboxFolderViewItem::addToFolder(LLFolderViewFolder* folder, LLFolderView computeFreshness(); } #endif - - return retval; } BOOL LLInboxFolderViewItem::handleDoubleClick(S32 x, S32 y, MASK mask) @@ -303,7 +230,7 @@ void LLInboxFolderViewItem::computeFreshness() if (last_expansion_utc > 0) { - mFresh = (mCreationDate > last_expansion_utc); + mFresh = (static_cast<LLFolderViewModelItemInventory*>(getViewModelItem())->getCreationDate() > last_expansion_utc); #if DEBUGGING_FRESHNESS if (mFresh) diff --git a/indra/newview/llpanelmarketplaceinboxinventory.h b/indra/newview/llpanelmarketplaceinboxinventory.h index d6b827ee3e..098969aca6 100644 --- a/indra/newview/llpanelmarketplaceinboxinventory.h +++ b/indra/newview/llpanelmarketplaceinboxinventory.h @@ -47,9 +47,6 @@ public: ~LLInboxInventoryPanel(); // virtual - void buildFolderView(const LLInventoryPanel::Params& params); - - // virtual LLFolderViewFolder * createFolderViewFolder(LLInvFVBridge * bridge); LLFolderViewItem * createFolderViewItem(LLInvFVBridge * bridge); }; @@ -63,9 +60,8 @@ public: Optional<LLBadge::Params> new_badge; Params() - : new_badge("new_badge") - { - } + : new_badge("new_badge") + {} }; LLInboxFolderViewFolder(const Params& p); @@ -81,8 +77,6 @@ public: bool isFresh() const { return mFresh; } protected: - void setCreationDate(time_t creation_date_utc); - bool mFresh; }; @@ -95,14 +89,13 @@ public: Optional<LLBadge::Params> new_badge; Params() - : new_badge("new_badge") - { - } + : new_badge("new_badge") + {} }; LLInboxFolderViewItem(const Params& p); - BOOL addToFolder(LLFolderViewFolder* folder, LLFolderView* root); + void addToFolder(LLFolderViewFolder* folder); BOOL handleDoubleClick(S32 x, S32 y, MASK mask); void draw(); diff --git a/indra/newview/llpanelmarketplaceoutboxinventory.cpp b/indra/newview/llpanelmarketplaceoutboxinventory.cpp deleted file mode 100644 index ff62cb23db..0000000000 --- a/indra/newview/llpanelmarketplaceoutboxinventory.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/** - * @file llpanelmarketplaceoutboxinventory.cpp - * @brief LLOutboxInventoryPanel class definition - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llpanelmarketplaceoutboxinventory.h" - -#include "llfolderview.h" -#include "llfoldervieweventlistener.h" -#include "llinventorybridge.h" -#include "llinventoryfunctions.h" -#include "llpanellandmarks.h" -#include "llplacesinventorybridge.h" -#include "lltrans.h" -#include "llviewerfoldertype.h" - - -// -// statics -// - -static LLDefaultChildRegistry::Register<LLOutboxInventoryPanel> r1("outbox_inventory_panel"); -static LLDefaultChildRegistry::Register<LLOutboxFolderViewFolder> r2("outbox_folder_view_folder"); - - -// -// LLOutboxInventoryPanel Implementation -// - -LLOutboxInventoryPanel::LLOutboxInventoryPanel(const LLOutboxInventoryPanel::Params& p) - : LLInventoryPanel(p) -{ -} - -LLOutboxInventoryPanel::~LLOutboxInventoryPanel() -{ -} - -// virtual -void LLOutboxInventoryPanel::buildFolderView(const LLInventoryPanel::Params& params) -{ - // Determine the root folder in case specified, and - // build the views starting with that folder. - - LLUUID root_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false); - - if (root_id == LLUUID::null) - { - llwarns << "Outbox inventory panel has no root folder!" << llendl; - root_id = LLUUID::generateNewID(); - } - - LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(LLAssetType::AT_CATEGORY, - LLAssetType::AT_CATEGORY, - LLInventoryType::IT_CATEGORY, - this, - NULL, - root_id); - - mFolderRoot = createFolderView(new_listener, params.use_label_suffix()); -} - -LLFolderViewFolder * LLOutboxInventoryPanel::createFolderViewFolder(LLInvFVBridge * bridge) -{ - LLOutboxFolderViewFolder::Params params; - - params.name = bridge->getDisplayName(); - params.icon = bridge->getIcon(); - params.icon_open = bridge->getOpenIcon(); - - if (mShowItemLinkOverlays) // if false, then links show up just like normal items - { - params.icon_overlay = LLUI::getUIImage("Inv_Link"); - } - - params.root = mFolderRoot; - params.listener = bridge; - params.tool_tip = params.name; - - return LLUICtrlFactory::create<LLOutboxFolderViewFolder>(params); -} - -LLFolderViewItem * LLOutboxInventoryPanel::createFolderViewItem(LLInvFVBridge * bridge) -{ - LLFolderViewItem::Params params; - - params.name = bridge->getDisplayName(); - params.icon = bridge->getIcon(); - params.icon_open = bridge->getOpenIcon(); - - if (mShowItemLinkOverlays) // if false, then links show up just like normal items - { - params.icon_overlay = LLUI::getUIImage("Inv_Link"); - } - - params.creation_date = bridge->getCreationDate(); - params.root = mFolderRoot; - params.listener = bridge; - params.rect = LLRect (0, 0, 0, 0); - params.tool_tip = params.name; - - return LLUICtrlFactory::create<LLOutboxFolderViewItem>(params); -} - -// -// LLOutboxFolderViewFolder Implementation -// - -LLOutboxFolderViewFolder::LLOutboxFolderViewFolder(const Params& p) - : LLFolderViewFolder(p) -{ -} - -// -// LLOutboxFolderViewItem Implementation -// - -LLOutboxFolderViewItem::LLOutboxFolderViewItem(const Params& p) - : LLFolderViewItem(p) -{ -} - -BOOL LLOutboxFolderViewItem::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - return TRUE; -} - -void LLOutboxFolderViewItem::openItem() -{ - // Intentionally do nothing to block attaching items from the outbox -} - -// eof diff --git a/indra/newview/llpanelmarketplaceoutboxinventory.h b/indra/newview/llpanelmarketplaceoutboxinventory.h deleted file mode 100644 index a6c522b7c2..0000000000 --- a/indra/newview/llpanelmarketplaceoutboxinventory.h +++ /dev/null @@ -1,78 +0,0 @@ -/** - * @file llpanelmarketplaceoutboxinventory.h - * @brief LLOutboxInventoryPanel class declaration - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_OUTBOXINVENTORYPANEL_H -#define LL_OUTBOXINVENTORYPANEL_H - - -#include "llinventorypanel.h" -#include "llfolderviewitem.h" - - -class LLOutboxInventoryPanel : public LLInventoryPanel -{ -public: - struct Params : public LLInitParam::Block<Params, LLInventoryPanel::Params> - { - Params() {} - }; - - LLOutboxInventoryPanel(const Params& p); - ~LLOutboxInventoryPanel(); - - // virtual - void buildFolderView(const LLInventoryPanel::Params& params); - - // virtual - LLFolderViewFolder * createFolderViewFolder(LLInvFVBridge * bridge); - LLFolderViewItem * createFolderViewItem(LLInvFVBridge * bridge); -}; - - -class LLOutboxFolderViewFolder : public LLFolderViewFolder -{ -public: - struct Params : public LLInitParam::Block<Params, LLFolderViewFolder::Params> - { - Params() {} - }; - - LLOutboxFolderViewFolder(const Params& p); -}; - - -class LLOutboxFolderViewItem : public LLFolderViewItem -{ -public: - LLOutboxFolderViewItem(const Params& p); - - // virtual - BOOL handleDoubleClick(S32 x, S32 y, MASK mask); - void openItem(); -}; - - -#endif //LL_OUTBOXINVENTORYPANEL_H diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index 1ca24f3031..82956beb3d 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -71,12 +71,13 @@ /// Class LLTaskInvFVBridge ///---------------------------------------------------------------------------- -class LLTaskInvFVBridge : public LLFolderViewEventListener +class LLTaskInvFVBridge : public LLFolderViewModelItemInventory { protected: LLUUID mUUID; std::string mName; mutable std::string mDisplayName; + mutable std::string mSearchableName; LLPanelObjectInventory* mPanel; U32 mFlags; LLAssetType::EType mAssetType; @@ -102,26 +103,29 @@ public: S32 getPrice(); static bool commitBuyItem(const LLSD& notification, const LLSD& response); - // LLFolderViewEventListener functionality + // LLFolderViewModelItemInventory functionality virtual const std::string& getName() const; virtual const std::string& getDisplayName() const; + virtual const std::string& getSearchableName() const; + virtual PermissionMask getPermissionMask() const { return PERM_NONE; } /*virtual*/ LLFolderType::EType getPreferredType() const { return LLFolderType::FT_NONE; } virtual const LLUUID& getUUID() const { return mUUID; } virtual time_t getCreationDate() const; + virtual void setCreationDate(time_t creation_date_utc); + virtual LLUIImagePtr getIcon() const; virtual void openItem(); virtual BOOL canOpenItem() const { return FALSE; } virtual void closeItem() {} - virtual void previewItem(); virtual void selectItem() {} virtual BOOL isItemRenameable() const; virtual BOOL renameItem(const std::string& new_name); virtual BOOL isItemMovable() const; virtual BOOL isItemRemovable() const; virtual BOOL removeItem(); - virtual void removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch); - virtual void move(LLFolderViewEventListener* parent_listener); + virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch); + virtual void move(LLFolderViewModelItem* parent_listener); virtual BOOL isItemCopyable() const; virtual BOOL copyToClipboard() const; virtual BOOL cutToClipboard() const; @@ -131,11 +135,15 @@ public: virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual void performAction(LLInventoryModel* model, std::string action); virtual BOOL isUpToDate() const { return TRUE; } - virtual BOOL hasChildren() const { return FALSE; } + virtual bool hasChildren() const { return FALSE; } virtual LLInventoryType::EType getInventoryType() const { return LLInventoryType::IT_NONE; } virtual LLWearableType::EType getWearableType() const { return LLWearableType::WT_NONE; } + virtual EInventorySortGroup getSortGroup() const { return SG_ITEM; } + virtual LLInventoryObject* getInventoryObject() const { return findInvObject(); } + // LLDragAndDropBridge functionality + virtual LLToolDragAndDrop::ESource getDragSource() const { return LLToolDragAndDrop::SOURCE_WORLD; } virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const; virtual BOOL dragOrDrop(MASK mask, BOOL drop, EDragAndDropType cargo_type, @@ -147,7 +155,8 @@ LLTaskInvFVBridge::LLTaskInvFVBridge( LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name, - U32 flags): + U32 flags) +: LLFolderViewModelItemInventory(panel->getRootViewModel()), mUUID(uuid), mName(name), mPanel(panel), @@ -330,15 +339,27 @@ const std::string& LLTaskInvFVBridge::getDisplayName() const } } + mSearchableName.assign(mDisplayName + getLabelSuffix()); + return mDisplayName; } +const std::string& LLTaskInvFVBridge::getSearchableName() const +{ + return mSearchableName; +} + + // BUG: No creation dates for task inventory time_t LLTaskInvFVBridge::getCreationDate() const { return 0; } +void LLTaskInvFVBridge::setCreationDate(time_t creation_date_utc) +{} + + LLUIImagePtr LLTaskInvFVBridge::getIcon() const { const BOOL item_is_multi = (mFlags & LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS); @@ -352,11 +373,6 @@ void LLTaskInvFVBridge::openItem() lldebugs << "LLTaskInvFVBridge::openItem()" << llendl; } -void LLTaskInvFVBridge::previewItem() -{ - openItem(); -} - BOOL LLTaskInvFVBridge::isItemRenameable() const { if(gAgent.isGodlike()) return TRUE; @@ -467,7 +483,7 @@ BOOL LLTaskInvFVBridge::removeItem() return FALSE; } -void LLTaskInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch) +void LLTaskInvFVBridge::removeBatch(std::vector<LLFolderViewModelItem*>& batch) { if (!mPanel) { @@ -507,7 +523,7 @@ void LLTaskInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& } } -void LLTaskInvFVBridge::move(LLFolderViewEventListener* parent_listener) +void LLTaskInvFVBridge::move(LLFolderViewModelItem* parent_listener) { } @@ -709,7 +725,7 @@ public: virtual BOOL renameItem(const std::string& new_name); virtual BOOL isItemRemovable() const; virtual void buildContextMenu(LLMenuGL& menu, U32 flags); - virtual BOOL hasChildren() const; + virtual bool hasChildren() const; virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const; virtual BOOL dragOrDrop(MASK mask, BOOL drop, EDragAndDropType cargo_type, @@ -717,6 +733,7 @@ public: std::string& tooltip_msg); virtual BOOL canOpenItem() const { return TRUE; } virtual void openItem(); + virtual EInventorySortGroup getSortGroup() const { return SG_NORMAL_FOLDER; } }; LLTaskCategoryBridge::LLTaskCategoryBridge( @@ -739,15 +756,7 @@ const std::string& LLTaskCategoryBridge::getDisplayName() const if (cat) { - // Localize "Contents" folder. - if (cat->getParentUUID().isNull() && cat->getName() == "Contents") - { - mDisplayName.assign(LLTrans::getString("ViewerObjectContents")); - } - else - { - mDisplayName.assign(cat->getName()); - } + mDisplayName.assign(cat->getName()); } return mDisplayName; @@ -775,7 +784,7 @@ void LLTaskCategoryBridge::buildContextMenu(LLMenuGL& menu, U32 flags) hide_context_entries(menu, items, disabled_items); } -BOOL LLTaskCategoryBridge::hasChildren() const +bool LLTaskCategoryBridge::hasChildren() const { // return TRUE if we have or do know know if we have children. // *FIX: For now, return FALSE - we will know for sure soon enough. @@ -1514,7 +1523,7 @@ BOOL LLPanelObjectInventory::postBuild() void LLPanelObjectInventory::doToSelected(const LLSD& userdata) { - mFolders->doToSelected(&gInventory, userdata); + LLInventoryAction::doToSelected(&gInventory, mFolders, userdata.asString()); } void LLPanelObjectInventory::clearContents() @@ -1526,6 +1535,8 @@ void LLPanelObjectInventory::clearContents() LLToolDragAndDrop::getInstance()->endDrag(); } + clearItemIDs(); + if( mScroller ) { // removes mFolders @@ -1541,21 +1552,24 @@ void LLPanelObjectInventory::reset() { clearContents(); - //setBorderVisible(FALSE); - mCommitCallbackRegistrar.pushScope(); // push local callbacks LLRect dummy_rect(0, 1, 1, 0); LLFolderView::Params p; p.name = "task inventory"; p.title = "task inventory"; - p.task_id = getTaskUUID(); p.parent_panel = this; p.tool_tip= LLTrans::getString("PanelContentsTooltip"); p.listener = LLTaskInvFVBridge::createObjectBridge(this, NULL); + p.folder_indentation = -14; // subtract space normally reserved for folder expanders + p.view_model = &mInventoryViewModel; + p.root = NULL; mFolders = LLUICtrlFactory::create<LLFolderView>(p); // this ensures that we never say "searching..." or "no items found" - mFolders->getFilter()->setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS); + //TODO RN: make this happen by manipulating filter object directly + LLInventoryFilter& inventoryFilter = dynamic_cast<LLInventoryFilter&>(mFolders->getFolderViewModel()->getFilter()); + inventoryFilter.setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS); + mFolders->setCallbackRegistrar(&mCommitCallbackRegistrar); if (hasFocus()) @@ -1600,7 +1614,7 @@ void LLPanelObjectInventory::inventoryChanged(LLViewerObject* object, iter != inventory->end(); ) { LLInventoryObject* item = *iter++; - LLFloaterProperties* floater = LLFloaterReg::findTypedInstance<LLFloaterProperties>("properites", item->getUUID()); + LLFloaterProperties* floater = LLFloaterReg::findTypedInstance<LLFloaterProperties>("properties", item->getUUID()); if(floater) { floater->refresh(); @@ -1615,15 +1629,20 @@ void LLPanelObjectInventory::updateInventory() // << " panel UUID: " << panel->mTaskUUID << "\n" // << " task UUID: " << object->mID << llendl; // We're still interested in this task's inventory. - std::set<LLUUID> selected_items; + std::vector<LLUUID> selected_item_ids; + std::set<LLFolderViewItem*> selected_items; BOOL inventory_has_focus = FALSE; - if (mHaveInventory) + if (mHaveInventory && mFolders) { selected_items = mFolders->getSelectionList(); inventory_has_focus = gFocusMgr.childHasKeyboardFocus(mFolders); } - - reset(); + for (std::set<LLFolderViewItem*>::iterator it = selected_items.begin(), end_it = selected_items.end(); + it != end_it; + ++it) + { + selected_item_ids.push_back(static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID()); + } LLViewerObject* objectp = gObjectList.findObject(mTaskUUID); if (objectp) @@ -1631,19 +1650,21 @@ void LLPanelObjectInventory::updateInventory() LLInventoryObject* inventory_root = objectp->getInventoryRoot(); LLInventoryObject::object_list_t contents; objectp->getInventoryContents(contents); + if (inventory_root) { - createFolderViews(inventory_root, contents); - mHaveInventory = TRUE; + reset(); mIsInventoryEmpty = FALSE; + createFolderViews(inventory_root, contents); mFolders->setEnabled(TRUE); } else { // TODO: create an empty inventory mIsInventoryEmpty = TRUE; - mHaveInventory = TRUE; } + + mHaveInventory = TRUE; } else { @@ -1653,11 +1674,12 @@ void LLPanelObjectInventory::updateInventory() } // restore previous selection - std::set<LLUUID>::iterator selection_it; - BOOL first_item = TRUE; - for (selection_it = selected_items.begin(); selection_it != selected_items.end(); ++selection_it) + std::vector<LLUUID>::iterator selection_it; + bool first_item = true; + for (selection_it = selected_item_ids.begin(); selection_it != selected_item_ids.end(); ++selection_it) { - LLFolderViewItem* selected_item = mFolders->getItemByID(*selection_it); + LLFolderViewItem* selected_item = getItemByID(*selection_it); + if (selected_item) { //HACK: "set" first item then "change" each other one to get keyboard focus right @@ -1673,7 +1695,10 @@ void LLPanelObjectInventory::updateInventory() } } - mFolders->requestArrange(); + if (mFolders) + { + mFolders->requestArrange(); + } mInventoryNeedsUpdate = FALSE; // Edit menu handler is set in onFocusReceived } @@ -1694,19 +1719,20 @@ void LLPanelObjectInventory::createFolderViews(LLInventoryObject* inventory_root bridge = LLTaskInvFVBridge::createObjectBridge(this, inventory_root); if(bridge) { - LLFolderViewFolder* new_folder = NULL; LLFolderViewFolder::Params p; p.name = inventory_root->getName(); - p.icon = LLUI::getUIImage("Inv_FolderClosed"); - p.icon_open = LLUI::getUIImage("Inv_FolderOpen"); + p.tool_tip = p.name; p.root = mFolders; p.listener = bridge; - p.tool_tip = p.name; - new_folder = LLUICtrlFactory::create<LLFolderViewFolder>(p); - new_folder->addToFolder(mFolders, mFolders); + + LLFolderViewFolder* new_folder = LLUICtrlFactory::create<LLFolderViewFolder>(p); + new_folder->addToFolder(mFolders); new_folder->toggleOpen(); - createViewsForCategory(&contents, inventory_root, new_folder); + if (!contents.empty()) + { + createViewsForCategory(&contents, inventory_root, new_folder); + } } } @@ -1738,8 +1764,6 @@ void LLPanelObjectInventory::createViewsForCategory(LLInventoryObject::object_li { LLFolderViewFolder::Params p; p.name = obj->getName(); - p.icon = LLUI::getUIImage("Inv_FolderClosed"); - p.icon_open = LLUI::getUIImage("Inv_FolderOpen"); p.root = mFolders; p.listener = bridge; p.tool_tip = p.name; @@ -1751,7 +1775,6 @@ void LLPanelObjectInventory::createViewsForCategory(LLInventoryObject::object_li { LLFolderViewItem::Params params; params.name(obj->getName()); - params.icon(bridge->getIcon()); params.creation_date(bridge->getCreationDate()); params.root(mFolders); params.listener(bridge); @@ -1759,7 +1782,8 @@ void LLPanelObjectInventory::createViewsForCategory(LLInventoryObject::object_li params.tool_tip = params.name; view = LLUICtrlFactory::create<LLFolderViewItem> (params); } - view->addToFolder(folder, mFolders); + view->addToFolder(folder); + addItemID(obj->getUUID(), view); } } @@ -1827,6 +1851,7 @@ void LLPanelObjectInventory::refresh() removeVOInventoryListener(); clearContents(); } + mInventoryViewModel.setTaskID(mTaskUUID); //llinfos << "LLPanelObjectInventory::refresh() " << mTaskUUID << llendl; } @@ -1914,7 +1939,10 @@ void LLPanelObjectInventory::idle(void* user_data) { LLPanelObjectInventory* self = (LLPanelObjectInventory*)user_data; - + if (self->mFolders) + { + self->mFolders->update(); + } if (self->mInventoryNeedsUpdate) { self->updateInventory(); @@ -1939,3 +1967,32 @@ void LLPanelObjectInventory::onFocusReceived() LLPanel::onFocusReceived(); } + + +LLFolderViewItem* LLPanelObjectInventory::getItemByID( const LLUUID& id ) +{ + std::map<LLUUID, LLFolderViewItem*>::iterator map_it; + map_it = mItemMap.find(id); + if (map_it != mItemMap.end()) + { + return map_it->second; + } + + return NULL; +} + +void LLPanelObjectInventory::removeItemID( const LLUUID& id ) +{ + mItemMap.erase(id); +} + +void LLPanelObjectInventory::addItemID( const LLUUID& id, LLFolderViewItem* itemp ) +{ + mItemMap[id] = itemp; +} + +void LLPanelObjectInventory::clearItemIDs() +{ + mItemMap.clear(); +} + diff --git a/indra/newview/llpanelobjectinventory.h b/indra/newview/llpanelobjectinventory.h index 607b705f7f..f497c695b3 100644 --- a/indra/newview/llpanelobjectinventory.h +++ b/indra/newview/llpanelobjectinventory.h @@ -29,6 +29,7 @@ #include "llvoinventorylistener.h" #include "llpanel.h" +#include "llinventorypanel.h" // for LLFolderViewModelInventory #include "llinventory.h" @@ -55,6 +56,8 @@ public: virtual BOOL postBuild(); + LLFolderViewModelInventory& getRootViewModel() { return mInventoryViewModel; } + void doToSelected(const LLSD& userdata); void refresh(); @@ -85,8 +88,15 @@ protected: LLInventoryObject* parent, LLFolderViewFolder* folder); void clearContents(); + LLFolderViewItem* getItemByID(const LLUUID& id); + + void addItemID( const LLUUID& id, LLFolderViewItem* itemp ); + void removeItemID(const LLUUID& id); + void clearItemIDs(); private: + std::map<LLUUID, LLFolderViewItem*> mItemMap; + LLScrollContainer* mScroller; LLFolderView* mFolders; @@ -94,6 +104,7 @@ private: BOOL mHaveInventory; BOOL mIsInventoryEmpty; BOOL mInventoryNeedsUpdate; + LLFolderViewModelInventory mInventoryViewModel; }; #endif // LL_LLPANELOBJECTINVENTORY_H diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index 35e2e96bab..d690a18477 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -270,7 +270,7 @@ private: if (inventory_panel->getVisible()) { - inventory_panel->setSortOrder(sort_order); + inventory_panel->getFolderViewModel()->setSorter(sort_order); } else { @@ -738,7 +738,7 @@ void LLPanelOutfitEdit::onSearchEdit(const std::string& string) } // save current folder open state if no filter currently applied - if (mInventoryItemsPanel->getRootFolder()->getFilterSubString().empty()) + if (mInventoryItemsPanel->getFilterSubString().empty()) { mSavedFolderState->setApply(FALSE); mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); @@ -885,13 +885,13 @@ LLPanelOutfitEdit::selection_info_t LLPanelOutfitEdit::getAddMorePanelSelectionT { if (mInventoryItemsPanel != NULL && mInventoryItemsPanel->getVisible()) { - std::set<LLUUID> selected_uuids = mInventoryItemsPanel->getRootFolder()->getSelectionList(); + std::set<LLFolderViewItem*> selected_items = mInventoryItemsPanel->getRootFolder()->getSelectionList(); - result.second = selected_uuids.size(); + result.second = selected_items.size(); if (result.second == 1) { - result.first = getWearableTypeByItemUUID(*(selected_uuids.begin())); + result.first = getWearableTypeByItemUUID(static_cast<LLFolderViewModelItemInventory*>((*selected_items.begin())->getViewModelItem())->getUUID()); } } else if (mWearableItemsList != NULL && mWearableItemsList->getVisible()) @@ -1310,7 +1310,7 @@ void LLPanelOutfitEdit::getCurrentItemUUID(LLUUID& selected_id) LLFolderViewItem* curr_item = mInventoryItemsPanel->getRootFolder()->getCurSelectedItem(); if (!curr_item) return; - LLFolderViewEventListener* listenerp = curr_item->getListener(); + LLFolderViewModelItemInventory* listenerp = static_cast<LLFolderViewModelItemInventory*>(curr_item->getViewModelItem()); if (!listenerp) return; selected_id = listenerp->getUUID(); @@ -1327,9 +1327,13 @@ void LLPanelOutfitEdit::getSelectedItemsUUID(uuid_vec_t& uuid_list) void (uuid_vec_t::* tmp)(LLUUID const &) = &uuid_vec_t::push_back; if (mInventoryItemsPanel->getVisible()) { - std::set<LLUUID> item_set = mInventoryItemsPanel->getRootFolder()->getSelectionList(); - - std::for_each(item_set.begin(), item_set.end(), boost::bind( tmp, &uuid_list, _1)); + std::set<LLFolderViewItem*> item_set = mInventoryItemsPanel->getRootFolder()->getSelectionList(); + for (std::set<LLFolderViewItem*>::iterator it = item_set.begin(), end_it = item_set.end(); + it != end_it; + ++it) + { + uuid_list.push_back(static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID()); + } } else if (mWearablesListViewPanel->getVisible()) { @@ -1374,13 +1378,13 @@ void LLPanelOutfitEdit::saveListSelection() { if(mWearablesListViewPanel->getVisible()) { - std::set<LLUUID> selected_ids = mInventoryItemsPanel->getRootFolder()->getSelectionList(); + std::set<LLFolderViewItem*> selected_ids = mInventoryItemsPanel->getRootFolder()->getSelectionList(); if(!selected_ids.size()) return; - for (std::set<LLUUID>::const_iterator item_id = selected_ids.begin(); item_id != selected_ids.end(); ++item_id) + for (std::set<LLFolderViewItem*>::const_iterator item_id = selected_ids.begin(); item_id != selected_ids.end(); ++item_id) { - mWearableItemsList->selectItemByUUID(*item_id, true); + mWearableItemsList->selectItemByUUID(static_cast<LLFolderViewModelItemInventory*>((*item_id)->getViewModelItem())->getUUID(), true); } mWearableItemsList->scrollToShowFirstSelectedItem(); } @@ -1398,7 +1402,7 @@ void LLPanelOutfitEdit::saveListSelection() for(std::vector<LLUUID>::const_iterator item_id = selected_ids.begin(); item_id != selected_ids.end(); ++item_id) { - LLFolderViewItem* item = root->getItemByID(*item_id); + LLFolderViewItem* item = mInventoryItemsPanel->getItemByID(*item_id); if (!item) continue; LLFolderViewFolder* parent = item->getParentFolder(); diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index f1380e7a36..260de40eef 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -72,6 +72,7 @@ static const std::string NEARBY_TAB_NAME = "nearby_panel"; 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"; +static const std::string BLOCKED_TAB_NAME = "blocked_panel"; // blocked avatars static const std::string COLLAPSED_BY_USER = "collapsed_by_user"; @@ -492,26 +493,37 @@ public: LLPanelPeople::LLPanelPeople() : LLPanel(), - mFilterSubString(LLStringUtil::null), - mFilterSubStringOrig(LLStringUtil::null), - mFilterEditor(NULL), mTabContainer(NULL), mOnlineFriendList(NULL), mAllFriendList(NULL), mNearbyList(NULL), mRecentList(NULL), mGroupList(NULL), - mNearbyGearButton(NULL), - mFriendsGearButton(NULL), - mGroupsGearButton(NULL), - mRecentGearButton(NULL), mMiniMap(NULL) { mFriendListUpdater = new LLFriendListUpdater(boost::bind(&LLPanelPeople::updateFriendList, this)); mNearbyListUpdater = new LLNearbyListUpdater(boost::bind(&LLPanelPeople::updateNearbyList, this)); mRecentListUpdater = new LLRecentListUpdater(boost::bind(&LLPanelPeople::updateRecentList, this)); mButtonsUpdater = new LLButtonsUpdater(boost::bind(&LLPanelPeople::updateButtons, this)); - mCommitCallbackRegistrar.add("People.addFriend", boost::bind(&LLPanelPeople::onAddFriendButtonClicked, this)); + + mCommitCallbackRegistrar.add("People.AddFriend", boost::bind(&LLPanelPeople::onAddFriendButtonClicked, this)); + mCommitCallbackRegistrar.add("People.AddFriendWizard", boost::bind(&LLPanelPeople::onAddFriendWizButtonClicked, this)); + mCommitCallbackRegistrar.add("People.DelFriend", boost::bind(&LLPanelPeople::onDeleteFriendButtonClicked, this)); + mCommitCallbackRegistrar.add("People.Group.Minus", boost::bind(&LLPanelPeople::onGroupMinusButtonClicked, this)); + mCommitCallbackRegistrar.add("People.Chat", boost::bind(&LLPanelPeople::onChatButtonClicked, this)); + mCommitCallbackRegistrar.add("People.Gear", boost::bind(&LLPanelPeople::onGearButtonClicked, this, _1)); + + mCommitCallbackRegistrar.add("People.Group.Plus.Action", boost::bind(&LLPanelPeople::onGroupPlusMenuItemClicked, this, _2)); + mCommitCallbackRegistrar.add("People.Friends.ViewSort.Action", boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemClicked, this, _2)); + mCommitCallbackRegistrar.add("People.Nearby.ViewSort.Action", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemClicked, this, _2)); + mCommitCallbackRegistrar.add("People.Groups.ViewSort.Action", boost::bind(&LLPanelPeople::onGroupsViewSortMenuItemClicked, this, _2)); + mCommitCallbackRegistrar.add("People.Recent.ViewSort.Action", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemClicked, this, _2)); + + mEnableCallbackRegistrar.add("People.Friends.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemCheck, this, _2)); + mEnableCallbackRegistrar.add("People.Recent.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemCheck, this, _2)); + mEnableCallbackRegistrar.add("People.Nearby.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemCheck, this, _2)); + + mEnableCallbackRegistrar.add("People.Group.Plus.Validate", boost::bind(&LLPanelPeople::onGroupPlusButtonValidate, this)); } LLPanelPeople::~LLPanelPeople() @@ -525,13 +537,6 @@ LLPanelPeople::~LLPanelPeople() { LLVoiceClient::getInstance()->removeObserver(this); } - - if (mGroupPlusMenuHandle.get()) mGroupPlusMenuHandle.get()->die(); - if (mNearbyViewSortMenuHandle.get()) mNearbyViewSortMenuHandle.get()->die(); - if (mNearbyViewSortMenuHandle.get()) mNearbyViewSortMenuHandle.get()->die(); - if (mGroupsViewSortMenuHandle.get()) mGroupsViewSortMenuHandle.get()->die(); - if (mRecentViewSortMenuHandle.get()) mRecentViewSortMenuHandle.get()->die(); - } void LLPanelPeople::onFriendsAccordionExpandedCollapsed(LLUICtrl* ctrl, const LLSD& param, LLAvatarList* avatar_list) @@ -553,11 +558,15 @@ void LLPanelPeople::onFriendsAccordionExpandedCollapsed(LLUICtrl* ctrl, const LL BOOL LLPanelPeople::postBuild() { - mFilterEditor = getChild<LLFilterEditor>("filter_input"); - mFilterEditor->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); + getChild<LLFilterEditor>("nearby_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); + getChild<LLFilterEditor>("friends_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); + getChild<LLFilterEditor>("groups_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); + getChild<LLFilterEditor>("recent_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); mTabContainer = getChild<LLTabContainer>("tabs"); mTabContainer->setCommitCallback(boost::bind(&LLPanelPeople::onTabSelected, this, _2)); + mSavedFilters.resize(mTabContainer->getTabCount()); + mSavedOriginalFilters.resize(mTabContainer->getTabCount()); LLPanel* friends_tab = getChild<LLPanel>(FRIENDS_TAB_NAME); // updater is active only if panel is visible to user. @@ -601,14 +610,6 @@ BOOL LLPanelPeople::postBuild() setSortOrder(mAllFriendList, (ESortOrder)gSavedSettings.getU32("FriendsSortOrder"), false); setSortOrder(mNearbyList, (ESortOrder)gSavedSettings.getU32("NearbyPeopleSortOrder"), false); - LLPanel* groups_panel = getChild<LLPanel>(GROUP_TAB_NAME); - groups_panel->childSetAction("activate_btn", boost::bind(&LLPanelPeople::onActivateButtonClicked, this)); - groups_panel->childSetAction("plus_btn", boost::bind(&LLPanelPeople::onGroupPlusButtonClicked, this)); - - LLPanel* friends_panel = getChild<LLPanel>(FRIENDS_TAB_NAME); - friends_panel->childSetAction("add_btn", boost::bind(&LLPanelPeople::onAddFriendWizButtonClicked, this)); - friends_panel->childSetAction("del_btn", boost::bind(&LLPanelPeople::onDeleteFriendButtonClicked, this)); - mOnlineFriendList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1)); mAllFriendList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1)); mNearbyList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1)); @@ -629,6 +630,19 @@ BOOL LLPanelPeople::postBuild() mGroupList->setCommitCallback(boost::bind(&LLPanelPeople::updateButtons, this)); mGroupList->setReturnCallback(boost::bind(&LLPanelPeople::onChatButtonClicked, this)); + LLMenuButton* groups_gear_btn = getChild<LLMenuButton>("groups_gear_btn"); + + // Use the context menu of the Groups list for the Groups tab gear menu. + LLToggleableMenu* groups_gear_menu = mGroupList->getContextMenu(); + if (groups_gear_menu) + { + groups_gear_btn->setMenu(groups_gear_menu, LLMenuButton::MP_BOTTOM_LEFT); + } + else + { + llwarns << "People->Groups list menu not found" << llendl; + } + LLAccordionCtrlTab* accordion_tab = getChild<LLAccordionCtrlTab>("tab_all"); accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLPanelPeople::onFriendsAccordionExpandedCollapsed, this, _1, _2, mAllFriendList)); @@ -637,70 +651,9 @@ BOOL LLPanelPeople::postBuild() accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLPanelPeople::onFriendsAccordionExpandedCollapsed, this, _1, _2, mOnlineFriendList)); - buttonSetAction("view_profile_btn", boost::bind(&LLPanelPeople::onViewProfileButtonClicked, this)); - buttonSetAction("group_info_btn", boost::bind(&LLPanelPeople::onGroupInfoButtonClicked, this)); - buttonSetAction("chat_btn", boost::bind(&LLPanelPeople::onChatButtonClicked, this)); - buttonSetAction("im_btn", boost::bind(&LLPanelPeople::onImButtonClicked, this)); - buttonSetAction("call_btn", boost::bind(&LLPanelPeople::onCallButtonClicked, this)); - buttonSetAction("group_call_btn", boost::bind(&LLPanelPeople::onGroupCallButtonClicked, this)); - buttonSetAction("teleport_btn", boost::bind(&LLPanelPeople::onTeleportButtonClicked, this)); - buttonSetAction("share_btn", boost::bind(&LLPanelPeople::onShareButtonClicked, this)); - // Must go after setting commit callback and initializing all pointers to children. mTabContainer->selectTabByName(NEARBY_TAB_NAME); - // Create menus. - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; - - registrar.add("People.Group.Plus.Action", boost::bind(&LLPanelPeople::onGroupPlusMenuItemClicked, this, _2)); - registrar.add("People.Group.Minus.Action", boost::bind(&LLPanelPeople::onGroupMinusButtonClicked, this)); - 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)); - - enable_registrar.add("People.Group.Minus.Enable", boost::bind(&LLPanelPeople::isRealGroup, this)); - enable_registrar.add("People.Friends.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemCheck, this, _2)); - enable_registrar.add("People.Recent.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemCheck, this, _2)); - enable_registrar.add("People.Nearby.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemCheck, this, _2)); - - mNearbyGearButton = getChild<LLMenuButton>("nearby_view_sort_btn"); - mFriendsGearButton = getChild<LLMenuButton>("friends_viewsort_btn"); - mGroupsGearButton = getChild<LLMenuButton>("groups_viewsort_btn"); - mRecentGearButton = getChild<LLMenuButton>("recent_viewsort_btn"); - - LLMenuGL* plus_menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_group_plus.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - mGroupPlusMenuHandle = plus_menu->getHandle(); - - LLToggleableMenu* nearby_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_nearby_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if(nearby_view_sort) - { - mNearbyViewSortMenuHandle = nearby_view_sort->getHandle(); - mNearbyGearButton->setMenu(nearby_view_sort); - } - - LLToggleableMenu* friend_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_friends_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if(friend_view_sort) - { - mFriendsViewSortMenuHandle = friend_view_sort->getHandle(); - mFriendsGearButton->setMenu(friend_view_sort); - } - - LLToggleableMenu* group_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_groups_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if(group_view_sort) - { - mGroupsViewSortMenuHandle = group_view_sort->getHandle(); - mGroupsGearButton->setMenu(group_view_sort); - } - - LLToggleableMenu* recent_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_recent_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if(recent_view_sort) - { - mRecentViewSortMenuHandle = recent_view_sort->getHandle(); - mRecentGearButton->setMenu(recent_view_sort); - } - LLVoiceClient::getInstance()->addObserver(this); // call this method in case some list is empty and buttons can be in inconsistent state @@ -735,9 +688,11 @@ void LLPanelPeople::updateFriendListHelpText() if (no_friends_text->getVisible()) { //update help text for empty lists - std::string message_name = mFilterSubString.empty() ? "no_friends_msg" : "no_filtered_friends_msg"; + const std::string& filter = mSavedOriginalFilters[mTabContainer->getCurrentPanelIndex()]; + + std::string message_name = filter.empty() ? "no_friends_msg" : "no_filtered_friends_msg"; LLStringUtil::format_map_t args; - args["[SEARCH_TERM]"] = LLURI::escape(mFilterSubStringOrig); + args["[SEARCH_TERM]"] = LLURI::escape(filter); no_friends_text->setText(getString(message_name, args)); } } @@ -821,31 +776,9 @@ void LLPanelPeople::updateRecentList() mRecentList->setDirty(); } -void LLPanelPeople::buttonSetVisible(std::string btn_name, BOOL visible) -{ - // To make sure we're referencing the right widget (a child of the button bar). - LLButton* button = getChild<LLView>("button_bar")->getChild<LLButton>(btn_name); - button->setVisible(visible); -} - -void LLPanelPeople::buttonSetEnabled(const std::string& btn_name, bool enabled) -{ - // To make sure we're referencing the right widget (a child of the button bar). - LLButton* button = getChild<LLView>("button_bar")->getChild<LLButton>(btn_name); - button->setEnabled(enabled); -} - -void LLPanelPeople::buttonSetAction(const std::string& btn_name, const commit_signal_t::slot_type& cb) -{ - // To make sure we're referencing the right widget (a child of the button bar). - LLButton* button = getChild<LLView>("button_bar")->getChild<LLButton>(btn_name); - button->setClickedCallback(cb); -} - void LLPanelPeople::updateButtons() { std::string cur_tab = getActiveTabName(); - bool nearby_tab_active = (cur_tab == NEARBY_TAB_NAME); bool friends_tab_active = (cur_tab == FRIENDS_TAB_NAME); bool group_tab_active = (cur_tab == GROUP_TAB_NAME); //bool recent_tab_active = (cur_tab == RECENT_TAB_NAME); @@ -856,28 +789,15 @@ void LLPanelPeople::updateButtons() bool item_selected = (selected_uuids.size() == 1); bool multiple_selected = (selected_uuids.size() >= 1); - buttonSetVisible("group_info_btn", group_tab_active); - buttonSetVisible("chat_btn", group_tab_active); - buttonSetVisible("view_profile_btn", !group_tab_active); - buttonSetVisible("im_btn", !group_tab_active); - buttonSetVisible("call_btn", !group_tab_active); - buttonSetVisible("group_call_btn", group_tab_active); - buttonSetVisible("teleport_btn", friends_tab_active); - buttonSetVisible("share_btn", nearby_tab_active || friends_tab_active); - if (group_tab_active) { - bool cur_group_active = true; - if (item_selected) { selected_id = mGroupList->getSelectedUUID(); - cur_group_active = (gAgent.getGroupID() == selected_id); } LLPanel* groups_panel = mTabContainer->getCurrentPanel(); - groups_panel->getChildView("activate_btn")->setEnabled(item_selected && !cur_group_active); // "none" or a non-active group selected - groups_panel->getChildView("minus_btn")->setEnabled(item_selected && selected_id.notNull()); + groups_panel->getChildView("minus_btn")->setEnabled(item_selected && selected_id.notNull()); // a real group selected } else { @@ -893,26 +813,20 @@ void LLPanelPeople::updateButtons() LLPanel* cur_panel = mTabContainer->getCurrentPanel(); if (cur_panel) { - cur_panel->getChildView("add_friend_btn")->setEnabled(!is_friend); + if (cur_panel->hasChild("add_friend_btn", TRUE)) + cur_panel->getChildView("add_friend_btn")->setEnabled(item_selected && !is_friend); + if (friends_tab_active) { - cur_panel->getChildView("del_btn")->setEnabled(multiple_selected); + cur_panel->getChildView("friends_del_btn")->setEnabled(multiple_selected); + } + + if (!group_tab_active) + { + cur_panel->getChildView("gear_btn")->setEnabled(multiple_selected); } } } - - bool enable_calls = LLVoiceClient::getInstance()->isVoiceWorking() && LLVoiceClient::getInstance()->voiceEnabled(); - - buttonSetEnabled("view_profile_btn",item_selected); - buttonSetEnabled("share_btn", item_selected); - buttonSetEnabled("im_btn", multiple_selected); // allow starting the friends conference for multiple selection - buttonSetEnabled("call_btn", multiple_selected && enable_calls); - buttonSetEnabled("teleport_btn", multiple_selected && LLAvatarActions::canOfferTeleport(selected_uuids)); - - bool none_group_selected = item_selected && selected_id.isNull(); - buttonSetEnabled("group_info_btn", !none_group_selected); - buttonSetEnabled("group_call_btn", !none_group_selected && enable_calls); - buttonSetEnabled("chat_btn", !none_group_selected); } std::string LLPanelPeople::getActiveTabName() const @@ -943,6 +857,9 @@ LLUUID LLPanelPeople::getCurrentItemID() const if (cur_tab == GROUP_TAB_NAME) return mGroupList->getSelectedUUID(); + if (cur_tab == BLOCKED_TAB_NAME) + return LLUUID::null; // FIXME? + llassert(0 && "unknown tab selected"); return LLUUID::null; } @@ -963,6 +880,8 @@ void LLPanelPeople::getCurrentItemIDs(uuid_vec_t& selected_uuids) const mRecentList->getSelectedUUIDs(selected_uuids); else if (cur_tab == GROUP_TAB_NAME) mGroupList->getSelectedUUIDs(selected_uuids); + else if (cur_tab == BLOCKED_TAB_NAME) + selected_uuids.clear(); // FIXME? else llassert(0 && "unknown tab selected"); @@ -1031,49 +950,60 @@ void LLPanelPeople::setSortOrder(LLAvatarList* list, ESortOrder order, bool save } } -bool LLPanelPeople::isRealGroup() -{ - return getCurrentItemID() != LLUUID::null; -} - void LLPanelPeople::onFilterEdit(const std::string& search_string) { - mFilterSubStringOrig = search_string; - LLStringUtil::trimHead(mFilterSubStringOrig); + const S32 cur_tab_idx = mTabContainer->getCurrentPanelIndex(); + std::string& filter = mSavedOriginalFilters[cur_tab_idx]; + std::string& saved_filter = mSavedFilters[cur_tab_idx]; + + filter = search_string; + LLStringUtil::trimHead(filter); + // Searches are case-insensitive - std::string search_upper = mFilterSubStringOrig; + std::string search_upper = filter; LLStringUtil::toUpper(search_upper); - if (mFilterSubString == search_upper) + if (saved_filter == search_upper) return; - mFilterSubString = search_upper; + saved_filter = search_upper; - //store accordion tabs state before any manipulation with accordion tabs - if(!mFilterSubString.empty()) + // Apply new filter to the current tab. + const std::string cur_tab = getActiveTabName(); + if (cur_tab == NEARBY_TAB_NAME) + { + mNearbyList->setNameFilter(filter); + } + else if (cur_tab == FRIENDS_TAB_NAME) + { + // store accordion tabs opened/closed state before any manipulation with accordion tabs + if (!saved_filter.empty()) { notifyChildren(LLSD().with("action","store_state")); } - - // Apply new filter. - mNearbyList->setNameFilter(mFilterSubStringOrig); - mOnlineFriendList->setNameFilter(mFilterSubStringOrig); - mAllFriendList->setNameFilter(mFilterSubStringOrig); - mRecentList->setNameFilter(mFilterSubStringOrig); - mGroupList->setNameFilter(mFilterSubStringOrig); + mOnlineFriendList->setNameFilter(filter); + mAllFriendList->setNameFilter(filter); setAccordionCollapsedByUser("tab_online", false); setAccordionCollapsedByUser("tab_all", false); - showFriendsAccordionsIfNeeded(); - //restore accordion tabs state _after_ all manipulations... - if(mFilterSubString.empty()) + // restore accordion tabs state _after_ all manipulations + if(saved_filter.empty()) { notifyChildren(LLSD().with("action","restore_state")); } } + else if (cur_tab == GROUP_TAB_NAME) + { + mGroupList->setNameFilter(filter); + } + else if (cur_tab == RECENT_TAB_NAME) + { + mRecentList->setNameFilter(filter); + } +} void LLPanelPeople::onTabSelected(const LLSD& param) { @@ -1081,11 +1011,6 @@ void LLPanelPeople::onTabSelected(const LLSD& param) updateButtons(); showFriendsAccordionsIfNeeded(); - - if (GROUP_TAB_NAME == tab_name) - mFilterEditor->setLabel(getString("groups_filter_label")); - else - mFilterEditor->setLabel(getString("people_filter_label")); } void LLPanelPeople::onAvatarListDoubleClicked(LLUICtrl* ctrl) @@ -1127,12 +1052,6 @@ void LLPanelPeople::onAvatarListCommitted(LLAvatarList* list) updateButtons(); } -void LLPanelPeople::onViewProfileButtonClicked() -{ - LLUUID id = getCurrentItemID(); - LLAvatarActions::showProfile(id); -} - void LLPanelPeople::onAddFriendButtonClicked() { LLUUID id = getCurrentItemID(); @@ -1191,11 +1110,6 @@ void LLPanelPeople::onDeleteFriendButtonClicked() } } -void LLPanelPeople::onGroupInfoButtonClicked() -{ - LLGroupActions::show(getCurrentItemID()); -} - void LLPanelPeople::onChatButtonClicked() { LLUUID group_id = getCurrentItemID(); @@ -1203,6 +1117,14 @@ void LLPanelPeople::onChatButtonClicked() LLGroupActions::startIM(group_id); } +void LLPanelPeople::onGearButtonClicked(LLUICtrl* btn) +{ + uuid_vec_t selected_uuids; + getCurrentItemIDs(selected_uuids); + // Spawn at bottom left corner of the button. + LLPanelPeopleMenus::gNearbyMenu.show(btn, selected_uuids, 0, 0); +} + void LLPanelPeople::onImButtonClicked() { uuid_vec_t selected_uuids; @@ -1219,11 +1141,6 @@ void LLPanelPeople::onImButtonClicked() } } -void LLPanelPeople::onActivateButtonClicked() -{ - LLGroupActions::activate(mGroupList->getSelectedUUID()); -} - // static void LLPanelPeople::onAvatarPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName> names) { @@ -1231,19 +1148,15 @@ void LLPanelPeople::onAvatarPicked(const uuid_vec_t& ids, const std::vector<LLAv LLAvatarActions::requestFriendshipDialog(ids[0], names[0].getCompleteName()); } -void LLPanelPeople::onGroupPlusButtonClicked() +bool LLPanelPeople::onGroupPlusButtonValidate() { if (!gAgent.canJoinGroups()) { LLNotificationsUtil::add("JoinedTooManyGroups"); - return; + return false; } - LLMenuGL* plus_menu = (LLMenuGL*)mGroupPlusMenuHandle.get(); - if (!plus_menu) - return; - - showGroupMenu(plus_menu); + return true; } void LLPanelPeople::onGroupMinusButtonClicked() @@ -1288,10 +1201,6 @@ void LLPanelPeople::onFriendsViewSortMenuItemClicked(const LLSD& userdata) mAllFriendList->showPermissions(show_permissions); mOnlineFriendList->showPermissions(show_permissions); } - else if (chosen_item == "panel_block_list_sidetray") - { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); - } } void LLPanelPeople::onGroupsViewSortMenuItemClicked(const LLSD& userdata) @@ -1324,10 +1233,6 @@ void LLPanelPeople::onNearbyViewSortMenuItemClicked(const LLSD& userdata) { setSortOrder(mNearbyList, E_SORT_BY_DISTANCE); } - else if (chosen_item == "panel_block_list_sidetray") - { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); - } } bool LLPanelPeople::onNearbyViewSortMenuItemCheck(const LLSD& userdata) @@ -1361,10 +1266,6 @@ void LLPanelPeople::onRecentViewSortMenuItemClicked(const LLSD& userdata) { mRecentList->toggleIcons(); } - else if (chosen_item == "panel_block_list_sidetray") - { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); - } } bool LLPanelPeople::onFriendsViewSortMenuItemCheck(const LLSD& userdata) @@ -1393,40 +1294,6 @@ bool LLPanelPeople::onRecentViewSortMenuItemCheck(const LLSD& userdata) return false; } -void LLPanelPeople::onCallButtonClicked() -{ - uuid_vec_t selected_uuids; - getCurrentItemIDs(selected_uuids); - - if (selected_uuids.size() == 1) - { - // initiate a P2P voice chat with the selected user - LLAvatarActions::startCall(getCurrentItemID()); - } - else if (selected_uuids.size() > 1) - { - // initiate an ad-hoc voice chat with multiple users - LLAvatarActions::startAdhocCall(selected_uuids); - } -} - -void LLPanelPeople::onGroupCallButtonClicked() -{ - LLGroupActions::startCall(getCurrentItemID()); -} - -void LLPanelPeople::onTeleportButtonClicked() -{ - uuid_vec_t selected_uuids; - getCurrentItemIDs(selected_uuids); - LLAvatarActions::offerTeleport(selected_uuids); -} - -void LLPanelPeople::onShareButtonClicked() -{ - LLAvatarActions::share(getCurrentItemID()); -} - void LLPanelPeople::onMoreButtonClicked() { // *TODO: not implemented yet diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h index 46c58cd139..da27f83074 100644 --- a/indra/newview/llpanelpeople.h +++ b/indra/newview/llpanelpeople.h @@ -80,31 +80,22 @@ private: std::string getActiveTabName() const; LLUUID getCurrentItemID() const; void getCurrentItemIDs(uuid_vec_t& selected_uuids) const; - void buttonSetVisible(std::string btn_name, BOOL visible); - void buttonSetEnabled(const std::string& btn_name, bool enabled); - void buttonSetAction(const std::string& btn_name, const commit_signal_t::slot_type& cb); void showGroupMenu(LLMenuGL* menu); void setSortOrder(LLAvatarList* list, ESortOrder order, bool save = true); // UI callbacks void onFilterEdit(const std::string& search_string); void onTabSelected(const LLSD& param); - void onViewProfileButtonClicked(); void onAddFriendButtonClicked(); void onAddFriendWizButtonClicked(); void onDeleteFriendButtonClicked(); - void onGroupInfoButtonClicked(); void onChatButtonClicked(); + void onGearButtonClicked(LLUICtrl* btn); void onImButtonClicked(); - void onCallButtonClicked(); - void onGroupCallButtonClicked(); - void onTeleportButtonClicked(); - void onShareButtonClicked(); void onMoreButtonClicked(); - void onActivateButtonClicked(); void onAvatarListDoubleClicked(LLUICtrl* ctrl); void onAvatarListCommitted(LLAvatarList* list); - void onGroupPlusButtonClicked(); + bool onGroupPlusButtonValidate(); void onGroupMinusButtonClicked(); void onGroupPlusMenuItemClicked(const LLSD& userdata); @@ -113,8 +104,6 @@ private: void onGroupsViewSortMenuItemClicked(const LLSD& userdata); void onRecentViewSortMenuItemClicked(const LLSD& userdata); - //returns false only if group is "none" - bool isRealGroup(); bool onFriendsViewSortMenuItemCheck(const LLSD& userdata); bool onRecentViewSortMenuItemCheck(const LLSD& userdata); bool onNearbyViewSortMenuItemCheck(const LLSD& userdata); @@ -135,7 +124,6 @@ private: bool isAccordionCollapsedByUser(LLUICtrl* acc_tab); bool isAccordionCollapsedByUser(const std::string& name); - LLFilterEditor* mFilterEditor; LLTabContainer* mTabContainer; LLAvatarList* mOnlineFriendList; LLAvatarList* mAllFriendList; @@ -144,24 +132,13 @@ private: LLGroupList* mGroupList; LLNetMap* mMiniMap; - LLHandle<LLView> mGroupPlusMenuHandle; - LLHandle<LLView> mNearbyViewSortMenuHandle; - LLHandle<LLView> mFriendsViewSortMenuHandle; - LLHandle<LLView> mGroupsViewSortMenuHandle; - LLHandle<LLView> mRecentViewSortMenuHandle; + std::vector<std::string> mSavedOriginalFilters; + std::vector<std::string> mSavedFilters; Updater* mFriendListUpdater; Updater* mNearbyListUpdater; Updater* mRecentListUpdater; Updater* mButtonsUpdater; - - LLMenuButton* mNearbyGearButton; - LLMenuButton* mFriendsGearButton; - LLMenuButton* mGroupsGearButton; - LLMenuButton* mRecentGearButton; - - std::string mFilterSubString; - std::string mFilterSubStringOrig; }; #endif //LL_LLPANELPEOPLE_H diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp index f12c4de2f7..c9eebe24d3 100644 --- a/indra/newview/llpanelpeoplemenus.cpp +++ b/indra/newview/llpanelpeoplemenus.cpp @@ -51,6 +51,7 @@ LLContextMenu* NearbyMenu::createMenu() // set up the callbacks for all of the avatar menu items LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + LLContextMenu* menu; if ( mUUIDs.size() == 1 ) { @@ -67,20 +68,21 @@ LLContextMenu* NearbyMenu::createMenu() registrar.add("Avatar.Share", boost::bind(&LLAvatarActions::share, id)); registrar.add("Avatar.Pay", boost::bind(&LLAvatarActions::pay, id)); registrar.add("Avatar.BlockUnblock", boost::bind(&LLAvatarActions::toggleBlock, id)); + registrar.add("Avatar.InviteToGroup", boost::bind(&LLAvatarActions::inviteToGroup, id)); 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 createFromFile("menu_people_nearby.xml"); + menu = createFromFile("menu_people_nearby.xml"); } else { // Set up for multi-selected People // registrar.add("Avatar.AddFriend", boost::bind(&LLAvatarActions::requestFriendshipDialog, mUUIDs)); // *TODO: unimplemented - registrar.add("Avatar.IM", boost::bind(&LLAvatarActions::startConference, mUUIDs)); - registrar.add("Avatar.Call", boost::bind(&LLAvatarActions::startAdhocCall, mUUIDs)); + registrar.add("Avatar.IM", boost::bind(&LLAvatarActions::startConference, mUUIDs, LLUUID::null)); + registrar.add("Avatar.Call", boost::bind(&LLAvatarActions::startAdhocCall, mUUIDs, LLUUID::null)); registrar.add("Avatar.OfferTeleport", boost::bind(&NearbyMenu::offerTeleport, this)); registrar.add("Avatar.RemoveFriend",boost::bind(&LLAvatarActions::removeFriendsDialog, mUUIDs)); // registrar.add("Avatar.Share", boost::bind(&LLAvatarActions::startIM, mUUIDs)); // *TODO: unimplemented @@ -88,8 +90,10 @@ LLContextMenu* NearbyMenu::createMenu() enable_registrar.add("Avatar.EnableItem", boost::bind(&NearbyMenu::enableContextMenuItem, this, _2)); // create the context menu from the XUI - return createFromFile("menu_people_nearby_multiselect.xml"); + menu = createFromFile("menu_people_nearby_multiselect.xml"); } + + return menu; } bool NearbyMenu::enableContextMenuItem(const LLSD& userdata) diff --git a/indra/newview/llpaneltopinfobar.cpp b/indra/newview/llpaneltopinfobar.cpp index 280cc11179..854deb00d0 100644 --- a/indra/newview/llpaneltopinfobar.cpp +++ b/indra/newview/llpaneltopinfobar.cpp @@ -230,7 +230,7 @@ void LLPanelTopInfoBar::buildLocationString(std::string& loc_str, bool show_coor void LLPanelTopInfoBar::setParcelInfoText(const std::string& new_text) { LLRect old_rect = getRect(); - const LLFontGL* font = mParcelInfoText->getDefaultFont(); + const LLFontGL* font = mParcelInfoText->getFont(); S32 new_text_width = font->getWidth(new_text); mParcelInfoText->setText(new_text); diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index 975a6c67d8..47518a365f 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -32,6 +32,7 @@ #include "llagent.h" #include "llimview.h" +#include "llpanelpeoplemenus.h" #include "llnotificationsutil.h" #include "llparticipantlist.h" #include "llspeakers.h" @@ -197,10 +198,10 @@ private: uuid_set_t mAvalineCallers; }; -LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, +LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* avatar_list, bool use_context_menu/* = true*/, - bool exclude_agent /*= true*/, + bool exclude_agent /*= true*/, bool can_toggle_icons /*= true*/) : mSpeakerMgr(data_source), mAvatarList(avatar_list), @@ -233,8 +234,9 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, if (use_context_menu) { - mParticipantListMenu = new LLParticipantListMenu(*this); - mAvatarList->setContextMenu(mParticipantListMenu); + //mParticipantListMenu = new LLParticipantListMenu(*this); + //mAvatarList->setContextMenu(mParticipantListMenu); + mAvatarList->setContextMenu(&LLPanelPeopleMenus::gNearbyMenu); } else { @@ -473,6 +475,7 @@ void LLParticipantList::update() { mSpeakerMgr->update(true); + // Need to resort the participant list if it's in sort by recent speaker order. if (E_SORT_BY_RECENT_SPEAKERS == getSortOrder() && !isHovered()) { // Resort avatar list @@ -670,7 +673,7 @@ bool LLParticipantList::SpeakerMuteListener::handleEvent(LLPointer<LLOldEvents:: return mParent.onSpeakerMuteEvent(event, userdata); } -LLContextMenu* LLParticipantList::LLParticipantListMenu::createMenu() +/*LLContextMenu* LLParticipantList::LLParticipantListMenu::createMenu() { // set up the callbacks for all of the avatar menu items LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; @@ -708,7 +711,7 @@ LLContextMenu* LLParticipantList::LLParticipantListMenu::createMenu() main_menu->arrangeAndClear(); return main_menu; -} +}*/ void LLParticipantList::LLParticipantListMenu::show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y) { diff --git a/indra/newview/llplacesinventorybridge.cpp b/indra/newview/llplacesinventorybridge.cpp index fe4cc0f55f..ebd9604c5b 100644 --- a/indra/newview/llplacesinventorybridge.cpp +++ b/indra/newview/llplacesinventorybridge.cpp @@ -85,34 +85,33 @@ void LLPlacesLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags) void LLPlacesFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { + std::vector<std::string> items; + std::vector<std::string> disabled_items; + + LLInventoryPanel* inv_panel = mInventoryPanel.get(); + bool is_open = false; + if (inv_panel) { - std::vector<std::string> items; - std::vector<std::string> disabled_items; + LLFolderViewFolder* folder = dynamic_cast<LLFolderViewFolder*>(inv_panel->getItemByID(mUUID)); + is_open = (NULL != folder) && folder->isOpen(); + } - LLInventoryPanel* inv_panel = mInventoryPanel.get(); - bool is_open = false; - if (inv_panel) - { - LLFolderViewFolder* folder = dynamic_cast<LLFolderViewFolder*>(inv_panel->getRootFolder()->getItemByID(mUUID)); - is_open = (NULL != folder) && folder->isOpen(); - } + // collect all items' names + fill_items_with_menu_items(items, menu); - // collect all items' names - fill_items_with_menu_items(items, menu); + // remove expand or collapse menu item depend on folder state + std::string collapse_expand_item_to_hide(is_open ? "expand" : "collapse"); + std::vector<std::string>::iterator it = std::find(items.begin(), items.end(), collapse_expand_item_to_hide); + if (it != items.end()) items.erase(it); - // remove expand or collapse menu item depend on folder state - std::string collapse_expand_item_to_hide(is_open ? "expand" : "collapse"); - std::vector<std::string>::iterator it = std::find(items.begin(), items.end(), collapse_expand_item_to_hide); - if (it != items.end()) items.erase(it); - // Disabled items are processed via LLLandmarksPanel::isActionEnabled() - // they should be synchronized with Places/My Landmarks/Gear menu. See EXT-1601 + // Disabled items are processed via LLLandmarksPanel::isActionEnabled() + // they should be synchronized with Places/My Landmarks/Gear menu. See EXT-1601 - // repeat parent functionality - sSelf = getHandle(); // necessary for "New Folder" functionality + // repeat parent functionality + sSelf = getHandle(); // necessary for "New Folder" functionality - hide_context_entries(menu, items, disabled_items); - } + hide_context_entries(menu, items, disabled_items); } //virtual @@ -140,7 +139,7 @@ LLFolderViewFolder* LLPlacesFolderBridge::getFolder() LLInventoryPanel* inv_panel = mInventoryPanel.get(); if (inv_panel) { - folder = dynamic_cast<LLFolderViewFolder*>(inv_panel->getRootFolder()->getItemByID(mUUID)); + folder = dynamic_cast<LLFolderViewFolder*>(inv_panel->getItemByID(mUUID)); } return folder; @@ -152,6 +151,7 @@ LLInvFVBridge* LLPlacesInventoryBridgeBuilder::createBridge( LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags/* = 0x00*/) const @@ -170,11 +170,12 @@ LLInvFVBridge* LLPlacesInventoryBridgeBuilder::createBridge( if (actual_asset_type == LLAssetType::AT_LINK_FOLDER) { // *TODO: Create a link folder handler instead if it is necessary - new_listener = LLInventoryFVBridgeBuilder::createBridge( + new_listener = LLInventoryFolderViewModelBuilder::createBridge( asset_type, actual_asset_type, inv_type, inventory, + view_model, root, uuid, flags); @@ -183,11 +184,12 @@ LLInvFVBridge* LLPlacesInventoryBridgeBuilder::createBridge( new_listener = new LLPlacesFolderBridge(inv_type, inventory, root, uuid); break; default: - new_listener = LLInventoryFVBridgeBuilder::createBridge( + new_listener = LLInventoryFolderViewModelBuilder::createBridge( asset_type, actual_asset_type, inv_type, inventory, + view_model, root, uuid, flags); diff --git a/indra/newview/llplacesinventorybridge.h b/indra/newview/llplacesinventorybridge.h index 52beacef9c..07d18d03c5 100644 --- a/indra/newview/llplacesinventorybridge.h +++ b/indra/newview/llplacesinventorybridge.h @@ -82,13 +82,14 @@ protected: * * It builds Bridges for Landmarks and Folders in Places Landmarks Panel */ -class LLPlacesInventoryBridgeBuilder : public LLInventoryFVBridgeBuilder +class LLPlacesInventoryBridgeBuilder : public LLInventoryFolderViewModelBuilder { public: /*virtual*/ LLInvFVBridge* createBridge(LLAssetType::EType asset_type, LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags = 0x00) const; diff --git a/indra/newview/llplacesinventorypanel.cpp b/indra/newview/llplacesinventorypanel.cpp index f7823f4fe8..db3f245389 100644 --- a/indra/newview/llplacesinventorypanel.cpp +++ b/indra/newview/llplacesinventorypanel.cpp @@ -30,7 +30,8 @@ #include "llplacesinventorypanel.h" -#include "llfoldervieweventlistener.h" +#include "llfolderviewmodel.h" +#include "llfolderview.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" #include "llpanellandmarks.h" @@ -57,44 +58,6 @@ LLPlacesInventoryPanel::~LLPlacesInventoryPanel() delete mSavedFolderState; } -void LLPlacesInventoryPanel::buildFolderView(const LLInventoryPanel::Params& params) -{ - // Determine the root folder in case specified, and - // build the views starting with that folder. - const LLFolderType::EType preferred_type = LLViewerFolderType::lookupTypeFromNewCategoryName(params.start_folder); - - LLUUID root_id; - - if ("LIBRARY" == params.start_folder()) - { - root_id = gInventory.getLibraryRootFolderID(); - } - else - { - root_id = (preferred_type != LLFolderType::FT_NONE ? gInventory.findCategoryUUIDForType(preferred_type) : LLUUID::null); - } - - LLRect folder_rect(0, - 0, - getRect().getWidth(), - 0); - LLPlacesFolderView::Params p; - p.name = getName(); - p.title = getLabel(); - p.rect = folder_rect; - p.listener = mInvFVBridgeBuilder->createBridge(LLAssetType::AT_CATEGORY, - LLAssetType::AT_CATEGORY, - LLInventoryType::IT_CATEGORY, - this, - NULL, - root_id); - p.parent_panel = this; - p.allow_multiselect = mAllowMultiSelect; - p.use_ellipses = true; // truncate inventory item text so remove horizontal scroller - mFolderRoot = (LLFolderView*)LLUICtrlFactory::create<LLPlacesFolderView>(p); -} - - // save current folder open state void LLPlacesInventoryPanel::saveFolderState() { @@ -161,7 +124,7 @@ BOOL LLPlacesFolderView::handleRightMouseDown(S32 x, S32 y, MASK mask) // then determine its type and set necessary menu handle if (getCurSelectedItem()) { - LLInventoryType::EType inventory_type = getCurSelectedItem()->getListener()->getInventoryType(); + LLInventoryType::EType inventory_type = static_cast<LLFolderViewModelItemInventory*>(getCurSelectedItem()->getViewModelItem())->getInventoryType(); inventory_type_menu_handle_t::iterator it_handle = mMenuHandlesByInventoryType.find(inventory_type); if (it_handle != mMenuHandlesByInventoryType.end()) diff --git a/indra/newview/llplacesinventorypanel.h b/indra/newview/llplacesinventorypanel.h index f647e7f970..1544b51aed 100644 --- a/indra/newview/llplacesinventorypanel.h +++ b/indra/newview/llplacesinventorypanel.h @@ -46,8 +46,6 @@ public: LLPlacesInventoryPanel(const Params& p); ~LLPlacesInventoryPanel(); - /*virtual*/ void buildFolderView(const LLInventoryPanel::Params& params); - void saveFolderState(); void restoreFolderState(); @@ -57,7 +55,7 @@ private: LLSaveFolderState* mSavedFolderState; }; - +//TODO RN: this class is currently unused, make sure that behavior remains class LLPlacesFolderView : public LLFolderView { public: @@ -77,8 +75,6 @@ public: mParentLandmarksPanel = panel; } - S32 getSelectedCount() { return (S32)mSelectedItems.size(); } - private: /** * holds pointer to landmark panel. This pointer is used in @c LLPlacesFolderView::handleRightMouseDown diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 88727bf59b..29eb5ce69e 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -305,7 +305,11 @@ BOOL LLFloaterScriptSearch::handleKeyHere(KEY key, MASK mask) { if (mEditorCore) { - return mEditorCore->handleKeyHere(key, mask); + BOOL handled = mEditorCore->handleKeyHere(key, mask); + if (!handled) + { + LLFloater::handleKeyHere(key, mask); + } } return FALSE; diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp index d340b304ca..a4a0198305 100644 --- a/indra/newview/llscreenchannel.cpp +++ b/indra/newview/llscreenchannel.cpp @@ -253,12 +253,26 @@ void LLScreenChannel::addToast(const LLToast::Params& p) { bool store_toast = false, show_toast = false; - mDisplayToastsAlways ? show_toast = true : show_toast = mWasStartUpToastShown && (mShowToasts || p.force_show); + if (mDisplayToastsAlways) + { + show_toast = true; + } + else + { + show_toast = mWasStartUpToastShown && (mShowToasts || p.force_show); + } store_toast = !show_toast && p.can_be_stored && mCanStoreToasts; if(!show_toast && !store_toast) { - mRejectToastSignal(p.notif_id); + LLNotificationPtr notification = LLNotifications::instance().find(p.notif_id); + + if (notification && + (!notification->canLogToIM() || !notification->hasFormElements())) + { + // only cancel notification if it isn't being used in IM session + LLNotifications::instance().cancel(notification); + } return; } @@ -371,7 +385,7 @@ void LLScreenChannel::storeToast(ToastElem& toast_elem) const LLToast* toast = toast_elem.getToast(); if (toast) { - mStoredToastList.push_back(toast_elem); + mStoredToastList.push_back(toast_elem); mOnStoreToast(toast->getPanel(), toast->getNotificationID()); } } @@ -410,14 +424,14 @@ void LLScreenChannel::loadStoredToastByNotificationIDToChannel(LLUUID id) LLToast* toast = it->getToast(); if (toast) { - if(toast->getVisible()) - { - // toast is already in channel - return; - } + if(toast->getVisible()) + { + // toast is already in channel + return; + } - toast->setIsHidden(false); - toast->startTimer(); + toast->setIsHidden(false); + toast->startTimer(); mToastList.push_back(*it); } @@ -425,34 +439,12 @@ void LLScreenChannel::loadStoredToastByNotificationIDToChannel(LLUUID id) } //-------------------------------------------------------------------------- -void LLScreenChannel::removeStoredToastByNotificationID(LLUUID id) -{ - // *TODO: may be remove this function - std::vector<ToastElem>::iterator it = find(mStoredToastList.begin(), mStoredToastList.end(), id); - - if( it == mStoredToastList.end() ) - return; - - const LLToast* toast = it->getToast(); - if (toast) - { - mRejectToastSignal(toast->getNotificationID()); - } - - // Call find() once more, because the mStoredToastList could have been changed - // in mRejectToastSignal callback and the iterator could have become invalid. - it = find(mStoredToastList.begin(), mStoredToastList.end(), id); - if (it != mStoredToastList.end()) - { - mStoredToastList.erase(it); - } -} - -//-------------------------------------------------------------------------- void LLScreenChannel::killToastByNotificationID(LLUUID id) { // searching among toasts on a screen std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), id); + LLNotificationPtr notification = LLNotifications::instance().find(id); + if (!notification) return; if( it != mToastList.end()) { @@ -465,42 +457,67 @@ void LLScreenChannel::killToastByNotificationID(LLUUID id) // the toast will be destroyed. if(toast && toast->isNotificationValid()) { - mRejectToastSignal(toast->getNotificationID()); + if (!notification->canLogToIM() || !notification->hasFormElements()) + { + // only cancel notification if it isn't being used in IM session + LLNotifications::instance().cancel(notification); + } } else { - - deleteToast(toast); - mToastList.erase(it); - redrawToasts(); + removeToastByNotificationID(id); } - return; } - - // searching among stored toasts - it = find(mStoredToastList.begin(), mStoredToastList.end(), id); - - if (it != mStoredToastList.end()) + else { - LLToast* toast = it->getToast(); - if (toast) + // searching among stored toasts + it = find(mStoredToastList.begin(), mStoredToastList.end(), id); + + if( it != mStoredToastList.end() ) { - // send signal to a listener to let him perform some action on toast rejecting - mRejectToastSignal(toast->getNotificationID()); - deleteToast(toast); + LLToast* toast = it->getToast(); + if (toast) + { + if (!notification->canLogToIM() || !notification->hasFormElements()) + { + // only cancel notification if it isn't being used in IM session + LLNotifications::instance().cancel(notification); + } + deleteToast(toast); + } + } + + // Call find() once more, because the mStoredToastList could have been changed + // via notification cancellation and the iterator could have become invalid. + it = find(mStoredToastList.begin(), mStoredToastList.end(), id); + if (it != mStoredToastList.end()) + { + mStoredToastList.erase(it); } } +} + +void LLScreenChannel::removeToastByNotificationID(LLUUID id) +{ + std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), id); + while( it != mToastList.end()) + { + deleteToast(it->getToast()); + mToastList.erase(it); + redrawToasts(); + // find next toast with matching id + it = find(mToastList.begin(), mToastList.end(), id); + } - // Call find() once more, because the mStoredToastList could have been changed - // in mRejectToastSignal callback and the iterator could have become invalid. it = find(mStoredToastList.begin(), mStoredToastList.end(), id); if (it != mStoredToastList.end()) { + deleteToast(it->getToast()); mStoredToastList.erase(it); } - } + void LLScreenChannel::killMatchedToasts(const Matcher& matcher) { std::list<const LLToast*> to_delete = findToasts(matcher); @@ -521,11 +538,11 @@ void LLScreenChannel::modifyToastByNotificationID(LLUUID id, LLPanel* panel) LLToast* toast = it->getToast(); if (toast) { - LLPanel* old_panel = toast->getPanel(); - toast->removeChild(old_panel); - delete old_panel; - toast->insertPanel(panel); - toast->startTimer(); + LLPanel* old_panel = toast->getPanel(); + toast->removeChild(old_panel); + delete old_panel; + toast->insertPanel(panel); + toast->startTimer(); } redrawToasts(); } @@ -679,7 +696,7 @@ void LLScreenChannel::showToastsCentre() return; } - LLRect toast_rect; + LLRect toast_rect; S32 bottom = (getRect().mTop - getRect().mBottom)/2 + toast->getRect().getHeight()/2; std::vector<ToastElem>::reverse_iterator it; diff --git a/indra/newview/llscreenchannel.h b/indra/newview/llscreenchannel.h index 56a9cf8b4b..e5f4807ab7 100644 --- a/indra/newview/llscreenchannel.h +++ b/indra/newview/llscreenchannel.h @@ -84,6 +84,7 @@ public: // kill or modify a toast by its ID virtual void killToastByNotificationID(LLUUID id) {}; virtual void modifyToastNotificationByID(LLUUID id, LLSD data) {}; + virtual void removeToastByNotificationID(LLUUID id){}; // hide all toasts from screen, but not remove them from a channel virtual void hideToastsFromScreen() {}; @@ -175,6 +176,7 @@ public: void addToast(const LLToast::Params& p); // kill or modify a toast by its ID void killToastByNotificationID(LLUUID id); + void removeToastByNotificationID(LLUUID id); void killMatchedToasts(const Matcher& matcher); void modifyToastByNotificationID(LLUUID id, LLPanel* panel); // hide all toasts from screen, but not remove them from a channel @@ -195,8 +197,6 @@ public: void loadStoredToastsToChannel(); // 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 @@ -227,16 +227,12 @@ public: // Channel's signals // signal on storing of faded toasts event - typedef boost::function<void (LLPanel* info_panel, const LLUUID id)> store_tost_callback_t; - typedef boost::signals2::signal<void (LLPanel* info_panel, const LLUUID id)> store_tost_signal_t; - store_tost_signal_t mOnStoreToast; - boost::signals2::connection setOnStoreToastCallback(store_tost_callback_t cb) { return mOnStoreToast.connect(cb); } - // signal on rejecting of a toast event - typedef boost::function<void (LLUUID id)> reject_tost_callback_t; - typedef boost::signals2::signal<void (LLUUID id)> reject_tost_signal_t; - reject_tost_signal_t mRejectToastSignal; boost::signals2::connection setOnRejectToastCallback(reject_tost_callback_t cb) { return mRejectToastSignal.connect(cb); } + typedef boost::signals2::signal<void (LLPanel* info_panel, const LLUUID id)> store_toast_signal_t; + boost::signals2::connection addOnStoreToastCallback(store_toast_signal_t::slot_type cb) { return mOnStoreToast.connect(cb); } private: + store_toast_signal_t mOnStoreToast; + class ToastElem { public: diff --git a/indra/newview/llsidepanelappearance.cpp b/indra/newview/llsidepanelappearance.cpp index 853656905c..b143240187 100644 --- a/indra/newview/llsidepanelappearance.cpp +++ b/indra/newview/llsidepanelappearance.cpp @@ -38,7 +38,7 @@ #include "llfiltereditor.h" #include "llfloaterreg.h" #include "llfloaterworldmap.h" -#include "llfoldervieweventlistener.h" +#include "llfolderviewmodel.h" #include "lloutfitobserver.h" #include "llpaneleditwearable.h" #include "llpaneloutfitsinventory.h" @@ -267,11 +267,11 @@ void LLSidepanelAppearance::onOpenOutfitButtonClicked() if (inventory_panel) { LLFolderView* root = inventory_panel->getRootFolder(); - LLFolderViewItem *outfit_folder = root->getItemByID(outfit_link->getLinkedUUID()); + LLFolderViewItem *outfit_folder = inventory_panel->getItemByID(outfit_link->getLinkedUUID()); if (outfit_folder) { outfit_folder->setOpen(!outfit_folder->isOpen()); - root->setSelectionFromRoot(outfit_folder,TRUE); + root->setSelection(outfit_folder,TRUE); root->scrollToShowSelection(); } } diff --git a/indra/newview/llsidepanelinventory.cpp b/indra/newview/llsidepanelinventory.cpp index 4f9ab318a5..acb232c77f 100644 --- a/indra/newview/llsidepanelinventory.cpp +++ b/indra/newview/llsidepanelinventory.cpp @@ -36,6 +36,7 @@ #include "llfirstuse.h" #include "llfloatersidepanelcontainer.h" #include "llfoldertype.h" +#include "llfolderview.h" #include "llhttpclient.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" @@ -259,9 +260,8 @@ void LLSidepanelInventory::updateInbox() // const bool do_not_create_folder = false; - const bool do_not_find_in_library = false; - const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, do_not_create_folder, do_not_find_in_library); + const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, do_not_create_folder); // Set up observer to listen for creation of inbox if at least one of them doesn't exist if (inbox_id.isNull()) @@ -383,10 +383,10 @@ void LLSidepanelInventory::onToggleInboxBtn() { inboxPanel->setTargetDim(gSavedPerAccountSettings.getS32("InventoryInboxHeight")); if (inboxPanel->isInVisibleChain()) - { - gSavedPerAccountSettings.setU32("LastInventoryInboxActivity", time_corrected()); - } + { + gSavedPerAccountSettings.setU32("LastInventoryInboxActivity", time_corrected()); } +} else { gSavedPerAccountSettings.setS32("InventoryInboxHeight", inboxPanel->getTargetDim()); @@ -472,7 +472,7 @@ void LLSidepanelInventory::performActionOnSelection(const std::string &action) } } - current_item->getListener()->performAction(mPanelMainInventory->getActivePanel()->getModel(), action); + static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->performAction(mPanelMainInventory->getActivePanel()->getModel(), action); } void LLSidepanelInventory::onWearButtonClicked() @@ -662,7 +662,7 @@ LLInventoryItem *LLSidepanelInventory::getSelectedItem() return NULL; } } - const LLUUID &item_id = current_item->getListener()->getUUID(); + const LLUUID &item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); LLInventoryItem *item = gInventory.getItem(item_id); return item; } @@ -671,7 +671,7 @@ U32 LLSidepanelInventory::getSelectedCount() { int count = 0; - std::set<LLUUID> selection_list = mPanelMainInventory->getActivePanel()->getRootFolder()->getSelectionList(); + std::set<LLFolderViewItem*> selection_list = mPanelMainInventory->getActivePanel()->getRootFolder()->getSelectionList(); count += selection_list.size(); if ((count == 0) && mInboxEnabled && (mInventoryPanelInbox != NULL)) @@ -722,9 +722,9 @@ void LLSidepanelInventory::clearSelections(bool clearMain, bool clearInbox) updateVerbs(); } -std::set<LLUUID> LLSidepanelInventory::getInboxSelectionList() +std::set<LLFolderViewItem*> LLSidepanelInventory::getInboxSelectionList() { - std::set<LLUUID> inventory_selected_uuids; + std::set<LLFolderViewItem*> inventory_selected_uuids; if (mInboxEnabled && (mInventoryPanelInbox != NULL)) { diff --git a/indra/newview/llsidepanelinventory.h b/indra/newview/llsidepanelinventory.h index a33607f50d..e8b2808d4f 100644 --- a/indra/newview/llsidepanelinventory.h +++ b/indra/newview/llsidepanelinventory.h @@ -63,7 +63,7 @@ public: BOOL isMainInventoryPanelActive() const; void clearSelections(bool clearMain, bool clearInbox); - std::set<LLUUID> getInboxSelectionList(); + std::set<LLFolderViewItem*> getInboxSelectionList(); void showItemInfoPanel(); void showTaskInfoPanel(); diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index 78c905f6ff..44d32f7d93 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -2776,7 +2776,7 @@ void renderVisibility(LLSpatialGroup* group, LLCamera* camera) void renderCrossHairs(LLVector3 position, F32 size, LLColor4 color) { - gGL.diffuseColor4fv(color.mV); + gGL.color4fv(color.mV); gGL.begin(LLRender::LINES); { gGL.vertex3fv((position - LLVector3(size, 0.f, 0.f)).mV); @@ -4024,7 +4024,7 @@ void renderAgentTarget(LLVOAvatar* avatar) if (avatar->isSelf()) { renderCrossHairs(avatar->getPositionAgent(), 0.2f, LLColor4(1, 0, 0, 0.8f)); - renderCrossHairs(avatar->mDrawable->getPositionAgent(), 0.2f, LLColor4(1, 0, 0, 0.8f)); + renderCrossHairs(avatar->mDrawable->getPositionAgent(), 0.2f, LLColor4(0, 1, 0, 0.8f)); renderCrossHairs(avatar->mRoot.getWorldPosition(), 0.2f, LLColor4(1, 1, 1, 0.8f)); renderCrossHairs(avatar->mPelvisp->getWorldPosition(), 0.2f, LLColor4(0, 0, 1, 0.8f)); } diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h index b9358cf37c..1c6f51e131 100644 --- a/indra/newview/llspeakers.h +++ b/indra/newview/llspeakers.h @@ -29,7 +29,6 @@ #include "llevent.h" #include "lleventtimer.h" -#include "llspeakers.h" #include "llvoicechannel.h" class LLSpeakerMgr; diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index a28d8d3546..ba9c2c9e2f 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -63,7 +63,7 @@ #include "llmemorystream.h" #include "llmessageconfig.h" #include "llmoveview.h" -#include "llnearbychat.h" +#include "llimfloatercontainer.h" #include "llnotifications.h" #include "llnotificationsutil.h" #include "llteleporthistory.h" @@ -94,6 +94,7 @@ #include "llcallingcard.h" #include "llconsole.h" #include "llcontainerview.h" +#include "llconversationlog.h" #include "lldebugview.h" #include "lldrawable.h" #include "lleventnotifier.h" @@ -1268,6 +1269,8 @@ bool idle_startup() display_startup(); LLStartUp::setStartupState( STATE_MULTIMEDIA_INIT ); + LLConversationLog::getInstance(); + return FALSE; } @@ -1378,14 +1381,9 @@ bool idle_startup() LLVoiceClient::getInstance()->updateSettings(); display_startup(); - //gCacheName is required for nearby chat history loading - //so I just moved nearby history loading a few states further - if (gSavedPerAccountSettings.getBOOL("LogShowHistory")) - { - LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); - if (nearby_chat) nearby_chat->loadHistory(); - } - display_startup(); + // create a container's instance for start a controlling conversation windows + // by the voice's events + LLIMFloaterContainer::getInstance(); // *Note: this is where gWorldMap used to be initialized. @@ -2165,7 +2163,6 @@ bool idle_startup() LLAgentPicksInfo::getInstance()->requestNumberOfPicks(); - LLIMFloater::initIMFloater(); display_startup(); llassert(LLPathfindingManager::getInstance() != NULL); diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index 0cb6c85012..18e0d9d0d2 100644 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -433,13 +433,19 @@ BOOL LLIMWellWindow::ObjectRowPanel::handleRightMouseDown(S32 x, S32 y, MASK mas ////////////////////////////////////////////////////////////////////////// // PUBLIC METHODS +LLNotificationWellWindow::WellNotificationChannel::WellNotificationChannel(LLNotificationWellWindow* well_window) +: LLNotificationChannel(LLNotificationChannel::Params().name(well_window->getPathname())), + mWellWindow(well_window) +{ + connectToChannel("Notifications"); + connectToChannel("Group Notifications"); + connectToChannel("Offer"); +} + LLNotificationWellWindow::LLNotificationWellWindow(const LLSD& key) -: LLSysWellWindow(key) +: LLSysWellWindow(key) { - // init connections to the list's update events - connectListUpdaterToSignal("notify"); - connectListUpdaterToSignal("groupnotify"); - connectListUpdaterToSignal("offer"); + mNotificationUpdates.reset(new WellNotificationChannel(this)); } // static @@ -519,7 +525,7 @@ void LLNotificationWellWindow::initChannel() LLSysWellWindow::initChannel(); if(mChannel) { - mChannel->setOnStoreToastCallback(boost::bind(&LLNotificationWellWindow::onStoreToast, this, _1, _2)); + mChannel->addOnStoreToastCallback(boost::bind(&LLNotificationWellWindow::onStoreToast, this, _1, _2)); } } @@ -546,20 +552,6 @@ void LLNotificationWellWindow::onStoreToast(LLPanel* info_panel, LLUUID id) addItem(p); } -void LLNotificationWellWindow::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(&LLNotificationWellWindow::removeItemByID, this, _1)); - } - else - { - llwarns << "LLSysWellWindow::connectListUpdaterToSignal() - could not get a handler for '" << notification_type <<"' type of notifications" << llendl; - } -} - void LLNotificationWellWindow::onItemClick(LLSysWellItem* item) { LLUUID id = item->getID(); @@ -574,6 +566,12 @@ void LLNotificationWellWindow::onItemClose(LLSysWellItem* item) mChannel->killToastByNotificationID(id); } +void LLNotificationWellWindow::onAdd( LLNotificationPtr notify ) +{ + removeItemByID(notify->getID()); +} + + /************************************************************************/ @@ -867,4 +865,4 @@ bool LLIMWellWindow::confirmCloseAll(const LLSD& notification, const LLSD& respo return false; } -// EOF + diff --git a/indra/newview/llsyswellwindow.h b/indra/newview/llsyswellwindow.h index 272e9cfcb1..8758c8c4e5 100644 --- a/indra/newview/llsyswellwindow.h +++ b/indra/newview/llsyswellwindow.h @@ -34,6 +34,7 @@ #include "llscreenchannel.h" #include "llscrollcontainer.h" #include "llimview.h" +#include "llnotifications.h" #include "boost/shared_ptr.hpp" @@ -111,7 +112,7 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void setVisible(BOOL visible); - + /*virtual*/ void onAdd(LLNotificationPtr notify); // Operating with items void addItem(LLSysWellItem::Params p); @@ -119,6 +120,18 @@ public: void closeAll(); protected: + struct WellNotificationChannel : public LLNotificationChannel + { + WellNotificationChannel(LLNotificationWellWindow*); + void onDelete(LLNotificationPtr notify) + { + mWellWindow->removeItemByID(notify->getID()); + } + + LLNotificationWellWindow* mWellWindow; + }; + + LLNotificationChannelPtr mNotificationUpdates; /*virtual*/ const std::string& getAnchorViewName() { return NOTIFICATION_WELL_ANCHOR_NAME; } private: @@ -126,12 +139,8 @@ private: void initChannel(); void clearScreenChannels(); - void onStoreToast(LLPanel* info_panel, LLUUID id); - // connect counter and list updaters to the corresponding signals - void connectListUpdaterToSignal(std::string notification_type); - // Handlers void onItemClick(LLSysWellItem* item); void onItemClose(LLSysWellItem* item); @@ -160,6 +169,7 @@ public: // LLIMSessionObserver observe triggers /*virtual*/ void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + /*virtual*/ void sessionVoiceOrIMStarted(const LLUUID& session_id) {}; /*virtual*/ void sessionRemoved(const LLUUID& session_id); /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index ec36cf48c2..65f0290060 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -39,7 +39,7 @@ #include "llfocusmgr.h" #include "llviewertexture.h" #include "llfolderview.h" -#include "llfoldervieweventlistener.h" +#include "llfolderviewmodel.h" #include "llinventory.h" #include "llinventoryfunctions.h" #include "llinventorymodelbackgroundfetch.h" @@ -58,6 +58,7 @@ #include "lltoolmgr.h" #include "lltoolpipette.h" #include "llfiltereditor.h" +#include "llwindow.h" #include "lltool.h" #include "llviewerwindow.h" @@ -186,7 +187,7 @@ protected: F32 mContextConeOpacity; LLSaveFolderState mSavedFolderState; BOOL mSelectedItemPinned; - + LLRadioGroup* mModeSelector; LLScrollListCtrl* mLocalScrollCtrl; @@ -372,7 +373,7 @@ BOOL LLFloaterTexturePicker::handleKeyHere(KEY key, MASK mask) { if (!root_folder->getCurSelectedItem()) { - LLFolderViewItem* itemp = root_folder->getItemByID(gInventory.getRootFolderID()); + LLFolderViewItem* itemp = mInventoryPanel->getItemByID(gInventory.getRootFolderID()); if (itemp) { root_folder->setSelection(itemp, FALSE, FALSE); @@ -454,7 +455,7 @@ BOOL LLFloaterTexturePicker::postBuild() // Commented out to scroll to currently selected texture. See EXT-5403. // // store this filter as the default one - // mInventoryPanel->getRootFolder()->getFilter()->markDefault(); + // mInventoryPanel->getRootFolder()->getFilter().markDefault(); // Commented out to stop opening all folders with textures // mInventoryPanel->openDefaultFolderForType(LLFolderType::FT_TEXTURE); @@ -637,11 +638,10 @@ void LLFloaterTexturePicker::draw() LLFolderView* folder_view = mInventoryPanel->getRootFolder(); if (!folder_view) return; - LLInventoryFilter* filter = folder_view->getFilter(); - if (!filter) return; + LLFolderViewFilter& filter = static_cast<LLFolderViewModelInventory*>(folder_view->getFolderViewModel())->getFilter(); - bool is_filter_active = folder_view->getCompletedFilterGeneration() < filter->getCurrentGeneration() && - filter->isNotDefault(); + bool is_filter_active = folder_view->getViewModelItem()->getLastFilterGeneration() < filter.getCurrentGeneration() && + filter.isNotDefault(); // After inventory panel filter is applied we have to update // constraint rect for the selected item because of folder view @@ -651,8 +651,9 @@ void LLFloaterTexturePicker::draw() if (!is_filter_active && !mSelectedItemPinned) { folder_view->setPinningSelectedItem(mSelectedItemPinned); - folder_view->dirtyFilter(); - folder_view->arrangeFromRoot(); + folder_view->getViewModelItem()->dirtyFilter(); + //TODO RN: test..still works without this? + //folder_view->arrangeFromRoot(); mSelectedItemPinned = TRUE; } @@ -815,7 +816,7 @@ void LLFloaterTexturePicker::onSelectionChange(const std::deque<LLFolderViewItem if (items.size()) { LLFolderViewItem* first_item = items.front(); - LLInventoryItem* itemp = gInventory.getItem(first_item->getListener()->getUUID()); + LLInventoryItem* itemp = gInventory.getItem(static_cast<LLFolderViewModelItemInventory*>(first_item->getViewModelItem())->getUUID()); mNoCopyTextureSelected = FALSE; if (itemp) { @@ -1011,7 +1012,7 @@ void LLFloaterTexturePicker::onFilterEdit(const std::string& search_string ) else if (mInventoryPanel->getFilterSubString().empty()) { // first letter in search term, save existing folder open state - if (!mInventoryPanel->getRootFolder()->isFilterModified()) + if (!mInventoryPanel->getFilter().isNotDefault()) { mSavedFolderState.setApply(FALSE); mInventoryPanel->getRootFolder()->applyFunctorRecursively(mSavedFolderState); @@ -1325,7 +1326,7 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLUUID id) // (i.e. op == TEXTURE_SELECT) or texture changes via DnD. else if (mCommitOnSelection || op == TEXTURE_SELECT) mViewModel->setDirty(); // *TODO: shouldn't we be using setValue() here? - + if(floaterp->isDirty() || id.notNull()) // mModelView->setDirty does not work. { setTentative( FALSE ); @@ -1337,10 +1338,10 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLUUID id) } else { - mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE); - lldebugs << "mImageItemID: " << mImageItemID << llendl; - mImageAssetID = floaterp->getAssetID(); - lldebugs << "mImageAssetID: " << mImageAssetID << llendl; + mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE); + lldebugs << "mImageItemID: " << mImageItemID << llendl; + mImageAssetID = floaterp->getAssetID(); + lldebugs << "mImageAssetID: " << mImageAssetID << llendl; } if (op == TEXTURE_SELECT && mOnSelectCallback) diff --git a/indra/newview/lltoast.h b/indra/newview/lltoast.h index e1d99b1bcb..ea62f758f8 100644 --- a/indra/newview/lltoast.h +++ b/indra/newview/lltoast.h @@ -169,6 +169,7 @@ public: // get/set Toast's flags or states // get information whether the notification corresponding to the toast is valid or not bool isNotificationValid(); + // get toast's Notification ID const LLUUID getNotificationID() const { return mNotificationID;} // get toast's Session ID @@ -212,7 +213,7 @@ private: //LLRootHandle<LLToast> mHandle; - LLPanel* mWrapperPanel; + LLPanel* mWrapperPanel; // timer counts a lifetime of a toast std::auto_ptr<LLToastLifeTimer> mTimer; @@ -220,8 +221,8 @@ private: F32 mToastLifetime; // in seconds F32 mToastFadingTime; // in seconds - LLPanel* mPanel; - LLButton* mHideBtn; + LLPanel* mPanel; + LLButton* mHideBtn; LLColor4 mBgColor; bool mCanFade; diff --git a/indra/newview/lltoastgroupnotifypanel.cpp b/indra/newview/lltoastgroupnotifypanel.cpp index 75178a6ef8..ed350ea144 100644 --- a/indra/newview/lltoastgroupnotifypanel.cpp +++ b/indra/newview/lltoastgroupnotifypanel.cpp @@ -51,7 +51,7 @@ const S32 LLToastGroupNotifyPanel::DEFAULT_MESSAGE_MAX_LINE_COUNT = 7; -LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(LLNotificationPtr& notification) +LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(const LLNotificationPtr& notification) : LLToastPanel(notification), mInventoryOffer(NULL) { @@ -112,7 +112,7 @@ LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(LLNotificationPtr& notification style.font = date_font; pMessageText->appendText(timeStr + "\n", TRUE, style); - style.font = pMessageText->getDefaultFont(); + style.font = pMessageText->getFont(); pMessageText->appendText(message, TRUE, style); //attachment diff --git a/indra/newview/lltoastgroupnotifypanel.h b/indra/newview/lltoastgroupnotifypanel.h index 7794ec9f63..dfdc6ae559 100644 --- a/indra/newview/lltoastgroupnotifypanel.h +++ b/indra/newview/lltoastgroupnotifypanel.h @@ -47,13 +47,10 @@ class LLToastGroupNotifyPanel public: void close(); - static bool onNewNotification(const LLSD& notification); - - // Non-transient messages. You can specify non-default button // layouts (like one for script dialogs) by passing various // numbers in for "layout". - LLToastGroupNotifyPanel(LLNotificationPtr& notification); + LLToastGroupNotifyPanel(const LLNotificationPtr& notification); /*virtual*/ ~LLToastGroupNotifyPanel(); protected: diff --git a/indra/newview/lltoastimpanel.h b/indra/newview/lltoastimpanel.h index a803387576..279dd69bc7 100644 --- a/indra/newview/lltoastimpanel.h +++ b/indra/newview/lltoastimpanel.h @@ -41,11 +41,11 @@ public: struct Params { LLNotificationPtr notification; - LLUUID avatar_id; - LLUUID session_id; - std::string from; - std::string time; - std::string message; + LLUUID avatar_id, + session_id; + std::string from, + time, + message; Params() {} }; diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp index d629f3abac..4a49922656 100644 --- a/indra/newview/lltoastnotifypanel.cpp +++ b/indra/newview/lltoastnotifypanel.cpp @@ -45,6 +45,9 @@ const S32 BOTTOM_PAD = VPAD * 3; const S32 IGNORE_BTN_TOP_DELTA = 3*VPAD;//additional ignore_btn padding S32 BUTTON_WIDTH = 90; +// *TODO: magic numbers(???) - copied from llnotify.cpp(250) +const S32 MAX_LENGTH = 512 + 20 + DB_FIRST_NAME_BUF_SIZE + DB_LAST_NAME_BUF_SIZE + DB_INV_ITEM_NAME_BUF_SIZE; + //static const LLFontGL* LLToastNotifyPanel::sFont = NULL; @@ -52,172 +55,12 @@ const LLFontGL* LLToastNotifyPanel::sFontSmall = NULL; LLToastNotifyPanel::button_click_signal_t LLToastNotifyPanel::sButtonClickSignal; -LLToastNotifyPanel::LLToastNotifyPanel(const LLNotificationPtr& notification, const LLRect& rect, bool show_images) : -LLToastPanel(notification), -mTextBox(NULL), -mInfoPanel(NULL), -mControlPanel(NULL), -mNumOptions(0), -mNumButtons(0), -mAddedDefaultBtn(false), -mCloseNotificationOnDestroy(true) +LLToastNotifyPanel::LLToastNotifyPanel(const LLNotificationPtr& notification, const LLRect& rect, bool show_images) +: LLToastPanel(notification), + LLInstanceTracker<LLToastNotifyPanel, LLUUID>(notification->getID()) { - buildFromFile( "panel_notification.xml"); - if(rect != LLRect::null) - { - this->setShape(rect); - } - mInfoPanel = getChild<LLPanel>("info_panel"); - mControlPanel = getChild<LLPanel>("control_panel"); - BUTTON_WIDTH = gSavedSettings.getS32("ToastButtonWidth"); - // customize panel's attributes - // is it intended for displaying a tip? - mIsTip = notification->getType() == "notifytip"; - // is it a script dialog? - mIsScriptDialog = (notification->getName() == "ScriptDialog" || notification->getName() == "ScriptDialogGroup"); - // is it a caution? - // - // caution flag can be set explicitly by specifying it in the notification payload, or it can be set implicitly if the - // notify xml template specifies that it is a caution - // tip-style notification handle 'caution' differently -they display the tip in a different color - mIsCaution = notification->getPriority() >= NOTIFICATION_PRIORITY_HIGH; - - // setup parameters - // get a notification message - mMessage = notification->getMessage(); - // init font variables - if (!sFont) - { - sFont = LLFontGL::getFontSansSerif(); - sFontSmall = LLFontGL::getFontSansSerifSmall(); - } - // initialize - setFocusRoot(!mIsTip); - // get a form for the notification - LLNotificationFormPtr form(notification->getForm()); - // get number of elements - mNumOptions = form->getNumElements(); - - // customize panel's outfit - // preliminary adjust panel's layout - //move to the end - //mIsTip ? adjustPanelForTipNotice() : adjustPanelForScriptNotice(form); - - // adjust text options according to the notification type - // add a caution textbox at the top of a caution notification - if (mIsCaution && !mIsTip) - { - mTextBox = getChild<LLTextBox>("caution_text_box"); - } - else - { - mTextBox = getChild<LLTextEditor>("text_editor_box"); - } - - // *TODO: magic numbers(???) - copied from llnotify.cpp(250) - const S32 MAX_LENGTH = 512 + 20 + DB_FIRST_NAME_BUF_SIZE + DB_LAST_NAME_BUF_SIZE + DB_INV_ITEM_NAME_BUF_SIZE; - - mTextBox->setMaxTextLength(MAX_LENGTH); - mTextBox->setVisible(TRUE); - mTextBox->setPlainText(!show_images); - mTextBox->setValue(notification->getMessage()); - - // add buttons for a script notification - if (mIsTip) - { - adjustPanelForTipNotice(); + init(rect, show_images); } - else - { - std::vector<index_button_pair_t> buttons; - buttons.reserve(mNumOptions); - S32 buttons_width = 0; - // create all buttons and accumulate they total width to reshape mControlPanel - for (S32 i = 0; i < mNumOptions; i++) - { - LLSD form_element = form->getElement(i); - if (form_element["type"].asString() != "button") - { - // not a button. - continue; - } - if (form_element["name"].asString() == TEXTBOX_MAGIC_TOKEN) - { - // a textbox pretending to be a button. - continue; - } - LLButton* new_button = createButton(form_element, TRUE); - buttons_width += new_button->getRect().getWidth(); - S32 index = form_element["index"].asInteger(); - buttons.push_back(index_button_pair_t(index,new_button)); - } - if (buttons.empty()) - { - addDefaultButton(); - } - else - { - const S32 button_panel_width = mControlPanel->getRect().getWidth();// do not change width of the panel - S32 button_panel_height = mControlPanel->getRect().getHeight(); - //try get an average h_pad to spread out buttons - S32 h_pad = (button_panel_width - buttons_width) / (S32(buttons.size())); - if(h_pad < 2*HPAD) - { - /* - * Probably it is a scriptdialog toast - * for a scriptdialog toast h_pad can be < 2*HPAD if we have a lot of buttons. - * In last case set default h_pad to avoid heaping of buttons - */ - S32 button_per_row = button_panel_width / BUTTON_WIDTH; - h_pad = (button_panel_width % BUTTON_WIDTH) / (button_per_row - 1);// -1 because we do not need space after last button in a row - if(h_pad < 2*HPAD) // still not enough space between buttons ? - { - h_pad = 2*HPAD; - } - } - if (mIsScriptDialog) - { - // we are using default width for script buttons so we can determinate button_rows - //to get a number of rows we divide the required width of the buttons to button_panel_width - S32 button_rows = llceil(F32(buttons.size() - 1) * (BUTTON_WIDTH + h_pad) / button_panel_width); - //S32 button_rows = (buttons.size() - 1) * (BUTTON_WIDTH + h_pad) / button_panel_width; - //reserve one row for the ignore_btn - button_rows++; - //calculate required panel height for scripdialog notification. - button_panel_height = button_rows * (BTN_HEIGHT + VPAD) + IGNORE_BTN_TOP_DELTA + BOTTOM_PAD; - } - else - { - // in common case buttons can have different widths so we need to calculate button_rows according to buttons_width - //S32 button_rows = llceil(F32(buttons.size()) * (buttons_width + h_pad) / button_panel_width); - S32 button_rows = llceil(F32((buttons.size() - 1) * h_pad + buttons_width) / button_panel_width); - //calculate required panel height - button_panel_height = button_rows * (BTN_HEIGHT + VPAD) + BOTTOM_PAD; - } - - // we need to keep min width and max height to make visible all buttons, because width of the toast can not be changed - adjustPanelForScriptNotice(button_panel_width, button_panel_height); - updateButtonsLayout(buttons, h_pad); - // save buttons for later use in disableButtons() - mButtons.assign(buttons.begin(), buttons.end()); - } - } - // adjust panel's height to the text size - mInfoPanel->setFollowsAll(); - snapToMessageHeight(mTextBox, MAX_LENGTH); - - if(notification->isReusable()) - { - mButtonClickConnection = sButtonClickSignal.connect( - boost::bind(&LLToastNotifyPanel::onToastPanelButtonClicked, this, _1, _2)); - - if(notification->isRespondedTo()) - { - // User selected an option in toast, now disable required buttons in IM window - disableRespondedOptions(notification); - } - } -} void LLToastNotifyPanel::addDefaultButton() { LLSD form_element; @@ -235,7 +78,6 @@ void LLToastNotifyPanel::addDefaultButton() } LLButton* LLToastNotifyPanel::createButton(const LLSD& form_element, BOOL is_option) { - InstanceAndS32* userdata = new InstanceAndS32; userdata->mSelf = this; userdata->mButtonName = is_option ? form_element["name"].asString() : ""; @@ -245,14 +87,15 @@ LLButton* LLToastNotifyPanel::createButton(const LLSD& form_element, BOOL is_opt LLButton::Params p; bool make_small_btn = form_element["index"].asInteger() == -1 || form_element["index"].asInteger() == -2; const LLFontGL* font = make_small_btn ? sFontSmall: sFont; // for block and ignore buttons in script dialog - p.name(form_element["name"].asString()); - p.label(form_element["text"].asString()); - p.font(font); + p.name = form_element["name"].asString(); + p.label = form_element["text"].asString(); + p.font = font; p.rect.height = BTN_HEIGHT; p.click_callback.function(boost::bind(&LLToastNotifyPanel::onClickButton, userdata)); p.rect.width = BUTTON_WIDTH; p.auto_resize = false; p.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM); + p.enabled = !form_element.has("enabled") || form_element["enabled"].asBoolean(); if (mIsCaution) { p.image_color(LLUIColorTable::instance().getColor("ButtonCautionImageColor")); @@ -287,16 +130,11 @@ LLToastNotifyPanel::~LLToastNotifyPanel() mButtonClickConnection.disconnect(); std::for_each(mBtnCallbackData.begin(), mBtnCallbackData.end(), DeletePointer()); - if (mCloseNotificationOnDestroy && LLNotificationsUtil::find(mNotification->getID()) != NULL) - { - // let reusable notification be deleted - mNotification->setReusable(false); - if (!mNotification->isPersistent()) + if (mIsTip) { LLNotifications::getInstance()->cancel(mNotification); } } -} void LLToastNotifyPanel::updateButtonsLayout(const std::vector<index_button_pair_t>& buttons, S32 h_pad) { @@ -393,210 +231,374 @@ void LLToastNotifyPanel::adjustPanelForTipNotice() } } -typedef std::set<std::string> button_name_set_t; -typedef std::map<std::string, button_name_set_t> disable_button_map_t; +//typedef std::set<std::string> button_name_set_t; +//typedef std::map<std::string, button_name_set_t> disable_button_map_t; +// +//disable_button_map_t initUserGiveItemDisableButtonMap() +//{ +// // see EXT-5905 for disable rules +// +// disable_button_map_t disable_map; +// button_name_set_t buttons; +// +// buttons.insert("Show"); +// disable_map.insert(std::make_pair("Show", buttons)); +// +// buttons.insert("Discard"); +// disable_map.insert(std::make_pair("Discard", buttons)); +// +// buttons.insert("Mute"); +// disable_map.insert(std::make_pair("Mute", buttons)); +// +// return disable_map; +//} +// +//disable_button_map_t initTeleportOfferedDisableButtonMap() +//{ +// disable_button_map_t disable_map; +// button_name_set_t buttons; +// +// buttons.insert("Teleport"); +// buttons.insert("Cancel"); +// +// disable_map.insert(std::make_pair("Teleport", buttons)); +// disable_map.insert(std::make_pair("Cancel", buttons)); +// +// return disable_map; +//} +// +//disable_button_map_t initFriendshipOfferedDisableButtonMap() +//{ +// disable_button_map_t disable_map; +// button_name_set_t buttons; +// +// buttons.insert("Accept"); +// buttons.insert("Decline"); +// +// disable_map.insert(std::make_pair("Accept", buttons)); +// disable_map.insert(std::make_pair("Decline", buttons)); +// +// return disable_map; +//} +// +//button_name_set_t getButtonDisableList(const std::string& notification_name, const std::string& button_name) +//{ +// static disable_button_map_t user_give_item_disable_map = initUserGiveItemDisableButtonMap(); +// static disable_button_map_t teleport_offered_disable_map = initTeleportOfferedDisableButtonMap(); +// static disable_button_map_t friendship_offered_disable_map = initFriendshipOfferedDisableButtonMap(); +// +// disable_button_map_t::const_iterator it; +// disable_button_map_t::const_iterator it_end; +// disable_button_map_t search_map; +// +// if("UserGiveItem" == notification_name) +// { +// search_map = user_give_item_disable_map; +// } +// else if("TeleportOffered" == notification_name) +// { +// search_map = teleport_offered_disable_map; +// } +// else if("OfferFriendship" == notification_name) +// { +// search_map = friendship_offered_disable_map; +// } +// +// it = search_map.find(button_name); +// it_end = search_map.end(); +// +// if(it_end != it) +// { +// return it->second; +// } +// return button_name_set_t(); +//} + +//void LLToastNotifyPanel::disableButtons(const std::string& notification_name, const std::string& selected_button) +//{ + //button_name_set_t buttons = getButtonDisableList(notification_name, selected_button); + + //std::vector<index_button_pair_t>::const_iterator it = mButtons.begin(); + //for ( ; it != mButtons.end(); it++) + //{ + // LLButton* btn = it->second; + // if(buttons.find(btn->getName()) != buttons.end()) + // { + // btn->setEnabled(FALSE); + // } + //} +//} -disable_button_map_t initUserGiveItemDisableButtonMap() +// static +void LLToastNotifyPanel::onClickButton(void* data) { - // see EXT-5905 for disable rules - - disable_button_map_t disable_map; - button_name_set_t buttons; - - buttons.insert("Show"); - disable_map.insert(std::make_pair("Show", buttons)); + InstanceAndS32* self_and_button = (InstanceAndS32*)data; + LLToastNotifyPanel* self = self_and_button->mSelf; + std::string button_name = self_and_button->mButtonName; - buttons.insert("Discard"); - disable_map.insert(std::make_pair("Discard", buttons)); + LLSD response = self->mNotification->getResponseTemplate(); + if (!self->mAddedDefaultBtn && !button_name.empty()) + { + response[button_name] = true; + } - buttons.insert("Mute"); - disable_map.insert(std::make_pair("Mute", buttons)); + // disable all buttons + self->mControlPanel->setEnabled(FALSE); - return disable_map; + // this might repost notification with new form data/enabled buttons + self->mNotification->respond(response); } -disable_button_map_t initTeleportOfferedDisableButtonMap() +void LLToastNotifyPanel::init( LLRect rect, bool show_images ) { - disable_button_map_t disable_map; - button_name_set_t buttons; + deleteAllChildren(); - buttons.insert("Teleport"); - buttons.insert("Cancel"); + mTextBox = NULL; + mInfoPanel = NULL; + mControlPanel = NULL; + mNumOptions = 0; + mNumButtons = 0; + mAddedDefaultBtn = false; - disable_map.insert(std::make_pair("Teleport", buttons)); - disable_map.insert(std::make_pair("Cancel", buttons)); + buildFromFile( "panel_notification.xml"); + if(rect != LLRect::null) + { + this->setShape(rect); + } + mInfoPanel = getChild<LLPanel>("info_panel"); + mInfoPanel->setFollowsAll(); - return disable_map; -} + mControlPanel = getChild<LLPanel>("control_panel"); + BUTTON_WIDTH = gSavedSettings.getS32("ToastButtonWidth"); + // customize panel's attributes + // is it intended for displaying a tip? + mIsTip = mNotification->getType() == "notifytip"; + // is it a script dialog? + mIsScriptDialog = (mNotification->getName() == "ScriptDialog" || mNotification->getName() == "ScriptDialogGroup"); + // is it a caution? + // + // caution flag can be set explicitly by specifying it in the notification payload, or it can be set implicitly if the + // notify xml template specifies that it is a caution + // tip-style notification handle 'caution' differently -they display the tip in a different color + mIsCaution = mNotification->getPriority() >= NOTIFICATION_PRIORITY_HIGH; -disable_button_map_t initFriendshipOfferedDisableButtonMap() + // setup parameters + // get a notification message + mMessage = mNotification->getMessage(); + // init font variables + if (!sFont) { - disable_button_map_t disable_map; - button_name_set_t buttons; - - buttons.insert("Accept"); - buttons.insert("Decline"); - - disable_map.insert(std::make_pair("Accept", buttons)); - disable_map.insert(std::make_pair("Decline", buttons)); - - return disable_map; + sFont = LLFontGL::getFontSansSerif(); + sFontSmall = LLFontGL::getFontSansSerifSmall(); } + // initialize + setFocusRoot(!mIsTip); + // get a form for the notification + LLNotificationFormPtr form(mNotification->getForm()); + // get number of elements + mNumOptions = form->getNumElements(); -button_name_set_t getButtonDisableList(const std::string& notification_name, const std::string& button_name) -{ - static disable_button_map_t user_give_item_disable_map = initUserGiveItemDisableButtonMap(); - static disable_button_map_t teleport_offered_disable_map = initTeleportOfferedDisableButtonMap(); - static disable_button_map_t friendship_offered_disable_map = initFriendshipOfferedDisableButtonMap(); - - disable_button_map_t::const_iterator it; - disable_button_map_t::const_iterator it_end; - disable_button_map_t search_map; + // customize panel's outfit + // preliminary adjust panel's layout + //move to the end + //mIsTip ? adjustPanelForTipNotice() : adjustPanelForScriptNotice(form); - if("UserGiveItem" == notification_name) - { - search_map = user_give_item_disable_map; - } - else if(("TeleportOffered" == notification_name) || ("TeleportOffered_MaturityExceeded" == notification_name)) + // adjust text options according to the notification type + // add a caution textbox at the top of a caution notification + if (mIsCaution && !mIsTip) { - search_map = teleport_offered_disable_map; + mTextBox = getChild<LLTextBox>("caution_text_box"); } - else if("OfferFriendship" == notification_name) + else { - search_map = friendship_offered_disable_map; + mTextBox = getChild<LLTextEditor>("text_editor_box"); } - it = search_map.find(button_name); - it_end = search_map.end(); + mTextBox->setMaxTextLength(MAX_LENGTH); + mTextBox->setVisible(TRUE); + mTextBox->setPlainText(!show_images); + mTextBox->setValue(mNotification->getMessage()); - if(it_end != it) + // add buttons for a script notification + if (mIsTip) { - return it->second; + adjustPanelForTipNotice(); } - return button_name_set_t(); -} - -void LLToastNotifyPanel::disableButtons(const std::string& notification_name, const std::string& selected_button) + else { - button_name_set_t buttons = getButtonDisableList(notification_name, selected_button); - - std::vector<index_button_pair_t>::const_iterator it = mButtons.begin(); - for ( ; it != mButtons.end(); it++) + std::vector<index_button_pair_t> buttons; + buttons.reserve(mNumOptions); + S32 buttons_width = 0; + // create all buttons and accumulate they total width to reshape mControlPanel + for (S32 i = 0; i < mNumOptions; i++) { - LLButton* btn = it->second; - if(buttons.find(btn->getName()) != buttons.end()) + LLSD form_element = form->getElement(i); + if (form_element["type"].asString() != "button") { - btn->setEnabled(FALSE); + // not a button. + continue; } + if (form_element["name"].asString() == TEXTBOX_MAGIC_TOKEN) + { + // a textbox pretending to be a button. + continue; } + LLButton* new_button = createButton(form_element, TRUE); + buttons_width += new_button->getRect().getWidth(); + S32 index = form_element["index"].asInteger(); + buttons.push_back(index_button_pair_t(index,new_button)); } - -// static -void LLToastNotifyPanel::onClickButton(void* data) + if (buttons.empty()) { - InstanceAndS32* self_and_button = (InstanceAndS32*)data; - LLToastNotifyPanel* self = self_and_button->mSelf; - std::string button_name = self_and_button->mButtonName; - - LLSD response = self->mNotification->getResponseTemplate(); - if (!self->mAddedDefaultBtn && !button_name.empty()) - { - response[button_name] = true; + addDefaultButton(); } - - bool is_reusable = self->mNotification->isReusable(); - // When we call respond(), LLOfferInfo will delete itself in inventory_offer_callback(), - // lets copy it while it's still valid. - LLOfferInfo* old_info = static_cast<LLOfferInfo*>(self->mNotification->getResponder()); - LLOfferInfo* new_info = NULL; - if(is_reusable && old_info) + else { - new_info = new LLOfferInfo(*old_info); - self->mNotification->setResponder(new_info); - } - - self->mNotification->respond(response); - - if(is_reusable) + const S32 button_panel_width = mControlPanel->getRect().getWidth();// do not change width of the panel + S32 button_panel_height = mControlPanel->getRect().getHeight(); + //try get an average h_pad to spread out buttons + S32 h_pad = (button_panel_width - buttons_width) / (S32(buttons.size())); + if(h_pad < 2*HPAD) { - sButtonClickSignal(self->mNotification->getID(), button_name); - } - else + /* + * Probably it is a scriptdialog toast + * for a scriptdialog toast h_pad can be < 2*HPAD if we have a lot of buttons. + * In last case set default h_pad to avoid heaping of buttons + */ + S32 button_per_row = button_panel_width / BUTTON_WIDTH; + h_pad = (button_panel_width % BUTTON_WIDTH) / (button_per_row - 1);// -1 because we do not need space after last button in a row + if(h_pad < 2*HPAD) // still not enough space between buttons ? { - // disable all buttons - self->mControlPanel->setEnabled(FALSE); + h_pad = 2*HPAD; } } - -void LLToastNotifyPanel::onToastPanelButtonClicked(const LLUUID& notification_id, const std::string btn_name) + if (mIsScriptDialog) { - if(mNotification->getID() == notification_id) + // we are using default width for script buttons so we can determinate button_rows + //to get a number of rows we divide the required width of the buttons to button_panel_width + S32 button_rows = llceil(F32(buttons.size() - 1) * (BUTTON_WIDTH + h_pad) / button_panel_width); + //S32 button_rows = (buttons.size() - 1) * (BUTTON_WIDTH + h_pad) / button_panel_width; + //reserve one row for the ignore_btn + button_rows++; + //calculate required panel height for scripdialog notification. + button_panel_height = button_rows * (BTN_HEIGHT + VPAD) + IGNORE_BTN_TOP_DELTA + BOTTOM_PAD; + } + else { - disableButtons(mNotification->getName(), btn_name); - } + // in common case buttons can have different widths so we need to calculate button_rows according to buttons_width + //S32 button_rows = llceil(F32(buttons.size()) * (buttons_width + h_pad) / button_panel_width); + S32 button_rows = llceil(F32((buttons.size() - 1) * h_pad + buttons_width) / button_panel_width); + //calculate required panel height + button_panel_height = button_rows * (BTN_HEIGHT + VPAD) + BOTTOM_PAD; } -void LLToastNotifyPanel::disableRespondedOptions(const LLNotificationPtr& notification) -{ - LLSD response = notification->getResponse(); - for (LLSD::map_const_iterator response_it = response.beginMap(); - response_it != response.endMap(); ++response_it) - { - if (response_it->second.isBoolean() && response_it->second.asBoolean()) - { - // that after multiple responses there can be many pressed buttons - // need to process them all - disableButtons(notification->getName(), response_it->first); + // we need to keep min width and max height to make visible all buttons, because width of the toast can not be changed + adjustPanelForScriptNotice(button_panel_width, button_panel_height); + updateButtonsLayout(buttons, h_pad); + // save buttons for later use in disableButtons() + //mButtons.assign(buttons.begin(), buttons.end()); } } + // adjust panel's height to the text size + snapToMessageHeight(mTextBox, MAX_LENGTH); } + +//void LLToastNotifyPanel::onToastPanelButtonClicked(const LLUUID& notification_id, const std::string btn_name) +//{ +// if(mNotification->getID() == notification_id) +// { +// disableButtons(mNotification->getName(), btn_name); +// } +//} + +//void LLToastNotifyPanel::disableRespondedOptions(const LLNotificationPtr& notification) +//{ +// LLSD response = notification->getResponse(); +// for (LLSD::map_const_iterator response_it = response.beginMap(); +// response_it != response.endMap(); ++response_it) +// { +// if (response_it->second.isBoolean() && response_it->second.asBoolean()) +// { +// // that after multiple responses there can be many pressed buttons +// // need to process them all +// disableButtons(notification->getName(), response_it->first); +// } +// } +//} + + ////////////////////////////////////////////////////////////////////////// LLIMToastNotifyPanel::LLIMToastNotifyPanel(LLNotificationPtr& pNotification, const LLUUID& session_id, const LLRect& rect /* = LLRect::null */, - bool show_images /* = true */) - : mSessionID(session_id), LLToastNotifyPanel(pNotification, rect, show_images) + bool show_images /* = true */, LLTextBase* parent_text) +: mSessionID(session_id), LLToastNotifyPanel(pNotification, rect, show_images), + mParentText(parent_text) { - mTextBox->setFollowsAll(); + compactButtons(); } LLIMToastNotifyPanel::~LLIMToastNotifyPanel() { - // We shouldn't delete notification when IM floater exists - // since that notification will be reused by IM floater. - // This may happened when IM floater reloads messages, exactly when user - // changes layout of IM chat log(disable/enable plaintext mode). - // See EXT-6500 - LLIMFloater* im_floater = LLIMFloater::findInstance(mSessionID); - if (im_floater != NULL && !im_floater->isDead()) - { - mCloseNotificationOnDestroy = false; - } } void LLIMToastNotifyPanel::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */) + { + LLToastPanel::reshape(width, height, called_from_parent); + + snapToMessageHeight(mTextBox, MAX_LENGTH); + } + +void LLIMToastNotifyPanel::compactButtons() { - S32 text_height = mTextBox->getTextBoundingRect().getHeight(); - S32 widget_height = mTextBox->getRect().getHeight(); - S32 delta = text_height - widget_height; - LLRect rc = getRect(); + mTextBox->setFollowsAll(); - rc.setLeftTopAndSize(rc.mLeft, rc.mTop, width, height + delta); - height = rc.getHeight(); - width = rc.getWidth(); + //we can't set follows in xml since it broke toasts behavior + setFollows(FOLLOWS_LEFT|FOLLOWS_RIGHT|FOLLOWS_TOP); - bool is_width_changed = width != getRect().getWidth(); + const child_list_t* children = getControlPanel()->getChildList(); + S32 offset = 0; + // Children were added by addChild() which uses push_front to insert them into list, + // so to get buttons in correct order reverse iterator is used (EXT-5906) + for (child_list_t::const_reverse_iterator it = children->rbegin(); it != children->rend(); it++) + { + LLButton * button = dynamic_cast<LLButton*> (*it); + if (button != NULL) + { + button->setOrigin( offset,button->getRect().mBottom); + button->setLeftHPad(2 * HPAD); + button->setRightHPad(2 * HPAD); + // set zero width before perform autoResize() + button->setRect(LLRect(button->getRect().mLeft, + button->getRect().mTop, + button->getRect().mLeft, + button->getRect().mBottom)); + button->setAutoResize(true); + button->autoResize(); + offset += HPAD + button->getRect().getWidth(); + button->setFollowsNone(); + } + } - LLToastPanel::reshape(width, height, called_from_parent); + if (mParentText) + { + mParentText->needsReflow(); + } +} - // Notification height required to display the text message depends on - // the width of the text box thus if panel width is changed the text box - // width is also changed then reshape() is called to adjust proper height. - if (is_width_changed) +void LLIMToastNotifyPanel::updateNotification() { - reshape(width, height, called_from_parent); + init(LLRect(), true); } + +void LLIMToastNotifyPanel::init( LLRect rect, bool show_images ) +{ + LLToastNotifyPanel::init(LLRect(), show_images); + + compactButtons(); } + // EOF + diff --git a/indra/newview/lltoastnotifypanel.h b/indra/newview/lltoastnotifypanel.h index db517ec858..f93c7745af 100644 --- a/indra/newview/lltoastnotifypanel.h +++ b/indra/newview/lltoastnotifypanel.h @@ -47,7 +47,7 @@ class LLNotificationForm; * @deprecated this class will be removed after all toast panel types are * implemented in separate classes. */ -class LLToastNotifyPanel: public LLToastPanel +class LLToastNotifyPanel: public LLToastPanel, public LLInstanceTracker<LLToastNotifyPanel, LLUUID> { public: /** @@ -61,10 +61,14 @@ public: * implement right class for desired toast panel. @see LLGenericTipPanel as example. */ LLToastNotifyPanel(const LLNotificationPtr& pNotification, const LLRect& rect = LLRect::null, bool show_images = true); + + virtual void init( LLRect rect, bool show_images ); + virtual ~LLToastNotifyPanel(); LLPanel * getControlPanel() { return mControlPanel; } - void setCloseNotificationOnDestroy(bool close) { mCloseNotificationOnDestroy = close; } + virtual void updateNotification() {} + protected: LLButton* createButton(const LLSD& form_element, BOOL is_option); @@ -76,8 +80,6 @@ protected: }; std::vector<InstanceAndS32*> mBtnCallbackData; - bool mCloseNotificationOnDestroy; - typedef std::pair<int,LLButton*> index_button_pair_t; void adjustPanelForScriptNotice(S32 max_width, S32 max_height); void adjustPanelForTipNotice(); @@ -93,9 +95,9 @@ protected: /** * Disable specific button(s) based on notification name and clicked button */ - void disableButtons(const std::string& notification_name, const std::string& selected_button); + //void disableButtons(const std::string& notification_name, const std::string& selected_button); - std::vector<index_button_pair_t> mButtons; + //std::vector<index_button_pair_t> mButtons; // panel elements LLTextBase* mTextBox; @@ -118,7 +120,7 @@ protected: /** * Process response data. Will disable selected options */ - void disableRespondedOptions(const LLNotificationPtr& notification); + //void disableRespondedOptions(const LLNotificationPtr& notification); bool mIsTip; bool mAddedDefaultBtn; @@ -137,13 +139,23 @@ class LLIMToastNotifyPanel : public LLToastNotifyPanel { public: - LLIMToastNotifyPanel(LLNotificationPtr& pNotification, const LLUUID& session_id, const LLRect& rect = LLRect::null, bool show_images = true); + LLIMToastNotifyPanel(LLNotificationPtr& pNotification, + const LLUUID& session_id, + const LLRect& rect = LLRect::null, + bool show_images = true, + LLTextBase* parent_text = NULL); + + void compactButtons(); + + virtual void updateNotification(); + virtual void init( LLRect rect, bool show_images ); ~LLIMToastNotifyPanel(); /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); protected: + LLTextBase* mParentText; LLUUID mSessionID; }; diff --git a/indra/newview/lltoastpanel.cpp b/indra/newview/lltoastpanel.cpp index c33fde99c5..187aee207c 100644 --- a/indra/newview/lltoastpanel.cpp +++ b/indra/newview/lltoastpanel.cpp @@ -70,7 +70,7 @@ void LLToastPanel::snapToMessageHeight(LLTextBase* message, S32 maxLineCount) if (message->getVisible()) { S32 heightDelta = 0; - S32 maxTextHeight = message->getDefaultFont()->getLineHeight() * maxLineCount; + S32 maxTextHeight = message->getFont()->getLineHeight() * maxLineCount; LLRect messageRect = message->getRect(); S32 oldTextHeight = messageRect.getHeight(); @@ -80,11 +80,14 @@ void LLToastPanel::snapToMessageHeight(LLTextBase* message, S32 maxLineCount) S32 requiredTextHeight = message->getTextBoundingRect().getHeight(); S32 newTextHeight = llmin(requiredTextHeight, maxTextHeight); - //Calculate last delta height deducting previous heightDelta - heightDelta = newTextHeight - oldTextHeight - heightDelta; + heightDelta = newTextHeight - oldTextHeight; + S32 new_panel_height = llmax(getRect().getHeight() + heightDelta, MIN_PANEL_HEIGHT); //reshape the panel with new height - reshape( getRect().getWidth(), llmax(getRect().getHeight() + heightDelta, MIN_PANEL_HEIGHT)); + if (new_panel_height != getRect().getHeight()) + { + reshape( getRect().getWidth(), new_panel_height); + } } } @@ -98,7 +101,7 @@ LLToastPanel* LLToastPanel::buidPanelFromNotification( if ("notifytip" == notification->getType()) { // if it is online/offline notification - if ("FriendOffline" == notification->getName() || "FriendOnline" == notification->getName()) + if ("FriendOnlineOffline" == notification->getName()) { res = new LLPanelOnlineStatus(notification); } diff --git a/indra/newview/lltoastpanel.h b/indra/newview/lltoastpanel.h index 346e014d73..c22557206b 100644 --- a/indra/newview/lltoastpanel.h +++ b/indra/newview/lltoastpanel.h @@ -33,19 +33,13 @@ #include <string> -class LLToastPanelBase: public LLPanel -{ -public: - virtual void init(LLSD& data){}; -}; - /** * Base class for all panels that can be added to the toast. * All toast panels should contain necessary logic for representing certain notification * but shouldn't contain logic related to this panel lifetime control and positioning * on the parent view. */ -class LLToastPanel: public LLPanel { +class LLToastPanel : public LLPanel { public: LLToastPanel(const LLNotificationPtr&); virtual ~LLToastPanel() = 0; diff --git a/indra/newview/lltoastscriptquestion.cpp b/indra/newview/lltoastscriptquestion.cpp index feeb8ca77b..91ba8c0247 100644 --- a/indra/newview/lltoastscriptquestion.cpp +++ b/indra/newview/lltoastscriptquestion.cpp @@ -66,8 +66,8 @@ void LLToastScriptQuestion::snapToMessageHeight() if (mMessage->getVisible() && mFooter->getVisible()) { S32 heightDelta = 0; - S32 maxTextHeight = (mMessage->getDefaultFont()->getLineHeight() * MAX_LINES_COUNT) - + (mFooter->getDefaultFont()->getLineHeight() * MAX_LINES_COUNT); + S32 maxTextHeight = (mMessage->getFont()->getLineHeight() * MAX_LINES_COUNT) + + (mFooter->getFont()->getLineHeight() * MAX_LINES_COUNT); LLRect messageRect = mMessage->getRect(); LLRect footerRect = mFooter->getRect(); diff --git a/indra/newview/lltoastscripttextbox.cpp b/indra/newview/lltoastscripttextbox.cpp index 2529ec865a..45fbabad59 100644 --- a/indra/newview/lltoastscripttextbox.cpp +++ b/indra/newview/lltoastscripttextbox.cpp @@ -65,7 +65,7 @@ LLToastScriptTextbox::LLToastScriptTextbox(const LLNotificationPtr& notification pMessageText->clear(); LLStyle::Params style; - style.font = pMessageText->getDefaultFont(); + style.font = pMessageText->getFont(); pMessageText->appendText(message, TRUE, style); //submit button diff --git a/indra/newview/lltoastscripttextbox.h b/indra/newview/lltoastscripttextbox.h index 8e69d8834d..7d33446248 100644 --- a/indra/newview/lltoastscripttextbox.h +++ b/indra/newview/lltoastscripttextbox.h @@ -39,8 +39,6 @@ class LLToastScriptTextbox public: void close(); - static bool onNewNotification(const LLSD& notification); - // Non-transient messages. You can specify non-default button // layouts (like one for script dialogs) by passing various // numbers in for "layout". diff --git a/indra/newview/lltoolbarview.cpp b/indra/newview/lltoolbarview.cpp index 81ad96f39e..2b5acdc1e8 100644 --- a/indra/newview/lltoolbarview.cpp +++ b/indra/newview/lltoolbarview.cpp @@ -603,7 +603,7 @@ BOOL LLToolBarView::handleDragTool( S32 x, S32 y, const LLUUID& uuid, LLAssetTyp BOOL LLToolBarView::handleDropTool( void* cargo_data, S32 x, S32 y, LLToolBar* toolbar) { BOOL handled = FALSE; - LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data; + LLInventoryObject* inv_item = static_cast<LLInventoryObject*>(cargo_data); LLAssetType::EType type = inv_item->getType(); if (type == LLAssetType::AT_WIDGET) diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index c69999981c..94c97158a8 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -58,7 +58,6 @@ #include "llviewerwindow.h" #include "llvoavatarself.h" #include "llworld.h" -#include "llclipboard.h" // syntactic sugar #define callMemberFunction(object,ptrToMember) ((object).*(ptrToMember)) @@ -654,33 +653,41 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, sOperationId++; } + // For people drag and drop we don't need an actual inventory object, + // instead we need the current cargo id, which should be a person id. + bool is_uuid_dragged = (mSource == SOURCE_PEOPLE); + if (top_view) { handled = TRUE; for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++) { - LLInventoryObject* cargo = locateInventory(item, cat); + S32 local_x, local_y; + top_view->screenPointToLocal( x, y, &local_x, &local_y ); + EAcceptance item_acceptance = ACCEPT_NO; + LLInventoryObject* cargo = locateInventory(item, cat); if (cargo) { - S32 local_x, local_y; - top_view->screenPointToLocal( x, y, &local_x, &local_y ); - EAcceptance item_acceptance = ACCEPT_NO; handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, FALSE, mCargoTypes[mCurItemIndex], (void*)cargo, &item_acceptance, mToolTipMsg); - if (handled) - { - // use sort order to determine priority of acceptance - *acceptance = (EAcceptance)llmin((U32)item_acceptance, (U32)*acceptance); - } } - else + else if (is_uuid_dragged) { - return; + handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, FALSE, + mCargoTypes[mCurItemIndex], + (void*)&mCargoIDs[mCurItemIndex], + &item_acceptance, + mToolTipMsg); + } + if (handled) + { + // use sort order to determine priority of acceptance + *acceptance = (EAcceptance)llmin((U32)item_acceptance, (U32)*acceptance); } } @@ -697,20 +704,27 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++) { - LLInventoryObject* cargo = locateInventory(item, cat); + S32 local_x, local_y; + EAcceptance item_acceptance; + top_view->screenPointToLocal( x, y, &local_x, &local_y ); + LLInventoryObject* cargo = locateInventory(item, cat); if (cargo) { - S32 local_x, local_y; - - EAcceptance item_acceptance; - top_view->screenPointToLocal( x, y, &local_x, &local_y ); handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, TRUE, mCargoTypes[mCurItemIndex], (void*)cargo, &item_acceptance, mToolTipMsg); } + else if (is_uuid_dragged) + { + handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, FALSE, + mCargoTypes[mCurItemIndex], + (void*)&mCargoIDs[mCurItemIndex], + &item_acceptance, + mToolTipMsg); + } } } if (handled) @@ -727,17 +741,27 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++) { + EAcceptance item_acceptance = ACCEPT_NO; + LLInventoryObject* cargo = locateInventory(item, cat); // fix for EXT-3191 - if (NULL == cargo) return; - - EAcceptance item_acceptance = ACCEPT_NO; - handled = handled && root_view->handleDragAndDrop(x, y, mask, FALSE, - mCargoTypes[mCurItemIndex], - (void*)cargo, - &item_acceptance, - mToolTipMsg); + if (cargo) + { + handled = handled && root_view->handleDragAndDrop(x, y, mask, FALSE, + mCargoTypes[mCurItemIndex], + (void*)cargo, + &item_acceptance, + mToolTipMsg); + } + else if (is_uuid_dragged) + { + handled = handled && root_view->handleDragAndDrop(x, y, mask, FALSE, + mCargoTypes[mCurItemIndex], + (void*)&mCargoIDs[mCurItemIndex], + &item_acceptance, + mToolTipMsg); + } if (handled) { // use sort order to determine priority of acceptance @@ -757,17 +781,25 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++) { - LLInventoryObject* cargo = locateInventory(item, cat); + EAcceptance item_acceptance; + LLInventoryObject* cargo = locateInventory(item, cat); if (cargo) { - EAcceptance item_acceptance; handled = handled && root_view->handleDragAndDrop(x, y, mask, TRUE, mCargoTypes[mCurItemIndex], (void*)cargo, &item_acceptance, mToolTipMsg); } + else if (is_uuid_dragged) + { + handled = handled && root_view->handleDragAndDrop(x, y, mask, TRUE, + mCargoTypes[mCurItemIndex], + (void*)&mCargoIDs[mCurItemIndex], + &item_acceptance, + mToolTipMsg); + } } } @@ -780,7 +812,7 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, if (!handled) { // Disallow drag and drop to 3D from the outbox - const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false); + const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); if (outbox_id.notNull()) { for (S32 item_index = 0; item_index < (S32)mCargoIDs.size(); item_index++) @@ -2509,7 +2541,13 @@ LLInventoryObject* LLToolDragAndDrop::locateInventory( { item = NULL; cat = NULL; - if(mCargoIDs.empty()) return NULL; + + if (mCargoIDs.empty() + || (mSource == SOURCE_PEOPLE)) ///< There is no inventory item for people drag and drop. + { + return NULL; + } + if((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY)) { // The object should be in user inventory. @@ -2545,6 +2583,7 @@ LLInventoryObject* LLToolDragAndDrop::locateInventory( { item = (LLViewerInventoryItem*)gToolBarView->getDragItem(); } + if(item) return item; if(cat) return cat; return NULL; diff --git a/indra/newview/lltooldraganddrop.h b/indra/newview/lltooldraganddrop.h index 41aee484db..f17300a76a 100644 --- a/indra/newview/lltooldraganddrop.h +++ b/indra/newview/lltooldraganddrop.h @@ -67,7 +67,8 @@ public: SOURCE_WORLD, SOURCE_NOTECARD, SOURCE_LIBRARY, - SOURCE_VIEWER + SOURCE_VIEWER, + SOURCE_PEOPLE }; void beginDrag(EDragAndDropType type, diff --git a/indra/newview/llviewerassettype.cpp b/indra/newview/llviewerassettype.cpp index a4b1c2155f..08ba5a5f25 100644 --- a/indra/newview/llviewerassettype.cpp +++ b/indra/newview/llviewerassettype.cpp @@ -83,6 +83,8 @@ LLViewerAssetDictionary::LLViewerAssetDictionary() addEntry(LLViewerAssetType::AT_WIDGET, new ViewerAssetEntry(DAD_WIDGET)); + addEntry(LLViewerAssetType::AT_PERSON, new ViewerAssetEntry(DAD_PERSON)); + addEntry(LLViewerAssetType::AT_NONE, new ViewerAssetEntry(DAD_NONE)); }; diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 1f7cf0cdd4..50735d10bd 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -50,6 +50,8 @@ #include "llfloaterbump.h" #include "llfloaterbvhpreview.h" #include "llfloatercamera.h" +#include "llfloaterconversationlog.h" +#include "llfloaterconversationpreview.h" #include "llfloaterdeleteenvpreset.h" #include "llfloaterdisplayname.h" #include "llfloatereditdaycycle.h" @@ -114,6 +116,7 @@ #include "llfloatertranslationsettings.h" #include "llfloateruipreview.h" #include "llfloatervoiceeffect.h" +#include "llfloatervoicevolume.h" #include "llfloaterwhitelistentry.h" #include "llfloaterwindowsize.h" #include "llfloaterworldmap.h" @@ -137,7 +140,6 @@ #include "llscriptfloater.h" #include "llfloatermodelpreview.h" #include "llcommandhandler.h" -#include "llnearbychatbar.h" // *NOTE: Please add files in alphabetical order to keep merges easy. @@ -190,9 +192,9 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("bumps", "floater_bumps.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterBump>); LLFloaterReg::add("camera", "floater_camera.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCamera>); - LLFloaterReg::add("chat_bar", "floater_chat_bar.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLNearbyChatBar>); - + LLFloaterReg::add("chat_bar", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLNearbyChat>); LLFloaterReg::add("compile_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCompileQueue>); + LLFloaterReg::add("conversation", "floater_conversation_log.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterConversationLog>); LLFloaterReg::add("destinations", "floater_destinations.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterDestinations>); @@ -224,6 +226,7 @@ void LLViewerFloaterReg::registerFloaters() LLInspectGroupUtil::registerFloater(); LLInspectObjectUtil::registerFloater(); LLInspectRemoteObjectUtil::registerFloater(); + LLFloaterVoiceVolumeUtil::registerFloater(); LLNotificationsUI::registerFloater(); LLFloaterDisplayNameUtil::registerFloater(); @@ -268,6 +271,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("picks", "floater_picks.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSidePanelContainer>); LLFloaterReg::add("pref_joystick", "floater_joystick.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterJoystick>); LLFloaterReg::add("preview_anim", "floater_preview_animation.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPreviewAnim>, "preview"); + LLFloaterReg::add("preview_conversation", "floater_conversation_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterConversationPreview>); LLFloaterReg::add("preview_gesture", "floater_preview_gesture.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPreviewGesture>, "preview"); LLFloaterReg::add("preview_notecard", "floater_preview_notecard.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPreviewNotecard>, "preview"); LLFloaterReg::add("preview_script", "floater_script_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPreviewLSL>, "preview"); diff --git a/indra/newview/llviewergesture.cpp b/indra/newview/llviewergesture.cpp index a32a78cbf9..c7d37e102e 100644 --- a/indra/newview/llviewergesture.cpp +++ b/indra/newview/llviewergesture.cpp @@ -40,7 +40,7 @@ #include "llviewermessage.h" // send_guid_sound_trigger #include "llviewernetwork.h" #include "llagent.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" // Globals LLViewerGestureList gGestureList; @@ -130,7 +130,7 @@ void LLViewerGesture::doTrigger( BOOL send_chat ) { // Don't play nodding animation, since that might not blend // with the gesture animation. - LLNearbyChatBar::getInstance()->sendChatFromViewer(mOutputString, CHAT_TYPE_NORMAL, FALSE); + LLNearbyChat::getInstance()->sendChatFromViewer(mOutputString, CHAT_TYPE_NORMAL, FALSE); } } diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index b47a41c44c..a187318eb7 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -1030,12 +1030,7 @@ void CreateGestureCallback::fire(const LLUUID& inv_item) gFloaterView->adjustToFitScreen(preview, FALSE); } -void AddFavoriteLandmarkCallback::fire(const LLUUID& inv_item_id) -{ - if (mTargetLandmarkId.isNull()) return; - gInventory.rearrangeFavoriteLandmarks(inv_item_id, mTargetLandmarkId); -} LLInventoryCallbackManager gInventoryCallbacks; @@ -1308,7 +1303,7 @@ const std::string NEW_NOTECARD_NAME = "New Note"; // *TODO:Translate? (probably const std::string NEW_GESTURE_NAME = "New Gesture"; // *TODO:Translate? (probably not) // ! REFACTOR ! Really need to refactor this so that it's not a bunch of if-then statements... -void menu_create_inventory_item(LLFolderView* root, LLFolderBridge *bridge, const LLSD& userdata, const LLUUID& default_parent_uuid) +void menu_create_inventory_item(LLInventoryPanel* panel, LLFolderBridge *bridge, const LLSD& userdata, const LLUUID& default_parent_uuid) { std::string type_name = userdata.asString(); @@ -1332,7 +1327,7 @@ void menu_create_inventory_item(LLFolderView* root, LLFolderBridge *bridge, cons LLUUID category = gInventory.createNewCategory(parent_id, preferred_type, LLStringUtil::null); gInventory.notifyObservers(); - root->setSelectionByID(category, TRUE); + panel->setSelectionByID(category, TRUE); } else if ("lsl" == type_name) { @@ -1375,7 +1370,7 @@ void menu_create_inventory_item(LLFolderView* root, LLFolderBridge *bridge, cons llwarns << "Can't create unrecognized type " << type_name << llendl; } } - root->setNeedsAutoRename(TRUE); + panel->getRootFolder()->setNeedsAutoRename(TRUE); } LLAssetType::EType LLViewerInventoryItem::getType() const @@ -1449,348 +1444,6 @@ const std::string& LLViewerInventoryItem::getName() const return LLInventoryItem::getName(); } -/** - * Class to store sorting order of favorites landmarks in a local file. EXT-3985. - * It replaced previously implemented solution to store sort index in landmark's name as a "<N>@" prefix. - * Data are stored in user home directory. - */ -class LLFavoritesOrderStorage : public LLSingleton<LLFavoritesOrderStorage> - , public LLDestroyClass<LLFavoritesOrderStorage> -{ - LOG_CLASS(LLFavoritesOrderStorage); -public: - /** - * Sets sort index for specified with LLUUID favorite landmark - */ - void setSortIndex(const LLUUID& inv_item_id, S32 sort_index); - - /** - * Gets sort index for specified with LLUUID favorite landmark - */ - S32 getSortIndex(const LLUUID& inv_item_id); - void removeSortIndex(const LLUUID& inv_item_id); - - void getSLURL(const LLUUID& asset_id); - - /** - * Implementation of LLDestroyClass. Calls cleanup() instance method. - * - * It is important this callback is called before gInventory is cleaned. - * For now it is called from LLAppViewer::cleanup() -> LLAppViewer::disconnectViewer(), - * Inventory is cleaned later from LLAppViewer::cleanup() after LLAppViewer::disconnectViewer() is called. - * @see cleanup() - */ - static void destroyClass(); - - const static S32 NO_INDEX; -private: - friend class LLSingleton<LLFavoritesOrderStorage>; - LLFavoritesOrderStorage() : mIsDirty(false) { load(); } - ~LLFavoritesOrderStorage() { save(); } - - /** - * Removes sort indexes for items which are not in Favorites bar for now. - */ - void cleanup(); - - const static std::string SORTING_DATA_FILE_NAME; - - void load(); - void save(); - - void saveFavoritesSLURLs(); - - // Remove record of current user's favorites from file on disk. - void removeFavoritesRecordOfUser(); - - void onLandmarkLoaded(const LLUUID& asset_id, LLLandmark* landmark); - void storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl); - - typedef std::map<LLUUID, S32> sort_index_map_t; - sort_index_map_t mSortIndexes; - - typedef std::map<LLUUID, std::string> slurls_map_t; - slurls_map_t mSLURLs; - - bool mIsDirty; - - struct IsNotInFavorites - { - IsNotInFavorites(const LLInventoryModel::item_array_t& items) - : mFavoriteItems(items) - { - - } - - /** - * Returns true if specified item is not found among inventory items - */ - bool operator()(const sort_index_map_t::value_type& id_index_pair) const - { - LLPointer<LLViewerInventoryItem> item = gInventory.getItem(id_index_pair.first); - if (item.isNull()) return true; - - LLInventoryModel::item_array_t::const_iterator found_it = - std::find(mFavoriteItems.begin(), mFavoriteItems.end(), item); - - return found_it == mFavoriteItems.end(); - } - private: - LLInventoryModel::item_array_t mFavoriteItems; - }; - -}; - -const std::string LLFavoritesOrderStorage::SORTING_DATA_FILE_NAME = "landmarks_sorting.xml"; -const S32 LLFavoritesOrderStorage::NO_INDEX = -1; - -void LLFavoritesOrderStorage::setSortIndex(const LLUUID& inv_item_id, S32 sort_index) -{ - mSortIndexes[inv_item_id] = sort_index; - mIsDirty = true; -} - -S32 LLFavoritesOrderStorage::getSortIndex(const LLUUID& inv_item_id) -{ - sort_index_map_t::const_iterator it = mSortIndexes.find(inv_item_id); - if (it != mSortIndexes.end()) - { - return it->second; - } - return NO_INDEX; -} - -void LLFavoritesOrderStorage::removeSortIndex(const LLUUID& inv_item_id) -{ - mSortIndexes.erase(inv_item_id); - mIsDirty = true; -} - -void LLFavoritesOrderStorage::getSLURL(const LLUUID& asset_id) -{ - slurls_map_t::iterator slurl_iter = mSLURLs.find(asset_id); - if (slurl_iter != mSLURLs.end()) return; // SLURL for current landmark is already cached - - LLLandmark* lm = gLandmarkList.getAsset(asset_id, - boost::bind(&LLFavoritesOrderStorage::onLandmarkLoaded, this, asset_id, _1)); - if (lm) - { - onLandmarkLoaded(asset_id, lm); - } -} - -// static -void LLFavoritesOrderStorage::destroyClass() -{ - LLFavoritesOrderStorage::instance().cleanup(); - if (gSavedPerAccountSettings.getBOOL("ShowFavoritesOnLogin")) - { - LLFavoritesOrderStorage::instance().saveFavoritesSLURLs(); - } - else - { - LLFavoritesOrderStorage::instance().removeFavoritesRecordOfUser(); - } -} - -void LLFavoritesOrderStorage::load() -{ - // load per-resident sorting information - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SORTING_DATA_FILE_NAME); - - LLSD settings_llsd; - llifstream file; - file.open(filename); - if (file.is_open()) - { - LLSDSerialize::fromXML(settings_llsd, file); - } - - for (LLSD::map_const_iterator iter = settings_llsd.beginMap(); - iter != settings_llsd.endMap(); ++iter) - { - mSortIndexes.insert(std::make_pair(LLUUID(iter->first), (S32)iter->second.asInteger())); - } -} - -void LLFavoritesOrderStorage::saveFavoritesSLURLs() -{ - // Do not change the file if we are not logged in yet. - if (!LLLoginInstance::getInstance()->authSuccess()) - { - llwarns << "Cannot save favorites: not logged in" << llendl; - return; - } - - std::string user_dir = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""); - if (user_dir.empty()) - { - llwarns << "Cannot save favorites: empty user dir name" << llendl; - return; - } - - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); - llifstream in_file; - in_file.open(filename); - LLSD fav_llsd; - if (in_file.is_open()) - { - LLSDSerialize::fromXML(fav_llsd, in_file); - } - - const LLUUID fav_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - gInventory.collectDescendents(fav_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); - - LLSD user_llsd; - for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); it++) - { - LLSD value; - value["name"] = (*it)->getName(); - value["asset_id"] = (*it)->getAssetUUID(); - - slurls_map_t::iterator slurl_iter = mSLURLs.find(value["asset_id"]); - if (slurl_iter != mSLURLs.end()) - { - lldebugs << "Saving favorite: idx=" << (*it)->getSortField() << ", SLURL=" << slurl_iter->second << ", value=" << value << llendl; - value["slurl"] = slurl_iter->second; - user_llsd[(*it)->getSortField()] = value; - } - else - { - llwarns << "Not saving favorite " << value["name"] << ": no matching SLURL" << llendl; - } - } - - LLAvatarName av_name; - LLAvatarNameCache::get( gAgentID, &av_name ); - lldebugs << "Saved favorites for " << av_name.getLegacyName() << llendl; - fav_llsd[av_name.getLegacyName()] = user_llsd; - - llofstream file; - file.open(filename); - LLSDSerialize::toPrettyXML(fav_llsd, file); -} - -void LLFavoritesOrderStorage::removeFavoritesRecordOfUser() -{ - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); - LLSD fav_llsd; - llifstream file; - file.open(filename); - if (!file.is_open()) return; - LLSDSerialize::fromXML(fav_llsd, file); - - LLAvatarName av_name; - LLAvatarNameCache::get( gAgentID, &av_name ); - lldebugs << "Removed favorites for " << av_name.getLegacyName() << llendl; - if (fav_llsd.has(av_name.getLegacyName())) - { - fav_llsd.erase(av_name.getLegacyName()); - } - - llofstream out_file; - out_file.open(filename); - LLSDSerialize::toPrettyXML(fav_llsd, out_file); - -} - -void LLFavoritesOrderStorage::onLandmarkLoaded(const LLUUID& asset_id, LLLandmark* landmark) -{ - if (!landmark) return; - - LLVector3d pos_global; - if (!landmark->getGlobalPos(pos_global)) - { - // If global position was unknown on first getGlobalPos() call - // it should be set for the subsequent calls. - landmark->getGlobalPos(pos_global); - } - - if (!pos_global.isExactlyZero()) - { - LLLandmarkActions::getSLURLfromPosGlobal(pos_global, - boost::bind(&LLFavoritesOrderStorage::storeFavoriteSLURL, this, asset_id, _1)); - } -} - -void LLFavoritesOrderStorage::storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl) -{ - lldebugs << "Saving landmark SLURL: " << slurl << llendl; - mSLURLs[asset_id] = slurl; -} - -void LLFavoritesOrderStorage::save() -{ - // nothing to save if clean - if (!mIsDirty) return; - - // If we quit from the login screen we will not have an SL account - // name. Don't try to save, otherwise we'll dump a file in - // C:\Program Files\SecondLife\ or similar. JC - std::string user_dir = gDirUtilp->getLindenUserDir(); - if (!user_dir.empty()) - { - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SORTING_DATA_FILE_NAME); - LLSD settings_llsd; - - for(sort_index_map_t::const_iterator iter = mSortIndexes.begin(); iter != mSortIndexes.end(); ++iter) - { - settings_llsd[iter->first.asString()] = iter->second; - } - - llofstream file; - file.open(filename); - LLSDSerialize::toPrettyXML(settings_llsd, file); - } -} - -void LLFavoritesOrderStorage::cleanup() -{ - // nothing to clean - if (!mIsDirty) return; - - const LLUUID fav_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - gInventory.collectDescendents(fav_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); - - IsNotInFavorites is_not_in_fav(items); - - sort_index_map_t aTempMap; - //copy unremoved values from mSortIndexes to aTempMap - std::remove_copy_if(mSortIndexes.begin(), mSortIndexes.end(), - inserter(aTempMap, aTempMap.begin()), - is_not_in_fav); - - //Swap the contents of mSortIndexes and aTempMap - mSortIndexes.swap(aTempMap); -} - - -S32 LLViewerInventoryItem::getSortField() const -{ - return LLFavoritesOrderStorage::instance().getSortIndex(mUUID); -} - -void LLViewerInventoryItem::setSortField(S32 sortField) -{ - LLFavoritesOrderStorage::instance().setSortIndex(mUUID, sortField); - getSLURL(); -} - -void LLViewerInventoryItem::getSLURL() -{ - LLFavoritesOrderStorage::instance().getSLURL(mAssetUUID); -} - -const LLPermissions& LLViewerInventoryItem::getPermissions() const -{ - // Use the actual permissions of the symlink, not its parent. - return LLInventoryItem::getPermissions(); -} - const LLUUID& LLViewerInventoryItem::getCreatorUUID() const { if (const LLViewerInventoryItem *linked_item = getLinkedItem()) @@ -1861,17 +1514,6 @@ LLWearableType::EType LLViewerInventoryItem::getWearableType() const return LLWearableType::EType(getFlags() & LLInventoryItemFlags::II_FLAGS_WEARABLES_MASK); } - -time_t LLViewerInventoryItem::getCreationDate() const -{ - return LLInventoryItem::getCreationDate(); -} - -U32 LLViewerInventoryItem::getCRC32() const -{ - return LLInventoryItem::getCRC32(); -} - // *TODO: mantipov: should be removed with LMSortPrefix patch in llinventorymodel.cpp, EXT-3985 static char getSeparator() { return '@'; } BOOL LLViewerInventoryItem::extractSortFieldAndDisplayName(const std::string& name, S32* sortField, std::string* displayName) diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 7822ef4da6..3cf03c3bc5 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -34,7 +34,7 @@ #include <boost/signals2.hpp> // boost::signals2::trackable -class LLFolderView; +class LLInventoryPanel; class LLFolderBridge; class LLViewerInventoryCategory; @@ -60,10 +60,6 @@ public: virtual const LLUUID& getAssetUUID() const; virtual const LLUUID& getProtectedAssetUUID() const; // returns LLUUID::null if current agent does not have permission to expose this asset's UUID to the user virtual const std::string& getName() const; - virtual S32 getSortField() const; - virtual void setSortField(S32 sortField); - virtual void getSLURL(); //Caches SLURL for landmark. //*TODO: Find a better way to do it and remove this method from here. - virtual const LLPermissions& getPermissions() const; virtual const bool getIsFullPerm() const; // 'fullperm' in the popular sense: modify-ok & copy-ok & transfer-ok, no special god rules applied virtual const LLUUID& getCreatorUUID() const; virtual const std::string& getDescription() const; @@ -72,8 +68,11 @@ public: virtual bool isWearableType() const; virtual LLWearableType::EType getWearableType() const; virtual U32 getFlags() const; - virtual time_t getCreationDate() const; - virtual U32 getCRC32() const; // really more of a checksum. + + using LLInventoryItem::getPermissions; + using LLInventoryItem::getCreationDate; + using LLInventoryItem::setCreationDate; + using LLInventoryItem::getCRC32; static BOOL extractSortFieldAndDisplayName(const std::string& name, S32* sortField, std::string* displayName); @@ -285,18 +284,6 @@ public: void fire(const LLUUID& inv_item); }; -class AddFavoriteLandmarkCallback : public LLInventoryCallback -{ -public: - AddFavoriteLandmarkCallback() : mTargetLandmarkId(LLUUID::null) {} - void setTargetLandmarkId(const LLUUID& target_uuid) { mTargetLandmarkId = target_uuid; } - -private: - void fire(const LLUUID& inv_item); - - LLUUID mTargetLandmarkId; -}; - // misc functions //void inventory_reliable_callback(void**, S32 status); @@ -372,7 +359,7 @@ void copy_inventory_from_notecard(const LLUUID& destination_id, U32 callback_id = 0); -void menu_create_inventory_item(LLFolderView* root, +void menu_create_inventory_item(LLInventoryPanel* root, LLFolderBridge* bridge, const LLSD& userdata, const LLUUID& default_parent_uuid = LLUUID::null); diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp index 1aa9fd8a45..385d3cd29a 100644 --- a/indra/newview/llviewerkeyboard.cpp +++ b/indra/newview/llviewerkeyboard.cpp @@ -31,7 +31,7 @@ #include "llmath.h" #include "llagent.h" #include "llagentcamera.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llviewercontrol.h" #include "llfocusmgr.h" #include "llmorphview.h" @@ -534,7 +534,7 @@ void stop_moving( EKeystate s ) void start_chat( EKeystate s ) { // start chat - LLNearbyChatBar::startChat(NULL); + LLNearbyChat::startChat(NULL); } void start_gesture( EKeystate s ) @@ -543,15 +543,15 @@ void start_gesture( EKeystate s ) if (KEYSTATE_UP == s && ! (focus_ctrlp && focus_ctrlp->acceptsTextInput())) { - if (LLNearbyChatBar::getInstance()->getCurrentChat().empty()) + if (LLNearbyChat::getInstance()->getCurrentChat().empty()) { // No existing chat in chat editor, insert '/' - LLNearbyChatBar::startChat("/"); + LLNearbyChat::startChat("/"); } else { // Don't overwrite existing text in chat editor - LLNearbyChatBar::startChat(NULL); + LLNearbyChat::startChat(NULL); } } } diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 01a54509ef..a993d195b5 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -3263,15 +3263,6 @@ bool enable_freeze_eject(const LLSD& avatar_id) return new_value; } - -void login_done(S32 which, void *user) -{ - llinfos << "Login done " << which << llendl; - - LLPanelLogin::closePanel(); -} - - bool callback_leave_group(const LLSD& notification, const LLSD& response) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 903f4437a7..b20b86a582 100755 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -184,6 +184,7 @@ bool friendship_offer_callback(const LLSD& notification, const LLSD& response) S32 option = LLNotificationsUtil::getSelectedOption(notification, response); LLMessageSystem* msg = gMessageSystem; const LLSD& payload = notification["payload"]; + LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID()); // add friend to recent people list LLRecentPeople::instance().add(payload["from_id"]); @@ -209,7 +210,6 @@ bool friendship_offer_callback(const LLSD& notification, const LLSD& response) msg->sendReliable(LLHost(payload["sender"].asString())); LLSD payload = notification["payload"]; - payload["SUPPRESS_TOAST"] = true; LLNotificationsUtil::add("FriendshipAcceptedByMe", notification["substitutions"], payload); break; @@ -217,7 +217,6 @@ bool friendship_offer_callback(const LLSD& notification, const LLSD& response) case 1: // Decline { LLSD payload = notification["payload"]; - payload["SUPPRESS_TOAST"] = true; LLNotificationsUtil::add("FriendshipDeclinedByMe", notification["substitutions"], payload); } @@ -246,6 +245,12 @@ bool friendship_offer_callback(const LLSD& notification, const LLSD& response) break; } + LLNotificationFormPtr modified_form(new LLNotificationForm(*notification_ptr->getForm())); + modified_form->setElementEnabled("Accept", false); + modified_form->setElementEnabled("Decline", false); + notification_ptr->updateForm(modified_form); + notification_ptr->repost(); + return false; } static LLNotificationFunctorRegistration friendship_offer_callback_reg("OfferFriendship", friendship_offer_callback); @@ -726,7 +731,7 @@ static void highlight_inventory_objects_in_panel(const std::vector<LLUUID>& item LLFolderView* fv = inventory_panel->getRootFolder(); if (fv) { - LLFolderViewItem* fv_item = fv->getItemByID(item_id); + LLFolderViewItem* fv_item = inventory_panel->getItemByID(item_id); if (fv_item) { LLFolderViewItem* fv_folder = fv_item->getParentFolder(); @@ -814,7 +819,13 @@ private: mSelectedItems.clear(); if (LLInventoryPanel::getActiveInventoryPanel()) { - mSelectedItems = LLInventoryPanel::getActiveInventoryPanel()->getRootFolder()->getSelectionList(); + std::set<LLFolderViewItem*> selection = LLInventoryPanel::getActiveInventoryPanel()->getRootFolder()->getSelectionList(); + for (std::set<LLFolderViewItem*>::iterator it = selection.begin(), end_it = selection.end(); + it != end_it; + ++it) + { + mSelectedItems.insert(static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID()); + } } mSelectedItems.erase(mMoveIntoFolderID); } @@ -849,7 +860,15 @@ private: } // get selected items (without destination folder) - selected_items_t selected_items = active_panel->getRootFolder()->getSelectionList(); + selected_items_t selected_items; + + std::set<LLFolderViewItem*> selection = LLInventoryPanel::getActiveInventoryPanel()->getRootFolder()->getSelectionList(); + for (std::set<LLFolderViewItem*>::iterator it = selection.begin(), end_it = selection.end(); + it != end_it; + ++it) + { + selected_items.insert(static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID()); + } selected_items.erase(mMoveIntoFolderID); // compare stored & current sets of selected items @@ -1155,7 +1174,7 @@ bool check_offer_throttle(const std::string& from_name, bool check_only) } } } - + // Return "true" if we have a preview method for that asset type, "false" otherwise bool check_asset_previewable(const LLAssetType::EType asset_type) { @@ -1478,16 +1497,15 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& itemp = (LLViewerInventoryItem*)gInventory.getItem(mObjectID); } + LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID()); + // For muting, we need to add the mute, then decline the offer. // This must be done here because: // * callback may be called immediately, // * adding the mute sends a message, // * we can't build two messages at once. - if (2 == button) // Block + if (IOR_MUTE == button) // Block { - LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID()); - - llassert(notification_ptr != NULL); if (notification_ptr != NULL) { gCacheName->get(mFromID, mFromGroup, boost::bind(&inventory_offer_mute_callback, _1, _2, _3)); @@ -1502,6 +1520,8 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& bool busy = gAgent.getBusy(); + LLNotificationFormPtr modified_form(notification_ptr ? new LLNotificationForm(*notification_ptr->getForm()) : new LLNotificationForm()); + switch(button) { case IOR_SHOW: @@ -1545,6 +1565,11 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& LL_WARNS("Messaging") << "inventory_offer_callback: unknown offer type" << LL_ENDL; break; } + + if (modified_form != NULL) + { + modified_form->setElementEnabled("Show", false); + } break; // end switch (mIM) @@ -1557,9 +1582,14 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& args["MESSAGE"] = log_message; LLNotificationsUtil::add("SystemMessageTip", args); } + break; case IOR_MUTE: + if (modified_form != NULL) + { + modified_form->setElementEnabled("Mute", false); + } // MUTE falls through to decline case IOR_DECLINE: { @@ -1595,6 +1625,13 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& { busy_message(gMessageSystem, mFromID); } + + if (modified_form != NULL) + { + modified_form->setElementEnabled("Show", false); + modified_form->setElementEnabled("Discard", false); + } + break; } default: @@ -1614,6 +1651,13 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& { delete this; } + + if (notification_ptr != NULL) + { + notification_ptr->updateForm(modified_form); + notification_ptr->repost(); + } + return false; } @@ -1991,6 +2035,15 @@ bool lure_callback(const LLSD& notification, const LLSD& response) lure_id); break; } + + LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID()); + + LLNotificationFormPtr modified_form(new LLNotificationForm(*notification_ptr->getForm())); + modified_form->setElementEnabled("Teleport", false); + modified_form->setElementEnabled("Cancel", false); + notification_ptr->updateForm(modified_form); + notification_ptr->repost(); + return false; } static LLNotificationFunctorRegistration lure_callback_reg("TeleportOffered", lure_callback); @@ -2386,6 +2439,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) from_id, name, buffer, + IM_OFFLINE == offline, LLStringUtil::null, dialog, parent_estate_id, @@ -2420,12 +2474,12 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) LL_INFOS("Messaging") << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << LL_ENDL; bool mute_im = is_muted; - if (accept_im_from_only_friend && !is_friend) + if(accept_im_from_only_friend&&!is_friend) { if (!gIMMgr->isNonFriendSessionNotified(session_id)) { std::string message = LLTrans::getString("IM_unblock_only_groups_friends"); - gIMMgr->addMessage(session_id, from_id, name, message); + gIMMgr->addMessage(session_id, from_id, name, message, IM_OFFLINE == offline); gIMMgr->addNotifiedNonFriendSessionID(session_id); } @@ -2438,6 +2492,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) from_id, name, buffer, + IM_OFFLINE == offline, LLStringUtil::null, dialog, parent_estate_id, @@ -2778,6 +2833,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) from_id, name, buffer, + IM_OFFLINE == offline, ll_safe_string((char*)binary_bucket), IM_SESSION_INVITE, parent_estate_id, @@ -2847,7 +2903,6 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) chat.mOwnerID = from_id; LLSD args; args["slurl"] = location; - args["type"] = LLNotificationsUI::NT_NEARBYCHAT; // Look for IRC-style emotes here so object name formatting is correct std::string prefix = message.substr(0, 4); @@ -3591,7 +3646,6 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) // pass owner_id to chat so that we can display the remote // object inspect for an object that is chatting with you LLSD args; - args["type"] = LLNotificationsUI::NT_NEARBYCHAT; chat.mOwnerID = owner_id; if (gSavedSettings.getBOOL("TranslateChat") && chat.mSourceType != CHAT_SOURCE_SYSTEM) @@ -6798,7 +6852,6 @@ bool handle_lure_callback(const LLSD& notification, const LLSD& response) //*TODO please rewrite all keys to the same case, lower or upper payload["from_id"] = target_id; - payload["SUPPRESS_TOAST"] = true; LLNotificationsUtil::add("TeleportOfferSent", args, payload); // Add the recepient to the recent people list. diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index af2eec9ba8..1bcf15913f 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -56,6 +56,7 @@ // linden library includes #include "llaudioengine.h" // mute on minimize +#include "llchatentry.h" #include "indra_constants.h" #include "llassetstorage.h" #include "llerrorcontrol.h" @@ -187,7 +188,7 @@ #include "llviewerjoystick.h" #include "llviewernetwork.h" #include "llpostprocess.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llagentui.h" #include "llwearablelist.h" @@ -1550,11 +1551,12 @@ LLViewerWindow::LLViewerWindow(const Params& p) // boost::lambda::var() constructs such a functor on the fly. mWindowListener.reset(new LLWindowListener(this, boost::lambda::var(gKeyboard))); mViewerWindowListener.reset(new LLViewerWindowListener(this)); - LLNotificationChannel::buildChannel("VW_alerts", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alert")); - LLNotificationChannel::buildChannel("VW_alertmodal", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alertmodal")); - LLNotifications::instance().getChannel("VW_alerts")->connectChanged(&LLViewerWindow::onAlert); - LLNotifications::instance().getChannel("VW_alertmodal")->connectChanged(&LLViewerWindow::onAlert); + mAlertsChannel.reset(new LLNotificationChannel("VW_alerts", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alert"))); + mModalAlertsChannel.reset(new LLNotificationChannel("VW_alertmodal", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alertmodal"))); + + mAlertsChannel->connectChanged(&LLViewerWindow::onAlert); + mModalAlertsChannel->connectChanged(&LLViewerWindow::onAlert); LLNotifications::instance().setIgnoreAllNotifications(gSavedSettings.getBOOL("IgnoreAllNotifications")); llinfos << "NOTE: ALL NOTIFICATIONS THAT OCCUR WILL GET ADDED TO IGNORE LIST FOR LATER RUNS." << llendl; @@ -2495,11 +2497,11 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) // Traverses up the hierarchy if( keyboard_focus ) { - LLNearbyChatBar* nearby_chat = LLFloaterReg::findTypedInstance<LLNearbyChatBar>("chat_bar"); + LLNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance<LLNearbyChat>("chat_bar"); if (nearby_chat) { - LLLineEditor* chat_editor = nearby_chat->getChatBox(); + LLChatEntry* chat_editor = nearby_chat->getChatBox(); // arrow keys move avatar while chatting hack if (chat_editor && chat_editor->hasFocus()) @@ -2562,11 +2564,11 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) if ( gSavedSettings.getS32("LetterKeysFocusChatBar") && !gAgentCamera.cameraMouselook() && !keyboard_focus && key < 0x80 && (mask == MASK_NONE || mask == MASK_SHIFT) ) { - LLLineEditor* chat_editor = LLFloaterReg::getTypedInstance<LLNearbyChatBar>("chat_bar")->getChatBox(); + LLChatEntry* chat_editor = LLNearbyChat::getInstance()->getChatBox(); if (chat_editor) { // passing NULL here, character will be added later when it is handled by character handler. - LLNearbyChatBar::getInstance()->startChat(NULL); + LLNearbyChat::getInstance()->startChat(NULL); return TRUE; } } diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index 6efcaeaf18..ee6a7793f8 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -418,6 +418,9 @@ private: bool mActive; bool mUIVisible; + boost::shared_ptr<class LLNotificationChannel> mAlertsChannel, + mModalAlertsChannel; + LLRect mWindowRectRaw; // whole window, including UI LLRect mWindowRectScaled; // whole window, scaled by UI size LLRect mWorldViewRectRaw; // area of screen for 3D world diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 05febdf93b..54465ba731 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -34,6 +34,8 @@ #include "llvoavatar.h" +#define XXX_STINSON_CHUI_REWORK // temporarily re-enabling the in-world voice-dot + #include <stdio.h> #include <ctype.h> @@ -62,6 +64,7 @@ #include "llhudmanager.h" #include "llhudnametag.h" #include "llhudtext.h" // for mText/mDebugText +#include "llinitparam.h" #include "llkeyframefallmotion.h" #include "llkeyframestandmotion.h" #include "llkeyframewalkmotion.h" @@ -191,6 +194,9 @@ const S32 MAX_BUBBLE_CHAT_LENGTH = DB_CHAT_MSG_STR_LEN; const S32 MAX_BUBBLE_CHAT_UTTERANCES = 12; const F32 CHAT_FADE_TIME = 8.0; const F32 BUBBLE_CHAT_TIME = CHAT_FADE_TIME * 3.f; +const F32 NAMETAG_UPDATE_THRESHOLD = 0.3f; +const F32 NAMETAG_VERTICAL_SCREEN_OFFSET = 25.f; +const F32 NAMETAG_VERT_OFFSET_WEIGHT = 0.17f; const LLColor4 DUMMY_COLOR = LLColor4(0.5,0.5,0.5,1.0); @@ -222,55 +228,62 @@ struct LLTextureMaskData **/ //------------------------------------------------------------------------ -// LLVOBoneInfo +// LLVOAvatarBoneInfo // Trans/Scale/Rot etc. info about each avatar bone. Used by LLVOAvatarSkeleton. //------------------------------------------------------------------------ -class LLVOAvatarBoneInfo +struct LLVOAvatarCollisionVolumeInfo : public LLInitParam::Block<LLVOAvatarCollisionVolumeInfo> { - friend class LLVOAvatar; - friend class LLVOAvatarSkeletonInfo; -public: - LLVOAvatarBoneInfo() : mIsJoint(FALSE) {} - ~LLVOAvatarBoneInfo() + LLVOAvatarCollisionVolumeInfo() + : name("name"), + pos("pos"), + rot("rot"), + scale("scale") + {} + + Mandatory<std::string> name; + Mandatory<LLVector3> pos, + rot, + scale; +}; + +struct LLVOAvatarChildJoint : public LLInitParam::ChoiceBlock<LLVOAvatarChildJoint> { - std::for_each(mChildList.begin(), mChildList.end(), DeletePointer()); - } - BOOL parseXml(LLXmlTreeNode* node); + Alternative<Lazy<struct LLVOAvatarBoneInfo, IS_A_BLOCK> > bone; + Alternative<LLVOAvatarCollisionVolumeInfo> collision_volume; -private: - std::string mName; - BOOL mIsJoint; - LLVector3 mPos; - LLVector3 mRot; - LLVector3 mScale; - LLVector3 mPivot; - typedef std::vector<LLVOAvatarBoneInfo*> child_list_t; - child_list_t mChildList; + LLVOAvatarChildJoint() + : bone("bone"), + collision_volume("collision_volume") + {} +}; + +struct LLVOAvatarBoneInfo : public LLInitParam::Block<LLVOAvatarBoneInfo, LLVOAvatarCollisionVolumeInfo> +{ + LLVOAvatarBoneInfo() + : pivot("pivot") + {} + + Mandatory<LLVector3> pivot; + Multiple<LLVOAvatarChildJoint> children; }; //------------------------------------------------------------------------ // LLVOAvatarSkeletonInfo // Overall avatar skeleton //------------------------------------------------------------------------ -class LLVOAvatarSkeletonInfo +struct LLVOAvatarSkeletonInfo : public LLInitParam::Block<LLVOAvatarSkeletonInfo> { - friend class LLVOAvatar; -public: - LLVOAvatarSkeletonInfo() : - mNumBones(0), mNumCollisionVolumes(0) {} - ~LLVOAvatarSkeletonInfo() - { - std::for_each(mBoneInfoList.begin(), mBoneInfoList.end(), DeletePointer()); - } - BOOL parseXml(LLXmlTreeNode* node); - S32 getNumBones() const { return mNumBones; } - S32 getNumCollisionVolumes() const { return mNumCollisionVolumes; } + LLVOAvatarSkeletonInfo() + : skeleton_root(""), + num_bones("num_bones"), + num_collision_volumes("num_collision_volumes"), + version("version") + {} -private: - S32 mNumBones; - S32 mNumCollisionVolumes; - typedef std::vector<LLVOAvatarBoneInfo*> bone_info_list_t; - bone_info_list_t mBoneInfoList; + Mandatory<std::string> version; + Mandatory<S32> num_bones, + num_collision_volumes; + Mandatory<LLVOAvatarChildJoint> skeleton_root; }; //----------------------------------------------------------------------------- @@ -595,7 +608,7 @@ private: // Static Data //----------------------------------------------------------------------------- LLXmlTree LLVOAvatar::sXMLTree; -LLXmlTree LLVOAvatar::sSkeletonXMLTree; +LLXMLNodePtr LLVOAvatar::sSkeletonXMLTree; LLVOAvatarSkeletonInfo* LLVOAvatar::sAvatarSkeletonInfo = NULL; LLVOAvatar::LLVOAvatarXmlInfo* LLVOAvatar::sAvatarXmlInfo = NULL; LLVOAvatarDictionary *LLVOAvatar::sAvatarDictionary = NULL; @@ -698,9 +711,13 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, LLMemType mt(LLMemType::MTYPE_AVATAR); //VTResume(); // VTune +#ifdef XXX_STINSON_CHUI_REWORK // mVoiceVisualizer is created by the hud effects manager and uses the HUD Effects pipeline const BOOL needsSendToSim = false; // currently, this HUD effect doesn't need to pack and unpack data to do its job mVoiceVisualizer = ( LLVoiceVisualizer *)LLHUDManager::getInstance()->createViewerEffect( LLHUDObject::LL_HUD_EFFECT_VOICE_VISUALIZER, needsSendToSim ); +#else // XXX_STINSON_CHUI_REWORK + mVoiceVisualizer = new LLVoiceVisualizer(); +#endif // XXX_STINSON_CHUI_REWORK lldebugs << "LLVOAvatar Constructor (0x" << this << ") id:" << mID << llendl; @@ -805,14 +822,14 @@ void LLVOAvatar::debugAvatarRezTime(std::string notification_name, std::string c //------------------------------------------------------------------------ LLVOAvatar::~LLVOAvatar() { - if (!mFullyLoaded) - { + if (!mFullyLoaded) + { debugAvatarRezTime("AvatarRezLeftCloudNotification","left after ruth seconds as cloud"); - } - else - { + } + else + { debugAvatarRezTime("AvatarRezLeftNotification","left sometime after declouding"); - } + } lldebugs << "LLVOAvatar Destructor (0x" << this << ") id:" << mID << llendl; @@ -876,7 +893,11 @@ void LLVOAvatar::markDead() mNameText = NULL; sNumVisibleChatBubbles--; } +#ifdef XXX_STINSON_CHUI_REWORK mVoiceVisualizer->markDead(); +#else // XXX_STINSON_CHUI_REWORK + mVoiceVisualizer->setStopSpeaking(); +#endif // XXX_STINSON_CHUI_REWORK LLLoadedCallbackEntry::cleanUpCallbackList(&mCallbackTextureList) ; LLViewerObject::markDead(); } @@ -1214,18 +1235,6 @@ void LLVOAvatar::initClass() llerrs << "Error parsing skeleton file: " << skeleton_path << llendl; } - // Process XML data - - // avatar_skeleton.xml - if (sAvatarSkeletonInfo) - { //this can happen if a login attempt failed - delete sAvatarSkeletonInfo; - } - sAvatarSkeletonInfo = new LLVOAvatarSkeletonInfo; - if (!sAvatarSkeletonInfo->parseXml(sSkeletonXMLTree.getRoot())) - { - llerrs << "Error parsing skeleton XML file: " << skeleton_path << llendl; - } // parse avatar_lad.xml if (sAvatarXmlInfo) { //this can happen if a login attempt failed @@ -1274,7 +1283,7 @@ void LLVOAvatar::initClass() void LLVOAvatar::cleanupClass() { deleteAndClear(sAvatarXmlInfo); - sSkeletonXMLTree.cleanup(); + sSkeletonXMLTree = NULL; sXMLTree.cleanup(); } @@ -1420,7 +1429,9 @@ void LLVOAvatar::initInstance(void) //VTPause(); // VTune +#ifdef XXX_STINSON_CHUI_REWORK mVoiceVisualizer->setVoiceEnabled( LLVoiceClient::getInstance()->getVoiceEnabled( mID ) ); +#endif // XXX_STINSON_CHUI_REWORK } @@ -1744,33 +1755,39 @@ BOOL LLVOAvatar::parseSkeletonFile(const std::string& filename) //------------------------------------------------------------------------- // parse the file //------------------------------------------------------------------------- - BOOL parsesuccess = sSkeletonXMLTree.parseFile( filename, FALSE ); - if (!parsesuccess) + LLXMLNodePtr skeleton_xml; + BOOL parsesuccess = LLXMLNode::parseFile(filename, skeleton_xml, NULL); + + if (!parsesuccess || skeleton_xml.isNull()) { llerrs << "Can't parse skeleton file: " << filename << llendl; return FALSE; } - // now sanity check xml file - LLXmlTreeNode* root = sSkeletonXMLTree.getRoot(); - if (!root) + // Process XML data + if (sAvatarSkeletonInfo) + { //this can happen if a login attempt failed + delete sAvatarSkeletonInfo; + } + sAvatarSkeletonInfo = new LLVOAvatarSkeletonInfo; + + LLXUIParser parser; + parser.readXUI(skeleton_xml, *sAvatarSkeletonInfo, filename); + if (!sAvatarSkeletonInfo->validateBlock()) { - llerrs << "No root node found in avatar skeleton file: " << filename << llendl; - return FALSE; + llerrs << "Error parsing skeleton XML file: " << filename << llendl; } - if( !root->hasName( "linden_skeleton" ) ) + if( !skeleton_xml->hasName( "linden_skeleton" ) ) { llerrs << "Invalid avatar skeleton file header: " << filename << llendl; return FALSE; } - std::string version; - static LLStdStringHandle version_string = LLXmlTree::addAttributeString("version"); - if( !root->getFastAttributeString( version_string, version ) || (version != "1.0") ) + if (sAvatarSkeletonInfo->version() != "1.0") { - llerrs << "Invalid avatar skeleton file version: " << version << " in file: " << filename << llendl; + llerrs << "Invalid avatar skeleton file version: " << sAvatarSkeletonInfo->version() << " in file: " << filename << llendl; return FALSE; } @@ -1779,14 +1796,13 @@ BOOL LLVOAvatar::parseSkeletonFile(const std::string& filename) //----------------------------------------------------------------------------- // setupBone() -//----------------------------------------------------------------------------- -BOOL LLVOAvatar::setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent, S32 &volume_num, S32 &joint_num) +//----------------------------------------------------------- +BOOL LLVOAvatar::setupBone(const LLVOAvatarChildJoint& info, LLViewerJoint* parent, S32 &volume_num, S32 &joint_num) { LLMemType mt(LLMemType::MTYPE_AVATAR); LLViewerJoint* joint = NULL; - - if (info->mIsJoint) + if (info.bone.isChosen()) { joint = (LLViewerJoint*)getCharacterJoint(joint_num); if (!joint) @@ -1794,7 +1810,23 @@ BOOL LLVOAvatar::setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent llwarns << "Too many bones" << llendl; return FALSE; } - joint->setName( info->mName ); + joint->setName( info.bone().name ); + joint->setPosition(info.bone().pos); + joint->setRotation(mayaQ(info.bone().rot().mV[VX], info.bone().rot().mV[VY], info.bone().rot().mV[VZ], LLQuaternion::XYZ)); + joint->setScale(info.bone().scale); + joint->setSkinOffset( info.bone().pivot ); + joint_num++; + + for (LLInitParam::ParamIterator<LLVOAvatarChildJoint>::const_iterator child_it = info.bone().children.begin(), + end_it = info.bone().children.end(); + child_it != end_it; + ++child_it) + { + if (!setupBone(*child_it, joint, volume_num, joint_num)) + { + return FALSE; + } + } } else // collision volume { @@ -1804,7 +1836,11 @@ BOOL LLVOAvatar::setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent return FALSE; } joint = (LLViewerJoint*)(&mCollisionVolumes[volume_num]); - joint->setName( info->mName ); + joint->setName( info.collision_volume.name); + joint->setPosition(info.collision_volume.pos); + joint->setRotation(mayaQ(info.collision_volume.rot().mV[VX], info.collision_volume.rot().mV[VY], info.collision_volume.rot().mV[VZ], LLQuaternion::XYZ)); + joint->setScale(info.collision_volume.scale); + volume_num++; } // add to parent @@ -1813,34 +1849,8 @@ BOOL LLVOAvatar::setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent parent->addChild( joint ); } - joint->setPosition(info->mPos); - joint->setRotation(mayaQ(info->mRot.mV[VX], info->mRot.mV[VY], - info->mRot.mV[VZ], LLQuaternion::XYZ)); - joint->setScale(info->mScale); - joint->setDefaultFromCurrentXform(); - if (info->mIsJoint) - { - joint->setSkinOffset( info->mPivot ); - joint_num++; - } - else // collision volume - { - volume_num++; - } - - // setup children - LLVOAvatarBoneInfo::child_list_t::const_iterator iter; - for (iter = info->mChildList.begin(); iter != info->mChildList.end(); ++iter) - { - LLVOAvatarBoneInfo *child_info = *iter; - if (!setupBone(child_info, joint, volume_num, joint_num)) - { - return FALSE; - } - } - return TRUE; } @@ -1854,36 +1864,32 @@ BOOL LLVOAvatar::buildSkeleton(const LLVOAvatarSkeletonInfo *info) //------------------------------------------------------------------------- // allocate joints //------------------------------------------------------------------------- - if (!allocateCharacterJoints(info->mNumBones)) + if (!allocateCharacterJoints(info->num_bones)) { - llerrs << "Can't allocate " << info->mNumBones << " joints" << llendl; + llerrs << "Can't allocate " << info->num_bones() << " joints" << llendl; return FALSE; } //------------------------------------------------------------------------- // allocate volumes //------------------------------------------------------------------------- - if (info->mNumCollisionVolumes) + if (info->num_collision_volumes) { - if (!allocateCollisionVolumes(info->mNumCollisionVolumes)) + if (!allocateCollisionVolumes(info->num_collision_volumes)) { - llerrs << "Can't allocate " << info->mNumCollisionVolumes << " collision volumes" << llendl; + llerrs << "Can't allocate " << info->num_collision_volumes() << " collision volumes" << llendl; return FALSE; } } S32 current_joint_num = 0; S32 current_volume_num = 0; - LLVOAvatarSkeletonInfo::bone_info_list_t::const_iterator iter; - for (iter = info->mBoneInfoList.begin(); iter != info->mBoneInfoList.end(); ++iter) + + if (!setupBone(info->skeleton_root, NULL, current_volume_num, current_joint_num)) { - LLVOAvatarBoneInfo *info = *iter; - if (!setupBone(info, NULL, current_volume_num, current_joint_num)) - { llerrs << "Error parsing bone in skeleton file" << llendl; return FALSE; } - } return TRUE; } @@ -2096,15 +2102,15 @@ void LLVOAvatar::releaseMeshData() LLFace* facep = mDrawable->getFace(0); if (facep) { - facep->setSize(0, 0); - for(S32 i = mNumInitFaces ; i < mDrawable->getNumFaces(); i++) - { - facep = mDrawable->getFace(i); + facep->setSize(0, 0); + for(S32 i = mNumInitFaces ; i < mDrawable->getNumFaces(); i++) + { + facep = mDrawable->getFace(i); if (facep) { - facep->setSize(0, 0); - } - } + facep->setSize(0, 0); + } + } } } @@ -2353,11 +2359,11 @@ U32 LLVOAvatar::processUpdateMessage(LLMessageSystem *mesgsys, U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp); // Print out arrival information once we have name of avatar. - if (has_name && getNVPair("FirstName")) - { - mDebugExistenceTimer.reset(); + if (has_name && getNVPair("FirstName")) + { + mDebugExistenceTimer.reset(); debugAvatarRezTime("AvatarRezArrivedNotification","avatar arrived"); - } + } if(retval & LLViewerObject::INVALID_UPDATE) { @@ -2525,6 +2531,7 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) { +#ifdef XXX_STINSON_CHUI_REWORK bool render_visualizer = voice_enabled; // Don't render the user's own voice visualizer when in mouselook, or when opening the mic is disabled. @@ -2537,6 +2544,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) } mVoiceVisualizer->setVoiceEnabled(render_visualizer); +#endif // XXX_STINSON_CHUI_REWORK if ( voice_enabled ) { @@ -2612,6 +2620,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) } } +#ifdef XXX_STINSON_CHUI_REWORK //-------------------------------------------------------------------------------------------- // here we get the approximate head position and set as sound source for the voice symbol // (the following version uses a tweak of "mHeadOffset" which handle sitting vs. standing) @@ -2629,6 +2638,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) tagPos[VZ] += ( mBodySize[VZ] + 0.125f ); mVoiceVisualizer->setVoiceSourceWorldPosition( tagPos ); } +#endif // XXX_STINSON_CHUI_REWORK }//if ( voiceEnabled ) } @@ -2868,8 +2878,8 @@ void LLVOAvatar::idleUpdateLoadingEffect() { LL_INFOS("Avatar") << avString() << "self isFullyLoaded, mFirstFullyVisible" << LL_ENDL; mFirstFullyVisible = FALSE; - LLAppearanceMgr::instance().onFirstFullyVisible(); - } + LLAppearanceMgr::instance().onFirstFullyVisible(); + } if (isFullyLoaded() && mFirstFullyVisible && !isSelf()) { LL_INFOS("Avatar") << avString() << "other isFullyLoaded, mFirstFullyVisible" << LL_ENDL; @@ -3017,43 +3027,43 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last) return; } - BOOL new_name = FALSE; - if (visible_chat != mVisibleChat) - { - mVisibleChat = visible_chat; - new_name = TRUE; - } - - if (sRenderGroupTitles != mRenderGroupTitles) - { - mRenderGroupTitles = sRenderGroupTitles; - new_name = TRUE; - } - - // First Calculate Alpha - // If alpha > 0, create mNameText if necessary, otherwise delete it - F32 alpha = 0.f; - if (mAppAngle > 5.f) - { - const F32 START_FADE_TIME = NAME_SHOW_TIME - FADE_DURATION; - if (!visible_chat && sRenderName == RENDER_NAME_FADE && time_visible > START_FADE_TIME) + BOOL new_name = FALSE; + if (visible_chat != mVisibleChat) { - alpha = 1.f - (time_visible - START_FADE_TIME) / FADE_DURATION; + mVisibleChat = visible_chat; + new_name = TRUE; } - else + + if (sRenderGroupTitles != mRenderGroupTitles) { - // ...not fading, full alpha - alpha = 1.f; + mRenderGroupTitles = sRenderGroupTitles; + new_name = TRUE; } - } - else if (mAppAngle > 2.f) - { - // far away is faded out also - alpha = (mAppAngle-2.f)/3.f; - } + + // First Calculate Alpha + // If alpha > 0, create mNameText if necessary, otherwise delete it + F32 alpha = 0.f; + if (mAppAngle > 5.f) + { + const F32 START_FADE_TIME = NAME_SHOW_TIME - FADE_DURATION; + if (!visible_chat && sRenderName == RENDER_NAME_FADE && time_visible > START_FADE_TIME) + { + alpha = 1.f - (time_visible - START_FADE_TIME) / FADE_DURATION; + } + else + { + // ...not fading, full alpha + alpha = 1.f; + } + } + else if (mAppAngle > 2.f) + { + // far away is faded out also + alpha = (mAppAngle-2.f)/3.f; + } if (alpha <= 0.f) - { + { if (mNameText) { mNameText->markDead(); @@ -3063,22 +3073,21 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last) return; } - if (!mNameText) - { + if (!mNameText) + { mNameText = static_cast<LLHUDNameTag*>( LLHUDObject::addHUDObject( - LLHUDObject::LL_HUD_NAME_TAG) ); + LLHUDObject::LL_HUD_NAME_TAG) ); //mNameText->setMass(10.f); - mNameText->setSourceObject(this); + mNameText->setSourceObject(this); mNameText->setVertAlignment(LLHUDNameTag::ALIGN_VERT_TOP); - mNameText->setVisibleOffScreen(TRUE); - mNameText->setMaxLines(11); - mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f); - sNumVisibleChatBubbles++; - new_name = TRUE; - } + mNameText->setVisibleOffScreen(TRUE); + mNameText->setMaxLines(11); + mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f); + sNumVisibleChatBubbles++; + new_name = TRUE; + } - LLVector3 name_position = idleUpdateNameTagPosition(root_pos_last); - mNameText->setPositionAgent(name_position); + idleUpdateNameTagPosition(root_pos_last); idleUpdateNameTagText(new_name); idleUpdateNameTagAlpha(new_name, alpha); } @@ -3166,7 +3175,7 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) // trim last ", " line.resize( line.length() - 2 ); addNameTagLine(line, name_tag_color, LLFontGL::NORMAL, - LLFontGL::getFontSansSerifSmall()); + LLFontGL::getFontSansSerifSmall()); } if (sRenderGroupTitles @@ -3175,7 +3184,7 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) std::string title_str = title->getString(); LLStringFn::replace_ascii_controlchars(title_str,LL_UNKNOWN_CHAR); addNameTagLine(title_str, name_tag_color, LLFontGL::NORMAL, - LLFontGL::getFontSansSerifSmall()); + LLFontGL::getFontSansSerifSmall()); } static LLUICachedControl<bool> show_display_names("NameTagShowDisplayNames"); @@ -3189,14 +3198,14 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) // ...call this function back when the name arrives // and force a rebuild LLAvatarNameCache::get(getID(), - boost::bind(&LLVOAvatar::clearNameTag, this)); + boost::bind(&LLVOAvatar::clearNameTag, this)); } // Might be blank if name not available yet, that's OK if (show_display_names) { addNameTagLine(av_name.mDisplayName, name_tag_color, LLFontGL::NORMAL, - LLFontGL::getFontSansSerif()); + LLFontGL::getFontSansSerif()); } // Suppress SLID display if display name matches exactly (ugh) if (show_usernames && !av_name.mIsDisplayNameDefault) @@ -3204,14 +3213,13 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) // *HACK: Desaturate the color LLColor4 username_color = name_tag_color * 0.83f; addNameTagLine(av_name.mUsername, username_color, LLFontGL::NORMAL, - LLFontGL::getFontSansSerifSmall()); + LLFontGL::getFontSansSerifSmall()); } } else { const LLFontGL* font = LLFontGL::getFontSansSerif(); - std::string full_name = - LLCacheName::buildFullName( firstname->getString(), lastname->getString() ); + std::string full_name = LLCacheName::buildFullName( firstname->getString(), lastname->getString() ); addNameTagLine(full_name, name_tag_color, LLFontGL::NORMAL, font); } @@ -3231,7 +3239,7 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) mNameText->setFont(LLFontGL::getFontSansSerif()); mNameText->setTextAlignment(LLHUDNameTag::ALIGN_TEXT_LEFT); mNameText->setFadeDistance(CHAT_NORMAL_RADIUS * 2.f, 5.f); - + char line[MAX_STRING]; /* Flawfinder: ignore */ line[0] = '\0'; std::deque<LLChat>::iterator chat_iter = mChats.begin(); @@ -3251,13 +3259,13 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) LLFontGL::StyleFlags style; switch(chat_iter->mChatType) { - case CHAT_TYPE_WHISPER: + case CHAT_TYPE_WHISPER: style = LLFontGL::ITALIC; break; - case CHAT_TYPE_SHOUT: + case CHAT_TYPE_SHOUT: style = LLFontGL::BOLD; break; - default: + default: style = LLFontGL::NORMAL; break; } @@ -3284,13 +3292,13 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) S32 dot_count = (llfloor(mTypingTimer.getElapsedTimeF32() * 3.f) + 2) % 3 + 1; switch(dot_count) { - case 1: + case 1: mNameText->addLine(".", new_chat); break; - case 2: + case 2: mNameText->addLine("..", new_chat); break; - case 3: + case 3: mNameText->addLine("...", new_chat); break; } @@ -3354,34 +3362,45 @@ void LLVOAvatar::invalidateNameTags() if (avatar->isDead()) continue; avatar->clearNameTag(); - } } // Compute name tag position during idle update -LLVector3 LLVOAvatar::idleUpdateNameTagPosition(const LLVector3& root_pos_last) +void LLVOAvatar::idleUpdateNameTagPosition(const LLVector3& root_pos_last) { LLQuaternion root_rot = mRoot.getWorldRotation(); + LLQuaternion inv_root_rot = ~root_rot; LLVector3 pixel_right_vec; LLVector3 pixel_up_vec; LLViewerCamera::getInstance()->getPixelVectors(root_pos_last, pixel_up_vec, pixel_right_vec); LLVector3 camera_to_av = root_pos_last - LLViewerCamera::getInstance()->getOrigin(); camera_to_av.normalize(); - LLVector3 local_camera_at = camera_to_av * ~root_rot; + LLVector3 local_camera_at = camera_to_av * inv_root_rot; LLVector3 local_camera_up = camera_to_av % LLViewerCamera::getInstance()->getLeftAxis(); local_camera_up.normalize(); - local_camera_up = local_camera_up * ~root_rot; + local_camera_up = local_camera_up * inv_root_rot; + + LLVector3 avatar_ellipsoid(mBodySize.mV[VX] * 0.4f, + mBodySize.mV[VY] * 0.4f, + mBodySize.mV[VZ] * NAMETAG_VERT_OFFSET_WEIGHT); - local_camera_up.scaleVec(mBodySize * 0.5f); - local_camera_at.scaleVec(mBodySize * 0.5f); + local_camera_up.scaleVec(avatar_ellipsoid); + local_camera_at.scaleVec(avatar_ellipsoid); - LLVector3 name_position = mRoot.getWorldPosition(); - name_position[VZ] -= mPelvisToFoot; - name_position[VZ] += (mBodySize[VZ]* 0.55f); + LLVector3 head_offset = (mHeadp->getLastWorldPosition() - mRoot.getLastWorldPosition()) * inv_root_rot; + + if (dist_vec(head_offset, mTargetRootToHeadOffset) > NAMETAG_UPDATE_THRESHOLD) + { + mTargetRootToHeadOffset = head_offset; + } + + mCurRootToHeadOffset = lerp(mCurRootToHeadOffset, mTargetRootToHeadOffset, LLCriticalDamp::getInterpolant(0.2f)); + + LLVector3 name_position = mRoot.getLastWorldPosition() + (mCurRootToHeadOffset * root_rot); name_position += (local_camera_up * root_rot) - (projected_vec(local_camera_at * root_rot, camera_to_av)); - name_position += pixel_up_vec * 15.f; + name_position += pixel_up_vec * NAMETAG_VERTICAL_SCREEN_OFFSET; - return name_position; + mNameText->setPositionAgent(name_position); } void LLVOAvatar::idleUpdateNameTagAlpha(BOOL new_name, F32 alpha) @@ -3454,9 +3473,9 @@ bool LLVOAvatar::isVisuallyMuted() const static LLCachedControl<U32> max_attachment_bytes(gSavedSettings, "RenderAutoMuteByteLimit"); static LLCachedControl<F32> max_attachment_area(gSavedSettings, "RenderAutoMuteSurfaceAreaLimit"); - return LLMuteList::getInstance()->isMuted(getID()) || - (mAttachmentGeometryBytes > max_attachment_bytes && max_attachment_bytes > 0) || - (mAttachmentSurfaceArea > max_attachment_area && max_attachment_area > 0.f); + return LLMuteList::getInstance()->isMuted(getID()) + || (mAttachmentGeometryBytes > max_attachment_bytes && max_attachment_bytes > 0) + || (mAttachmentSurfaceArea > max_attachment_area && max_attachment_area > 0.f); } //------------------------------------------------------------------------ @@ -3497,8 +3516,6 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) } } - LLVector3d root_pos_global; - if (!mIsBuilt) { return FALSE; @@ -3513,7 +3530,6 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) mTimeVisible.reset(); } - //-------------------------------------------------------------------- // the rest should only be done occasionally for far away avatars //-------------------------------------------------------------------- @@ -3916,10 +3932,6 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) if ( playSound ) { -// F32 gain = clamp_rescale( mSpeedAccum, -// AUDIO_STEP_LO_SPEED, AUDIO_STEP_HI_SPEED, -// AUDIO_STEP_LO_GAIN, AUDIO_STEP_HI_GAIN ); - const F32 STEP_VOLUME = 0.1f; const LLUUID& step_sound_id = getStepSound(); @@ -4136,13 +4148,6 @@ void LLVOAvatar::updateVisibility() { releaseMeshData(); } - // this breaks off-screen chat bubbles - //if (mNameText) - //{ - // mNameText->markDead(); - // mNameText = NULL; - // sNumVisibleChatBubbles--; - //} } mVisible = visible; @@ -4158,46 +4163,6 @@ bool LLVOAvatar::shouldAlphaMask() } -U32 LLVOAvatar::renderSkinnedAttachments() -{ - /*U32 num_indices = 0; - - const U32 data_mask = LLVertexBuffer::MAP_VERTEX | - LLVertexBuffer::MAP_NORMAL | - LLVertexBuffer::MAP_TEXCOORD0 | - LLVertexBuffer::MAP_COLOR | - LLVertexBuffer::MAP_WEIGHT4; - - for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); - iter != mAttachmentPoints.end(); - ++iter) - { - LLViewerJointAttachment* attachment = iter->second; - for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); - attachment_iter != attachment->mAttachedObjects.end(); - ++attachment_iter) - { - const LLViewerObject* attached_object = (*attachment_iter); - if (attached_object && !attached_object->isHUDAttachment()) - { - const LLDrawable* drawable = attached_object->mDrawable; - if (drawable) - { - for (S32 i = 0; i < drawable->getNumFaces(); ++i) - { - LLFace* face = drawable->getFace(i); - if (face->isState(LLFace::RIGGED)) - { - - } - } - } - } - - return num_indices;*/ - return 0; -} - //----------------------------------------------------------------------------- // renderSkinned() //----------------------------------------------------------------------------- @@ -4218,11 +4183,11 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass) { //LOD changed or new mesh created, allocate new vertex buffer if needed if (needs_rebuild || mDirtyMesh >= 2 || mVisibilityRank <= 4) { - updateMeshData(); + updateMeshData(); mDirtyMesh = 0; - mNeedsSkin = TRUE; - mDrawable->clearState(LLDrawable::REBUILD_GEOMETRY); - } + mNeedsSkin = TRUE; + mDrawable->clearState(LLDrawable::REBUILD_GEOMETRY); + } } if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_AVATAR) <= 0) @@ -4251,13 +4216,13 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass) if (face) { LLVertexBuffer* vb = face->getVertexBuffer(); - if (vb) - { - vb->flush(); - } + if (vb) + { + vb->flush(); } } } + } else { mNeedsSkin = FALSE; @@ -5900,7 +5865,6 @@ BOOL LLVOAvatar::updateJointLODs() F32 avatar_num_factor = clamp_rescale((F32)sNumVisibleAvatars, 8, 25, 1.f, avatar_num_min_factor); F32 area_scale = 0.16f; - { if (isSelf()) { if(gAgentCamera.cameraCustomizeAvatar() || gAgentCamera.cameraMouselook()) @@ -5930,7 +5894,6 @@ BOOL LLVOAvatar::updateJointLODs() dirtyMesh(2); return TRUE; } - } return FALSE; } @@ -6219,14 +6182,9 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO ) if ( pVObj ) { const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( pVObj->getVolume()->getParams().getSculptID(), pVObj ); - if ( pSkinData ) - { - const int jointCnt = pSkinData->mJointNames.size(); - bool fullRig = ( jointCnt>=20 ) ? true : false; - if ( fullRig ) - { - const int bindCnt = pSkinData->mAlternateBindMatrix.size(); - if ( bindCnt > 0 ) + if (pSkinData + && pSkinData->mJointNames.size() > 20 // full rig + && pSkinData->mAlternateBindMatrix.size() > 0) { LLVOAvatar::resetJointPositionsToDefault(); //Need to handle the repositioning of the cam, updating rig data etc during outfit editing @@ -6241,8 +6199,6 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO ) } } } - } -} //----------------------------------------------------------------------------- // detachObject() //----------------------------------------------------------------------------- @@ -6391,11 +6347,7 @@ void LLVOAvatar::getOffObject() at_axis.mV[VZ] = 0.f; at_axis.normalize(); gAgent.resetAxes(at_axis); - - //reset orientation -// mRoot.setRotation(avWorldRot); gAgentCamera.setThirdPersonHeadOffset(LLVector3(0.f, 0.f, 1.f)); - gAgentCamera.setSitCamera(LLUUID::null); } } @@ -6445,7 +6397,6 @@ LLColor4 LLVOAvatar::getGlobalColor( const std::string& color_name ) const } else { -// return LLColor4( .5f, .5f, .5f, .5f ); return LLColor4( 0.f, 1.f, 1.f, 1.f ); // good debugging color } } @@ -6610,8 +6561,8 @@ BOOL LLVOAvatar::processFullyLoadedChange(bool loading) mFullyLoaded = (mFullyLoadedTimer.getElapsedTimeF32() > PAUSE); - if (!mPreviousFullyLoaded && !loading && mFullyLoaded) - { + if (!mPreviousFullyLoaded && !loading && mFullyLoaded) + { debugAvatarRezTime("AvatarRezNotification","fully loaded"); } @@ -7204,10 +7155,6 @@ LLBBox LLVOAvatar::getHUDBBox() const return bbox; } -void LLVOAvatar::rebuildHUD() -{ -} - //----------------------------------------------------------------------------- // onFirstTEMessageReceived() //----------------------------------------------------------------------------- @@ -7333,7 +7280,10 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) && baked_index != BAKED_SKIRT) { setTEImage(mBakedTextureDatas[baked_index].mTextureIndex, - LLViewerTextureManager::getFetchedTexture(mBakedTextureDatas[baked_index].mLastTextureIndex, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE)); + LLViewerTextureManager::getFetchedTexture(mBakedTextureDatas[baked_index].mLastTextureIndex, + TRUE, + LLViewerTexture::BOOST_NONE, + LLViewerTexture::LOD_TEXTURE)); } } @@ -7592,7 +7542,7 @@ void LLVOAvatar::onInitialBakedTextureLoaded( BOOL success, LLViewerFetchedTextu LLUUID *avatar_idp = (LLUUID *)userdata; LLVOAvatar *selfp = (LLVOAvatar *)gObjectList.findObject(*avatar_idp); - + if (selfp) { LL_DEBUGS("Avatar") << selfp->avString() << "discard_level " << discard_level << " success " << success << " final " << final << LL_ENDL; @@ -7643,13 +7593,6 @@ void LLVOAvatar::onBakedTextureLoaded(BOOL success, // Called when baked texture is loaded and also when we start up with a baked texture void LLVOAvatar::useBakedTexture( const LLUUID& id ) { - - - /* if(id == head_baked->getID()) - mHeadBakedLoaded = TRUE; - mLastHeadBakedID = id; - mHeadMesh0.setTexture( head_baked ); - mHeadMesh1.setTexture( head_baked ); */ for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { LLViewerTexture* image_baked = getImage( mBakedTextureDatas[i].mTextureIndex, 0 ); @@ -7893,111 +7836,111 @@ LLVOAvatar::LLVOAvatarXmlInfo::~LLVOAvatarXmlInfo() std::for_each(mMorphMaskInfoList.begin(), mMorphMaskInfoList.end(), DeletePointer()); } -//----------------------------------------------------------------------------- -// LLVOAvatarBoneInfo::parseXml() -//----------------------------------------------------------------------------- -BOOL LLVOAvatarBoneInfo::parseXml(LLXmlTreeNode* node) -{ - if (node->hasName("bone")) - { - mIsJoint = TRUE; - static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name"); - if (!node->getFastAttributeString(name_string, mName)) - { - llwarns << "Bone without name" << llendl; - return FALSE; - } - } - else if (node->hasName("collision_volume")) - { - mIsJoint = FALSE; - static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name"); - if (!node->getFastAttributeString(name_string, mName)) - { - mName = "Collision Volume"; - } - } - else - { - llwarns << "Invalid node " << node->getName() << llendl; - return FALSE; - } - - static LLStdStringHandle pos_string = LLXmlTree::addAttributeString("pos"); - if (!node->getFastAttributeVector3(pos_string, mPos)) - { - llwarns << "Bone without position" << llendl; - return FALSE; - } - - static LLStdStringHandle rot_string = LLXmlTree::addAttributeString("rot"); - if (!node->getFastAttributeVector3(rot_string, mRot)) - { - llwarns << "Bone without rotation" << llendl; - return FALSE; - } - - static LLStdStringHandle scale_string = LLXmlTree::addAttributeString("scale"); - if (!node->getFastAttributeVector3(scale_string, mScale)) - { - llwarns << "Bone without scale" << llendl; - return FALSE; - } - - if (mIsJoint) - { - static LLStdStringHandle pivot_string = LLXmlTree::addAttributeString("pivot"); - if (!node->getFastAttributeVector3(pivot_string, mPivot)) - { - llwarns << "Bone without pivot" << llendl; - return FALSE; - } - } - - // parse children - LLXmlTreeNode* child; - for( child = node->getFirstChild(); child; child = node->getNextChild() ) - { - LLVOAvatarBoneInfo *child_info = new LLVOAvatarBoneInfo; - if (!child_info->parseXml(child)) - { - delete child_info; - return FALSE; - } - mChildList.push_back(child_info); - } - return TRUE; -} - -//----------------------------------------------------------------------------- -// LLVOAvatarSkeletonInfo::parseXml() -//----------------------------------------------------------------------------- -BOOL LLVOAvatarSkeletonInfo::parseXml(LLXmlTreeNode* node) -{ - static LLStdStringHandle num_bones_string = LLXmlTree::addAttributeString("num_bones"); - if (!node->getFastAttributeS32(num_bones_string, mNumBones)) - { - llwarns << "Couldn't find number of bones." << llendl; - return FALSE; - } - - static LLStdStringHandle num_collision_volumes_string = LLXmlTree::addAttributeString("num_collision_volumes"); - node->getFastAttributeS32(num_collision_volumes_string, mNumCollisionVolumes); - - LLXmlTreeNode* child; - for( child = node->getFirstChild(); child; child = node->getNextChild() ) - { - LLVOAvatarBoneInfo *info = new LLVOAvatarBoneInfo; - if (!info->parseXml(child)) - { - delete info; - llwarns << "Error parsing bone in skeleton file" << llendl; - return FALSE; - } - mBoneInfoList.push_back(info); - } - return TRUE; -} +////----------------------------------------------------------------------------- +//// LLVOAvatarBoneInfo::parseXml() +////----------------------------------------------------------------------------- +//BOOL LLVOAvatarBoneInfo::parseXml(LLXmlTreeNode* node) +//{ +// if (node->hasName("bone")) +// { +// mIsJoint = TRUE; +// static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name"); +// if (!node->getFastAttributeString(name_string, mName)) +// { +// llwarns << "Bone without name" << llendl; +// return FALSE; +// } +// } +// else if (node->hasName("collision_volume")) +// { +// mIsJoint = FALSE; +// static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name"); +// if (!node->getFastAttributeString(name_string, mName)) +// { +// mName = "Collision Volume"; +// } +// } +// else +// { +// llwarns << "Invalid node " << node->getName() << llendl; +// return FALSE; +// } +// +// static LLStdStringHandle pos_string = LLXmlTree::addAttributeString("pos"); +// if (!node->getFastAttributeVector3(pos_string, mPos)) +// { +// llwarns << "Bone without position" << llendl; +// return FALSE; +// } +// +// static LLStdStringHandle rot_string = LLXmlTree::addAttributeString("rot"); +// if (!node->getFastAttributeVector3(rot_string, mRot)) +// { +// llwarns << "Bone without rotation" << llendl; +// return FALSE; +// } +// +// static LLStdStringHandle scale_string = LLXmlTree::addAttributeString("scale"); +// if (!node->getFastAttributeVector3(scale_string, mScale)) +// { +// llwarns << "Bone without scale" << llendl; +// return FALSE; +// } +// +// if (mIsJoint) +// { +// static LLStdStringHandle pivot_string = LLXmlTree::addAttributeString("pivot"); +// if (!node->getFastAttributeVector3(pivot_string, mPivot)) +// { +// llwarns << "Bone without pivot" << llendl; +// return FALSE; +// } +// } +// +// // parse children +// LLXmlTreeNode* child; +// for( child = node->getFirstChild(); child; child = node->getNextChild() ) +// { +// LLVOAvatarBoneInfo *child_info = new LLVOAvatarBoneInfo; +// if (!child_info->parseXml(child)) +// { +// delete child_info; +// return FALSE; +// } +// mChildList.push_back(child_info); +// } +// return TRUE; +//} +// +////----------------------------------------------------------------------------- +//// LLVOAvatarSkeletonInfo::parseXml() +////----------------------------------------------------------------------------- +//BOOL LLVOAvatarSkeletonInfo::parseXml(LLXmlTreeNode* node) +//{ +// static LLStdStringHandle num_bones_string = LLXmlTree::addAttributeString("num_bones"); +// if (!node->getFastAttributeS32(num_bones_string, mNumBones)) +// { +// llwarns << "Couldn't find number of bones." << llendl; +// return FALSE; +// } +// +// static LLStdStringHandle num_collision_volumes_string = LLXmlTree::addAttributeString("num_collision_volumes"); +// node->getFastAttributeS32(num_collision_volumes_string, mNumCollisionVolumes); +// +// LLXmlTreeNode* child; +// for( child = node->getFirstChild(); child; child = node->getNextChild() ) +// { +// LLVOAvatarBoneInfo *info = new LLVOAvatarBoneInfo; +// if (!info->parseXml(child)) +// { +// delete info; +// llwarns << "Error parsing bone in skeleton file" << llendl; +// return FALSE; +// } +// mBoneInfoList.push_back(info); +// } +// return TRUE; +//} //----------------------------------------------------------------------------- // parseXmlSkeletonNode(): parses <skeleton> nodes from XML tree diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index f5692bb52f..3dffb9d029 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -67,8 +67,9 @@ class LLVoiceVisualizer; class LLHUDNameTag; class LLHUDEffectSpiral; class LLTexGlobalColor; -class LLVOAvatarBoneInfo; -class LLVOAvatarSkeletonInfo; +struct LLVOAvatarBoneInfo; +struct LLVOAvatarChildJoint; +struct LLVOAvatarSkeletonInfo; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // LLVOAvatar @@ -243,7 +244,7 @@ public: void idleUpdateWindEffect(); void idleUpdateNameTag(const LLVector3& root_pos_last); void idleUpdateNameTagText(BOOL new_name); - LLVector3 idleUpdateNameTagPosition(const LLVector3& root_pos_last); + void idleUpdateNameTagPosition(const LLVector3& root_pos_last); void idleUpdateNameTagAlpha(BOOL new_name, F32 alpha); LLColor4 getNameTagColor(bool is_friend); void clearNameTag(); @@ -363,6 +364,8 @@ public: F32 mLastPelvisToFoot; F32 mPelvisFixup; F32 mLastPelvisFixup; + LLVector3 mCurRootToHeadOffset; + LLVector3 mTargetRootToHeadOffset; LLVector3 mHeadOffset; // current head position LLViewerJoint mRoot; @@ -375,7 +378,7 @@ protected: void buildCharacter(); virtual BOOL loadAvatar(); - BOOL setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent, S32 ¤t_volume_num, S32 ¤t_joint_num); + BOOL setupBone(const LLVOAvatarChildJoint& info, LLViewerJoint* parent, S32 ¤t_volume_num, S32 ¤t_joint_num); BOOL buildSkeleton(const LLVOAvatarSkeletonInfo *info); private: BOOL mIsBuilt; // state of deferred character building @@ -419,7 +422,7 @@ public: //-------------------------------------------------------------------- private: static LLXmlTree sXMLTree; // avatar config file - static LLXmlTree sSkeletonXMLTree; // avatar skeleton file + static LLXMLNodePtr sSkeletonXMLTree; // avatar skeleton file /** Skeleton ** ** @@ -437,7 +440,6 @@ public: U32 renderRigid(); U32 renderSkinned(EAvatarRenderPass pass); F32 getLastSkinTime() { return mLastSkinTime; } - U32 renderSkinnedAttachments(); U32 renderTransparent(BOOL first_pass); void renderCollisionVolumes(); static void deleteCachedImages(bool clearAll=true); @@ -786,7 +788,6 @@ public: public: BOOL hasHUDAttachment() const; LLBBox getHUDBBox() const; - void rebuildHUD(); void resetHUDAttachments(); BOOL canAttachMoreObjects() const; BOOL canAttachMoreObjects(U32 n) const; diff --git a/indra/newview/llvoicevisualizer.cpp b/indra/newview/llvoicevisualizer.cpp index 47060720e7..d380a8672f 100644 --- a/indra/newview/llvoicevisualizer.cpp +++ b/indra/newview/llvoicevisualizer.cpp @@ -45,6 +45,7 @@ //29de489d-0491-fb00-7dab-f9e686d31e83 +#ifdef XXX_STINSON_CHUI_REWORK //-------------------------------------------------------------------------------------- // sound symbol constants //-------------------------------------------------------------------------------------- @@ -60,6 +61,7 @@ const F32 BASE_BRIGHTNESS = 0.7f; // gray level of the voice indicator when qu const F32 DOT_SIZE = 0.05f; // size of the dot billboard texture const F32 DOT_OPACITY = 0.7f; // how opaque the dot is const F32 WAVE_MOTION_RATE = 1.5f; // scalar applied to consecutive waves as a function of speaking amplitude +#endif // XXX_STINSON_CHUI_REWORK //-------------------------------------------------------------------------------------- // gesticulation constants @@ -67,22 +69,13 @@ const F32 WAVE_MOTION_RATE = 1.5f; // scalar applied to consecutive waves as a const F32 DEFAULT_MINIMUM_GESTICULATION_AMPLITUDE = 0.2f; const F32 DEFAULT_MAXIMUM_GESTICULATION_AMPLITUDE = 1.0f; +#ifdef XXX_STINSON_CHUI_REWORK //-------------------------------------------------------------------------------------- // other constants //-------------------------------------------------------------------------------------- const F32 ONE_HALF = 1.0f; // to clarify intent and reduce magic numbers in the code. const LLVector3 WORLD_UPWARD_DIRECTION = LLVector3( 0.0f, 0.0f, 1.0f ); // Z is up in SL - - -//------------------------------------------------------------------ -// handles parameter updates -//------------------------------------------------------------------ -static bool handleVoiceVisualizerPrefsChanged(const LLSD& newvalue) -{ - // Note: Ignore the specific event value, we look up the ones we want - LLVoiceVisualizer::setPreferences(); - return true; -} +#endif // XXX_STINSON_CHUI_REWORK //------------------------------------------------------------------ // Initialize the statics @@ -105,12 +98,28 @@ F32 LLVoiceVisualizer::sAahPowerTransfersf = 0.0f; //----------------------------------------------- // constructor //----------------------------------------------- +#ifdef XXX_STINSON_CHUI_REWORK LLVoiceVisualizer::LLVoiceVisualizer( const U8 type ) -:LLHUDEffect( type ) + : LLHUDEffect(type) +#else // XXX_STINSON_CHUI_REWORK +LLVoiceVisualizer::LLVoiceVisualizer() + : LLRefCount(), + mTimer(), + mStartTime(0.0), + mCurrentlySpeaking(false), + mSpeakingAmplitude(0.0f), + mMaxGesticulationAmplitude(DEFAULT_MAXIMUM_GESTICULATION_AMPLITUDE), + mMinGesticulationAmplitude(DEFAULT_MINIMUM_GESTICULATION_AMPLITUDE) +#endif // XXX_STINSON_CHUI_REWORK { +#ifdef XXX_STINSON_CHUI_REWORK mCurrentTime = mTimer.getTotalSeconds(); mPreviousTime = mCurrentTime; mStartTime = mCurrentTime; +#else // XXX_STINSON_CHUI_REWORK + mStartTime = mTimer.getTotalSeconds(); +#endif // XXX_STINSON_CHUI_REWORK +#ifdef XXX_STINSON_CHUI_REWORK mVoiceSourceWorldPosition = LLVector3( 0.0f, 0.0f, 0.0f ); mSpeakingAmplitude = 0.0f; mCurrentlySpeaking = false; @@ -119,9 +128,11 @@ LLVoiceVisualizer::LLVoiceVisualizer( const U8 type ) mMaxGesticulationAmplitude = DEFAULT_MAXIMUM_GESTICULATION_AMPLITUDE; mSoundSymbol.mActive = true; mSoundSymbol.mPosition = LLVector3( 0.0f, 0.0f, 0.0f ); +#endif // XXX_STINSON_CHUI_REWORK mTimer.reset(); +#ifdef XXX_STINSON_CHUI_REWORK const char* sound_level_img[] = { "voice_meter_dot.j2c", @@ -143,6 +154,7 @@ LLVoiceVisualizer::LLVoiceVisualizer( const U8 type ) } mSoundSymbol.mTexture[0]->setFilteringOption(LLTexUnit::TFO_ANISOTROPIC); +#endif // XXX_STINSON_CHUI_REWORK // The first instance loads the initial state from prefs. if (!sPrefsInitialized) @@ -150,18 +162,19 @@ LLVoiceVisualizer::LLVoiceVisualizer( const U8 type ) setPreferences(); // Set up our listener to get updates on all prefs values we care about. - gSavedSettings.getControl("LipSyncEnabled")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _2)); - gSavedSettings.getControl("LipSyncOohAahRate")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _2)); - gSavedSettings.getControl("LipSyncOoh")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _2)); - gSavedSettings.getControl("LipSyncAah")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _2)); - gSavedSettings.getControl("LipSyncOohPowerTransfer")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _2)); - gSavedSettings.getControl("LipSyncAahPowerTransfer")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _2)); + gSavedSettings.getControl("LipSyncEnabled")->getSignal()->connect(boost::bind(&LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged, _2)); + gSavedSettings.getControl("LipSyncOohAahRate")->getSignal()->connect(boost::bind(&LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged, _2)); + gSavedSettings.getControl("LipSyncOoh")->getSignal()->connect(boost::bind(&LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged, _2)); + gSavedSettings.getControl("LipSyncAah")->getSignal()->connect(boost::bind(&LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged, _2)); + gSavedSettings.getControl("LipSyncOohPowerTransfer")->getSignal()->connect(boost::bind(&LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged, _2)); + gSavedSettings.getControl("LipSyncAahPowerTransfer")->getSignal()->connect(boost::bind(&LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged, _2)); sPrefsInitialized = true; } }//--------------------------------------------------- +#ifdef XXX_STINSON_CHUI_REWORK //--------------------------------------------------- void LLVoiceVisualizer::setMinGesticulationAmplitude( F32 m ) { @@ -182,13 +195,16 @@ void LLVoiceVisualizer::setVoiceEnabled( bool v ) mVoiceEnabled = v; }//--------------------------------------------------- +#endif // XXX_STINSON_CHUI_REWORK //--------------------------------------------------- void LLVoiceVisualizer::setStartSpeaking() { mStartTime = mTimer.getTotalSeconds(); mCurrentlySpeaking = true; +#ifdef XXX_STINSON_CHUI_REWORK mSoundSymbol.mActive = true; +#endif // XXX_STINSON_CHUI_REWORK }//--------------------------------------------------- @@ -217,6 +233,15 @@ void LLVoiceVisualizer::setSpeakingAmplitude( F32 a ) }//--------------------------------------------------- +//------------------------------------------------------------------ +// handles parameter updates +//------------------------------------------------------------------ +bool LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged(const LLSD& newvalue) +{ + // Note: Ignore the specific event value, we look up the ones we want + LLVoiceVisualizer::setPreferences(); + return true; +} //--------------------------------------------------- void LLVoiceVisualizer::setPreferences( ) @@ -334,6 +359,7 @@ void LLVoiceVisualizer::lipSyncOohAah( F32& ooh, F32& aah ) }//--------------------------------------------------- +#ifdef XXX_STINSON_CHUI_REWORK //--------------------------------------------------- // this method is inherited from HUD Effect //--------------------------------------------------- @@ -526,16 +552,13 @@ void LLVoiceVisualizer::render() }//--------------------------------------------------- - - - - //--------------------------------------------------- void LLVoiceVisualizer::setVoiceSourceWorldPosition( const LLVector3 &p ) { mVoiceSourceWorldPosition = p; }//--------------------------------------------------- +#endif // XXX_STINSON_CHUI_REWORK //--------------------------------------------------- VoiceGesticulationLevel LLVoiceVisualizer::getCurrentGesticulationLevel() @@ -566,6 +589,7 @@ LLVoiceVisualizer::~LLVoiceVisualizer() }//---------------------------------------------- +#ifdef XXX_STINSON_CHUI_REWORK //--------------------------------------------------- // "packData" is inherited from HUDEffect //--------------------------------------------------- @@ -616,10 +640,4 @@ void LLVoiceVisualizer::markDead() LLHUDEffect::markDead(); }//------------------------------------------------------------------ - - - - - - - +#endif // XXX_STINSON_CHUI_REWORK diff --git a/indra/newview/llvoicevisualizer.h b/indra/newview/llvoicevisualizer.h index e434c7f3f1..5da592c48e 100644 --- a/indra/newview/llvoicevisualizer.h +++ b/indra/newview/llvoicevisualizer.h @@ -42,7 +42,12 @@ #ifndef LL_VOICE_VISUALIZER_H #define LL_VOICE_VISUALIZER_H +#define XXX_STINSON_CHUI_REWORK // temporarily re-enabling the in-world voice-dot +#ifdef XXX_STINSON_CHUI_REWORK #include "llhudeffect.h" +#else // XXX_STINSON_CHUI_REWORK +#include "llpointer.h" +#endif // XXX_STINSON_CHUI_REWORK //----------------------------------------------------------------------------------------------- // The values of voice gesticulation represent energy levels for avatar animation, based on @@ -60,34 +65,45 @@ enum VoiceGesticulationLevel NUM_VOICE_GESTICULATION_LEVELS }; +#ifdef XXX_STINSON_CHUI_REWORK const static int NUM_VOICE_SYMBOL_WAVES = 7; +#endif // XXX_STINSON_CHUI_REWORK //---------------------------------------------------- // LLVoiceVisualizer class //---------------------------------------------------- +#ifdef XXX_STINSON_CHUI_REWORK class LLVoiceVisualizer : public LLHUDEffect +#else // XXX_STINSON_CHUI_REWORK +class LLVoiceVisualizer : public LLRefCount +#endif // XXX_STINSON_CHUI_REWORK { //--------------------------------------------------- // public methods //--------------------------------------------------- public: - LLVoiceVisualizer ( const U8 type ); //constructor +#ifdef XXX_STINSON_CHUI_REWORK + LLVoiceVisualizer( const U8 type ); //constructor +#else // XXX_STINSON_CHUI_REWORK + LLVoiceVisualizer(); //constructor +#endif // XXX_STINSON_CHUI_REWORK ~LLVoiceVisualizer(); //destructor - - friend class LLHUDObject; +#ifdef XXX_STINSON_CHUI_REWORK void setVoiceSourceWorldPosition( const LLVector3 &p ); // this should be the position of the speaking avatar's head void setMinGesticulationAmplitude( F32 ); // the lower range of meaningful amplitude for setting gesticulation level void setMaxGesticulationAmplitude( F32 ); // the upper range of meaningful amplitude for setting gesticulation level +#endif // XXX_STINSON_CHUI_REWORK void setStartSpeaking(); // tell me when the av starts speaking +#ifdef XXX_STINSON_CHUI_REWORK void setVoiceEnabled( bool ); // tell me whether or not the user is voice enabled +#endif // XXX_STINSON_CHUI_REWORK void setSpeakingAmplitude( F32 ); // tell me how loud the av is speaking (ranges from 0 to 1) void setStopSpeaking(); // tell me when the av stops speaking bool getCurrentlySpeaking(); // the get for the above set VoiceGesticulationLevel getCurrentGesticulationLevel(); // based on voice amplitude, I'll give you the current "energy level" of avatar speech - static void setPreferences( ); - static void lipStringToF32s ( std::string& in_string, F32*& out_F32s, U32& count_F32s ); // convert a string of digits to an array of floats void lipSyncOohAah( F32& ooh, F32& aah ); +#ifdef XXX_STINSON_CHUI_REWORK void render(); // inherited from HUD Effect void packData(LLMessageSystem *mesgsys); // inherited from HUD Effect void unpackData(LLMessageSystem *mesgsys, S32 blocknum); // inherited from HUD Effect @@ -103,12 +119,17 @@ class LLVoiceVisualizer : public LLHUDEffect //---------------------------------------------------------------------------------------------- void setMaxGesticulationAmplitude(); void setMinGesticulationAmplitude(); +#endif // XXX_STINSON_CHUI_REWORK //--------------------------------------------------- // private members //--------------------------------------------------- private: - + static bool handleVoiceVisualizerPrefsChanged(const LLSD& newvalue); + static void setPreferences( ); + static void lipStringToF32s ( std::string& in_string, F32*& out_F32s, U32& count_F32s ); // convert a string of digits to an array of floats + +#ifdef XXX_STINSON_CHUI_REWORK struct SoundSymbol { F32 mWaveExpansion [ NUM_VOICE_SYMBOL_WAVES ]; @@ -119,15 +140,20 @@ class LLVoiceVisualizer : public LLHUDEffect bool mActive; LLVector3 mPosition; }; +#endif // XXX_STINSON_CHUI_REWORK LLFrameTimer mTimer; // so I can ask the current time in seconds F64 mStartTime; // time in seconds when speaking started +#ifdef XXX_STINSON_CHUI_REWORK F64 mCurrentTime; // current time in seconds, captured every step F64 mPreviousTime; // copy of "current time" from last frame SoundSymbol mSoundSymbol; // the sound symbol that appears over the avatar's head bool mVoiceEnabled; // if off, no rendering should happen +#endif // XXX_STINSON_CHUI_REWORK bool mCurrentlySpeaking; // is the user currently speaking? +#ifdef XXX_STINSON_CHUI_REWORK LLVector3 mVoiceSourceWorldPosition; // give this to me every step - I need it to update the sound symbol +#endif // XXX_STINSON_CHUI_REWORK F32 mSpeakingAmplitude; // this should be set as often as possible when the user is speaking F32 mMaxGesticulationAmplitude; // this is the upper-limit of the envelope of detectable gesticulation leves F32 mMinGesticulationAmplitude; // this is the lower-limit of the envelope of detectable gesticulation leves diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 820d1d73e1..f1bf4a6d75 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -3964,6 +3964,7 @@ void LLVivoxVoiceClient::messageEvent( session->mCallerID, session->mName.c_str(), message.c_str(), + false, LLStringUtil::null, // default arg IM_NOTHING_SPECIAL, // default arg 0, // default arg diff --git a/indra/newview/llvopartgroup.cpp b/indra/newview/llvopartgroup.cpp index 9cce68fff6..a88418a5a2 100644 --- a/indra/newview/llvopartgroup.cpp +++ b/indra/newview/llvopartgroup.cpp @@ -152,8 +152,8 @@ bool ll_is_part_idx_allocated(S32 idx, S32* start, S32* end) void LLVOPartGroup::freeVBSlot(S32 idx) { llassert(idx < LL_MAX_PARTICLE_COUNT && idx >= 0); - llassert(sVBSlotCursor > sVBSlotFree); - llassert(ll_is_part_idx_allocated(idx, sVBSlotCursor, sVBSlotFree+LL_MAX_PARTICLE_COUNT)); + //llassert(sVBSlotCursor > sVBSlotFree); + //llassert(ll_is_part_idx_allocated(idx, sVBSlotCursor, sVBSlotFree+LL_MAX_PARTICLE_COUNT)); if (sVBSlotCursor > sVBSlotFree) { diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 7140515e46..76c27aaa08 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -5206,11 +5206,6 @@ void LLPipeline::rebuildPools() } max_count--; } - - if (isAgentAvatarValid()) - { - gAgentAvatarp->rebuildHUD(); - } } void LLPipeline::addToQuickLookup( LLDrawPool* new_poolp ) @@ -5698,7 +5693,7 @@ void LLPipeline::calcNearbyLights(LLCamera& camera) // crazy cast so that we can overwrite the fade value // even though gcc enforces sets as const // (fade value doesn't affect sort so this is safe) - Light* farthest_light = ((Light*) (&(*(mNearbyLights.rbegin())))); + Light* farthest_light = (const_cast<Light*>(&(*(mNearbyLights.rbegin())))); if (light->dist < farthest_light->dist) { if (farthest_light->fade >= 0.f) @@ -6729,7 +6724,7 @@ void LLPipeline::resetVertexBuffers(LLDrawable* drawable) } void LLPipeline::resetVertexBuffers() -{ +{ mResetVertexBuffers = true; } diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index 9bf2922033..05230b8bd5 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -1,103 +1,103 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <colors> - <!-- Named Colors --> - <color - name="EmphasisColor" - value="0.38 0.694 0.573 1" /> - <color - name="EmphasisColor_13" - value="0.38 0.694 0.573 0.13" /> - <color - name="EmphasisColor_35" - value="0.38 0.694 0.573 0.35" /> - <color - name="White" - value="1 1 1 1" /> - <color - name="White_05" - value="1 1 1 0.05" /> - <color - name="White_10" - value="1 1 1 0.1" /> - <color - name="White_25" - value="1 1 1 0.25" /> - <color - name="White_50" - value="1 1 1 0.5" /> - <color - name="LtGray" - value="0.75 0.75 0.75 1" /> - <color - name="LtGray_35" - value="0.75 0.75 0.75 0.35" /> - <color - name="LtGray_50" - value="0.75 0.75 0.75 0.50" /> - <color - name="Gray" - value="0.5 0.5 0.5 1" /> - <color - name="DkGray" - value="0.125 0.125 0.125 1" /> - <color - name="DkGray_66" - value="0.125 0.125 0.125 .66" /> - <color - name="DkGray2" - value="0.169 0.169 0.169 1" /> - <color - name="MouseGray" - value="0.191 0.191 0.191 1" /> - <color - name="Black" - value="0 0 0 1" /> - <colork - name="Black_10" - value="0 0 0 0.1" /> - <color - name="Black_25" - value="0 0 0 0.25" /> - <color - name="Black_50" - value="0 0 0 0.5" /> - <color - name="FrogGreen" - value="0.26 0.345 0.263 1" /> - <color - name="Red" - value="1 0 0 1" /> - <color - name="Blue" - value="0 0 1 1" /> - <color - name="Yellow" - value="1 1 0 1" /> - <color - name="Green" - value="0 1 0 1" /> - <color - name="Transparent" - value="0 0 0 0" /> - <color - name="Purple" - value="1 0 1 1" /> - <color - name="Lime" - value=".8 1 .73 1" /> - <color - name="LtYellow" - value="1 1 .79 1" /> - <color - name="DrYellow" - value="1 0.86 0 1" /> - <color - name="LtOrange" - value="1 .85 .73 1" /> - <color - name="MdBlue" - value=".07 .38 .51 1" /> + <!-- Named Colors --> + <color + name="EmphasisColor" + value="0.38 0.694 0.573 1" /> + <color + name="EmphasisColor_13" + value="0.38 0.694 0.573 0.13" /> + <color + name="EmphasisColor_35" + value="0.38 0.694 0.573 0.35" /> + <color + name="White" + value="1 1 1 1" /> + <color + name="White_05" + value="1 1 1 0.05" /> + <color + name="White_10" + value="1 1 1 0.1" /> + <color + name="White_25" + value="1 1 1 0.25" /> + <color + name="White_50" + value="1 1 1 0.5" /> + <color + name="LtGray" + value="0.75 0.75 0.75 1" /> + <color + name="LtGray_35" + value="0.75 0.75 0.75 0.35" /> + <color + name="LtGray_50" + value="0.75 0.75 0.75 0.50" /> + <color + name="Gray" + value="0.5 0.5 0.5 1" /> + <color + name="DkGray" + value="0.125 0.125 0.125 1" /> + <color + name="DkGray_66" + value="0.125 0.125 0.125 .66" /> + <color + name="DkGray2" + value="0.169 0.169 0.169 1" /> + <color + name="MouseGray" + value="0.191 0.191 0.191 1" /> + <color + name="Black" + value="0 0 0 1" /> + <colork + name="Black_10" + value="0 0 0 0.1" /> + <color + name="Black_25" + value="0 0 0 0.25" /> + <color + name="Black_50" + value="0 0 0 0.5" /> + <color + name="FrogGreen" + value="0.26 0.345 0.263 1" /> + <color + name="Red" + value="1 0 0 1" /> + <color + name="Blue" + value="0 0 1 1" /> + <color + name="Yellow" + value="1 1 0 1" /> + <color + name="Green" + value="0 1 0 1" /> + <color + name="Transparent" + value="0 0 0 0" /> + <color + name="Purple" + value="1 0 1 1" /> + <color + name="Lime" + value=".8 1 .73 1" /> + <color + name="LtYellow" + value="1 1 .79 1" /> + <color + name="DrYellow" + value="1 0.86 0 1" /> + <color + name="LtOrange" + value="1 .85 .73 1" /> + <color + name="MdBlue" + value=".07 .38 .51 1" /> <color name="LtRed" value="1 0.2 0.2 1" /> @@ -115,527 +115,527 @@ value="0 0 1 0.8" /> <!-- This color name makes potentially unused colors show up bright purple. - Leave this here until all Unused? are removed below, otherwise - the viewer generates many warnings on startup. --> + Leave this here until all Unused? are removed below, otherwise + the viewer generates many warnings on startup. --> <color - name="Unused?" - value=".831 1 0 1" /> + name="Unused?" + value=".831 1 0 1" /> <!-- UI Definitions --> - <color - name="AccordionHeaderTextColor" - reference="LtGray" /> - <color - name="AgentChatColor" - reference="White" /> - <color - name="AlertBoxColor" - value="0.24 0.24 0.24 1" /> - <color - name="AlertCautionBoxColor" - value="1 0.82 0.46 1" /> - <color - name="AlertCautionTextColor" - reference="LtYellow" /> - <color - name="AvatarListItemIconDefaultColor" - reference="White" /> - <color - name="AvatarListItemIconOnlineColor" - reference="White" /> - <color - name="AvatarListItemIconOfflineColor" - value="0.5 0.5 0.5 0.5" /> - <color - name="AvatarListItemIconVoiceInvitedColor" - reference="AvatarListItemIconOfflineColor" /> - <color - name="AvatarListItemIconVoiceJoinedColor" - reference="AvatarListItemIconOnlineColor" /> - <color - name="AvatarListItemIconVoiceLeftColor" - reference="AvatarListItemIconOfflineColor" /> - <color - name="BadgeImageColor" - value="1.0 0.40 0.0 1.0" /> - <color - name="BadgeBorderColor" - value="0.9 0.9 0.9 1.0" /> - <color - name="BadgeLabelColor" - reference="White" /> - <color - name="ButtonBorderColor" - reference="Unused?" /> - <color - name="ButtonCautionImageColor" - reference="Unused?" /> - <color - name="ButtonColor" - reference="Unused?" /> - <color - name="ButtonFlashBgColor" - reference="Unused?" /> - <color - name="ButtonImageColor" - reference="White" /> - <color - name="ButtonLabelColor" - reference="LtGray" /> - <color - name="ButtonLabelDisabledColor" - reference="White_25" /> - <color - name="ButtonLabelSelectedColor" - reference="White" /> - <color - name="ButtonLabelSelectedDisabledColor" - reference="White_25" /> - <color - name="ButtonSelectedBgColor" - reference="Unused?" /> - <color - name="ButtonSelectedColor" - reference="Unused?" /> - <color - name="ButtonUnselectedBgColor" - reference="Unused?" /> - <color - name="ButtonUnselectedFgColor" - reference="Unused?" /> - <color - name="ChatHistoryBgColor" - reference="Transparent" /> - <color - name="ChatHistoryTextColor" - reference="LtGray" /> - <color - name="ChicletFlashColor" - value="0.114 0.65 0.1" /> - <color - name="ColorDropShadow" - reference="Black_50" /> - <color - name="ColorPaletteEntry01" - reference="Black" /> - <color - name="ColorPaletteEntry02" - reference="Gray" /> - <color - name="ColorPaletteEntry03" - value="0.5 0 0 1" /> - <color - name="ColorPaletteEntry04" - value="0.5 0.5 0 1" /> - <color - name="ColorPaletteEntry05" - value="0 0.5 0 1" /> - <color - name="ColorPaletteEntry06" - value="0 0.5 0.5 1" /> - <color - name="ColorPaletteEntry07" - value="0 0 0.5 1" /> - <color - name="ColorPaletteEntry08" - value="0.5 0 0.5 1" /> - <color - name="ColorPaletteEntry09" - value="0.5 0.5 0 1" /> - <color - name="ColorPaletteEntry10" - value="0 0.25 0.25 1" /> - <color - name="ColorPaletteEntry11" - value="0 0.5 1 1" /> - <color - name="ColorPaletteEntry12" - value="0 0.25 0.5 1" /> - <color - name="ColorPaletteEntry13" - value="0.5 0 1 1" /> - <color - name="ColorPaletteEntry14" - value="0.5 0.25 0 1" /> - <color - name="ColorPaletteEntry15" - reference="White" /> - <color - name="ColorPaletteEntry16" - reference="LtYellow" /> - <color - name="ColorPaletteEntry17" - reference="White" /> - <color - name="ColorPaletteEntry18" - reference="LtGray" /> - <color - name="ColorPaletteEntry19" - reference="Red" /> - <color - name="ColorPaletteEntry20" - reference="Yellow" /> - <color - name="ColorPaletteEntry21" - reference="Green" /> - <color - name="ColorPaletteEntry22" - value="0 1 1 1" /> - <color - name="ColorPaletteEntry23" - reference="Blue" /> - <color - name="ColorPaletteEntry24" - reference="Purple" /> - <color - name="ColorPaletteEntry25" - value="1 1 0.5 1" /> - <color - name="ColorPaletteEntry26" - value="0 1 0.5 1" /> - <color - name="ColorPaletteEntry27" - value="0.5 1 1 1" /> - <color - name="ColorPaletteEntry28" - value="0.5 0.5 1 1" /> - <color - name="ColorPaletteEntry29" - value="1 0 0.5 1" /> - <color - name="ColorPaletteEntry30" - value="1 0.5 0 1" /> - <color - name="ColorPaletteEntry31" - reference="White" /> - <color - name="ColorPaletteEntry32" - reference="White" /> - <color - name="ComboListBgColor" - reference="DkGray" /> - <color - name="ConsoleBackground" - reference="Black" /> - <color - name="ContextSilhouetteColor" - reference="EmphasisColor" /> - <color - name="DefaultHighlightDark" - reference="White_10" /> - <color - name="DefaultHighlightLight" - reference="White_25" /> - <color - name="DefaultShadowDark" - reference="Black_50" /> - <color - name="DefaultShadowLight" - reference="Black_50" /> - <color - name="EffectColor" - reference="White" /> - <color - name="FilterBackgroundColor" - reference="Black" /> - <color - name="FilterTextColor" - value="0.38 0.69 0.57 1" /> - <color - name="FloaterButtonImageColor" - reference="LtGray" /> - <color - name="FloaterDefaultBackgroundColor" - reference="DkGray_66" /> - <color - name="FloaterFocusBackgroundColor" - reference="DkGray2" /> - <color - name="FloaterFocusBorderColor" - reference="Black_50" /> - <color - name="FloaterUnfocusBorderColor" - reference="Black_50" /> - <color - name="FocusColor" - reference="EmphasisColor" /> - <color - name="FolderViewLoadingMessageTextColor" - value="0.3344 0.5456 0.5159 1" /> - <color - name="GridFocusPointColor" - reference="White_50" /> - <color - name="GridlineBGColor" - value="0.92 0.92 1 0.78" /> - <color - name="GridlineColor" - reference="White" /> - <color - name="GridlineShadowColor" - value="0 0 0 0.31" /> - <color - name="GroupNotifyBoxColor" - value="0.3344 0.5456 0.5159 1" /> - <color - name="GroupNotifyTextColor" - reference="White"/> - <color - name="GroupNotifyDimmedTextColor" - reference="LtGray" /> - <color - name="GroupOverTierColor" - value="0.43 0.06 0.06 1" /> - <color - name="HTMLLinkColor" - reference="EmphasisColor" /> - <color - name="HealthTextColor" - reference="White" /> - <color - name="HelpBgColor" - reference="Unused?" /> - <color - name="HelpFgColor" - reference="Unused?" /> - <color - name="HelpScrollHighlightColor" - reference="Unused?" /> - <color - name="HelpScrollShadowColor" - reference="Unused?" /> - <color - name="HelpScrollThumbColor" - reference="Unused?" /> - <color - name="HelpScrollTrackColor" - reference="Unused?" /> - <color - name="HighlightChildColor" - reference="Yellow" /> - <color - name="HighlightInspectColor" - value="1 0 1 1" /> - <color - name="HighlightParentColor" - value="0.67 0.83 0.96 1" /> - <color - name="IMHistoryBgColor" - reference="Unused?" /> - <color - name="IMHistoryTextColor" - reference="Unused?" /> - <color - name="IconDisabledColor" - reference="White_25" /> - <color - name="IconEnabledColor" - reference="White" /> - <color - name="InventoryBackgroundColor" - reference="DkGray2" /> - <color - name="InventoryFocusOutlineColor" - reference="White_25" /> - <color - name="InventoryItemSuffixColor" - reference="White_25" /> - <color - name="InventoryItemLibraryColor" - reference="EmphasisColor" /> - <color - name="InventoryItemLinkColor" - reference="LtGray_50" /> - <color - name="InventoryMouseOverColor" - reference="LtGray_35" /> - <color - name="InventorySearchStatusColor" - reference="EmphasisColor" /> - <color - name="LabelDisabledColor" - reference="White_25" /> - <color - name="LabelSelectedColor" - reference="White" /> - <color - name="LabelSelectedDisabledColor" - reference="White_25" /> - <color - name="LabelTextColor" - reference="LtGray" /> - <color - name="LoginProgressBarBgColor" - reference="Unused?" /> - <color - name="LoginProgressBarFgColor" - reference="Unused?" /> - <color - name="LoginProgressBoxBorderColor" - value="0 0.12 0.24 0" /> - <color - name="LoginProgressBoxCenterColor" - value="0 0 0 0.78" /> - <color - name="LoginProgressBoxShadowColor" - value="0 0 0 0.78" /> - <color - name="LoginProgressBoxTextColor" - reference="White" /> - <color - name="MapAvatarColor" - reference="Green" /> - <color - name="MapAvatarFriendColor" - reference="Yellow" /> - <color - name="MapAvatarSelfColor" - value="0.53125 0 0.498047 1" /> - <color - name="MapFrustumColor" - reference="White_10" /> - <color - name="MapFrustumRotatingColor" - value="1 1 1 0.2" /> - <color - name="MapTrackColor" - reference="Red" /> - <color - name="MapTrackDisabledColor" - value="0.5 0 0 1" /> - <color - name="MenuBarBgColor" - reference="DkGray" /> - <color - name="MenuBarGodBgColor" - reference="FrogGreen" /> - <color - name="MenuDefaultBgColor" - reference="DkGray2" /> - <color - name="MenuItemDisabledColor" - reference="LtGray_50" /> - <color - name="MenuItemEnabledColor" - reference="LtGray" /> - <color - name="MenuItemHighlightBgColor" - reference="EmphasisColor_35" /> - <color - name="MenuItemHighlightFgColor" - reference="White" /> - <color - name="MenuNonProductionBgColor" - reference="Black" /> - <color - name="MenuNonProductionGodBgColor" - value="0.263 0.325 0.345 1" /> - <color - name="MenuPopupBgColor" - reference="DkGray2" /> - <color - name="ModelUploaderLabels" - value="1 0.6 0 1" /> - <color - name="MultiSliderDisabledThumbColor" - reference="Black" /> - <color - name="MultiSliderThumbCenterColor" - reference="White" /> - <color - name="MultiSliderThumbCenterSelectedColor" - reference="Green" /> - <color - name="MultiSliderThumbOutlineColor" - reference="Unused?" /> - <color - name="MultiSliderTrackColor" - reference="LtGray" /> - <color - name="MultiSliderTriangleColor" - reference="Yellow" /> + <color + name="AccordionHeaderTextColor" + reference="LtGray" /> + <color + name="AgentChatColor" + reference="White" /> + <color + name="AlertBoxColor" + value="0.24 0.24 0.24 1" /> + <color + name="AlertCautionBoxColor" + value="1 0.82 0.46 1" /> + <color + name="AlertCautionTextColor" + reference="LtYellow" /> + <color + name="AvatarListItemIconDefaultColor" + reference="White" /> + <color + name="AvatarListItemIconOnlineColor" + reference="White" /> + <color + name="AvatarListItemIconOfflineColor" + value="0.5 0.5 0.5 0.5" /> + <color + name="AvatarListItemIconVoiceInvitedColor" + reference="AvatarListItemIconOfflineColor" /> + <color + name="AvatarListItemIconVoiceJoinedColor" + reference="AvatarListItemIconOnlineColor" /> + <color + name="AvatarListItemIconVoiceLeftColor" + reference="AvatarListItemIconOfflineColor" /> + <color + name="BadgeImageColor" + value="1.0 0.40 0.0 1.0" /> + <color + name="BadgeBorderColor" + value="0.9 0.9 0.9 1.0" /> + <color + name="BadgeLabelColor" + reference="White" /> + <color + name="ButtonBorderColor" + reference="Unused?" /> + <color + name="ButtonCautionImageColor" + reference="Unused?" /> + <color + name="ButtonColor" + reference="Unused?" /> + <color + name="ButtonFlashBgColor" + reference="Unused?" /> + <color + name="ButtonImageColor" + reference="White" /> + <color + name="ButtonLabelColor" + reference="LtGray" /> + <color + name="ButtonLabelDisabledColor" + reference="White_25" /> + <color + name="ButtonLabelSelectedColor" + reference="White" /> + <color + name="ButtonLabelSelectedDisabledColor" + reference="White_25" /> + <color + name="ButtonSelectedBgColor" + reference="Unused?" /> + <color + name="ButtonSelectedColor" + reference="Unused?" /> + <color + name="ButtonUnselectedBgColor" + reference="Unused?" /> + <color + name="ButtonUnselectedFgColor" + reference="Unused?" /> + <color + name="ChatHistoryBgColor" + reference="Transparent" /> + <color + name="ChatHistoryTextColor" + reference="LtGray" /> + <color + name="ChicletFlashColor" + value="0.114 0.65 0.1" /> + <color + name="ColorDropShadow" + reference="Black_50" /> + <color + name="ColorPaletteEntry01" + reference="Black" /> + <color + name="ColorPaletteEntry02" + reference="Gray" /> + <color + name="ColorPaletteEntry03" + value="0.5 0 0 1" /> + <color + name="ColorPaletteEntry04" + value="0.5 0.5 0 1" /> + <color + name="ColorPaletteEntry05" + value="0 0.5 0 1" /> + <color + name="ColorPaletteEntry06" + value="0 0.5 0.5 1" /> + <color + name="ColorPaletteEntry07" + value="0 0 0.5 1" /> + <color + name="ColorPaletteEntry08" + value="0.5 0 0.5 1" /> + <color + name="ColorPaletteEntry09" + value="0.5 0.5 0 1" /> + <color + name="ColorPaletteEntry10" + value="0 0.25 0.25 1" /> + <color + name="ColorPaletteEntry11" + value="0 0.5 1 1" /> + <color + name="ColorPaletteEntry12" + value="0 0.25 0.5 1" /> + <color + name="ColorPaletteEntry13" + value="0.5 0 1 1" /> + <color + name="ColorPaletteEntry14" + value="0.5 0.25 0 1" /> + <color + name="ColorPaletteEntry15" + reference="White" /> + <color + name="ColorPaletteEntry16" + reference="LtYellow" /> + <color + name="ColorPaletteEntry17" + reference="White" /> + <color + name="ColorPaletteEntry18" + reference="LtGray" /> + <color + name="ColorPaletteEntry19" + reference="Red" /> + <color + name="ColorPaletteEntry20" + reference="Yellow" /> + <color + name="ColorPaletteEntry21" + reference="Green" /> + <color + name="ColorPaletteEntry22" + value="0 1 1 1" /> + <color + name="ColorPaletteEntry23" + reference="Blue" /> + <color + name="ColorPaletteEntry24" + reference="Purple" /> + <color + name="ColorPaletteEntry25" + value="1 1 0.5 1" /> + <color + name="ColorPaletteEntry26" + value="0 1 0.5 1" /> + <color + name="ColorPaletteEntry27" + value="0.5 1 1 1" /> + <color + name="ColorPaletteEntry28" + value="0.5 0.5 1 1" /> + <color + name="ColorPaletteEntry29" + value="1 0 0.5 1" /> + <color + name="ColorPaletteEntry30" + value="1 0.5 0 1" /> + <color + name="ColorPaletteEntry31" + reference="White" /> + <color + name="ColorPaletteEntry32" + reference="White" /> + <color + name="ComboListBgColor" + reference="DkGray" /> + <color + name="ConsoleBackground" + reference="Black" /> + <color + name="ContextSilhouetteColor" + reference="EmphasisColor" /> + <color + name="DefaultHighlightDark" + reference="White_10" /> + <color + name="DefaultHighlightLight" + reference="White_25" /> + <color + name="DefaultShadowDark" + reference="Black_50" /> + <color + name="DefaultShadowLight" + reference="Black_50" /> + <color + name="EffectColor" + reference="White" /> + <color + name="FilterBackgroundColor" + reference="Black" /> + <color + name="FilterTextColor" + value="0.38 0.69 0.57 1" /> + <color + name="FloaterButtonImageColor" + reference="LtGray" /> + <color + name="FloaterDefaultBackgroundColor" + reference="DkGray_66" /> + <color + name="FloaterFocusBackgroundColor" + reference="DkGray2" /> + <color + name="FloaterFocusBorderColor" + reference="Black_50" /> + <color + name="FloaterUnfocusBorderColor" + reference="Black_50" /> + <color + name="FocusColor" + reference="EmphasisColor" /> + <color + name="FolderViewLoadingMessageTextColor" + value="0.3344 0.5456 0.5159 1" /> + <color + name="GridFocusPointColor" + reference="White_50" /> + <color + name="GridlineBGColor" + value="0.92 0.92 1 0.78" /> + <color + name="GridlineColor" + reference="White" /> + <color + name="GridlineShadowColor" + value="0 0 0 0.31" /> + <color + name="GroupNotifyBoxColor" + value="0.3344 0.5456 0.5159 1" /> + <color + name="GroupNotifyTextColor" + reference="White"/> + <color + name="GroupNotifyDimmedTextColor" + reference="LtGray" /> + <color + name="GroupOverTierColor" + value="0.43 0.06 0.06 1" /> + <color + name="HTMLLinkColor" + reference="EmphasisColor" /> + <color + name="HealthTextColor" + reference="White" /> + <color + name="HelpBgColor" + reference="Unused?" /> + <color + name="HelpFgColor" + reference="Unused?" /> + <color + name="HelpScrollHighlightColor" + reference="Unused?" /> + <color + name="HelpScrollShadowColor" + reference="Unused?" /> + <color + name="HelpScrollThumbColor" + reference="Unused?" /> + <color + name="HelpScrollTrackColor" + reference="Unused?" /> + <color + name="HighlightChildColor" + reference="Yellow" /> + <color + name="HighlightInspectColor" + value="1 0 1 1" /> + <color + name="HighlightParentColor" + value="0.67 0.83 0.96 1" /> + <color + name="IMHistoryBgColor" + reference="Unused?" /> + <color + name="IMHistoryTextColor" + reference="Unused?" /> + <color + name="IconDisabledColor" + reference="White_25" /> + <color + name="IconEnabledColor" + reference="White" /> + <color + name="InventoryBackgroundColor" + reference="DkGray2" /> + <color + name="InventoryFocusOutlineColor" + reference="White_25" /> + <color + name="InventoryItemSuffixColor" + reference="White_25" /> + <color + name="InventoryItemLibraryColor" + reference="EmphasisColor" /> + <color + name="InventoryItemLinkColor" + reference="LtGray_50" /> + <color + name="InventoryMouseOverColor" + reference="LtGray_35" /> + <color + name="InventorySearchStatusColor" + reference="EmphasisColor" /> + <color + name="LabelDisabledColor" + reference="White_25" /> + <color + name="LabelSelectedColor" + reference="White" /> + <color + name="LabelSelectedDisabledColor" + reference="White_25" /> + <color + name="LabelTextColor" + reference="LtGray" /> + <color + name="LoginProgressBarBgColor" + reference="Unused?" /> + <color + name="LoginProgressBarFgColor" + reference="Unused?" /> + <color + name="LoginProgressBoxBorderColor" + value="0 0.12 0.24 0" /> + <color + name="LoginProgressBoxCenterColor" + value="0 0 0 0.78" /> + <color + name="LoginProgressBoxShadowColor" + value="0 0 0 0.78" /> + <color + name="LoginProgressBoxTextColor" + reference="White" /> + <color + name="MapAvatarColor" + reference="Green" /> + <color + name="MapAvatarFriendColor" + reference="Yellow" /> + <color + name="MapAvatarSelfColor" + value="0.53125 0 0.498047 1" /> + <color + name="MapFrustumColor" + reference="White_10" /> + <color + name="MapFrustumRotatingColor" + value="1 1 1 0.2" /> + <color + name="MapTrackColor" + reference="Red" /> + <color + name="MapTrackDisabledColor" + value="0.5 0 0 1" /> + <color + name="MenuBarBgColor" + reference="DkGray" /> + <color + name="MenuBarGodBgColor" + reference="FrogGreen" /> + <color + name="MenuDefaultBgColor" + reference="DkGray2" /> + <color + name="MenuItemDisabledColor" + reference="LtGray_50" /> + <color + name="MenuItemEnabledColor" + reference="LtGray" /> + <color + name="MenuItemHighlightBgColor" + reference="EmphasisColor_35" /> + <color + name="MenuItemHighlightFgColor" + reference="White" /> + <color + name="MenuNonProductionBgColor" + reference="Black" /> + <color + name="MenuNonProductionGodBgColor" + value="0.263 0.325 0.345 1" /> + <color + name="MenuPopupBgColor" + reference="DkGray2" /> + <color + name="ModelUploaderLabels" + value="1 0.6 0 1" /> + <color + name="MultiSliderDisabledThumbColor" + reference="Black" /> + <color + name="MultiSliderThumbCenterColor" + reference="White" /> + <color + name="MultiSliderThumbCenterSelectedColor" + reference="Green" /> + <color + name="MultiSliderThumbOutlineColor" + reference="Unused?" /> + <color + name="MultiSliderTrackColor" + reference="LtGray" /> + <color + name="MultiSliderTriangleColor" + reference="Yellow" /> <!-- - <color + <color name="NameTagBackground" value="0.85 0.85 0.85 0.80" /> - --> - <color + --> + <color name="NameTagBackground" value="0 0 0 1" /> - <color - name="NameTagChat" - reference="White" /> - <color - name="NameTagFriend" - value="0.447 0.784 0.663 1" /> - <color - name="NameTagLegacy" - reference="White" /> - <color - name="NameTagMatch" - reference="White" /> - <color - name="NameTagMismatch" - reference="White" /> - <color - name="NetMapBackgroundColor" - value="0 0 0 1" /> - <color - name="NetMapGroupOwnAboveWater" - reference="Purple" /> - <color - name="NetMapGroupOwnBelowWater" - value="0.78 0 0.78 1" /> - <color - name="NetMapOtherOwnAboveWater" - value="0.24 0.24 0.24 1" /> - <color - name="NetMapOtherOwnBelowWater" - value="0.12 0.12 0.12 1" /> - <color - name="NetMapYouOwnAboveWater" - value="0 1 1 1" /> - <color - name="NetMapYouOwnBelowWater" - value="0 0.78 0.78 1" /> - <color - name="NotifyBoxColor" - value="LtGray" /> - <color - name="NotifyCautionBoxColor" - value="1 0.82 0.46 1" /> - <color - name="NotifyCautionWarnColor" - reference="White" /> - <color - name="NotifyTextColor" - reference="White" /> - <color - name="ObjectBubbleColor" - reference="DkGray_66" /> - <color - name="ObjectChatColor" - reference="EmphasisColor" /> - <color - name="OverdrivenColor" - reference="Red" /> - <color - name="PanelDefaultBackgroundColor" - reference="DkGray" /> - <color - name="PanelDefaultHighlightLight" - reference="White_50" /> - <color - name="PanelFocusBackgroundColor" - reference="DkGray2" /> - <color - name="PanelNotificationBackground" - value="1 0.3 0.3 0" /> - <color - name="ParcelHoverColor" - reference="White" /> - <color + <color + name="NameTagChat" + reference="White" /> + <color + name="NameTagFriend" + value="0.447 0.784 0.663 1" /> + <color + name="NameTagLegacy" + reference="White" /> + <color + name="NameTagMatch" + reference="White" /> + <color + name="NameTagMismatch" + reference="White" /> + <color + name="NetMapBackgroundColor" + value="0 0 0 1" /> + <color + name="NetMapGroupOwnAboveWater" + reference="Purple" /> + <color + name="NetMapGroupOwnBelowWater" + value="0.78 0 0.78 1" /> + <color + name="NetMapOtherOwnAboveWater" + value="0.24 0.24 0.24 1" /> + <color + name="NetMapOtherOwnBelowWater" + value="0.12 0.12 0.12 1" /> + <color + name="NetMapYouOwnAboveWater" + value="0 1 1 1" /> + <color + name="NetMapYouOwnBelowWater" + value="0 0.78 0.78 1" /> + <color + name="NotifyBoxColor" + value="LtGray" /> + <color + name="NotifyCautionBoxColor" + value="1 0.82 0.46 1" /> + <color + name="NotifyCautionWarnColor" + reference="White" /> + <color + name="NotifyTextColor" + reference="White" /> + <color + name="ObjectBubbleColor" + reference="DkGray_66" /> + <color + name="ObjectChatColor" + reference="EmphasisColor" /> + <color + name="OverdrivenColor" + reference="Red" /> + <color + name="PanelDefaultBackgroundColor" + reference="DkGray" /> + <color + name="PanelDefaultHighlightLight" + reference="White_50" /> + <color + name="PanelFocusBackgroundColor" + reference="DkGray2" /> + <color + name="PanelNotificationBackground" + value="1 0.3 0.3 0" /> + <color + name="ParcelHoverColor" + reference="White" /> + <color name="PathfindingErrorColor" reference="LtRed" /> <color @@ -657,205 +657,205 @@ name="PathfindingCharacterBeaconColor" reference="Red_80" /> <color - name="PieMenuBgColor" - value="0.24 0.24 0.24 0.59" /> - <color - name="PieMenuLineColor" - value="0 0 0 0.5" /> - <color - name="PieMenuSelectedColor" - value="0.72 0.72 0.74 0.3" /> - <color - name="PropertyColorAuction" - value="0.5 0 1 0.4" /> - <color - name="PropertyColorAvail" - reference="Transparent" /> - <color - name="PropertyColorForSale" - value="1 0.5 0 0.4" /> - <color - name="PropertyColorGroup" - value="0 0.72 0.72 0.4" /> - <color - name="PropertyColorOther" - value="1 0 0 0.4" /> - <color - name="PropertyColorSelf" - value="0 1 0 0.4" /> - <color - name="ScriptBgReadOnlyColor" - value="0.39 0.39 0.39 1" /> - <color - name="ScriptErrorColor" - reference="Red" /> - <color - name="ScrollBGStripeColor" - reference="Transparent" /> - <color - name="ScrollBgReadOnlyColor" + name="PieMenuBgColor" + value="0.24 0.24 0.24 0.59" /> + <color + name="PieMenuLineColor" + value="0 0 0 0.5" /> + <color + name="PieMenuSelectedColor" + value="0.72 0.72 0.74 0.3" /> + <color + name="PropertyColorAuction" + value="0.5 0 1 0.4" /> + <color + name="PropertyColorAvail" + reference="Transparent" /> + <color + name="PropertyColorForSale" + value="1 0.5 0 0.4" /> + <color + name="PropertyColorGroup" + value="0 0.72 0.72 0.4" /> + <color + name="PropertyColorOther" + value="1 0 0 0.4" /> + <color + name="PropertyColorSelf" + value="0 1 0 0.4" /> + <color + name="ScriptBgReadOnlyColor" + value="0.39 0.39 0.39 1" /> + <color + name="ScriptErrorColor" + reference="Red" /> + <color + name="ScrollBGStripeColor" + reference="Transparent" /> + <color + name="ScrollBgReadOnlyColor" reference="Transparent" /> - <color - name="ScrollBgWriteableColor" - reference="White_05" /> - <color - name="ScrollDisabledColor" - reference="White_25" /> - <color - name="ScrollHighlightedColor" - reference="Unused?" /> - <color - name="ScrollHoveredColor" - reference="EmphasisColor_13" /> - <color - name="ScrollSelectedBGColor" - reference="EmphasisColor_35" /> - <color - name="ScrollSelectedFGColor" - reference="White" /> - <color - name="ScrollUnselectedColor" - reference="LtGray" /> - <color - name="ScrollbarThumbColor" - reference="White" /> - <color - name="ScrollbarTrackColor" - reference="Black" /> - <color - name="SelectedOutfitTextColor" - reference="EmphasisColor" /> - <color - name="SilhouetteChildColor" - value="0.13 0.42 0.77 1" /> - <color - name="SilhouetteParentColor" - reference="Yellow" /> - <color - name="SliderDisabledThumbColor" - reference="White_25" /> - <color - name="SliderThumbCenterColor" - reference="White" /> - <color - name="SliderThumbOutlineColor" - reference="White" /> - <color - name="SliderTrackColor" - reference="Unused?" /> - <color - name="SpeakingColor" - reference="FrogGreen" /> - <color - name="SystemChatColor" - reference="LtGray" /> - <color - name="TextBgFocusColor" - reference="White" /> - <color - name="TextBgReadOnlyColor" - reference="White_05" /> - <color - name="TextBgWriteableColor" - reference="LtGray" /> - <color - name="TextCursorColor" - reference="Black" /> - <color - name="TextDefaultColor" - reference="Black" /> - <color - name="TextEmbeddedItemColor" - value="0 0 0.5 1" /> - <color - name="TextEmbeddedItemReadOnlyColor" - reference="Unused?" /> - <color - name="TextFgColor" - value="0.102 0.102 0.102 1" /> - <color - name="TextFgReadOnlyColor" - reference="LtGray" /> - <color - name="TextFgTentativeColor" - value="0.4 0.4 0.4 1" /> - <color - name="TimeTextColor" - reference="LtGray" /> - <color - name="TitleBarFocusColor" - reference="White_10" /> - <color - name="ToastBackground" - value="0.3 0.3 0.3 0" /> - <color - name="ToolTipBgColor" - value="0.937 0.89 0.655 1" /> - <color - name="ToolTipBorderColor" - value="0.812 0.753 0.451 1" /> - <color - name="ToolTipTextColor" - reference="DkGray2" /> - <color - name="InspectorTipTextColor" - reference="LtGray" /> - <color - name="UserChatColor" - reference="White" /> - <color - name="llOwnerSayChatColor" - reference="LtYellow" /> + <color + name="ScrollBgWriteableColor" + reference="White_05" /> + <color + name="ScrollDisabledColor" + reference="White_25" /> + <color + name="ScrollHighlightedColor" + reference="Unused?" /> + <color + name="ScrollHoveredColor" + reference="EmphasisColor_13" /> + <color + name="ScrollSelectedBGColor" + reference="EmphasisColor_35" /> + <color + name="ScrollSelectedFGColor" + reference="White" /> + <color + name="ScrollUnselectedColor" + reference="LtGray" /> + <color + name="ScrollbarThumbColor" + reference="White" /> + <color + name="ScrollbarTrackColor" + reference="Black" /> + <color + name="SelectedOutfitTextColor" + reference="EmphasisColor" /> + <color + name="SilhouetteChildColor" + value="0.13 0.42 0.77 1" /> + <color + name="SilhouetteParentColor" + reference="Yellow" /> + <color + name="SliderDisabledThumbColor" + reference="White_25" /> + <color + name="SliderThumbCenterColor" + reference="White" /> + <color + name="SliderThumbOutlineColor" + reference="White" /> + <color + name="SliderTrackColor" + reference="Unused?" /> + <color + name="SpeakingColor" + reference="FrogGreen" /> + <color + name="SystemChatColor" + reference="LtGray" /> + <color + name="TextBgFocusColor" + reference="White" /> + <color + name="TextBgReadOnlyColor" + reference="White_05" /> + <color + name="TextBgWriteableColor" + reference="LtGray" /> + <color + name="TextCursorColor" + reference="Black" /> + <color + name="TextDefaultColor" + reference="Black" /> + <color + name="TextEmbeddedItemColor" + value="0 0 0.5 1" /> + <color + name="TextEmbeddedItemReadOnlyColor" + reference="Unused?" /> + <color + name="TextFgColor" + value="0.102 0.102 0.102 1" /> + <color + name="TextFgReadOnlyColor" + reference="LtGray" /> + <color + name="TextFgTentativeColor" + value="0.4 0.4 0.4 1" /> + <color + name="TimeTextColor" + reference="LtGray" /> + <color + name="TitleBarFocusColor" + reference="White_10" /> + <color + name="ToastBackground" + value="0.3 0.3 0.3 0" /> + <color + name="ToolTipBgColor" + value="0.937 0.89 0.655 1" /> + <color + name="ToolTipBorderColor" + value="0.812 0.753 0.451 1" /> + <color + name="ToolTipTextColor" + reference="DkGray2" /> + <color + name="InspectorTipTextColor" + reference="LtGray" /> + <color + name="UserChatColor" + reference="Yellow" /> + <color + name="llOwnerSayChatColor" + reference="LtYellow" /> - <!-- New Colors --> - <color - name="OutputMonitorMutedColor" - reference="DkGray2" /> - <color - name="SysWellItemUnselected" - value="0 0 0 0" /> - <color - name="SysWellItemSelected" - value="0.3 0.3 0.3 1.0" /> - <color - name="ColorSwatchBorderColor" - value="0.45098 0.517647 0.607843 1"/> - <color - name="ChatTimestampColor" - reference="White" /> - <color - name="MenuBarProjectBgColor" - reference="MdBlue" /> + <!-- New Colors --> + <color + name="OutputMonitorMutedColor" + reference="DkGray2" /> + <color + name="SysWellItemUnselected" + value="0 0 0 0" /> + <color + name="SysWellItemSelected" + value="0.3 0.3 0.3 1.0" /> + <color + name="ColorSwatchBorderColor" + value="0.45098 0.517647 0.607843 1"/> + <color + name="ChatTimestampColor" + reference="White" /> + <color + name="MenuBarProjectBgColor" + reference="MdBlue" /> - <color + <color name="MeshImportTableNormalColor" value="1 1 1 1"/> - <color + <color name="MeshImportTableHighlightColor" value="0.2 0.8 1 1"/> - <color - name="DirectChatColor" - reference="LtOrange" /> + <color + name="DirectChatColor" + reference="LtOrange" /> - <color + <color name="ToolbarDropZoneColor" value=".48 .69 1 .5" /> - <!-- Generic color names (legacy) --> + <!-- Generic color names (legacy) --> <color - name="white" - value="1 1 1 1"/> + name="white" + value="1 1 1 1"/> <color - name="black" - value="0 0 0 1"/> + name="black" + value="0 0 0 1"/> <color - name="red" - value="1 0 0 1"/> + name="red" + value="1 0 0 1"/> <color - name="green" - value="0 1 0 1"/> + name="green" + value="0 1 0 1"/> <color - name="blue" - value="0 0 1 1"/> + name="blue" + value="0 0 1 1"/> </colors> diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_add_person.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_add_person.png Binary files differnew file mode 100755 index 0000000000..f024c733f3 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_add_person.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_arrow_ne.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_arrow_ne.png Binary files differnew file mode 100755 index 0000000000..a19e720d42 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_arrow_ne.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_arrow_sw.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_arrow_sw.png Binary files differnew file mode 100755 index 0000000000..7f3f42639d --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_arrow_sw.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_call_log.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_call_log.png Binary files differnew file mode 100755 index 0000000000..2880eb766a --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_call_log.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_close.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_close.png Binary files differnew file mode 100755 index 0000000000..25a939d7f5 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_close.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_collapse.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_collapse.png Binary files differnew file mode 100755 index 0000000000..82baabde47 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_collapse.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_expand.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_expand.png Binary files differnew file mode 100755 index 0000000000..7d64abb042 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_expand.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_hang_up.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_hang_up.png Binary files differnew file mode 100755 index 0000000000..f0da962c2d --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_hang_up.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_open_call.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_open_call.png Binary files differnew file mode 100755 index 0000000000..0db001dcdb --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_open_call.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_plus.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_plus.png Binary files differnew file mode 100755 index 0000000000..0cf7edc2d4 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_plus.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_sort.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_sort.png Binary files differnew file mode 100755 index 0000000000..a0c15a6d3e --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_sort.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 06f8f8c670..212f05d284 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -162,7 +162,21 @@ with the same filename but different name <texture name="ComboButton_On" file_name="widgets/ComboButton_On.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" /> <texture name="ComboButton_Off" file_name="widgets/ComboButton_Off.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" /> <texture name="ComboButton_UpOff" file_name="widgets/ComboButton_UpOff.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" /> + <texture name="Container" file_name="containers/Container.png" preload="false" /> + + <texture name="Conv_toolbar_add_person" file_name="icons/Conv_toolbar_add_person.png" preload="false" /> + <texture name="Conv_toolbar_arrow_ne" file_name="icons/Conv_toolbar_arrow_ne.png" preload="false" /> + <texture name="Conv_toolbar_arrow_sw" file_name="icons/Conv_toolbar_arrow_sw.png" preload="false" /> + <texture name="Conv_toolbar_call_log" file_name="icons/Conv_toolbar_call_log.png" preload="false" /> + <texture name="Conv_toolbar_close" file_name="icons/Conv_toolbar_close.png" preload="false" /> + <texture name="Conv_toolbar_collapse" file_name="icons/Conv_toolbar_collapse.png" preload="false" /> + <texture name="Conv_toolbar_expand" file_name="icons/Conv_toolbar_expand.png" preload="false" /> + <texture name="Conv_toolbar_hang_up" file_name="icons/Conv_toolbar_hang_up.png" preload="false" /> + <texture name="Conv_toolbar_open_call" file_name="icons/Conv_toolbar_open_call.png" preload="false" /> + <texture name="Conv_toolbar_plus" file_name="icons/Conv_toolbar_plus.png" preload="false" /> + <texture name="Conv_toolbar_sort" file_name="icons/Conv_toolbar_sort.png" preload="false" /> + <texture name="Copy" file_name="icons/Copy.png" preload="false" /> <texture name="DisclosureArrow_Opened_Off" file_name="widgets/DisclosureArrow_Opened_Off.png" preload="true" /> diff --git a/indra/newview/skins/default/xui/en/floater_chat_bar.xml b/indra/newview/skins/default/xui/en/floater_chat_bar.xml deleted file mode 100644 index 405557242f..0000000000 --- a/indra/newview/skins/default/xui/en/floater_chat_bar.xml +++ /dev/null @@ -1,85 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<floater - positioning="specified" - left="10" - bottom="-10" - height="60" - layout="topleft" - legacy_header_height="25" - single_instance="true" - title="NEARBY CHAT" - save_rect="true" - save_visibility="true" - can_close="true" - can_minimize="true" - help_topic="chat_bar" - min_height="60" - min_width="150" - can_resize="true" - default_tab_group="1" - name="chat_bar" - width="300"> - <panel - top="20" - class="panel_nearby_chat" - follow="all" - width="300" - height="0" - visible="false" - filename="panel_nearby_chat.xml" - name="nearby_chat" /> - <panel width="300" - height="31" - left="0" - name="bottom_panel" - bottom="-1" - follows="left|right|bottom" - tab_group="1"> - <line_editor - border_style="line" - border_thickness="1" - follows="left|right" - height="23" - label="Click here to chat." - layout="topleft" - left_delta="7" - left="0" - max_length_bytes="1023" - name="chat_box" - spellcheck="true" - text_pad_left="5" - text_pad_right="25" - tool_tip="Press Enter to say, Ctrl+Enter to shout" - top="2" - width="255" /> - <output_monitor - auto_update="true" - follows="right" - draw_border="false" - height="16" - layout="topleft" - left_pad="-24" - mouse_opaque="true" - name="chat_zone_indicator" - top="6" - visible="true" - width="20" /> - <button - follows="right" - is_toggle="true" - width="20" - top="2" - layout="topleft" - left_pad="12" - image_disabled="ComboButton_UpOff" - image_unselected="ComboButton_UpOff" - image_selected="ComboButton_On" - image_pressed="ComboButton_UpSelected" - image_pressed_selected="ComboButton_Selected" - height="23" - chrome="true" - name="show_nearby_chat" - tool_tip="Shows/hides nearby chat log"> - </button> - </panel> -</floater> diff --git a/indra/newview/skins/default/xui/en/floater_conversation_log.xml b/indra/newview/skins/default/xui/en/floater_conversation_log.xml new file mode 100644 index 0000000000..9cdeb0d788 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_conversation_log.xml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> + +<floater + can_resize="true" + positioning="cascading" + height="400" + min_height="100" + min_width="390" + layout="topleft" + name="floater_conversation_log" + save_rect="true" + single_instance="true" + reuse_instance="true" + title="CONVERSATION LOG" + width="450"> + <panel + follows="left|top|right" + height="27" + layout="topleft" + left="0" + name="buttons_panel" + top="0"> + <filter_editor + follows="left|top|right" + height="23" + layout="topleft" + left="8" + label="Filter People" + max_length_chars="300" + name="people_filter_input" + text_color="Black" + text_pad_left="10" + top="4" + width="364" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="5" + menu_filename="menu_conversation_log_view.xml" + menu_position="bottomleft" + name="conversation_view_btn" + top="3" + width="31" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="OptionsMenu_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + name="conversations_gear_btn" + top="3" + width="31" /> + </panel> + <panel + follows="all" + height="370" + layout="topleft" + left="5" + name="buttons_panel" + right="-5" + top_pad="5"> + <conversation_log_list + opaque="true" + allow_select="true" + follows="all" + height="360" + layout="topleft" + left="3" + keep_selection_visible_on_reshape="true" + item_pad="2" + multi_select="false" + name="conversation_log_list" + right="-3" + top="5" /> + </panel> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_conversation_preview.xml b/indra/newview/skins/default/xui/en/floater_conversation_preview.xml new file mode 100644 index 0000000000..27b744aefb --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_conversation_preview.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + legacy_header_height="18" + can_resize="true" + default_tab_group="1" + height="361" + layout="topleft" + min_height="243" + min_width="234" + name="preview_conversation" + title="CONVERSATION:" + width="400"> + <floater.string + name="Title"> + CONVERSATION: [NAME] + </floater.string> + <text + type="string" + length="1" + follows="left|top" + font="SansSerif" + height="19" + layout="topleft" + left="10" + name="desc txt" + top="22" + width="90"> + Description: + </text> + <line_editor + border_style="line" + border_thickness="1" + enabled="false" + follows="left|top|right" + font="SansSerif" + height="22" + layout="topleft" + left_pad="0" + max_length_bytes="127" + name="description" + width="296" /> + <chat_history + font="SansSerifSmall" + follows="all" + visible="true" + height="310" + name="chat_history" + parse_highlights="true" + parse_urls="true" + left="5" + width="390"> + </chat_history> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_im_container.xml b/indra/newview/skins/default/xui/en/floater_im_container.xml index e123de46c2..ea6fd65b95 100644 --- a/indra/newview/skins/default/xui/en/floater_im_container.xml +++ b/indra/newview/skins/default/xui/en/floater_im_container.xml @@ -1,49 +1,127 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <multi_floater - can_close="false" + can_close="true" can_minimize="true" can_resize="true" - height="390" + height="430" layout="topleft" + min_height="50" name="floater_im_box" help_topic="floater_im_box" save_rect="true" save_visibility="true" single_instance="true" title="CONVERSATIONS" - width="396"> - <tab_container - follows="left|right|top|bottom" - height="390" + width="680"> + <string + name="collapse_icon" + value="Conv_toolbar_collapse"/> + <string + name="expand_icon" + value="Conv_toolbar_expand"/> + <layout_stack + animate="true" + follows="all" + height="430" layout="topleft" - left="1" - name="im_box_tab_container" - tab_position="bottom" - tab_width="64" - tab_max_width = "134" - tab_height="16" - use_custom_icon_ctrl="true" - tab_icon_ctrl_pad="2" - halign="left" - use_ellipses="true" + left="0" + name="conversations_stack" + orientation="horizontal" top="0" - width="394"> - <first_tab - tab_bottom_image_flash="Toolbar_Left_Flash"/> - <middle_tab - tab_bottom_image_flash="Toolbar_Middle_Flash"/> - <last_tab - tab_bottom_image_flash="Toolbar_Right_Flash"/> - </tab_container> - <icon - color="DefaultShadowLight" - enabled="false" - follows="left|right|bottom" - height="17" - image_name="tabarea.tga" - layout="bottomleft" - left="1" - name="im_box_tab_container_icon" - bottom="10" - width="394" /> + width="680"> + <layout_panel + auto_resize="true" + user_resize="true" + height="430" + name="conversations_layout_panel" + min_dim="51" + width="268" + min_width="120"> + <layout_stack + animate="false" + follows="left|top|right" + height="35" + layout="topleft" + left="0" + name="conversations_pane_buttons_stack" + orientation="horizontal" + top="0" + width="268"> + <layout_panel + auto_resize="true" + height="35" + name="conversations_pane_buttons_expanded"> + <menu_button + follows="top|left" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + menu_filename="menu_participant_view.xml" + layout="topleft" + left="10" + name="sort_btn" + top="5" + width="31" /> + <button + follows="top|left" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_plus" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left_pad="4" + name="add_btn" + tool_tip="Add button on the left panel" + width="31"/> + </layout_panel> + <layout_panel + auto_resize="false" + height="35" + name="conversations_pane_buttons_collapsed" + width="41"> + <button + follows="right|top" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_collapse" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left="5" + name="expand_collapse_btn" + width="31" /> + </layout_panel> + </layout_stack> + <panel + follows="all" + layout="topleft" + name="conversations_list_panel" + opaque="true" + top_pad="0" + left="5" + height="390" + width="263"/> + </layout_panel> + <layout_panel + auto_resize="true" + user_resize="true" + height="430" + name="messages_layout_panel" + width="412" + min_width="205"> + <panel_container + follows="all" + height="430" + layout="topleft" + left="0" + name="im_box_tab_container" + top="0" + width="412"/> + </layout_panel> + </layout_stack> </multi_floater> diff --git a/indra/newview/skins/default/xui/en/floater_im_session.xml b/indra/newview/skins/default/xui/en/floater_im_session.xml index 040b66623e..4abe4d6941 100644 --- a/indra/newview/skins/default/xui/en/floater_im_session.xml +++ b/indra/newview/skins/default/xui/en/floater_im_session.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <floater - legacy_header_height="18" background_visible="true" default_tab_group="1" height="355" @@ -10,84 +9,253 @@ can_dock="false" can_minimize="true" can_close="true" + save_rect="true" visible="false" width="394" can_resize="true" + can_tear_off="false" min_width="250" - min_height="190"> + min_height="190" + positioning="relative"> + <floater.string + name="NearbyChatTitle" + value="Nearby Chat"/> + <floater.string name="call_btn_start">Conv_toolbar_open_call</floater.string> + <floater.string name="call_btn_stop">Conv_toolbar_hang_up</floater.string> + <floater.string + name="collapse_icon" + value="Conv_toolbar_collapse"/> + <floater.string + name="expand_icon" + value="Conv_toolbar_expand"/> + <floater.string + name="tear_off_icon" + value="Conv_toolbar_arrow_ne"/> + <floater.string + name="return_icon" + value="Conv_toolbar_arrow_sw"/> + <view + follows="all" + layout="topleft" + name="contents_view" + top="0" + left="0" + height="355" + width="394"> + <panel + follows="left|top|right" + layout="topleft" + name="toolbar_panel" + top="0" + left="0" + height="35" + width="394"> + <menu_button + menu_filename="menu_im_session_showmodes.xml" + follows="top|left" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left="5" + name="view_options_btn" + top="5" + width="31" /> + <button + enabled="false" + follows="top|left" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_add_person" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left_pad="4" + name="add_btn" + width="31"/> + <button + follows="top|left" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_open_call" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left_pad="4" + name="voice_call_btn" + width="31"/> + <button + follows="right|top" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_close" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left="283" + name="close_btn" + width="31" /> + <button + follows="right|top" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_collapse" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left_pad="5" + name="expand_collapse_btn" + width="31" /> + <button + follows="right|top" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_arrow_ne" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left_pad="5" + name="tear_off_btn" + width="31" /> + </panel> <layout_stack animate="true" default_tab_group="2" follows="all" - height="320" + height="310" width="394" layout="topleft" orientation="horizontal" name="im_panels" tab_group="1" - top="20" + top_pad="0" left="0"> <layout_panel - name="im_control_panel_holder" + name="speakers_list_panel" + follows="all" min_width="115" width="150" - height="320" + height="310" auto_resize="false"> - <panel - name="panel_im_control_panel" + <avatar_list + color="DkGray2" + follows="all" + height="310" + ignore_online_status="true" layout="topleft" - height="320" - width="150" - follows="all"/> + name="speakers_list" + opaque="false" + show_info_btn="true" + show_profile_btn="false" + show_speaking_indicator="false" + width="150" /> </layout_panel> <layout_panel default_tab_group="3" left="0" tab_group="2" + follows="all" top="0" - height="200" - width="244" - user_resize="true"> - <button - height="20" - follows="left|top" - top="0" - left="2" - image_overlay="TabIcon_Open_Off" + height="310" + width="244" + layout="topleft" + user_resize="true" + auto_resize="true" + visible="true" + name="left_part_holder"> + <panel + name="trnsAndChat_panel" + follows="all" + layout="topleft" + visible="true" + height="275" + width="244"> + <layout_stack + animate="true" + default_tab_group="2" + follows="all" + height="275" + width="244" layout="topleft" - width="25" - name="slide_left_btn" /> - <button - height="20" - follows="left|top" + visible="true" + orientation="vertical" + name="translate_and_chat_stack" + tab_group="1" + left_pad="0" top="0" - left="2" - image_overlay="TabIcon_Close_Off" - width="25" - name="slide_right_btn" /> + left="0"> + <layout_panel + auto_resize="false" + height="26" + layout="topleft" + left_delta="0" + name="translate_chat_checkbox_lp" + top_delta="0" + visible="true" + width="230"> + <check_box + top="10" + control_name="TranslateChat" + enabled="true" + height="16" + label="Translate chat" + layout="topleft" + left="5" + name="translate_chat_checkbox" + width="230" /> + </layout_panel> + <layout_panel + height="248" + width="230" + layout="topleft" + follows="all" + left_delta="0" + top_delta="0" + bottom="0" + visible="true" + user_resize="true" + auto_resize="true" + name="chat_holder"> <chat_history font="SansSerifSmall" - follows="left|right|top|bottom" - height="150" + follows="all" + visible="true" + height="240" name="chat_history" parse_highlights="true" parse_urls="true" - left="1" - width="238"> + width="230" + left="0"> </chat_history> - <line_editor - bottom="0" - left="3" - follows="left|right|bottom" - font="SansSerifSmall" - height="20" - label="To" - layout="bottomleft" - name="chat_editor" - spellcheck="true" - tab_group="3" - width="236"> - </line_editor> + </layout_panel> + </layout_stack> + </panel> + <chat_editor + bottom="0" + expand_lines_count="5" + follows="left|right|bottom" + font="SansSerifSmall" + visible="true" + height="20" + is_expandable="true" + label="To" + layout="bottomleft" + name="chat_editor" + max_length="1023" + spellcheck="true" + tab_group="3" + width="220" + left="10" + wrap="true"> + </chat_editor> </layout_panel> </layout_stack> + </view> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_people.xml b/indra/newview/skins/default/xui/en/floater_people.xml index 08d0b00a83..8e143623ab 100644 --- a/indra/newview/skins/default/xui/en/floater_people.xml +++ b/indra/newview/skins/default/xui/en/floater_people.xml @@ -7,20 +7,20 @@ height="570" help_topic="sidebar_people" min_height="440" - min_width="333" + min_width="390" layout="topleft" name="floater_people" save_rect="true" single_instance="true" reuse_instance="true" title="PEOPLE" - width="333"> + width="390"> <panel_container default_panel_name="panel_people" follows="all" height="570" name="main_panel" - width="333"> + width="390"> <panel class="panel_people" name="panel_people" @@ -31,11 +31,5 @@ filename="panel_group_info_sidetray.xml" label="Group Profile" font="SansSerifBold"/> - <panel - class="panel_block_list_sidetray" - name="panel_block_list_sidetray" - filename="panel_block_list_sidetray.xml" - label="Blocked Residents & Objects" - font="SansSerifBold"/> </panel_container> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_voice_volume.xml b/indra/newview/skins/default/xui/en/floater_voice_volume.xml new file mode 100644 index 0000000000..9346295d5b --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_voice_volume.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<!-- + Not can_close / no title to avoid window chrome + Single instance - only have one at a time, recycle it each spawn +--> +<floater + legacy_header_height="25" + bevel_style="in" + bg_opaque_image="Inspector_Background" + can_close="false" + can_minimize="false" + height="90" + layout="topleft" + name="floater_voice_volume" + single_instance="true" + sound_flags="0" + title="VOICE VOLUME" + visible="true" + width="245"> + <text + follows="top|left|right" + font="SansSerifSmall" + height="21" + left="10" + name="avatar_name" + parse_urls="false" + top="35" + text_color="White" + translate="false" + use_ellipses="true" + value="TestString PleaseIgnore" + width="225" /> + <slider + follows="top|left" + height="23" + increment="0.01" + left="1" + max_val="0.95" + min_val="0.05" + name="volume_slider" + show_text="false" + tool_tip="Voice volume" + top_pad="0" + value="0.5" + width="200" /> + <button + follows="top|left" + height="16" + image_disabled="Audio_Off" + image_disabled_selected="AudioMute_Off" + image_hover_selected="AudioMute_Over" + image_selected="AudioMute_Off" + image_unselected="Audio_Off" + is_toggle="true" + left_pad="0" + top_delta="4" + name="mute_btn" + width="16" /> +</floater> diff --git a/indra/newview/skins/default/xui/en/inspect_avatar.xml b/indra/newview/skins/default/xui/en/inspect_avatar.xml index bc3bcd331b..c3481e6d4c 100644 --- a/indra/newview/skins/default/xui/en/inspect_avatar.xml +++ b/indra/newview/skins/default/xui/en/inspect_avatar.xml @@ -9,7 +9,7 @@ bg_opaque_image="Inspector_Background" can_close="false" can_minimize="false" - height="164" + height="130" layout="topleft" name="inspect_avatar" single_instance="true" @@ -94,32 +94,17 @@ use_ellipses="true" width="220">This is my second life description and I really think it is great. But for some reason my description is super extra long because I like to talk a whole lot </text> - <slider - follows="top|left" - height="23" - increment="0.01" - left="1" - max_val="0.95" - min_val="0.05" - name="volume_slider" - show_text="false" - tool_tip="Voice volume" - top_pad="0" - value="0.5" - width="200" /> - <button + <text follows="top|left" height="16" - image_disabled="Audio_Off" - image_disabled_selected="AudioMute_Off" - image_hover_selected="AudioMute_Over" - image_selected="AudioMute_Off" - image_unselected="Audio_Off" - is_toggle="true" - left_pad="0" - top_delta="4" - name="mute_btn" - width="16" /> + left="8" + name="avatar_profile_link" + font="SansSerifSmall" + text_color="White" + top_pad="5" + translate="false" + value="[[LINK] View full profile]" + width="175" /> <avatar_icon follows="top|left" height="38" @@ -130,83 +115,4 @@ name="avatar_icon" top="10" width="38" /> -<!-- Overlapping buttons for default actions - llinspectavatar.cpp makes visible the most likely default action ---> - <button - follows="top|left" - height="20" - label="Add Friend" - left="8" - top="135" - name="add_friend_btn" - width="90" /> - <button - follows="top|left" - height="20" - label="IM" - left_delta="0" - top_delta="0" - name="im_btn" - width="80" - commit_callback.function="InspectAvatar.IM"/> - <button - follows="top|left" - height="20" - label="Profile" - layout="topleft" - name="view_profile_btn" - left_delta="96" - top_delta="0" - tab_stop="false" - width="80" /> - <!-- gear buttons here --> - <menu_button - follows="top|left" - height="20" - layout="topleft" - image_overlay="OptionsMenu_Off" - menu_filename="menu_inspect_avatar_gear.xml" - name="gear_btn" - right="-5" - top_delta="0" - width="35" /> - <menu_button - follows="top|left" - height="20" - image_overlay="OptionsMenu_Off" - menu_filename="menu_inspect_self_gear.xml" - name="gear_self_btn" - right="-5" - top_delta="0" - width="35" /> - <panel - follows="top|left" - top="164" - left="0" - height="60" - width="228" - visible="false" - background_visible="true" - name="moderator_panel" - background_opaque="true" - bg_opaque_color="MouseGray"> - <button - name="disable_voice" - label="Disable Voice" - top="20" - width="95" - height="20" - left="10" - commit_callback.function="InspectAvatar.DisableVoice"/> - <button - name="enable_voice" - label="Enable Voice" - top="20" - width="95" - height="20" - left="10" - visible="false" - commit_callback.function="InspectAvatar.EnableVoice"/> - </panel> </floater> diff --git a/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml b/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml new file mode 100644 index 0000000000..b8d0eef956 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml @@ -0,0 +1,134 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + layout="topleft" + name="Conversation Context Menu"> + <menu_item_call + label="IM..." + layout="topleft" + name="IM"> + <on_click + function="Calllog.Action" + parameter="im" /> + <on_enable + function="Calllog.Enable" + parameter="can_im" /> + </menu_item_call> + <menu_item_call + label="Voice call..." + layout="topleft" + name="Call"> + <on_click + function="Calllog.Action" + parameter="call" /> + <on_enable + function="Calllog.Enable" + parameter="can_call" /> + </menu_item_call> + <menu_item_call + label="Open chat history..." + layout="topleft" + name="Chat history"> + <on_click + function="Calllog.Action" + parameter="chat_history" /> + <on_enable + function="Calllog.Enable" + parameter="can_view_chat_history" /> + </menu_item_call> + <menu_item_call + label="View Profile" + layout="topleft" + name="View Profile"> + <on_click + function="Calllog.Action" + parameter="view_profile" /> + <on_enable + function="Calllog.Enable" + parameter="can_view_profile" /> + </menu_item_call> + <menu_item_call + label="Offer Teleport" + name="teleport"> + <on_click + function="Calllog.Action" + parameter="offer_teleport"/> + <on_enable + function="Calllog.Enable" + parameter="can_offer_teleport"/> + </menu_item_call> + <menu_item_separator /> + <menu_item_check + label="Add friend/Remove friend" + layout="topleft" + name="Friend_add_remove"> + <menu_item_check.on_click + function="Calllog.Action" + parameter="add_rem_friend" /> + <menu_item_check.on_check + function="Calllog.Check" + parameter="is_friend" /> + <menu_item_check.on_enable + function="Calllog.Enable" + parameter="add_rem_friend" /> + </menu_item_check> + <menu_item_call + label="Invite to group..." + layout="topleft" + name="Invite"> + <on_click + function="Calllog.Action" + parameter="invite_to_group"/> + <on_enable + function="Calllog.Enable" + parameter="can_invite_to_group" /> + </menu_item_call> + <menu_item_separator /> + <menu_item_call + label="Map" + layout="topleft" + name="Map"> + <on_click + function="Calllog.Action" + parameter="show_on_map" /> + <on_enable + function="Calllog.Enable" + parameter="can_show_on_map" /> + </menu_item_call> + <menu_item_call + label="Share" + layout="topleft" + name="Share"> + <on_click + function="Calllog.Action" + parameter="share" /> + <on_enable + function="Calllog.Enable" + parameter="can_share" /> + </menu_item_call> + <menu_item_call + label="Pay" + layout="topleft" + name="Pay"> + <on_click + function="Calllog.Action" + parameter="pay" /> + <on_enable + function="Calllog.Enable" + parameter="can_pay" /> + </menu_item_call> + <menu_item_check + label="Block/Unblock" + layout="topleft" + name="Block/Unblock"> + <menu_item_check.on_click + function="Calllog.Action" + parameter="block"/> + <menu_item_check.on_check + function="Calllog.Check" + parameter="is_blocked" /> + <menu_item_check.on_enable + function="Calllog.Enable" + parameter="can_block" /> + </menu_item_check> + +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_conversation_log_view.xml b/indra/newview/skins/default/xui/en/menu_conversation_log_view.xml new file mode 100644 index 0000000000..4ab8cb4f7d --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_conversation_log_view.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + name="menu_conversation_view" + left="0" bottom="0" visible="false" + mouse_opaque="false"> + <menu_item_check + label="Sort by name" + name="sort_by_name"> + <on_click + function="CallLog.Action" + parameter="sort_by_name"/> + <on_check + function="CallLog.Check" + parameter="sort_by_name"/> + </menu_item_check> + <menu_item_check + label="Sort by date" + name="sort_by_date"> + <on_click + function="CallLog.Action" + parameter="sort_by_date" /> + <on_check + function="CallLog.Check" + parameter="sort_by_date" /> + </menu_item_check> + <menu_item_separator /> + <menu_item_check + label="Sort friends on top" + name="sort_by_friends"> + <on_click + function="CallLog.Action" + parameter="sort_friends_on_top" /> + <on_check + function="CallLog.Check" + parameter="sort_friends_on_top" /> + </menu_item_check> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_group_plus.xml b/indra/newview/skins/default/xui/en/menu_group_plus.xml index fce7414d80..eca9e7f3c9 100644 --- a/indra/newview/skins/default/xui/en/menu_group_plus.xml +++ b/indra/newview/skins/default/xui/en/menu_group_plus.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu name="menu_group_plus" +<toggleable_menu name="menu_group_plus" left="0" bottom="0" visible="false" mouse_opaque="false"> <menu_item_call name="item_join" label="Join Group..."> @@ -8,4 +8,4 @@ <menu_item_call name="item_new" label="New Group..."> <menu_item_call.on_click function="People.Group.Plus.Action" userdata="new_group" /> </menu_item_call> -</menu> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_im_session_showmodes.xml b/indra/newview/skins/default/xui/en/menu_im_session_showmodes.xml new file mode 100644 index 0000000000..483f24afd0 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_im_session_showmodes.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + name="menu_modes" + left="0" bottom="0" visible="false" + mouse_opaque="false"> + <menu_item_check + label="Compact view" + name="compact_view"> + <menu_item_check.on_click + function="IMSession.Menu.Action" + parameter="compact_view"/> + <menu_item_check.on_check + function="IMSession.Menu.CompactExpandedModes.CheckItem" + parameter="compact_view"/> + </menu_item_check> + <menu_item_check + label="Expanded view" + name="expanded_view"> + <menu_item_check.on_click + function="IMSession.Menu.Action" + parameter="expanded_view"/> + <menu_item_check.on_check + function="IMSession.Menu.CompactExpandedModes.CheckItem" + parameter="expanded_view"/> + </menu_item_check> + <menu_item_separator layout="topleft" /> + <menu_item_check name="IMShowTime" label="Show time"> + <menu_item_check.on_click + function="IMSession.Menu.Action" + parameter="IMShowTime" /> + <menu_item_check.on_check + function="IMSession.Menu.ShowModes.CheckItem" + parameter="IMShowTime" /> + <menu_item_check.on_enable + function="IMSession.Menu.ShowModes.Enable" + parameter="IMShowTime" /> + </menu_item_check> + <menu_item_check name="IMShowNamesForP2PConv" label="Show names in one-to-one conversations"> + <menu_item_check.on_click + function="IMSession.Menu.Action" + parameter="IMShowNamesForP2PConv" /> + <menu_item_check.on_check + function="IMSession.Menu.ShowModes.CheckItem" + parameter="IMShowNamesForP2PConv" /> + <menu_item_check.on_enable + function="IMSession.Menu.ShowModes.Enable" + parameter="IMShowNamesForP2PConv" /> + + </menu_item_check> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml b/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml deleted file mode 100644 index 76b188220d..0000000000 --- a/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml +++ /dev/null @@ -1,143 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<toggleable_menu - create_jump_keys="true" - layout="topleft" - mouse_opaque="false" - visible="false" - name="Gear Menu"> - <menu_item_call - label="View Profile" - enabled="true" - name="view_profile"> - <menu_item_call.on_click - function="InspectAvatar.ViewProfile"/> - </menu_item_call> - <menu_item_call - label="Add Friend" - name="add_friend"> - <menu_item_call.on_click - function="InspectAvatar.AddFriend"/> - <menu_item_call.on_enable - function="InspectAvatar.Gear.Enable"/> - </menu_item_call> - <menu_item_call - label="IM" - name="im"> - <menu_item_call.on_click - function="InspectAvatar.IM"/> - </menu_item_call> - <menu_item_call - label="Call" - enabled="true" - name="call"> - <menu_item_call.on_click - function="InspectAvatar.Call"/> - <menu_item_call.on_enable - function="InspectAvatar.Gear.EnableCall"/> - </menu_item_call> - <menu_item_call - label="Teleport" - name="teleport"> - <menu_item_call.on_click - function="InspectAvatar.Teleport"/> - <menu_item_call.on_enable - function="InspectAvatar.Gear.EnableTeleportOffer"/> - </menu_item_call> - <menu_item_call - label="Invite to Group" - name="invite_to_group"> - <menu_item_call.on_click - function="InspectAvatar.InviteToGroup"/> - </menu_item_call> - <menu_item_separator /> - <menu_item_call - label="Block" - name="block"> - <menu_item_call.on_click - function="InspectAvatar.ToggleMute"/> - <menu_item_call.on_visible - function="InspectAvatar.EnableMute" /> - </menu_item_call> - <menu_item_call - label="Unblock" - name="unblock"> - <menu_item_call.on_click - function="InspectAvatar.ToggleMute"/> - <menu_item_call.on_visible - function="InspectAvatar.EnableUnmute" /> - </menu_item_call> - <menu_item_call - label="Report" - name="report"> - <menu_item_call.on_click - function="InspectAvatar.Report"/> - </menu_item_call> - <menu_item_call - label="Freeze" - name="freeze"> - <menu_item_call.on_click - function="InspectAvatar.Freeze"/> - <menu_item_call.on_visible - function="InspectAvatar.VisibleFreeze"/> - </menu_item_call> - <menu_item_call - label="Eject" - name="eject"> - <menu_item_call.on_click - function="InspectAvatar.Eject"/> - <menu_item_call.on_visible - function="InspectAvatar.VisibleEject"/> - </menu_item_call> - <menu_item_call - label="Kick" - name="kick"> - <menu_item_call.on_click - function="InspectAvatar.Kick"/> - <menu_item_call.on_visible - function="InspectAvatar.EnableGod"/> - </menu_item_call> - <menu_item_call - label="CSR" - name="csr"> - <menu_item_call.on_click - function="InspectAvatar.CSR" /> - <menu_item_call.on_visible - function="InspectAvatar.EnableGod" /> - </menu_item_call> - <menu_item_call - label="Debug Textures" - name="debug"> - <menu_item_call.on_click - function="Avatar.Debug"/> - <menu_item_call.on_visible - function="IsGodCustomerService"/> - </menu_item_call> - <menu_item_call - label="Find On Map" - name="find_on_map"> - <menu_item_call.on_click - function="InspectAvatar.FindOnMap"/> - <menu_item_call.on_visible - function="InspectAvatar.VisibleFindOnMap"/> - </menu_item_call> - <menu_item_call - label="Zoom In" - name="zoom_in"> - <menu_item_call.on_click - function="InspectAvatar.ZoomIn"/> - <menu_item_call.on_visible - function="InspectAvatar.VisibleZoomIn"/> - </menu_item_call> - <menu_item_call - label="Pay" - name="pay"> - <menu_item_call.on_click - function="InspectAvatar.Pay"/> - </menu_item_call> - <menu_item_call - label="Share" - name="share"> - <menu_item_call.on_click - function="InspectAvatar.Share"/> - </menu_item_call> -</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_inspect_self_gear.xml b/indra/newview/skins/default/xui/en/menu_inspect_self_gear.xml deleted file mode 100644 index 5e7b16ed4a..0000000000 --- a/indra/newview/skins/default/xui/en/menu_inspect_self_gear.xml +++ /dev/null @@ -1,252 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<toggleable_menu - layout="topleft" - name="Self Pie"> - <menu_item_call - label="Sit Down" - layout="topleft" - name="Sit Down Here"> - <menu_item_call.on_click - function="Self.SitDown" - parameter="" /> - <menu_item_call.on_enable - function="Self.EnableSitDown" /> - </menu_item_call> - <menu_item_call - label="Stand Up" - layout="topleft" - name="Stand Up"> - <menu_item_call.on_click - function="Self.StandUp" - parameter="" /> - <menu_item_call.on_enable - function="Self.EnableStandUp" /> - </menu_item_call> - <context_menu - label="Take Off" - layout="topleft" - name="Take Off >"> - <context_menu - label="Clothes" - layout="topleft" - name="Clothes >"> - <menu_item_call - enabled="false" - label="Shirt" - layout="topleft" - name="Shirt"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="shirt" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="shirt" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Pants" - layout="topleft" - name="Pants"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="pants" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="pants" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Skirt" - layout="topleft" - name="Skirt"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="skirt" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="skirt" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Shoes" - layout="topleft" - name="Shoes"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="shoes" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="shoes" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Socks" - layout="topleft" - name="Socks"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="socks" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="socks" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Jacket" - layout="topleft" - name="Jacket"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="jacket" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="jacket" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Gloves" - layout="topleft" - name="Gloves"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="gloves" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="gloves" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Undershirt" - layout="topleft" - name="Self Undershirt"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="undershirt" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="undershirt" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Underpants" - layout="topleft" - name="Self Underpants"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="underpants" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="underpants" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Tattoo" - layout="topleft" - name="Self Tattoo"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="tattoo" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="tattoo" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Alpha" - layout="topleft" - name="Self Alpha"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="alpha" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="alpha" /> - </menu_item_call> - <menu_item_separator - layout="topleft" /> - <menu_item_call - label="All Clothes" - layout="topleft" - name="All Clothes"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="all" /> - </menu_item_call> - </context_menu> - <context_menu - label="HUD" - layout="topleft" - name="Object Detach HUD" /> - <context_menu - label="Detach" - layout="topleft" - name="Object Detach" /> - <menu_item_call - label="Detach All" - layout="topleft" - name="Detach All"> - <menu_item_call.on_click - function="Self.RemoveAllAttachments" - parameter="" /> - <menu_item_call.on_enable - function="Self.EnableRemoveAllAttachments" /> - </menu_item_call> - </context_menu> - <menu_item_call - label="Change Outfit" - layout="topleft" - name="Chenge Outfit"> - <menu_item_call.on_click - function="CustomizeAvatar" /> - <menu_item_call.on_enable - function="Edit.EnableCustomizeAvatar" /> - </menu_item_call> - <menu_item_call label="Edit My Outfit" - layout="topleft" - name="Edit Outfit"> - <menu_item_call.on_click - function="EditOutfit" /> - <menu_item_call.on_enable - function="Edit.EnableCustomizeAvatar" /> - </menu_item_call> - <menu_item_call label="Edit My Shape" - layout="topleft" - name="Edit My Shape"> - <menu_item_call.on_click - function="EditShape" /> - <menu_item_call.on_enable - function="Edit.EnableEditShape" /> - </menu_item_call> - <menu_item_call - label="My Friends" - layout="topleft" - name="Friends..."> - <menu_item_call.on_click - function="SideTray.PanelPeopleTab" - parameter="friends_panel" /> - </menu_item_call> - <menu_item_call - label="My Groups" - layout="topleft" - name="Groups..."> - <menu_item_call.on_click - function="SideTray.PanelPeopleTab" - parameter="groups_panel" /> - </menu_item_call> - <menu_item_call - label="My Profile" - layout="topleft" - name="Profile..."> - <menu_item_call.on_click - function="ShowAgentProfile" - parameter="agent" /> - </menu_item_call> - <menu_item_call - label="Debug Textures" - name="Debug..."> - <menu_item_call.on_click - function="Avatar.Debug" /> - <menu_item_call.on_visible - function="IsGodCustomerService"/> - </menu_item_call> -</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_participant_view.xml b/indra/newview/skins/default/xui/en/menu_participant_view.xml new file mode 100644 index 0000000000..6401b0e3b7 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_participant_view.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + layout="topleft" + name="participant_manu_view"> + <menu_item_check + label="Open conversation log" + name="Conversation" + visible="true"> + <menu_item_check.on_check + function="Floater.Visible" + parameter="conversation" /> + <menu_item_check.on_click + function="Floater.Toggle" + parameter="conversation" /> + </menu_item_check> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_blocked_gear.xml b/indra/newview/skins/default/xui/en/menu_people_blocked_gear.xml new file mode 100644 index 0000000000..63295ea27b --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_blocked_gear.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + name="menu_blocked_gear" + left="0" bottom="0" visible="false" + mouse_opaque="false"> + <menu_item_call + label="Unblock" + name="unblock"> + <on_click + function="Block.Action" + parameter="unblock_item" /> + <on_enable + function="Block.Enable" + parameter="unblock_item" /> + </menu_item_call> + <menu_item_call + label="Profile..." + name="profile"> + <on_click + function="Block.Action" + parameter="profile_item"/> + <on_enable + function="Block.Enable" + parameter="profile_item" /> + </menu_item_call> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_blocked_plus.xml b/indra/newview/skins/default/xui/en/menu_people_blocked_plus.xml new file mode 100644 index 0000000000..0c7155667e --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_blocked_plus.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + name="menu_blocked_plus" + left="0" bottom="0" visible="false" + mouse_opaque="false"> + <menu_item_call + label="Block Resident by name..." + name="block_resident_by_name"> + <on_click + function="Block.Action" + parameter="block_res_by_name"/> + </menu_item_call> + <menu_item_call + label="Block object by name" + name="block_object_by_name"> + <on_click + function="Block.Action" + parameter="block_obj_by_name"/> + </menu_item_call> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_blocked_view.xml b/indra/newview/skins/default/xui/en/menu_people_blocked_view.xml new file mode 100644 index 0000000000..2efb70ee37 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_blocked_view.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + name="menu_blocked_view" + left="0" bottom="0" visible="false" + mouse_opaque="false"> + <menu_item_check + label="Sort by name" + name="sort_by_name"> + <on_click + function="Block.Action" + parameter="sort_by_name"/> + <on_check + function="Block.Check" + parameter="sort_by_name"/> + </menu_item_check> + <menu_item_check + label="Sort by type" + name="sort_by_type"> + <on_click + function="Block.Action" + parameter="sort_by_type" /> + <on_check + function="Block.Check" + parameter="sort_by_type" /> + </menu_item_check> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_friends_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_friends_view.xml index b452f96e7a..eab7b8c085 100644 --- a/indra/newview/skins/default/xui/en/menu_people_friends_view_sort.xml +++ b/indra/newview/skins/default/xui/en/menu_people_friends_view.xml @@ -40,8 +40,4 @@ function="CheckControl" parameter="FriendsListShowPermissions" /> </menu_item_check> - <menu_item_separator layout="topleft" /> - <menu_item_call name="show_blocked_list" label="Show Blocked Residents & Objects"> - <menu_item_call.on_click function="People.Friends.ViewSort.Action" parameter="panel_block_list_sidetray" /> - </menu_item_call> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_groups.xml b/indra/newview/skins/default/xui/en/menu_people_groups.xml index 8f89d37dbb..1e0364b84e 100644 --- a/indra/newview/skins/default/xui/en/menu_people_groups.xml +++ b/indra/newview/skins/default/xui/en/menu_people_groups.xml @@ -1,8 +1,18 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu name="menu_group_plus" +<toggleable_menu name="menu_group_plus" left="0" bottom="0" visible="false" mouse_opaque="false" opaque="true" color="MenuDefaultBgColor"> <menu_item_call + label="Activate" + name="Activate"> + <menu_item_call.on_click + function="People.Groups.Action" + parameter="activate" /> + <menu_item_call.on_enable + function="People.Groups.Enable" + parameter="activate" /> + </menu_item_call> + <menu_item_call label="View Info" name="View Info"> <menu_item_call.on_click @@ -23,7 +33,7 @@ parameter="chat" /> </menu_item_call> <menu_item_call - label="Call" + label="Voice call" name="Call"> <menu_item_call.on_click function="People.Groups.Action" @@ -34,17 +44,6 @@ </menu_item_call> <menu_item_separator /> <menu_item_call - label="Activate" - name="Activate"> - <menu_item_call.on_click - function="People.Groups.Action" - parameter="activate" /> - <menu_item_call.on_enable - function="People.Groups.Enable" - parameter="activate" /> - </menu_item_call> - <menu_item_separator /> - <menu_item_call label="Leave" name="Leave"> <menu_item_call.on_click @@ -54,4 +53,4 @@ function="People.Groups.Enable" parameter="leave" /> </menu_item_call> -</menu> +</toggleable_menu> 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.xml index c710fe3b9b..73f79f1e70 100644 --- a/indra/newview/skins/default/xui/en/menu_people_groups_view_sort.xml +++ b/indra/newview/skins/default/xui/en/menu_people_groups_view.xml @@ -14,13 +14,4 @@ function="CheckControl" parameter="GroupListShowIcons" /> </menu_item_check> - <menu_item_call - label="Leave Selected Group" - layout="topleft" - name="Leave Selected Group"> - <menu_item_call.on_click - function="People.Group.Minus.Action"/> - <menu_item_call.on_enable - function="People.Group.Minus.Enable"/> - </menu_item_call> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_nearby.xml b/indra/newview/skins/default/xui/en/menu_people_nearby.xml index d2e35e4cc0..b7c9ab1fe3 100644 --- a/indra/newview/skins/default/xui/en/menu_people_nearby.xml +++ b/indra/newview/skins/default/xui/en/menu_people_nearby.xml @@ -10,12 +10,39 @@ function="Avatar.Profile" /> </menu_item_call> <menu_item_call + label="IM" + layout="topleft" + name="IM"> + <menu_item_call.on_click + function="Avatar.IM" /> + </menu_item_call> + <menu_item_call + label="Offer Teleport" + name="teleport"> + <menu_item_call.on_click + function="Avatar.OfferTeleport"/> + <menu_item_call.on_enable + function="Avatar.EnableItem" + parameter="can_offer_teleport"/> + </menu_item_call> + <menu_item_call + label="Voice call" + layout="topleft" + name="Call"> + <menu_item_call.on_click + function="Avatar.Call" /> + <menu_item_call.on_enable + function="Avatar.EnableItem" + parameter="can_call" /> + </menu_item_call> + <menu_item_separator /> + <menu_item_call label="Add Friend" layout="topleft" name="Add Friend"> <menu_item_call.on_click function="Avatar.AddFriend" /> - <menu_item_call.on_enable + <menu_item_call.on_visible function="Avatar.EnableItem" parameter="can_add" /> </menu_item_call> @@ -30,22 +57,13 @@ parameter="can_delete" /> </menu_item_call> <menu_item_call - label="IM" + label="Invite to group..." layout="topleft" - name="IM"> + name="Invite"> <menu_item_call.on_click - function="Avatar.IM" /> - </menu_item_call> - <menu_item_call - label="Call" - layout="topleft" - name="Call"> - <menu_item_call.on_click - function="Avatar.Call" /> - <menu_item_call.on_enable - function="Avatar.EnableItem" - parameter="can_call" /> + function="Avatar.InviteToGroup" /> </menu_item_call> + <menu_item_separator /> <menu_item_call label="Map" layout="topleft" @@ -83,13 +101,5 @@ function="Avatar.EnableItem" parameter="can_block" /> </menu_item_check> - <menu_item_call - label="Offer Teleport" - name="teleport"> - <menu_item_call.on_click - function="Avatar.OfferTeleport"/> - <menu_item_call.on_enable - function="Avatar.EnableItem" - parameter="can_offer_teleport"/> - </menu_item_call> + </context_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_nearby_view.xml b/indra/newview/skins/default/xui/en/menu_people_nearby_view.xml new file mode 100644 index 0000000000..da88ca9f4d --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_nearby_view.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + name="menu_group_plus" + left="0" bottom="0" visible="false" + mouse_opaque="false"> + <menu_item_check + label="Sort by Recent Speakers" + name="sort_by_recent_speakers"> + <menu_item_check.on_click + function="People.Nearby.ViewSort.Action" + parameter="sort_by_recent_speakers"/> + <menu_item_check.on_check + function="People.Nearby.ViewSort.CheckItem" + parameter="sort_by_recent_speakers"/> + </menu_item_check> + <menu_item_check + label="Sort by Name" + name="sort_name"> + <menu_item_check.on_click + function="People.Nearby.ViewSort.Action" + parameter="sort_name"/> + <menu_item_check.on_check + function="People.Nearby.ViewSort.CheckItem" + parameter="sort_name"/> + </menu_item_check> + <menu_item_check + label="Sort by Distance" + name="sort_distance"> + <menu_item_check.on_click + function="People.Nearby.ViewSort.Action" + parameter="sort_distance"/> + <menu_item_check.on_check + function="People.Nearby.ViewSort.CheckItem" + parameter="sort_distance"/> + </menu_item_check> + <menu_item_separator layout="topleft" /> + <menu_item_check name="view_icons" label="View People Icons"> + <menu_item_check.on_click + function="People.Nearby.ViewSort.Action" + parameter="view_icons" /> + <menu_item_check.on_check + function="CheckControl" + parameter="NearbyListShowIcons" /> + </menu_item_check> + <menu_item_check name ="view_map" label="View Map"> + <menu_item_check.on_check + function="CheckControl" + parameter="NearbyListShowMap" /> + <menu_item_check.on_click + function="ToggleControl" + parameter="NearbyListShowMap" /> + </menu_item_check> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_nearby_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_nearby_view_sort.xml deleted file mode 100644 index 614dd693c5..0000000000 --- a/indra/newview/skins/default/xui/en/menu_people_nearby_view_sort.xml +++ /dev/null @@ -1,57 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<toggleable_menu - name="menu_group_plus" - left="0" bottom="0" visible="false" - mouse_opaque="false"> - <menu_item_check - label="Sort by Recent Speakers" - name="sort_by_recent_speakers"> - <menu_item_check.on_click - function="People.Nearby.ViewSort.Action" - parameter="sort_by_recent_speakers"/> - <menu_item_check.on_check - function="People.Nearby.ViewSort.CheckItem" - parameter="sort_by_recent_speakers"/> - </menu_item_check> - <menu_item_check - label="Sort by Name" - name="sort_name"> - <menu_item_check.on_click - function="People.Nearby.ViewSort.Action" - parameter="sort_name"/> - <menu_item_check.on_check - function="People.Nearby.ViewSort.CheckItem" - parameter="sort_name"/> - </menu_item_check> - <menu_item_check - label="Sort by Distance" - name="sort_distance"> - <menu_item_check.on_click - function="People.Nearby.ViewSort.Action" - parameter="sort_distance"/> - <menu_item_check.on_check - function="People.Nearby.ViewSort.CheckItem" - parameter="sort_distance"/> - </menu_item_check> - <menu_item_separator layout="topleft" /> - <menu_item_check name="view_icons" label="View People Icons"> - <menu_item_check.on_click - function="People.Nearby.ViewSort.Action" - parameter="view_icons" /> - <menu_item_check.on_check - function="CheckControl" - parameter="NearbyListShowIcons" /> - </menu_item_check> - <menu_item_check name ="view_map" label="View Map"> - <menu_item_check.on_check - function="CheckControl" - parameter="NearbyListShowMap" /> - <menu_item_check.on_click - function="ToggleControl" - parameter="NearbyListShowMap" /> - </menu_item_check> - <menu_item_separator layout="topleft" /> - <menu_item_call name="show_blocked_list" label="Show Blocked Residents & Objects"> - <menu_item_call.on_click function="People.Nearby.ViewSort.Action" userdata="panel_block_list_sidetray" /> - </menu_item_call> -</toggleable_menu> 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.xml index 485a5a658c..1dbc90dd2b 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.xml @@ -32,8 +32,4 @@ function="CheckControl" parameter="RecentListShowIcons" /> </menu_item_check> - <menu_item_separator layout="topleft" /> - <menu_item_call name="show_blocked_list" label="Show Blocked Residents & Objects"> - <menu_item_call.on_click function="People.Recent.ViewSort.Action" userdata="panel_block_list_sidetray" /> - </menu_item_call> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 13f073a1c2..6df02a25af 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -3048,6 +3048,7 @@ Would you like to trust this authority? icon="alertmodal.tga" name="GrantedModifyRights" persist="true" + log_to_im="true" type="notify"> [NAME] has given you permission to edit their objects. </notification> @@ -3056,6 +3057,7 @@ Would you like to trust this authority? icon="alertmodal.tga" name="RevokedModifyRights" persist="true" + log_to_im="true" type="notify"> Your privilege to modify [NAME]'s objects has been revoked </notification> @@ -4240,6 +4242,8 @@ Are you sure you want to change the Estate Covenant? <notification icon="notifytip.tga" name="RegionEntryAccessBlocked_Notify" + log_to_im="false" + log_to_chat="true" type="notifytip"> <tag>fail</tag> The region you're trying to visit contains [REGIONMATURITY] content, but your current preferences are set to exclude [REGIONMATURITY] content. @@ -4248,6 +4252,8 @@ The region you're trying to visit contains [REGIONMATURITY] content, but your cu <notification icon="notifytip.tga" name="RegionEntryAccessBlocked_NotifyAdultsOnly" + log_to_im="false" + log_to_chat="true" type="notifytip"> <tag>fail</tag> The region you're trying to visit contains [REGIONMATURITY] content, which is accessible to adults only. @@ -4319,6 +4325,8 @@ The region you're trying to visit contains [REGIONMATURITY] content, but your cu <notification icon="notifytip.tga" name="TeleportEntryAccessBlocked_Notify" + log_to_im="false" + log_to_chat="true" type="notifytip"> <unique> <context>REGIONMATURITY</context> @@ -4330,6 +4338,8 @@ The region you're trying to visit contains [REGIONMATURITY] content, but your cu <notification icon="notifytip.tga" name="TeleportEntryAccessBlocked_NotifyAdultsOnly" + log_to_im="false" + log_to_chat="true" type="notifytip"> <unique> <context>REGIONMATURITY</context> @@ -4450,6 +4460,8 @@ You won't receive any more notifications that you're about to visit a region wit <notification icon="notifytip.tga" name="LandClaimAccessBlocked_Notify" + log_to_im="false" + log_to_chat="true" type="notifytip"> The land you're trying to claim contains [REGIONMATURITY] content, but your current preferences are set to exclude [REGIONMATURITY] content. <tag>fail</tag> @@ -4458,6 +4470,8 @@ You won't receive any more notifications that you're about to visit a region wit <notification icon="notifytip.tga" name="LandClaimAccessBlocked_NotifyAdultsOnly" + log_to_im="false" + log_to_chat="true" type="notifytip"> <tag>fail</tag> The land you're trying to claim contains [REGIONMATURITY] content, which is accessible to adults only. @@ -4515,6 +4529,8 @@ You won't receive any more notifications that you're about to visit a region wit <notification icon="notifytip.tga" name="LandBuyAccessBlocked_Notify" + log_to_im="false" + log_to_chat="true" type="notifytip"> The land you're trying to buy contains [REGIONMATURITY] content, but your current preferences are set to exclude [REGIONMATURITY] content. <tag>fail</tag> @@ -4523,6 +4539,8 @@ You won't receive any more notifications that you're about to visit a region wit <notification icon="notifytip.tga" name="LandBuyAccessBlocked_NotifyAdultsOnly" + log_to_im="false" + log_to_chat="true" type="notifytip"> <tag>fail</tag> The land you're trying to buy contains [REGIONMATURITY] content, which is accessible to adults only. @@ -5464,6 +5482,8 @@ The string [STRING_NAME] is missing from strings.xml <notification icon="notifytip.tga" name="IMSystemMessageTip" + log_to_im="true" + log_to_chat="false" type="notifytip"> [MESSAGE] </notification> @@ -5507,18 +5527,14 @@ Topic: [SUBJECT], Message: [MESSAGE] <notification icon="notifytip.tga" - name="FriendOnline" + name="FriendOnlineOffline" + log_to_chat="false" type="notifytip"> <tag>friendship</tag> -<nolink>[NAME]</nolink> is Online - </notification> - - <notification - icon="notifytip.tga" - name="FriendOffline" - type="notifytip"> - <tag>friendship</tag> -<nolink>[NAME]</nolink> is Offline +<nolink>[NAME]</nolink> is [STATUS] + <unique combine="cancel_old"> + <context>NAME</context> + </unique> </notification> <notification @@ -5762,6 +5778,8 @@ You don't have permission to copy this. <notification icon="notifytip.tga" name="InventoryAccepted" + log_to_im="true" + log_to_chat="false" type="notifytip"> [NAME] received your inventory offer. </notification> @@ -5769,6 +5787,8 @@ You don't have permission to copy this. <notification icon="notifytip.tga" name="InventoryDeclined" + log_to_im="true" + log_to_chat="false" type="notifytip"> [NAME] declined your inventory offer. </notification> @@ -5850,6 +5870,7 @@ Please select at least one type of content to search (General, Moderate, or Adul <notification icon="notify.tga" name="PaymentReceived" + log_to_im="true" persist="true" type="notify"> <tag>funds</tag> @@ -5859,6 +5880,7 @@ Please select at least one type of content to search (General, Moderate, or Adul <notification icon="notify.tga" name="PaymentSent" + log_to_im="true" persist="true" type="notify"> <tag>funds</tag> @@ -6003,6 +6025,7 @@ The objects on the selected parcel that are NOT owned by you have been returned <notification icon="notify.tga" name="ServerObjectMessage" + log_to_im="true" persist="true" type="notify"> Message from [NAME]: @@ -6421,6 +6444,7 @@ Your object named <nolink>[OBJECTFROMNAME]</nolink> has given you th <notification icon="notify.tga" name="UserGiveItem" + log_to_im ="true" type="offer"> [NAME_SLURL] has given you this [OBJECTTYPE]: [ITEM_SLURL] @@ -6476,6 +6500,8 @@ Your object named <nolink>[OBJECTFROMNAME]</nolink> has given you th <notification icon="notify.tga" name="TeleportOffered" + log_to_im="true" + log_to_chat="false" type="offer"> [NAME_SLURL] has offered to teleport you to their location: @@ -6497,6 +6523,8 @@ Your object named <nolink>[OBJECTFROMNAME]</nolink> has given you th <notification icon="notify.tga" name="TeleportOffered_MaturityExceeded" + log_to_im="true" + log_to_chat="false" type="offer"> [NAME_SLURL] has offered to teleport you to their location: @@ -6520,6 +6548,8 @@ This region contains [REGION_CONTENT_MATURITY] content, but your current prefere <notification icon="notify.tga" name="TeleportOffered_MaturityBlocked" + log_to_im="true" + log_to_chat="false" type="notifytip"> [NAME_SLURL] has offered to teleport you to their location: @@ -6533,6 +6563,9 @@ However, this region contains content accessible to adults only. <notification icon="notify.tga" name="TeleportOfferSent" + log_to_im="true" + log_to_chat="false" + show_toast="false" type="offer"> Teleport offer sent to [TO_NAME] </notification> @@ -6560,6 +6593,7 @@ However, this region contains content accessible to adults only. <notification icon="notify.tga" name="OfferFriendship" + log_to_im="true" type="offer"> <tag>friendship</tag> <tag>confirm</tag> @@ -6583,6 +6617,8 @@ However, this region contains content accessible to adults only. <notification icon="notify.tga" name="FriendshipOffered" + log_to_im="true" + show_toast="false" type="offer"> <tag>friendship</tag> You have offered friendship to [TO_NAME] @@ -6612,6 +6648,7 @@ However, this region contains content accessible to adults only. <notification icon="notify.tga" name="FriendshipAccepted" + log_to_im="true" type="offer"> <tag>friendship</tag> <nolink>[NAME]</nolink> accepted your friendship offer. @@ -6620,6 +6657,7 @@ However, this region contains content accessible to adults only. <notification icon="notify.tga" name="FriendshipDeclined" + log_to_im="true" persist="true" type="notify"> <tag>friendship</tag> @@ -6629,6 +6667,8 @@ However, this region contains content accessible to adults only. <notification icon="notify.tga" name="FriendshipAcceptedByMe" + log_to_im="true" + show_toast="false" type="offer"> <tag>friendship</tag> Friendship offer accepted. @@ -6637,6 +6677,8 @@ Friendship offer accepted. <notification icon="notify.tga" name="FriendshipDeclinedByMe" + log_to_im="true" + show_toast="false" type="offer"> <tag>friendship</tag> Friendship offer declined. diff --git a/indra/newview/skins/default/xui/en/panel_adhoc_control_panel.xml b/indra/newview/skins/default/xui/en/panel_adhoc_control_panel.xml deleted file mode 100644 index d68fa6ca6c..0000000000 --- a/indra/newview/skins/default/xui/en/panel_adhoc_control_panel.xml +++ /dev/null @@ -1,95 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<panel - border="false" - follows="all" - height="215" - name="panel_im_control_panel" - width="150"> - <layout_stack - mouse_opaque="false" - border_size="0" - clip="false" - follows="all" - height="215" - layout="topleft" - left="3" - name="vertical_stack" - orientation="vertical" - top="0" - width="147"> - <layout_panel - auto_resize="true" - follows="top|left" - height="130" - layout="topleft" - left="0" - min_height="0" - mouse_opaque="false" - width="147" - top="0" - name="speakers_list_panel"> - <avatar_list - color="DkGray2" - follows="all" - height="130" - ignore_online_status="true" - layout="topleft" - name="speakers_list" - opaque="false" - show_info_btn="true" - show_profile_btn="false" - show_speaking_indicator="false" - width="147" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="130" - name="call_btn_panel" - visible="false"> - <button - follows="all" - height="20" - label="Call" - name="call_btn" - width="130" - top="0" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="130" - name="end_call_btn_panel" - visible="false"> - <button - follows="all" - height="20" - label="Leave Call" - name="end_call_btn" - top="0"/> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="130" - name="voice_ctrls_btn_panel" - visible="false"> - <button - follows="all" - height="20" - label="Voice Controls" - name="voice_ctrls_btn" - top="0" - use_ellipses="true" /> - </layout_panel> - </layout_stack> -</panel> diff --git a/indra/newview/skins/default/xui/en/panel_block_list_sidetray.xml b/indra/newview/skins/default/xui/en/panel_block_list_sidetray.xml index 7c67fd7f83..24f7d44cce 100644 --- a/indra/newview/skins/default/xui/en/panel_block_list_sidetray.xml +++ b/indra/newview/skins/default/xui/en/panel_block_list_sidetray.xml @@ -4,88 +4,95 @@ follows="left|top|right|bottom" height="305" layout="topleft" + left="0" name="block_list_panel" help_topic="blocked_list" min_height="350" min_width="240" - width="280"> - <button - follows="top|left" - height="24" - image_hover_unselected="BackButton_Over" - image_pressed="BackButton_Press" - image_unselected="BackButton_Off" - layout="topleft" - name="back" - left="4" - tab_stop="false" - top="1" - width="30"/> - <text - follows="top|left|right" - font="SansSerifLargeBold" - height="20" - layout="topleft" - left_pad="10" - name="title_text" - text_color="White" - top="5" - width="250"> - Block List - </text> - <scroll_list + width="323"> + <panel + follows="left|top|right" + height="27" + label="bottom_panel" + layout="topleft" + left="0" + name="blocked_buttons_panel" + right="-1" + top="0"> + <filter_editor + follows="left|top|right" + height="23" + layout="topleft" + left="6" + label="Filter" + max_length_chars="300" + name="blocked_filter_input" + text_color="Black" + text_pad_left="10" + top="4" + width="177" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="OptionsMenu_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="8" + menu_filename="menu_people_blocked_gear.xml" + menu_position="bottomleft" + name="blocked_gear_btn" + top="3" + width="31" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + menu_filename="menu_people_blocked_view.xml" + menu_position="bottomleft" + name="view_btn" + top_delta="0" + width="31" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="AddItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + menu_filename="menu_people_blocked_plus.xml" + menu_position="bottomleft" + name="plus_btn" + top_delta="0" + width="31"/> + <button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="TrashItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + left_pad="2" + layout="topleft" + name="unblock_btn" + top_delta="0" + width="31"/> + </panel> + <block_list follows="all" - height="190" + height="273" layout="topleft" - left="5" + left="3" name="blocked" tool_tip="List of currently blocked Residents" - top="30" - width="270"> - <scroll_list.columns - name="item_name" /> - <scroll_list.columns - name="item_type" - width="96" /> - </scroll_list> - <button - follows="left|bottom" - height="23" - label="Block person" - layout="topleft" - left_delta="0" - name="Block resident..." - tool_tip="Pick a Resident to block" - top_pad="4" - width="210"> - <button.commit_callback - function="Block.ClickPick" /> - </button> - <button - follows="left|bottom" - height="23" - label="Block object by name" - layout="topleft" - left_delta="0" - name="Block object by name..." - tool_tip="Pick an object to block by name" - top_pad="4" - width="210" > - <button.commit_callback - function="Block.ClickBlockByName" /> - </button> - <button - enabled="false" - follows="left|bottom" - height="23" - label="Unblock" - layout="topleft" - left_delta="0" - name="Unblock" - tool_tip="Remove Resident or object from blocked list" - top_pad="4" - width="210" > - <button.commit_callback - function="Block.ClickRemove" /> - </button> + top="31" + right="-1"/> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_blocked_list_item.xml b/indra/newview/skins/default/xui/en/panel_blocked_list_item.xml new file mode 100644 index 0000000000..84e7e467b1 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_blocked_list_item.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + follows="top|right|left" + height="23" + layout="topleft" + left="0" + name="blocked_list_item" + top="0" + width="380"> + <icon + height="24" + follows="top|right|left" + image_name="ListItem_Select" + layout="topleft" + left="0" + name="selected_icon" + top="0" + visible="false" + width="380" /> + <icon + follows="top|right|left" + height="24" + image_name="ListItem_Over" + layout="topleft" + left="0" + name="hovered_icon" + top="0" + visible="false" + width="380" /> + <avatar_icon + default_icon_name="Generic_Person" + follows="top|left" + height="20" + layout="topleft" + left="5" + mouse_opaque="true" + top="2" + visible="false" + width="20" /> + <group_icon + default_icon_name="Generic_Group" + follows="top|left" + height="20" + layout="topleft" + left="5" + mouse_opaque="true" + top="2" + visible="false" + width="20" /> + <icon + follows="top|left" + height="16" + image_name="Inv_Object" + layout="topleft" + left="7" + name="object_icon" + top="4" + visible="false" + width="16" /> + <text + follows="left|right" + font="SansSerifSmall" + font.color="DkGray" + height="15" + layout="topleft" + left_pad="5" + name="item_name" + parse_urls="false" + top="6" + use_ellipses="true" + width="180" /> +</panel>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/panel_conversation_log_list_item.xml b/indra/newview/skins/default/xui/en/panel_conversation_log_list_item.xml new file mode 100644 index 0000000000..3c98e32e7d --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_conversation_log_list_item.xml @@ -0,0 +1,108 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + follows="top|right|left" + height="23" + layout="topleft" + left="0" + name="conversation_log_list_item" + top="0" + width="380"> + <icon + height="24" + follows="top|right|left" + image_name="ListItem_Select" + layout="topleft" + left="0" + name="selected_icon" + top="0" + visible="false" + width="380" /> + <icon + follows="top|right|left" + height="24" + image_name="ListItem_Over" + layout="topleft" + left="0" + name="hovered_icon" + top="0" + visible="false" + width="380" /> + <icon + default_icon_name="voice_session_icon" + follows="top|left" + height="20" + layout="topleft" + left="5" + image_name="Audio_Press" + mouse_opaque="true" + name="voice_session_icon" + top="2" + visible="false" + width="20" /> + <icon + default_icon_name="incoming_unread_im_icon" + follows="top|left" + height="20" + layout="topleft" + left="5" + image_name="Movement_Backward_Off" + mouse_opaque="false" + name="unread_ims_icon" + top="2" + visible="false" + width="20" /> + <avatar_icon + default_icon_name="Generic_Person" + follows="top|left" + height="20" + layout="topleft" + left_pad="5" + mouse_opaque="true" + top="2" + visible="false" + width="20" /> + <group_icon + default_icon_name="Generic_Group" + follows="top|left" + height="20" + layout="topleft" + mouse_opaque="true" + top="2" + visible="false" + width="20" /> + <text + follows="left|right" + font="SansSerifSmall" + font.color="DkGray" + height="15" + layout="topleft" + left_pad="5" + name="conversation_name" + parse_urls="false" + top="6" + use_ellipses="true" + width="180" /> + <text + follows="right" + font="SansSerifSmall" + font.color="DkGray" + height="15" + layout="topleft" + left_pad="5" + name="date_time" + parse_urls="false" + top="6" + use_ellipses="true" + width="110"/> + <button + name="delete_btn" + layout="topleft" + follows="top|right" + image_unselected="Toast_CloseBtn" + image_selected="Toast_CloseBtn" + top="5" + left_pad="0" + height="14" + width="14" + tab_stop="false"/> +</panel>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/panel_group_control_panel.xml b/indra/newview/skins/default/xui/en/panel_group_control_panel.xml deleted file mode 100644 index ad10e53a4e..0000000000 --- a/indra/newview/skins/default/xui/en/panel_group_control_panel.xml +++ /dev/null @@ -1,109 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<panel - border="false" - follows="all" - height="238" - name="panel_im_control_panel" - width="150"> - <layout_stack - mouse_opaque="false" - border_size="0" - clip="false" - follows="all" - height="238" - layout="topleft" - left="5" - name="vertical_stack" - orientation="vertical" - top="0" - width="145"> - <layout_panel - auto_resize="true" - follows="top|left" - height="100" - layout="topleft" - min_height="0" - mouse_opaque="false" - width="145" - top="0" - name="speakers_list_panel"> - <avatar_list - color="DkGray2" - follows="all" - height="100" - ignore_online_status="true" - layout="topleft" - name="speakers_list" - opaque="false" - show_info_btn="true" - show_profile_btn="false" - show_speaking_indicator="false" - width="145" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="28" - layout="topleft" - min_height="28" - width="130" - name="group_info_btn_panel"> - <button - follows="left|right|bottom" - height="23" - label="Group Profile" - name="group_info_btn" - use_ellipses="true" - top="5" - width="130" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="28" - layout="topleft" - min_height="28" - width="130" - name="call_btn_panel"> - <button - follows="all" - height="23" - label="Call Group" - name="call_btn" - use_ellipses="true" - width="130" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="28" - layout="topleft" - min_height="28" - width="130" - name="end_call_btn_panel" - visible="false"> - <button - follows="all" - height="23" - label="Leave Call" - name="end_call_btn" - use_ellipses="true" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="28" - layout="topleft" - min_height="28" - width="130" - name="voice_ctrls_btn_panel" - visible="false"> - <button - follows="all" - height="23" - label="Open Voice Controls" - name="voice_ctrls_btn" - use_ellipses="true" /> - </layout_panel> - </layout_stack> -</panel> diff --git a/indra/newview/skins/default/xui/en/panel_im_control_panel.xml b/indra/newview/skins/default/xui/en/panel_im_control_panel.xml deleted file mode 100644 index 8fcd6ccbaf..0000000000 --- a/indra/newview/skins/default/xui/en/panel_im_control_panel.xml +++ /dev/null @@ -1,166 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<panel - border="false" - height="300" - name="panel_im_control_panel" - width="150"> - <avatar_icon - follows="left|top" - height="105" - left_delta="20" - name="avatar_icon" - top="-5" - width="114"/> - <layout_stack - mouse_opaque="false" - border_size="0" - clip="false" - follows="all" - height="183" - layout="topleft" - left="5" - name="button_stack" - orientation="vertical" - top_pad="5" - width="145"> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="20" - layout="topleft" - left="2" - min_height="20" - width="140" - name="view_profile_btn_panel" - top="0" > - <button - follows="left|top|right" - height="23" - label="Profile" - name="view_profile_btn" - top="0" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="add_friend_btn_panel"> - <button - follows="left|top|right" - height="23" - label="Add Friend" - name="add_friend_btn" - top="5" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="teleport_btn_panel"> - <button - auto_resize="false" - follows="left|top|right" - height="23" - label="Teleport" - name="teleport_btn" - tool_tip = "Offer to teleport this person" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="share_btn_panel"> - <button - auto_resize="true" - follows="left|top|right" - height="23" - label="Share" - name="share_btn" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="pay_btn_panel"> - <button - auto_resize="true" - follows="left|top|right" - height="23" - label="Pay" - name="pay_btn" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="call_btn_panel"> - <button - follows="left|top|right" - height="23" - label="Call" - name="call_btn" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="end_call_btn_panel" - visible="false"> - <button - follows="left|top|right" - height="23" - label="End Call" - name="end_call_btn" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="voice_ctrls_btn_panel" - visible="false"> - <button - follows="left|top|right" - height="23" - label="Voice Controls" - name="voice_ctrls_btn" - width="140" /> - </layout_panel> - <layout_panel - mouse_opaque="false" - auto_resize="true" - follows="top|left" - height="0" - layout="topleft" - min_height="0" - width="140" - name="spacer"/> - </layout_stack> -</panel> diff --git a/indra/newview/skins/default/xui/en/panel_inbox_inventory.xml b/indra/newview/skins/default/xui/en/panel_inbox_inventory.xml index 413e22e444..433a3181cd 100644 --- a/indra/newview/skins/default/xui/en/panel_inbox_inventory.xml +++ b/indra/newview/skins/default/xui/en/panel_inbox_inventory.xml @@ -2,7 +2,7 @@ <inbox_inventory_panel accepts_drag_and_drop="false" name="inventory_inbox" - start_folder="Received Items" + start_folder.type="inbox" follows="all" layout="topleft" top="0" left="0" height="165" width="308" top_pad="0" diff --git a/indra/newview/skins/default/xui/en/panel_landmarks.xml b/indra/newview/skins/default/xui/en/panel_landmarks.xml index 2a5933e3e9..67a09949ce 100644 --- a/indra/newview/skins/default/xui/en/panel_landmarks.xml +++ b/indra/newview/skins/default/xui/en/panel_landmarks.xml @@ -35,7 +35,9 @@ left="0" mouse_opaque="true" name="favorites_list" - start_folder="Favorites" + scroll.hide_scrollbar="true" + folder_view.use_ellipses="true" + start_folder.name="Favorites" width="307"/> </accordion_tab> <accordion_tab @@ -51,7 +53,9 @@ left="0" mouse_opaque="true" name="landmarks_list" - start_folder="Landmarks" + scroll.hide_scrollbar="true" + folder_view.use_ellipses="true" + start_folder.name="Landmarks" width="307"/> </accordion_tab> <accordion_tab @@ -67,7 +71,9 @@ left="0" mouse_opaque="true" name="my_inventory_list" - start_folder="My Inventory" + scroll.hide_scrollbar="true" + folder_view.use_ellipses="true" + start_folder.name="My Inventory" width="307"/> </accordion_tab> <accordion_tab @@ -83,7 +89,9 @@ left="0" mouse_opaque="true" name="library_list" - start_folder="LIBRARY" + scroll.hide_scrollbar="true" + folder_view.use_ellipses="true" + start_folder.name="LIBRARY" width="313"/> </accordion_tab> </accordion> diff --git a/indra/newview/skins/default/xui/en/panel_nearby_chat.xml b/indra/newview/skins/default/xui/en/panel_nearby_chat.xml index d683116eb8..4de56b424e 100644 --- a/indra/newview/skins/default/xui/en/panel_nearby_chat.xml +++ b/indra/newview/skins/default/xui/en/panel_nearby_chat.xml @@ -1,20 +1,22 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel follows="all" - height="300" + top="0" + bottom_delta="10" help_topic="nearby_chat" layout="topleft" name="nearby_chat" - width="320"> + width="242" + height="169"> <layout_stack follows="all" - height="295" + height="164" layout="topleft" left="0" name="stack" top="5" orientation="vertical" - width="320"> + width="242"> <layout_panel auto_resize="false" height="26" @@ -23,7 +25,7 @@ name="translate_chat_checkbox_lp" top_delta="0" visible="true" - width="313"> + width="230"> <check_box top="10" control_name="TranslateChat" @@ -33,15 +35,15 @@ layout="topleft" left="5" name="translate_chat_checkbox" - width="300" /> + width="230" /> </layout_panel> <layout_panel auto_resize="true" - height="277" + height="138" left_delta="0" layout="topleft" name="chat_history_lp" - width="318"> + width="242"> <chat_history bg_readonly_color="ChatHistoryBgColor" bg_writeable_color="ChatHistoryBgColor" @@ -49,7 +51,7 @@ layout="topleft" left="5" left_widget_pad="0" - height="272" + height="138" name="chat_history" parse_highlights="true" parse_urls="true" @@ -57,7 +59,7 @@ text_color="ChatHistoryTextColor" text_readonly_color="ChatHistoryTextColor" top="0" - width="313" /> + width="237" /> </layout_panel> </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_outbox_inventory.xml b/indra/newview/skins/default/xui/en/panel_outbox_inventory.xml index a3d39e55af..203febbf18 100644 --- a/indra/newview/skins/default/xui/en/panel_outbox_inventory.xml +++ b/indra/newview/skins/default/xui/en/panel_outbox_inventory.xml @@ -1,7 +1,10 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<outbox_inventory_panel +<inventory_panel name="inventory_outbox" - start_folder="Outbox" + start_folder.name="Outbox" + show_empty_message="false" + show_load_status="false" + start_folder.type="outbox" follows="all" layout="topleft" top="0" left="0" height="165" width="308" top_pad="0" @@ -12,6 +15,11 @@ bevel_style="none" show_item_link_overlays="true" tool_tip="Drag and drop items here to prepare them for sale on your storefront" - > - <scroll reserve_scroll_corner="false" /> -</outbox_inventory_panel> + scroll.reserve_scroll_corner="false"> + <folder folder_arrow_image="Folder_Arrow" + folder_indentation="8" + item_height="20" + item_top_pad="4" + selection_image="Rounded_Square"/> + <item allow_open="false"/> +</inventory_panel> diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml index 98c7c49ff4..09156b41b5 100644 --- a/indra/newview/skins/default/xui/en/panel_people.xml +++ b/indra/newview/skins/default/xui/en/panel_people.xml @@ -38,12 +38,6 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M name="no_filtered_friends_msg"> Didn't find what you're looking for? Try [secondlife:///app/search/people/[SEARCH_TERM] Search]. </string> - <string - name="people_filter_label" - value="Filter People" /> - <string - name="groups_filter_label" - value="Filter Groups" /> <!-- *WORKAROUND: for group_list.no_items_msg & group_list.no_filtered_items_msg attributes. They are not defined as translatable in VLT. See EXT-5931 @@ -60,21 +54,9 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M <string name="AltMiniMapToolTipMsg" value="[REGION](Double-click to teleport, shift-drag to pan)"/> - <filter_editor - follows="left|top|right" - height="23" - layout="topleft" - left="10" - label="Filter" - max_length_chars="300" - name="filter_input" - text_color="Black" - text_pad_left="10" - top="3" - width="303" /> <tab_container + bottom="-10" follows="all" - height="383" layout="topleft" left="3" name="tabs" @@ -82,31 +64,116 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M tab_min_width="70" tab_height="30" tab_position="top" - top_pad="10" + top="0" halign="center" - width="319"> - <panel + right="-5"> + +<!-- ================================= NEARBY tab =========================== --> + + <panel background_opaque="true" background_visible="true" bg_alpha_color="DkGray" bg_opaque_color="DkGray" + bottom="-1" follows="all" - height="383" label="NEARBY" layout="topleft" left="0" help_topic="people_nearby_tab" name="nearby_panel" - top="0" - width="313"> + right="-1" + top="0"> + <panel + follows="left|top|right" + height="27" + label="bottom_panel" + layout="topleft" + left="0" + name="nearby_buttons_panel" + right="-1" + top="0"> + <filter_editor + follows="left|top|right" + height="23" + layout="topleft" + left="6" + label="Filter People" + max_length_chars="300" + name="nearby_filter_input" + text_color="Black" + text_pad_left="10" + top="4" + width="178" /> + <button + commit_callback.function="People.Gear" + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="OptionsMenu_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="7" + name="gear_btn" + top="3" + width="31" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + menu_filename="menu_people_nearby_view.xml" + menu_position="bottomleft" + name="nearby_view_btn" + top_delta="0" + width="31" /> + <button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="AddItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + name="add_friend_btn" + top_delta="0" + width="31"> + <commit_callback + function="People.AddFriend" /> + </button> + <dnd_button + enabled="false" + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="TrashItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + left_pad="2" + layout="topleft" + name="nearby_del_btn" + top_delta="0" + width="31"> + <commit_callback + function="People.DelFriend" /> + </dnd_button> + </panel> <layout_stack clip="false" follows="all" - height="355" + height="410" layout="topleft" + left="0" mouse_opaque="false" orientation="vertical" - width="313"> + right="-1" + top_pad="0"> <layout_panel height="142" layout="topleft" @@ -123,16 +190,16 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M left="3" mouse_opaque="false" name="Net Map" - top="4" - width="305"/> + right="-1" + top="4" /> </layout_panel> <layout_panel height="213" layout="topleft" min_dim="100" mouse_opaque="false" - user_resize="true" - width="313"> + right="-1" + user_resize="true"> <avatar_list allow_select="true" follows="all" @@ -143,84 +210,118 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M keep_one_selected="false" multi_select="true" name="avatar_list" - top="2" - width="306" /> + right="-1" + top="2" /> </layout_panel> </layout_stack> - <panel - background_visible="true" - follows="left|right|bottom" - height="27" - label="bottom_panel" - layout="topleft" - left="3" - name="bottom_panel" - top_pad="0" - width="313"> - <menu_button - follows="bottom|left" - height="25" - image_hover_unselected="Toolbar_Left_Over" - image_overlay="OptionsMenu_Off" - image_selected="Toolbar_Left_Selected" - image_unselected="Toolbar_Left_Off" - layout="topleft" - left="0" - name="nearby_view_sort_btn" - tool_tip="Options" - top="1" - width="31" /> - <button - follows="bottom|left" - height="25" - image_hover_unselected="Toolbar_Middle_Over" - image_overlay="AddItem_Off" - image_selected="Toolbar_Middle_Selected" - image_unselected="Toolbar_Middle_Off" - layout="topleft" - left_pad="1" - name="add_friend_btn" - tool_tip="Add selected Resident to your friends List" - width="31"> - <commit_callback - function="People.addFriend" /> - </button> - <icon - follows="bottom|left|right" - height="25" - image_name="Toolbar_Right_Off" - layout="topleft" - left_pad="1" - name="dummy_icon" - width="243" - /> - </panel> </panel> + +<!-- ================================= FRIENDS tab ========================== --> + <panel background_opaque="true" background_visible="true" bg_alpha_color="DkGray" bg_opaque_color="DkGray" + bottom="-1" follows="all" - height="383" label="MY FRIENDS" layout="topleft" left="0" help_topic="people_friends_tab" name="friends_panel" - top="0" - width="313"> + right="-1" + top="0"> + <panel + follows="left|top|right" + height="27" + label="bottom_panel" + layout="topleft" + left="0" + name="friends_buttons_panel" + right="-1" + top="0"> + <filter_editor + follows="left|top|right" + height="23" + layout="topleft" + left="6" + label="Filter People" + max_length_chars="300" + name="friends_filter_input" + text_color="Black" + text_pad_left="10" + top="4" + width="177" /> + <button + commit_callback.function="People.Gear" + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="OptionsMenu_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="8" + name="gear_btn" + top="3" + width="31" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + menu_filename="menu_people_friends_view.xml" + menu_position="bottomleft" + name="friends_view_btn" + top_delta="0" + width="31" /> + <button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="AddItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + name="friends_add_btn" + top_delta="0" + width="31"> + <commit_callback + function="People.AddFriendWizard" /> + </button> + <dnd_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="TrashItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + left_pad="2" + layout="topleft" + name="friends_del_btn" + top_delta="0" + width="31"> + <commit_callback + function="People.DelFriend" /> + </dnd_button> + </panel> <accordion background_visible="true" bg_alpha_color="DkGray2" bg_opaque_color="DkGray2" follows="all" - height="356" + height="408" layout="topleft" left="3" name="friends_accordion" - top="0" - width="307"> + right="-2" + top_pad="2"> <accordion_tab layout="topleft" height="172" @@ -257,247 +358,129 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M width="307" /> </accordion_tab> </accordion> - <panel - background_visible="true" - follows="left|right|bottom" - height="27" - label="bottom_panel" - layout="topleft" - left="3" - name="bottom_panel" - top_pad="0" - width="313"> - - <layout_stack - animate="false" - border_size="0" - follows="left|right|bottom" - height="25" - layout="topleft" - orientation="horizontal" - top_pad="1" - left="0" - name="bottom_panel" - width="308"> - <layout_panel - auto_resize="false" - height="25" - layout="topleft" - name="options_gear_btn_panel" - width="32"> - <menu_button - follows="bottom|left" - tool_tip="Show additional options" - height="25" - image_hover_unselected="Toolbar_Left_Over" - image_overlay="OptionsMenu_Off" - image_selected="Toolbar_Left_Selected" - image_unselected="Toolbar_Left_Off" - layout="topleft" - left="0" - name="friends_viewsort_btn" - top="0" - width="31" /> - </layout_panel> - <layout_panel - auto_resize="false" - height="25" - layout="topleft" - name="add_btn_panel" - width="32"> - <button - follows="bottom|left" - height="25" - image_hover_unselected="Toolbar_Middle_Over" - image_overlay="AddItem_Off" - image_selected="Toolbar_Middle_Selected" - image_unselected="Toolbar_Middle_Off" - layout="topleft" - left="0" - name="add_btn" - tool_tip="Offer friendship to a Resident" - top="0" - width="31" /> - </layout_panel> - <layout_panel - auto_resize="true" - height="25" - layout="topleft" - name="dummy_panel" - width="210"> - <icon - follows="bottom|left|right" - height="25" - image_name="Toolbar_Middle_Off" - layout="topleft" - left="0" - top="0" - name="dummy_icon" - width="210" /> - </layout_panel> - <layout_panel - auto_resize="false" - height="25" - layout="topleft" - name="trash_btn_panel" - width="31"> - <dnd_button - follows="bottom|left" - height="25" - image_hover_unselected="Toolbar_Right_Over" - image_overlay="TrashItem_Off" - image_selected="Toolbar_Right_Selected" - image_unselected="Toolbar_Right_Off" - left="0" - layout="topleft" - name="del_btn" - tool_tip="Remove selected person from your Friends list" - top="0" - width="31"/> - </layout_panel> - </layout_stack><!-- - - <button - follows="bottom|left" - tool_tip="Options" - height="25" - image_hover_unselected="Toolbar_Left_Over" - image_overlay="OptionsMenu_Off" - image_selected="Toolbar_Left_Selected" - image_unselected="Toolbar_Left_Off" - layout="topleft" - left="0" - name="friends_viewsort_btn" - top="1" - width="31" /> - <button - follows="bottom|left" - height="25" - image_hover_unselected="Toolbar_Middle_Over" - image_overlay="AddItem_Off" - image_selected="Toolbar_Middle_Selected" - image_unselected="Toolbar_Middle_Off" - layout="topleft" - left_pad="1" - name="add_btn" - tool_tip="Offer friendship to a Resident" - width="31" /> - <icon - follows="bottom|left|right" - height="25" - image_name="Toolbar_Middle_Off" - layout="topleft" - left_pad="1" - name="dummy_icon" - width="209" - /> - <button - follows="bottom|left" - height="25" - image_hover_unselected="Toolbar_Right_Over" - image_overlay="TrashItem_Off" - image_selected="Toolbar_Right_Selected" - image_unselected="Toolbar_Right_Off" - layout="topleft" - left_pad="1" - name="del_btn" - tool_tip="Remove selected person from your Friends list" - width="31" /> - --></panel> <text follows="all" height="450" left="13" name="no_friends_help_text" - top="10" - width="293" + right="-13" + top="37" wrap="true" /> </panel> + +<!-- ================================= GROUPS tab =========================== --> + <panel background_opaque="true" background_visible="true" bg_alpha_color="DkGray" bg_opaque_color="DkGray" + bottom="-1" follows="all" - height="383" label="MY GROUPS" layout="topleft" left="0" help_topic="people_groups_tab" name="groups_panel" - top="0" - width="313"> + right="-1" + top="0"> <!-- *NOTE: no_groups_msg & group_list attributes are not defined as translatable in VLT. See EXT-5931 Values are set from appropriate strings at the top of file via LLPeoplePanel::postBuild() --> - <group_list - allow_select="true" - follows="all" - height="356" - layout="topleft" - left="3" - name="group_list" - top="0" - width="307" /> <panel - background_visible="true" - follows="left|right|bottom" + follows="left|top|right" height="27" label="bottom_panel" layout="topleft" left="0" - name="bottom_panel" - top_pad="0" - width="313"> - <menu_button - follows="bottom|left" - tool_tip="Options" - height="25" - image_hover_unselected="Toolbar_Left_Over" - image_overlay="OptionsMenu_Off" - image_selected="Toolbar_Left_Selected" - image_unselected="Toolbar_Left_Off" - layout="topleft" - left="3" - name="groups_viewsort_btn" - top="1" - width="31" /> - <button - follows="bottom|left" + name="groups_buttons_panel" + right="-1" + top="0"> + <filter_editor + follows="left|top|right" + height="23" + layout="topleft" + left="6" + label="Filter Groups" + max_length_chars="300" + name="groups_filter_input" + text_color="Black" + text_pad_left="10" + top="4" + width="177" /> + <menu_button + follows="right" height="25" image_hover_unselected="Toolbar_Middle_Over" - image_overlay="AddItem_Off" + image_overlay="OptionsMenu_Off" image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - left_pad="1" - name="plus_btn" - tool_tip="Join group/Create new group" + left_pad="8" + name="groups_gear_btn" + top="3" width="31" /> - <button - follows="bottom|left" + <menu_button + follows="right" height="25" image_hover_unselected="Toolbar_Middle_Over" - image_overlay="Activate_Checkmark" + image_overlay="Conv_toolbar_sort" image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - left_pad="1" - name="activate_btn" - tool_tip="Activate selected group" + left_pad="2" + menu_filename="menu_people_groups_view.xml" + menu_position="bottomleft" + name="groups_view_btn" + top_delta="0" width="31" /> - <icon - follows="bottom|left|right" - height="25" - image_name="Toolbar_Right_Off" - layout="topleft" - left_pad="1" - name="dummy_icon" - width="212" - /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="AddItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + menu_filename="menu_group_plus.xml" + menu_position="bottomleft" + name="plus_btn" + top_delta="0" + width="31"> + <validate_callback + function="People.Group.Plus.Validate" /> + </menu_button> + <dnd_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="TrashItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + left_pad="2" + layout="topleft" + name="minus_btn" + top_delta="0" + width="31"> + <commit_callback + function="People.Group.Minus" /> + </dnd_button> </panel> + <group_list + allow_select="true" + follows="all" + height="406" + layout="topleft" + left="3" + name="group_list" + right="-2" + top_pad="4" /> </panel> + +<!-- ================================= RECENT tab =========================== --> + <panel background_opaque="true" background_visible="true" @@ -510,265 +493,129 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M left="0" help_topic="people_recent_tab" name="recent_panel" - top="0" - width="313"> - <avatar_list - allow_select="true" - follows="all" - height="356" - layout="topleft" - left="3" - multi_select="true" - name="avatar_list" - show_last_interaction_time="true" - top="0" - width="307" /> + right="-1" + top="0"> <panel - background_visible="true" - follows="left|right|bottom" + follows="left|top|right" height="27" label="bottom_panel" layout="topleft" - left="3" - name="bottom_panel" - top_pad="0" - width="313"> - <menu_button - follows="bottom|left" - tool_tip="Options" - height="25" - image_hover_unselected="Toolbar_Left_Over" - image_overlay="OptionsMenu_Off" - image_selected="Toolbar_Left_Selected" - image_unselected="Toolbar_Left_Off" - layout="topleft" - name="recent_viewsort_btn" - top="1" - width="31" /> - <button - follows="bottom|left" + left="0" + name="recent_buttons_panel" + right="-1" + top="0"> + <filter_editor + follows="left|top|right" + height="23" + layout="topleft" + left="6" + label="Filter People" + max_length_chars="300" + name="recent_filter_input" + text_color="Black" + text_pad_left="10" + top="4" + width="177" /> + <button + commit_callback.function="People.Gear" + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="OptionsMenu_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="8" + name="gear_btn" + top="3" + width="31" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + menu_filename="menu_people_recent_view.xml" + menu_position="bottomleft" + name="recent_view_btn" + top_delta="0" + width="31" /> + <button + follows="right" height="25" image_hover_unselected="Toolbar_Middle_Over" image_overlay="AddItem_Off" image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - left_pad="1" + left_pad="2" name="add_friend_btn" - tool_tip="Add selected Resident to your friends List" + top_delta="0" width="31"> - <commit_callback - function="People.addFriend" /> - </button> - <icon - follows="bottom|left|right" - height="25" - image_name="Toolbar_Right_Off" - layout="topleft" - left_pad="1" - name="dummy_icon" - width="244" - /> + <commit_callback + function="People.AddFriend" /> + </button> + <dnd_button + enabled="false" + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="TrashItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + left_pad="2" + layout="topleft" + name="recent_del_btn" + top_delta="0" + width="31"> + <commit_callback + function="People.DelFriend" /> + </dnd_button> </panel> + <avatar_list + allow_select="true" + follows="all" + height="351" + layout="topleft" + left="3" + multi_select="true" + name="avatar_list" + show_last_interaction_time="true" + right="-2" + top_pad="4" /> </panel> - </tab_container> - <panel - follows="bottom|left|right" - height="23" - layout="topleft" - left="8" - top_pad="4" - name="button_bar" - width="313"> -<!--********************************Profile; IM; Call, Share, Teleport********************************--> - <layout_stack - follows="bottom|left|right" - height="23" - layout="topleft" - name="bottom_bar_ls" - left="0" - orientation="horizontal" - top_pad="0" - width="313"> +<!-- ================================= BLOCKED tab ========================== --> - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left="0" - name="view_profile_btn_lp" - auto_resize="true" - width="68"> - <button - follows="bottom|left|right" - height="23" - label="Profile" - layout="topleft" - left="1" - name="view_profile_btn" - tool_tip="Show picture, groups, and other Residents information" - top="0" - width="67" /> - </layout_panel> - - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left_pad="3" - name="im_btn_lp" - auto_resize="true" - width="41"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="IM" - layout="topleft" - name="im_btn" - tool_tip="Open instant message session" - top="0" - width="40" /> - </layout_panel> - - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left_pad="3" - name="call_btn_lp" - auto_resize="true" - width="52"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="Call" - layout="topleft" - name="call_btn" - tool_tip="Call this Resident" - top="0" - width="51" /> - </layout_panel> - - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left_pad="3" - name="share_btn_lp" - auto_resize="true" - width="66"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="Share" - layout="topleft" - name="share_btn" - tool_tip="Share an inventory item" - top="0" - width="65" /> - </layout_panel> - - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left_pad="3" - name="teleport_btn_lp" - auto_resize="true" - width="77"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="Teleport" - layout="topleft" - name="teleport_btn" - tool_tip="Offer teleport" - top="0" - width="76" /> - </layout_panel> - </layout_stack> - -<!--********************************Group Profile; Group Chat; Group Call buttons************************--> - <layout_stack - follows="bottom|left|right" - height="23" - layout="topleft" - mouse_opaque="false" - name="bottom_bar_ls1" - left="0" - orientation="horizontal" - top="0" - width="313"> - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left="0" - mouse_opaque="false" - name="group_info_btn_lp" - auto_resize="true" - width="108"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="Group Profile" - layout="topleft" - mouse_opaque="false" - name="group_info_btn" - tool_tip="Show group information" - top="0" - width="107" /> - </layout_panel> - - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left_pad="3" - mouse_opaque="false" - name="chat_btn_lp" - auto_resize="true" - width="101"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="Group Chat" - layout="topleft" - mouse_opaque="false" - name="chat_btn" - tool_tip="Open chat session" - top="0" - width="100" /> - </layout_panel> - - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left_pad="3" - mouse_opaque="false" - name="group_call_btn_lp" - auto_resize="true" - width="96"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="Group Call" - layout="topleft" - mouse_opaque="false" - name="group_call_btn" - tool_tip="Call this group" - top="0" - width="95" /> - </layout_panel> - </layout_stack> - </panel> + <panel + background_opaque="true" + background_visible="true" + bg_alpha_color="DkGray" + bg_opaque_color="DkGray" + follows="all" + height="383" + label="BLOCKED" + layout="topleft" + left="0" + help_topic="people_blocked_tab" + name="blocked_panel" + right="-1" + top="0"> + <panel + class="panel_block_list_sidetray" + height="383" + name="panel_block_list_sidetray" + filename="panel_block_list_sidetray.xml" + follows="all" + label="Blocked Residents & Objects" + layout="topleft" + left="0" + font="SansSerifBold" + top="0" + right="-1" /> + </panel> + </tab_container> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml index 27193a984f..c76a3cfaaf 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml @@ -76,15 +76,6 @@ top_pad="5" width="400" /> <check_box - enabled="false" - height="16" - label="Enable plain text IM and chat history" - layout="topleft" - left_delta="0" - name="plain_text_chat_history" - top_pad="5" - width="400" /> - <check_box control_name="UseChatBubbles" follows="left|top" height="16" @@ -95,55 +86,6 @@ name="bubble_text_chat" width="150" /> <text - name="show_ims_in_label" - follows="left|top" - layout="topleft" - left="30" - height="20" - width="170" - top_pad="15"> - Show IMs in: - </text> - <text - name="requires_restart_label" - follows="left|top" - layout="topleft" - top_delta="0" - left="170" - height="20" - width="130" - text_color="White_25"> - (requires restart) - </text> - <radio_group - follows="left|top" - height="30" - left="40" - control_name="ChatWindow" - name="chat_window" - top_pad="0" - tool_tip="Show your Instant Messages in separate floaters, or in one floater with many tabs (Requires restart)" - width="150"> - <radio_item - height="16" - label="Separate Windows" - layout="topleft" - left="0" - name="radio" - value="0" - top="0" - width="150" /> - <radio_item - height="16" - label="Tabs" - layout="topleft" - left_delta="0" - name="radio2" - value="1" - top_pad="5" - width="150" /> - </radio_group> - <text name="disable_toast_label" follows="left|top" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index d5186e4c1b..2806bcaa2e 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -404,9 +404,10 @@ Please try logging in again in a minute.</string> <string name="AddAndRemoveJoints">Add and remove joints with other objects</string> <string name="ChangePermissions">Change its permissions</string> <string name="TrackYourCamera">Track your camera</string> - <string name="ControlYourCamera">Control your camera</string> - <string name="TeleportYourAgent">Teleport you</string> - <string name="NotConnected">Not Connected</string> + <string name="ControlYourCamera">Control your camera</string> + <string name="NotConnected">Not Connected</string> + <string name="AgentNameSubst">(You)</string> <!-- Substitution for agent name --> + <string name="TeleportYourAgent">Teleport you</string> <!-- Sim Access labels --> <string name="SIM_ACCESS_PG">General</string> @@ -2270,8 +2271,10 @@ Drag folders to this area and click "Send to Marketplace" to list them for sale <string name="InvFolder Gestures">Gestures</string> <string name="InvFolder Favorite">My Favorites</string> <!-- historically default name of the Favorites folder can start from either "f" or "F" letter. - We should localize both of them with the same value --> + Also, it can be written as "Favorite" or "Favorites". + We should localize all variants of them with the same value --> <string name="InvFolder favorite">My Favorites</string> + <string name="InvFolder Favorites">My Favorites</string> <string name="InvFolder Current Outfit">Current Outfit</string> <string name="InvFolder Initial Outfits">Initial Outfits</string> <string name="InvFolder My Outfits">My Outfits</string> @@ -2280,6 +2283,7 @@ Drag folders to this area and click "Send to Marketplace" to list them for sale <!-- are used for Friends and Friends/All folders in Inventory "Calling cards" folder. See EXT-694--> <string name="InvFolder Friends">Friends</string> + <string name="InvFolder Received Items">Received Items</string> <string name="InvFolder All">All</string> <string name="no_attachments">No attachments worn</string> @@ -2579,9 +2583,6 @@ Drag folders to this area and click "Send to Marketplace" to list them for sale <string name="GroupMoneyDebits">Debits</string> <string name="GroupMoneyDate">[weekday,datetime,utc] [mth,datetime,utc] [day,datetime,utc], [year,datetime,utc]</string> - <!-- viewer object --> - <string name="ViewerObjectContents">Contents</string> - <!-- Viewer menu --> <string name="AcquiredItems">Acquired Items</string> <string name="Cancel">Cancel</string> @@ -3377,6 +3378,8 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. <string name="IM_moderator_label">(Moderator)</string> <string name="Saved_message">(Saved [LONG_TIMESTAMP])</string> <string name="IM_unblock_only_groups_friends">To see this message, you must uncheck 'Only friends and groups can call or IM me' in Preferences/Privacy.</string> + <string name="OnlineStatus">Online</string> + <string name="OfflineStatus">Offline</string> <!-- voice calls --> <string name="answered_call">Your call has been answered</string> @@ -3823,6 +3826,7 @@ Try enclosing path to the editor with double quotes. <string name="Command_Avatar_Label">Avatar</string> <string name="Command_Build_Label">Build</string> <string name="Command_Chat_Label">Chat</string> + <string name="Command_Conversations_Label">Conversations</string> <string name="Command_Compass_Label">Compass</string> <string name="Command_Destinations_Label">Destinations</string> <string name="Command_Gestures_Label">Gestures</string> @@ -3849,6 +3853,7 @@ Try enclosing path to the editor with double quotes. <string name="Command_Avatar_Tooltip">Choose a complete avatar</string> <string name="Command_Build_Tooltip">Building objects and reshaping terrain</string> <string name="Command_Chat_Tooltip">Chat with people nearby using text</string> + <string name="Command_Conversations_Tooltip">Converse with everyone</string> <string name="Command_Compass_Tooltip">Compass</string> <string name="Command_Destinations_Tooltip">Destinations of interest</string> <string name="Command_Gestures_Tooltip">Gestures for your avatar</string> diff --git a/indra/newview/skins/default/xui/en/widgets/chat_editor.xml b/indra/newview/skins/default/xui/en/widgets/chat_editor.xml new file mode 100644 index 0000000000..f9facb593a --- /dev/null +++ b/indra/newview/skins/default/xui/en/widgets/chat_editor.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<chat_editor + name="chat_editor" + show_context_menu="true"/> diff --git a/indra/newview/skins/default/xui/en/widgets/inbox_inventory_panel.xml b/indra/newview/skins/default/xui/en/widgets/inbox_inventory_panel.xml index 830c27bdac..d5b10e7f51 100644 --- a/indra/newview/skins/default/xui/en/widgets/inbox_inventory_panel.xml +++ b/indra/newview/skins/default/xui/en/widgets/inbox_inventory_panel.xml @@ -1,2 +1,3 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<inbox_inventory_panel show_load_status="false" /> +<inbox_inventory_panel show_load_status="false" + start_folder.type="inbox"/> diff --git a/indra/newview/skins/default/xui/en/widgets/outbox_folder_view_folder.xml b/indra/newview/skins/default/xui/en/widgets/outbox_folder_view_folder.xml deleted file mode 100644 index d19c47f54f..0000000000 --- a/indra/newview/skins/default/xui/en/widgets/outbox_folder_view_folder.xml +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<outbox_folder_view_folder - folder_arrow_image="Folder_Arrow" - folder_indentation="8" - item_height="20" - item_top_pad="4" - selection_image="Rounded_Square" - > -</outbox_folder_view_folder> diff --git a/indra/newview/skins/default/xui/en/widgets/outbox_inventory_panel.xml b/indra/newview/skins/default/xui/en/widgets/outbox_inventory_panel.xml deleted file mode 100644 index 3964569da2..0000000000 --- a/indra/newview/skins/default/xui/en/widgets/outbox_inventory_panel.xml +++ /dev/null @@ -1,2 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<outbox_inventory_panel show_empty_message="false" show_load_status="false" /> diff --git a/indra/newview/skins/default/xui/en/widgets/text.xml b/indra/newview/skins/default/xui/en/widgets/text.xml index 134f2d7522..2102074674 100644 --- a/indra/newview/skins/default/xui/en/widgets/text.xml +++ b/indra/newview/skins/default/xui/en/widgets/text.xml @@ -9,6 +9,7 @@ h_pad="0" allow_scroll="false" text_readonly_color="LabelTextColor" + text_tentative_color="TextFgTentativeColor" bg_writeable_color="FloaterDefaultBackgroundColor" use_ellipses="false" bg_visible="false" |