diff options
72 files changed, 2281 insertions, 1389 deletions
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index f2cdad8854..2a0dcaf333 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -1888,41 +1888,50 @@ void LLFloaterView::reshapeFloater(S32 width, S32 height, BOOL called_from_paren // dependents use same follow flags as their "dependee" continue; } - LLRect r = floaterp->getRect(); - - // Compute absolute distance from each edge of screen - S32 left_offset = llabs(r.mLeft - 0); - S32 right_offset = llabs(old_width - r.mRight); - - S32 top_offset = llabs(old_height - r.mTop); - S32 bottom_offset = llabs(r.mBottom - 0); // Make if follow the edge it is closest to U32 follow_flags = 0x0; - if (left_offset < right_offset) + if (floaterp->isMinimized()) { - follow_flags |= FOLLOWS_LEFT; + follow_flags |= (FOLLOWS_LEFT | FOLLOWS_TOP); } else { - follow_flags |= FOLLOWS_RIGHT; - } + LLRect r = floaterp->getRect(); - // "No vertical adjustment" usually means that the bottom of the view - // has been pushed up or down. Hence we want the floaters to follow - // the top. - if (!adjust_vertical) - { - follow_flags |= FOLLOWS_TOP; - } - else if (top_offset < bottom_offset) - { - follow_flags |= FOLLOWS_TOP; - } - else - { - follow_flags |= FOLLOWS_BOTTOM; + // Compute absolute distance from each edge of screen + S32 left_offset = llabs(r.mLeft - 0); + S32 right_offset = llabs(old_width - r.mRight); + + S32 top_offset = llabs(old_height - r.mTop); + S32 bottom_offset = llabs(r.mBottom - 0); + + + if (left_offset < right_offset) + { + follow_flags |= FOLLOWS_LEFT; + } + else + { + follow_flags |= FOLLOWS_RIGHT; + } + + // "No vertical adjustment" usually means that the bottom of the view + // has been pushed up or down. Hence we want the floaters to follow + // the top. + if (!adjust_vertical) + { + follow_flags |= FOLLOWS_TOP; + } + else if (top_offset < bottom_offset) + { + follow_flags |= FOLLOWS_TOP; + } + else + { + follow_flags |= FOLLOWS_BOTTOM; + } } floaterp->setFollows(follow_flags); @@ -2172,16 +2181,16 @@ void LLFloaterView::getMinimizePosition(S32 *left, S32 *bottom) const LLFloater::Params& default_params = LLFloater::getDefaultParams(); S32 floater_header_size = default_params.header_height; static LLUICachedControl<S32> minimized_width ("UIMinimizedWidth", 0); - S32 col = 0; LLRect snap_rect_local = getLocalSnapRect(); - for(S32 row = snap_rect_local.mBottom; - row < snap_rect_local.getHeight() - floater_header_size; - row += floater_header_size ) //loop rows - { - for(col = snap_rect_local.mLeft; - col < snap_rect_local.getWidth() - minimized_width; - col += minimized_width) + for(S32 col = snap_rect_local.mLeft; + col < snap_rect_local.getWidth() - minimized_width; + col += minimized_width) + { + for(S32 row = snap_rect_local.mTop - floater_header_size; + row > floater_header_size; + row -= floater_header_size ) //loop rows { + bool foundGap = TRUE; for(child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index b129bca1f3..9d44f34ea8 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -252,6 +252,7 @@ set(viewer_SOURCE_FILES llinventoryfilter.cpp llinventoryfunctions.cpp llinventorymodel.cpp + llinventoryobserver.cpp llinventorypanel.cpp llinventorysubtreepanel.cpp lljoystickbutton.cpp @@ -748,6 +749,7 @@ set(viewer_HEADER_FILES llinventoryfilter.h llinventoryfunctions.h llinventorymodel.h + llinventoryobserver.h llinventorypanel.h llinventorysubtreepanel.h lljoystickbutton.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 38f14793d9..8ad52784d3 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4259,7 +4259,29 @@ <key>Value</key> <integer>0</integer> </map> - <key>LogMessages</key> + <key>LoginSRVTimeout</key> + <map> + <key>Comment</key> + <string>Duration in seconds of the login SRV request timeout</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>10.0</real> + </map> + <key>LoginSRVPump</key> + <map> + <key>Comment</key> + <string>Name of the message pump that handles SRV request</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>LLAres</string> + </map> + <key>LogMessages</key> <map> <key>Comment</key> <string>Log network traffic</string> @@ -5501,7 +5523,7 @@ <key>Type</key> <string>Boolean</string> <key>Value</key> - <integer>0</integer> + <integer>1</integer> </map> <key>QAMode</key> <map> diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index 9938c3db2b..9b4986247f 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -887,9 +887,8 @@ void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgs lldebugs << " " << LLWearableDictionary::getTypeLabel(type) << llendl; } - // What we do here is get the complete information on the items in - // the inventory, and set up an observer that will wait for that to - // happen. + // Get the complete information on the items in the inventory and set up an observer + // that will trigger when the complete information is fetched. LLInventoryFetchDescendentsObserver::folder_ref_t folders; folders.push_back(current_outfit_id); outfit->fetchDescendents(folders); @@ -2023,6 +2022,8 @@ void LLInitialWearablesFetch::done() else { processWearablesMessage(); + // Create links for attachments that may have arrived before the COF existed. + LLAppearanceManager::linkRegisteredAttachments(); } delete this; } diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index d14de1c301..0901289dac 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -1123,6 +1123,7 @@ void LLAppearanceManager::wearItem( LLInventoryItem* item, bool do_update ) { if (do_update) LLAppearanceManager::updateAppearanceFromCOF(); + return; } else { @@ -1134,6 +1135,7 @@ void LLAppearanceManager::wearItem( LLInventoryItem* item, bool do_update ) LLAssetType::AT_LINK, cb); } + return; } /* static */ @@ -1281,3 +1283,22 @@ void LLAppearanceManager::unregisterAttachment(const LLUUID& item_id) //llinfos << "no link changes, inv link not enabled" << llendl; } } + +/* static */ +void LLAppearanceManager::linkRegisteredAttachments() +{ + for (std::set<LLUUID>::iterator it = sRegisteredAttachments.begin(); + it != sRegisteredAttachments.end(); + ++it) + { + LLUUID item_id = *it; + LLViewerInventoryItem *item = gInventory.getItem(item_id); + if (item) + { + wearItem(item, false); + gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id); + gInventory.notifyObservers(); + } + } + sRegisteredAttachments.clear(); +} diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index 56f54dfc23..7dea16b6cf 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -71,6 +71,7 @@ public: static void unregisterAttachment(const LLUUID& item_id); static void registerAttachment(const LLUUID& item_id); static void setAttachmentInvLinkEnable(bool val); + static void linkRegisteredAttachments(); private: static void filterWearableItems(LLInventoryModel::item_array_t& items, S32 max_per_type); diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index f631978565..ee4a9df15f 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -192,7 +192,7 @@ void LLAvatarActions::startIM(const LLUUID& id) // static void LLAvatarActions::startCall(const LLUUID& id) { - if (id.isNull() || isCalling(id)) + if (id.isNull()) { return; } diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp index 291f645ea2..dd06959472 100644 --- a/indra/newview/llbottomtray.cpp +++ b/indra/newview/llbottomtray.cpp @@ -40,6 +40,7 @@ #include "llimfloater.h" // for LLIMFloater #include "lllayoutstack.h" #include "llnearbychatbar.h" +#include "llspeakbutton.h" #include "llsplitbutton.h" #include "llsyswellwindow.h" #include "llfloatercamera.h" @@ -51,6 +52,7 @@ LLBottomTray::LLBottomTray(const LLSD&) mNearbyChatBar(NULL), mToolbarStack(NULL) , mMovementButton(NULL) +, mResizeState(RS_NORESIZE) // Add more members { mFactoryMap["chat_bar"] = LLCallbackMap(LLBottomTray::createNearbyChatBar, NULL); @@ -185,6 +187,28 @@ void LLBottomTray::sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& } } +// virtual +void LLBottomTray::onChange(EStatusType status, const std::string &channelURI, bool proximal) +{ + // Time it takes to connect to voice channel might be pretty long, + // so don't expect user login or STATUS_VOICE_ENABLED to be followed by STATUS_JOINED. + BOOL enable = FALSE; + + switch (status) + { + // Do not add STATUS_VOICE_ENABLED because voice chat is + // inactive until STATUS_JOINED + case STATUS_JOINED: + enable = TRUE; + break; + default: + enable = FALSE; + break; + } + + mSpeakBtn->setEnabled(enable); +} + //virtual void LLBottomTray::onFocusLost() { @@ -238,22 +262,22 @@ void LLBottomTray::showBottomTrayContextMenu(S32 x, S32 y, MASK mask) void LLBottomTray::showGestureButton(BOOL visible) { - showTrayButton(RS_BUTTON_GESTURES, visible); + setTrayButtonVisibleIfPossible(RS_BUTTON_GESTURES, visible); } void LLBottomTray::showMoveButton(BOOL visible) { - showTrayButton(RS_BUTTON_MOVEMENT, visible); + setTrayButtonVisibleIfPossible(RS_BUTTON_MOVEMENT, visible); } void LLBottomTray::showCameraButton(BOOL visible) { - showTrayButton(RS_BUTTON_CAMERA, visible); + setTrayButtonVisibleIfPossible(RS_BUTTON_CAMERA, visible); } void LLBottomTray::showSnapshotButton(BOOL visible) { - showTrayButton(RS_BUTTON_SNAPSHOT, visible); + setTrayButtonVisibleIfPossible(RS_BUTTON_SNAPSHOT, visible); } namespace @@ -280,6 +304,19 @@ BOOL LLBottomTray::postBuild() mSnapshotPanel = getChild<LLPanel>("snapshot_panel"); setRightMouseDownCallback(boost::bind(&LLBottomTray::showBottomTrayContextMenu,this, _2, _3,_4)); + mSpeakBtn = getChild<LLSpeakButton>("talk"); + + // Speak button should be initially disabled because + // it takes some time between logging in to world and connecting to voice channel. + mSpeakBtn->setEnabled(FALSE); + + // Localization tool doesn't understand custom buttons like <talk_button> + mSpeakBtn->setSpeakToolTip( getString("SpeakBtnToolTip") ); + mSpeakBtn->setShowToolTip( getString("VoiceControlBtnToolTip") ); + + // Registering Chat Bar to receive Voice client status change notifications. + gVoiceClient->addObserver(this); + if (mChicletPanel && mToolbarStack && mNearbyChatBar) { verifyChildControlsSizes(); @@ -331,74 +368,87 @@ void LLBottomTray::verifyChildControlsSizes() mNearbyChatBar->setRect(rect); } } -#define __FEATURE_EXT_991 + void LLBottomTray::reshape(S32 width, S32 height, BOOL called_from_parent) { - lldebugs << "****************************************" << llendl; + static S32 debug_calling_number = 0; + lldebugs << "**************************************** " << ++debug_calling_number << llendl; S32 current_width = getRect().getWidth(); + S32 delta_width = width - current_width; lldebugs << "Reshaping: " << ", width: " << width - << ", height: " << height - << ", called_from_parent: " << called_from_parent << ", cur width: " << current_width - << ", cur height: " << getRect().getHeight() + << ", delta_width: " << delta_width + << ", called_from_parent: " << called_from_parent << llendl; if (mNearbyChatBar) log(mNearbyChatBar, "before"); if (mChicletPanel) log(mChicletPanel, "before"); + // stores width size on which bottom tray is less than width required by its children. EXT-991 + static S32 extra_shrink_width = 0; + bool should_be_reshaped = true; + if (mChicletPanel && mToolbarStack && mNearbyChatBar) { mToolbarStack->updatePanelAutoResize(PANEL_CHICLET_NAME, TRUE); verifyChildControlsSizes(); - updateResizeState(width, current_width); - } - - LLPanel::reshape(width, height, called_from_parent); - - - if (mNearbyChatBar) log(mNearbyChatBar, "after"); - if (mChicletPanel) log(mChicletPanel, "after"); -} - -void LLBottomTray::updateResizeState(S32 new_width, S32 cur_width) -{ - mResizeState = RS_NORESIZE; - - S32 delta_width = new_width - cur_width; -// if (delta_width == 0) return; - bool shrink = new_width < cur_width; - - const S32 chiclet_panel_width = mChicletPanel->getParent()->getRect().getWidth(); - const S32 chiclet_panel_min_width = mChicletPanel->getMinWidth(); - - const S32 chatbar_panel_width = mNearbyChatBar->getRect().getWidth(); - const S32 chatbar_panel_min_width = mNearbyChatBar->getMinWidth(); - const S32 chatbar_panel_max_width = mNearbyChatBar->getMaxWidth(); - lldebugs << "chatbar_panel_width: " << chatbar_panel_width - << ", chatbar_panel_min_width: " << chatbar_panel_min_width - << ", chatbar_panel_max_width: " << chatbar_panel_max_width - << ", chiclet_panel_width: " << chiclet_panel_width - << ", chiclet_panel_min_width: " << chiclet_panel_min_width - << llendl; + // bottom tray is narrowed + if (delta_width < 0) + { + if (extra_shrink_width > 0) + { + // is world rect was extra shrunk and decreasing again only update this value + // to delta_width negative + extra_shrink_width -= delta_width; // use "-=" because delta_width is negative + should_be_reshaped = false; + } + else + { + extra_shrink_width = processWidthDecreased(delta_width); - // bottom tray is narrowed - if (shrink) - { - processWidthDecreased(delta_width); + // increase new width to extra_shrink_width value to not reshape less than bottom tray minimum + width += extra_shrink_width; + } + } + // bottom tray is widen + else + { + if (extra_shrink_width > delta_width) + { + // Less than minimum width is more than increasing (delta_width) + // only reduce it value and make no reshape + extra_shrink_width -= delta_width; + should_be_reshaped = false; + } + else + { + if (extra_shrink_width > 0) + { + // If we have some extra shrink width let's reduce delta_width & width + delta_width -= extra_shrink_width; + width -= extra_shrink_width; + extra_shrink_width = 0; + } + processWidthIncreased(delta_width); + } + } } - // bottom tray is widen - else + + lldebugs << "There is no enough width to reshape all children: " << extra_shrink_width << llendl; + if (should_be_reshaped) { - processWidthIncreased(delta_width); + lldebugs << "Reshape all children with width: " << width << llendl; + LLPanel::reshape(width, height, called_from_parent); } - lldebugs << "New resize state: " << mResizeState << llendl; + if (mNearbyChatBar) log(mNearbyChatBar, "after"); + if (mChicletPanel) log(mChicletPanel, "after"); } -void LLBottomTray::processWidthDecreased(S32 delta_width) +S32 LLBottomTray::processWidthDecreased(S32 delta_width) { bool still_should_be_processed = true; @@ -409,7 +459,6 @@ void LLBottomTray::processWidthDecreased(S32 delta_width) { // we have some space to decrease chiclet panel S32 panel_delta_min = chiclet_panel_width - chiclet_panel_min_width; - mResizeState |= RS_CHICLET_PANEL; S32 delta_panel = llmin(-delta_width, panel_delta_min); @@ -437,27 +486,25 @@ void LLBottomTray::processWidthDecreased(S32 delta_width) { // we have some space to decrease chatbar panel S32 panel_delta_min = chatbar_panel_width - chatbar_panel_min_width; - mResizeState |= RS_CHATBAR_INPUT; S32 delta_panel = llmin(-delta_width, panel_delta_min); - // is chatbar panel width enough to process resizing? + // whether chatbar panel width is enough to process resizing? delta_width += panel_delta_min; - still_should_be_processed = delta_width < 0; mNearbyChatBar->reshape(mNearbyChatBar->getRect().getWidth() - delta_panel, mNearbyChatBar->getRect().getHeight()); + log(mChicletPanel, "after processing panel decreasing via nearby chatbar panel"); + lldebugs << "RS_CHATBAR_INPUT" << ", delta_panel: " << delta_panel << ", delta_width: " << delta_width << llendl; - - log(mChicletPanel, "after nearby was processed"); - } + S32 extra_shrink_width = 0; S32 buttons_freed_width = 0; if (still_should_be_processed) { @@ -480,7 +527,9 @@ void LLBottomTray::processWidthDecreased(S32 delta_width) if (delta_width < 0) { - llwarns << "WARNING: there is no enough room for bottom tray, resizing still should be processed" << llendl; + extra_shrink_width = -delta_width; + lldebugs << "There is no enough room for bottom tray, resizing still should be processed: " + << extra_shrink_width << llendl; } if (buttons_freed_width > 0) @@ -491,10 +540,14 @@ void LLBottomTray::processWidthDecreased(S32 delta_width) lldebugs << buttons_freed_width << llendl; } } + + return extra_shrink_width; } void LLBottomTray::processWidthIncreased(S32 delta_width) { + if (delta_width <= 0) return; + const S32 chiclet_panel_width = mChicletPanel->getParent()->getRect().getWidth(); const S32 chiclet_panel_min_width = mChicletPanel->getMinWidth(); @@ -573,7 +626,6 @@ void LLBottomTray::processWidthIncreased(S32 delta_width) S32 chatbar_panel_width_ = mNearbyChatBar->getRect().getWidth(); if (delta_width > 0 && chatbar_panel_width_ < chatbar_panel_max_width) { - mResizeState |= RS_CHATBAR_INPUT; S32 delta_panel_max = chatbar_panel_max_width - chatbar_panel_width_; S32 delta_panel = llmin(delta_width, delta_panel_max); delta_width -= delta_panel_max; @@ -589,7 +641,7 @@ bool LLBottomTray::processShowButton(EResizeState shown_object_type, S32* availa lldebugs << "There is no object to process for state: " << shown_object_type << llendl; return false; } - bool can_be_shown = canButtonBeShown(panel); + bool can_be_shown = canButtonBeShown(shown_object_type); if (can_be_shown) { //validate if we have enough room to show this button @@ -600,22 +652,23 @@ bool LLBottomTray::processShowButton(EResizeState shown_object_type, S32* availa *available_width -= required_width; *buttons_required_width += required_width; - showTrayButton(shown_object_type, true); + setTrayButtonVisible(shown_object_type, true); lldebugs << "processing object type: " << shown_object_type << ", buttons_required_width: " << *buttons_required_width << llendl; + mResizeState &= ~shown_object_type; } } return can_be_shown; } -void LLBottomTray::processHideButton(EResizeState shown_object_type, S32* required_width, S32* buttons_freed_width) +void LLBottomTray::processHideButton(EResizeState processed_object_type, S32* required_width, S32* buttons_freed_width) { - LLPanel* panel = mStateProcessedObjectMap[shown_object_type]; + LLPanel* panel = mStateProcessedObjectMap[processed_object_type]; if (NULL == panel) { - lldebugs << "There is no object to process for state: " << shown_object_type << llendl; + lldebugs << "There is no object to process for state: " << processed_object_type << llendl; return; } @@ -628,17 +681,19 @@ void LLBottomTray::processHideButton(EResizeState shown_object_type, S32* requir *buttons_freed_width += *required_width; } - showTrayButton(shown_object_type, false); + setTrayButtonVisible(processed_object_type, false); - lldebugs << "processing object type: " << shown_object_type + mResizeState |= processed_object_type; + + lldebugs << "processing object type: " << processed_object_type << ", buttons_freed_width: " << *buttons_freed_width << llendl; } } -bool LLBottomTray::canButtonBeShown(LLPanel* panel) const +bool LLBottomTray::canButtonBeShown(EResizeState processed_object_type) const { - bool can_be_shown = !panel->getVisible(); + bool can_be_shown = mResizeState & processed_object_type; if (can_be_shown) { // *TODO: mantipov: synchronize with situation when button was hidden via context menu; @@ -654,7 +709,7 @@ void LLBottomTray::initStateProcessedObjectMap() mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_SNAPSHOT, mSnapshotPanel)); } -void LLBottomTray::showTrayButton(EResizeState shown_object_type, bool visible) +void LLBottomTray::setTrayButtonVisible(EResizeState shown_object_type, bool visible) { LLPanel* panel = mStateProcessedObjectMap[shown_object_type]; if (NULL == panel) @@ -665,4 +720,49 @@ void LLBottomTray::showTrayButton(EResizeState shown_object_type, bool visible) panel->setVisible(visible); } + +void LLBottomTray::setTrayButtonVisibleIfPossible(EResizeState shown_object_type, bool visible) +{ + bool can_be_set = true; + + if (visible) + { + LLPanel* panel = mStateProcessedObjectMap[shown_object_type]; + if (NULL == panel) + { + lldebugs << "There is no object to process for state: " << shown_object_type << llendl; + return; + } + + const S32 chatbar_panel_width = mNearbyChatBar->getRect().getWidth(); + const S32 chatbar_panel_min_width = mNearbyChatBar->getMinWidth(); + + const S32 chiclet_panel_width = mChicletPanel->getParent()->getRect().getWidth(); + const S32 chiclet_panel_min_width = mChicletPanel->getMinWidth(); + + const S32 available_width = (chatbar_panel_width - chatbar_panel_min_width) + + (chiclet_panel_width - chiclet_panel_min_width); + + const S32 required_width = panel->getRect().getWidth(); + can_be_set = available_width >= required_width; + } + + if (can_be_set) + { + setTrayButtonVisible(shown_object_type, visible); + + // if we hide the button mark it NOT to show while future bottom tray extending + if (!visible) + { + mResizeState &= ~shown_object_type; + } + } + else + { + // mark this button to show it while future bottom tray extending + mResizeState |= shown_object_type; + LLNotifications::instance().add("BottomTrayButtonCanNotBeShown"); + } +} + //EOF diff --git a/indra/newview/llbottomtray.h b/indra/newview/llbottomtray.h index 2972a2b1ac..86d341f233 100644 --- a/indra/newview/llbottomtray.h +++ b/indra/newview/llbottomtray.h @@ -33,7 +33,7 @@ #ifndef LL_LLBOTTOMPANEL_H #define LL_LLBOTTOMPANEL_H -#include <llmenugl.h> +#include "llmenugl.h" #include "llpanel.h" #include "llimview.h" @@ -51,6 +51,7 @@ class LLBottomTray : public LLSingleton<LLBottomTray> , public LLPanel , public LLIMSessionObserver + , public LLVoiceClientStatusObserver { LOG_CLASS(LLBottomTray); friend class LLSingleton<LLBottomTray>; @@ -75,6 +76,10 @@ public: virtual void onFocusLost(); virtual void setVisible(BOOL visible); + // Implements LLVoiceClientStatusObserver::onChange() to enable the speak + // button when voice is available + /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); + void showBottomTrayContextMenu(S32 x, S32 y, MASK mask); void showGestureButton(BOOL visible); @@ -98,14 +103,30 @@ private: void updateResizeState(S32 new_width, S32 cur_width); void verifyChildControlsSizes(); - void processWidthDecreased(S32 delta_width); + S32 processWidthDecreased(S32 delta_width); void processWidthIncreased(S32 delta_width); void log(LLView* panel, const std::string& descr); bool processShowButton(EResizeState shown_object_type, S32* available_width, S32* buttons_required_width); - void processHideButton(EResizeState shown_object_type, S32* required_width, S32* buttons_freed_width); - bool canButtonBeShown(LLPanel* panel) const; + void processHideButton(EResizeState processed_object_type, S32* required_width, S32* buttons_freed_width); + bool canButtonBeShown(EResizeState processed_object_type) const; void initStateProcessedObjectMap(); - void showTrayButton(EResizeState shown_object_type, bool visible); + + /** + * Sets passed visibility to object specified by resize type. + */ + void setTrayButtonVisible(EResizeState shown_object_type, bool visible); + + /** + * Sets passed visibility to object specified by resize type if it is possible. + * + * If it is impossible to show required button due to there is no enough room in bottom tray + * it will no be shown. Is called via context menu commands. + * Method also stores resize state to be processed while future bottom tray extending: + * - if hidden while resizing button should be hidden it will not be shown while extending; + * - if hidden via context menu button should be shown but there is no enough room for now + * it will be shown while extending. + */ + void setTrayButtonVisibleIfPossible(EResizeState shown_object_type, bool visible); MASK mResizeState; diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp index 4523267edd..442dc660cd 100644 --- a/indra/newview/llchatbar.cpp +++ b/indra/newview/llchatbar.cpp @@ -210,8 +210,9 @@ void LLChatBar::refreshGestures() // collect list of unique gestures std::map <std::string, BOOL> unique; - LLGestureManager::item_map_t::iterator it; - for (it = LLGestureManager::instance().mActive.begin(); it != LLGestureManager::instance().mActive.end(); ++it) + LLGestureManager::item_map_t::const_iterator it; + const LLGestureManager::item_map_t& active_gestures = LLGestureManager::instance().getActiveGestures(); + for (it = active_gestures.begin(); it != active_gestures.end(); ++it) { LLMultiGesture* gesture = (*it).second; if (gesture) diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 046e1d92dc..f228373063 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -53,7 +53,7 @@ std::string formatCurrentTime() time_t utc_time; utc_time = time_corrected(); std::string timeStr ="["+ LLTrans::getString("TimeHour")+"]:[" - +LLTrans::getString("TimeMin")+"] "; + +LLTrans::getString("TimeMin")+"]"; LLSD substitution; @@ -84,6 +84,10 @@ public: if (level == "profile") { + LLSD params; + params["object_id"] = getAvatarId(); + + LLFloaterReg::showInstance("inspect_object", params); } else if (level == "block") { @@ -344,7 +348,9 @@ LLView* LLChatHistory::getHeader(const LLChat& chat,const LLStyle::Params& style void LLChatHistory::appendWidgetMessage(const LLChat& chat, LLStyle::Params& style_params) { LLView* view = NULL; - std::string view_text = "\n[" + formatCurrentTime() + "] " + chat.mFromName + ": "; + std::string view_text = "\n[" + formatCurrentTime() + "] "; + if (utf8str_trim(chat.mFromName).size() != 0 && chat.mFromName != SYSTEM_FROM) + view_text += chat.mFromName + ": "; LLInlineViewSegment::Params p; p.force_newline = true; diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index fd86192650..9e290c8c04 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -838,11 +838,15 @@ void im_chiclet_callback(LLChicletPanel* panel, const LLSD& data){ LLUUID session_id = data["session_id"].asUUID(); LLUUID from_id = data["from_id"].asUUID(); const std::string from = data["from"].asString(); + S32 unread = data["num_unread"].asInteger(); - //we do not show balloon (indicator of new messages) for system messages and our own messages - if (from_id.isNull() || from_id == gAgentID || SYSTEM_FROM == from) return; + // if new message came + if(unread != 0) + { + //we do not show balloon (indicator of new messages) for system messages and our own messages + if (from_id.isNull() || from_id == gAgentID || SYSTEM_FROM == from) return; + } - S32 unread = data["num_unread"].asInteger(); LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); if (im_floater && im_floater->getVisible()) { @@ -862,7 +866,6 @@ void im_chiclet_callback(LLChicletPanel* panel, const LLSD& data){ llwarns << "Unable to set counter for chiclet " << session_id << llendl; } } - } diff --git a/indra/newview/llfloatergesture.cpp b/indra/newview/llfloatergesture.cpp index c114eed4a2..ca0ba96a08 100644 --- a/indra/newview/llfloatergesture.cpp +++ b/indra/newview/llfloatergesture.cpp @@ -89,6 +89,52 @@ LLFloaterGesture::LLFloaterGesture(const LLSD& key) //LLUICtrlFactory::getInstance()->buildFloater(this, "floater_gesture.xml"); } +void LLFloaterGesture::done() +{ + //this method can be called twice: for GestureFolder and once after loading all sudir of GestureFolder + if (gInventory.isCategoryComplete(mGestureFolderID)) + { + LL_DEBUGS("Gesture")<< "mGestureFolderID loaded" << LL_ENDL; + // we load only gesture folder without childred. + LLInventoryModel::cat_array_t* categories; + LLInventoryModel::item_array_t* items; + folder_ref_t unloaded_folders; + LL_DEBUGS("Gesture")<< "Get subdirs of Gesture Folder...." << LL_ENDL; + gInventory.getDirectDescendentsOf(mGestureFolderID, categories, items); + if (categories->empty()) + { + gInventory.removeObserver(this); + LL_INFOS("Gesture")<< "Gesture dos NOT contains sub-directories."<< LL_ENDL; + return; + } + LL_DEBUGS("Gesture")<< "There are " << categories->size() << " Folders "<< LL_ENDL; + for (LLInventoryModel::cat_array_t::iterator it = categories->begin(); it != categories->end(); it++) + { + if (!gInventory.isCategoryComplete(it->get()->getUUID())) + { + unloaded_folders.push_back(it->get()->getUUID()); + LL_DEBUGS("Gesture")<< it->get()->getName()<< " Folder added to fetchlist"<< LL_ENDL; + } + + } + if (!unloaded_folders.empty()) + { + LL_DEBUGS("Gesture")<< "Fetching subdirectories....." << LL_ENDL; + fetchDescendents(unloaded_folders); + } + else + { + LL_DEBUGS("Gesture")<< "All Gesture subdirectories have been loaded."<< LL_ENDL; + gInventory.removeObserver(this); + buildGestureList(); + } + } + else + { + LL_WARNS("Gesture")<< "Gesture list was NOT loaded"<< LL_ENDL; + } +} + // virtual LLFloaterGesture::~LLFloaterGesture() { @@ -121,7 +167,14 @@ BOOL LLFloaterGesture::postBuild() childSetVisible("play_btn", true); childSetVisible("stop_btn", false); setDefaultBtn("play_btn"); - + mGestureFolderID = gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE, false); + + folder_ref_t folders; + folders.push_back(mGestureFolderID); + //perform loading Gesture directory anyway to make sure that all subdirectory are loaded too. See method done() for details. + gInventory.addObserver(this); + fetchDescendents(folders); + buildGestureList(); childSetFocus("gesture_list"); @@ -171,101 +224,125 @@ void LLFloaterGesture::buildGestureList() if (! (list && scroll)) return; - // attempt to preserve scroll position through re-builds - // since we do re-build any time anything dirties - S32 current_scroll_pos = scroll->getScrollPos(); - + LLUUID selected_item = list->getCurrentID(); + LL_DEBUGS("Gesture")<< "Rebuilding gesture list "<< LL_ENDL; list->operateOnAll(LLCtrlListInterface::OP_DELETE); - LLGestureManager::item_map_t::iterator it; - for (it = LLGestureManager::instance().mActive.begin(); it != LLGestureManager::instance().mActive.end(); ++it) + LLGestureManager::item_map_t::const_iterator it; + const LLGestureManager::item_map_t& active_gestures = LLGestureManager::instance().getActiveGestures(); + for (it = active_gestures.begin(); it != active_gestures.end(); ++it) { - const LLUUID& item_id = (*it).first; - LLMultiGesture* gesture = (*it).second; + addGesture(it->first,it->second, list); + } + if (gInventory.isCategoryComplete(mGestureFolderID)) + { + LLIsType is_gesture(LLAssetType::AT_GESTURE); + LLInventoryModel::cat_array_t categories; + LLInventoryModel::item_array_t items; + gInventory.collectDescendentsIf(mGestureFolderID, categories, items, + LLInventoryModel::EXCLUDE_TRASH, is_gesture); - // Note: Can have NULL item if inventory hasn't arrived yet. - std::string item_name = getString("loading"); - LLInventoryItem* item = gInventory.getItem(item_id); - if (item) + for (LLInventoryModel::item_array_t::iterator it = items.begin(); it!= items.end(); ++it) { - item_name = item->getName(); + LLInventoryItem* item = it->get(); + if (active_gestures.find(item->getUUID()) == active_gestures.end()) + { + // if gesture wasn't loaded yet, we can display only name + addGesture(item->getUUID(), NULL, list); + } } + } + // attempt to preserve scroll position through re-builds + // since we do re-build any time anything dirties + if(list->selectByValue(LLSD(selected_item))) + { + scroll->scrollToShowSelected(); + } +} - std::string font_style = "NORMAL"; - // If gesture is playing, bold it +void LLFloaterGesture::addGesture(const LLUUID& item_id , LLMultiGesture* gesture,LLCtrlListInterface * list ) +{ + // Note: Can have NULL item if inventory hasn't arrived yet. + static std::string item_name = getString("loading"); + LLInventoryItem* item = gInventory.getItem(item_id); + if (item) + { + item_name = item->getName(); + } + + static std::string font_style = "NORMAL"; + // If gesture is playing, bold it - LLSD element; - element["id"] = item_id; + LLSD element; + element["id"] = item_id; - if (gesture) + if (gesture) + { + if (gesture->mPlaying) { - if (gesture->mPlaying) - { - font_style = "BOLD"; - } + font_style = "BOLD"; + } - element["columns"][0]["column"] = "trigger"; - element["columns"][0]["value"] = gesture->mTrigger; - element["columns"][0]["font"]["name"] = "SANSSERIF"; - element["columns"][0]["font"]["style"] = font_style; + element["columns"][0]["column"] = "trigger"; + element["columns"][0]["value"] = gesture->mTrigger; + element["columns"][0]["font"]["name"] = "SANSSERIF"; + element["columns"][0]["font"]["style"] = font_style; - std::string key_string = LLKeyboard::stringFromKey(gesture->mKey); - std::string buffer; + std::string key_string = LLKeyboard::stringFromKey(gesture->mKey); + std::string buffer; - if (gesture->mKey == KEY_NONE) - { - buffer = "---"; - key_string = "~~~"; // alphabetize to end - } - else - { - buffer = LLKeyboard::stringFromAccelerator( gesture->mMask, gesture->mKey ); - } + if (gesture->mKey == KEY_NONE) + { + buffer = "---"; + key_string = "~~~"; // alphabetize to end + } + else + { + buffer = LLKeyboard::stringFromAccelerator(gesture->mMask, + gesture->mKey); + } - element["columns"][1]["column"] = "shortcut"; - element["columns"][1]["value"] = buffer; - element["columns"][1]["font"]["name"] = "SANSSERIF"; - element["columns"][1]["font"]["style"] = font_style; + element["columns"][1]["column"] = "shortcut"; + element["columns"][1]["value"] = buffer; + element["columns"][1]["font"]["name"] = "SANSSERIF"; + element["columns"][1]["font"]["style"] = font_style; - // hidden column for sorting - element["columns"][2]["column"] = "key"; - element["columns"][2]["value"] = key_string; - element["columns"][2]["font"]["name"] = "SANSSERIF"; - element["columns"][2]["font"]["style"] = font_style; + // hidden column for sorting + element["columns"][2]["column"] = "key"; + element["columns"][2]["value"] = key_string; + element["columns"][2]["font"]["name"] = "SANSSERIF"; + element["columns"][2]["font"]["style"] = font_style; - // Only add "playing" if we've got the name, less confusing. JC - if (item && gesture->mPlaying) - { - item_name += " " + getString("playing"); - } - element["columns"][3]["column"] = "name"; - element["columns"][3]["value"] = item_name; - element["columns"][3]["font"]["name"] = "SANSSERIF"; - element["columns"][3]["font"]["style"] = font_style; - } - else + // Only add "playing" if we've got the name, less confusing. JC + if (item && gesture->mPlaying) { - element["columns"][0]["column"] = "trigger"; - element["columns"][0]["value"] = ""; - element["columns"][0]["font"]["name"] = "SANSSERIF"; - element["columns"][0]["font"]["style"] = font_style; - element["columns"][0]["column"] = "trigger"; - element["columns"][0]["value"] = "---"; - element["columns"][0]["font"]["name"] = "SANSSERIF"; - element["columns"][0]["font"]["style"] = font_style; - element["columns"][2]["column"] = "key"; - element["columns"][2]["value"] = "~~~"; - element["columns"][2]["font"]["name"] = "SANSSERIF"; - element["columns"][2]["font"]["style"] = font_style; - element["columns"][3]["column"] = "name"; - element["columns"][3]["value"] = item_name; - element["columns"][3]["font"]["name"] = "SANSSERIF"; - element["columns"][3]["font"]["style"] = font_style; + item_name += " " + getString("playing"); } - list->addElement(element, ADD_BOTTOM); + element["columns"][3]["column"] = "name"; + element["columns"][3]["value"] = item_name; + element["columns"][3]["font"]["name"] = "SANSSERIF"; + element["columns"][3]["font"]["style"] = font_style; } - - scroll->setScrollPos(current_scroll_pos); + else + { + element["columns"][0]["column"] = "trigger"; + element["columns"][0]["value"] = ""; + element["columns"][0]["font"]["name"] = "SANSSERIF"; + element["columns"][0]["font"]["style"] = font_style; + element["columns"][0]["column"] = "trigger"; + element["columns"][0]["value"] = "---"; + element["columns"][0]["font"]["name"] = "SANSSERIF"; + element["columns"][0]["font"]["style"] = font_style; + element["columns"][2]["column"] = "key"; + element["columns"][2]["value"] = "~~~"; + element["columns"][2]["font"]["name"] = "SANSSERIF"; + element["columns"][2]["font"]["style"] = font_style; + element["columns"][3]["column"] = "name"; + element["columns"][3]["value"] = item_name; + element["columns"][3]["font"]["name"] = "SANSSERIF"; + element["columns"][3]["font"]["style"] = font_style; + } + list->addElement(element, ADD_BOTTOM); } void LLFloaterGesture::onClickInventory() @@ -284,14 +361,21 @@ void LLFloaterGesture::onClickPlay() LLCtrlListInterface *list = childGetListInterface("gesture_list"); if (!list) return; const LLUUID& item_id = list->getCurrentID(); + if(item_id.isNull()) return; - if (LLGestureManager::instance().isGesturePlaying(item_id)) + LL_DEBUGS("Gesture")<<" Trying to play gesture id: "<< item_id <<LL_ENDL; + if(!LLGestureManager::instance().isGestureActive(item_id)) { - LLGestureManager::instance().stopGesture(item_id); + // we need to inform server about gesture activating to be consistent with LLPreviewGesture. + BOOL inform_server = TRUE; + BOOL deactivate_similar = FALSE; + LLGestureManager::instance().activateGestureWithAsset(item_id, gInventory.getItem(item_id)->getAssetUUID(), inform_server, deactivate_similar); + LL_DEBUGS("Gesture")<< "Activating gesture with inventory ID: " << item_id <<LL_ENDL; + LLGestureManager::instance().setGestureLoadedCallback(item_id, boost::bind(&LLFloaterGesture::playGesture, this, item_id)); } else { - LLGestureManager::instance().playGesture(item_id); + playGesture(item_id); } } @@ -345,3 +429,14 @@ void LLFloaterGesture::onCommitList() childSetVisible("stop_btn", false); } } +void LLFloaterGesture::playGesture(LLUUID item_id) +{ + if (LLGestureManager::instance().isGesturePlaying(item_id)) + { + LLGestureManager::instance().stopGesture(item_id); + } + else + { + LLGestureManager::instance().playGesture(item_id); + } +} diff --git a/indra/newview/llfloatergesture.h b/indra/newview/llfloatergesture.h index 9c1ab27cb0..9d047bf1cf 100644 --- a/indra/newview/llfloatergesture.h +++ b/indra/newview/llfloatergesture.h @@ -38,7 +38,7 @@ #define LL_LLFLOATERGESTURE_H #include "llfloater.h" - +#include "llinventorymodel.h" #include "lldarray.h" class LLScrollContainer; @@ -51,31 +51,35 @@ class LLGestureOptions; class LLScrollListCtrl; class LLFloaterGestureObserver; class LLFloaterGestureInventoryObserver; +class LLMultiGesture; class LLFloaterGesture -: public LLFloater +: public LLFloater, LLInventoryFetchDescendentsObserver { + LOG_CLASS(LLFloaterGesture); public: LLFloaterGesture(const LLSD& key); virtual ~LLFloaterGesture(); virtual BOOL postBuild(); - + virtual void done (); void refreshAll(); protected: // Reads from the gesture manager's list of active gestures // and puts them in this list. void buildGestureList(); - + void addGesture(const LLUUID& item_id, LLMultiGesture* gesture, LLCtrlListInterface * list); void onClickInventory(); void onClickEdit(); void onClickPlay(); void onClickNew(); void onCommitList(); + void playGesture(LLUUID item_id); protected: LLUUID mSelectedID; + LLUUID mGestureFolderID; LLFloaterGestureObserver* mObserver; }; diff --git a/indra/newview/llfriendcard.cpp b/indra/newview/llfriendcard.cpp index 1ff2566dca..481b75cf73 100644 --- a/indra/newview/llfriendcard.cpp +++ b/indra/newview/llfriendcard.cpp @@ -91,44 +91,39 @@ const LLUUID& get_folder_uuid(const LLUUID& parentFolderUUID, LLInventoryCollect return LLUUID::null; } - -// LLViewerInventoryCategory::fetchDescendents has it own period of fetching. -// for now it is FETCH_TIMER_EXPIRY = 10.0f; So made our period a bit more. -const F32 FETCH_FRIENDS_DESCENDENTS_PERIOD = 11.0f; - - /** - * Intended to call passed callback after the specified period of time. + * Class for fetching initial friend cards data * - * Implemented to fix an issue when Inventory folders are in incomplete state. See EXT-2061, EXT-1935, EXT-813. - * For now it uses to periodically sync Inventory Friends/All folder with a Agent's Friends List - * until it is complete. - */ -class FriendListUpdater : public LLEventTimer + * Implemented to fix an issue when Inventory folders are in incomplete state. + * See EXT-2320, EXT-2061, EXT-1935, EXT-813. + * Uses a callback to sync Inventory Friends/All folder with agent's Friends List. + */ +class LLInitialFriendCardsFetch : public LLInventoryFetchDescendentsObserver { public: - typedef boost::function<bool()> callback_t; + typedef boost::function<void()> callback_t; - FriendListUpdater(callback_t cb, F32 period) - : LLEventTimer(period) - , mCallback(cb) - { - mEventTimer.start(); - } + LLInitialFriendCardsFetch(callback_t cb) + : mCheckFolderCallback(cb) {} - virtual BOOL tick() // from LLEventTimer - { - return mCallback(); - } + /* virtual */ void done(); private: - callback_t mCallback; + callback_t mCheckFolderCallback; }; +void LLInitialFriendCardsFetch::done() +{ + // This observer is no longer needed. + gInventory.removeObserver(this); + + mCheckFolderCallback(); + + delete this; +} // LLFriendCardsManager Constructor / Destructor LLFriendCardsManager::LLFriendCardsManager() -: mFriendsAllFolderCompleted(true) { LLAvatarTracker::instance().addObserver(this); } @@ -167,30 +162,6 @@ const LLUUID LLFriendCardsManager::extractAvatarID(const LLUUID& avatarID) return rv; } -// be sure LLInventoryModel::buildParentChildMap() has been called before it. -// and this method must be called before any actions with friend list -void LLFriendCardsManager::ensureFriendFoldersExist() -{ - const LLUUID callingCardsFolderID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD); - - LLUUID friendFolderUUID = findFriendFolderUUIDImpl(); - - if (friendFolderUUID.isNull()) - { - friendFolderUUID = gInventory.createNewCategory(callingCardsFolderID, - LLFolderType::FT_CALLINGCARD, get_friend_folder_name()); - } - - LLUUID friendAllSubfolderUUID = findFriendAllSubfolderUUIDImpl(); - - if (friendAllSubfolderUUID.isNull()) - { - friendAllSubfolderUUID = gInventory.createNewCategory(friendFolderUUID, - LLFolderType::FT_CALLINGCARD, get_friend_all_subfolder_name()); - } -} - - bool LLFriendCardsManager::isItemInAnyFriendsList(const LLViewerInventoryItem* item) { if (item->getType() != LLAssetType::AT_CALLINGCARD) @@ -305,63 +276,12 @@ bool LLFriendCardsManager::isAnyFriendCategory(const LLUUID& catID) const return TRUE == gInventory.isObjectDescendentOf(catID, friendFolderID); } -bool LLFriendCardsManager::syncFriendsFolder() +void LLFriendCardsManager::syncFriendCardsFolders() { - //lets create "Friends" and "Friends/All" in the Inventory "Calling Cards" if they are absent - LLFriendCardsManager::instance().ensureFriendFoldersExist(); - - LLAvatarTracker::buddy_map_t all_buddies; - LLAvatarTracker::instance().copyBuddyList(all_buddies); - - // 1. Remove Friend Cards for non-friends - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - - gInventory.collectDescendents(findFriendAllSubfolderUUIDImpl(), cats, items, LLInventoryModel::EXCLUDE_TRASH); - - LLInventoryModel::item_array_t::const_iterator it; - for (it = items.begin(); it != items.end(); ++it) - { - lldebugs << "Check if buddy is in list: " << (*it)->getName() << " " << (*it)->getCreatorUUID() << llendl; - if (NULL == get_ptr_in_map(all_buddies, (*it)->getCreatorUUID())) - { - lldebugs << "NONEXISTS, so remove it" << llendl; - removeFriendCardFromInventory((*it)->getCreatorUUID()); - } - } - - // 2. Add missing Friend Cards for friends - LLAvatarTracker::buddy_map_t::const_iterator buddy_it = all_buddies.begin(); - llinfos << "try to build friends, count: " << all_buddies.size() << llendl; - mFriendsAllFolderCompleted = true; - for(; buddy_it != all_buddies.end(); ++buddy_it) - { - const LLUUID& buddy_id = (*buddy_it).first; - addFriendCardToInventory(buddy_id); - } - - if (!mFriendsAllFolderCompleted) - { - forceFriendListIsLoaded(findFriendAllSubfolderUUIDImpl()); - - static bool timer_started = false; - if (!timer_started) - { - lldebugs << "Create and start timer to sync Inventory Friends All folder with Friends list" << llendl; - - // do not worry about destruction of the FriendListUpdater. - // It will be deleted by LLEventTimer::updateClass when FriendListUpdater::tick() returns true. - new FriendListUpdater(boost::bind(&LLFriendCardsManager::syncFriendsFolder, this), - FETCH_FRIENDS_DESCENDENTS_PERIOD); - } - timer_started = true; - } - else - { - lldebugs << "Friends/All Inventory folder is synchronized with the Agent's Friends List" << llendl; - } + const LLUUID callingCardsFolderID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD); - return mFriendsAllFolderCompleted; + fetchAndCheckFolderDescendents(callingCardsFolderID, + boost::bind(&LLFriendCardsManager::ensureFriendsFolderExists, this)); } void LLFriendCardsManager::collectFriendsLists(folderid_buddies_map_t& folderBuddiesMap) const @@ -482,6 +402,122 @@ void LLFriendCardsManager::findMatchedFriendCards(const LLUUID& avatarID, LLInve } } +void LLFriendCardsManager::fetchAndCheckFolderDescendents(const LLUUID& folder_id, callback_t cb) +{ + // This instance will be deleted in LLInitialFriendCardsFetch::done(). + LLInitialFriendCardsFetch* fetch = new LLInitialFriendCardsFetch(cb); + + LLInventoryFetchDescendentsObserver::folder_ref_t folders; + folders.push_back(folder_id); + + fetch->fetchDescendents(folders); + if(fetch->isEverythingComplete()) + { + // everything is already here - call done. + fetch->done(); + } + else + { + // it's all on it's way - add an observer, and the inventory + // will call done for us when everything is here. + gInventory.addObserver(fetch); + } +} + +// Make sure LLInventoryModel::buildParentChildMap() has been called before it. +// This method must be called before any actions with friends list. +void LLFriendCardsManager::ensureFriendsFolderExists() +{ + const LLUUID calling_cards_folder_ID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD); + + // If "Friends" folder exists in "Calling Cards" we should check if "All" sub-folder + // exists in "Friends", otherwise we create it. + LLUUID friends_folder_ID = findFriendFolderUUIDImpl(); + if (friends_folder_ID.notNull()) + { + fetchAndCheckFolderDescendents(friends_folder_ID, + boost::bind(&LLFriendCardsManager::ensureFriendsAllFolderExists, this)); + } + else + { + if (!gInventory.isCategoryComplete(calling_cards_folder_ID)) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(calling_cards_folder_ID); + std::string cat_name = cat ? cat->getName() : "unknown"; + llwarns << "Failed to find \"" << cat_name << "\" category descendents in Category Tree." << llendl; + } + + friends_folder_ID = gInventory.createNewCategory(calling_cards_folder_ID, + LLFolderType::FT_CALLINGCARD, get_friend_folder_name()); + + gInventory.createNewCategory(friends_folder_ID, + LLFolderType::FT_CALLINGCARD, get_friend_all_subfolder_name()); + + // Now when we have all needed folders we can sync their contents with buddies list. + syncFriendsFolder(); + } +} + +// Make sure LLFriendCardsManager::ensureFriendsFolderExists() has been called before it. +void LLFriendCardsManager::ensureFriendsAllFolderExists() +{ + LLUUID friends_all_folder_ID = findFriendAllSubfolderUUIDImpl(); + if (friends_all_folder_ID.notNull()) + { + fetchAndCheckFolderDescendents(friends_all_folder_ID, + boost::bind(&LLFriendCardsManager::syncFriendsFolder, this)); + } + else + { + LLUUID friends_folder_ID = findFriendFolderUUIDImpl(); + + if (!gInventory.isCategoryComplete(friends_folder_ID)) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(friends_folder_ID); + std::string cat_name = cat ? cat->getName() : "unknown"; + llwarns << "Failed to find \"" << cat_name << "\" category descendents in Category Tree." << llendl; + } + + friends_all_folder_ID = gInventory.createNewCategory(friends_folder_ID, + LLFolderType::FT_CALLINGCARD, get_friend_all_subfolder_name()); + + // Now when we have all needed folders we can sync their contents with buddies list. + syncFriendsFolder(); + } +} + +void LLFriendCardsManager::syncFriendsFolder() +{ + LLAvatarTracker::buddy_map_t all_buddies; + LLAvatarTracker::instance().copyBuddyList(all_buddies); + + // 1. Remove Friend Cards for non-friends + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + + gInventory.collectDescendents(findFriendAllSubfolderUUIDImpl(), cats, items, LLInventoryModel::EXCLUDE_TRASH); + + LLInventoryModel::item_array_t::const_iterator it; + for (it = items.begin(); it != items.end(); ++it) + { + lldebugs << "Check if buddy is in list: " << (*it)->getName() << " " << (*it)->getCreatorUUID() << llendl; + if (NULL == get_ptr_in_map(all_buddies, (*it)->getCreatorUUID())) + { + lldebugs << "NONEXISTS, so remove it" << llendl; + removeFriendCardFromInventory((*it)->getCreatorUUID()); + } + } + + // 2. Add missing Friend Cards for friends + LLAvatarTracker::buddy_map_t::const_iterator buddy_it = all_buddies.begin(); + llinfos << "try to build friends, count: " << all_buddies.size() << llendl; + for(; buddy_it != all_buddies.end(); ++buddy_it) + { + const LLUUID& buddy_id = (*buddy_it).first; + addFriendCardToInventory(buddy_id); + } +} + class CreateFriendCardCallback : public LLInventoryCallback { public: @@ -494,9 +530,8 @@ public: } }; -bool LLFriendCardsManager::addFriendCardToInventory(const LLUUID& avatarID) +void LLFriendCardsManager::addFriendCardToInventory(const LLUUID& avatarID) { - LLInventoryModel* invModel = &gInventory; bool shouldBeAdded = true; std::string name; @@ -518,13 +553,6 @@ bool LLFriendCardsManager::addFriendCardToInventory(const LLUUID& avatarID) lldebugs << "is found in sentRequests: " << name << llendl; } - LLUUID friendListFolderID = findFriendAllSubfolderUUIDImpl(); - if (friendListFolderID.notNull() && shouldBeAdded && !invModel->isCategoryComplete(friendListFolderID)) - { - mFriendsAllFolderCompleted = false; - shouldBeAdded = false; - lldebugs << "Friends/All category is not completed" << llendl; - } if (shouldBeAdded) { putAvatarData(avatarID); @@ -533,10 +561,8 @@ bool LLFriendCardsManager::addFriendCardToInventory(const LLUUID& avatarID) // TODO: mantipov: Is CreateFriendCardCallback really needed? Probably not LLPointer<LLInventoryCallback> cb = new CreateFriendCardCallback(); - create_inventory_callingcard(avatarID, friendListFolderID, cb); + create_inventory_callingcard(avatarID, findFriendAllSubfolderUUIDImpl(), cb); } - - return shouldBeAdded; } void LLFriendCardsManager::removeFriendCardFromInventory(const LLUUID& avatarID) @@ -582,11 +608,4 @@ void LLFriendCardsManager::onFriendListUpdate(U32 changed_mask) } } -void LLFriendCardsManager::forceFriendListIsLoaded(const LLUUID& folder_id) const -{ - bool fetching_inventory = gInventory.fetchDescendentsOf(folder_id); - lldebugs << "Trying to fetch descendants of Friends/All Inventory folder, fetched: " - << fetching_inventory << llendl; -} - // EOF diff --git a/indra/newview/llfriendcard.h b/indra/newview/llfriendcard.h index feea05bc1d..98dc3153d0 100644 --- a/indra/newview/llfriendcard.h +++ b/indra/newview/llfriendcard.h @@ -58,14 +58,6 @@ public: } /** - * Ensures that all necessary folders are created in Inventory. - * - * For now it processes Calling Card, Calling Card/Friends & Calling Card/Friends/All folders - */ - void ensureFriendFoldersExist(); - - - /** * Determines if specified Inventory Calling Card exists in any of lists * in the Calling Card/Friends/ folder (Default, or Custom) */ @@ -88,11 +80,10 @@ public: bool isAnyFriendCategory(const LLUUID& catID) const; /** - * Synchronizes content of the Calling Card/Friends/All Global Inventory folder with Agent's Friend List - * - * @return true - if folder is already synchronized, false otherwise. + * Checks whether "Friends" and "Friends/All" folders exist in "Calling Cards" category + * (creates them otherwise) and fetches their contents to synchronize with Agent's Friends List. */ - bool syncFriendsFolder(); + void syncFriendCardsFolders(); /*! * \brief @@ -108,6 +99,8 @@ public: void collectFriendsLists(folderid_buddies_map_t& folderBuddiesMap) const; private: + typedef boost::function<void()> callback_t; + LLFriendCardsManager(); ~LLFriendCardsManager(); @@ -133,10 +126,29 @@ private: const LLUUID& findFriendCardInventoryUUIDImpl(const LLUUID& avatarID); void findMatchedFriendCards(const LLUUID& avatarID, LLInventoryModel::item_array_t& items) const; + void fetchAndCheckFolderDescendents(const LLUUID& folder_id, callback_t cb); + + /** + * Checks whether "Calling Cards/Friends" folder exists. If not, creates it with "All" + * sub-folder and synchronizes its contents with buddies list. + */ + void ensureFriendsFolderExists(); + + /** + * Checks whether "Calling Cards/Friends/All" folder exists. If not, creates it and + * synchronizes its contents with buddies list. + */ + void ensureFriendsAllFolderExists(); + + /** + * Synchronizes content of the Calling Card/Friends/All Global Inventory folder with Agent's Friend List + */ + void syncFriendsFolder(); + /** * Adds avatar specified by its UUID into the Calling Card/Friends/All Global Inventory folder */ - bool addFriendCardToInventory(const LLUUID& avatarID); + void addFriendCardToInventory(const LLUUID& avatarID); /** * Removes an avatar specified by its UUID from the Calling Card/Friends/All Global Inventory folder @@ -146,20 +158,11 @@ private: void onFriendListUpdate(U32 changed_mask); - /** - * Force fetching of the Inventory folder specified by passed folder's LLUUID. - * - * It only sends request to server, server reply should be processed in other place. - * Because request can be sent via UDP we need to periodically check if request was completed with success. - */ - void forceFriendListIsLoaded(const LLUUID& folder_id) const; - private: typedef std::set<LLUUID> avatar_uuid_set_t; avatar_uuid_set_t mBuddyIDSet; - bool mFriendsAllFolderCompleted; }; #endif // LL_LLFRIENDCARD_H diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp index 59274c8638..8e774dc199 100644 --- a/indra/newview/llgesturemgr.cpp +++ b/indra/newview/llgesturemgr.cpp @@ -37,7 +37,6 @@ // system #include <functional> #include <algorithm> -#include <boost/tokenizer.hpp> // library #include "lldatapacker.h" @@ -206,6 +205,9 @@ struct LLLoadInfo // If inform_server is true, will send a message upstream to update // the user_gesture_active table. +/** + * It will load a gesture from remote storage + */ void LLGestureManager::activateGestureWithAsset(const LLUUID& item_id, const LLUUID& asset_id, BOOL inform_server, @@ -921,8 +923,8 @@ void LLGestureManager::onLoadComplete(LLVFS *vfs, delete info; info = NULL; - - LLGestureManager::instance().mLoadingCount--; + LLGestureManager& self = LLGestureManager::instance(); + self.mLoadingCount--; if (0 == status) { @@ -944,15 +946,15 @@ void LLGestureManager::onLoadComplete(LLVFS *vfs, { if (deactivate_similar) { - LLGestureManager::instance().deactivateSimilarGestures(gesture, item_id); + self.deactivateSimilarGestures(gesture, item_id); // Display deactivation message if this was the last of the bunch. - if (LLGestureManager::instance().mLoadingCount == 0 - && LLGestureManager::instance().mDeactivateSimilarNames.length() > 0) + if (self.mLoadingCount == 0 + && self.mDeactivateSimilarNames.length() > 0) { // we're done with this set of deactivations LLSD args; - args["NAMES"] = LLGestureManager::instance().mDeactivateSimilarNames; + args["NAMES"] = self.mDeactivateSimilarNames; LLNotifications::instance().add("DeactivatedGesturesTrigger", args); } } @@ -965,9 +967,9 @@ void LLGestureManager::onLoadComplete(LLVFS *vfs, else { // Watch this item and set gesture name when item exists in inventory - LLGestureManager::instance().watchItem(item_id); + self.watchItem(item_id); } - LLGestureManager::instance().mActive[item_id] = gesture; + self.mActive[item_id] = gesture; // Everything has been successful. Add to the active list. gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id); @@ -989,14 +991,21 @@ void LLGestureManager::onLoadComplete(LLVFS *vfs, gAgent.sendReliableMessage(); } + callback_map_t::iterator i_cb = self.mCallbackMap.find(item_id); + + if(i_cb != self.mCallbackMap.end()) + { + i_cb->second(gesture); + self.mCallbackMap.erase(i_cb); + } - LLGestureManager::instance().notifyObservers(); + self.notifyObservers(); } else { llwarns << "Unable to load gesture" << llendl; - LLGestureManager::instance().mActive.erase(item_id); + self.mActive.erase(item_id); delete gesture; gesture = NULL; diff --git a/indra/newview/llgesturemgr.h b/indra/newview/llgesturemgr.h index 947773d66d..7c3b742780 100644 --- a/indra/newview/llgesturemgr.h +++ b/indra/newview/llgesturemgr.h @@ -57,6 +57,12 @@ public: class LLGestureManager : public LLSingleton<LLGestureManager>, public LLInventoryCompletionObserver { public: + + typedef boost::function<void (LLMultiGesture* loaded_gesture)> gesture_loaded_callback_t; + // Maps inventory item_id to gesture + typedef std::map<LLUUID, LLMultiGesture*> item_map_t; + typedef std::map<LLUUID, gesture_loaded_callback_t> callback_map_t; + LLGestureManager(); ~LLGestureManager(); @@ -97,6 +103,7 @@ public: BOOL isGesturePlaying(const LLUUID& item_id); + const item_map_t& getActiveGestures() const { return mActive; } // Force a gesture to be played, for example, if it is being // previewed. void playGesture(LLMultiGesture* gesture); @@ -106,7 +113,15 @@ public: // Also remove from playing list void stopGesture(LLMultiGesture* gesture); void stopGesture(const LLUUID& item_id); - + /** + * Add cb into callbackMap. + * Note: + * Manager will call cb after gesture will be loaded and will remove cb automatically. + */ + void setGestureLoadedCallback(LLUUID inv_item_id, gesture_loaded_callback_t cb) + { + mCallbackMap[inv_item_id] = cb; + } // Trigger the first gesture that matches this key. // Returns TRUE if it finds a gesture bound to that key. BOOL triggerGesture(KEY key, MASK mask); @@ -144,13 +159,7 @@ protected: LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); -public: - BOOL mValid; - std::vector<LLMultiGesture*> mPlaying; - - // Maps inventory item_id to gesture - typedef std::map<LLUUID, LLMultiGesture*> item_map_t; - +private: // Active gestures. // NOTE: The gesture pointer CAN BE NULL. This means that // there is a gesture with that item_id, but the asset data @@ -159,8 +168,11 @@ public: S32 mLoadingCount; std::string mDeactivateSimilarNames; - + std::vector<LLGestureManagerObserver*> mObservers; + callback_map_t mCallbackMap; + std::vector<LLMultiGesture*> mPlaying; + BOOL mValid; }; #endif diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 5ea900e46d..059912d5d4 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -120,11 +120,7 @@ void LLIMFloater::newIMCallback(const LLSD& data){ LLUUID session_id = data["session_id"].asUUID(); LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); - if (floater == NULL) - { - llwarns << "new_im_callback for non-existent session_id " << session_id << llendl; - return; - } + if (floater == NULL) return; // update if visible, otherwise will be updated when opened if (floater->getVisible()) diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 0b8b5935f8..87b801d73b 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -848,8 +848,11 @@ void LLFloaterIMPanel::processSessionUpdate(const LLSD& session_update) } - //update the speakers dropdown too - mSpeakerPanel->setVoiceModerationCtrlMode(voice_moderated); + //update the speakers dropdown too, if it's available + if (mSpeakerPanel) + { + mSpeakerPanel->setVoiceModerationCtrlMode(voice_moderated); + } } } diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 9c38d9c5fc..ee785e7ecb 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -46,6 +46,7 @@ #include "llagent.h" #include "llavatariconctrl.h" +#include "llbottomtray.h" #include "llcallingcard.h" #include "llchat.h" #include "llresmgr.h" @@ -73,6 +74,7 @@ #include "llvoicechannel.h" #include "lltrans.h" #include "llrecentpeople.h" +#include "llsyswellwindow.h" #include "llfirstuse.h" #include "llagentui.h" @@ -1087,16 +1089,90 @@ LLIMMgr::onConfirmForceCloseError( //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLOutgoingCallDialog +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LLOutgoingCallDialog::LLOutgoingCallDialog(const LLSD& payload) : + LLDockableFloater(NULL, false, payload), + mPayload(payload) +{ +} + +void LLOutgoingCallDialog::getAllowedRect(LLRect& rect) +{ + rect = gViewerWindow->getWorldViewRectRaw(); +} + +void LLOutgoingCallDialog::onOpen(const LLSD& key) +{ + // tell the user which voice channel they are leaving + if (!mPayload["old_channel_name"].asString().empty()) + { + childSetTextArg("leaving", "[CURRENT_CHAT]", mPayload["old_channel_name"].asString()); + } + else + { + childSetTextArg("leaving", "[CURRENT_CHAT]", getString("localchat")); + } + + std::string callee_name = mPayload["session_name"].asString(); + if (callee_name == "anonymous") + { + callee_name = getString("anonymous"); + } + + setTitle(callee_name); + + LLSD callee_id = mPayload["other_user_id"]; + childSetTextArg("calling", "[CALLEE_NAME]", callee_name); + LLAvatarIconCtrl* icon = getChild<LLAvatarIconCtrl>("avatar_icon"); + icon->setValue(callee_id); + + // dock the dialog to the sys well, where other sys messages appear + setDockControl(new LLDockControl(LLBottomTray::getInstance()->getSysWell(), + this, getDockTongue(), LLDockControl::TOP, + boost::bind(&LLOutgoingCallDialog::getAllowedRect, this, _1))); +} + + +//static +void LLOutgoingCallDialog::onCancel(void* user_data) +{ + LLOutgoingCallDialog* self = (LLOutgoingCallDialog*)user_data; + + if (!gIMMgr) + return; + + LLUUID session_id = self->mPayload["session_id"].asUUID(); + gIMMgr->endCall(session_id); + + self->closeFloater(); +} + + +BOOL LLOutgoingCallDialog::postBuild() +{ + BOOL success = LLDockableFloater::postBuild(); + + childSetAction("Cancel", onCancel, this); + + return success; +} + + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLIncomingCallDialog //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LLIncomingCallDialog::LLIncomingCallDialog(const LLSD& payload) : - LLModalDialog(payload), + LLDockableFloater(NULL, false, payload), mPayload(payload) { } BOOL LLIncomingCallDialog::postBuild() { + LLDockableFloater::postBuild(); + LLSD caller_id = mPayload["caller_id"]; EInstantMessage type = (EInstantMessage)mPayload["type"].asInteger(); @@ -1115,6 +1191,11 @@ BOOL LLIncomingCallDialog::postBuild() call_type = getString("VoiceInviteAdHoc"); } + // check to see if this is an Avaline call + LLUUID session_id = mPayload["session_id"].asUUID(); + bool is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(session_id);
+ childSetVisible("Start IM", is_avatar); // no IM for avaline + LLUICtrl* caller_name_widget = getChild<LLUICtrl>("caller name"); caller_name_widget->setValue(caller_name + " " + call_type); LLAvatarIconCtrl* icon = getChild<LLAvatarIconCtrl>("avatar_icon"); @@ -1128,6 +1209,30 @@ BOOL LLIncomingCallDialog::postBuild() return TRUE; } +void LLIncomingCallDialog::getAllowedRect(LLRect& rect) +{ + rect = gViewerWindow->getWorldViewRectRaw(); +} + +void LLIncomingCallDialog::onOpen(const LLSD& key) +{ + // tell the user which voice channel they would be leaving + LLVoiceChannel *voice = LLVoiceChannel::getCurrentVoiceChannel(); + if (voice && !voice->getSessionName().empty()) + { + childSetTextArg("question", "[CURRENT_CHAT]", voice->getSessionName()); + } + else + { + childSetTextArg("question", "[CURRENT_CHAT]", getString("localchat")); + } + + // dock the dialog to the sys well, where other sys messages appear + setDockControl(new LLDockControl(LLBottomTray::getInstance()->getSysWell(), + this, getDockTongue(), LLDockControl::TOP, + boost::bind(&LLIncomingCallDialog::getAllowedRect, this, _1))); +} + //static void LLIncomingCallDialog::onAccept(void* user_data) { diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index c566b111ca..62a54bc081 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -34,9 +34,11 @@ #define LL_LLIMVIEW_H #include "lldarray.h" +#include "lldockablefloater.h" #include "llspeakers.h" //for LLIMSpeakerMgr #include "llimpanel.h" //for voice channels #include "llmodaldialog.h" +#include "lldockablefloater.h" #include "llinstantmessage.h" #include "lluuid.h" #include "llmultifloater.h" @@ -401,12 +403,13 @@ private: LLSD mPendingAgentListUpdates; }; -class LLIncomingCallDialog : public LLModalDialog +class LLIncomingCallDialog : public LLDockableFloater { public: LLIncomingCallDialog(const LLSD& payload); /*virtual*/ BOOL postBuild(); + /*virtual*/ void onOpen(const LLSD& key); static void onAccept(void* user_data); static void onReject(void* user_data); @@ -414,6 +417,23 @@ public: private: void processCallResponse(S32 response); + void getAllowedRect(LLRect& rect); + + LLSD mPayload; +}; + +class LLOutgoingCallDialog : public LLDockableFloater +{ +public: + LLOutgoingCallDialog(const LLSD& payload); + + /*virtual*/ BOOL postBuild(); + /*virtual*/ void onOpen(const LLSD& key); + + static void onCancel(void* user_data); + +private: + void getAllowedRect(LLRect& rect); LLSD mPayload; }; diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index e7d7eb19d0..4b0d524906 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -176,6 +176,7 @@ LLInventoryModel::LLInventoryModel() mRootFolderID(), mLibraryRootFolderID(), mLibraryOwnerID(), + mIsNotifyObservers(FALSE), mIsAgentInvUsable(false) { } @@ -537,7 +538,10 @@ void LLInventoryModel::updateLinkedItems(const LLUUID& object_id) item_array, LLInventoryModel::INCLUDE_TRASH, is_linked_item_match); - + if (cat_array.empty() && item_array.empty()) + { + return; + } for (LLInventoryModel::cat_array_t::iterator cat_iter = cat_array.begin(); cat_iter != cat_array.end(); cat_iter++) @@ -639,6 +643,7 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item) new_item = old_item; LLUUID old_parent_id = old_item->getParentUUID(); LLUUID new_parent_id = item->getParentUUID(); + if(old_parent_id != new_parent_id) { // need to update the parent-child tree @@ -1133,6 +1138,15 @@ BOOL LLInventoryModel::containsObserver(LLInventoryObserver* observer) const // The optional argument 'service_name' is used by Agent Inventory Service [DEV-20328] void LLInventoryModel::notifyObservers(const std::string service_name) { + if (mIsNotifyObservers) + { + // Within notifyObservers, something called notifyObservers + // again. This type of recursion is unsafe because it causes items to be + // processed twice, and this can easily lead to infinite loops. + llwarns << "Call was made to notifyObservers within notifyObservers!" << llendl; + return; + } + mIsNotifyObservers = TRUE; for (observer_list_t::iterator iter = mObservers.begin(); iter != mObservers.end(); ) { @@ -1154,12 +1168,21 @@ void LLInventoryModel::notifyObservers(const std::string service_name) mModifyMask = LLInventoryObserver::NONE; mChangedItemIDs.clear(); + mIsNotifyObservers = FALSE; } // store flag for change // and id of object change applies to void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent) { + if (mIsNotifyObservers) + { + // Something marked an item for change within a call to notifyObservers + // (which is in the process of processing the list of items marked for change). + // This means the change may fail to be processed. + llwarns << "Adding changed mask within notify observers! Change will likely be lost." << llendl; + } + mModifyMask |= mask; if (referent.notNull()) { @@ -1833,13 +1856,13 @@ void LLInventoryModel::addItem(LLViewerInventoryItem* item) { //llinfos << "LLInventoryModel::addItem()" << llendl; - - // This can happen if assettype enums change. This can be a backwards compatibility issue - // in some viewer prototypes prior to when the AT_LINK enum changed from 23 to 24. + // This can happen if assettype enums from llassettype.h ever change. + // For example, there is a known backwards compatibility issue in some viewer prototypes prior to when + // the AT_LINK enum changed from 23 to 24. if ((item->getType() == LLAssetType::AT_NONE) || LLAssetType::lookup(item->getType()) == LLAssetType::badLookup()) { - llwarns << "Got bad asset type for item ( name: " << item->getName() << " type: " << item->getType() << " inv-type: " << item->getInventoryType() << " ), ignoring." << llendl; + llwarns << "Got bad asset type for item [ name: " << item->getName() << " type: " << item->getType() << " inv-type: " << item->getInventoryType() << " ], ignoring." << llendl; return; } if(item) @@ -1848,7 +1871,7 @@ void LLInventoryModel::addItem(LLViewerInventoryItem* item) // The item will show up as a broken link. if (item->getIsBrokenLink()) { - llinfos << "Adding broken link ( name: " << item->getName() << " itemID: " << item->getUUID() << " assetID: " << item->getAssetUUID() << " ) parent: " << item->getParentUUID() << llendl; + llinfos << "Adding broken link [ name: " << item->getName() << " itemID: " << item->getUUID() << " assetID: " << item->getAssetUUID() << " ) parent: " << item->getParentUUID() << llendl; } mItemMap[item->getUUID()] = item; } @@ -2176,7 +2199,7 @@ bool LLInventoryModel::loadSkeleton( // Add all the items loaded which are parented to a // category with a correctly cached parent - count = items.count(); + S32 bad_link_count = 0; cat_map_t::iterator unparented = mCategoryMap.end(); for(item_array_t::const_iterator item_iter = items.begin(); item_iter != items.end(); @@ -2193,7 +2216,11 @@ bool LLInventoryModel::loadSkeleton( // This can happen if the linked object's baseobj is removed from the cache but the linked object is still in the cache. if (item->getIsBrokenLink()) { - llinfos << "Attempted to add cached link item without baseobj present ( name: " << item->getName() << " itemID: " << item->getUUID() << " assetID: " << item->getAssetUUID() << " ). Ignoring and invalidating " << cat->getName() << " . " << llendl; + bad_link_count++; + lldebugs << "Attempted to add cached link item without baseobj present ( name: " + << item->getName() << " itemID: " << item->getUUID() + << " assetID: " << item->getAssetUUID() + << " ). Ignoring and invalidating " << cat->getName() << " . " << llendl; invalid_categories.insert(cit->second); continue; } @@ -2203,6 +2230,12 @@ bool LLInventoryModel::loadSkeleton( } } } + if (bad_link_count > 0) + { + llinfos << "Attempted to add " << bad_link_count + << " cached link items without baseobj present. " + << "The corresponding categories were invalidated." << llendl; + } } else { @@ -3307,6 +3340,12 @@ void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**) for(i = 0; i < count; ++i) { titem->unpackMessage(msg, _PREHASH_ItemData, i); + // If the item has already been added (e.g. from link prefetch), then it doesn't need to be re-added. + if (gInventory.getItem(titem->getUUID())) + { + llinfos << "Skipping prefetched item [ Name: " << titem->getName() << " | Type: " << titem->getActualType() << " | ItemUUID: " << titem->getUUID() << " ] " << llendl; + continue; + } gInventory.updateItem(titem); } @@ -3682,513 +3721,6 @@ bool LLNameCategoryCollector::operator()( return false; } - - -///---------------------------------------------------------------------------- -/// Observers -///---------------------------------------------------------------------------- - -void LLInventoryCompletionObserver::changed(U32 mask) -{ - // scan through the incomplete items and move or erase them as - // appropriate. - if(!mIncomplete.empty()) - { - for(item_ref_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); ) - { - LLViewerInventoryItem* item = gInventory.getItem(*it); - if(!item) - { - it = mIncomplete.erase(it); - continue; - } - if(item->isComplete()) - { - mComplete.push_back(*it); - it = mIncomplete.erase(it); - continue; - } - ++it; - } - if(mIncomplete.empty()) - { - done(); - } - } -} - -void LLInventoryCompletionObserver::watchItem(const LLUUID& id) -{ - if(id.notNull()) - { - mIncomplete.push_back(id); - } -} - - -void LLInventoryFetchObserver::changed(U32 mask) -{ - // scan through the incomplete items and move or erase them as - // appropriate. - if(!mIncomplete.empty()) - { - for(item_ref_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); ) - { - LLViewerInventoryItem* item = gInventory.getItem(*it); - if(!item) - { - // BUG: This can cause done() to get called prematurely below. - // This happens with the LLGestureInventoryFetchObserver that - // loads gestures at startup. JC - it = mIncomplete.erase(it); - continue; - } - if(item->isComplete()) - { - mComplete.push_back(*it); - it = mIncomplete.erase(it); - continue; - } - ++it; - } - if(mIncomplete.empty()) - { - done(); - } - } - //llinfos << "LLInventoryFetchObserver::changed() mComplete size " << mComplete.size() << llendl; - //llinfos << "LLInventoryFetchObserver::changed() mIncomplete size " << mIncomplete.size() << llendl; -} - -bool LLInventoryFetchObserver::isEverythingComplete() const -{ - return mIncomplete.empty(); -} - -void fetch_items_from_llsd(const LLSD& items_llsd) -{ - if (!items_llsd.size()) return; - LLSD body; - body[0]["cap_name"] = "FetchInventory"; - body[1]["cap_name"] = "FetchLib"; - for (S32 i=0; i<items_llsd.size();i++) - { - if (items_llsd[i]["owner_id"].asString() == gAgent.getID().asString()) - { - body[0]["items"].append(items_llsd[i]); - continue; - } - if (items_llsd[i]["owner_id"].asString() == ALEXANDRIA_LINDEN_ID.asString()) - { - body[1]["items"].append(items_llsd[i]); - continue; - } - } - - for (S32 i=0; i<body.size(); i++) - { - if (0 >= body[i].size()) continue; - std::string url = gAgent.getRegion()->getCapability(body[i]["cap_name"].asString()); - - if (!url.empty()) - { - body[i]["agent_id"] = gAgent.getID(); - LLHTTPClient::post(url, body[i], new LLInventoryModel::fetchInventoryResponder(body[i])); - break; - } - - LLMessageSystem* msg = gMessageSystem; - BOOL start_new_message = TRUE; - for (S32 j=0; j<body[i]["items"].size(); j++) - { - LLSD item_entry = body[i]["items"][j]; - if(start_new_message) - { - start_new_message = FALSE; - msg->newMessageFast(_PREHASH_FetchInventory); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - } - msg->nextBlockFast(_PREHASH_InventoryData); - msg->addUUIDFast(_PREHASH_OwnerID, item_entry["owner_id"].asUUID()); - msg->addUUIDFast(_PREHASH_ItemID, item_entry["item_id"].asUUID()); - if(msg->isSendFull(NULL)) - { - start_new_message = TRUE; - gAgent.sendReliableMessage(); - } - } - if(!start_new_message) - { - gAgent.sendReliableMessage(); - } - } -} - -void LLInventoryFetchObserver::fetchItems( - const LLInventoryFetchObserver::item_ref_t& ids) -{ - LLUUID owner_id; - LLSD items_llsd; - for(item_ref_t::const_iterator it = ids.begin(); it < ids.end(); ++it) - { - LLViewerInventoryItem* item = gInventory.getItem(*it); - if(item) - { - if(item->isComplete()) - { - // It's complete, so put it on the complete container. - mComplete.push_back(*it); - continue; - } - else - { - owner_id = item->getPermissions().getOwner(); - } - } - else - { - // assume it's agent inventory. - owner_id = gAgent.getID(); - } - - // It's incomplete, so put it on the incomplete container, and - // pack this on the message. - mIncomplete.push_back(*it); - - // Prepare the data to fetch - LLSD item_entry; - item_entry["owner_id"] = owner_id; - item_entry["item_id"] = (*it); - items_llsd.append(item_entry); - } - fetch_items_from_llsd(items_llsd); -} - -// virtual -void LLInventoryFetchDescendentsObserver::changed(U32 mask) -{ - for(folder_ref_t::iterator it = mIncompleteFolders.begin(); it < mIncompleteFolders.end();) - { - LLViewerInventoryCategory* cat = gInventory.getCategory(*it); - if(!cat) - { - it = mIncompleteFolders.erase(it); - continue; - } - if(isComplete(cat)) - { - mCompleteFolders.push_back(*it); - it = mIncompleteFolders.erase(it); - continue; - } - ++it; - } - if(mIncompleteFolders.empty()) - { - done(); - } -} - -void LLInventoryFetchDescendentsObserver::fetchDescendents( - const folder_ref_t& ids) -{ - for(folder_ref_t::const_iterator it = ids.begin(); it != ids.end(); ++it) - { - LLViewerInventoryCategory* cat = gInventory.getCategory(*it); - if(!cat) continue; - if(!isComplete(cat)) - { - cat->fetchDescendents(); //blindly fetch it without seeing if anything else is fetching it. - mIncompleteFolders.push_back(*it); //Add to list of things being downloaded for this observer. - } - else - { - mCompleteFolders.push_back(*it); - } - } -} - -bool LLInventoryFetchDescendentsObserver::isEverythingComplete() const -{ - return mIncompleteFolders.empty(); -} - -bool LLInventoryFetchDescendentsObserver::isComplete(LLViewerInventoryCategory* cat) -{ - S32 version = cat->getVersion(); - S32 descendents = cat->getDescendentCount(); - if((LLViewerInventoryCategory::VERSION_UNKNOWN == version) - || (LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN == descendents)) - { - return false; - } - // it might be complete - check known descendents against - // currently available. - LLInventoryModel::cat_array_t* cats; - LLInventoryModel::item_array_t* items; - gInventory.getDirectDescendentsOf(cat->getUUID(), cats, items); - if(!cats || !items) - { - // bit of a hack - pretend we're done if they are gone or - // incomplete. should never know, but it would suck if this - // kept tight looping because of a corrupt memory state. - return true; - } - S32 known = cats->count() + items->count(); - if(descendents == known) - { - // hey - we're done. - return true; - } - return false; -} - -void LLInventoryFetchComboObserver::changed(U32 mask) -{ - if(!mIncompleteItems.empty()) - { - for(item_ref_t::iterator it = mIncompleteItems.begin(); it < mIncompleteItems.end(); ) - { - LLViewerInventoryItem* item = gInventory.getItem(*it); - if(!item) - { - it = mIncompleteItems.erase(it); - continue; - } - if(item->isComplete()) - { - mCompleteItems.push_back(*it); - it = mIncompleteItems.erase(it); - continue; - } - ++it; - } - } - if(!mIncompleteFolders.empty()) - { - for(folder_ref_t::iterator it = mIncompleteFolders.begin(); it < mIncompleteFolders.end();) - { - LLViewerInventoryCategory* cat = gInventory.getCategory(*it); - if(!cat) - { - it = mIncompleteFolders.erase(it); - continue; - } - if(gInventory.isCategoryComplete(*it)) - { - mCompleteFolders.push_back(*it); - it = mIncompleteFolders.erase(it); - continue; - } - ++it; - } - } - if(!mDone && mIncompleteItems.empty() && mIncompleteFolders.empty()) - { - mDone = true; - done(); - } -} - -void LLInventoryFetchComboObserver::fetch( - const folder_ref_t& folder_ids, - const item_ref_t& item_ids) -{ - lldebugs << "LLInventoryFetchComboObserver::fetch()" << llendl; - for(folder_ref_t::const_iterator fit = folder_ids.begin(); fit != folder_ids.end(); ++fit) - { - LLViewerInventoryCategory* cat = gInventory.getCategory(*fit); - if(!cat) continue; - if(!gInventory.isCategoryComplete(*fit)) - { - cat->fetchDescendents(); - lldebugs << "fetching folder " << *fit <<llendl; - mIncompleteFolders.push_back(*fit); - } - else - { - mCompleteFolders.push_back(*fit); - lldebugs << "completing folder " << *fit <<llendl; - } - } - - // Now for the items - we fetch everything which is not a direct - // descendent of an incomplete folder because the item will show - // up in an inventory descendents message soon enough so we do not - // have to fetch it individually. - LLSD items_llsd; - LLUUID owner_id; - for(item_ref_t::const_iterator iit = item_ids.begin(); iit != item_ids.end(); ++iit) - { - LLViewerInventoryItem* item = gInventory.getItem(*iit); - if(!item) - { - lldebugs << "uanble to find item " << *iit << llendl; - continue; - } - if(item->isComplete()) - { - // It's complete, so put it on the complete container. - mCompleteItems.push_back(*iit); - lldebugs << "completing item " << *iit << llendl; - continue; - } - else - { - mIncompleteItems.push_back(*iit); - owner_id = item->getPermissions().getOwner(); - } - if(std::find(mIncompleteFolders.begin(), mIncompleteFolders.end(), item->getParentUUID()) == mIncompleteFolders.end()) - { - LLSD item_entry; - item_entry["owner_id"] = owner_id; - item_entry["item_id"] = (*iit); - items_llsd.append(item_entry); - } - else - { - lldebugs << "not worrying about " << *iit << llendl; - } - } - fetch_items_from_llsd(items_llsd); -} - -void LLInventoryExistenceObserver::watchItem(const LLUUID& id) -{ - if(id.notNull()) - { - mMIA.push_back(id); - } -} - -void LLInventoryExistenceObserver::changed(U32 mask) -{ - // scan through the incomplete items and move or erase them as - // appropriate. - if(!mMIA.empty()) - { - for(item_ref_t::iterator it = mMIA.begin(); it < mMIA.end(); ) - { - LLViewerInventoryItem* item = gInventory.getItem(*it); - if(!item) - { - ++it; - continue; - } - mExist.push_back(*it); - it = mMIA.erase(it); - } - if(mMIA.empty()) - { - done(); - } - } -} - -void LLInventoryAddedObserver::changed(U32 mask) -{ - if(!(mask & LLInventoryObserver::ADD)) - { - return; - } - - // *HACK: If this was in response to a packet off - // the network, figure out which item was updated. - LLMessageSystem* msg = gMessageSystem; - - std::string msg_name; - if (mMessageName.empty()) - { - msg_name = msg->getMessageName(); - } - else - { - msg_name = mMessageName; - } - - if (msg_name.empty()) - { - return; - } - - // We only want newly created inventory items. JC - if ( msg_name != "UpdateCreateInventoryItem") - { - return; - } - - LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem; - S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_InventoryData); - for(S32 i = 0; i < num_blocks; ++i) - { - titem->unpackMessage(msg, _PREHASH_InventoryData, i); - if (!(titem->getUUID().isNull())) - { - //we don't do anything with null keys - mAdded.push_back(titem->getUUID()); - } - } - if (!mAdded.empty()) - { - done(); - } -} - -LLInventoryTransactionObserver::LLInventoryTransactionObserver( - const LLTransactionID& transaction_id) : - mTransactionID(transaction_id) -{ -} - -void LLInventoryTransactionObserver::changed(U32 mask) -{ - if(mask & LLInventoryObserver::ADD) - { - // This could be it - see if we are processing a bulk update - LLMessageSystem* msg = gMessageSystem; - if(msg->getMessageName() - && (0 == strcmp(msg->getMessageName(), "BulkUpdateInventory"))) - { - // we have a match for the message - now check the - // transaction id. - LLUUID id; - msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, id); - if(id == mTransactionID) - { - // woo hoo, we found it - folder_ref_t folders; - item_ref_t items; - S32 count; - count = msg->getNumberOfBlocksFast(_PREHASH_FolderData); - S32 i; - for(i = 0; i < count; ++i) - { - msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_FolderID, id, i); - if(id.notNull()) - { - folders.push_back(id); - } - } - count = msg->getNumberOfBlocksFast(_PREHASH_ItemData); - for(i = 0; i < count; ++i) - { - msg->getUUIDFast(_PREHASH_ItemData, _PREHASH_ItemID, id, i); - if(id.notNull()) - { - items.push_back(id); - } - } - - // call the derived class the implements this method. - done(folders, items); - } - } - } -} - - ///---------------------------------------------------------------------------- /// LLAssetIDMatches ///---------------------------------------------------------------------------- diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index faf026887a..bd64591194 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -41,40 +41,27 @@ #include "lluuid.h" #include "llpermissionsflags.h" #include "llstring.h" - #include <map> #include <set> #include <string> #include <vector> -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryObserver -// -// This class is designed to be a simple abstract base class which can -// relay messages when the inventory changes. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// ! REFACTOR ! Remove llinventoryobservers.h and have other files that need it explicitly +// include llinventoryobservers.h instead of llinventorymodel.h . This will reduce dependency on +// llinventorymodel.h. +#include "llinventoryobserver.h" + +class LLInventoryObserver; +class LLInventoryObject; +class LLInventoryItem; +class LLInventoryCategory; +class LLViewerInventoryItem; +class LLViewerInventoryCategory; +class LLViewerInventoryItem; +class LLViewerInventoryCategory; +class LLMessageSystem; +class LLInventoryCollectFunctor; -class LLInventoryObserver -{ -public: - // This enumeration is a way to refer to what changed in a more - // human readable format. You can mask the value provided by - // chaged() to see if the observer is interested in the change. - enum - { - NONE = 0, - LABEL = 1, // name changed - INTERNAL = 2, // internal change, eg, asset uuid different - ADD = 4, // something added - REMOVE = 8, // something deleted - STRUCTURE = 16, // structural change, eg, item or folder moved - CALLING_CARD = 32, // online, grant status, cancel, etc change - ALL = 0xffffffff - }; - virtual ~LLInventoryObserver() {}; - virtual void changed(U32 mask) = 0; - std::string mMessageName; // used by Agent Inventory Service only. [DEV-20328] -}; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLInventoryModel @@ -87,16 +74,6 @@ public: //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLInventoryObject; -class LLInventoryItem; -class LLInventoryCategory; -class LLViewerInventoryItem; -class LLViewerInventoryCategory; -class LLViewerInventoryItem; -class LLViewerInventoryCategory; -class LLMessageSystem; -class LLInventoryCollectFunctor; - class LLInventoryModel { public: @@ -473,23 +450,12 @@ protected: cat_array_t* getUnlockedCatArray(const LLUUID& id); item_array_t* getUnlockedItemArray(const LLUUID& id); -protected: +private: // Variables used to track what has changed since the last notify. U32 mModifyMask; typedef std::set<LLUUID> changed_items_t; changed_items_t mChangedItemIDs; - // Information for tracking the actual inventory. We index this - // information in a lot of different ways so we can access - // the inventory using several different identifiers. - // mInventory member data is the 'master' list of inventory, and - // mCategoryMap and mItemMap store uuid->object mappings. - typedef std::map<LLUUID, LLPointer<LLViewerInventoryCategory> > cat_map_t; - typedef std::map<LLUUID, LLPointer<LLViewerInventoryItem> > item_map_t; - //inv_map_t mInventory; - cat_map_t mCategoryMap; - item_map_t mItemMap; - std::map<LLUUID, bool> mCategoryLock; std::map<LLUUID, bool> mItemLock; @@ -525,6 +491,21 @@ protected: // This flag is used to handle an invalid inventory state. bool mIsAgentInvUsable; +private: + // Information for tracking the actual inventory. We index this + // information in a lot of different ways so we can access + // the inventory using several different identifiers. + // mInventory member data is the 'master' list of inventory, and + // mCategoryMap and mItemMap store uuid->object mappings. + typedef std::map<LLUUID, LLPointer<LLViewerInventoryCategory> > cat_map_t; + typedef std::map<LLUUID, LLPointer<LLViewerInventoryItem> > item_map_t; + //inv_map_t mInventory; + cat_map_t mCategoryMap; + item_map_t mItemMap; + + // Flag set when notifyObservers is being called, to look for bugs + // where it's called recursively. + BOOL mIsNotifyObservers; public: // *NOTE: DEBUG functionality void dumpInventory() const; @@ -767,183 +748,5 @@ public: LLInventoryItem* item); }; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryCompletionObserver -// -// Class which can be used as a base class for doing something when -// when all observed items are locally complete. This class implements -// the changed() method of LLInventoryObserver and declares a new -// method named done() which is called when all watched items have -// complete information in the inventory model. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLInventoryCompletionObserver : public LLInventoryObserver -{ -public: - LLInventoryCompletionObserver() {} - virtual void changed(U32 mask); - - void watchItem(const LLUUID& id); - -protected: - virtual void done() = 0; - - typedef std::vector<LLUUID> item_ref_t; - item_ref_t mComplete; - item_ref_t mIncomplete; -}; - - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryFetchObserver -// -// This class is much like the LLInventoryCompletionObserver, except -// that it handles all the the fetching necessary. Override the done() -// method to do the thing you want. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLInventoryFetchObserver : public LLInventoryObserver -{ -public: - LLInventoryFetchObserver() {} - virtual void changed(U32 mask); - - typedef std::vector<LLUUID> item_ref_t; - - bool isEverythingComplete() const; - void fetchItems(const item_ref_t& ids); - virtual void done() = 0; - -protected: - item_ref_t mComplete; - item_ref_t mIncomplete; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryFetchDescendentsObserver -// -// This class is much like the LLInventoryCompletionObserver, except -// that it handles fetching based on category. Override the done() -// method to do the thing you want. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLInventoryFetchDescendentsObserver : public LLInventoryObserver -{ -public: - LLInventoryFetchDescendentsObserver() {} - virtual void changed(U32 mask); - - typedef std::vector<LLUUID> folder_ref_t; - void fetchDescendents(const folder_ref_t& ids); - bool isEverythingComplete() const; - virtual void done() = 0; - -protected: - bool isComplete(LLViewerInventoryCategory* cat); - folder_ref_t mIncompleteFolders; - folder_ref_t mCompleteFolders; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryFetchComboObserver -// -// This class does an appropriate combination of fetch descendents and -// item fetches based on completion of categories and items. Much like -// the fetch and fetch descendents, this will call done() when everything -// has arrived. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLInventoryFetchComboObserver : public LLInventoryObserver -{ -public: - LLInventoryFetchComboObserver() : mDone(false) {} - virtual void changed(U32 mask); - - typedef std::vector<LLUUID> folder_ref_t; - typedef std::vector<LLUUID> item_ref_t; - void fetch(const folder_ref_t& folder_ids, const item_ref_t& item_ids); - - virtual void done() = 0; - -protected: - bool mDone; - folder_ref_t mCompleteFolders; - folder_ref_t mIncompleteFolders; - item_ref_t mCompleteItems; - item_ref_t mIncompleteItems; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryExistenceObserver -// -// This class is used as a base class for doing somethign when all the -// observed item ids exist in the inventory somewhere. You can derive -// a class from this class and implement the done() method to do -// something useful. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLInventoryExistenceObserver : public LLInventoryObserver -{ -public: - LLInventoryExistenceObserver() {} - virtual void changed(U32 mask); - - void watchItem(const LLUUID& id); - -protected: - virtual void done() = 0; - - typedef std::vector<LLUUID> item_ref_t; - item_ref_t mExist; - item_ref_t mMIA; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryAddedObserver -// -// This class is used as a base class for doing something when -// a new item arrives in inventory. -// It does not watch for a certain UUID, rather it acts when anything is added -// Derive a class from this class and implement the done() method to do -// something useful. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLInventoryAddedObserver : public LLInventoryObserver -{ -public: - LLInventoryAddedObserver() : mAdded() {} - virtual void changed(U32 mask); - -protected: - virtual void done() = 0; - - typedef std::vector<LLUUID> item_ref_t; - item_ref_t mAdded; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInventoryTransactionObserver -// -// Class which can be used as a base class for doing something when an -// inventory transaction completes. -// -// *NOTE: This class is not quite complete. Avoid using unless you fix up it's -// functionality gaps. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLInventoryTransactionObserver : public LLInventoryObserver -{ -public: - LLInventoryTransactionObserver(const LLTransactionID& transaction_id); - virtual void changed(U32 mask); - -protected: - typedef std::vector<LLUUID> folder_ref_t; - typedef std::vector<LLUUID> item_ref_t; - virtual void done(const folder_ref_t& folders, const item_ref_t& items) = 0; - - LLTransactionID mTransactionID; -}; - - #endif // LL_LLINVENTORYMODEL_H diff --git a/indra/newview/llinventoryobserver.cpp b/indra/newview/llinventoryobserver.cpp new file mode 100644 index 0000000000..3ccf593d27 --- /dev/null +++ b/indra/newview/llinventoryobserver.cpp @@ -0,0 +1,564 @@ +/** + * @file llinventoryobserver.cpp + * @brief Implementation of the inventory observers used to track agent inventory. + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llinventoryobserver.h" + +#include "llassetstorage.h" +#include "llcrc.h" +#include "lldir.h" +#include "llsys.h" +#include "llxfermanager.h" +#include "message.h" + +#include "llagent.h" +#include "llagentwearables.h" +#include "llfloater.h" +#include "llfocusmgr.h" +#include "llinventorybridge.h" +#include "llinventoryfunctions.h" +#include "llinventorymodel.h" +#include "llviewermessage.h" +#include "llviewerwindow.h" +#include "llviewerregion.h" +#include "llappviewer.h" +#include "lldbstrings.h" +#include "llviewerstats.h" +#include "llmutelist.h" +#include "llnotifications.h" +#include "llcallbacklist.h" +#include "llpreview.h" +#include "llviewercontrol.h" +#include "llvoavatarself.h" +#include "llsdutil.h" +#include <deque> + +void LLInventoryCompletionObserver::changed(U32 mask) +{ + // scan through the incomplete items and move or erase them as + // appropriate. + if(!mIncomplete.empty()) + { + for(item_ref_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); ) + { + LLViewerInventoryItem* item = gInventory.getItem(*it); + if(!item) + { + it = mIncomplete.erase(it); + continue; + } + if(item->isComplete()) + { + mComplete.push_back(*it); + it = mIncomplete.erase(it); + continue; + } + ++it; + } + if(mIncomplete.empty()) + { + done(); + } + } +} + +void LLInventoryCompletionObserver::watchItem(const LLUUID& id) +{ + if(id.notNull()) + { + mIncomplete.push_back(id); + } +} + + +void LLInventoryFetchObserver::changed(U32 mask) +{ + // scan through the incomplete items and move or erase them as + // appropriate. + if(!mIncomplete.empty()) + { + for(item_ref_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); ) + { + LLViewerInventoryItem* item = gInventory.getItem(*it); + if(!item) + { + // BUG: This can cause done() to get called prematurely below. + // This happens with the LLGestureInventoryFetchObserver that + // loads gestures at startup. JC + it = mIncomplete.erase(it); + continue; + } + if(item->isComplete()) + { + mComplete.push_back(*it); + it = mIncomplete.erase(it); + continue; + } + ++it; + } + if(mIncomplete.empty()) + { + done(); + } + } + //llinfos << "LLInventoryFetchObserver::changed() mComplete size " << mComplete.size() << llendl; + //llinfos << "LLInventoryFetchObserver::changed() mIncomplete size " << mIncomplete.size() << llendl; +} + +bool LLInventoryFetchObserver::isEverythingComplete() const +{ + return mIncomplete.empty(); +} + +void fetch_items_from_llsd(const LLSD& items_llsd) +{ + if (!items_llsd.size()) return; + LLSD body; + body[0]["cap_name"] = "FetchInventory"; + body[1]["cap_name"] = "FetchLib"; + for (S32 i=0; i<items_llsd.size();i++) + { + if (items_llsd[i]["owner_id"].asString() == gAgent.getID().asString()) + { + body[0]["items"].append(items_llsd[i]); + continue; + } + if (items_llsd[i]["owner_id"].asString() == ALEXANDRIA_LINDEN_ID.asString()) + { + body[1]["items"].append(items_llsd[i]); + continue; + } + } + + for (S32 i=0; i<body.size(); i++) + { + if (0 >= body[i].size()) continue; + std::string url = gAgent.getRegion()->getCapability(body[i]["cap_name"].asString()); + + if (!url.empty()) + { + body[i]["agent_id"] = gAgent.getID(); + LLHTTPClient::post(url, body[i], new LLInventoryModel::fetchInventoryResponder(body[i])); + break; + } + + LLMessageSystem* msg = gMessageSystem; + BOOL start_new_message = TRUE; + for (S32 j=0; j<body[i]["items"].size(); j++) + { + LLSD item_entry = body[i]["items"][j]; + if(start_new_message) + { + start_new_message = FALSE; + msg->newMessageFast(_PREHASH_FetchInventory); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + } + msg->nextBlockFast(_PREHASH_InventoryData); + msg->addUUIDFast(_PREHASH_OwnerID, item_entry["owner_id"].asUUID()); + msg->addUUIDFast(_PREHASH_ItemID, item_entry["item_id"].asUUID()); + if(msg->isSendFull(NULL)) + { + start_new_message = TRUE; + gAgent.sendReliableMessage(); + } + } + if(!start_new_message) + { + gAgent.sendReliableMessage(); + } + } +} + +void LLInventoryFetchObserver::fetchItems( + const LLInventoryFetchObserver::item_ref_t& ids) +{ + LLUUID owner_id; + LLSD items_llsd; + for(item_ref_t::const_iterator it = ids.begin(); it < ids.end(); ++it) + { + LLViewerInventoryItem* item = gInventory.getItem(*it); + if(item) + { + if(item->isComplete()) + { + // It's complete, so put it on the complete container. + mComplete.push_back(*it); + continue; + } + else + { + owner_id = item->getPermissions().getOwner(); + } + } + else + { + // assume it's agent inventory. + owner_id = gAgent.getID(); + } + + // It's incomplete, so put it on the incomplete container, and + // pack this on the message. + mIncomplete.push_back(*it); + + // Prepare the data to fetch + LLSD item_entry; + item_entry["owner_id"] = owner_id; + item_entry["item_id"] = (*it); + items_llsd.append(item_entry); + } + fetch_items_from_llsd(items_llsd); +} + +// virtual +void LLInventoryFetchDescendentsObserver::changed(U32 mask) +{ + for(folder_ref_t::iterator it = mIncompleteFolders.begin(); it < mIncompleteFolders.end();) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(*it); + if(!cat) + { + it = mIncompleteFolders.erase(it); + continue; + } + if(isComplete(cat)) + { + mCompleteFolders.push_back(*it); + it = mIncompleteFolders.erase(it); + continue; + } + ++it; + } + if(mIncompleteFolders.empty()) + { + done(); + } +} + +void LLInventoryFetchDescendentsObserver::fetchDescendents( + const folder_ref_t& ids) +{ + for(folder_ref_t::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(*it); + if(!cat) continue; + if(!isComplete(cat)) + { + cat->fetchDescendents(); //blindly fetch it without seeing if anything else is fetching it. + mIncompleteFolders.push_back(*it); //Add to list of things being downloaded for this observer. + } + else + { + mCompleteFolders.push_back(*it); + } + } +} + +bool LLInventoryFetchDescendentsObserver::isEverythingComplete() const +{ + return mIncompleteFolders.empty(); +} + +bool LLInventoryFetchDescendentsObserver::isComplete(LLViewerInventoryCategory* cat) +{ + S32 version = cat->getVersion(); + S32 descendents = cat->getDescendentCount(); + if((LLViewerInventoryCategory::VERSION_UNKNOWN == version) + || (LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN == descendents)) + { + return false; + } + // it might be complete - check known descendents against + // currently available. + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(cat->getUUID(), cats, items); + if(!cats || !items) + { + // bit of a hack - pretend we're done if they are gone or + // incomplete. should never know, but it would suck if this + // kept tight looping because of a corrupt memory state. + return true; + } + S32 known = cats->count() + items->count(); + if(descendents == known) + { + // hey - we're done. + return true; + } + return false; +} + +void LLInventoryFetchComboObserver::changed(U32 mask) +{ + if(!mIncompleteItems.empty()) + { + for(item_ref_t::iterator it = mIncompleteItems.begin(); it < mIncompleteItems.end(); ) + { + LLViewerInventoryItem* item = gInventory.getItem(*it); + if(!item) + { + it = mIncompleteItems.erase(it); + continue; + } + if(item->isComplete()) + { + mCompleteItems.push_back(*it); + it = mIncompleteItems.erase(it); + continue; + } + ++it; + } + } + if(!mIncompleteFolders.empty()) + { + for(folder_ref_t::iterator it = mIncompleteFolders.begin(); it < mIncompleteFolders.end();) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(*it); + if(!cat) + { + it = mIncompleteFolders.erase(it); + continue; + } + if(gInventory.isCategoryComplete(*it)) + { + mCompleteFolders.push_back(*it); + it = mIncompleteFolders.erase(it); + continue; + } + ++it; + } + } + if(!mDone && mIncompleteItems.empty() && mIncompleteFolders.empty()) + { + mDone = true; + done(); + } +} + +void LLInventoryFetchComboObserver::fetch( + const folder_ref_t& folder_ids, + const item_ref_t& item_ids) +{ + lldebugs << "LLInventoryFetchComboObserver::fetch()" << llendl; + for(folder_ref_t::const_iterator fit = folder_ids.begin(); fit != folder_ids.end(); ++fit) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(*fit); + if(!cat) continue; + if(!gInventory.isCategoryComplete(*fit)) + { + cat->fetchDescendents(); + lldebugs << "fetching folder " << *fit <<llendl; + mIncompleteFolders.push_back(*fit); + } + else + { + mCompleteFolders.push_back(*fit); + lldebugs << "completing folder " << *fit <<llendl; + } + } + + // Now for the items - we fetch everything which is not a direct + // descendent of an incomplete folder because the item will show + // up in an inventory descendents message soon enough so we do not + // have to fetch it individually. + LLSD items_llsd; + LLUUID owner_id; + for(item_ref_t::const_iterator iit = item_ids.begin(); iit != item_ids.end(); ++iit) + { + LLViewerInventoryItem* item = gInventory.getItem(*iit); + if(!item) + { + lldebugs << "uanble to find item " << *iit << llendl; + continue; + } + if(item->isComplete()) + { + // It's complete, so put it on the complete container. + mCompleteItems.push_back(*iit); + lldebugs << "completing item " << *iit << llendl; + continue; + } + else + { + mIncompleteItems.push_back(*iit); + owner_id = item->getPermissions().getOwner(); + } + if(std::find(mIncompleteFolders.begin(), mIncompleteFolders.end(), item->getParentUUID()) == mIncompleteFolders.end()) + { + LLSD item_entry; + item_entry["owner_id"] = owner_id; + item_entry["item_id"] = (*iit); + items_llsd.append(item_entry); + } + else + { + lldebugs << "not worrying about " << *iit << llendl; + } + } + fetch_items_from_llsd(items_llsd); +} + +void LLInventoryExistenceObserver::watchItem(const LLUUID& id) +{ + if(id.notNull()) + { + mMIA.push_back(id); + } +} + +void LLInventoryExistenceObserver::changed(U32 mask) +{ + // scan through the incomplete items and move or erase them as + // appropriate. + if(!mMIA.empty()) + { + for(item_ref_t::iterator it = mMIA.begin(); it < mMIA.end(); ) + { + LLViewerInventoryItem* item = gInventory.getItem(*it); + if(!item) + { + ++it; + continue; + } + mExist.push_back(*it); + it = mMIA.erase(it); + } + if(mMIA.empty()) + { + done(); + } + } +} + +void LLInventoryAddedObserver::changed(U32 mask) +{ + if(!(mask & LLInventoryObserver::ADD)) + { + return; + } + + // *HACK: If this was in response to a packet off + // the network, figure out which item was updated. + LLMessageSystem* msg = gMessageSystem; + + std::string msg_name; + if (mMessageName.empty()) + { + msg_name = msg->getMessageName(); + } + else + { + msg_name = mMessageName; + } + + if (msg_name.empty()) + { + return; + } + + // We only want newly created inventory items. JC + if ( msg_name != "UpdateCreateInventoryItem") + { + return; + } + + LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem; + S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_InventoryData); + for(S32 i = 0; i < num_blocks; ++i) + { + titem->unpackMessage(msg, _PREHASH_InventoryData, i); + if (!(titem->getUUID().isNull())) + { + //we don't do anything with null keys + mAdded.push_back(titem->getUUID()); + } + } + if (!mAdded.empty()) + { + done(); + } +} + +LLInventoryTransactionObserver::LLInventoryTransactionObserver( + const LLTransactionID& transaction_id) : + mTransactionID(transaction_id) +{ +} + +void LLInventoryTransactionObserver::changed(U32 mask) +{ + if(mask & LLInventoryObserver::ADD) + { + // This could be it - see if we are processing a bulk update + LLMessageSystem* msg = gMessageSystem; + if(msg->getMessageName() + && (0 == strcmp(msg->getMessageName(), "BulkUpdateInventory"))) + { + // we have a match for the message - now check the + // transaction id. + LLUUID id; + msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, id); + if(id == mTransactionID) + { + // woo hoo, we found it + folder_ref_t folders; + item_ref_t items; + S32 count; + count = msg->getNumberOfBlocksFast(_PREHASH_FolderData); + S32 i; + for(i = 0; i < count; ++i) + { + msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_FolderID, id, i); + if(id.notNull()) + { + folders.push_back(id); + } + } + count = msg->getNumberOfBlocksFast(_PREHASH_ItemData); + for(i = 0; i < count; ++i) + { + msg->getUUIDFast(_PREHASH_ItemData, _PREHASH_ItemID, id, i); + if(id.notNull()) + { + items.push_back(id); + } + } + + // call the derived class the implements this method. + done(folders, items); + } + } + } +} diff --git a/indra/newview/llinventoryobserver.h b/indra/newview/llinventoryobserver.h new file mode 100644 index 0000000000..384e6292e8 --- /dev/null +++ b/indra/newview/llinventoryobserver.h @@ -0,0 +1,249 @@ +/** + * @file llinventoryobserver.h + * @brief LLInventoryObserver class header file + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLINVENTORYOBSERVERS_H +#define LL_LLINVENTORYOBSERVERS_H + +#include "lluuid.h" +#include <string> +#include <vector> + +class LLViewerInventoryCategory; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryObserver +// +// This class is designed to be a simple abstract base class which can +// relay messages when the inventory changes. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLInventoryObserver +{ +public: + // This enumeration is a way to refer to what changed in a more + // human readable format. You can mask the value provided by + // chaged() to see if the observer is interested in the change. + enum + { + NONE = 0, + LABEL = 1, // name changed + INTERNAL = 2, // internal change, eg, asset uuid different + ADD = 4, // something added + REMOVE = 8, // something deleted + STRUCTURE = 16, // structural change, eg, item or folder moved + CALLING_CARD = 32, // online, grant status, cancel, etc change + ALL = 0xffffffff + }; + virtual ~LLInventoryObserver() {}; + virtual void changed(U32 mask) = 0; + std::string mMessageName; // used by Agent Inventory Service only. [DEV-20328] +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryCompletionObserver +// +// Class which can be used as a base class for doing something when +// when all observed items are locally complete. This class implements +// the changed() method of LLInventoryObserver and declares a new +// method named done() which is called when all watched items have +// complete information in the inventory model. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLInventoryCompletionObserver : public LLInventoryObserver +{ +public: + LLInventoryCompletionObserver() {} + virtual void changed(U32 mask); + + void watchItem(const LLUUID& id); + +protected: + virtual void done() = 0; + + typedef std::vector<LLUUID> item_ref_t; + item_ref_t mComplete; + item_ref_t mIncomplete; +}; + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryFetchObserver +// +// This class is much like the LLInventoryCompletionObserver, except +// that it handles all the the fetching necessary. Override the done() +// method to do the thing you want. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLInventoryFetchObserver : public LLInventoryObserver +{ +public: + LLInventoryFetchObserver() {} + virtual void changed(U32 mask); + + typedef std::vector<LLUUID> item_ref_t; + + bool isEverythingComplete() const; + void fetchItems(const item_ref_t& ids); + virtual void done() = 0; + +protected: + item_ref_t mComplete; + item_ref_t mIncomplete; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryFetchDescendentsObserver +// +// This class is much like the LLInventoryCompletionObserver, except +// that it handles fetching based on category. Override the done() +// method to do the thing you want. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLInventoryFetchDescendentsObserver : public LLInventoryObserver +{ +public: + LLInventoryFetchDescendentsObserver() {} + virtual void changed(U32 mask); + + typedef std::vector<LLUUID> folder_ref_t; + void fetchDescendents(const folder_ref_t& ids); + bool isEverythingComplete() const; + virtual void done() = 0; + +protected: + bool isComplete(LLViewerInventoryCategory* cat); + folder_ref_t mIncompleteFolders; + folder_ref_t mCompleteFolders; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryFetchComboObserver +// +// This class does an appropriate combination of fetch descendents and +// item fetches based on completion of categories and items. Much like +// the fetch and fetch descendents, this will call done() when everything +// has arrived. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLInventoryFetchComboObserver : public LLInventoryObserver +{ +public: + LLInventoryFetchComboObserver() : mDone(false) {} + virtual void changed(U32 mask); + + typedef std::vector<LLUUID> folder_ref_t; + typedef std::vector<LLUUID> item_ref_t; + void fetch(const folder_ref_t& folder_ids, const item_ref_t& item_ids); + + virtual void done() = 0; + +protected: + bool mDone; + folder_ref_t mCompleteFolders; + folder_ref_t mIncompleteFolders; + item_ref_t mCompleteItems; + item_ref_t mIncompleteItems; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryExistenceObserver +// +// This class is used as a base class for doing somethign when all the +// observed item ids exist in the inventory somewhere. You can derive +// a class from this class and implement the done() method to do +// something useful. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLInventoryExistenceObserver : public LLInventoryObserver +{ +public: + LLInventoryExistenceObserver() {} + virtual void changed(U32 mask); + + void watchItem(const LLUUID& id); + +protected: + virtual void done() = 0; + + typedef std::vector<LLUUID> item_ref_t; + item_ref_t mExist; + item_ref_t mMIA; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryAddedObserver +// +// This class is used as a base class for doing something when +// a new item arrives in inventory. +// It does not watch for a certain UUID, rather it acts when anything is added +// Derive a class from this class and implement the done() method to do +// something useful. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLInventoryAddedObserver : public LLInventoryObserver +{ +public: + LLInventoryAddedObserver() : mAdded() {} + virtual void changed(U32 mask); + +protected: + virtual void done() = 0; + + typedef std::vector<LLUUID> item_ref_t; + item_ref_t mAdded; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryTransactionObserver +// +// Class which can be used as a base class for doing something when an +// inventory transaction completes. +// +// *NOTE: This class is not quite complete. Avoid using unless you fix up it's +// functionality gaps. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLInventoryTransactionObserver : public LLInventoryObserver +{ +public: + LLInventoryTransactionObserver(const LLTransactionID& transaction_id); + virtual void changed(U32 mask); + +protected: + typedef std::vector<LLUUID> folder_ref_t; + typedef std::vector<LLUUID> item_ref_t; + virtual void done(const folder_ref_t& folders, const item_ref_t& items) = 0; + + LLTransactionID mTransactionID; +}; + + +#endif // LL_LLINVENTORYOBSERVERS_H + diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 9a71e53441..dfd4af5c28 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -191,11 +191,9 @@ BOOL LLInventoryPanel::postBuild() {
rebuildViewsFor(mStartFolderID);
mHasInventoryConnection = true;
+ defaultOpenInventory();
}
- // bit of a hack to make sure the inventory is open.
- mFolders->openFolder(preferred_type != LLFolderType::FT_NONE ? LLViewerFolderType::lookupNewCategoryName(preferred_type) : "My Inventory");
-
if (mSortOrderSetting != INHERIT_SORT_ORDER)
{
setSortOrder(gSavedSettings.getU32(mSortOrderSetting));
@@ -300,6 +298,7 @@ void LLInventoryPanel::modelChanged(U32 mask) {
rebuildViewsFor(mStartFolderID);
mHasInventoryConnection = true;
+ defaultOpenInventory();
return;
}
@@ -561,6 +560,25 @@ void LLInventoryPanel::buildNewViews(const LLUUID& id) }
}
+// bit of a hack to make sure the inventory is open.
+void LLInventoryPanel::defaultOpenInventory()
+{
+ const LLFolderType::EType preferred_type = LLViewerFolderType::lookupTypeFromNewCategoryName(mStartFolderString);
+ if (preferred_type != LLFolderType::FT_NONE)
+ {
+ const std::string& top_level_folder_name = LLViewerFolderType::lookupNewCategoryName(preferred_type);
+ mFolders->openFolder(top_level_folder_name);
+ }
+ else
+ {
+ // Get the first child (it should be "My Inventory") and
+ // open it up by name (just to make sure the first child is actually a folder).
+ LLView* first_child = mFolders->getFirstChild();
+ const std::string& first_child_name = first_child->getName();
+ mFolders->openFolder(first_child_name);
+ }
+}
+
struct LLConfirmPurgeData
{
LLUUID mID;
diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index 9f74fad5c1..e398c44105 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -165,7 +165,7 @@ protected: // Given the id and the parent, build all of the folder views. void rebuildViewsFor(const LLUUID& id); virtual void buildNewViews(const LLUUID& id); // made virtual to support derived classes. EXT-719 - + void defaultOpenInventory(); // open the first level of inventory protected: LLInventoryModel* mInventory; LLInventoryObserver* mInventoryObserver; diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index 945294f3f2..a01426ea87 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -182,6 +182,9 @@ void LLLoginInstance::constructAuthParams(const LLSD& credentials) mRequestData["method"] = "login_to_simulator"; mRequestData["params"] = request_params; mRequestData["options"] = requested_options; + + mRequestData["cfg_srv_timeout"] = gSavedSettings.getF32("LoginSRVTimeout"); + mRequestData["cfg_srv_pump"] = gSavedSettings.getString("LoginSRVPump"); } bool LLLoginInstance::handleLoginEvent(const LLSD& event) diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp index 3993431311..333646d2c5 100644 --- a/indra/newview/llnearbychatbar.cpp +++ b/indra/newview/llnearbychatbar.cpp @@ -36,7 +36,6 @@ #include "lltrans.h" #include "llnearbychatbar.h" -#include "llspeakbutton.h" #include "llbottomtray.h" #include "llagent.h" #include "llgesturemgr.h" @@ -98,9 +97,10 @@ void LLGestureComboBox::refreshGestures() clearRows(); mGestures.clear(); - LLGestureManager::item_map_t::iterator it; + LLGestureManager::item_map_t::const_iterator it; + const LLGestureManager::item_map_t& active_gestures = LLGestureManager::instance().getActiveGestures(); LLSD::Integer idx(0); - for (it = LLGestureManager::instance().mActive.begin(); it != LLGestureManager::instance().mActive.end(); ++it) + for (it = active_gestures.begin(); it != active_gestures.end(); ++it) { LLMultiGesture* gesture = (*it).second; if (gesture) @@ -234,14 +234,6 @@ BOOL LLNearbyChatBar::postBuild() mOutputMonitor = getChild<LLOutputMonitorCtrl>("chat_zone_indicator"); mOutputMonitor->setVisible(FALSE); - mSpeakBtn = getParent()->getChild<LLSpeakButton>("talk"); - - // Speak button should be initially disabled because - // it takes some time between logging in to world and connecting to voice channel. - mSpeakBtn->setEnabled(FALSE); - - // Registering Chat Bar to receive Voice client status change notifications. - gVoiceClient->addObserver(this); return TRUE; } @@ -733,27 +725,6 @@ public: } }; -void LLNearbyChatBar::onChange(EStatusType status, const std::string &channelURI, bool proximal) -{ - // Time it takes to connect to voice channel might be pretty long, - // so don't expect user login or STATUS_VOICE_ENABLED to be followed by STATUS_JOINED. - BOOL enable = FALSE; - - switch (status) - { - // Do not add STATUS_VOICE_ENABLED because voice chat is - // inactive until STATUS_JOINED - case STATUS_JOINED: - enable = TRUE; - break; - default: - enable = FALSE; - break; - } - - mSpeakBtn->setEnabled(enable); -} - // Creating the object registers with the dispatcher. LLChatHandler gChatHandler; diff --git a/indra/newview/llnearbychatbar.h b/indra/newview/llnearbychatbar.h index 56ee706a97..224118e088 100644 --- a/indra/newview/llnearbychatbar.h +++ b/indra/newview/llnearbychatbar.h @@ -42,9 +42,6 @@ #include "llspeakers.h" -class LLSpeakButton; - - class LLGestureComboBox : public LLComboBox , public LLGestureManagerObserver @@ -76,7 +73,6 @@ protected: class LLNearbyChatBar : public LLPanel -, public LLVoiceClientStatusObserver { public: // constructor for inline chat-bars (e.g. hosted in chat history window) @@ -105,11 +101,6 @@ public: S32 getMinWidth() const; S32 getMaxWidth() const; - /** - * Implements LLVoiceClientStatusObserver::onChange() - */ - /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); - protected: static BOOL matchChatTypeTrigger(const std::string& in_str, std::string* out_str); static void onChatBoxKeystroke(LLLineEditor* caller, void* userdata); @@ -127,7 +118,6 @@ protected: static S32 sLastSpecialChatChannel; LLLineEditor* mChatBox; - LLSpeakButton* mSpeakBtn; LLOutputMonitorCtrl* mOutputMonitor; LLLocalSpeakerMgr* mSpeakerMgr; }; diff --git a/indra/newview/llnearbychathandler.cpp b/indra/newview/llnearbychathandler.cpp index b4e0ab198a..f3b63c8616 100644 --- a/indra/newview/llnearbychathandler.cpp +++ b/indra/newview/llnearbychathandler.cpp @@ -332,7 +332,8 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg) //only messages from AGENTS if(CHAT_SOURCE_OBJECT == chat_msg.mSourceType) { - return;//dn't show toast for messages from objects + if(chat_msg.mChatType == CHAT_TYPE_DEBUG_MSG) + return;//ok for now we don't skip messeges from object, so skip only debug messages } LLUUID id; diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp index 1770138b3e..9cd949c9cc 100644 --- a/indra/newview/llpanelimcontrolpanel.cpp +++ b/indra/newview/llpanelimcontrolpanel.cpp @@ -174,10 +174,8 @@ void LLPanelIMControlPanel::setSessionId(const LLUUID& session_id) getChild<LLAvatarIconCtrl>("avatar_icon")->setValue(mAvatarID); - // Fetch the currect name - gCacheName->get(mAvatarID, FALSE, boost::bind(&LLPanelIMControlPanel::nameUpdatedCallback, this, _1, _2, _3, _4)); - - // Disable profile button if participant is not realy SL avatar + // 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 ) @@ -188,6 +186,13 @@ void LLPanelIMControlPanel::setSessionId(const LLUUID& session_id) childSetEnabled("share_btn", FALSE); childSetEnabled("teleport_btn", FALSE); childSetEnabled("pay_btn", FALSE); + + getChild<LLTextBox>("avatar_name")->setValue(im_session->mName);
+ } + else + { + // If the participant is an avatar, fetch the currect name + gCacheName->get(mAvatarID, FALSE, boost::bind(&LLPanelIMControlPanel::nameUpdatedCallback, this, _1, _2, _3, _4)); } } diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 15a75cb930..e3b2ab77aa 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -204,11 +204,6 @@ void LLPanelMainInventory::initListCommandsHandlers() mListCommands->childSetAction("options_gear_btn", boost::bind(&LLPanelMainInventory::onGearButtonClick, this));
mListCommands->childSetAction("trash_btn", boost::bind(&LLPanelMainInventory::onTrashButtonClick, this));
mListCommands->childSetAction("add_btn", boost::bind(&LLPanelMainInventory::onAddButtonClick, this));
- /*
- mListCommands->getChild<LLButton>("add_btn")->setHeldDownCallback(boost::bind(&LLPanelMainInventory::onAddButtonHeldDown, this));
- static const LLSD add_landmark_command("add_landmark");
- mListCommands->childSetAction("add_btn", boost::bind(&LLPanelMainInventory::onAddAction, this, add_landmark_command));
- */
LLDragAndDropButton* trash_btn = mListCommands->getChild<LLDragAndDropButton>("trash_btn");
trash_btn->setDragAndDropHandler(boost::bind(&LLPanelMainInventory::handleDragAndDropToTrash, this
diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index 9ba94c8ca9..ca87ebf5a2 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -519,7 +519,6 @@ BOOL LLPanelPeople::postBuild() 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)); - groups_panel->childSetAction("minus_btn", boost::bind(&LLPanelPeople::onGroupMinusButtonClicked, this)); LLPanel* friends_panel = getChild<LLPanel>(FRIENDS_TAB_NAME); friends_panel->childSetAction("add_btn", boost::bind(&LLPanelPeople::onAddFriendWizButtonClicked, this)); @@ -568,6 +567,7 @@ BOOL LLPanelPeople::postBuild() 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)); @@ -1193,7 +1193,7 @@ void LLPanelPeople::onCallButtonClicked() if (selected_uuids.size() == 1) { // initiate a P2P voice chat with the selected user - LLAvatarActions::startCall(selected_uuids[0]); + LLAvatarActions::startCall(getCurrentItemID()); } else if (selected_uuids.size() > 1) { diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp index ed606d5457..81eb133b07 100644 --- a/indra/newview/llscreenchannel.cpp +++ b/indra/newview/llscreenchannel.cpp @@ -455,7 +455,7 @@ void LLScreenChannel::createOverflowToast(S32 bottom, F32 timer) if(!mOverflowToastPanel) return; - mOverflowToastPanel->setOnFadeCallback(boost::bind(&LLScreenChannel::closeOverflowToastPanel, this)); + mOverflowToastPanel->setOnFadeCallback(boost::bind(&LLScreenChannel::onOverflowToastHide, this)); LLTextBox* text_box = mOverflowToastPanel->getChild<LLTextBox>("toast_text"); std::string text = llformat(mOverflowFormatString.c_str(),mHiddenToastsNum); diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index 8c5439d47e..6ca6734598 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -435,7 +435,7 @@ void LLSpatialGroup::clearDrawMap() BOOL LLSpatialGroup::isRecentlyVisible() const { - return (LLDrawable::getCurrentFrame() - (S32)mVisible) < LLDrawable::getMinVisFrameRange() ; + return (LLDrawable::getCurrentFrame() - mVisible[LLViewerCamera::sCurCameraID]) < LLDrawable::getMinVisFrameRange() ; } BOOL LLSpatialGroup::isVisible() const diff --git a/indra/newview/llspeakbutton.cpp b/indra/newview/llspeakbutton.cpp index 57ea018f25..51d53b2674 100644 --- a/indra/newview/llspeakbutton.cpp +++ b/indra/newview/llspeakbutton.cpp @@ -133,6 +133,16 @@ LLSpeakButton::~LLSpeakButton() LLTransientFloaterMgr::getInstance()->removeControlView(mShowBtn); } +void LLSpeakButton::setSpeakToolTip(const std::string& msg) +{ + mSpeakBtn->setToolTip(msg); +} + +void LLSpeakButton::setShowToolTip(const std::string& msg) +{ + mShowBtn->setToolTip(msg); +} + void LLSpeakButton::onMouseDown_SpeakBtn() { bool down = true; diff --git a/indra/newview/llspeakbutton.h b/indra/newview/llspeakbutton.h index e213c562dd..02c8ab3890 100644 --- a/indra/newview/llspeakbutton.h +++ b/indra/newview/llspeakbutton.h @@ -62,6 +62,11 @@ public: /*virtual*/ ~LLSpeakButton(); /*virtual*/ void draw(); + // *HACK: Need to put tooltips in a translatable location, + // the panel that contains this button. + void setSpeakToolTip(const std::string& msg); + void setShowToolTip(const std::string& msg); + protected: friend class LLUICtrlFactory; LLSpeakButton(const Params& p); diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index 2ed82b7d62..261bdbcfc0 100644 --- a/indra/newview/llspeakers.cpp +++ b/indra/newview/llspeakers.cpp @@ -359,6 +359,9 @@ void LLSpeakerMgr::updateSpeakerList() LLPointer<LLSpeaker> LLSpeakerMgr::findSpeaker(const LLUUID& speaker_id) { + //In some conditions map causes crash if it is empty(Windows only), adding check (EK) + if (mSpeakers.size() == 0) + return NULL; speaker_map_t::iterator found_it = mSpeakers.find(speaker_id); if (found_it == mSpeakers.end()) { diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 64dcd7b97f..696b0d9af1 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -873,6 +873,9 @@ bool idle_startup() gViewerWindow->getWindow()->show(); display_startup(); + //DEV-10530. do cleanup. remove at some later date. jan-2009 + LLFloaterPreference::cleanupBadSetting(); + // DEV-16927. The following code removes errant keystrokes that happen while the window is being // first made visible. #ifdef _WIN32 @@ -1688,8 +1691,11 @@ bool idle_startup() //all categories loaded. lets create "My Favorites" category gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE,true); - // lets create "Friends" and "Friends/All" in the Inventory "Calling Cards" and fill it with buddies - LLFriendCardsManager::instance().syncFriendsFolder(); + // Checks whether "Friends" and "Friends/All" folders exist in "Calling Cards" folder, + // fetches their contents if needed and synchronizes it with buddies list. + // If the folders are not found they are created. + LLFriendCardsManager::instance().syncFriendCardsFolders(); + // set up callbacks llinfos << "Registering Callbacks" << llendl; @@ -1900,9 +1906,6 @@ bool idle_startup() //DEV-17797. get null folder. Any items found here moved to Lost and Found LLInventoryModel::findLostItems(); - //DEV-10530. do cleanup. remove at some later date. jan-2009 - LLFloaterPreference::cleanupBadSetting(); - LLStartUp::setStartupState( STATE_PRECACHE ); timeout.reset(); return FALSE; diff --git a/indra/newview/lltoastimpanel.cpp b/indra/newview/lltoastimpanel.cpp index 9370e318cf..1ea5f515b7 100644 --- a/indra/newview/lltoastimpanel.cpp +++ b/indra/newview/lltoastimpanel.cpp @@ -69,7 +69,7 @@ LLToastIMPanel::LLToastIMPanel(LLToastIMPanel::Params &p) : LLToastPanel(p.notif mNotification = p.notification; // if message comes from the system - there shouldn't be a reply btn - if(p.from == "Second Life") + if(p.from == SYSTEM_FROM) { mAvatar->setVisible(FALSE); sys_msg_icon->setVisible(TRUE); diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 964d3bc2fa..7772f613f0 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -197,7 +197,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("notifications_console", "floater_notifications_console.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterNotificationConsole>); LLFloaterReg::add("openobject", "floater_openobject.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterOpenObject>); - + LLFloaterReg::add("outgoing_call", "floater_outgoing_call.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLOutgoingCallDialog>); LLFloaterReg::add("parcel_info", "floater_preview_url.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterParcelInfo>); LLFloaterPayUtil::registerFloater(); diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 493457704b..69d4da373e 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -135,9 +135,19 @@ public: LLMimeDiscoveryResponder( viewer_media_t media_impl) : mMediaImpl(media_impl), mInitialized(false) - {} - + { + if(mMediaImpl->mMimeTypeProbe != NULL) + { + llerrs << "impl already has an outstanding responder" << llendl; + } + + mMediaImpl->mMimeTypeProbe = this; + } + ~LLMimeDiscoveryResponder() + { + disconnectOwner(); + } virtual void completedHeader(U32 status, const std::string& reason, const LLSD& content) { @@ -149,23 +159,54 @@ public: virtual void error( U32 status, const std::string& reason ) { + llwarns << "responder failed with status " << status << ", reason " << reason << llendl; + if(mMediaImpl) + { + mMediaImpl->mMediaSourceFailed = true; + } // completeAny(status, "none/none"); } void completeAny(U32 status, const std::string& mime_type) { - if(!mInitialized && ! mime_type.empty()) + // the call to initializeMedia may disconnect the responder, which will clear mMediaImpl. + // Make a local copy so we can call loadURI() afterwards. + LLViewerMediaImpl *impl = mMediaImpl; + + if(impl && !mInitialized && ! mime_type.empty()) { - if(mMediaImpl->initializeMedia(mime_type)) + if(impl->initializeMedia(mime_type)) { mInitialized = true; - mMediaImpl->loadURI(); + impl->loadURI(); + disconnectOwner(); } } } + + void cancelRequest() + { + disconnectOwner(); + } + +private: + void disconnectOwner() + { + if(mMediaImpl) + { + if(mMediaImpl->mMimeTypeProbe != this) + { + llerrs << "internal error: mMediaImpl->mMimeTypeProbe != this" << llendl; + } - public: - viewer_media_t mMediaImpl; + mMediaImpl->mMimeTypeProbe = NULL; + } + mMediaImpl = NULL; + } + + +public: + LLViewerMediaImpl *mMediaImpl; bool mInitialized; }; static LLViewerMedia::impl_list sViewerMediaImplList; @@ -708,6 +749,7 @@ LLViewerMediaImpl::LLViewerMediaImpl( const LLUUID& texture_id, mIsDisabled(false), mIsParcelMedia(false), mProximity(-1), + mMimeTypeProbe(NULL), mIsUpdated(false) { @@ -811,7 +853,9 @@ void LLViewerMediaImpl::destroyMediaSource() { oldImage->setPlaying(FALSE) ; } - + + cancelMimeTypeProbe(); + if(mMediaSource) { delete mMediaSource; @@ -1316,6 +1360,8 @@ void LLViewerMediaImpl::unload() ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mime_type, bool rediscover_type, bool server_request) { + cancelMimeTypeProbe(); + if(mMediaURL != url) { // Don't carry media play state across distinct URLs. @@ -1358,6 +1404,12 @@ void LLViewerMediaImpl::navigateInternal() // Helpful to have media urls in log file. Shouldn't be spammy. llinfos << "media id= " << mTextureId << " url=" << mMediaURL << " mime_type=" << mMimeType << llendl; + if(mMimeTypeProbe != NULL) + { + llwarns << "MIME type probe already in progress -- bailing out." << llendl; + return; + } + if(mNavigateServerRequest) { setNavState(MEDIANAVSTATE_SERVER_SENT); @@ -1390,7 +1442,7 @@ void LLViewerMediaImpl::navigateInternal() if(scheme.empty() || "http" == scheme || "https" == scheme) { - LLHTTPClient::getHeaderOnly( mMediaURL, new LLMimeDiscoveryResponder(this)); + LLHTTPClient::getHeaderOnly( mMediaURL, new LLMimeDiscoveryResponder(this), 10.0f); } else if("data" == scheme || "file" == scheme || "about" == scheme) { @@ -1521,7 +1573,15 @@ void LLViewerMediaImpl::update() { if(mMediaSource == NULL) { - if(mPriority != LLPluginClassMedia::PRIORITY_UNLOADED) + if(mPriority == LLPluginClassMedia::PRIORITY_UNLOADED) + { + // This media source should not be loaded. + } + else if(mMimeTypeProbe != NULL) + { + // this media source is doing a MIME type probe -- don't try loading it again. + } + else { // This media may need to be loaded. if(sMediaCreateTimer.hasExpired()) @@ -2120,6 +2180,21 @@ void LLViewerMediaImpl::setNavState(EMediaNavState state) } } +void LLViewerMediaImpl::cancelMimeTypeProbe() +{ + if(mMimeTypeProbe != NULL) + { + // There doesn't seem to be a way to actually cancel an outstanding request. + // Simulate it by telling the LLMimeDiscoveryResponder not to write back any results. + mMimeTypeProbe->cancelRequest(); + + // The above should already have set mMimeTypeProbe to NULL. + if(mMimeTypeProbe != NULL) + { + llerrs << "internal error: mMimeTypeProbe is not NULL after cancelling request." << llendl; + } + } +} void LLViewerMediaImpl::addObject(LLVOVolume* obj) { diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index 3f5f3ca746..719deb28bf 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -48,6 +48,7 @@ class LLUUID; class LLViewerMediaTexture; class LLMediaEntry; class LLVOVolume ; +class LLMimeDiscoveryResponder; typedef LLPointer<LLViewerMediaImpl> viewer_media_t; /////////////////////////////////////////////////////////////////////////////// @@ -294,6 +295,7 @@ public: EMediaNavState getNavState() { return mMediaNavState; } void setNavState(EMediaNavState state); + void cancelMimeTypeProbe(); public: // a single media url with some data and an impl. LLPluginClassMedia* mMediaSource; @@ -331,7 +333,8 @@ public: bool mIsDisabled; bool mIsParcelMedia; S32 mProximity; - + LLMimeDiscoveryResponder *mMimeTypeProbe; + private: BOOL mIsUpdated ; std::list< LLVOVolume* > mObjectList ; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 976d89a5b7..728fb7c616 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -1879,7 +1879,9 @@ class LLAdvancedDebugAvatarTextures : public view_listener_t { bool handleEvent(const LLSD& userdata) { +#ifndef LL_RELEASE_FOR_DOWNLOAD handle_debug_avatar_textures(NULL); +#endif return true; } }; @@ -1893,7 +1895,9 @@ class LLAdvancedDumpAvatarLocalTextures : public view_listener_t { bool handleEvent(const LLSD& userdata) { +#ifndef LL_RELEASE_FOR_DOWNLOAD handle_dump_avatar_local_textures(NULL); +#endif return true; } }; @@ -7860,10 +7864,8 @@ void initialize_menus() view_listener_t::addMenu(new LLAdvancedCheckDebugCharacterVis(), "Advanced.CheckDebugCharacterVis"); view_listener_t::addMenu(new LLAdvancedDumpAttachments(), "Advanced.DumpAttachments"); view_listener_t::addMenu(new LLAdvancedRebakeTextures(), "Advanced.RebakeTextures"); - #ifndef LL_RELEASE_FOR_DOWNLOAD view_listener_t::addMenu(new LLAdvancedDebugAvatarTextures(), "Advanced.DebugAvatarTextures"); view_listener_t::addMenu(new LLAdvancedDumpAvatarLocalTextures(), "Advanced.DumpAvatarLocalTextures"); - #endif // Advanced > Network view_listener_t::addMenu(new LLAdvancedEnableMessageLog(), "Advanced.EnableMessageLog"); view_listener_t::addMenu(new LLAdvancedDisableMessageLog(), "Advanced.DisableMessageLog"); diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 9923c9ac74..85bc26c9c0 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -206,33 +206,31 @@ LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(const U32 wid LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTexture( const LLUUID &image_id, BOOL usemipmaps, - S32 boost_priority, + LLViewerTexture::EBoostLevel boost_priority, S8 texture_type, LLGLint internal_format, LLGLenum primary_format, LLHost request_from_host) { - llassert_always(boost_priority >= LLViewerTexture::BOOST_NONE) ; return gTextureList.getImage(image_id, usemipmaps, boost_priority, texture_type, internal_format, primary_format, request_from_host) ; } LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromFile( const std::string& filename, BOOL usemipmaps, - S32 boost_priority, + LLViewerTexture::EBoostLevel boost_priority, S8 texture_type, LLGLint internal_format, LLGLenum primary_format, const LLUUID& force_id) { - llassert_always(boost_priority >= LLViewerTexture::BOOST_NONE) ; return gTextureList.getImageFromFile(filename, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id) ; } //static LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromUrl(const std::string& url, BOOL usemipmaps, - S32 boost_priority, + LLViewerTexture::EBoostLevel boost_priority, S8 texture_type, LLGLint internal_format, LLGLenum primary_format, @@ -1485,9 +1483,8 @@ F32 LLViewerFetchedTexture::calcDecodePriority() if ( mBoostLevel > BOOST_HIGH) { priority += 10000000.f; - } - - if(mAdditionalDecodePriority > 0.0f) + } + else if(mAdditionalDecodePriority > 0.0f) { // 1-9 S32 additional_priority = (S32)(1.0f + mAdditionalDecodePriority*8.0f + .5f); // round @@ -3147,8 +3144,7 @@ F32 LLViewerMediaTexture::getMaxVirtualSize() if(mNeedsResetMaxVirtualSize) { - mMaxVirtualSize = 0.f ;//reset - mNeedsResetMaxVirtualSize = FALSE ; + addTextureStats(0.f, FALSE) ;//reset } if(mIsPlaying) //media is playing diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index bde87d1dd5..141979052d 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -107,12 +107,11 @@ public: enum EBoostLevel { - //skip 0 and 1 to avoid mistakenly mixing boost level with boolean numbers. - BOOST_NONE = 2, - BOOST_AVATAR_BAKED = 3, - BOOST_AVATAR = 4, - BOOST_CLOUDS = 5, - BOOST_SCULPTED = 6, + BOOST_NONE = 0, + BOOST_AVATAR_BAKED = 1, + BOOST_AVATAR = 2, + BOOST_CLOUDS = 3, + BOOST_SCULPTED = 4, BOOST_HIGH = 10, BOOST_TERRAIN = 11, // has to be high priority for minimap / low detail @@ -668,7 +667,7 @@ public: static LLViewerFetchedTexture* getFetchedTexture(const LLUUID &image_id, BOOL usemipmap = TRUE, - S32 boost_priority = LLViewerTexture::BOOST_NONE, // Get the requested level immediately upon creation. + LLViewerTexture::EBoostLevel boost_priority = LLViewerTexture::BOOST_NONE, // Get the requested level immediately upon creation. S8 texture_type = LLViewerTexture::FETCHED_TEXTURE, LLGLint internal_format = 0, LLGLenum primary_format = 0, @@ -677,7 +676,7 @@ public: static LLViewerFetchedTexture* getFetchedTextureFromFile(const std::string& filename, BOOL usemipmap = TRUE, - S32 boost_priority = LLViewerTexture::BOOST_NONE, + LLViewerTexture::EBoostLevel boost_priority = LLViewerTexture::BOOST_NONE, S8 texture_type = LLViewerTexture::FETCHED_TEXTURE, LLGLint internal_format = 0, LLGLenum primary_format = 0, @@ -686,7 +685,7 @@ public: static LLViewerFetchedTexture* getFetchedTextureFromUrl(const std::string& url, BOOL usemipmap = TRUE, - S32 boost_priority = LLViewerTexture::BOOST_NONE, + LLViewerTexture::EBoostLevel boost_priority = LLViewerTexture::BOOST_NONE, S8 texture_type = LLViewerTexture::FETCHED_TEXTURE, LLGLint internal_format = 0, LLGLenum primary_format = 0, diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 081b7cc483..703a13976c 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -325,7 +325,7 @@ void LLViewerTextureList::restoreGL() LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string& filename, BOOL usemipmaps, - S32 boost_priority, + LLViewerTexture::EBoostLevel boost_priority, S8 texture_type, LLGLint internal_format, LLGLenum primary_format, @@ -345,7 +345,7 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string& LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string& url, BOOL usemipmaps, - S32 boost_priority, + LLViewerTexture::EBoostLevel boost_priority, S8 texture_type, LLGLint internal_format, LLGLenum primary_format, @@ -411,7 +411,7 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string& LLViewerFetchedTexture* LLViewerTextureList::getImage(const LLUUID &image_id, BOOL usemipmaps, - S32 boost_priority, + LLViewerTexture::EBoostLevel boost_priority, S8 texture_type, LLGLint internal_format, LLGLenum primary_format, @@ -441,7 +441,7 @@ LLViewerFetchedTexture* LLViewerTextureList::getImage(const LLUUID &image_id, //when this function is called, there is no such texture in the gTextureList with image_id. LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id, BOOL usemipmaps, - S32 boost_priority, + LLViewerTexture::EBoostLevel boost_priority, S8 texture_type, LLGLint internal_format, LLGLenum primary_format, @@ -1346,7 +1346,7 @@ LLUIImagePtr LLUIImageList::getUIImageByID(const LLUUID& image_id, S32 priority) const BOOL use_mips = FALSE; const LLRect scale_rect = LLRect::null; - return loadUIImageByID(image_id, use_mips, scale_rect, priority); + return loadUIImageByID(image_id, use_mips, scale_rect, (LLViewerTexture::EBoostLevel)priority); } LLUIImagePtr LLUIImageList::getUIImage(const std::string& image_name, S32 priority) @@ -1360,21 +1360,27 @@ LLUIImagePtr LLUIImageList::getUIImage(const std::string& image_name, S32 priori const BOOL use_mips = FALSE; const LLRect scale_rect = LLRect::null; - return loadUIImageByName(image_name, image_name, use_mips, scale_rect, priority); + return loadUIImageByName(image_name, image_name, use_mips, scale_rect, (LLViewerTexture::EBoostLevel)priority); } LLUIImagePtr LLUIImageList::loadUIImageByName(const std::string& name, const std::string& filename, - BOOL use_mips, const LLRect& scale_rect, S32 boost_priority ) + BOOL use_mips, const LLRect& scale_rect, LLViewerTexture::EBoostLevel boost_priority ) { - if (boost_priority == 0) boost_priority = LLViewerFetchedTexture::BOOST_UI; + if (boost_priority == LLViewerTexture::BOOST_NONE) + { + boost_priority = LLViewerTexture::BOOST_UI; + } LLViewerFetchedTexture* imagep = LLViewerTextureManager::getFetchedTextureFromFile(filename, MIPMAP_NO, boost_priority); return loadUIImage(imagep, name, use_mips, scale_rect); } LLUIImagePtr LLUIImageList::loadUIImageByID(const LLUUID& id, - BOOL use_mips, const LLRect& scale_rect, S32 boost_priority) + BOOL use_mips, const LLRect& scale_rect, LLViewerTexture::EBoostLevel boost_priority) { - if (boost_priority == 0) boost_priority = LLViewerFetchedTexture::BOOST_UI; + if (boost_priority == LLViewerTexture::BOOST_NONE) + { + boost_priority = LLViewerTexture::BOOST_UI; + } LLViewerFetchedTexture* imagep = LLViewerTextureManager::getFetchedTexture(id, MIPMAP_NO, boost_priority); return loadUIImage(imagep, id.asString(), use_mips, scale_rect); } diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h index 3c9c81a689..028f8441ab 100644 --- a/indra/newview/llviewertexturelist.h +++ b/indra/newview/llviewertexturelist.h @@ -130,7 +130,7 @@ private: LLViewerFetchedTexture * getImage(const LLUUID &image_id, BOOL usemipmap = TRUE, - S32 boost_priority = LLViewerTexture::BOOST_NONE, // Get the requested level immediately upon creation. + LLViewerTexture::EBoostLevel boost_priority = LLViewerTexture::BOOST_NONE, // Get the requested level immediately upon creation. S8 texture_type = LLViewerTexture::FETCHED_TEXTURE, LLGLint internal_format = 0, LLGLenum primary_format = 0, @@ -139,7 +139,7 @@ private: LLViewerFetchedTexture * getImageFromFile(const std::string& filename, BOOL usemipmap = TRUE, - S32 boost_priority = LLViewerTexture::BOOST_NONE, // Get the requested level immediately upon creation. + LLViewerTexture::EBoostLevel boost_priority = LLViewerTexture::BOOST_NONE, // Get the requested level immediately upon creation. S8 texture_type = LLViewerTexture::FETCHED_TEXTURE, LLGLint internal_format = 0, LLGLenum primary_format = 0, @@ -148,7 +148,7 @@ private: LLViewerFetchedTexture* getImageFromUrl(const std::string& url, BOOL usemipmap = TRUE, - BOOL level_immediate = FALSE, // Get the requested level immediately upon creation. + LLViewerTexture::EBoostLevel boost_priority = LLViewerTexture::BOOST_NONE, // Get the requested level immediately upon creation. S8 texture_type = LLViewerTexture::FETCHED_TEXTURE, LLGLint internal_format = 0, LLGLenum primary_format = 0, @@ -157,7 +157,7 @@ private: LLViewerFetchedTexture* createImage(const LLUUID &image_id, BOOL usemipmap = TRUE, - S32 boost_priority = LLViewerTexture::BOOST_NONE, // Get the requested level immediately upon creation. + LLViewerTexture::EBoostLevel boost_priority = LLViewerTexture::BOOST_NONE, // Get the requested level immediately upon creation. S8 texture_type = LLViewerTexture::FETCHED_TEXTURE, LLGLint internal_format = 0, LLGLenum primary_format = 0, @@ -228,9 +228,11 @@ public: static void onUIImageLoaded( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata ); private: LLUIImagePtr loadUIImageByName(const std::string& name, const std::string& filename, - BOOL use_mips = FALSE, const LLRect& scale_rect = LLRect::null, S32 boost_priority = 0); + BOOL use_mips = FALSE, const LLRect& scale_rect = LLRect::null, + LLViewerTexture::EBoostLevel boost_priority = LLViewerTexture::BOOST_UI); LLUIImagePtr loadUIImageByID(const LLUUID& id, - BOOL use_mips = FALSE, const LLRect& scale_rect = LLRect::null, S32 boost_priority = 0); + BOOL use_mips = FALSE, const LLRect& scale_rect = LLRect::null, + LLViewerTexture::EBoostLevel boost_priority = LLViewerTexture::BOOST_UI); LLUIImagePtr loadUIImage(LLViewerFetchedTexture* imagep, const std::string& name, BOOL use_mips = FALSE, const LLRect& scale_rect = LLRect::null); diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index 89649407ff..d93913b944 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -306,8 +306,10 @@ void LLVoiceChannel::activate() // activating the proximal channel between IM calls LLVoiceChannel* old_channel = sCurrentVoiceChannel; sCurrentVoiceChannel = this; + mCallDialogPayload["old_channel_name"] = ""; if (old_channel) { + mCallDialogPayload["old_channel_name"] = old_channel->getSessionName(); old_channel->deactivate(); } } @@ -870,6 +872,19 @@ void LLVoiceChannelP2P::setState(EState state) { // HACK: Open/close the call window if needed. toggleCallWindowIfNeeded(state); + + // *HACK: open outgoing call floater if needed, might be better done elsewhere. + mCallDialogPayload["session_id"] = mSessionID; + mCallDialogPayload["session_name"] = mSessionName; + mCallDialogPayload["other_user_id"] = mOtherUserID; + if (!mReceivedCall && state == STATE_RINGING) + { + llinfos << "RINGINGGGGGGGG " << mSessionName << llendl; + if (!mSessionName.empty()) + { + LLFloaterReg::showInstance("outgoing_call", mCallDialogPayload, TRUE); + } + } // you only "answer" voice invites in p2p mode // so provide a special purpose message here diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index 20b6157b48..639585de55 100644 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -109,6 +109,7 @@ protected: EState mState; std::string mSessionName; LLSD mNotifyArgs; + LLSD mCallDialogPayload; BOOL mIgnoreNextSessionLeave; LLHandle<LLPanel> mLoginNotificationHandle; diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 39d4bb0c02..5fedfc943b 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -1598,7 +1598,7 @@ void LLVoiceClient::stateMachine() } else { - LL_WARNS("Voice") << "region doesn't have ParcelVoiceInfoRequest capability. This is normal for a short time after teleporting, but bad if it persists for very long." << LL_ENDL; + LL_WARNS_ONCE("Voice") << "region doesn't have ParcelVoiceInfoRequest capability. This is normal for a short time after teleporting, but bad if it persists for very long." << LL_ENDL; } } } @@ -4273,7 +4273,7 @@ void LLVoiceClient::mediaStreamUpdatedEvent( if(incoming) { // Send the voice chat invite to the GUI layer - // TODO: Question: Should we correlate with the mute list here? + // *TODO: Question: Should we correlate with the mute list here? session->mIMSessionID = LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID); session->mVoiceInvitePending = true; if(session->mName.empty()) diff --git a/indra/newview/llworldmipmap.cpp b/indra/newview/llworldmipmap.cpp index 8d3165b98c..9897f40c4e 100644 --- a/indra/newview/llworldmipmap.cpp +++ b/indra/newview/llworldmipmap.cpp @@ -196,7 +196,7 @@ LLPointer<LLViewerFetchedTexture> LLWorldMipmap::loadObjectsTile(U32 grid_x, U32 // END DEBUG //LL_INFOS("World Map") << "LLWorldMipmap::loadObjectsTile(), URL = " << imageurl << LL_ENDL; - LLPointer<LLViewerFetchedTexture> img = LLViewerTextureManager::getFetchedTextureFromUrl(imageurl, TRUE, FALSE, LLViewerTexture::LOD_TEXTURE); + LLPointer<LLViewerFetchedTexture> img = LLViewerTextureManager::getFetchedTextureFromUrl(imageurl, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); img->setBoostLevel(LLViewerTexture::BOOST_MAP); // Return the smart pointer diff --git a/indra/newview/skins/default/xui/en/floater_gesture.xml b/indra/newview/skins/default/xui/en/floater_gesture.xml index b23482655c..a3ac878202 100644 --- a/indra/newview/skins/default/xui/en/floater_gesture.xml +++ b/indra/newview/skins/default/xui/en/floater_gesture.xml @@ -84,21 +84,21 @@ top_delta="0" width="18" /> <button - follows="bottom|left" + follows="bottom|right" font="SansSerifBigBold" height="18" image_selected="TrashItem_Press" image_unselected="TrashItem_Off" image_disabled="TrashItem_Disabled" layout="topleft" - left_pad="230" name="del_btn" + right="-5" tool_tip="Delete this gesture" top_delta="0" width="18" /> </panel> <button - follows="bottom|right" + follows="left|bottom" height="23" label="Edit" layout="topleft" @@ -107,7 +107,7 @@ top_pad="5" width="83" /> <button - follows="bottom|right" + follows="left|bottom" height="23" label="Play" layout="topleft" @@ -116,7 +116,7 @@ top_delta="0" width="83" /> <button - follows="bottom|right" + follows="left|bottom" height="23" label="Stop" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/floater_incoming_call.xml b/indra/newview/skins/default/xui/en/floater_incoming_call.xml index 16873df310..9c2898945b 100644 --- a/indra/newview/skins/default/xui/en/floater_incoming_call.xml +++ b/indra/newview/skins/default/xui/en/floater_incoming_call.xml @@ -4,13 +4,17 @@ can_close="false" can_minimize="false" can_tear_off="false" - height="200" + height="125" layout="topleft" name="incoming call" help_topic="incoming_call" title="UNKNOWN PERSON IS CALLING" - width="240"> - <floater.string + width="410"> + <floater.string + name="localchat"> + Local Voice Chat + </floater.string> + <floater.string name="anonymous"> anonymous </floater.string> @@ -31,18 +35,26 @@ left_delta="19" top="35" width="36" /> - <text_editor - font="SansSerif" - height="64" - border_visible="false" + <text + font="SansSerifLarge" + height="20" layout="topleft" left="77" - max_length="2147483647" name="caller name" - read_only="true" - top="21" - width="163" + top="27" + width="315" word_wrap="true" /> + <text + font="SansSerif" + height="50" + layout="topleft" + left="77" + name="question" + top="52" + width="315" + word_wrap="true"> + Do you want to leave [CURRENT_CHAT] and join this voice chat? + </text> <button height="24" label="Accept" @@ -57,16 +69,14 @@ label="Reject" label_selected="Reject" layout="topleft" - left_delta="0" name="Reject" - top_pad="12" + left_pad="10" width="100" /> <button height="24" label="Start IM" layout="topleft" - left_delta="0" name="Start IM" - top_pad="12" + left_pad="10" width="100" /> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_inventory.xml b/indra/newview/skins/default/xui/en/floater_inventory.xml index 30639f955f..b48c962413 100644 --- a/indra/newview/skins/default/xui/en/floater_inventory.xml +++ b/indra/newview/skins/default/xui/en/floater_inventory.xml @@ -36,10 +36,10 @@ filename="panel_main_inventory.xml" follows="all" layout="topleft" + hide_top_panel="true" left="0" label="Inventory Panel" name="Inventory Panel" top="15" - width="467"> -</panel> + width="467" /> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_outgoing_call.xml b/indra/newview/skins/default/xui/en/floater_outgoing_call.xml new file mode 100644 index 0000000000..44956f7e52 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_outgoing_call.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + legacy_header_height="18" + can_close="false" + can_minimize="false" + can_tear_off="false" + height="125" + layout="topleft" + name="outgoing call" + help_topic="outgoing_call" + title="CALLING" + width="410"> + <floater.string + name="localchat"> + Local Voice Chat + </floater.string> + <floater.string + name="anonymous"> + anonymous + </floater.string> + <floater.string + name="VoiceInviteP2P"> + is calling. + </floater.string> + <floater.string + name="VoiceInviteAdHoc"> + has joined a Voice Chat call with a conference chat. + </floater.string> + <avatar_icon + enabled="false" + follows="left|top" + height="36" + image_name="icon_avatar_online.tga" + layout="topleft" + left_delta="19" + top="35" + width="36" /> + <text + font="SansSerifLarge" + height="20" + layout="topleft" + left="77" + name="calling" + top="27" + width="315" + word_wrap="true"> +Calling [CALLEE_NAME] + </text> + <text + font="SansSerif" + height="50" + layout="topleft" + left="77" + name="leaving" + top="52" + width="315" + word_wrap="true"> +Leaving [CURRENT_CHAT]. + </text> + <button + height="24" + label="Cancel" + label_selected="Cancel" + left="70" + layout="topleft" + name="Cancel" + left_pad="10" + width="100" /> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml index c33d7cf31d..b2f46bc433 100644 --- a/indra/newview/skins/default/xui/en/floater_tools.xml +++ b/indra/newview/skins/default/xui/en/floater_tools.xml @@ -262,6 +262,18 @@ <check_box.commit_callback function="BuildTool.selectComponent"/> </check_box> + + <text + text_color="LtGray_50" + follows="top|left" + halign="left" + left="13" + name="RenderingCost" + top_pad="9" + type="string" + width="100"> + รพ: [COUNT] + </text> <check_box control_name="ScaleUniform" height="19" @@ -325,7 +337,7 @@ <button.commit_callback function="BuildTool.gridOptions"/> </button> - <button + <button follows="left|top" height="20" image_disabled="Object_Cube" @@ -701,6 +713,7 @@ function="BuildTool.applyToSelection"/> </button> <text + text_color="LtGray_50" type="string" length="1" height="12" @@ -714,6 +727,7 @@ Objects: [COUNT] </text> <text + text_color="LtGray_50" type="string" length="1" follows="left|top" @@ -730,7 +744,7 @@ halign="center" left="0" name="Object Info Tabs" - tab_max_width="55" + tab_max_width="54" tab_min_width="40" tab_position="top" tab_height="25" diff --git a/indra/newview/skins/default/xui/en/floater_tos.xml b/indra/newview/skins/default/xui/en/floater_tos.xml index 4e2cce1428..1adb824e2a 100644 --- a/indra/newview/skins/default/xui/en/floater_tos.xml +++ b/indra/newview/skins/default/xui/en/floater_tos.xml @@ -53,22 +53,6 @@ Please read the following Terms of Service carefully. To continue logging in to [SECOND_LIFE], you must accept the agreement. </text> - <text_editor - type="string" - length="1" - follows="left|top" - font="SansSerif" - height="283" - layout="topleft" - left_delta="0" - max_length="65536" - name="tos_text" - top_pad="43" - width="568" - handle_edit_keys_directly="true" - word_wrap="true"> - TOS_TEXT - </text_editor> <web_browser follows="left|top" height="340" @@ -76,6 +60,6 @@ you must accept the agreement. left_delta="0" name="tos_html" start_url="data:text/html,%3Chtml%3E%3Chead%3E%3C/head%3E%3Cbody text=%22000000%22%3E%3Ch2%3E Loading %3Ca%20target%3D%22_external%22%20href%3D%22http%3A//secondlife.com/app/tos/%22%3ETerms%20of%20Service%3C/a%3E...%3C/h2%3E %3C/body%3E %3C/html%3E" - top_delta="-27" + top_delta="0" width="568" /> </floater> diff --git a/indra/newview/skins/default/xui/en/menu_people_groups_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_groups_view_sort.xml index 6dd44255bf..304492bedb 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_sort.xml @@ -13,15 +13,11 @@ function="CheckControl" parameter="GroupListShowIcons" /> </menu_item_check> - <menu_item_check + <menu_item_call label="Leave Selected Group" layout="topleft" name="Leave Selected Group"> - <menu_item_check.on_click - function="People.Groups.ViewSort.Action" - parameter="show_icons" /> - <menu_item_check.on_check - function="CheckControl" - parameter="GroupListShowIcons" /> - </menu_item_check> + <menu_item_call.on_click + function="People.Group.Minus.Action"/> + </menu_item_call> </menu> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 9c4e394f0d..70b21d14be 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -5741,6 +5741,15 @@ Are you sure you want to delete your teleport history? yestext="OK"/> </notification> + <notification + icon="alert.tga" + name="BottomTrayButtonCanNotBeShown" + type="alert"> +Selected button can not be shown right now. +The button will be shown when there is enough space for it.
+ </notification> + + <global name="UnsupportedCPU"> - Your CPU speed does not meet the minimum requirements. </global> diff --git a/indra/newview/skins/default/xui/en/panel_activeim_row.xml b/indra/newview/skins/default/xui/en/panel_activeim_row.xml index 5562ec8406..38294c907a 100644 --- a/indra/newview/skins/default/xui/en/panel_activeim_row.xml +++ b/indra/newview/skins/default/xui/en/panel_activeim_row.xml @@ -7,7 +7,9 @@ left="0" height="35" width="318" - background_visible="false"> + background_opaque="false"
+ background_visible="true"
+ bg_alpha_color="0.0 0.0 0.0 0.0" > <chiclet_im_p2p name="p2p_chiclet" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_bottomtray.xml b/indra/newview/skins/default/xui/en/panel_bottomtray.xml index 8188016455..a902f50582 100644 --- a/indra/newview/skins/default/xui/en/panel_bottomtray.xml +++ b/indra/newview/skins/default/xui/en/panel_bottomtray.xml @@ -13,6 +13,8 @@ chrome="true" border_visible="false" width="1000"> + <string name="SpeakBtnToolTip">Turns microphone on/off</string> + <string name="VoiceControlBtnToolTip">Shows/hides voice control panel</string> <layout_stack mouse_opaque="false" border_size="0" @@ -70,9 +72,7 @@ left="0" name="talk" top="3" - width="100" - speak_button.tool_tip="Turns microphone on/off" - show_button.tool_tip="Shows/hides voice control panel" /> + width="100" /> </layout_panel> <icon auto_resize="false" @@ -333,6 +333,6 @@ as for parent layout_panel (chiclet_list_panel) to resize bottom tray properly. min_width="4" right="-1" top="0" - width="26"/> + width="4"/> </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_group_notify.xml b/indra/newview/skins/default/xui/en/panel_group_notify.xml index ef3120174e..984a799b41 100644 --- a/indra/newview/skins/default/xui/en/panel_group_notify.xml +++ b/indra/newview/skins/default/xui/en/panel_group_notify.xml @@ -1,77 +1,117 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<panel background_visible="true" bevel_style="in" bg_alpha_color="0 0 0 0" - height="155" label="instant_message" layout="topleft" left="0" - name="panel_group_notify" top="0" width="350"> - <string - name="message_max_lines_count"> - 4 - </string> - <panel follows="top" background_visible="true" bevel_style="in" bg_alpha_color="black" - height="30" label="header" layout="topleft" left="0" name="header" - top="0" width="350"> - <icon follows="left|top|right|bottom" height="20" width="20" layout="topleft" - top="5" left="5" mouse_opaque="true" name="group_icon"/> - <text type="string" length="1" follows="left|top|right|bottom" - font="SansSerifBigBold" height="20" layout="topleft" left_pad="10" name="title" - text_color="GroupNotifyTextColor" top="5" width="275" use_ellipses="true"> - Sender Name / Group Name - </text> - </panel> - <text - follows="top" - height="20" - layout="topleft" - left="25" - name="subject" - text_color="GroupNotifyTextColor" - font="SansSerifBig" - top="40" - use_ellipses="true" - value="subject" - width="300" - word_wrap="true"> - subject - </text> - <text - follows="top" - height="20" - layout="topleft" - left="25" - name="datetime" - text_color="GroupNotifyTextColor" - font="SansSerif" - top="80" - use_ellipses="true" - value="datetime" - width="300" - word_wrap="true"> - datetime - </text> - <text - follows="left|top|bottom|right" - height="0" - layout="topleft" - left="25" - name="message" - text_color="GroupNotifyTextColor" - top="100" - use_ellipses="true" - value="message" - width="300" - word_wrap="true" - visible="true" > - </text> - <icon - follows="left|bottom|right" height="15" width="15" - layout="topleft" bottom="122" left="25" mouse_opaque="true" name="attachment_icon" visible="true" - /> - <text font="SansSerif" font.style="UNDERLINE" font_shadow="none" - type="string" length="1" follows="left|bottom|right" layout="topleft" - left="45" bottom="122" height="15" width="280" name="attachment" - text_color="GroupNotifyTextColor" visible="true"> - Attachment - </text> - - <button label="OK" layout="topleft" bottom="145" left="140" height="20" - width="70" name="btn_ok" follows="bottom" /> -</panel>
\ No newline at end of file +<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ background_visible="true"
+ bevel_style="in"
+ bg_alpha_color="0 0 0 0"
+ height="135"
+ label="instant_message"
+ layout="topleft"
+ left="0"
+ name="panel_group_notify"
+ top="0"
+ width="305">
+ <string
+ name="message_max_lines_count"
+ value="4" />
+ <panel
+ background_visible="true"
+ bevel_style="in"
+ bg_alpha_color="black"
+ follows="top"
+ height="30"
+ label="header"
+ layout="topleft"
+ left="0"
+ name="header"
+ top="0"
+ width="305">
+ <icon
+ follows="left|top|right|bottom"
+ height="20"
+ layout="topleft"
+ left="5"
+ mouse_opaque="true"
+ name="group_icon"
+ top="5"
+ width="20" />
+ <text
+ follows="left|top|right|bottom"
+ font="SansSerifBigBold"
+ height="20"
+ layout="topleft"
+ left_pad="10"
+ name="title"
+ text_color="GroupNotifyTextColor"
+ top="5"
+ use_ellipses="true"
+ value="Sender Name / Group Name"
+ width="230" />
+ </panel>
+ <text
+ follows="top"
+ font="SansSerifBig"
+ height="20"
+ layout="topleft"
+ left="25"
+ name="subject"
+ text_color="GroupNotifyTextColor"
+ top="40"
+ use_ellipses="true"
+ value="subject"
+ width="270"
+ wrap="true" />
+ <text
+ follows="top"
+ font="SansSerif"
+ height="20"
+ layout="topleft"
+ left="25"
+ name="datetime"
+ text_color="GroupNotifyTextColor"
+ top="80"
+ use_ellipses="true"
+ value="datetime"
+ width="270"
+ wrap="true" />
+ <text
+ follows="left|top|bottom|right"
+ height="0"
+ layout="topleft"
+ left="25"
+ name="message"
+ text_color="GroupNotifyTextColor"
+ top="100"
+ use_ellipses="true"
+ value="message"
+ width="270"
+ wrap="true" />
+ <icon
+ bottom="122"
+ follows="left|bottom|right"
+ height="15"
+ layout="topleft"
+ left="25"
+ mouse_opaque="true"
+ name="attachment_icon"
+ width="15" />
+ <text
+ bottom="122"
+ follows="left|bottom|right"
+ font="SansSerif"
+ height="15"
+ layout="topleft"
+ left="45"
+ name="attachment"
+ text_color="GroupNotifyTextColor"
+ value="Attachment"
+ width="280" />
+ <button
+ bottom="130"
+ follows="bottom"
+ height="20"
+ label="OK"
+ layout="topleft"
+ left="25"
+ name="btn_ok"
+ width="70" />
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml b/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml index a77094e942..ecf35523cd 100644 --- a/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml +++ b/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml @@ -23,7 +23,7 @@ layout="topleft" left_delta="7" left="0" - max_length="254" + max_length="512" name="chat_box" tool_tip="Press Enter to say, Ctrl+Enter to shout" top="0" diff --git a/indra/newview/skins/default/xui/en/panel_notification.xml b/indra/newview/skins/default/xui/en/panel_notification.xml index 40f8da0c2c..462188ee24 100644 --- a/indra/newview/skins/default/xui/en/panel_notification.xml +++ b/indra/newview/skins/default/xui/en/panel_notification.xml @@ -10,7 +10,7 @@ left="0" name="notification_panel" top="0" - height="10" + height="140" width="305"> <!-- THIS PANEL CONTROLS TOAST HEIGHT? --> <panel @@ -20,18 +20,18 @@ bg_alpha_color="0.3 0.3 0.3 0" bg_opaque_color="0.3 0.3 0.3 0" follows="left|right|top" - height="10" + height="100" label="info_panel" layout="topleft" left="0" name="info_panel" top="0" width="305"> - <!-- <text + <text border_visible="false" follows="left|right|top|bottom" font="SansSerif" - height="90" + height="85" layout="topleft" left="10" name="text_box" @@ -39,21 +39,21 @@ text_color="white" top="10" visible="false" - width="300" + width="285" wrap="true"/> <text border_visible="false" follows="left|right|top|bottom" font="SansSerifBold" - height="90" + height="85" layout="topleft" - left="45" + left="10" name="caution_text_box" text_color="1 0.82 0.46 1" - top="5" + top="10" visible="false" - width="300" - wrap="true"/> --> + width="285" + wrap="true"/> <text_editor h_pad="0" v_pad="0" @@ -63,6 +63,7 @@ enabled="false" follows="left|right|top|bottom" font="SansSerif" + height="85" layout="topleft" left="10" mouse_opaque="false" @@ -81,12 +82,13 @@ <panel background_visible="false" follows="left|right|bottom" + height="40" label="control_panel" layout="topleft" left="0" left_delta="-38" name="control_panel" - top="20"> + top_pad="0"> </panel> <!-- <icon diff --git a/indra/newview/skins/default/xui/en/panel_sys_well_item.xml b/indra/newview/skins/default/xui/en/panel_sys_well_item.xml index 7722583ce2..63e60aab78 100644 --- a/indra/newview/skins/default/xui/en/panel_sys_well_item.xml +++ b/indra/newview/skins/default/xui/en/panel_sys_well_item.xml @@ -9,7 +9,10 @@ width="300" height="35" layout="topleft" - follows="left|right"> + follows="left|right" + background_opaque="false"
+ background_visible="true"
+ bg_alpha_color="0.0 0.0 0.0 0.0" > <text top="2" left="10" diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index ea66bfa197..761c17cfd2 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -2863,7 +2863,7 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. Connecting... </string> <string name="conference-title"> - Friends Conference + Ad-hoc Conference </string> <string name="inventory_item_offered-im"> Inventory item offered diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp index d31a81e128..7b28a3b72c 100644 --- a/indra/newview/tests/lllogininstance_test.cpp +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -76,6 +76,7 @@ LLControlGroup::LLControlGroup(const std::string& name) : LLControlGroup::~LLControlGroup() {} void LLControlGroup::setBOOL(const std::string& name, BOOL val) {} BOOL LLControlGroup::getBOOL(const std::string& name) { return FALSE; } +F32 LLControlGroup::getF32(const std::string& name) { return 0.0f; } U32 LLControlGroup::saveToFile(const std::string& filename, BOOL nondefault_only) { return 1; } void LLControlGroup::setString(const std::string& name, const std::string& val) {} std::string LLControlGroup::getString(const std::string& name) { return "test_string"; } diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp index 7a30315b9a..b14c59ab9a 100644 --- a/indra/viewer_components/login/lllogin.cpp +++ b/indra/viewer_components/login/lllogin.cpp @@ -70,7 +70,7 @@ public: LLEventPump& getEventPump() { return mPump; } private: - void sendProgressEvent(const std::string& state, const std::string& change, + LLSD getProgressEventLLSD(const std::string& state, const std::string& change, const LLSD& data = LLSD()) { LLSD status_data; @@ -87,7 +87,13 @@ private: { status_data["data"] = data; } + return status_data; + } + void sendProgressEvent(const std::string& state, const std::string& change, + const LLSD& data = LLSD()) + { + LLSD status_data = getProgressEventLLSD(state, change, data); mPump.post(status_data); } @@ -140,15 +146,28 @@ void LLLogin::Impl::login_(LLCoros::self& self, std::string uri, LLSD credential // Request SRV record. LL_INFOS("LLLogin") << "Requesting SRV record from " << uri << LL_ENDL; - // *NOTE:Mani - Completely arbitrary timeout value for SRV request. - filter.errorAfter(5, "SRV Request timed out!"); + // *NOTE:Mani - Completely arbitrary default timeout value for SRV request. + F32 seconds_to_timeout = 5.0f; + if(credentials.has("cfg_srv_timeout")) + { + seconds_to_timeout = credentials["cfg_srv_timeout"].asReal(); + } + + filter.eventAfter(seconds_to_timeout, + getProgressEventLLSD("offline", "fail.login")); + + std::string srv_pump_name = "LLAres"; + if(credentials.has("cfg_srv_pump")) + { + srv_pump_name = credentials["cfg_srv_pump"].asString(); + } - // Make request + // Make request LLSD request; request["op"] = "rewriteURI"; request["uri"] = uri; request["reply"] = replyPump.getName(); - rewrittenURIs = postAndWait(self, request, "LLAres", filter); + rewrittenURIs = postAndWait(self, request, srv_pump_name, filter); } // we no longer need the filter LLEventPump& xmlrpcPump(LLEventPumps::instance().obtain("LLXMLRPCTransaction")); diff --git a/indra/viewer_components/login/tests/lllogin_test.cpp b/indra/viewer_components/login/tests/lllogin_test.cpp index a8ae2883d5..99ea796ad0 100644 --- a/indra/viewer_components/login/tests/lllogin_test.cpp +++ b/indra/viewer_components/login/tests/lllogin_test.cpp @@ -47,6 +47,7 @@ public: bool call(const LLSD& event) { mDebug(STRINGIZE("LoginListener called!: " << event)); + mLastEvent = event; return false; } @@ -414,4 +415,42 @@ namespace tut ensure_equals("Failed to offline", listener.lastEvent()["state"].asString(), "offline"); } + +/* + template<> template<> + void llviewerlogin_object::test<5>() + { + DEBUG; + // Test SRV request timeout. + set_test_name("LLLogin SRV timeout testing"); + + // Testing normal login procedure. + LLEventStream llaresPump("LLAres"); // Dummy LLAres pump. + + // LLAresListener dummyLLAres("dummy_llares"); + // dummyLLAres.listenTo(llaresPump); + + LLLogin login; + LoginListener listener("test_ear"); + listener.listenTo(login.getEventPump()); + + LLSD credentials; + credentials["first"] = "these"; + credentials["last"] = "don't"; + credentials["passwd"] = "matter"; + credentials["cfg_srv_timeout"] = 0.0f; + + login.connect("login.bar.com", credentials); + + ensure_equals("SRV State", listener.lastEvent()["change"].asString(), "srvrequest"); + + // Get the mainloop eventpump, which needs a pinging in order to drive the + // SRV timeout. + LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); + LLSD frame_event; + mainloop.post(frame_event); + + ensure_equals("SRV Failure", listener.lastEvent()["change"].asString(), "fail.login"); + } +*/ } |