diff options
Diffstat (limited to 'indra/newview')
369 files changed, 18439 insertions, 18812 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index a8a7e05fd5..c6ba8a22bd 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -110,6 +110,8 @@ set(viewer_SOURCE_FILES llavatarlist.cpp llavatarlistitem.cpp llavatarpropertiesprocessor.cpp + llblockedlistitem.cpp + llblocklist.cpp llbox.cpp llbreadcrumbview.cpp llbrowsernotification.cpp @@ -135,6 +137,11 @@ set(viewer_SOURCE_FILES llcommandlineparser.cpp llcompilequeue.cpp llconfirmationmanager.cpp + llconversationlog.cpp + llconversationloglist.cpp + llconversationloglistitem.cpp + llconversationmodel.cpp + llconversationview.cpp llcurrencyuimanager.cpp llcylinder.cpp lldateutil.cpp @@ -190,7 +197,10 @@ set(viewer_SOURCE_FILES llfloaterbuycurrencyhtml.cpp llfloaterbuyland.cpp llfloatercamera.cpp + llfloaterchatvoicevolume.cpp llfloatercolorpicker.cpp + llfloaterconversationlog.cpp + llfloaterconversationpreview.cpp llfloaterdeleteenvpreset.cpp llfloaterdestinations.cpp llfloaterdisplayname.cpp @@ -258,13 +268,13 @@ set(viewer_SOURCE_FILES llfloateruipreview.cpp llfloaterurlentry.cpp llfloatervoiceeffect.cpp + llfloatervoicevolume.cpp llfloaterwebcontent.cpp llfloaterwebprofile.cpp llfloaterwhitelistentry.cpp llfloaterwindowsize.cpp llfloaterworldmap.cpp - llfolderview.cpp - llfolderviewitem.cpp + llfolderviewmodelinventory.cpp llfollowcam.cpp llfriendcard.cpp llgesturelistener.cpp @@ -290,6 +300,7 @@ set(viewer_SOURCE_FILES llhudrender.cpp llhudtext.cpp llhudview.cpp + llimconversation.cpp llimfloater.cpp llimfloatercontainer.cpp llimhandler.cpp @@ -344,7 +355,6 @@ set(viewer_SOURCE_FILES llnamelistctrl.cpp llnavigationbar.cpp llnearbychat.cpp - llnearbychatbar.cpp llnearbychathandler.cpp llnearbychatbarlistener.cpp llnetmap.cpp @@ -376,7 +386,6 @@ set(viewer_SOURCE_FILES llpanelgroupnotices.cpp llpanelgrouproles.cpp llpanelhome.cpp - llpanelimcontrolpanel.cpp llpanelland.cpp llpanellandaudio.cpp llpanellandmarkinfo.cpp @@ -387,7 +396,6 @@ set(viewer_SOURCE_FILES llpanelmaininventory.cpp llpanelmarketplaceinbox.cpp llpanelmarketplaceinboxinventory.cpp - llpanelmarketplaceoutboxinventory.cpp llpanelmediasettingsgeneral.cpp llpanelmediasettingspermissions.cpp llpanelmediasettingssecurity.cpp @@ -442,6 +450,7 @@ set(viewer_SOURCE_FILES llphysicsshapebuilderutil.cpp llplacesinventorybridge.cpp llplacesinventorypanel.cpp + llplacesfolderview.cpp llpopupview.cpp llpolymesh.cpp llpolymorph.cpp @@ -686,6 +695,8 @@ set(viewer_HEADER_FILES llavatarlist.h llavatarlistitem.h llavatarpropertiesprocessor.h + llblockedlistitem.h + llblocklist.h llbox.h llbreadcrumbview.h llbuycurrencyhtml.h @@ -711,6 +722,11 @@ set(viewer_HEADER_FILES llcommandlineparser.h llcompilequeue.h llconfirmationmanager.h + llconversationlog.h + llconversationloglist.h + llconversationloglistitem.h + llconversationmodel.h + llconversationview.h llcurrencyuimanager.h llcylinder.h lldateutil.h @@ -766,7 +782,10 @@ set(viewer_HEADER_FILES llfloaterbuycurrencyhtml.h llfloaterbuyland.h llfloatercamera.h + llfloaterchatvoicevolume.h llfloatercolorpicker.h + llfloaterconversationlog.h + llfloaterconversationpreview.h llfloaterdeleteenvpreset.h llfloaterdestinations.h llfloaterdisplayname.h @@ -834,14 +853,13 @@ set(viewer_HEADER_FILES llfloateruipreview.h llfloaterurlentry.h llfloatervoiceeffect.h + llfloatervoicevolume.h llfloaterwebcontent.h llfloaterwebprofile.h llfloaterwhitelistentry.h llfloaterwindowsize.h llfloaterworldmap.h - llfolderview.h - llfoldervieweventlistener.h - llfolderviewitem.h + llfolderviewmodelinventory.h llfollowcam.h llfriendcard.h llgesturelistener.h @@ -866,6 +884,7 @@ set(viewer_HEADER_FILES llhudrender.h llhudtext.h llhudview.h + llimconversation.h llimfloater.h llimfloatercontainer.h llimview.h @@ -920,7 +939,6 @@ set(viewer_HEADER_FILES llnamelistctrl.h llnavigationbar.h llnearbychat.h - llnearbychatbar.h llnearbychathandler.h llnearbychatbarlistener.h llnetmap.h @@ -946,7 +964,6 @@ set(viewer_HEADER_FILES llpanelgroupnotices.h llpanelgrouproles.h llpanelhome.h - llpanelimcontrolpanel.h llpanelland.h llpanellandaudio.h llpanellandmarkinfo.h @@ -957,7 +974,6 @@ set(viewer_HEADER_FILES llpanelmaininventory.h llpanelmarketplaceinbox.h llpanelmarketplaceinboxinventory.h - llpanelmarketplaceoutboxinventory.h llpanelmediasettingsgeneral.h llpanelmediasettingspermissions.h llpanelmediasettingssecurity.h @@ -1007,6 +1023,7 @@ set(viewer_HEADER_FILES llphysicsshapebuilderutil.h llplacesinventorybridge.h llplacesinventorypanel.h + llplacesfolderview.h llpolymesh.h llpolymorph.h llpopupview.h diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml index 73df064ab2..d4bbd84d0f 100644 --- a/indra/newview/app_settings/commands.xml +++ b/indra/newview/app_settings/commands.xml @@ -46,11 +46,11 @@ available_in_toybox="true" icon="Command_Chat_Icon" label_ref="Command_Chat_Label" - tooltip_ref="Command_Chat_Tooltip" + tooltip_ref="Command_Conversations_Tooltip" execute_function="Floater.ToggleOrBringToFront" - execute_parameters="chat_bar" + execute_parameters="im_container" is_running_function="Floater.IsOpen" - is_running_parameters="chat_bar" + is_running_parameters="im_container" /> <command name="compass" available_in_toybox="false" @@ -239,14 +239,4 @@ is_running_function="Floater.IsOpen" is_running_parameters="camera" /> - <command name="voice" - available_in_toybox="true" - icon="Command_Voice_Icon" - label_ref="Command_Voice_Label" - tooltip_ref="Command_Voice_Tooltip" - execute_function="Floater.ToggleOrBringToFront" - execute_parameters="voice_controls" - is_running_function="Floater.IsOpen" - is_running_parameters="voice_controls" - /> </commands> diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 1bf773bb9e..0537487ca3 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2,6 +2,28 @@ <llsd xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="llsd.xsd"> <map> + <key>IMShowTime</key> + <map> + <key>Comment</key> + <string>Enable(disable) timestamp showing in the chat.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> + <key>IMShowNamesForP2PConv</key> + <map> + <key>Comment</key> + <string>Enable(disable) showing of a names in the chat.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>CrashHostUrl</key> <map> <key>Comment</key> @@ -47,7 +69,7 @@ <key>Type</key> <string>F32</string> <key>Value</key> - <real>0.95</real> + <real>1</real> </map> <key>AdvanceSnapshot</key> <map> @@ -1595,17 +1617,6 @@ <key>Value</key> <integer>1</integer> </map> - <key>ChatWindow</key> - <map> - <key>Comment</key> - <string>Show chat in multiple windows(by default) or in one multi-tabbed window(requires restart)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>S32</string> - <key>Value</key> - <integer>0</integer> - </map> <key>CheesyBeacon</key> <map> <key>Comment</key> @@ -1628,6 +1639,72 @@ <key>Value</key> <string /> </map> + <key>ContextConeInAlpha</key> + <map> + <key>Comment</key> + <string>Cone In Alpha</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>0.0</real> + </map> + <key>ContextConeOutAlpha</key> + <map> + <key>Comment</key> + <string>Cone Out Alpha</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>1.0</real> + </map> + <key>ContextConeFadeTime</key> + <map> + <key>Comment</key> + <string>Cone Fade Time</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>.08</real> + </map> + <key>ConversationHistoryPageSize</key> + <map> + <key>Comment</key> + <string>Chat history of conversation opened from call log is displayed by pages. So this is number of entries per page.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>100</integer> + </map> + <key>ConversationSortOrder</key> + <map> + <key>Comment</key> + <string>Specifies sort key for conversations</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>131073</integer> + </map> + <key>NearbyChatIsNotTornOff</key> + <map> + <key>Comment</key> + <string>saving torn-off state of the nearby chat between sessions</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>CloseChatOnReturn</key> <map> <key>Comment</key> @@ -4225,7 +4302,7 @@ <key>Type</key> <string>F32</string> <key>Value</key> - <real>0.65</real> + <real>0.95</real> </map> <key>InBandwidth</key> <map> @@ -8090,7 +8167,7 @@ <key>Type</key> <string>F32</string> <key>Value</key> - <real>0</real> + <real>-0.007</real> </map> <key>RenderShadowOffsetError</key> <map> @@ -9835,7 +9912,7 @@ <key>ShowScriptErrorsLocation</key> <map> <key>Comment</key> - <string>Show script error in chat or window</string> + <string>Show script error in chat (0) or window (1).</string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -10019,6 +10096,39 @@ <key>Value</key> <integer>2</integer> </map> + <key>BlockPeopleSortOrder</key> + <map> + <key>Comment</key> + <string>Specifies sort order for recent people (0 = by name, 1 = by type)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>2</integer> + </map> + <key>CallLogSortOrder</key> + <map> + <key>Comment</key> + <string>Specifies sort order for Call Log (0 = by name, 1 = by date)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>1</integer> + </map> + <key>SortFriendsFirst</key> + <map> + <key>Comment</key> + <string>Specifies whether friends will be sorted first in Call Log</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> <key>ShowPGSearchAll</key> <map> <key>Comment</key> diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml index 143126b334..1f637ef3ff 100644 --- a/indra/newview/app_settings/settings_per_account.xml +++ b/indra/newview/app_settings/settings_per_account.xml @@ -22,6 +22,61 @@ <key>Value</key> <string>The Resident you messaged is in 'busy mode' which means they have requested not to be disturbed. Your message will still be shown in their IM panel for later viewing.</string> </map> + <key>ConversationsExpandMessagePaneFirst</key> + <map> + <key>Comment</key> + <string>Expand either messages or conversations list pane from Conversations compact mode.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>1</integer> + </map> + <key>ConversationsListPaneCollapsed</key> + <map> + <key>Comment</key> + <string>Stores the expanded/collapsed state of the conversations list pane in Conversations floater.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>ConversationsListPaneWidth</key> + <map> + <key>Comment</key> + <string>Conversations floater list pane width.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>268</integer> + </map> + <key>ConversationsMessagePaneCollapsed</key> + <map> + <key>Comment</key> + <string>Stores the expanded/collapsed state of Conversations floater message pane.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>ConversationsMessagePaneWidth</key> + <map> + <key>Comment</key> + <string>Conversations floater message pane width.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>S32</string> + <key>Value</key> + <integer>412</integer> + </map> <key>InstantMessageLogPath</key> <map> <key>Comment</key> diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl index 8db4cb58cf..12706f130b 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl @@ -34,10 +34,10 @@ out vec4 frag_color; VARYING vec4 vertex_color; VARYING vec2 vary_texcoord0; -uniform sampler2DRectShadow shadowMap0; -uniform sampler2DRectShadow shadowMap1; -uniform sampler2DRectShadow shadowMap2; -uniform sampler2DRectShadow shadowMap3; +uniform sampler2DShadow shadowMap0; +uniform sampler2DShadow shadowMap1; +uniform sampler2DShadow shadowMap2; +uniform sampler2DShadow shadowMap3; uniform sampler2DRect depthMap; uniform mat4 shadow_matrix[6]; @@ -58,22 +58,22 @@ uniform float shadow_bias; uniform mat4 inv_proj; -float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc) +float pcfShadow(sampler2DShadow shadowMap, vec4 stc) { stc.xyz /= stc.w; stc.z += shadow_bias; - - stc.x = floor(stc.x + fract(stc.y*12345)); // add some chaotic jitter to X sample pos according to Y to disguise the snapping going on here + + stc.x = floor(stc.x*shadow_res.x + fract(stc.y*shadow_res.y*12345))/shadow_res.x; // add some chaotic jitter to X sample pos according to Y to disguise the snapping going on here - float cs = shadow2DRect(shadowMap, stc.xyz).x; + float cs = shadow2D(shadowMap, stc.xyz).x; float shadow = cs; - - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(2.0, 1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(1.0, -1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-1.0, 1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-2.0, -1.5, 0.0)).x; - - return shadow*0.2; + + shadow += shadow2D(shadowMap, stc.xyz+vec3(2.0/shadow_res.x, 1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(1.0/shadow_res.x, -1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(-1.0/shadow_res.x, 1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(-2.0/shadow_res.x, -1.5/shadow_res.y, 0.0)).x; + + return shadow*0.2; } @@ -99,8 +99,7 @@ void main() if (spos.z < near_split.z) { lpos = shadow_matrix[3]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.z, 0.0)/transition_domain.z; shadow += pcfShadow(shadowMap3, lpos)*w; @@ -111,8 +110,7 @@ void main() if (spos.z < near_split.y && spos.z > far_split.z) { lpos = shadow_matrix[2]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.y, 0.0)/transition_domain.y; w -= max(near_split.z-spos.z, 0.0)/transition_domain.z; @@ -123,8 +121,7 @@ void main() if (spos.z < near_split.x && spos.z > far_split.y) { lpos = shadow_matrix[1]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.x, 0.0)/transition_domain.x; w -= max(near_split.y-spos.z, 0.0)/transition_domain.y; @@ -135,8 +132,7 @@ void main() if (spos.z > far_split.x) { lpos = shadow_matrix[0]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(near_split.x-spos.z, 0.0)/transition_domain.x; diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedF.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedF.glsl index 33958a5010..228dc104ac 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedF.glsl @@ -31,17 +31,16 @@ out vec4 frag_color; #define frag_color gl_FragColor #endif -uniform sampler2DRectShadow shadowMap0; -uniform sampler2DRectShadow shadowMap1; -uniform sampler2DRectShadow shadowMap2; -uniform sampler2DRectShadow shadowMap3; +uniform sampler2DShadow shadowMap0; +uniform sampler2DShadow shadowMap1; +uniform sampler2DShadow shadowMap2; +uniform sampler2DShadow shadowMap3; uniform sampler2DRect depthMap; uniform sampler2D diffuseMap; uniform mat4 shadow_matrix[6]; uniform vec4 shadow_clip; uniform vec2 screen_res; -uniform vec2 shadow_res; vec3 atmosLighting(vec3 light); vec3 scaleSoftClip(vec3 light); @@ -54,6 +53,7 @@ VARYING vec3 vary_pointlight_col; VARYING vec2 vary_texcoord0; VARYING vec4 vertex_color; +uniform vec2 shadow_res; uniform float shadow_bias; uniform mat4 inv_proj; @@ -71,22 +71,22 @@ vec4 getPosition(vec2 pos_screen) return pos; } -float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc) +float pcfShadow(sampler2DShadow shadowMap, vec4 stc) { stc.xyz /= stc.w; stc.z += shadow_bias; - stc.x = floor(stc.x + fract(stc.y*12345)); // add some chaotic jitter to X sample pos according to Y to disguise the snapping going on here + stc.x = floor(stc.x*shadow_res.x + fract(stc.y*12345))/shadow_res.x; // add some chaotic jitter to X sample pos according to Y to disguise the snapping going on here - float cs = shadow2DRect(shadowMap, stc.xyz).x; + float cs = shadow2D(shadowMap, stc.xyz).x; float shadow = cs; - - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(2.0, 1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(1.0, -1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-1.0, 1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-2.0, -1.5, 0.0)).x; + + shadow += shadow2D(shadowMap, stc.xyz+vec3(2.0/shadow_res.x, 1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(1.0/shadow_res.x, -1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(-1.0/shadow_res.x, 1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(-2.0/shadow_res.x, -1.5/shadow_res.y, 0.0)).x; - return shadow*0.2; + return shadow*0.2; } @@ -112,8 +112,7 @@ void main() if (spos.z < near_split.z) { lpos = shadow_matrix[3]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.z, 0.0)/transition_domain.z; shadow += pcfShadow(shadowMap3, lpos)*w; @@ -124,8 +123,7 @@ void main() if (spos.z < near_split.y && spos.z > far_split.z) { lpos = shadow_matrix[2]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.y, 0.0)/transition_domain.y; w -= max(near_split.z-spos.z, 0.0)/transition_domain.z; @@ -136,8 +134,7 @@ void main() if (spos.z < near_split.x && spos.z > far_split.y) { lpos = shadow_matrix[1]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.x, 0.0)/transition_domain.x; w -= max(near_split.y-spos.z, 0.0)/transition_domain.y; @@ -148,8 +145,7 @@ void main() if (spos.z > far_split.x) { lpos = shadow_matrix[0]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(near_split.x-spos.z, 0.0)/transition_domain.x; diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedNoColorF.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedNoColorF.glsl index 2093fc37dc..c3950a10e1 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedNoColorF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedNoColorF.glsl @@ -33,17 +33,16 @@ out vec4 frag_color; uniform float minimum_alpha; -uniform sampler2DRectShadow shadowMap0; -uniform sampler2DRectShadow shadowMap1; -uniform sampler2DRectShadow shadowMap2; -uniform sampler2DRectShadow shadowMap3; +uniform sampler2DShadow shadowMap0; +uniform sampler2DShadow shadowMap1; +uniform sampler2DShadow shadowMap2; +uniform sampler2DShadow shadowMap3; uniform sampler2DRect depthMap; uniform sampler2D diffuseMap; uniform mat4 shadow_matrix[6]; uniform vec4 shadow_clip; uniform vec2 screen_res; -uniform vec2 shadow_res; vec3 atmosLighting(vec3 light); vec3 scaleSoftClip(vec3 light); @@ -55,6 +54,8 @@ VARYING vec3 vary_position; VARYING vec3 vary_pointlight_col; VARYING vec2 vary_texcoord0; +uniform vec2 shadow_res; + uniform float shadow_bias; uniform mat4 inv_proj; @@ -72,20 +73,20 @@ vec4 getPosition(vec2 pos_screen) return pos; } -float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc) +float pcfShadow(sampler2DShadow shadowMap, vec4 stc) { stc.xyz /= stc.w; stc.z += shadow_bias; - stc.x = floor(stc.x + fract(stc.y*12345)); // add some chaotic jitter to X sample pos according to Y to disguise the snapping going on here + stc.x = floor(stc.x*shadow_res.x + fract(stc.y*12345))/shadow_res.x; // add some chaotic jitter to X sample pos according to Y to disguise the snapping going on here + float cs = shadow2D(shadowMap, stc.xyz).x; - float cs = shadow2DRect(shadowMap, stc.xyz).x; float shadow = cs; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(2.0, 1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(1.0, -1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-1.0, 1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-2.0, -1.5, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(2.0/shadow_res.x, 1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(1.0/shadow_res.x, -1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(-1.0/shadow_res.x, 1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(-2.0/shadow_res.x, -1.5/shadow_res.y, 0.0)).x; return shadow*0.2; } @@ -120,8 +121,7 @@ void main() if (spos.z < near_split.z) { lpos = shadow_matrix[3]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.z, 0.0)/transition_domain.z; shadow += pcfShadow(shadowMap3, lpos)*w; @@ -132,8 +132,7 @@ void main() if (spos.z < near_split.y && spos.z > far_split.z) { lpos = shadow_matrix[2]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.y, 0.0)/transition_domain.y; w -= max(near_split.z-spos.z, 0.0)/transition_domain.z; @@ -144,8 +143,7 @@ void main() if (spos.z < near_split.x && spos.z > far_split.y) { lpos = shadow_matrix[1]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.x, 0.0)/transition_domain.x; w -= max(near_split.y-spos.z, 0.0)/transition_domain.y; @@ -156,8 +154,7 @@ void main() if (spos.z > far_split.x) { lpos = shadow_matrix[0]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(near_split.x-spos.z, 0.0)/transition_domain.x; diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl index db3d760359..c1495b145e 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl @@ -35,10 +35,10 @@ out vec4 frag_color; uniform sampler2DRect depthMap; uniform sampler2DRect normalMap; -uniform sampler2DRectShadow shadowMap0; -uniform sampler2DRectShadow shadowMap1; -uniform sampler2DRectShadow shadowMap2; -uniform sampler2DRectShadow shadowMap3; +uniform sampler2DShadow shadowMap0; +uniform sampler2DShadow shadowMap1; +uniform sampler2DShadow shadowMap2; +uniform sampler2DShadow shadowMap3; uniform sampler2DShadow shadowMap4; uniform sampler2DShadow shadowMap5; @@ -55,10 +55,10 @@ VARYING vec2 vary_fragcoord; uniform mat4 inv_proj; uniform vec2 screen_res; -uniform vec2 shadow_res; uniform vec2 proj_shadow_res; uniform vec3 sun_dir; +uniform vec2 shadow_res; uniform float shadow_bias; uniform float shadow_offset; @@ -78,30 +78,31 @@ vec4 getPosition(vec2 pos_screen) return pos; } -float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl, vec2 pos_screen) +float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl, vec2 pos_screen) { stc.xyz /= stc.w; - stc.z += shadow_bias*scl; + stc.z += shadow_bias; - stc.x = floor(stc.x + fract(pos_screen.y*0.666666666)); // add some jitter to X sample pos according to Y to disguise the snapping going on here + stc.x = floor(stc.x*shadow_res.x + fract(pos_screen.y*0.666666666))/shadow_res.x; // add some jitter to X sample pos according to Y to disguise the snapping going on here + float cs = shadow2D(shadowMap, stc.xyz).x; - float cs = shadow2DRect(shadowMap, stc.xyz).x; float shadow = cs; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(2.0, 1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(1.0, -1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-2.0, 1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-1.0, -1.5, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(2.0/shadow_res.x, 1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(1.0/shadow_res.x, -1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(-2.0/shadow_res.x, 1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(-1.0/shadow_res.x, -1.5/shadow_res.y, 0.0)).x; + - return shadow*0.2; + return shadow*0.2; } -float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl, vec2 pos_screen) +float pcfSpotShadow(sampler2DShadow shadowMap, vec4 stc, float scl, vec2 pos_screen) { stc.xyz /= stc.w; stc.z += spot_shadow_bias*scl; stc.x = floor(proj_shadow_res.x * stc.x + fract(pos_screen.y*0.666666666)) / proj_shadow_res.x; // snap - + float cs = shadow2D(shadowMap, stc.xyz).x; float shadow = cs; @@ -162,8 +163,7 @@ void main() if (spos.z < near_split.z) { lpos = shadow_matrix[3]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.z, 0.0)/transition_domain.z; shadow += pcfShadow(shadowMap3, lpos, 0.25, pos_screen)*w; @@ -174,8 +174,7 @@ void main() if (spos.z < near_split.y && spos.z > far_split.z) { lpos = shadow_matrix[2]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.y, 0.0)/transition_domain.y; w -= max(near_split.z-spos.z, 0.0)/transition_domain.z; @@ -186,7 +185,6 @@ void main() if (spos.z < near_split.x && spos.z > far_split.y) { lpos = shadow_matrix[1]*spos; - lpos.xy *= shadow_res; float w = 1.0; w -= max(spos.z-far_split.x, 0.0)/transition_domain.x; @@ -198,7 +196,6 @@ void main() if (spos.z > far_split.x) { lpos = shadow_matrix[0]*spos; - lpos.xy *= shadow_res; float w = 1.0; w -= max(near_split.x-spos.z, 0.0)/transition_domain.x; @@ -237,11 +234,11 @@ void main() //spotlight shadow 1 vec4 lpos = shadow_matrix[4]*spos; - frag_color[2] = pcfShadow(shadowMap4, lpos, 0.8, pos_screen); + frag_color[2] = pcfSpotShadow(shadowMap4, lpos, 0.8, pos_screen); //spotlight shadow 2 lpos = shadow_matrix[5]*spos; - frag_color[3] = pcfShadow(shadowMap5, lpos, 0.8, pos_screen); + frag_color[3] = pcfSpotShadow(shadowMap5, lpos, 0.8, pos_screen); //frag_color.rgb = pos.xyz; //frag_color.b = shadow; diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl index dfe108eb01..039fca9df2 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl @@ -34,10 +34,10 @@ out vec4 frag_color; uniform sampler2DRect depthMap; uniform sampler2DRect normalMap; -uniform sampler2DRectShadow shadowMap0; -uniform sampler2DRectShadow shadowMap1; -uniform sampler2DRectShadow shadowMap2; -uniform sampler2DRectShadow shadowMap3; +uniform sampler2DShadow shadowMap0; +uniform sampler2DShadow shadowMap1; +uniform sampler2DShadow shadowMap2; +uniform sampler2DShadow shadowMap3; uniform sampler2DShadow shadowMap4; uniform sampler2DShadow shadowMap5; uniform sampler2D noiseMap; @@ -55,10 +55,11 @@ VARYING vec2 vary_fragcoord; uniform mat4 inv_proj; uniform vec2 screen_res; -uniform vec2 shadow_res; uniform vec2 proj_shadow_res; uniform vec3 sun_dir; +uniform vec2 shadow_res; + uniform float shadow_bias; uniform float shadow_offset; @@ -139,30 +140,30 @@ float calcAmbientOcclusion(vec4 pos, vec3 norm) return min(ret, 1.0); } -float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl, vec2 pos_screen) +float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl, vec2 pos_screen) { stc.xyz /= stc.w; - stc.z += shadow_bias*scl; + stc.z += shadow_bias; - stc.x = floor(stc.x + fract(pos_screen.y*0.666666666)); + stc.x = floor(stc.x*shadow_res.x + fract(pos_screen.y*0.666666666))/shadow_res.x; + float cs = shadow2D(shadowMap, stc.xyz).x; - float cs = shadow2DRect(shadowMap, stc.xyz).x; float shadow = cs; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(2.0, 1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(1.0, -1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-1.0, 1.5, 0.0)).x; - shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-2.0, -1.5, 0.0)).x; - + shadow += shadow2D(shadowMap, stc.xyz+vec3(2.0/shadow_res.x, 1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(1.0/shadow_res.x, -1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(-1.0/shadow_res.x, 1.5/shadow_res.y, 0.0)).x; + shadow += shadow2D(shadowMap, stc.xyz+vec3(-2.0/shadow_res.x, -1.5/shadow_res.y, 0.0)).x; + return shadow*0.2; } -float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl, vec2 pos_screen) +float pcfSpotShadow(sampler2DShadow shadowMap, vec4 stc, float scl, vec2 pos_screen) { stc.xyz /= stc.w; stc.z += spot_shadow_bias*scl; stc.x = floor(proj_shadow_res.x * stc.x + fract(pos_screen.y*0.666666666)) / proj_shadow_res.x; // snap - + float cs = shadow2D(shadowMap, stc.xyz).x; float shadow = cs; @@ -223,8 +224,7 @@ void main() if (spos.z < near_split.z) { lpos = shadow_matrix[3]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.z, 0.0)/transition_domain.z; shadow += pcfShadow(shadowMap3, lpos, 0.25, pos_screen)*w; @@ -235,8 +235,7 @@ void main() if (spos.z < near_split.y && spos.z > far_split.z) { lpos = shadow_matrix[2]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.y, 0.0)/transition_domain.y; w -= max(near_split.z-spos.z, 0.0)/transition_domain.z; @@ -247,8 +246,7 @@ void main() if (spos.z < near_split.x && spos.z > far_split.y) { lpos = shadow_matrix[1]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(spos.z-far_split.x, 0.0)/transition_domain.x; w -= max(near_split.y-spos.z, 0.0)/transition_domain.y; @@ -259,8 +257,7 @@ void main() if (spos.z > far_split.x) { lpos = shadow_matrix[0]*spos; - lpos.xy *= shadow_res; - + float w = 1.0; w -= max(near_split.x-spos.z, 0.0)/transition_domain.x; @@ -298,11 +295,11 @@ void main() //spotlight shadow 1 vec4 lpos = shadow_matrix[4]*spos; - frag_color[2] = pcfShadow(shadowMap4, lpos, 0.8, pos_screen); + frag_color[2] = pcfSpotShadow(shadowMap4, lpos, 0.8, pos_screen); //spotlight shadow 2 lpos = shadow_matrix[5]*spos; - frag_color[3] = pcfShadow(shadowMap5, lpos, 0.8, pos_screen); + frag_color[3] = pcfSpotShadow(shadowMap5, lpos, 0.8, pos_screen); //frag_color.rgb = pos.xyz; //frag_color.b = shadow; diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 447836910d..bb0dbc7ff0 100755 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -54,7 +54,7 @@ #include "llmorphview.h" #include "llmoveview.h" #include "llnavigationbar.h" // to show/hide navigation bar when changing mouse look state -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llnotificationsutil.h" #include "llpanelpathfindingrebakenavmesh.h" #include "llpaneltopinfobar.h" @@ -1911,7 +1911,8 @@ void LLAgent::startTyping() { sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_START); } - LLNearbyChatBar::getInstance()->sendChatFromViewer("", CHAT_TYPE_START, FALSE); + (LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat"))-> + sendChatFromViewer("", CHAT_TYPE_START, FALSE); } //----------------------------------------------------------------------------- @@ -1923,7 +1924,8 @@ void LLAgent::stopTyping() { clearRenderState(AGENT_STATE_TYPING); sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_STOP); - LLNearbyChatBar::getInstance()->sendChatFromViewer("", CHAT_TYPE_STOP, FALSE); + (LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat"))-> + sendChatFromViewer("", CHAT_TYPE_STOP, FALSE); } } diff --git a/indra/newview/llagentwearablesfetch.cpp b/indra/newview/llagentwearablesfetch.cpp index e2417cdddb..e31e39dca2 100644 --- a/indra/newview/llagentwearablesfetch.cpp +++ b/indra/newview/llagentwearablesfetch.cpp @@ -342,7 +342,7 @@ void LLLibraryOutfitsFetch::folderDone() } mClothingID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING); - mLibraryClothingID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING, false, true); + mLibraryClothingID = gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_CLOTHING, false); // If Library->Clothing->Initial Outfits exists, use that. LLNameCategoryCollector matchFolderFunctor("Initial Outfits"); diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 6d67e098a6..510abf198a 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -2330,7 +2330,7 @@ void LLAppearanceMgr::copyLibraryGestures() // Copy gestures LLUUID lib_gesture_cat_id = - gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE,false,true); + gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_GESTURE,false); if (lib_gesture_cat_id.isNull()) { llwarns << "Unable to copy gestures, source category not found" << llendl; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 4de80037ed..d6c781020d 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -42,6 +42,7 @@ #include "llagentcamera.h" #include "llagentlanguage.h" #include "llagentwearables.h" +#include "llimfloatercontainer.h" #include "llwindow.h" #include "llviewerstats.h" #include "llviewerstatsrecorder.h" @@ -59,6 +60,7 @@ #include "llares.h" #include "llcurl.h" #include "llcalc.h" +#include "llconversationlog.h" #include "lltexturestats.h" #include "lltexturestats.h" #include "llviewerwindow.h" @@ -1203,7 +1205,7 @@ bool LLAppViewer::mainLoop() LLVoiceChannel::initClass(); LLVoiceClient::getInstance()->init(gServicePump); - LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLCallFloater::sOnCurrentChannelChanged, _1), true); + LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLIMFloaterContainer::onCurrentChannelChanged, _1), true); LLTimer frameTimer,idleTimer; LLTimer debugTime; LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); @@ -1832,6 +1834,9 @@ bool LLAppViewer::cleanup() // save mute list. gMuteList used to also be deleted here too. LLMuteList::getInstance()->cache(gAgent.getID()); + //save call log list + LLConversationLog::instance().cache(); + if (mPurgeOnExit) { llinfos << "Purging all cache files on exit" << llendflush; diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index fdd4565e50..13262efb3b 100755 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -42,6 +42,7 @@ #include "llappviewer.h" // for gLastVersionChannel #include "llcachename.h" #include "llcallingcard.h" // for LLAvatarTracker +#include "llconversationlog.h" #include "llfloateravatarpicker.h" // for LLFloaterAvatarPicker #include "llfloatergroupinvite.h" #include "llfloatergroups.h" @@ -55,6 +56,7 @@ #include "llinventorybridge.h" #include "llinventorymodel.h" // for gInventory.findCategoryUUIDForType #include "llinventorypanel.h" +#include "llimfloatercontainer.h" #include "llimview.h" // for gIMMgr #include "llmutelist.h" #include "llnotificationsutil.h" // for LLNotificationsUtil @@ -66,7 +68,6 @@ #include "llviewerobjectlist.h" #include "llviewermessage.h" // for handle_lure #include "llviewerregion.h" -#include "llimfloater.h" #include "lltrans.h" #include "llcallingcard.h" #include "llslurl.h" // IDEVO @@ -183,7 +184,7 @@ static void on_avatar_name_cache_start_im(const LLUUID& agent_id, LLUUID session_id = gIMMgr->addSession(name, IM_NOTHING_SPECIAL, agent_id); if (session_id != LLUUID::null) { - LLIMFloater::show(session_id); + LLIMFloaterContainer::getInstance()->showConversation(session_id); } make_ui_sound("UISndStartIM"); } @@ -235,7 +236,7 @@ void LLAvatarActions::startCall(const LLUUID& id) } // static -void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids) +void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids, const LLUUID& floater_id) { if (ids.size() == 0) { @@ -252,7 +253,7 @@ void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids) // create the new ad hoc voice session const std::string title = LLTrans::getString("conference-title"); LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START, - ids[0], id_array, true); + ids[0], id_array, true, floater_id); if (session_id == LLUUID::null) { return; @@ -285,7 +286,7 @@ bool LLAvatarActions::canCall() } // static -void LLAvatarActions::startConference(const uuid_vec_t& ids) +void LLAvatarActions::startConference(const uuid_vec_t& ids, const LLUUID& floater_id) { // *HACK: Copy into dynamic array LLDynamicArray<LLUUID> id_array; @@ -294,11 +295,15 @@ void LLAvatarActions::startConference(const uuid_vec_t& ids) id_array.push_back(*it); } const std::string title = LLTrans::getString("conference-title"); - LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START, ids[0], id_array); - if (session_id != LLUUID::null) + LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START, ids[0], id_array, false, floater_id); + + if (session_id == LLUUID::null) { - LLIMFloater::show(session_id); + return; } + + LLIMFloaterContainer::getInstance()->showConversation(session_id); + make_ui_sound("UISndStartIM"); } @@ -529,23 +534,6 @@ namespace action_give_inventory return acceptable; } - static void build_residents_string(const std::vector<LLAvatarName> avatar_names, std::string& residents_string) - { - llassert(avatar_names.size() > 0); - - const std::string& separator = LLTrans::getString("words_separator"); - for (std::vector<LLAvatarName>::const_iterator it = avatar_names.begin(); ; ) - { - LLAvatarName av_name = *it; - residents_string.append(av_name.mDisplayName); - if (++it == avatar_names.end()) - { - break; - } - residents_string.append(separator); - } - } - static void build_items_string(const std::set<LLUUID>& inventory_selected_uuids , std::string& items_string) { llassert(inventory_selected_uuids.size() > 0); @@ -681,7 +669,7 @@ namespace action_give_inventory } std::string residents; - build_residents_string(avatar_names, residents); + LLAvatarActions::buildResidentsString(avatar_names, residents); std::string items; build_items_string(inventory_selected_uuids, items); @@ -712,38 +700,84 @@ namespace action_give_inventory } } +// static +void LLAvatarActions::buildResidentsString(std::vector<LLAvatarName> avatar_names, std::string& residents_string) +{ + llassert(avatar_names.size() > 0); + + std::sort(avatar_names.begin(), avatar_names.end()); + const std::string& separator = LLTrans::getString("words_separator"); + for (std::vector<LLAvatarName>::const_iterator it = avatar_names.begin(); ; ) + { + residents_string.append((*it).mDisplayName); + if (++it == avatar_names.end()) + { + break; + } + residents_string.append(separator); + } +} +// static +void LLAvatarActions::buildResidentsString(const uuid_vec_t& avatar_uuids, std::string& residents_string) +{ + std::vector<LLAvatarName> avatar_names; + uuid_vec_t::const_iterator it = avatar_uuids.begin(); + for (; it != avatar_uuids.end(); ++it) + { + LLAvatarName av_name; + if (LLAvatarNameCache::get(*it, &av_name)) + { + avatar_names.push_back(av_name); + } + } + + // We should check whether the vector is not empty to pass the assertion + // that avatar_names.size() > 0 in LLAvatarActions::buildResidentsString. + if (!avatar_names.empty()) + { + LLAvatarActions::buildResidentsString(avatar_names, residents_string); + } +} //static std::set<LLUUID> LLAvatarActions::getInventorySelectedUUIDs() { - std::set<LLUUID> inventory_selected_uuids; + std::set<LLFolderViewItem*> inventory_selected; LLInventoryPanel* active_panel = action_give_inventory::get_active_inventory_panel(); if (active_panel) { - inventory_selected_uuids = active_panel->getRootFolder()->getSelectionList(); + inventory_selected= active_panel->getRootFolder()->getSelectionList(); } - if (inventory_selected_uuids.empty()) + if (inventory_selected.empty()) { LLSidepanelInventory *sidepanel_inventory = LLFloaterSidePanelContainer::getPanel<LLSidepanelInventory>("inventory"); if (sidepanel_inventory) { - inventory_selected_uuids = sidepanel_inventory->getInboxSelectionList(); + inventory_selected= sidepanel_inventory->getInboxSelectionList(); } } + std::set<LLUUID> inventory_selected_uuids; + for (std::set<LLFolderViewItem*>::iterator it = inventory_selected.begin(), end_it = inventory_selected.end(); + it != end_it; + ++it) + { + inventory_selected_uuids.insert(static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID()); + } return inventory_selected_uuids; } //static -void LLAvatarActions::shareWithAvatars() +void LLAvatarActions::shareWithAvatars(LLView * panel) { using namespace action_give_inventory; + LLFloater* root_floater = gFloaterView->getParentFloater(panel); LLFloaterAvatarPicker* picker = - LLFloaterAvatarPicker::show(boost::bind(give_inventory, _1, _2), TRUE, FALSE); + LLFloaterAvatarPicker::show(boost::bind(give_inventory, _1, _2), TRUE, FALSE, FALSE, root_floater->getName()); if (!picker) { return; @@ -751,6 +785,11 @@ void LLAvatarActions::shareWithAvatars() picker->setOkBtnEnableCb(boost::bind(is_give_inventory_acceptable)); picker->openFriendsTab(); + + if (root_floater) + { + root_floater->addDependentFloater(picker); + } LLNotificationsUtil::add("ShareNotification"); } @@ -769,15 +808,15 @@ bool LLAvatarActions::canShareSelectedItems(LLInventoryPanel* inv_panel /* = NUL // check selection in the panel LLFolderView* root_folder = inv_panel->getRootFolder(); - const std::set<LLUUID> inventory_selected_uuids = root_folder->getSelectionList(); - if (inventory_selected_uuids.empty()) return false; // nothing selected + const std::set<LLFolderViewItem*> inventory_selected = root_folder->getSelectionList(); + if (inventory_selected.empty()) return false; // nothing selected bool can_share = true; - std::set<LLUUID>::const_iterator it = inventory_selected_uuids.begin(); - const std::set<LLUUID>::const_iterator it_end = inventory_selected_uuids.end(); + std::set<LLFolderViewItem*>::const_iterator it = inventory_selected.begin(); + const std::set<LLFolderViewItem*>::const_iterator it_end = inventory_selected.end(); for (; it != it_end; ++it) { - LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it); + LLViewerInventoryCategory* inv_cat = gInventory.getCategory(static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID()); // any category can be offered. if (inv_cat) { @@ -785,9 +824,9 @@ bool LLAvatarActions::canShareSelectedItems(LLInventoryPanel* inv_panel /* = NUL } // check if inventory item can be given - LLFolderViewItem* item = root_folder->getItemByID(*it); + LLFolderViewItem* item = *it; if (!item) return false; - LLInvFVBridge* bridge = dynamic_cast<LLInvFVBridge*>(item->getListener()); + LLInvFVBridge* bridge = dynamic_cast<LLInvFVBridge*>(item->getViewModelItem()); if (bridge && bridge->canShare()) { continue; @@ -820,6 +859,26 @@ void LLAvatarActions::toggleBlock(const LLUUID& id) } // static +void LLAvatarActions::toggleMuteVoice(const LLUUID& id) +{ + std::string name; + gCacheName->getFullName(id, name); // needed for mute + + LLMuteList* mute_list = LLMuteList::getInstance(); + bool is_muted = mute_list->isMuted(id, LLMute::flagVoiceChat); + + LLMute mute(id, name, LLMute::AGENT); + if (!is_muted) + { + mute_list->add(mute, LLMute::flagVoiceChat); + } + else + { + mute_list->remove(mute, LLMute::flagVoiceChat); + } +} + +// static bool LLAvatarActions::canOfferTeleport(const LLUUID& id) { // First use LLAvatarTracker::isBuddy() @@ -865,6 +924,22 @@ void LLAvatarActions::inviteToGroup(const LLUUID& id) } } +// static +void LLAvatarActions::viewChatHistory(const LLUUID& id) +{ + const std::vector<LLConversation>& conversations = LLConversationLog::instance().getConversations(); + std::vector<LLConversation>::const_iterator iter = conversations.begin(); + + for (; iter != conversations.end(); ++iter) + { + if (iter->getParticipantID() == id) + { + LLFloaterReg::showInstance("preview_conversation", iter->getSessionID(), true); + break; + } + } +} + //== private methods ======================================================================================== // static @@ -1015,7 +1090,6 @@ void LLAvatarActions::requestFriendship(const LLUUID& target_id, const std::stri LLSD payload; payload["from_id"] = target_id; - payload["SUPPRESS_TOAST"] = true; LLNotificationsUtil::add("FriendshipOffered", args, payload); } @@ -1034,6 +1108,12 @@ bool LLAvatarActions::isBlocked(const LLUUID& id) } // static +bool LLAvatarActions::isVoiceMuted(const LLUUID& id) +{ + return LLMuteList::getInstance()->isMuted(id, LLMute::flagVoiceChat); +} + +// static bool LLAvatarActions::canBlock(const LLUUID& id) { std::string full_name; diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h index 748b7cb3d1..6e1198cd09 100644 --- a/indra/newview/llavataractions.h +++ b/indra/newview/llavataractions.h @@ -34,8 +34,10 @@ #include <string> #include <vector> +class LLAvatarName; class LLInventoryPanel; class LLFloater; +class LLView; /** * Friend-related actions (add, remove, offer teleport, etc) @@ -81,14 +83,14 @@ public: static void startCall(const LLUUID& id); /** - * Start an ad-hoc conference voice call with multiple users + * Start an ad-hoc conference voice call with multiple users in a specific IM floater. */ - static void startAdhocCall(const uuid_vec_t& ids); + static void startAdhocCall(const uuid_vec_t& ids, const LLUUID& floater_id = LLUUID::null); /** - * Start conference chat with the given avatars. + * Start conference chat with the given avatars in a specific IM floater. */ - static void startConference(const uuid_vec_t& ids); + static void startConference(const uuid_vec_t& ids, const LLUUID& floater_id = LLUUID::null); /** * Show avatar profile. @@ -116,7 +118,7 @@ public: /** * Share items with the picked avatars. */ - static void shareWithAvatars(); + static void shareWithAvatars(LLView * panel); /** * Block/unblock the avatar. @@ -124,6 +126,11 @@ public: static void toggleBlock(const LLUUID& id); /** + * Block/unblock the avatar voice. + */ + static void toggleMuteVoice(const LLUUID& id); + + /** * Return true if avatar with "id" is a friend */ static bool isFriend(const LLUUID& id); @@ -134,6 +141,11 @@ public: static bool isBlocked(const LLUUID& id); /** + * @return true if the avatar voice is blocked + */ + static bool isVoiceMuted(const LLUUID& id); + + /** * @return true if you can block the avatar */ static bool canBlock(const LLUUID& id); @@ -198,6 +210,27 @@ public: */ static bool canShareSelectedItems(LLInventoryPanel* inv_panel = NULL); + /** + * Builds a string of residents' display names separated by "words_separator" string. + * + * @param avatar_names - a vector of given avatar names from which resulting string is built + * @param residents_string - the resulting string + */ + static void buildResidentsString(std::vector<LLAvatarName> avatar_names, std::string& residents_string); + + /** + * Builds a string of residents' display names separated by "words_separator" string. + * + * @param avatar_uuids - a vector of given avatar uuids from which resulting string is built + * @param residents_string - the resulting string + */ + static void buildResidentsString(const uuid_vec_t& avatar_uuids, std::string& residents_string); + + /** + * Opens the chat history for avatar + */ + static void viewChatHistory(const LLUUID& id); + static std::set<LLUUID> getInventorySelectedUUIDs(); private: diff --git a/indra/newview/llavatariconctrl.cpp b/indra/newview/llavatariconctrl.cpp index b539ac38ed..6355f0db56 100644..100755 --- a/indra/newview/llavatariconctrl.cpp +++ b/indra/newview/llavatariconctrl.cpp @@ -28,6 +28,8 @@ #include "llavatariconctrl.h" +#include <boost/signals2.hpp> + // viewer includes #include "llagent.h" #include "llavatarconstants.h" @@ -148,9 +150,13 @@ LLAvatarIconCtrl::Params::Params() LLAvatarIconCtrl::LLAvatarIconCtrl(const LLAvatarIconCtrl::Params& p) -: LLIconCtrl(p), + : LLIconCtrl(p), + LLAvatarPropertiesObserver(), + mAvatarId(), + mFullName(), mDrawTooltip(p.draw_tooltip), - mDefaultIconName(p.default_icon_name) + mDefaultIconName(p.default_icon_name), + mAvatarNameCacheConnection() { mPriority = LLViewerFetchedTexture::BOOST_ICON; @@ -203,6 +209,11 @@ LLAvatarIconCtrl::~LLAvatarIconCtrl() LLAvatarPropertiesProcessor::getInstance()->removeObserver(mAvatarId, this); // Name callbacks will be automatically disconnected since LLUICtrl is trackable } + + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } } //virtual @@ -245,9 +256,20 @@ void LLAvatarIconCtrl::setValue(const LLSD& value) LLIconCtrl::setValue(value); } - LLAvatarNameCache::get(mAvatarId, - boost::bind(&LLAvatarIconCtrl::onAvatarNameCache, - this, _1, _2)); + fetchAvatarName(); +} + +void LLAvatarIconCtrl::fetchAvatarName() +{ + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + + if (mAvatarId.notNull()) + { + mAvatarNameCacheConnection = LLAvatarNameCache::get(mAvatarId, boost::bind(&LLAvatarIconCtrl::onAvatarNameCache, this, _1, _2)); + } } bool LLAvatarIconCtrl::updateFromCache() diff --git a/indra/newview/llavatariconctrl.h b/indra/newview/llavatariconctrl.h index 7f568fc5b8..f55967926a 100644 --- a/indra/newview/llavatariconctrl.h +++ b/indra/newview/llavatariconctrl.h @@ -27,6 +27,8 @@ #ifndef LL_LLAVATARICONCTRL_H #define LL_LLAVATARICONCTRL_H +#include <boost/signals2.hpp> + #include "lliconctrl.h" #include "llavatarpropertiesprocessor.h" #include "llviewermenu.h" @@ -86,20 +88,24 @@ public: // LLAvatarPropertiesProcessor observer trigger virtual void processProperties(void* data, EAvatarProcessorType type); - void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); - const LLUUID& getAvatarId() const { return mAvatarId; } const std::string& getFullName() const { return mFullName; } void setDrawTooltip(bool value) { mDrawTooltip = value;} protected: - LLUUID mAvatarId; - std::string mFullName; - bool mDrawTooltip; - std::string mDefaultIconName; + LLUUID mAvatarId; + std::string mFullName; + bool mDrawTooltip; + std::string mDefaultIconName; bool updateFromCache(); + +private: + void fetchAvatarName(); + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); + + boost::signals2::connection mAvatarNameCacheConnection; }; #endif // LL_LLAVATARICONCTRL_H diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp index 30eecfe323..7ff1b39573 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -27,6 +27,8 @@ #include "llviewerprecompiledheaders.h" +#include <boost/signals2.hpp> + #include "llavataractions.h" #include "llavatarlistitem.h" @@ -38,6 +40,7 @@ #include "llavatarnamecache.h" #include "llavatariconctrl.h" #include "lloutputmonitorctrl.h" +#include "lltooldraganddrop.h" bool LLAvatarListItem::sStaticInitialized = false; S32 LLAvatarListItem::sLeftPadding = 0; @@ -58,7 +61,8 @@ LLAvatarListItem::Params::Params() LLAvatarListItem::LLAvatarListItem(bool not_from_ui_factory/* = true*/) -: LLPanel(), + : LLPanel(), + LLFriendObserver(), mAvatarIcon(NULL), mAvatarName(NULL), mLastInteractionTime(NULL), @@ -73,7 +77,8 @@ LLAvatarListItem::LLAvatarListItem(bool not_from_ui_factory/* = true*/) mShowInfoBtn(true), mShowProfileBtn(true), mShowPermissions(false), - mHovered(false) + mHovered(false), + mAvatarNameCacheConnection() { if (not_from_ui_factory) { @@ -86,7 +91,14 @@ LLAvatarListItem::LLAvatarListItem(bool not_from_ui_factory/* = true*/) LLAvatarListItem::~LLAvatarListItem() { if (mAvatarId.notNull()) + { LLAvatarTracker::instance().removeParticularFriendObserver(mAvatarId, this); + } + + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } } BOOL LLAvatarListItem::postBuild() @@ -129,6 +141,19 @@ BOOL LLAvatarListItem::postBuild() return TRUE; } +void LLAvatarListItem::fetchAvatarName() +{ + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + + if (mAvatarId.notNull()) + { + mAvatarNameCacheConnection = LLAvatarNameCache::get(getAvatarId(), boost::bind(&LLAvatarListItem::onAvatarNameCache, this, _2)); + } +} + S32 LLAvatarListItem::notifyParent(const LLSD& info) { if (info.has("visibility_changed")) @@ -259,8 +284,7 @@ void LLAvatarListItem::setAvatarId(const LLUUID& id, const LLUUID& session_id, b mAvatarIcon->setValue(id); // Set avatar name. - LLAvatarNameCache::get(id, - boost::bind(&LLAvatarListItem::onAvatarNameCache, this, _2)); + fetchAvatarName(); } } @@ -334,6 +358,61 @@ BOOL LLAvatarListItem::handleDoubleClick(S32 x, S32 y, MASK mask) return LLPanel::handleDoubleClick(x, y, mask); } +BOOL LLAvatarListItem::handleMouseDown(S32 x, S32 y, MASK mask) +{ + if (LLUICtrl::handleMouseDown(x, y, mask)) + { + return TRUE; + } + + gFocusMgr.setMouseCapture(this); + + S32 screen_x; + S32 screen_y; + localPointToScreen(x, y, &screen_x, &screen_y); + LLToolDragAndDrop::getInstance()->setDragStart(screen_x, screen_y); + + return TRUE; +} + +BOOL LLAvatarListItem::handleMouseUp( S32 x, S32 y, MASK mask ) +{ + if (LLUICtrl::childrenHandleMouseUp(x, y, mask)) + { + return TRUE; + } + + if(hasMouseCapture()) + { + gFocusMgr.setMouseCapture(NULL); + } + return TRUE; +} + +BOOL LLAvatarListItem::handleHover(S32 x, S32 y, MASK mask) +{ + bool handled = hasMouseCapture(); + if(handled) + { + S32 screen_x; + S32 screen_y; + localPointToScreen(x, y, &screen_x, &screen_y); + + if(LLToolDragAndDrop::getInstance()->isOverThreshold(screen_x, screen_y)) + { + // First, create the global drag and drop object + std::vector<EDragAndDropType> types; + uuid_vec_t cargo_ids; + types.push_back(DAD_PERSON); + cargo_ids.push_back(mAvatarId); + LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_PEOPLE; + LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, src); + } + } + + return handled; +} + void LLAvatarListItem::setValue( const LLSD& value ) { if (!value.isMap()) return;; @@ -358,8 +437,7 @@ std::string LLAvatarListItem::getAvatarToolTip() const void LLAvatarListItem::updateAvatarName() { - LLAvatarNameCache::get(getAvatarId(), - boost::bind(&LLAvatarListItem::onAvatarNameCache, this, _2)); + fetchAvatarName(); } //== PRIVATE SECITON ========================================================== diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h index c95ac39696..41853b6b51 100644 --- a/indra/newview/llavatarlistitem.h +++ b/indra/newview/llavatarlistitem.h @@ -27,6 +27,8 @@ #ifndef LL_LLAVATARLISTITEM_H #define LL_LLAVATARLISTITEM_H +#include <boost/signals2.hpp> + #include "llpanel.h" #include "lloutputmonitorctrl.h" #include "llbutton.h" @@ -112,6 +114,9 @@ public: void onProfileBtnClick(); /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask ); + /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); protected: /** @@ -214,6 +219,9 @@ private: /// true when the mouse pointer is hovering over this item bool mHovered; + + void fetchAvatarName(); + boost::signals2::connection mAvatarNameCacheConnection; static bool sStaticInitialized; // this variable is introduced to improve code readability static S32 sLeftPadding; // padding to first left visible child (icon or name) diff --git a/indra/newview/llblockedlistitem.cpp b/indra/newview/llblockedlistitem.cpp new file mode 100644 index 0000000000..d9afd2b629 --- /dev/null +++ b/indra/newview/llblockedlistitem.cpp @@ -0,0 +1,114 @@ +/** + * @file llviewerobjectlistitem.cpp + * @brief viewer object list item implementation + * + * Class LLPanelInventoryListItemBase displays inventory item as an element + * of LLInventoryItemsList. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llblockedlistitem.h" + +// llui +#include "lliconctrl.h" +#include "lltextbox.h" +#include "lltextutil.h" + +// newview +#include "llavatariconctrl.h" +#include "llgroupiconctrl.h" +#include "llinventoryicon.h" +#include "llviewerobject.h" + +LLBlockedListItem::LLBlockedListItem(const LLMute* item) +: LLPanel(), + mItemID(item->mID), + mItemName(item->mName), + mMuteType(item->mType) +{ + buildFromFile("panel_blocked_list_item.xml"); +} + +BOOL LLBlockedListItem::postBuild() +{ + mTitleCtrl = getChild<LLTextBox>("item_name"); + mTitleCtrl->setValue(mItemName); + + switch (mMuteType) + { + case LLMute::AGENT: + case LLMute::EXTERNAL: + { + LLAvatarIconCtrl* avatar_icon = getChild<LLAvatarIconCtrl>("avatar_icon"); + avatar_icon->setVisible(TRUE); + avatar_icon->setValue(mItemID); + } + break; + case LLMute::GROUP: + { + LLGroupIconCtrl* group_icon = getChild<LLGroupIconCtrl>("group_icon"); + group_icon->setVisible(TRUE); + group_icon->setValue(mItemID); + } + break; + case LLMute::OBJECT: + case LLMute::BY_NAME: + getChild<LLUICtrl>("object_icon")->setVisible(TRUE); + break; + + default: + break; + } + + return TRUE; +} + +void LLBlockedListItem::onMouseEnter(S32 x, S32 y, MASK mask) +{ + getChildView("hovered_icon")->setVisible(true); + LLPanel::onMouseEnter(x, y, mask); +} + +void LLBlockedListItem::onMouseLeave(S32 x, S32 y, MASK mask) +{ + getChildView("hovered_icon")->setVisible(false); + LLPanel::onMouseLeave(x, y, mask); +} + +void LLBlockedListItem::setValue(const LLSD& value) +{ + if (!value.isMap() || !value.has("selected")) + { + return; + } + + getChildView("selected_icon")->setVisible(value["selected"]); +} + +void LLBlockedListItem::highlightName(const std::string& highlited_text) +{ + LLStyle::Params params; + LLTextUtil::textboxSetHighlightedVal(mTitleCtrl, params, mItemName, highlited_text); +} diff --git a/indra/newview/llblockedlistitem.h b/indra/newview/llblockedlistitem.h new file mode 100644 index 0000000000..05409e8a3b --- /dev/null +++ b/indra/newview/llblockedlistitem.h @@ -0,0 +1,73 @@ +/** + * @file llviewerobjectlistitem.h + * @brief viewer object list item header file + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef LLVIEWEROBJECTLISTITEM_H_ +#define LLVIEWEROBJECTLISTITEM_H_ + +#include "llmutelist.h" +#include "llpanel.h" +#include "llstyle.h" +#include "lltextbox.h" +#include "lliconctrl.h" + +/** + * This class represents items of LLBlockList, which represents + * contents of LLMuteList. LLMuteList "consists" of LLMute items. + * Each LLMute represents either blocked avatar or object and + * stores info about mute type (avatar or object) + * + * Each item consists if object/avatar icon and object/avatar name + * + * To create a blocked list item just need to pass LLMute pointer + * and appropriate block list item will be created depending on + * LLMute type (LLMute::EType) and other LLMute's info + */ +class LLBlockedListItem : public LLPanel +{ +public: + + LLBlockedListItem(const LLMute* item); + virtual BOOL postBuild(); + + void onMouseEnter(S32 x, S32 y, MASK mask); + void onMouseLeave(S32 x, S32 y, MASK mask); + + virtual void setValue(const LLSD& value); + + void highlightName(const std::string& highlited_text); + const std::string& getName() const { return mItemName; } + const LLMute::EType& getType() const { return mMuteType; } + const LLUUID& getUUID() const { return mItemID; } + +private: + + LLTextBox* mTitleCtrl; + const LLUUID mItemID; + std::string mItemName; + LLMute::EType mMuteType; + +}; + +#endif /* LLVIEWEROBJECTLISTITEM_H_ */ diff --git a/indra/newview/llblocklist.cpp b/indra/newview/llblocklist.cpp new file mode 100644 index 0000000000..066cb71677 --- /dev/null +++ b/indra/newview/llblocklist.cpp @@ -0,0 +1,284 @@ +/** + * @file llblocklist.cpp + * @brief List of the blocked avatars and objects. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llblocklist.h" + +#include "llavataractions.h" +#include "llblockedlistitem.h" +#include "llfloatersidepanelcontainer.h" +#include "llviewermenu.h" + +static LLDefaultChildRegistry::Register<LLBlockList> r("block_list"); + +static const LLBlockListNameComparator NAME_COMPARATOR; +static const LLBlockListNameTypeComparator NAME_TYPE_COMPARATOR; + +LLBlockList::LLBlockList(const Params& p) +: LLFlatListViewEx(p), + mSelectedItem(NULL), + mDirty(true) +{ + + LLMuteList::getInstance()->addObserver(this); + + // Set up context menu. + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + + registrar.add ("Block.Action", boost::bind(&LLBlockList::onCustomAction, this, _2)); + enable_registrar.add("Block.Enable", boost::bind(&LLBlockList::isActionEnabled, this, _2)); + + LLToggleableMenu* context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>( + "menu_people_blocked_gear.xml", + gMenuHolder, + LLViewerMenuHolderGL::child_registry_t::instance()); + if(context_menu) + { + mContextMenu = context_menu->getHandle(); + } +} + +LLBlockList::~LLBlockList() +{ + if (mContextMenu.get()) + { + mContextMenu.get()->die(); + } + + LLMuteList::getInstance()->removeObserver(this); +} + +BOOL LLBlockList::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + BOOL handled = LLUICtrl::handleRightMouseDown(x, y, mask); + + LLToggleableMenu* context_menu = mContextMenu.get(); + if (context_menu && size()) + { + context_menu->buildDrawLabels(); + context_menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, context_menu, x, y); + } + + return handled; +} + +void LLBlockList::setNameFilter(const std::string& filter) +{ + std::string filter_upper = filter; + LLStringUtil::toUpper(filter_upper); + if (mNameFilter != filter_upper) + { + mNameFilter = filter_upper; + setDirty(); + } +} + +void LLBlockList::sortByName() +{ + setComparator(&NAME_COMPARATOR); + sort(); +} + +void LLBlockList::sortByType() +{ + setComparator(&NAME_TYPE_COMPARATOR); + sort(); +} + +void LLBlockList::draw() +{ + if (mDirty) + { + refresh(); + } + + LLFlatListView::draw(); +} + +void LLBlockList::addNewItem(const LLMute* mute) +{ + LLBlockedListItem* item = new LLBlockedListItem(mute); + if (!mNameFilter.empty()) + { + item->highlightName(mNameFilter); + } + addItem(item, item->getUUID(), ADD_BOTTOM); +} + +void LLBlockList::refresh() +{ + bool have_filter = !mNameFilter.empty(); + + // save selection to restore it after list rebuilt + LLUUID selected = getSelectedUUID(); + + // calling refresh may be initiated by removing currently selected item + // so select next item and save the selection to restore it after list rebuilt + if (!selectNextItemPair(false, true)) + { + selectNextItemPair(true, true); + } + LLUUID next_selected = getSelectedUUID(); + + clear(); + + std::vector<LLMute> mutes = LLMuteList::instance().getMutes(); + std::vector<LLMute>::const_iterator mute_it = mutes.begin(); + + for (; mute_it != mutes.end(); ++mute_it) + { + if (have_filter && !findInsensitive(mute_it->mName, mNameFilter)) + continue; + + addNewItem(&*mute_it); + } + + if (getItemPair(selected)) + { + // restore previously selected item + selectItemPair(getItemPair(selected), true); + } + else if (getItemPair(next_selected)) + { + // previously selected item was removed, so select next item + selectItemPair(getItemPair(next_selected), true); + } + + // Sort the list. + sort(); + + setDirty(false); +} + +bool LLBlockList::findInsensitive(std::string haystack, const std::string& needle_upper) +{ + LLStringUtil::toUpper(haystack); + return haystack.find(needle_upper) != std::string::npos; +} + +LLBlockedListItem* LLBlockList::getBlockedItem() const +{ + LLPanel* panel = LLFlatListView::getSelectedItem(); + LLBlockedListItem* item = dynamic_cast<LLBlockedListItem*>(panel); + return item; +} + +bool LLBlockList::isActionEnabled(const LLSD& userdata) +{ + bool action_enabled = true; + + const std::string command_name = userdata.asString(); + + if ("profile_item" == command_name) + { + LLBlockedListItem* item = getBlockedItem(); + action_enabled = item && (LLMute::AGENT == item->getType()); + } + + if ("unblock_item" == command_name) + { + action_enabled = getSelectedItem() != NULL; + } + + return action_enabled; +} + +void LLBlockList::onCustomAction(const LLSD& userdata) +{ + if (!isActionEnabled(userdata)) + { + return; + } + + LLBlockedListItem* item = getBlockedItem(); + const std::string command_name = userdata.asString(); + + if ("unblock_item" == command_name) + { + LLMute mute(item->getUUID(), item->getName()); + LLMuteList::getInstance()->remove(mute); + } + else if ("profile_item" == command_name) + { + switch(item->getType()) + { + + case LLMute::AGENT: + LLAvatarActions::showProfile(item->getUUID()); + break; + + default: + break; + } + } +} + +bool LLBlockListItemComparator::compare(const LLPanel* item1, const LLPanel* item2) const +{ + const LLBlockedListItem* blocked_item1 = dynamic_cast<const LLBlockedListItem*>(item1); + const LLBlockedListItem* blocked_item2 = dynamic_cast<const LLBlockedListItem*>(item2); + + if (!blocked_item1 || !blocked_item2) + { + llerror("blocked_item1 and blocked_item2 cannot be null", 0); + return true; + } + + return doCompare(blocked_item1, blocked_item2); +} + +bool LLBlockListNameComparator::doCompare(const LLBlockedListItem* blocked_item1, const LLBlockedListItem* blocked_item2) const +{ + std::string name1 = blocked_item1->getName(); + std::string name2 = blocked_item2->getName(); + + LLStringUtil::toUpper(name1); + LLStringUtil::toUpper(name2); + + return name1 < name2; +} + +bool LLBlockListNameTypeComparator::doCompare(const LLBlockedListItem* blocked_item1, const LLBlockedListItem* blocked_item2) const +{ + LLMute::EType type1 = blocked_item1->getType(); + LLMute::EType type2 = blocked_item2->getType(); + + // if mute type is LLMute::BY_NAME or LLMute::OBJECT it means that this mute is an object + bool both_mutes_are_objects = (LLMute::OBJECT == type1 || LLMute::BY_NAME == type1) && (LLMute::OBJECT == type2 || LLMute::BY_NAME == type2); + + // mute types may be different, but since both LLMute::BY_NAME and LLMute::OBJECT types represent objects + // it's needed to perform additional checking of both_mutes_are_objects variable + if (type1 != type2 && !both_mutes_are_objects) + { + // objects in block list go first, so return true if mute type is not an avatar + return LLMute::AGENT != type1; + } + + return NAME_COMPARATOR.compare(blocked_item1, blocked_item2); +} diff --git a/indra/newview/llblocklist.h b/indra/newview/llblocklist.h new file mode 100644 index 0000000000..1a215710f4 --- /dev/null +++ b/indra/newview/llblocklist.h @@ -0,0 +1,138 @@ +/** + * @file llblocklist.h + * @brief List of the blocked avatars and objects. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef LLBLOCKLIST_H_ +#define LLBLOCKLIST_H_ + +#include "llflatlistview.h" +#include "lllistcontextmenu.h" +#include "llmutelist.h" +#include "lltoggleablemenu.h" + +class LLBlockedListItem; +class LLMute; + +/** + * List of blocked avatars and objects. + * This list represents contents of the LLMuteList. + * Each change in LLMuteList leads to rebuilding this list, so + * it's always in actual state. + */ +class LLBlockList: public LLFlatListViewEx, public LLMuteListObserver +{ + LOG_CLASS(LLBlockList); +public: + struct Params : public LLInitParam::Block<Params, LLFlatListViewEx::Params> + { + Params(){}; + }; + + LLBlockList(const Params& p); + virtual ~LLBlockList(); + + virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + LLToggleableMenu* getContextMenu() const { return mContextMenu.get(); } + LLBlockedListItem* getBlockedItem() const; + + virtual void onChange() { refresh(); } + virtual void draw(); + + void setNameFilter(const std::string& filter); + void sortByName(); + void sortByType(); + void refresh(); + +private: + + void addNewItem(const LLMute* mute); + void setDirty(bool dirty = true) { mDirty = dirty; } + bool findInsensitive(std::string haystack, const std::string& needle_upper); + + bool isActionEnabled(const LLSD& userdata); + void onCustomAction (const LLSD& userdata); + + + LLHandle<LLToggleableMenu> mContextMenu; + + LLBlockedListItem* mSelectedItem; + std::string mNameFilter; + bool mDirty; + +}; + + +/* + * Abstract comparator for blocked items + */ +class LLBlockListItemComparator : public LLFlatListView::ItemComparator +{ + LOG_CLASS(LLBlockListItemComparator); + +public: + LLBlockListItemComparator() {}; + virtual ~LLBlockListItemComparator() {}; + + virtual bool compare(const LLPanel* item1, const LLPanel* item2) const; + +protected: + + virtual bool doCompare(const LLBlockedListItem* blocked_item1, const LLBlockedListItem* blocked_item2) const = 0; +}; + + +/* + * Compares items by name + */ +class LLBlockListNameComparator : public LLBlockListItemComparator +{ + LOG_CLASS(LLBlockListNameComparator); + +public: + LLBlockListNameComparator() {}; + virtual ~LLBlockListNameComparator() {}; + +protected: + + virtual bool doCompare(const LLBlockedListItem* blocked_item1, const LLBlockedListItem* blocked_item2) const; +}; + +/* + * Compares items by type and then by name within type + * Objects come first then avatars + */ +class LLBlockListNameTypeComparator : public LLBlockListItemComparator +{ + LOG_CLASS(LLBlockListNameTypeComparator); + +public: + LLBlockListNameTypeComparator() {}; + virtual ~LLBlockListNameTypeComparator() {}; + +protected: + + virtual bool doCompare(const LLBlockedListItem* blocked_item1, const LLBlockedListItem* blocked_item2) const; +}; + +#endif /* LLBLOCKLIST_H_ */ diff --git a/indra/newview/llbrowsernotification.cpp b/indra/newview/llbrowsernotification.cpp index 6e77d1e336..9e608d2c8b 100644 --- a/indra/newview/llbrowsernotification.cpp +++ b/indra/newview/llbrowsernotification.cpp @@ -35,11 +35,8 @@ using namespace LLNotificationsUI; -bool LLBrowserNotification::processNotification(const LLSD& notify) +bool LLBrowserNotification::processNotification(const LLNotificationPtr& notification) { - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - if (!notification) return false; - LLUUID media_id = notification->getPayload()["media_id"].asUUID(); LLMediaCtrl* media_instance = LLMediaCtrl::getInstance(media_id); if (media_instance) diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp index f2375bfa4f..e767609d74 100644 --- a/indra/newview/llcallfloater.cpp +++ b/indra/newview/llcallfloater.cpp @@ -334,7 +334,7 @@ void LLCallFloater::refreshParticipantList() if (!non_avatar_caller) { llassert(mParticipants == NULL); // check for possible memory leak - mParticipants = new LLParticipantList(mSpeakerManager, mAvatarList, true, mVoiceType != VC_GROUP_CHAT && mVoiceType != VC_AD_HOC_CHAT, false); + mParticipants = new LLParticipantList(mSpeakerManager, mAvatarList, mConversationViewModel, true, mVoiceType != VC_GROUP_CHAT && mVoiceType != VC_AD_HOC_CHAT, false); mParticipants->setValidateSpeakerCallback(boost::bind(&LLCallFloater::validateSpeaker, this, _1)); const U32 speaker_sort_order = gSavedSettings.getU32("SpeakerParticipantDefaultOrder"); mParticipants->setSortOrder(LLParticipantList::EParticipantSortOrder(speaker_sort_order)); @@ -364,7 +364,8 @@ void LLCallFloater::onAvatarListRefreshed() } // static -void LLCallFloater::sOnCurrentChannelChanged(const LLUUID& /*session_id*/) +// This entry point now disable, but left for later use. +void LLCallFloater::onCurrentChannelChanged(const LLUUID& /*session_id*/) { LLVoiceChannel* channel = LLVoiceChannel::getCurrentVoiceChannel(); diff --git a/indra/newview/llcallfloater.h b/indra/newview/llcallfloater.h index 00a3f76e56..e1c7b3f43a 100644 --- a/indra/newview/llcallfloater.h +++ b/indra/newview/llcallfloater.h @@ -31,6 +31,7 @@ #include "lltransientdockablefloater.h" #include "llvoicechannel.h" #include "llvoiceclient.h" +#include "llconversationmodel.h" class LLAvatarList; class LLAvatarListItem; @@ -73,7 +74,7 @@ public: */ /*virtual*/ void onParticipantsChanged(); - static void sOnCurrentChannelChanged(const LLUUID& session_id); + static void onCurrentChannelChanged(const LLUUID& session_id); private: typedef enum e_voice_controls_type @@ -228,6 +229,7 @@ private: LLSpeakerMgr* mSpeakerManager; LLParticipantList* mParticipants; LLAvatarList* mAvatarList; + LLConversationViewModel mConversationViewModel; LLNonAvatarCaller* mNonAvatarCaller; EVoiceControls mVoiceType; LLPanel* mAgentPanel; @@ -258,7 +260,7 @@ private: * * Is used to ignore voice channel changed callback for the same channel. * - * @see sOnCurrentChannelChanged() + * @see onCurrentChannelChanged() */ static LLVoiceChannel* sCurrentVoiceChannel; diff --git a/indra/newview/llcallingcard.cpp b/indra/newview/llcallingcard.cpp index 0d55c4429a..60d60abd45 100644 --- a/indra/newview/llcallingcard.cpp +++ b/indra/newview/llcallingcard.cpp @@ -54,6 +54,7 @@ #include "llresmgr.h" #include "llslurl.h" #include "llimview.h" +#include "lltrans.h" #include "llviewercontrol.h" #include "llviewernetwork.h" #include "llviewerobjectlist.h" @@ -723,12 +724,13 @@ static void on_avatar_name_cache_notify(const LLUUID& agent_id, // Use display name only because this user is your friend LLSD args; args["NAME"] = av_name.mDisplayName; + args["STATUS"] = online ? LLTrans::getString("OnlineStatus") : LLTrans::getString("OfflineStatus"); LLNotificationPtr notification; if (online) { notification = - LLNotificationsUtil::add("FriendOnline", + LLNotificationsUtil::add("FriendOnlineOffline", args, payload.with("respond_on_mousedown", TRUE), boost::bind(&LLAvatarActions::startIM, agent_id)); @@ -736,7 +738,7 @@ static void on_avatar_name_cache_notify(const LLUUID& agent_id, else { notification = - LLNotificationsUtil::add("FriendOffline", args, payload); + LLNotificationsUtil::add("FriendOnlineOffline", args, payload); } // If there's an open IM session with this agent, send a notification there too. diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 84e73e96fa..605e3ece51 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -28,6 +28,8 @@ #include "llchathistory.h" +#include <boost/signals2.hpp> + #include "llavatarnamecache.h" #include "llinstantmessage.h" @@ -110,7 +112,8 @@ public: mFrom(), mSessionID(), mMinUserNameWidth(0), - mUserNameFont(NULL) + mUserNameFont(NULL), + mAvatarNameCacheConnection() {} static LLChatHistoryHeader* createInstance(const std::string& file_name) @@ -124,6 +127,11 @@ public: { // Detach the info button so that it doesn't get destroyed (EXT-8463). hideInfoCtrl(); + + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } } BOOL handleMouseUp(S32 x, S32 y, MASK mask) @@ -143,7 +151,8 @@ public: { LLMuteList::getInstance()->add(LLMute(getAvatarId(), mFrom, LLMute::OBJECT)); - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD().with("blocked_to_select", getAvatarId())); + LLFloaterSidePanelContainer::showPanel("people", "panel_people", + LLSD().with("people_panel_tab_name", "blocked_panel").with("blocked_to_select", getAvatarId())); } } @@ -282,8 +291,7 @@ public: // Start with blank so sample data from XUI XML doesn't // flash on the screen user_name->setValue( LLSD() ); - LLAvatarNameCache::get(mAvatarID, - boost::bind(&LLChatHistoryHeader::onAvatarNameCache, this, _1, _2)); + fetchAvatarName(); } else if (chat.mChatStyle == CHAT_STYLE_HISTORY || mSourceType == CHAT_SOURCE_AGENT) @@ -415,31 +423,6 @@ public: } } - void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name) - { - mFrom = av_name.mDisplayName; - - LLTextBox* user_name = getChild<LLTextBox>("user_name"); - user_name->setValue( LLSD(av_name.mDisplayName ) ); - user_name->setToolTip( av_name.mUsername ); - - if (gSavedSettings.getBOOL("NameTagShowUsernames") && - LLAvatarNameCache::useDisplayNames() && - !av_name.mIsDisplayNameDefault) - { - LLStyle::Params style_params_name; - LLColor4 userNameColor = LLUIColorTable::instance().getColor("EmphasisColor"); - style_params_name.color(userNameColor); - style_params_name.font.name("SansSerifSmall"); - style_params_name.font.style("NORMAL"); - style_params_name.readonly_color(userNameColor); - user_name->appendText(" - " + av_name.mUsername, FALSE, style_params_name); - } - setToolTip( av_name.mUsername ); - // name might have changed, update width - updateMinUserNameWidth(); - } - protected: static const S32 PADDING = 20; @@ -554,6 +537,45 @@ private: user_name->reshape(user_rect.getWidth() + delta_pos_x, user_rect.getHeight()); } + void fetchAvatarName() + { + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + + if (mAvatarID.notNull()) + { + mAvatarNameCacheConnection = LLAvatarNameCache::get(mAvatarID, + boost::bind(&LLChatHistoryHeader::onAvatarNameCache, this, _1, _2)); + } + } + + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name) + { + mFrom = av_name.mDisplayName; + + LLTextBox* user_name = getChild<LLTextBox>("user_name"); + user_name->setValue( LLSD(av_name.mDisplayName ) ); + user_name->setToolTip( av_name.mUsername ); + + if (gSavedSettings.getBOOL("NameTagShowUsernames") && + LLAvatarNameCache::useDisplayNames() && + !av_name.mIsDisplayNameDefault) + { + LLStyle::Params style_params_name; + LLColor4 userNameColor = LLUIColorTable::instance().getColor("EmphasisColor"); + style_params_name.color(userNameColor); + style_params_name.font.name("SansSerifSmall"); + style_params_name.font.style("NORMAL"); + style_params_name.readonly_color(userNameColor); + user_name->appendText(" - " + av_name.mUsername, FALSE, style_params_name); + } + setToolTip( av_name.mUsername ); + // name might have changed, update width + updateMinUserNameWidth(); + } + protected: LLHandle<LLView> mPopupMenuHandleAvatar; LLHandle<LLView> mPopupMenuHandleObject; @@ -568,6 +590,9 @@ protected: S32 mMinUserNameWidth; const LLFontGL* mUserNameFont; + +private: + boost::signals2::connection mAvatarNameCacheConnection; }; LLUICtrl* LLChatHistoryHeader::sInfoCtrl = NULL; @@ -584,7 +609,8 @@ LLChatHistory::LLChatHistory(const LLChatHistory::Params& p) mBottomSeparatorPad(p.bottom_separator_pad), mTopHeaderPad(p.top_header_pad), mBottomHeaderPad(p.bottom_header_pad), - mIsLastMessageFromLog(false) + mIsLastMessageFromLog(false), + mNotifyAboutUnreadMsg(p.notify_unread_msg) { LLTextEditor::Params editor_params(p); editor_params.rect = getLocalRect(); @@ -594,6 +620,14 @@ LLChatHistory::LLChatHistory(const LLChatHistory::Params& p) mEditor = LLUICtrlFactory::create<LLTextEditor>(editor_params, this); } +LLSD LLChatHistory::getValue() const +{ + LLSD* text=new LLSD(); + text->assign(mEditor->getText()); + return *text; + +} + LLChatHistory::~LLChatHistory() { this->clear(); @@ -695,6 +729,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL { LLFastTimer _(FTM_APPEND_MESSAGE); bool use_plain_text_chat_history = args["use_plain_text_chat_history"].asBoolean(); + bool square_brackets = false; // square brackets necessary for a system messages llassert(mEditor); if (!mEditor) @@ -702,9 +737,10 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL return; } + bool from_me = chat.mFromID == gAgent.getID(); mEditor->setPlainText(use_plain_text_chat_history); - if (!mEditor->scrolledToEnd() && chat.mFromID != gAgent.getID() && !chat.mFromName.empty()) + if (mNotifyAboutUnreadMsg && !mEditor->scrolledToEnd() && !from_me && !chat.mFromName.empty()) { mUnreadChatSources.insert(chat.mFromName); mMoreChatPanel->setVisible(TRUE); @@ -734,16 +770,23 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL } LLColor4 txt_color = LLUIColorTable::instance().getColor("White"); + LLColor4 name_color(txt_color); + LLViewerChat::getChatColor(chat,txt_color); LLFontGL* fontp = LLViewerChat::getChatFont(); std::string font_name = LLFontGL::nameFromFont(fontp); std::string font_size = LLFontGL::sizeFromFont(fontp); - LLStyle::Params style_params; - style_params.color(txt_color); - style_params.readonly_color(txt_color); - style_params.font.name(font_name); - style_params.font.size(font_size); - style_params.font.style(input_append_params.font.style); + + LLStyle::Params body_message_params; + body_message_params.color(txt_color); + body_message_params.readonly_color(txt_color); + body_message_params.font.name(font_name); + body_message_params.font.size(font_size); + body_message_params.font.style(input_append_params.font.style); + + LLStyle::Params name_params(body_message_params); + name_params.color(name_color); + name_params.readonly_color(name_color); std::string prefix = chat.mText.substr(0, 4); @@ -766,29 +809,51 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL if (irc_me || chat.mChatStyle == CHAT_STYLE_IRC) { delimiter = LLStringUtil::null; - style_params.font.style = "ITALIC"; + name_params.font.style = "ITALIC"; } bool message_from_log = chat.mChatStyle == CHAT_STYLE_HISTORY; // We graying out chat history by graying out messages that contains full date in a time string if (message_from_log) { - style_params.color(LLColor4::grey); - style_params.readonly_color(LLColor4::grey); + txt_color = LLColor4::grey; + body_message_params.color(txt_color); + body_message_params.readonly_color(txt_color); + name_params.color(txt_color); + name_params.readonly_color(txt_color); } + bool prependNewLineState = mEditor->getText().size() != 0; + + // compact mode: show a timestamp and name if (use_plain_text_chat_history) { - LLStyle::Params timestamp_style(style_params); + square_brackets = chat.mFromName == SYSTEM_FROM; + + LLStyle::Params timestamp_style(body_message_params); + + // out of the timestamp + if (args["show_time"].asBoolean()) + { if (!message_from_log) { LLColor4 timestamp_color = LLUIColorTable::instance().getColor("ChatTimestampColor"); timestamp_style.color(timestamp_color); timestamp_style.readonly_color(timestamp_color); } - mEditor->appendText("[" + chat.mTimeStr + "] ", mEditor->getLength() != 0, timestamp_style); + mEditor->appendText("[" + chat.mTimeStr + "] ", prependNewLineState, timestamp_style); + prependNewLineState = false; + } + + // out the opening square bracket (if need) + if (square_brackets) + { + mEditor->appendText("[", prependNewLineState, body_message_params); + prependNewLineState = false; + } - if (utf8str_trim(chat.mFromName).size() != 0) + // names showing + if (args["show_names_for_p2p_conv"].asBoolean() && utf8str_trim(chat.mFromName).size() != 0) { // Don't hotlink any messages from the system (e.g. "Second Life:"), so just add those in plain text. if ( chat.mSourceType == CHAT_SOURCE_OBJECT && chat.mFromID.notNull()) @@ -798,32 +863,47 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL // set the link for the object name to be the objectim SLapp // (don't let object names with hyperlinks override our objectim Url) - LLStyle::Params link_params(style_params); + LLStyle::Params link_params(body_message_params); LLColor4 link_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); link_params.color = link_color; link_params.readonly_color = link_color; link_params.is_link = true; link_params.link_href = url; - mEditor->appendText(chat.mFromName + delimiter, - false, link_params); + mEditor->appendText(chat.mFromName + delimiter, prependNewLineState, link_params); + prependNewLineState = false; } else if ( chat.mFromName != SYSTEM_FROM && chat.mFromID.notNull() && !message_from_log) { - LLStyle::Params link_params(style_params); + LLStyle::Params link_params(body_message_params); link_params.overwriteFrom(LLStyleMap::instance().lookupAgent(chat.mFromID)); + if (from_me) + { std::string localized_name; + bool is_localized = LLTrans::findString(localized_name, "AgentNameSubst"); + mEditor->appendText((is_localized? localized_name:"(You)") + delimiter, + prependNewLineState, link_params); + prependNewLineState = false; + } + else + { // Add link to avatar's inspector and delimiter to message. - mEditor->appendText(std::string(link_params.link_href) + delimiter, false, link_params); + mEditor->appendText(std::string(link_params.link_href) + delimiter, + prependNewLineState, link_params); + prependNewLineState = false; + } } else { - mEditor->appendText("<nolink>" + chat.mFromName + "</nolink>" + delimiter, false, style_params); + mEditor->appendText("<nolink>" + chat.mFromName + "</nolink>" + delimiter, + prependNewLineState, body_message_params); + prependNewLineState = false; } } } - else + else // showing timestamp and name in the expanded mode { + prependNewLineState = false; LLView* view = NULL; LLInlineViewSegment::Params p; p.force_newline = true; @@ -844,7 +924,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL } else { - view = getHeader(chat, style_params, args); + view = getHeader(chat, name_params, args); if (mEditor->getLength() == 0) p.top_pad = 0; else @@ -873,41 +953,16 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL mIsLastMessageFromLog = message_from_log; } + // body of the message processing + + // notify processing if (chat.mNotifId.notNull()) { LLNotificationPtr notification = LLNotificationsUtil::find(chat.mNotifId); if (notification != NULL) { LLIMToastNotifyPanel* notify_box = new LLIMToastNotifyPanel( - notification, chat.mSessionID, LLRect::null, !use_plain_text_chat_history); - //we can't set follows in xml since it broke toasts behavior - notify_box->setFollowsLeft(); - notify_box->setFollowsRight(); - notify_box->setFollowsTop(); - - ctrl_list_t ctrls = notify_box->getControlPanel()->getCtrlList(); - S32 offset = 0; - // Children were added by addChild() which uses push_front to insert them into list, - // so to get buttons in correct order reverse iterator is used (EXT-5906) - for (ctrl_list_t::reverse_iterator it = ctrls.rbegin(); it != ctrls.rend(); it++) - { - LLButton * button = dynamic_cast<LLButton*> (*it); - if (button != NULL) - { - button->setOrigin( offset, - button->getRect().mBottom); - button->setLeftHPad(2 * HPAD); - button->setRightHPad(2 * HPAD); - // set zero width before perform autoResize() - button->setRect(LLRect(button->getRect().mLeft, - button->getRect().mTop, button->getRect().mLeft, - button->getRect().mBottom)); - button->setAutoResize(true); - button->autoResize(); - offset += HPAD + button->getRect().getWidth(); - button->setFollowsNone(); - } - } + notification, chat.mSessionID, LLRect::null, !use_plain_text_chat_history, mEditor); //Prepare the rect for the view LLRect target_rect = mEditor->getDocumentView()->getRect(); @@ -924,6 +979,8 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL mEditor->appendWidget(params, "\n", false); } } + + // usual messages showing else { std::string message = irc_me ? chat.mText.substr(3) : chat.mText; @@ -931,7 +988,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL //MESSAGE TEXT PROCESSING //*HACK getting rid of redundant sender names in system notifications sent using sender name (see EXT-5010) - if (use_plain_text_chat_history && gAgentID != chat.mFromID && chat.mFromID.notNull()) + if (use_plain_text_chat_history && !from_me && chat.mFromID.notNull()) { std::string slurl_about = SLURL_APP_AGENT + chat.mFromID.asString() + SLURL_ABOUT; if (message.length() > slurl_about.length() && @@ -946,13 +1003,19 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL message = chat.mFromName + message; } - mEditor->appendText(message, FALSE, style_params); + if (square_brackets) + { + message += "]"; + } + + mEditor->appendText(message, prependNewLineState, body_message_params); + prependNewLineState = false; } mEditor->blockUndo(); // automatically scroll to end when receiving chat from myself - if (chat.mFromID == gAgentID) + if (from_me) { mEditor->setCursorAndScrollToEnd(); } diff --git a/indra/newview/llchathistory.h b/indra/newview/llchathistory.h index 28344e6a10..bb6d4fb59c 100644 --- a/indra/newview/llchathistory.h +++ b/indra/newview/llchathistory.h @@ -60,6 +60,8 @@ class LLChatHistory : public LLUICtrl Optional<LLTextBox::Params> more_chat_text; + Optional<bool> notify_unread_msg; + Params() : message_header("message_header"), message_separator("message_separator"), @@ -71,7 +73,8 @@ class LLChatHistory : public LLUICtrl bottom_separator_pad("bottom_separator_pad"), top_header_pad("top_header_pad"), bottom_header_pad("bottom_header_pad"), - more_chat_text("more_chat_text") + more_chat_text("more_chat_text"), + notify_unread_msg("notify_unread_msg", true) {} }; @@ -100,7 +103,7 @@ class LLChatHistory : public LLUICtrl public: ~LLChatHistory(); - + LLSD getValue() const; void initFromParams(const Params&); /** @@ -122,6 +125,7 @@ class LLChatHistory : public LLUICtrl LLUUID mLastFromID; LLDate mLastMessageTime; bool mIsLastMessageFromLog; + bool mNotifyAboutUnreadMsg; //std::string mLastMessageTimeStr; std::string mMessageHeaderFilename; diff --git a/indra/newview/llchatitemscontainerctrl.cpp b/indra/newview/llchatitemscontainerctrl.cpp index 9a84280f25..f1b5c42ef3 100644 --- a/indra/newview/llchatitemscontainerctrl.cpp +++ b/indra/newview/llchatitemscontainerctrl.cpp @@ -35,7 +35,7 @@ #include "llfloaterreg.h" #include "lllocalcliprect.h" #include "lltrans.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llviewercontrol.h" #include "llagentdata.h" @@ -96,8 +96,15 @@ void LLNearbyChatToastPanel::reshape (S32 width, S32 height, BOOL called_from_p { LLPanel::reshape(width, height,called_from_parent); - LLUICtrl* msg_text = getChild<LLUICtrl>("msg_text", false); - LLUICtrl* icon = getChild<LLUICtrl>("avatar_icon", false); + // reshape() may be called from LLView::initFromParams() before the children are created. + // We call findChild() instead of getChild() here to avoid creating dummy controls. + LLUICtrl* msg_text = findChild<LLUICtrl>("msg_text", false); + LLUICtrl* icon = findChild<LLUICtrl>("avatar_icon", false); + + if (!msg_text || !icon) + { + return; + } LLRect msg_text_rect = msg_text->getRect(); LLRect avatar_rect = icon->getRect(); @@ -316,12 +323,12 @@ BOOL LLNearbyChatToastPanel::handleMouseUp (S32 x, S32 y, MASK mask) return TRUE; else { - LLNearbyChatBar::getInstance()->showHistory(); + (LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat"))->showHistory(); return FALSE; } } - LLNearbyChatBar::getInstance()->showHistory(); + (LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat"))->showHistory(); return LLPanel::handleMouseUp(x,y,mask); } @@ -355,6 +362,8 @@ BOOL LLNearbyChatToastPanel::handleRightMouseDown(S32 x, S32 y, MASK mask) } void LLNearbyChatToastPanel::draw() { + LLPanel::draw(); + if(mIsDirty) { LLAvatarIconCtrl* icon = getChild<LLAvatarIconCtrl>("avatar_icon", false); @@ -372,7 +381,6 @@ void LLNearbyChatToastPanel::draw() } mIsDirty = false; } - LLToastPanelBase::draw(); } diff --git a/indra/newview/llchatitemscontainerctrl.h b/indra/newview/llchatitemscontainerctrl.h index 1d700dcede..89b0c4f37a 100644 --- a/indra/newview/llchatitemscontainerctrl.h +++ b/indra/newview/llchatitemscontainerctrl.h @@ -40,7 +40,7 @@ typedef enum e_show_item_header CHATITEMHEADER_SHOW_BOTH } EShowItemHeader; -class LLNearbyChatToastPanel: public LLToastPanelBase +class LLNearbyChatToastPanel : public LLPanel { protected: LLNearbyChatToastPanel() diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index a661808d1f..e328186fd6 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -34,6 +34,7 @@ #include "llgroupactions.h" #include "lliconctrl.h" #include "llimfloater.h" +#include "llimfloatercontainer.h" #include "llimview.h" #include "llfloaterreg.h" #include "lllocalcliprect.h" @@ -335,30 +336,15 @@ void LLIMWellChiclet::messageCountChanged(const LLSD& session_data) /* LLNotificationChiclet implementation */ /************************************************************************/ LLNotificationChiclet::LLNotificationChiclet(const Params& p) -: LLSysWellChiclet(p) -, mUreadSystemNotifications(0) +: LLSysWellChiclet(p), + mUreadSystemNotifications(0) { - // connect counter handlers to the signals - connectCounterUpdatersToSignal("notify"); - connectCounterUpdatersToSignal("groupnotify"); - connectCounterUpdatersToSignal("offer"); - + mNotificationChannel.reset(new ChicletNotificationChannel(this)); // ensure that notification well window exists, to synchronously // handle toast add/delete events. LLNotificationWellWindow::getInstance()->setSysWellChiclet(this); } -void LLNotificationChiclet::connectCounterUpdatersToSignal(const std::string& notification_type) -{ - LLNotificationsUI::LLNotificationManager* manager = LLNotificationsUI::LLNotificationManager::getInstance(); - LLNotificationsUI::LLEventHandler* n_handler = manager->getHandlerForNotification(notification_type); - if(n_handler) - { - n_handler->setNewNotificationCallback(boost::bind(&LLNotificationChiclet::incUreadSystemNotifications, this)); - n_handler->setDelNotification(boost::bind(&LLNotificationChiclet::decUreadSystemNotifications, this)); - } -} - void LLNotificationChiclet::onMenuItemClicked(const LLSD& user_data) { std::string action = user_data.asString(); @@ -407,6 +393,18 @@ void LLNotificationChiclet::setCounter(S32 counter) updateWidget(getCounter() == 0); } + +bool LLNotificationChiclet::ChicletNotificationChannel::filterNotification( LLNotificationPtr notification ) +{ + if( !(notification->canLogToIM() && notification->hasFormElements()) + && (!notification->getPayload().has("give_inventory_notification") + || notification->getPayload()["give_inventory_notification"])) + { + return true; + } + return false; +} + ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// @@ -1199,7 +1197,7 @@ void LLChicletPanel::onCurrentVoiceChannelChanged(const LLUUID& session_id) chiclet->setShowSpeaker(true); if (gSavedSettings.getBOOL("OpenIMOnVoice")) { - LLIMFloater::show(chiclet->getSessionId()); + LLIMFloaterContainer::getInstance()->showConversation(session_id); } } } diff --git a/indra/newview/llchiclet.h b/indra/newview/llchiclet.h index 19683492c2..6395f5b694 100644 --- a/indra/newview/llchiclet.h +++ b/indra/newview/llchiclet.h @@ -34,6 +34,7 @@ #include "lloutputmonitorctrl.h" #include "llgroupmgr.h" #include "llimview.h" +#include "llnotifications.h" class LLMenuGL; class LLIMFloater; @@ -872,9 +873,11 @@ class LLIMWellChiclet : public LLSysWellChiclet, LLIMSessionObserver { friend class LLUICtrlFactory; public: - virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) {} - virtual void sessionRemoved(const LLUUID& session_id) { messageCountChanged(LLSD()); } - virtual void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) {} + /*virtual*/ void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) {} + /*virtual*/ void sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) {} + /*virtual*/ void sessionVoiceOrIMStarted(const LLUUID& session_id) {}; + /*virtual*/ void sessionRemoved(const LLUUID& session_id) { messageCountChanged(LLSD()); } + /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) {} ~LLIMWellChiclet(); protected: @@ -911,11 +914,35 @@ protected: class LLNotificationChiclet : public LLSysWellChiclet { + LOG_CLASS(LLNotificationChiclet); + friend class LLUICtrlFactory; public: struct Params : public LLInitParam::Block<Params, LLSysWellChiclet::Params>{}; protected: + struct ChicletNotificationChannel : public LLNotificationChannel + { + ChicletNotificationChannel(LLNotificationChiclet* chiclet) + : LLNotificationChannel(LLNotificationChannel::Params().filter(filterNotification).name(chiclet->getSessionId().asString())), + mChiclet(chiclet) + { + // connect counter handlers to the signals + connectToChannel("Group Notifications"); + connectToChannel("Offer"); + connectToChannel("Notifications"); + } + + static bool filterNotification(LLNotificationPtr notify); + // connect counter updaters to the corresponding signals + /*virtual*/ void onAdd(LLNotificationPtr p) { mChiclet->setCounter(++mChiclet->mUreadSystemNotifications); } + /*virtual*/ void onDelete(LLNotificationPtr p) { mChiclet->setCounter(--mChiclet->mUreadSystemNotifications); } + + LLNotificationChiclet* const mChiclet; + }; + + boost::scoped_ptr<ChicletNotificationChannel> mNotificationChannel; + LLNotificationChiclet(const Params& p); /** @@ -933,12 +960,6 @@ protected: */ /*virtual*/ void createMenu(); - // connect counter updaters to the corresponding signals - void connectCounterUpdatersToSignal(const std::string& notification_type); - - // methods for updating a number of unread System notifications - void incUreadSystemNotifications() { setCounter(++mUreadSystemNotifications); } - void decUreadSystemNotifications() { setCounter(--mUreadSystemNotifications); } /*virtual*/ void setCounter(S32 counter); S32 mUreadSystemNotifications; }; diff --git a/indra/newview/llchicletbar.cpp b/indra/newview/llchicletbar.cpp index f1bc51fbe7..3ebb83b336 100644 --- a/indra/newview/llchicletbar.cpp +++ b/indra/newview/llchicletbar.cpp @@ -57,19 +57,11 @@ LLChicletBar::LLChicletBar(const LLSD&) : mChicletPanel(NULL), mToolbarStack(NULL) { - // Firstly add our self to IMSession observers, so we catch session events - // before chiclets do that. - LLIMMgr::getInstance()->addSessionObserver(this); - buildFromFile("panel_chiclet_bar.xml"); } LLChicletBar::~LLChicletBar() { - if (!LLSingleton<LLIMMgr>::destroyed()) - { - LLIMMgr::getInstance()->removeSessionObserver(this); - } } LLIMChiclet* LLChicletBar::createIMChiclet(const LLUUID& session_id) @@ -102,6 +94,13 @@ void LLChicletBar::sessionAdded(const LLUUID& session_id, const std::string& nam // no need to spawn chiclets for participants in P2P calls called through Avaline if (session->isP2P() && session->isOtherParticipantAvaline()) return; + // Do not spawn chiclet when using the new multitab conversation UI + if (LLIMConversation::isChatMultiTab()) + { + LLIMConversation::addToHost(session_id); + return; + } + if (getChicletPanel()->findChiclet<LLChiclet>(session_id)) return; LLIMChiclet* chiclet = createIMChiclet(session_id); @@ -109,7 +108,7 @@ void LLChicletBar::sessionAdded(const LLUUID& session_id, const std::string& nam { chiclet->setIMSessionName(name); chiclet->setOtherParticipantId(other_participant_id); - + LLIMFloater::onIMChicletCreated(session_id); } @@ -125,10 +124,12 @@ void LLChicletBar::sessionRemoved(const LLUUID& session_id) if(getChicletPanel()) { // IM floater should be closed when session removed and associated chiclet closed - LLIMFloater* iMfloater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); - if (iMfloater != NULL) + LLIMFloater* im_floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); + if (im_floater != NULL && !im_floater->getStartConferenceInSameFloater()) { - iMfloater->closeFloater(); + // Close the IM floater only if we are not planning to close the P2P chat + // and start a new conference in the same floater. + im_floater->closeFloater(); } getChicletPanel()->removeChiclet(session_id); diff --git a/indra/newview/llchicletbar.h b/indra/newview/llchicletbar.h index 1427bf95e0..a9a5b61ae7 100644 --- a/indra/newview/llchicletbar.h +++ b/indra/newview/llchicletbar.h @@ -50,9 +50,11 @@ public: LLChicletPanel* getChicletPanel() { return mChicletPanel; } // LLIMSessionObserver observe triggers - virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); - virtual void sessionRemoved(const LLUUID& session_id); - void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); + /*virtual*/ void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + /*virtual*/ void sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) {}; + /*virtual*/ void sessionVoiceOrIMStarted(const LLUUID& session_id) {}; + /*virtual*/ void sessionRemoved(const LLUUID& session_id); + /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); S32 getTotalUnreadIMCount(); diff --git a/indra/newview/llconversationlog.cpp b/indra/newview/llconversationlog.cpp new file mode 100644 index 0000000000..7a5a476efb --- /dev/null +++ b/indra/newview/llconversationlog.cpp @@ -0,0 +1,498 @@ +/** + * @file llconversationlog.h + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llagent.h" +#include "llavatarnamecache.h" +#include "llconversationlog.h" +#include "lltrans.h" + +const int CONVERSATION_LIFETIME = 30; // lifetime of LLConversation is 30 days by spec + +struct Conversation_params +{ + Conversation_params(time_t time) + : mTime(time), + mTimestamp(LLConversation::createTimestamp(time)) + {} + + time_t mTime; + std::string mTimestamp; + SessionType mConversationType; + std::string mConversationName; + std::string mHistoryFileName; + LLUUID mSessionID; + LLUUID mParticipantID; + bool mHasOfflineIMs; +}; + +/************************************************************************/ +/* LLConversation implementation */ +/************************************************************************/ + +LLConversation::LLConversation(const Conversation_params& params) +: mTime(params.mTime), + mTimestamp(params.mTimestamp), + mConversationType(params.mConversationType), + mConversationName(params.mConversationName), + mHistoryFileName(params.mHistoryFileName), + mSessionID(params.mSessionID), + mParticipantID(params.mParticipantID), + mHasOfflineIMs(params.mHasOfflineIMs) +{ + setListenIMFloaterOpened(); +} + +LLConversation::LLConversation(const LLIMModel::LLIMSession& session) +: mTime(time_corrected()), + mTimestamp(createTimestamp(mTime)), + mConversationType(session.mSessionType), + mConversationName(session.mName), + mHistoryFileName(session.mHistoryFileName), + mSessionID(session.isOutgoingAdHoc() ? session.generateOutgouigAdHocHash() : session.mSessionID), + mParticipantID(session.mOtherParticipantID), + mHasOfflineIMs(session.mHasOfflineMessage) +{ + setListenIMFloaterOpened(); +} + +LLConversation::LLConversation(const LLConversation& conversation) +{ + mTime = conversation.getTime(); + mTimestamp = conversation.getTimestamp(); + mConversationType = conversation.getConversationType(); + mConversationName = conversation.getConversationName(); + mHistoryFileName = conversation.getHistoryFileName(); + mSessionID = conversation.getSessionID(); + mParticipantID = conversation.getParticipantID(); + mHasOfflineIMs = conversation.hasOfflineMessages(); + + setListenIMFloaterOpened(); +} + +LLConversation::~LLConversation() +{ + mIMFloaterShowedConnection.disconnect(); +} + +void LLConversation::updateTimestamp() +{ + mTime = time_corrected(); + mTimestamp = createTimestamp(mTime); +} + +void LLConversation::onIMFloaterShown(const LLUUID& session_id) +{ + if (mSessionID == session_id) + { + mHasOfflineIMs = false; + } +} + +// static +const std::string LLConversation::createTimestamp(const time_t& utc_time) +{ + std::string timeStr; + LLSD substitution; + substitution["datetime"] = (S32) utc_time; + + timeStr = "["+LLTrans::getString ("TimeMonth")+"]/[" + +LLTrans::getString ("TimeDay")+"]/[" + +LLTrans::getString ("TimeYear")+"] [" + +LLTrans::getString ("TimeHour")+"]:[" + +LLTrans::getString ("TimeMin")+"]"; + + + LLStringUtil::format (timeStr, substitution); + return timeStr; +} + +bool LLConversation::isOlderThan(U32 days) const +{ + time_t now = time_corrected(); + U32 age = (U32)((now - mTime) / SEC_PER_DAY); // age of conversation in days + + return age > days; +} + +void LLConversation::setListenIMFloaterOpened() +{ + LLIMFloater* floater = LLIMFloater::findInstance(mSessionID); + + bool offline_ims_visible = LLIMFloater::isVisible(floater) && floater->hasFocus(); + + // we don't need to listen for im floater with this conversation is opened + // if floater is already opened or this conversation doesn't have unread offline messages + if (mHasOfflineIMs && !offline_ims_visible) + { + mIMFloaterShowedConnection = LLIMFloater::setIMFloaterShowedCallback(boost::bind(&LLConversation::onIMFloaterShown, this, _1)); + } + else + { + mHasOfflineIMs = false; + } +} + +/************************************************************************/ +/* LLConversationLogFriendObserver implementation */ +/************************************************************************/ + +// Note : An LLSingleton like LLConversationLog cannot be an LLFriendObserver +// at the same time. +// This is because avatar observers are deleted by the observed object which +// conflicts with the way LLSingleton are deleted. + +class LLConversationLogFriendObserver : public LLFriendObserver +{ +public: + LLConversationLogFriendObserver() {} + virtual ~LLConversationLogFriendObserver() {} + virtual void changed(U32 mask); +}; + +void LLConversationLogFriendObserver::changed(U32 mask) +{ + if (mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE)) + { + LLConversationLog::instance().notifyObservers(); + } +} + +/************************************************************************/ +/* LLConversationLog implementation */ +/************************************************************************/ + +LLConversationLog::LLConversationLog() +{ + LLControlVariable* ctrl = gSavedPerAccountSettings.getControl("LogInstantMessages").get(); + if (ctrl) + { + ctrl->getSignal()->connect(boost::bind(&LLConversationLog::enableLogging, this, _2)); + if (ctrl->getValue().asBoolean()) + { + enableLogging(true); + } + } +} + +void LLConversationLog::enableLogging(bool enable) +{ + if (enable) + { + loadFromFile(getFileName()); + + LLIMMgr::instance().addSessionObserver(this); + newMessageSignalConnection = LLIMModel::instance().addNewMsgCallback(boost::bind(&LLConversationLog::onNewMessageReceived, this, _1)); + + mFriendObserver = new LLConversationLogFriendObserver; + LLAvatarTracker::instance().addObserver(mFriendObserver); + } + else + { + saveToFile(getFileName()); + + LLIMMgr::instance().removeSessionObserver(this); + newMessageSignalConnection.disconnect(); + LLAvatarTracker::instance().removeObserver(mFriendObserver); + mConversations.clear(); + } + + notifyObservers(); +} + +void LLConversationLog::logConversation(const LLUUID& session_id) +{ + const LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id); + LLConversation* conversation = findConversation(session); + + if (session && conversation) + { + updateConversationTimestamp(conversation); + } + else if (session && !conversation) + { + createConversation(session); + } +} + +void LLConversationLog::createConversation(const LLIMModel::LLIMSession* session) +{ + if (session) + { + LLConversation conversation(*session); + mConversations.push_back(conversation); + + if (LLIMModel::LLIMSession::P2P_SESSION == session->mSessionType) + { + LLAvatarNameCache::get(session->mOtherParticipantID, boost::bind(&LLConversationLog::onAvatarNameCache, this, _1, _2, session)); + } + + notifyObservers(); + } +} + +void LLConversationLog::updateConversationName(const LLIMModel::LLIMSession* session, const std::string& name) +{ + if (!session) + { + return; + } + + LLConversation* conversation = findConversation(session); + if (conversation) + { + conversation->setConverstionName(name); + notifyPrticularConversationObservers(conversation->getSessionID(), LLConversationLogObserver::CHANGED_NAME); + } +} + +void LLConversationLog::updateConversationTimestamp(LLConversation* conversation) +{ + if (conversation) + { + conversation->updateTimestamp(); + notifyPrticularConversationObservers(conversation->getSessionID(), LLConversationLogObserver::CHANGED_TIME); + } +} + +LLConversation* LLConversationLog::findConversation(const LLIMModel::LLIMSession* session) +{ + if (!session) + { + return NULL; + } + + const LLUUID session_id = session->isOutgoingAdHoc() ? session->generateOutgouigAdHocHash() : session->mSessionID; + + conversations_vec_t::iterator conv_it = mConversations.begin(); + for(; conv_it != mConversations.end(); ++conv_it) + { + if (conv_it->getSessionID() == session_id) + { + return &*conv_it; + } + } + + return NULL; +} + +void LLConversationLog::removeConversation(const LLConversation& conversation) +{ + conversations_vec_t::iterator conv_it = mConversations.begin(); + for(; conv_it != mConversations.end(); ++conv_it) + { + if (conv_it->getSessionID() == conversation.getSessionID() && conv_it->getTime() == conversation.getTime()) + { + mConversations.erase(conv_it); + notifyObservers(); + return; + } + } +} + +const LLConversation* LLConversationLog::getConversation(const LLUUID& session_id) +{ + conversations_vec_t::const_iterator conv_it = mConversations.begin(); + for(; conv_it != mConversations.end(); ++conv_it) + { + if (conv_it->getSessionID() == session_id) + { + return &*conv_it; + } + } + + return NULL; +} + +void LLConversationLog::addObserver(LLConversationLogObserver* observer) +{ + mObservers.insert(observer); +} + +void LLConversationLog::removeObserver(LLConversationLogObserver* observer) +{ + mObservers.erase(observer); +} + +void LLConversationLog::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) +{ + logConversation(session_id); +} + +void LLConversationLog::cache() +{ + if (gSavedPerAccountSettings.getBOOL("LogInstantMessages")) + { + saveToFile(getFileName()); + } +} + +std::string LLConversationLog::getFileName() +{ + std::string filename = "conversation"; + return gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, filename) + ".log"; +} + +bool LLConversationLog::saveToFile(const std::string& filename) +{ + if (!filename.size()) + { + llwarns << "Call log list filename is empty!" << llendl; + return false; + } + + LLFILE* fp = LLFile::fopen(filename, "wb"); + if (!fp) + { + llwarns << "Couldn't open call log list" << filename << llendl; + return false; + } + + std::string participant_id; + std::string conversation_id; + + conversations_vec_t::const_iterator conv_it = mConversations.begin(); + for (; conv_it != mConversations.end(); ++conv_it) + { + conv_it->getSessionID().toString(conversation_id); + conv_it->getParticipantID().toString(participant_id); + + // examples of two file entries + // [1343221177] 0 1 0 John Doe| 7e4ec5be-783f-49f5-71dz-16c58c64c145 4ec62a74-c246-0d25-2af6-846beac2aa55 john.doe| + // [1343222639] 2 0 0 Ad-hoc Conference| c3g67c89-c479-4c97-b21d-32869bcfe8rc 68f1c33e-4135-3e3e-a897-8c9b23115c09 Ad-hoc Conference hash597394a0-9982-766d-27b8-c75560213b9a| + + fprintf(fp, "[%d] %d %d %d %s| %s %s %s|\n", + (S32)conv_it->getTime(), + (S32)conv_it->getConversationType(), + (S32)0, + (S32)conv_it->hasOfflineMessages(), + conv_it->getConversationName().c_str(), + participant_id.c_str(), + conversation_id.c_str(), + conv_it->getHistoryFileName().c_str()); + } + fclose(fp); + return true; +} +bool LLConversationLog::loadFromFile(const std::string& filename) +{ + if(!filename.size()) + { + llwarns << "Call log list filename is empty!" << llendl; + return false; + } + + LLFILE* fp = LLFile::fopen(filename, "rb"); + if (!fp) + { + llwarns << "Couldn't open call log list" << filename << llendl; + return false; + } + + char buffer[MAX_STRING]; + char conv_name_buffer[MAX_STRING]; + char part_id_buffer[MAX_STRING]; + char conv_id_buffer[MAX_STRING]; + char history_file_name[MAX_STRING]; + int has_offline_ims; + int stype; + S32 time; + // before CHUI-348 it was a flag of conversation voice state + int prereserved_unused; + + while (!feof(fp) && fgets(buffer, MAX_STRING, fp)) + { + conv_name_buffer[0] = '\0'; + part_id_buffer[0] = '\0'; + conv_id_buffer[0] = '\0'; + + sscanf(buffer, "[%d] %d %d %d %[^|]| %s %s %[^|]|", + &time, + &stype, + &prereserved_unused, + &has_offline_ims, + conv_name_buffer, + part_id_buffer, + conv_id_buffer, + history_file_name); + + Conversation_params params(time); + params.mConversationType = (SessionType)stype; + params.mHasOfflineIMs = has_offline_ims; + params.mConversationName = std::string(conv_name_buffer); + params.mParticipantID = LLUUID(part_id_buffer); + params.mSessionID = LLUUID(conv_id_buffer); + params.mHistoryFileName = std::string(history_file_name); + + LLConversation conversation(params); + + // CHUI-325 + // The conversation log should be capped to the last 30 days. Conversations with the last utterance + // being over 30 days old should be purged from the conversation log text file on login. + if (conversation.isOlderThan(CONVERSATION_LIFETIME)) + { + continue; + } + + mConversations.push_back(conversation); + } + fclose(fp); + + LLFile::remove(filename); + cache(); + + notifyObservers(); + return true; +} + +void LLConversationLog::notifyObservers() +{ + std::set<LLConversationLogObserver*>::const_iterator iter = mObservers.begin(); + for (; iter != mObservers.end(); ++iter) + { + (*iter)->changed(); + } +} + +void LLConversationLog::notifyPrticularConversationObservers(const LLUUID& session_id, U32 mask) +{ + std::set<LLConversationLogObserver*>::const_iterator iter = mObservers.begin(); + for (; iter != mObservers.end(); ++iter) + { + (*iter)->changed(session_id, mask); + } +} + +void LLConversationLog::onNewMessageReceived(const LLSD& data) +{ + const LLUUID session_id = data["session_id"].asUUID(); + logConversation(session_id); +} + +void LLConversationLog::onAvatarNameCache(const LLUUID& participant_id, const LLAvatarName& av_name, const LLIMModel::LLIMSession* session) +{ + updateConversationName(session, av_name.getCompleteName()); +} diff --git a/indra/newview/llconversationlog.h b/indra/newview/llconversationlog.h new file mode 100644 index 0000000000..b92cf0f5e2 --- /dev/null +++ b/indra/newview/llconversationlog.h @@ -0,0 +1,195 @@ +/** + * @file llconversationlog.h + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLCONVERSATIONLOG_H_ +#define LLCONVERSATIONLOG_H_ + +#include "llcallingcard.h" +#include "llimfloater.h" +#include "llimview.h" + +class LLConversationLogObserver; +struct Conversation_params; + +typedef LLIMModel::LLIMSession::SType SessionType; + +/* + * This class represents a particular session(conversation) of any type(im/voice/p2p/group/...) by storing some of session's data. + * Each LLConversation object has a corresponding visual representation in a form of LLConversationLogListItem. + */ +class LLConversation +{ +public: + + LLConversation(const Conversation_params& params); + LLConversation(const LLIMModel::LLIMSession& session); + LLConversation(const LLConversation& conversation); + + ~LLConversation(); + + const SessionType& getConversationType() const { return mConversationType; } + const std::string& getConversationName() const { return mConversationName; } + const std::string& getHistoryFileName() const { return mHistoryFileName; } + const LLUUID& getSessionID() const { return mSessionID; } + const LLUUID& getParticipantID() const { return mParticipantID; } + const std::string& getTimestamp() const { return mTimestamp; } + const time_t& getTime() const { return mTime; } + bool hasOfflineMessages() const { return mHasOfflineIMs; } + + void setConverstionName(std::string conv_name) { mConversationName = conv_name; } + + bool isOlderThan(U32 days) const; + + /* + * updates last interaction time + */ + void updateTimestamp(); + + /* + * Resets flag of unread offline message to false when im floater with this conversation is opened. + */ + void onIMFloaterShown(const LLUUID& session_id); + + /* + * returns string representation(in form of: mm/dd/yyyy hh:mm) of time when conversation was started + */ + static const std::string createTimestamp(const time_t& utc_time); + +private: + + /* + * If conversation has unread offline messages sets callback for opening LLIMFloater + * with this conversation. + */ + void setListenIMFloaterOpened(); + + boost::signals2::connection mIMFloaterShowedConnection; + + time_t mTime; // last interaction time + SessionType mConversationType; + std::string mConversationName; + std::string mHistoryFileName; + LLUUID mSessionID; + LLUUID mParticipantID; + bool mHasOfflineIMs; + std::string mTimestamp; // last interaction time in form of: mm/dd/yyyy hh:mm +}; + +/** + * LLConversationLog stores all agent's conversations. + * This class is responsible for creating and storing LLConversation objects when im or voice session starts. + * Also this class saves/retrieves conversations to/from file. + * + * Also please note that it may be several conversations with the same sessionID stored in the conversation log. + * To distinguish two conversations with the same sessionID it's also needed to compare their creation date. + */ + +class LLConversationLog : public LLSingleton<LLConversationLog>, LLIMSessionObserver +{ + friend class LLSingleton<LLConversationLog>; +public: + + void removeConversation(const LLConversation& conversation); + + /** + * Returns first conversation with matched session_id + */ + const LLConversation* getConversation(const LLUUID& session_id); + const std::vector<LLConversation>& getConversations() { return mConversations; } + + void addObserver(LLConversationLogObserver* observer); + void removeObserver(LLConversationLogObserver* observer); + + // LLIMSessionObserver triggers + virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + virtual void sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) {}; // Stub + virtual void sessionRemoved(const LLUUID& session_id){} // Stub + virtual void sessionVoiceOrIMStarted(const LLUUID& session_id){}; // Stub + virtual void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id){}; // Stub + + void notifyObservers(); + + void onNewMessageReceived(const LLSD& data); + + /** + * public method which is called on viewer exit to save conversation log + */ + void cache(); + +private: + + LLConversationLog(); + + void enableLogging(bool enable); + + /** + * adds conversation to the conversation list and notifies observers + */ + void logConversation(const LLUUID& session_id); + + void notifyPrticularConversationObservers(const LLUUID& session_id, U32 mask); + + /** + * constructs file name in which conversations log will be saved + * file name is conversation.log + */ + std::string getFileName(); + + bool saveToFile(const std::string& filename); + bool loadFromFile(const std::string& filename); + + void onAvatarNameCache(const LLUUID& participant_id, const LLAvatarName& av_name, const LLIMModel::LLIMSession* session); + + void createConversation(const LLIMModel::LLIMSession* session); + void updateConversationTimestamp(LLConversation* conversation); + void updateConversationName(const LLIMModel::LLIMSession* session, const std::string& name); + + LLConversation* findConversation(const LLIMModel::LLIMSession* session); + + typedef std::vector<LLConversation> conversations_vec_t; + std::vector<LLConversation> mConversations; + std::set<LLConversationLogObserver*> mObservers; + + LLFriendObserver* mFriendObserver; // Observer of the LLAvatarTracker instance + + boost::signals2::connection newMessageSignalConnection; +}; + +class LLConversationLogObserver +{ +public: + + enum EConversationChange + { + CHANGED_TIME = 1, // last interaction time changed + CHANGED_NAME = 2 // conversation name changed + }; + + virtual ~LLConversationLogObserver(){} + virtual void changed() = 0; + virtual void changed(const LLUUID& session_id, U32 mask){}; +}; + +#endif /* LLCONVERSATIONLOG_H_ */ diff --git a/indra/newview/llconversationloglist.cpp b/indra/newview/llconversationloglist.cpp new file mode 100644 index 0000000000..429e99f7ad --- /dev/null +++ b/indra/newview/llconversationloglist.cpp @@ -0,0 +1,484 @@ +/** + * @file llconversationloglist.cpp + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llavataractions.h" +#include "llagent.h" +#include "llfloaterreg.h" +#include "llfloaterconversationpreview.h" +#include "llgroupactions.h" +#include "llconversationloglist.h" +#include "llconversationloglistitem.h" +#include "llviewermenu.h" + +static LLDefaultChildRegistry::Register<LLConversationLogList> r("conversation_log_list"); + +static LLConversationLogListNameComparator NAME_COMPARATOR; +static LLConversationLogListDateComparator DATE_COMPARATOR; + +LLConversationLogList::LLConversationLogList(const Params& p) +: LLFlatListViewEx(p), + mIsDirty(true) +{ + LLConversationLog::instance().addObserver(this); + + // Set up context menu. + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar check_registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + + registrar.add ("Calllog.Action", boost::bind(&LLConversationLogList::onCustomAction, this, _2)); + check_registrar.add ("Calllog.Check", boost::bind(&LLConversationLogList::isActionChecked,this, _2)); + enable_registrar.add("Calllog.Enable", boost::bind(&LLConversationLogList::isActionEnabled,this, _2)); + + LLToggleableMenu* context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>( + "menu_conversation_log_gear.xml", + gMenuHolder, + LLViewerMenuHolderGL::child_registry_t::instance()); + if(context_menu) + { + mContextMenu = context_menu->getHandle(); + } + + mIsFriendsOnTop = gSavedSettings.getBOOL("SortFriendsFirst"); +} + +LLConversationLogList::~LLConversationLogList() +{ + if (mContextMenu.get()) + { + mContextMenu.get()->die(); + } + + LLConversationLog::instance().removeObserver(this); +} + +void LLConversationLogList::draw() +{ + if (mIsDirty) + { + refresh(); + } + LLFlatListViewEx::draw(); +} + +BOOL LLConversationLogList::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + BOOL handled = LLUICtrl::handleRightMouseDown(x, y, mask); + + LLToggleableMenu* context_menu = mContextMenu.get(); + { + context_menu->buildDrawLabels(); + if (context_menu && size()) + context_menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, context_menu, x, y); + } + + return handled; +} + +void LLConversationLogList::setNameFilter(const std::string& filter) +{ + std::string filter_upper = filter; + LLStringUtil::toUpper(filter_upper); + if (mNameFilter != filter_upper) + { + mNameFilter = filter_upper; + setDirty(); + } +} + +bool LLConversationLogList::findInsensitive(std::string haystack, const std::string& needle_upper) +{ + LLStringUtil::toUpper(haystack); + return haystack.find(needle_upper) != std::string::npos; +} + +void LLConversationLogList::sortByName() +{ + setComparator(&NAME_COMPARATOR); + sort(); +} + +void LLConversationLogList::sortByDate() +{ + setComparator(&DATE_COMPARATOR); + sort(); +} + +void LLConversationLogList::toggleSortFriendsOnTop() +{ + mIsFriendsOnTop = !mIsFriendsOnTop; + gSavedSettings.setBOOL("SortFriendsFirst", mIsFriendsOnTop); + sort(); +} + +void LLConversationLogList::changed() +{ + refresh(); +} + +void LLConversationLogList::changed(const LLUUID& session_id, U32 mask) +{ + LLConversationLogListItem* item = getConversationLogListItem(session_id); + + if (!item) + { + return; + } + + if (mask & LLConversationLogObserver::CHANGED_TIME) + { + item->updateTimestamp(); + + // if list is sorted by date and a date of some item has changed, + // than the whole list should be rebuilt + if (E_SORT_BY_DATE == getSortOrder()) + { + mIsDirty = true; + } + } + else if (mask & LLConversationLogObserver::CHANGED_NAME) + { + item->updateName(); + // if list is sorted by name and a name of some item has changed, + // than the whole list should be rebuilt + if (E_SORT_BY_DATE == getSortOrder()) + { + mIsDirty = true; + } + } +} + +void LLConversationLogList::addNewItem(const LLConversation* conversation) +{ + LLConversationLogListItem* item = new LLConversationLogListItem(&*conversation); + if (!mNameFilter.empty()) + { + item->highlightNameDate(mNameFilter); + } + addItem(item, conversation->getSessionID(), ADD_TOP); +} + +void LLConversationLogList::refresh() +{ + rebuildList(); + sort(); + + mIsDirty = false; +} + +void LLConversationLogList::rebuildList() +{ + clear(); + + bool have_filter = !mNameFilter.empty(); + + const std::vector<LLConversation>& conversations = LLConversationLog::instance().getConversations(); + std::vector<LLConversation>::const_iterator iter = conversations.begin(); + + for (; iter != conversations.end(); ++iter) + { + bool not_found = have_filter && !findInsensitive(iter->getConversationName(), mNameFilter) && !findInsensitive(iter->getTimestamp(), mNameFilter); + if (not_found) + continue; + + addNewItem(&*iter); + } +} + +void LLConversationLogList::onCustomAction(const LLSD& userdata) +{ + const std::string command_name = userdata.asString(); + const LLUUID& selected_id = getSelectedConversation()->getParticipantID(); + LLIMModel::LLIMSession::SType stype = getSelectedSessionType(); + + if ("im" == command_name) + { + switch (stype) + { + case LLIMModel::LLIMSession::P2P_SESSION: + LLAvatarActions::startIM(selected_id); + break; + + case LLIMModel::LLIMSession::GROUP_SESSION: + LLGroupActions::startIM(getSelectedConversation()->getSessionID()); + break; + + default: + break; + } + } + else if ("call" == command_name) + { + switch (stype) + { + case LLIMModel::LLIMSession::P2P_SESSION: + LLAvatarActions::startCall(selected_id); + break; + + case LLIMModel::LLIMSession::GROUP_SESSION: + LLGroupActions::startCall(getSelectedConversation()->getSessionID()); + break; + + default: + break; + } + } + else if ("view_profile" == command_name) + { + switch (stype) + { + case LLIMModel::LLIMSession::P2P_SESSION: + LLAvatarActions::showProfile(selected_id); + break; + + case LLIMModel::LLIMSession::GROUP_SESSION: + LLGroupActions::show(getSelectedConversation()->getSessionID()); + break; + + default: + break; + } + } + else if ("chat_history" == command_name) + { + const LLUUID& session_id = getSelectedConversation()->getSessionID(); + LLFloaterReg::showInstance("preview_conversation", session_id, true); + } + else if ("offer_teleport" == command_name) + { + LLAvatarActions::offerTeleport(selected_id); + } + else if("add_friend" == command_name) + { + if (!LLAvatarActions::isFriend(selected_id)) + { + LLAvatarActions::requestFriendshipDialog(selected_id); + } + } + else if("remove_friend" == command_name) + { + if (LLAvatarActions::isFriend(selected_id)) + { + LLAvatarActions::removeFriendDialog(selected_id); + } + } + else if ("invite_to_group" == command_name) + { + LLAvatarActions::inviteToGroup(selected_id); + } + else if ("show_on_map" == command_name) + { + LLAvatarActions::showOnMap(selected_id); + } + else if ("share" == command_name) + { + LLAvatarActions::share(selected_id); + } + else if ("pay" == command_name) + { + LLAvatarActions::pay(selected_id); + } + else if ("block" == command_name) + { + LLAvatarActions::toggleBlock(selected_id); + } +} + +bool LLConversationLogList::isActionEnabled(const LLSD& userdata) +{ + if (numSelected() != 1) + { + return false; + } + + const std::string command_name = userdata.asString(); + + LLIMModel::LLIMSession::SType stype = getSelectedSessionType(); + const LLUUID& selected_id = getSelectedConversation()->getParticipantID(); + + bool is_p2p = LLIMModel::LLIMSession::P2P_SESSION == stype; + bool is_group = LLIMModel::LLIMSession::GROUP_SESSION == stype; + + if ("can_im" == command_name || "can_view_profile" == command_name) + { + return is_p2p || is_group; + } + else if ("can_view_chat_history" == command_name) + { + return true; + } + else if ("can_call" == command_name) + { + return (is_p2p || is_group) && LLAvatarActions::canCall(); + } + else if ("add_rem_friend" == command_name || + "can_invite_to_group" == command_name || + "can_share" == command_name || + "can_block" == command_name || + "can_pay" == command_name) + { + return is_p2p; + } + else if("can_offer_teleport" == command_name) + { + return is_p2p && LLAvatarActions::canOfferTeleport(selected_id); + } + else if ("can_show_on_map") + { + return is_p2p && ((LLAvatarTracker::instance().isBuddyOnline(selected_id) && is_agent_mappable(selected_id)) || gAgent.isGodlike()); + } + + return false; +} + +bool LLConversationLogList::isActionChecked(const LLSD& userdata) +{ + const std::string command_name = userdata.asString(); + + const LLUUID& selected_id = getSelectedConversation()->getParticipantID(); + bool is_p2p = LLIMModel::LLIMSession::P2P_SESSION == getSelectedSessionType(); + + if ("is_blocked" == command_name) + { + return is_p2p && LLAvatarActions::isBlocked(selected_id); + } + else if ("is_friend" == command_name) + { + return is_p2p && LLAvatarActions::isFriend(selected_id); + } + else if ("is_not_friend" == command_name) + { + return is_p2p && !LLAvatarActions::isFriend(selected_id); + } + + return false; +} + +LLIMModel::LLIMSession::SType LLConversationLogList::getSelectedSessionType() +{ + const LLConversationLogListItem* item = getSelectedConversationPanel(); + + if (item) + { + return item->getConversation()->getConversationType(); + } + + return LLIMModel::LLIMSession::NONE_SESSION; +} + +const LLConversationLogListItem* LLConversationLogList::getSelectedConversationPanel() +{ + LLPanel* panel = LLFlatListViewEx::getSelectedItem(); + LLConversationLogListItem* conv_panel = dynamic_cast<LLConversationLogListItem*>(panel); + + return conv_panel; +} + +const LLConversation* LLConversationLogList::getSelectedConversation() +{ + const LLConversationLogListItem* panel = getSelectedConversationPanel(); + + if (panel) + { + return panel->getConversation(); + } + + return NULL; +} + +LLConversationLogListItem* LLConversationLogList::getConversationLogListItem(const LLUUID& session_id) +{ + std::vector<LLPanel*> panels; + LLFlatListViewEx::getItems(panels); + std::vector<LLPanel*>::iterator iter = panels.begin(); + + for (; iter != panels.end(); ++iter) + { + LLConversationLogListItem* item = dynamic_cast<LLConversationLogListItem*>(*iter); + if (item && session_id == item->getConversation()->getSessionID()) + { + return item; + } + } + + return NULL; +} + +LLConversationLogList::ESortOrder LLConversationLogList::getSortOrder() +{ + return static_cast<ESortOrder>(gSavedSettings.getU32("CallLogSortOrder")); +} + +bool LLConversationLogListItemComparator::compare(const LLPanel* item1, const LLPanel* item2) const +{ + const LLConversationLogListItem* conversation_item1 = dynamic_cast<const LLConversationLogListItem*>(item1); + const LLConversationLogListItem* conversation_item2 = dynamic_cast<const LLConversationLogListItem*>(item2); + + if (!conversation_item1 || !conversation_item2) + { + llerror("conversation_item1 and conversation_item2 cannot be null", 0); + return true; + } + + return doCompare(conversation_item1, conversation_item2); +} + +bool LLConversationLogListNameComparator::doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const +{ + std::string name1 = conversation1->getConversation()->getConversationName(); + std::string name2 = conversation2->getConversation()->getConversationName(); + const LLUUID& id1 = conversation1->getConversation()->getParticipantID(); + const LLUUID& id2 = conversation2->getConversation()->getParticipantID(); + + LLStringUtil::toUpper(name1); + LLStringUtil::toUpper(name2); + + bool friends_first = gSavedSettings.getBOOL("SortFriendsFirst"); + if (friends_first && (LLAvatarActions::isFriend(id1) ^ LLAvatarActions::isFriend(id2))) + { + return LLAvatarActions::isFriend(id1); + } + + return name1 < name2; +} + +bool LLConversationLogListDateComparator::doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const +{ + time_t date1 = conversation1->getConversation()->getTime(); + time_t date2 = conversation2->getConversation()->getTime(); + const LLUUID& id1 = conversation1->getConversation()->getParticipantID(); + const LLUUID& id2 = conversation2->getConversation()->getParticipantID(); + + bool friends_first = gSavedSettings.getBOOL("SortFriendsFirst"); + if (friends_first && (LLAvatarActions::isFriend(id1) ^ LLAvatarActions::isFriend(id2))) + { + return LLAvatarActions::isFriend(id1); + } + + return date1 > date2; +} diff --git a/indra/newview/llconversationloglist.h b/indra/newview/llconversationloglist.h new file mode 100644 index 0000000000..62ec57e09e --- /dev/null +++ b/indra/newview/llconversationloglist.h @@ -0,0 +1,153 @@ +/** + * @file llconversationloglist.h + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLCONVERSATIONLOGLIST_H_ +#define LLCONVERSATIONLOGLIST_H_ + +#include "llconversationlog.h" +#include "llflatlistview.h" +#include "lltoggleablemenu.h" + +class LLConversationLogListItem; + +/** + * List of all agent's conversations. I.e. history of conversations. + * This list represents contents of the LLConversationLog. + * Each change in LLConversationLog leads to rebuilding this list, so + * it's always in actual state. + */ + +class LLConversationLogList: public LLFlatListViewEx, public LLConversationLogObserver +{ + LOG_CLASS(LLConversationLogList); +public: + + typedef enum e_sort_oder{ + E_SORT_BY_NAME = 0, + E_SORT_BY_DATE = 1, + } ESortOrder; + + struct Params : public LLInitParam::Block<Params, LLFlatListViewEx::Params> + { + Params(){}; + }; + + LLConversationLogList(const Params& p); + virtual ~LLConversationLogList(); + + virtual void draw(); + + virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + + LLToggleableMenu* getContextMenu() const { return mContextMenu.get(); } + + void addNewItem(const LLConversation* conversation); + void setNameFilter(const std::string& filter); + void sortByName(); + void sortByDate(); + void toggleSortFriendsOnTop(); + bool getSortFriendsOnTop() const { return mIsFriendsOnTop; } + + /** + * Changes from LLConversationLogObserver + */ + virtual void changed(); + virtual void changed(const LLUUID& session_id, U32 mask); + +private: + + void setDirty(bool dirty = true) { mIsDirty = dirty; } + void refresh(); + + /** + * Clears list and re-adds items from LLConverstationLog + * If filter is not empty re-adds items which match the filter + */ + void rebuildList(); + + bool findInsensitive(std::string haystack, const std::string& needle_upper); + + void onCustomAction (const LLSD& userdata); + bool isActionEnabled(const LLSD& userdata); + bool isActionChecked(const LLSD& userdata); + + LLIMModel::LLIMSession::SType getSelectedSessionType(); + const LLConversationLogListItem* getSelectedConversationPanel(); + const LLConversation* getSelectedConversation(); + LLConversationLogListItem* getConversationLogListItem(const LLUUID& session_id); + + ESortOrder getSortOrder(); + + LLHandle<LLToggleableMenu> mContextMenu; + bool mIsDirty; + bool mIsFriendsOnTop; + std::string mNameFilter; +}; + +/** + * Abstract comparator for ConversationLogList items + */ +class LLConversationLogListItemComparator : public LLFlatListView::ItemComparator +{ + LOG_CLASS(LLConversationLogListItemComparator); + +public: + LLConversationLogListItemComparator() {}; + virtual ~LLConversationLogListItemComparator() {}; + + virtual bool compare(const LLPanel* item1, const LLPanel* item2) const; + +protected: + + virtual bool doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const = 0; +}; + +class LLConversationLogListNameComparator : public LLConversationLogListItemComparator +{ + LOG_CLASS(LLConversationLogListNameComparator); + +public: + LLConversationLogListNameComparator() {}; + virtual ~LLConversationLogListNameComparator() {}; + +protected: + + virtual bool doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const; +}; + +class LLConversationLogListDateComparator : public LLConversationLogListItemComparator +{ + LOG_CLASS(LLConversationLogListDateComparator); + +public: + LLConversationLogListDateComparator() {}; + virtual ~LLConversationLogListDateComparator() {}; + +protected: + + virtual bool doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const; +}; + +#endif /* LLCONVERSATIONLOGLIST_H_ */ diff --git a/indra/newview/llconversationloglistitem.cpp b/indra/newview/llconversationloglistitem.cpp new file mode 100644 index 0000000000..b4ae5f19da --- /dev/null +++ b/indra/newview/llconversationloglistitem.cpp @@ -0,0 +1,179 @@ +/** + * @file llconversationloglistitem.cpp + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +// llui +#include "lliconctrl.h" +#include "lltextbox.h" +#include "lltextutil.h" + +// newview +#include "llavataractions.h" +#include "llavatariconctrl.h" +#include "llconversationlog.h" +#include "llconversationloglistitem.h" +#include "llgroupactions.h" +#include "llgroupiconctrl.h" +#include "llinventoryicon.h" + +LLConversationLogListItem::LLConversationLogListItem(const LLConversation* conversation) +: LLPanel(), + mConversation(conversation), + mConversationName(NULL), + mConversationDate(NULL) +{ + buildFromFile("panel_conversation_log_list_item.xml"); + + LLIMFloater* floater = LLIMFloater::findInstance(mConversation->getSessionID()); + + bool ims_are_read = LLIMFloater::isVisible(floater) && floater->hasFocus(); + + if (mConversation->hasOfflineMessages() && !ims_are_read) + { + mIMFloaterShowedConnection = LLIMFloater::setIMFloaterShowedCallback(boost::bind(&LLConversationLogListItem::onIMFloaterShown, this, _1)); + } +} + +LLConversationLogListItem::~LLConversationLogListItem() +{ + mIMFloaterShowedConnection.disconnect(); +} + +BOOL LLConversationLogListItem::postBuild() +{ + initIcons(); + + // set conversation name + mConversationName = getChild<LLTextBox>("conversation_name"); + mConversationName->setValue(mConversation->getConversationName()); + + // set conversation date and time + mConversationDate = getChild<LLTextBox>("date_time"); + mConversationDate->setValue(mConversation->getTimestamp()); + + getChild<LLButton>("delete_btn")->setClickedCallback(boost::bind(&LLConversationLogListItem::onRemoveBtnClicked, this)); + setDoubleClickCallback(boost::bind(&LLConversationLogListItem::onDoubleClick, this)); + + return TRUE; +} + +void LLConversationLogListItem::initIcons() +{ + switch (mConversation->getConversationType()) + { + case LLIMModel::LLIMSession::P2P_SESSION: + case LLIMModel::LLIMSession::ADHOC_SESSION: + { + LLAvatarIconCtrl* avatar_icon = getChild<LLAvatarIconCtrl>("avatar_icon"); + avatar_icon->setVisible(TRUE); + avatar_icon->setValue(mConversation->getParticipantID()); + break; + } + case LLIMModel::LLIMSession::GROUP_SESSION: + { + LLGroupIconCtrl* group_icon = getChild<LLGroupIconCtrl>("group_icon"); + group_icon->setVisible(TRUE); + group_icon->setValue(mConversation->getSessionID()); + break; + } + default: + break; + } + + if (mConversation->hasOfflineMessages()) + { + getChild<LLIconCtrl>("unread_ims_icon")->setVisible(TRUE); + } +} + +void LLConversationLogListItem::updateTimestamp() +{ + mConversationDate->setValue(mConversation->getTimestamp()); +} + +void LLConversationLogListItem::updateName() +{ + mConversationName->setValue(mConversation->getConversationName()); +} + +void LLConversationLogListItem::onMouseEnter(S32 x, S32 y, MASK mask) +{ + getChildView("hovered_icon")->setVisible(true); + LLPanel::onMouseEnter(x, y, mask); +} + +void LLConversationLogListItem::onMouseLeave(S32 x, S32 y, MASK mask) +{ + getChildView("hovered_icon")->setVisible(false); + LLPanel::onMouseLeave(x, y, mask); +} + +void LLConversationLogListItem::setValue(const LLSD& value) +{ + if (!value.isMap() || !value.has("selected")) + { + return; + } + + getChildView("selected_icon")->setVisible(value["selected"]); +} + +void LLConversationLogListItem::onIMFloaterShown(const LLUUID& session_id) +{ + if (mConversation->getSessionID() == session_id) + { + getChild<LLIconCtrl>("unread_ims_icon")->setVisible(FALSE); + } +} + +void LLConversationLogListItem::onRemoveBtnClicked() +{ + LLConversationLog::instance().removeConversation(*mConversation); +} + +void LLConversationLogListItem::highlightNameDate(const std::string& highlited_text) +{ + LLStyle::Params params; + LLTextUtil::textboxSetHighlightedVal(mConversationName, params, mConversation->getConversationName(), highlited_text); + LLTextUtil::textboxSetHighlightedVal(mConversationDate, params, mConversation->getTimestamp(), highlited_text); +} + +void LLConversationLogListItem::onDoubleClick() +{ + switch (mConversation->getConversationType()) + { + case LLIMModel::LLIMSession::P2P_SESSION: + LLAvatarActions::startIM(mConversation->getParticipantID()); + break; + + case LLIMModel::LLIMSession::GROUP_SESSION: + LLGroupActions::startIM(mConversation->getSessionID()); + break; + + default: + break; + } +} diff --git a/indra/newview/llconversationloglistitem.h b/indra/newview/llconversationloglistitem.h new file mode 100644 index 0000000000..1bf7a0ed93 --- /dev/null +++ b/indra/newview/llconversationloglistitem.h @@ -0,0 +1,85 @@ +/** + * @file llconversationloglistitem.h + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLCONVERSATIONLOGLISTITEM_H_ +#define LLCONVERSATIONLOGLISTITEM_H_ + +#include "llimfloater.h" +#include "llpanel.h" + +class LLTextBox; +class LLConversation; + +/** + * This class is a visual representation of LLConversation, each of which is LLConversationLog entry. + * LLConversationLogList consists of these LLConversationLogListItems. + * LLConversationLogListItem consists of: + * conversaion_type_icon + * conversaion_name + * conversaion_date + * Also LLConversationLogListItem holds pointer to its LLConversationLog. + */ + +class LLConversationLogListItem : public LLPanel +{ +public: + LLConversationLogListItem(const LLConversation* conversation); + virtual ~LLConversationLogListItem(); + + void onMouseEnter(S32 x, S32 y, MASK mask); + void onMouseLeave(S32 x, S32 y, MASK mask); + + virtual void setValue(const LLSD& value); + + virtual BOOL postBuild(); + + void onIMFloaterShown(const LLUUID& session_id); + void onRemoveBtnClicked(); + + const LLConversation* getConversation() const { return mConversation; } + + void highlightNameDate(const std::string& highlited_text); + + void onDoubleClick(); + + /** + * updates string value of last interaction time from conversation + */ + void updateTimestamp(); + void updateName(); + +private: + + void initIcons(); + + const LLConversation* mConversation; + + LLTextBox* mConversationName; + LLTextBox* mConversationDate; + + boost::signals2::connection mIMFloaterShowedConnection; +}; + +#endif /* LLCONVERSATIONLOGITEM_H_ */ diff --git a/indra/newview/llconversationmodel.cpp b/indra/newview/llconversationmodel.cpp new file mode 100644 index 0000000000..47a82bfe17 --- /dev/null +++ b/indra/newview/llconversationmodel.cpp @@ -0,0 +1,559 @@ +/** + * @file llconversationmodel.cpp + * @brief Implementation of conversations list + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +#include "llviewerprecompiledheaders.h" + +#include "llagent.h" +#include "llavatarnamecache.h" +#include "llavataractions.h" +#include "llevents.h" +#include "llsdutil.h" +#include "llconversationmodel.h" +#include "llimview.h" //For LLIMModel + +// +// Conversation items : common behaviors +// + +LLConversationItem::LLConversationItem(std::string display_name, const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) : + LLFolderViewModelItemCommon(root_view_model), + mName(display_name), + mUUID(uuid), + mNeedsRefresh(true), + mConvType(CONV_UNKNOWN), + mLastActiveTime(0.0) +{ +} + +LLConversationItem::LLConversationItem(const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) : + LLFolderViewModelItemCommon(root_view_model), + mName(""), + mUUID(uuid), + mNeedsRefresh(true), + mConvType(CONV_UNKNOWN), + mLastActiveTime(0.0) +{ +} + +LLConversationItem::LLConversationItem(LLFolderViewModelInterface& root_view_model) : + LLFolderViewModelItemCommon(root_view_model), + mName(""), + mUUID(), + mNeedsRefresh(true), + mConvType(CONV_UNKNOWN), + mLastActiveTime(0.0) +{ +} + +void LLConversationItem::postEvent(const std::string& event_type, LLConversationItemSession* session, LLConversationItemParticipant* participant) +{ + LLUUID session_id = (session ? session->getUUID() : LLUUID()); + LLUUID participant_id = (participant ? participant->getUUID() : LLUUID()); + LLSD event(LLSDMap("type", event_type)("session_uuid", session_id)("participant_uuid", participant_id)); + LLEventPumps::instance().obtain("ConversationsEvents").post(event); +} + +// Virtual action callbacks +void LLConversationItem::performAction(LLInventoryModel* model, std::string action) +{ +} + +void LLConversationItem::openItem( void ) +{ +} + +void LLConversationItem::closeItem( void ) +{ +} + +void LLConversationItem::previewItem( void ) +{ +} + +void LLConversationItem::showProperties(void) +{ +} + +void LLConversationItem::buildParticipantMenuOptions(menuentry_vec_t& items) +{ + items.push_back(std::string("view_profile")); + items.push_back(std::string("im")); + items.push_back(std::string("offer_teleport")); + items.push_back(std::string("voice_call")); + items.push_back(std::string("chat_history")); + items.push_back(std::string("separator_chat_history")); + items.push_back(std::string("add_friend")); + items.push_back(std::string("remove_friend")); + items.push_back(std::string("invite_to_group")); + items.push_back(std::string("separator_invite_to_group")); + items.push_back(std::string("map")); + items.push_back(std::string("share")); + items.push_back(std::string("pay")); + items.push_back(std::string("block_unblock")); + + if(this->getType() != CONV_SESSION_1_ON_1) + { + items.push_back(std::string("Moderator Options Separator")); + items.push_back(std::string("Moderator Options")); + items.push_back(std::string("AllowTextChat")); + items.push_back(std::string("moderate_voice_separator")); + items.push_back(std::string("ModerateVoiceMuteSelected")); + items.push_back(std::string("ModerateVoiceUnMuteSelected")); + items.push_back(std::string("ModerateVoiceMute")); + items.push_back(std::string("ModerateVoiceUnmute")); + } + +} + +// +// LLConversationItemSession +// + +LLConversationItemSession::LLConversationItemSession(std::string display_name, const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) : + LLConversationItem(display_name,uuid,root_view_model), + mIsLoaded(false) +{ + mConvType = CONV_SESSION_UNKNOWN; +} + +LLConversationItemSession::LLConversationItemSession(const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) : + LLConversationItem(uuid,root_view_model) +{ + mConvType = CONV_SESSION_UNKNOWN; +} + +bool LLConversationItemSession::hasChildren() const +{ + return getChildrenCount() > 0; +} + +void LLConversationItemSession::addParticipant(LLConversationItemParticipant* participant) +{ + addChild(participant); + mIsLoaded = true; + mNeedsRefresh = true; + updateParticipantName(participant); + postEvent("add_participant", this, participant); +} + +void LLConversationItemSession::updateParticipantName(LLConversationItemParticipant* participant) +{ + // We modify the session name only in the case of an ad-hoc session, exit otherwise (nothing to do) + if (getType() != CONV_SESSION_AD_HOC) + { + return; + } + // Avoid changing the default name if no participant present yet + if (mChildren.size() == 0) + { + return; + } + // Build a string containing the participants names and check if ready for display (we don't want "(waiting)" in there) + bool all_names_resolved = true; + uuid_vec_t temp_uuids; // uuids vector for building the added participants' names string + child_list_t::iterator iter = mChildren.begin(); + while (iter != mChildren.end()) + { + LLConversationItemParticipant* current_participant = dynamic_cast<LLConversationItemParticipant*>(*iter); + temp_uuids.push_back(current_participant->getUUID()); + LLAvatarName av_name; + if (!LLAvatarNameCache::get(current_participant->getUUID(), &av_name)) + { + // If the name is not in the cache yet, bail out + // Note: we don't bind ourselves to the LLAvatarNameCache event as we are called by + // onAvatarNameCache() which is itself attached to the same event. + all_names_resolved = false; + break; + } + iter++; + } + if (all_names_resolved) + { + std::string new_session_name; + LLAvatarActions::buildResidentsString(temp_uuids, new_session_name); + renameItem(new_session_name); + postEvent("update_session", this, NULL); + } +} + +void LLConversationItemSession::removeParticipant(LLConversationItemParticipant* participant) +{ + removeChild(participant); + mNeedsRefresh = true; + postEvent("remove_participant", this, participant); +} + +void LLConversationItemSession::removeParticipant(const LLUUID& participant_id) +{ + LLConversationItemParticipant* participant = findParticipant(participant_id); + if (participant) + { + removeParticipant(participant); + } +} + +void LLConversationItemSession::clearParticipants() +{ + clearChildren(); + mIsLoaded = false; + mNeedsRefresh = true; +} + +LLConversationItemParticipant* LLConversationItemSession::findParticipant(const LLUUID& participant_id) +{ + // This is *not* a general tree parsing algorithm. It assumes that a session contains only + // items (LLConversationItemParticipant) that have themselve no children. + LLConversationItemParticipant* participant = NULL; + child_list_t::iterator iter; + for (iter = mChildren.begin(); iter != mChildren.end(); iter++) + { + participant = dynamic_cast<LLConversationItemParticipant*>(*iter); + if (participant->hasSameValue(participant_id)) + { + break; + } + } + return (iter == mChildren.end() ? NULL : participant); +} + +void LLConversationItemSession::setParticipantIsMuted(const LLUUID& participant_id, bool is_muted) +{ + LLConversationItemParticipant* participant = findParticipant(participant_id); + if (participant) + { + participant->setIsMuted(is_muted); + } +} + +void LLConversationItemSession::setParticipantIsModerator(const LLUUID& participant_id, bool is_moderator) +{ + LLConversationItemParticipant* participant = findParticipant(participant_id); + if (participant) + { + participant->setIsModerator(is_moderator); + } +} + +void LLConversationItemSession::setTimeNow(const LLUUID& participant_id) +{ + mLastActiveTime = LLFrameTimer::getElapsedSeconds(); + mNeedsRefresh = true; + LLConversationItemParticipant* participant = findParticipant(participant_id); + if (participant) + { + participant->setTimeNow(); + } +} + +void LLConversationItemSession::setDistance(const LLUUID& participant_id, F64 dist) +{ + LLConversationItemParticipant* participant = findParticipant(participant_id); + if (participant) + { + participant->setDistance(dist); + mNeedsRefresh = true; + } +} + +void LLConversationItemSession::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + lldebugs << "LLConversationItemParticipant::buildContextMenu()" << llendl; + menuentry_vec_t items; + menuentry_vec_t disabled_items; + + if(this->getType() == CONV_SESSION_1_ON_1) + { + items.push_back(std::string("close_conversation")); + items.push_back(std::string("separator_disconnect_from_voice")); + buildParticipantMenuOptions(items); + } + else if(this->getType() == CONV_SESSION_GROUP) + { + items.push_back(std::string("close_conversation")); + addVoiceOptions(items); + items.push_back(std::string("chat_history")); + items.push_back(std::string("separator_chat_history")); + items.push_back(std::string("group_profile")); + items.push_back(std::string("activate_group")); + items.push_back(std::string("leave_group")); + } + else if(this->getType() == CONV_SESSION_AD_HOC) + { + items.push_back(std::string("close_conversation")); + addVoiceOptions(items); + items.push_back(std::string("chat_history")); + } + + hide_context_entries(menu, items, disabled_items); +} + +void LLConversationItemSession::addVoiceOptions(menuentry_vec_t& items) +{ + LLVoiceChannel* voice_channel = LLIMModel::getInstance() ? LLIMModel::getInstance()->getVoiceChannel(this->getUUID()) : NULL; + + if(voice_channel != LLVoiceChannel::getCurrentVoiceChannel()) + { + items.push_back(std::string("open_voice_conversation")); + } + else + { + items.push_back(std::string("disconnect_from_voice")); + } +} + +// The time of activity of a session is the time of the most recent activity, session and participants included +const bool LLConversationItemSession::getTime(F64& time) const +{ + F64 most_recent_time = mLastActiveTime; + bool has_time = (most_recent_time > 0.1); + LLConversationItemParticipant* participant = NULL; + child_list_t::const_iterator iter; + for (iter = mChildren.begin(); iter != mChildren.end(); iter++) + { + participant = dynamic_cast<LLConversationItemParticipant*>(*iter); + F64 participant_time; + if (participant->getTime(participant_time)) + { + has_time = true; + most_recent_time = llmax(most_recent_time,participant_time); + } + } + if (has_time) + { + time = most_recent_time; + } + return has_time; +} + +void LLConversationItemSession::dumpDebugData() +{ + llinfos << "Merov debug : session " << this << ", uuid = " << mUUID << ", name = " << mName << ", is loaded = " << mIsLoaded << llendl; + LLConversationItemParticipant* participant = NULL; + child_list_t::iterator iter; + for (iter = mChildren.begin(); iter != mChildren.end(); iter++) + { + participant = dynamic_cast<LLConversationItemParticipant*>(*iter); + participant->dumpDebugData(); + } +} + +// +// LLConversationItemParticipant +// + +LLConversationItemParticipant::LLConversationItemParticipant(std::string display_name, const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) : + LLConversationItem(display_name,uuid,root_view_model), + mIsMuted(false), + mIsModerator(false), + mDistToAgent(-1.0), + mAvatarNameCacheConnection() +{ + mConvType = CONV_PARTICIPANT; +} + +LLConversationItemParticipant::LLConversationItemParticipant(const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) : + LLConversationItem(uuid,root_view_model), + mIsMuted(false), + mIsModerator(false), + mDistToAgent(-1.0), + mAvatarNameCacheConnection() +{ + mConvType = CONV_PARTICIPANT; +} + +LLConversationItemParticipant::~LLConversationItemParticipant() +{ + // Disconnect any previous avatar name cache connection to ensure + // that the callback method is not called after destruction + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } +} + +void LLConversationItemParticipant::fetchAvatarName() +{ + // Disconnect any previous avatar name cache connection + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + + // Request the avatar name from the cache + llassert(getUUID().notNull()); + if (getUUID().notNull()) + { + mAvatarNameCacheConnection = LLAvatarNameCache::get(getUUID(), boost::bind(&LLConversationItemParticipant::onAvatarNameCache, this, _2)); + } +} + +void LLConversationItemParticipant::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + menuentry_vec_t items; + menuentry_vec_t disabled_items; + + if(gAgent.getID() != mUUID) + { + buildParticipantMenuOptions(items); + } + hide_context_entries(menu, items, disabled_items); +} + +void LLConversationItemParticipant::onAvatarNameCache(const LLAvatarName& av_name) +{ + mName = (av_name.mUsername.empty() ? av_name.mDisplayName : av_name.mUsername); + mDisplayName = (av_name.mDisplayName.empty() ? av_name.mUsername : av_name.mDisplayName); + mNeedsRefresh = true; + if(mParent != NULL) + { + LLConversationItemSession* parent_session = dynamic_cast<LLConversationItemSession*>(mParent); + if (parent_session != NULL) + { + parent_session->requestSort(); + parent_session->updateParticipantName(this); + postEvent("update_participant", parent_session, this); + } + } +} + +void LLConversationItemParticipant::dumpDebugData() +{ + llinfos << "Merov debug : participant, uuid = " << mUUID << ", name = " << mName << ", display name = " << mDisplayName << ", muted = " << mIsMuted << ", moderator = " << mIsModerator << llendl; +} + +// +// LLConversationSort +// + +// Comparison operator: returns "true" is a comes before b, "false" otherwise +bool LLConversationSort::operator()(const LLConversationItem* const& a, const LLConversationItem* const& b) const +{ + LLConversationItem::EConversationType type_a = a->getType(); + LLConversationItem::EConversationType type_b = b->getType(); + + if ((type_a == LLConversationItem::CONV_PARTICIPANT) && (type_b == LLConversationItem::CONV_PARTICIPANT)) + { + // If both items are participants + U32 sort_order = getSortOrderParticipants(); + if (sort_order == LLConversationFilter::SO_DATE) + { + F64 time_a = 0.0; + F64 time_b = 0.0; + bool has_time_a = a->getTime(time_a); + bool has_time_b = b->getTime(time_b); + if (has_time_a && has_time_b) + { + // Most recent comes first + return (time_a > time_b); + } + else if (has_time_a || has_time_b) + { + // If we have only one time available, the element with time must come first + return has_time_a; + } + // If no time available, we'll default to sort by name at the end of this method + } + else if (sort_order == LLConversationFilter::SO_DISTANCE) + { + F64 dist_a = 0.0; + F64 dist_b = 0.0; + bool has_dist_a = a->getDistanceToAgent(dist_a); + bool has_dist_b = b->getDistanceToAgent(dist_b); + if (has_dist_a && has_dist_b) + { + // Closest comes first + return (dist_a < dist_b); + } + else if (has_dist_a || has_dist_b) + { + // If we have only one distance available, the element with it must come first + return has_dist_a; + } + // If no distance available, we'll default to sort by name at the end of this method + } + } + else if ((type_a > LLConversationItem::CONV_PARTICIPANT) && (type_b > LLConversationItem::CONV_PARTICIPANT)) + { + // If both are sessions + U32 sort_order = getSortOrderSessions(); + if ((type_a == LLConversationItem::CONV_SESSION_NEARBY) || (type_b == LLConversationItem::CONV_SESSION_NEARBY)) + { + // If one is the nearby session, put nearby session *always* first + return (type_a == LLConversationItem::CONV_SESSION_NEARBY); + } + else if (sort_order == LLConversationFilter::SO_DATE) + { + // Sort by time + F64 time_a = 0.0; + F64 time_b = 0.0; + bool has_time_a = a->getTime(time_a); + bool has_time_b = b->getTime(time_b); + if (has_time_a && has_time_b) + { + // Most recent comes first + return (time_a > time_b); + } + else if (has_time_a || has_time_b) + { + // If we have only one time available, the element with time must come first + return has_time_a; + } + // If no time available, we'll default to sort by name at the end of this method + } + else if (sort_order == LLConversationFilter::SO_SESSION_TYPE) + { + if (type_a != type_b) + { + // Lowest types come first. See LLConversationItem definition of types + return (type_a < type_b); + } + // If types are identical, we'll default to sort by name at the end of this method + } + } + else + { + // If one item is a participant and the other a session, the session comes before the participant + // so we simply compare the type + // Notes: as a consequence, CONV_UNKNOWN (which should never get created...) always come first + return (type_a > type_b); + } + // By default, in all other possible cases (including sort order type LLConversationFilter::SO_NAME of course), + // we sort by name + S32 compare = LLStringUtil::compareDict(a->getName(), b->getName()); + return (compare < 0); +} + +// +// LLConversationViewModel +// + +void LLConversationViewModel::sort(LLFolderViewFolder* folder) +{ + base_t::sort(folder); +} + +// EOF diff --git a/indra/newview/llconversationmodel.h b/indra/newview/llconversationmodel.h new file mode 100755 index 0000000000..481d46af58 --- /dev/null +++ b/indra/newview/llconversationmodel.h @@ -0,0 +1,303 @@ +/** + * @file llconversationmodel.h + * @brief Implementation of conversations list + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLCONVERSATIONMODEL_H +#define LL_LLCONVERSATIONMODEL_H + +#include <boost/signals2.hpp> + +#include "llavatarname.h" +#include "llfolderviewitem.h" +#include "llfolderviewmodel.h" +#include "llviewerfoldertype.h" + +// Implementation of conversations list + +class LLConversationItem; +class LLConversationItemSession; +class LLConversationItemParticipant; + +typedef std::map<LLUUID, LLConversationItem*> conversations_items_map; +typedef std::map<LLUUID, LLFolderViewItem*> conversations_widgets_map; + +typedef std::vector<std::string> menuentry_vec_t; + +// Conversation items: we hold a list of those and create an LLFolderViewItem widget for each +// that we tuck into the mConversationsListPanel. +class LLConversationItem : public LLFolderViewModelItemCommon +{ +public: + enum EConversationType + { + CONV_UNKNOWN = 0, + CONV_PARTICIPANT = 1, + CONV_SESSION_NEARBY = 2, // The order counts here as it is used to sort sessions by type + CONV_SESSION_1_ON_1 = 3, + CONV_SESSION_AD_HOC = 4, + CONV_SESSION_GROUP = 5, + CONV_SESSION_UNKNOWN = 6 + }; + + LLConversationItem(std::string display_name, const LLUUID& uuid, LLFolderViewModelInterface& root_view_model); + LLConversationItem(const LLUUID& uuid, LLFolderViewModelInterface& root_view_model); + LLConversationItem(LLFolderViewModelInterface& root_view_model); + virtual ~LLConversationItem() {} + + // Stub those things we won't really be using in this conversation context + virtual const std::string& getName() const { return mName; } + virtual const std::string& getDisplayName() const { return mName; } + virtual const std::string& getSearchableName() const { return mName; } + virtual const LLUUID& getUUID() const { return mUUID; } + virtual time_t getCreationDate() const { return 0; } + virtual LLPointer<LLUIImage> getIcon() const { return NULL; } + virtual LLPointer<LLUIImage> getOpenIcon() const { return getIcon(); } + virtual LLFontGL::StyleFlags getLabelStyle() const { return LLFontGL::NORMAL; } + virtual std::string getLabelSuffix() const { return LLStringUtil::null; } + virtual BOOL isItemRenameable() const { return TRUE; } + virtual BOOL renameItem(const std::string& new_name) { mName = new_name; mNeedsRefresh = true; return TRUE; } + virtual BOOL isItemMovable( void ) const { return FALSE; } + virtual BOOL isItemRemovable( void ) const { return FALSE; } + virtual BOOL isItemInTrash( void) const { return FALSE; } + virtual BOOL removeItem() { return FALSE; } + virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch) { } + virtual void move( LLFolderViewModelItem* parent_listener ) { } + virtual BOOL isItemCopyable() const { return FALSE; } + virtual BOOL copyToClipboard() const { return FALSE; } + virtual BOOL cutToClipboard() const { return FALSE; } + virtual BOOL isClipboardPasteable() const { return FALSE; } + virtual void pasteFromClipboard() { } + virtual void pasteLinkFromClipboard() { } + virtual void buildContextMenu(LLMenuGL& menu, U32 flags) { } + virtual BOOL isUpToDate() const { return TRUE; } + virtual bool hasChildren() const { return FALSE; } + + virtual bool potentiallyVisible() { return true; } + virtual bool filter( LLFolderViewFilter& filter) { return false; } + virtual bool descendantsPassedFilter(S32 filter_generation = -1) { return true; } + virtual void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0) { } + virtual bool passedFilter(S32 filter_generation = -1) { return true; } + + // The action callbacks + virtual void performAction(LLInventoryModel* model, std::string action); + virtual void openItem( void ); + virtual void closeItem( void ); + virtual void previewItem( void ); + virtual void selectItem(void) { } + virtual void showProperties(void); + + // Methods used in sorting (see LLConversationSort::operator()) + EConversationType const getType() const { return mConvType; } + virtual const bool getTime(F64& time) const { time = mLastActiveTime; return (time > 0.1); } + virtual const bool getDistanceToAgent(F64& distance) const { return false; } + + // This method will be called to determine if a drop can be + // performed, and will set drop to TRUE if a drop is + // requested. + // Returns TRUE if a drop is possible/happened, FALSE otherwise. + virtual BOOL dragOrDrop(MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + std::string& tooltip_msg) { return FALSE; } + +// bool hasSameValues(std::string name, const LLUUID& uuid) { return ((name == mName) && (uuid == mUUID)); } + bool hasSameValue(const LLUUID& uuid) { return (uuid == mUUID); } + + void resetRefresh() { mNeedsRefresh = false; } + bool needsRefresh() { return mNeedsRefresh; } + + void postEvent(const std::string& event_type, LLConversationItemSession* session, LLConversationItemParticipant* participant); + + void buildParticipantMenuOptions(menuentry_vec_t& items); + +protected: + std::string mName; // Name of the session or the participant + LLUUID mUUID; // UUID of the session or the participant + EConversationType mConvType; // Type of conversation item + bool mNeedsRefresh; // Flag signaling to the view that something changed for this item + F64 mLastActiveTime; +}; + +class LLConversationItemSession : public LLConversationItem +{ +public: + LLConversationItemSession(std::string display_name, const LLUUID& uuid, LLFolderViewModelInterface& root_view_model); + LLConversationItemSession(const LLUUID& uuid, LLFolderViewModelInterface& root_view_model); + virtual ~LLConversationItemSession() {} + + /*virtual*/ bool hasChildren() const; + LLPointer<LLUIImage> getIcon() const { return NULL; } + void setSessionID(const LLUUID& session_id) { mUUID = session_id; mNeedsRefresh = true; } + void addParticipant(LLConversationItemParticipant* participant); + void updateParticipantName(LLConversationItemParticipant* participant); + void removeParticipant(LLConversationItemParticipant* participant); + void removeParticipant(const LLUUID& participant_id); + void clearParticipants(); + LLConversationItemParticipant* findParticipant(const LLUUID& participant_id); + + void setParticipantIsMuted(const LLUUID& participant_id, bool is_muted); + void setParticipantIsModerator(const LLUUID& participant_id, bool is_moderator); + void setTimeNow(const LLUUID& participant_id); + void setDistance(const LLUUID& participant_id, F64 dist); + + bool isLoaded() { return mIsLoaded; } + + void buildContextMenu(LLMenuGL& menu, U32 flags); + void addVoiceOptions(menuentry_vec_t& items); + virtual const bool getTime(F64& time) const; + + void dumpDebugData(); + +private: + bool mIsLoaded; // true if at least one participant has been added to the session, false otherwise +}; + +class LLConversationItemParticipant : public LLConversationItem +{ +public: + LLConversationItemParticipant(std::string display_name, const LLUUID& uuid, LLFolderViewModelInterface& root_view_model); + LLConversationItemParticipant(const LLUUID& uuid, LLFolderViewModelInterface& root_view_model); + virtual ~LLConversationItemParticipant(); + + virtual const std::string& getDisplayName() const { return mDisplayName; } + + bool isMuted() { return mIsMuted; } + bool isModerator() {return mIsModerator; } + void setIsMuted(bool is_muted) { mIsMuted = is_muted; mNeedsRefresh = true; } + void setIsModerator(bool is_moderator) { mIsModerator = is_moderator; mNeedsRefresh = true; } + void setTimeNow() { mLastActiveTime = LLFrameTimer::getElapsedSeconds(); mNeedsRefresh = true; } + void setDistance(F64 dist) { mDistToAgent = dist; mNeedsRefresh = true; } + + void buildContextMenu(LLMenuGL& menu, U32 flags); + + virtual const bool getDistanceToAgent(F64& dist) const { dist = mDistToAgent; return (dist >= 0.0); } + + void fetchAvatarName(); + + void dumpDebugData(); + +private: + void onAvatarNameCache(const LLAvatarName& av_name); + + bool mIsMuted; // default is false + bool mIsModerator; // default is false + std::string mDisplayName; + F64 mDistToAgent; // Distance to the agent. A negative (meaningless) value means the distance has not been set. + boost::signals2::connection mAvatarNameCacheConnection; +}; + +// We don't want to ever filter conversations but we need to declare that class to create a conversation view model. +// We just stubb everything for the moment. +class LLConversationFilter : public LLFolderViewFilter +{ +public: + + enum ESortOrderType + { + SO_NAME = 0, // Sort by name + SO_DATE = 0x1, // Sort by date (most recent) + SO_SESSION_TYPE = 0x2, // Sort by type (valid only for sessions) + SO_DISTANCE = 0x3, // Sort by distance (valid only for participants in nearby chat) + }; + // Default sort order is by type for sessions and by date for participants + static const U32 SO_DEFAULT = (SO_SESSION_TYPE << 16) | (SO_DATE); + + LLConversationFilter() { mEmpty = ""; } + ~LLConversationFilter() {} + + bool check(const LLFolderViewModelItem* item) { return true; } + bool checkFolder(const LLFolderViewModelItem* folder) const { return true; } + void setEmptyLookupMessage(const std::string& message) { } + std::string getEmptyLookupMessage() const { return mEmpty; } + bool showAllResults() const { return true; } + std::string::size_type getStringMatchOffset(LLFolderViewModelItem* item) const { return std::string::npos; } + std::string::size_type getFilterStringSize() const { return 0; } + + bool isActive() const { return false; } + bool isModified() const { return false; } + void clearModified() { } + const std::string& getName() const { return mEmpty; } + const std::string& getFilterText() { return mEmpty; } + void setModified(EFilterModified behavior = FILTER_RESTART) { } + + void setFilterCount(S32 count) { } + S32 getFilterCount() const { return 0; } + void decrementFilterCount() { } + + bool isDefault() const { return true; } + bool isNotDefault() const { return false; } + void markDefault() { } + void resetDefault() { } + + S32 getCurrentGeneration() const { return 0; } + S32 getFirstSuccessGeneration() const { return 0; } + S32 getFirstRequiredGeneration() const { return 0; } +private: + std::string mEmpty; +}; + +class LLConversationSort +{ +public: + LLConversationSort(U32 order = LLConversationFilter::SO_DEFAULT) : mSortOrder(order) { } + + // 16 LSB bits used for participants, 16 MSB bits for sessions + U32 getSortOrderSessions() const { return ((mSortOrder >> 16) & 0xFFFF); } + U32 getSortOrderParticipants() const { return (mSortOrder & 0xFFFF); } + void setSortOrderSessions(LLConversationFilter::ESortOrderType session) { mSortOrder = ((session & 0xFFFF) << 16) | (mSortOrder & 0xFFFF); } + void setSortOrderParticipants(LLConversationFilter::ESortOrderType participant) { mSortOrder = (mSortOrder & 0xFFFF0000) | (participant & 0xFFFF); } + + bool operator()(const LLConversationItem* const& a, const LLConversationItem* const& b) const; + operator U32() const { return mSortOrder; } +private: + // Note: we're treating this value as a sort order bitmask as done in other places in the code (e.g. inventory) + U32 mSortOrder; +}; + +class LLConversationViewModel +: public LLFolderViewModel<LLConversationSort, LLConversationItem, LLConversationItem, LLConversationFilter> +{ +public: + typedef LLFolderViewModel<LLConversationSort, LLConversationItem, LLConversationItem, LLConversationFilter> base_t; + + void sort(LLFolderViewFolder* folder); + bool contentsReady() { return true; } // *TODO : we need to check that participants names are available somewhat + bool startDrag(std::vector<LLFolderViewModelItem*>& items) { return false; } // We do not allow drag of conversation items + +private: +}; + +// Utility function to hide all entries except those in the list +// Can be called multiple times on the same menu (e.g. if multiple items +// are selected). If "append" is false, then only common enabled items +// are set as enabled. + +//(defined in inventorybridge.cpp) +//TODO: Gilbert Linden - Refactor to make this function non-global +void hide_context_entries(LLMenuGL& menu, + const menuentry_vec_t &entries_to_show, + const menuentry_vec_t &disabled_entries); + +#endif // LL_LLCONVERSATIONMODEL_H diff --git a/indra/newview/llconversationview.cpp b/indra/newview/llconversationview.cpp new file mode 100755 index 0000000000..de0c65e24f --- /dev/null +++ b/indra/newview/llconversationview.cpp @@ -0,0 +1,584 @@ +/** + * @file llconversationview.cpp + * @brief Implementation of conversations list widgets and views + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +#include "llviewerprecompiledheaders.h" + +#include "llconversationview.h" + +#include <boost/bind.hpp> +#include "llagentdata.h" +#include "llconversationmodel.h" +#include "llimfloater.h" +#include "llnearbychat.h" +#include "llimconversation.h" +#include "llimfloatercontainer.h" +#include "llfloaterreg.h" +#include "llgroupiconctrl.h" +#include "lluictrlfactory.h" + +// +// Implementation of conversations list session widgets +// +static LLDefaultChildRegistry::Register<LLConversationViewSession> r_conversation_view_session("conversation_view_session"); + +const LLColor4U DEFAULT_WHITE(255, 255, 255); + +class LLNearbyVoiceClientStatusObserver : public LLVoiceClientStatusObserver +{ +public: + + LLNearbyVoiceClientStatusObserver(LLConversationViewSession* conv) + : conversation(conv) + {} + + virtual void onChange(EStatusType status, const std::string &channelURI, bool proximal) + { + if (conversation + && status != STATUS_JOINING + && status != STATUS_LEFT_CHANNEL + && LLVoiceClient::getInstance()->voiceEnabled() + && LLVoiceClient::getInstance()->isVoiceWorking()) + { + conversation->showVoiceIndicator(); + } + } + +private: + LLConversationViewSession* conversation; +}; + +LLConversationViewSession::Params::Params() : + container() +{} + +LLConversationViewSession::LLConversationViewSession(const LLConversationViewSession::Params& p): + LLFolderViewFolder(p), + mContainer(p.container), + mItemPanel(NULL), + mCallIconLayoutPanel(NULL), + mSessionTitle(NULL), + mSpeakingIndicator(NULL), + mVoiceClientObserver(NULL), + mMinimizedMode(false) +{ +} + +LLConversationViewSession::~LLConversationViewSession() +{ + mActiveVoiceChannelConnection.disconnect(); + + if(LLVoiceClient::instanceExists() && mVoiceClientObserver) + { + LLVoiceClient::getInstance()->removeObserver(mVoiceClientObserver); + } +} + +BOOL LLConversationViewSession::postBuild() +{ + LLFolderViewItem::postBuild(); + + mItemPanel = LLUICtrlFactory::getInstance()->createFromFile<LLPanel>("panel_conversation_list_item.xml", NULL, LLPanel::child_registry_t::instance()); + addChild(mItemPanel); + + mCallIconLayoutPanel = mItemPanel->getChild<LLPanel>("call_icon_panel"); + mSessionTitle = mItemPanel->getChild<LLTextBox>("conversation_title"); + + mActiveVoiceChannelConnection = LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLConversationViewSession::onCurrentVoiceSessionChanged, this, _1)); + mSpeakingIndicator = getChild<LLOutputMonitorCtrl>("speaking_indicatorn"); + + LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(getViewModelItem()); + if (vmi) + { + switch(vmi->getType()) + { + case LLConversationItem::CONV_PARTICIPANT: + case LLConversationItem::CONV_SESSION_1_ON_1: + { + LLIMModel::LLIMSession* session= LLIMModel::instance().findIMSession(vmi->getUUID()); + if (session) + { + LLAvatarIconCtrl* icon = mItemPanel->getChild<LLAvatarIconCtrl>("avatar_icon"); + icon->setVisible(true); + icon->setValue(session->mOtherParticipantID); + mSpeakingIndicator->setSpeakerId(gAgentID, session->mSessionID, true); + } + break; + } + case LLConversationItem::CONV_SESSION_AD_HOC: + { + LLGroupIconCtrl* icon = mItemPanel->getChild<LLGroupIconCtrl>("group_icon"); + icon->setVisible(true); + mSpeakingIndicator->setSpeakerId(gAgentID, vmi->getUUID(), true); + } + case LLConversationItem::CONV_SESSION_GROUP: + { + LLGroupIconCtrl* icon = mItemPanel->getChild<LLGroupIconCtrl>("group_icon"); + icon->setVisible(true); + icon->setValue(vmi->getUUID()); + mSpeakingIndicator->setSpeakerId(gAgentID, vmi->getUUID(), true); + break; + } + case LLConversationItem::CONV_SESSION_NEARBY: + { + LLIconCtrl* icon = mItemPanel->getChild<LLIconCtrl>("nearby_chat_icon"); + icon->setVisible(true); + mSpeakingIndicator->setSpeakerId(gAgentID, LLUUID::null, true); + if(LLVoiceClient::instanceExists()) + { + LLNearbyVoiceClientStatusObserver* mVoiceClientObserver = new LLNearbyVoiceClientStatusObserver(this); + LLVoiceClient::getInstance()->addObserver(mVoiceClientObserver); + } + break; + } + default: + break; + } + } + + refresh(); + + return TRUE; +} + +void LLConversationViewSession::draw() +{ + getViewModelItem()->update(); + + const LLFolderViewItem::Params& default_params = LLUICtrlFactory::getDefaultParams<LLFolderViewItem>(); + const BOOL show_context = (getRoot() ? getRoot()->getShowSelectionContext() : FALSE); + + // we don't draw the open folder arrow in minimized mode + if (!mMinimizedMode) + { + // update the rotation angle of open folder arrow + updateLabelRotation(); + + drawOpenFolderArrow(default_params, sFgColor); + } + + // draw highlight for selected items + drawHighlight(show_context, true, sHighlightBgColor, sFocusOutlineColor, sMouseOverColor); + + // Draw children if root folder, or any other folder that is open. Do not draw children when animating to closed state or you get rendering overlap. + bool draw_children = getRoot() == static_cast<LLFolderViewFolder*>(this) || isOpen(); + + for (folders_t::iterator iter = mFolders.begin(); + iter != mFolders.end();) + { + folders_t::iterator fit = iter++; + (*fit)->setVisible(draw_children); + } + for (items_t::iterator iter = mItems.begin(); + iter != mItems.end();) + { + items_t::iterator iit = iter++; + (*iit)->setVisible(draw_children); + } + + LLView::draw(); +} + +// virtual +S32 LLConversationViewSession::arrange(S32* width, S32* height) +{ + S32 h_pad = getIndentation() + mArrowSize; + LLRect rect(mMinimizedMode ? getLocalRect().mLeft : h_pad, + getLocalRect().mTop, + getLocalRect().mRight, + getLocalRect().mTop - getItemHeight()); + mItemPanel->setShape(rect); + + return LLFolderViewFolder::arrange(width, height); +} + +// virtual +void LLConversationViewSession::toggleOpen() +{ + // conversations should not be opened while in minimized mode + if (!mMinimizedMode) + { + LLFolderViewFolder::toggleOpen(); + + // do item's selection when opened + if (LLFolderViewFolder::isOpen()) + { + getParentFolder()->setSelection(this, true); + } + + } +} + +void LLConversationViewSession::selectItem() +{ + + LLConversationItem* item = dynamic_cast<LLConversationItem*>(mViewModelItem); + LLFloater* session_floater = LLIMConversation::getConversation(item->getUUID()); + LLMultiFloater* host_floater = session_floater->getHost(); + + if (host_floater == mContainer) + { + // Always expand the message pane if the panel is hosted by the container + mContainer->collapseMessagesPane(false); + // Switch to the conversation floater that is being selected + mContainer->selectFloater(session_floater); + } + + // Set the focus on the selected floater + session_floater->setFocus(TRUE); + // Store the active session + LLIMFloaterContainer::getInstance()->setSelectedSession(item->getUUID()); + + + LLFolderViewItem::selectItem(); +} + +void LLConversationViewSession::toggleMinimizedMode(bool is_minimized) +{ + mMinimizedMode = is_minimized; + + // hide the layout stack which contains all item's child widgets + // except for the icon which we display in minimized mode + getChild<LLView>("conversation_item_stack")->setVisible(!mMinimizedMode); + + S32 h_pad = getIndentation() + mArrowSize; + mItemPanel->translate(mMinimizedMode ? -h_pad : h_pad, 0); +} + +void LLConversationViewSession::setVisibleIfDetached(BOOL visible) +{ + // Do this only if the conversation floater has been torn off (i.e. no multi floater host) and is not minimized + // Note: minimized dockable floaters are brought to front hence unminimized when made visible and we don't want that here + LLConversationItem* item = dynamic_cast<LLConversationItem*>(mViewModelItem); + LLFloater* session_floater = LLIMConversation::getConversation(item->getUUID()); + + if (session_floater && !session_floater->getHost() && !session_floater->isMinimized()) + { + session_floater->setVisible(visible); + } +} + +LLConversationViewParticipant* LLConversationViewSession::findParticipant(const LLUUID& participant_id) +{ + // This is *not* a general tree parsing algorithm. We search only in the mItems list + // assuming there is no mFolders which makes sense for sessions (sessions don't contain + // sessions). + LLConversationViewParticipant* participant = NULL; + items_t::const_iterator iter; + for (iter = getItemsBegin(); iter != getItemsEnd(); iter++) + { + participant = dynamic_cast<LLConversationViewParticipant*>(*iter); + if (participant->hasSameValue(participant_id)) + { + break; + } + } + return (iter == getItemsEnd() ? NULL : participant); +} + +void LLConversationViewSession::showVoiceIndicator() +{ + if (LLVoiceChannel::getCurrentVoiceChannel()->getSessionID().isNull()) + { + mCallIconLayoutPanel->setVisible(true); + } +} + +void LLConversationViewSession::refresh() +{ + // Refresh the session view from its model data + LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(getViewModelItem()); + vmi->resetRefresh(); + + if (mSessionTitle) + { + mSessionTitle->setText(vmi->getDisplayName()); + } + + // Note: for the moment, all that needs to be done is done by LLFolderViewItem::refresh() + + // Do the regular upstream refresh + LLFolderViewFolder::refresh(); +} + +void LLConversationViewSession::onCurrentVoiceSessionChanged(const LLUUID& session_id) +{ + LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(getViewModelItem()); + + if (vmi) + { + bool is_active = vmi->getUUID() == session_id; + bool is_nearby = vmi->getType() == LLConversationItem::CONV_SESSION_NEARBY; + + if (is_nearby) + { + mSpeakingIndicator->setSpeakerId(is_active ? gAgentID : LLUUID::null); + } + + mSpeakingIndicator->switchIndicator(is_active); + mCallIconLayoutPanel->setVisible(is_active); + } +} + +// +// Implementation of conversations list participant (avatar) widgets +// + +static LLDefaultChildRegistry::Register<LLConversationViewParticipant> r("conversation_view_participant"); +bool LLConversationViewParticipant::sStaticInitialized = false; +S32 LLConversationViewParticipant::sChildrenWidths[LLConversationViewParticipant::ALIC_COUNT]; + +LLConversationViewParticipant::Params::Params() : +container(), +participant_id(), +avatar_icon("avatar_icon"), +info_button("info_button"), +output_monitor("output_monitor") +{} + +LLConversationViewParticipant::LLConversationViewParticipant( const LLConversationViewParticipant::Params& p ): + LLFolderViewItem(p), + mAvatarIcon(NULL), + mInfoBtn(NULL), + mSpeakingIndicator(NULL), + mUUID(p.participant_id) +{ +} + +void LLConversationViewParticipant::initFromParams(const LLConversationViewParticipant::Params& params) +{ + LLAvatarIconCtrl::Params avatar_icon_params(params.avatar_icon()); + applyXUILayout(avatar_icon_params, this); + LLAvatarIconCtrl * avatarIcon = LLUICtrlFactory::create<LLAvatarIconCtrl>(avatar_icon_params); + addChild(avatarIcon); + + LLButton::Params info_button_params(params.info_button()); + applyXUILayout(info_button_params, this); + LLButton * button = LLUICtrlFactory::create<LLButton>(info_button_params); + addChild(button); + + LLOutputMonitorCtrl::Params output_monitor_params(params.output_monitor()); + applyXUILayout(output_monitor_params, this); + LLOutputMonitorCtrl * outputMonitor = LLUICtrlFactory::create<LLOutputMonitorCtrl>(output_monitor_params); + addChild(outputMonitor); +} + +BOOL LLConversationViewParticipant::postBuild() +{ + mAvatarIcon = getChild<LLAvatarIconCtrl>("avatar_icon"); + + mInfoBtn = getChild<LLButton>("info_btn"); + mInfoBtn->setClickedCallback(boost::bind(&LLConversationViewParticipant::onInfoBtnClick, this)); + mInfoBtn->setVisible(false); + + mSpeakingIndicator = getChild<LLOutputMonitorCtrl>("speaking_indicator"); + + if (!sStaticInitialized) + { + // Remember children widths including their padding from the next sibling, + // so that we can hide and show them again later. + initChildrenWidths(this); + sStaticInitialized = true; + } + + updateChildren(); + return LLFolderViewItem::postBuild(); +} + +void LLConversationViewParticipant::draw() +{ + static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); + static LLUIColor sHighlightFgColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE); + static LLUIColor sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE); + static LLUIColor sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE); + static LLUIColor sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE); + + const BOOL show_context = (getRoot() ? getRoot()->getShowSelectionContext() : FALSE); + + const LLFontGL* font = getLabelFontForStyle(mLabelStyle); + F32 right_x = 0; + + F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad; + F32 text_left = (F32)getLabelXPos(); + LLColor4 color = mIsSelected ? sHighlightFgColor : sFgColor; + + drawHighlight(show_context, mIsSelected, sHighlightBgColor, sFocusOutlineColor, sMouseOverColor); + drawLabel(font, text_left, y, color, right_x); + + LLView::draw(); +} + +void LLConversationViewParticipant::selectItem() +{ + LLConversationItem* vmi = this->getParentFolder() ? static_cast<LLConversationItem*>(this->getParentFolder()->getViewModelItem()) : NULL; + LLIMFloaterContainer* container = LLIMFloaterContainer::getInstance(); + LLFloater* session_floater; + + if(vmi) + { + session_floater = LLIMConversation::getConversation(vmi->getUUID()); + + //Only execute when switching floaters (conversations) + if(vmi->getUUID() != container->getSelectedSession()) + { + container->selectFloater(session_floater); + // Store the active session + container->setSelectedSession(vmi->getUUID()); + } + + //Redirect focus to the conversation floater + session_floater->setFocus(TRUE); + } + + LLFolderViewItem::selectItem(); +} + +void LLConversationViewParticipant::refresh() +{ + // Refresh the participant view from its model data + LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(getViewModelItem()); + vmi->resetRefresh(); + + // Note: for the moment, all that needs to be done is done by LLFolderViewItem::refresh() + + // Do the regular upstream refresh + LLFolderViewItem::refresh(); +} + +void LLConversationViewParticipant::addToFolder(LLFolderViewFolder* folder) +{ + //Add the item to the folder (conversation) + LLFolderViewItem::addToFolder(folder); + + //Now retrieve the folder (conversation) UUID, which is the speaker session + LLConversationItem* vmi = this->getParentFolder() ? dynamic_cast<LLConversationItem*>(this->getParentFolder()->getViewModelItem()) : NULL; + if(vmi) + { + //Allows speaking icon image to be loaded based on mUUID + mAvatarIcon->setValue(mUUID); + + //Allows the speaker indicator to be activated based on the user and conversation + mSpeakingIndicator->setSpeakerId(mUUID, vmi->getUUID()); + } +} + +void LLConversationViewParticipant::onInfoBtnClick() +{ + LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", mUUID)); +} + +void LLConversationViewParticipant::onMouseEnter(S32 x, S32 y, MASK mask) +{ + mInfoBtn->setVisible(true); + updateChildren(); + LLFolderViewItem::onMouseEnter(x, y, mask); +} + +void LLConversationViewParticipant::onMouseLeave(S32 x, S32 y, MASK mask) +{ + mInfoBtn->setVisible(false); + updateChildren(); + LLFolderViewItem::onMouseLeave(x, y, mask); +} + +S32 LLConversationViewParticipant::getLabelXPos() +{ + return mAvatarIcon->getRect().mRight + mIconPad; +} + +// static +void LLConversationViewParticipant::initChildrenWidths(LLConversationViewParticipant* self) +{ + //speaking indicator width + padding + S32 speaking_indicator_width = self->getRect().getWidth() - self->mSpeakingIndicator->getRect().mLeft; + + //info btn width + padding + S32 info_btn_width = self->mSpeakingIndicator->getRect().mLeft - self->mInfoBtn->getRect().mLeft; + + S32 index = ALIC_COUNT; + sChildrenWidths[--index] = info_btn_width; + sChildrenWidths[--index] = speaking_indicator_width; + llassert(index == 0); +} + +void LLConversationViewParticipant::updateChildren() +{ + mLabelPaddingRight = DEFAULT_LABEL_PADDING_RIGHT; + LLView* control; + S32 ctrl_width; + LLRect controlRect; + + //Cycles through controls starting from right to left + for (S32 i = 0; i < ALIC_COUNT; ++i) + { + control = getItemChildView((EAvatarListItemChildIndex)i); + + // skip invisible views + if (!control->getVisible()) continue; + + //Get current pos/dimensions + controlRect = control->getRect(); + + ctrl_width = sChildrenWidths[i]; // including space between current & left controls + // accumulate the amount of space taken by the controls + mLabelPaddingRight += ctrl_width; + + //Reposition visible controls in case adjacent controls to the right are hidden. + controlRect.setLeftTopAndSize( + getLocalRect().getWidth() - mLabelPaddingRight, + controlRect.mTop, + controlRect.getWidth(), + controlRect.getHeight()); + + //Sets the new position + control->setShape(controlRect); + } +} + +LLView* LLConversationViewParticipant::getItemChildView(EAvatarListItemChildIndex child_view_index) +{ + LLView* child_view = NULL; + + switch (child_view_index) + { + case ALIC_SPEAKER_INDICATOR: + child_view = mSpeakingIndicator; + break; + case ALIC_INFO_BUTTON: + child_view = mInfoBtn; + break; + default: + LL_WARNS("AvatarItemReshape") << "Unexpected child view index is passed: " << child_view_index << LL_ENDL; + llassert(0); + break; + // leave child_view untouched + } + + return child_view; +} + +// EOF + diff --git a/indra/newview/llconversationview.h b/indra/newview/llconversationview.h new file mode 100755 index 0000000000..4d77a4ade0 --- /dev/null +++ b/indra/newview/llconversationview.h @@ -0,0 +1,156 @@ +/** + * @file llconversationview.h + * @brief Implementation of conversations list widgets and views + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLCONVERSATIONVIEW_H +#define LL_LLCONVERSATIONVIEW_H + +#include "llfolderviewitem.h" + +#include "llavatariconctrl.h" +#include "llbutton.h" +#include "lloutputmonitorctrl.h" + +class LLTextBox; +class LLIMFloaterContainer; +class LLConversationViewSession; +class LLConversationViewParticipant; + +class LLVoiceClientStatusObserver; + +// Implementation of conversations list session widgets + +class LLConversationViewSession : public LLFolderViewFolder +{ +public: + struct Params : public LLInitParam::Block<Params, LLFolderViewItem::Params> + { + Optional<LLIMFloaterContainer*> container; + + Params(); + }; + +protected: + friend class LLUICtrlFactory; + LLConversationViewSession( const Params& p ); + + LLIMFloaterContainer* mContainer; + +public: + virtual ~LLConversationViewSession(); + virtual void selectItem(); + + /*virtual*/ BOOL postBuild(); + /*virtual*/ void draw(); + + /*virtual*/ S32 arrange(S32* width, S32* height); + + /*virtual*/ void toggleOpen(); + + /*virtual*/ bool isMinimized() { return mMinimizedMode; } + + void toggleMinimizedMode(bool is_minimized); + + void setVisibleIfDetached(BOOL visible); + LLConversationViewParticipant* findParticipant(const LLUUID& participant_id); + + void showVoiceIndicator(); + + virtual void refresh(); + +private: + + void onCurrentVoiceSessionChanged(const LLUUID& session_id); + + LLPanel* mItemPanel; + LLPanel* mCallIconLayoutPanel; + LLTextBox* mSessionTitle; + LLOutputMonitorCtrl* mSpeakingIndicator; + + bool mMinimizedMode; + + LLVoiceClientStatusObserver* mVoiceClientObserver; + + boost::signals2::connection mActiveVoiceChannelConnection; +}; + +// Implementation of conversations list participant (avatar) widgets + +class LLConversationViewParticipant : public LLFolderViewItem +{ + +public: + + struct Params : public LLInitParam::Block<Params, LLFolderViewItem::Params> + { + Optional<LLIMFloaterContainer*> container; + Optional<LLUUID> participant_id; + Optional<LLAvatarIconCtrl::Params> avatar_icon; + Optional<LLButton::Params> info_button; + Optional<LLOutputMonitorCtrl::Params> output_monitor; + + Params(); + }; + + virtual ~LLConversationViewParticipant( void ) { } + void selectItem(); + bool hasSameValue(const LLUUID& uuid) { return (uuid == mUUID); } + virtual void refresh(); + void addToFolder(LLFolderViewFolder* folder); + + void onMouseEnter(S32 x, S32 y, MASK mask); + void onMouseLeave(S32 x, S32 y, MASK mask); + + /*virtual*/ S32 getLabelXPos(); + +protected: + friend class LLUICtrlFactory; + LLConversationViewParticipant( const Params& p ); + void initFromParams(const Params& params); + BOOL postBuild(); + /*virtual*/ void draw(); + + void onInfoBtnClick(); + +private: + LLAvatarIconCtrl* mAvatarIcon; + LLButton * mInfoBtn; + LLOutputMonitorCtrl* mSpeakingIndicator; + LLUUID mUUID; // UUID of the participant + + typedef enum e_avatar_item_child { + ALIC_SPEAKER_INDICATOR, + ALIC_INFO_BUTTON, + ALIC_COUNT, + } EAvatarListItemChildIndex; + + static bool sStaticInitialized; // this variable is introduced to improve code readability + static S32 sChildrenWidths[ALIC_COUNT]; + static void initChildrenWidths(LLConversationViewParticipant* self); + void updateChildren(); + LLView* getItemChildView(EAvatarListItemChildIndex child_view_index); +}; + +#endif // LL_LLCONVERSATIONVIEW_H diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp index a0289cd2af..954fd429a5 100644 --- a/indra/newview/lldrawable.cpp +++ b/indra/newview/lldrawable.cpp @@ -99,7 +99,6 @@ void LLDrawable::init() mPositionGroup.clear(); mExtents[0].clear(); mExtents[1].clear(); - mQuietCount = 0; mState = 0; mVObjp = NULL; @@ -407,6 +406,8 @@ void LLDrawable::makeActive() if (!isRoot() && !mParent->isActive()) { mParent->makeActive(); + //NOTE: linked set will now NEVER become static + mParent->setState(LLDrawable::ACTIVE_CHILD); } //all child objects must also be active @@ -426,41 +427,27 @@ void LLDrawable::makeActive() if (mVObjp->getPCode() == LL_PCODE_VOLUME) { - if (mVObjp->isFlexible()) - { - return; - } - } - - if (mVObjp->getPCode() == LL_PCODE_VOLUME) - { gPipeline.markRebuild(this, LLDrawable::REBUILD_VOLUME, TRUE); } updatePartition(); } - if (isRoot()) - { - mQuietCount = 0; - } - else - { - getParent()->mQuietCount = 0; - } + llassert(isAvatar() || isRoot() || mParent->isActive()); } void LLDrawable::makeStatic(BOOL warning_enabled) { - if (isState(ACTIVE)) + if (isState(ACTIVE) && + !isState(ACTIVE_CHILD) && + !mVObjp->isAttachment() && + !mVObjp->isFlexible()) { clearState(ACTIVE | ANIMATED_CHILD); - if (mParent.notNull() && mParent->isActive() && warning_enabled) - { - LL_WARNS_ONCE("Drawable") << "Drawable becomes static with active parent!" << LL_ENDL; - } - + //drawable became static with active parent, not acceptable + llassert(mParent.isNull() || !mParent->isActive() || !warning_enabled); + LLViewerObject::const_child_list_t& child_list = mVObjp->getChildren(); for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); iter != child_list.end(); iter++) @@ -487,8 +474,8 @@ void LLDrawable::makeStatic(BOOL warning_enabled) mSpatialBridge->markDead(); setSpatialBridge(NULL); } + updatePartition(); } - updatePartition(); } // Returns "distance" between target destination and resulting xfrom @@ -638,8 +625,6 @@ BOOL LLDrawable::updateMove() return FALSE; } - makeActive(); - BOOL done; if (isState(MOVE_UNDAMPED)) @@ -648,6 +633,7 @@ BOOL LLDrawable::updateMove() } else { + makeActive(); done = updateMoveDamped(); } return done; diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h index 960c64fa9e..b1e32bdb5b 100644 --- a/indra/newview/lldrawable.h +++ b/indra/newview/lldrawable.h @@ -292,6 +292,7 @@ public: RIGGED = 0x08000000, PARTITION_MOVE = 0x10000000, ANIMATED_CHILD = 0x20000000, + ACTIVE_CHILD = 0x40000000, } EDrawableFlags; private: //aligned members @@ -305,8 +306,6 @@ public: LLPointer<LLDrawable> mParent; F32 mDistanceWRTCamera; - - S32 mQuietCount; static S32 getCurrentFrame() { return sCurVisible; } static S32 getMinVisFrameRange(); diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp index 013c698445..94dd927d26 100644 --- a/indra/newview/lldrawpool.cpp +++ b/indra/newview/lldrawpool.cpp @@ -419,6 +419,7 @@ void LLRenderPass::applyModelMatrix(LLDrawInfo& params) gGL.loadMatrix(gGLModelView); if (params.mModelMatrix) { + llassert(gGL.getMatrixMode() == LLRender::MM_MODELVIEW); gGL.multMatrix((GLfloat*) params.mModelMatrix->mMatrix); } gPipeline.mMatrixOpCount++; diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp index 7fc78fb382..9bc32fddbd 100644 --- a/indra/newview/lldrawpoolterrain.cpp +++ b/indra/newview/lldrawpoolterrain.cpp @@ -308,6 +308,7 @@ void LLDrawPoolTerrain::drawLoop() if (model_matrix != gGLLastMatrix) { + llassert(gGL.getMatrixMode() == LLRender::MM_MODELVIEW); gGLLastMatrix = model_matrix; gGL.loadMatrix(gGLModelView); if (model_matrix) @@ -594,7 +595,8 @@ void LLDrawPoolTerrain::renderFull4TU() gGL.matrixMode(LLRender::MM_TEXTURE); gGL.loadIdentity(); gGL.translatef(-1.f, 0.f, 0.f); - + gGL.matrixMode(LLRender::MM_MODELVIEW); + // Set alpha texture and do lighting modulation gGL.getTexUnit(3)->setTextureColorBlend(LLTexUnit::TBO_MULT, LLTexUnit::TBS_PREV_COLOR, LLTexUnit::TBS_VERT_COLOR); gGL.getTexUnit(3)->setTextureAlphaBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_TEX_ALPHA); @@ -742,6 +744,7 @@ void LLDrawPoolTerrain::renderFull2TU() gGL.matrixMode(LLRender::MM_TEXTURE); gGL.loadIdentity(); gGL.translatef(-1.f, 0.f, 0.f); + gGL.matrixMode(LLRender::MM_MODELVIEW); // Care about alpha only gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_PREV_COLOR); @@ -781,6 +784,7 @@ void LLDrawPoolTerrain::renderFull2TU() gGL.matrixMode(LLRender::MM_TEXTURE); gGL.loadIdentity(); gGL.translatef(-2.f, 0.f, 0.f); + gGL.matrixMode(LLRender::MM_MODELVIEW); // Care about alpha only gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_PREV_COLOR); diff --git a/indra/newview/lldrawpooltree.cpp b/indra/newview/lldrawpooltree.cpp index 83f04e45a8..fedbd782dc 100644 --- a/indra/newview/lldrawpooltree.cpp +++ b/indra/newview/lldrawpooltree.cpp @@ -116,6 +116,7 @@ void LLDrawPoolTree::render(S32 pass) gGL.loadMatrix(gGLModelView); if (model_matrix) { + llassert(gGL.getMatrixMode() == LLRender::MM_MODELVIEW); gGL.multMatrix((GLfloat*) model_matrix->mMatrix); } gPipeline.mMatrixOpCount++; diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp index bf8338e5f2..fa42b157a7 100644 --- a/indra/newview/lldynamictexture.cpp +++ b/indra/newview/lldynamictexture.cpp @@ -129,7 +129,7 @@ void LLViewerDynamicTexture::preRender(BOOL clear_depth) llassert(mFullHeight <= 512); llassert(mFullWidth <= 512); - if (gGLManager.mHasFramebufferObject && gPipeline.mWaterDis.isComplete()) + if (gGLManager.mHasFramebufferObject && gPipeline.mWaterDis.isComplete() && !gGLManager.mIsATI) { //using offscreen render target, just use the bottom left corner mOrigin.set(0, 0); } @@ -216,14 +216,12 @@ BOOL LLViewerDynamicTexture::updateAllInstances() return TRUE; } -#if 0 //THIS CAUSES MAINT-1092 - bool use_fbo = gGLManager.mHasFramebufferObject && gPipeline.mWaterDis.isComplete(); + bool use_fbo = gGLManager.mHasFramebufferObject && gPipeline.mWaterDis.isComplete() && !gGLManager.mIsATI; if (use_fbo) { gPipeline.mWaterDis.bindTarget(); } -#endif LLGLSLShader::bindNoShader(); LLVertexBuffer::unbind(); @@ -258,12 +256,10 @@ BOOL LLViewerDynamicTexture::updateAllInstances() } } -#if 0 if (use_fbo) { gPipeline.mWaterDis.flush(); } -#endif return ret; } diff --git a/indra/newview/llexpandabletextbox.cpp b/indra/newview/llexpandabletextbox.cpp index 935dcb74b0..a50184460b 100644 --- a/indra/newview/llexpandabletextbox.cpp +++ b/indra/newview/llexpandabletextbox.cpp @@ -150,7 +150,7 @@ void LLExpandableTextBox::LLTextBoxEx::showExpandText() std::pair<S32, S32> visible_lines = getVisibleLines(true); S32 last_line = visible_lines.second - 1; - LLStyle::Params expander_style(getDefaultStyleParams()); + LLStyle::Params expander_style(getStyleParams()); expander_style.font.style = "UNDERLINE"; expander_style.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); LLExpanderSegment* expanderp = new LLExpanderSegment(new LLStyle(expander_style), getLineStart(last_line), getLength() + 1, mExpanderLabel, *this); @@ -166,7 +166,7 @@ void LLExpandableTextBox::LLTextBoxEx::hideExpandText() if (mExpanderVisible) { // this will overwrite the expander segment and all text styling with a single style - LLStyleConstSP sp(new LLStyle(getDefaultStyleParams())); + LLStyleConstSP sp(new LLStyle(getStyleParams())); LLNormalTextSegment* segmentp = new LLNormalTextSegment(sp, 0, getLength() + 1, *this); insertSegment(segmentp); diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index 4cbc9cab4a..c68577db75 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -38,6 +38,7 @@ #include "lltooltip.h" #include "llagent.h" +#include "llavatarnamecache.h" #include "llclipboard.h" #include "llclipboard.h" #include "llinventorybridge.h" @@ -45,12 +46,14 @@ #include "llfloatersidepanelcontainer.h" #include "llfloaterworldmap.h" #include "lllandmarkactions.h" +#include "lllogininstance.h" #include "llnotificationsutil.h" #include "lltoggleablemenu.h" #include "llviewerinventory.h" #include "llviewermenu.h" #include "llviewermenu.h" #include "lltooldraganddrop.h" +#include "llsdserialize.h" static LLDefaultChildRegistry::Register<LLFavoritesBarCtrl> r("favorites_bar"); @@ -317,7 +320,8 @@ public: if (item) { - item->setSortField(mSortField); + LLFavoritesOrderStorage::instance().setSortIndex(item, mSortField); + item->setComplete(TRUE); item->updateServer(FALSE); @@ -339,8 +343,8 @@ struct LLFavoritesSort // TODO - made it customizible using gSavedSettings bool operator()(const LLViewerInventoryItem* const& a, const LLViewerInventoryItem* const& b) { - S32 sortField1 = a->getSortField(); - S32 sortField2 = b->getSortField(); + S32 sortField1 = LLFavoritesOrderStorage::instance().getSortIndex(a->getUUID()); + S32 sortField2 = LLFavoritesOrderStorage::instance().getSortIndex(b->getUUID()); if (!(sortField1 < 0 && sortField2 < 0)) { @@ -528,7 +532,7 @@ void LLFavoritesBarCtrl::handleExistingFavoriteDragAndDrop(S32 x, S32 y) mItems.push_back(gInventory.getItem(mDragItemId)); } - gInventory.saveItemsOrder(mItems); + LLFavoritesOrderStorage::instance().saveItemsOrder(mItems); LLToggleableMenu* menu = (LLToggleableMenu*) mOverflowMenuHandle.get(); @@ -587,7 +591,8 @@ void LLFavoritesBarCtrl::handleNewFavoriteDragAndDrop(LLInventoryItem *item, con } else { - currItem->setSortField(++sortField); + LLFavoritesOrderStorage::instance().setSortIndex(currItem, ++sortField); + currItem->setComplete(TRUE); currItem->updateServer(FALSE); @@ -640,7 +645,7 @@ void LLFavoritesBarCtrl::changed(U32 mask) for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) { - (*i)->getSLURL(); + LLFavoritesOrderStorage::instance().getSLURL((*i)->getAssetUUID()); } updateButtons(); } @@ -909,7 +914,7 @@ BOOL LLFavoritesBarCtrl::collectFavoriteItems(LLInventoryModel::item_array_t &it S32 sortField = 0; for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) { - (*i)->setSortField(++sortField); + LLFavoritesOrderStorage::instance().setSortIndex((*i), ++sortField); } } @@ -1355,7 +1360,7 @@ BOOL LLFavoritesBarCtrl::needToSaveItemsOrder(const LLInventoryModel::item_array // if there is an item without sort order field set, we need to save items order for (LLInventoryModel::item_array_t::const_iterator i = items.begin(); i != items.end(); ++i) { - if ((*i)->getSortField() < 0) + if (LLFavoritesOrderStorage::instance().getSortIndex((*i)->getUUID()) < 0) { result = TRUE; break; @@ -1390,4 +1395,291 @@ void LLFavoritesBarCtrl::insertItem(LLInventoryModel::item_array_t& items, const } } +const std::string LLFavoritesOrderStorage::SORTING_DATA_FILE_NAME = "landmarks_sorting.xml"; +const S32 LLFavoritesOrderStorage::NO_INDEX = -1; + +void LLFavoritesOrderStorage::setSortIndex(const LLViewerInventoryItem* inv_item, S32 sort_index) +{ + mSortIndexes[inv_item->getUUID()] = sort_index; + mIsDirty = true; + getSLURL(inv_item->getAssetUUID()); +} + +S32 LLFavoritesOrderStorage::getSortIndex(const LLUUID& inv_item_id) +{ + sort_index_map_t::const_iterator it = mSortIndexes.find(inv_item_id); + if (it != mSortIndexes.end()) + { + return it->second; + } + return NO_INDEX; +} + +void LLFavoritesOrderStorage::removeSortIndex(const LLUUID& inv_item_id) +{ + mSortIndexes.erase(inv_item_id); + mIsDirty = true; +} + +void LLFavoritesOrderStorage::getSLURL(const LLUUID& asset_id) +{ + slurls_map_t::iterator slurl_iter = mSLURLs.find(asset_id); + if (slurl_iter != mSLURLs.end()) return; // SLURL for current landmark is already cached + + LLLandmark* lm = gLandmarkList.getAsset(asset_id, + boost::bind(&LLFavoritesOrderStorage::onLandmarkLoaded, this, asset_id, _1)); + if (lm) + { + onLandmarkLoaded(asset_id, lm); + } +} + +// static +void LLFavoritesOrderStorage::destroyClass() +{ + LLFavoritesOrderStorage::instance().cleanup(); + if (gSavedPerAccountSettings.getBOOL("ShowFavoritesOnLogin")) + { + LLFavoritesOrderStorage::instance().saveFavoritesSLURLs(); + } + else + { + LLFavoritesOrderStorage::instance().removeFavoritesRecordOfUser(); + } +} + +void LLFavoritesOrderStorage::load() +{ + // load per-resident sorting information + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SORTING_DATA_FILE_NAME); + + LLSD settings_llsd; + llifstream file; + file.open(filename); + if (file.is_open()) + { + LLSDSerialize::fromXML(settings_llsd, file); + } + + for (LLSD::map_const_iterator iter = settings_llsd.beginMap(); + iter != settings_llsd.endMap(); ++iter) + { + mSortIndexes.insert(std::make_pair(LLUUID(iter->first), (S32)iter->second.asInteger())); + } +} + +void LLFavoritesOrderStorage::saveFavoritesSLURLs() +{ + // Do not change the file if we are not logged in yet. + if (!LLLoginInstance::getInstance()->authSuccess()) + { + llwarns << "Cannot save favorites: not logged in" << llendl; + return; + } + + std::string user_dir = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""); + if (user_dir.empty()) + { + llwarns << "Cannot save favorites: empty user dir name" << llendl; + return; + } + + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); + llifstream in_file; + in_file.open(filename); + LLSD fav_llsd; + if (in_file.is_open()) + { + LLSDSerialize::fromXML(fav_llsd, in_file); + } + + const LLUUID fav_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(fav_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); + + LLSD user_llsd; + for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); it++) + { + LLSD value; + value["name"] = (*it)->getName(); + value["asset_id"] = (*it)->getAssetUUID(); + + slurls_map_t::iterator slurl_iter = mSLURLs.find(value["asset_id"]); + if (slurl_iter != mSLURLs.end()) + { + lldebugs << "Saving favorite: idx=" << LLFavoritesOrderStorage::instance().getSortIndex((*it)->getUUID()) << ", SLURL=" << slurl_iter->second << ", value=" << value << llendl; + value["slurl"] = slurl_iter->second; + user_llsd[LLFavoritesOrderStorage::instance().getSortIndex((*it)->getUUID())] = value; + } + else + { + llwarns << "Not saving favorite " << value["name"] << ": no matching SLURL" << llendl; + } + } + + LLAvatarName av_name; + LLAvatarNameCache::get( gAgentID, &av_name ); + lldebugs << "Saved favorites for " << av_name.getLegacyName() << llendl; + fav_llsd[av_name.getLegacyName()] = user_llsd; + + llofstream file; + file.open(filename); + LLSDSerialize::toPrettyXML(fav_llsd, file); +} + +void LLFavoritesOrderStorage::removeFavoritesRecordOfUser() +{ + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); + LLSD fav_llsd; + llifstream file; + file.open(filename); + if (!file.is_open()) return; + LLSDSerialize::fromXML(fav_llsd, file); + + LLAvatarName av_name; + LLAvatarNameCache::get( gAgentID, &av_name ); + lldebugs << "Removed favorites for " << av_name.getLegacyName() << llendl; + if (fav_llsd.has(av_name.getLegacyName())) + { + fav_llsd.erase(av_name.getLegacyName()); + } + + llofstream out_file; + out_file.open(filename); + LLSDSerialize::toPrettyXML(fav_llsd, out_file); + +} + +void LLFavoritesOrderStorage::onLandmarkLoaded(const LLUUID& asset_id, LLLandmark* landmark) +{ + if (!landmark) return; + + LLVector3d pos_global; + if (!landmark->getGlobalPos(pos_global)) + { + // If global position was unknown on first getGlobalPos() call + // it should be set for the subsequent calls. + landmark->getGlobalPos(pos_global); + } + + if (!pos_global.isExactlyZero()) + { + LLLandmarkActions::getSLURLfromPosGlobal(pos_global, + boost::bind(&LLFavoritesOrderStorage::storeFavoriteSLURL, this, asset_id, _1)); + } +} + +void LLFavoritesOrderStorage::storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl) +{ + lldebugs << "Saving landmark SLURL: " << slurl << llendl; + mSLURLs[asset_id] = slurl; +} + +void LLFavoritesOrderStorage::save() +{ + // nothing to save if clean + if (!mIsDirty) return; + + // If we quit from the login screen we will not have an SL account + // name. Don't try to save, otherwise we'll dump a file in + // C:\Program Files\SecondLife\ or similar. JC + std::string user_dir = gDirUtilp->getLindenUserDir(); + if (!user_dir.empty()) + { + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SORTING_DATA_FILE_NAME); + LLSD settings_llsd; + + for(sort_index_map_t::const_iterator iter = mSortIndexes.begin(); iter != mSortIndexes.end(); ++iter) + { + settings_llsd[iter->first.asString()] = iter->second; + } + + llofstream file; + file.open(filename); + LLSDSerialize::toPrettyXML(settings_llsd, file); + } +} + +void LLFavoritesOrderStorage::cleanup() +{ + // nothing to clean + if (!mIsDirty) return; + + const LLUUID fav_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(fav_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); + + IsNotInFavorites is_not_in_fav(items); + + sort_index_map_t aTempMap; + //copy unremoved values from mSortIndexes to aTempMap + std::remove_copy_if(mSortIndexes.begin(), mSortIndexes.end(), + inserter(aTempMap, aTempMap.begin()), + is_not_in_fav); + + //Swap the contents of mSortIndexes and aTempMap + mSortIndexes.swap(aTempMap); +} + +void LLFavoritesOrderStorage::saveItemsOrder( const LLInventoryModel::item_array_t& items ) +{ + int sortField = 0; + + // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field + for (LLInventoryModel::item_array_t::const_iterator i = items.begin(); i != items.end(); ++i) + { + LLViewerInventoryItem* item = *i; + + setSortIndex(item, ++sortField); + + item->setComplete(TRUE); + item->updateServer(FALSE); + + gInventory.updateItem(item); + + // Tell the parent folder to refresh its sort order. + gInventory.addChangedMask(LLInventoryObserver::SORT, item->getParentUUID()); + } + + gInventory.notifyObservers(); +} + +// See also LLInventorySort where landmarks in the Favorites folder are sorted. +class LLViewerInventoryItemSort +{ +public: + bool operator()(const LLPointer<LLViewerInventoryItem>& a, const LLPointer<LLViewerInventoryItem>& b) + { + return LLFavoritesOrderStorage::instance().getSortIndex(a->getUUID()) + < LLFavoritesOrderStorage::instance().getSortIndex(b->getUUID()); + } +}; + +// * @param source_item_id - LLUUID of the source item to be moved into new position +// * @param target_item_id - LLUUID of the target item before which source item should be placed. +void LLFavoritesOrderStorage::rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLIsType is_type(LLAssetType::AT_LANDMARK); + LLUUID favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + gInventory.collectDescendentsIf(favorites_id, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type); + + // ensure items are sorted properly before changing order. EXT-3498 + std::sort(items.begin(), items.end(), LLViewerInventoryItemSort()); + + // update order + gInventory.updateItemsOrder(items, source_item_id, target_item_id); + + saveItemsOrder(items); +} + +void AddFavoriteLandmarkCallback::fire(const LLUUID& inv_item_id) +{ + if (mTargetLandmarkId.isNull()) return; + + LLFavoritesOrderStorage::instance().rearrangeFavoriteLandmarks(inv_item_id, mTargetLandmarkId); +} // EOF diff --git a/indra/newview/llfavoritesbar.h b/indra/newview/llfavoritesbar.h index 447d30f1f4..e000adc4aa 100644 --- a/indra/newview/llfavoritesbar.h +++ b/indra/newview/llfavoritesbar.h @@ -33,6 +33,7 @@ #include "llinventoryobserver.h" #include "llinventorymodel.h" +#include "llviewerinventory.h" class LLMenuItemCallGL; class LLToggleableMenu; @@ -161,5 +162,115 @@ private: boost::signals2::connection mEndDragConnection; }; +class AddFavoriteLandmarkCallback : public LLInventoryCallback +{ +public: + AddFavoriteLandmarkCallback() : mTargetLandmarkId(LLUUID::null) {} + void setTargetLandmarkId(const LLUUID& target_uuid) { mTargetLandmarkId = target_uuid; } + +private: + void fire(const LLUUID& inv_item); + + LLUUID mTargetLandmarkId; +}; + +/** + * Class to store sorting order of favorites landmarks in a local file. EXT-3985. + * It replaced previously implemented solution to store sort index in landmark's name as a "<N>@" prefix. + * Data are stored in user home directory. + */ +class LLFavoritesOrderStorage : public LLSingleton<LLFavoritesOrderStorage> + , public LLDestroyClass<LLFavoritesOrderStorage> +{ + LOG_CLASS(LLFavoritesOrderStorage); +public: + /** + * Sets sort index for specified with LLUUID favorite landmark + */ + void setSortIndex(const LLViewerInventoryItem* inv_item, S32 sort_index); + + /** + * Gets sort index for specified with LLUUID favorite landmark + */ + S32 getSortIndex(const LLUUID& inv_item_id); + void removeSortIndex(const LLUUID& inv_item_id); + + void getSLURL(const LLUUID& asset_id); + + // Saves current order of the passed items using inventory item sort field. + // Resets 'items' sort fields and saves them on server. + // Is used to save order for Favorites folder. + void saveItemsOrder(const LLInventoryModel::item_array_t& items); + + void rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id); + + /** + * Implementation of LLDestroyClass. Calls cleanup() instance method. + * + * It is important this callback is called before gInventory is cleaned. + * For now it is called from LLAppViewer::cleanup() -> LLAppViewer::disconnectViewer(), + * Inventory is cleaned later from LLAppViewer::cleanup() after LLAppViewer::disconnectViewer() is called. + * @see cleanup() + */ + static void destroyClass(); + + const static S32 NO_INDEX; +private: + friend class LLSingleton<LLFavoritesOrderStorage>; + LLFavoritesOrderStorage() : mIsDirty(false) { load(); } + ~LLFavoritesOrderStorage() { save(); } + + /** + * Removes sort indexes for items which are not in Favorites bar for now. + */ + void cleanup(); + + const static std::string SORTING_DATA_FILE_NAME; + + void load(); + void save(); + + void saveFavoritesSLURLs(); + + // Remove record of current user's favorites from file on disk. + void removeFavoritesRecordOfUser(); + + void onLandmarkLoaded(const LLUUID& asset_id, class LLLandmark* landmark); + void storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl); + + typedef std::map<LLUUID, S32> sort_index_map_t; + sort_index_map_t mSortIndexes; + + typedef std::map<LLUUID, std::string> slurls_map_t; + slurls_map_t mSLURLs; + + bool mIsDirty; + + struct IsNotInFavorites + { + IsNotInFavorites(const LLInventoryModel::item_array_t& items) + : mFavoriteItems(items) + { + + } + + /** + * Returns true if specified item is not found among inventory items + */ + bool operator()(const sort_index_map_t::value_type& id_index_pair) const + { + LLPointer<LLViewerInventoryItem> item = gInventory.getItem(id_index_pair.first); + if (item.isNull()) return true; + + LLInventoryModel::item_array_t::const_iterator found_it = + std::find(mFavoriteItems.begin(), mFavoriteItems.end(), item); + + return found_it == mFavoriteItems.end(); + } + private: + LLInventoryModel::item_array_t mFavoriteItems; + }; + +}; #endif // LL_LLFAVORITESBARCTRL_H diff --git a/indra/newview/llfirstuse.cpp b/indra/newview/llfirstuse.cpp index a9f52282a5..e2850f5181 100644 --- a/indra/newview/llfirstuse.cpp +++ b/indra/newview/llfirstuse.cpp @@ -74,7 +74,7 @@ void LLFirstUse::resetFirstUse() // static void LLFirstUse::otherAvatarChatFirst(bool enable) { - firstUseNotification("FirstOtherChatBeforeUser", enable, "HintChat", LLSD(), LLSD().with("target", "chat_bar").with("direction", "top_right").with("distance", 24)); + firstUseNotification("FirstOtherChatBeforeUser", enable, "HintChat", LLSD(), LLSD().with("target", "nearby_chat").with("direction", "top_right").with("distance", 24)); } // static diff --git a/indra/newview/llflexibleobject.cpp b/indra/newview/llflexibleobject.cpp index 9745bb6d64..606d77f645 100644 --- a/indra/newview/llflexibleobject.cpp +++ b/indra/newview/llflexibleobject.cpp @@ -44,6 +44,8 @@ #include "llvoavatar.h" /*static*/ F32 LLVolumeImplFlexible::sUpdateFactor = 1.0f; +std::vector<LLVolumeImplFlexible*> LLVolumeImplFlexible::sInstanceList; +std::vector<S32> LLVolumeImplFlexible::sUpdateDelay; static LLFastTimer::DeclareTimer FTM_FLEXIBLE_REBUILD("Rebuild"); static LLFastTimer::DeclareTimer FTM_DO_FLEXIBLE_UPDATE("Update"); @@ -70,8 +72,45 @@ LLVolumeImplFlexible::LLVolumeImplFlexible(LLViewerObject* vo, LLFlexibleObjectD { mVO->mDrawable->makeActive() ; } + + mInstanceIndex = sInstanceList.size(); + sInstanceList.push_back(this); + sUpdateDelay.push_back(0); }//----------------------------------------------- +LLVolumeImplFlexible::~LLVolumeImplFlexible() +{ + S32 end_idx = sInstanceList.size()-1; + + if (end_idx != mInstanceIndex) + { + sInstanceList[mInstanceIndex] = sInstanceList[end_idx]; + sInstanceList[mInstanceIndex]->mInstanceIndex = mInstanceIndex; + sUpdateDelay[mInstanceIndex] = sUpdateDelay[end_idx]; + } + + sInstanceList.pop_back(); + sUpdateDelay.pop_back(); +} + +//static +void LLVolumeImplFlexible::updateClass() +{ + std::vector<S32>::iterator delay_iter = sUpdateDelay.begin(); + + for (std::vector<LLVolumeImplFlexible*>::iterator iter = sInstanceList.begin(); + iter != sInstanceList.end(); + ++iter) + { + --(*delay_iter); + if (*delay_iter <= 0) + { + (*iter)->doIdleUpdate(); + } + ++delay_iter; + } +} + LLVector3 LLVolumeImplFlexible::getFramePosition() const { return mVO->getRenderPosition(); @@ -296,7 +335,7 @@ void LLVolumeImplFlexible::updateRenderRes() // optimization similar to what Havok does for objects that are stationary. //--------------------------------------------------------------------------------- static LLFastTimer::DeclareTimer FTM_FLEXIBLE_UPDATE("Update Flexies"); -void LLVolumeImplFlexible::doIdleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVolumeImplFlexible::doIdleUpdate() { LLDrawable* drawablep = mVO->mDrawable; @@ -304,13 +343,8 @@ void LLVolumeImplFlexible::doIdleUpdate(LLAgent &agent, LLWorld &world, const F6 { //LLFastTimer ftm(FTM_FLEXIBLE_UPDATE); - //flexible objects never go static - drawablep->mQuietCount = 0; - if (!drawablep->isRoot()) - { - LLViewerObject* parent = (LLViewerObject*) mVO->getParent(); - parent->mDrawable->mQuietCount = 0; - } + //ensure drawable is active + drawablep->makeActive(); if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FLEXIBLE)) { @@ -321,12 +355,18 @@ void LLVolumeImplFlexible::doIdleUpdate(LLAgent &agent, LLWorld &world, const F6 updateRenderRes(); gPipeline.markRebuild(drawablep, LLDrawable::REBUILD_POSITION, FALSE); } - else if (visible && - !drawablep->isState(LLDrawable::IN_REBUILD_Q1) && + else + { + F32 pixel_area = mVO->getPixelArea(); + + U32 update_period = (U32) (LLViewerCamera::getInstance()->getScreenPixelArea()*0.01f/(pixel_area*(sUpdateFactor+1.f)))+1; + + if (visible) + { + if (!drawablep->isState(LLDrawable::IN_REBUILD_Q1) && mVO->getPixelArea() > 256.f) { U32 id; - F32 pixel_area = mVO->getPixelArea(); if (mVO->isRootEdit()) { @@ -338,15 +378,23 @@ void LLVolumeImplFlexible::doIdleUpdate(LLAgent &agent, LLWorld &world, const F6 id = parent->getVolumeInterfaceID(); } - U32 update_period = (U32) (LLViewerCamera::getInstance()->getScreenPixelArea()*0.01f/(pixel_area*(sUpdateFactor+1.f)))+1; - if ((LLDrawable::getCurrentFrame()+id)%update_period == 0) { + sUpdateDelay[mInstanceIndex] = (S32) update_period-1; + updateRenderRes(); + gPipeline.markRebuild(drawablep, LLDrawable::REBUILD_POSITION, FALSE); } } } + else + { + sUpdateDelay[mInstanceIndex] = (S32) update_period; + } +} + + } } } @@ -369,8 +417,7 @@ void LLVolumeImplFlexible::doFlexibleUpdate() if ((mSimulateRes == 0 || !mInitialized) && mVO->mDrawable->isVisible()) { BOOL force_update = mSimulateRes == 0 ? TRUE : FALSE; - - doIdleUpdate(gAgent, *LLWorld::getInstance(), 0.0); + doIdleUpdate(); if (!force_update || !gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FLEXIBLE)) { diff --git a/indra/newview/llflexibleobject.h b/indra/newview/llflexibleobject.h index 56d579d86f..beb281a906 100644 --- a/indra/newview/llflexibleobject.h +++ b/indra/newview/llflexibleobject.h @@ -70,8 +70,16 @@ struct LLFlexibleObjectSection //--------------------------------------------------------- class LLVolumeImplFlexible : public LLVolumeInterface { +private: + static std::vector<LLVolumeImplFlexible*> sInstanceList; + static std::vector<S32> sUpdateDelay; + S32 mInstanceIndex; + public: + static void updateClass(); + LLVolumeImplFlexible(LLViewerObject* volume, LLFlexibleObjectData* attributes); + ~LLVolumeImplFlexible(); // Implements LLVolumeInterface U32 getID() const { return mID; } @@ -79,7 +87,7 @@ class LLVolumeImplFlexible : public LLVolumeInterface LLQuaternion getFrameRotation() const; LLVolumeInterfaceType getInterfaceType() const { return INTERFACE_FLEXIBLE; } void updateRenderRes(); - void doIdleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + void doIdleUpdate(); BOOL doUpdateGeometry(LLDrawable *drawable); LLVector3 getPivotPosition() const; void onSetVolume(const LLVolumeParams &volume_params, const S32 detail); diff --git a/indra/newview/llfloateravatarpicker.cpp b/indra/newview/llfloateravatarpicker.cpp index 0290e7cdf0..6ada809cdb 100644 --- a/indra/newview/llfloateravatarpicker.cpp +++ b/indra/newview/llfloateravatarpicker.cpp @@ -49,6 +49,8 @@ #include "llscrolllistcell.h" #include "lltabcontainer.h" #include "lluictrlfactory.h" +#include "llfocusmgr.h" +#include "lldraghandle.h" #include "message.h" //#include "llsdserialize.h" @@ -58,11 +60,14 @@ static std::map<LLUUID, LLAvatarName> sAvatarNameMap; LLFloaterAvatarPicker* LLFloaterAvatarPicker::show(select_callback_t callback, BOOL allow_multiple, - BOOL closeOnSelect) + BOOL closeOnSelect, + BOOL skip_agent, + const std::string& name, + LLView * frustumOrigin) { // *TODO: Use a key to allow this not to be an effective singleton LLFloaterAvatarPicker* floater = - LLFloaterReg::showTypedInstance<LLFloaterAvatarPicker>("avatar_picker"); + LLFloaterReg::showTypedInstance<LLFloaterAvatarPicker>("avatar_picker", LLSD(name)); if (!floater) { llwarns << "Cannot instantiate avatar picker" << llendl; @@ -73,6 +78,7 @@ LLFloaterAvatarPicker* LLFloaterAvatarPicker::show(select_callback_t callback, floater->setAllowMultiple(allow_multiple); floater->mNearMeListComplete = FALSE; floater->mCloseOnSelect = closeOnSelect; + floater->mExcludeAgentFromSearchResults = skip_agent; if (!closeOnSelect) { @@ -83,6 +89,11 @@ LLFloaterAvatarPicker* LLFloaterAvatarPicker::show(select_callback_t callback, floater->getChild<LLButton>("cancel_btn")->setLabel(close_string); } + if(frustumOrigin) + { + floater->mFrustumOrigin = frustumOrigin->getHandle(); + } + return floater; } @@ -91,9 +102,17 @@ LLFloaterAvatarPicker::LLFloaterAvatarPicker(const LLSD& key) : LLFloater(key), mNumResultsReturned(0), mNearMeListComplete(FALSE), - mCloseOnSelect(FALSE) + mCloseOnSelect(FALSE), + mContextConeOpacity (0.f), + mContextConeInAlpha(0.f), + mContextConeOutAlpha(0.f), + mContextConeFadeTime(0.f) { mCommitCallbackRegistrar.add("Refresh.FriendList", boost::bind(&LLFloaterAvatarPicker::populateFriend, this)); + + mContextConeInAlpha = gSavedSettings.getF32("ContextConeInAlpha"); + mContextConeOutAlpha = gSavedSettings.getF32("ContextConeOutAlpha"); + mContextConeFadeTime = gSavedSettings.getF32("ContextConeFadeTime"); } BOOL LLFloaterAvatarPicker::postBuild() @@ -338,8 +357,67 @@ void LLFloaterAvatarPicker::populateFriend() friends_scroller->sortByColumnIndex(0, TRUE); } +void LLFloaterAvatarPicker::drawFrustum() +{ + if(mFrustumOrigin.get()) + { + LLView * frustumOrigin = mFrustumOrigin.get(); + LLRect origin_rect; + frustumOrigin->localRectToOtherView(frustumOrigin->getLocalRect(), &origin_rect, this); + // draw context cone connecting color picker with color swatch in parent floater + LLRect local_rect = getLocalRect(); + if (hasFocus() && frustumOrigin->isInVisibleChain() && mContextConeOpacity > 0.001f) + { + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + LLGLEnable(GL_CULL_FACE); + gGL.begin(LLRender::QUADS); + { + gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity); + gGL.vertex2i(origin_rect.mLeft, origin_rect.mTop); + gGL.vertex2i(origin_rect.mRight, origin_rect.mTop); + gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity); + gGL.vertex2i(local_rect.mRight, local_rect.mTop); + gGL.vertex2i(local_rect.mLeft, local_rect.mTop); + + gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity); + gGL.vertex2i(local_rect.mLeft, local_rect.mTop); + gGL.vertex2i(local_rect.mLeft, local_rect.mBottom); + gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity); + gGL.vertex2i(origin_rect.mLeft, origin_rect.mBottom); + gGL.vertex2i(origin_rect.mLeft, origin_rect.mTop); + + gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity); + gGL.vertex2i(local_rect.mRight, local_rect.mBottom); + gGL.vertex2i(local_rect.mRight, local_rect.mTop); + gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity); + gGL.vertex2i(origin_rect.mRight, origin_rect.mTop); + gGL.vertex2i(origin_rect.mRight, origin_rect.mBottom); + + gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity); + gGL.vertex2i(local_rect.mLeft, local_rect.mBottom); + gGL.vertex2i(local_rect.mRight, local_rect.mBottom); + gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity); + gGL.vertex2i(origin_rect.mRight, origin_rect.mBottom); + gGL.vertex2i(origin_rect.mLeft, origin_rect.mBottom); + } + gGL.end(); + } + + if (gFocusMgr.childHasMouseCapture(getDragHandle())) + { + mContextConeOpacity = lerp(mContextConeOpacity, gSavedSettings.getF32("PickerContextOpacity"), LLCriticalDamp::getInterpolant(mContextConeFadeTime)); + } + else + { + mContextConeOpacity = lerp(mContextConeOpacity, 0.f, LLCriticalDamp::getInterpolant(mContextConeFadeTime)); + } + } +} + void LLFloaterAvatarPicker::draw() { + drawFrustum(); + // sometimes it is hard to determine when Select/Ok button should be disabled (see LLAvatarActions::shareWithAvatars). // lets check this via mOkButtonValidateSignal callback periodically. static LLFrameTimer timer; @@ -382,8 +460,9 @@ class LLAvatarPickerResponder : public LLHTTPClient::Responder { public: LLUUID mQueryID; + std::string mName; - LLAvatarPickerResponder(const LLUUID& id) : mQueryID(id) { } + LLAvatarPickerResponder(const LLUUID& id, const std::string& name) : mQueryID(id), mName(name) { } /*virtual*/ void completed(U32 status, const std::string& reason, const LLSD& content) { @@ -396,7 +475,7 @@ public: if (isGoodStatus(status) || status == 400) { LLFloaterAvatarPicker* floater = - LLFloaterReg::findTypedInstance<LLFloaterAvatarPicker>("avatar_picker"); + LLFloaterReg::findTypedInstance<LLFloaterAvatarPicker>("avatar_picker", mName); if (floater) { floater->processResponse(mQueryID, content); @@ -439,7 +518,7 @@ void LLFloaterAvatarPicker::find() url += "?page_size=100&names="; url += LLURI::escape(text); llinfos << "avatar picker " << url << llendl; - LLHTTPClient::get(url, new LLAvatarPickerResponder(mQueryID)); + LLHTTPClient::get(url, new LLAvatarPickerResponder(mQueryID, getKey().asString())); } else { @@ -581,35 +660,38 @@ void LLFloaterAvatarPicker::processAvatarPickerReply(LLMessageSystem* msg, void* msg->getUUIDFast( _PREHASH_Data,_PREHASH_AvatarID, avatar_id, i); msg->getStringFast(_PREHASH_Data,_PREHASH_FirstName, first_name, i); msg->getStringFast(_PREHASH_Data,_PREHASH_LastName, last_name, i); - - std::string avatar_name; - if (avatar_id.isNull()) - { - LLStringUtil::format_map_t map; - map["[TEXT]"] = floater->getChild<LLUICtrl>("Edit")->getValue().asString(); - avatar_name = floater->getString("not_found", map); - search_results->setEnabled(FALSE); - floater->getChildView("ok_btn")->setEnabled(FALSE); - } - else + + if (avatar_id != agent_id || !floater->isExcludeAgentFromSearchResults()) // exclude agent from search results? { - avatar_name = LLCacheName::buildFullName(first_name, last_name); - search_results->setEnabled(TRUE); - found_one = TRUE; + std::string avatar_name; + if (avatar_id.isNull()) + { + LLStringUtil::format_map_t map; + map["[TEXT]"] = floater->getChild<LLUICtrl>("Edit")->getValue().asString(); + avatar_name = floater->getString("not_found", map); + search_results->setEnabled(FALSE); + floater->getChildView("ok_btn")->setEnabled(FALSE); + } + else + { + avatar_name = LLCacheName::buildFullName(first_name, last_name); + search_results->setEnabled(TRUE); + found_one = TRUE; - LLAvatarName av_name; - av_name.mLegacyFirstName = first_name; - av_name.mLegacyLastName = last_name; - av_name.mDisplayName = avatar_name; - const LLUUID& agent_id = avatar_id; - sAvatarNameMap[agent_id] = av_name; + LLAvatarName av_name; + av_name.mLegacyFirstName = first_name; + av_name.mLegacyLastName = last_name; + av_name.mDisplayName = avatar_name; + const LLUUID& agent_id = avatar_id; + sAvatarNameMap[agent_id] = av_name; + } + LLSD element; + element["id"] = avatar_id; // value + element["columns"][0]["column"] = "name"; + element["columns"][0]["value"] = avatar_name; + search_results->addElement(element); } - LLSD element; - element["id"] = avatar_id; // value - element["columns"][0]["column"] = "name"; - element["columns"][0]["value"] = avatar_name; - search_results->addElement(element); } if (found_one) @@ -624,52 +706,58 @@ void LLFloaterAvatarPicker::processAvatarPickerReply(LLMessageSystem* msg, void* void LLFloaterAvatarPicker::processResponse(const LLUUID& query_id, const LLSD& content) { // Check for out-of-date query - if (query_id != mQueryID) return; + if (query_id == mQueryID) + { + LLScrollListCtrl* search_results = getChild<LLScrollListCtrl>("SearchResults"); - LLScrollListCtrl* search_results = getChild<LLScrollListCtrl>("SearchResults"); + LLSD agents = content["agents"]; + + // clear "Searching" label on first results + search_results->deleteAllItems(); - LLSD agents = content["agents"]; - if (agents.size() == 0) - { - LLStringUtil::format_map_t map; - map["[TEXT]"] = childGetText("Edit"); LLSD item; - item["id"] = LLUUID::null; - item["columns"][0]["column"] = "name"; - item["columns"][0]["value"] = getString("not_found", map); - search_results->addElement(item); - search_results->setEnabled(false); - getChildView("ok_btn")->setEnabled(false); - return; - } + LLSD::array_const_iterator it = agents.beginArray(); + for ( ; it != agents.endArray(); ++it) + { + const LLSD& row = *it; + if (row["id"].asUUID() != gAgent.getID() || !mExcludeAgentFromSearchResults) + { + item["id"] = row["id"]; + LLSD& columns = item["columns"]; + columns[0]["column"] = "name"; + columns[0]["value"] = row["display_name"]; + columns[1]["column"] = "username"; + columns[1]["value"] = row["username"]; + search_results->addElement(item); + + // add the avatar name to our list + LLAvatarName avatar_name; + avatar_name.fromLLSD(row); + sAvatarNameMap[row["id"].asUUID()] = avatar_name; + } + } - // clear "Searching" label on first results - search_results->deleteAllItems(); - - LLSD item; - LLSD::array_const_iterator it = agents.beginArray(); - for ( ; it != agents.endArray(); ++it) - { - const LLSD& row = *it; - item["id"] = row["id"]; - LLSD& columns = item["columns"]; - columns[0]["column"] = "name"; - columns[0]["value"] = row["display_name"]; - columns[1]["column"] = "username"; - columns[1]["value"] = row["username"]; - search_results->addElement(item); - - // add the avatar name to our list - LLAvatarName avatar_name; - avatar_name.fromLLSD(row); - sAvatarNameMap[row["id"].asUUID()] = avatar_name; - } - - getChildView("ok_btn")->setEnabled(true); - search_results->setEnabled(true); - search_results->selectFirstItem(); - onList(); - search_results->setFocus(TRUE); + if (search_results->isEmpty()) + { + LLStringUtil::format_map_t map; + map["[TEXT]"] = childGetText("Edit"); + LLSD item; + item["id"] = LLUUID::null; + item["columns"][0]["column"] = "name"; + item["columns"][0]["value"] = getString("not_found", map); + search_results->addElement(item); + search_results->setEnabled(false); + getChildView("ok_btn")->setEnabled(false); + } + else + { + getChildView("ok_btn")->setEnabled(true); + search_results->setEnabled(true); + search_results->selectFirstItem(); + onList(); + search_results->setFocus(TRUE); + } + } } //static diff --git a/indra/newview/llfloateravatarpicker.h b/indra/newview/llfloateravatarpicker.h index 96c039443a..ed3e51c56f 100644 --- a/indra/newview/llfloateravatarpicker.h +++ b/indra/newview/llfloateravatarpicker.h @@ -34,7 +34,7 @@ class LLAvatarName; class LLScrollListCtrl; -class LLFloaterAvatarPicker : public LLFloater +class LLFloaterAvatarPicker :public LLFloater { public: typedef boost::signals2::signal<bool(const uuid_vec_t&), boost_boolean_combiner> validate_signal_t; @@ -45,7 +45,10 @@ public: // Call this to select an avatar. static LLFloaterAvatarPicker* show(select_callback_t callback, BOOL allow_multiple = FALSE, - BOOL closeOnSelect = FALSE); + BOOL closeOnSelect = FALSE, + BOOL skip_agent = FALSE, + const std::string& name = "", + LLView * frustumOrigin = NULL); LLFloaterAvatarPicker(const LLSD& key); virtual ~LLFloaterAvatarPicker(); @@ -63,6 +66,7 @@ public: std::string& tooltip_msg); void openFriendsTab(); + BOOL isExcludeAgentFromSearchResults() {return mExcludeAgentFromSearchResults;} private: void editKeystroke(class LLLineEditor* caller, void* user_data); @@ -84,13 +88,20 @@ private: void setAllowMultiple(BOOL allow_multiple); LLScrollListCtrl* getActiveList(); + void drawFrustum(); virtual void draw(); virtual BOOL handleKeyHere(KEY key, MASK mask); LLUUID mQueryID; - int mNumResultsReturned; + int mNumResultsReturned; BOOL mNearMeListComplete; BOOL mCloseOnSelect; + BOOL mExcludeAgentFromSearchResults; + LLHandle <LLView> mFrustumOrigin; + F32 mContextConeOpacity; + F32 mContextConeInAlpha; + F32 mContextConeOutAlpha; + F32 mContextConeFadeTime; validate_signal_t mOkButtonValidateSignal; select_callback_t mSelectionCallback; diff --git a/indra/newview/llfloaterchatvoicevolume.cpp b/indra/newview/llfloaterchatvoicevolume.cpp new file mode 100644 index 0000000000..3c76a3a43c --- /dev/null +++ b/indra/newview/llfloaterchatvoicevolume.cpp @@ -0,0 +1,44 @@ +/** + * @file llfloaterchatvoicevolume.cpp + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloaterchatvoicevolume.h" + +LLFloaterChatVoiceVolume::LLFloaterChatVoiceVolume(const LLSD& key) +: LLInspect(key) +{ +} + +void LLFloaterChatVoiceVolume::onOpen(const LLSD& key) +{ + LLInspect::onOpen(key); + LLUI::positionViewNearMouse(this); +} + +LLFloaterChatVoiceVolume::~LLFloaterChatVoiceVolume() +{ + LLTransientFloaterMgr::getInstance()->removeControlView(this); +}; diff --git a/indra/newview/llfloaterchatvoicevolume.h b/indra/newview/llfloaterchatvoicevolume.h new file mode 100644 index 0000000000..61ad92b6da --- /dev/null +++ b/indra/newview/llfloaterchatvoicevolume.h @@ -0,0 +1,44 @@ +/** + * @file llfloaterchatvoicevolume.h + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLFLOATERCHATVOICEVOLUME_H_ +#define LLFLOATERCHATVOICEVOLUME_H_ + +#include "llinspect.h" +#include "lltransientfloatermgr.h" + +class LLFloaterChatVoiceVolume : public LLInspect, LLTransientFloater +{ +public: + + LLFloaterChatVoiceVolume(const LLSD& key); + virtual ~LLFloaterChatVoiceVolume(); + + virtual void onOpen(const LLSD& key); + + /*virtual*/ LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::GLOBAL; } +}; + +#endif /* LLFLOATERCHATVOICEVOLUME_H_ */ diff --git a/indra/newview/llfloatercolorpicker.cpp b/indra/newview/llfloatercolorpicker.cpp index 05d73c2416..3c230bc58c 100644 --- a/indra/newview/llfloatercolorpicker.cpp +++ b/indra/newview/llfloatercolorpicker.cpp @@ -62,10 +62,6 @@ #include <sstream> #include <iomanip> -const F32 CONTEXT_CONE_IN_ALPHA = 0.0f; -const F32 CONTEXT_CONE_OUT_ALPHA = 1.f; -const F32 CONTEXT_FADE_TIME = 0.08f; - ////////////////////////////////////////////////////////////////////////////// // // Class LLFloaterColorPicker @@ -105,7 +101,10 @@ LLFloaterColorPicker::LLFloaterColorPicker (LLColorSwatchCtrl* swatch, BOOL show mSwatch ( swatch ), mActive ( TRUE ), mCanApplyImmediately ( show_apply_immediate ), - mContextConeOpacity ( 0.f ) + mContextConeOpacity ( 0.f ), + mContextConeInAlpha ( 0.f ), + mContextConeOutAlpha ( 0.f ), + mContextConeFadeTime ( 0.f ) { buildFromFile ( "floater_color_picker.xml"); @@ -117,6 +116,10 @@ LLFloaterColorPicker::LLFloaterColorPicker (LLColorSwatchCtrl* swatch, BOOL show mApplyImmediateCheck->setEnabled(FALSE); mApplyImmediateCheck->set(FALSE); } + + mContextConeInAlpha = gSavedSettings.getF32("ContextConeInAlpha"); + mContextConeOutAlpha = gSavedSettings.getF32("ContextConeOutAlpha"); + mContextConeFadeTime = gSavedSettings.getF32("ContextConeFadeTime"); } LLFloaterColorPicker::~LLFloaterColorPicker() @@ -486,37 +489,37 @@ void LLFloaterColorPicker::draw() mSwatch->localRectToOtherView(mSwatch->getLocalRect(), &swatch_rect, this); // draw context cone connecting color picker with color swatch in parent floater LLRect local_rect = getLocalRect(); - if (gFocusMgr.childHasKeyboardFocus(this) && mSwatch->isInVisibleChain() && mContextConeOpacity > 0.001f) + if (hasFocus() && mSwatch->isInVisibleChain() && mContextConeOpacity > 0.001f) { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); LLGLEnable(GL_CULL_FACE); gGL.begin(LLRender::QUADS); { - gGL.color4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity); + gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity); gGL.vertex2i(swatch_rect.mLeft, swatch_rect.mTop); gGL.vertex2i(swatch_rect.mRight, swatch_rect.mTop); - gGL.color4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity); + gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity); gGL.vertex2i(local_rect.mRight, local_rect.mTop); gGL.vertex2i(local_rect.mLeft, local_rect.mTop); - gGL.color4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity); + gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity); gGL.vertex2i(local_rect.mLeft, local_rect.mTop); gGL.vertex2i(local_rect.mLeft, local_rect.mBottom); - gGL.color4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity); + gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity); gGL.vertex2i(swatch_rect.mLeft, swatch_rect.mBottom); gGL.vertex2i(swatch_rect.mLeft, swatch_rect.mTop); - gGL.color4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity); + gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity); gGL.vertex2i(local_rect.mRight, local_rect.mBottom); gGL.vertex2i(local_rect.mRight, local_rect.mTop); - gGL.color4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity); + gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity); gGL.vertex2i(swatch_rect.mRight, swatch_rect.mTop); gGL.vertex2i(swatch_rect.mRight, swatch_rect.mBottom); - gGL.color4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity); + gGL.color4f(0.f, 0.f, 0.f, mContextConeOutAlpha * mContextConeOpacity); gGL.vertex2i(local_rect.mLeft, local_rect.mBottom); gGL.vertex2i(local_rect.mRight, local_rect.mBottom); - gGL.color4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity); + gGL.color4f(0.f, 0.f, 0.f, mContextConeInAlpha * mContextConeOpacity); gGL.vertex2i(swatch_rect.mRight, swatch_rect.mBottom); gGL.vertex2i(swatch_rect.mLeft, swatch_rect.mBottom); } @@ -525,11 +528,12 @@ void LLFloaterColorPicker::draw() if (gFocusMgr.childHasMouseCapture(getDragHandle())) { - mContextConeOpacity = lerp(mContextConeOpacity, gSavedSettings.getF32("PickerContextOpacity"), LLCriticalDamp::getInterpolant(CONTEXT_FADE_TIME)); + mContextConeOpacity = lerp(mContextConeOpacity, gSavedSettings.getF32("PickerContextOpacity"), + LLCriticalDamp::getInterpolant(mContextConeFadeTime)); } else { - mContextConeOpacity = lerp(mContextConeOpacity, 0.f, LLCriticalDamp::getInterpolant(CONTEXT_FADE_TIME)); + mContextConeOpacity = lerp(mContextConeOpacity, 0.f, LLCriticalDamp::getInterpolant(mContextConeFadeTime)); } mPipetteBtn->setToggleState(LLToolMgr::getInstance()->getCurrentTool() == LLToolPipette::getInstance()); diff --git a/indra/newview/llfloatercolorpicker.h b/indra/newview/llfloatercolorpicker.h index 8e387c4f7c..bab0617712 100644 --- a/indra/newview/llfloatercolorpicker.h +++ b/indra/newview/llfloatercolorpicker.h @@ -189,6 +189,10 @@ class LLFloaterColorPicker LLButton* mPipetteBtn; F32 mContextConeOpacity; + F32 mContextConeInAlpha; + F32 mContextConeOutAlpha; + F32 mContextConeFadeTime; + }; #endif // LL_LLFLOATERCOLORPICKER_H diff --git a/indra/newview/llfloaterconversationlog.cpp b/indra/newview/llfloaterconversationlog.cpp new file mode 100644 index 0000000000..089aec1905 --- /dev/null +++ b/indra/newview/llfloaterconversationlog.cpp @@ -0,0 +1,145 @@ +/** + * @file llfloaterconversationlog.cpp + * @brief Functionality of the "conversation log" floater + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#include "llviewerprecompiledheaders.h" + +#include "llconversationloglist.h" +#include "llfiltereditor.h" +#include "llfloaterconversationlog.h" +#include "llfloaterreg.h" +#include "llmenubutton.h" + +LLFloaterConversationLog::LLFloaterConversationLog(const LLSD& key) +: LLFloater(key), + mConversationLogList(NULL) +{ + mCommitCallbackRegistrar.add("CallLog.Action", boost::bind(&LLFloaterConversationLog::onCustomAction, this, _2)); + mEnableCallbackRegistrar.add("CallLog.Check", boost::bind(&LLFloaterConversationLog::isActionChecked, this, _2)); +} + +BOOL LLFloaterConversationLog::postBuild() +{ + mConversationLogList = getChild<LLConversationLogList>("conversation_log_list"); + + switch (gSavedSettings.getU32("CallLogSortOrder")) + { + case LLConversationLogList::E_SORT_BY_NAME: + mConversationLogList->sortByName(); + break; + + case LLConversationLogList::E_SORT_BY_DATE: + mConversationLogList->sortByDate(); + break; + } + + // Use the context menu of the Conversation list for the Conversation tab gear menu. + LLToggleableMenu* conversations_gear_menu = mConversationLogList->getContextMenu(); + if (conversations_gear_menu) + { + getChild<LLMenuButton>("conversations_gear_btn")->setMenu(conversations_gear_menu, LLMenuButton::MP_BOTTOM_LEFT); + } + + getChild<LLFilterEditor>("people_filter_input")->setCommitCallback(boost::bind(&LLFloaterConversationLog::onFilterEdit, this, _2)); + + LLControlVariable* ctrl = gSavedPerAccountSettings.getControl("LogInstantMessages").get(); + if (ctrl) + { + ctrl->getSignal()->connect(boost::bind(&LLFloaterConversationLog::onCallLoggingEnabledDisabled, this, _2)); + onCallLoggingEnabledDisabled(ctrl->getValue().asBoolean()); + } + + return LLFloater::postBuild(); +} + +void LLFloaterConversationLog::draw() +{ + LLFloater::draw(); +} + +void LLFloaterConversationLog::onFilterEdit(const std::string& search_string) +{ + std::string filter = search_string; + LLStringUtil::trimHead(filter); + + mConversationLogList->setNameFilter(filter); +} + + +void LLFloaterConversationLog::onCustomAction (const LLSD& userdata) +{ + const std::string command_name = userdata.asString(); + + if ("sort_by_name" == command_name) + { + mConversationLogList->sortByName(); + gSavedSettings.setU32("CallLogSortOrder", LLConversationLogList::E_SORT_BY_NAME); + } + else if ("sort_by_date" == command_name) + { + mConversationLogList->sortByDate(); + gSavedSettings.setU32("CallLogSortOrder", LLConversationLogList::E_SORT_BY_DATE); + } + else if ("sort_friends_on_top" == command_name) + { + mConversationLogList->toggleSortFriendsOnTop(); + } + else if ("view_nearby_chat_history" == command_name) + { + LLFloaterReg::showInstance("preview_conversation", LLSD(LLUUID::null), true); + } +} + +bool LLFloaterConversationLog::isActionEnabled(const LLSD& userdata) +{ + return true; +} + +bool LLFloaterConversationLog::isActionChecked(const LLSD& userdata) +{ + const std::string command_name = userdata.asString(); + + U32 sort_order = gSavedSettings.getU32("CallLogSortOrder"); + + if ("sort_by_name" == command_name) + { + return sort_order == LLConversationLogList::E_SORT_BY_NAME; + } + else if ("sort_by_date" == command_name) + { + return sort_order == LLConversationLogList::E_SORT_BY_DATE; + } + else if ("sort_friends_on_top" == command_name) + { + return gSavedSettings.getBOOL("SortFriendsFirst"); + } + + return false; +} + +void LLFloaterConversationLog::onCallLoggingEnabledDisabled(bool enabled) +{ + std::string no_items_msg = enabled ? "" : getString("logging_calls_disabled"); + mConversationLogList->setNoItemsCommentText(no_items_msg); +} diff --git a/indra/newview/llfloaterconversationlog.h b/indra/newview/llfloaterconversationlog.h new file mode 100644 index 0000000000..9e79cbd7d8 --- /dev/null +++ b/indra/newview/llfloaterconversationlog.h @@ -0,0 +1,58 @@ +/** + * @file llfloaterconversationlog.h + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERCONVERSATIONLOG_H_ +#define LL_LLFLOATERCONVERSATIONLOG_H_ + +#include "llfloater.h" + +class LLConversationLogList; + +class LLFloaterConversationLog : public LLFloater +{ +public: + + LLFloaterConversationLog(const LLSD& key); + virtual ~LLFloaterConversationLog(){}; + + virtual BOOL postBuild(); + + virtual void draw(); + + void onFilterEdit(const std::string& search_string); + +private: + + void onCustomAction (const LLSD& userdata); + bool isActionEnabled(const LLSD& userdata); + bool isActionChecked(const LLSD& userdata); + + void onCallLoggingEnabledDisabled(bool enabled); + + LLConversationLogList* mConversationLogList; +}; + + +#endif /* LLFLOATERCONVERSATIONLOG_H_ */ diff --git a/indra/newview/llfloaterconversationpreview.cpp b/indra/newview/llfloaterconversationpreview.cpp new file mode 100644 index 0000000000..a3825eafc8 --- /dev/null +++ b/indra/newview/llfloaterconversationpreview.cpp @@ -0,0 +1,169 @@ +/** + * @file llfloaterconversationpreview.cpp + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llconversationlog.h" +#include "llfloaterconversationpreview.h" +#include "llimview.h" +#include "lllineeditor.h" +#include "llnearbychat.h" +#include "llspinctrl.h" +#include "lltrans.h" + +LLFloaterConversationPreview::LLFloaterConversationPreview(const LLSD& session_id) +: LLFloater(session_id), + mChatHistory(NULL), + mSessionID(session_id.asUUID()), + mCurrentPage(0), + mPageSize(gSavedSettings.getS32("ConversationHistoryPageSize")) +{} + +BOOL LLFloaterConversationPreview::postBuild() +{ + mChatHistory = getChild<LLChatHistory>("chat_history"); + + const LLConversation* conv = LLConversationLog::instance().getConversation(mSessionID); + std::string name; + std::string file; + + if (mSessionID != LLUUID::null && conv) + { + name = conv->getConversationName(); + file = conv->getHistoryFileName(); + } + else + { + name = LLTrans::getString("NearbyChatTitle"); + file = "chat"; + } + + LLStringUtil::format_map_t args; + args["[NAME]"] = name; + std::string title = getString("Title", args); + setTitle(title); + + LLSD load_params; + load_params["load_all_history"] = true; + load_params["cut_off_todays_date"] = false; + + LLLogChat::loadChatHistory(file, mMessages, load_params); + mCurrentPage = mMessages.size() / mPageSize; + + mPageSpinner = getChild<LLSpinCtrl>("history_page_spin"); + mPageSpinner->setCommitCallback(boost::bind(&LLFloaterConversationPreview::onMoreHistoryBtnClick, this)); + mPageSpinner->setMinValue(1); + mPageSpinner->setMaxValue(mCurrentPage + 1); + mPageSpinner->set(mCurrentPage + 1); + + std::string total_page_num = llformat("/ %d", mCurrentPage + 1); + getChild<LLTextBox>("page_num_label")->setValue(total_page_num); + + return LLFloater::postBuild(); +} + +void LLFloaterConversationPreview::draw() +{ + LLFloater::draw(); +} + +void LLFloaterConversationPreview::onOpen(const LLSD& key) +{ + showHistory(); +} + +void LLFloaterConversationPreview::showHistory() +{ + if (!mMessages.size()) + { + return; + } + + mChatHistory->clear(); + + std::ostringstream message; + std::list<LLSD>::const_iterator iter = mMessages.begin(); + + int delta = 0; + if (mCurrentPage) + { + int remainder = mMessages.size() % mPageSize; + delta = (remainder == 0) ? 0 : (mPageSize - remainder); + } + + std::advance(iter, (mCurrentPage * mPageSize) - delta); + + for (int msg_num = 0; (iter != mMessages.end() && msg_num < mPageSize); ++iter, ++msg_num) + { + LLSD msg = *iter; + + LLUUID from_id = LLUUID::null; + std::string time = msg["time"].asString(); + std::string from = msg["from"].asString(); + std::string message = msg["message"].asString(); + + if (msg["from_id"].isDefined()) + { + from_id = msg["from_id"].asUUID(); + } + else + { + std::string legacy_name = gCacheName->buildLegacyName(from); + gCacheName->getUUID(legacy_name, from_id); + } + + LLChat chat; + chat.mFromID = from_id; + chat.mSessionID = mSessionID; + chat.mFromName = from; + chat.mTimeStr = time; + chat.mChatStyle = CHAT_STYLE_HISTORY; + chat.mText = message; + + if (from_id.isNull() && SYSTEM_FROM == from) + { + chat.mSourceType = CHAT_SOURCE_SYSTEM; + + } + else if (from_id.isNull()) + { + chat.mSourceType = LLNearbyChat::isWordsName(from) ? CHAT_SOURCE_UNKNOWN : CHAT_SOURCE_OBJECT; + } + + mChatHistory->appendMessage(chat); + } + +} + +void LLFloaterConversationPreview::onMoreHistoryBtnClick() +{ + mCurrentPage = (int)(mPageSpinner->getValueF32()); + if (--mCurrentPage < 0) + { + return; + } + + showHistory(); +} diff --git a/indra/newview/llfloaterconversationpreview.h b/indra/newview/llfloaterconversationpreview.h new file mode 100644 index 0000000000..0341e5d2a0 --- /dev/null +++ b/indra/newview/llfloaterconversationpreview.h @@ -0,0 +1,59 @@ +/** + * @file llfloaterconversationpreview.h + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLFLOATERCONVERSATIONPREVIEW_H_ +#define LLFLOATERCONVERSATIONPREVIEW_H_ + +#include "llchathistory.h" +#include "llfloater.h" + +class LLSpinCtrl; + +class LLFloaterConversationPreview : public LLFloater +{ +public: + + LLFloaterConversationPreview(const LLSD& session_id); + virtual ~LLFloaterConversationPreview(){}; + + virtual BOOL postBuild(); + + virtual void draw(); + virtual void onOpen(const LLSD& key); + +private: + void onMoreHistoryBtnClick(); + void showHistory(); + + LLSpinCtrl* mPageSpinner; + LLChatHistory* mChatHistory; + LLUUID mSessionID; + int mCurrentPage; + int mPageSize; + + std::list<LLSD> mMessages; +}; + +#endif /* LLFLOATERCONVERSATIONPREVIEW_H_ */ diff --git a/indra/newview/llfloatergodtools.cpp b/indra/newview/llfloatergodtools.cpp index fb905eae11..38abdcc830 100644 --- a/indra/newview/llfloatergodtools.cpp +++ b/indra/newview/llfloatergodtools.cpp @@ -1123,11 +1123,13 @@ bool LLPanelObjectTools::callbackSimWideDeletes( const LLSD& notification, const void LLPanelObjectTools::onClickSet() { - LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLPanelObjectTools::callbackAvatarID, this, _1,_2)); + LLView * button = findChild<LLButton>("Set Target"); + LLFloater * root_floater = gFloaterView->getParentFloater(this); + LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLPanelObjectTools::callbackAvatarID, this, _1,_2), FALSE, FALSE, FALSE, root_floater->getName(), button); // grandparent is a floater, which can have a dependent if (picker) { - gFloaterView->getParentFloater(this)->addDependentFloater(picker); + root_floater->addDependentFloater(picker); } } diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index df8ecb6fd9..a5dd197336 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -1045,6 +1045,8 @@ void LLPanelLandGeneral::onCommitAny(LLUICtrl *ctrl, void *userdata) void LLPanelLandGeneral::onClickSellLand(void* data) { LLViewerParcelMgr::getInstance()->startSellLand(); + LLPanelLandGeneral *panelp = (LLPanelLandGeneral *)data; + panelp->refresh(); } // static @@ -2362,12 +2364,6 @@ LLPanelLandAccess::~LLPanelLandAccess() void LLPanelLandAccess::refresh() { LLFloater* parent_floater = gFloaterView->getParentFloater(this); - - if (mListAccess) - mListAccess->deleteAllItems(); - if (mListBanned) - mListBanned->deleteAllItems(); - LLParcel *parcel = mParcel->getParcel(); // Display options @@ -2385,7 +2381,11 @@ void LLPanelLandAccess::refresh() getChild<LLUICtrl>("GroupCheck")->setLabelArg("[GROUP]", group_name ); // Allow list + if (mListAccess) { + // Clear the sort order so we don't re-sort on every add. + mListAccess->clearSortOrder(); + mListAccess->deleteAllItems(); S32 count = parcel->mAccessList.size(); getChild<LLUICtrl>("AccessList")->setToolTipArg(LLStringExplicit("[LISTED]"), llformat("%d",count)); getChild<LLUICtrl>("AccessList")->setToolTipArg(LLStringExplicit("[MAX]"), llformat("%d",PARCEL_MAX_ACCESS_LIST)); @@ -2420,13 +2420,17 @@ void LLPanelLandAccess::refresh() } suffix.append(" " + parent_floater->getString("Remaining") + ")"); } - if (mListAccess) - mListAccess->addNameItem(entry.mID, ADD_DEFAULT, TRUE, suffix); + mListAccess->addNameItem(entry.mID, ADD_DEFAULT, TRUE, suffix); } + mListAccess->sortByName(TRUE); } // Ban List + if(mListBanned) { + // Clear the sort order so we don't re-sort on every add. + mListBanned->clearSortOrder(); + mListBanned->deleteAllItems(); S32 count = parcel->mBanList.size(); getChild<LLUICtrl>("BannedList")->setToolTipArg(LLStringExplicit("[LISTED]"), llformat("%d",count)); @@ -2464,6 +2468,7 @@ void LLPanelLandAccess::refresh() } mListBanned->addNameItem(entry.mID, ADD_DEFAULT, TRUE, suffix); } + mListBanned->sortByName(TRUE); } if(parcel->getRegionDenyAnonymousOverride()) @@ -2599,13 +2604,13 @@ void LLPanelLandAccess::refresh_ui() getChildView("AccessList")->setEnabled(can_manage_allowed); S32 allowed_list_count = parcel->mAccessList.size(); getChildView("add_allowed")->setEnabled(can_manage_allowed && allowed_list_count < PARCEL_MAX_ACCESS_LIST); - BOOL has_selected = mListAccess->getSelectionInterface()->getFirstSelectedIndex() >= 0; + BOOL has_selected = (mListAccess && mListAccess->getSelectionInterface()->getFirstSelectedIndex() >= 0); getChildView("remove_allowed")->setEnabled(can_manage_allowed && has_selected); getChildView("BannedList")->setEnabled(can_manage_banned); S32 banned_list_count = parcel->mBanList.size(); getChildView("add_banned")->setEnabled(can_manage_banned && banned_list_count < PARCEL_MAX_ACCESS_LIST); - has_selected = mListBanned->getSelectionInterface()->getFirstSelectedIndex() >= 0; + has_selected = (mListBanned && mListBanned->getSelectionInterface()->getFirstSelectedIndex() >= 0); getChildView("remove_banned")->setEnabled(can_manage_banned && has_selected); } } @@ -2730,11 +2735,13 @@ void LLPanelLandAccess::onCommitAny(LLUICtrl *ctrl, void *userdata) void LLPanelLandAccess::onClickAddAccess() { + LLView * button = findChild<LLButton>("add_allowed"); + LLFloater * root_floater = gFloaterView->getParentFloater(this); LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show( - boost::bind(&LLPanelLandAccess::callbackAvatarCBAccess, this, _1)); + boost::bind(&LLPanelLandAccess::callbackAvatarCBAccess, this, _1), FALSE, FALSE, FALSE, root_floater->getName(), button); if (picker) { - gFloaterView->getParentFloater(this)->addDependentFloater(picker); + root_floater->addDependentFloater(picker); } } @@ -2779,11 +2786,13 @@ void LLPanelLandAccess::onClickRemoveAccess(void* data) // static void LLPanelLandAccess::onClickAddBanned() { + LLView * button = findChild<LLButton>("add_banned"); + LLFloater * root_floater = gFloaterView->getParentFloater(this); LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show( - boost::bind(&LLPanelLandAccess::callbackAvatarCBBanned, this, _1)); + boost::bind(&LLPanelLandAccess::callbackAvatarCBBanned, this, _1), FALSE, FALSE, FALSE, root_floater->getName(), button); if (picker) { - gFloaterView->getParentFloater(this)->addDependentFloater(picker); + root_floater->addDependentFloater(picker); } } diff --git a/indra/newview/llfloaternotificationsconsole.cpp b/indra/newview/llfloaternotificationsconsole.cpp index 2681d4b34d..4f35c325a8 100644 --- a/indra/newview/llfloaternotificationsconsole.cpp +++ b/indra/newview/llfloaternotificationsconsole.cpp @@ -44,21 +44,16 @@ public: BOOL postBuild(); private: - bool update(const LLSD& payload, bool passed_filter); + bool update(const LLSD& payload); static void toggleClick(void* user_data); static void onClickNotification(void* user_data); - static void onClickNotificationReject(void* user_data); LLNotificationChannelPtr mChannelPtr; - LLNotificationChannelPtr mChannelRejectsPtr; }; LLNotificationChannelPanel::LLNotificationChannelPanel(const LLNotificationChannelPanel::Params& p) : LLLayoutPanel(p) { mChannelPtr = LLNotifications::instance().getChannel(p.name); - mChannelRejectsPtr = LLNotificationChannelPtr( - LLNotificationChannel::buildChannel(p.name() + "rejects", mChannelPtr->getParentChannelName(), - !boost::bind(mChannelPtr->getFilter(), _1))); buildFromFile( "panel_notifications_channel.xml"); } @@ -68,15 +63,11 @@ BOOL LLNotificationChannelPanel::postBuild() header_button->setLabel(mChannelPtr->getName()); header_button->setClickedCallback(toggleClick, this); - mChannelPtr->connectChanged(boost::bind(&LLNotificationChannelPanel::update, this, _1, true)); - mChannelRejectsPtr->connectChanged(boost::bind(&LLNotificationChannelPanel::update, this, _1, false)); + mChannelPtr->connectChanged(boost::bind(&LLNotificationChannelPanel::update, this, _1)); LLScrollListCtrl* scroll = getChild<LLScrollListCtrl>("notifications_list"); scroll->setDoubleClickCallback(onClickNotification, this); scroll->setRect(LLRect( getRect().mLeft, getRect().mTop, getRect().mRight, 0)); - scroll = getChild<LLScrollListCtrl>("notification_rejects_list"); - scroll->setDoubleClickCallback(onClickNotificationReject, this); - scroll->setRect(LLRect( getRect().mLeft, getRect().mTop, getRect().mRight, 0)); return TRUE; } @@ -97,8 +88,6 @@ void LLNotificationChannelPanel::toggleClick(void *user_data) // turn off tab stop for collapsed panel self->getChild<LLScrollListCtrl>("notifications_list")->setTabStop(!header_button->getToggleState()); self->getChild<LLScrollListCtrl>("notifications_list")->setVisible(!header_button->getToggleState()); - self->getChild<LLScrollListCtrl>("notification_rejects_list")->setTabStop(!header_button->getToggleState()); - self->getChild<LLScrollListCtrl>("notification_rejects_list")->setVisible(!header_button->getToggleState()); } /*static*/ @@ -118,24 +107,7 @@ void LLNotificationChannelPanel::onClickNotification(void* user_data) } } -/*static*/ -void LLNotificationChannelPanel::onClickNotificationReject(void* user_data) -{ - LLNotificationChannelPanel* self = (LLNotificationChannelPanel*)user_data; - if (!self) return; - LLScrollListItem* firstselected = self->getChild<LLScrollListCtrl>("notification_rejects_list")->getFirstSelected(); - llassert(firstselected); - if (firstselected) - { - void* data = firstselected->getUserdata(); - if (data) - { - gFloaterView->getParentFloater(self)->addDependentFloater(new LLFloaterNotification((LLNotification*)data), TRUE); - } - } -} - -bool LLNotificationChannelPanel::update(const LLSD& payload, bool passed_filter) +bool LLNotificationChannelPanel::update(const LLSD& payload) { LLNotificationPtr notification = LLNotifications::instance().find(payload["id"].asUUID()); if (notification) @@ -151,9 +123,7 @@ bool LLNotificationChannelPanel::update(const LLSD& payload, bool passed_filter) row["columns"][2]["column"] = "date"; row["columns"][2]["type"] = "date"; - LLScrollListItem* sli = passed_filter ? - getChild<LLScrollListCtrl>("notifications_list")->addElement(row) : - getChild<LLScrollListCtrl>("notification_rejects_list")->addElement(row); + LLScrollListItem* sli = getChild<LLScrollListCtrl>("notifications_list")->addElement(row); sli->setUserdata(&(*notification)); } diff --git a/indra/newview/llfloateroutbox.cpp b/indra/newview/llfloateroutbox.cpp index 540f977305..18ed36d0f3 100644 --- a/indra/newview/llfloateroutbox.cpp +++ b/indra/newview/llfloateroutbox.cpp @@ -44,14 +44,12 @@ #include "llviewernetwork.h" #include "llwindowshade.h" -#define USE_WINDOWSHADE_DIALOGS 0 - ///---------------------------------------------------------------------------- /// LLOutboxNotification class ///---------------------------------------------------------------------------- -bool LLNotificationsUI::LLOutboxNotification::processNotification(const LLSD& notify) +bool LLNotificationsUI::LLOutboxNotification::processNotification(const LLNotificationPtr& notify) { LLFloaterOutbox* outbox_floater = LLFloaterReg::getTypedInstance<LLFloaterOutbox>("outbox"); @@ -60,6 +58,14 @@ bool LLNotificationsUI::LLOutboxNotification::processNotification(const LLSD& no return false; } +void LLNotificationsUI::LLOutboxNotification::onDelete(LLNotificationPtr p) +{ + LLNotificationsUI::LLSysHandler * sys_handler = dynamic_cast<LLNotificationsUI::LLSysHandler*>(LLNotifications::instance().getChannel("AlertModal").get()); + if (sys_handler) + { + sys_handler->onDelete(p); + } +} ///---------------------------------------------------------------------------- /// LLOutboxAddedObserver helper class @@ -168,9 +174,8 @@ void LLFloaterOutbox::onOpen(const LLSD& key) if (mOutboxId.isNull()) { const bool do_not_create_folder = false; - const bool do_not_find_in_library = false; - const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, do_not_create_folder, do_not_find_in_library); + const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, do_not_create_folder); if (outbox_id.isNull()) { @@ -244,8 +249,9 @@ void LLFloaterOutbox::setupOutbox(const LLUUID& outboxId) mOutboxInventoryPanel->setShape(inventory_placeholder_rect); // Set the sort order newest to oldest - mOutboxInventoryPanel->setSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME); - mOutboxInventoryPanel->getFilter()->markDefault(); + + mOutboxInventoryPanel->getFolderViewModel()->setSorter(LLInventoryFilter::SO_FOLDERS_BY_NAME); + mOutboxInventoryPanel->getFilter().markDefault(); fetchOutboxContents(); @@ -380,7 +386,7 @@ BOOL LLFloaterOutbox::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, // Determine if the mouse is inside the inventory panel itself or just within the floater bool pointInInventoryPanel = false; bool pointInInventoryPanelChild = false; - LLFolderView * root_folder = mOutboxInventoryPanel->getRootFolder(); + LLFolderView* root_folder = mOutboxInventoryPanel->getRootFolder(); if (mOutboxInventoryPanel->getVisible()) { S32 inv_x, inv_y; @@ -437,10 +443,10 @@ void LLFloaterOutbox::onOutboxChanged() { llassert(!mOutboxId.isNull()); - if (mOutboxInventoryPanel) - { - mOutboxInventoryPanel->requestSort(); - } + //if (mOutboxInventoryPanel) + //{ + // mOutboxInventoryPanel->requestSort(); + //} fetchOutboxContents(); @@ -516,52 +522,11 @@ void LLFloaterOutbox::initializationReportError(U32 status, const LLSD& content) updateView(); } -void LLFloaterOutbox::showNotification(const LLSD& notify) +void LLFloaterOutbox::showNotification(const LLNotificationPtr& notification) { - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if (!notification) - { - llerrs << "Unable to find outbox notification!" << notify.asString() << llendl; - - return; - } - -#if USE_WINDOWSHADE_DIALOGS - - if (mWindowShade) - { - delete mWindowShade; - } - - LLRect floater_rect = getLocalRect(); - floater_rect.mTop -= getHeaderHeight(); - floater_rect.stretch(-5, 0); - - LLWindowShade::Params params; - params.name = "notification_shade"; - params.rect = floater_rect; - params.follows.flags = FOLLOWS_ALL; - params.modal = true; - params.can_close = false; - params.shade_color = LLColor4::white % 0.25f; - params.text_color = LLColor4::white; - - mWindowShade = LLUICtrlFactory::create<LLWindowShade>(params); - - addChild(mWindowShade); - mWindowShade->show(notification); - -#else - - LLNotificationsUI::LLEventHandler * handler = - LLNotificationsUI::LLNotificationManager::instance().getHandlerForNotification("alertmodal"); - - LLNotificationsUI::LLSysHandler * sys_handler = dynamic_cast<LLNotificationsUI::LLSysHandler *>(handler); + LLNotificationsUI::LLSysHandler * sys_handler = dynamic_cast<LLNotificationsUI::LLSysHandler*>(LLNotifications::instance().getChannel("AlertModal").get()); llassert(sys_handler); - sys_handler->processNotification(notify); - -#endif + sys_handler->processNotification(notification); } diff --git a/indra/newview/llfloateroutbox.h b/indra/newview/llfloateroutbox.h index 18baccf1c9..a91d8c1139 100644 --- a/indra/newview/llfloateroutbox.h +++ b/indra/newview/llfloateroutbox.h @@ -64,7 +64,7 @@ public: EAcceptance* accept, std::string& tooltip_msg); - void showNotification(const LLSD& notify); + void showNotification(const LLNotificationPtr& notification); BOOL handleHover(S32 x, S32 y, MASK mask); void onMouseLeave(S32 x, S32 y, MASK mask); diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 5752f839ce..c78a803bf3 100755 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -425,13 +425,7 @@ void LLFloaterPreference::saveAvatarProperties( void ) BOOL LLFloaterPreference::postBuild() { - gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLIMFloater::processChatHistoryStyleUpdate, _2)); - - gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLNearbyChat::processChatHistoryStyleUpdate, _2)); - - gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLIMFloater::processChatHistoryStyleUpdate, _2)); - - gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLNearbyChat::processChatHistoryStyleUpdate, _2)); + gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLIMConversation::processChatHistoryStyleUpdate)); gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLViewerChat::signalChatFontChanged)); @@ -461,14 +455,11 @@ BOOL LLFloaterPreference::postBuild() void LLFloaterPreference::onBusyResponseChanged() { // set "BusyResponseChanged" TRUE if user edited message differs from default, FALSE otherwise - if (LLTrans::getString("BusyModeResponseDefault") != getChild<LLUICtrl>("busy_response")->getValue().asString()) - { - gSavedPerAccountSettings.setBOOL("BusyResponseChanged", TRUE ); - } - else - { - gSavedPerAccountSettings.setBOOL("BusyResponseChanged", FALSE ); - } + bool busy_flag = + LLTrans::getString("BusyModeResponseDefault") + != getChild<LLUICtrl>("busy_response")->getValue().asString(); + + gSavedPerAccountSettings.setBOOL("BusyResponseChanged", busy_flag ); } LLFloaterPreference::~LLFloaterPreference() @@ -553,8 +544,6 @@ void LLFloaterPreference::apply() // LLWString busy_response = utf8str_to_wstring(getChild<LLUICtrl>("busy_response")->getValue().asString()); // LLWStringUtil::replaceTabsWithSpaces(busy_response, 4); - - gSavedSettings.setBOOL("PlainTextChatHistory", getChild<LLUICtrl>("plain_text_chat_history")->getValue().asBoolean()); if (mGotPersonalInfo) { @@ -1440,8 +1429,6 @@ void LLFloaterPreference::setPersonalInfo(const std::string& visibility, bool im getChild<LLUICtrl>("online_visibility")->setLabelArg("[DIR_VIS]", mDirectoryVisibility); getChildView("send_im_to_email")->setEnabled(TRUE); getChild<LLUICtrl>("send_im_to_email")->setValue(im_via_email); - getChildView("plain_text_chat_history")->setEnabled(TRUE); - getChild<LLUICtrl>("plain_text_chat_history")->setValue(gSavedSettings.getBOOL("PlainTextChatHistory")); getChildView("log_instant_messages")->setEnabled(TRUE); // getChildView("log_chat")->setEnabled(TRUE); // getChildView("busy_response")->setEnabled(TRUE); @@ -1523,7 +1510,8 @@ void LLFloaterPreference::onChangeMaturity() // but the UI for this will still be enabled void LLFloaterPreference::onClickBlockList() { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); + LLFloaterSidePanelContainer::showPanel("people", "panel_people", + LLSD().with("people_panel_tab_name", "blocked_panel")); } void LLFloaterPreference::onClickProxySettings() diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 17850ff35d..e6b76159a1 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -287,8 +287,7 @@ void LLFloaterRegionInfo::processEstateOwnerRequest(LLMessageSystem* msg,void**) //dispatch the message dispatch.dispatch(request, invoice, strings); - LLViewerRegion* region = gAgent.getRegion(); - panel->updateControls(region); + panel->updateControls(gAgent.getRegion()); } @@ -648,8 +647,10 @@ void LLPanelRegionGeneralInfo::onClickKick() // this depends on the grandparent view being a floater // in order to set up floater dependency + LLView * button = findChild<LLButton>("kick_btn"); LLFloater* parent_floater = gFloaterView->getParentFloater(this); - LLFloater* child_floater = LLFloaterAvatarPicker::show(boost::bind(&LLPanelRegionGeneralInfo::onKickCommit, this, _1), FALSE, TRUE); + LLFloater* child_floater = LLFloaterAvatarPicker::show(boost::bind(&LLPanelRegionGeneralInfo::onKickCommit, this, _1), + FALSE, TRUE, FALSE, parent_floater->getName(), button); if (child_floater) { parent_floater->addDependentFloater(child_floater); @@ -925,7 +926,14 @@ BOOL LLPanelRegionDebugInfo::sendUpdate() void LLPanelRegionDebugInfo::onClickChooseAvatar() { - LLFloaterAvatarPicker::show(boost::bind(&LLPanelRegionDebugInfo::callbackAvatarID, this, _1, _2), FALSE, TRUE); + LLView * button = findChild<LLButton>("choose_avatar_btn"); + LLFloater* parent_floater = gFloaterView->getParentFloater(this); + LLFloater * child_floater = LLFloaterAvatarPicker::show(boost::bind(&LLPanelRegionDebugInfo::callbackAvatarID, this, _1, _2), + FALSE, TRUE, FALSE, parent_floater->getName(), button); + if (child_floater) + { + parent_floater->addDependentFloater(child_floater); + } } @@ -1471,8 +1479,10 @@ void LLPanelEstateInfo::onClickKickUser() { // this depends on the grandparent view being a floater // in order to set up floater dependency + LLView * button = findChild<LLButton>("kick_user_from_estate_btn"); LLFloater* parent_floater = gFloaterView->getParentFloater(this); - LLFloater* child_floater = LLFloaterAvatarPicker::show(boost::bind(&LLPanelEstateInfo::onKickUserCommit, this, _1), FALSE, TRUE); + LLFloater* child_floater = LLFloaterAvatarPicker::show(boost::bind(&LLPanelEstateInfo::onKickUserCommit, this, _1), + FALSE, TRUE, FALSE, parent_floater->getName(), button); if (child_floater) { parent_floater->addDependentFloater(child_floater); @@ -1647,8 +1657,39 @@ bool LLPanelEstateInfo::accessAddCore2(const LLSD& notification, const LLSD& res } LLEstateAccessChangeInfo* change_info = new LLEstateAccessChangeInfo(notification["payload"]); + //Get parent floater name + LLPanelEstateInfo* panel = LLFloaterRegionInfo::getPanelEstate(); + LLFloater* parent_floater = panel ? gFloaterView->getParentFloater(panel) : NULL; + const std::string& parent_floater_name = parent_floater ? parent_floater->getName() : ""; + + //Determine the button that triggered opening of the avatar picker + //(so that a shadow frustum from the button to the avatar picker can be created) + LLView * button = NULL; + switch(change_info->mOperationFlag) + { + case ESTATE_ACCESS_ALLOWED_AGENT_ADD: + button = panel->findChild<LLButton>("add_allowed_avatar_btn"); + break; + + case ESTATE_ACCESS_BANNED_AGENT_ADD: + button = panel->findChild<LLButton>("add_banned_avatar_btn"); + break; + + case ESTATE_ACCESS_MANAGER_ADD: + button = panel->findChild<LLButton>("add_estate_manager_btn"); + break; + } + // avatar picker yes multi-select, yes close-on-select - LLFloaterAvatarPicker::show(boost::bind(&LLPanelEstateInfo::accessAddCore3, _1, (void*)change_info), TRUE, TRUE); + LLFloater* child_floater = LLFloaterAvatarPicker::show(boost::bind(&LLPanelEstateInfo::accessAddCore3, _1, (void*)change_info), + TRUE, TRUE, FALSE, parent_floater_name, button); + + //Allows the closed parent floater to close the child floater (avatar picker) + if (child_floater) + { + parent_floater->addDependentFloater(child_floater); + } + return false; } @@ -1924,10 +1965,18 @@ void LLPanelEstateInfo::updateControls(LLViewerRegion* region) BOOL manager = (region && region->isEstateManager()); setCtrlsEnabled(god || owner || manager); + BOOL has_allowed_avatar = getChild<LLNameListCtrl>("allowed_avatar_name_list")->getFirstSelected() ? TRUE : FALSE; + BOOL has_allowed_group = getChild<LLNameListCtrl>("allowed_group_name_list")->getFirstSelected() ? TRUE : FALSE; + BOOL has_banned_agent = getChild<LLNameListCtrl>("banned_avatar_name_list")->getFirstSelected() ? TRUE : FALSE; + BOOL has_estate_manager = getChild<LLNameListCtrl>("estate_manager_name_list")->getFirstSelected() ? TRUE : FALSE; + getChildView("add_allowed_avatar_btn")->setEnabled(god || owner || manager); - getChildView("remove_allowed_avatar_btn")->setEnabled(god || owner || manager); + getChildView("remove_allowed_avatar_btn")->setEnabled(has_allowed_avatar && (god || owner || manager)); + getChildView("allowed_avatar_name_list")->setEnabled(god || owner || manager); + getChildView("add_allowed_group_btn")->setEnabled(god || owner || manager); - getChildView("remove_allowed_group_btn")->setEnabled(god || owner || manager); + getChildView("remove_allowed_group_btn")->setEnabled(has_allowed_group && (god || owner || manager) ); + getChildView("allowed_group_name_list")->setEnabled(god || owner || manager); // Can't ban people from mainland, orientation islands, etc. because this // creates much network traffic and server load. @@ -1935,14 +1984,15 @@ void LLPanelEstateInfo::updateControls(LLViewerRegion* region) bool linden_estate = isLindenEstate(); bool enable_ban = (god || owner || manager) && !linden_estate; getChildView("add_banned_avatar_btn")->setEnabled(enable_ban); - getChildView("remove_banned_avatar_btn")->setEnabled(enable_ban); + getChildView("remove_banned_avatar_btn")->setEnabled(has_banned_agent && enable_ban); + getChildView("banned_avatar_name_list")->setEnabled(god || owner || manager); getChildView("message_estate_btn")->setEnabled(god || owner || manager); getChildView("kick_user_from_estate_btn")->setEnabled(god || owner || manager); // estate managers can't add estate managers getChildView("add_estate_manager_btn")->setEnabled(god || owner); - getChildView("remove_estate_manager_btn")->setEnabled(god || owner); + getChildView("remove_estate_manager_btn")->setEnabled(has_estate_manager && (god || owner)); getChildView("estate_manager_name_list")->setEnabled(god || owner); refresh(); @@ -1979,10 +2029,8 @@ bool LLPanelEstateInfo::refreshFromRegion(LLViewerRegion* region) void LLPanelEstateInfo::updateChild(LLUICtrl* child_ctrl) { - if (checkRemovalButton(child_ctrl->getName())) - { - // do nothing - } + // Ensure appropriate state of the management ui. + updateControls(gAgent.getRegion()); } bool LLPanelEstateInfo::estateUpdate(LLMessageSystem* msg) @@ -2080,23 +2128,8 @@ void LLPanelEstateInfo::refreshFromEstate() getChild<LLUICtrl>("limit_payment")->setValue(estate_info.getDenyAnonymous()); getChild<LLUICtrl>("limit_age_verified")->setValue(estate_info.getDenyAgeUnverified()); - // If visible from mainland, disable the access allowed - // UI, as anyone can teleport there. - // However, gods need to be able to edit the access list for - // linden estates, regardless of visibility, to allow object - // and L$ transfers. - { - bool visible_from_mainland = estate_info.getIsExternallyVisible(); - bool god = gAgent.isGodlike(); - bool linden_estate = isLindenEstate(); - - bool enable_agent = (!visible_from_mainland || (god && linden_estate)); - bool enable_group = enable_agent; - bool enable_ban = !linden_estate; - - setAccessAllowedEnabled(enable_agent, enable_group, enable_ban); - } - + // Ensure appriopriate state of the management UI + updateControls(gAgent.getRegion()); refresh(); } @@ -2225,47 +2258,6 @@ void LLPanelEstateInfo::setOwnerName(const std::string& name) getChild<LLUICtrl>("estate_owner")->setValue(LLSD(name)); } -void LLPanelEstateInfo::setAccessAllowedEnabled(bool enable_agent, - bool enable_group, - bool enable_ban) -{ - getChildView("allow_resident_label")->setEnabled(enable_agent); - getChildView("allowed_avatar_name_list")->setEnabled(enable_agent); - getChildView("allowed_avatar_name_list")->setVisible( enable_agent); - getChildView("add_allowed_avatar_btn")->setEnabled(enable_agent); - getChildView("remove_allowed_avatar_btn")->setEnabled(enable_agent); - - // Groups - getChildView("allow_group_label")->setEnabled(enable_group); - getChildView("allowed_group_name_list")->setEnabled(enable_group); - getChildView("allowed_group_name_list")->setVisible( enable_group); - getChildView("add_allowed_group_btn")->setEnabled(enable_group); - getChildView("remove_allowed_group_btn")->setEnabled(enable_group); - - // Ban - getChildView("ban_resident_label")->setEnabled(enable_ban); - getChildView("banned_avatar_name_list")->setEnabled(enable_ban); - getChildView("banned_avatar_name_list")->setVisible( enable_ban); - getChildView("add_banned_avatar_btn")->setEnabled(enable_ban); - getChildView("remove_banned_avatar_btn")->setEnabled(enable_ban); - - // Update removal buttons if needed - if (enable_agent) - { - checkRemovalButton("allowed_avatar_name_list"); - } - - if (enable_group) - { - checkRemovalButton("allowed_group_name_list"); - } - - if (enable_ban) - { - checkRemovalButton("banned_avatar_name_list"); - } -} - void LLPanelEstateInfo::clearAccessLists() { LLNameListCtrl* name_list = getChild<LLNameListCtrl>("allowed_avatar_name_list"); @@ -2279,39 +2271,7 @@ void LLPanelEstateInfo::clearAccessLists() { name_list->deleteAllItems(); } -} - -// enables/disables the "remove" button for the various allow/ban lists -BOOL LLPanelEstateInfo::checkRemovalButton(std::string name) -{ - std::string btn_name = ""; - if (name == "allowed_avatar_name_list") - { - btn_name = "remove_allowed_avatar_btn"; - } - else if (name == "allowed_group_name_list") - { - btn_name = "remove_allowed_group_btn"; - } - else if (name == "banned_avatar_name_list") - { - btn_name = "remove_banned_avatar_btn"; - } - else if (name == "estate_manager_name_list") - { - //ONLY OWNER CAN ADD /DELET ESTATE MANAGER - LLViewerRegion* region = gAgent.getRegion(); - if (region && (region->getOwner() == gAgent.getID())) - { - btn_name = "remove_estate_manager_btn"; - } - } - - // enable the remove button if something is selected - LLNameListCtrl* name_list = getChild<LLNameListCtrl>(name); - getChildView(btn_name)->setEnabled(name_list && name_list->getFirstSelected() ? TRUE : FALSE); - - return (btn_name != ""); + updateControls(gAgent.getRegion()); } // static @@ -2792,15 +2752,15 @@ bool LLDispatchSetEstateAccess::operator()( if (allowed_agent_name_list) { - //allowed_agent_name_list->deleteAllItems(); + // Don't sort these as we add them, sort them when we are done. + allowed_agent_name_list->clearSortOrder(); for (S32 i = 0; i < num_allowed_agents && i < ESTATE_MAX_ACCESS_IDS; i++) { LLUUID id; memcpy(id.mData, strings[index++].data(), UUID_BYTES); /* Flawfinder: ignore */ allowed_agent_name_list->addNameItem(id); } - panel->getChildView("remove_allowed_avatar_btn")->setEnabled(allowed_agent_name_list->getFirstSelected() ? TRUE : FALSE); - allowed_agent_name_list->sortByColumnIndex(0, TRUE); + allowed_agent_name_list->sortByName(TRUE); } } @@ -2817,6 +2777,8 @@ bool LLDispatchSetEstateAccess::operator()( if (allowed_group_name_list) { + // Don't sort these as we add them, sort them when we are done. + allowed_group_name_list->clearSortOrder(); allowed_group_name_list->deleteAllItems(); for (S32 i = 0; i < num_allowed_groups && i < ESTATE_MAX_GROUP_IDS; i++) { @@ -2824,8 +2786,7 @@ bool LLDispatchSetEstateAccess::operator()( memcpy(id.mData, strings[index++].data(), UUID_BYTES); /* Flawfinder: ignore */ allowed_group_name_list->addGroupNameItem(id); } - panel->getChildView("remove_allowed_group_btn")->setEnabled(allowed_group_name_list->getFirstSelected() ? TRUE : FALSE); - allowed_group_name_list->sortByColumnIndex(0, TRUE); + allowed_group_name_list->sortByName(TRUE); } } @@ -2849,15 +2810,16 @@ bool LLDispatchSetEstateAccess::operator()( if (banned_agent_name_list) { - //banned_agent_name_list->deleteAllItems(); + // Don't sort these as we add them, sort them when we are done. + banned_agent_name_list->clearSortOrder(); + for (S32 i = 0; i < num_banned_agents && i < ESTATE_MAX_ACCESS_IDS; i++) { LLUUID id; memcpy(id.mData, strings[index++].data(), UUID_BYTES); /* Flawfinder: ignore */ banned_agent_name_list->addNameItem(id); } - panel->getChildView("remove_banned_avatar_btn")->setEnabled(banned_agent_name_list->getFirstSelected() ? TRUE : FALSE); - banned_agent_name_list->sortByColumnIndex(0, TRUE); + banned_agent_name_list->sortByName(TRUE); } } @@ -2872,6 +2834,9 @@ bool LLDispatchSetEstateAccess::operator()( panel->getChild<LLNameListCtrl>("estate_manager_name_list"); if (estate_manager_name_list) { + // Don't sort these as we add them, sort them when we are done. + estate_manager_name_list->clearSortOrder(); + estate_manager_name_list->deleteAllItems(); // Clear existing entries // There should be only ESTATE_MAX_MANAGERS people in the list, but if the database gets more (SL-46107) don't @@ -2883,11 +2848,13 @@ bool LLDispatchSetEstateAccess::operator()( memcpy(id.mData, strings[index++].data(), UUID_BYTES); /* Flawfinder: ignore */ estate_manager_name_list->addNameItem(id); } - panel->getChildView("remove_estate_manager_btn")->setEnabled(estate_manager_name_list->getFirstSelected() ? TRUE : FALSE); - estate_manager_name_list->sortByColumnIndex(0, TRUE); + estate_manager_name_list->sortByName(TRUE); } } + // Update the buttons which may change based on the list contents but also needs to account for general access features. + panel->updateControls(gAgent.getRegion()); + return true; } diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h index e36ef4604b..f0499f1903 100644 --- a/indra/newview/llfloaterregioninfo.h +++ b/indra/newview/llfloaterregioninfo.h @@ -312,9 +312,6 @@ public: const std::string getOwnerName() const; void setOwnerName(const std::string& name); - // If visible from mainland, allowed agent and allowed groups - // are ignored, so must disable UI. - void setAccessAllowedEnabled(bool enable_agent, bool enable_group, bool enable_ban); protected: virtual BOOL sendUpdate(); // confirmation dialog callback @@ -324,7 +321,6 @@ protected: void commitEstateManagers(); void clearAccessLists(); - BOOL checkRemovalButton(std::string name); BOOL checkSunHourSlider(LLUICtrl* child_ctrl); U32 mEstateID; diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index 3ec1e372eb..cf2481f99a 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -285,10 +285,13 @@ void LLFloaterReporter::getObjectInfo(const LLUUID& object_id) void LLFloaterReporter::onClickSelectAbuser() { - LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLFloaterReporter::callbackAvatarID, this, _1, _2), FALSE, TRUE ); + LLView * button = findChild<LLButton>("select_abuser", TRUE); + + LLFloater * root_floater = gFloaterView->getParentFloater(this); + LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLFloaterReporter::callbackAvatarID, this, _1, _2), FALSE, TRUE, FALSE, root_floater->getName(), button); if (picker) { - gFloaterView->getParentFloater(this)->addDependentFloater(picker); + root_floater->addDependentFloater(picker); } } diff --git a/indra/newview/llfloatersellland.cpp b/indra/newview/llfloatersellland.cpp index 64c0dfa023..484ecbcd04 100644 --- a/indra/newview/llfloatersellland.cpp +++ b/indra/newview/llfloatersellland.cpp @@ -392,7 +392,8 @@ void LLFloaterSellLandUI::onChangeValue(LLUICtrl *ctrl, void *userdata) void LLFloaterSellLandUI::doSelectAgent() { - LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLFloaterSellLandUI::callbackAvatarPick, this, _1, _2), FALSE, TRUE); + LLView * button = findChild<LLView>("sell_to_select_agent"); + LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLFloaterSellLandUI::callbackAvatarPick, this, _1, _2), FALSE, TRUE, FALSE, this->getName(), button); // grandparent is a floater, in order to set up dependency if (picker) { diff --git a/indra/newview/llfloatersidepanelcontainer.cpp b/indra/newview/llfloatersidepanelcontainer.cpp index 5385977d95..96b5c6b09b 100644 --- a/indra/newview/llfloatersidepanelcontainer.cpp +++ b/indra/newview/llfloatersidepanelcontainer.cpp @@ -61,7 +61,7 @@ LLPanel* LLFloaterSidePanelContainer::openChildPanel(const std::string& panel_na if (!getVisible()) { - openFloater(); + openFloater(); } LLPanel* panel = NULL; @@ -69,10 +69,7 @@ LLPanel* LLFloaterSidePanelContainer::openChildPanel(const std::string& panel_na LLSideTrayPanelContainer* container = dynamic_cast<LLSideTrayPanelContainer*>(view->getParent()); if (container) { - LLSD new_params = params; - new_params[LLSideTrayPanelContainer::PARAM_SUB_PANEL_NAME] = panel_name; - container->onOpen(new_params); - + container->openPanel(panel_name, params); panel = container->getCurrentPanel(); } else if ((panel = dynamic_cast<LLPanel*>(view)) != NULL) diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp index 48484786f6..7cf358c8e5 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -657,8 +657,8 @@ void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask) mBtnEdit ->setToggleState( edit_visible ); mRadioGroupEdit->setVisible( edit_visible ); - bool linked_parts = gSavedSettings.getBOOL("EditLinkedParts"); - getChildView("RenderingCost")->setVisible( !linked_parts && (edit_visible || focus_visible || move_visible) && sShowObjectCost); + //bool linked_parts = gSavedSettings.getBOOL("EditLinkedParts"); + //getChildView("RenderingCost")->setVisible( !linked_parts && (edit_visible || focus_visible || move_visible) && sShowObjectCost); mBtnLink->setVisible(edit_visible); mBtnUnlink->setVisible(edit_visible); diff --git a/indra/newview/llfloatertopobjects.cpp b/indra/newview/llfloatertopobjects.cpp index 87d048c15b..2d91a61b54 100644 --- a/indra/newview/llfloatertopobjects.cpp +++ b/indra/newview/llfloatertopobjects.cpp @@ -82,6 +82,7 @@ LLFloaterTopObjects::LLFloaterTopObjects(const LLSD& key) mCommitCallbackRegistrar.add("TopObjects.Refresh", boost::bind(&LLFloaterTopObjects::onRefresh, this)); mCommitCallbackRegistrar.add("TopObjects.GetByObjectName", boost::bind(&LLFloaterTopObjects::onGetByObjectName, this)); mCommitCallbackRegistrar.add("TopObjects.GetByOwnerName", boost::bind(&LLFloaterTopObjects::onGetByOwnerName, this)); + mCommitCallbackRegistrar.add("TopObjects.GetByParcelName", boost::bind(&LLFloaterTopObjects::onGetByParcelName, this)); mCommitCallbackRegistrar.add("TopObjects.CommitObjectsList",boost::bind(&LLFloaterTopObjects::onCommitObjectsList, this)); } @@ -99,21 +100,6 @@ BOOL LLFloaterTopObjects::postBuild() setDefaultBtn("show_beacon_btn"); - /* - LLLineEditor* line_editor = getChild<LLLineEditor>("owner_name_editor"); - if (line_editor) - { - line_editor->setCommitOnFocusLost(FALSE); - line_editor->setCommitCallback(onGetByOwnerName, this); - } - - line_editor = getChild<LLLineEditor>("object_name_editor"); - if (line_editor) - { - line_editor->setCommitOnFocusLost(FALSE); - line_editor->setCommitCallback(onGetByObjectName, this); - }*/ - mCurrentMode = STAT_REPORT_TOP_SCRIPTS; mFlags = 0; mFilter.clear(); @@ -168,9 +154,11 @@ void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data) F32 score; std::string name_buf; std::string owner_buf; + std::string parcel_buf("unknown"); F32 mono_score = 0.f; bool have_extended_data = false; S32 public_urls = 0; + F32 script_memory = 0.f; msg->getU32Fast(_PREHASH_ReportData, _PREHASH_TaskLocalID, task_local_id, block); msg->getUUIDFast(_PREHASH_ReportData, _PREHASH_TaskID, task_id, block); @@ -180,12 +168,18 @@ void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data) msg->getF32Fast(_PREHASH_ReportData, _PREHASH_Score, score, block); msg->getStringFast(_PREHASH_ReportData, _PREHASH_TaskName, name_buf, block); msg->getStringFast(_PREHASH_ReportData, _PREHASH_OwnerName, owner_buf, block); + if(msg->has("DataExtended")) { have_extended_data = true; msg->getU32("DataExtended", "TimeStamp", time_stamp, block); msg->getF32("DataExtended", "MonoScore", mono_score, block); msg->getS32("DataExtended", "PublicURLs", public_urls, block); + if (msg->getSize("DataExtended", "ParcelName") > 0) + { + msg->getString("DataExtended", "ParcelName", parcel_buf, block); + msg->getF32("DataExtended", "Size", script_memory, block); + } } LLSD element; @@ -193,13 +187,14 @@ void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data) element["id"] = task_id; LLSD columns; - columns[0]["column"] = "score"; - columns[0]["value"] = llformat("%0.3f", score); - columns[0]["font"] = "SANSSERIF"; + S32 column_num = 0; + columns[column_num]["column"] = "score"; + columns[column_num]["value"] = llformat("%0.3f", score); + columns[column_num++]["font"] = "SANSSERIF"; - columns[1]["column"] = "name"; - columns[1]["value"] = name_buf; - columns[1]["font"] = "SANSSERIF"; + columns[column_num]["column"] = "name"; + columns[column_num]["value"] = name_buf; + columns[column_num++]["font"] = "SANSSERIF"; // Owner names can have trailing spaces sent from server LLStringUtil::trim(owner_buf); @@ -215,28 +210,33 @@ void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data) // ...just strip out legacy "Resident" name owner_buf = LLCacheName::cleanFullName(owner_buf); } - columns[2]["column"] = "owner"; - columns[2]["value"] = owner_buf; - columns[2]["font"] = "SANSSERIF"; - - columns[3]["column"] = "location"; - columns[3]["value"] = llformat("<%0.1f,%0.1f,%0.1f>", location_x, location_y, location_z); - columns[3]["font"] = "SANSSERIF"; - columns[4]["column"] = "time"; - columns[4]["type"] = "date"; - columns[4]["value"] = LLDate((time_t)time_stamp); - columns[4]["font"] = "SANSSERIF"; + columns[column_num]["column"] = "owner"; + columns[column_num]["value"] = owner_buf; + columns[column_num++]["font"] = "SANSSERIF"; + + columns[column_num]["column"] = "location"; + columns[column_num]["value"] = llformat("<%0.1f,%0.1f,%0.1f>", location_x, location_y, location_z); + columns[column_num++]["font"] = "SANSSERIF"; + + columns[column_num]["column"] = "parcel"; + columns[column_num]["value"] = parcel_buf; + columns[column_num++]["font"] = "SANSSERIF"; + + columns[column_num]["column"] = "time"; + columns[column_num]["type"] = "date"; + columns[column_num]["value"] = LLDate((time_t)time_stamp); + columns[column_num++]["font"] = "SANSSERIF"; if (mCurrentMode == STAT_REPORT_TOP_SCRIPTS && have_extended_data) { - columns[5]["column"] = "mono_time"; - columns[5]["value"] = llformat("%0.3f", mono_score); - columns[5]["font"] = "SANSSERIF"; + columns[column_num]["column"] = "memory"; + columns[column_num]["value"] = llformat("%0.0f", (script_memory / 1000.f)); + columns[column_num++]["font"] = "SANSSERIF"; - columns[6]["column"] = "URLs"; - columns[6]["value"] = llformat("%d", public_urls); - columns[6]["font"] = "SANSSERIF"; + columns[column_num]["column"] = "URLs"; + columns[column_num]["value"] = llformat("%d", public_urls); + columns[column_num++]["font"] = "SANSSERIF"; } element["columns"] = columns; list->addElement(element); @@ -260,18 +260,18 @@ void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data) { setTitle(getString("top_scripts_title")); list->setColumnLabel("score", getString("scripts_score_label")); - list->setColumnLabel("mono_time", getString("scripts_mono_time_label")); LLUIString format = getString("top_scripts_text"); format.setArg("[COUNT]", llformat("%d", total_count)); - format.setArg("[TIME]", llformat("%0.1f", mtotalScore)); + format.setArg("[TIME]", llformat("%0.3f", mtotalScore)); getChild<LLUICtrl>("title_text")->setValue(LLSD(format)); } else { setTitle(getString("top_colliders_title")); list->setColumnLabel("score", getString("colliders_score_label")); - list->setColumnLabel("mono_time", ""); + list->setColumnLabel("URLs", ""); + list->setColumnLabel("memory", ""); LLUIString format = getString("top_colliders_text"); format.setArg("[COUNT]", llformat("%d", total_count)); getChild<LLUICtrl>("title_text")->setValue(LLSD(format)); @@ -301,6 +301,7 @@ void LLFloaterTopObjects::updateSelectionInfo() { getChild<LLUICtrl>("object_name_editor")->setValue(sli->getColumn(1)->getValue().asString()); getChild<LLUICtrl>("owner_name_editor")->setValue(sli->getColumn(2)->getValue().asString()); + getChild<LLUICtrl>("parcel_name_editor")->setValue(sli->getColumn(4)->getValue().asString()); } } @@ -480,6 +481,15 @@ void LLFloaterTopObjects::onGetByOwnerName() onRefresh(); } + +void LLFloaterTopObjects::onGetByParcelName() +{ + mFlags = STAT_FILTER_BY_PARCEL_NAME; + mFilter = getChild<LLUICtrl>("parcel_name_editor")->getValue().asString(); + onRefresh(); +} + + void LLFloaterTopObjects::showBeacon() { LLScrollListCtrl* list = getChild<LLScrollListCtrl>("objects_list"); diff --git a/indra/newview/llfloatertopobjects.h b/indra/newview/llfloatertopobjects.h index a608ca20f1..6edc46cf79 100644 --- a/indra/newview/llfloatertopobjects.h +++ b/indra/newview/llfloatertopobjects.h @@ -73,9 +73,7 @@ private: void onGetByOwnerName(); void onGetByObjectName(); - -// static void onGetByOwnerNameClicked(void* data) { onGetByOwnerName(NULL, data); }; -// static void onGetByObjectNameClicked(void* data) { onGetByObjectName(NULL, data); }; + void onGetByParcelName(); void showBeacon(); diff --git a/indra/newview/llfloatertranslationsettings.cpp b/indra/newview/llfloatertranslationsettings.cpp index 1a17183efd..29d7732a68 100644 --- a/indra/newview/llfloatertranslationsettings.cpp +++ b/indra/newview/llfloatertranslationsettings.cpp @@ -29,7 +29,7 @@ #include "llfloatertranslationsettings.h" // Viewer includes -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "lltranslate.h" #include "llviewercontrol.h" // for gSavedSettings @@ -293,6 +293,7 @@ void LLFloaterTranslationSettings::onBtnOK() gSavedSettings.setString("TranslationService", getSelectedService()); gSavedSettings.setString("BingTranslateAPIKey", getEnteredBingKey()); gSavedSettings.setString("GoogleTranslateAPIKey", getEnteredGoogleKey()); - LLNearbyChatBar::getInstance()->showTranslationCheckbox(LLTranslate::isTranslationConfigured()); + (LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat"))-> + showTranslationCheckbox(LLTranslate::isTranslationConfigured()); closeFloater(false); } diff --git a/indra/newview/llfloatervoicevolume.cpp b/indra/newview/llfloatervoicevolume.cpp new file mode 100644 index 0000000000..87b388b30a --- /dev/null +++ b/indra/newview/llfloatervoicevolume.cpp @@ -0,0 +1,209 @@ +/** + * @file llfloatervoicevolume.cpp + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloatervoicevolume.h" + +// Linden libraries +#include "llavatarname.h" +#include "llavatarnamecache.h" +#include "llfloater.h" +#include "llfloaterreg.h" +#include "lltextbox.h" + +// viewer files +#include "llagent.h" +#include "llavataractions.h" +#include "llinspect.h" +#include "lltransientfloatermgr.h" +#include "llvoiceclient.h" + +class LLAvatarName; + +////////////////////////////////////////////////////////////////////////////// +// LLFloaterVoiceVolume +////////////////////////////////////////////////////////////////////////////// + +// Avatar Inspector, a small information window used when clicking +// on avatar names in the 2D UI and in the ambient inspector widget for +// the 3D world. +class LLFloaterVoiceVolume : public LLInspect, LLTransientFloater +{ + friend class LLFloaterReg; + +public: + // avatar_id - Avatar ID for which to show information + // Inspector will be positioned relative to current mouse position + LLFloaterVoiceVolume(const LLSD& avatar_id); + virtual ~LLFloaterVoiceVolume(); + + /*virtual*/ BOOL postBuild(void); + + // Because floater is single instance, need to re-parse data on each spawn + // (for example, inspector about same avatar but in different position) + /*virtual*/ void onOpen(const LLSD& avatar_id); + + /*virtual*/ LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::GLOBAL; } + +private: + // Set the volume slider to this user's current client-side volume setting, + // hiding/disabling if the user is not nearby. + void updateVolumeControls(); + + void onClickMuteVolume(); + void onVolumeChange(const LLSD& data); + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); + +private: + LLUUID mAvatarID; + // Need avatar name information to spawn friend add request + LLAvatarName mAvatarName; +}; + +LLFloaterVoiceVolume::LLFloaterVoiceVolume(const LLSD& sd) +: LLInspect(LLSD()) // single_instance, doesn't really need key +, mAvatarID() // set in onOpen() *Note: we used to show partner's name but we dont anymore --angela 3rd Dec* +, mAvatarName() +{ + LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::GLOBAL, this); + LLTransientFloater::init(this); +} + +LLFloaterVoiceVolume::~LLFloaterVoiceVolume() +{ + LLTransientFloaterMgr::getInstance()->removeControlView(this); +} + +/*virtual*/ +BOOL LLFloaterVoiceVolume::postBuild(void) +{ + getChild<LLUICtrl>("mute_btn")->setCommitCallback( + boost::bind(&LLFloaterVoiceVolume::onClickMuteVolume, this) ); + + getChild<LLUICtrl>("volume_slider")->setCommitCallback( + boost::bind(&LLFloaterVoiceVolume::onVolumeChange, this, _2)); + + return TRUE; +} + + +// Multiple calls to showInstance("floater_voice_volume", foo) will provide different +// LLSD for foo, which we will catch here. +//virtual +void LLFloaterVoiceVolume::onOpen(const LLSD& data) +{ + // Start open animation + LLInspect::onOpen(data); + + // Extract appropriate avatar id + mAvatarID = data["avatar_id"]; + + LLUI::positionViewNearMouse(this); + + getChild<LLUICtrl>("avatar_name")->setValue(""); + updateVolumeControls(); + + LLAvatarNameCache::get(mAvatarID, + boost::bind(&LLFloaterVoiceVolume::onAvatarNameCache, this, _1, _2)); +} + +void LLFloaterVoiceVolume::updateVolumeControls() +{ + bool voice_enabled = LLVoiceClient::getInstance()->getVoiceEnabled(mAvatarID); + + LLUICtrl* mute_btn = getChild<LLUICtrl>("mute_btn"); + LLUICtrl* volume_slider = getChild<LLUICtrl>("volume_slider"); + + // Do not display volume slider and mute button if it + // is ourself or we are not in a voice channel together + if (!voice_enabled || (mAvatarID == gAgent.getID())) + { + mute_btn->setVisible(false); + volume_slider->setVisible(false); + } + else + { + mute_btn->setVisible(true); + volume_slider->setVisible(true); + + // By convention, we only display and toggle voice mutes, not all mutes + bool is_muted = LLAvatarActions::isVoiceMuted(mAvatarID); + bool is_linden = LLStringUtil::endsWith(mAvatarName.getLegacyName(), " Linden"); + + mute_btn->setEnabled(!is_linden); + mute_btn->setValue(is_muted); + + volume_slider->setEnabled(!is_muted); + + F32 volume; + if (is_muted) + { + // it's clearer to display their volume as zero + volume = 0.f; + } + else + { + // actual volume + volume = LLVoiceClient::getInstance()->getUserVolume(mAvatarID); + } + volume_slider->setValue((F64)volume); + } + +} + +void LLFloaterVoiceVolume::onClickMuteVolume() +{ + LLAvatarActions::toggleMuteVoice(mAvatarID); + updateVolumeControls(); +} + +void LLFloaterVoiceVolume::onVolumeChange(const LLSD& data) +{ + F32 volume = (F32)data.asReal(); + LLVoiceClient::getInstance()->setUserVolume(mAvatarID, volume); +} + +void LLFloaterVoiceVolume::onAvatarNameCache( + const LLUUID& agent_id, + const LLAvatarName& av_name) +{ + if (agent_id != mAvatarID) + { + return; + } + + getChild<LLUICtrl>("avatar_name")->setValue(av_name.getCompleteName()); + mAvatarName = av_name; +} + +////////////////////////////////////////////////////////////////////////////// +// LLFloaterVoiceVolumeUtil +////////////////////////////////////////////////////////////////////////////// +void LLFloaterVoiceVolumeUtil::registerFloater() +{ + LLFloaterReg::add("floater_voice_volume", "floater_voice_volume.xml", + &LLFloaterReg::build<LLFloaterVoiceVolume>); +} diff --git a/indra/newview/llfloatervoicevolume.h b/indra/newview/llfloatervoicevolume.h new file mode 100644 index 0000000000..8fcf7f250b --- /dev/null +++ b/indra/newview/llfloatervoicevolume.h @@ -0,0 +1,35 @@ +/** + * @file llfloatervoicevolume.h + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERVOICEVOLUME_H +#define LL_LLFLOATERVOICEVOLUME_H + +namespace LLFloaterVoiceVolumeUtil +{ + // Register with LLFloaterReg + void registerFloater(); +} + +#endif // LL_LLFLOATERVOICEVOLUME_H diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp deleted file mode 100644 index 8e540a0cc8..0000000000 --- a/indra/newview/llfolderview.cpp +++ /dev/null @@ -1,2640 +0,0 @@ -/** - * @file llfolderview.cpp - * @brief Implementation of the folder view collection of classes. - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llfolderview.h" - -#include "llcallbacklist.h" -#include "llinventorybridge.h" -#include "llclipboard.h" // *TODO: remove this once hack below gone. -#include "llinventoryfilter.h" -#include "llinventoryfunctions.h" -#include "llinventorymodelbackgroundfetch.h" -#include "llinventorypanel.h" -#include "llfoldertype.h" -#include "llfloaterinventory.h"// hacked in for the bonus context menu items. -#include "llkeyboard.h" -#include "lllineeditor.h" -#include "llmenugl.h" -#include "llpanel.h" -#include "llpreview.h" -#include "llscrollcontainer.h" // hack to allow scrolling -#include "lltooldraganddrop.h" -#include "lltrans.h" -#include "llui.h" -#include "llviewertexture.h" -#include "llviewertexturelist.h" -#include "llviewerjointattachment.h" -#include "llviewermenu.h" -#include "lluictrlfactory.h" -#include "llviewercontrol.h" -#include "llviewerfoldertype.h" -#include "llviewerwindow.h" -#include "llvoavatar.h" -#include "llfloaterproperties.h" -#include "llnotificationsutil.h" - -// Linden library includes -#include "lldbstrings.h" -#include "llfocusmgr.h" -#include "llfontgl.h" -#include "llgl.h" -#include "llrender.h" -#include "llinventory.h" - -// Third-party library includes -#include <algorithm> - -///---------------------------------------------------------------------------- -/// Local function declarations, constants, enums, and typedefs -///---------------------------------------------------------------------------- - -const S32 RENAME_WIDTH_PAD = 4; -const S32 RENAME_HEIGHT_PAD = 1; -const S32 AUTO_OPEN_STACK_DEPTH = 16; -const S32 MIN_ITEM_WIDTH_VISIBLE = LLFolderViewItem::ICON_WIDTH - + LLFolderViewItem::ICON_PAD - + LLFolderViewItem::ARROW_SIZE - + LLFolderViewItem::TEXT_PAD - + /*first few characters*/ 40; -const S32 MINIMUM_RENAMER_WIDTH = 80; - -// *TODO: move in params in xml if necessary. Requires modification of LLFolderView & LLInventoryPanel Params. -const S32 STATUS_TEXT_HPAD = 6; -const S32 STATUS_TEXT_VPAD = 8; - -enum { - SIGNAL_NO_KEYBOARD_FOCUS = 1, - SIGNAL_KEYBOARD_FOCUS = 2 -}; - -F32 LLFolderView::sAutoOpenTime = 1.f; - -void delete_selected_item(void* user_data); -void copy_selected_item(void* user_data); -void open_selected_items(void* user_data); -void properties_selected_items(void* user_data); -void paste_items(void* user_data); - - -//--------------------------------------------------------------------------- - -// Tells all folders in a folderview to sort their items -// (and only their items, not folders) by a certain function. -class LLSetItemSortFunction : public LLFolderViewFunctor -{ -public: - LLSetItemSortFunction(U32 ordering) - : mSortOrder(ordering) {} - virtual ~LLSetItemSortFunction() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); - - U32 mSortOrder; -}; - - -// Set the sort order. -void LLSetItemSortFunction::doFolder(LLFolderViewFolder* folder) -{ - folder->setItemSortOrder(mSortOrder); -} - -// Do nothing. -void LLSetItemSortFunction::doItem(LLFolderViewItem* item) -{ - return; -} - -//--------------------------------------------------------------------------- - -// Tells all folders in a folderview to close themselves -// For efficiency, calls setOpenArrangeRecursively(). -// The calling function must then call: -// LLFolderView* root = getRoot(); -// if( root ) -// { -// root->arrange( NULL, NULL ); -// root->scrollToShowSelection(); -// } -// to patch things up. -class LLCloseAllFoldersFunctor : public LLFolderViewFunctor -{ -public: - LLCloseAllFoldersFunctor(BOOL close) { mOpen = !close; } - virtual ~LLCloseAllFoldersFunctor() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); - - BOOL mOpen; -}; - - -// Set the sort order. -void LLCloseAllFoldersFunctor::doFolder(LLFolderViewFolder* folder) -{ - folder->setOpenArrangeRecursively(mOpen); -} - -// Do nothing. -void LLCloseAllFoldersFunctor::doItem(LLFolderViewItem* item) -{ } - -///---------------------------------------------------------------------------- -/// Class LLFolderViewScrollContainer -///---------------------------------------------------------------------------- - -// virtual -const LLRect LLFolderViewScrollContainer::getScrolledViewRect() const -{ - LLRect rect = LLRect::null; - if (mScrolledView) - { - LLFolderView* folder_view = dynamic_cast<LLFolderView*>(mScrolledView); - if (folder_view) - { - S32 height = folder_view->mRunningHeight; - - rect = mScrolledView->getRect(); - rect.setLeftTopAndSize(rect.mLeft, rect.mTop, rect.getWidth(), height); - } - } - - return rect; -} - -LLFolderViewScrollContainer::LLFolderViewScrollContainer(const LLScrollContainer::Params& p) -: LLScrollContainer(p) -{} - -///---------------------------------------------------------------------------- -/// Class LLFolderView -///---------------------------------------------------------------------------- -LLFolderView::Params::Params() -: task_id("task_id"), - title("title"), - use_label_suffix("use_label_suffix"), - allow_multiselect("allow_multiselect", true), - show_empty_message("show_empty_message", true), - show_load_status("show_load_status", true), - use_ellipses("use_ellipses", false) -{ -} - - -// Default constructor -LLFolderView::LLFolderView(const Params& p) -: LLFolderViewFolder(p), - mRunningHeight(0), - mScrollContainer( NULL ), - mPopupMenuHandle(), - mAllowMultiSelect(p.allow_multiselect), - mShowEmptyMessage(p.show_empty_message), - mShowFolderHierarchy(FALSE), - mSourceID(p.task_id), - mRenameItem( NULL ), - mNeedsScroll( FALSE ), - mUseLabelSuffix(p.use_label_suffix), - mPinningSelectedItem(FALSE), - mNeedsAutoSelect( FALSE ), - mAutoSelectOverride(FALSE), - mNeedsAutoRename(FALSE), - mDebugFilters(FALSE), - mSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME), // This gets overridden by a pref immediately - mFilter( new LLInventoryFilter(p.title) ), - mShowSelectionContext(FALSE), - mShowSingleSelection(FALSE), - mArrangeGeneration(0), - mSignalSelectCallback(0), - mMinWidth(0), - mDragAndDropThisFrame(FALSE), - mCallbackRegistrar(NULL), - mParentPanel(p.parent_panel), - mUseEllipses(p.use_ellipses), - mDraggingOverItem(NULL), - mStatusTextBox(NULL) -{ - mRoot = this; - - mShowLoadStatus = p.show_load_status(); - - LLRect rect = p.rect; - LLRect new_rect(rect.mLeft, rect.mBottom + getRect().getHeight(), rect.mLeft + getRect().getWidth(), rect.mBottom); - setRect( rect ); - reshape(rect.getWidth(), rect.getHeight()); - mIsOpen = TRUE; // this view is always open. - mAutoOpenItems.setDepth(AUTO_OPEN_STACK_DEPTH); - mAutoOpenCandidate = NULL; - mAutoOpenTimer.stop(); - mKeyboardSelection = FALSE; - const LLFolderViewItem::Params& item_params = - LLUICtrlFactory::getDefaultParams<LLFolderViewItem>(); - S32 indentation = item_params.folder_indentation(); - mIndentation = -indentation; // children start at indentation 0 - gIdleCallbacks.addFunction(idle, this); - - //clear label - // go ahead and render root folder as usual - // just make sure the label ("Inventory Folder") never shows up - mLabel = LLStringUtil::null; - - //mRenamer->setWriteableBgColor(LLColor4::white); - // Escape is handled by reverting the rename, not commiting it (default behavior) - LLLineEditor::Params params; - params.name("ren"); - params.rect(rect); - params.font(getLabelFontForStyle(LLFontGL::NORMAL)); - params.max_length.bytes(DB_INV_ITEM_NAME_STR_LEN); - params.commit_callback.function(boost::bind(&LLFolderView::commitRename, this, _2)); - params.prevalidate_callback(&LLTextValidate::validateASCIIPrintableNoPipe); - params.commit_on_focus_lost(true); - params.visible(false); - mRenamer = LLUICtrlFactory::create<LLLineEditor> (params); - addChild(mRenamer); - - // Textbox - LLTextBox::Params text_p; - LLFontGL* font = getLabelFontForStyle(mLabelStyle); - LLRect new_r = LLRect(rect.mLeft + ICON_PAD, - rect.mTop - TEXT_PAD, - rect.mRight, - rect.mTop - TEXT_PAD - font->getLineHeight()); - text_p.rect(new_r); - text_p.name(std::string(p.name)); - text_p.font(font); - text_p.visible(false); - text_p.parse_urls(true); - text_p.wrap(true); // allow multiline text. See EXT-7564, EXT-7047 - // set text padding the same as in People panel. EXT-7047, EXT-4837 - text_p.h_pad(STATUS_TEXT_HPAD); - text_p.v_pad(STATUS_TEXT_VPAD); - mStatusTextBox = LLUICtrlFactory::create<LLTextBox> (text_p); - mStatusTextBox->setFollowsLeft(); - mStatusTextBox->setFollowsTop(); - //addChild(mStatusTextBox); - - - // make the popup menu available - LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_inventory.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if (!menu) - { - menu = LLUICtrlFactory::getDefaultWidget<LLMenuGL>("inventory_menu"); - } - menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor")); - mPopupMenuHandle = menu->getHandle(); - - mListener->openItem(); -} - -// Destroys the object -LLFolderView::~LLFolderView( void ) -{ - closeRenamer(); - - // The release focus call can potentially call the - // scrollcontainer, which can potentially be called with a partly - // destroyed scollcontainer. Just null it out here, and no worries - // about calling into the invalid scroll container. - // Same with the renamer. - mScrollContainer = NULL; - mRenameItem = NULL; - mRenamer = NULL; - mStatusTextBox = NULL; - - mAutoOpenItems.removeAllNodes(); - gIdleCallbacks.deleteFunction(idle, this); - - if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die(); - - mAutoOpenItems.removeAllNodes(); - clearSelection(); - mItems.clear(); - mFolders.clear(); - - mItemMap.clear(); - - delete mFilter; - mFilter = NULL; -} - -BOOL LLFolderView::canFocusChildren() const -{ - return FALSE; -} - -static LLFastTimer::DeclareTimer FTM_SORT("Sort Inventory"); - -void LLFolderView::setSortOrder(U32 order) -{ - if (order != mSortOrder) - { - LLFastTimer t(FTM_SORT); - - mSortOrder = order; - - sortBy(order); - arrangeAll(); - } -} - - -U32 LLFolderView::getSortOrder() const -{ - return mSortOrder; -} - -BOOL LLFolderView::addFolder( LLFolderViewFolder* folder) -{ - // enforce sort order of My Inventory followed by Library - if (folder->getListener()->getUUID() == gInventory.getLibraryRootFolderID()) - { - mFolders.push_back(folder); - } - else - { - mFolders.insert(mFolders.begin(), folder); - } - folder->setShowLoadStatus(mShowLoadStatus); - folder->setOrigin(0, 0); - folder->reshape(getRect().getWidth(), 0); - folder->setVisible(FALSE); - addChild( folder ); - folder->dirtyFilter(); - folder->requestArrange(); - return TRUE; -} - -void LLFolderView::closeAllFolders() -{ - // Close all the folders - setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_DOWN); - arrangeAll(); -} - -void LLFolderView::openTopLevelFolders() -{ - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->setOpen(TRUE); - } -} - -void LLFolderView::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse) -{ - // call base class to do proper recursion - LLFolderViewFolder::setOpenArrangeRecursively(openitem, recurse); - // make sure root folder is always open - mIsOpen = TRUE; -} - -static LLFastTimer::DeclareTimer FTM_ARRANGE("Arrange"); - -// This view grows and shrinks to enclose all of its children items and folders. -S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_generation ) -{ - if (getListener()->getUUID().notNull()) - { - if (mNeedsSort) - { - mFolders.sort(mSortFunction); - mItems.sort(mSortFunction); - mNeedsSort = false; - } - } - - LLFastTimer t2(FTM_ARRANGE); - - filter_generation = mFilter->getMinRequiredGeneration(); - mMinWidth = 0; - - mHasVisibleChildren = hasFilteredDescendants(filter_generation); - // arrange always finishes, so optimistically set the arrange generation to the most current - mLastArrangeGeneration = getRoot()->getArrangeGeneration(); - - LLInventoryFilter::EFolderShow show_folder_state = - getRoot()->getFilter()->getShowFolderState(); - - S32 total_width = LEFT_PAD; - S32 running_height = mDebugFilters ? LLFontGL::getFontMonospace()->getLineHeight() : 0; - S32 target_height = running_height; - S32 parent_item_height = getRect().getHeight(); - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - LLFolderViewFolder* folderp = (*fit); - if (getDebugFilters()) - { - folderp->setVisible(TRUE); - } - else - { - folderp->setVisible((show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders? - (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation)))); - } - - if (folderp->getVisible()) - { - S32 child_height = 0; - S32 child_width = 0; - S32 child_top = parent_item_height - running_height; - - target_height += folderp->arrange( &child_width, &child_height, filter_generation ); - - mMinWidth = llmax(mMinWidth, child_width); - total_width = llmax( total_width, child_width ); - running_height += child_height; - folderp->setOrigin( ICON_PAD, child_top - (*fit)->getRect().getHeight() ); - } - } - - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - LLFolderViewItem* itemp = (*iit); - itemp->setVisible(itemp->getFiltered(filter_generation)); - - if (itemp->getVisible()) - { - S32 child_width = 0; - S32 child_height = 0; - S32 child_top = parent_item_height - running_height; - - target_height += itemp->arrange( &child_width, &child_height, filter_generation ); - itemp->reshape(itemp->getRect().getWidth(), child_height); - - mMinWidth = llmax(mMinWidth, child_width); - total_width = llmax( total_width, child_width ); - running_height += child_height; - itemp->setOrigin( ICON_PAD, child_top - itemp->getRect().getHeight() ); - } - } - - if(!mHasVisibleChildren)// is there any filtered items ? - { - //Nope. We need to display status textbox, let's reserve some place for it - running_height = mStatusTextBox->getTextPixelHeight(); - target_height = running_height; - } - - mRunningHeight = running_height; - LLRect scroll_rect = mScrollContainer->getContentWindowRect(); - reshape( llmax(scroll_rect.getWidth(), total_width), running_height ); - - LLRect new_scroll_rect = mScrollContainer->getContentWindowRect(); - if (new_scroll_rect.getWidth() != scroll_rect.getWidth()) - { - reshape( llmax(scroll_rect.getWidth(), total_width), running_height ); - } - - // move item renamer text field to item's new position - updateRenamerPosition(); - - mTargetHeight = (F32)target_height; - return llround(mTargetHeight); -} - -const std::string LLFolderView::getFilterSubString(BOOL trim) -{ - return mFilter->getFilterSubString(trim); -} - -static LLFastTimer::DeclareTimer FTM_FILTER("Filter Inventory"); - -void LLFolderView::filter( LLInventoryFilter& filter ) -{ - LLFastTimer t2(FTM_FILTER); - filter.setFilterCount(llclamp(gSavedSettings.getS32("FilterItemsPerFrame"), 1, 5000)); - - if (getCompletedFilterGeneration() < filter.getCurrentGeneration()) - { - mPassedFilter = FALSE; - mMinWidth = 0; - LLFolderViewFolder::filter(filter); - } - else - { - mPassedFilter = TRUE; - } -} - -void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent) -{ - LLRect scroll_rect; - if (mScrollContainer) - { - LLView::reshape(width, height, called_from_parent); - scroll_rect = mScrollContainer->getContentWindowRect(); - } - width = llmax(mMinWidth, scroll_rect.getWidth()); - height = llmax(mRunningHeight, scroll_rect.getHeight()); - - // Restrict width within scroll container's width - if (mUseEllipses && mScrollContainer) - { - width = scroll_rect.getWidth(); - } - LLView::reshape(width, height, called_from_parent); - mReshapeSignal(mSelectedItems, FALSE); -} - -void LLFolderView::addToSelectionList(LLFolderViewItem* item) -{ - if (item->isSelected()) - { - removeFromSelectionList(item); - } - if (mSelectedItems.size()) - { - mSelectedItems.back()->setIsCurSelection(FALSE); - } - item->setIsCurSelection(TRUE); - mSelectedItems.push_back(item); -} - -void LLFolderView::removeFromSelectionList(LLFolderViewItem* item) -{ - if (mSelectedItems.size()) - { - mSelectedItems.back()->setIsCurSelection(FALSE); - } - - selected_items_t::iterator item_iter; - for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end();) - { - if (*item_iter == item) - { - item_iter = mSelectedItems.erase(item_iter); - } - else - { - ++item_iter; - } - } - if (mSelectedItems.size()) - { - mSelectedItems.back()->setIsCurSelection(TRUE); - } -} - -LLFolderViewItem* LLFolderView::getCurSelectedItem( void ) -{ - if(mSelectedItems.size()) - { - LLFolderViewItem* itemp = mSelectedItems.back(); - llassert(itemp->getIsCurSelection()); - return itemp; - } - return NULL; -} - - -// Record the selected item and pass it down the hierachy. -BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL openitem, - BOOL take_keyboard_focus) -{ - mSignalSelectCallback = take_keyboard_focus ? SIGNAL_KEYBOARD_FOCUS : SIGNAL_NO_KEYBOARD_FOCUS; - - if( selection == this ) - { - return FALSE; - } - - if( selection && take_keyboard_focus) - { - mParentPanel->setFocus(TRUE); - } - - // clear selection down here because change of keyboard focus can potentially - // affect selection - clearSelection(); - - if(selection) - { - addToSelectionList(selection); - } - - BOOL rv = LLFolderViewFolder::setSelection(selection, openitem, take_keyboard_focus); - if(openitem && selection) - { - selection->getParentFolder()->requestArrange(); - } - - llassert(mSelectedItems.size() <= 1); - - return rv; -} - -void LLFolderView::setSelectionByID(const LLUUID& obj_id, BOOL take_keyboard_focus) -{ - LLFolderViewItem* itemp = getItemByID(obj_id); - if(itemp && itemp->getListener()) - { - itemp->arrangeAndSet(TRUE, take_keyboard_focus); - mSelectThisID.setNull(); - return; - } - else - { - // save the desired item to be selected later (if/when ready) - mSelectThisID = obj_id; - } -} - -void LLFolderView::updateSelection() -{ - if (mSelectThisID.notNull()) - { - setSelectionByID(mSelectThisID, false); - } -} - -BOOL LLFolderView::changeSelection(LLFolderViewItem* selection, BOOL selected) -{ - BOOL rv = FALSE; - - // can't select root folder - if(!selection || selection == this) - { - return FALSE; - } - - if (!mAllowMultiSelect) - { - clearSelection(); - } - - selected_items_t::iterator item_iter; - for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter) - { - if (*item_iter == selection) - { - break; - } - } - - BOOL on_list = (item_iter != mSelectedItems.end()); - - if(selected && !on_list) - { - addToSelectionList(selection); - } - if(!selected && on_list) - { - removeFromSelectionList(selection); - } - - rv = LLFolderViewFolder::changeSelection(selection, selected); - - mSignalSelectCallback = SIGNAL_KEYBOARD_FOCUS; - - return rv; -} - -static LLFastTimer::DeclareTimer FTM_SANITIZE_SELECTION("Sanitize Selection"); -void LLFolderView::sanitizeSelection() -{ - LLFastTimer _(FTM_SANITIZE_SELECTION); - // store off current item in case it is automatically deselected - // and we want to preserve context - LLFolderViewItem* original_selected_item = getCurSelectedItem(); - - // Cache "Show all folders" filter setting - BOOL show_all_folders = (getRoot()->getFilter()->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS); - - std::vector<LLFolderViewItem*> items_to_remove; - selected_items_t::iterator item_iter; - for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter) - { - LLFolderViewItem* item = *item_iter; - - // ensure that each ancestor is open and potentially passes filtering - BOOL visible = item->potentiallyVisible(); // initialize from filter state for this item - // modify with parent open and filters states - LLFolderViewFolder* parent_folder = item->getParentFolder(); - if ( parent_folder ) - { - if ( show_all_folders ) - { // "Show all folders" is on, so this folder is visible - visible = TRUE; - } - else - { // Move up through parent folders and see what's visible - while(parent_folder) - { - visible = visible && parent_folder->isOpen() && parent_folder->potentiallyVisible(); - parent_folder = parent_folder->getParentFolder(); - } - } - } - - // deselect item if any ancestor is closed or didn't pass filter requirements. - if (!visible) - { - items_to_remove.push_back(item); - } - - // disallow nested selections (i.e. folder items plus one or more ancestors) - // could check cached mum selections count and only iterate if there are any - // but that may be a premature optimization. - selected_items_t::iterator other_item_iter; - for (other_item_iter = mSelectedItems.begin(); other_item_iter != mSelectedItems.end(); ++other_item_iter) - { - LLFolderViewItem* other_item = *other_item_iter; - for( parent_folder = other_item->getParentFolder(); parent_folder; parent_folder = parent_folder->getParentFolder()) - { - if (parent_folder == item) - { - // this is a descendent of the current folder, remove from list - items_to_remove.push_back(other_item); - break; - } - } - } - - // Don't allow invisible items (such as root folders) to be selected. - if (item == getRoot()) - { - items_to_remove.push_back(item); - } - } - - std::vector<LLFolderViewItem*>::iterator item_it; - for (item_it = items_to_remove.begin(); item_it != items_to_remove.end(); ++item_it ) - { - changeSelection(*item_it, FALSE); // toggle selection (also removes from list) - } - - // if nothing selected after prior constraints... - if (mSelectedItems.empty()) - { - // ...select first available parent of original selection - LLFolderViewItem* new_selection = NULL; - if (original_selected_item) - { - for(LLFolderViewFolder* parent_folder = original_selected_item->getParentFolder(); - parent_folder; - parent_folder = parent_folder->getParentFolder()) - { - if (parent_folder->potentiallyVisible()) - { - // give initial selection to first ancestor folder that potentially passes the filter - if (!new_selection) - { - new_selection = parent_folder; - } - - // if any ancestor folder of original item is closed, move the selection up - // to the highest closed - if (!parent_folder->isOpen()) - { - new_selection = parent_folder; - } - } - } - } - else - { - new_selection = NULL; - } - - if (new_selection) - { - setSelection(new_selection, FALSE, FALSE); - } - } -} - -void LLFolderView::clearSelection() -{ - for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); - item_it != mSelectedItems.end(); - ++item_it) - { - (*item_it)->setUnselected(); - } - - mSelectedItems.clear(); - mSelectThisID.setNull(); -} - -std::set<LLUUID> LLFolderView::getSelectionList() const -{ - std::set<LLUUID> selection; - for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); - item_it != mSelectedItems.end(); - ++item_it) - { - selection.insert((*item_it)->getListener()->getUUID()); - } - return selection; -} - -BOOL LLFolderView::startDrag(LLToolDragAndDrop::ESource source) -{ - std::vector<EDragAndDropType> types; - uuid_vec_t cargo_ids; - selected_items_t::iterator item_it; - BOOL can_drag = TRUE; - if (!mSelectedItems.empty()) - { - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - EDragAndDropType type = DAD_NONE; - LLUUID id = LLUUID::null; - can_drag = can_drag && (*item_it)->getListener()->startDrag(&type, &id); - - types.push_back(type); - cargo_ids.push_back(id); - } - - LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, source, mSourceID); - } - return can_drag; -} - -void LLFolderView::commitRename( const LLSD& data ) -{ - finishRenamingItem(); -} - -void LLFolderView::draw() -{ - static LLUIColor sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", LLColor4::white); - if (mDebugFilters) - { - std::string current_filter_string = llformat("Current Filter: %d, Least Filter: %d, Auto-accept Filter: %d", - mFilter->getCurrentGeneration(), mFilter->getMinRequiredGeneration(), mFilter->getMustPassGeneration()); - LLFontGL::getFontMonospace()->renderUTF8(current_filter_string, 0, 2, - getRect().getHeight() - LLFontGL::getFontMonospace()->getLineHeight(), LLColor4(0.5f, 0.5f, 0.8f, 1.f), - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); - } - - //LLFontGL* font = getLabelFontForStyle(mLabelStyle); - - // if cursor has moved off of me during drag and drop - // close all auto opened folders - if (!mDragAndDropThisFrame) - { - closeAutoOpenedFolders(); - } - - // while dragging, update selection rendering to reflect single/multi drag status - if (LLToolDragAndDrop::getInstance()->hasMouseCapture()) - { - EAcceptance last_accept = LLToolDragAndDrop::getInstance()->getLastAccept(); - if (last_accept == ACCEPT_YES_SINGLE || last_accept == ACCEPT_YES_COPY_SINGLE) - { - setShowSingleSelection(TRUE); - } - else - { - setShowSingleSelection(FALSE); - } - } - else - { - setShowSingleSelection(FALSE); - } - - - if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout") || !mSearchString.size()) - { - mSearchString.clear(); - } - - if (hasVisibleChildren() - || mFilter->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS) - { - mStatusText.clear(); - mStatusTextBox->setVisible( FALSE ); - } - else if (mShowEmptyMessage) - { - if (LLInventoryModelBackgroundFetch::instance().folderFetchActive() || mCompletedFilterGeneration < mFilter->getMinRequiredGeneration()) - { - mStatusText = LLTrans::getString("Searching"); - } - else - { - if (getFilter()) - { - LLStringUtil::format_map_t args; - args["[SEARCH_TERM]"] = LLURI::escape(getFilter()->getFilterSubStringOrig()); - mStatusText = LLTrans::getString(getFilter()->getEmptyLookupMessage(), args); - } - } - mStatusTextBox->setValue(mStatusText); - mStatusTextBox->setVisible( TRUE ); - - // firstly reshape message textbox with current size. This is necessary to - // LLTextBox::getTextPixelHeight works properly - const LLRect local_rect = getLocalRect(); - mStatusTextBox->setShape(local_rect); - - // get preferable text height... - S32 pixel_height = mStatusTextBox->getTextPixelHeight(); - bool height_changed = local_rect.getHeight() != pixel_height; - if (height_changed) - { - // ... if it does not match current height, lets rearrange current view. - // This will indirectly call ::arrange and reshape of the status textbox. - // We should call this method to also notify parent about required rect. - // See EXT-7564, EXT-7047. - arrangeFromRoot(); - LLUI::popMatrix(); - LLUI::pushMatrix(); - LLUI::translate((F32)getRect().mLeft, (F32)getRect().mBottom); - } - } - - // skip over LLFolderViewFolder::draw since we don't want the folder icon, label, - // and arrow for the root folder - LLView::draw(); - - mDragAndDropThisFrame = FALSE; -} - -void LLFolderView::finishRenamingItem( void ) -{ - if(!mRenamer) - { - return; - } - if( mRenameItem ) - { - mRenameItem->rename( mRenamer->getText() ); - } - - closeRenamer(); - - // List is re-sorted alphabeticly, so scroll to make sure the selected item is visible. - scrollToShowSelection(); -} - -void LLFolderView::closeRenamer( void ) -{ - if (mRenamer && mRenamer->getVisible()) - { - // Triggers onRenamerLost() that actually closes the renamer. - gViewerWindow->removePopup(mRenamer); - } -} - -void LLFolderView::removeSelectedItems( void ) -{ - if (mSelectedItems.empty()) return; - LLSD args; - args["QUESTION"] = LLTrans::getString(mSelectedItems.size() > 1 ? "DeleteItems" : "DeleteItem"); - LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&LLFolderView::onItemsRemovalConfirmation, this, _1, _2)); -} - -bool isDescendantOfASelectedItem(LLFolderViewItem* item, const std::vector<LLFolderViewItem*>& selectedItems) -{ - LLFolderViewItem* item_parent = dynamic_cast<LLFolderViewItem*>(item->getParent()); - - if (item_parent) - { - for(std::vector<LLFolderViewItem*>::const_iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) - { - const LLFolderViewItem* const selected_item = (*it); - - LLFolderViewItem* parent = item_parent; - - while (parent) - { - if (selected_item == parent) - { - return true; - } - - parent = dynamic_cast<LLFolderViewItem*>(parent->getParent()); - } - } - } - - return false; -} - -// static -void LLFolderView::removeCutItems() -{ - // There's no item in "cut" mode on the clipboard -> exit - if (!LLClipboard::instance().isCutMode()) - return; - - // Get the list of clipboard item uuids and iterate through them - LLDynamicArray<LLUUID> objects; - LLClipboard::instance().pasteFromClipboard(objects); - for (LLDynamicArray<LLUUID>::const_iterator iter = objects.begin(); - iter != objects.end(); - ++iter) - { - gInventory.removeObject(*iter); - } -} - -void LLFolderView::onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (option != 0) return; // canceled - - if(getVisible() && getEnabled()) - { - // just in case we're removing the renaming item. - mRenameItem = NULL; - - // create a temporary structure which we will use to remove - // items, since the removal will futz with internal data - // structures. - std::vector<LLFolderViewItem*> items; - S32 count = mSelectedItems.size(); - if(count == 0) return; - LLFolderViewItem* item = NULL; - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - item = *item_it; - if (item && item->isRemovable()) - { - items.push_back(item); - } - else - { - llinfos << "Cannot delete " << item->getName() << llendl; - return; - } - } - - // iterate through the new container. - count = items.size(); - LLUUID new_selection_id; - if(count == 1) - { - LLFolderViewItem* item_to_delete = items[0]; - LLFolderViewFolder* parent = item_to_delete->getParentFolder(); - LLFolderViewItem* new_selection = item_to_delete->getNextOpenNode(FALSE); - if (!new_selection) - { - new_selection = item_to_delete->getPreviousOpenNode(FALSE); - } - if(parent) - { - if (parent->removeItem(item_to_delete)) - { - // change selection on successful delete - if (new_selection) - { - setSelectionFromRoot(new_selection, new_selection->isOpen(), mParentPanel->hasFocus()); - } - else - { - setSelectionFromRoot(NULL, mParentPanel->hasFocus()); - } - } - } - arrangeAll(); - } - else if (count > 1) - { - LLDynamicArray<LLFolderViewEventListener*> listeners; - LLFolderViewEventListener* listener; - LLFolderViewItem* last_item = items[count - 1]; - LLFolderViewItem* new_selection = last_item->getNextOpenNode(FALSE); - while(new_selection && new_selection->isSelected()) - { - new_selection = new_selection->getNextOpenNode(FALSE); - } - if (!new_selection) - { - new_selection = last_item->getPreviousOpenNode(FALSE); - while (new_selection && (new_selection->isSelected() || isDescendantOfASelectedItem(new_selection, items))) - { - new_selection = new_selection->getPreviousOpenNode(FALSE); - } - } - if (new_selection) - { - setSelectionFromRoot(new_selection, new_selection->isOpen(), mParentPanel->hasFocus()); - } - else - { - setSelectionFromRoot(NULL, mParentPanel->hasFocus()); - } - - for(S32 i = 0; i < count; ++i) - { - listener = items[i]->getListener(); - if(listener && (listeners.find(listener) == LLDynamicArray<LLFolderViewEventListener*>::FAIL)) - { - listeners.put(listener); - } - } - listener = listeners.get(0); - if(listener) - { - listener->removeBatch(listeners); - } - } - arrangeAll(); - scrollToShowSelection(); - } -} - -// open the selected item. -void LLFolderView::openSelectedItems( void ) -{ - if(getVisible() && getEnabled()) - { - if (mSelectedItems.size() == 1) - { - mSelectedItems.front()->openItem(); - } - else - { - LLMultiPreview* multi_previewp = new LLMultiPreview(); - LLMultiProperties* multi_propertiesp = new LLMultiProperties(); - - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - // IT_{OBJECT,ATTACHMENT} creates LLProperties - // floaters; others create LLPreviews. Put - // each one in the right type of container. - LLFolderViewEventListener* listener = (*item_it)->getListener(); - bool is_prop = listener && (listener->getInventoryType() == LLInventoryType::IT_OBJECT || listener->getInventoryType() == LLInventoryType::IT_ATTACHMENT); - if (is_prop) - LLFloater::setFloaterHost(multi_propertiesp); - else - LLFloater::setFloaterHost(multi_previewp); - (*item_it)->openItem(); - } - - LLFloater::setFloaterHost(NULL); - // *NOTE: LLMulti* will safely auto-delete when open'd - // without any children. - multi_previewp->openFloater(LLSD()); - multi_propertiesp->openFloater(LLSD()); - } - } -} - -void LLFolderView::propertiesSelectedItems( void ) -{ - if(getVisible() && getEnabled()) - { - if (mSelectedItems.size() == 1) - { - LLFolderViewItem* folder_item = mSelectedItems.front(); - if(!folder_item) return; - folder_item->getListener()->showProperties(); - } - else - { - LLMultiProperties* multi_propertiesp = new LLMultiProperties(); - - LLFloater::setFloaterHost(multi_propertiesp); - - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - (*item_it)->getListener()->showProperties(); - } - - LLFloater::setFloaterHost(NULL); - multi_propertiesp->openFloater(LLSD()); - } - } -} - -void LLFolderView::changeType(LLInventoryModel *model, LLFolderType::EType new_folder_type) -{ - LLFolderBridge *folder_bridge = LLFolderBridge::sSelf.get(); - - if (!folder_bridge) return; - LLViewerInventoryCategory *cat = folder_bridge->getCategory(); - if (!cat) return; - cat->changeType(new_folder_type); -} - -void LLFolderView::autoOpenItem( LLFolderViewFolder* item ) -{ - if ((mAutoOpenItems.check() == item) || - (mAutoOpenItems.getDepth() >= (U32)AUTO_OPEN_STACK_DEPTH) || - item->isOpen()) - { - return; - } - - // close auto-opened folders - LLFolderViewFolder* close_item = mAutoOpenItems.check(); - while (close_item && close_item != item->getParentFolder()) - { - mAutoOpenItems.pop(); - close_item->setOpenArrangeRecursively(FALSE); - close_item = mAutoOpenItems.check(); - } - - item->requestArrange(); - - mAutoOpenItems.push(item); - - item->setOpen(TRUE); - LLRect content_rect = mScrollContainer->getContentWindowRect(); - LLRect constraint_rect(0,content_rect.getHeight(), content_rect.getWidth(), 0); - scrollToShowItem(item, constraint_rect); -} - -void LLFolderView::closeAutoOpenedFolders() -{ - while (mAutoOpenItems.check()) - { - LLFolderViewFolder* close_item = mAutoOpenItems.pop(); - close_item->setOpen(FALSE); - } - - if (mAutoOpenCandidate) - { - mAutoOpenCandidate->setAutoOpenCountdown(0.f); - } - mAutoOpenCandidate = NULL; - mAutoOpenTimer.stop(); -} - -BOOL LLFolderView::autoOpenTest(LLFolderViewFolder* folder) -{ - if (folder && mAutoOpenCandidate == folder) - { - if (mAutoOpenTimer.getStarted()) - { - if (!mAutoOpenCandidate->isOpen()) - { - mAutoOpenCandidate->setAutoOpenCountdown(clamp_rescale(mAutoOpenTimer.getElapsedTimeF32(), 0.f, sAutoOpenTime, 0.f, 1.f)); - } - if (mAutoOpenTimer.getElapsedTimeF32() > sAutoOpenTime) - { - autoOpenItem(folder); - mAutoOpenTimer.stop(); - return TRUE; - } - } - return FALSE; - } - - // otherwise new candidate, restart timer - if (mAutoOpenCandidate) - { - mAutoOpenCandidate->setAutoOpenCountdown(0.f); - } - mAutoOpenCandidate = folder; - mAutoOpenTimer.start(); - return FALSE; -} - -BOOL LLFolderView::canCopy() const -{ - if (!(getVisible() && getEnabled() && (mSelectedItems.size() > 0))) - { - return FALSE; - } - - for (selected_items_t::const_iterator selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it) - { - const LLFolderViewItem* item = *selected_it; - if (!item->getListener()->isItemCopyable()) - { - return FALSE; - } - } - return TRUE; -} - -// copy selected item -void LLFolderView::copy() -{ - // *NOTE: total hack to clear the inventory clipboard - LLClipboard::instance().reset(); - S32 count = mSelectedItems.size(); - if(getVisible() && getEnabled() && (count > 0)) - { - LLFolderViewEventListener* listener = NULL; - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - listener = (*item_it)->getListener(); - if(listener) - { - listener->copyToClipboard(); - } - } - } - mSearchString.clear(); -} - -BOOL LLFolderView::canCut() const -{ - if (!(getVisible() && getEnabled() && (mSelectedItems.size() > 0))) - { - return FALSE; - } - - for (selected_items_t::const_iterator selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it) - { - const LLFolderViewItem* item = *selected_it; - const LLFolderViewEventListener* listener = item->getListener(); - - if (!listener || !listener->isItemRemovable()) - { - return FALSE; - } - } - return TRUE; -} - -void LLFolderView::cut() -{ - // clear the inventory clipboard - LLClipboard::instance().reset(); - S32 count = mSelectedItems.size(); - if(getVisible() && getEnabled() && (count > 0)) - { - LLFolderViewEventListener* listener = NULL; - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - listener = (*item_it)->getListener(); - if(listener) - { - listener->cutToClipboard(); - } - } - LLFolderView::removeCutItems(); - } - mSearchString.clear(); -} - -BOOL LLFolderView::canPaste() const -{ - if (mSelectedItems.empty()) - { - return FALSE; - } - - if(getVisible() && getEnabled()) - { - for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); - item_it != mSelectedItems.end(); ++item_it) - { - // *TODO: only check folders and parent folders of items - const LLFolderViewItem* item = (*item_it); - const LLFolderViewEventListener* listener = item->getListener(); - if(!listener || !listener->isClipboardPasteable()) - { - const LLFolderViewFolder* folderp = item->getParentFolder(); - listener = folderp->getListener(); - if (!listener || !listener->isClipboardPasteable()) - { - return FALSE; - } - } - } - return TRUE; - } - return FALSE; -} - -// paste selected item -void LLFolderView::paste() -{ - if(getVisible() && getEnabled()) - { - // find set of unique folders to paste into - std::set<LLFolderViewItem*> folder_set; - - selected_items_t::iterator selected_it; - for (selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it) - { - LLFolderViewItem* item = *selected_it; - LLFolderViewEventListener* listener = item->getListener(); - if (listener->getInventoryType() != LLInventoryType::IT_CATEGORY) - { - item = item->getParentFolder(); - } - folder_set.insert(item); - } - - std::set<LLFolderViewItem*>::iterator set_iter; - for(set_iter = folder_set.begin(); set_iter != folder_set.end(); ++set_iter) - { - LLFolderViewEventListener* listener = (*set_iter)->getListener(); - if(listener && listener->isClipboardPasteable()) - { - listener->pasteFromClipboard(); - } - } - } - mSearchString.clear(); -} - -// public rename functionality - can only start the process -void LLFolderView::startRenamingSelectedItem( void ) -{ - // make sure selection is visible - scrollToShowSelection(); - - S32 count = mSelectedItems.size(); - LLFolderViewItem* item = NULL; - if(count > 0) - { - item = mSelectedItems.front(); - } - if(getVisible() && getEnabled() && (count == 1) && item && item->getListener() && - item->getListener()->isItemRenameable()) - { - mRenameItem = item; - - updateRenamerPosition(); - - - mRenamer->setText(item->getName()); - mRenamer->selectAll(); - mRenamer->setVisible( TRUE ); - // set focus will fail unless item is visible - mRenamer->setFocus( TRUE ); - mRenamer->setTopLostCallback(boost::bind(&LLFolderView::onRenamerLost, this)); - gViewerWindow->addPopup(mRenamer); - } -} - -BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) -{ - BOOL handled = FALSE; - - // SL-51858: Key presses are not being passed to the Popup menu. - // A proper fix is non-trivial so instead just close the menu. - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); - if (menu && menu->isOpen()) - { - LLMenuGL::sMenuContainer->hideMenus(); - } - - LLView *item = NULL; - if (getChildCount() > 0) - { - item = *(getChildList()->begin()); - } - - switch( key ) - { - case KEY_F2: - mSearchString.clear(); - startRenamingSelectedItem(); - handled = TRUE; - break; - - case KEY_RETURN: - if (mask == MASK_NONE) - { - if( mRenameItem && mRenamer->getVisible() ) - { - finishRenamingItem(); - mSearchString.clear(); - handled = TRUE; - } - else - { - LLFolderView::openSelectedItems(); - handled = TRUE; - } - } - break; - - case KEY_ESCAPE: - if( mRenameItem && mRenamer->getVisible() ) - { - closeRenamer(); - handled = TRUE; - } - mSearchString.clear(); - break; - - case KEY_PAGE_UP: - mSearchString.clear(); - mScrollContainer->pageUp(30); - handled = TRUE; - break; - - case KEY_PAGE_DOWN: - mSearchString.clear(); - mScrollContainer->pageDown(30); - handled = TRUE; - break; - - case KEY_HOME: - mSearchString.clear(); - mScrollContainer->goToTop(); - handled = TRUE; - break; - - case KEY_END: - mSearchString.clear(); - mScrollContainer->goToBottom(); - break; - - case KEY_DOWN: - if((mSelectedItems.size() > 0) && mScrollContainer) - { - LLFolderViewItem* last_selected = getCurSelectedItem(); - - if (!mKeyboardSelection) - { - setSelection(last_selected, FALSE, TRUE); - mKeyboardSelection = TRUE; - } - - LLFolderViewItem* next = NULL; - if (mask & MASK_SHIFT) - { - // don't shift select down to children of folders (they are implicitly selected through parent) - next = last_selected->getNextOpenNode(FALSE); - if (next) - { - if (next->isSelected()) - { - // shrink selection - changeSelectionFromRoot(last_selected, FALSE); - } - else if (last_selected->getParentFolder() == next->getParentFolder()) - { - // grow selection - changeSelectionFromRoot(next, TRUE); - } - } - } - else - { - next = last_selected->getNextOpenNode(); - if( next ) - { - if (next == last_selected) - { - //special case for LLAccordionCtrl - if(notifyParent(LLSD().with("action","select_next")) > 0 )//message was processed - { - clearSelection(); - return TRUE; - } - return FALSE; - } - setSelection( next, FALSE, TRUE ); - } - else - { - //special case for LLAccordionCtrl - if(notifyParent(LLSD().with("action","select_next")) > 0 )//message was processed - { - clearSelection(); - return TRUE; - } - return FALSE; - } - } - scrollToShowSelection(); - mSearchString.clear(); - handled = TRUE; - } - break; - - case KEY_UP: - if((mSelectedItems.size() > 0) && mScrollContainer) - { - LLFolderViewItem* last_selected = mSelectedItems.back(); - - if (!mKeyboardSelection) - { - setSelection(last_selected, FALSE, TRUE); - mKeyboardSelection = TRUE; - } - - LLFolderViewItem* prev = NULL; - if (mask & MASK_SHIFT) - { - // don't shift select down to children of folders (they are implicitly selected through parent) - prev = last_selected->getPreviousOpenNode(FALSE); - if (prev) - { - if (prev->isSelected()) - { - // shrink selection - changeSelectionFromRoot(last_selected, FALSE); - } - else if (last_selected->getParentFolder() == prev->getParentFolder()) - { - // grow selection - changeSelectionFromRoot(prev, TRUE); - } - } - } - else - { - prev = last_selected->getPreviousOpenNode(); - if( prev ) - { - if (prev == this) - { - // If case we are in accordion tab notify parent to go to the previous accordion - if(notifyParent(LLSD().with("action","select_prev")) > 0 )//message was processed - { - clearSelection(); - return TRUE; - } - - return FALSE; - } - setSelection( prev, FALSE, TRUE ); - } - } - scrollToShowSelection(); - mSearchString.clear(); - - handled = TRUE; - } - break; - - case KEY_RIGHT: - if(mSelectedItems.size()) - { - LLFolderViewItem* last_selected = getCurSelectedItem(); - last_selected->setOpen( TRUE ); - mSearchString.clear(); - handled = TRUE; - } - break; - - case KEY_LEFT: - if(mSelectedItems.size()) - { - LLFolderViewItem* last_selected = getCurSelectedItem(); - LLFolderViewItem* parent_folder = last_selected->getParentFolder(); - if (!last_selected->isOpen() && parent_folder && parent_folder->getParentFolder()) - { - setSelection(parent_folder, FALSE, TRUE); - } - else - { - last_selected->setOpen( FALSE ); - } - mSearchString.clear(); - scrollToShowSelection(); - handled = TRUE; - } - break; - } - - if (!handled && mParentPanel->hasFocus()) - { - if (key == KEY_BACKSPACE) - { - mSearchTimer.reset(); - if (mSearchString.size()) - { - mSearchString.erase(mSearchString.size() - 1, 1); - } - search(getCurSelectedItem(), mSearchString, FALSE); - handled = TRUE; - } - } - - return handled; -} - - -BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char) -{ - if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL - { - return FALSE; - } - - if (uni_char > 0x7f) - { - llwarns << "LLFolderView::handleUnicodeCharHere - Don't handle non-ascii yet, aborting" << llendl; - return FALSE; - } - - BOOL handled = FALSE; - if (mParentPanel->hasFocus()) - { - // SL-51858: Key presses are not being passed to the Popup menu. - // A proper fix is non-trivial so instead just close the menu. - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); - if (menu && menu->isOpen()) - { - LLMenuGL::sMenuContainer->hideMenus(); - } - - //do text search - if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout")) - { - mSearchString.clear(); - } - mSearchTimer.reset(); - if (mSearchString.size() < 128) - { - mSearchString += uni_char; - } - search(getCurSelectedItem(), mSearchString, FALSE); - - handled = TRUE; - } - - return handled; -} - - -BOOL LLFolderView::canDoDelete() const -{ - if (mSelectedItems.size() == 0) return FALSE; - - for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - if (!(*item_it)->getListener()->isItemRemovable()) - { - return FALSE; - } - } - return TRUE; -} - -void LLFolderView::doDelete() -{ - if(mSelectedItems.size() > 0) - { - removeSelectedItems(); - } -} - - -BOOL LLFolderView::handleMouseDown( S32 x, S32 y, MASK mask ) -{ - mKeyboardSelection = FALSE; - mSearchString.clear(); - - mParentPanel->setFocus(TRUE); - - LLEditMenuHandler::gEditMenuHandler = this; - - return LLView::handleMouseDown( x, y, mask ); -} - -BOOL LLFolderView::search(LLFolderViewItem* first_item, const std::string &search_string, BOOL backward) -{ - // get first selected item - LLFolderViewItem* search_item = first_item; - - // make sure search string is upper case - std::string upper_case_string = search_string; - LLStringUtil::toUpper(upper_case_string); - - // if nothing selected, select first item in folder - if (!search_item) - { - // start from first item - search_item = getNextFromChild(NULL); - } - - // search over all open nodes for first substring match (with wrapping) - BOOL found = FALSE; - LLFolderViewItem* original_search_item = search_item; - do - { - // wrap at end - if (!search_item) - { - if (backward) - { - search_item = getPreviousFromChild(NULL); - } - else - { - search_item = getNextFromChild(NULL); - } - if (!search_item || search_item == original_search_item) - { - break; - } - } - - const std::string current_item_label(search_item->getSearchableLabel()); - S32 search_string_length = llmin(upper_case_string.size(), current_item_label.size()); - if (!current_item_label.compare(0, search_string_length, upper_case_string)) - { - found = TRUE; - break; - } - if (backward) - { - search_item = search_item->getPreviousOpenNode(); - } - else - { - search_item = search_item->getNextOpenNode(); - } - - } while(search_item != original_search_item); - - - if (found) - { - setSelection(search_item, FALSE, TRUE); - scrollToShowSelection(); - } - - return found; -} - -BOOL LLFolderView::handleDoubleClick( S32 x, S32 y, MASK mask ) -{ - // skip LLFolderViewFolder::handleDoubleClick() - return LLView::handleDoubleClick( x, y, mask ); -} - -BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask ) -{ - // all user operations move keyboard focus to inventory - // this way, we know when to stop auto-updating a search - mParentPanel->setFocus(TRUE); - - BOOL handled = childrenHandleRightMouseDown(x, y, mask) != NULL; - S32 count = mSelectedItems.size(); - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); - if ( handled - && ( count > 0 && (hasVisibleChildren() || mFilter->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS) ) // show menu only if selected items are visible - && menu ) - { - if (mCallbackRegistrar) - mCallbackRegistrar->pushScope(); - - updateMenuOptions(menu); - - menu->updateParent(LLMenuGL::sMenuContainer); - LLMenuGL::showPopup(this, menu, x, y); - if (mCallbackRegistrar) - mCallbackRegistrar->popScope(); - } - else - { - if (menu && menu->getVisible()) - { - menu->setVisible(FALSE); - } - setSelection(NULL, FALSE, TRUE); - } - return handled; -} - -// Add "--no options--" if the menu is completely blank. -BOOL LLFolderView::addNoOptions(LLMenuGL* menu) const -{ - const std::string nooptions_str = "--no options--"; - LLView *nooptions_item = NULL; - - const LLView::child_list_t *list = menu->getChildList(); - for (LLView::child_list_t::const_iterator itor = list->begin(); - itor != list->end(); - ++itor) - { - LLView *menu_item = (*itor); - if (menu_item->getVisible()) - { - return FALSE; - } - std::string name = menu_item->getName(); - if (menu_item->getName() == nooptions_str) - { - nooptions_item = menu_item; - } - } - if (nooptions_item) - { - nooptions_item->setVisible(TRUE); - nooptions_item->setEnabled(FALSE); - return TRUE; - } - return FALSE; -} - -BOOL LLFolderView::handleHover( S32 x, S32 y, MASK mask ) -{ - return LLView::handleHover( x, y, mask ); -} - -BOOL LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - mDragAndDropThisFrame = TRUE; - // have children handle it first - BOOL handled = LLView::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, - accept, tooltip_msg); - - // when drop is not handled by child, it should be handled - // by the folder which is the hierarchy root. - if (!handled) - { - if (getListener()->getUUID().notNull()) - { - handled = LLFolderViewFolder::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); - } - else - { - if (!mFolders.empty()) - { - // dispatch to last folder as a hack to support "Contents" folder in object inventory - handled = mFolders.back()->handleDragAndDropFromChild(mask,drop,cargo_type,cargo_data,accept,tooltip_msg); - } - } - } - - if (handled) - { - lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderView" << llendl; - } - - return handled; -} - -void LLFolderView::deleteAllChildren() -{ - closeRenamer(); - if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die(); - mPopupMenuHandle = LLHandle<LLView>(); - mScrollContainer = NULL; - mRenameItem = NULL; - mRenamer = NULL; - mStatusTextBox = NULL; - - clearSelection(); - LLView::deleteAllChildren(); -} - -void LLFolderView::scrollToShowSelection() -{ - if ( mSelectedItems.size() ) - { - mNeedsScroll = TRUE; - } -} - -// If the parent is scroll container, scroll it to make the selection -// is maximally visible. -void LLFolderView::scrollToShowItem(LLFolderViewItem* item, const LLRect& constraint_rect) -{ - if (!mScrollContainer) return; - - // don't scroll to items when mouse is being used to scroll/drag and drop - if (gFocusMgr.childHasMouseCapture(mScrollContainer)) - { - mNeedsScroll = FALSE; - return; - } - - // if item exists and is in visible portion of parent folder... - if(item) - { - LLRect local_rect = item->getLocalRect(); - LLRect item_scrolled_rect; // item position relative to display area of scroller - LLRect visible_doc_rect = mScrollContainer->getVisibleContentRect(); - - S32 icon_height = mIcon.isNull() ? 0 : mIcon->getHeight(); - S32 label_height = getLabelFontForStyle(mLabelStyle)->getLineHeight(); - // when navigating with keyboard, only move top of opened folder on screen, otherwise show whole folder - S32 max_height_to_show = item->isOpen() && mScrollContainer->hasFocus() ? (llmax( icon_height, label_height ) + ICON_PAD) : local_rect.getHeight(); - - // get portion of item that we want to see... - LLRect item_local_rect = LLRect(item->getIndentation(), - local_rect.getHeight(), - llmin(MIN_ITEM_WIDTH_VISIBLE, local_rect.getWidth()), - llmax(0, local_rect.getHeight() - max_height_to_show)); - - LLRect item_doc_rect; - - item->localRectToOtherView(item_local_rect, &item_doc_rect, this); - - mScrollContainer->scrollToShowRect( item_doc_rect, constraint_rect ); - - } -} - -LLRect LLFolderView::getVisibleRect() -{ - S32 visible_height = mScrollContainer->getRect().getHeight(); - S32 visible_width = mScrollContainer->getRect().getWidth(); - LLRect visible_rect; - visible_rect.setLeftTopAndSize(-getRect().mLeft, visible_height - getRect().mBottom, visible_width, visible_height); - return visible_rect; -} - -BOOL LLFolderView::getShowSelectionContext() -{ - if (mShowSelectionContext) - { - return TRUE; - } - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); - if (menu && menu->getVisible()) - { - return TRUE; - } - return FALSE; -} - -void LLFolderView::setShowSingleSelection(BOOL show) -{ - if (show != mShowSingleSelection) - { - mMultiSelectionFadeTimer.reset(); - mShowSingleSelection = show; - } -} - -void LLFolderView::addItemID(const LLUUID& id, LLFolderViewItem* itemp) -{ - mItemMap[id] = itemp; -} - -void LLFolderView::removeItemID(const LLUUID& id) -{ - mItemMap.erase(id); -} - -LLFastTimer::DeclareTimer FTM_GET_ITEM_BY_ID("Get FolderViewItem by ID"); -LLFolderViewItem* LLFolderView::getItemByID(const LLUUID& id) -{ - LLFastTimer _(FTM_GET_ITEM_BY_ID); - if (id == getListener()->getUUID()) - { - return this; - } - - std::map<LLUUID, LLFolderViewItem*>::iterator map_it; - map_it = mItemMap.find(id); - if (map_it != mItemMap.end()) - { - return map_it->second; - } - - return NULL; -} - -LLFolderViewFolder* LLFolderView::getFolderByID(const LLUUID& id) -{ - if (id == getListener()->getUUID()) - { - return this; - } - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end(); - ++iter) - { - LLFolderViewFolder *folder = (*iter); - if (folder->getListener()->getUUID() == id) - { - return folder; - } - } - return NULL; -} - -bool LLFolderView::doToSelected(LLInventoryModel* model, const LLSD& userdata) -{ - std::string action = userdata.asString(); - - if ("rename" == action) - { - startRenamingSelectedItem(); - return true; - } - if ("delete" == action) - { - removeSelectedItems(); - return true; - } - if (("copy" == action) || ("cut" == action)) - { - // Clear the clipboard before we start adding things on it - LLClipboard::instance().reset(); - } - - static const std::string change_folder_string = "change_folder_type_"; - if (action.length() > change_folder_string.length() && - (action.compare(0,change_folder_string.length(),"change_folder_type_") == 0)) - { - LLFolderType::EType new_folder_type = LLViewerFolderType::lookupTypeFromXUIName(action.substr(change_folder_string.length())); - changeType(model, new_folder_type); - return true; - } - - - std::set<LLUUID> selected_items = getSelectionList(); - - LLMultiPreview* multi_previewp = NULL; - LLMultiProperties* multi_propertiesp = NULL; - - if (("task_open" == action || "open" == action) && selected_items.size() > 1) - { - multi_previewp = new LLMultiPreview(); - gFloaterView->addChild(multi_previewp); - - LLFloater::setFloaterHost(multi_previewp); - - } - else if (("task_properties" == action || "properties" == action) && selected_items.size() > 1) - { - multi_propertiesp = new LLMultiProperties(); - gFloaterView->addChild(multi_propertiesp); - - LLFloater::setFloaterHost(multi_propertiesp); - } - - std::set<LLUUID>::iterator set_iter; - - for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) - { - LLFolderViewItem* folder_item = getItemByID(*set_iter); - if(!folder_item) continue; - LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getListener(); - if(!bridge) continue; - bridge->performAction(model, action); - } - - LLFloater::setFloaterHost(NULL); - if (multi_previewp) - { - multi_previewp->openFloater(LLSD()); - } - else if (multi_propertiesp) - { - multi_propertiesp->openFloater(LLSD()); - } - - return true; -} - -static LLFastTimer::DeclareTimer FTM_AUTO_SELECT("Open and Select"); -static LLFastTimer::DeclareTimer FTM_INVENTORY("Inventory"); - -// Main idle routine -void LLFolderView::doIdle() -{ - // If this is associated with the user's inventory, don't do anything - // until that inventory is loaded up. - const LLInventoryPanel *inventory_panel = dynamic_cast<LLInventoryPanel*>(mParentPanel); - if (inventory_panel && !inventory_panel->getIsViewsInitialized()) - { - return; - } - - LLFastTimer t2(FTM_INVENTORY); - - BOOL debug_filters = gSavedSettings.getBOOL("DebugInventoryFilters"); - if (debug_filters != getDebugFilters()) - { - mDebugFilters = debug_filters; - arrangeAll(); - } - BOOL filter_modified_and_active = mFilter->isModified() && mFilter->isNotDefault(); - mNeedsAutoSelect = filter_modified_and_active && - !(gFocusMgr.childHasKeyboardFocus(this) || gFocusMgr.getMouseCapture()); - mFilter->clearModified(); - - // filter to determine visibility before arranging - filterFromRoot(); - - // automatically show matching items, and select first one if we had a selection - if (mNeedsAutoSelect) - { - LLFastTimer t3(FTM_AUTO_SELECT); - // select new item only if a filtered item not currently selected - LLFolderViewItem* selected_itemp = mSelectedItems.empty() ? NULL : mSelectedItems.back(); - if (!mAutoSelectOverride && (!selected_itemp || !selected_itemp->potentiallyVisible())) - { - // these are named variables to get around gcc not binding non-const references to rvalues - // and functor application is inherently non-const to allow for stateful functors - LLSelectFirstFilteredItem functor; - applyFunctorRecursively(functor); - } - - // Open filtered folders for folder views with mAutoSelectOverride=TRUE. - // Used by LLPlacesFolderView. - if (!mFilter->getFilterSubString().empty()) - { - // these are named variables to get around gcc not binding non-const references to rvalues - // and functor application is inherently non-const to allow for stateful functors - LLOpenFilteredFolders functor; - applyFunctorRecursively(functor); - } - - scrollToShowSelection(); - } - - BOOL filter_finished = mCompletedFilterGeneration >= mFilter->getCurrentGeneration() - && !LLInventoryModelBackgroundFetch::instance().folderFetchActive(); - if (filter_finished - || gFocusMgr.childHasKeyboardFocus(inventory_panel) - || gFocusMgr.childHasMouseCapture(inventory_panel)) - { - // finishing the filter process, giving focus to the folder view, or dragging the scrollbar all stop the auto select process - mNeedsAutoSelect = FALSE; - } - - - // during filtering process, try to pin selected item's location on screen - // this will happen when searching your inventory and when new items arrive - if (!filter_finished) - { - // calculate rectangle to pin item to at start of animated rearrange - if (!mPinningSelectedItem && !mSelectedItems.empty()) - { - // lets pin it! - mPinningSelectedItem = TRUE; - - LLRect visible_content_rect = mScrollContainer->getVisibleContentRect(); - LLFolderViewItem* selected_item = mSelectedItems.back(); - - LLRect item_rect; - selected_item->localRectToOtherView(selected_item->getLocalRect(), &item_rect, this); - // if item is visible in scrolled region - if (visible_content_rect.overlaps(item_rect)) - { - // then attempt to keep it in same place on screen - mScrollConstraintRect = item_rect; - mScrollConstraintRect.translate(-visible_content_rect.mLeft, -visible_content_rect.mBottom); - } - else - { - // otherwise we just want it onscreen somewhere - LLRect content_rect = mScrollContainer->getContentWindowRect(); - mScrollConstraintRect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight()); - } - } - } - else - { - // stop pinning selected item after folders stop rearranging - if (!needsArrange()) - { - mPinningSelectedItem = FALSE; - } - } - - LLRect constraint_rect; - if (mPinningSelectedItem) - { - // use last known constraint rect for pinned item - constraint_rect = mScrollConstraintRect; - } - else - { - // during normal use (page up/page down, etc), just try to fit item on screen - LLRect content_rect = mScrollContainer->getContentWindowRect(); - constraint_rect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight()); - } - - - BOOL is_visible = isInVisibleChain(); - - if ( is_visible ) - { - sanitizeSelection(); - if( needsArrange() ) - { - arrangeFromRoot(); - } - } - - if (mSelectedItems.size() && mNeedsScroll) - { - scrollToShowItem(mSelectedItems.back(), constraint_rect); - // continue scrolling until animated layout change is done - if (filter_finished - && (!needsArrange() || !is_visible)) - { - mNeedsScroll = FALSE; - } - } - - if (mSignalSelectCallback) - { - //RN: we use keyboard focus as a proxy for user-explicit actions - BOOL take_keyboard_focus = (mSignalSelectCallback == SIGNAL_KEYBOARD_FOCUS); - mSelectSignal(mSelectedItems, take_keyboard_focus); - } - mSignalSelectCallback = FALSE; -} - - -//static -void LLFolderView::idle(void* user_data) -{ - LLFolderView* self = (LLFolderView*)user_data; - if ( self ) - { // Do the real idle - self->doIdle(); - } -} - -void LLFolderView::dumpSelectionInformation() -{ - llinfos << "LLFolderView::dumpSelectionInformation()" << llendl; - llinfos << "****************************************" << llendl; - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) - { - llinfos << " " << (*item_it)->getName() << llendl; - } - llinfos << "****************************************" << llendl; -} - -void LLFolderView::updateRenamerPosition() -{ - if(mRenameItem) - { - // See also LLFolderViewItem::draw() - S32 x = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mRenameItem->getIndentation(); - S32 y = mRenameItem->getRect().getHeight() - mRenameItem->getItemHeight() - RENAME_HEIGHT_PAD; - mRenameItem->localPointToScreen( x, y, &x, &y ); - screenPointToLocal( x, y, &x, &y ); - mRenamer->setOrigin( x, y ); - - LLRect scroller_rect(0, 0, gViewerWindow->getWindowWidthScaled(), 0); - if (mScrollContainer) - { - scroller_rect = mScrollContainer->getContentWindowRect(); - } - - S32 width = llmax(llmin(mRenameItem->getRect().getWidth() - x, scroller_rect.getWidth() - x - getRect().mLeft), MINIMUM_RENAMER_WIDTH); - S32 height = mRenameItem->getItemHeight() - RENAME_HEIGHT_PAD; - mRenamer->reshape( width, height, TRUE ); - } -} - -// Update visibility and availability (i.e. enabled/disabled) of context menu items. -void LLFolderView::updateMenuOptions(LLMenuGL* menu) -{ - const LLView::child_list_t *list = menu->getChildList(); - - LLView::child_list_t::const_iterator menu_itor; - for (menu_itor = list->begin(); menu_itor != list->end(); ++menu_itor) - { - (*menu_itor)->setVisible(FALSE); - (*menu_itor)->pushVisible(TRUE); - (*menu_itor)->setEnabled(TRUE); - } - - // Successively filter out invalid options - - U32 flags = FIRST_SELECTED_ITEM; - for (selected_items_t::iterator item_itor = mSelectedItems.begin(); - item_itor != mSelectedItems.end(); - ++item_itor) - { - LLFolderViewItem* selected_item = (*item_itor); - selected_item->buildContextMenu(*menu, flags); - flags = 0x0; - } - - addNoOptions(menu); -} - -// Refresh the context menu (that is already shown). -void LLFolderView::updateMenu() -{ - LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); - if (menu && menu->getVisible()) - { - updateMenuOptions(menu); - menu->needsArrange(); // update menu height if needed - } -} - -bool LLFolderView::selectFirstItem() -{ - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();++iter) - { - LLFolderViewFolder* folder = (*iter ); - if (folder->getVisible()) - { - LLFolderViewItem* itemp = folder->getNextFromChild(0,true); - if(itemp) - setSelection(itemp,FALSE,TRUE); - return true; - } - - } - for(items_t::iterator iit = mItems.begin(); - iit != mItems.end(); ++iit) - { - LLFolderViewItem* itemp = (*iit); - if (itemp->getVisible()) - { - setSelection(itemp,FALSE,TRUE); - return true; - } - } - return false; -} -bool LLFolderView::selectLastItem() -{ - for(items_t::reverse_iterator iit = mItems.rbegin(); - iit != mItems.rend(); ++iit) - { - LLFolderViewItem* itemp = (*iit); - if (itemp->getVisible()) - { - setSelection(itemp,FALSE,TRUE); - return true; - } - } - for (folders_t::reverse_iterator iter = mFolders.rbegin(); - iter != mFolders.rend();++iter) - { - LLFolderViewFolder* folder = (*iter); - if (folder->getVisible()) - { - LLFolderViewItem* itemp = folder->getPreviousFromChild(0,true); - if(itemp) - setSelection(itemp,FALSE,TRUE); - return true; - } - } - return false; -} - - -S32 LLFolderView::notify(const LLSD& info) -{ - if(info.has("action")) - { - std::string str_action = info["action"]; - if(str_action == "select_first") - { - setFocus(true); - selectFirstItem(); - scrollToShowSelection(); - return 1; - - } - else if(str_action == "select_last") - { - setFocus(true); - selectLastItem(); - scrollToShowSelection(); - return 1; - } - } - return 0; -} - - -///---------------------------------------------------------------------------- -/// Local function definitions -///---------------------------------------------------------------------------- - -void LLFolderView::onRenamerLost() -{ - if (mRenamer && mRenamer->getVisible()) - { - mRenamer->setVisible(FALSE); - - // will commit current name (which could be same as original name) - mRenamer->setFocus(FALSE); - } - - if( mRenameItem ) - { - setSelectionFromRoot( mRenameItem, TRUE ); - mRenameItem = NULL; - } -} - -LLFolderViewItem* LLFolderView::getNextUnselectedItem() -{ - LLFolderViewItem* last_item = *mSelectedItems.rbegin(); - LLFolderViewItem* new_selection = last_item->getNextOpenNode(FALSE); - while(new_selection && new_selection->isSelected()) - { - new_selection = new_selection->getNextOpenNode(FALSE); - } - if (!new_selection) - { - new_selection = last_item->getPreviousOpenNode(FALSE); - while (new_selection && (new_selection->isInSelection())) - { - new_selection = new_selection->getPreviousOpenNode(FALSE); - } - } - return new_selection; -} - -LLInventoryFilter* LLFolderView::getFilter() -{ - return mFilter; -} - -void LLFolderView::setFilterPermMask( PermissionMask filter_perm_mask ) -{ - mFilter->setFilterPermissions(filter_perm_mask); -} - -U32 LLFolderView::getFilterObjectTypes() const -{ - return mFilter->getFilterObjectTypes(); -} - -PermissionMask LLFolderView::getFilterPermissions() const -{ - return mFilter->getFilterPermissions(); -} - -BOOL LLFolderView::isFilterModified() -{ - return mFilter->isNotDefault(); -} - -void delete_selected_item(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data); - fv->removeSelectedItems(); - } -} - -void copy_selected_item(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data); - fv->copy(); - } -} - -void paste_items(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data); - fv->paste(); - } -} - -void open_selected_items(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data); - fv->openSelectedItems(); - } -} - -void properties_selected_items(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data); - fv->propertiesSelectedItems(); - } -} diff --git a/indra/newview/llfolderview.h b/indra/newview/llfolderview.h deleted file mode 100644 index 3f78312a98..0000000000 --- a/indra/newview/llfolderview.h +++ /dev/null @@ -1,377 +0,0 @@ -/** - * @file llfolderview.h - * @brief Definition of the folder view collection of classes. - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -/** - * - * The folder view collection of classes provides an interface for - * making a 'folder view' similar to the way the a single pane file - * folder interface works. - * - */ - -#ifndef LL_LLFOLDERVIEW_H -#define LL_LLFOLDERVIEW_H - -#include "llfolderviewitem.h" // because LLFolderView is-a LLFolderViewFolder - -#include "lluictrl.h" -#include "v4color.h" -#include "lldarray.h" -#include "stdenums.h" -#include "lldepthstack.h" -#include "lleditmenuhandler.h" -#include "llfontgl.h" -#include "llscrollcontainer.h" -#include "lltooldraganddrop.h" -#include "llviewertexture.h" - -class LLFolderViewEventListener; -class LLFolderViewFolder; -class LLFolderViewItem; -class LLInventoryModel; -class LLPanel; -class LLLineEditor; -class LLMenuGL; -class LLUICtrl; -class LLTextBox; - -/** - * Class LLFolderViewScrollContainer - * - * A scroll container which provides the information about the height - * of currently displayed folder view contents. - * Used for updating vertical scroll bar visibility in inventory panel. - * See LLScrollContainer::calcVisibleSize(). - */ -class LLFolderViewScrollContainer : public LLScrollContainer -{ -public: - /*virtual*/ ~LLFolderViewScrollContainer() {}; - /*virtual*/ const LLRect getScrolledViewRect() const; - -protected: - LLFolderViewScrollContainer(const LLScrollContainer::Params& p); - friend class LLUICtrlFactory; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLFolderView -// -// The LLFolderView represents the root level folder view object. -// It manages the screen region of the folder view. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLFolderView : public LLFolderViewFolder, public LLEditMenuHandler -{ -public: - struct Params : public LLInitParam::Block<Params, LLFolderViewFolder::Params> - { - Mandatory<LLPanel*> parent_panel; - Optional<LLUUID> task_id; - Optional<std::string> title; - Optional<bool> use_label_suffix, - allow_multiselect, - show_empty_message, - show_load_status, - use_ellipses; - - Params(); - }; - - friend class LLFolderViewScrollContainer; - - LLFolderView(const Params&); - virtual ~LLFolderView( void ); - - virtual BOOL canFocusChildren() const; - - virtual LLFolderView* getRoot() { return this; } - - // FolderViews default to sort by name. This will change that, - // and resort the items if necessary. - void setSortOrder(U32 order); - void setFilterPermMask(PermissionMask filter_perm_mask); - - typedef boost::signals2::signal<void (const std::deque<LLFolderViewItem*>& items, BOOL user_action)> signal_t; - void setSelectCallback(const signal_t::slot_type& cb) { mSelectSignal.connect(cb); } - void setReshapeCallback(const signal_t::slot_type& cb) { mReshapeSignal.connect(cb); } - - // filter is never null - LLInventoryFilter* getFilter(); - const std::string getFilterSubString(BOOL trim = FALSE); - U32 getFilterObjectTypes() const; - PermissionMask getFilterPermissions() const; - // *NOTE: use getFilter()->getShowFolderState(); - //LLInventoryFilter::EFolderShow getShowFolderState(); - U32 getSortOrder() const; - BOOL isFilterModified(); - - bool getAllowMultiSelect() { return mAllowMultiSelect; } - - // Close all folders in the view - void closeAllFolders(); - void openTopLevelFolders(); - - virtual void toggleOpen() {}; - virtual void setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse); - virtual BOOL addFolder( LLFolderViewFolder* folder); - - // Find width and height of this object and its children. Also - // makes sure that this view and its children are the right size. - virtual S32 arrange( S32* width, S32* height, S32 filter_generation ); - - void arrangeAll() { mArrangeGeneration++; } - S32 getArrangeGeneration() { return mArrangeGeneration; } - - // Apply filters to control visibility of inventory items - virtual void filter( LLInventoryFilter& filter); - - // Get the last selected item - virtual LLFolderViewItem* getCurSelectedItem( void ); - - // Record the selected item and pass it down the hierarchy. - virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem, - BOOL take_keyboard_focus); - - // Used by menu callbacks - void setSelectionByID(const LLUUID& obj_id, BOOL take_keyboard_focus); - - // Called once a frame to update the selection if mSelectThisID has been set - void updateSelection(); - - // This method is used to toggle the selection of an item. - // Walks children and keeps track of selected objects. - virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected); - - virtual std::set<LLUUID> getSelectionList() const; - - // Make sure if ancestor is selected, descendents are not - void sanitizeSelection(); - void clearSelection(); - void addToSelectionList(LLFolderViewItem* item); - void removeFromSelectionList(LLFolderViewItem* item); - - BOOL startDrag(LLToolDragAndDrop::ESource source); - void setDragAndDropThisFrame() { mDragAndDropThisFrame = TRUE; } - void setDraggingOverItem(LLFolderViewItem* item) { mDraggingOverItem = item; } - LLFolderViewItem* getDraggingOverItem() { return mDraggingOverItem; } - - // Deletion functionality - void removeSelectedItems(); - static void removeCutItems(); - - // Open the selected item - void openSelectedItems( void ); - void propertiesSelectedItems( void ); - - // Change the folder type - void changeType(LLInventoryModel *model, LLFolderType::EType new_folder_type); - - void autoOpenItem(LLFolderViewFolder* item); - void closeAutoOpenedFolders(); - BOOL autoOpenTest(LLFolderViewFolder* item); - - // Copy & paste - virtual void copy(); - virtual BOOL canCopy() const; - - virtual void cut(); - virtual BOOL canCut() const; - - virtual void paste(); - virtual BOOL canPaste() const; - - virtual void doDelete(); - virtual BOOL canDoDelete() const; - - LLFolderViewItem* getNextUnselectedItem(); - - // Public rename functionality - can only start the process - void startRenamingSelectedItem( void ); - - // These functions were used when there was only one folderview, - // and relied on that concept. This functionality is now handled - // by the listeners and the lldraganddroptool. - //LLFolderViewItem* getMovingItem() { return mMovingItem; } - //void setMovingItem( LLFolderViewItem* item ) { mMovingItem = item; } - //void dragItemIntoFolder( LLFolderViewItem* moving_item, LLFolderViewFolder* dst_folder, BOOL drop, BOOL* accept ); - //void dragFolderIntoFolder( LLFolderViewFolder* moving_folder, LLFolderViewFolder* dst_folder, BOOL drop, BOOL* accept ); - - // LLView functionality - ///*virtual*/ BOOL handleKey( KEY key, MASK mask, BOOL called_from_parent ); - /*virtual*/ BOOL handleKeyHere( KEY key, MASK mask ); - /*virtual*/ BOOL handleUnicodeCharHere(llwchar uni_char); - /*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask ); - /*virtual*/ BOOL handleDoubleClick( S32 x, S32 y, MASK mask ); - /*virtual*/ BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); - /*virtual*/ BOOL handleHover( S32 x, S32 y, MASK mask ); - /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); - /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); - /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask) { setShowSelectionContext(FALSE); } - virtual void draw(); - virtual void deleteAllChildren(); - - void scrollToShowSelection(); - void scrollToShowItem(LLFolderViewItem* item, const LLRect& constraint_rect); - void setScrollContainer( LLScrollContainer* parent ) { mScrollContainer = parent; } - LLRect getVisibleRect(); - - BOOL search(LLFolderViewItem* first_item, const std::string &search_string, BOOL backward); - void setShowSelectionContext(BOOL show) { mShowSelectionContext = show; } - BOOL getShowSelectionContext(); - void setShowSingleSelection(BOOL show); - BOOL getShowSingleSelection() { return mShowSingleSelection; } - F32 getSelectionFadeElapsedTime() { return mMultiSelectionFadeTimer.getElapsedTimeF32(); } - bool getUseEllipses() { return mUseEllipses; } - - void addItemID(const LLUUID& id, LLFolderViewItem* itemp); - void removeItemID(const LLUUID& id); - LLFolderViewItem* getItemByID(const LLUUID& id); - LLFolderViewFolder* getFolderByID(const LLUUID& id); - - bool doToSelected(LLInventoryModel* model, const LLSD& userdata); - - void doIdle(); // Real idle routine - static void idle(void* user_data); // static glue to doIdle() - - BOOL needsAutoSelect() { return mNeedsAutoSelect && !mAutoSelectOverride; } - BOOL needsAutoRename() { return mNeedsAutoRename; } - void setNeedsAutoRename(BOOL val) { mNeedsAutoRename = val; } - void setPinningSelectedItem(BOOL val) { mPinningSelectedItem = val; } - void setAutoSelectOverride(BOOL val) { mAutoSelectOverride = val; } - - void setCallbackRegistrar(LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* registrar) { mCallbackRegistrar = registrar; } - - BOOL getDebugFilters() { return mDebugFilters; } - - LLPanel* getParentPanel() { return mParentPanel; } - // DEBUG only - void dumpSelectionInformation(); - - virtual S32 notify(const LLSD& info) ; - - bool useLabelSuffix() { return mUseLabelSuffix; } - void updateMenu(); - -private: - void updateMenuOptions(LLMenuGL* menu); - void updateRenamerPosition(); - -protected: - LLScrollContainer* mScrollContainer; // NULL if this is not a child of a scroll container. - - void commitRename( const LLSD& data ); - void onRenamerLost(); - - void finishRenamingItem( void ); - void closeRenamer( void ); - - bool selectFirstItem(); - bool selectLastItem(); - - BOOL addNoOptions(LLMenuGL* menu) const; - - void onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response); - -protected: - LLHandle<LLView> mPopupMenuHandle; - - typedef std::deque<LLFolderViewItem*> selected_items_t; - selected_items_t mSelectedItems; - BOOL mKeyboardSelection; - BOOL mAllowMultiSelect; - BOOL mShowEmptyMessage; - BOOL mShowFolderHierarchy; - LLUUID mSourceID; - - // Renaming variables and methods - LLFolderViewItem* mRenameItem; // The item currently being renamed - LLLineEditor* mRenamer; - - BOOL mNeedsScroll; - BOOL mPinningSelectedItem; - LLRect mScrollConstraintRect; - BOOL mNeedsAutoSelect; - BOOL mAutoSelectOverride; - BOOL mNeedsAutoRename; - bool mUseLabelSuffix; - - BOOL mDebugFilters; - U32 mSortOrder; - LLDepthStack<LLFolderViewFolder> mAutoOpenItems; - LLFolderViewFolder* mAutoOpenCandidate; - LLFrameTimer mAutoOpenTimer; - LLFrameTimer mSearchTimer; - std::string mSearchString; - LLInventoryFilter* mFilter; - BOOL mShowSelectionContext; - BOOL mShowSingleSelection; - LLFrameTimer mMultiSelectionFadeTimer; - S32 mArrangeGeneration; - - signal_t mSelectSignal; - signal_t mReshapeSignal; - S32 mSignalSelectCallback; - S32 mMinWidth; - S32 mRunningHeight; - std::map<LLUUID, LLFolderViewItem*> mItemMap; - BOOL mDragAndDropThisFrame; - - LLUUID mSelectThisID; // if non null, select this item - - LLPanel* mParentPanel; - - /** - * Is used to determine if we need to cut text In LLFolderViewItem to avoid horizontal scroll. - * NOTE: For now it's used only to cut LLFolderViewItem::mLabel text for Landmarks in Places Panel. - */ - bool mUseEllipses; // See EXT-719 - - /** - * Contains item under mouse pointer while dragging - */ - LLFolderViewItem* mDraggingOverItem; // See EXT-719 - - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* mCallbackRegistrar; - -public: - static F32 sAutoOpenTime; - LLTextBox* mStatusTextBox; - -}; - -bool sort_item_name(LLFolderViewItem* a, LLFolderViewItem* b); -bool sort_item_date(LLFolderViewItem* a, LLFolderViewItem* b); - -// Flags for buildContextMenu() -const U32 SUPPRESS_OPEN_ITEM = 0x1; -const U32 FIRST_SELECTED_ITEM = 0x2; - -#endif // LL_LLFOLDERVIEW_H diff --git a/indra/newview/llfoldervieweventlistener.h b/indra/newview/llfoldervieweventlistener.h deleted file mode 100644 index 06682dcbf1..0000000000 --- a/indra/newview/llfoldervieweventlistener.h +++ /dev/null @@ -1,103 +0,0 @@ -/** - * @file llfoldervieweventlistener.h - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ -#ifndef LLFOLDERVIEWEVENTLISTENER_H -#define LLFOLDERVIEWEVENTLISTENER_H - -#include "lldarray.h" // *TODO: convert to std::vector -#include "llfoldertype.h" -#include "llfontgl.h" // just for StyleFlags enum -#include "llinventorytype.h" -#include "llpermissionsflags.h" -#include "llpointer.h" -#include "llwearabletype.h" - - -class LLFolderViewItem; -class LLFolderView; -class LLFontGL; -class LLInventoryModel; -class LLMenuGL; -class LLScrollContainer; -class LLUIImage; -class LLUUID; - -// This is an abstract base class that users of the folderview classes -// would use to catch the useful events emitted from the folder -// views. -class LLFolderViewEventListener -{ -public: - virtual ~LLFolderViewEventListener( void ) {} - virtual const std::string& getName() const = 0; - virtual const std::string& getDisplayName() const = 0; - virtual const LLUUID& getUUID() const = 0; - virtual time_t getCreationDate() const = 0; // UTC seconds - virtual PermissionMask getPermissionMask() const = 0; - virtual LLFolderType::EType getPreferredType() const = 0; - virtual LLPointer<LLUIImage> getIcon() const = 0; - virtual LLPointer<LLUIImage> getOpenIcon() const { return getIcon(); } - virtual LLFontGL::StyleFlags getLabelStyle() const = 0; - virtual std::string getLabelSuffix() const = 0; - virtual void openItem( void ) = 0; - virtual void closeItem( void ) = 0; - virtual void previewItem( void ) = 0; - virtual void selectItem(void) = 0; - virtual void showProperties(void) = 0; - virtual BOOL isItemRenameable() const = 0; - virtual BOOL renameItem(const std::string& new_name) = 0; - virtual BOOL isItemMovable( void ) const = 0; // Can be moved to another folder - virtual BOOL isItemRemovable( void ) const = 0; // Can be destroyed - virtual BOOL isItemInTrash( void) const { return FALSE; } // TODO: make into pure virtual. - virtual BOOL removeItem() = 0; - virtual void removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch) = 0; - virtual void move( LLFolderViewEventListener* parent_listener ) = 0; - virtual BOOL isItemCopyable() const = 0; - virtual BOOL copyToClipboard() const = 0; - virtual BOOL cutToClipboard() const = 0; - virtual BOOL isClipboardPasteable() const = 0; - virtual void pasteFromClipboard() = 0; - virtual void pasteLinkFromClipboard() = 0; - virtual void buildContextMenu(LLMenuGL& menu, U32 flags) = 0; - virtual BOOL isUpToDate() const = 0; - virtual BOOL hasChildren() const = 0; - virtual LLInventoryType::EType getInventoryType() const = 0; - virtual void performAction(LLInventoryModel* model, std::string action) = 0; - virtual LLWearableType::EType getWearableType() const = 0; - - // This method should be called when a drag begins. returns TRUE - // if the drag can begin, otherwise FALSE. - virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const = 0; - - // This method will be called to determine if a drop can be - // performed, and will set drop to TRUE if a drop is - // requested. Returns TRUE if a drop is possible/happened, - // otherwise FALSE. - virtual BOOL dragOrDrop(MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - std::string& tooltip_msg) = 0; -}; - -#endif diff --git a/indra/newview/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp deleted file mode 100644 index 3aa16b4413..0000000000 --- a/indra/newview/llfolderviewitem.cpp +++ /dev/null @@ -1,2901 +0,0 @@ -/** -* @file llfolderviewitem.cpp -* @brief Items and folders that can appear in a hierarchical folder view -* -* $LicenseInfo:firstyear=2001&license=viewerlgpl$ -* Second Life Viewer Source Code -* Copyright (C) 2010, Linden Research, Inc. -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public -* License as published by the Free Software Foundation; -* version 2.1 of the License only. -* -* This library is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this library; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -* -* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -* $/LicenseInfo$ -*/ -#include "llviewerprecompiledheaders.h" - -#include "llfolderviewitem.h" - -// viewer includes -#include "llfolderview.h" // Items depend extensively on LLFolderViews -#include "llfoldervieweventlistener.h" -#include "llviewerfoldertype.h" -#include "llinventorybridge.h" // for LLItemBridge in LLInventorySort::operator() -#include "llinventoryfilter.h" -#include "llinventoryfunctions.h" -#include "llinventorymodelbackgroundfetch.h" -#include "llpanel.h" -#include "llviewercontrol.h" // gSavedSettings -#include "llviewerwindow.h" // Argh, only for setCursor() - -// linden library includes -#include "llclipboard.h" -#include "llfocusmgr.h" // gFocusMgr -#include "lltrans.h" - -///---------------------------------------------------------------------------- -/// Class LLFolderViewItem -///---------------------------------------------------------------------------- - -static LLDefaultChildRegistry::Register<LLFolderViewItem> r("folder_view_item"); - -// statics -std::map<U8, LLFontGL*> LLFolderViewItem::sFonts; // map of styles to fonts - -// only integers can be initialized in header -const F32 LLFolderViewItem::FOLDER_CLOSE_TIME_CONSTANT = 0.02f; -const F32 LLFolderViewItem::FOLDER_OPEN_TIME_CONSTANT = 0.03f; - -const LLColor4U DEFAULT_WHITE(255, 255, 255); - - -//static -LLFontGL* LLFolderViewItem::getLabelFontForStyle(U8 style) -{ - LLFontGL* rtn = sFonts[style]; - if (!rtn) // grab label font with this style, lazily - { - LLFontDescriptor labelfontdesc("SansSerif", "Small", style); - rtn = LLFontGL::getFont(labelfontdesc); - if (!rtn) - { - rtn = LLFontGL::getFontDefault(); - } - sFonts[style] = rtn; - } - return rtn; -} - -//static -void LLFolderViewItem::initClass() -{ -} - -//static -void LLFolderViewItem::cleanupClass() -{ - sFonts.clear(); -} - - -// NOTE: Optimize this, we call it a *lot* when opening a large inventory -LLFolderViewItem::Params::Params() -: icon(), - icon_open(), - icon_overlay(), - root(), - listener(), - folder_arrow_image("folder_arrow_image"), - folder_indentation("folder_indentation"), - selection_image("selection_image"), - item_height("item_height"), - item_top_pad("item_top_pad"), - creation_date() -{} - -// Default constructor -LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) -: LLView(p), - mLabelWidth(0), - mLabelWidthDirty(false), - mParentFolder( NULL ), - mIsSelected( FALSE ), - mIsCurSelection( FALSE ), - mSelectPending(FALSE), - mLabelStyle( LLFontGL::NORMAL ), - mHasVisibleChildren(FALSE), - mIndentation(0), - mItemHeight(p.item_height), - mPassedFilter(FALSE), - mLastFilterGeneration(-1), - mStringMatchOffset(std::string::npos), - mControlLabelRotation(0.f), - mDragAndDropTarget(FALSE), - mIsLoading(FALSE), - mLabel(p.name), - mRoot(p.root), - mCreationDate(p.creation_date), - mIcon(p.icon), - mIconOpen(p.icon_open), - mIconOverlay(p.icon_overlay), - mListener(p.listener), - mShowLoadStatus(false), - mIsMouseOverTitle(false) -{ -} - -BOOL LLFolderViewItem::postBuild() -{ - refresh(); - return TRUE; -} - -// Destroys the object -LLFolderViewItem::~LLFolderViewItem( void ) -{ - delete mListener; - mListener = NULL; -} - -LLFolderView* LLFolderViewItem::getRoot() -{ - return mRoot; -} - -// Returns true if this object is a child (or grandchild, etc.) of potential_ancestor. -BOOL LLFolderViewItem::isDescendantOf( const LLFolderViewFolder* potential_ancestor ) -{ - LLFolderViewItem* root = this; - while( root->mParentFolder ) - { - if( root->mParentFolder == potential_ancestor ) - { - return TRUE; - } - root = root->mParentFolder; - } - return FALSE; -} - -LLFolderViewItem* LLFolderViewItem::getNextOpenNode(BOOL include_children) -{ - if (!mParentFolder) - { - return NULL; - } - - LLFolderViewItem* itemp = mParentFolder->getNextFromChild( this, include_children ); - while(itemp && !itemp->getVisible()) - { - LLFolderViewItem* next_itemp = itemp->mParentFolder->getNextFromChild( itemp, include_children ); - if (itemp == next_itemp) - { - // hit last item - return itemp->getVisible() ? itemp : this; - } - itemp = next_itemp; - } - - return itemp; -} - -LLFolderViewItem* LLFolderViewItem::getPreviousOpenNode(BOOL include_children) -{ - if (!mParentFolder) - { - return NULL; - } - - LLFolderViewItem* itemp = mParentFolder->getPreviousFromChild( this, include_children ); - - // Skip over items that are invisible or are hidden from the UI. - while(itemp && !itemp->getVisible()) - { - LLFolderViewItem* next_itemp = itemp->mParentFolder->getPreviousFromChild( itemp, include_children ); - if (itemp == next_itemp) - { - // hit first item - return itemp->getVisible() ? itemp : this; - } - itemp = next_itemp; - } - - return itemp; -} - -// is this item something we think we should be showing? -// for example, if we haven't gotten around to filtering it yet, then the answer is yes -// until we find out otherwise -BOOL LLFolderViewItem::potentiallyVisible() -{ - // we haven't been checked against min required filter - // or we have and we passed - return potentiallyFiltered(); -} - -BOOL LLFolderViewItem::potentiallyFiltered() -{ - return getLastFilterGeneration() < getRoot()->getFilter()->getMinRequiredGeneration() || getFiltered(); -} - -BOOL LLFolderViewItem::getFiltered() -{ - return mPassedFilter && mLastFilterGeneration >= getRoot()->getFilter()->getMinRequiredGeneration(); -} - -BOOL LLFolderViewItem::getFiltered(S32 filter_generation) -{ - return mPassedFilter && mLastFilterGeneration >= filter_generation; -} - -void LLFolderViewItem::setFiltered(BOOL filtered, S32 filter_generation) -{ - mPassedFilter = filtered; - mLastFilterGeneration = filter_generation; -} - -void LLFolderViewItem::setIcon(LLUIImagePtr icon) -{ - mIcon = icon; -} - -// refresh information from the listener -void LLFolderViewItem::refreshFromListener() -{ - if(mListener) - { - mLabel = mListener->getDisplayName(); - LLFolderType::EType preferred_type = mListener->getPreferredType(); - - // *TODO: to be removed when database supports multi language. This is a - // temporary attempt to display the inventory folder in the user locale. - // mantipov: *NOTE: be sure this code is synchronized with LLFriendCardsManager::findChildFolderUUID - // it uses the same way to find localized string - - // HACK: EXT - 6028 ([HARD CODED]? Inventory > Library > "Accessories" folder) - // Translation of Accessories folder in Library inventory folder - bool accessories = false; - if(mLabel == std::string("Accessories")) - { - //To ensure that Accessories folder is in Library we have to check its parent folder. - //Due to parent LLFolderViewFloder is not set to this item yet we have to check its parent via Inventory Model - LLInventoryCategory* cat = gInventory.getCategory(mListener->getUUID()); - if(cat) - { - const LLUUID& parent_folder_id = cat->getParentUUID(); - accessories = (parent_folder_id == gInventory.getLibraryRootFolderID()); - } - } - - //"Accessories" inventory category has folder type FT_NONE. So, this folder - //can not be detected as protected with LLFolderType::lookupIsProtectedType - if (accessories || LLFolderType::lookupIsProtectedType(preferred_type)) - { - LLTrans::findString(mLabel, "InvFolder " + mLabel); - }; - - setToolTip(mLabel); - setIcon(mListener->getIcon()); - time_t creation_date = mListener->getCreationDate(); - if ((creation_date > 0) && (mCreationDate != creation_date)) - { - setCreationDate(creation_date); - dirtyFilter(); - } - if (mRoot->useLabelSuffix()) - { - mLabelStyle = mListener->getLabelStyle(); - mLabelSuffix = mListener->getLabelSuffix(); - } - } -} - -void LLFolderViewItem::refresh() -{ - refreshFromListener(); - - std::string searchable_label(mLabel); - searchable_label.append(mLabelSuffix); - LLStringUtil::toUpper(searchable_label); - - if (mSearchableLabel.compare(searchable_label)) - { - mSearchableLabel.assign(searchable_label); - dirtyFilter(); - // some part of label has changed, so overall width has potentially changed, and sort order too - if (mParentFolder) - { - mParentFolder->requestSort(); - mParentFolder->requestArrange(); - } - } - - mLabelWidthDirty = true; -} - -void LLFolderViewItem::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor) -{ - functor(mListener); -} - -// This function is called when items are added or view filters change. It's -// implemented here but called by derived classes when folding the -// views. -void LLFolderViewItem::filterFromRoot( void ) -{ - LLFolderViewItem* root = getRoot(); - - root->filter(*((LLFolderView*)root)->getFilter()); -} - -// This function is called when the folder view is dirty. It's -// implemented here but called by derived classes when folding the -// views. -void LLFolderViewItem::arrangeFromRoot() -{ - LLFolderViewItem* root = getRoot(); - - S32 height = 0; - S32 width = 0; - S32 total_height = root->arrange( &width, &height, 0 ); - - LLSD params; - params["action"] = "size_changes"; - params["height"] = total_height; - getParent()->notifyParent(params); -} - -// Utility function for LLFolderView -void LLFolderViewItem::arrangeAndSet(BOOL set_selection, - BOOL take_keyboard_focus) -{ - LLFolderView* root = getRoot(); - if (getParentFolder()) - { - getParentFolder()->requestArrange(); - } - if(set_selection) - { - setSelectionFromRoot(this, TRUE, take_keyboard_focus); - if(root) - { - root->scrollToShowSelection(); - } - } -} - -// This function clears the currently selected item, and records the -// specified selected item appropriately for display and use in the -// UI. If open is TRUE, then folders are opened up along the way to -// the selection. -void LLFolderViewItem::setSelectionFromRoot(LLFolderViewItem* selection, - BOOL openitem, - BOOL take_keyboard_focus) -{ - getRoot()->setSelection(selection, openitem, take_keyboard_focus); -} - -// helper function to change the selection from the root. -void LLFolderViewItem::changeSelectionFromRoot(LLFolderViewItem* selection, BOOL selected) -{ - getRoot()->changeSelection(selection, selected); -} - -std::set<LLUUID> LLFolderViewItem::getSelectionList() const -{ - std::set<LLUUID> selection; - return selection; -} - -EInventorySortGroup LLFolderViewItem::getSortGroup() const -{ - return SG_ITEM; -} - -// addToFolder() returns TRUE if it succeeds. FALSE otherwise -BOOL LLFolderViewItem::addToFolder(LLFolderViewFolder* folder, LLFolderView* root) -{ - if (!folder) - { - return FALSE; - } - mParentFolder = folder; - root->addItemID(getListener()->getUUID(), this); - return folder->addItem(this); -} - - -// Finds width and height of this object and its children. Also -// makes sure that this view and its children are the right size. -S32 LLFolderViewItem::arrange( S32* width, S32* height, S32 filter_generation) -{ - const Params& p = LLUICtrlFactory::getDefaultParams<LLFolderViewItem>(); - S32 indentation = p.folder_indentation(); - // Only indent deeper items in hierarchy - mIndentation = (getParentFolder() - && getParentFolder()->getParentFolder() ) - ? mParentFolder->getIndentation() + indentation - : 0; - if (mLabelWidthDirty) - { - mLabelWidth = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + getLabelFontForStyle(mLabelStyle)->getWidth(mLabel) + getLabelFontForStyle(mLabelStyle)->getWidth(mLabelSuffix) + TEXT_PAD_RIGHT; - mLabelWidthDirty = false; - } - - *width = llmax(*width, mLabelWidth + mIndentation); - - // determine if we need to use ellipses to avoid horizontal scroll. EXT-719 - bool use_ellipses = getRoot()->getUseEllipses(); - if (use_ellipses) - { - // limit to set rect to avoid horizontal scrollbar - *width = llmin(*width, getRoot()->getRect().getWidth()); - } - *height = getItemHeight(); - return *height; -} - -S32 LLFolderViewItem::getItemHeight() -{ - return mItemHeight; -} - -void LLFolderViewItem::filter( LLInventoryFilter& filter) -{ - const BOOL previous_passed_filter = mPassedFilter; - const BOOL passed_filter = filter.check(this); - - // If our visibility will change as a result of this filter, then - // we need to be rearranged in our parent folder - if (mParentFolder) - { - if (getVisible() != passed_filter - || previous_passed_filter != passed_filter ) - mParentFolder->requestArrange(); - } - - setFiltered(passed_filter, filter.getCurrentGeneration()); - mStringMatchOffset = filter.getStringMatchOffset(); - filter.decrementFilterCount(); - - if (getRoot()->getDebugFilters()) - { - mStatusText = llformat("%d", mLastFilterGeneration); - } -} - -void LLFolderViewItem::dirtyFilter() -{ - mLastFilterGeneration = -1; - // bubble up dirty flag all the way to root - if (getParentFolder()) - { - getParentFolder()->setCompletedFilterGeneration(-1, TRUE); - } -} - -// *TODO: This can be optimized a lot by simply recording that it is -// selected in the appropriate places, and assuming that set selection -// means 'deselect' for a leaf item. Do this optimization after -// multiple selection is implemented to make sure it all plays nice -// together. -BOOL LLFolderViewItem::setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus) -{ - if (selection == this && !mIsSelected) - { - selectItem(); - } - else if (mIsSelected) // Deselect everything else. - { - deselectItem(); - } - return mIsSelected; -} - -BOOL LLFolderViewItem::changeSelection(LLFolderViewItem* selection, BOOL selected) -{ - if (selection == this) - { - if (mIsSelected) - { - deselectItem(); - } - else - { - selectItem(); - } - return TRUE; - } - return FALSE; -} - -void LLFolderViewItem::deselectItem(void) -{ - mIsSelected = FALSE; -} - -void LLFolderViewItem::selectItem(void) -{ - if (mIsSelected == FALSE) - { - if (mListener) - { - mListener->selectItem(); - } - mIsSelected = TRUE; - } -} - -BOOL LLFolderViewItem::isMovable() -{ - if( mListener ) - { - return mListener->isItemMovable(); - } - else - { - return TRUE; - } -} - -BOOL LLFolderViewItem::isRemovable() -{ - if( mListener ) - { - return mListener->isItemRemovable(); - } - else - { - return TRUE; - } -} - -void LLFolderViewItem::destroyView() -{ - if (mParentFolder) - { - // removeView deletes me - mParentFolder->removeView(this); - } -} - -// Call through to the viewed object and return true if it can be -// removed. -//BOOL LLFolderViewItem::removeRecursively(BOOL single_item) -BOOL LLFolderViewItem::remove() -{ - if(!isRemovable()) - { - return FALSE; - } - if(mListener) - { - return mListener->removeItem(); - } - return TRUE; -} - -// Build an appropriate context menu for the item. -void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags) -{ - if(mListener) - { - mListener->buildContextMenu(menu, flags); - } -} - -void LLFolderViewItem::openItem( void ) -{ - if( mListener ) - { - mListener->openItem(); - } -} - -void LLFolderViewItem::preview( void ) -{ - if (mListener) - { - mListener->previewItem(); - } -} - -void LLFolderViewItem::rename(const std::string& new_name) -{ - if( !new_name.empty() ) - { - if( mListener ) - { - mListener->renameItem(new_name); - - if(mParentFolder) - { - mParentFolder->requestSort(); - } - } - } -} - -const std::string& LLFolderViewItem::getSearchableLabel() const -{ - return mSearchableLabel; -} - -LLViewerInventoryItem * LLFolderViewItem::getInventoryItem(void) -{ - if (!getListener()) return NULL; - return gInventory.getItem(getListener()->getUUID()); -} - -const std::string& LLFolderViewItem::getName( void ) const -{ - if(mListener) - { - return mListener->getName(); - } - return mLabel; -} - -// LLView functionality -BOOL LLFolderViewItem::handleRightMouseDown( S32 x, S32 y, MASK mask ) -{ - if(!mIsSelected) - { - setSelectionFromRoot(this, FALSE); - } - make_ui_sound("UISndClick"); - return TRUE; -} - -BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask ) -{ - if (LLView::childrenHandleMouseDown(x, y, mask)) - { - return TRUE; - } - - // No handler needed for focus lost since this class has no - // state that depends on it. - gFocusMgr.setMouseCapture( this ); - - if (!mIsSelected) - { - if(mask & MASK_CONTROL) - { - changeSelectionFromRoot(this, !mIsSelected); - } - else if (mask & MASK_SHIFT) - { - getParentFolder()->extendSelectionTo(this); - } - else - { - setSelectionFromRoot(this, FALSE); - } - make_ui_sound("UISndClick"); - } - else - { - mSelectPending = TRUE; - } - - if( isMovable() ) - { - S32 screen_x; - S32 screen_y; - localPointToScreen(x, y, &screen_x, &screen_y ); - LLToolDragAndDrop::getInstance()->setDragStart( screen_x, screen_y ); - } - return TRUE; -} - -BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask ) -{ - mIsMouseOverTitle = (y > (getRect().getHeight() - mItemHeight)); - - if( hasMouseCapture() && isMovable() ) - { - S32 screen_x; - S32 screen_y; - localPointToScreen(x, y, &screen_x, &screen_y ); - BOOL can_drag = TRUE; - if( LLToolDragAndDrop::getInstance()->isOverThreshold( screen_x, screen_y ) ) - { - LLFolderView* root = getRoot(); - - if(root->getCurSelectedItem()) - { - LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_WORLD; - - // *TODO: push this into listener and remove - // dependency on llagent - if (mListener - && gInventory.isObjectDescendentOf(mListener->getUUID(), gInventory.getRootFolderID())) - { - src = LLToolDragAndDrop::SOURCE_AGENT; - } - else if (mListener - && gInventory.isObjectDescendentOf(mListener->getUUID(), gInventory.getLibraryRootFolderID())) - { - src = LLToolDragAndDrop::SOURCE_LIBRARY; - } - - can_drag = root->startDrag(src); - if (can_drag) - { - // if (mListener) mListener->startDrag(); - // RN: when starting drag and drop, clear out last auto-open - root->autoOpenTest(NULL); - root->setShowSelectionContext(TRUE); - - // Release keyboard focus, so that if stuff is dropped into the - // world, pressing the delete key won't blow away the inventory - // item. - gFocusMgr.setKeyboardFocus(NULL); - - return LLToolDragAndDrop::getInstance()->handleHover( x, y, mask ); - } - } - } - - if (can_drag) - { - gViewerWindow->setCursor(UI_CURSOR_ARROW); - } - else - { - gViewerWindow->setCursor(UI_CURSOR_NOLOCKED); - } - return TRUE; - } - else - { - getRoot()->setShowSelectionContext(FALSE); - gViewerWindow->setCursor(UI_CURSOR_ARROW); - // let parent handle this then... - return FALSE; - } -} - - -BOOL LLFolderViewItem::handleDoubleClick( S32 x, S32 y, MASK mask ) -{ - preview(); - return TRUE; -} - -BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask ) -{ - if (LLView::childrenHandleMouseUp(x, y, mask)) - { - return TRUE; - } - - // if mouse hasn't moved since mouse down... - if ( pointInView(x, y) && mSelectPending ) - { - //...then select - if(mask & MASK_CONTROL) - { - changeSelectionFromRoot(this, !mIsSelected); - } - else if (mask & MASK_SHIFT) - { - getParentFolder()->extendSelectionTo(this); - } - else - { - setSelectionFromRoot(this, FALSE); - } - } - - mSelectPending = FALSE; - - if( hasMouseCapture() ) - { - getRoot()->setShowSelectionContext(FALSE); - gFocusMgr.setMouseCapture( NULL ); - } - return TRUE; -} - -void LLFolderViewItem::onMouseLeave(S32 x, S32 y, MASK mask) -{ - mIsMouseOverTitle = false; -} - -BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - BOOL accepted = FALSE; - BOOL handled = FALSE; - if(mListener) - { - accepted = mListener->dragOrDrop(mask,drop,cargo_type,cargo_data, tooltip_msg); - handled = accepted; - if (accepted) - { - mDragAndDropTarget = TRUE; - *accept = ACCEPT_YES_MULTI; - } - else - { - *accept = ACCEPT_NO; - } - } - if(mParentFolder && !handled) - { - // store this item to get it in LLFolderBridge::dragItemIntoFolder on drop event. - mRoot->setDraggingOverItem(this); - handled = mParentFolder->handleDragAndDropFromChild(mask,drop,cargo_type,cargo_data,accept,tooltip_msg); - mRoot->setDraggingOverItem(NULL); - } - if (handled) - { - lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewItem" << llendl; - } - - return handled; -} - -void LLFolderViewItem::draw() -{ - static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); - static LLUIColor sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE); - static LLUIColor sHighlightFgColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE); - static LLUIColor sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE); - static LLUIColor sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE); - static LLUIColor sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE); - static LLUIColor sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemColor", DEFAULT_WHITE); - static LLUIColor sLibraryColor = LLUIColorTable::instance().getColor("InventoryItemLibraryColor", DEFAULT_WHITE); - static LLUIColor sLinkColor = LLUIColorTable::instance().getColor("InventoryItemLinkColor", DEFAULT_WHITE); - static LLUIColor sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE); - static LLUIColor sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE); - - const Params& default_params = LLUICtrlFactory::getDefaultParams<LLFolderViewItem>(); - const S32 TOP_PAD = default_params.item_top_pad; - const S32 FOCUS_LEFT = 1; - const LLFontGL* font = getLabelFontForStyle(mLabelStyle); - - const BOOL in_inventory = getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(), gInventory.getRootFolderID()); - const BOOL in_library = getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(), gInventory.getLibraryRootFolderID()); - - //--------------------------------------------------------------------------------// - // Draw open folder arrow - // - const bool up_to_date = mListener && mListener->isUpToDate(); - const bool possibly_has_children = ((up_to_date && hasVisibleChildren()) // we fetched our children and some of them have passed the filter... - || (!up_to_date && mListener && mListener->hasChildren())); // ...or we know we have children but haven't fetched them (doesn't obey filter) - if (possibly_has_children) - { - LLUIImage* arrow_image = default_params.folder_arrow_image; - gl_draw_scaled_rotated_image( - mIndentation, getRect().getHeight() - ARROW_SIZE - TEXT_PAD - TOP_PAD, - ARROW_SIZE, ARROW_SIZE, mControlLabelRotation, arrow_image->getImage(), sFgColor); - } - - - //--------------------------------------------------------------------------------// - // Draw highlight for selected items - // - const BOOL show_context = getRoot()->getShowSelectionContext(); - const BOOL filled = show_context || (getRoot()->getParentPanel()->hasFocus()); // If we have keyboard focus, draw selection filled - const S32 focus_top = getRect().getHeight(); - const S32 focus_bottom = getRect().getHeight() - mItemHeight; - const bool folder_open = (getRect().getHeight() > mItemHeight + 4); - if (mIsSelected) // always render "current" item. Only render other selected items if mShowSingleSelection is FALSE - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - LLColor4 bg_color = sHighlightBgColor; - if (!mIsCurSelection) - { - // do time-based fade of extra objects - F32 fade_time = getRoot()->getSelectionFadeElapsedTime(); - if (getRoot()->getShowSingleSelection()) - { - // fading out - bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, bg_color.mV[VALPHA], 0.f); - } - else - { - // fading in - bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, 0.f, bg_color.mV[VALPHA]); - } - } - gl_rect_2d(FOCUS_LEFT, - focus_top, - getRect().getWidth() - 2, - focus_bottom, - bg_color, filled); - if (mIsCurSelection) - { - gl_rect_2d(FOCUS_LEFT, - focus_top, - getRect().getWidth() - 2, - focus_bottom, - sFocusOutlineColor, FALSE); - } - if (folder_open) - { - gl_rect_2d(FOCUS_LEFT, - focus_bottom + 1, // overlap with bottom edge of above rect - getRect().getWidth() - 2, - 0, - sFocusOutlineColor, FALSE); - if (show_context) - { - gl_rect_2d(FOCUS_LEFT, - focus_bottom + 1, - getRect().getWidth() - 2, - 0, - sHighlightBgColor, TRUE); - } - } - } - else if (mIsMouseOverTitle) - { - gl_rect_2d(FOCUS_LEFT, - focus_top, - getRect().getWidth() - 2, - focus_bottom, - sMouseOverColor, FALSE); - } - - //--------------------------------------------------------------------------------// - // Draw DragNDrop highlight - // - if (mDragAndDropTarget) - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gl_rect_2d(FOCUS_LEFT, - focus_top, - getRect().getWidth() - 2, - focus_bottom, - sHighlightBgColor, FALSE); - if (folder_open) - { - gl_rect_2d(FOCUS_LEFT, - focus_bottom + 1, // overlap with bottom edge of above rect - getRect().getWidth() - 2, - 0, - sHighlightBgColor, FALSE); - } - mDragAndDropTarget = FALSE; - } - - const LLViewerInventoryItem *item = getInventoryItem(); - const BOOL highlight_link = mIconOverlay && item && item->getIsLinkType(); - //--------------------------------------------------------------------------------// - // Draw open icon - // - const S32 icon_x = mIndentation + ARROW_SIZE + TEXT_PAD; - if (!mIconOpen.isNull() && (llabs(mControlLabelRotation) > 80)) // For open folders - { - mIconOpen->draw(icon_x, getRect().getHeight() - mIconOpen->getHeight() - TOP_PAD + 1); - } - else if (mIcon) - { - mIcon->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1); - } - - if (highlight_link) - { - mIconOverlay->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1); - } - - //--------------------------------------------------------------------------------// - // Exit if no label to draw - // - if (mLabel.empty()) - { - return; - } - - LLColor4 color = (mIsSelected && filled) ? sHighlightFgColor : sFgColor; - if (highlight_link) color = sLinkColor; - if (in_library) color = sLibraryColor; - - F32 right_x = 0; - F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD; - F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation); - - //--------------------------------------------------------------------------------// - // Highlight filtered text - // - if (getRoot()->getDebugFilters()) - { - if (!getFiltered() && !possibly_has_children) - { - color.mV[VALPHA] *= 0.5f; - } - LLColor4 filter_color = mLastFilterGeneration >= getRoot()->getFilter()->getCurrentGeneration() ? - LLColor4(0.5f, 0.8f, 0.5f, 1.f) : - LLColor4(0.8f, 0.5f, 0.5f, 1.f); - LLFontGL::getFontMonospace()->renderUTF8(mStatusText, 0, text_left, y, filter_color, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x, FALSE ); - text_left = right_x; - } - //--------------------------------------------------------------------------------// - // Draw the actual label text - // - font->renderUTF8(mLabel, 0, text_left, y, color, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - S32_MAX, getRect().getWidth() - (S32) text_left, &right_x, TRUE); - - //--------------------------------------------------------------------------------// - // Draw "Loading..." text - // - bool root_is_loading = false; - if (in_inventory) - { - root_is_loading = LLInventoryModelBackgroundFetch::instance().inventoryFetchInProgress(); - } - if (in_library) - { - root_is_loading = LLInventoryModelBackgroundFetch::instance().libraryFetchInProgress(); - } - if ((mIsLoading - && mTimeSinceRequestStart.getElapsedTimeF32() >= gSavedSettings.getF32("FolderLoadingMessageWaitTime")) - || (LLInventoryModelBackgroundFetch::instance().folderFetchActive() - && root_is_loading - && mShowLoadStatus)) - { - std::string load_string = " ( " + LLTrans::getString("LoadingData") + " ) "; - font->renderUTF8(load_string, 0, right_x, y, sSearchStatusColor, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x, FALSE); - } - - //--------------------------------------------------------------------------------// - // Draw label suffix - // - if (!mLabelSuffix.empty()) - { - font->renderUTF8( mLabelSuffix, 0, right_x, y, sSuffixColor, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x, FALSE ); - } - - //--------------------------------------------------------------------------------// - // Highlight string match - // - if (mStringMatchOffset != std::string::npos) - { - // don't draw backgrounds for zero-length strings - S32 filter_string_length = getRoot()->getFilterSubString().size(); - if (filter_string_length > 0) - { - std::string combined_string = mLabel + mLabelSuffix; - S32 left = llround(text_left) + font->getWidth(combined_string, 0, mStringMatchOffset) - 1; - S32 right = left + font->getWidth(combined_string, mStringMatchOffset, filter_string_length) + 2; - S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD); - S32 top = getRect().getHeight() - TOP_PAD; - - LLUIImage* box_image = default_params.selection_image; - LLRect box_rect(left, top, right, bottom); - box_image->draw(box_rect, sFilterBGColor); - F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, mStringMatchOffset); - F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD; - font->renderUTF8( combined_string, mStringMatchOffset, match_string_left, yy, - sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - filter_string_length, S32_MAX, &right_x, FALSE ); - } - } -} - -bool LLFolderViewItem::isInSelection() const -{ - return mIsSelected || (mParentFolder && mParentFolder->isInSelection()); -} - -///---------------------------------------------------------------------------- -/// Class LLFolderViewFolder -///---------------------------------------------------------------------------- - -LLFolderViewFolder::LLFolderViewFolder( const LLFolderViewItem::Params& p ): - LLFolderViewItem( p ), // 0 = no create time - mIsOpen(FALSE), - mExpanderHighlighted(FALSE), - mCurHeight(0.f), - mTargetHeight(0.f), - mAutoOpenCountdown(0.f), - mSubtreeCreationDate(0), - mAmTrash(LLFolderViewFolder::UNKNOWN), - mLastArrangeGeneration( -1 ), - mLastCalculatedWidth(0), - mCompletedFilterGeneration(-1), - mMostFilteredDescendantGeneration(-1), - mNeedsSort(false), - mPassedFolderFilter(FALSE) -{ -} - -// Destroys the object -LLFolderViewFolder::~LLFolderViewFolder( void ) -{ - // The LLView base class takes care of object destruction. make sure that we - // don't have mouse or keyboard focus - gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() -} - -void LLFolderViewFolder::setFilteredFolder(bool filtered, S32 filter_generation) -{ - mPassedFolderFilter = filtered; - mLastFilterGeneration = filter_generation; -} - -bool LLFolderViewFolder::getFilteredFolder(S32 filter_generation) -{ - return mPassedFolderFilter && mLastFilterGeneration >= getRoot()->getFilter()->getMinRequiredGeneration(); -} - -// addToFolder() returns TRUE if it succeeds. FALSE otherwise -BOOL LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder, LLFolderView* root) -{ - if (!folder) - { - return FALSE; - } - mParentFolder = folder; - root->addItemID(getListener()->getUUID(), this); - return folder->addFolder(this); -} - -// Finds width and height of this object and its children. Also -// makes sure that this view and its children are the right size. -S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) -{ - // sort before laying out contents - if (mNeedsSort) - { - mFolders.sort(mSortFunction); - mItems.sort(mSortFunction); - mNeedsSort = false; - } - - // evaluate mHasVisibleChildren - mHasVisibleChildren = false; - if (hasFilteredDescendants(filter_generation)) - { - // We have to verify that there's at least one child that's not filtered out - bool found = false; - // Try the items first - for (items_t::iterator iit = mItems.begin(); iit != mItems.end(); ++iit) - { - LLFolderViewItem* itemp = (*iit); - found = (itemp->getFiltered(filter_generation)); - if (found) - break; - } - if (!found) - { - // If no item found, try the folders - for (folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit) - { - LLFolderViewFolder* folderp = (*fit); - found = ( folderp->getListener() - && (folderp->getFiltered(filter_generation) - || (folderp->getFilteredFolder(filter_generation) - && folderp->hasFilteredDescendants(filter_generation)))); - if (found) - break; - } - } - - mHasVisibleChildren = found; - } - - // calculate height as a single item (without any children), and reshapes rectangle to match - LLFolderViewItem::arrange( width, height, filter_generation ); - - // clamp existing animated height so as to never get smaller than a single item - mCurHeight = llmax((F32)*height, mCurHeight); - - // initialize running height value as height of single item in case we have no children - *height = getItemHeight(); - F32 running_height = (F32)*height; - F32 target_height = (F32)*height; - - // are my children visible? - if (needsArrange()) - { - // set last arrange generation first, in case children are animating - // and need to be arranged again - mLastArrangeGeneration = getRoot()->getArrangeGeneration(); - if (mIsOpen) - { - // Add sizes of children - S32 parent_item_height = getRect().getHeight(); - - for(folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit) - { - LLFolderViewFolder* folderp = (*fit); - if (getRoot()->getDebugFilters()) - { - folderp->setVisible(TRUE); - } - else - { - folderp->setVisible( folderp->getListener() - && (folderp->getFiltered(filter_generation) - || (folderp->getFilteredFolder(filter_generation) - && folderp->hasFilteredDescendants(filter_generation)))); // passed filter or has descendants that passed filter - } - - if (folderp->getVisible()) - { - S32 child_width = *width; - S32 child_height = 0; - S32 child_top = parent_item_height - llround(running_height); - - target_height += folderp->arrange( &child_width, &child_height, filter_generation ); - - running_height += (F32)child_height; - *width = llmax(*width, child_width); - folderp->setOrigin( 0, child_top - folderp->getRect().getHeight() ); - } - } - for(items_t::iterator iit = mItems.begin(); - iit != mItems.end(); ++iit) - { - LLFolderViewItem* itemp = (*iit); - if (getRoot()->getDebugFilters()) - { - itemp->setVisible(TRUE); - } - else - { - itemp->setVisible(itemp->getFiltered(filter_generation)); - } - - if (itemp->getVisible()) - { - S32 child_width = *width; - S32 child_height = 0; - S32 child_top = parent_item_height - llround(running_height); - - target_height += itemp->arrange( &child_width, &child_height, filter_generation ); - // don't change width, as this item is as wide as its parent folder by construction - itemp->reshape( itemp->getRect().getWidth(), child_height); - - running_height += (F32)child_height; - *width = llmax(*width, child_width); - itemp->setOrigin( 0, child_top - itemp->getRect().getHeight() ); - } - } - } - - mTargetHeight = target_height; - // cache this width so next time we can just return it - mLastCalculatedWidth = *width; - } - else - { - // just use existing width - *width = mLastCalculatedWidth; - } - - // animate current height towards target height - if (llabs(mCurHeight - mTargetHeight) > 1.f) - { - mCurHeight = lerp(mCurHeight, mTargetHeight, LLCriticalDamp::getInterpolant(mIsOpen ? FOLDER_OPEN_TIME_CONSTANT : FOLDER_CLOSE_TIME_CONSTANT)); - - requestArrange(); - - // hide child elements that fall out of current animated height - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - // number of pixels that bottom of folder label is from top of parent folder - if (getRect().getHeight() - (*fit)->getRect().mTop + (*fit)->getItemHeight() - > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP) - { - // hide if beyond current folder height - (*fit)->setVisible(FALSE); - } - } - - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - // number of pixels that bottom of item label is from top of parent folder - if (getRect().getHeight() - (*iit)->getRect().mBottom - > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP) - { - (*iit)->setVisible(FALSE); - } - } - } - else - { - mCurHeight = mTargetHeight; - } - - // don't change width as this item is already as wide as its parent folder - reshape(getRect().getWidth(),llround(mCurHeight)); - - // pass current height value back to parent - *height = llround(mCurHeight); - - return llround(mTargetHeight); -} - -BOOL LLFolderViewFolder::needsArrange() -{ - return mLastArrangeGeneration < getRoot()->getArrangeGeneration(); -} - -void LLFolderViewFolder::requestSort() -{ - mNeedsSort = true; - // whenever item order changes, we need to lay things out again - requestArrange(); -} - -void LLFolderViewFolder::setCompletedFilterGeneration(S32 generation, BOOL recurse_up) -{ - //mMostFilteredDescendantGeneration = llmin(mMostFilteredDescendantGeneration, generation); - mCompletedFilterGeneration = generation; - // only aggregate up if we are a lower (older) value - if (recurse_up - && mParentFolder - && generation < mParentFolder->getCompletedFilterGeneration()) - { - mParentFolder->setCompletedFilterGeneration(generation, TRUE); - } -} - -void LLFolderViewFolder::filter( LLInventoryFilter& filter) -{ - S32 filter_generation = filter.getCurrentGeneration(); - // if failed to pass filter newer than must_pass_generation - // you will automatically fail this time, so we only - // check against items that have passed the filter - S32 must_pass_generation = filter.getMustPassGeneration(); - - bool autoopen_folders = (filter.hasFilterString()); - - // if we have already been filtered against this generation, skip out - if (getCompletedFilterGeneration() >= filter_generation) - { - return; - } - - // filter folder itself - if (getLastFilterGeneration() < filter_generation) - { - if (getLastFilterGeneration() >= must_pass_generation // folder has been compared to a valid precursor filter - && !mPassedFilter) // and did not pass the filter - { - // go ahead and flag this folder as done - mLastFilterGeneration = filter_generation; - mStringMatchOffset = std::string::npos; - } - else // filter self only on first pass through - { - // filter against folder rules - filterFolder(filter); - // and then item rules - LLFolderViewItem::filter( filter ); - } - } - - if (getRoot()->getDebugFilters()) - { - mStatusText = llformat("%d", mLastFilterGeneration); - mStatusText += llformat("(%d)", mCompletedFilterGeneration); - mStatusText += llformat("+%d", mMostFilteredDescendantGeneration); - } - - // all descendants have been filtered later than must pass generation - // but none passed - if(getCompletedFilterGeneration() >= must_pass_generation && !hasFilteredDescendants(must_pass_generation)) - { - // don't traverse children if we've already filtered them since must_pass_generation - // and came back with nothing - return; - } - - // we entered here with at least one filter iteration left - // check to see if we have any more before continuing on to children - if (filter.getFilterCount() < 0) - { - return; - } - - // when applying a filter, matching folders get their contents downloaded first - if (filter.isNotDefault() - && getFiltered(filter.getMinRequiredGeneration()) - && (mListener - && !gInventory.isCategoryComplete(mListener->getUUID()))) - { - LLInventoryModelBackgroundFetch::instance().start(mListener->getUUID()); - } - - // now query children - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end(); - ++iter) - { - LLFolderViewFolder* folder = (*iter); - // have we run out of iterations this frame? - if (filter.getFilterCount() < 0) - { - break; - } - - // mMostFilteredDescendantGeneration might have been reset - // in which case we need to update it even for folders that - // don't need to be filtered anymore - if (folder->getCompletedFilterGeneration() >= filter_generation) - { - // track latest generation to pass any child items - if (folder->getFiltered() || folder->hasFilteredDescendants(filter.getMinRequiredGeneration())) - { - mMostFilteredDescendantGeneration = filter_generation; - requestArrange(); - } - // just skip it, it has already been filtered - continue; - } - - // update this folders filter status (and children) - folder->filter( filter ); - - // track latest generation to pass any child items - if (folder->getFiltered() || folder->hasFilteredDescendants(filter_generation)) - { - mMostFilteredDescendantGeneration = filter_generation; - requestArrange(); - if (getRoot()->needsAutoSelect() && autoopen_folders) - { - folder->setOpenArrangeRecursively(TRUE); - } - } - } - - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end(); - ++iter) - { - LLFolderViewItem* item = (*iter); - if (filter.getFilterCount() < 0) - { - break; - } - if (item->getLastFilterGeneration() >= filter_generation) - { - if (item->getFiltered()) - { - mMostFilteredDescendantGeneration = filter_generation; - requestArrange(); - } - continue; - } - - if (item->getLastFilterGeneration() >= must_pass_generation && - !item->getFiltered(must_pass_generation)) - { - // failed to pass an earlier filter that was a subset of the current one - // go ahead and flag this item as done - item->setFiltered(FALSE, filter_generation); - continue; - } - - item->filter( filter ); - - if (item->getFiltered(filter.getMinRequiredGeneration())) - { - mMostFilteredDescendantGeneration = filter_generation; - requestArrange(); - } - } - - // if we didn't use all filter iterations - // that means we filtered all of our descendants - // instead of exhausting the filter count for this frame - if (filter.getFilterCount() > 0) - { - // flag this folder as having completed filter pass for all descendants - setCompletedFilterGeneration(filter_generation, FALSE/*dont recurse up to root*/); - } -} - -void LLFolderViewFolder::filterFolder(LLInventoryFilter& filter) -{ - const BOOL previous_passed_filter = mPassedFolderFilter; - const BOOL passed_filter = filter.checkFolder(this); - - // If our visibility will change as a result of this filter, then - // we need to be rearranged in our parent folder - if (mParentFolder) - { - if (getVisible() != passed_filter - || previous_passed_filter != passed_filter ) - { - mParentFolder->requestArrange(); - } - } - - setFilteredFolder(passed_filter, filter.getCurrentGeneration()); - filter.decrementFilterCount(); - - if (getRoot()->getDebugFilters()) - { - mStatusText = llformat("%d", mLastFilterGeneration); - } -} - -void LLFolderViewFolder::setFiltered(BOOL filtered, S32 filter_generation) -{ - // if this folder is now filtered, but wasn't before - // (it just passed) - if (filtered && !mPassedFilter) - { - // reset current height, because last time we drew it - // it might have had more visible items than now - mCurHeight = 0.f; - } - - LLFolderViewItem::setFiltered(filtered, filter_generation); -} - -void LLFolderViewFolder::dirtyFilter() -{ - // we're a folder, so invalidate our completed generation - setCompletedFilterGeneration(-1, FALSE); - LLFolderViewItem::dirtyFilter(); -} - -BOOL LLFolderViewFolder::getFiltered() -{ - return getFilteredFolder(getRoot()->getFilter()->getMinRequiredGeneration()) - && LLFolderViewItem::getFiltered(); -} - -BOOL LLFolderViewFolder::getFiltered(S32 filter_generation) -{ - return getFilteredFolder(filter_generation) && LLFolderViewItem::getFiltered(filter_generation); -} - -BOOL LLFolderViewFolder::hasFilteredDescendants(S32 filter_generation) -{ - return mMostFilteredDescendantGeneration >= filter_generation; -} - - -BOOL LLFolderViewFolder::hasFilteredDescendants() -{ - return mMostFilteredDescendantGeneration >= getRoot()->getFilter()->getCurrentGeneration(); -} - -// Passes selection information on to children and record selection -// information if necessary. -BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL openitem, - BOOL take_keyboard_focus) -{ - BOOL rv = FALSE; - if (selection == this) - { - if (!isSelected()) - { - selectItem(); - } - rv = TRUE; - } - else - { - if (isSelected()) - { - deselectItem(); - } - rv = FALSE; - } - BOOL child_selected = FALSE; - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - if((*fit)->setSelection(selection, openitem, take_keyboard_focus)) - { - rv = TRUE; - child_selected = TRUE; - } - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - if((*iit)->setSelection(selection, openitem, take_keyboard_focus)) - { - rv = TRUE; - child_selected = TRUE; - } - } - if(openitem && child_selected) - { - setOpenArrangeRecursively(TRUE); - } - return rv; -} - -// This method is used to change the selection of an item. -// Recursively traverse all children; if 'selection' is 'this' then change -// the select status if necessary. -// Returns TRUE if the selection state of this folder, or of a child, was changed. -BOOL LLFolderViewFolder::changeSelection(LLFolderViewItem* selection, BOOL selected) -{ - BOOL rv = FALSE; - if(selection == this) - { - if (isSelected() != selected) - { - rv = TRUE; - if (selected) - { - selectItem(); - } - else - { - deselectItem(); - } - } - } - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - if((*fit)->changeSelection(selection, selected)) - { - rv = TRUE; - } - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - if((*iit)->changeSelection(selection, selected)) - { - rv = TRUE; - } - } - return rv; -} - -LLFolderViewFolder* LLFolderViewFolder::getCommonAncestor(LLFolderViewItem* item_a, LLFolderViewItem* item_b, bool& reverse) -{ - if (!item_a->getParentFolder() || !item_b->getParentFolder()) return NULL; - - std::deque<LLFolderViewFolder*> item_a_ancestors; - - LLFolderViewFolder* parent = item_a->getParentFolder(); - while(parent) - { - item_a_ancestors.push_back(parent); - parent = parent->getParentFolder(); - } - - std::deque<LLFolderViewFolder*> item_b_ancestors; - - parent = item_b->getParentFolder(); - while(parent) - { - item_b_ancestors.push_back(parent); - parent = parent->getParentFolder(); - } - - LLFolderViewFolder* common_ancestor = item_a->getRoot(); - - while(item_a_ancestors.size() > item_b_ancestors.size()) - { - item_a = item_a_ancestors.front(); - item_a_ancestors.pop_front(); - } - - while(item_b_ancestors.size() > item_a_ancestors.size()) - { - item_b = item_b_ancestors.front(); - item_b_ancestors.pop_front(); - } - - while(item_a_ancestors.size()) - { - common_ancestor = item_a_ancestors.front(); - - if (item_a_ancestors.front() == item_b_ancestors.front()) - { - // which came first, sibling a or sibling b? - for (folders_t::iterator it = common_ancestor->mFolders.begin(), end_it = common_ancestor->mFolders.end(); - it != end_it; - ++it) - { - LLFolderViewItem* item = *it; - - if (item == item_a) - { - reverse = false; - return common_ancestor; - } - if (item == item_b) - { - reverse = true; - return common_ancestor; - } - } - - for (items_t::iterator it = common_ancestor->mItems.begin(), end_it = common_ancestor->mItems.end(); - it != end_it; - ++it) - { - LLFolderViewItem* item = *it; - - if (item == item_a) - { - reverse = false; - return common_ancestor; - } - if (item == item_b) - { - reverse = true; - return common_ancestor; - } - } - break; - } - - item_a = item_a_ancestors.front(); - item_a_ancestors.pop_front(); - item_b = item_b_ancestors.front(); - item_b_ancestors.pop_front(); - } - - return NULL; -} - -void LLFolderViewFolder::gatherChildRangeExclusive(LLFolderViewItem* start, LLFolderViewItem* end, bool reverse, std::vector<LLFolderViewItem*>& items) -{ - bool selecting = start == NULL; - if (reverse) - { - for (items_t::reverse_iterator it = mItems.rbegin(), end_it = mItems.rend(); - it != end_it; - ++it) - { - if (*it == end) - { - return; - } - if (selecting) - { - items.push_back(*it); - } - - if (*it == start) - { - selecting = true; - } - } - for (folders_t::reverse_iterator it = mFolders.rbegin(), end_it = mFolders.rend(); - it != end_it; - ++it) - { - if (*it == end) - { - return; - } - - if (selecting) - { - items.push_back(*it); - } - - if (*it == start) - { - selecting = true; - } - } - } - else - { - for (folders_t::iterator it = mFolders.begin(), end_it = mFolders.end(); - it != end_it; - ++it) - { - if (*it == end) - { - return; - } - - if (selecting) - { - items.push_back(*it); - } - - if (*it == start) - { - selecting = true; - } - } - for (items_t::iterator it = mItems.begin(), end_it = mItems.end(); - it != end_it; - ++it) - { - if (*it == end) - { - return; - } - - if (selecting) - { - items.push_back(*it); - } - - if (*it == start) - { - selecting = true; - } - } - } -} - -void LLFolderViewFolder::extendSelectionTo(LLFolderViewItem* new_selection) -{ - if (getRoot()->getAllowMultiSelect() == FALSE) return; - - LLFolderViewItem* cur_selected_item = getRoot()->getCurSelectedItem(); - if (cur_selected_item == NULL) - { - cur_selected_item = new_selection; - } - - - bool reverse = false; - LLFolderViewFolder* common_ancestor = getCommonAncestor(cur_selected_item, new_selection, reverse); - if (!common_ancestor) return; - - LLFolderViewItem* last_selected_item_from_cur = cur_selected_item; - LLFolderViewFolder* cur_folder = cur_selected_item->getParentFolder(); - - std::vector<LLFolderViewItem*> items_to_select_forward; - - while(cur_folder != common_ancestor) - { - cur_folder->gatherChildRangeExclusive(last_selected_item_from_cur, NULL, reverse, items_to_select_forward); - - last_selected_item_from_cur = cur_folder; - cur_folder = cur_folder->getParentFolder(); - } - - std::vector<LLFolderViewItem*> items_to_select_reverse; - - LLFolderViewItem* last_selected_item_from_new = new_selection; - cur_folder = new_selection->getParentFolder(); - while(cur_folder != common_ancestor) - { - cur_folder->gatherChildRangeExclusive(last_selected_item_from_new, NULL, !reverse, items_to_select_reverse); - - last_selected_item_from_new = cur_folder; - cur_folder = cur_folder->getParentFolder(); - } - - common_ancestor->gatherChildRangeExclusive(last_selected_item_from_cur, last_selected_item_from_new, reverse, items_to_select_forward); - - for (std::vector<LLFolderViewItem*>::reverse_iterator it = items_to_select_reverse.rbegin(), end_it = items_to_select_reverse.rend(); - it != end_it; - ++it) - { - items_to_select_forward.push_back(*it); - } - - LLFolderView* root = getRoot(); - - for (std::vector<LLFolderViewItem*>::iterator it = items_to_select_forward.begin(), end_it = items_to_select_forward.end(); - it != end_it; - ++it) - { - LLFolderViewItem* item = *it; - if (item->isSelected()) - { - root->removeFromSelectionList(item); - } - else - { - item->selectItem(); - } - root->addToSelectionList(item); - } - - if (new_selection->isSelected()) - { - root->removeFromSelectionList(new_selection); - } - else - { - new_selection->selectItem(); - } - root->addToSelectionList(new_selection); -} - - -void LLFolderViewFolder::destroyView() -{ - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - LLFolderViewItem* item = (*iit); - getRoot()->removeItemID(item->getListener()->getUUID()); - } - - std::for_each(mItems.begin(), mItems.end(), DeletePointer()); - mItems.clear(); - - while (!mFolders.empty()) - { - LLFolderViewFolder *folderp = mFolders.back(); - folderp->destroyView(); // removes entry from mFolders - } - - //deleteAllChildren(); - - if (mParentFolder) - { - mParentFolder->removeView(this); - } -} - -// remove the specified item (and any children) if possible. Return -// TRUE if the item was deleted. -BOOL LLFolderViewFolder::removeItem(LLFolderViewItem* item) -{ - if(item->remove()) - { - return TRUE; - } - return FALSE; -} - -// simply remove the view (and any children) Don't bother telling the -// listeners. -void LLFolderViewFolder::removeView(LLFolderViewItem* item) -{ - if (!item || item->getParentFolder() != this) - { - return; - } - // deselect without traversing hierarchy - if (item->isSelected()) - { - item->deselectItem(); - } - getRoot()->removeFromSelectionList(item); - extractItem(item); - delete item; -} - -// extractItem() removes the specified item from the folder, but -// doesn't delete it. -void LLFolderViewFolder::extractItem( LLFolderViewItem* item ) -{ - items_t::iterator it = std::find(mItems.begin(), mItems.end(), item); - if(it == mItems.end()) - { - // This is an evil downcast. However, it's only doing - // pointer comparison to find if (which it should be ) the - // item is in the container, so it's pretty safe. - LLFolderViewFolder* f = static_cast<LLFolderViewFolder*>(item); - folders_t::iterator ft; - ft = std::find(mFolders.begin(), mFolders.end(), f); - if (ft != mFolders.end()) - { - mFolders.erase(ft); - } - } - else - { - mItems.erase(it); - } - //item has been removed, need to update filter - dirtyFilter(); - //because an item is going away regardless of filter status, force rearrange - requestArrange(); - getRoot()->removeItemID(item->getListener()->getUUID()); - removeChild(item); -} - -bool LLFolderViewFolder::isTrash() const -{ - if (mAmTrash == LLFolderViewFolder::UNKNOWN) - { - mAmTrash = mListener->getUUID() == gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH, false) ? LLFolderViewFolder::TRASH : LLFolderViewFolder::NOT_TRASH; - } - return mAmTrash == LLFolderViewFolder::TRASH; -} - -void LLFolderViewFolder::sortBy(U32 order) -{ - if (!mSortFunction.updateSort(order)) - { - // No changes. - return; - } - - // Propagate this change to sub folders - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->sortBy(order); - } - - // Don't sort the topmost folders (My Inventory and Library) - if (mListener->getUUID().notNull()) - { - mFolders.sort(mSortFunction); - mItems.sort(mSortFunction); - } - - if (order & LLInventoryFilter::SO_DATE) - { - time_t latest = 0; - - if (!mItems.empty()) - { - LLFolderViewItem* item = *(mItems.begin()); - latest = item->getCreationDate(); - } - - if (!mFolders.empty()) - { - LLFolderViewFolder* folder = *(mFolders.begin()); - if (folder->getCreationDate() > latest) - { - latest = folder->getCreationDate(); - } - } - mSubtreeCreationDate = latest; - } -} - -void LLFolderViewFolder::setItemSortOrder(U32 ordering) -{ - if (mSortFunction.updateSort(ordering)) - { - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->setItemSortOrder(ordering); - } - - mFolders.sort(mSortFunction); - mItems.sort(mSortFunction); - } -} - -EInventorySortGroup LLFolderViewFolder::getSortGroup() const -{ - if (isTrash()) - { - return SG_TRASH_FOLDER; - } - - if( mListener ) - { - if(LLFolderType::lookupIsProtectedType(mListener->getPreferredType())) - { - return SG_SYSTEM_FOLDER; - } - } - - return SG_NORMAL_FOLDER; -} - -BOOL LLFolderViewFolder::isMovable() -{ - if( mListener ) - { - if( !(mListener->isItemMovable()) ) - { - return FALSE; - } - - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - if(!(*iit)->isMovable()) - { - return FALSE; - } - } - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - if(!(*fit)->isMovable()) - { - return FALSE; - } - } - } - return TRUE; -} - - -BOOL LLFolderViewFolder::isRemovable() -{ - if( mListener ) - { - if( !(mListener->isItemRemovable()) ) - { - return FALSE; - } - - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - if(!(*iit)->isRemovable()) - { - return FALSE; - } - } - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - if(!(*fit)->isRemovable()) - { - return FALSE; - } - } - } - return TRUE; -} - -// this is an internal method used for adding items to folders. -BOOL LLFolderViewFolder::addItem(LLFolderViewItem* item) -{ - mItems.push_back(item); - - item->setRect(LLRect(0, 0, getRect().getWidth(), 0)); - item->setVisible(FALSE); - - addChild(item); - - item->dirtyFilter(); - - // Update the folder creation date if the child is newer than our current date - setCreationDate(llmax<time_t>(mCreationDate, item->getCreationDate())); - - // Handle sorting - requestArrange(); - requestSort(); - - // Traverse parent folders and update creation date and resort, if necessary - LLFolderViewFolder* parentp = getParentFolder(); - while (parentp) - { - // Update the folder creation date if the child is newer than our current date - parentp->setCreationDate(llmax<time_t>(parentp->mCreationDate, item->getCreationDate())); - - if (parentp->mSortFunction.isByDate()) - { - // parent folder doesn't have a time stamp yet, so get it from us - parentp->requestSort(); - } - - parentp = parentp->getParentFolder(); - } - - return TRUE; -} - -// this is an internal method used for adding items to folders. -BOOL LLFolderViewFolder::addFolder(LLFolderViewFolder* folder) -{ - mFolders.push_back(folder); - folder->setOrigin(0, 0); - folder->reshape(getRect().getWidth(), 0); - folder->setVisible(FALSE); - addChild( folder ); - folder->dirtyFilter(); - // rearrange all descendants too, as our indentation level might have changed - folder->requestArrange(TRUE); - requestSort(); - LLFolderViewFolder* parentp = getParentFolder(); - while (parentp && !parentp->mSortFunction.isByDate()) - { - // parent folder doesn't have a time stamp yet, so get it from us - parentp->requestSort(); - parentp = parentp->getParentFolder(); - } - return TRUE; -} - -void LLFolderViewFolder::requestArrange(BOOL include_descendants) -{ - mLastArrangeGeneration = -1; - // flag all items up to root - if (mParentFolder) - { - mParentFolder->requestArrange(); - } - - if (include_descendants) - { - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end(); - ++iter) - { - (*iter)->requestArrange(TRUE); - } - } -} - -void LLFolderViewFolder::toggleOpen() -{ - setOpen(!mIsOpen); -} - -// Force a folder open or closed -void LLFolderViewFolder::setOpen(BOOL openitem) -{ - setOpenArrangeRecursively(openitem); -} - -void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse) -{ - BOOL was_open = mIsOpen; - mIsOpen = openitem; - if (mListener) - { - if(!was_open && openitem) - { - mListener->openItem(); - } - else if(was_open && !openitem) - { - mListener->closeItem(); - } - } - - if (recurse == RECURSE_DOWN || recurse == RECURSE_UP_DOWN) - { - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->setOpenArrangeRecursively(openitem, RECURSE_DOWN); /* Flawfinder: ignore */ - } - } - if (mParentFolder - && (recurse == RECURSE_UP - || recurse == RECURSE_UP_DOWN)) - { - mParentFolder->setOpenArrangeRecursively(openitem, RECURSE_UP); - } - - if (was_open != mIsOpen) - { - requestArrange(); - } -} - -BOOL LLFolderViewFolder::handleDragAndDropFromChild(MASK mask, - BOOL drop, - EDragAndDropType c_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - BOOL accepted = mListener && mListener->dragOrDrop(mask,drop,c_type,cargo_data, tooltip_msg); - if (accepted) - { - mDragAndDropTarget = TRUE; - *accept = ACCEPT_YES_MULTI; - } - else - { - *accept = ACCEPT_NO; - } - - // drag and drop to child item, so clear pending auto-opens - getRoot()->autoOpenTest(NULL); - - return TRUE; -} - -void LLFolderViewFolder::openItem( void ) -{ - toggleOpen(); -} - -void LLFolderViewFolder::applyFunctorToChildren(LLFolderViewFunctor& functor) -{ - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - functor.doItem((*fit)); - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - functor.doItem((*iit)); - } -} - -void LLFolderViewFolder::applyFunctorRecursively(LLFolderViewFunctor& functor) -{ - functor.doFolder(this); - - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->applyFunctorRecursively(functor); - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - functor.doItem((*iit)); - } -} - -void LLFolderViewFolder::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor) -{ - functor(mListener); - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->applyListenerFunctorRecursively(functor); - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - (*iit)->applyListenerFunctorRecursively(functor); - } -} - -// LLView functionality -BOOL LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - BOOL handled = FALSE; - - if (mIsOpen) - { - handled = (childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL); - } - - if (!handled) - { - handleDragAndDropToThisFolder(mask, drop, cargo_type, cargo_data, accept, tooltip_msg); - - lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewFolder" << llendl; - } - - return TRUE; -} - -BOOL LLFolderViewFolder::handleDragAndDropToThisFolder(MASK mask, - BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - BOOL accepted = mListener && mListener->dragOrDrop(mask, drop, cargo_type, cargo_data, tooltip_msg); - - if (accepted) - { - mDragAndDropTarget = TRUE; - *accept = ACCEPT_YES_MULTI; - } - else - { - *accept = ACCEPT_NO; - } - - if (!drop && accepted) - { - getRoot()->autoOpenTest(this); - } - - return TRUE; -} - - -BOOL LLFolderViewFolder::handleRightMouseDown( S32 x, S32 y, MASK mask ) -{ - BOOL handled = FALSE; - // fetch contents of this folder, as context menu can depend on contents - // still, user would have to open context menu again to see the changes - gInventory.fetchDescendentsOf(mListener->getUUID()); - - if( mIsOpen ) - { - handled = childrenHandleRightMouseDown( x, y, mask ) != NULL; - } - if (!handled) - { - handled = LLFolderViewItem::handleRightMouseDown( x, y, mask ); - } - return handled; -} - - -BOOL LLFolderViewFolder::handleHover(S32 x, S32 y, MASK mask) -{ - mIsMouseOverTitle = (y > (getRect().getHeight() - mItemHeight)); - - BOOL handled = LLView::handleHover(x, y, mask); - - if (!handled) - { - // this doesn't do child processing - handled = LLFolderViewItem::handleHover(x, y, mask); - } - - return handled; -} - -BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask ) -{ - BOOL handled = FALSE; - if( mIsOpen ) - { - handled = childrenHandleMouseDown(x,y,mask) != NULL; - } - if( !handled ) - { - if(mIndentation < x && x < mIndentation + ARROW_SIZE + TEXT_PAD) - { - toggleOpen(); - handled = TRUE; - } - else - { - // do normal selection logic - handled = LLFolderViewItem::handleMouseDown(x, y, mask); - } - } - - return handled; -} - -BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask ) -{ - /* Disable outfit double click to wear - const LLUUID &cat_uuid = getListener()->getUUID(); - const LLViewerInventoryCategory *cat = gInventory.getCategory(cat_uuid); - if (cat && cat->getPreferredType() == LLFolderType::FT_OUTFIT) - { - getListener()->performAction(NULL, NULL,"replaceoutfit"); - return TRUE; - } - */ - - BOOL handled = FALSE; - if( mIsOpen ) - { - handled = childrenHandleDoubleClick( x, y, mask ) != NULL; - } - if( !handled ) - { - if(mIndentation < x && x < mIndentation + ARROW_SIZE + TEXT_PAD) - { - // don't select when user double-clicks plus sign - // so as not to contradict single-click behavior - toggleOpen(); - } - else - { - setSelectionFromRoot(this, FALSE); - toggleOpen(); - } - handled = TRUE; - } - return handled; -} - -void LLFolderViewFolder::draw() -{ - if (mAutoOpenCountdown != 0.f) - { - mControlLabelRotation = mAutoOpenCountdown * -90.f; - } - else if (mIsOpen) - { - mControlLabelRotation = lerp(mControlLabelRotation, -90.f, LLCriticalDamp::getInterpolant(0.04f)); - } - else - { - mControlLabelRotation = lerp(mControlLabelRotation, 0.f, LLCriticalDamp::getInterpolant(0.025f)); - } - - bool possibly_has_children = false; - bool up_to_date = mListener && mListener->isUpToDate(); - if(!up_to_date - && mListener->hasChildren()) // we know we have children but haven't fetched them (doesn't obey filter) - { - possibly_has_children = true; - } - - - BOOL loading = (mIsOpen - && possibly_has_children - && !up_to_date ); - - if ( loading && !mIsLoading ) - { - // Measure how long we've been in the loading state - mTimeSinceRequestStart.reset(); - } - - mIsLoading = loading; - - LLFolderViewItem::draw(); - - // draw children if root folder, or any other folder that is open or animating to closed state - if( getRoot() == this || (mIsOpen || mCurHeight != mTargetHeight )) - { - LLView::draw(); - } - - mExpanderHighlighted = FALSE; -} - -time_t LLFolderViewFolder::getCreationDate() const -{ - return llmax<time_t>(mCreationDate, mSubtreeCreationDate); -} - - -BOOL LLFolderViewFolder::potentiallyVisible() -{ - // folder should be visible by it's own filter status - return LLFolderViewItem::potentiallyVisible() - // or one or more of its descendants have passed the minimum filter requirement - || hasFilteredDescendants(getRoot()->getFilter()->getMinRequiredGeneration()) - // or not all of its descendants have been checked against minimum filter requirement - || getCompletedFilterGeneration() < getRoot()->getFilter()->getMinRequiredGeneration(); -} - -// this does prefix traversal, as folders are listed above their contents -LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, BOOL include_children ) -{ - BOOL found_item = FALSE; - - LLFolderViewItem* result = NULL; - // when not starting from a given item, start at beginning - if(item == NULL) - { - found_item = TRUE; - } - - // find current item among children - folders_t::iterator fit = mFolders.begin(); - folders_t::iterator fend = mFolders.end(); - - items_t::iterator iit = mItems.begin(); - items_t::iterator iend = mItems.end(); - - // if not trivially starting at the beginning, we have to find the current item - if (!found_item) - { - // first, look among folders, since they are always above items - for(; fit != fend; ++fit) - { - if(item == (*fit)) - { - found_item = TRUE; - // if we are on downwards traversal - if (include_children && (*fit)->isOpen()) - { - // look for first descendant - return (*fit)->getNextFromChild(NULL, TRUE); - } - // otherwise advance to next folder - ++fit; - include_children = TRUE; - break; - } - } - - // didn't find in folders? Check items... - if (!found_item) - { - for(; iit != iend; ++iit) - { - if(item == (*iit)) - { - found_item = TRUE; - // point to next item - ++iit; - break; - } - } - } - } - - if (!found_item) - { - // you should never call this method with an item that isn't a child - // so we should always find something - llassert(FALSE); - return NULL; - } - - // at this point, either iit or fit point to a candidate "next" item - // if both are out of range, we need to punt up to our parent - - // now, starting from found folder, continue through folders - // searching for next visible folder - while(fit != fend && !(*fit)->getVisible()) - { - // turn on downwards traversal for next folder - ++fit; - } - - if (fit != fend) - { - result = (*fit); - } - else - { - // otherwise, scan for next visible item - while(iit != iend && !(*iit)->getVisible()) - { - ++iit; - } - - // check to see if we have a valid item - if (iit != iend) - { - result = (*iit); - } - } - - if( !result && mParentFolder ) - { - // If there are no siblings or children to go to, recurse up one level in the tree - // and skip children for this folder, as we've already discounted them - result = mParentFolder->getNextFromChild(this, FALSE); - } - - return result; -} - -// this does postfix traversal, as folders are listed above their contents -LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* item, BOOL include_children ) -{ - BOOL found_item = FALSE; - - LLFolderViewItem* result = NULL; - // when not starting from a given item, start at end - if(item == NULL) - { - found_item = TRUE; - } - - // find current item among children - folders_t::reverse_iterator fit = mFolders.rbegin(); - folders_t::reverse_iterator fend = mFolders.rend(); - - items_t::reverse_iterator iit = mItems.rbegin(); - items_t::reverse_iterator iend = mItems.rend(); - - // if not trivially starting at the end, we have to find the current item - if (!found_item) - { - // first, look among items, since they are always below the folders - for(; iit != iend; ++iit) - { - if(item == (*iit)) - { - found_item = TRUE; - // point to next item - ++iit; - break; - } - } - - // didn't find in items? Check folders... - if (!found_item) - { - for(; fit != fend; ++fit) - { - if(item == (*fit)) - { - found_item = TRUE; - // point to next folder - ++fit; - break; - } - } - } - } - - if (!found_item) - { - // you should never call this method with an item that isn't a child - // so we should always find something - llassert(FALSE); - return NULL; - } - - // at this point, either iit or fit point to a candidate "next" item - // if both are out of range, we need to punt up to our parent - - // now, starting from found item, continue through items - // searching for next visible item - while(iit != iend && !(*iit)->getVisible()) - { - ++iit; - } - - if (iit != iend) - { - // we found an appropriate item - result = (*iit); - } - else - { - // otherwise, scan for next visible folder - while(fit != fend && !(*fit)->getVisible()) - { - ++fit; - } - - // check to see if we have a valid folder - if (fit != fend) - { - // try selecting child element of this folder - if ((*fit)->isOpen()) - { - result = (*fit)->getPreviousFromChild(NULL); - } - else - { - result = (*fit); - } - } - } - - if( !result ) - { - // If there are no siblings or children to go to, recurse up one level in the tree - // which gets back to this folder, which will only be visited if it is a valid, visible item - result = this; - } - - return result; -} - - -bool LLInventorySort::updateSort(U32 order) -{ - if (order != mSortOrder) - { - mSortOrder = order; - mByDate = (order & LLInventoryFilter::SO_DATE); - mSystemToTop = (order & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP); - mFoldersByName = (order & LLInventoryFilter::SO_FOLDERS_BY_NAME); - return true; - } - return false; -} - -bool LLInventorySort::operator()(const LLFolderViewItem* const& a, const LLFolderViewItem* const& b) -{ - // ignore sort order for landmarks in the Favorites folder. - // they should be always sorted as in Favorites bar. See EXT-719 - if (a->getSortGroup() == SG_ITEM - && b->getSortGroup() == SG_ITEM - && a->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK - && b->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK) - { - - static const LLUUID& favorites_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); - - LLUUID a_uuid = a->getParentFolder()->getListener()->getUUID(); - LLUUID b_uuid = b->getParentFolder()->getListener()->getUUID(); - - if ((a_uuid == favorites_folder_id && b_uuid == favorites_folder_id)) - { - // *TODO: mantipov: probably it is better to add an appropriate method to LLFolderViewItem - // or to LLInvFVBridge - LLViewerInventoryItem* aitem = (static_cast<const LLItemBridge*>(a->getListener()))->getItem(); - LLViewerInventoryItem* bitem = (static_cast<const LLItemBridge*>(b->getListener()))->getItem(); - if (!aitem || !bitem) - return false; - S32 a_sort = aitem->getSortField(); - S32 b_sort = bitem->getSortField(); - return a_sort < b_sort; - } - } - - // We sort by name if we aren't sorting by date - // OR if these are folders and we are sorting folders by name. - bool by_name = (!mByDate - || (mFoldersByName - && (a->getSortGroup() != SG_ITEM))); - - if (a->getSortGroup() != b->getSortGroup()) - { - if (mSystemToTop) - { - // Group order is System Folders, Trash, Normal Folders, Items - return (a->getSortGroup() < b->getSortGroup()); - } - else if (mByDate) - { - // Trash needs to go to the bottom if we are sorting by date - if ( (a->getSortGroup() == SG_TRASH_FOLDER) - || (b->getSortGroup() == SG_TRASH_FOLDER)) - { - return (b->getSortGroup() == SG_TRASH_FOLDER); - } - } - } - - if (by_name) - { - S32 compare = LLStringUtil::compareDict(a->getLabel(), b->getLabel()); - if (0 == compare) - { - return (a->getCreationDate() > b->getCreationDate()); - } - else - { - return (compare < 0); - } - } - else - { - // BUG: This is very very slow. The getCreationDate() is log n in number - // of inventory items. - time_t first_create = a->getCreationDate(); - time_t second_create = b->getCreationDate(); - if (first_create == second_create) - { - return (LLStringUtil::compareDict(a->getLabel(), b->getLabel()) < 0); - } - else - { - return (first_create > second_create); - } - } -} diff --git a/indra/newview/llfolderviewitem.h b/indra/newview/llfolderviewitem.h deleted file mode 100644 index 577b6b54a2..0000000000 --- a/indra/newview/llfolderviewitem.h +++ /dev/null @@ -1,577 +0,0 @@ -/** -* @file llfolderviewitem.h -* @brief Items and folders that can appear in a hierarchical folder view -* -* $LicenseInfo:firstyear=2001&license=viewerlgpl$ -* Second Life Viewer Source Code -* Copyright (C) 2010, Linden Research, Inc. -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU Lesser General Public -* License as published by the Free Software Foundation; -* version 2.1 of the License only. -* -* This library is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this library; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -* -* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -* $/LicenseInfo$ -*/ -#ifndef LLFOLDERVIEWITEM_H -#define LLFOLDERVIEWITEM_H - -#include "llview.h" -#include "lldarray.h" // *TODO: Eliminate, forward declare -#include "lluiimage.h" - -class LLFontGL; -class LLFolderView; -class LLFolderViewEventListener; -class LLFolderViewFolder; -class LLFolderViewFunctor; -class LLFolderViewItem; -class LLFolderViewListenerFunctor; -class LLInventoryFilter; -class LLMenuGL; -class LLUIImage; -class LLViewerInventoryItem; - -// These are grouping of inventory types. -// Order matters when sorting system folders to the top. -enum EInventorySortGroup -{ - SG_SYSTEM_FOLDER, - SG_TRASH_FOLDER, - SG_NORMAL_FOLDER, - SG_ITEM -}; - -// *TODO: do we really need one sort object per folder? -// can we just have one of these per LLFolderView ? -class LLInventorySort -{ -public: - LLInventorySort() - : mSortOrder(0), - mByDate(false), - mSystemToTop(false), - mFoldersByName(false) { } - - // Returns true if order has changed - bool updateSort(U32 order); - U32 getSort() { return mSortOrder; } - bool isByDate() { return mByDate; } - - bool operator()(const LLFolderViewItem* const& a, const LLFolderViewItem* const& b); -private: - U32 mSortOrder; - bool mByDate; - bool mSystemToTop; - bool mFoldersByName; -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLFolderViewItem -// -// An instance of this class represents a single item in a folder view -// such as an inventory item or a file. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLFolderViewItem : public LLView -{ -public: - static void initClass(); - static void cleanupClass(); - - struct Params : public LLInitParam::Block<Params, LLView::Params> - { - Optional<LLUIImage*> icon; - Optional<LLUIImage*> icon_open; // used for folders - Optional<LLUIImage*> icon_overlay; // for links - Optional<LLFolderView*> root; - Mandatory<LLFolderViewEventListener*> listener; - - Optional<LLUIImage*> folder_arrow_image; - Optional<S32> folder_indentation; // pixels - Optional<LLUIImage*> selection_image; - Optional<S32> item_height; // pixels - Optional<S32> item_top_pad; // pixels - - Optional<S32> creation_date; //UTC seconds - - Params(); - }; - - // layout constants - static const S32 LEFT_PAD = 5; - // LEFT_INDENTATION is set via folder_indentation above - static const S32 ICON_PAD = 2; - static const S32 ICON_WIDTH = 16; - static const S32 TEXT_PAD = 1; - static const S32 TEXT_PAD_RIGHT = 4; - static const S32 ARROW_SIZE = 12; - static const S32 MAX_FOLDER_ITEM_OVERLAP = 2; - // animation parameters - static const F32 FOLDER_CLOSE_TIME_CONSTANT; - static const F32 FOLDER_OPEN_TIME_CONSTANT; - - // Mostly for debugging printout purposes. - const std::string& getSearchableLabel() { return mSearchableLabel; } - - BOOL isLoading() const { return mIsLoading; } - -private: - BOOL mIsSelected; - -protected: - friend class LLUICtrlFactory; - friend class LLFolderViewEventListener; - - LLFolderViewItem(const Params& p); - - std::string mLabel; - std::string mSearchableLabel; - S32 mLabelWidth; - bool mLabelWidthDirty; - time_t mCreationDate; - LLFolderViewFolder* mParentFolder; - LLFolderViewEventListener* mListener; - BOOL mIsCurSelection; - BOOL mSelectPending; - LLFontGL::StyleFlags mLabelStyle; - std::string mLabelSuffix; - LLUIImagePtr mIcon; - std::string mStatusText; - LLUIImagePtr mIconOpen; - LLUIImagePtr mIconOverlay; - BOOL mHasVisibleChildren; - S32 mIndentation; - S32 mItemHeight; - BOOL mPassedFilter; - S32 mLastFilterGeneration; - std::string::size_type mStringMatchOffset; - F32 mControlLabelRotation; - LLFolderView* mRoot; - BOOL mDragAndDropTarget; - BOOL mIsLoading; - LLTimer mTimeSinceRequestStart; - bool mShowLoadStatus; - bool mIsMouseOverTitle; - - // helper function to change the selection from the root. - void changeSelectionFromRoot(LLFolderViewItem* selection, BOOL selected); - - // this is an internal method used for adding items to folders. A - // no-op at this level, but reimplemented in derived classes. - virtual BOOL addItem(LLFolderViewItem*) { return FALSE; } - virtual BOOL addFolder(LLFolderViewFolder*) { return FALSE; } - - static LLFontGL* getLabelFontForStyle(U8 style); - - virtual void setCreationDate(time_t creation_date_utc) { mCreationDate = creation_date_utc; } - -public: - BOOL postBuild(); - - // This function clears the currently selected item, and records - // the specified selected item appropriately for display and use - // in the UI. If open is TRUE, then folders are opened up along - // the way to the selection. - void setSelectionFromRoot(LLFolderViewItem* selection, BOOL openitem, - BOOL take_keyboard_focus = TRUE); - - // This function is called when the folder view is dirty. It's - // implemented here but called by derived classes when folding the - // views. - void arrangeFromRoot(); - void filterFromRoot( void ); - - void arrangeAndSet(BOOL set_selection, BOOL take_keyboard_focus); - - virtual ~LLFolderViewItem( void ); - - // addToFolder() returns TRUE if it succeeds. FALSE otherwise - enum { ARRANGE = TRUE, DO_NOT_ARRANGE = FALSE }; - virtual BOOL addToFolder(LLFolderViewFolder* folder, LLFolderView* root); - - virtual EInventorySortGroup getSortGroup() const; - - // Finds width and height of this object and it's children. Also - // makes sure that this view and it's children are the right size. - virtual S32 arrange( S32* width, S32* height, S32 filter_generation ); - virtual S32 getItemHeight(); - - // applies filters to control visibility of inventory items - virtual void filter( LLInventoryFilter& filter); - - // updates filter serial number and optionally propagated value up to root - S32 getLastFilterGeneration() { return mLastFilterGeneration; } - - virtual void dirtyFilter(); - - // If 'selection' is 'this' then note that otherwise ignore. - // Returns TRUE if this item ends up being selected. - virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus); - - // This method is used to set the selection state of an item. - // If 'selection' is 'this' then note selection. - // Returns TRUE if the selection state of this item was changed. - virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected); - - // this method is used to deselect this element - void deselectItem(); - - // this method is used to select this element - virtual void selectItem(); - - // gets multiple-element selection - virtual std::set<LLUUID> getSelectionList() const; - - // Returns true is this object and all of its children can be removed (deleted by user) - virtual BOOL isRemovable(); - - // Returns true is this object and all of its children can be moved - virtual BOOL isMovable(); - - // destroys this item recursively - virtual void destroyView(); - - BOOL isSelected() const { return mIsSelected; } - bool isInSelection() const; - - void setUnselected() { mIsSelected = FALSE; } - - void setIsCurSelection(BOOL select) { mIsCurSelection = select; } - - BOOL getIsCurSelection() { return mIsCurSelection; } - - BOOL hasVisibleChildren() { return mHasVisibleChildren; } - - void setShowLoadStatus(bool status) { mShowLoadStatus = status; } - - // Call through to the viewed object and return true if it can be - // removed. Returns true if it's removed. - //virtual BOOL removeRecursively(BOOL single_item); - BOOL remove(); - - // Build an appropriate context menu for the item. Flags unused. - void buildContextMenu(LLMenuGL& menu, U32 flags); - - // This method returns the actual name of the thing being - // viewed. This method will ask the viewed object itself. - const std::string& getName( void ) const; - - const std::string& getSearchableLabel( void ) const; - - // This method returns the label displayed on the view. This - // method was primarily added to allow sorting on the folder - // contents possible before the entire view has been constructed. - const std::string& getLabel() const { return mLabel; } - - // Used for sorting, like getLabel() above. - virtual time_t getCreationDate() const { return mCreationDate; } - - LLFolderViewFolder* getParentFolder( void ) { return mParentFolder; } - const LLFolderViewFolder* getParentFolder( void ) const { return mParentFolder; } - - LLFolderViewItem* getNextOpenNode( BOOL include_children = TRUE ); - LLFolderViewItem* getPreviousOpenNode( BOOL include_children = TRUE ); - - const LLFolderViewEventListener* getListener( void ) const { return mListener; } - LLFolderViewEventListener* getListener( void ) { return mListener; } - - // Gets the inventory item if it exists (null otherwise) - LLViewerInventoryItem * getInventoryItem(void); - - // just rename the object. - void rename(const std::string& new_name); - - // open - virtual void openItem( void ); - virtual void preview(void); - - // Show children (unfortunate that this is called "open") - virtual void setOpen(BOOL open = TRUE) {}; - - virtual BOOL isOpen() const { return FALSE; } - - virtual LLFolderView* getRoot(); - BOOL isDescendantOf( const LLFolderViewFolder* potential_ancestor ); - S32 getIndentation() { return mIndentation; } - - virtual BOOL potentiallyVisible(); // do we know for a fact that this item won't be displayed? - virtual BOOL potentiallyFiltered(); // do we know for a fact that this item has been filtered out? - - virtual BOOL getFiltered(); - virtual BOOL getFiltered(S32 filter_generation); - virtual void setFiltered(BOOL filtered, S32 filter_generation); - - // change the icon - void setIcon(LLUIImagePtr icon); - - // refresh information from the object being viewed. - void refreshFromListener(); - virtual void refresh(); - - virtual void applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor); - - // LLView functionality - virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); - virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask ); - virtual BOOL handleHover( S32 x, S32 y, MASK mask ); - virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask ); - virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask ); - - virtual void onMouseLeave(S32 x, S32 y, MASK mask); - - virtual LLView* findChildView(const std::string& name, BOOL recurse) const { return NULL; } - - // virtual void handleDropped(); - virtual void draw(); - virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); - -private: - static std::map<U8, LLFontGL*> sFonts; // map of styles to fonts -}; - - -// function used for sorting. -typedef bool (*sort_order_f)(LLFolderViewItem* a, LLFolderViewItem* b); - - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLFolderViewFolder -// -// An instance of an LLFolderViewFolder represents a collection of -// more folders and items. This is used to build the hierarchy of -// items found in the folder view. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLFolderViewFolder : public LLFolderViewItem -{ -protected: - LLFolderViewFolder( const LLFolderViewItem::Params& ); - friend class LLUICtrlFactory; - -public: - typedef enum e_trash - { - UNKNOWN, TRASH, NOT_TRASH - } ETrash; - - typedef std::list<LLFolderViewItem*> items_t; - typedef std::list<LLFolderViewFolder*> folders_t; - -protected: - items_t mItems; - folders_t mFolders; - LLInventorySort mSortFunction; - - BOOL mIsOpen; - BOOL mExpanderHighlighted; - F32 mCurHeight; - F32 mTargetHeight; - F32 mAutoOpenCountdown; - time_t mSubtreeCreationDate; - mutable ETrash mAmTrash; - S32 mLastArrangeGeneration; - S32 mLastCalculatedWidth; - S32 mCompletedFilterGeneration; - S32 mMostFilteredDescendantGeneration; - bool mNeedsSort; - bool mPassedFolderFilter; - -public: - typedef enum e_recurse_type - { - RECURSE_NO, - RECURSE_UP, - RECURSE_DOWN, - RECURSE_UP_DOWN - } ERecurseType; - - - virtual ~LLFolderViewFolder( void ); - - virtual BOOL potentiallyVisible(); - - LLFolderViewItem* getNextFromChild( LLFolderViewItem*, BOOL include_children = TRUE ); - LLFolderViewItem* getPreviousFromChild( LLFolderViewItem*, BOOL include_children = TRUE ); - - // addToFolder() returns TRUE if it succeeds. FALSE otherwise - virtual BOOL addToFolder(LLFolderViewFolder* folder, LLFolderView* root); - - // Finds width and height of this object and it's children. Also - // makes sure that this view and it's children are the right size. - virtual S32 arrange( S32* width, S32* height, S32 filter_generation ); - - BOOL needsArrange(); - void requestSort(); - - // Returns the sort group (system, trash, folder) for this folder. - virtual EInventorySortGroup getSortGroup() const; - - virtual void setCompletedFilterGeneration(S32 generation, BOOL recurse_up); - virtual S32 getCompletedFilterGeneration() { return mCompletedFilterGeneration; } - - BOOL hasFilteredDescendants(S32 filter_generation); - BOOL hasFilteredDescendants(); - - // applies filters to control visibility of inventory items - virtual void filter( LLInventoryFilter& filter); - virtual void setFiltered(BOOL filtered, S32 filter_generation); - virtual BOOL getFiltered(); - virtual BOOL getFiltered(S32 filter_generation); - - virtual void dirtyFilter(); - - // folder-specific filtering (filter status propagates top down instead of bottom up) - void filterFolder(LLInventoryFilter& filter); - void setFilteredFolder(bool filtered, S32 filter_generation); - bool getFilteredFolder(S32 filter_generation); - - // Passes selection information on to children and record - // selection information if necessary. - // Returns TRUE if this object (or a child) ends up being selected. - // If 'openitem' is TRUE then folders are opened up along the way to the selection. - virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus); - - // This method is used to change the selection of an item. - // Recursively traverse all children; if 'selection' is 'this' then change - // the select status if necessary. - // Returns TRUE if the selection state of this folder, or of a child, was changed. - virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected); - - // this method is used to group select items - void extendSelectionTo(LLFolderViewItem* selection); - - // Returns true is this object and all of its children can be removed. - virtual BOOL isRemovable(); - - // Returns true is this object and all of its children can be moved - virtual BOOL isMovable(); - - // destroys this folder, and all children - virtual void destroyView(); - - // If this folder can be removed, remove all children that can be - // removed, return TRUE if this is empty after the operation and - // it's viewed folder object can be removed. - //virtual BOOL removeRecursively(BOOL single_item); - //virtual BOOL remove(); - - // remove the specified item (and any children) if - // possible. Return TRUE if the item was deleted. - BOOL removeItem(LLFolderViewItem* item); - - // simply remove the view (and any children) Don't bother telling - // the listeners. - void removeView(LLFolderViewItem* item); - - // extractItem() removes the specified item from the folder, but - // doesn't delete it. - void extractItem( LLFolderViewItem* item ); - - // This function is called by a child that needs to be resorted. - void resort(LLFolderViewItem* item); - - void setItemSortOrder(U32 ordering); - void sortBy(U32); - //BOOL (*func)(LLFolderViewItem* a, LLFolderViewItem* b)); - - void setAutoOpenCountdown(F32 countdown) { mAutoOpenCountdown = countdown; } - - // folders can be opened. This will usually be called by internal - // methods. - virtual void toggleOpen(); - - // Force a folder open or closed - virtual void setOpen(BOOL openitem = TRUE); - - // Called when a child is refreshed. - // don't rearrange child folder contents unless explicitly requested - virtual void requestArrange(BOOL include_descendants = FALSE); - - // internal method which doesn't update the entire view. This - // method was written because the list iterators destroy the state - // of other iterations, thus, we can't arrange while iterating - // through the children (such as when setting which is selected. - virtual void setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse = RECURSE_NO); - - // Get the current state of the folder. - virtual BOOL isOpen() const { return mIsOpen; } - - // special case if an object is dropped on the child. - BOOL handleDragAndDropFromChild(MASK mask, - BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); - - void applyFunctorRecursively(LLFolderViewFunctor& functor); - virtual void applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor); - - // Just apply this functor to the folder's immediate children. - void applyFunctorToChildren(LLFolderViewFunctor& functor); - - virtual void openItem( void ); - virtual BOOL addItem(LLFolderViewItem* item); - virtual BOOL addFolder( LLFolderViewFolder* folder); - - // LLView functionality - virtual BOOL handleHover(S32 x, S32 y, MASK mask); - virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); - virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask ); - virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask ); - virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); - BOOL handleDragAndDropToThisFolder(MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); - virtual void draw(); - - time_t getCreationDate() const; - bool isTrash() const; - - folders_t::const_iterator getFoldersBegin() const { return mFolders.begin(); } - folders_t::const_iterator getFoldersEnd() const { return mFolders.end(); } - folders_t::size_type getFoldersCount() const { return mFolders.size(); } - - items_t::const_iterator getItemsBegin() const { return mItems.begin(); } - items_t::const_iterator getItemsEnd() const { return mItems.end(); } - items_t::size_type getItemsCount() const { return mItems.size(); } - LLFolderViewFolder* getCommonAncestor(LLFolderViewItem* item_a, LLFolderViewItem* item_b, bool& reverse); - void gatherChildRangeExclusive(LLFolderViewItem* start, LLFolderViewItem* end, bool reverse, std::vector<LLFolderViewItem*>& items); -}; - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLFolderViewListenerFunctor -// -// This simple abstract base class can be used to applied to all -// listeners in a hierarchy. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLFolderViewListenerFunctor -{ -public: - virtual ~LLFolderViewListenerFunctor() {} - virtual void operator()(LLFolderViewEventListener* listener) = 0; -}; - -#endif // LLFOLDERVIEWITEM_H diff --git a/indra/newview/llfolderviewmodelinventory.cpp b/indra/newview/llfolderviewmodelinventory.cpp new file mode 100644 index 0000000000..8a4b4bae84 --- /dev/null +++ b/indra/newview/llfolderviewmodelinventory.cpp @@ -0,0 +1,322 @@ +/* + * @file llfolderviewmodelinventory.cpp + * @brief Implementation of the inventory-specific view model + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llfolderviewmodelinventory.h" +#include "llinventorymodelbackgroundfetch.h" +#include "llinventorypanel.h" +#include "lltooldraganddrop.h" + +// +// class LLFolderViewModelInventory +// +static LLFastTimer::DeclareTimer FTM_INVENTORY_SORT("Sort"); + +bool LLFolderViewModelInventory::startDrag(std::vector<LLFolderViewModelItem*>& items) +{ + std::vector<EDragAndDropType> types; + uuid_vec_t cargo_ids; + std::vector<LLFolderViewModelItem*>::iterator item_it; + bool can_drag = true; + if (!items.empty()) + { + for (item_it = items.begin(); item_it != items.end(); ++item_it) + { + EDragAndDropType type = DAD_NONE; + LLUUID id = LLUUID::null; + can_drag = can_drag && static_cast<LLFolderViewModelItemInventory*>(*item_it)->startDrag(&type, &id); + + types.push_back(type); + cargo_ids.push_back(id); + } + + LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, + static_cast<LLFolderViewModelItemInventory*>(items.front())->getDragSource(), mTaskID); + } + return can_drag; +} + + +void LLFolderViewModelInventory::sort( LLFolderViewFolder* folder ) +{ + LLFastTimer _(FTM_INVENTORY_SORT); + + if (!needsSort(folder->getViewModelItem())) return; + + LLFolderViewModelItemInventory* modelp = static_cast<LLFolderViewModelItemInventory*>(folder->getViewModelItem()); + if (modelp->getUUID().isNull()) return; + + for (std::list<LLFolderViewFolder*>::iterator it = folder->getFoldersBegin(), end_it = folder->getFoldersEnd(); + it != end_it; + ++it) + { + LLFolderViewFolder* child_folderp = *it; + sort(child_folderp); + + if (child_folderp->getFoldersCount() > 0) + { + time_t most_recent_folder_time = + static_cast<LLFolderViewModelItemInventory*>((*child_folderp->getFoldersBegin())->getViewModelItem())->getCreationDate(); + LLFolderViewModelItemInventory* modelp = static_cast<LLFolderViewModelItemInventory*>(child_folderp->getViewModelItem()); + if (most_recent_folder_time > modelp->getCreationDate()) + { + modelp->setCreationDate(most_recent_folder_time); + } + } + if (child_folderp->getItemsCount() > 0) + { + time_t most_recent_item_time = + static_cast<LLFolderViewModelItemInventory*>((*child_folderp->getItemsBegin())->getViewModelItem())->getCreationDate(); + + LLFolderViewModelItemInventory* modelp = static_cast<LLFolderViewModelItemInventory*>(child_folderp->getViewModelItem()); + if (most_recent_item_time > modelp->getCreationDate()) + { + modelp->setCreationDate(most_recent_item_time); + } + } + } + base_t::sort(folder); +} + +bool LLFolderViewModelInventory::contentsReady() +{ + return !LLInventoryModelBackgroundFetch::instance().folderFetchActive(); +} + +void LLFolderViewModelItemInventory::requestSort() +{ + LLFolderViewModelItemCommon::requestSort(); + LLFolderViewFolder* folderp = dynamic_cast<LLFolderViewFolder*>(mFolderViewItem); + if (folderp) + { + folderp->requestArrange(); + } + if (static_cast<LLFolderViewModelInventory&>(mRootViewModel).getSorter().isByDate()) + { + // sort by date potentially affects parent folders which use a date + // derived from newest item in them + if (mParent) + { + mParent->requestSort(); + } + } +} + +void LLFolderViewModelItemInventory::setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset, std::string::size_type string_size) +{ + LLFolderViewModelItemCommon::setPassedFilter(passed, filter_generation, string_offset, string_size); + + bool passed_filter_before = mPrevPassedAllFilters; + mPrevPassedAllFilters = passedFilter(filter_generation); + + if (passed_filter_before != mPrevPassedAllFilters) + { + //TODO RN: ensure this still happens, but without dependency on folderview + LLFolderViewFolder* parent_folder = mFolderViewItem->getParentFolder(); + if (parent_folder) + { + parent_folder->requestArrange(); + } + } +} + +bool LLFolderViewModelItemInventory::filterChildItem( LLFolderViewModelItem* item, LLFolderViewFilter& filter ) +{ + S32 filter_generation = filter.getCurrentGeneration(); + + bool continue_filtering = true; + if (item->getLastFilterGeneration() < filter_generation) + { + // recursive application of the filter for child items + continue_filtering = item->filter( filter ); + } + + // track latest generation to pass any child items, for each folder up to root + if (item->passedFilter()) + { + LLFolderViewModelItemInventory* view_model = this; + + while(view_model && view_model->mMostFilteredDescendantGeneration < filter_generation) + { + view_model->mMostFilteredDescendantGeneration = filter_generation; + view_model = static_cast<LLFolderViewModelItemInventory*>(view_model->mParent); + } + } + + return continue_filtering; +} + +bool LLFolderViewModelItemInventory::filter( LLFolderViewFilter& filter) +{ + const S32 filter_generation = filter.getCurrentGeneration(); + const S32 must_pass_generation = filter.getFirstRequiredGeneration(); + + if (getLastFilterGeneration() >= must_pass_generation + && getLastFolderFilterGeneration() >= must_pass_generation + && !passedFilter(must_pass_generation)) + { + // failed to pass an earlier filter that was a subset of the current one + // go ahead and flag this item as done + setPassedFilter(false, filter_generation); + setPassedFolderFilter(false, filter_generation); + return true; + } + + const bool passed_filter_folder = (getInventoryType() == LLInventoryType::IT_CATEGORY) + ? filter.checkFolder(this) + : true; + setPassedFolderFilter(passed_filter_folder, filter_generation); + + if(!mChildren.empty() + && (getLastFilterGeneration() < must_pass_generation // haven't checked descendants against minimum required generation to pass + || descendantsPassedFilter(must_pass_generation))) // or at least one descendant has passed the minimum requirement + { + // now query children + for (child_list_t::iterator iter = mChildren.begin(), end_iter = mChildren.end(); + iter != end_iter && filter.getFilterCount() > 0; + ++iter) + { + if (!filterChildItem((*iter), filter)) + { + break; + } + } + } + + // if we didn't use all filter iterations + // that means we filtered all of our descendants + // so filter ourselves now + if (filter.getFilterCount() > 0) + { + filter.decrementFilterCount(); + + const bool passed_filter = filter.check(this); + setPassedFilter(passed_filter, filter_generation, filter.getStringMatchOffset(this), filter.getFilterStringSize()); + return true; + } + else + { + return false; + } +} + +LLFolderViewModelInventory* LLInventoryPanel::getFolderViewModel() +{ + return &mInventoryViewModel; +} + + +const LLFolderViewModelInventory* LLInventoryPanel::getFolderViewModel() const +{ + return &mInventoryViewModel; +} + +bool LLInventorySort::operator()(const LLFolderViewModelItemInventory* const& a, const LLFolderViewModelItemInventory* const& b) const +{ + // ignore sort order for landmarks in the Favorites folder. + // they should be always sorted as in Favorites bar. See EXT-719 + //TODO RN: fix sorting in favorites folder + //if (a->getSortGroup() == SG_ITEM + // && b->getSortGroup() == SG_ITEM + // && a->getInventoryType() == LLInventoryType::IT_LANDMARK + // && b->getInventoryType() == LLInventoryType::IT_LANDMARK) + //{ + + // static const LLUUID& favorites_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + + // LLUUID a_uuid = a->getParentFolder()->getUUID(); + // LLUUID b_uuid = b->getParentFolder()->getUUID(); + + // if ((a_uuid == favorites_folder_id && b_uuid == favorites_folder_id)) + // { + // // *TODO: mantipov: probably it is better to add an appropriate method to LLFolderViewItem + // // or to LLInvFVBridge + // LLViewerInventoryItem* aitem = (static_cast<const LLItemBridge*>(a))->getItem(); + // LLViewerInventoryItem* bitem = (static_cast<const LLItemBridge*>(b))->getItem(); + // if (!aitem || !bitem) + // return false; + // S32 a_sort = aitem->getSortField(); + // S32 b_sort = bitem->getSortField(); + // return a_sort < b_sort; + // } + //} + + // We sort by name if we aren't sorting by date + // OR if these are folders and we are sorting folders by name. + bool by_name = (!mByDate + || (mFoldersByName + && (a->getSortGroup() != SG_ITEM))); + + if (a->getSortGroup() != b->getSortGroup()) + { + if (mSystemToTop) + { + // Group order is System Folders, Trash, Normal Folders, Items + return (a->getSortGroup() < b->getSortGroup()); + } + else if (mByDate) + { + // Trash needs to go to the bottom if we are sorting by date + if ( (a->getSortGroup() == SG_TRASH_FOLDER) + || (b->getSortGroup() == SG_TRASH_FOLDER)) + { + return (b->getSortGroup() == SG_TRASH_FOLDER); + } + } + } + + if (by_name) + { + S32 compare = LLStringUtil::compareDict(a->getDisplayName(), b->getDisplayName()); + if (0 == compare) + { + return (a->getCreationDate() > b->getCreationDate()); + } + else + { + return (compare < 0); + } + } + else + { + time_t first_create = a->getCreationDate(); + time_t second_create = b->getCreationDate(); + if (first_create == second_create) + { + return (LLStringUtil::compareDict(a->getDisplayName(), b->getDisplayName()) < 0); + } + else + { + return (first_create > second_create); + } + } +} + +LLFolderViewModelItemInventory::LLFolderViewModelItemInventory( class LLFolderViewModelInventory& root_view_model ) + : LLFolderViewModelItemCommon(root_view_model), + mPrevPassedAllFilters(false) +{ +} diff --git a/indra/newview/llfolderviewmodelinventory.h b/indra/newview/llfolderviewmodelinventory.h new file mode 100644 index 0000000000..890d03d1c9 --- /dev/null +++ b/indra/newview/llfolderviewmodelinventory.h @@ -0,0 +1,118 @@ +/** + * @file llfolderviewmodelinventory.h + * @brief view model implementation specific to inventory + * class definition + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFOLDERVIEWMODELINVENTORY_H +#define LL_LLFOLDERVIEWMODELINVENTORY_H + +#include "llinventoryfilter.h" +#include "llinventory.h" +#include "llwearabletype.h" +#include "lltooldraganddrop.h" + +class LLFolderViewModelItemInventory + : public LLFolderViewModelItemCommon +{ +public: + LLFolderViewModelItemInventory(class LLFolderViewModelInventory& root_view_model); + virtual const LLUUID& getUUID() const = 0; + virtual time_t getCreationDate() const = 0; // UTC seconds + virtual void setCreationDate(time_t creation_date_utc) = 0; + virtual PermissionMask getPermissionMask() const = 0; + virtual LLFolderType::EType getPreferredType() const = 0; + virtual void showProperties(void) = 0; + virtual BOOL isItemInTrash( void) const { return FALSE; } // TODO: make into pure virtual. + virtual BOOL isUpToDate() const = 0; + virtual bool hasChildren() const = 0; + virtual LLInventoryType::EType getInventoryType() const = 0; + virtual void performAction(LLInventoryModel* model, std::string action) = 0; + virtual LLWearableType::EType getWearableType() const = 0; + virtual EInventorySortGroup getSortGroup() const = 0; + virtual LLInventoryObject* getInventoryObject() const = 0; + virtual void requestSort(); + virtual void setPassedFilter(bool filtered, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0); + virtual bool filter( LLFolderViewFilter& filter); + virtual bool filterChildItem( LLFolderViewModelItem* item, LLFolderViewFilter& filter); + + virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const = 0; + virtual LLToolDragAndDrop::ESource getDragSource() const = 0; + +protected: + bool mPrevPassedAllFilters; +}; + +class LLInventorySort +{ +public: + struct Params : public LLInitParam::Block<Params> + { + Optional<S32> order; + + Params() + : order("order", 0) + {} + }; + + LLInventorySort(S32 order = 0) + { + fromParams(Params().order(order)); + } + + bool isByDate() const { return mByDate; } + U32 getSortOrder() const { return mSortOrder; } + void toParams(Params& p) { p.order(mSortOrder);} + void fromParams(Params& p) + { + mSortOrder = p.order; + mByDate = (mSortOrder & LLInventoryFilter::SO_DATE); + mSystemToTop = (mSortOrder & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP); + mFoldersByName = (mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_NAME); + } + + bool operator()(const LLFolderViewModelItemInventory* const& a, const LLFolderViewModelItemInventory* const& b) const; +private: + U32 mSortOrder; + bool mByDate; + bool mSystemToTop; + bool mFoldersByName; +}; + +class LLFolderViewModelInventory + : public LLFolderViewModel<LLInventorySort, LLFolderViewModelItemInventory, LLFolderViewModelItemInventory, LLInventoryFilter> +{ +public: + typedef LLFolderViewModel<LLInventorySort, LLFolderViewModelItemInventory, LLFolderViewModelItemInventory, LLInventoryFilter> base_t; + + void setTaskID(const LLUUID& id) {mTaskID = id;} + + void sort(LLFolderViewFolder* folder); + bool contentsReady(); + bool startDrag(std::vector<LLFolderViewModelItem*>& items); + +private: + LLUUID mTaskID; +}; +#endif // LL_LLFOLDERVIEWMODELINVENTORY_H diff --git a/indra/newview/llfollowcam.cpp b/indra/newview/llfollowcam.cpp index b670af1782..47612fe25c 100644 --- a/indra/newview/llfollowcam.cpp +++ b/indra/newview/llfollowcam.cpp @@ -38,7 +38,6 @@ std::vector<LLFollowCamParams*> LLFollowCamMgr::sParamStack; //------------------------------------------------------- // constants //------------------------------------------------------- -const F32 ONE_HALF = 0.5; const F32 FOLLOW_CAM_ZOOM_FACTOR = 0.1f; const F32 FOLLOW_CAM_MIN_ZOOM_AMOUNT = 0.1f; const F32 DISTANCE_EPSILON = 0.0001f; diff --git a/indra/newview/llfriendcard.cpp b/indra/newview/llfriendcard.cpp index 11401d6c68..a64ddd185d 100644 --- a/indra/newview/llfriendcard.cpp +++ b/indra/newview/llfriendcard.cpp @@ -47,13 +47,13 @@ static const std::string INVENTORY_STRING_FRIENDS_ALL_SUBFOLDER = "All"; // helper functions // NOTE: For now Friends & All folders are created as protected folders of the LLFolderType::FT_CALLINGCARD type. -// So, their names will be processed in the LLFolderViewItem::refreshFromListener() to be localized +// So, their names will be processed in the LLFolderViewItem::refresh() to be localized // using "InvFolder LABEL_NAME" as LLTrans::findString argument. // We must use in this file their hard-coded names to ensure found them on different locales. EXT-5829. // These hard-coded names will be stored in InventoryItems but shown localized in FolderViewItems -// If hack in the LLFolderViewItem::refreshFromListener() to localize protected folder is removed +// If hack in the LLFolderViewItem::refresh() to localize protected folder is removed // or these folders are not protected these names should be localized in another place/way. inline const std::string get_friend_folder_name() { diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp index 66ca76bfb0..0996af6125 100644 --- a/indra/newview/llgesturemgr.cpp +++ b/indra/newview/llgesturemgr.cpp @@ -35,6 +35,7 @@ // library #include "llaudioengine.h" #include "lldatapacker.h" +#include "llfloaterreg.h" #include "llinventory.h" #include "llkeyframemotion.h" #include "llmultigesture.h" @@ -51,7 +52,7 @@ #include "llviewermessage.h" #include "llvoavatarself.h" #include "llviewerstats.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llappearancemgr.h" #include "llgesturelistener.h" @@ -997,7 +998,8 @@ void LLGestureMgr::runStep(LLMultiGesture* gesture, LLGestureStep* step) const BOOL animate = FALSE; - LLNearbyChatBar::getInstance()->sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate); + (LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat"))-> + sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate); gesture->mCurrentStep++; break; diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index 623ebb76f2..15eca39bce 100644 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -36,10 +36,10 @@ #include "llfloaterreg.h" #include "llfloatersidepanelcontainer.h" #include "llgroupmgr.h" +#include "llimfloatercontainer.h" #include "llimview.h" // for gIMMgr #include "llnotificationsutil.h" #include "llstatusbar.h" // can_afford_transaction() -#include "llimfloater.h" #include "groupchatlistener.h" // @@ -335,7 +335,7 @@ LLUUID LLGroupActions::startIM(const LLUUID& group_id) group_id); if (session_id != LLUUID::null) { - LLIMFloater::show(session_id); + LLIMFloaterContainer::getInstance()->showConversation(session_id); } make_ui_sound("UISndStartIM"); return session_id; diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp index 129cddda45..aba3d74d87 100644 --- a/indra/newview/llgrouplist.cpp +++ b/indra/newview/llgrouplist.cpp @@ -86,7 +86,7 @@ LLGroupList::LLGroupList(const Params& p) registrar.add("People.Groups.Action", boost::bind(&LLGroupList::onContextMenuItemClick, this, _2)); enable_registrar.add("People.Groups.Enable", boost::bind(&LLGroupList::onContextMenuItemEnable, this, _2)); - LLMenuGL* context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_people_groups.xml", + LLToggleableMenu* context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_groups.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); if(context_menu) mContextMenuHandle = context_menu->getHandle(); @@ -112,7 +112,7 @@ BOOL LLGroupList::handleRightMouseDown(S32 x, S32 y, MASK mask) { BOOL handled = LLUICtrl::handleRightMouseDown(x, y, mask); - LLMenuGL* context_menu = (LLMenuGL*)mContextMenuHandle.get(); + LLToggleableMenu* context_menu = mContextMenuHandle.get(); if (context_menu && size() > 0) { context_menu->buildDrawLabels(); @@ -406,7 +406,7 @@ void LLGroupListItem::setActive(bool active) // *BUG: setName() overrides the style params. // Active group should be bold. - LLFontDescriptor new_desc(mGroupNameBox->getDefaultFont()->getFontDesc()); + LLFontDescriptor new_desc(mGroupNameBox->getFont()->getFontDesc()); // *NOTE dzaporozhan // On Windows LLFontGL::NORMAL will not remove LLFontGL::BOLD if font diff --git a/indra/newview/llgrouplist.h b/indra/newview/llgrouplist.h index 8abf14b3d0..e96a720886 100644 --- a/indra/newview/llgrouplist.h +++ b/indra/newview/llgrouplist.h @@ -28,10 +28,13 @@ #define LL_LLGROUPLIST_H #include "llevent.h" +#include "llpointer.h" + #include "llflatlistview.h" #include "llpanel.h" -#include "llpointer.h" #include "llstyle.h" +#include "lltoggleablemenu.h" + #include "llgroupmgr.h" /** @@ -45,6 +48,10 @@ class LLGroupList: public LLFlatListViewEx, public LLOldEvents::LLSimpleListener { LOG_CLASS(LLGroupList); public: + struct Params : public LLInitParam::Block<Params, LLFlatListViewEx::Params> + { + Params(){}; + }; LLGroupList(const Params& p); virtual ~LLGroupList(); @@ -57,6 +64,8 @@ public: void toggleIcons(); bool getIconsVisible() const { return mShowIcons; } + LLToggleableMenu* getContextMenu() const { return mContextMenuHandle.get(); } + private: void setDirty(bool val = true) { mDirty = val; } void refresh(); @@ -66,7 +75,7 @@ private: bool onContextMenuItemClick(const LLSD& userdata); bool onContextMenuItemEnable(const LLSD& userdata); - LLHandle<LLView> mContextMenuHandle; + LLHandle<LLToggleableMenu> mContextMenuHandle; bool mShowIcons; bool mDirty; diff --git a/indra/newview/llgroupmgr.cpp b/indra/newview/llgroupmgr.cpp index efffd0f98e..aceb7f0614 100644 --- a/indra/newview/llgroupmgr.cpp +++ b/indra/newview/llgroupmgr.cpp @@ -63,7 +63,7 @@ #pragma warning(pop) // Restore all warnings to the previous state #endif -const U32 MAX_CACHED_GROUPS = 10; +const U32 MAX_CACHED_GROUPS = 20; // // LLRoleActionSet @@ -234,10 +234,16 @@ LLGroupMgrGroupData::LLGroupMgrGroupData(const LLUUID& id) : mRoleDataComplete(FALSE), mRoleMemberDataComplete(FALSE), mGroupPropertiesDataComplete(FALSE), - mPendingRoleMemberRequest(FALSE) + mPendingRoleMemberRequest(FALSE), + mAccessTime(0.0f) { } +void LLGroupMgrGroupData::setAccessed() +{ + mAccessTime = (F32)LLFrameTimer::getTotalSeconds(); +} + BOOL LLGroupMgrGroupData::getRoleData(const LLUUID& role_id, LLRoleData& role_data) { role_data_map_t::const_iterator it; @@ -1360,7 +1366,7 @@ void LLGroupMgr::processCreateGroupReply(LLMessageSystem* msg, void ** data) LLGroupMgrGroupData* LLGroupMgr::createGroupData(const LLUUID& id) { - LLGroupMgrGroupData* group_datap; + LLGroupMgrGroupData* group_datap = NULL; group_map_t::iterator existing_group = LLGroupMgr::getInstance()->mGroups.find(id); if (existing_group == LLGroupMgr::getInstance()->mGroups.end()) @@ -1373,6 +1379,11 @@ LLGroupMgrGroupData* LLGroupMgr::createGroupData(const LLUUID& id) group_datap = existing_group->second; } + if (group_datap) + { + group_datap->setAccessed(); + } + return group_datap; } @@ -1413,25 +1424,41 @@ void LLGroupMgr::notifyObservers(LLGroupChange gc) void LLGroupMgr::addGroup(LLGroupMgrGroupData* group_datap) { - if (mGroups.size() > MAX_CACHED_GROUPS) + while (mGroups.size() >= MAX_CACHED_GROUPS) { - // get rid of groups that aren't observed - for (group_map_t::iterator gi = mGroups.begin(); gi != mGroups.end() && mGroups.size() > MAX_CACHED_GROUPS / 2; ) + // LRU: Remove the oldest un-observed group from cache until group size is small enough + + F32 oldest_access = LLFrameTimer::getTotalSeconds(); + group_map_t::iterator oldest_gi = mGroups.end(); + + for (group_map_t::iterator gi = mGroups.begin(); gi != mGroups.end(); ++gi ) { observer_multimap_t::iterator oi = mObservers.find(gi->first); if (oi == mObservers.end()) { - // not observed - LLGroupMgrGroupData* unobserved_groupp = gi->second; - delete unobserved_groupp; - mGroups.erase(gi++); - } - else - { - ++gi; + if (gi->second + && (gi->second->getAccessTime() < oldest_access)) + { + oldest_access = gi->second->getAccessTime(); + oldest_gi = gi; + } } } + + if (oldest_gi != mGroups.end()) + { + delete oldest_gi->second; + mGroups.erase(oldest_gi); + } + else + { + // All groups must be currently open, none to remove. + // Just add the new group anyway, but get out of this loop as it + // will never drop below max_cached_groups. + break; + } } + mGroups[group_datap->getID()] = group_datap; } diff --git a/indra/newview/llgroupmgr.h b/indra/newview/llgroupmgr.h index faf0531c10..df3cd17e03 100644 --- a/indra/newview/llgroupmgr.h +++ b/indra/newview/llgroupmgr.h @@ -86,7 +86,7 @@ public: BOOL isInRole(const LLUUID& role_id) { return (mRolesList.find(role_id) != mRolesList.end()); } -protected: +private: LLUUID mID; S32 mContribution; U64 mAgentPowers; @@ -233,6 +233,9 @@ public: BOOL isRoleMemberDataComplete() { return mRoleMemberDataComplete; } BOOL isGroupPropertiesDataComplete() { return mGroupPropertiesDataComplete; } + F32 getAccessTime() const { return mAccessTime; } + void setAccessed(); + public: typedef std::map<LLUUID,LLGroupMemberData*> member_list_t; typedef std::map<LLUUID,LLGroupRoleData*> role_list_t; @@ -280,6 +283,7 @@ private: BOOL mGroupPropertiesDataComplete; BOOL mPendingRoleMemberRequest; + F32 mAccessTime; }; struct LLRoleAction diff --git a/indra/newview/llhudnametag.cpp b/indra/newview/llhudnametag.cpp index 482294c8a6..3336097955 100644 --- a/indra/newview/llhudnametag.cpp +++ b/indra/newview/llhudnametag.cpp @@ -166,7 +166,6 @@ BOOL LLHUDNameTag::lineSegmentIntersect(const LLVector3& start, const LLVector3& } // scale screen size of borders down - //RN: for now, text on hud objects is never occluded LLVector3 x_pixel_vec; LLVector3 y_pixel_vec; @@ -187,45 +186,29 @@ BOOL LLHUDNameTag::lineSegmentIntersect(const LLVector3& start, const LLVector3& + (y_pixel_vec * screen_offset.mV[VY]); - //if (mUseBubble) + LLVector3 bg_pos = render_position + + (F32)mOffsetY * y_pixel_vec + - (width_vec / 2.f) + - (height_vec); + + LLVector3 v[] = { - LLVector3 bg_pos = render_position - + (F32)mOffsetY * y_pixel_vec - - (width_vec / 2.f) - - (height_vec); - //LLUI::translate(bg_pos.mV[VX], bg_pos.mV[VY], bg_pos.mV[VZ]); + bg_pos, + bg_pos + width_vec, + bg_pos + width_vec + height_vec, + bg_pos + height_vec, + }; - LLVector3 v[] = - { - bg_pos, - bg_pos + width_vec, - bg_pos + width_vec + height_vec, - bg_pos + height_vec, - }; + LLVector3 dir = end-start; + F32 a, b, t; - if (debug_render) + if (LLTriangleRayIntersect(v[0], v[1], v[2], start, dir, a, b, t, FALSE) || + LLTriangleRayIntersect(v[2], v[3], v[0], start, dir, a, b, t, FALSE) ) + { + if (t <= 1.f) { - gGL.begin(LLRender::LINE_STRIP); - gGL.vertex3fv(v[0].mV); - gGL.vertex3fv(v[1].mV); - gGL.vertex3fv(v[2].mV); - gGL.vertex3fv(v[3].mV); - gGL.vertex3fv(v[0].mV); - gGL.vertex3fv(v[2].mV); - gGL.end(); - } - - LLVector3 dir = end-start; - F32 a, b, t; - - if (LLTriangleRayIntersect(v[0], v[1], v[2], start, dir, a, b, t, FALSE) || - LLTriangleRayIntersect(v[2], v[3], v[0], start, dir, a, b, t, FALSE) ) - { - if (t <= 1.f) - { - intersection = start + dir*t; - return TRUE; - } + intersection = start + dir*t; + return TRUE; } } @@ -241,12 +224,6 @@ void LLHUDNameTag::render() } } -void LLHUDNameTag::renderForSelect() -{ - LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); - renderText(TRUE); -} - void LLHUDNameTag::renderText(BOOL for_select) { if (!mVisible || mHidden) @@ -299,24 +276,6 @@ void LLHUDNameTag::renderText(BOOL for_select) LLColor4 bg_color = LLUIColorTable::instance().getColor("NameTagBackground"); bg_color.setAlpha(gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor); - // maybe a no-op? - //const S32 border_height = 16; - //const S32 border_width = 16; - const S32 border_height = 8; - const S32 border_width = 8; - - // *TODO move this into helper function - F32 border_scale = 1.f; - - if (border_height * 2 > mHeight) - { - border_scale = (F32)mHeight / ((F32)border_height * 2.f); - } - if (border_width * 2 > mWidth) - { - border_scale = llmin(border_scale, (F32)mWidth / ((F32)border_width * 2.f)); - } - // scale screen size of borders down //RN: for now, text on hud objects is never occluded @@ -325,152 +284,34 @@ void LLHUDNameTag::renderText(BOOL for_select) LLViewerCamera::getInstance()->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec); - LLVector2 border_scale_vec((F32)border_width / (F32)imagep->getTextureWidth(), (F32)border_height / (F32)imagep->getTextureHeight()); LLVector3 width_vec = mWidth * x_pixel_vec; LLVector3 height_vec = mHeight * y_pixel_vec; - LLVector3 scaled_border_width = (F32)llfloor(border_scale * (F32)border_width) * x_pixel_vec; - LLVector3 scaled_border_height = (F32)llfloor(border_scale * (F32)border_height) * y_pixel_vec; mRadius = (width_vec + height_vec).magVec() * 0.5f; LLCoordGL screen_pos; LLViewerCamera::getInstance()->projectPosAgentToScreen(mPositionAgent, screen_pos, FALSE); - LLVector2 screen_offset; -// if (!mUseBubble) -// { -// screen_offset = mPositionOffset; -// } -// else -// { - screen_offset = updateScreenPos(mPositionOffset); -// } + LLVector2 screen_offset = updateScreenPos(mPositionOffset); LLVector3 render_position = mPositionAgent + (x_pixel_vec * screen_offset.mV[VX]) + (y_pixel_vec * screen_offset.mV[VY]); -// if (mUseBubble) + LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); + LLRect screen_rect; + screen_rect.setCenterAndSize(0, static_cast<S32>(lltrunc(-mHeight / 2 + mOffsetY)), static_cast<S32>(lltrunc(mWidth)), static_cast<S32>(lltrunc(mHeight))); + imagep->draw3D(render_position, x_pixel_vec, y_pixel_vec, screen_rect, bg_color); + if (mLabelSegments.size()) { - LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); - LLUI::pushMatrix(); - { - LLVector3 bg_pos = render_position - + (F32)mOffsetY * y_pixel_vec - - (width_vec / 2.f) - - (height_vec); - LLUI::translate(bg_pos.mV[VX], bg_pos.mV[VY], bg_pos.mV[VZ]); - - if (for_select) - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - S32 name = mSourceObject->mGLName; - LLColor4U coloru((U8)(name >> 16), (U8)(name >> 8), (U8)name); - gGL.color4ubv(coloru.mV); - gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, height_vec); - LLUI::popMatrix(); - return; - } - else - { - gGL.getTexUnit(0)->bind(imagep->getImage()); - - gGL.color4fv(bg_color.mV); - gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, height_vec); - - if ( mLabelSegments.size()) - { - LLUI::pushMatrix(); - { - gGL.color4f(text_color.mV[VX], text_color.mV[VY], text_color.mV[VZ], gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor); - LLVector3 label_height = (mFontp->getLineHeight() * mLabelSegments.size() + (VERTICAL_PADDING / 3.f)) * y_pixel_vec; - LLVector3 label_offset = height_vec - label_height; - LLUI::translate(label_offset.mV[VX], label_offset.mV[VY], label_offset.mV[VZ]); - gl_segmented_rect_3d_tex_top(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, label_height); - } - LLUI::popMatrix(); - } - } - - BOOL outside_width = llabs(mPositionOffset.mV[VX]) > mWidth * 0.5f; - BOOL outside_height = llabs(mPositionOffset.mV[VY] + (mVertAlignment == ALIGN_VERT_TOP ? mHeight * 0.5f : 0.f)) > mHeight * (mVertAlignment == ALIGN_VERT_TOP ? mHeight * 0.75f : 0.5f); + LLUIImagePtr rect_top_image = LLUI::getUIImage("Rounded_Rect_Top"); + LLRect label_top_rect = screen_rect; + const S32 label_height = llround((mFontp->getLineHeight() * (F32)mLabelSegments.size() + (VERTICAL_PADDING / 3.f))); + label_top_rect.mBottom = label_top_rect.mTop - label_height; + LLColor4 label_top_color = text_color; + label_top_color.mV[VALPHA] = gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor; - // draw line segments pointing to parent object - if (!mOffscreen && (outside_width || outside_height)) - { - LLUI::pushMatrix(); - { - gGL.color4fv(bg_color.mV); - LLVector3 target_pos = -1.f * (mPositionOffset.mV[VX] * x_pixel_vec + mPositionOffset.mV[VY] * y_pixel_vec); - target_pos += (width_vec / 2.f); - target_pos += mVertAlignment == ALIGN_VERT_CENTER ? (height_vec * 0.5f) : LLVector3::zero; - target_pos -= 3.f * x_pixel_vec; - target_pos -= 6.f * y_pixel_vec; - LLUI::translate(target_pos.mV[VX], target_pos.mV[VY], target_pos.mV[VZ]); - gl_segmented_rect_3d_tex(border_scale_vec, 3.f * x_pixel_vec, 3.f * y_pixel_vec, 6.f * x_pixel_vec, 6.f * y_pixel_vec); - } - LLUI::popMatrix(); - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - LLGLDepthTest gls_depth(mZCompare ? GL_TRUE : GL_FALSE, GL_FALSE); - - LLVector3 box_center_offset; - box_center_offset = (width_vec * 0.5f) + (height_vec * 0.5f); - LLUI::translate(box_center_offset.mV[VX], box_center_offset.mV[VY], box_center_offset.mV[VZ]); - gGL.color4fv(bg_color.mV); - LLUI::setLineWidth(2.0); - gGL.begin(LLRender::LINES); - { - if (outside_width) - { - LLVector3 vert; - // draw line in x then y - if (mPositionOffset.mV[VX] < 0.f) - { - // start at right edge - vert = width_vec * 0.5f; - gGL.vertex3fv(vert.mV); - } - else - { - // start at left edge - vert = width_vec * -0.5f; - gGL.vertex3fv(vert.mV); - } - vert = -mPositionOffset.mV[VX] * x_pixel_vec; - gGL.vertex3fv(vert.mV); - gGL.vertex3fv(vert.mV); - vert -= mPositionOffset.mV[VY] * y_pixel_vec; - vert -= ((mVertAlignment == ALIGN_VERT_TOP) ? (height_vec * 0.5f) : LLVector3::zero); - gGL.vertex3fv(vert.mV); - } - else - { - LLVector3 vert; - // draw line in y then x - if (mPositionOffset.mV[VY] < 0.f) - { - // start at top edge - vert = (height_vec * 0.5f) - (mPositionOffset.mV[VX] * x_pixel_vec); - gGL.vertex3fv(vert.mV); - } - else - { - // start at bottom edge - vert = (height_vec * -0.5f) - (mPositionOffset.mV[VX] * x_pixel_vec); - gGL.vertex3fv(vert.mV); - } - vert = -mPositionOffset.mV[VY] * y_pixel_vec - mPositionOffset.mV[VX] * x_pixel_vec; - vert -= ((mVertAlignment == ALIGN_VERT_TOP) ? (height_vec * 0.5f) : LLVector3::zero); - gGL.vertex3fv(vert.mV); - } - } - gGL.end(); - LLUI::setLineWidth(1.0); - - } - } - LLUI::popMatrix(); + rect_top_image->draw3D(render_position, x_pixel_vec, y_pixel_vec, label_top_rect, label_top_color); } F32 y_offset = (F32)mOffsetY; @@ -874,29 +715,26 @@ void LLHUDNameTag::updateAll() for (r_it = sVisibleTextObjects.rbegin(); r_it != sVisibleTextObjects.rend(); ++r_it) { LLHUDNameTag* textp = (*r_it); -// if (textp->mUseBubble) -// { - if (current_screen_area / screen_area > LOD_2_SCREEN_COVERAGE) - { - textp->setLOD(3); - } - else if (current_screen_area / screen_area > LOD_1_SCREEN_COVERAGE) - { - textp->setLOD(2); - } - else if (current_screen_area / screen_area > LOD_0_SCREEN_COVERAGE) - { - textp->setLOD(1); - } - else - { - textp->setLOD(0); - } - textp->updateSize(); - // find on-screen position and initialize collision rectangle - textp->mTargetPositionOffset = textp->updateScreenPos(LLVector2::zero); - current_screen_area += (F32)(textp->mSoftScreenRect.getWidth() * textp->mSoftScreenRect.getHeight()); -// } + if (current_screen_area / screen_area > LOD_2_SCREEN_COVERAGE) + { + textp->setLOD(3); + } + else if (current_screen_area / screen_area > LOD_1_SCREEN_COVERAGE) + { + textp->setLOD(2); + } + else if (current_screen_area / screen_area > LOD_0_SCREEN_COVERAGE) + { + textp->setLOD(1); + } + else + { + textp->setLOD(0); + } + textp->updateSize(); + // find on-screen position and initialize collision rectangle + textp->mTargetPositionOffset = textp->updateScreenPos(LLVector2::zero); + current_screen_area += (F32)(textp->mSoftScreenRect.getWidth() * textp->mSoftScreenRect.getHeight()); } LLStat* camera_vel_stat = LLViewerCamera::getInstance()->getVelocityStat(); @@ -914,20 +752,12 @@ void LLHUDNameTag::updateAll() { LLHUDNameTag* src_textp = (*src_it); -// if (!src_textp->mUseBubble) -// { -// continue; -// } VisibleTextObjectIterator dst_it = src_it; ++dst_it; for (; dst_it != sVisibleTextObjects.end(); ++dst_it) { LLHUDNameTag* dst_textp = (*dst_it); -// if (!dst_textp->mUseBubble) -// { -// continue; -// } if (src_textp->mSoftScreenRect.overlaps(dst_textp->mSoftScreenRect)) { LLRectf intersect_rect = src_textp->mSoftScreenRect; @@ -976,10 +806,6 @@ void LLHUDNameTag::updateAll() VisibleTextObjectIterator this_object_it; for (this_object_it = sVisibleTextObjects.begin(); this_object_it != sVisibleTextObjects.end(); ++this_object_it) { -// if (!(*this_object_it)->mUseBubble) -// { -// continue; -// } (*this_object_it)->mPositionOffset = lerp((*this_object_it)->mPositionOffset, (*this_object_it)->mTargetPositionOffset, LLCriticalDamp::getInterpolant(POSITION_DAMPING_TC)); } } @@ -1037,10 +863,6 @@ void LLHUDNameTag::addPickable(std::set<LLViewerObject*> &pick_list) VisibleTextObjectIterator text_it; for (text_it = sVisibleTextObjects.begin(); text_it != sVisibleTextObjects.end(); ++text_it) { -// if (!(*text_it)->mUseBubble) -// { -// continue; -// } pick_list.insert((*text_it)->mSourceObject); } } diff --git a/indra/newview/llhudnametag.h b/indra/newview/llhudnametag.h index 3325c22def..72647d5b26 100644 --- a/indra/newview/llhudnametag.h +++ b/indra/newview/llhudnametag.h @@ -118,7 +118,6 @@ public: /*virtual*/ void markDead(); friend class LLHUDObject; /*virtual*/ F32 getDistance() const { return mLastDistance; } - //void setUseBubble(BOOL use_bubble) { mUseBubble = use_bubble; } S32 getLOD() { return mLOD; } BOOL getVisible() { return mVisible; } BOOL getHidden() const { return mHidden; } @@ -136,7 +135,6 @@ protected: LLHUDNameTag(const U8 type); /*virtual*/ void render(); - /*virtual*/ void renderForSelect(); void renderText(BOOL for_select); static void updateAll(); void setLOD(S32 lod); diff --git a/indra/newview/llhudobject.cpp b/indra/newview/llhudobject.cpp index 95d57d08d8..0960846510 100644 --- a/indra/newview/llhudobject.cpp +++ b/indra/newview/llhudobject.cpp @@ -232,9 +232,11 @@ LLHUDEffect *LLHUDObject::addHUDEffect(const U8 type) case LL_HUD_EFFECT_LOOKAT: hud_objectp = new LLHUDEffectLookAt(type); break; +#ifdef XXX_STINSON_CHUI_REWORK case LL_HUD_EFFECT_VOICE_VISUALIZER: hud_objectp = new LLVoiceVisualizer(type); break; +#endif // XXX_STINSON_CHUI_REWORK case LL_HUD_EFFECT_POINTAT: hud_objectp = new LLHUDEffectPointAt(type); break; diff --git a/indra/newview/llhudobject.h b/indra/newview/llhudobject.h index 2f7a98c86c..32cffe6839 100644 --- a/indra/newview/llhudobject.h +++ b/indra/newview/llhudobject.h @@ -39,6 +39,8 @@ #include "lldrawpool.h" // TODO: eliminate, unused below #include <list> +#define XXX_STINSON_CHUI_REWORK // temporarily re-enabling the in-world voice-dot + class LLViewerCamera; class LLFontGL; class LLFace; @@ -94,7 +96,9 @@ public: LL_HUD_EFFECT_EDIT, LL_HUD_EFFECT_LOOKAT, LL_HUD_EFFECT_POINTAT, +#ifdef XXX_STINSON_CHUI_REWORK LL_HUD_EFFECT_VOICE_VISUALIZER, // Ventrella +#endif // XXX_STINSON_CHUI_REWORK LL_HUD_NAME_TAG, LL_HUD_EFFECT_BLOB }; diff --git a/indra/newview/llimconversation.cpp b/indra/newview/llimconversation.cpp new file mode 100644 index 0000000000..b687e18cae --- /dev/null +++ b/indra/newview/llimconversation.cpp @@ -0,0 +1,588 @@ +/** + * @file llimconversation.cpp + * @brief LLIMConversation class implements the common behavior of LNearbyChatBar + * @brief and LLIMFloater for hosting both in LLIMContainer + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llimconversation.h" + +#include "llchatentry.h" +#include "llchathistory.h" +#include "llchiclet.h" +#include "llchicletbar.h" +#include "lldraghandle.h" +#include "llfloaterreg.h" +#include "llimfloater.h" +#include "llimfloatercontainer.h" // to replace separate IM Floaters with multifloater container +#include "lllayoutstack.h" +#include "llnearbychat.h" + +const F32 REFRESH_INTERVAL = 0.2f; + +LLIMConversation::LLIMConversation(const LLSD& session_id) + : LLTransientDockableFloater(NULL, true, session_id) + , mIsP2PChat(false) + , mExpandCollapseBtn(NULL) + , mTearOffBtn(NULL) + , mCloseBtn(NULL) + , mSessionID(session_id.asUUID()) + , mParticipantList(NULL) + , mChatHistory(NULL) + , mInputEditor(NULL) + , mInputEditorTopPad(0) + , mRefreshTimer(new LLTimer()) +{ + mSession = LLIMModel::getInstance()->findIMSession(mSessionID); + + mCommitCallbackRegistrar.add("IMSession.Menu.Action", + boost::bind(&LLIMConversation::onIMSessionMenuItemClicked, this, _2)); + mEnableCallbackRegistrar.add("IMSession.Menu.CompactExpandedModes.CheckItem", + boost::bind(&LLIMConversation::onIMCompactExpandedMenuItemCheck, this, _2)); + mEnableCallbackRegistrar.add("IMSession.Menu.ShowModes.CheckItem", + boost::bind(&LLIMConversation::onIMShowModesMenuItemCheck, this, _2)); + mEnableCallbackRegistrar.add("IMSession.Menu.ShowModes.Enable", + boost::bind(&LLIMConversation::onIMShowModesMenuItemEnable, this, _2)); + + // Zero expiry time is set only once to allow initial update. + mRefreshTimer->setTimerExpirySec(0); + mRefreshTimer->start(); +} + +LLIMConversation::~LLIMConversation() +{ + if (mParticipantList) + { + delete mParticipantList; + mParticipantList = NULL; + } + + delete mRefreshTimer; +} + +//static +LLIMConversation* LLIMConversation::findConversation(const LLUUID& uuid) +{ + LLIMConversation* conv; + + if (uuid.isNull()) + { + conv = LLFloaterReg::findTypedInstance<LLIMConversation>("nearby_chat"); + } + else + { + conv = LLFloaterReg::findTypedInstance<LLIMConversation>("impanel", LLSD(uuid)); + } + + return conv; +}; + +//static +LLIMConversation* LLIMConversation::getConversation(const LLUUID& uuid) +{ + LLIMConversation* conv; + + if (uuid.isNull()) + { + conv = LLFloaterReg::getTypedInstance<LLIMConversation>("nearby_chat"); + } + else + { + conv = LLFloaterReg::getTypedInstance<LLIMConversation>("impanel", LLSD(uuid)); + } + + return conv; +}; + +void LLIMConversation::setVisible(BOOL visible) +{ + LLTransientDockableFloater::setVisible(visible); + + if(visible) + { + LLIMConversation::addToHost(mSessionID); + } + setFocus(visible); +} + + + +void LLIMConversation::addToHost(const LLUUID& session_id) +{ + if ((session_id.notNull() && !gIMMgr->hasSession(session_id)) + || !LLIMConversation::isChatMultiTab()) + { + return; + } + + // Get the floater: this will create the instance if it didn't exist + LLIMConversation* conversp = LLIMConversation::getConversation(session_id); + if (conversp) + { + LLIMFloaterContainer* floater_container = LLIMFloaterContainer::getInstance(); + + // Do not add again existing floaters + if (floater_container && !conversp->isHostAttached()) + { + conversp->setHostAttached(true); + + if (!conversp->isNearbyChat() + || gSavedSettings.getBOOL("NearbyChatIsNotTornOff")) + { + floater_container->addFloater(conversp, TRUE, LLTabContainer::END); + } + else + { + // setting of the "potential" host for Nearby Chat: this sequence sets + // LLFloater::mHostHandle = NULL (a current host), but + // LLFloater::mLastHostHandle = floater_container (a "future" host) + conversp->setHost(floater_container); + conversp->setHost(NULL); + } + + } + } +} + +BOOL LLIMConversation::postBuild() +{ + BOOL result; + + mCloseBtn = getChild<LLButton>("close_btn"); + mCloseBtn->setCommitCallback(boost::bind(&LLFloater::onClickClose, this)); + + mExpandCollapseBtn = getChild<LLButton>("expand_collapse_btn"); + mExpandCollapseBtn->setClickedCallback(boost::bind(&LLIMConversation::onSlide, this)); + + mParticipantListPanel = getChild<LLLayoutPanel>("speakers_list_panel"); + + mTearOffBtn = getChild<LLButton>("tear_off_btn"); + mTearOffBtn->setCommitCallback(boost::bind(&LLIMConversation::onTearOffClicked, this)); + + mChatHistory = getChild<LLChatHistory>("chat_history"); + + mInputEditor = getChild<LLChatEntry>("chat_editor"); + mInputEditor->setTextExpandedCallback(boost::bind(&LLIMConversation::reshapeChatHistory, this)); + mInputEditor->setCommitOnFocusLost( FALSE ); + mInputEditor->setPassDelete(TRUE); + mInputEditor->setFont(LLViewerChat::getChatFont()); + + mInputEditorTopPad = mChatHistory->getRect().mBottom - mInputEditor->getRect().mTop; + + setOpenPositioning(LLFloaterEnums::POSITIONING_RELATIVE); + + buildParticipantList(); + + updateHeaderAndToolbar(); + + mSaveRect = isTornOff(); + initRectControl(); + + if (isChatMultiTab()) + { + if (mIsNearbyChat) + { + setCanClose(FALSE); + } + result = LLFloater::postBuild(); + } + else + { + result = LLDockableFloater::postBuild(); + } + + return result; +} + +void LLIMConversation::draw() +{ + LLTransientDockableFloater::draw(); + + if (mRefreshTimer->hasExpired()) + { + if (mParticipantList) + { + mParticipantList->update(); + } + + refresh(); + updateHeaderAndToolbar(); + + // Restart the refresh timer + mRefreshTimer->setTimerExpirySec(REFRESH_INTERVAL); + } +} + +void LLIMConversation::enableDisableCallBtn() +{ + getChildView("voice_call_btn")->setEnabled( + mSessionID.notNull() + && mSession + && mSession->mSessionInitialized + && LLVoiceClient::getInstance()->voiceEnabled() + && LLVoiceClient::getInstance()->isVoiceWorking() + && mSession->mCallBackEnabled); +} + + +void LLIMConversation::onFocusReceived() +{ + setBackgroundOpaque(true); + + if (mSessionID.notNull() && isInVisibleChain()) + { + LLIMModel::instance().sendNoUnreadMessages(mSessionID); + } + + LLTransientDockableFloater::onFocusReceived(); +} + +void LLIMConversation::onFocusLost() +{ + setBackgroundOpaque(false); + LLTransientDockableFloater::onFocusLost(); +} + +std::string LLIMConversation::appendTime() +{ + time_t utc_time; + utc_time = time_corrected(); + std::string timeStr ="["+ LLTrans::getString("TimeHour")+"]:[" + +LLTrans::getString("TimeMin")+"]"; + + LLSD substitution; + + substitution["datetime"] = (S32) utc_time; + LLStringUtil::format (timeStr, substitution); + + return timeStr; +} + +void LLIMConversation::appendMessage(const LLChat& chat, const LLSD &args) +{ + // Update the participant activity time + LLIMFloaterContainer* im_box = LLIMFloaterContainer::findInstance(); + if (im_box) + { + im_box->setTimeNow(mSessionID,chat.mFromID); + } + + + LLChat& tmp_chat = const_cast<LLChat&>(chat); + + if(tmp_chat.mTimeStr.empty()) + tmp_chat.mTimeStr = appendTime(); + + if (!chat.mMuted) + { + tmp_chat.mFromName = chat.mFromName; + LLSD chat_args; + if (args) chat_args = args; + chat_args["use_plain_text_chat_history"] = + gSavedSettings.getBOOL("PlainTextChatHistory"); + chat_args["show_time"] = gSavedSettings.getBOOL("IMShowTime"); + chat_args["show_names_for_p2p_conv"] = + !mIsP2PChat || gSavedSettings.getBOOL("IMShowNamesForP2PConv"); + + if (mChatHistory) + { + mChatHistory->appendMessage(chat, chat_args); + } + } +} + + +void LLIMConversation::buildParticipantList() +{ + if (mIsNearbyChat) + { + LLLocalSpeakerMgr* speaker_manager = LLLocalSpeakerMgr::getInstance(); + mParticipantList = new LLParticipantList(speaker_manager, getChild<LLAvatarList>("speakers_list"), mConversationViewModel, true, false); + } + else + { + LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + // for group and ad-hoc chat we need to include agent into list + if(!mIsP2PChat && mSessionID.notNull() && speaker_manager) + { + delete mParticipantList; // remove the old list and create a new one if the session id has changed + mParticipantList = new LLParticipantList(speaker_manager, getChild<LLAvatarList>("speakers_list"), mConversationViewModel, true, false); + } + } + updateHeaderAndToolbar(); +} + +void LLIMConversation::onSortMenuItemClicked(const LLSD& userdata) +{ + // TODO: Check this code when sort order menu will be added. (EM) + if (!mParticipantList) + { + return; + } + + std::string chosen_item = userdata.asString(); + + if (chosen_item == "sort_name") + { + mParticipantList->setSortOrder(LLParticipantList::E_SORT_BY_NAME); + } + +} + +void LLIMConversation::onIMSessionMenuItemClicked(const LLSD& userdata) +{ + std::string item = userdata.asString(); + + if (item == "compact_view" || item == "expanded_view") + { + gSavedSettings.setBOOL("PlainTextChatHistory", item == "compact_view"); + } + else + { + bool prev_value = gSavedSettings.getBOOL(item); + gSavedSettings.setBOOL(item, !prev_value); + } + + LLIMConversation::processChatHistoryStyleUpdate(); +} + + +bool LLIMConversation::onIMCompactExpandedMenuItemCheck(const LLSD& userdata) +{ + std::string item = userdata.asString(); + bool is_plain_text_mode = gSavedSettings.getBOOL("PlainTextChatHistory"); + + return is_plain_text_mode? item == "compact_view" : item == "expanded_view"; +} + + +bool LLIMConversation::onIMShowModesMenuItemCheck(const LLSD& userdata) +{ + return gSavedSettings.getBOOL(userdata.asString()); +} + +// enable/disable states for the "show time" and "show names" items of the show-modes menu +bool LLIMConversation::onIMShowModesMenuItemEnable(const LLSD& userdata) +{ + std::string item = userdata.asString(); + bool plain_text = gSavedSettings.getBOOL("PlainTextChatHistory"); + bool is_not_names = (item != "IMShowNamesForP2PConv"); + return (plain_text && (is_not_names || mIsP2PChat)); +} + +void LLIMConversation::hideOrShowTitle() +{ + const LLFloater::Params& default_params = LLFloater::getDefaultParams(); + S32 floater_header_size = default_params.header_height; + LLView* floater_contents = getChild<LLView>("contents_view"); + + LLRect floater_rect = getLocalRect(); + S32 top_border_of_contents = floater_rect.mTop - (isTornOff()? floater_header_size : 0); + LLRect handle_rect (0, floater_rect.mTop, floater_rect.mRight, top_border_of_contents); + LLRect contents_rect (0, top_border_of_contents, floater_rect.mRight, floater_rect.mBottom); + mDragHandle->setShape(handle_rect); + mDragHandle->setVisible(isTornOff()); + floater_contents->setShape(contents_rect); +} + +void LLIMConversation::hideAllStandardButtons() +{ + for (S32 i = 0; i < BUTTON_COUNT; i++) + { + if (mButtons[i]) + { + // Hide the standard header buttons in a docked IM floater. + mButtons[i]->setVisible(false); + } + } +} + +void LLIMConversation::updateHeaderAndToolbar() +{ + // prevent start conversation before its container + LLIMFloaterContainer::getInstance(); + + bool is_torn_off = checkIfTornOff(); + if (!is_torn_off) + { + hideAllStandardButtons(); + } + + hideOrShowTitle(); + + // Participant list should be visible only in torn off floaters. + bool is_participant_list_visible = + is_torn_off + && gSavedSettings.getBOOL("IMShowControlPanel") + && !mIsP2PChat; + + mParticipantListPanel->setVisible(is_participant_list_visible); + + // Display collapse image (<<) if the floater is hosted + // or if it is torn off but has an open control panel. + bool is_expanded = !is_torn_off || is_participant_list_visible; + mExpandCollapseBtn->setImageOverlay(getString(is_expanded ? "collapse_icon" : "expand_icon")); + + // toggle floater's drag handle and title visibility + if (mDragHandle) + { + mDragHandle->setTitleVisible(is_torn_off); + } + + // The button (>>) should be disabled for torn off P2P conversations. + mExpandCollapseBtn->setEnabled(!is_torn_off || !mIsP2PChat); + + mTearOffBtn->setImageOverlay(getString(is_torn_off? "return_icon" : "tear_off_icon")); + mTearOffBtn->setToolTip(getString(!is_torn_off? "tooltip_to_separate_window" : "tooltip_to_main_window")); + + mCloseBtn->setVisible(!is_torn_off && !mIsNearbyChat); + + enableDisableCallBtn(); + + showTranslationCheckbox(); +} + +void LLIMConversation::reshapeChatHistory() +{ + LLRect chat_rect = mChatHistory->getRect(); + LLRect input_rect = mInputEditor->getRect(); + + int delta_height = chat_rect.mBottom - (input_rect.mTop + mInputEditorTopPad); + + chat_rect.setLeftTopAndSize(chat_rect.mLeft, chat_rect.mTop, chat_rect.getWidth(), chat_rect.getHeight() + delta_height); + mChatHistory->setShape(chat_rect); +} + +void LLIMConversation::showTranslationCheckbox(BOOL show) +{ + getChild<LLUICtrl>("translate_chat_checkbox_lp")->setVisible(mIsNearbyChat? show : FALSE); +} + +// static +void LLIMConversation::processChatHistoryStyleUpdate() +{ + LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel"); + for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); + iter != inst_list.end(); ++iter) + { + LLIMFloater* floater = dynamic_cast<LLIMFloater*>(*iter); + if (floater) + { + floater->reloadMessages(); + } + } + + LLNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance<LLNearbyChat>("nearby_chat"); + if (nearby_chat) + { + nearby_chat->reloadMessages(); + } +} + +void LLIMConversation::updateCallBtnState(bool callIsActive) +{ + getChild<LLButton>("voice_call_btn")->setImageOverlay( + callIsActive? getString("call_btn_stop") : getString("call_btn_start")); + enableDisableCallBtn(); + +} + +void LLIMConversation::onSlide(LLIMConversation* self) +{ + LLIMFloaterContainer* host_floater = dynamic_cast<LLIMFloaterContainer*>(self->getHost()); + if (host_floater) + { + // Hide the messages pane if a floater is hosted in the Conversations + host_floater->collapseMessagesPane(true); + } + else ///< floater is torn off + { + if (!self->mIsP2PChat) + { + bool expand = !self->mParticipantListPanel->getVisible(); + + // Expand/collapse the IM control panel + self->mParticipantListPanel->setVisible(expand); + + gSavedSettings.setBOOL("IMShowControlPanel", expand); + + self->mExpandCollapseBtn->setImageOverlay(self->getString(expand ? "collapse_icon" : "expand_icon")); + } + } +} + +/*virtual*/ +void LLIMConversation::onOpen(const LLSD& key) +{ + if (!checkIfTornOff()) + { + LLIMFloaterContainer* host_floater = dynamic_cast<LLIMFloaterContainer*>(getHost()); + // Show the messages pane when opening a floater hosted in the Conversations + host_floater->collapseMessagesPane(false); + } +} + +// virtual +void LLIMConversation::onClose(bool app_quitting) +{ + // Always suppress the IM from the conversations list on close if present for any reason + if (LLIMConversation::isChatMultiTab()) + { + LLIMFloaterContainer* im_box = LLIMFloaterContainer::findInstance(); + if (im_box) + { + im_box->removeConversationListItem(mKey); + } + } +} + +void LLIMConversation::onTearOffClicked() +{ + setFollows(isTornOff()? FOLLOWS_ALL : FOLLOWS_NONE); + mSaveRect = isTornOff(); + initRectControl(); + LLFloater::onClickTearOff(this); + updateHeaderAndToolbar(); +} + +// static +bool LLIMConversation::isChatMultiTab() +{ + // Restart is required in order to change chat window type. + return true; +} + +bool LLIMConversation::checkIfTornOff() +{ + bool isTorn = !getHost(); + + if (isTorn != isTornOff()) + { + setTornOff(isTorn); + updateHeaderAndToolbar(); + } + + return isTorn; +} diff --git a/indra/newview/llimconversation.h b/indra/newview/llimconversation.h new file mode 100644 index 0000000000..bff4cb4a31 --- /dev/null +++ b/indra/newview/llimconversation.h @@ -0,0 +1,158 @@ +/** + * @file llimconversation.h + * @brief LLIMConversation class implements the common behavior of LNearbyChatBar + * @brief and LLIMFloater for hosting both in LLIMContainer + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_IMCONVERSATION_H +#define LL_IMCONVERSATION_H + +#include "lllayoutstack.h" +#include "llparticipantlist.h" +#include "lltransientdockablefloater.h" +#include "llviewercontrol.h" +#include "lleventtimer.h" +#include "llimview.h" +#include "llconversationmodel.h" + +class LLPanelChatControlPanel; +class LLChatEntry; +class LLChatHistory; + +class LLIMConversation + : public LLTransientDockableFloater +{ + +public: + LOG_CLASS(LLIMConversation); + + LLIMConversation(const LLSD& session_id); + ~LLIMConversation(); + + // reload all message with new settings of visual modes + static void processChatHistoryStyleUpdate(); + + /** + * Returns true if chat is displayed in multi tabbed floater + * false if chat is displayed in multiple windows + */ + static bool isChatMultiTab(); + + // add conversation to container + static void addToHost(const LLUUID& session_id); + + bool isHostAttached() {return mIsHostAttached;} + void setHostAttached(bool is_attached) {mIsHostAttached = is_attached;} + + static LLIMConversation* findConversation(const LLUUID& uuid); + static LLIMConversation* getConversation(const LLUUID& uuid); + + // show/hide the translation check box + void showTranslationCheckbox(const BOOL visible = FALSE); + + bool isNearbyChat() {return mIsNearbyChat;} + + // LLFloater overrides + /*virtual*/ void onOpen(const LLSD& key); + /*virtual*/ void onClose(bool app_quitting); + /*virtual*/ BOOL postBuild(); + /*virtual*/ void draw(); + /*virtual*/ void setVisible(BOOL visible); + + +protected: + + // callback for click on any items of the visual states menu + void onIMSessionMenuItemClicked(const LLSD& userdata); + + // callback for check/uncheck of the expanded/collapse mode's switcher + bool onIMCompactExpandedMenuItemCheck(const LLSD& userdata); + + // + bool onIMShowModesMenuItemCheck(const LLSD& userdata); + bool onIMShowModesMenuItemEnable(const LLSD& userdata); + static void onSlide(LLIMConversation *self); + virtual void onTearOffClicked(); + + // refresh a visual state of the Call button + void updateCallBtnState(bool callIsActive); + + void buildParticipantList(); + void onSortMenuItemClicked(const LLSD& userdata); + + void hideOrShowTitle(); // toggle the floater's drag handle + void hideAllStandardButtons(); + + /// Update floater header and toolbar buttons when hosted/torn off state is toggled. + void updateHeaderAndToolbar(); + + // set the enable/disable state for the Call button + virtual void enableDisableCallBtn(); + + // process focus events to set a currently active session + /* virtual */ void onFocusLost(); + /* virtual */ void onFocusReceived(); + + // prepare chat's params and out one message to chatHistory + void appendMessage(const LLChat& chat, const LLSD &args = 0); + + std::string appendTime(); + + bool mIsNearbyChat; + bool mIsP2PChat; + + LLIMModel::LLIMSession* mSession; + + LLLayoutPanel* mParticipantListPanel; + LLParticipantList* mParticipantList; + LLUUID mSessionID; + LLConversationViewModel mConversationViewModel; + + LLChatHistory* mChatHistory; + LLChatEntry* mInputEditor; + int mInputEditorTopPad; // padding between input field and chat history + + LLButton* mExpandCollapseBtn; + LLButton* mTearOffBtn; + LLButton* mCloseBtn; + +private: + /// Refreshes the floater at a constant rate. + virtual void refresh() = 0; + + /** + * Adjusts chat history height to fit vertically with input chat field + * and avoid overlapping, since input chat field can be vertically expanded. + * Implementation: chat history bottom "follows" top+top_pad of input chat field + */ + void reshapeChatHistory(); + + bool checkIfTornOff(); + bool mIsHostAttached; + + LLTimer* mRefreshTimer; ///< Defines the rate at which refresh() is called. +}; + + +#endif // LL_IMCONVERSATION_H diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 63eedcdfea..3545b8ff18 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -28,44 +28,44 @@ #include "llimfloater.h" +#include "lldraghandle.h" #include "llnotificationsutil.h" #include "llagent.h" #include "llappviewer.h" +#include "llavataractions.h" #include "llavatarnamecache.h" #include "llbutton.h" #include "llchannelmanager.h" #include "llchiclet.h" #include "llchicletbar.h" #include "llfloaterreg.h" +#include "llfloateravatarpicker.h" #include "llimfloatercontainer.h" // to replace separate IM Floaters with multifloater container #include "llinventoryfunctions.h" -#include "lllayoutstack.h" -#include "lllineeditor.h" +//#include "lllayoutstack.h" +#include "llchatentry.h" #include "lllogchat.h" -#include "llpanelimcontrolpanel.h" #include "llscreenchannel.h" #include "llsyswellwindow.h" #include "lltrans.h" #include "llchathistory.h" #include "llnotifications.h" #include "llviewerwindow.h" -#include "llvoicechannel.h" #include "lltransientfloatermgr.h" #include "llinventorymodel.h" #include "llrootview.h" #include "llspeakers.h" #include "llviewerchat.h" +#include "llnotificationmanager.h" #include "llautoreplace.h" +floater_showed_signal_t LLIMFloater::sIMFloaterShowedSignal; + LLIMFloater::LLIMFloater(const LLUUID& session_id) - : LLTransientDockableFloater(NULL, true, session_id), - mControlPanel(NULL), - mSessionID(session_id), + : LLIMConversation(session_id), mLastMessageIndex(-1), mDialog(IM_NOTHING_SPECIAL), - mChatHistory(NULL), - mInputEditor(NULL), mSavedTitle(), mTypingStart(), mShouldSendTypingState(false), @@ -74,38 +74,13 @@ LLIMFloater::LLIMFloater(const LLUUID& session_id) mTypingTimer(), mTypingTimeoutTimer(), mPositioned(false), - mSessionInitialized(false) + mSessionInitialized(false), + mStartConferenceInSameFloater(false) { - LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(mSessionID); - if (im_session) - { - mSessionInitialized = im_session->mSessionInitialized; + mIsNearbyChat = false; + + initIMSession(session_id); - mDialog = im_session->mType; - switch(mDialog){ - case IM_NOTHING_SPECIAL: - case IM_SESSION_P2P_INVITE: - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelIMControl, this); - break; - case IM_SESSION_CONFERENCE_START: - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelAdHocControl, this); - break; - case IM_SESSION_GROUP_START: - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this); - break; - case IM_SESSION_INVITE: - if (gAgent.isInGroup(mSessionID)) - { - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this); - } - else - { - mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelAdHocControl, this); - } - break; - default: break; - } - } setOverlapsScreenChannel(true); LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this); @@ -113,50 +88,61 @@ LLIMFloater::LLIMFloater(const LLUUID& session_id) setDocked(true); } -void LLIMFloater::onFocusLost() + +// virtual +void LLIMFloater::refresh() { - LLIMModel::getInstance()->resetActiveSessionID(); - - LLChicletBar::getInstance()->getChicletPanel()->setChicletToggleState(mSessionID, false); + if (mMeTyping) +{ + // Time out if user hasn't typed for a while. + if (mTypingTimeoutTimer.getElapsedTimeF32() > LLAgent::TYPING_TIMEOUT_SECS) + { + setTyping(false); + } + } } -void LLIMFloater::onFocusReceived() +// virtual +void LLIMFloater::onClickCloseBtn() { - LLIMModel::getInstance()->setActiveSessionID(mSessionID); + LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(mSessionID); - LLChicletBar::getInstance()->getChicletPanel()->setChicletToggleState(mSessionID, true); + if (session != NULL) + { + bool is_call_with_chat = session->isGroupSessionType() + || session->isAdHocSessionType() || session->isP2PSessionType(); - if (getVisible()) + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); + + if (is_call_with_chat && voice_channel != NULL + && voice_channel->isActive()) + { + LLSD payload; + payload["session_id"] = mSessionID; + LLNotificationsUtil::add("ConfirmLeaveCall", LLSD(), payload, confirmLeaveCallCallback); + return; + } + } + else { - LLIMModel::instance().sendNoUnreadMessages(mSessionID); + llwarns << "Empty session with id: " << (mSessionID.asString()) << llendl; + return; } -} - -// virtual -void LLIMFloater::onClose(bool app_quitting) -{ - setTyping(false); - // The source of much argument and design thrashing - // Should the window hide or the session close when the X is clicked? - // - // Last change: - // EXT-3516 X Button should end IM session, _ button should hide - gIMMgr->leaveSession(mSessionID); + LLIMConversation::onClickCloseBtn(); } /* static */ -void LLIMFloater::newIMCallback(const LLSD& data){ - +void LLIMFloater::newIMCallback(const LLSD& data) +{ if (data["num_unread"].asInteger() > 0 || data["from_id"].asUUID().isNull()) { LLUUID session_id = data["session_id"].asUUID(); LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); - if (floater == NULL) return; // update if visible, otherwise will be updated when opened - if (floater->getVisible()) + if (floater && floater->getVisible()) { floater->updateMessages(); } @@ -183,113 +169,112 @@ void LLIMFloater::onVisibilityChange(const LLSD& new_visibility) void LLIMFloater::onSendMsg( LLUICtrl* ctrl, void* userdata ) { LLIMFloater* self = (LLIMFloater*) userdata; - self->sendMsg(); + self->sendMsgFromInputEditor(); self->setTyping(false); } -void LLIMFloater::sendMsg() +void LLIMFloater::sendMsgFromInputEditor() { - if (!gAgent.isGodlike() - && (mDialog == IM_NOTHING_SPECIAL) - && mOtherParticipantUUID.isNull()) - { - llinfos << "Cannot send IM to everyone unless you're a god." << llendl; - return; - } - - if (mInputEditor) + if (gAgent.isGodlike() + || (mDialog != IM_NOTHING_SPECIAL) + || !mOtherParticipantUUID.isNull()) { - LLWString text = mInputEditor->getConvertedText(); - if(!text.empty()) + if (mInputEditor) { - // Truncate and convert to UTF8 for transport - std::string utf8_text = wstring_to_utf8str(text); - utf8_text = utf8str_truncate(utf8_text, MAX_MSG_BUF_SIZE - 1); - - if (mSessionInitialized) + LLWString text = mInputEditor->getWText(); + LLWStringUtil::trim(text); + LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. + if(!text.empty()) { - LLIMModel::sendMessage(utf8_text, mSessionID, - mOtherParticipantUUID,mDialog); - } - else - { - //queue up the message to send once the session is initialized - mQueuedMsgsForInit.append(utf8_text); - } + // Truncate and convert to UTF8 for transport + std::string utf8_text = wstring_to_utf8str(text); - mInputEditor->setText(LLStringUtil::null); + sendMsg(utf8_text); - updateMessages(); + mInputEditor->setText(LLStringUtil::null); + } } } + else + { + llinfos << "Cannot send IM to everyone unless you're a god." << llendl; + } } +void LLIMFloater::sendMsg(const std::string& msg) +{ + const std::string utf8_text = utf8str_truncate(msg, MAX_MSG_BUF_SIZE - 1); + if (mSessionInitialized) + { + LLIMModel::sendMessage(utf8_text, mSessionID, mOtherParticipantUUID, mDialog); + } + else + { + //queue up the message to send once the session is initialized + mQueuedMsgsForInit.append(utf8_text); + } -LLIMFloater::~LLIMFloater() -{ - LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this); + updateMessages(); } -//virtual -BOOL LLIMFloater::postBuild() +LLIMFloater::~LLIMFloater() { - const LLUUID& other_party_id = LLIMModel::getInstance()->getOtherParticipantID(mSessionID); - if (other_party_id.notNull()) + mParticipantsListRefreshConnection.disconnect(); + mVoiceChannelStateChangeConnection.disconnect(); + if(LLVoiceClient::instanceExists()) { - mOtherParticipantUUID = other_party_id; + LLVoiceClient::getInstance()->removeObserver(this); } - mControlPanel->setSessionId(mSessionID); - mControlPanel->getParent()->setVisible(gSavedSettings.getBOOL("IMShowControlPanel")); + LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this); +} - LLButton* slide_left = getChild<LLButton>("slide_left_btn"); - slide_left->setVisible(mControlPanel->getParent()->getVisible()); - slide_left->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); - LLButton* slide_right = getChild<LLButton>("slide_right_btn"); - slide_right->setVisible(!mControlPanel->getParent()->getVisible()); - slide_right->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); +void LLIMFloater::initIMSession(const LLUUID& session_id) +{ + // Change the floater key to bind it to a new session. + setKey(session_id); - mInputEditor = getChild<LLLineEditor>("chat_editor"); - mInputEditor->setMaxTextLength(1023); - // enable line history support for instant message bar - mInputEditor->setEnableLineHistory(TRUE); - // *TODO Establish LineEditor with autoreplace callback - mInputEditor->setAutoreplaceCallback(boost::bind(&LLAutoReplace::autoreplaceCallback, LLAutoReplace::getInstance(), _1, _2)); + mSessionID = session_id; + mSession = LLIMModel::getInstance()->findIMSession(mSessionID); - LLFontGL* font = LLViewerChat::getChatFont(); - mInputEditor->setFont(font); - - mInputEditor->setFocusReceivedCallback( boost::bind(onInputEditorFocusReceived, _1, this) ); - mInputEditor->setFocusLostCallback( boost::bind(onInputEditorFocusLost, _1, this) ); - mInputEditor->setKeystrokeCallback( onInputEditorKeystroke, this ); - mInputEditor->setCommitOnFocusLost( FALSE ); - mInputEditor->setRevertOnEsc( FALSE ); - mInputEditor->setReplaceNewlinesWithSpaces( FALSE ); - mInputEditor->setPassDelete( TRUE ); + if (mSession) + { + mIsP2PChat = mSession->isP2PSessionType(); + mSessionInitialized = mSession->mSessionInitialized; - childSetCommitCallback("chat_editor", onSendMsg, this); - - mChatHistory = getChild<LLChatHistory>("chat_history"); + mDialog = mSession->mType; + } +} - setDocked(true); +void LLIMFloater::initIMFloater() +{ + const LLUUID& other_party_id = + LLIMModel::getInstance()->getOtherParticipantID(mSessionID); + if (other_party_id.notNull()) + { + mOtherParticipantUUID = other_party_id; + } + + boundVoiceChannel(); mTypingStart = LLTrans::getString("IM_typing_start_string"); + // Show control panel in torn off floaters only. + mParticipantListPanel->setVisible(!getHost() && gSavedSettings.getBOOL("IMShowControlPanel")); + // Disable input editor if session cannot accept text - LLIMModel::LLIMSession* im_session = - LLIMModel::instance().findIMSession(mSessionID); - if( im_session && !im_session->mTextIMPossible ) + if ( mSession && !mSession->mTextIMPossible ) { mInputEditor->setEnabled(FALSE); mInputEditor->setLabel(LLTrans::getString("IM_unavailable_text_label")); } - if ( im_session && im_session->isP2PSessionType()) + if (mIsP2PChat) { // look up display name for window title - LLAvatarNameCache::get(im_session->mOtherParticipantID, + LLAvatarNameCache::get(mSession->mOtherParticipantID, boost::bind(&LLIMFloater::onAvatarNameCache, this, _1, _2)); } @@ -297,167 +282,374 @@ BOOL LLIMFloater::postBuild() { std::string session_name(LLIMModel::instance().getName(mSessionID)); updateSessionName(session_name, session_name); + + // For ad hoc conferences we should update the title with participants names. + if ((IM_SESSION_INVITE == mDialog && !gAgent.isInGroup(mSessionID)) + || mDialog == IM_SESSION_CONFERENCE_START) + { + if (mParticipantsListRefreshConnection.connected()) + { + mParticipantsListRefreshConnection.disconnect(); + } + + LLAvatarList* avatar_list = getChild<LLAvatarList>("speakers_list"); + mParticipantsListRefreshConnection = avatar_list->setRefreshCompleteCallback( + boost::bind(&LLIMFloater::onParticipantsListChanged, this, _1)); + } } +} + +//virtual +BOOL LLIMFloater::postBuild() +{ + BOOL result = LLIMConversation::postBuild(); + + mInputEditor->setMaxTextLength(1023); + // enable line history support for instant message bar + // XXX stinson TODO : resolve merge by adding autoreplace to text editors +#if 0 + // *TODO Establish LineEditor with autoreplace callback + mInputEditor->setAutoreplaceCallback(boost::bind(&LLAutoReplace::autoreplaceCallback, LLAutoReplace::getInstance(), _1, _2)); +#endif + + mInputEditor->setFocusReceivedCallback( boost::bind(onInputEditorFocusReceived, _1, this) ); + mInputEditor->setFocusLostCallback( boost::bind(onInputEditorFocusLost, _1, this) ); + mInputEditor->setKeystrokeCallback( boost::bind(onInputEditorKeystroke, _1, this) ); + mInputEditor->setCommitCallback(boost::bind(onSendMsg, _1, this)); + + setDocked(true); + + LLButton* add_btn = getChild<LLButton>("add_btn"); + + // Allow to add chat participants depending on the session type + add_btn->setEnabled(isInviteAllowed()); + add_btn->setClickedCallback(boost::bind(&LLIMFloater::onAddButtonClicked, this)); + + childSetAction("voice_call_btn", boost::bind(&LLIMFloater::onCallButtonClicked, this)); + + LLVoiceClient::getInstance()->addObserver(this); //*TODO if session is not initialized yet, add some sort of a warning message like "starting session...blablabla" //see LLFloaterIMPanel for how it is done (IB) - if(isChatMultiTab()) + initIMFloater(); + + return result; +} + +void LLIMFloater::onAddButtonClicked() +{ + LLView * button = findChild<LLView>("toolbar_panel")->findChild<LLButton>("add_btn"); + LLFloater* root_floater = gFloaterView->getParentFloater(this); + LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLIMFloater::addSessionParticipants, this, _1), TRUE, TRUE, FALSE, root_floater->getName(), button); + if (!picker) { - return LLFloater::postBuild(); + return; } - else + + // Need to disable 'ok' button when selected users are already in conversation. + picker->setOkBtnEnableCb(boost::bind(&LLIMFloater::canAddSelectedToChat, this, _1)); + + if (root_floater) { - return LLDockableFloater::postBuild(); + root_floater->addDependentFloater(picker); } } -void LLIMFloater::updateSessionName(const std::string& ui_title, - const std::string& ui_label) +bool LLIMFloater::canAddSelectedToChat(const uuid_vec_t& uuids) { - mInputEditor->setLabel(LLTrans::getString("IM_to_label") + " " + ui_label); - setTitle(ui_title); + if (!mSession + || mDialog == IM_SESSION_GROUP_START + || mDialog == IM_SESSION_INVITE && gAgent.isInGroup(mSessionID)) + { + return false; + } + + if (mIsP2PChat) + { + // For a P2P session just check if we are not adding the other participant. + + for (uuid_vec_t::const_iterator id = uuids.begin(); + id != uuids.end(); ++id) + { + if (*id == mOtherParticipantUUID) + { + return false; + } + } + } + else + { + // For a conference session we need to check against the list from LLSpeakerMgr, + // because this list may change when participants join or leave the session. + + LLSpeakerMgr::speaker_list_t speaker_list; + LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if (speaker_mgr) + { + speaker_mgr->getSpeakerList(&speaker_list, true); + } + + for (uuid_vec_t::const_iterator id = uuids.begin(); + id != uuids.end(); ++id) + { + for (LLSpeakerMgr::speaker_list_t::const_iterator it = speaker_list.begin(); + it != speaker_list.end(); ++it) + { + const LLPointer<LLSpeaker>& speaker = *it; + if (*id == speaker->mID) + { + return false; + } + } + } + } + + return true; } -void LLIMFloater::onAvatarNameCache(const LLUUID& agent_id, - const LLAvatarName& av_name) +void LLIMFloater::addSessionParticipants(const uuid_vec_t& uuids) { - // Use display name only for labels, as the extended name will be in the - // floater title - std::string ui_title = av_name.getCompleteName(); - updateSessionName(ui_title, av_name.mDisplayName); - mTypingStart.setArg("[NAME]", ui_title); + if (mIsP2PChat) + { + LLSD payload; + LLSD args; + + LLNotificationsUtil::add("ConfirmAddingChatParticipants", args, payload, + boost::bind(&LLIMFloater::addP2PSessionParticipants, this, _1, _2, uuids)); + } + else + { + // remember whom we have invited, to notify others later, when the invited ones actually join + mInvitedParticipants.insert(mInvitedParticipants.end(), uuids.begin(), uuids.end()); + + inviteToSession(uuids); + } } -// virtual -void LLIMFloater::draw() +void LLIMFloater::addP2PSessionParticipants(const LLSD& notification, const LLSD& response, const uuid_vec_t& uuids) { - if ( mMeTyping ) + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 0) { - // Time out if user hasn't typed for a while. - if ( mTypingTimeoutTimer.getElapsedTimeF32() > LLAgent::TYPING_TIMEOUT_SECS ) - { - setTyping(false); - } + return; } - LLTransientDockableFloater::draw(); + mStartConferenceInSameFloater = true; + + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); + + // first check whether this is a voice session + bool is_voice_call = voice_channel != NULL && voice_channel->isActive(); + + uuid_vec_t temp_ids; + + // Add the initial participant of a P2P session + temp_ids.push_back(mOtherParticipantUUID); + temp_ids.insert(temp_ids.end(), uuids.begin(), uuids.end()); + + // then we can close the current session + onClose(false); + + // we start a new session so reset the initialization flag + mSessionInitialized = false; + + // remember whom we have invited, to notify others later, when the invited ones actually join + mInvitedParticipants.insert(mInvitedParticipants.end(), uuids.begin(), uuids.end()); + + // Start a new ad hoc voice call if we invite new participants to a P2P call, + // or start a text chat otherwise. + if (is_voice_call) + { + LLAvatarActions::startAdhocCall(temp_ids, mSessionID); + } + else + { + LLAvatarActions::startConference(temp_ids, mSessionID); + } } +void LLIMFloater::sendParticipantsAddedNotification(const uuid_vec_t& uuids) +{ + std::string names_string; + LLAvatarActions::buildResidentsString(uuids, names_string); + LLStringUtil::format_map_t args; + args["[NAME]"] = names_string; -// static -void* LLIMFloater::createPanelIMControl(void* userdata) + sendMsg(getString(uuids.size() > 1 ? "multiple_participants_added" : "participant_added", args)); +} + +void LLIMFloater::boundVoiceChannel() { - LLIMFloater *self = (LLIMFloater*)userdata; - self->mControlPanel = new LLPanelIMControlPanel(); - self->mControlPanel->setXMLFilename("panel_im_control_panel.xml"); - return self->mControlPanel; + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); + if(voice_channel) + { + mVoiceChannelStateChangeConnection = voice_channel->setStateChangedCallback( + boost::bind(&LLIMFloater::onVoiceChannelStateChanged, this, _1, _2)); + + //call (either p2p, group or ad-hoc) can be already in started state + bool callIsActive = voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED; + updateCallBtnState(callIsActive); + } } +void LLIMFloater::onCallButtonClicked() +{ + LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); + if (voice_channel) + { + bool is_call_active = voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED; + if (is_call_active) + { + gIMMgr->endCall(mSessionID); + } + else + { + gIMMgr->startCall(mSessionID); + } + } +} -// static -void* LLIMFloater::createPanelGroupControl(void* userdata) +void LLIMFloater::onChange(EStatusType status, const std::string &channelURI, bool proximal) { - LLIMFloater *self = (LLIMFloater*)userdata; - self->mControlPanel = new LLPanelGroupControlPanel(self->mSessionID); - self->mControlPanel->setXMLFilename("panel_group_control_panel.xml"); - return self->mControlPanel; + if(status != STATUS_JOINING && status != STATUS_LEFT_CHANNEL) + { + enableDisableCallBtn(); + } } -// static -void* LLIMFloater::createPanelAdHocControl(void* userdata) +void LLIMFloater::onVoiceChannelStateChanged( + const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state) { - LLIMFloater *self = (LLIMFloater*)userdata; - self->mControlPanel = new LLPanelAdHocControlPanel(self->mSessionID); - self->mControlPanel->setXMLFilename("panel_adhoc_control_panel.xml"); - return self->mControlPanel; + bool callIsActive = new_state >= LLVoiceChannel::STATE_CALL_STARTED; + updateCallBtnState(callIsActive); } -void LLIMFloater::onSlide() +void LLIMFloater::updateSessionName(const std::string& ui_title, + const std::string& ui_label) { - mControlPanel->getParent()->setVisible(!mControlPanel->getParent()->getVisible()); + mInputEditor->setLabel(LLTrans::getString("IM_to_label") + " " + ui_label); + setTitle(ui_title); +} - gSavedSettings.setBOOL("IMShowControlPanel", mControlPanel->getParent()->getVisible()); +void LLIMFloater::onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name) +{ + // Use display name only for labels, as the extended name will be in the + // floater title + std::string ui_title = av_name.getCompleteName(); + updateSessionName(ui_title, av_name.mDisplayName); + mTypingStart.setArg("[NAME]", ui_title); +} + +void LLIMFloater::onParticipantsListChanged(LLUICtrl* ctrl) +{ + LLAvatarList* avatar_list = dynamic_cast<LLAvatarList*>(ctrl); + if (!avatar_list) + { + return; + } + + bool all_names_resolved = true; + std::vector<LLSD> participants_uuids; + uuid_vec_t temp_uuids; // uuids vector for building the added participants' names string + + avatar_list->getValues(participants_uuids); + + // Check whether we have all participants names in LLAvatarNameCache + for (std::vector<LLSD>::const_iterator it = participants_uuids.begin(); it != participants_uuids.end(); ++it) + { + const LLUUID& id = it->asUUID(); + temp_uuids.push_back(id); + LLAvatarName av_name; + if (!LLAvatarNameCache::get(id, &av_name)) + { + all_names_resolved = false; + + // If a name is not found in cache, request it and continue the process recursively + // until all ids are resolved into names. + LLAvatarNameCache::get(id, + boost::bind(&LLIMFloater::onParticipantsListChanged, this, avatar_list)); + break; + } + } - getChild<LLButton>("slide_left_btn")->setVisible(mControlPanel->getParent()->getVisible()); - getChild<LLButton>("slide_right_btn")->setVisible(!mControlPanel->getParent()->getVisible()); + if (all_names_resolved) + { + std::string ui_title; + LLAvatarActions::buildResidentsString(temp_uuids, ui_title); + updateSessionName(ui_title, ui_title); + } } + //static LLIMFloater* LLIMFloater::show(const LLUUID& session_id) { closeHiddenIMToasts(); - if (!gIMMgr->hasSession(session_id)) return NULL; - - if(!isChatMultiTab()) - { - //hide all - LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel"); - for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); - iter != inst_list.end(); ++iter) - { - LLIMFloater* floater = dynamic_cast<LLIMFloater*>(*iter); - if (floater && floater->isDocked()) - { - floater->setVisible(false); - } - } - } + if (!gIMMgr->hasSession(session_id)) + return NULL; + // Test the existence of the floater before we try to create it bool exist = findInstance(session_id); + // Get the floater: this will create the instance if it didn't exist LLIMFloater* floater = getInstance(session_id); - if (!floater) return NULL; + if (!floater) + return NULL; - if(isChatMultiTab()) - { - LLIMFloaterContainer* floater_container = LLIMFloaterContainer::getInstance(); + LLIMFloaterContainer* floater_container = LLIMFloaterContainer::getInstance(); - // do not add existed floaters to avoid adding torn off instances - if (!exist) + // Do not add again existing floaters + if (!exist) + { + // LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END; + // TODO: mantipov: use LLTabContainer::RIGHT_OF_CURRENT if it exists + LLTabContainer::eInsertionPoint i_pt = LLTabContainer::END; + if (floater_container) { - // LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END; - // TODO: mantipov: use LLTabContainer::RIGHT_OF_CURRENT if it exists - LLTabContainer::eInsertionPoint i_pt = LLTabContainer::END; - - if (floater_container) - { - floater_container->addFloater(floater, TRUE, i_pt); - } + floater_container->addFloater(floater, TRUE, i_pt); } - - floater->openFloater(floater->getKey()); } - else - { - // Docking may move chat window, hide it before moving, or user will see how window "jumps" - floater->setVisible(false); - - if (floater->getDockControl() == NULL) - { - LLChiclet* chiclet = - LLChicletBar::getInstance()->getChicletPanel()->findChiclet<LLChiclet>( - session_id); - if (chiclet == NULL) - { - llerror("Dock chiclet for LLIMFloater doesn't exists", 0); - } - else - { - LLChicletBar::getInstance()->getChicletPanel()->scrollToChiclet(chiclet); - } - floater->setDockControl(new LLDockControl(chiclet, floater, floater->getDockTongue(), - LLDockControl::BOTTOM)); - } + floater->openFloater(floater->getKey()); - // window is positioned, now we can show it. - } floater->setVisible(TRUE); return floater; } +//static +LLIMFloater* LLIMFloater::findInstance(const LLUUID& session_id) +{ + LLIMFloater* conversation = + LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); + + return conversation; +} + +LLIMFloater* LLIMFloater::getInstance(const LLUUID& session_id) +{ + LLIMFloater* conversation = + LLFloaterReg::getTypedInstance<LLIMFloater>("impanel", session_id); + + return conversation; +} + +void LLIMFloater::onClose(bool app_quitting) +{ + setTyping(false); + + // The source of much argument and design thrashing + // Should the window hide or the session close when the X is clicked? + // + // Last change: + // EXT-3516 X Button should end IM session, _ button should hide + gIMMgr->leaveSession(mSessionID); + + // Clean up the conversation *after* the session has been ended + LLIMConversation::onClose(app_quitting); +} void LLIMFloater::setDocked(bool docked, bool pop_on_undock) { @@ -479,12 +671,26 @@ void LLIMFloater::setDocked(bool docked, bool pop_on_undock) } } +void LLIMFloater::setFocus(BOOL focusFlag) +{ + LLTransientDockableFloater::setFocus(focusFlag); + + //Redirect focus to input editor + if (focusFlag) + { + updateMessages(); + mInputEditor->setFocus(TRUE); + } + +} + void LLIMFloater::setVisible(BOOL visible) { LLNotificationsUI::LLScreenChannel* channel = static_cast<LLNotificationsUI::LLScreenChannel*> (LLNotificationsUI::LLChannelManager::getInstance()-> findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID")))); - LLTransientDockableFloater::setVisible(visible); + + LLIMConversation::setVisible(visible); // update notification channel state if(channel) @@ -493,21 +699,6 @@ void LLIMFloater::setVisible(BOOL visible) channel->redrawToasts(); } - BOOL is_minimized = visible && isChatMultiTab() - ? LLIMFloaterContainer::getInstance()->isMinimized() - : !visible; - - if (!is_minimized && mChatHistory && mInputEditor) - { - //only if floater was construced and initialized from xml - updateMessages(); - //prevent stealing focus when opening a background IM tab (EXT-5387, checking focus for EXT-6781) - if (!isChatMultiTab() || hasFocus()) - { - mInputEditor->setFocus(TRUE); - } - } - if(!visible) { LLIMChiclet* chiclet = LLChicletBar::getInstance()->getChicletPanel()->findChiclet<LLIMChiclet>(mSessionID); @@ -516,13 +707,24 @@ void LLIMFloater::setVisible(BOOL visible) chiclet->setToggleState(false); } } + + if (visible && isInVisibleChain()) + { + sIMFloaterShowedSignal(mSessionID); + + } + + setFocus(visible); } BOOL LLIMFloater::getVisible() { + bool visible; + if(isChatMultiTab()) { - LLIMFloaterContainer* im_container = LLIMFloaterContainer::getInstance(); + LLIMFloaterContainer* im_container = + LLIMFloaterContainer::getInstance(); // Treat inactive floater as invisible. bool is_active = im_container->getActiveFloater() == this; @@ -530,16 +732,21 @@ BOOL LLIMFloater::getVisible() //torn off floater is always inactive if (!is_active && getHost() != im_container) { - return LLTransientDockableFloater::getVisible(); + visible = LLTransientDockableFloater::getVisible(); } - + else + { // getVisible() returns TRUE when Tabbed IM window is minimized. - return is_active && !im_container->isMinimized() && im_container->getVisible(); + visible = is_active && !im_container->isMinimized() + && im_container->getVisible(); + } } else { - return LLTransientDockableFloater::getVisible(); + visible = LLTransientDockableFloater::getVisible(); } + + return visible; } //static @@ -547,7 +754,8 @@ bool LLIMFloater::toggle(const LLUUID& session_id) { if(!isChatMultiTab()) { - LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); + LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>( + "impanel", session_id); if (floater && floater->getVisible() && floater->hasFocus()) { // clicking on chiclet to close floater just hides it to maintain existing @@ -568,17 +776,6 @@ bool LLIMFloater::toggle(const LLUUID& session_id) return true; } -//static -LLIMFloater* LLIMFloater::findInstance(const LLUUID& session_id) -{ - return LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); -} - -LLIMFloater* LLIMFloater::getInstance(const LLUUID& session_id) -{ - return LLFloaterReg::getTypedInstance<LLIMFloater>("impanel", session_id); -} - void LLIMFloater::sessionInitReplyReceived(const LLUUID& im_session_id) { mSessionInitialized = true; @@ -586,53 +783,40 @@ void LLIMFloater::sessionInitReplyReceived(const LLUUID& im_session_id) //will be different only for an ad-hoc im session if (mSessionID != im_session_id) { - mSessionID = im_session_id; - setKey(im_session_id); - mControlPanel->setSessionId(im_session_id); - } + initIMSession(im_session_id); - // updating "Call" button from group control panel here to enable it without placing into draw() (EXT-4796) - if(gAgent.isInGroup(im_session_id)) - { - mControlPanel->updateCallButton(); + buildParticipantList(); } + + initIMFloater(); //*TODO here we should remove "starting session..." warning message if we added it in postBuild() (IB) - - //need to send delayed messaged collected while waiting for session initialization - if (!mQueuedMsgsForInit.size()) return; - LLSD::array_iterator iter; - for ( iter = mQueuedMsgsForInit.beginArray(); - iter != mQueuedMsgsForInit.endArray(); - ++iter) + //need to send delayed messages collected while waiting for session initialization + if (mQueuedMsgsForInit.size()) { - LLIMModel::sendMessage(iter->asString(), mSessionID, - mOtherParticipantUUID, mDialog); + LLSD::array_iterator iter; + for ( iter = mQueuedMsgsForInit.beginArray(); + iter != mQueuedMsgsForInit.endArray(); ++iter) + { + LLIMModel::sendMessage(iter->asString(), mSessionID, + mOtherParticipantUUID, mDialog); + } + + mQueuedMsgsForInit.clear(); } } void LLIMFloater::updateMessages() { - bool use_plain_text_chat_history = gSavedSettings.getBOOL("PlainTextChatHistory"); - std::list<LLSD> messages; // we shouldn't reset unread message counters if IM floater doesn't have focus - if (hasFocus()) - { - LLIMModel::instance().getMessages(mSessionID, messages, mLastMessageIndex+1); - } - else - { - LLIMModel::instance().getMessagesSilently(mSessionID, messages, mLastMessageIndex+1); - } + LLIMModel::instance().getMessages( + mSessionID, messages, mLastMessageIndex + 1, hasFocus()); if (messages.size()) { - LLSD chat_args; - chat_args["use_plain_text_chat_history"] = use_plain_text_chat_history; - std::ostringstream message; std::list<LLSD>::const_reverse_iterator iter = messages.rbegin(); std::list<LLSD>::const_reverse_iterator iter_end = messages.rend(); @@ -682,7 +866,8 @@ void LLIMFloater::updateMessages() chat.mText = message; } - mChatHistory->appendMessage(chat, chat_args); + // Add the message to the chat log + appendMessage(chat); mLastMessageIndex = msg["index"].asInteger(); // if it is a notification - next message is a notification history log, so skip it @@ -706,6 +891,7 @@ void LLIMFloater::reloadMessages() mChatHistory->clear(); mLastMessageIndex = -1; updateMessages(); + mInputEditor->setFont(LLViewerChat::getChatFont()); } // static @@ -732,19 +918,13 @@ void LLIMFloater::onInputEditorFocusLost(LLFocusableElement* caller, void* userd } // static -void LLIMFloater::onInputEditorKeystroke(LLLineEditor* caller, void* userdata) +void LLIMFloater::onInputEditorKeystroke(LLTextEditor* caller, void* userdata) { LLIMFloater* self = (LLIMFloater*)userdata; std::string text = self->mInputEditor->getText(); - if (!text.empty()) - { - self->setTyping(true); - } - else - { + // Deleting all text counts as stopping typing. - self->setTyping(false); - } + self->setTyping(!text.empty()); } void LLIMFloater::setTyping(bool typing) @@ -769,27 +949,24 @@ void LLIMFloater::setTyping(bool typing) // much network traffic. Only send in person-to-person IMs. if ( mShouldSendTypingState && mDialog == IM_NOTHING_SPECIAL ) { - if ( mMeTyping ) + // Still typing, send 'start typing' notification or + // send 'stop typing' notification immediately + if (!mMeTyping || mTypingTimer.getElapsedTimeF32() > 1.f) { - if ( mTypingTimer.getElapsedTimeF32() > 1.f ) - { - // Still typing, send 'start typing' notification - LLIMModel::instance().sendTypingState(mSessionID, mOtherParticipantUUID, TRUE); - mShouldSendTypingState = false; - } + LLIMModel::instance().sendTypingState(mSessionID, + mOtherParticipantUUID, mMeTyping); + mShouldSendTypingState = false; } - else + } + + if (!mIsNearbyChat) + { + LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); + if (speaker_mgr) { - // Send 'stop typing' notification immediately - LLIMModel::instance().sendTypingState(mSessionID, mOtherParticipantUUID, FALSE); - mShouldSendTypingState = false; + speaker_mgr->setSpeakerTyping(gAgent.getID(), FALSE); } } - - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); - if (speaker_mgr) - speaker_mgr->setSpeakerTyping(gAgent.getID(), FALSE); - } void LLIMFloater::processIMTyping(const LLIMInfo* im_info, BOOL typing) @@ -808,55 +985,66 @@ void LLIMFloater::processIMTyping(const LLIMInfo* im_info, BOOL typing) void LLIMFloater::processAgentListUpdates(const LLSD& body) { - if ( !body.isMap() ) return; + uuid_vec_t joined_uuids; - if ( body.has("agent_updates") && body["agent_updates"].isMap() ) + if (body.isMap() && body.has("agent_updates") && body["agent_updates"].isMap()) { - LLSD agent_data = body["agent_updates"].get(gAgentID.asString()); - if (agent_data.isMap() && agent_data.has("info")) + LLSD::map_const_iterator update_it; + for(update_it = body["agent_updates"].beginMap(); + update_it != body["agent_updates"].endMap(); + ++update_it) { - LLSD agent_info = agent_data["info"]; + LLUUID agent_id(update_it->first); + LLSD agent_data = update_it->second; - if (agent_info.has("mutes")) + if (agent_data.isMap()) { - BOOL moderator_muted_text = agent_info["mutes"]["text"].asBoolean(); - mInputEditor->setEnabled(!moderator_muted_text); - std::string label; - if (moderator_muted_text) - label = LLTrans::getString("IM_muted_text_label"); - else - label = LLTrans::getString("IM_to_label") + " " + LLIMModel::instance().getName(mSessionID); - mInputEditor->setLabel(label); + // store the new participants in joined_uuids + if (agent_data.has("transition") && agent_data["transition"].asString() == "ENTER") + { + joined_uuids.push_back(agent_id); + } - if (moderator_muted_text) - LLNotificationsUtil::add("TextChatIsMutedByModerator"); + // process the moderator mutes + if (agent_id == gAgentID && agent_data.has("info") && agent_data["info"].has("mutes")) + { + BOOL moderator_muted_text = agent_data["info"]["mutes"]["text"].asBoolean(); + mInputEditor->setEnabled(!moderator_muted_text); + std::string label; + if (moderator_muted_text) + label = LLTrans::getString("IM_muted_text_label"); + else + label = LLTrans::getString("IM_to_label") + " " + LLIMModel::instance().getName(mSessionID); + mInputEditor->setLabel(label); + + if (moderator_muted_text) + LLNotificationsUtil::add("TextChatIsMutedByModerator"); + } } } } -} -void LLIMFloater::updateChatHistoryStyle() -{ - mChatHistory->clear(); - mLastMessageIndex = -1; - updateMessages(); -} + // the vectors need to be sorted for computing the intersection and difference + std::sort(mInvitedParticipants.begin(), mInvitedParticipants.end()); + std::sort(joined_uuids.begin(), joined_uuids.end()); -void LLIMFloater::processChatHistoryStyleUpdate(const LLSD& newvalue) -{ - LLFontGL* font = LLViewerChat::getChatFont(); - LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel"); - for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); - iter != inst_list.end(); ++iter) + uuid_vec_t intersection; // uuids of invited residents who have joined the conversation + std::set_intersection(mInvitedParticipants.begin(), mInvitedParticipants.end(), + joined_uuids.begin(), joined_uuids.end(), + std::back_inserter(intersection)); + + if (intersection.size() > 0) { - LLIMFloater* floater = dynamic_cast<LLIMFloater*>(*iter); - if (floater) - { - floater->updateChatHistoryStyle(); - floater->mInputEditor->setFont(font); - } + sendParticipantsAddedNotification(intersection); } + // Remove all joined participants from invited array. + // The difference between the two vectors (the elements in mInvitedParticipants which are not in joined_uuids) + // is placed at the beginning of mInvitedParticipants, then all other elements are erased. + mInvitedParticipants.erase(std::set_difference(mInvitedParticipants.begin(), mInvitedParticipants.end(), + joined_uuids.begin(), joined_uuids.end(), + mInvitedParticipants.begin()), + mInvitedParticipants.end()); } void LLIMFloater::processSessionUpdate(const LLSD& session_update) @@ -870,7 +1058,8 @@ void LLIMFloater::processSessionUpdate(const LLSD& session_update) if (voice_moderated) { - setTitle(session_label + std::string(" ") + LLTrans::getString("IM_moderated_chat_label")); + setTitle(session_label + std::string(" ") + + LLTrans::getString("IM_moderated_chat_label")); } else { @@ -883,98 +1072,56 @@ void LLIMFloater::processSessionUpdate(const LLSD& session_update) } } -BOOL LLIMFloater::handleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, EDragAndDropType cargo_type, - void *cargo_data, EAcceptance *accept, +// virtual +BOOL LLIMFloater::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, std::string& tooltip_msg) { - - if (mDialog == IM_NOTHING_SPECIAL) - { - LLToolDragAndDrop::handleGiveDragAndDrop(mOtherParticipantUUID, mSessionID, drop, - cargo_type, cargo_data, accept); - } - - // handle case for dropping calling cards (and folders of calling cards) onto invitation panel for invites - else if (isInviteAllowed()) + if (cargo_type == DAD_PERSON) { - *accept = ACCEPT_NO; - - if (cargo_type == DAD_CALLINGCARD) + if (dropPerson(static_cast<LLUUID*>(cargo_data), drop)) { - if (dropCallingCard((LLInventoryItem*)cargo_data, drop)) - { - *accept = ACCEPT_YES_MULTI; - } + *accept = ACCEPT_YES_MULTI; } - else if (cargo_type == DAD_CATEGORY) + else { - if (dropCategory((LLInventoryCategory*)cargo_data, drop)) - { - *accept = ACCEPT_YES_MULTI; - } + *accept = ACCEPT_NO; } } + else if (mDialog == IM_NOTHING_SPECIAL) + { + LLToolDragAndDrop::handleGiveDragAndDrop(mOtherParticipantUUID, mSessionID, drop, + cargo_type, cargo_data, accept); + } + return TRUE; } -BOOL LLIMFloater::dropCallingCard(LLInventoryItem* item, BOOL drop) +bool LLIMFloater::dropPerson(LLUUID* person_id, bool drop) { - BOOL rv = isInviteAllowed(); - if(rv && item && item->getCreatorUUID().notNull()) - { - if(drop) - { - uuid_vec_t ids; - ids.push_back(item->getCreatorUUID()); - inviteToSession(ids); - } - } - else + bool res = person_id && person_id->notNull(); + if(res) { - // set to false if creator uuid is null. - rv = FALSE; - } - return rv; -} + uuid_vec_t ids; + ids.push_back(*person_id); -BOOL LLIMFloater::dropCategory(LLInventoryCategory* category, BOOL drop) -{ - BOOL rv = isInviteAllowed(); - if(rv && category) - { - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - LLUniqueBuddyCollector buddies; - gInventory.collectDescendentsIf(category->getUUID(), - cats, - items, - LLInventoryModel::EXCLUDE_TRASH, - buddies); - S32 count = items.count(); - if(count == 0) + res = canAddSelectedToChat(ids); + if(res && drop) { - rv = FALSE; - } - else if(drop) - { - uuid_vec_t ids; - ids.reserve(count); - for(S32 i = 0; i < count; ++i) - { - ids.push_back(items.get(i)->getCreatorUUID()); - } - inviteToSession(ids); + addSessionParticipants(ids); } } - return rv; + + return res; } BOOL LLIMFloater::isInviteAllowed() const { - return ( (IM_SESSION_CONFERENCE_START == mDialog) - || (IM_SESSION_INVITE == mDialog) ); + || (IM_SESSION_INVITE == mDialog && !gAgent.isInGroup(mSessionID)) + || mIsP2PChat); } class LLSessionInviteResponder : public LLHTTPClient::Responder @@ -998,45 +1145,39 @@ private: BOOL LLIMFloater::inviteToSession(const uuid_vec_t& ids) { LLViewerRegion* region = gAgent.getRegion(); - if (!region) - { - return FALSE; - } - - S32 count = ids.size(); + bool is_region_exist = region != NULL; - if( isInviteAllowed() && (count > 0) ) + if (is_region_exist) { - llinfos << "LLIMFloater::inviteToSession() - inviting participants" << llendl; + S32 count = ids.size(); - std::string url = region->getCapability("ChatSessionRequest"); + if( isInviteAllowed() && (count > 0) ) + { + llinfos << "LLIMFloater::inviteToSession() - inviting participants" << llendl; - LLSD data; + std::string url = region->getCapability("ChatSessionRequest"); - data["params"] = LLSD::emptyArray(); - for (int i = 0; i < count; i++) + LLSD data; + data["params"] = LLSD::emptyArray(); + for (int i = 0; i < count; i++) + { + data["params"].append(ids[i]); + } + data["method"] = "invite"; + data["session-id"] = mSessionID; + LLHTTPClient::post(url, data,new LLSessionInviteResponder(mSessionID)); + } + else { - data["params"].append(ids[i]); + llinfos << "LLIMFloater::inviteToSession -" + << " no need to invite agents for " + << mDialog << llendl; + // successful add, because everyone that needed to get added + // was added. } - - data["method"] = "invite"; - data["session-id"] = mSessionID; - LLHTTPClient::post( - url, - data, - new LLSessionInviteResponder( - mSessionID)); - } - else - { - llinfos << "LLIMFloater::inviteToSession -" - << " no need to invite agents for " - << mDialog << llendl; - // successful add, because everyone that needed to get added - // was added. } - return TRUE; + return is_region_exist; } void LLIMFloater::addTypingIndicator(const LLIMInfo* im_info) @@ -1077,7 +1218,6 @@ void LLIMFloater::removeTypingIndicator(const LLIMInfo* im_info) speaker_mgr->setSpeakerTyping(im_info->mFromID, FALSE); } } - } } @@ -1094,7 +1234,8 @@ void LLIMFloater::closeHiddenIMToasts() } }; - LLNotificationsUI::LLScreenChannel* channel = LLNotificationsUI::LLChannelManager::getNotificationScreenChannel(); + LLNotificationsUI::LLScreenChannel* channel = + LLNotificationsUI::LLChannelManager::getNotificationScreenChannel(); if (channel != NULL) { channel->closeHiddenToasts(IMToastMatcher()); @@ -1107,7 +1248,7 @@ void LLIMFloater::confirmLeaveCallCallback(const LLSD& notification, const LLSD& const LLSD& payload = notification["payload"]; LLUUID session_id = payload["session_id"]; - LLFloater* im_floater = LLFloaterReg::findInstance("impanel", session_id); + LLFloater* im_floater = findInstance(session_id); if (option == 0 && im_floater != NULL) { im_floater->closeFloater(); @@ -1117,79 +1258,33 @@ void LLIMFloater::confirmLeaveCallCallback(const LLSD& notification, const LLSD& } // static -bool LLIMFloater::isChatMultiTab() -{ - // Restart is required in order to change chat window type. - static bool is_single_window = gSavedSettings.getS32("ChatWindow") == 1; - return is_single_window; -} - -// static -void LLIMFloater::initIMFloater() -{ - // This is called on viewer start up - // init chat window type before user changed it in preferences - isChatMultiTab(); -} - -//static void LLIMFloater::sRemoveTypingIndicator(const LLSD& data) { LLUUID session_id = data["session_id"]; - if (session_id.isNull()) return; + if (session_id.isNull()) + return; LLUUID from_id = data["from_id"]; - if (gAgentID == from_id || LLUUID::null == from_id) return; + if (gAgentID == from_id || LLUUID::null == from_id) + return; LLIMFloater* floater = LLIMFloater::findInstance(session_id); - if (!floater) return; + if (!floater) + return; - if (IM_NOTHING_SPECIAL != floater->mDialog) return; + if (IM_NOTHING_SPECIAL != floater->mDialog) + return; floater->removeTypingIndicator(); } +// static void LLIMFloater::onIMChicletCreated( const LLUUID& session_id ) { - - if (isChatMultiTab()) - { - LLIMFloaterContainer* im_box = LLIMFloaterContainer::getInstance(); - if (!im_box) return; - - if (LLIMFloater::findInstance(session_id)) return; - - LLIMFloater* new_tab = LLIMFloater::getInstance(session_id); - - im_box->addFloater(new_tab, FALSE, LLTabContainer::END); - } - + LLIMFloater::addToHost(session_id); } -void LLIMFloater::onClickCloseBtn() +boost::signals2::connection LLIMFloater::setIMFloaterShowedCallback(const floater_showed_signal_t::slot_type& cb) { - - LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession( - mSessionID); - - if (session == NULL) - { - llwarns << "Empty session." << llendl; - return; - } - - bool is_call_with_chat = session->isGroupSessionType() - || session->isAdHocSessionType() || session->isP2PSessionType(); - - LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID); - - if (is_call_with_chat && voice_channel != NULL && voice_channel->isActive()) - { - LLSD payload; - payload["session_id"] = mSessionID; - LLNotificationsUtil::add("ConfirmLeaveCall", LLSD(), payload, confirmLeaveCallCallback); - return; - } - - LLFloater::onClickCloseBtn(); + return LLIMFloater::sIMFloaterShowedSignal.connect(cb); } diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index f7cd35b5eb..6c69ed3462 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -27,41 +27,55 @@ #ifndef LL_IMFLOATER_H #define LL_IMFLOATER_H +#include "llimview.h" +#include "llimconversation.h" #include "llinstantmessage.h" #include "lllogchat.h" #include "lltooldraganddrop.h" -#include "lltransientdockablefloater.h" +#include "llvoicechannel.h" +#include "llvoiceclient.h" class LLAvatarName; -class LLLineEditor; +class LLButton; +class LLChatEntry; +class LLTextEditor; class LLPanelChatControlPanel; class LLChatHistory; class LLInventoryItem; class LLInventoryCategory; +typedef boost::signals2::signal<void(const LLUUID& session_id)> floater_showed_signal_t; + /** * Individual IM window that appears at the bottom of the screen, * optionally "docked" to the bottom tray. */ -class LLIMFloater : public LLTransientDockableFloater +class LLIMFloater + : public LLVoiceClientStatusObserver + , public LLIMConversation { LOG_CLASS(LLIMFloater); public: LLIMFloater(const LLUUID& session_id); virtual ~LLIMFloater(); - + + void initIMSession(const LLUUID& session_id); + void initIMFloater(); + // LLView overrides /*virtual*/ BOOL postBuild(); + /*virtual*/ void setFocus(BOOL focusFlag); /*virtual*/ void setVisible(BOOL visible); /*virtual*/ BOOL getVisible(); // Check typing timeout timer. - /*virtual*/ void draw(); + + static LLIMFloater* findInstance(const LLUUID& session_id); + static LLIMFloater* getInstance(const LLUUID& session_id); // LLFloater overrides /*virtual*/ void onClose(bool app_quitting); /*virtual*/ void setDocked(bool docked, bool pop_on_undock = true); - // Make IM conversion visible and update the message history static LLIMFloater* show(const LLUUID& session_id); @@ -69,17 +83,14 @@ public: // Returns true iff panel became visible static bool toggle(const LLUUID& session_id); - static LLIMFloater* findInstance(const LLUUID& session_id); - - static LLIMFloater* getInstance(const LLUUID& session_id); - void sessionInitReplyReceived(const LLUUID& im_session_id); // get new messages from LLIMModel void updateMessages(); void reloadMessages(); - static void onSendMsg( LLUICtrl*, void*); - void sendMsg(); + static void onSendMsg(LLUICtrl*, void*); + void sendMsgFromInputEditor(); + void sendMsg(const std::string& msg); // callback for LLIMModel on new messages // route to specific floater if it is visible @@ -89,62 +100,71 @@ public: void setPositioned(bool b) { mPositioned = b; }; void onVisibilityChange(const LLSD& new_visibility); + + // Implements LLVoiceClientStatusObserver::onChange() to enable the call + // button when voice is available + void onChange(EStatusType status, const std::string &channelURI, + bool proximal); + + virtual LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; } + virtual void onVoiceChannelStateChanged( + const LLVoiceChannel::EState& old_state, + const LLVoiceChannel::EState& new_state); + void processIMTyping(const LLIMInfo* im_info, BOOL typing); void processAgentListUpdates(const LLSD& body); void processSessionUpdate(const LLSD& session_update); - void updateChatHistoryStyle(); - static void processChatHistoryStyleUpdate(const LLSD& newvalue); - - BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, EDragAndDropType cargo_type, - void *cargo_data, EAcceptance *accept, - std::string& tooltip_msg); + /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg); - /** - * Returns true if chat is displayed in multi tabbed floater - * false if chat is displayed in multiple windows - */ - static bool isChatMultiTab(); - - static void initIMFloater(); //used as a callback on receiving new IM message static void sRemoveTypingIndicator(const LLSD& data); - static void onIMChicletCreated(const LLUUID& session_id); - virtual LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; } + bool getStartConferenceInSameFloater() const { return mStartConferenceInSameFloater; } + const LLUUID& getOtherParticipantUUID() {return mOtherParticipantUUID;} -protected: - /* virtual */ - void onClickCloseBtn(); + static boost::signals2::connection setIMFloaterShowedCallback(const floater_showed_signal_t::slot_type& cb); + static floater_showed_signal_t sIMFloaterShowedSignal; private: - // process focus events to set a currently active session - /* virtual */ void onFocusLost(); - /* virtual */ void onFocusReceived(); + + /*virtual*/ void refresh(); + + /*virtual*/ void onClickCloseBtn(); // Update the window title, input field help text, etc. void updateSessionName(const std::string& ui_title, const std::string& ui_label); - + // For display name lookups for IM window titles void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); - - BOOL dropCallingCard(LLInventoryItem* item, BOOL drop); - BOOL dropCategory(LLInventoryCategory* category, BOOL drop); + + /// Updates the list of ad hoc conference participants + /// in an IM floater title. + void onParticipantsListChanged(LLUICtrl* ctrl); + + bool dropPerson(LLUUID* person_id, bool drop); BOOL isInviteAllowed() const; BOOL inviteToSession(const uuid_vec_t& agent_ids); - - static void onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata ); - static void onInputEditorFocusLost(LLFocusableElement* caller, void* userdata); - static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata); - void setTyping(bool typing); - void onSlide(); - static void* createPanelIMControl(void* userdata); - static void* createPanelGroupControl(void* userdata); - static void* createPanelAdHocControl(void* userdata); + static void onInputEditorFocusReceived( LLFocusableElement* caller,void* userdata ); + static void onInputEditorFocusLost(LLFocusableElement* caller, void* userdata); + static void onInputEditorKeystroke(LLTextEditor* caller, void* userdata); + void setTyping(bool typing); + void onAddButtonClicked(); + void addSessionParticipants(const uuid_vec_t& uuids); + void addP2PSessionParticipants(const LLSD& notification, const LLSD& response, const uuid_vec_t& uuids); + void sendParticipantsAddedNotification(const uuid_vec_t& uuids); + bool canAddSelectedToChat(const uuid_vec_t& uuids); + + void onCallButtonClicked(); + + void boundVoiceChannel(); // Add the "User is typing..." indicator. void addTypingIndicator(const LLIMInfo* im_info); @@ -156,14 +176,10 @@ private: static void confirmLeaveCallCallback(const LLSD& notification, const LLSD& response); - LLPanelChatControlPanel* mControlPanel; - LLUUID mSessionID; S32 mLastMessageIndex; EInstantMessage mDialog; LLUUID mOtherParticipantUUID; - LLChatHistory* mChatHistory; - LLLineEditor* mInputEditor; bool mPositioned; std::string mSavedTitle; @@ -176,7 +192,15 @@ private: bool mSessionInitialized; LLSD mQueuedMsgsForInit; -}; + bool mStartConferenceInSameFloater; + + uuid_vec_t mInvitedParticipants; + + // connection to voice channel state change signal + boost::signals2::connection mVoiceChannelStateChangeConnection; + + boost::signals2::connection mParticipantsListRefreshConnection; +}; #endif // LL_IMFLOATER_H diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp index 0f0ae896a2..f85aa9a353 100644 --- a/indra/newview/llimfloatercontainer.cpp +++ b/indra/newview/llimfloatercontainer.cpp @@ -27,56 +27,208 @@ #include "llviewerprecompiledheaders.h" +#include "llimfloater.h" #include "llimfloatercontainer.h" + #include "llfloaterreg.h" -#include "llimview.h" +#include "lllayoutstack.h" +#include "llnearbychat.h" + +#include "llagent.h" +#include "llavataractions.h" #include "llavatariconctrl.h" +#include "llavatarnamecache.h" +#include "llcallbacklist.h" +#include "llgroupactions.h" #include "llgroupiconctrl.h" -#include "llagent.h" +#include "llfloateravatarpicker.h" +#include "llfloaterpreference.h" +#include "llimview.h" +#include "llnotificationsutil.h" #include "lltransientfloatermgr.h" +#include "llviewercontrol.h" +#include "llconversationview.h" +#include "llcallbacklist.h" +#include "llworld.h" +#include "llsdserialize.h" // // LLIMFloaterContainer // LLIMFloaterContainer::LLIMFloaterContainer(const LLSD& seed) -: LLMultiFloater(seed) +: LLMultiFloater(seed), + mExpandCollapseBtn(NULL), + mConversationsRoot(NULL), + mConversationsEventStream("ConversationsEvents"), + mInitialized(false) { + mEnableCallbackRegistrar.add("IMFloaterContainer.Check", boost::bind(&LLIMFloaterContainer::isActionChecked, this, _2)); + mCommitCallbackRegistrar.add("IMFloaterContainer.Action", boost::bind(&LLIMFloaterContainer::onCustomAction, this, _2)); + + mEnableCallbackRegistrar.add("Avatar.CheckItem", boost::bind(&LLIMFloaterContainer::checkContextMenuItem, this, _2)); + mEnableCallbackRegistrar.add("Avatar.EnableItem", boost::bind(&LLIMFloaterContainer::enableContextMenuItem, this, _2)); + mCommitCallbackRegistrar.add("Avatar.DoToSelected", boost::bind(&LLIMFloaterContainer::doToSelected, this, _2)); + + mCommitCallbackRegistrar.add("Group.DoToSelected", boost::bind(&LLIMFloaterContainer::doToSelectedGroup, this, _2)); + + // Firstly add our self to IMSession observers, so we catch session events + LLIMMgr::getInstance()->addSessionObserver(this); + mAutoResize = FALSE; LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this); } LLIMFloaterContainer::~LLIMFloaterContainer() { + mConversationsEventStream.stopListening("ConversationsRefresh"); + + gIdleCallbacks.deleteFunction(idle, this); + mNewMessageConnection.disconnect(); LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this); + + gSavedPerAccountSettings.setBOOL("ConversationsListPaneCollapsed", mConversationsPane->isCollapsed()); + gSavedPerAccountSettings.setBOOL("ConversationsMessagePaneCollapsed", mMessagesPane->isCollapsed()); + + if (!LLSingleton<LLIMMgr>::destroyed()) + { + LLIMMgr::getInstance()->removeSessionObserver(this); + } +} + +void LLIMFloaterContainer::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) +{ + LLIMConversation::addToHost(session_id); + addConversationListItem(session_id); +} + +void LLIMFloaterContainer::sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) +{ + selectConversation(session_id); +} + +void LLIMFloaterContainer::sessionVoiceOrIMStarted(const LLUUID& session_id) +{ + LLIMConversation::addToHost(session_id); + addConversationListItem(session_id); +} + +void LLIMFloaterContainer::sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) +{ + addConversationListItem(new_session_id, removeConversationListItem(old_session_id)); +} + +void LLIMFloaterContainer::sessionRemoved(const LLUUID& session_id) +{ + removeConversationListItem(session_id); } +// static +void LLIMFloaterContainer::onCurrentChannelChanged(const LLUUID& session_id) +{ + if (session_id != LLUUID::null) + { + LLIMFloaterContainer::getInstance()->showConversation(session_id); + } +} + + BOOL LLIMFloaterContainer::postBuild() { mNewMessageConnection = LLIMModel::instance().mNewMsgSignal.connect(boost::bind(&LLIMFloaterContainer::onNewMessageReceived, this, _1)); // Do not call base postBuild to not connect to mCloseSignal to not close all floaters via Close button // mTabContainer will be initialized in LLMultiFloater::addChild() + + setTabContainer(getChild<LLTabContainer>("im_box_tab_container")); + + mConversationsStack = getChild<LLLayoutStack>("conversations_stack"); + mConversationsPane = getChild<LLLayoutPanel>("conversations_layout_panel"); + mMessagesPane = getChild<LLLayoutPanel>("messages_layout_panel"); + + mConversationsListPanel = getChild<LLPanel>("conversations_list_panel"); + + // Open IM session with selected participant on double click event + mConversationsListPanel->setDoubleClickCallback(boost::bind(&LLIMFloaterContainer::doToSelected, this, LLSD("im"))); + + // Create the root model and view for all conversation sessions + LLConversationItem* base_item = new LLConversationItem(getRootViewModel()); + + LLFolderView::Params p(LLUICtrlFactory::getDefaultParams<LLFolderView>()); + p.name = getName(); + p.title = getLabel(); + p.rect = LLRect(0, 0, getRect().getWidth(), 0); + p.parent_panel = mConversationsListPanel; + p.tool_tip = p.name; + p.listener = base_item; + p.view_model = &mConversationViewModel; + p.root = NULL; + p.use_ellipses = true; + p.options_menu = "menu_conversation.xml"; + mConversationsRoot = LLUICtrlFactory::create<LLFolderView>(p); + mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar); + + // Add listener to conversation model events + mConversationsEventStream.listen("ConversationsRefresh", boost::bind(&LLIMFloaterContainer::onConversationModelEvent, this, _1)); + + // a scroller for folder view + LLRect scroller_view_rect = mConversationsListPanel->getRect(); + scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); + LLScrollContainer::Params scroller_params(LLUICtrlFactory::getDefaultParams<LLFolderViewScrollContainer>()); + scroller_params.rect(scroller_view_rect); + + LLScrollContainer* scroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params); + scroller->setFollowsAll(); + mConversationsListPanel->addChild(scroller); + scroller->addChild(mConversationsRoot); + mConversationsRoot->setScrollContainer(scroller); + mConversationsRoot->setFollowsAll(); + mConversationsRoot->addChild(mConversationsRoot->mStatusTextBox); + + addConversationListItem(LLUUID()); // manually add nearby chat + + mExpandCollapseBtn = getChild<LLButton>("expand_collapse_btn"); + mExpandCollapseBtn->setClickedCallback(boost::bind(&LLIMFloaterContainer::onExpandCollapseButtonClicked, this)); + + childSetAction("add_btn", boost::bind(&LLIMFloaterContainer::onAddButtonClicked, this)); + + collapseMessagesPane(gSavedPerAccountSettings.getBOOL("ConversationsMessagePaneCollapsed")); + collapseConversationsPane(gSavedPerAccountSettings.getBOOL("ConversationsListPaneCollapsed")); + LLAvatarNameCache::addUseDisplayNamesCallback(boost::bind(&LLIMConversation::processChatHistoryStyleUpdate)); + + if (! mMessagesPane->isCollapsed()) + { + S32 list_width = gSavedPerAccountSettings.getS32("ConversationsListPaneWidth"); + LLRect list_size = mConversationsPane->getRect(); + S32 left_pad = mConversationsListPanel->getRect().mLeft; + list_size.mRight = list_size.mLeft + list_width - left_pad; + + mConversationsPane->handleReshape(list_size, TRUE); + } + + // Init the sort order now that the root had been created + setSortOrder(LLConversationSort(gSavedSettings.getU32("ConversationSortOrder"))); + + mInitialized = true; + + // Add callbacks: + // We'll take care of view updates on idle + gIdleCallbacks.addFunction(idle, this); + // When display name option change, we need to reload all participant names + LLAvatarNameCache::addUseDisplayNamesCallback(boost::bind(&LLIMFloaterContainer::processParticipantsStyleUpdate, this)); + return TRUE; } void LLIMFloaterContainer::onOpen(const LLSD& key) { LLMultiFloater::onOpen(key); -/* - if (key.isDefined()) - { - LLIMFloater* im_floater = LLIMFloater::findInstance(key.asUUID()); - if (im_floater) - { - im_floater->openFloater(); - } - } -*/ + openNearbyChat(); } -void LLIMFloaterContainer::addFloater(LLFloater* floaterp, - BOOL select_added_floater, - LLTabContainer::eInsertionPoint insertion_point) +// virtual +void LLIMFloaterContainer::addFloater(LLFloater* floaterp, + BOOL select_added_floater, + LLTabContainer::eInsertionPoint insertion_point) { if(!floaterp) return; @@ -86,11 +238,15 @@ void LLIMFloaterContainer::addFloater(LLFloater* floaterp, openFloater(floaterp->getKey()); return; } + + // Make sure the message panel is open when adding a floater or it stays mysteriously hidden + collapseMessagesPane(false); + // Add the floater LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point); LLUUID session_id = floaterp->getKey(); - + LLIconCtrl* icon = 0; if(gAgent.isInGroup(session_id, TRUE)) @@ -103,8 +259,8 @@ void LLIMFloaterContainer::addFloater(LLFloater* floaterp, floaterp->mCloseSignal.connect(boost::bind(&LLIMFloaterContainer::onCloseFloater, this, session_id)); } else - { - LLUUID avatar_id = LLIMModel::getInstance()->getOtherParticipantID(session_id); + { LLUUID avatar_id = session_id.notNull()? + LLIMModel::getInstance()->getOtherParticipantID(session_id) : LLUUID(); LLAvatarIconCtrl::Params icon_params; icon_params.avatar_id = avatar_id; @@ -113,15 +269,41 @@ void LLIMFloaterContainer::addFloater(LLFloater* floaterp, mSessions[session_id] = floaterp; floaterp->mCloseSignal.connect(boost::bind(&LLIMFloaterContainer::onCloseFloater, this, session_id)); } + + // forced resize of the floater + LLRect wrapper_rect = this->mTabContainer->getLocalRect(); + floaterp->setRect(wrapper_rect); + mTabContainer->setTabImage(floaterp, icon); } + void LLIMFloaterContainer::onCloseFloater(LLUUID& id) { mSessions.erase(id); setFocus(TRUE); } +// virtual +void LLIMFloaterContainer::computeResizeLimits(S32& new_min_width, S32& new_min_height) +{ + // possibly increase floater's minimum height according to children's minimums + for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) + { + LLFloater* floaterp = dynamic_cast<LLFloater*>(mTabContainer->getPanelByIndex(tab_idx)); + if (floaterp) + { + new_min_height = llmax(new_min_height, floaterp->getMinHeight()); + } + } + + S32 conversations_pane_min_dim = mConversationsPane->getRelevantMinDim(); + S32 messages_pane_min_dim = mMessagesPane->getRelevantMinDim(); + + // set floater's minimum width according to relevant minimal children's dimensionals + new_min_width = conversations_pane_min_dim + messages_pane_min_dim + LLPANEL_BORDER_WIDTH*2; +} + void LLIMFloaterContainer::onNewMessageReceived(const LLSD& data) { LLUUID session_id = data["session_id"].asUUID(); @@ -136,6 +318,21 @@ void LLIMFloaterContainer::onNewMessageReceived(const LLSD& data) } } +void LLIMFloaterContainer::onExpandCollapseButtonClicked() +{ + if (mConversationsPane->isCollapsed() && mMessagesPane->isCollapsed() + && gSavedPerAccountSettings.getBOOL("ConversationsExpandMessagePaneFirst")) + { + // Expand the messages pane from ultra minimized state + // if it was collapsed last in order. + collapseMessagesPane(false); + } + else + { + collapseConversationsPane(!mConversationsPane->isCollapsed()); + } +} + LLIMFloaterContainer* LLIMFloaterContainer::findInstance() { return LLFloaterReg::findTypedInstance<LLIMFloaterContainer>("im_container"); @@ -146,19 +343,1135 @@ LLIMFloaterContainer* LLIMFloaterContainer::getInstance() return LLFloaterReg::getTypedInstance<LLIMFloaterContainer>("im_container"); } -void LLIMFloaterContainer::setMinimized(BOOL b) +// Update all participants in the conversation lists +void LLIMFloaterContainer::processParticipantsStyleUpdate() +{ + // On each session in mConversationsItems + for (conversations_items_map::iterator it_session = mConversationsItems.begin(); it_session != mConversationsItems.end(); it_session++) + { + // Get the current session descriptors + LLConversationItem* session_model = it_session->second; + // Iterate through each model participant child + LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = session_model->getChildrenBegin(); + LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = session_model->getChildrenEnd(); + while (current_participant_model != end_participant_model) + { + LLConversationItemParticipant* participant_model = dynamic_cast<LLConversationItemParticipant*>(*current_participant_model); + // Get the avatar name for this participant id from the cache and update the model + participant_model->fetchAvatarName(); + // Next participant + current_participant_model++; + } + } +} + +// static +void LLIMFloaterContainer::idle(void* user_data) +{ + LLIMFloaterContainer* self = static_cast<LLIMFloaterContainer*>(user_data); + + // Update the distance to agent in the nearby chat session if required + // Note: it makes no sense of course to update the distance in other session + if (self->mConversationViewModel.getSorter().getSortOrderParticipants() == LLConversationFilter::SO_DISTANCE) + { + self->setNearbyDistances(); + } + self->mConversationsRoot->update(); +} + +bool LLIMFloaterContainer::onConversationModelEvent(const LLSD& event) +{ + // For debug only + //std::ostringstream llsd_value; + //llsd_value << LLSDOStreamer<LLSDNotationFormatter>(event) << std::endl; + //llinfos << "Merov debug : onConversationModelEvent, event = " << llsd_value.str() << llendl; + // end debug + + // Note: In conversations, the model is not responsible for creating the view, which is a good thing. This means that + // the model could change substantially and the view could echo only a portion of this model (though currently the + // conversation view does echo the conversation model 1 to 1). + // Consequently, the participant views need to be created either by the session view or by the container panel. + // For the moment, we create them here, at the container level, to conform to the pattern implemented in llinventorypanel.cpp + // (see LLInventoryPanel::buildNewViews()). + + std::string type = event.get("type").asString(); + LLUUID session_id = event.get("session_uuid").asUUID(); + LLUUID participant_id = event.get("participant_uuid").asUUID(); + + LLConversationViewSession* session_view = dynamic_cast<LLConversationViewSession*>(mConversationsWidgets[session_id]); + if (!session_view) + { + // We skip events that are not associated to a session + return false; + } + LLConversationViewParticipant* participant_view = session_view->findParticipant(participant_id); + + if (type == "remove_participant") + { + if (participant_view) + { + session_view->extractItem(participant_view); + delete participant_view; + session_view->refresh(); + mConversationsRoot->arrangeAll(); + } + } + else if (type == "add_participant") + { + if (!participant_view) + { + LLConversationItemSession* session_model = dynamic_cast<LLConversationItemSession*>(mConversationsItems[session_id]); + if (session_model) + { + LLConversationItemParticipant* participant_model = session_model->findParticipant(participant_id); + if (participant_model) + { + participant_view = createConversationViewParticipant(participant_model); + participant_view->addToFolder(session_view); + participant_view->setVisible(TRUE); + } + } + + } + } + else if (type == "update_participant") + { + if (participant_view) + { + participant_view->refresh(); + } + } + else if (type == "update_session") + { + session_view->refresh(); + } + + mConversationViewModel.requestSortAll(); + mConversationsRoot->arrangeAll(); + + return false; +} + +void LLIMFloaterContainer::draw() +{ + if (mTabContainer->getTabCount() == 0) + { + // Do not close the container when every conversation is torn off because the user + // still needs the conversation list. Simply collapse the message pane in that case. + collapseMessagesPane(true); + } + LLFloater::draw(); +} + +void LLIMFloaterContainer::tabClose() +{ + if (mTabContainer->getTabCount() == 0) + { + // Do not close the container when every conversation is torn off because the user + // still needs the conversation list. Simply collapse the message pane in that case. + collapseMessagesPane(true); + } +} + +void LLIMFloaterContainer::setVisible(BOOL visible) +{ LLNearbyChat* nearby_chat; + if (visible) + { + // Make sure we have the Nearby Chat present when showing the conversation container + nearby_chat = LLFloaterReg::findTypedInstance<LLNearbyChat>("nearby_chat"); + if (nearby_chat == NULL) + { + // If not found, force the creation of the nearby chat conversation panel + // *TODO: find a way to move this to XML as a default panel or something like that + LLSD name("nearby_chat"); + LLFloaterReg::toggleInstanceOrBringToFront(name); + } + openNearbyChat(); + } + + nearby_chat = LLFloaterReg::findTypedInstance<LLNearbyChat>("nearby_chat"); + if (nearby_chat) + { + LLIMConversation::addToHost(LLUUID()); + } + + // We need to show/hide all the associated conversations that have been torn off + // (and therefore, are not longer managed by the multifloater), + // so that they show/hide with the conversations manager. + conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin(); + for (;widget_it != mConversationsWidgets.end(); ++widget_it) + { + LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(widget_it->second); + if (widget) + { + widget->setVisibleIfDetached(visible); + } + } + + // Now, do the normal multifloater show/hide + LLMultiFloater::setVisible(visible); + +} + +void LLIMFloaterContainer::collapseMessagesPane(bool collapse) +{ + if (mMessagesPane->isCollapsed() == collapse) + { + return; + } + + if (collapse) + { + // Save the messages pane width before collapsing it. + gSavedPerAccountSettings.setS32("ConversationsMessagePaneWidth", mMessagesPane->getRect().getWidth()); + + // Save the order in which the panels are closed to reverse user's last action. + gSavedPerAccountSettings.setBOOL("ConversationsExpandMessagePaneFirst", mConversationsPane->isCollapsed()); + } + + // Save left pane rectangle before collapsing/expanding right pane. + LLRect prevRect = mConversationsPane->getRect(); + + // Show/hide the messages pane. + mConversationsStack->collapsePanel(mMessagesPane, collapse); + + if (!collapse) + { + // Make sure layout is updated before resizing conversation pane. + mConversationsStack->updateLayout(); + } + + updateState(collapse, gSavedPerAccountSettings.getS32("ConversationsMessagePaneWidth")); + if (!collapse) + { + // Restore conversation's pane previous width after expanding messages pane. + mConversationsPane->setTargetDim(prevRect.getWidth()); + } +} +void LLIMFloaterContainer::collapseConversationsPane(bool collapse) +{ + if (mConversationsPane->isCollapsed() == collapse) + { + return; + } + + LLView* button_panel = getChild<LLView>("conversations_pane_buttons_expanded"); + button_panel->setVisible(!collapse); + mExpandCollapseBtn->setImageOverlay(getString(collapse ? "expand_icon" : "collapse_icon")); + + if (collapse) + { + // Save the conversations pane width before collapsing it. + gSavedPerAccountSettings.setS32("ConversationsListPaneWidth", mConversationsPane->getRect().getWidth()); + + // Save the order in which the panels are closed to reverse user's last action. + gSavedPerAccountSettings.setBOOL("ConversationsExpandMessagePaneFirst", !mMessagesPane->isCollapsed()); + } + + mConversationsStack->collapsePanel(mConversationsPane, collapse); + + S32 collapsed_width = mConversationsPane->getMinDim(); + updateState(collapse, gSavedPerAccountSettings.getS32("ConversationsListPaneWidth") - collapsed_width); + + for (conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin(); + widget_it != mConversationsWidgets.end(); ++widget_it) + { + LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(widget_it->second); + if (widget) + { + widget->toggleMinimizedMode(collapse); + + // force closing all open conversations when collapsing to minimized state + if (collapse) + { + widget->setOpen(false); + } +} + } +} + +void LLIMFloaterContainer::updateState(bool collapse, S32 delta_width) +{ + LLRect floater_rect = getRect(); + floater_rect.mRight += ((collapse ? -1 : 1) * delta_width); + + // Set by_user = true so that reshaped rect is saved in user_settings. + setShape(floater_rect, true); + + updateResizeLimits(); + + bool is_left_pane_expanded = !mConversationsPane->isCollapsed(); + bool is_right_pane_expanded = !mMessagesPane->isCollapsed(); + + setCanResize(is_left_pane_expanded || is_right_pane_expanded); + setCanMinimize(is_left_pane_expanded || is_right_pane_expanded); + + // force set correct size for the title after show/hide minimize button + LLRect cur_rect = getRect(); + LLRect force_rect = cur_rect; + force_rect.mRight = cur_rect.mRight + 1; + setRect(force_rect); + setRect(cur_rect); + + // restore floater's resize limits (prevent collapse when left panel is expanded) + if (is_left_pane_expanded && !is_right_pane_expanded) + { + S32 expanded_min_size = mConversationsPane->getExpandedMinDim(); + setResizeLimits(expanded_min_size, expanded_min_size); + } + +} + +void LLIMFloaterContainer::onAddButtonClicked() +{ + LLView * button = findChild<LLView>("conversations_pane_buttons_expanded")->findChild<LLButton>("add_btn"); + LLFloater* root_floater = gFloaterView->getParentFloater(this); + LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLIMFloaterContainer::onAvatarPicked, this, _1), TRUE, TRUE, TRUE, root_floater->getName(), button); + + if (picker && root_floater) + { + root_floater->addDependentFloater(picker); + } +} + +void LLIMFloaterContainer::onAvatarPicked(const uuid_vec_t& ids) +{ + if (ids.size() == 1) + { + LLAvatarActions::startIM(ids.back()); + } + else + { + LLAvatarActions::startConference(ids); + } +} + +void LLIMFloaterContainer::onCustomAction(const LLSD& userdata) +{ + std::string command = userdata.asString(); + + if ("sort_sessions_by_type" == command) + { + setSortOrderSessions(LLConversationFilter::SO_SESSION_TYPE); + } + if ("sort_sessions_by_name" == command) + { + setSortOrderSessions(LLConversationFilter::SO_NAME); + } + if ("sort_sessions_by_recent" == command) + { + setSortOrderSessions(LLConversationFilter::SO_DATE); + } + if ("sort_participants_by_name" == command) + { + setSortOrderParticipants(LLConversationFilter::SO_NAME); + } + if ("sort_participants_by_recent" == command) + { + setSortOrderParticipants(LLConversationFilter::SO_DATE); + } + if ("sort_participants_by_distance" == command) + { + setSortOrderParticipants(LLConversationFilter::SO_DISTANCE); + } + if ("chat_preferences" == command) + { + LLFloaterPreference* floater_prefs = LLFloaterReg::showTypedInstance<LLFloaterPreference>("preferences"); + if (floater_prefs) + { + LLTabContainer* tab_container = floater_prefs->getChild<LLTabContainer>("pref core"); + LLPanel* chat_panel = tab_container->getPanelByName("chat"); + if (tab_container && chat_panel) + { + tab_container->selectTabPanel(chat_panel); + } + } + } +} + +BOOL LLIMFloaterContainer::isActionChecked(const LLSD& userdata) { - if (isMinimized() == b) return; + LLConversationSort order = mConversationViewModel.getSorter(); + std::string command = userdata.asString(); + if ("sort_sessions_by_type" == command) + { + return (order.getSortOrderSessions() == LLConversationFilter::SO_SESSION_TYPE); + } + if ("sort_sessions_by_name" == command) + { + return (order.getSortOrderSessions() == LLConversationFilter::SO_NAME); + } + if ("sort_sessions_by_recent" == command) + { + return (order.getSortOrderSessions() == LLConversationFilter::SO_DATE); + } + if ("sort_participants_by_name" == command) + { + return (order.getSortOrderParticipants() == LLConversationFilter::SO_NAME); + } + if ("sort_participants_by_recent" == command) + { + return (order.getSortOrderParticipants() == LLConversationFilter::SO_DATE); + } + if ("sort_participants_by_distance" == command) + { + return (order.getSortOrderParticipants() == LLConversationFilter::SO_DISTANCE); + } - LLMultiFloater::setMinimized(b); - // Hide minimized floater (see EXT-5315) - setVisible(!b); + return FALSE; +} - if (isMinimized()) return; +void LLIMFloaterContainer::setSortOrderSessions(const LLConversationFilter::ESortOrderType order) +{ + LLConversationSort old_order = mConversationViewModel.getSorter(); + if (order != old_order.getSortOrderSessions()) + { + old_order.setSortOrderSessions(order); + setSortOrder(old_order); + } +} - if (getActiveFloater()) +void LLIMFloaterContainer::setSortOrderParticipants(const LLConversationFilter::ESortOrderType order) +{ + LLConversationSort old_order = mConversationViewModel.getSorter(); + if (order != old_order.getSortOrderParticipants()) { - getActiveFloater()->setVisible(TRUE); + old_order.setSortOrderParticipants(order); + setSortOrder(old_order); + } +} + +void LLIMFloaterContainer::setSortOrder(const LLConversationSort& order) +{ + mConversationViewModel.setSorter(order); + mConversationsRoot->arrangeAll(); + // try to keep selection onscreen, even if it wasn't to start with + mConversationsRoot->scrollToShowSelection(); + gSavedSettings.setU32("ConversationSortOrder", (U32)order); +} + +void LLIMFloaterContainer::getSelectedUUIDs(uuid_vec_t& selected_uuids) +{ + const std::set<LLFolderViewItem*> selectedItems = mConversationsRoot->getSelectionList(); + + std::set<LLFolderViewItem*>::const_iterator it = selectedItems.begin(); + const std::set<LLFolderViewItem*>::const_iterator it_end = selectedItems.end(); + LLConversationItem * conversationItem; + + for (; it != it_end; ++it) + { + conversationItem = static_cast<LLConversationItem *>((*it)->getViewModelItem()); + selected_uuids.push_back(conversationItem->getUUID()); + } +} + +const LLConversationItem * LLIMFloaterContainer::getCurSelectedViewModelItem() +{ + LLConversationItem * conversationItem = NULL; + + if(mConversationsRoot && + mConversationsRoot->getCurSelectedItem() && + mConversationsRoot->getCurSelectedItem()->getViewModelItem()) + { + conversationItem = static_cast<LLConversationItem *>(mConversationsRoot->getCurSelectedItem()->getViewModelItem()); + } + + return conversationItem; +} + +void LLIMFloaterContainer::getParticipantUUIDs(uuid_vec_t& selected_uuids) +{ + //Find the conversation floater associated with the selected id + const LLConversationItem * conversationItem = getCurSelectedViewModelItem(); + + if(conversationItem->getType() == LLConversationItem::CONV_PARTICIPANT) + { + getSelectedUUIDs(selected_uuids); + } + //When a one-on-one conversation exists, retrieve the participant id from the conversation floater + else if(conversationItem->getType() == LLConversationItem::CONV_SESSION_1_ON_1) + { + LLIMFloater *conversationFloater = LLIMFloater::findInstance(conversationItem->getUUID()); + LLUUID participantID = conversationFloater->getOtherParticipantUUID(); + selected_uuids.push_back(participantID); + } +} + +void LLIMFloaterContainer::doToParticipants(const std::string& command, uuid_vec_t& selectedIDS) +{ + if(selectedIDS.size() > 0) + { + const LLUUID& userID = selectedIDS.front(); + if(gAgent.getID() != userID) + { + if ("view_profile" == command) + { + LLAvatarActions::showProfile(userID); + } + else if("im" == command) + { + LLAvatarActions::startIM(userID); + } + else if("offer_teleport" == command) + { + LLAvatarActions::offerTeleport(selectedIDS); + } + else if("voice_call" == command) + { + LLAvatarActions::startCall(userID); + } + else if("chat_history" == command) + { + LLAvatarActions::viewChatHistory(userID); + } + else if("add_friend" == command) + { + LLAvatarActions::requestFriendshipDialog(userID); + } + else if("remove_friend" == command) + { + LLAvatarActions::removeFriendDialog(userID); + } + else if("invite_to_group" == command) + { + LLAvatarActions::inviteToGroup(userID); + } + else if("map" == command) + { + LLAvatarActions::showOnMap(userID); + } + else if("share" == command) + { + LLAvatarActions::share(userID); + } + else if("pay" == command) + { + LLAvatarActions::pay(userID); + } + else if("block_unblock" == command) + { + LLAvatarActions::toggleBlock(userID); + } + else if("selected" == command || "mute_all" == command || "unmute_all" == command) + { + moderateVoice(command, userID); + } + else if ("toggle_allow_text_chat" == command) + { + toggleAllowTextChat(userID); + } + } + } +} + +void LLIMFloaterContainer::doToSelectedConversation(const std::string& command, uuid_vec_t& selectedIDS) +{ + //Find the conversation floater associated with the selected id + const LLConversationItem * conversationItem = getCurSelectedViewModelItem(); + LLIMFloater *conversationFloater = LLIMFloater::findInstance(conversationItem->getUUID()); + + if(conversationFloater) + { + //Close the selected conversation + if("close_conversation" == command) + { + LLFloater::onClickClose(conversationFloater); + } + else if("open_voice_conversation" == command) + { + gIMMgr->startCall(conversationItem->getUUID()); + } + else if("disconnect_from_voice" == command) + { + gIMMgr->endCall(conversationItem->getUUID()); + } + else if("chat_history" == command) + { + const LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(conversationItem->getUUID()); + + if (NULL != session) + { + const LLUUID session_id = session->isOutgoingAdHoc() ? session->generateOutgouigAdHocHash() : session->mSessionID; + LLFloaterReg::showInstance("preview_conversation", session_id, true); + } + } + else + { + doToParticipants(command, selectedIDS); + } + } +} + +void LLIMFloaterContainer::doToSelected(const LLSD& userdata) +{ + std::string command = userdata.asString(); + const LLConversationItem * conversationItem = getCurSelectedViewModelItem(); + uuid_vec_t selected_uuids; + + if(conversationItem != NULL) + { + getParticipantUUIDs(selected_uuids); + + if(conversationItem->getType() == LLConversationItem::CONV_PARTICIPANT) + { + doToParticipants(command, selected_uuids); + } + else + { + doToSelectedConversation(command, selected_uuids); + } + } +} + +void LLIMFloaterContainer::doToSelectedGroup(const LLSD& userdata) +{ + std::string action = userdata.asString(); + LLUUID selected_group = getCurSelectedViewModelItem()->getUUID(); + + if (action == "group_profile") + { + LLGroupActions::show(selected_group); + } + else if (action == "activate_group") + { + LLGroupActions::activate(selected_group); + } + else if (action == "leave_group") + { + LLGroupActions::leave(selected_group); + } +} + +bool LLIMFloaterContainer::enableContextMenuItem(const LLSD& userdata) +{ + std::string item = userdata.asString(); + uuid_vec_t uuids; + getParticipantUUIDs(uuids); + + if(item == std::string("can_activate_group")) + { + LLUUID selected_group_id = getCurSelectedViewModelItem()->getUUID(); + return gAgent.getGroupID() != selected_group_id; + } + + if(uuids.size() <= 0) + { + return false; + } + + // Note: can_block and can_delete is used only for one person selected menu + // so we don't need to go over all uuids. + + if (item == std::string("can_block")) + { + const LLUUID& id = uuids.front(); + return LLAvatarActions::canBlock(id); + } + else if (item == std::string("can_add")) + { + // We can add friends if: + // - there are selected people + // - and there are no friends among selection yet. + + //EXT-7389 - disable for more than 1 + if(uuids.size() > 1) + { + return false; + } + + bool result = true; + + uuid_vec_t::const_iterator + id = uuids.begin(), + uuids_end = uuids.end(); + + for (;id != uuids_end; ++id) + { + if ( LLAvatarActions::isFriend(*id) ) + { + result = false; + break; + } + } + + return result; + } + else if (item == std::string("can_delete")) + { + // We can remove friends if: + // - there are selected people + // - and there are only friends among selection. + + bool result = (uuids.size() > 0); + + uuid_vec_t::const_iterator + id = uuids.begin(), + uuids_end = uuids.end(); + + for (;id != uuids_end; ++id) + { + if ( !LLAvatarActions::isFriend(*id) ) + { + result = false; + break; + } + } + + return result; + } + else if (item == std::string("can_call")) + { + return LLAvatarActions::canCall(); + } + else if (item == std::string("can_show_on_map")) + { + const LLUUID& id = uuids.front(); + + return (LLAvatarTracker::instance().isBuddyOnline(id) && is_agent_mappable(id)) + || gAgent.isGodlike(); + } + else if(item == std::string("can_offer_teleport")) + { + return LLAvatarActions::canOfferTeleport(uuids); + } + else if("can_moderate_voice" == item || "can_allow_text_chat" == item || "can_mute" == item || "can_unmute" == item) + { + return enableModerateContextMenuItem(item); + } + + return false; +} + +bool LLIMFloaterContainer::checkContextMenuItem(const LLSD& userdata) +{ + std::string item = userdata.asString(); + uuid_vec_t mUUIDs; + getParticipantUUIDs(mUUIDs); + + if(mUUIDs.size() > 0 ) + { + if ("is_blocked" == item) + { + return LLAvatarActions::isBlocked(mUUIDs.front()); + } + else if ("is_allowed_text_chat" == item) + { + const LLSpeaker * speakerp = getSpeakerOfSelectedParticipant(getSpeakerMgrForSelectedParticipant()); + + if (NULL != speakerp) + { + return !speakerp->mModeratorMutedText; + } + } + } + + return false; +} + +void LLIMFloaterContainer::showConversation(const LLUUID& session_id) +{ + setVisibleAndFrontmost(false); + selectConversation(session_id); +} + +//Will select only the conversation item +void LLIMFloaterContainer::selectConversation(const LLUUID& session_id) +{ + LLFolderViewItem* widget = mConversationsWidgets[session_id]; + if (widget) + { + (widget->getRoot())->setSelection(widget, FALSE, FALSE); + } +} + + +void LLIMFloaterContainer::setTimeNow(const LLUUID& session_id, const LLUUID& participant_id) +{ + conversations_items_map::iterator item_it = mConversationsItems.find(session_id); + if (item_it != mConversationsItems.end()) + { + LLConversationItemSession* item = dynamic_cast<LLConversationItemSession*>(item_it->second); + if (item) + { + item->setTimeNow(participant_id); + mConversationViewModel.requestSortAll(); + mConversationsRoot->arrangeAll(); + } + } +} + +void LLIMFloaterContainer::setNearbyDistances() +{ + // Get the nearby chat session: that's the one with uuid nul in mConversationsItems + conversations_items_map::iterator item_it = mConversationsItems.find(LLUUID()); + if (item_it != mConversationsItems.end()) + { + LLConversationItemSession* item = dynamic_cast<LLConversationItemSession*>(item_it->second); + if (item) + { + // Get the positions of the nearby avatars and their ids + std::vector<LLVector3d> positions; + uuid_vec_t avatar_ids; + LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), gSavedSettings.getF32("NearMeRange")); + // Get the position of the agent + const LLVector3d& me_pos = gAgent.getPositionGlobal(); + // For each nearby avatar, compute and update the distance + int avatar_count = positions.size(); + for (int i = 0; i < avatar_count; i++) + { + F64 dist = dist_vec_squared(positions[i], me_pos); + item->setDistance(avatar_ids[i],dist); + } + // Also does it for the agent itself + item->setDistance(gAgent.getID(),0.0f); + // Request resort + mConversationViewModel.requestSortAll(); + mConversationsRoot->arrangeAll(); + } + } +} + +void LLIMFloaterContainer::addConversationListItem(const LLUUID& uuid, bool isWidgetSelected /*= false*/) +{ + bool is_nearby_chat = uuid.isNull(); + + //Stores the display name for the conversation line item + std::string display_name = is_nearby_chat ? LLTrans::getString("NearbyChatLabel") : LLIMModel::instance().getName(uuid); + + // Check if the item is not already in the list, exit if it is and has the same name and uuid (nothing to do) + // Note: this happens often, when reattaching a torn off conversation for instance + conversations_items_map::iterator item_it = mConversationsItems.find(uuid); + if (item_it != mConversationsItems.end()) + { + return; + } + + // Remove the conversation item that might exist already: it'll be recreated anew further down anyway + // and nothing wrong will happen removing it if it doesn't exist + removeConversationListItem(uuid,false); + + // Create a conversation session model + LLConversationItemSession* item = NULL; + LLSpeakerMgr* speaker_manager = (is_nearby_chat ? (LLSpeakerMgr*)(LLLocalSpeakerMgr::getInstance()) : LLIMModel::getInstance()->getSpeakerManager(uuid)); + if (speaker_manager) + { + item = new LLParticipantList(speaker_manager, NULL, getRootViewModel(), true, false); + } + if (!item) + { + llwarns << "Couldn't create conversation session item : " << display_name << llendl; + return; + } + item->renameItem(display_name); + item->updateParticipantName(NULL); + + mConversationsItems[uuid] = item; + + // Create a widget from it + LLConversationViewSession* widget = createConversationItemWidget(item); + mConversationsWidgets[uuid] = widget; + + // Add a new conversation widget to the root folder of the folder view + widget->addToFolder(mConversationsRoot); + widget->requestArrange(); + + // Create the participants widgets now + // Note: usually, we do not get an updated avatar list at that point + LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = item->getChildrenBegin(); + LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = item->getChildrenEnd(); + while (current_participant_model != end_participant_model) + { + LLConversationItem* participant_model = dynamic_cast<LLConversationItem*>(*current_participant_model); + LLConversationViewParticipant* participant_view = createConversationViewParticipant(participant_model); + participant_view->addToFolder(widget); + current_participant_model++; + } + + // set the widget to minimized mode if conversations pane is collapsed + widget->toggleMinimizedMode(mConversationsPane->isCollapsed()); + + if (isWidgetSelected) + { + selectConversation(uuid); + // scroll to newly added item + mConversationsRoot->scrollToShowSelection(); + } + + return; +} + +bool LLIMFloaterContainer::removeConversationListItem(const LLUUID& uuid, bool change_focus) +{ + // Delete the widget and the associated conversation item + // Note : since the mConversationsItems is also the listener to the widget, deleting + // the widget will also delete its listener + bool isWidgetSelected = false; + LLFolderViewItem* new_selection = NULL; + conversations_widgets_map::iterator widget_it = mConversationsWidgets.find(uuid); + if (widget_it != mConversationsWidgets.end()) + { + LLFolderViewItem* widget = widget_it->second; + if (widget) + { + isWidgetSelected = widget->isSelected(); + new_selection = mConversationsRoot->getNextFromChild(widget); + if(new_selection == NULL) + { + new_selection = mConversationsRoot->getPreviousFromChild(widget); + } + widget->destroyView(); + } + } + + // Suppress the conversation items and widgets from their respective maps + mConversationsItems.erase(uuid); + mConversationsWidgets.erase(uuid); + + // Don't let the focus fall IW, select and refocus on the first conversation in the list + if (change_focus) + { + setFocus(TRUE); + if(new_selection != NULL) + { + LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(new_selection->getViewModelItem()); + if(vmi != NULL) + { + selectConversation(vmi->getUUID()); + } + } + } + return isWidgetSelected; +} + +LLConversationViewSession* LLIMFloaterContainer::createConversationItemWidget(LLConversationItem* item) +{ + LLConversationViewSession::Params params; + + params.name = item->getDisplayName(); + params.root = mConversationsRoot; + params.listener = item; + params.tool_tip = params.name; + params.container = this; + + return LLUICtrlFactory::create<LLConversationViewSession>(params); +} + +LLConversationViewParticipant* LLIMFloaterContainer::createConversationViewParticipant(LLConversationItem* item) +{ + LLConversationViewParticipant::Params params; + LLRect panel_rect = mConversationsListPanel->getRect(); + + params.name = item->getDisplayName(); + //params.icon = bridge->getIcon(); + //params.icon_open = bridge->getOpenIcon(); + //params.creation_date = bridge->getCreationDate(); + params.root = mConversationsRoot; + params.listener = item; + + //24 is the the current hight of an item (itemHeight) loaded from conversation_view_participant.xml. + params.rect = LLRect (0, 24, panel_rect.getWidth(), 0); + params.tool_tip = params.name; + params.participant_id = item->getUUID(); + + return LLUICtrlFactory::create<LLConversationViewParticipant>(params); +} + +bool LLIMFloaterContainer::enableModerateContextMenuItem(const std::string& userdata) +{ + // only group moderators can perform actions related to this "enable callback" + if (!isGroupModerator()) + { + return false; + } + + LLSpeaker * speakerp = getSpeakerOfSelectedParticipant(getSpeakerMgrForSelectedParticipant()); + if (NULL == speakerp) + { + return false; + } + + bool voice_channel = speakerp->isInVoiceChannel(); + + if ("can_moderate_voice" == userdata) + { + return voice_channel; + } + else if ("can_mute" == userdata) + { + return voice_channel && !isMuted(getCurSelectedViewModelItem()->getUUID()); + } + else if ("can_unmute" == userdata) + { + return voice_channel && isMuted(getCurSelectedViewModelItem()->getUUID()); + } + + // The last invoke is used to check whether the "can_allow_text_chat" will enabled + return LLVoiceClient::getInstance()->isParticipantAvatar(getCurSelectedViewModelItem()->getUUID()); +} + +bool LLIMFloaterContainer::isGroupModerator() +{ + LLSpeakerMgr * speaker_manager = getSpeakerMgrForSelectedParticipant(); + if (NULL == speaker_manager) + { + llwarns << "Speaker manager is missing" << llendl; + return false; + } + + // Is session a group call/chat? + if(gAgent.isInGroup(speaker_manager->getSessionID())) + { + LLSpeaker * speaker = speaker_manager->findSpeaker(gAgentID).get(); + + // Is agent a moderator? + return speaker && speaker->mIsModerator; + } + + return false; +} + +void LLIMFloaterContainer::moderateVoice(const std::string& command, const LLUUID& userID) +{ + if (!gAgent.getRegion()) return; + + if (command.compare("selected")) + { + moderateVoiceAllParticipants(command.compare("mute_all")); + } + else + { + moderateVoiceParticipant(userID, isMuted(userID)); + } +} + +bool LLIMFloaterContainer::isMuted(const LLUUID& avatar_id) +{ + const LLSpeaker * speakerp = getSpeakerOfSelectedParticipant(getSpeakerMgrForSelectedParticipant()); + return NULL == speakerp ? true : speakerp->mStatus == LLSpeaker::STATUS_MUTED; +} + +void LLIMFloaterContainer::moderateVoiceAllParticipants(bool unmute) +{ + LLIMSpeakerMgr * speaker_managerp = dynamic_cast<LLIMSpeakerMgr*>(getSpeakerMgrForSelectedParticipant()); + + if (NULL != speaker_managerp) + { + if (!unmute) + { + LLSD payload; + payload["session_id"] = speaker_managerp->getSessionID(); + LLNotificationsUtil::add("ConfirmMuteAll", LLSD(), payload, confirmMuteAllCallback); + return; + } + + speaker_managerp->moderateVoiceAllParticipants(unmute); + } +} + +// static +void LLIMFloaterContainer::confirmMuteAllCallback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + // if Cancel pressed + if (option == 1) + { + return; + } + + const LLSD& payload = notification["payload"]; + const LLUUID& session_id = payload["session_id"]; + + LLIMSpeakerMgr * speaker_manager = dynamic_cast<LLIMSpeakerMgr*> ( + LLIMModel::getInstance()->getSpeakerManager(session_id)); + if (speaker_manager) + { + speaker_manager->moderateVoiceAllParticipants(false); + } + + return; +} + +void LLIMFloaterContainer::moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute) +{ + LLIMSpeakerMgr * speaker_managerp = dynamic_cast<LLIMSpeakerMgr *>(getSpeakerMgrForSelectedParticipant()); + + if (NULL != speaker_managerp) + { + speaker_managerp->moderateVoiceParticipant(avatar_id, unmute); + } +} + +LLSpeakerMgr * LLIMFloaterContainer::getSpeakerMgrForSelectedParticipant() +{ + LLFolderViewItem * selected_folder_itemp = mConversationsRoot->getCurSelectedItem(); + if (NULL == selected_folder_itemp) + { + llwarns << "Current selected item is null" << llendl; + return NULL; + } + + LLFolderViewFolder * conversation_itemp = selected_folder_itemp->getParentFolder(); + + conversations_widgets_map::const_iterator iter = mConversationsWidgets.begin(); + conversations_widgets_map::const_iterator end = mConversationsWidgets.end(); + const LLUUID * conversation_uuidp = NULL; + while(iter != end) + { + if (iter->second == conversation_itemp) + { + conversation_uuidp = &iter->first; + break; + } + ++iter; + } + if (NULL == conversation_uuidp) + { + llwarns << "Cannot find conversation item widget" << llendl; + return NULL; + } + + return conversation_uuidp->isNull() ? (LLSpeakerMgr *)LLLocalSpeakerMgr::getInstance() + : LLIMModel::getInstance()->getSpeakerManager(*conversation_uuidp); +} + +LLSpeaker * LLIMFloaterContainer::getSpeakerOfSelectedParticipant(LLSpeakerMgr * speaker_managerp) +{ + if (NULL == speaker_managerp) + { + llwarns << "Speaker manager is missing" << llendl; + return NULL; + } + + const LLConversationItem * participant_itemp = getCurSelectedViewModelItem(); + if (NULL == participant_itemp) + { + llwarns << "Cannot evaluate current selected view model item" << llendl; + return NULL; + } + + return speaker_managerp->findSpeaker(participant_itemp->getUUID()); +} + +void LLIMFloaterContainer::toggleAllowTextChat(const LLUUID& participant_uuid) +{ + LLIMSpeakerMgr * speaker_managerp = dynamic_cast<LLIMSpeakerMgr*>(getSpeakerMgrForSelectedParticipant()); + if (NULL != speaker_managerp) + { + speaker_managerp->toggleAllowTextChat(participant_uuid); + } +} + +void LLIMFloaterContainer::openNearbyChat() +{ + // If there's only one conversation in the container and that conversation is the nearby chat + //(which it should be...), open it so to make the list of participants visible. This happens to be the most common case when opening the Chat floater. + if(mConversationsItems.size() == 1) + { + LLConversationViewSession* nearby_chat = dynamic_cast<LLConversationViewSession*>(mConversationsWidgets[LLUUID()]); + if (nearby_chat) + { + nearby_chat->setOpen(TRUE); + } } } diff --git a/indra/newview/llimfloatercontainer.h b/indra/newview/llimfloatercontainer.h index 892ecef48d..05ea94019b 100644 --- a/indra/newview/llimfloatercontainer.h +++ b/indra/newview/llimfloatercontainer.h @@ -30,14 +30,27 @@ #include <map> #include <vector> +#include "llimview.h" +#include "llevents.h" #include "llfloater.h" #include "llmultifloater.h" #include "llavatarpropertiesprocessor.h" #include "llgroupmgr.h" +#include "lltrans.h" +#include "llconversationmodel.h" +#include "llconversationview.h" +class LLButton; +class LLLayoutPanel; +class LLLayoutStack; class LLTabContainer; +class LLIMFloaterContainer; +class LLSpeaker; +class LLSpeakerMgr; -class LLIMFloaterContainer : public LLMultiFloater +class LLIMFloaterContainer + : public LLMultiFloater + , public LLIMSessionObserver { public: LLIMFloaterContainer(const LLSD& seed); @@ -45,26 +58,115 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); + /*virtual*/ void draw(); + /*virtual*/ void setVisible(BOOL visible); void onCloseFloater(LLUUID& id); /*virtual*/ void addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END); + + void showConversation(const LLUUID& session_id); + void selectConversation(const LLUUID& session_id); + /*virtual*/ void tabClose(); static LLFloater* getCurrentVoiceFloater(); - static LLIMFloaterContainer* findInstance(); - static LLIMFloaterContainer* getInstance(); - virtual void setMinimized(BOOL b); + static void onCurrentChannelChanged(const LLUUID& session_id); + + void collapseMessagesPane(bool collapse); + + // Callbacks + static void idle(void* user_data); + + // LLIMSessionObserver observe triggers + /*virtual*/ void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + /*virtual*/ void sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + /*virtual*/ void sessionVoiceOrIMStarted(const LLUUID& session_id); + /*virtual*/ void sessionRemoved(const LLUUID& session_id); + /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); + + LLConversationViewModel& getRootViewModel() { return mConversationViewModel; } + LLUUID getSelectedSession() { return mSelectedSession; } + void setSelectedSession(LLUUID sessionID) { mSelectedSession = sessionID; } private: typedef std::map<LLUUID,LLFloater*> avatarID_panel_map_t; avatarID_panel_map_t mSessions; boost::signals2::connection mNewMessageConnection; + /*virtual*/ void computeResizeLimits(S32& new_min_width, S32& new_min_height); + void onNewMessageReceived(const LLSD& data); + + void onExpandCollapseButtonClicked(); + void processParticipantsStyleUpdate(); + + void collapseConversationsPane(bool collapse); + + void updateState(bool collapse, S32 delta_width); + + void onAddButtonClicked(); + void onAvatarPicked(const uuid_vec_t& ids); + + BOOL isActionChecked(const LLSD& userdata); + void onCustomAction (const LLSD& userdata); + void setSortOrderSessions(const LLConversationFilter::ESortOrderType order); + void setSortOrderParticipants(const LLConversationFilter::ESortOrderType order); + void setSortOrder(const LLConversationSort& order); + + void getSelectedUUIDs(uuid_vec_t& selected_uuids); + const LLConversationItem * getCurSelectedViewModelItem(); + void getParticipantUUIDs(uuid_vec_t& selected_uuids); + void doToSelected(const LLSD& userdata); + void doToSelectedConversation(const std::string& command, uuid_vec_t& selectedIDS); + void doToParticipants(const std::string& item, uuid_vec_t& selectedIDS); + void doToSelectedGroup(const LLSD& userdata); + bool checkContextMenuItem(const LLSD& userdata); + bool enableContextMenuItem(const LLSD& userdata); + + static void confirmMuteAllCallback(const LLSD& notification, const LLSD& response); + bool enableModerateContextMenuItem(const std::string& userdata); + LLSpeaker * getSpeakerOfSelectedParticipant(LLSpeakerMgr * speaker_managerp); + LLSpeakerMgr * getSpeakerMgrForSelectedParticipant(); + bool isGroupModerator(); + bool isMuted(const LLUUID& avatar_id); + void moderateVoice(const std::string& command, const LLUUID& userID); + void moderateVoiceAllParticipants(bool unmute); + void moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute); + void toggleAllowTextChat(const LLUUID& participant_uuid); + void openNearbyChat(); + + LLButton* mExpandCollapseBtn; + LLLayoutPanel* mMessagesPane; + LLLayoutPanel* mConversationsPane; + LLLayoutStack* mConversationsStack; + + bool mInitialized; + + LLUUID mSelectedSession; + + // Conversation list implementation +public: + bool removeConversationListItem(const LLUUID& uuid, bool change_focus = true); + void addConversationListItem(const LLUUID& uuid, bool isWidgetSelected = false); + void setTimeNow(const LLUUID& session_id, const LLUUID& participant_id); + void setNearbyDistances(); + +private: + LLConversationViewSession* createConversationItemWidget(LLConversationItem* item); + LLConversationViewParticipant* createConversationViewParticipant(LLConversationItem* item); + bool onConversationModelEvent(const LLSD& event); + + // Conversation list data + LLPanel* mConversationsListPanel; // This is the main widget we add conversation widget to + conversations_items_map mConversationsItems; + conversations_widgets_map mConversationsWidgets; + LLConversationViewModel mConversationViewModel; + LLFolderView* mConversationsRoot; + LLEventStream mConversationsEventStream; }; #endif // LL_LLIMFLOATERCONTAINER_H diff --git a/indra/newview/llimhandler.cpp b/indra/newview/llimhandler.cpp index 07d73c8c66..047472a282 100644 --- a/indra/newview/llimhandler.cpp +++ b/indra/newview/llimhandler.cpp @@ -37,10 +37,9 @@ using namespace LLNotificationsUI; //-------------------------------------------------------------------------- -LLIMHandler::LLIMHandler(e_notification_type type, const LLSD& id) +LLIMHandler::LLIMHandler() +: LLSysHandler("IM Notifications", "notifytoast") { - mType = type; - // Getting a Channel for our notifications mChannel = LLChannelManager::getInstance()->createNotificationChannel()->getHandle(); } @@ -59,72 +58,49 @@ void LLIMHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLIMHandler::processNotification(const LLSD& notify) +bool LLIMHandler::processNotification(const LLNotificationPtr& notification) { if(mChannel.isDead()) { return false; } - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if(!notification) - return false; - // arrange a channel on a screen if(!mChannel.get()->getVisible()) { initChannel(); } - if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") - { - LLSD substitutions = notification->getSubstitutions(); - - // According to comments in LLIMMgr::addMessage(), if we get message - // from ourselves, the sender id is set to null. This fixes EXT-875. - LLUUID avatar_id = substitutions["FROM_ID"].asUUID(); - if (avatar_id.isNull()) - avatar_id = gAgentID; - - LLToastIMPanel::Params im_p; - im_p.notification = notification; - im_p.avatar_id = avatar_id; - im_p.from = substitutions["FROM"].asString(); - im_p.time = substitutions["TIME"].asString(); - im_p.message = substitutions["MESSAGE"].asString(); - im_p.session_id = substitutions["SESSION_ID"].asUUID(); - - LLToastIMPanel* im_box = new LLToastIMPanel(im_p); - - LLToast::Params p; - p.notif_id = notification->getID(); - p.session_id = im_p.session_id; - p.notification = notification; - p.panel = im_box; - p.can_be_stored = false; - p.on_delete_toast = boost::bind(&LLIMHandler::onDeleteToast, this, _1); - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->addToast(p); - - // send a signal to the counter manager; - mNewNotificationSignal(); - } - else if (notify["sigtype"].asString() == "delete") - { - mChannel.get()->killToastByNotificationID(notification->getID()); - } - return false; -} + LLSD substitutions = notification->getSubstitutions(); + + // According to comments in LLIMMgr::addMessage(), if we get message + // from ourselves, the sender id is set to null. This fixes EXT-875. + LLUUID avatar_id = substitutions["FROM_ID"].asUUID(); + if (avatar_id.isNull()) + avatar_id = gAgentID; + + LLToastIMPanel::Params im_p; + im_p.notification = notification; + im_p.avatar_id = avatar_id; + im_p.from = substitutions["FROM"].asString(); + im_p.time = substitutions["TIME"].asString(); + im_p.message = substitutions["MESSAGE"].asString(); + im_p.session_id = substitutions["SESSION_ID"].asUUID(); + + LLToastIMPanel* im_box = new LLToastIMPanel(im_p); + + LLToast::Params p; + p.notif_id = notification->getID(); + p.session_id = im_p.session_id; + p.notification = notification; + p.panel = im_box; + p.can_be_stored = false; + LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); + if(channel) + channel->addToast(p); -//-------------------------------------------------------------------------- -void LLIMHandler::onDeleteToast(LLToast* toast) -{ - // send a signal to the counter manager - mDelNotificationSignal(); + return false; } -//-------------------------------------------------------------------------- diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 4000570872..d5f1e81933 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -41,7 +41,7 @@ #include "lltextutil.h" #include "lltrans.h" #include "lluictrlfactory.h" - +#include "llimconversation.h" #include "llagent.h" #include "llagentui.h" #include "llappviewer.h" @@ -49,6 +49,7 @@ #include "llcallingcard.h" #include "llchat.h" #include "llimfloater.h" +#include "llimfloatercontainer.h" #include "llgroupiconctrl.h" #include "llmd5.h" #include "llmutelist.h" @@ -108,7 +109,7 @@ static void on_avatar_name_cache_toast(const LLUUID& agent_id, args["FROM"] = av_name.getCompleteName(); args["FROM_ID"] = msg["from_id"]; args["SESSION_ID"] = msg["session_id"]; - LLNotificationsUtil::add("IMToast", args, LLSD(), boost::bind(&LLIMFloater::show, msg["session_id"].asUUID())); + LLNotificationsUtil::add("IMToast", args, LLSD(), boost::bind(&LLIMFloaterContainer::showConversation, LLIMFloaterContainer::getInstance(), msg["session_id"].asUUID())); } void toast_callback(const LLSD& msg){ @@ -118,12 +119,12 @@ void toast_callback(const LLSD& msg){ return; } - // check whether incoming IM belongs to an active session or not - if (LLIMModel::getInstance()->getActiveSessionID().notNull() - && LLIMModel::getInstance()->getActiveSessionID() == msg["session_id"]) - { - return; - } + // Skip toasting if we have open window of IM with this session id + LLIMFloater* open_im_floater = LLIMFloater::findInstance(msg["session_id"]); + if (open_im_floater && open_im_floater->isInVisibleChain() && open_im_floater->hasFocus()) + { + return; + } // Skip toasting for system messages if (msg["from_id"].asUUID() == LLUUID::null) @@ -145,40 +146,22 @@ void toast_callback(const LLSD& msg){ return; } - // Skip toasting if we have open window of IM with this session id - LLIMFloater* open_im_floater = LLIMFloater::findInstance(msg["session_id"]); - if (open_im_floater && open_im_floater->getVisible()) - { - return; - } - LLAvatarNameCache::get(msg["from_id"].asUUID(), boost::bind(&on_avatar_name_cache_toast, _1, _2, msg)); } -void LLIMModel::setActiveSessionID(const LLUUID& session_id) -{ - // check if such an ID really exists - if (!findIMSession(session_id)) - { - llwarns << "Trying to set as active a non-existent session!" << llendl; - return; - } - - mActiveSessionID = session_id; -} - LLIMModel::LLIMModel() { addNewMsgCallback(boost::bind(&LLIMFloater::newIMCallback, _1)); addNewMsgCallback(boost::bind(&toast_callback, _1)); } -LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice) +LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg) : mSessionID(session_id), mName(name), mType(type), + mHasOfflineMessage(has_offline_msg), mParticipantUnreadMessageCount(0), mNumUnread(0), mOtherParticipantID(other_participant_id), @@ -262,7 +245,7 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& std::list<LLSD> chat_history; //involves parsing of a chat history - LLLogChat::loadAllHistory(mHistoryFileName, chat_history); + LLLogChat::loadChatHistory(mHistoryFileName, chat_history); addMessagesFromHistory(chat_history); } @@ -375,6 +358,8 @@ void LLIMModel::LLIMSession::onVoiceChannelStateChanged(const LLVoiceChannel::ES break; } } + default: + break; } // Update speakers list when connected if (LLVoiceChannel::STATE_CONNECTED == new_state) @@ -533,7 +518,7 @@ LLIMModel::LLIMSession* LLIMModel::findAdHocIMSession(const uuid_vec_t& ids) return NULL; } -bool LLIMModel::LLIMSession::isOutgoingAdHoc() +bool LLIMModel::LLIMSession::isOutgoingAdHoc() const { return IM_SESSION_CONFERENCE_START == mType; } @@ -553,6 +538,19 @@ bool LLIMModel::LLIMSession::isOtherParticipantAvaline() return !mOtherParticipantIsAvatar; } +LLUUID LLIMModel::LLIMSession::generateOutgouigAdHocHash() const +{ + LLUUID hash = LLUUID::null; + + if (mInitialTargetIDs.size()) + { + std::set<LLUUID> sorted_uuids(mInitialTargetIDs.begin(), mInitialTargetIDs.end()); + hash = generateHash(sorted_uuids); + } + + return hash; +} + void LLIMModel::LLIMSession::buildHistoryFileName() { mHistoryFileName = mName; @@ -569,7 +567,7 @@ void LLIMModel::LLIMSession::buildHistoryFileName() if (mInitialTargetIDs.size()) { std::set<LLUUID> sorted_uuids(mInitialTargetIDs.begin(), mInitialTargetIDs.end()); - mHistoryFileName = mName + " hash" + generateHash(sorted_uuids); + mHistoryFileName = mName + " hash" + generateHash(sorted_uuids).asString(); } else { @@ -603,7 +601,7 @@ void LLIMModel::LLIMSession::buildHistoryFileName() } //static -std::string LLIMModel::LLIMSession::generateHash(const std::set<LLUUID>& sorted_uuids) +LLUUID LLIMModel::LLIMSession::generateHash(const std::set<LLUUID>& sorted_uuids) { LLMD5 md5_uuid; @@ -617,7 +615,7 @@ std::string LLIMModel::LLIMSession::generateHash(const std::set<LLUUID>& sorted_ LLUUID participants_md5_hash; md5_uuid.raw_digest((unsigned char*) participants_md5_hash.mData); - return participants_md5_hash.asString(); + return participants_md5_hash; } void LLIMModel::processSessionInitializedReply(const LLUUID& old_session_id, const LLUUID& new_session_id) @@ -631,8 +629,6 @@ void LLIMModel::processSessionInitializedReply(const LLUUID& old_session_id, con { mId2SessionMap.erase(old_session_id); mId2SessionMap[new_session_id] = session; - - gIMMgr->notifyObserverSessionIDUpdated(old_session_id, new_session_id); } LLIMFloater* im_floater = LLIMFloater::findInstance(old_session_id); @@ -641,6 +637,11 @@ void LLIMModel::processSessionInitializedReply(const LLUUID& old_session_id, con im_floater->sessionInitReplyReceived(new_session_id); } + if (old_session_id != new_session_id) + { + gIMMgr->notifyObserverSessionIDUpdated(old_session_id, new_session_id); + } + // auto-start the call on session initialization? if (session->mStartCallOnInitialize) { @@ -676,7 +677,7 @@ void LLIMModel::testMessages() //session name should not be empty bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, - const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice) + const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg) { if (name.empty()) { @@ -690,7 +691,7 @@ bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, co return false; } - LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids, voice); + LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids, voice, has_offline_msg); mId2SessionMap[session_id] = session; // When notifying observer, name of session is used instead of "name", because they may not be the @@ -702,10 +703,10 @@ bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, co } -bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, bool voice) +bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, bool voice, bool has_offline_msg) { uuid_vec_t no_ids; - return newSession(session_id, name, type, other_participant_id, no_ids, voice); + return newSession(session_id, name, type, other_participant_id, no_ids, voice, has_offline_msg); } bool LLIMModel::clearSession(const LLUUID& session_id) @@ -716,6 +717,16 @@ bool LLIMModel::clearSession(const LLUUID& session_id) return true; } +void LLIMModel::getMessages(const LLUUID& session_id, std::list<LLSD>& messages, int start_index, const bool sendNoUnreadMsgs) +{ + getMessagesSilently(session_id, messages, start_index); + + if (sendNoUnreadMsgs) + { + sendNoUnreadMessages(session_id); + } +} + void LLIMModel::getMessagesSilently(const LLUUID& session_id, std::list<LLSD>& messages, int start_index) { LLIMSession* session = findIMSession(session_id); @@ -757,13 +768,6 @@ void LLIMModel::sendNoUnreadMessages(const LLUUID& session_id) mNoUnreadMsgsSignal(arg); } -void LLIMModel::getMessages(const LLUUID& session_id, std::list<LLSD>& messages, int start_index) -{ - getMessagesSilently(session_id, messages, start_index); - - sendNoUnreadMessages(session_id); -} - bool LLIMModel::addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text) { LLIMSession* session = findIMSession(session_id); @@ -904,7 +908,7 @@ const LLUUID& LLIMModel::getOtherParticipantID(const LLUUID& session_id) const LLIMSession* session = findIMSession(session_id); if (!session) { - llwarns << "session " << session_id << "does not exist " << llendl; + llwarns << "session " << session_id << " does not exist " << llendl; return LLUUID::null; } @@ -2395,6 +2399,7 @@ void LLIMMgr::addMessage( const LLUUID& target_id, const std::string& from, const std::string& msg, + bool is_offline_msg, const std::string& session_name, EInstantMessage dialog, U32 parent_estate_id, @@ -2420,7 +2425,7 @@ void LLIMMgr::addMessage( bool new_session = !hasSession(new_session_id); if (new_session) { - LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id); + LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id, false, is_offline_msg); // When we get a new IM, and if you are a god, display a bit // of information about the source. This is to help liaisons @@ -2479,11 +2484,9 @@ void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& mess LLChat chat(message); chat.mSourceType = CHAT_SOURCE_SYSTEM; - - LLFloater* chat_bar = LLFloaterReg::getInstance("chat_bar"); - LLNearbyChat* nearby_chat = chat_bar->findChild<LLNearbyChat>("nearby_chat"); - if(nearby_chat) + LLNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance<LLNearbyChat>("nearby_chat"); + if (nearby_chat) { nearby_chat->addMessage(chat); } @@ -2497,6 +2500,7 @@ void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& mess gIMMgr->addMessage(session_id, LLUUID::null, SYSTEM_FROM, message.getString()); } // log message to file + else { std::string session_name; @@ -2579,7 +2583,8 @@ LLUUID LLIMMgr::addSession( { LLDynamicArray<LLUUID> ids; ids.put(other_participant_id); - return addSession(name, dialog, other_participant_id, ids, voice); + LLUUID session_id = addSession(name, dialog, other_participant_id, ids, voice); + return session_id; } // Adds a session using the given session_id. If the session already exists @@ -2588,7 +2593,8 @@ LLUUID LLIMMgr::addSession( const std::string& name, EInstantMessage dialog, const LLUUID& other_participant_id, - const LLDynamicArray<LLUUID>& ids, bool voice) + const LLDynamicArray<LLUUID>& ids, bool voice, + const LLUUID& floater_id) { if (0 == ids.getLength()) { @@ -2603,6 +2609,19 @@ LLUUID LLIMMgr::addSession( LLUUID session_id = computeSessionID(dialog,other_participant_id); + if (floater_id.notNull()) + { + LLIMFloater* im_floater = LLIMFloater::findInstance(floater_id); + + if (im_floater && im_floater->getStartConferenceInSameFloater()) + { + // The IM floater should be initialized with a new session_id + // so that it is found by that id when creating a chiclet in LLIMFloater::onIMChicletCreated, + // and a new floater is not created. + im_floater->initIMSession(session_id); + } + } + bool new_session = !LLIMModel::getInstance()->findIMSession(session_id); //works only for outgoing ad-hoc sessions @@ -2616,10 +2635,17 @@ LLUUID LLIMMgr::addSession( } } + //Notify observers that a session was added if (new_session) { LLIMModel::getInstance()->newSession(session_id, name, dialog, other_participant_id, ids, voice); } + //Notifies observers that the session was already added + else + { + std::string session_name = LLIMModel::getInstance()->getName(session_id); + LLIMMgr::getInstance()->notifyObserverSessionActivated(session_id, session_name, other_participant_id); + } //we don't need to show notes about online/offline, mute/unmute users' statuses for existing sessions if (!new_session) return session_id; @@ -2634,6 +2660,8 @@ LLUUID LLIMMgr::addSession( noteMutedUsers(session_id, ids); } + notifyObserverSessionVoiceOrIMStarted(session_id); + return session_id; } @@ -2920,6 +2948,22 @@ void LLIMMgr::notifyObserverSessionAdded(const LLUUID& session_id, const std::st } } +void LLIMMgr::notifyObserverSessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) +{ + for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++) + { + (*it)->sessionActivated(session_id, name, other_participant_id); + } +} + +void LLIMMgr::notifyObserverSessionVoiceOrIMStarted(const LLUUID& session_id) +{ + for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++) + { + (*it)->sessionVoiceOrIMStarted(session_id); + } +} + void LLIMMgr::notifyObserverSessionRemoved(const LLUUID& session_id) { for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++) @@ -3279,6 +3323,7 @@ public: from_id, name, buffer, + IM_OFFLINE == offline, std::string((char*)&bin_bucket[0]), IM_SESSION_INVITE, message_params["parent_estate_id"].asInteger(), diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 7c2cd03d97..054388bc6c 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -70,10 +70,11 @@ public: GROUP_SESSION, ADHOC_SESSION, AVALINE_SESSION, + NONE_SESSION, } SType; LLIMSession(const LLUUID& session_id, const std::string& name, - const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice); + const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg); virtual ~LLIMSession(); void sessionInitReplyReceived(const LLUUID& new_session_id); @@ -84,7 +85,7 @@ public: /** @deprecated */ static void chatFromLogFile(LLLogChat::ELogLineType type, const LLSD& msg, void* userdata); - bool isOutgoingAdHoc(); + bool isOutgoingAdHoc() const; bool isAdHoc(); bool isP2P(); bool isOtherParticipantAvaline(); @@ -94,6 +95,8 @@ public: bool isGroupSessionType() const { return mSessionType == GROUP_SESSION;} bool isAvalineSessionType() const { return mSessionType == AVALINE_SESSION;} + LLUUID generateOutgouigAdHocHash() const; + //*TODO make private /** ad-hoc sessions involve sophisticated chat history file naming schemes */ void buildHistoryFileName(); @@ -133,22 +136,17 @@ public: //if IM session is created for a voice call bool mStartedAsIMCall; + bool mHasOfflineMessage; + private: void onAdHocNameCache(const LLAvatarName& av_name); - static std::string generateHash(const std::set<LLUUID>& sorted_uuids); + static LLUUID generateHash(const std::set<LLUUID>& sorted_uuids); }; LLIMModel(); - - //we should control the currently active session - LLUUID mActiveSessionID; - void setActiveSessionID(const LLUUID& session_id); - void resetActiveSessionID() { mActiveSessionID.setNull(); } - LLUUID getActiveSessionID() { return mActiveSessionID; } - /** Session id to session object */ std::map<LLUUID, LLIMSession*> mId2SessionMap; @@ -181,10 +179,10 @@ public: * @param name session name should not be empty, will return false if empty */ bool newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, - const uuid_vec_t& ids, bool voice = false); + const uuid_vec_t& ids, bool voice = false, bool has_offline_msg = false); bool newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, - const LLUUID& other_participant_id, bool voice = false); + const LLUUID& other_participant_id, bool voice = false, bool has_offline_msg = false); /** * Remove all session data associated with a session specified by session_id @@ -192,12 +190,6 @@ public: bool clearSession(const LLUUID& session_id); /** - * Populate supplied std::list with messages starting from index specified by start_index without - * emitting no unread messages signal. - */ - void getMessagesSilently(const LLUUID& session_id, std::list<LLSD>& messages, int start_index = 0); - - /** * Sends no unread messages signal. */ void sendNoUnreadMessages(const LLUUID& session_id); @@ -205,7 +197,7 @@ public: /** * Populate supplied std::list with messages starting from index specified by start_index */ - void getMessages(const LLUUID& session_id, std::list<LLSD>& messages, int start_index = 0); + void getMessages(const LLUUID& session_id, std::list<LLSD>& messages, int start_index = 0, const bool sendNoUnreadMsgs = true); /** * Add a message to an IM Model - the message is saved in a message store associated with a session specified by session_id @@ -288,6 +280,12 @@ public: private: /** + * Populate supplied std::list with messages starting from index specified by start_index without + * emitting no unread messages signal. + */ + void getMessagesSilently(const LLUUID& session_id, std::list<LLSD>& messages, int start_index = 0); + + /** * Add message to a list of message associated with session specified by session_id */ bool addToHistory(const LLUUID& session_id, const std::string& from, const LLUUID& from_id, const std::string& utf8_text); @@ -298,6 +296,8 @@ class LLIMSessionObserver public: virtual ~LLIMSessionObserver() {} virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) = 0; + virtual void sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) = 0; + virtual void sessionVoiceOrIMStarted(const LLUUID& session_id) = 0; virtual void sessionRemoved(const LLUUID& session_id) = 0; virtual void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) = 0; }; @@ -324,6 +324,7 @@ public: const LLUUID& target_id, const std::string& from, const std::string& msg, + bool is_offline_msg = false, const std::string& session_name = LLStringUtil::null, EInstantMessage dialog = IM_NOTHING_SPECIAL, U32 parent_estate_id = 0, @@ -347,10 +348,12 @@ public: // Adds a session using a specific group of starting agents // the dialog type is assumed correct. Returns the uuid of the session. + // A session can be added to a floater specified by floater_id. LLUUID addSession(const std::string& name, EInstantMessage dialog, const LLUUID& other_participant_id, - const LLDynamicArray<LLUUID>& ids, bool voice = false); + const LLDynamicArray<LLUUID>& ids, bool voice = false, + const LLUUID& floater_id = LLUUID::null); /** * Creates a P2P session with the requisite handle for responding to voice calls. @@ -460,6 +463,9 @@ private: static void onInviteNameLookup(LLSD payload, const LLUUID& id, const std::string& name, bool is_group); void notifyObserverSessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + //Triggers when a session has already been added + void notifyObserverSessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + void notifyObserverSessionVoiceOrIMStarted(const LLUUID& session_id); void notifyObserverSessionRemoved(const LLUUID& session_id); void notifyObserverSessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp index 17d0b0ffbb..8a15cd279f 100644 --- a/indra/newview/llinspectavatar.cpp +++ b/indra/newview/llinspectavatar.cpp @@ -28,38 +28,21 @@ #include "llinspectavatar.h" // viewer files -#include "llagent.h" -#include "llagentdata.h" -#include "llavataractions.h" +#include "llavatariconctrl.h" #include "llavatarnamecache.h" #include "llavatarpropertiesprocessor.h" -#include "llcallingcard.h" #include "lldateutil.h" -#include "llfloaterreporter.h" -#include "llfloaterworldmap.h" -#include "llimview.h" #include "llinspect.h" -#include "llmutelist.h" -#include "llpanelblockedlist.h" +#include "llslurl.h" #include "llstartup.h" -#include "llspeakers.h" -#include "llviewermenu.h" -#include "llvoiceclient.h" -#include "llviewerobjectlist.h" #include "lltransientfloatermgr.h" -#include "llnotificationsutil.h" // Linden libraries #include "llfloater.h" #include "llfloaterreg.h" -#include "llmenubutton.h" #include "lltextbox.h" -#include "lltoggleablemenu.h" #include "lltooltip.h" // positionViewNearMouse() #include "lltrans.h" -#include "lluictrl.h" - -#include "llavatariconctrl.h" class LLFetchAvatarData; @@ -81,22 +64,13 @@ public: LLInspectAvatar(const LLSD& avatar_id); virtual ~LLInspectAvatar(); - /*virtual*/ BOOL postBuild(void); - // Because floater is single instance, need to re-parse data on each spawn // (for example, inspector about same avatar but in different position) /*virtual*/ void onOpen(const LLSD& avatar_id); - // When closing they should close their gear menu - /*virtual*/ void onClose(bool app_quitting); - // Update view based on information from avatar properties processor void processAvatarData(LLAvatarData* data); - // override the inspector mouse leave so timer is only paused if - // gear menu is not open - /* virtual */ void onMouseLeave(S32 x, S32 y, MASK mask); - virtual LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::GLOBAL; } private: @@ -104,47 +78,6 @@ private: // Used on construction and if avatar id changes. void requestUpdate(); - // Set the volume slider to this user's current client-side volume setting, - // hiding/disabling if the user is not nearby. - void updateVolumeSlider(); - - // Shows/hides moderator panel depending on voice state - void updateModeratorPanel(); - - // Moderator ability to enable/disable voice chat for avatar - void toggleSelectedVoice(bool enabled); - - // Button callbacks - void onClickAddFriend(); - void onClickViewProfile(); - void onClickIM(); - void onClickCall(); - void onClickTeleport(); - void onClickInviteToGroup(); - void onClickPay(); - void onClickShare(); - void onToggleMute(); - void onClickReport(); - void onClickFreeze(); - void onClickEject(); - void onClickKick(); - void onClickCSR(); - void onClickZoomIn(); - void onClickFindOnMap(); - bool onVisibleFindOnMap(); - bool onVisibleEject(); - bool onVisibleFreeze(); - bool onVisibleZoomIn(); - void onClickMuteVolume(); - void onVolumeChange(const LLSD& data); - bool enableMute(); - bool enableUnmute(); - bool enableTeleportOffer(); - bool godModeEnabled(); - - // Is used to determine if "Add friend" option should be enabled in gear menu - bool isNotFriend(); - void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); @@ -209,39 +142,8 @@ LLInspectAvatar::LLInspectAvatar(const LLSD& sd) mAvatarName(), mPropertiesRequest(NULL) { - mCommitCallbackRegistrar.add("InspectAvatar.ViewProfile", boost::bind(&LLInspectAvatar::onClickViewProfile, this)); - mCommitCallbackRegistrar.add("InspectAvatar.AddFriend", boost::bind(&LLInspectAvatar::onClickAddFriend, this)); - mCommitCallbackRegistrar.add("InspectAvatar.IM", - boost::bind(&LLInspectAvatar::onClickIM, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Call", boost::bind(&LLInspectAvatar::onClickCall, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Teleport", boost::bind(&LLInspectAvatar::onClickTeleport, this)); - mCommitCallbackRegistrar.add("InspectAvatar.InviteToGroup", boost::bind(&LLInspectAvatar::onClickInviteToGroup, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Pay", boost::bind(&LLInspectAvatar::onClickPay, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Share", boost::bind(&LLInspectAvatar::onClickShare, this)); - mCommitCallbackRegistrar.add("InspectAvatar.ToggleMute", boost::bind(&LLInspectAvatar::onToggleMute, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Freeze", boost::bind(&LLInspectAvatar::onClickFreeze, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Eject", boost::bind(&LLInspectAvatar::onClickEject, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Kick", boost::bind(&LLInspectAvatar::onClickKick, this)); - mCommitCallbackRegistrar.add("InspectAvatar.CSR", boost::bind(&LLInspectAvatar::onClickCSR, this)); - mCommitCallbackRegistrar.add("InspectAvatar.Report", boost::bind(&LLInspectAvatar::onClickReport, this)); - mCommitCallbackRegistrar.add("InspectAvatar.FindOnMap", boost::bind(&LLInspectAvatar::onClickFindOnMap, this)); - mCommitCallbackRegistrar.add("InspectAvatar.ZoomIn", boost::bind(&LLInspectAvatar::onClickZoomIn, this)); - mCommitCallbackRegistrar.add("InspectAvatar.DisableVoice", boost::bind(&LLInspectAvatar::toggleSelectedVoice, this, false)); - mCommitCallbackRegistrar.add("InspectAvatar.EnableVoice", boost::bind(&LLInspectAvatar::toggleSelectedVoice, this, true)); - - mEnableCallbackRegistrar.add("InspectAvatar.EnableGod", boost::bind(&LLInspectAvatar::godModeEnabled, this)); - mEnableCallbackRegistrar.add("InspectAvatar.VisibleFindOnMap", boost::bind(&LLInspectAvatar::onVisibleFindOnMap, this)); - mEnableCallbackRegistrar.add("InspectAvatar.VisibleEject", boost::bind(&LLInspectAvatar::onVisibleEject, this)); - mEnableCallbackRegistrar.add("InspectAvatar.VisibleFreeze", boost::bind(&LLInspectAvatar::onVisibleFreeze, this)); - mEnableCallbackRegistrar.add("InspectAvatar.VisibleZoomIn", boost::bind(&LLInspectAvatar::onVisibleZoomIn, this)); - mEnableCallbackRegistrar.add("InspectAvatar.Gear.Enable", boost::bind(&LLInspectAvatar::isNotFriend, this)); - mEnableCallbackRegistrar.add("InspectAvatar.Gear.EnableCall", boost::bind(&LLAvatarActions::canCall)); - mEnableCallbackRegistrar.add("InspectAvatar.Gear.EnableTeleportOffer", boost::bind(&LLInspectAvatar::enableTeleportOffer, this)); - mEnableCallbackRegistrar.add("InspectAvatar.EnableMute", boost::bind(&LLInspectAvatar::enableMute, this)); - mEnableCallbackRegistrar.add("InspectAvatar.EnableUnmute", boost::bind(&LLInspectAvatar::enableUnmute, this)); - // can't make the properties request until the widgets are constructed - // as it might return immediately, so do it in postBuild. + // as it might return immediately, so do it in onOpen. LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::GLOBAL, this); LLTransientFloater::init(this); @@ -257,25 +159,6 @@ LLInspectAvatar::~LLInspectAvatar() LLTransientFloaterMgr::getInstance()->removeControlView(this); } -/*virtual*/ -BOOL LLInspectAvatar::postBuild(void) -{ - getChild<LLUICtrl>("add_friend_btn")->setCommitCallback( - boost::bind(&LLInspectAvatar::onClickAddFriend, this) ); - - getChild<LLUICtrl>("view_profile_btn")->setCommitCallback( - boost::bind(&LLInspectAvatar::onClickViewProfile, this) ); - - getChild<LLUICtrl>("mute_btn")->setCommitCallback( - boost::bind(&LLInspectAvatar::onClickMuteVolume, this) ); - - getChild<LLUICtrl>("volume_slider")->setCommitCallback( - boost::bind(&LLInspectAvatar::onVolumeChange, this, _2)); - - return TRUE; -} - - // Multiple calls to showInstance("inspect_avatar", foo) will provide different // LLSD for foo, which we will catch here. //virtual @@ -287,11 +170,6 @@ void LLInspectAvatar::onOpen(const LLSD& data) // Extract appropriate avatar id mAvatarID = data["avatar_id"]; - BOOL self = mAvatarID == gAgent.getID(); - - getChild<LLUICtrl>("gear_self_btn")->setVisible(self); - getChild<LLUICtrl>("gear_btn")->setVisible(!self); - // Position the inspector relative to the mouse cursor // Similar to how tooltips are positioned // See LLToolTipMgr::createToolTip @@ -304,20 +182,13 @@ void LLInspectAvatar::onOpen(const LLSD& data) LLUI::positionViewNearMouse(this); } + // Generate link to avatar profile. + getChild<LLUICtrl>("avatar_profile_link")->setTextArg("[LINK]", LLSLURL("agent", mAvatarID, "about").getSLURLString()); + // can't call from constructor as widgets are not built yet requestUpdate(); - - updateVolumeSlider(); - - updateModeratorPanel(); } -// virtual -void LLInspectAvatar::onClose(bool app_quitting) -{ - getChild<LLMenuButton>("gear_btn")->hideMenu(); -} - void LLInspectAvatar::requestUpdate() { // Don't make network requests when spawning from the debug menu at the @@ -344,25 +215,6 @@ void LLInspectAvatar::requestUpdate() delete mPropertiesRequest; mPropertiesRequest = new LLFetchAvatarData(mAvatarID, this); - // You can't re-add someone as a friend if they are already your friend - bool is_friend = LLAvatarTracker::instance().getBuddyInfo(mAvatarID) != NULL; - bool is_self = (mAvatarID == gAgentID); - if (is_self) - { - getChild<LLUICtrl>("add_friend_btn")->setVisible(false); - getChild<LLUICtrl>("im_btn")->setVisible(false); - } - else if (is_friend) - { - getChild<LLUICtrl>("add_friend_btn")->setVisible(false); - getChild<LLUICtrl>("im_btn")->setVisible(true); - } - else - { - getChild<LLUICtrl>("add_friend_btn")->setVisible(true); - getChild<LLUICtrl>("im_btn")->setVisible(false); - } - // Use an avatar_icon even though the image id will come down with the // avatar properties because the avatar_icon code maintains a cache of icons // and this may result in the image being visible sooner. @@ -405,214 +257,6 @@ void LLInspectAvatar::processAvatarData(LLAvatarData* data) mPropertiesRequest = NULL; } -// For the avatar inspector, we only want to unpause the fade timer -// if neither the gear menu or self gear menu are open -void LLInspectAvatar::onMouseLeave(S32 x, S32 y, MASK mask) -{ - LLToggleableMenu* gear_menu = getChild<LLMenuButton>("gear_btn")->getMenu(); - LLToggleableMenu* gear_menu_self = getChild<LLMenuButton>("gear_self_btn")->getMenu(); - if ( gear_menu && gear_menu->getVisible() && - gear_menu_self && gear_menu_self->getVisible() ) - { - return; - } - - if(childHasVisiblePopupMenu()) - { - return; - } - - mOpenTimer.unpause(); -} - -void LLInspectAvatar::updateModeratorPanel() -{ - bool enable_moderator_panel = false; - - if (LLVoiceChannel::getCurrentVoiceChannel() && - mAvatarID != gAgent.getID()) - { - LLUUID session_id = LLVoiceChannel::getCurrentVoiceChannel()->getSessionID(); - - if (session_id != LLUUID::null) - { - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id); - - if (speaker_mgr) - { - LLPointer<LLSpeaker> self_speakerp = speaker_mgr->findSpeaker(gAgent.getID()); - LLPointer<LLSpeaker> selected_speakerp = speaker_mgr->findSpeaker(mAvatarID); - - if(speaker_mgr->isVoiceActive() && selected_speakerp && - selected_speakerp->isInVoiceChannel() && - ((self_speakerp && self_speakerp->mIsModerator) || gAgent.isGodlike())) - { - getChild<LLUICtrl>("enable_voice")->setVisible(selected_speakerp->mModeratorMutedVoice); - getChild<LLUICtrl>("disable_voice")->setVisible(!selected_speakerp->mModeratorMutedVoice); - - enable_moderator_panel = true; - } - } - } - } - - if (enable_moderator_panel) - { - if (!getChild<LLUICtrl>("moderator_panel")->getVisible()) - { - getChild<LLUICtrl>("moderator_panel")->setVisible(true); - // stretch the floater so it can accommodate the moderator panel - reshape(getRect().getWidth(), getRect().getHeight() + getChild<LLUICtrl>("moderator_panel")->getRect().getHeight()); - } - } - else if (getChild<LLUICtrl>("moderator_panel")->getVisible()) - { - getChild<LLUICtrl>("moderator_panel")->setVisible(false); - // shrink the inspector floater back to original size - reshape(getRect().getWidth(), getRect().getHeight() - getChild<LLUICtrl>("moderator_panel")->getRect().getHeight()); - } -} - -void LLInspectAvatar::toggleSelectedVoice(bool enabled) -{ - LLUUID session_id = LLVoiceChannel::getCurrentVoiceChannel()->getSessionID(); - LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(session_id); - - if (speaker_mgr) - { - if (!gAgent.getRegion()) - return; - - std::string url = gAgent.getRegion()->getCapability("ChatSessionRequest"); - LLSD data; - data["method"] = "mute update"; - data["session-id"] = session_id; - data["params"] = LLSD::emptyMap(); - data["params"]["agent_id"] = mAvatarID; - data["params"]["mute_info"] = LLSD::emptyMap(); - // ctrl value represents ability to type, so invert - data["params"]["mute_info"]["voice"] = !enabled; - - class MuteVoiceResponder : public LLHTTPClient::Responder - { - public: - MuteVoiceResponder(const LLUUID& session_id) - { - mSessionID = session_id; - } - - virtual void error(U32 status, const std::string& reason) - { - llwarns << status << ": " << reason << llendl; - - if ( gIMMgr ) - { - //403 == you're not a mod - //should be disabled if you're not a moderator - if ( 403 == status ) - { - gIMMgr->showSessionEventError( - "mute", - "not_a_moderator", - mSessionID); - } - else - { - gIMMgr->showSessionEventError( - "mute", - "generic", - mSessionID); - } - } - } - - private: - LLUUID mSessionID; - }; - - LLHTTPClient::post( - url, - data, - new MuteVoiceResponder(speaker_mgr->getSessionID())); - } - - closeFloater(); - -} - -void LLInspectAvatar::updateVolumeSlider() -{ - bool voice_enabled = LLVoiceClient::getInstance()->getVoiceEnabled(mAvatarID); - - // Do not display volume slider and mute button if it - // is ourself or we are not in a voice channel together - if (!voice_enabled || (mAvatarID == gAgent.getID())) - { - getChild<LLUICtrl>("mute_btn")->setVisible(false); - getChild<LLUICtrl>("volume_slider")->setVisible(false); - } - - else - { - getChild<LLUICtrl>("mute_btn")->setVisible(true); - getChild<LLUICtrl>("volume_slider")->setVisible(true); - - // By convention, we only display and toggle voice mutes, not all mutes - bool is_muted = LLMuteList::getInstance()-> - isMuted(mAvatarID, LLMute::flagVoiceChat); - - LLUICtrl* mute_btn = getChild<LLUICtrl>("mute_btn"); - - bool is_linden = LLStringUtil::endsWith(mAvatarName.getLegacyName(), " Linden"); - - mute_btn->setEnabled( !is_linden); - mute_btn->setValue( is_muted ); - - LLUICtrl* volume_slider = getChild<LLUICtrl>("volume_slider"); - volume_slider->setEnabled( !is_muted ); - - F32 volume; - - if (is_muted) - { - // it's clearer to display their volume as zero - volume = 0.f; - } - else - { - // actual volume - volume = LLVoiceClient::getInstance()->getUserVolume(mAvatarID); - } - volume_slider->setValue( (F64)volume ); - } - -} - -void LLInspectAvatar::onClickMuteVolume() -{ - // By convention, we only display and toggle voice mutes, not all mutes - LLMuteList* mute_list = LLMuteList::getInstance(); - bool is_muted = mute_list->isMuted(mAvatarID, LLMute::flagVoiceChat); - - LLMute mute(mAvatarID, mAvatarName.getLegacyName(), LLMute::AGENT); - if (!is_muted) - { - mute_list->add(mute, LLMute::flagVoiceChat); - } - else - { - mute_list->remove(mute, LLMute::flagVoiceChat); - } - - updateVolumeSlider(); -} - -void LLInspectAvatar::onVolumeChange(const LLSD& data) -{ - F32 volume = (F32)data.asReal(); - LLVoiceClient::getInstance()->setUserVolume(mAvatarID, volume); -} - void LLInspectAvatar::onAvatarNameCache( const LLUUID& agent_id, const LLAvatarName& av_name) @@ -640,215 +284,6 @@ void LLInspectAvatar::onAvatarNameCache( } } -void LLInspectAvatar::onClickAddFriend() -{ - LLAvatarActions::requestFriendshipDialog(mAvatarID, mAvatarName.getLegacyName()); - closeFloater(); -} - -void LLInspectAvatar::onClickViewProfile() -{ - LLAvatarActions::showProfile(mAvatarID); - closeFloater(); -} - -bool LLInspectAvatar::isNotFriend() -{ - return !LLAvatarActions::isFriend(mAvatarID); -} - -bool LLInspectAvatar::onVisibleFindOnMap() -{ - return gAgent.isGodlike() || is_agent_mappable(mAvatarID); -} - -bool LLInspectAvatar::onVisibleEject() -{ - return enable_freeze_eject( LLSD(mAvatarID) ); -} - -bool LLInspectAvatar::onVisibleFreeze() -{ - // either user is a god and can do long distance freeze - // or check for target proximity and permissions - return gAgent.isGodlike() || enable_freeze_eject(LLSD(mAvatarID)); -} - -bool LLInspectAvatar::onVisibleZoomIn() -{ - return gObjectList.findObject(mAvatarID); -} - -void LLInspectAvatar::onClickIM() -{ - LLAvatarActions::startIM(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickCall() -{ - LLAvatarActions::startCall(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickTeleport() -{ - LLAvatarActions::offerTeleport(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickInviteToGroup() -{ - LLAvatarActions::inviteToGroup(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickPay() -{ - LLAvatarActions::pay(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickShare() -{ - LLAvatarActions::share(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onToggleMute() -{ - LLMute mute(mAvatarID, mAvatarName.mDisplayName, LLMute::AGENT); - - if (LLMuteList::getInstance()->isMuted(mute.mID, mute.mName)) - { - LLMuteList::getInstance()->remove(mute); - } - else - { - LLMuteList::getInstance()->add(mute); - } - - LLPanelBlockedList::showPanelAndSelect(mute.mID); - closeFloater(); -} - -void LLInspectAvatar::onClickReport() -{ - LLFloaterReporter::showFromAvatar(mAvatarID, mAvatarName.getCompleteName()); - closeFloater(); -} - -bool godlike_freeze(const LLSD& notification, const LLSD& response) -{ - LLUUID avatar_id = notification["payload"]["avatar_id"].asUUID(); - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - - switch (option) - { - case 0: - LLAvatarActions::freeze(avatar_id); - break; - case 1: - LLAvatarActions::unfreeze(avatar_id); - break; - default: - break; - } - - return false; -} - -void LLInspectAvatar::onClickFreeze() -{ - if (gAgent.isGodlike()) - { - // use godlike freeze-at-a-distance, with confirmation - LLNotificationsUtil::add("FreezeAvatar", - LLSD(), - LLSD().with("avatar_id", mAvatarID), - godlike_freeze); - } - else - { - // use default "local" version of freezing that requires avatar to be in range - handle_avatar_freeze( LLSD(mAvatarID) ); - } - closeFloater(); -} - -void LLInspectAvatar::onClickEject() -{ - handle_avatar_eject( LLSD(mAvatarID) ); - closeFloater(); -} - -void LLInspectAvatar::onClickKick() -{ - LLAvatarActions::kick(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickCSR() -{ - std::string name; - gCacheName->getFullName(mAvatarID, name); - LLAvatarActions::csr(mAvatarID, name); - closeFloater(); -} - -void LLInspectAvatar::onClickZoomIn() -{ - handle_zoom_to_object(mAvatarID); - closeFloater(); -} - -void LLInspectAvatar::onClickFindOnMap() -{ - gFloaterWorldMap->trackAvatar(mAvatarID, mAvatarName.mDisplayName); - LLFloaterReg::showInstance("world_map"); -} - - -bool LLInspectAvatar::enableMute() -{ - bool is_linden = LLStringUtil::endsWith(mAvatarName.getLegacyName(), " Linden"); - bool is_self = mAvatarID == gAgent.getID(); - - if (!is_linden && !is_self && !LLMuteList::getInstance()->isMuted(mAvatarID, mAvatarName.getLegacyName())) - { - return true; - } - else - { - return false; - } -} - -bool LLInspectAvatar::enableUnmute() -{ - bool is_linden = LLStringUtil::endsWith(mAvatarName.getLegacyName(), " Linden"); - bool is_self = mAvatarID == gAgent.getID(); - - if (!is_linden && !is_self && LLMuteList::getInstance()->isMuted(mAvatarID, mAvatarName.getLegacyName())) - { - return true; - } - else - { - return false; - } -} - -bool LLInspectAvatar::enableTeleportOffer() -{ - return LLAvatarActions::canOfferTeleport(mAvatarID); -} - -bool LLInspectAvatar::godModeEnabled() -{ - return gAgent.isGodlike(); -} - ////////////////////////////////////////////////////////////////////////////// // LLInspectAvatarUtil ////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index b819100b9b..5d8d82b226 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -37,6 +37,7 @@ #include "llappearancemgr.h" #include "llattachmentsmgr.h" #include "llavataractions.h" +#include "llfavoritesbar.h" // management of favorites folder #include "llfloateropenobject.h" #include "llfloaterreg.h" #include "llfloatersidepanelcontainer.h" @@ -45,7 +46,7 @@ #include "llfriendcard.h" #include "llgesturemgr.h" #include "llgiveinventory.h" -#include "llimfloater.h" +#include "llimfloatercontainer.h" #include "llimview.h" #include "llclipboard.h" #include "llinventorydefines.h" @@ -115,10 +116,10 @@ void teleport_via_landmark(const LLUUID& asset_id); static BOOL can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit); static bool check_category(LLInventoryModel* model, const LLUUID& cat_id, - LLFolderView* active_folder_view, + LLInventoryPanel* active_panel, LLInventoryFilter* filter); static bool check_item(const LLUUID& item_id, - LLFolderView* active_folder_view, + LLInventoryPanel* active_panel, LLInventoryFilter* filter); // Helper functions @@ -189,7 +190,8 @@ LLInvFVBridge::LLInvFVBridge(LLInventoryPanel* inventory, mUUID(uuid), mRoot(root), mInvType(LLInventoryType::IT_NONE), - mIsLink(FALSE) + mIsLink(FALSE), + LLFolderViewModelItemInventory(inventory->getRootViewModel()) { mInventoryPanel = inventory->getInventoryPanelHandle(); const LLInventoryObject* obj = getInventoryObject(); @@ -208,7 +210,11 @@ const std::string& LLInvFVBridge::getName() const const std::string& LLInvFVBridge::getDisplayName() const { - return getName(); + if(mDisplayName.empty()) + { + buildDisplayName(); + } + return mDisplayName; } // Folders have full perms @@ -227,9 +233,24 @@ LLFolderType::EType LLInvFVBridge::getPreferredType() const // Folders don't have creation dates. time_t LLInvFVBridge::getCreationDate() const { - return 0; + LLInventoryObject* objectp = getInventoryObject(); + if (objectp) + { + return objectp->getCreationDate(); + } + return (time_t)0; } +void LLInvFVBridge::setCreationDate(time_t creation_date_utc) +{ + LLInventoryObject* objectp = getInventoryObject(); + if (objectp) + { + objectp->setCreationDate(creation_date_utc); + } +} + + // Can be destroyed (or moved to trash) BOOL LLInvFVBridge::isItemRemovable() const { @@ -283,7 +304,7 @@ void LLInvFVBridge::showProperties() */ } -void LLInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch) +void LLInvFVBridge::removeBatch(std::vector<LLFolderViewModelItem*>& batch) { // Deactivate gestures when moving them into Trash LLInvFVBridge* bridge; @@ -292,11 +313,11 @@ void LLInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batc LLViewerInventoryCategory* cat = NULL; LLInventoryModel::cat_array_t descendent_categories; LLInventoryModel::item_array_t descendent_items; - S32 count = batch.count(); + S32 count = batch.size(); S32 i,j; for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.get(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); if (item) @@ -309,7 +330,7 @@ void LLInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batc } for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.get(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID()); if (cat) @@ -327,7 +348,7 @@ void LLInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batc removeBatchNoCheck(batch); } -void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener*>& batch) +void LLInvFVBridge::removeBatchNoCheck(std::vector<LLFolderViewModelItem*>& batch) { // this method moves a bunch of items and folders to the trash. As // per design guidelines for the inventory model, the message is @@ -343,14 +364,14 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener* uuid_vec_t move_ids; LLInventoryModel::update_map_t update; bool start_new_message = true; - S32 count = batch.count(); + S32 count = batch.size(); S32 i; // first, hide any 'preview' floaters that correspond to the items // being deleted. for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.get(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); if(item) @@ -363,7 +384,7 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener* for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.get(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); if(item) @@ -404,7 +425,7 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener* for(i = 0; i < count; ++i) { - bridge = (LLInvFVBridge*)(batch.get(i)); + bridge = (LLInvFVBridge*)(batch[i]); if(!bridge || !bridge->isItemRemovable()) continue; LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID()); if(cat) @@ -556,7 +577,7 @@ void hide_context_entries(LLMenuGL& menu, // descend into split menus: LLMenuItemBranchGL* branchp = dynamic_cast<LLMenuItemBranchGL*>(menu_item); - if ((name == "More") && branchp) + if (NULL != branchp) { hide_context_entries(*branchp->getBranch(), entries_to_show, disabled_entries); } @@ -876,6 +897,12 @@ LLInventoryModel* LLInvFVBridge::getInventoryModel() const return panel ? panel->getModel() : NULL; } +LLInventoryFilter* LLInvFVBridge::getInventoryFilter() const +{ + LLInventoryPanel* panel = mInventoryPanel.get(); + return panel ? &(panel->getFilter()) : NULL; +} + BOOL LLInvFVBridge::isItemInTrash() const { LLInventoryModel* model = getInventoryModel(); @@ -928,7 +955,7 @@ BOOL LLInvFVBridge::isCOFFolder() const BOOL LLInvFVBridge::isInboxFolder() const { - const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false, false); + const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false); if (inbox_id.isNull()) { @@ -968,7 +995,7 @@ BOOL LLInvFVBridge::isOutboxFolderDirectParent() const const LLUUID LLInvFVBridge::getOutboxFolder() const { - const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false); + const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); return outbox_id; } @@ -1000,6 +1027,7 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type, LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags) @@ -1250,10 +1278,10 @@ bool LLInvFVBridge::canListOnMarketplaceNow() const if (can_list) { - LLFolderViewFolder * object_folderp = mRoot->getFolderByID(object_id); + LLFolderViewFolder * object_folderp = mInventoryPanel.get() ? mInventoryPanel.get()->getFolderByID(object_id) : NULL; if (object_folderp) { - can_list = !object_folderp->isLoading(); + can_list = !static_cast<LLFolderBridge*>(object_folderp->getViewModelItem())->isLoading(); } } @@ -1261,7 +1289,7 @@ bool LLInvFVBridge::canListOnMarketplaceNow() const { // Get outbox id const LLUUID & outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); - LLFolderViewItem * outbox_itemp = mRoot->getItemByID(outbox_id); + LLFolderViewItem * outbox_itemp = mInventoryPanel.get() ? mInventoryPanel.get()->getItemByID(outbox_id) : NULL; if (outbox_itemp) { @@ -1271,7 +1299,7 @@ bool LLInvFVBridge::canListOnMarketplaceNow() const void * cargo_data = (void *) obj; std::string tooltip_msg; - can_list = outbox_itemp->getListener()->dragOrDrop(mask, drop, cargo_type, cargo_data, tooltip_msg); + can_list = outbox_itemp->getViewModelItem()->dragOrDrop(mask, drop, cargo_type, cargo_data, tooltip_msg); } } } @@ -1283,14 +1311,30 @@ bool LLInvFVBridge::canListOnMarketplaceNow() const #endif } +LLToolDragAndDrop::ESource LLInvFVBridge::getDragSource() const +{ + if (gInventory.isObjectDescendentOf(getUUID(), gInventory.getRootFolderID())) + { + return LLToolDragAndDrop::SOURCE_AGENT; + } + else if (gInventory.isObjectDescendentOf(getUUID(), gInventory.getLibraryRootFolderID())) + { + return LLToolDragAndDrop::SOURCE_LIBRARY; + } + + return LLToolDragAndDrop::SOURCE_VIEWER; +} + + // +=================================================+ // | InventoryFVBridgeBuilder | // +=================================================+ -LLInvFVBridge* LLInventoryFVBridgeBuilder::createBridge(LLAssetType::EType asset_type, +LLInvFVBridge* LLInventoryFolderViewModelBuilder::createBridge(LLAssetType::EType asset_type, LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags /* = 0x00 */) const @@ -1299,6 +1343,7 @@ LLInvFVBridge* LLInventoryFVBridgeBuilder::createBridge(LLAssetType::EType asset actual_asset_type, inv_type, inventory, + view_model, root, uuid, flags); @@ -1355,10 +1400,7 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) else if ("cut" == action) { cutToClipboard(); - // MAINT-1197: This is temp code to work around a deselection/reselection bug. Please discard when merging CHUI. - LLFolderViewItem* item_to_select = mRoot->getNextUnselectedItem(); - LLFolderView::removeCutItems(); - mRoot->setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, false); + gInventory.removeObject(mUUID); return; } else if ("copy" == action) @@ -1371,10 +1413,10 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) LLInventoryItem* itemp = model->getItem(mUUID); if (!itemp) return; - LLFolderViewItem* folder_view_itemp = mRoot->getItemByID(itemp->getParentUUID()); + LLFolderViewItem* folder_view_itemp = mInventoryPanel.get()->getItemByID(itemp->getParentUUID()); if (!folder_view_itemp) return; - folder_view_itemp->getListener()->pasteFromClipboard(); + folder_view_itemp->getViewModelItem()->pasteFromClipboard(); return; } else if ("paste_link" == action) @@ -1383,10 +1425,10 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) LLInventoryItem* itemp = model->getItem(mUUID); if (!itemp) return; - LLFolderViewItem* folder_view_itemp = mRoot->getItemByID(itemp->getParentUUID()); + LLFolderViewItem* folder_view_itemp = mInventoryPanel.get()->getItemByID(itemp->getParentUUID()); if (!folder_view_itemp) return; - folder_view_itemp->getListener()->pasteLinkFromClipboard(); + folder_view_itemp->getViewModelItem()->pasteLinkFromClipboard(); return; } else if (isMarketplaceCopyAction(action)) @@ -1396,7 +1438,7 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) LLInventoryItem* itemp = model->getItem(mUUID); if (!itemp) return; - const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false); + const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); copy_item_to_outbox(itemp, outbox_id, LLUUID::null, LLToolDragAndDrop::getOperationId()); } } @@ -1489,6 +1531,15 @@ LLUIImagePtr LLItemBridge::getIcon() const return LLInventoryIcon::getIcon(LLInventoryIcon::ICONNAME_OBJECT); } +LLUIImagePtr LLItemBridge::getIconOverlay() const +{ + if (getItem() && getItem()->getIsLinkType()) + { + return LLUI::getUIImage("Inv_Link"); + } + return NULL; +} + PermissionMask LLItemBridge::getPermissionMask() const { LLViewerInventoryItem* item = getItem(); @@ -1497,26 +1548,27 @@ PermissionMask LLItemBridge::getPermissionMask() const return perm_mask; } -const std::string& LLItemBridge::getDisplayName() const +void LLItemBridge::buildDisplayName() const { - if(mDisplayName.empty()) + if(getItem()) { - buildDisplayName(getItem(), mDisplayName); + mDisplayName.assign(getItem()->getName()); } - return mDisplayName; + else + { + mDisplayName.assign(LLStringUtil::null); } -void LLItemBridge::buildDisplayName(LLInventoryItem* item, std::string& name) + mSearchableName.assign(mDisplayName); + mSearchableName.append(getLabelSuffix()); + LLStringUtil::toUpper(mSearchableName); + + //Name set, so trigger a sort + if(mParent) { - if(item) - { - name.assign(item->getName()); + mParent->requestSort(); } - else - { - name.assign(LLStringUtil::null); } -} LLFontGL::StyleFlags LLItemBridge::getLabelStyle() const { @@ -1632,18 +1684,17 @@ BOOL LLItemBridge::renameItem(const std::string& new_name) { LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); new_item->rename(new_name); - buildDisplayName(new_item, mDisplayName); new_item->updateServer(FALSE); model->updateItem(new_item); model->notifyObservers(); + buildDisplayName(); } // return FALSE because we either notified observers (& therefore // rebuilt) or we didn't update. return FALSE; } - BOOL LLItemBridge::removeItem() { if(!isItemRemovable()) @@ -1651,7 +1702,6 @@ BOOL LLItemBridge::removeItem() return FALSE; } - // move it to the trash LLPreview::hide(mUUID, TRUE); LLInventoryModel* model = getInventoryModel(); @@ -1789,6 +1839,99 @@ void LLFolderBridge::selectItem() LLInventoryModelBackgroundFetch::instance().start(getUUID(), true); } +void LLFolderBridge::buildDisplayName() const +{ + LLFolderType::EType preferred_type = getPreferredType(); + + // *TODO: to be removed when database supports multi language. This is a + // temporary attempt to display the inventory folder in the user locale. + // mantipov: *NOTE: be sure this code is synchronized with LLFriendCardsManager::findChildFolderUUID + // it uses the same way to find localized string + + // HACK: EXT - 6028 ([HARD CODED]? Inventory > Library > "Accessories" folder) + // Translation of Accessories folder in Library inventory folder + bool accessories = false; + if(getName() == "Accessories") + { + //To ensure that Accessories folder is in Library we have to check its parent folder. + //Due to parent LLFolderViewFloder is not set to this item yet we have to check its parent via Inventory Model + LLInventoryCategory* cat = gInventory.getCategory(getUUID()); + if(cat) + { + const LLUUID& parent_folder_id = cat->getParentUUID(); + accessories = (parent_folder_id == gInventory.getLibraryRootFolderID()); + } + } + + //"Accessories" inventory category has folder type FT_NONE. So, this folder + //can not be detected as protected with LLFolderType::lookupIsProtectedType + mDisplayName.assign(getName()); + if (accessories || LLFolderType::lookupIsProtectedType(preferred_type)) + { + LLTrans::findString(mDisplayName, std::string("InvFolder ") + getName(), LLSD()); + } + + mSearchableName.assign(mDisplayName); + mSearchableName.append(getLabelSuffix()); + LLStringUtil::toUpper(mSearchableName); + + //Name set, so trigger a sort + if(mParent) + { + mParent->requestSort(); + } +} + + +void LLFolderBridge::update() +{ + bool possibly_has_children = false; + bool up_to_date = isUpToDate(); + if(!up_to_date && hasChildren()) // we know we have children but haven't fetched them (doesn't obey filter) + { + possibly_has_children = true; + } + + bool loading = (possibly_has_children + && !up_to_date ); + + if (loading != mIsLoading) + { + if ( loading && !mIsLoading ) + { + // Measure how long we've been in the loading state + mTimeSinceRequestStart.reset(); + } + + const BOOL in_inventory = gInventory.isObjectDescendentOf(getUUID(), gInventory.getRootFolderID()); + const BOOL in_library = gInventory.isObjectDescendentOf(getUUID(), gInventory.getLibraryRootFolderID()); + + bool root_is_loading = false; + if (in_inventory) + { + root_is_loading = LLInventoryModelBackgroundFetch::instance().inventoryFetchInProgress(); + } + if (in_library) + { + root_is_loading = LLInventoryModelBackgroundFetch::instance().libraryFetchInProgress(); + } + if ((mIsLoading + && mTimeSinceRequestStart.getElapsedTimeF32() >= gSavedSettings.getF32("FolderLoadingMessageWaitTime")) + || (LLInventoryModelBackgroundFetch::instance().folderFetchActive() + && root_is_loading)) + { + mDisplayName = LLInvFVBridge::getDisplayName() + " ( " + LLTrans::getString("LoadingData") + " ) "; + mIsLoading = true; + } + else + { + mDisplayName = LLInvFVBridge::getDisplayName(); + mIsLoading = false; + } + } +} + + // Iterate through a folder's children to determine if // all the children are removable. class LLIsItemRemovable : public LLFolderViewFunctor @@ -1797,11 +1940,11 @@ public: LLIsItemRemovable() : mPassed(TRUE) {} virtual void doFolder(LLFolderViewFolder* folder) { - mPassed &= folder->getListener()->isItemRemovable(); + mPassed &= folder->getViewModelItem()->isItemRemovable(); } virtual void doItem(LLFolderViewItem* item) { - mPassed &= item->getListener()->isItemRemovable(); + mPassed &= item->getViewModelItem()->isItemRemovable(); } BOOL mPassed; }; @@ -1815,7 +1958,7 @@ BOOL LLFolderBridge::isItemRemovable() const } LLInventoryPanel* panel = mInventoryPanel.get(); - LLFolderViewFolder* folderp = dynamic_cast<LLFolderViewFolder*>(panel ? panel->getRootFolder()->getItemByID(mUUID) : NULL); + LLFolderViewFolder* folderp = dynamic_cast<LLFolderViewFolder*>(panel ? panel->getItemByID(mUUID) : NULL); if (folderp) { LLIsItemRemovable folder_test; @@ -2054,7 +2197,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, LLInventoryPanel* destination_panel = mInventoryPanel.get(); if (!destination_panel) return false; - LLInventoryFilter* filter = destination_panel->getFilter(); + LLInventoryFilter* filter = getInventoryFilter(); if (!filter) return false; const LLUUID &cat_id = inv_cat->getUUID(); @@ -2273,7 +2416,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, { // Check whether the folder being dragged from active inventory panel // passes the filter of the destination panel. - is_movable = check_category(model, cat_id, active_folder_view, filter); + is_movable = check_category(model, cat_id, active_panel, filter); } } } @@ -2347,7 +2490,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } else { - if (model->isObjectDescendentOf(cat_id, model->findCategoryUUIDForType(LLFolderType::FT_INBOX, false, false))) + if (model->isObjectDescendentOf(cat_id, model->findCategoryUUIDForType(LLFolderType::FT_INBOX, false))) { set_dad_inbox_object(cat_id); } @@ -2699,7 +2842,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) { if ("open" == action) { - LLFolderViewFolder *f = dynamic_cast<LLFolderViewFolder *>(mRoot->getItemByID(mUUID)); + LLFolderViewFolder *f = dynamic_cast<LLFolderViewFolder *>(mInventoryPanel.get()->getItemByID(mUUID)); if (f) { f->setOpen(TRUE); @@ -2746,10 +2889,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) else if ("cut" == action) { cutToClipboard(); - // MAINT-1197: This is temp code to work around a deselection/reselection bug. Please discard when merging CHUI. - LLFolderViewItem* item_to_select = mRoot->getNextUnselectedItem(); - LLFolderView::removeCutItems(); - mRoot->setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, false); + gInventory.removeObject(mUUID); return; } else if ("copy" == action) @@ -2790,7 +2930,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) LLInventoryCategory * cat = gInventory.getCategory(mUUID); if (!cat) return; - const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false); + const LLUUID outbox_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); copy_folder_to_outbox(cat, outbox_id, cat->getUUID(), LLToolDragAndDrop::getOperationId()); } #if ENABLE_MERCHANT_SEND_TO_MARKETPLACE_CONTEXT_MENU @@ -2886,17 +3026,24 @@ LLUIImagePtr LLFolderBridge::getIcon() const LLUIImagePtr LLFolderBridge::getIcon(LLFolderType::EType preferred_type) { return LLUI::getUIImage(LLViewerFolderType::lookupIconName(preferred_type, FALSE)); - /*case LLAssetType::AT_MESH: - control = "inv_folder_mesh.tga"; - break;*/ } -LLUIImagePtr LLFolderBridge::getOpenIcon() const +LLUIImagePtr LLFolderBridge::getIconOpen() const { return LLUI::getUIImage(LLViewerFolderType::lookupIconName(getPreferredType(), TRUE)); } +LLUIImagePtr LLFolderBridge::getIconOverlay() const +{ + if (getInventoryObject() && getInventoryObject()->getIsLinkType()) + { + return LLUI::getUIImage("Inv_Link"); + } + return NULL; +} + + BOOL LLFolderBridge::renameItem(const std::string& new_name) { rename_category(getInventoryModel(), mUUID, new_name); @@ -2960,6 +3107,19 @@ bool LLFolderBridge::removeItemResponse(const LLSD& notification, const LLSD& re return FALSE; } +//Recursively update the folder's creation date +void LLFolderBridge::updateHierarchyCreationDate(time_t date) +{ + if(getCreationDate() < date) + { + setCreationDate(date); + if(mParent) + { + static_cast<LLFolderBridge *>(mParent)->updateHierarchyCreationDate(date); + } + } +} + void LLFolderBridge::pasteFromClipboard() { LLInventoryModel* model = getInventoryModel(); @@ -2977,7 +3137,7 @@ void LLFolderBridge::pasteFromClipboard() if (move_is_into_outbox) { - LLFolderViewItem * outbox_itemp = mRoot->getItemByID(mUUID); + LLFolderViewItem * outbox_itemp = mInventoryPanel.get()->getItemByID(mUUID); if (outbox_itemp) { @@ -3000,7 +3160,7 @@ void LLFolderBridge::pasteFromClipboard() void * cargo_data = (void *) item; std::string tooltip_msg; - can_list = outbox_itemp->getListener()->dragOrDrop(mask, drop, cargo_type, cargo_data, tooltip_msg); + can_list = outbox_itemp->getViewModelItem()->dragOrDrop(mask, drop, cargo_type, cargo_data, tooltip_msg); } } @@ -3042,7 +3202,8 @@ void LLFolderBridge::pasteFromClipboard() LLViewerInventoryCategory* vicat = (LLViewerInventoryCategory *) model->getCategory(item_id); llassert(vicat); if (vicat) - { + { + //changeCategoryParent() implicity calls dirtyFilter changeCategoryParent(model, vicat, parent_id, FALSE); } } @@ -3052,6 +3213,7 @@ void LLFolderBridge::pasteFromClipboard() llassert(viitem); if (viitem) { + //changeItemParent() implicity calls dirtyFilter changeItemParent(model, viitem, parent_id, FALSE); } } @@ -3172,7 +3334,7 @@ BOOL LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInv return ((item_array.count() > 0) ? TRUE : FALSE ); } -void LLFolderBridge::buildContextMenuBaseOptions(U32 flags) +void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items) { LLInventoryModel* model = getInventoryModel(); llassert(model != NULL); @@ -3183,30 +3345,30 @@ void LLFolderBridge::buildContextMenuBaseOptions(U32 flags) if (lost_and_found_id == mUUID) { // This is the lost+found folder. - mItems.push_back(std::string("Empty Lost And Found")); + items.push_back(std::string("Empty Lost And Found")); - mDisabledItems.push_back(std::string("New Folder")); - mDisabledItems.push_back(std::string("New Script")); - mDisabledItems.push_back(std::string("New Note")); - mDisabledItems.push_back(std::string("New Gesture")); - mDisabledItems.push_back(std::string("New Clothes")); - mDisabledItems.push_back(std::string("New Body Parts")); + disabled_items.push_back(std::string("New Folder")); + disabled_items.push_back(std::string("New Script")); + disabled_items.push_back(std::string("New Note")); + disabled_items.push_back(std::string("New Gesture")); + disabled_items.push_back(std::string("New Clothes")); + disabled_items.push_back(std::string("New Body Parts")); } if(trash_id == mUUID) { // This is the trash. - mItems.push_back(std::string("Empty Trash")); + items.push_back(std::string("Empty Trash")); } else if(isItemInTrash()) { // This is a folder in the trash. - mItems.clear(); // clear any items that used to exist - addTrashContextMenuOptions(mItems, mDisabledItems); + items.clear(); // clear any items that used to exist + addTrashContextMenuOptions(items, disabled_items); } else if(isOutboxFolder()) { - addOutboxContextMenuOptions(flags, mItems, mDisabledItems); + addOutboxContextMenuOptions(flags, items, disabled_items); } else if(isAgentInventory()) // do not allow creating in library { @@ -3220,40 +3382,40 @@ void LLFolderBridge::buildContextMenuBaseOptions(U32 flags) // Do not allow to create 2-level subfolder in the Calling Card/Friends folder. EXT-694. if (!LLFriendCardsManager::instance().isCategoryInFriendFolder(cat)) { - mItems.push_back(std::string("New Folder")); + items.push_back(std::string("New Folder")); } - mItems.push_back(std::string("New Script")); - mItems.push_back(std::string("New Note")); - mItems.push_back(std::string("New Gesture")); - mItems.push_back(std::string("New Clothes")); - mItems.push_back(std::string("New Body Parts")); + items.push_back(std::string("New Script")); + items.push_back(std::string("New Note")); + items.push_back(std::string("New Gesture")); + items.push_back(std::string("New Clothes")); + items.push_back(std::string("New Body Parts")); } #if SUPPORT_ENSEMBLES // Changing folder types is an unfinished unsupported feature // and can lead to unexpected behavior if enabled. - mItems.push_back(std::string("Change Type")); + items.push_back(std::string("Change Type")); const LLViewerInventoryCategory *cat = getCategory(); if (cat && LLFolderType::lookupIsProtectedType(cat->getPreferredType())) { - mDisabledItems.push_back(std::string("Change Type")); + disabled_items.push_back(std::string("Change Type")); } #endif - getClipboardEntries(false, mItems, mDisabledItems, flags); + getClipboardEntries(false, items, disabled_items, flags); } else { // Want some but not all of the items from getClipboardEntries for outfits. if (cat && (cat->getPreferredType() == LLFolderType::FT_OUTFIT)) { - mItems.push_back(std::string("Rename")); + items.push_back(std::string("Rename")); - addDeleteContextMenuOptions(mItems, mDisabledItems); + addDeleteContextMenuOptions(items, disabled_items); // EXT-4030: disallow deletion of currently worn outfit const LLViewerInventoryItem *base_outfit_link = LLAppearanceMgr::instance().getBaseOutfitLink(); if (base_outfit_link && (cat == base_outfit_link->getLinkedCategory())) { - mDisabledItems.push_back(std::string("Delete")); + disabled_items.push_back(std::string("Delete")); } } } @@ -3282,20 +3444,44 @@ void LLFolderBridge::buildContextMenuBaseOptions(U32 flags) // Preemptively disable system folder removal if more than one item selected. if ((flags & FIRST_SELECTED_ITEM) == 0) { - mDisabledItems.push_back(std::string("Delete System Folder")); + disabled_items.push_back(std::string("Delete System Folder")); } if (!isOutboxFolder()) { - mItems.push_back(std::string("Share")); + items.push_back(std::string("Share")); if (!canShare()) { - mDisabledItems.push_back(std::string("Share")); + disabled_items.push_back(std::string("Share")); + } + } + // Add menu items that are dependent on the contents of the folder. + LLViewerInventoryCategory* category = (LLViewerInventoryCategory *) model->getCategory(mUUID); + if (category) + { + uuid_vec_t folders; + folders.push_back(category->getUUID()); + + sSelf = getHandle(); + LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(folders); + fetch->startFetch(); + if (fetch->isFinished()) + { + // Do not call execute() or done() here as if the folder is here, there's likely no point drilling down + // This saves lots of time as buildContextMenu() is called a lot + delete fetch; + buildContextMenuFolderOptions(flags, items, disabled_items); } + else + { + // it's all on its way - add an observer, and the inventory will call done for us when everything is here. + inc_busy_count(); + gInventory.addObserver(fetch); } } +} -void LLFolderBridge::buildContextMenuFolderOptions(U32 flags) +void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items) { // Build folder specific options back up LLInventoryModel* model = getInventoryModel(); @@ -3322,21 +3508,21 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags) LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD); if (mCallingCards || checkFolderForContentsOfType(model, is_callingcard)) { - mItems.push_back(std::string("Calling Card Separator")); - mItems.push_back(std::string("Conference Chat Folder")); - mItems.push_back(std::string("IM All Contacts In Folder")); + items.push_back(std::string("Calling Card Separator")); + items.push_back(std::string("Conference Chat Folder")); + items.push_back(std::string("IM All Contacts In Folder")); } } if (!isItemRemovable()) { - mDisabledItems.push_back(std::string("Delete")); + disabled_items.push_back(std::string("Delete")); } #ifndef LL_RELEASE_FOR_DOWNLOAD if (LLFolderType::lookupIsProtectedType(type)) { - mItems.push_back(std::string("Delete System Folder")); + items.push_back(std::string("Delete System Folder")); } #endif @@ -3351,7 +3537,7 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags) checkFolderForContentsOfType(model, is_object) || checkFolderForContentsOfType(model, is_gesture) ) { - mItems.push_back(std::string("Folder Wearables Separator")); + items.push_back(std::string("Folder Wearables Separator")); // Only enable add/replace outfit for non-system folders. if (!is_system_folder) @@ -3359,25 +3545,25 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags) // Adding an outfit onto another (versus replacing) doesn't make sense. if (type != LLFolderType::FT_OUTFIT) { - mItems.push_back(std::string("Add To Outfit")); + items.push_back(std::string("Add To Outfit")); } - mItems.push_back(std::string("Replace Outfit")); + items.push_back(std::string("Replace Outfit")); } if (is_ensemble) { - mItems.push_back(std::string("Wear As Ensemble")); + items.push_back(std::string("Wear As Ensemble")); } - mItems.push_back(std::string("Remove From Outfit")); + items.push_back(std::string("Remove From Outfit")); if (!LLAppearanceMgr::getCanRemoveFromCOF(mUUID)) { - mDisabledItems.push_back(std::string("Remove From Outfit")); + disabled_items.push_back(std::string("Remove From Outfit")); } if (!LLAppearanceMgr::instance().getCanReplaceCOF(mUUID)) { - mDisabledItems.push_back(std::string("Replace Outfit")); + disabled_items.push_back(std::string("Replace Outfit")); } - mItems.push_back(std::string("Outfit Separator")); + items.push_back(std::string("Outfit Separator")); } } @@ -3386,49 +3572,28 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { sSelf.markDead(); - mItems.clear(); - mDisabledItems.clear(); + // fetch contents of this folder, as context menu can depend on contents + // still, user would have to open context menu again to see the changes + gInventory.fetchDescendentsOf(getUUID()); + + + menuentry_vec_t items; + menuentry_vec_t disabled_items; lldebugs << "LLFolderBridge::buildContextMenu()" << llendl; LLInventoryModel* model = getInventoryModel(); if(!model) return; - buildContextMenuBaseOptions(flags); - - // Add menu items that are dependent on the contents of the folder. - LLViewerInventoryCategory* category = (LLViewerInventoryCategory *) model->getCategory(mUUID); - if (category) - { - uuid_vec_t folders; - folders.push_back(category->getUUID()); - - sSelf = getHandle(); - LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(folders); - fetch->startFetch(); - if (fetch->isFinished()) - { - // Do not call execute() or done() here as if the folder is here, there's likely no point drilling down - // This saves lots of time as buildContextMenu() is called a lot - delete fetch; - buildContextMenuFolderOptions(flags); - } - else - { - // it's all on its way - add an observer, and the inventory will call done for us when everything is here. - inc_busy_count(); - gInventory.addObserver(fetch); - } - } - - hide_context_entries(menu, mItems, mDisabledItems); + buildContextMenuOptions(flags, items, disabled_items); + hide_context_entries(menu, items, disabled_items); // Reposition the menu, in case we're adding items to an existing menu. menu.needsArrange(); menu.arrangeAndClear(); } -BOOL LLFolderBridge::hasChildren() const +bool LLFolderBridge::hasChildren() const { LLInventoryModel* model = getInventoryModel(); if(!model) return FALSE; @@ -3518,25 +3683,6 @@ void LLFolderBridge::pasteClipboard(void* user_data) if(self) self->pasteFromClipboard(); } -void LLFolderBridge::createNewCategory(void* user_data) -{ - LLFolderBridge* bridge = (LLFolderBridge*)user_data; - if(!bridge) return; - LLInventoryPanel* panel = bridge->mInventoryPanel.get(); - if (!panel) return; - LLInventoryModel* model = panel->getModel(); - if(!model) return; - LLUUID id; - id = model->createNewCategory(bridge->getUUID(), - LLFolderType::FT_NONE, - LLStringUtil::null); - model->notifyObservers(); - - // At this point, the bridge has probably been deleted, but the - // view is still there. - panel->setSelection(id, TAKE_FOCUS_YES); -} - void LLFolderBridge::createNewShirt(void* user_data) { LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_SHIRT); @@ -3602,6 +3748,24 @@ void LLFolderBridge::createNewEyes(void* user_data) LLFolderBridge::createWearable((LLFolderBridge*)user_data, LLWearableType::WT_EYES); } +EInventorySortGroup LLFolderBridge::getSortGroup() const +{ + LLFolderType::EType preferred_type = getPreferredType(); + + if (preferred_type == LLFolderType::FT_TRASH) + { + return SG_TRASH_FOLDER; + } + + if(LLFolderType::lookupIsProtectedType(preferred_type)) + { + return SG_SYSTEM_FOLDER; + } + + return SG_NORMAL_FOLDER; +} + + // static void LLFolderBridge::createWearable(LLFolderBridge* bridge, LLWearableType::EType type) { @@ -3704,9 +3868,10 @@ void LLFolderBridge::dropToFavorites(LLInventoryItem* inv_item) LLPointer<AddFavoriteLandmarkCallback> cb = new AddFavoriteLandmarkCallback(); LLInventoryPanel* panel = mInventoryPanel.get(); LLFolderViewItem* drag_over_item = panel ? panel->getRootFolder()->getDraggingOverItem() : NULL; - if (drag_over_item && drag_over_item->getListener()) + LLFolderViewModelItemInventory* view_model = drag_over_item ? static_cast<LLFolderViewModelItemInventory*>(drag_over_item->getViewModelItem()) : NULL; + if (view_model) { - cb.get()->setTargetLandmarkId(drag_over_item->getListener()->getUUID()); + cb.get()->setTargetLandmarkId(view_model->getUUID()); } copy_inventory_item( @@ -3755,7 +3920,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, LLInventoryPanel* destination_panel = mInventoryPanel.get(); if (!destination_panel) return false; - LLInventoryFilter* filter = destination_panel->getFilter(); + LLInventoryFilter* filter = getInventoryFilter(); if (!filter) return false; const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); @@ -3872,13 +4037,10 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // passes the filter of the destination panel. if (accept && active_panel) { - LLFolderView* active_folder_view = active_panel->getRootFolder(); - if (!active_folder_view) return false; - - LLFolderViewItem* fv_item = active_folder_view->getItemByID(inv_item->getUUID()); + LLFolderViewItem* fv_item = active_panel->getItemByID(inv_item->getUUID()); if (!fv_item) return false; - accept = filter->check(fv_item); + accept = filter->check(fv_item->getViewModelItem()); } if (accept && drop) @@ -3890,6 +4052,8 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, } // If an item is being dragged between windows, unselect everything in the active window // so that we don't follow the selection to its new location (which is very annoying). + // RN: a better solution would be to deselect automatically when an item is moved + // and then select any item that is dropped only in the panel that it is dropped in if (active_panel && (destination_panel != active_panel)) { active_panel->unSelectAll(); @@ -3907,8 +4071,8 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, if (itemp) { LLUUID srcItemId = inv_item->getUUID(); - LLUUID destItemId = itemp->getListener()->getUUID(); - gInventory.rearrangeFavoriteLandmarks(srcItemId, destItemId); + LLUUID destItemId = static_cast<LLFolderViewModelItemInventory*>(itemp->getViewModelItem())->getUUID(); + LLFavoritesOrderStorage::instance().rearrangeFavoriteLandmarks(srcItemId, destItemId); } } @@ -3940,7 +4104,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, else { // set up observer to select item once drag and drop from inbox is complete - if (gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false, false))) + if (gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false))) { set_dad_inbox_object(inv_item->getUUID()); } @@ -4095,13 +4259,10 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // passes the filter of the destination panel. if (accept && active_panel) { - LLFolderView* active_folder_view = active_panel->getRootFolder(); - if (!active_folder_view) return false; - - LLFolderViewItem* fv_item = active_folder_view->getItemByID(inv_item->getUUID()); + LLFolderViewItem* fv_item = active_panel->getItemByID(inv_item->getUUID()); if (!fv_item) return false; - accept = filter->check(fv_item); + accept = filter->check(fv_item->getViewModelItem()); } if (accept && drop) @@ -4141,10 +4302,10 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // static bool check_category(LLInventoryModel* model, const LLUUID& cat_id, - LLFolderView* active_folder_view, + LLInventoryPanel* active_panel, LLInventoryFilter* filter) { - if (!model || !active_folder_view || !filter) + if (!model || !active_panel || !filter) return false; if (!filter->checkFolder(cat_id)) @@ -4164,13 +4325,13 @@ bool check_category(LLInventoryModel* model, // Empty folder should be checked as any other folder view item. // If we are filtering by date the folder should not pass because // it doesn't have its own creation date. See LLInvFVBridge::getCreationDate(). - return check_item(cat_id, active_folder_view, filter); + return check_item(cat_id, active_panel, filter); } for (S32 i = 0; i < num_descendent_categories; ++i) { LLInventoryCategory* category = descendent_categories[i]; - if(!check_category(model, category->getUUID(), active_folder_view, filter)) + if(!check_category(model, category->getUUID(), active_panel, filter)) { return false; } @@ -4179,7 +4340,7 @@ bool check_category(LLInventoryModel* model, for (S32 i = 0; i < num_descendent_items; ++i) { LLViewerInventoryItem* item = descendent_items[i]; - if(!check_item(item->getUUID(), active_folder_view, filter)) + if(!check_item(item->getUUID(), active_panel, filter)) { return false; } @@ -4190,15 +4351,15 @@ bool check_category(LLInventoryModel* model, // static bool check_item(const LLUUID& item_id, - LLFolderView* active_folder_view, + LLInventoryPanel* active_panel, LLInventoryFilter* filter) { - if (!active_folder_view || !filter) return false; + if (!active_panel || !filter) return false; - LLFolderViewItem* fv_item = active_folder_view->getItemByID(item_id); + LLFolderViewItem* fv_item = active_panel->getItemByID(item_id); if (!fv_item) return false; - return filter->check(fv_item); + return filter->check(fv_item->getViewModelItem()); } // +=================================================+ @@ -4300,15 +4461,6 @@ void LLSoundBridge::openItem() } } -void LLSoundBridge::previewItem() -{ - LLViewerInventoryItem* item = getItem(); - if(item) - { - send_sound_trigger(item->getAssetUUID(), 1.0); - } -} - void LLSoundBridge::openSoundPreview(void* which) { LLSoundBridge *me = (LLSoundBridge *)which; @@ -4524,7 +4676,7 @@ LLCallingCardBridge::~LLCallingCardBridge() void LLCallingCardBridge::refreshFolderViewItem() { LLInventoryPanel* panel = mInventoryPanel.get(); - LLFolderViewItem* itemp = panel ? panel->getRootFolder()->getItemByID(mUUID) : NULL; + LLFolderViewItem* itemp = panel ? panel->getItemByID(mUUID) : NULL; if (itemp) { itemp->refresh(); @@ -4552,7 +4704,7 @@ void LLCallingCardBridge::performAction(LLInventoryModel* model, std::string act LLUUID session_id = gIMMgr->addSession(callingcard_name, IM_NOTHING_SPECIAL, item->getCreatorUUID()); if (session_id != LLUUID::null) { - LLIMFloater::show(session_id); + LLIMFloaterContainer::getInstance()->showConversation(session_id); } } } @@ -5315,11 +5467,10 @@ BOOL LLObjectBridge::renameItem(const std::string& new_name) { LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); new_item->rename(new_name); - buildDisplayName(new_item, mDisplayName); new_item->updateServer(FALSE); model->updateItem(new_item); - model->notifyObservers(); + buildDisplayName(); if (isAgentAvatarValid()) { @@ -5919,16 +6070,6 @@ void LLMeshBridge::openItem() } } -void LLMeshBridge::previewItem() -{ - LLViewerInventoryItem* item = getItem(); - if(item) - { - // preview mesh - } -} - - void LLMeshBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { lldebugs << "LLMeshBridge::buildContextMenu()" << llendl; @@ -6017,14 +6158,15 @@ void LLLinkFolderBridge::gotoItem() const LLUUID &cat_uuid = getFolderID(); if (!cat_uuid.isNull()) { - if (LLFolderViewItem *base_folder = mRoot->getItemByID(cat_uuid)) + LLFolderViewItem *base_folder = mInventoryPanel.get()->getItemByID(cat_uuid); + if (base_folder) { if (LLInventoryModel* model = getInventoryModel()) { model->fetchDescendentsOf(cat_uuid); } base_folder->setOpen(TRUE); - mRoot->setSelectionFromRoot(base_folder,TRUE); + mRoot->setSelection(base_folder,TRUE); mRoot->scrollToShowSelection(); } } @@ -6355,9 +6497,8 @@ LLInvFVBridgeAction* LLInvFVBridgeAction::createAction(LLAssetType::EType asset_ /************************************************************************/ void LLRecentItemsFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { - LLFolderBridge::buildContextMenu(menu, flags); - - menuentry_vec_t disabled_items, items = getMenuItems(); + menuentry_vec_t disabled_items, items; + buildContextMenuOptions(flags, items, disabled_items); items.erase(std::remove(items.begin(), items.end(), std::string("New Folder")), items.end()); @@ -6369,42 +6510,29 @@ LLInvFVBridge* LLRecentInventoryBridgeBuilder::createBridge( LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags /*= 0x00*/ ) const { LLInvFVBridge* new_listener = NULL; - switch(asset_type) + if (asset_type == LLAssetType::AT_CATEGORY + && actual_asset_type != LLAssetType::AT_LINK_FOLDER) { - case LLAssetType::AT_CATEGORY: - if (actual_asset_type == LLAssetType::AT_LINK_FOLDER) + new_listener = new LLRecentItemsFolderBridge(inv_type, inventory, root, uuid); + } + else { - // *TODO: Create a link folder handler instead if it is necessary - new_listener = LLInventoryFVBridgeBuilder::createBridge( - asset_type, + new_listener = LLInventoryFolderViewModelBuilder::createBridge(asset_type, actual_asset_type, inv_type, inventory, + view_model, root, uuid, flags); - break; } - new_listener = new LLRecentItemsFolderBridge(inv_type, inventory, root, uuid); - break; - default: - new_listener = LLInventoryFVBridgeBuilder::createBridge( - asset_type, - actual_asset_type, - inv_type, - inventory, - root, - uuid, - flags); - } return new_listener; - } - // EOF diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index dc9e88d54d..b33972167c 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -29,11 +29,13 @@ #include "llcallingcard.h" #include "llfloaterproperties.h" -#include "llfoldervieweventlistener.h" +#include "llfolderviewmodel.h" #include "llinventorymodel.h" #include "llinventoryobserver.h" +#include "llinventorypanel.h" #include "llviewercontrol.h" #include "llwearable.h" +#include "lltooldraganddrop.h" class LLInventoryFilter; class LLInventoryPanel; @@ -41,7 +43,7 @@ class LLInventoryModel; class LLMenuGL; class LLCallingCardObserver; class LLViewerJointAttachment; - +class LLFolderView; typedef std::vector<std::string> menuentry_vec_t; @@ -56,7 +58,7 @@ typedef std::vector<std::string> menuentry_vec_t; // functionality a bit. (except for folders, you can create those // manually...) //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLInvFVBridge : public LLFolderViewEventListener +class LLInvFVBridge : public LLFolderViewModelItemInventory { public: // This method is a convenience function which creates the correct @@ -65,6 +67,7 @@ public: LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags = 0x00); @@ -78,23 +81,25 @@ public: // LLInvFVBridge functionality //-------------------------------------------------------------------- virtual const LLUUID& getUUID() const { return mUUID; } - virtual void clearDisplayName() {} + virtual void clearDisplayName() { mDisplayName.clear(); } virtual void restoreItem() {} virtual void restoreToWorld() {} //-------------------------------------------------------------------- - // Inherited LLFolderViewEventListener functions + // Inherited LLFolderViewModelItemInventory functions //-------------------------------------------------------------------- virtual const std::string& getName() const; virtual const std::string& getDisplayName() const; + const std::string& getSearchableName() const { return mSearchableName; } + virtual PermissionMask getPermissionMask() const; virtual LLFolderType::EType getPreferredType() const; virtual time_t getCreationDate() const; + virtual void setCreationDate(time_t creation_date_utc); virtual LLFontGL::StyleFlags getLabelStyle() const { return LLFontGL::NORMAL; } virtual std::string getLabelSuffix() const { return LLStringUtil::null; } virtual void openItem() {} virtual void closeItem() {} - virtual void previewItem() {openItem();} virtual void showProperties(); virtual BOOL isItemRenameable() const { return TRUE; } //virtual BOOL renameItem(const std::string& new_name) {} @@ -103,8 +108,8 @@ public: virtual BOOL isItemInTrash() const; virtual BOOL isLink() const; //virtual BOOL removeItem() = 0; - virtual void removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch); - virtual void move(LLFolderViewEventListener* new_parent_bridge) {} + virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch); + virtual void move(LLFolderViewModelItem* new_parent_bridge) {} virtual BOOL isItemCopyable() const { return FALSE; } virtual BOOL copyToClipboard() const; virtual BOOL cutToClipboard() const; @@ -115,6 +120,7 @@ public: void getClipboardEntries(bool show_asset_id, menuentry_vec_t &items, menuentry_vec_t &disabled_items, U32 flags); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); + virtual LLToolDragAndDrop::ESource getDragSource() const; virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const; virtual BOOL dragOrDrop(MASK mask, BOOL drop, EDragAndDropType cargo_type, @@ -122,6 +128,9 @@ public: std::string& tooltip_msg) { return FALSE; } virtual LLInventoryType::EType getInventoryType() const { return mInvType; } virtual LLWearableType::EType getWearableType() const { return LLWearableType::WT_NONE; } + EInventorySortGroup getSortGroup() const { return SG_ITEM; } + virtual LLInventoryObject* getInventoryObject() const; + //-------------------------------------------------------------------- // Convenience functions for adding various common menu options. @@ -138,16 +147,16 @@ protected: protected: LLInvFVBridge(LLInventoryPanel* inventory, LLFolderView* root, const LLUUID& uuid); - LLInventoryObject* getInventoryObject() const; LLInventoryModel* getInventoryModel() const; + LLInventoryFilter* getInventoryFilter() const; BOOL isLinkedObjectInTrash() const; // Is this obj or its baseobj in the trash? BOOL isLinkedObjectMissing() const; // Is this a linked obj whose baseobj is not in inventory? BOOL isAgentInventory() const; // false if lost or in the inventory library - BOOL isCOFFolder() const; // true if COF or descendent of - BOOL isInboxFolder() const; // true if COF or descendent of marketplace inbox - BOOL isOutboxFolder() const; // true if COF or descendent of marketplace outbox + BOOL isCOFFolder() const; // true if COF or descendant of + BOOL isInboxFolder() const; // true if COF or descendant of marketplace inbox + BOOL isOutboxFolder() const; // true if COF or descendant of marketplace outbox BOOL isOutboxFolderDirectParent() const; const LLUUID getOutboxFolder() const; @@ -160,30 +169,36 @@ protected: LLViewerInventoryCategory* item, const LLUUID& new_parent, BOOL restamp); - void removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener*>& batch); + void removeBatchNoCheck(std::vector<LLFolderViewModelItem*>& batch); protected: - LLHandle<LLInventoryPanel> mInventoryPanel; - LLFolderView* mRoot; - const LLUUID mUUID; // item id - LLInventoryType::EType mInvType; - BOOL mIsLink; + LLHandle<LLInventoryPanel> mInventoryPanel; + LLFolderView* mRoot; + const LLUUID mUUID; // item id + LLInventoryType::EType mInvType; + bool mIsLink; + LLTimer mTimeSinceRequestStart; + mutable std::string mDisplayName; + mutable std::string mSearchableName; + void purgeItem(LLInventoryModel *model, const LLUUID &uuid); + virtual void buildDisplayName() const {} }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLInvFVBridgeBuilder +// Class LLInventoryFolderViewModelBuilder // -// This class intended to build Folder View Bridge via LLInvFVBridge::createBridge. -// It can be overridden with another way of creation necessary Inventory-Folder-View-Bridge. +// This class intended to build Folder View Model via LLInvFVBridge::createBridge. +// It can be overridden with another way of creation necessary Inventory Folder View Models. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLInventoryFVBridgeBuilder +class LLInventoryFolderViewModelBuilder { public: - virtual ~LLInventoryFVBridgeBuilder() {} + virtual ~LLInventoryFolderViewModelBuilder() {} virtual LLInvFVBridge* createBridge(LLAssetType::EType asset_type, LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags = 0x00) const; @@ -203,7 +218,6 @@ public: virtual void restoreToWorld(); virtual void gotoItem(); virtual LLUIImagePtr getIcon() const; - virtual const std::string& getDisplayName() const; virtual std::string getLabelSuffix() const; virtual LLFontGL::StyleFlags getLabelStyle() const; virtual PermissionMask getPermissionMask() const; @@ -212,19 +226,17 @@ public: virtual BOOL renameItem(const std::string& new_name); virtual BOOL removeItem(); virtual BOOL isItemCopyable() const; - virtual BOOL hasChildren() const { return FALSE; } + virtual bool hasChildren() const { return FALSE; } virtual BOOL isUpToDate() const { return TRUE; } - - /*virtual*/ void clearDisplayName() { mDisplayName.clear(); } + virtual LLUIImagePtr getIconOverlay() const; LLViewerInventoryItem* getItem() const; protected: BOOL confirmRemoveItem(const LLSD& notification, const LLSD& response); virtual BOOL isItemPermissive() const; - static void buildDisplayName(LLInventoryItem* item, std::string& name); + virtual void buildDisplayName() const; - mutable std::string mDisplayName; }; class LLFolderBridge : public LLInvFVBridge @@ -232,15 +244,18 @@ class LLFolderBridge : public LLInvFVBridge public: LLFolderBridge(LLInventoryPanel* inventory, LLFolderView* root, - const LLUUID& uuid) : - LLInvFVBridge(inventory, root, uuid), + const LLUUID& uuid) + : LLInvFVBridge(inventory, root, uuid), mCallingCards(FALSE), - mWearables(FALSE) + mWearables(FALSE), + mIsLoading(false) {} BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop, std::string& tooltip_msg); BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop, std::string& tooltip_msg); + virtual void buildDisplayName() const; + virtual void performAction(LLInventoryModel* model, std::string action); virtual void openItem(); virtual void closeItem(); @@ -250,7 +265,9 @@ public: virtual LLFolderType::EType getPreferredType() const; virtual LLUIImagePtr getIcon() const; - virtual LLUIImagePtr getOpenIcon() const; + virtual LLUIImagePtr getIconOpen() const; + virtual LLUIImagePtr getIconOverlay() const; + static LLUIImagePtr getIcon(LLFolderType::EType preferred_type); virtual BOOL renameItem(const std::string& new_name); @@ -258,11 +275,12 @@ public: virtual BOOL removeItem(); BOOL removeSystemFolder(); bool removeItemResponse(const LLSD& notification, const LLSD& response); + void updateHierarchyCreationDate(time_t date); virtual void pasteFromClipboard(); virtual void pasteLinkFromClipboard(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); - virtual BOOL hasChildren() const; + virtual bool hasChildren() const; virtual BOOL dragOrDrop(MASK mask, BOOL drop, EDragAndDropType cargo_type, void* cargo_data, @@ -275,20 +293,24 @@ public: virtual BOOL isClipboardPasteable() const; virtual BOOL isClipboardPasteableAsLink() const; + EInventorySortGroup getSortGroup() const; + virtual void update(); + static void createWearable(LLFolderBridge* bridge, LLWearableType::EType type); LLViewerInventoryCategory* getCategory() const; LLHandle<LLFolderBridge> getHandle() { mHandle.bind(this); return mHandle; } + bool isLoading() { return mIsLoading; } + protected: - void buildContextMenuBaseOptions(U32 flags); - void buildContextMenuFolderOptions(U32 flags); + void buildContextMenuOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items); + void buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items); //-------------------------------------------------------------------- // Menu callbacks //-------------------------------------------------------------------- static void pasteClipboard(void* user_data); - static void createNewCategory(void* user_data); static void createNewShirt(void* user_data); static void createNewPants(void* user_data); static void createNewShoes(void* user_data); @@ -308,8 +330,6 @@ protected: void modifyOutfit(BOOL append); void determineFolderType(); - menuentry_vec_t getMenuItems() { return mItems; } // returns a copy of current menu items - void dropToFavorites(LLInventoryItem* inv_item); void dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit); @@ -321,11 +341,12 @@ public: static void staticFolderOptionsMenu(); private: - BOOL mCallingCards; - BOOL mWearables; - menuentry_vec_t mItems; - menuentry_vec_t mDisabledItems; - LLRootHandle<LLFolderBridge> mHandle; + + bool mCallingCards; + bool mWearables; + bool mIsLoading; + LLTimer mTimeSinceRequestStart; + LLRootHandle<LLFolderBridge> mHandle; }; class LLTextureBridge : public LLItemBridge @@ -354,7 +375,6 @@ public: const LLUUID& uuid) : LLItemBridge(inventory, root, uuid) {} virtual void openItem(); - virtual void previewItem(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); static void openSoundPreview(void*); }; @@ -544,7 +564,6 @@ class LLMeshBridge : public LLItemBridge public: virtual LLUIImagePtr getIcon() const; virtual void openItem(); - virtual void previewItem(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); protected: @@ -620,7 +639,7 @@ public: }; // Bridge builder to create Inventory-Folder-View-Bridge for Recent Inventory Panel -class LLRecentInventoryBridgeBuilder : public LLInventoryFVBridgeBuilder +class LLRecentInventoryBridgeBuilder : public LLInventoryFolderViewModelBuilder { public: // Overrides FolderBridge for Recent Inventory Panel. @@ -629,6 +648,7 @@ public: LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags = 0x00) const; diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index 4573074c73..c913269aad 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -29,7 +29,7 @@ #include "llinventoryfilter.h" // viewer includes -#include "llfoldervieweventlistener.h" +#include "llfolderviewmodel.h" #include "llfolderviewitem.h" #include "llinventorymodel.h" #include "llinventorymodelbackgroundfetch.h" @@ -42,109 +42,90 @@ #include "llclipboard.h" #include "lltrans.h" +//TODO RN: fix use of static cast as much as possible + LLFastTimer::DeclareTimer FT_FILTER_CLIPBOARD("Filter Clipboard"); -LLInventoryFilter::FilterOps::FilterOps() : - mFilterObjectTypes(0xffffffffffffffffULL), - mFilterCategoryTypes(0xffffffffffffffffULL), - mFilterWearableTypes(0xffffffffffffffffULL), - mMinDate(time_min()), - mMaxDate(time_max()), - mHoursAgo(0), - mShowFolderState(SHOW_NON_EMPTY_FOLDERS), - mPermissions(PERM_NONE), - mFilterTypes(FILTERTYPE_OBJECT), - mFilterUUID(LLUUID::null), - mFilterLinks(FILTERLINK_INCLUDE_LINKS) +LLInventoryFilter::FilterOps::FilterOps(const Params& p) +: mFilterObjectTypes(p.object_types), + mFilterCategoryTypes(p.category_types), + mFilterWearableTypes(p.wearable_types), + mMinDate(p.date_range.min_date), + mMaxDate(p.date_range.max_date), + mHoursAgo(p.hours_ago), + mShowFolderState(p.show_folder_state), + mPermissions(p.permissions), + mFilterTypes(p.types), + mFilterUUID(p.uuid), + mFilterLinks(p.links) { } ///---------------------------------------------------------------------------- /// Class LLInventoryFilter ///---------------------------------------------------------------------------- -LLInventoryFilter::LLInventoryFilter(const std::string& name) -: mName(name), - mModified(FALSE), - mNeedTextRebuild(TRUE), - mEmptyLookupMessage("InventoryNoMatchingItems") +LLInventoryFilter::LLInventoryFilter(const Params& p) +: mName(p.name), + mFilterModified(FILTER_NONE), + mEmptyLookupMessage("InventoryNoMatchingItems"), + mFilterOps(p.filter_ops), + mFilterSubString(p.substring), + mCurrentGeneration(0), + mFirstRequiredGeneration(0), + mFirstSuccessGeneration(0), + mFilterCount(0) { - mOrder = SO_FOLDERS_BY_NAME; // This gets overridden by a pref immediately - - mSubStringMatchOffset = 0; - mFilterSubString.clear(); - mFilterGeneration = 0; - mMustPassGeneration = S32_MAX; - mMinRequiredGeneration = 0; - mFilterCount = 0; - mNextFilterGeneration = mFilterGeneration + 1; - - mLastLogoff = gSavedPerAccountSettings.getU32("LastLogoff"); - mFilterBehavior = FILTER_NONE; + mNextFilterGeneration = mCurrentGeneration + 1; // copy mFilterOps into mDefaultFilterOps markDefault(); } -LLInventoryFilter::~LLInventoryFilter() -{ -} - -BOOL LLInventoryFilter::check(const LLFolderViewItem* item) +bool LLInventoryFilter::check(const LLFolderViewModelItem* item) { + const LLFolderViewModelItemInventory* listener = static_cast<const LLFolderViewModelItemInventory*>(item); // Clipboard cut items are *always* filtered so we need this value upfront - const LLFolderViewEventListener* listener = item->getListener(); const BOOL passed_clipboard = (listener ? checkAgainstClipboard(listener->getUUID()) : TRUE); // If it's a folder and we're showing all folders, return automatically. - const BOOL is_folder = (dynamic_cast<const LLFolderViewFolder*>(item) != NULL); + const BOOL is_folder = listener->getInventoryType() == LLInventoryType::IT_CATEGORY; if (is_folder && (mFilterOps.mShowFolderState == LLInventoryFilter::SHOW_ALL_FOLDERS)) { return passed_clipboard; } - mSubStringMatchOffset = mFilterSubString.size() ? item->getSearchableLabel().find(mFilterSubString) : std::string::npos; + std::string::size_type string_offset = mFilterSubString.size() ? listener->getSearchableName().find(mFilterSubString) : std::string::npos; - const BOOL passed_filtertype = checkAgainstFilterType(item); - const BOOL passed_permissions = checkAgainstPermissions(item); - const BOOL passed_filterlink = checkAgainstFilterLinks(item); - const BOOL passed = (passed_filtertype && - passed_permissions && - passed_filterlink && - passed_clipboard && - (mFilterSubString.size() == 0 || mSubStringMatchOffset != std::string::npos)); + BOOL passed = (mFilterSubString.size() == 0 || string_offset != std::string::npos); + passed = passed && checkAgainstFilterType(listener); + passed = passed && checkAgainstPermissions(listener); + passed = passed && checkAgainstFilterLinks(listener); + passed = passed && passed_clipboard; return passed; } bool LLInventoryFilter::check(const LLInventoryItem* item) { - mSubStringMatchOffset = mFilterSubString.size() ? item->getName().find(mFilterSubString) : std::string::npos; + std::string::size_type string_offset = mFilterSubString.size() ? item->getName().find(mFilterSubString) : std::string::npos; const bool passed_filtertype = checkAgainstFilterType(item); const bool passed_permissions = checkAgainstPermissions(item); const BOOL passed_clipboard = checkAgainstClipboard(item->getUUID()); - const bool passed = (passed_filtertype && - passed_permissions && - passed_clipboard && - (mFilterSubString.size() == 0 || mSubStringMatchOffset != std::string::npos)); + const bool passed = (passed_filtertype + && passed_permissions + && passed_clipboard + && (mFilterSubString.size() == 0 || string_offset != std::string::npos)); return passed; } -bool LLInventoryFilter::checkFolder(const LLFolderViewFolder* folder) const +bool LLInventoryFilter::checkFolder(const LLFolderViewModelItem* item) const { - if (!folder) - { - llwarns << "The filter can not be checked on an invalid folder." << llendl; - llassert(false); // crash in development builds - return false; - } - - const LLFolderViewEventListener* listener = folder->getListener(); + const LLFolderViewModelItemInventory* listener = static_cast<const LLFolderViewModelItemInventory*>(item); if (!listener) { - llwarns << "Folder view event listener not found." << llendl; - llassert(false); // crash in development builds + llerrs << "Folder view event listener not found." << llendl; return false; } @@ -155,6 +136,13 @@ bool LLInventoryFilter::checkFolder(const LLFolderViewFolder* folder) const bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const { + // when applying a filter, matching folders get their contents downloaded first + if (isNotDefault() + && !gInventory.isCategoryComplete(folder_id)) + { + LLInventoryModelBackgroundFetch::instance().start(folder_id); + } + // Always check against the clipboard const BOOL passed_clipboard = checkAgainstClipboard(folder_id); @@ -163,14 +151,14 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const { return passed_clipboard; } - + if (mFilterOps.mFilterTypes & FILTERTYPE_CATEGORY) { // Can only filter categories for items in your inventory // (e.g. versus in-world object contents). const LLViewerInventoryCategory *cat = gInventory.getCategory(folder_id); if (!cat) - return false; + return folder_id.isNull(); LLFolderType::EType cat_type = cat->getPreferredType(); if (cat_type != LLFolderType::FT_NONE && (1LL << cat_type & mFilterOps.mFilterCategoryTypes) == U64(0)) return false; @@ -179,9 +167,8 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const return passed_clipboard; } -BOOL LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) const +bool LLInventoryFilter::checkAgainstFilterType(const LLFolderViewModelItemInventory* listener) const { - const LLFolderViewEventListener* listener = item->getListener(); if (!listener) return FALSE; LLInventoryType::EType object_type = listener->getInventoryType(); @@ -268,7 +255,7 @@ BOOL LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) con } } } - + return TRUE; } @@ -347,13 +334,12 @@ bool LLInventoryFilter::checkAgainstClipboard(const LLUUID& object_id) const return true; } -BOOL LLInventoryFilter::checkAgainstPermissions(const LLFolderViewItem* item) const +bool LLInventoryFilter::checkAgainstPermissions(const LLFolderViewModelItemInventory* listener) const { - const LLFolderViewEventListener* listener = item->getListener(); if (!listener) return FALSE; PermissionMask perm = listener->getPermissionMask(); - const LLInvFVBridge *bridge = dynamic_cast<const LLInvFVBridge *>(item->getListener()); + const LLInvFVBridge *bridge = dynamic_cast<const LLInvFVBridge *>(listener); if (bridge && bridge->isLink()) { const LLUUID& linked_uuid = gInventory.getLinkedItemID(bridge->getUUID()); @@ -375,9 +361,8 @@ bool LLInventoryFilter::checkAgainstPermissions(const LLInventoryItem* item) con return (perm & mFilterOps.mPermissions) == mFilterOps.mPermissions; } -BOOL LLInventoryFilter::checkAgainstFilterLinks(const LLFolderViewItem* item) const +bool LLInventoryFilter::checkAgainstFilterLinks(const LLFolderViewModelItemInventory* listener) const { - const LLFolderViewEventListener* listener = item->getListener(); if (!listener) return TRUE; const LLUUID object_id = listener->getUUID(); @@ -397,20 +382,21 @@ const std::string& LLInventoryFilter::getFilterSubString(BOOL trim) const return mFilterSubString; } -std::string::size_type LLInventoryFilter::getStringMatchOffset() const +std::string::size_type LLInventoryFilter::getStringMatchOffset(LLFolderViewModelItem* item) const { - return mSubStringMatchOffset; + const LLFolderViewModelItemInventory* listener = static_cast<const LLFolderViewModelItemInventory*>(item); + return mFilterSubString.size() ? listener->getSearchableName().find(mFilterSubString) : std::string::npos; } -BOOL LLInventoryFilter::isDefault() const +bool LLInventoryFilter::isDefault() const { return !isNotDefault(); } // has user modified default filter params? -BOOL LLInventoryFilter::isNotDefault() const +bool LLInventoryFilter::isNotDefault() const { - BOOL not_default = FALSE; + S32 not_default = 0; not_default |= (mFilterOps.mFilterObjectTypes != mDefaultFilterOps.mFilterObjectTypes); not_default |= (mFilterOps.mFilterCategoryTypes != mDefaultFilterOps.mFilterCategoryTypes); @@ -422,11 +408,11 @@ BOOL LLInventoryFilter::isNotDefault() const not_default |= (mFilterOps.mMinDate != mDefaultFilterOps.mMinDate); not_default |= (mFilterOps.mMaxDate != mDefaultFilterOps.mMaxDate); not_default |= (mFilterOps.mHoursAgo != mDefaultFilterOps.mHoursAgo); - - return not_default; + + return not_default != 0; } -BOOL LLInventoryFilter::isActive() const +bool LLInventoryFilter::isActive() const { return mFilterOps.mFilterObjectTypes != 0xffffffffffffffffULL || mFilterOps.mFilterCategoryTypes != 0xffffffffffffffffULL @@ -440,16 +426,9 @@ BOOL LLInventoryFilter::isActive() const || mFilterOps.mHoursAgo != 0; } -BOOL LLInventoryFilter::isModified() const +bool LLInventoryFilter::isModified() const { - return mModified; -} - -BOOL LLInventoryFilter::isModifiedAndClear() -{ - BOOL ret = mModified; - mModified = FALSE; - return ret; + return mFilterModified != FILTER_NONE; } void LLInventoryFilter::updateFilterTypes(U64 types, U64& current_types) @@ -613,9 +592,10 @@ void LLInventoryFilter::setDateRange(time_t min_date, time_t max_date) void LLInventoryFilter::setDateRangeLastLogoff(BOOL sl) { + static LLCachedControl<U32> s_last_logoff(gSavedPerAccountSettings, "LastLogoff", 0); if (sl && !isSinceLogoff()) { - setDateRange(mLastLogoff, time_max()); + setDateRange(s_last_logoff(), time_max()); setModified(); } if (!sl && isSinceLogoff()) @@ -634,17 +614,18 @@ void LLInventoryFilter::setDateRangeLastLogoff(BOOL sl) } } -BOOL LLInventoryFilter::isSinceLogoff() const +bool LLInventoryFilter::isSinceLogoff() const { - return (mFilterOps.mMinDate == (time_t)mLastLogoff) && + static LLCachedControl<U32> s_last_logoff(gSavedSettings, "LastLogoff", 0); + + return (mFilterOps.mMinDate == (time_t)s_last_logoff()) && (mFilterOps.mMaxDate == time_max()) && (mFilterOps.mFilterTypes & FILTERTYPE_DATE); } void LLInventoryFilter::clearModified() { - mModified = FALSE; - mFilterBehavior = FILTER_NONE; + mFilterModified = FILTER_NONE; } void LLInventoryFilter::setHoursAgo(U32 hours) @@ -722,15 +703,6 @@ void LLInventoryFilter::setShowFolderState(EFolderShow state) } } -void LLInventoryFilter::setSortOrder(U32 order) -{ - if (mOrder != order) - { - mOrder = order; - setModified(); - } -} - void LLInventoryFilter::markDefault() { mDefaultFilterOps = mFilterOps; @@ -742,83 +714,68 @@ void LLInventoryFilter::resetDefault() setModified(); } -void LLInventoryFilter::setModified(EFilterBehavior behavior) +void LLInventoryFilter::setModified(EFilterModified behavior) { - mModified = TRUE; - mNeedTextRebuild = TRUE; - mFilterGeneration = mNextFilterGeneration++; + mFilterText.clear(); + mCurrentGeneration = mNextFilterGeneration++; - if (mFilterBehavior == FILTER_NONE) + if (mFilterModified == FILTER_NONE) { - mFilterBehavior = behavior; + mFilterModified = behavior; } - else if (mFilterBehavior != behavior) + else if (mFilterModified != behavior) { // trying to do both less restrictive and more restrictive filter // basically means restart from scratch - mFilterBehavior = FILTER_RESTART; + mFilterModified = FILTER_RESTART; } - if (isNotDefault()) + // if not keeping current filter results, update last valid as well + switch(mFilterModified) { - // if not keeping current filter results, update last valid as well - switch(mFilterBehavior) - { - case FILTER_RESTART: - mMustPassGeneration = mFilterGeneration; - mMinRequiredGeneration = mFilterGeneration; - break; - case FILTER_LESS_RESTRICTIVE: - mMustPassGeneration = mFilterGeneration; - break; - case FILTER_MORE_RESTRICTIVE: - mMinRequiredGeneration = mFilterGeneration; - // must have passed either current filter generation (meaningless, as it hasn't been run yet) - // or some older generation, so keep the value - mMustPassGeneration = llmin(mMustPassGeneration, mFilterGeneration); - break; - default: - llerrs << "Bad filter behavior specified" << llendl; - } - } - else - { - // shortcut disabled filters to show everything immediately - mMinRequiredGeneration = 0; - mMustPassGeneration = S32_MAX; + case FILTER_RESTART: + mFirstRequiredGeneration = mCurrentGeneration; + mFirstSuccessGeneration = mCurrentGeneration; + break; + case FILTER_LESS_RESTRICTIVE: + mFirstRequiredGeneration = mCurrentGeneration; + break; + case FILTER_MORE_RESTRICTIVE: + mFirstSuccessGeneration = mCurrentGeneration; + break; + default: + llerrs << "Bad filter behavior specified" << llendl; } } -BOOL LLInventoryFilter::isFilterObjectTypesWith(LLInventoryType::EType t) const +bool LLInventoryFilter::isFilterObjectTypesWith(LLInventoryType::EType t) const { return mFilterOps.mFilterObjectTypes & (1LL << t); } const std::string& LLInventoryFilter::getFilterText() { - if (!mNeedTextRebuild) + if (!mFilterText.empty()) { return mFilterText; } - mNeedTextRebuild = FALSE; std::string filtered_types; std::string not_filtered_types; BOOL filtered_by_type = FALSE; BOOL filtered_by_all_types = TRUE; S32 num_filter_types = 0; + mFilterText.clear(); if (isFilterObjectTypesWith(LLInventoryType::IT_ANIMATION)) { - //filtered_types += " Animations,"; filtered_types += LLTrans::getString("Animations"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Animations,"; not_filtered_types += LLTrans::getString("Animations"); filtered_by_all_types = FALSE; @@ -826,140 +783,120 @@ const std::string& LLInventoryFilter::getFilterText() if (isFilterObjectTypesWith(LLInventoryType::IT_CALLINGCARD)) { - //filtered_types += " Calling Cards,"; filtered_types += LLTrans::getString("Calling Cards"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Calling Cards,"; not_filtered_types += LLTrans::getString("Calling Cards"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_WEARABLE)) { - //filtered_types += " Clothing,"; filtered_types += LLTrans::getString("Clothing"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Clothing,"; not_filtered_types += LLTrans::getString("Clothing"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_GESTURE)) { - //filtered_types += " Gestures,"; filtered_types += LLTrans::getString("Gestures"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Gestures,"; not_filtered_types += LLTrans::getString("Gestures"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_LANDMARK)) { - //filtered_types += " Landmarks,"; filtered_types += LLTrans::getString("Landmarks"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Landmarks,"; not_filtered_types += LLTrans::getString("Landmarks"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_NOTECARD)) { - //filtered_types += " Notecards,"; filtered_types += LLTrans::getString("Notecards"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Notecards,"; not_filtered_types += LLTrans::getString("Notecards"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_OBJECT) && isFilterObjectTypesWith(LLInventoryType::IT_ATTACHMENT)) { - //filtered_types += " Objects,"; filtered_types += LLTrans::getString("Objects"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Objects,"; not_filtered_types += LLTrans::getString("Objects"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_LSL)) { - //filtered_types += " Scripts,"; filtered_types += LLTrans::getString("Scripts"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Scripts,"; not_filtered_types += LLTrans::getString("Scripts"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_SOUND)) { - //filtered_types += " Sounds,"; filtered_types += LLTrans::getString("Sounds"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Sounds,"; not_filtered_types += LLTrans::getString("Sounds"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_TEXTURE)) { - //filtered_types += " Textures,"; filtered_types += LLTrans::getString("Textures"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Textures,"; not_filtered_types += LLTrans::getString("Textures"); filtered_by_all_types = FALSE; } if (isFilterObjectTypesWith(LLInventoryType::IT_SNAPSHOT)) { - //filtered_types += " Snapshots,"; filtered_types += LLTrans::getString("Snapshots"); filtered_by_type = TRUE; num_filter_types++; } else { - //not_filtered_types += " Snapshots,"; not_filtered_types += LLTrans::getString("Snapshots"); filtered_by_all_types = FALSE; } @@ -975,7 +912,6 @@ const std::string& LLInventoryFilter::getFilterText() } else { - //mFilterText += "No "; mFilterText += LLTrans::getString("No Filters"); mFilterText += not_filtered_types; } @@ -985,66 +921,55 @@ const std::string& LLInventoryFilter::getFilterText() if (isSinceLogoff()) { - //mFilterText += " - Since Logoff"; mFilterText += LLTrans::getString("Since Logoff"); } return mFilterText; } -void LLInventoryFilter::toLLSD(LLSD& data) const -{ - data["filter_types"] = (LLSD::Integer)getFilterObjectTypes(); - data["min_date"] = (LLSD::Integer)getMinDate(); - data["max_date"] = (LLSD::Integer)getMaxDate(); - data["hours_ago"] = (LLSD::Integer)getHoursAgo(); - data["show_folder_state"] = (LLSD::Integer)getShowFolderState(); - data["permissions"] = (LLSD::Integer)getFilterPermissions(); - data["substring"] = (LLSD::String)getFilterSubString(); - data["sort_order"] = (LLSD::Integer)getSortOrder(); - data["since_logoff"] = (LLSD::Boolean)isSinceLogoff(); -} -void LLInventoryFilter::fromLLSD(LLSD& data) +LLInventoryFilter& LLInventoryFilter::operator=( const LLInventoryFilter& other ) { - if(data.has("filter_types")) - { - setFilterObjectTypes((U64)data["filter_types"].asInteger()); - } - - if(data.has("min_date") && data.has("max_date")) - { - setDateRange(data["min_date"].asInteger(), data["max_date"].asInteger()); - } - - if(data.has("hours_ago")) - { - setHoursAgo((U32)data["hours_ago"].asInteger()); - } - - if(data.has("show_folder_state")) - { - setShowFolderState((EFolderShow)data["show_folder_state"].asInteger()); - } + setFilterObjectTypes(other.getFilterObjectTypes()); + setDateRange(other.getMinDate(), other.getMaxDate()); + setHoursAgo(other.getHoursAgo()); + setShowFolderState(other.getShowFolderState()); + setFilterPermissions(other.getFilterPermissions()); + setFilterSubString(other.getFilterSubString()); + setDateRangeLastLogoff(other.isSinceLogoff()); + return *this; +} - if(data.has("permissions")) - { - setFilterPermissions((PermissionMask)data["permissions"].asInteger()); - } - if(data.has("substring")) - { - setFilterSubString(std::string(data["substring"].asString())); - } +void LLInventoryFilter::toParams(Params& params) const +{ + params.filter_ops.types = getFilterObjectTypes(); + params.filter_ops.category_types = getFilterCategoryTypes(); + params.filter_ops.wearable_types = getFilterWearableTypes(); + params.filter_ops.date_range.min_date = getMinDate(); + params.filter_ops.date_range.max_date = getMaxDate(); + params.filter_ops.hours_ago = getHoursAgo(); + params.filter_ops.show_folder_state = getShowFolderState(); + params.filter_ops.permissions = getFilterPermissions(); + params.substring = getFilterSubString(); + params.since_logoff = isSinceLogoff(); +} - if(data.has("sort_order")) +void LLInventoryFilter::fromParams(const Params& params) +{ + if (!params.validateBlock()) { - setSortOrder((U32)data["sort_order"].asInteger()); + return; } - if(data.has("since_logoff")) - { - setDateRangeLastLogoff((bool)data["since_logoff"].asBoolean()); - } + setFilterObjectTypes(params.filter_ops.types); + setFilterCategoryTypes(params.filter_ops.category_types); + setFilterWearableTypes(params.filter_ops.wearable_types); + setDateRange(params.filter_ops.date_range.min_date, params.filter_ops.date_range.max_date); + setHoursAgo(params.filter_ops.hours_ago); + setShowFolderState(params.filter_ops.show_folder_state); + setFilterPermissions(params.filter_ops.permissions); + setFilterSubString(params.substring); + setDateRangeLastLogoff(params.since_logoff); } U64 LLInventoryFilter::getFilterObjectTypes() const @@ -1057,11 +982,21 @@ U64 LLInventoryFilter::getFilterCategoryTypes() const return mFilterOps.mFilterCategoryTypes; } -BOOL LLInventoryFilter::hasFilterString() const +U64 LLInventoryFilter::getFilterWearableTypes() const +{ + return mFilterOps.mFilterWearableTypes; +} + +bool LLInventoryFilter::hasFilterString() const { return mFilterSubString.size() > 0; } +std::string::size_type LLInventoryFilter::getFilterStringSize() const +{ + return mFilterSubString.size(); +} + PermissionMask LLInventoryFilter::getFilterPermissions() const { return mFilterOps.mPermissions; @@ -1088,14 +1023,6 @@ LLInventoryFilter::EFolderShow LLInventoryFilter::getShowFolderState() const { return mFilterOps.mShowFolderState; } -U32 LLInventoryFilter::getSortOrder() const -{ - return mOrder; -} -const std::string& LLInventoryFilter::getName() const -{ - return mName; -} void LLInventoryFilter::setFilterCount(S32 count) { @@ -1113,15 +1040,15 @@ void LLInventoryFilter::decrementFilterCount() S32 LLInventoryFilter::getCurrentGeneration() const { - return mFilterGeneration; + return mCurrentGeneration; } -S32 LLInventoryFilter::getMinRequiredGeneration() const +S32 LLInventoryFilter::getFirstSuccessGeneration() const { - return mMinRequiredGeneration; + return mFirstSuccessGeneration; } -S32 LLInventoryFilter::getMustPassGeneration() const +S32 LLInventoryFilter::getFirstRequiredGeneration() const { - return mMustPassGeneration; + return mFirstRequiredGeneration; } void LLInventoryFilter::setEmptyLookupMessage(const std::string& message) @@ -1129,9 +1056,12 @@ void LLInventoryFilter::setEmptyLookupMessage(const std::string& message) mEmptyLookupMessage = message; } -const std::string& LLInventoryFilter::getEmptyLookupMessage() const +std::string LLInventoryFilter::getEmptyLookupMessage() const { - return mEmptyLookupMessage; + LLStringUtil::format_map_t args; + args["[SEARCH_TERM]"] = LLURI::escape(getFilterSubStringOrig()); + + return LLTrans::getString(mEmptyLookupMessage, args); } @@ -1141,3 +1071,27 @@ bool LLInventoryFilter::areDateLimitsSet() || mFilterOps.mMaxDate != time_max() || mFilterOps.mHoursAgo != 0; } + +bool LLInventoryFilter::showAllResults() const +{ + return hasFilterString(); +} + + + +bool LLInventoryFilter::FilterOps::DateRange::validateBlock( bool emit_errors /*= true*/ ) const +{ + bool valid = LLInitParam::Block<DateRange>::validateBlock(emit_errors); + if (valid) + { + if (max_date() < min_date()) + { + if (emit_errors) + { + llwarns << "max_date should be greater or equal to min_date" << llendl; + } + valid = false; + } + } + return valid; +} diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h index 9e600c036f..4912b5ca91 100644 --- a/indra/newview/llinventoryfilter.h +++ b/indra/newview/llinventoryfilter.h @@ -29,12 +29,13 @@ #include "llinventorytype.h" #include "llpermissionsflags.h" +#include "llfolderviewmodel.h" class LLFolderViewItem; class LLFolderViewFolder; class LLInventoryItem; -class LLInventoryFilter +class LLInventoryFilter : public LLFolderViewFilter { public: enum EFolderShow @@ -44,14 +45,6 @@ public: SHOW_NO_FOLDERS }; - enum EFilterBehavior - { - FILTER_NONE, // nothing to do, already filtered - FILTER_RESTART, // restart filtering from scratch - FILTER_LESS_RESTRICTIVE, // existing filtered items will certainly pass this filter - FILTER_MORE_RESTRICTIVE // if you didn't pass the previous filter, you definitely won't pass this one - }; - enum EFilterType { FILTERTYPE_NONE = 0, FILTERTYPE_OBJECT = 0x1 << 0, // normal default search-by-object-type @@ -59,7 +52,7 @@ public: FILTERTYPE_UUID = 0x1 << 2, // find the object with UUID and any links to it FILTERTYPE_DATE = 0x1 << 3, // search by date range FILTERTYPE_WEARABLE = 0x1 << 4, // search by wearable type - FILTERTYPE_EMPTYFOLDERS = 0x1 << 5 // pass if folder is not a system folder to be hidden if empty + FILTERTYPE_EMPTYFOLDERS = 0x1 << 5 // pass if folder is not a system folder to be hidden if }; enum EFilterLink @@ -77,16 +70,92 @@ public: SO_SYSTEM_FOLDERS_TO_TOP = 0x1 << 2 // Force system folders to be on top }; - LLInventoryFilter(const std::string& name); - virtual ~LLInventoryFilter(); + struct FilterOps + { + struct DateRange : public LLInitParam::Block<DateRange> + { + Optional<time_t> min_date, + max_date; + + DateRange() + : min_date("min_date", time_min()), + max_date("max_date", time_max()) + {} + + bool validateBlock(bool emit_errors = true) const; + }; + + struct Params : public LLInitParam::Block<Params> + { + Optional<U32> types; + Optional<U64> object_types, + wearable_types, + category_types; + Optional<EFilterLink> links; + Optional<LLUUID> uuid; + Optional<DateRange> date_range; + Optional<S32> hours_ago; + Optional<EFolderShow> show_folder_state; + Optional<PermissionMask> permissions; + + Params() + : types("filter_types", FILTERTYPE_OBJECT), + object_types("object_types", 0xffffFFFFffffFFFFULL), + wearable_types("wearable_types", 0xffffFFFFffffFFFFULL), + category_types("category_types", 0xffffFFFFffffFFFFULL), + links("links", FILTERLINK_INCLUDE_LINKS), + uuid("uuid"), + date_range("date_range"), + hours_ago("hours_ago", 0), + show_folder_state("show_folder_state", SHOW_NON_EMPTY_FOLDERS), + permissions("permissions", PERM_NONE) + {} + }; + + FilterOps(const Params& = Params()); + + U32 mFilterTypes; + U64 mFilterObjectTypes, // For _OBJECT + mFilterWearableTypes, + mFilterLinks, + mFilterCategoryTypes; // For _CATEGORY + LLUUID mFilterUUID; // for UUID + + time_t mMinDate, + mMaxDate; + U32 mHoursAgo; + + EFolderShow mShowFolderState; + PermissionMask mPermissions; + }; + + struct Params : public LLInitParam::Block<Params> + { + Optional<std::string> name; + Optional<FilterOps::Params> filter_ops; + Optional<std::string> substring; + Optional<bool> since_logoff; + + Params() + : name("name"), + filter_ops(""), + substring("substring"), + since_logoff("since_logoff") + {} + }; + + LLInventoryFilter(const Params& p = Params()); + LLInventoryFilter(const LLInventoryFilter& other) { *this = other; } + virtual ~LLInventoryFilter() {} // +-------------------------------------------------------------------+ // + Parameters // +-------------------------------------------------------------------+ - void setFilterObjectTypes(U64 types); U64 getFilterObjectTypes() const; U64 getFilterCategoryTypes() const; - BOOL isFilterObjectTypesWith(LLInventoryType::EType t) const; + U64 getFilterWearableTypes() const; + bool isFilterObjectTypesWith(LLInventoryType::EType t) const; + void setFilterObjectTypes(U64 types); void setFilterCategoryTypes(U64 types); void setFilterUUID(const LLUUID &object_id); void setFilterWearableTypes(U64 types); @@ -96,7 +165,7 @@ public: void setFilterSubString(const std::string& string); const std::string& getFilterSubString(BOOL trim = FALSE) const; const std::string& getFilterSubStringOrig() const { return mFilterSubStringOrig; } - BOOL hasFilterString() const; + bool hasFilterString() const; void setFilterPermissions(PermissionMask perms); PermissionMask getFilterPermissions() const; @@ -115,43 +184,35 @@ public: // +-------------------------------------------------------------------+ // + Execution And Results // +-------------------------------------------------------------------+ - BOOL check(const LLFolderViewItem* item); + bool check(const LLFolderViewModelItem* listener); bool check(const LLInventoryItem* item); - bool checkFolder(const LLFolderViewFolder* folder) const; + bool checkFolder(const LLFolderViewModelItem* listener) const; bool checkFolder(const LLUUID& folder_id) const; - BOOL checkAgainstFilterType(const LLFolderViewItem* item) const; - bool checkAgainstFilterType(const LLInventoryItem* item) const; - BOOL checkAgainstPermissions(const LLFolderViewItem* item) const; - bool checkAgainstPermissions(const LLInventoryItem* item) const; - BOOL checkAgainstFilterLinks(const LLFolderViewItem* item) const; - bool checkAgainstClipboard(const LLUUID& object_id) const; - std::string::size_type getStringMatchOffset() const; + bool showAllResults() const; + std::string::size_type getStringMatchOffset(LLFolderViewModelItem* item) const; + std::string::size_type getFilterStringSize() const; // +-------------------------------------------------------------------+ // + Presentation // +-------------------------------------------------------------------+ void setShowFolderState( EFolderShow state); EFolderShow getShowFolderState() const; - void setSortOrder(U32 order); - U32 getSortOrder() const; - void setEmptyLookupMessage(const std::string& message); - const std::string& getEmptyLookupMessage() const; + std::string getEmptyLookupMessage() const; // +-------------------------------------------------------------------+ // + Status // +-------------------------------------------------------------------+ - BOOL isActive() const; - BOOL isModified() const; - BOOL isModifiedAndClear(); - BOOL isSinceLogoff() const; + bool isActive() const; + bool isModified() const; + bool isSinceLogoff() const; void clearModified(); - const std::string& getName() const; + const std::string& getName() const { return mName; } const std::string& getFilterText(); //RN: this is public to allow system to externally force a global refilter - void setModified(EFilterBehavior behavior = FILTER_RESTART); + void setModified(EFilterModified behavior = FILTER_RESTART); // +-------------------------------------------------------------------+ // + Count @@ -163,8 +224,8 @@ public: // +-------------------------------------------------------------------+ // + Default // +-------------------------------------------------------------------+ - BOOL isDefault() const; - BOOL isNotDefault() const; + bool isDefault() const; + bool isNotDefault() const; void markDefault(); void resetDefault(); @@ -172,57 +233,42 @@ public: // + Generation // +-------------------------------------------------------------------+ S32 getCurrentGeneration() const; - S32 getMinRequiredGeneration() const; - S32 getMustPassGeneration() const; + S32 getFirstSuccessGeneration() const; + S32 getFirstRequiredGeneration() const; + // +-------------------------------------------------------------------+ // + Conversion // +-------------------------------------------------------------------+ - void toLLSD(LLSD& data) const; - void fromLLSD(LLSD& data); + void toParams(Params& params) const; + void fromParams(const Params& p); + + LLInventoryFilter& operator =(const LLInventoryFilter& other); private: bool areDateLimitsSet(); - - struct FilterOps - { - FilterOps(); - U32 mFilterTypes; - - U64 mFilterObjectTypes; // For _OBJECT - U64 mFilterWearableTypes; - U64 mFilterCategoryTypes; // For _CATEGORY - LLUUID mFilterUUID; // for UUID - - time_t mMinDate; - time_t mMaxDate; - U32 mHoursAgo; - EFolderShow mShowFolderState; - PermissionMask mPermissions; - U64 mFilterLinks; - }; - - U32 mOrder; - U32 mLastLogoff; + bool checkAgainstFilterType(const class LLFolderViewModelItemInventory* listener) const; + bool checkAgainstFilterType(const LLInventoryItem* item) const; + bool checkAgainstPermissions(const class LLFolderViewModelItemInventory* listener) const; + bool checkAgainstPermissions(const LLInventoryItem* item) const; + bool checkAgainstFilterLinks(const class LLFolderViewModelItemInventory* listener) const; + bool checkAgainstClipboard(const LLUUID& object_id) const; FilterOps mFilterOps; FilterOps mDefaultFilterOps; - std::string::size_type mSubStringMatchOffset; std::string mFilterSubString; std::string mFilterSubStringOrig; const std::string mName; - S32 mFilterGeneration; - S32 mMustPassGeneration; - S32 mMinRequiredGeneration; + S32 mCurrentGeneration; + S32 mFirstRequiredGeneration; + S32 mFirstSuccessGeneration; S32 mNextFilterGeneration; S32 mFilterCount; - EFilterBehavior mFilterBehavior; + EFilterModified mFilterModified; - BOOL mModified; - BOOL mNeedTextRebuild; std::string mFilterText; std::string mEmptyLookupMessage; }; diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index ab5b082915..1ae6fd91ce 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -45,7 +45,7 @@ // newview includes #include "llappearancemgr.h" #include "llappviewer.h" -//#include "llfirstuse.h" +#include "llclipboard.h" #include "llfloaterinventory.h" #include "llfloatersidepanelcontainer.h" #include "llfocusmgr.h" @@ -74,8 +74,10 @@ #include "llsidepanelinventory.h" #include "lltabcontainer.h" #include "lltooldraganddrop.h" +#include "lltrans.h" #include "lluictrlfactory.h" #include "llviewermessage.h" +#include "llviewerfoldertype.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" #include "llviewerwindow.h" @@ -440,7 +442,7 @@ void show_item_original(const LLUUID& item_uuid) //sidetray inventory panel LLSidepanelInventory *sidepanel_inventory = LLFloaterSidePanelContainer::getPanel<LLSidepanelInventory>("inventory"); - bool reset_inventory_filter = !floater_inventory->isInVisibleChain(); + bool do_reset_inventory_filter = !floater_inventory->isInVisibleChain(); LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(); if (!active_panel) @@ -460,37 +462,49 @@ void show_item_original(const LLUUID& item_uuid) } active_panel->setSelection(gInventory.getLinkedItemID(item_uuid), TAKE_FOCUS_NO); - if(reset_inventory_filter) + if(do_reset_inventory_filter) { - //inventory floater - bool floater_inventory_visible = false; + reset_inventory_filter(); + } +} + + +void reset_inventory_filter() +{ + //inventory floater + bool floater_inventory_visible = false; - LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("inventory"); - for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter) + LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("inventory"); + for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter) + { + LLFloaterInventory* floater_inventory = dynamic_cast<LLFloaterInventory*>(*iter); + if (floater_inventory) { - LLFloaterInventory* floater_inventory = dynamic_cast<LLFloaterInventory*>(*iter); - if (floater_inventory) - { - LLPanelMainInventory* main_inventory = floater_inventory->getMainInventoryPanel(); + LLPanelMainInventory* main_inventory = floater_inventory->getMainInventoryPanel(); - main_inventory->onFilterEdit(""); + main_inventory->onFilterEdit(""); - if(floater_inventory->getVisible()) - { - floater_inventory_visible = true; - } + if(floater_inventory->getVisible()) + { + floater_inventory_visible = true; } } - if(sidepanel_inventory && !floater_inventory_visible) + } + + if(!floater_inventory_visible) + { + LLSidepanelInventory *sidepanel_inventory = LLFloaterSidePanelContainer::getPanel<LLSidepanelInventory>("inventory"); + if (sidepanel_inventory) { LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); - - main_inventory->onFilterEdit(""); + if (main_inventory) + { + main_inventory->onFilterEdit(""); + } } } } - void open_outbox() { LLFloaterReg::showInstance("outbox"); @@ -948,7 +962,7 @@ void LLSaveFolderState::setApply(BOOL apply) void LLSaveFolderState::doFolder(LLFolderViewFolder* folder) { LLMemType mt(LLMemType::MTYPE_INVENTORY_DO_FOLDER); - LLInvFVBridge* bridge = (LLInvFVBridge*)folder->getListener(); + LLInvFVBridge* bridge = (LLInvFVBridge*)folder->getViewModelItem(); if(!bridge) return; if(mApply) @@ -983,7 +997,7 @@ void LLSaveFolderState::doFolder(LLFolderViewFolder* folder) void LLOpenFilteredFolders::doItem(LLFolderViewItem *item) { - if (item->getFiltered()) + if (item->passedFilter()) { item->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP); } @@ -991,12 +1005,12 @@ void LLOpenFilteredFolders::doItem(LLFolderViewItem *item) void LLOpenFilteredFolders::doFolder(LLFolderViewFolder* folder) { - if (folder->getFiltered() && folder->getParentFolder()) + if (folder->LLFolderViewItem::passedFilter() && folder->getParentFolder()) { folder->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP); } // if this folder didn't pass the filter, and none of its descendants did - else if (!folder->getFiltered() && !folder->hasFilteredDescendants()) + else if (!folder->getViewModelItem()->passedFilter() && !folder->getViewModelItem()->descendantsPassedFilter()) { folder->setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_NO); } @@ -1004,7 +1018,7 @@ void LLOpenFilteredFolders::doFolder(LLFolderViewFolder* folder) void LLSelectFirstFilteredItem::doItem(LLFolderViewItem *item) { - if (item->getFiltered() && !mItemSelected) + if (item->passedFilter() && !mItemSelected) { item->getRoot()->setSelection(item, FALSE, FALSE); if (item->getParentFolder()) @@ -1017,7 +1031,7 @@ void LLSelectFirstFilteredItem::doItem(LLFolderViewItem *item) void LLSelectFirstFilteredItem::doFolder(LLFolderViewFolder* folder) { - if (folder->getFiltered() && !mItemSelected) + if (folder->LLFolderViewItem::passedFilter() && !mItemSelected) { folder->getRoot()->setSelection(folder, FALSE, FALSE); if (folder->getParentFolder()) @@ -1044,3 +1058,87 @@ void LLOpenFoldersWithSelection::doFolder(LLFolderViewFolder* folder) } } +void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root, const std::string& action) +{ + if ("rename" == action) + { + root->startRenamingSelectedItem(); + return; + } + if ("delete" == action) + { + LLSD args; + args["QUESTION"] = LLTrans::getString(root->getNumSelectedItems() > 1 ? "DeleteItems" : "DeleteItem"); + LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&LLInventoryAction::onItemsRemovalConfirmation, _1, _2, root)); + return; + } + if (("copy" == action) || ("cut" == action)) + { + // Clear the clipboard before we start adding things on it + LLClipboard::instance().reset(); + } + + static const std::string change_folder_string = "change_folder_type_"; + if (action.length() > change_folder_string.length() && + (action.compare(0,change_folder_string.length(),"change_folder_type_") == 0)) + { + LLFolderType::EType new_folder_type = LLViewerFolderType::lookupTypeFromXUIName(action.substr(change_folder_string.length())); + LLFolderViewModelItemInventory* inventory_item = static_cast<LLFolderViewModelItemInventory*>(root->getViewModelItem()); + LLViewerInventoryCategory *cat = model->getCategory(inventory_item->getUUID()); + if (!cat) return; + cat->changeType(new_folder_type); + return; + } + + + std::set<LLFolderViewItem*> selected_items = root->getSelectionList(); + + LLMultiPreview* multi_previewp = NULL; + LLMultiProperties* multi_propertiesp = NULL; + + if (("task_open" == action || "open" == action) && selected_items.size() > 1) + { + multi_previewp = new LLMultiPreview(); + gFloaterView->addChild(multi_previewp); + + LLFloater::setFloaterHost(multi_previewp); + + } + else if (("task_properties" == action || "properties" == action) && selected_items.size() > 1) + { + multi_propertiesp = new LLMultiProperties(); + gFloaterView->addChild(multi_propertiesp); + + LLFloater::setFloaterHost(multi_propertiesp); + } + + std::set<LLFolderViewItem*>::iterator set_iter; + + for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) + { + LLFolderViewItem* folder_item = *set_iter; + if(!folder_item) continue; + LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem(); + if(!bridge) continue; + bridge->performAction(model, action); + } + + LLFloater::setFloaterHost(NULL); + if (multi_previewp) + { + multi_previewp->openFloater(LLSD()); + } + else if (multi_propertiesp) + { + multi_propertiesp->openFloater(LLSD()); + } +} + +void LLInventoryAction::onItemsRemovalConfirmation( const LLSD& notification, const LLSD& response, LLFolderView* root ) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) + { + root->removeSelectedItems(); + } +} diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 5cf9c528b0..11fc17ce9b 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -56,6 +56,7 @@ void show_item_profile(const LLUUID& item_uuid); void show_task_item_profile(const LLUUID& item_uuid, const LLUUID& object_id); void show_item_original(const LLUUID& item_uuid); +void reset_inventory_filter(); void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name); @@ -418,21 +419,6 @@ public: class LLFolderViewItem; class LLFolderViewFolder; -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLFolderViewFunctor -// -// Simple abstract base class for applying a functor to folders and -// items in a folder view hierarchy. This is suboptimal for algorithms -// that only work folders or only work on items, but I'll worry about -// that later when it's determined to be too slow. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLFolderViewFunctor -{ -public: - virtual ~LLFolderViewFunctor() {} - virtual void doFolder(LLFolderViewFolder* folder) = 0; - virtual void doItem(LLFolderViewItem* item) = 0; -}; class LLInventoryState { @@ -442,49 +428,13 @@ public: static LLUUID sWearNewClothingTransactionID; // wear all clothing in this transaction }; -class LLSelectFirstFilteredItem : public LLFolderViewFunctor +struct LLInventoryAction { -public: - LLSelectFirstFilteredItem() : mItemSelected(FALSE) {} - virtual ~LLSelectFirstFilteredItem() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); - BOOL wasItemSelected() { return mItemSelected; } -protected: - BOOL mItemSelected; -}; + static void doToSelected(class LLInventoryModel* model, class LLFolderView* root, const std::string& action); -class LLOpenFilteredFolders : public LLFolderViewFunctor -{ -public: - LLOpenFilteredFolders() {} - virtual ~LLOpenFilteredFolders() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); + static void onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response, LLFolderView* root); }; -class LLSaveFolderState : public LLFolderViewFunctor -{ -public: - LLSaveFolderState() : mApply(FALSE) {} - virtual ~LLSaveFolderState() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item) {} - void setApply(BOOL apply); - void clearOpenFolders() { mOpenFolders.clear(); } -protected: - std::set<LLUUID> mOpenFolders; - BOOL mApply; -}; - -class LLOpenFoldersWithSelection : public LLFolderViewFunctor -{ -public: - LLOpenFoldersWithSelection() {} - virtual ~LLOpenFoldersWithSelection() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); -}; #endif // LL_LLINVENTORYFUNCTIONS_H diff --git a/indra/newview/llinventoryicon.h b/indra/newview/llinventoryicon.h index c7e2998a20..5c8acf9e85 100644 --- a/indra/newview/llinventoryicon.h +++ b/indra/newview/llinventoryicon.h @@ -1,5 +1,5 @@ /** - * @file llinventoryfunctions.h + * @file llinventoryicon.h * @brief Miscellaneous inventory-related functions and classes * class definition * diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 6e23d7c701..e7d59d92d9 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -373,13 +373,12 @@ void LLInventoryModel::unlockDirectDescendentArrays(const LLUUID& cat_id) // specifies 'type' as what it defaults to containing. The category is // not necessarily only for that type. *NOTE: This will create a new // inventory category on the fly if one does not exist. -const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType preferred_type, - bool create_folder, - bool find_in_library) +const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType preferred_type, bool create_folder/*, + bool find_in_library*/) { LLUUID rv = LLUUID::null; - const LLUUID &root_id = (find_in_library) ? gInventory.getLibraryRootFolderID() : gInventory.getRootFolderID(); + const LLUUID &root_id = /*(find_in_library) ? gInventory.getLibraryRootFolderID() :*/ gInventory.getRootFolderID(); if(LLFolderType::FT_ROOT_INVENTORY == preferred_type) { rv = root_id; @@ -402,7 +401,44 @@ const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType prefe } } - if(rv.isNull() && isInventoryUsable() && (create_folder && !find_in_library)) + if(rv.isNull() && isInventoryUsable() && (create_folder && true/*!find_in_library*/)) + { + if(root_id.notNull()) + { + return createNewCategory(root_id, preferred_type, LLStringUtil::null); + } + } + return rv; +} + +const LLUUID LLInventoryModel::findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type, bool create_folder) +{ + LLUUID rv = LLUUID::null; + + const LLUUID &root_id = gInventory.getLibraryRootFolderID(); + if(LLFolderType::FT_ROOT_INVENTORY == preferred_type) + { + rv = root_id; + } + else if (root_id.notNull()) + { + cat_array_t* cats = NULL; + cats = get_ptr_in_map(mParentChildCategoryTree, root_id); + if(cats) + { + S32 count = cats->count(); + for(S32 i = 0; i < count; ++i) + { + if(cats->get(i)->getPreferredType() == preferred_type) + { + rv = cats->get(i)->getUUID(); + break; + } + } + } + } + + if(rv.isNull() && isInventoryUsable() && (create_folder && true/*!find_in_library*/)) { if(root_id.notNull()) { @@ -950,6 +986,7 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat) cat_array->put(old_cat); } mask |= LLInventoryObserver::STRUCTURE; + mask |= LLInventoryObserver::INTERNAL; } if(old_cat->getName() != cat->getName()) { @@ -1245,14 +1282,33 @@ void LLInventoryModel::purgeDescendentsOf(const LLUUID& id) items, INCLUDE_TRASH); S32 count = items.count(); + + item_map_t::iterator item_map_end = mItemMap.end(); + cat_map_t::iterator cat_map_end = mCategoryMap.end(); + LLUUID uu_id; + for(S32 i = 0; i < count; ++i) { - deleteObject(items.get(i)->getUUID()); + uu_id = items.get(i)->getUUID(); + + // This check prevents the deletion of a previously deleted item. + // This is necessary because deletion is not done in a hierarchical + // order. The current item may have been already deleted as a child + // of its deleted parent. + if (mItemMap.find(uu_id) != item_map_end) + { + deleteObject(uu_id); + } } + count = categories.count(); for(S32 i = 0; i < count; ++i) { - deleteObject(categories.get(i)->getUUID()); + uu_id = categories.get(i)->getUUID(); + if (mCategoryMap.find(uu_id) != cat_map_end) + { + deleteObject(uu_id); + } } } } @@ -3239,68 +3295,7 @@ void LLInventoryModel::updateItemsOrder(LLInventoryModel::item_array_t& items, c } } -//* @param[in] items vector of items in order to be saved. -void LLInventoryModel::saveItemsOrder(const LLInventoryModel::item_array_t& items) -{ - int sortField = 0; - - // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field - for (item_array_t::const_iterator i = items.begin(); i != items.end(); ++i) - { - LLViewerInventoryItem* item = *i; - - item->setSortField(++sortField); - item->setComplete(TRUE); - item->updateServer(FALSE); - - updateItem(item); - - // Tell the parent folder to refresh its sort order. - addChangedMask(LLInventoryObserver::SORT, item->getParentUUID()); - } - - notifyObservers(); -} - -// See also LLInventorySort where landmarks in the Favorites folder are sorted. -class LLViewerInventoryItemSort -{ -public: - bool operator()(const LLPointer<LLViewerInventoryItem>& a, const LLPointer<LLViewerInventoryItem>& b) - { - return a->getSortField() < b->getSortField(); - } -}; -/** - * Sorts passed items by LLViewerInventoryItem sort field. - * - * @param[in, out] items - array of items, not sorted. - */ -static void rearrange_item_order_by_sort_field(LLInventoryModel::item_array_t& items) -{ - static LLViewerInventoryItemSort sort_functor; - std::sort(items.begin(), items.end(), sort_functor); -} - -// * @param source_item_id - LLUUID of the source item to be moved into new position -// * @param target_item_id - LLUUID of the target item before which source item should be placed. -void LLInventoryModel::rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id) -{ - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - LLIsType is_type(LLAssetType::AT_LANDMARK); - LLUUID favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); - gInventory.collectDescendentsIf(favorites_id, cats, items, LLInventoryModel::EXCLUDE_TRASH, is_type); - - // ensure items are sorted properly before changing order. EXT-3498 - rearrange_item_order_by_sort_field(items); - - // update order - updateItemsOrder(items, source_item_id, target_item_id); - - saveItemsOrder(items); -} //---------------------------------------------------------------------------- diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index 8382e875b4..503de627a0 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -231,11 +231,12 @@ public: // Returns the uuid of the category that specifies 'type' as what it // defaults to containing. The category is not necessarily only for that type. // NOTE: If create_folder is true, this will create a new inventory category - // on the fly if one does not exist. *NOTE: if find_in_library is true it - // will search in the user's library folder instead of "My Inventory" + // on the fly if one does not exist. const LLUUID findCategoryUUIDForType(LLFolderType::EType preferred_type, - bool create_folder = true, - bool find_in_library = false); + bool create_folder = true); + // will search in the user's library folder instead of "My Inventory" + const LLUUID findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type, + bool create_folder = true); // Get whatever special folder this object is a child of, if any. const LLViewerInventoryCategory *getFirstNondefaultParent(const LLUUID& obj_id) const; @@ -362,15 +363,6 @@ public: // Returns end() of the vector if not found. static LLInventoryModel::item_array_t::iterator findItemIterByUUID(LLInventoryModel::item_array_t& items, const LLUUID& id); - // Saves current order of the passed items using inventory item sort field. - // Resets 'items' sort fields and saves them on server. - // Is used to save order for Favorites folder. - void saveItemsOrder(const LLInventoryModel::item_array_t& items); - - // Rearranges Landmarks inside Favorites folder. - // Moves source landmark before target one. - void rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id); - //-------------------------------------------------------------------- // Creation //-------------------------------------------------------------------- diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 71dd963f28..dafc71b59c 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -38,11 +38,13 @@ #include "llfloaterreg.h" #include "llfloatersidepanelcontainer.h" #include "llfolderview.h" -#include "llimfloater.h" +#include "llfolderviewitem.h" +#include "llimfloatercontainer.h" #include "llimview.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" #include "llinventorymodelbackgroundfetch.h" +#include "llpreview.h" #include "llsidepanelinventory.h" #include "llviewerattachmenu.h" #include "llviewerfoldertype.h" @@ -53,8 +55,7 @@ static LLDefaultChildRegistry::Register<LLInventoryPanel> r("inventory_panel"); const std::string LLInventoryPanel::DEFAULT_SORT_ORDER = std::string("InventorySortOrder"); const std::string LLInventoryPanel::RECENTITEMS_SORT_ORDER = std::string("RecentItemsSortOrder"); const std::string LLInventoryPanel::INHERIT_SORT_ORDER = std::string(""); -static const LLInventoryFVBridgeBuilder INVENTORY_BRIDGE_BUILDER; - +static const LLInventoryFolderViewModelBuilder INVENTORY_BRIDGE_BUILDER; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLInventoryPanelObserver @@ -134,78 +135,79 @@ LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) : mAllowMultiSelect(p.allow_multi_select), mShowItemLinkOverlays(p.show_item_link_overlays), mShowEmptyMessage(p.show_empty_message), - mShowLoadStatus(p.show_load_status), mViewsInitialized(false), mInvFVBridgeBuilder(NULL) { mInvFVBridgeBuilder = &INVENTORY_BRIDGE_BUILDER; - // contex menu callbacks + // context menu callbacks mCommitCallbackRegistrar.add("Inventory.DoToSelected", boost::bind(&LLInventoryPanel::doToSelected, this, _2)); mCommitCallbackRegistrar.add("Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH)); mCommitCallbackRegistrar.add("Inventory.EmptyLostAndFound", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND)); mCommitCallbackRegistrar.add("Inventory.DoCreate", boost::bind(&LLInventoryPanel::doCreate, this, _2)); mCommitCallbackRegistrar.add("Inventory.AttachObject", boost::bind(&LLInventoryPanel::attachObject, this, _2)); mCommitCallbackRegistrar.add("Inventory.BeginIMSession", boost::bind(&LLInventoryPanel::beginIMSession, this)); - mCommitCallbackRegistrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars)); + mCommitCallbackRegistrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars, this)); } -void LLInventoryPanel::buildFolderView(const LLInventoryPanel::Params& params) +LLFolderView * LLInventoryPanel::createFolderRoot(LLUUID root_id ) { - // Determine the root folder in case specified, and - // build the views starting with that folder. - - std::string start_folder_name(params.start_folder()); + LLFolderView::Params p(mParams.folder_view); + p.name = getName(); + p.title = getLabel(); + p.rect = LLRect(0, 0, getRect().getWidth(), 0); + p.parent_panel = this; + p.tool_tip = p.name; + p.listener = mInvFVBridgeBuilder->createBridge( LLAssetType::AT_CATEGORY, + LLAssetType::AT_CATEGORY, + LLInventoryType::IT_CATEGORY, + this, + &mInventoryViewModel, + NULL, + root_id); + p.view_model = &mInventoryViewModel; + p.use_label_suffix = mParams.use_label_suffix; + p.allow_multiselect = mAllowMultiSelect; + p.show_empty_message = mShowEmptyMessage; + p.show_item_link_overlays = mShowItemLinkOverlays; + p.root = NULL; + p.options_menu = "menu_inventory.xml"; + + return LLUICtrlFactory::create<LLFolderView>(p); +} - const LLFolderType::EType preferred_type = LLViewerFolderType::lookupTypeFromNewCategoryName(start_folder_name); - - LLUUID root_id; - - if ("LIBRARY" == params.start_folder()) - { - root_id = gInventory.getLibraryRootFolderID(); - } - else +void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) { - root_id = (preferred_type != LLFolderType::FT_NONE) - ? gInventory.findCategoryUUIDForType(preferred_type, false, false) - : LLUUID::null; - } - - if ((root_id == LLUUID::null) && !start_folder_name.empty()) + // save off copy of params + mParams = params; + // Clear up the root view + // Note: This needs to be done *before* we build the new folder view + LLUUID root_id = getRootFolderID(); + if (mFolderRoot) { - llwarns << "No category found that matches start_folder: " << start_folder_name << llendl; - root_id = LLUUID::generateNewID(); + removeItemID(root_id); + mFolderRoot->destroyView(); + mFolderRoot = NULL; } - LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(LLAssetType::AT_CATEGORY, - LLAssetType::AT_CATEGORY, - LLInventoryType::IT_CATEGORY, - this, - NULL, - root_id); - - mFolderRoot = createFolderView(new_listener, params.use_label_suffix()); -} - -void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) -{ LLMemType mt(LLMemType::MTYPE_INVENTORY_POST_BUILD); - + mCommitCallbackRegistrar.pushScope(); // registered as a widget; need to push callback scope ourselves + { + // Determine the root folder in case specified, and + // build the views starting with that folder. + mFolderRoot = createFolderRoot(root_id); - buildFolderView(params); - + addItemID(root_id, mFolderRoot); +} mCommitCallbackRegistrar.popScope(); - mFolderRoot->setCallbackRegistrar(&mCommitCallbackRegistrar); // Scroller - { LLRect scroller_view_rect = getRect(); scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); - LLScrollContainer::Params scroller_params(params.scroll()); + LLScrollContainer::Params scroller_params(mParams.scroll()); scroller_params.rect(scroller_view_rect); mScroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params); addChild(mScroller); @@ -213,7 +215,6 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) mFolderRoot->setScrollContainer(mScroller); mFolderRoot->setFollowsAll(); mFolderRoot->addChild(mFolderRoot->mStatusTextBox); - } // Set up the callbacks from the inventory we're viewing, and then build everything. mInventoryObserver = new LLInventoryPanelObserver(this); @@ -228,6 +229,7 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) { initializeViews(); } + gIdleCallbacks.addFunction(onIdle, (void*)this); if (mSortOrderSetting != INHERIT_SORT_ORDER) @@ -240,32 +242,31 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) } // hide inbox - getFilter()->setFilterCategoryTypes(getFilter()->getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_INBOX)); - getFilter()->setFilterCategoryTypes(getFilter()->getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_OUTBOX)); + getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_INBOX)); + getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_OUTBOX)); // set the filter for the empty folder if the debug setting is on if (gSavedSettings.getBOOL("DebugHideEmptySystemFolders")) { - getFilter()->setFilterEmptySystemFolders(); + getFilter().setFilterEmptySystemFolders(); } // keep track of the clipboard state so that we avoid filtering too much mClipboardState = LLClipboard::instance().getGeneration(); // Initialize base class params. - LLPanel::initFromParams(params); + LLPanel::initFromParams(mParams); } LLInventoryPanel::~LLInventoryPanel() { - if (mFolderRoot) - { - U32 sort_order = mFolderRoot->getSortOrder(); + gIdleCallbacks.deleteFunction(idle, this); + + U32 sort_order = getFolderViewModel()->getSorter().getSortOrder(); if (mSortOrderSetting != INHERIT_SORT_ORDER) { gSavedSettings.setU32(mSortOrderSetting, sort_order); } - } gIdleCallbacks.deleteFunction(onIdle, this); @@ -281,82 +282,68 @@ LLInventoryPanel::~LLInventoryPanel() void LLInventoryPanel::draw() { // Select the desired item (in case it wasn't loaded when the selection was requested) - mFolderRoot->updateSelection(); - - // Nudge the filter if the clipboard state changed - if (mClipboardState != LLClipboard::instance().getGeneration()) - { - mClipboardState = LLClipboard::instance().getGeneration(); - getFilter()->setModified(LLClipboard::instance().isCutMode() ? LLInventoryFilter::FILTER_MORE_RESTRICTIVE : LLInventoryFilter::FILTER_LESS_RESTRICTIVE); - } + updateSelection(); LLPanel::draw(); } -LLInventoryFilter* LLInventoryPanel::getFilter() +const LLInventoryFilter& LLInventoryPanel::getFilter() const { - if (mFolderRoot) - { - return mFolderRoot->getFilter(); + return getFolderViewModel()->getFilter(); } - return NULL; -} -const LLInventoryFilter* LLInventoryPanel::getFilter() const +LLInventoryFilter& LLInventoryPanel::getFilter() { - if (mFolderRoot) - { - return mFolderRoot->getFilter(); - } - return NULL; + return getFolderViewModel()->getFilter(); } void LLInventoryPanel::setFilterTypes(U64 types, LLInventoryFilter::EFilterType filter_type) { if (filter_type == LLInventoryFilter::FILTERTYPE_OBJECT) - getFilter()->setFilterObjectTypes(types); + getFilter().setFilterObjectTypes(types); if (filter_type == LLInventoryFilter::FILTERTYPE_CATEGORY) - getFilter()->setFilterCategoryTypes(types); + getFilter().setFilterCategoryTypes(types); } U32 LLInventoryPanel::getFilterObjectTypes() const { - return mFolderRoot->getFilterObjectTypes(); + return getFilter().getFilterObjectTypes(); } U32 LLInventoryPanel::getFilterPermMask() const { - return mFolderRoot->getFilterPermissions(); + return getFilter().getFilterPermissions(); } void LLInventoryPanel::setFilterPermMask(PermissionMask filter_perm_mask) { - getFilter()->setFilterPermissions(filter_perm_mask); + getFilter().setFilterPermissions(filter_perm_mask); } void LLInventoryPanel::setFilterWearableTypes(U64 types) { - getFilter()->setFilterWearableTypes(types); + getFilter().setFilterWearableTypes(types); } void LLInventoryPanel::setFilterSubString(const std::string& string) { - getFilter()->setFilterSubString(string); + getFilter().setFilterSubString(string); } const std::string LLInventoryPanel::getFilterSubString() { - return mFolderRoot->getFilterSubString(); + return getFilter().getFilterSubString(); } void LLInventoryPanel::setSortOrder(U32 order) { - getFilter()->setSortOrder(order); - if (getFilter()->isModified()) + LLInventorySort sorter(order); + if (order != getFolderViewModel()->getSorter().getSortOrder()) { - mFolderRoot->setSortOrder(order); + getFolderViewModel()->setSorter(sorter); + mFolderRoot->arrangeAll(); // try to keep selection onscreen, even if it wasn't to start with mFolderRoot->scrollToShowSelection(); } @@ -364,37 +351,32 @@ void LLInventoryPanel::setSortOrder(U32 order) U32 LLInventoryPanel::getSortOrder() const { - return mFolderRoot->getSortOrder(); -} - -void LLInventoryPanel::requestSort() -{ - mFolderRoot->requestSort(); + return getFolderViewModel()->getSorter().getSortOrder(); } void LLInventoryPanel::setSinceLogoff(BOOL sl) { - getFilter()->setDateRangeLastLogoff(sl); + getFilter().setDateRangeLastLogoff(sl); } void LLInventoryPanel::setHoursAgo(U32 hours) { - getFilter()->setHoursAgo(hours); + getFilter().setHoursAgo(hours); } void LLInventoryPanel::setFilterLinks(U64 filter_links) { - getFilter()->setFilterLinks(filter_links); + getFilter().setFilterLinks(filter_links); } void LLInventoryPanel::setShowFolderState(LLInventoryFilter::EFolderShow show) { - getFilter()->setShowFolderState(show); + getFilter().setShowFolderState(show); } LLInventoryFilter::EFolderShow LLInventoryPanel::getShowFolderState() { - return getFilter()->getShowFolderState(); + return getFilter().getShowFolderState(); } void LLInventoryPanel::modelChanged(U32 mask) @@ -418,11 +400,20 @@ void LLInventoryPanel::modelChanged(U32 mask) { const LLUUID& item_id = (*items_iter); const LLInventoryObject* model_item = model->getObject(item_id); - LLFolderViewItem* view_item = mFolderRoot->getItemByID(item_id); + LLFolderViewItem* view_item = getItemByID(item_id); + LLFolderViewModelItemInventory* viewmodel_item = + static_cast<LLFolderViewModelItemInventory*>(view_item ? view_item->getViewModelItem() : NULL); // LLFolderViewFolder is derived from LLFolderViewItem so dynamic_cast from item // to folder is the fast way to get a folder without searching through folders tree. - LLFolderViewFolder* view_folder = dynamic_cast<LLFolderViewFolder*>(view_item); + LLFolderViewFolder* view_folder = NULL; + + // Check requires as this item might have already been deleted + // as a child of its deleted parent. + if (model_item && view_item) + { + view_folder = dynamic_cast<LLFolderViewFolder*>(view_item); + } ////////////////////////////// // LABEL Operation @@ -433,7 +424,7 @@ void LLInventoryPanel::modelChanged(U32 mask) if (view_item) { // Request refresh on this item (also flags for filtering) - LLInvFVBridge* bridge = (LLInvFVBridge*)view_item->getListener(); + LLInvFVBridge* bridge = (LLInvFVBridge*)view_item->getViewModelItem(); if(bridge) { // Clear the display name first, so it gets properly re-built during refresh() bridge->clearDisplayName(); @@ -449,11 +440,15 @@ void LLInventoryPanel::modelChanged(U32 mask) if (mask & LLInventoryObserver::REBUILD) { handled = true; - if (model_item && view_item) + if (model_item && view_item && viewmodel_item) { + const LLUUID& idp = viewmodel_item->getUUID(); view_item->destroyView(); + removeItemID(idp); } view_item = buildNewViews(item_id); + viewmodel_item = + static_cast<LLFolderViewModelItemInventory*>(view_item ? view_item->getViewModelItem() : NULL); view_folder = dynamic_cast<LLFolderViewFolder *>(view_item); } @@ -475,7 +470,7 @@ void LLInventoryPanel::modelChanged(U32 mask) { if (view_folder) { - view_folder->requestSort(); + view_folder->getViewModelItem()->requestSort(); } } @@ -510,20 +505,24 @@ void LLInventoryPanel::modelChanged(U32 mask) else if (model_item && view_item) { // Don't process the item if it is the root - if (view_item->getRoot() != view_item) + if (view_item->getParentFolder()) { - LLFolderViewFolder* new_parent = (LLFolderViewFolder*)mFolderRoot->getItemByID(model_item->getParentUUID()); + LLFolderViewFolder* new_parent = (LLFolderViewFolder*)getItemByID(model_item->getParentUUID()); // Item has been moved. if (view_item->getParentFolder() != new_parent) { if (new_parent != NULL) { // Item is to be moved and we found its new parent in the panel's directory, so move the item's UI. - view_item->getParentFolder()->extractItem(view_item); - view_item->addToFolder(new_parent, mFolderRoot); + view_item->addToFolder(new_parent); + addItemID(viewmodel_item->getUUID(), view_item); } else { + // Remove the item ID before destroying the view because the view-model-item gets + // destroyed when the view is destroyed + removeItemID(viewmodel_item->getUUID()); + // Item is to be moved outside the panel's directory (e.g. moved to trash for a panel that // doesn't include trash). Just remove the item's UI. view_item->destroyView(); @@ -535,9 +534,10 @@ void LLInventoryPanel::modelChanged(U32 mask) ////////////////////////////// // REMOVE Operation // This item has been removed from memory, but its associated UI element still exists. - else if (!model_item && view_item) + else if (!model_item && view_item && viewmodel_item) { // Remove the item's UI. + removeItemID(viewmodel_item->getUUID()); view_item->destroyView(); } } @@ -549,6 +549,43 @@ LLFolderView* LLInventoryPanel::getRootFolder() return mFolderRoot; } +LLUUID LLInventoryPanel::getRootFolderID() +{ + if (mFolderRoot && mFolderRoot->getViewModelItem()) + { + return static_cast<LLFolderViewModelItemInventory*>(mFolderRoot->getViewModelItem())->getUUID(); + + } + else + { + LLUUID root_id; + if (mParams.start_folder.id.isChosen()) + { + root_id = mParams.start_folder.id; + } + else + { + const LLFolderType::EType preferred_type = mParams.start_folder.type.isChosen() + ? mParams.start_folder.type + : LLViewerFolderType::lookupTypeFromNewCategoryName(mParams.start_folder.name); + + if ("LIBRARY" == mParams.start_folder.name()) + { + root_id = gInventory.getLibraryRootFolderID(); + } + else if (preferred_type != LLFolderType::FT_NONE) + { + root_id = gInventory.findCategoryUUIDForType(preferred_type, false); + if (root_id.isNull()) + { + llwarns << "Could not find folder of type " << preferred_type << llendl; + root_id.generateNewID(); + } + } + } + return root_id; + } +} // static void LLInventoryPanel::onIdle(void *userdata) @@ -568,16 +605,72 @@ void LLInventoryPanel::onIdle(void *userdata) } } -const LLUUID& LLInventoryPanel::getRootFolderID() const +struct DirtyFilterFunctor : public LLFolderViewFunctor { - return mFolderRoot->getListener()->getUUID(); + /*virtual*/ void doFolder(LLFolderViewFolder* folder) + { + folder->getViewModelItem()->dirtyFilter(); + } + /*virtual*/ void doItem(LLFolderViewItem* item) + { + item->getViewModelItem()->dirtyFilter(); + } +}; + +void LLInventoryPanel::idle(void* user_data) +{ + LLInventoryPanel* panel = (LLInventoryPanel*)user_data; + // Nudge the filter if the clipboard state changed + if (panel->mClipboardState != LLClipboard::instance().getGeneration()) + { + panel->mClipboardState = LLClipboard::instance().getGeneration(); + const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); + LLFolderViewFolder* trash_folder = panel->getFolderByID(trash_id); + if (trash_folder) + { + DirtyFilterFunctor dirtyFilterFunctor; + trash_folder->applyFunctorToChildren(dirtyFilterFunctor); + } + + } + + panel->mFolderRoot->update(); + // while dragging, update selection rendering to reflect single/multi drag status + if (LLToolDragAndDrop::getInstance()->hasMouseCapture()) + { + EAcceptance last_accept = LLToolDragAndDrop::getInstance()->getLastAccept(); + if (last_accept == ACCEPT_YES_SINGLE || last_accept == ACCEPT_YES_COPY_SINGLE) + { + panel->mFolderRoot->setShowSingleSelection(TRUE); + } + else + { + panel->mFolderRoot->setShowSingleSelection(FALSE); + } + } + else + { + panel->mFolderRoot->setShowSingleSelection(FALSE); + } } + void LLInventoryPanel::initializeViews() { if (!gInventory.isInventoryUsable()) return; - rebuildViewsFor(getRootFolderID()); + LLUUID root_id = getRootFolderID(); + if (root_id.notNull()) + { + buildNewViews(getRootFolderID()); + } + else + { + buildNewViews(gInventory.getRootFolderID()); + buildNewViews(gInventory.getLibraryRootFolderID()); + } + + gIdleCallbacks.addFunction(idle, this); mViewsInitialized = true; @@ -587,14 +680,14 @@ void LLInventoryPanel::initializeViews() if (gAgent.isFirstLogin()) { // Auto open the user's library - LLFolderViewFolder* lib_folder = mFolderRoot->getFolderByID(gInventory.getLibraryRootFolderID()); + LLFolderViewFolder* lib_folder = getFolderByID(gInventory.getLibraryRootFolderID()); if (lib_folder) { lib_folder->setOpen(TRUE); } // Auto close the user's my inventory folder - LLFolderViewFolder* my_inv_folder = mFolderRoot->getFolderByID(gInventory.getRootFolderID()); + LLFolderViewFolder* my_inv_folder = getFolderByID(gInventory.getRootFolderID()); if (my_inv_folder) { my_inv_folder->setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_DOWN); @@ -602,54 +695,12 @@ void LLInventoryPanel::initializeViews() } } -LLFolderViewItem* LLInventoryPanel::rebuildViewsFor(const LLUUID& id) -{ - // Destroy the old view for this ID so we can rebuild it. - LLFolderViewItem* old_view = mFolderRoot->getItemByID(id); - if (old_view) - { - old_view->destroyView(); - } - - return buildNewViews(id); -} - -LLFolderView * LLInventoryPanel::createFolderView(LLInvFVBridge * bridge, bool useLabelSuffix) -{ - LLRect folder_rect(0, - 0, - getRect().getWidth(), - 0); - - LLFolderView::Params p; - - p.name = getName(); - p.title = getLabel(); - p.rect = folder_rect; - p.parent_panel = this; - p.tool_tip = p.name; - p.listener = bridge; - p.use_label_suffix = useLabelSuffix; - p.allow_multiselect = mAllowMultiSelect; - p.show_empty_message = mShowEmptyMessage; - p.show_load_status = mShowLoadStatus; - - return LLUICtrlFactory::create<LLFolderView>(p); -} LLFolderViewFolder * LLInventoryPanel::createFolderViewFolder(LLInvFVBridge * bridge) { - LLFolderViewFolder::Params params; + LLFolderViewFolder::Params params(mParams.folder); params.name = bridge->getDisplayName(); - params.icon = bridge->getIcon(); - params.icon_open = bridge->getOpenIcon(); - - if (mShowItemLinkOverlays) // if false, then links show up just like normal items - { - params.icon_overlay = LLUI::getUIImage("Inv_Link"); - } - params.root = mFolderRoot; params.listener = bridge; params.tool_tip = params.name; @@ -659,17 +710,9 @@ LLFolderViewFolder * LLInventoryPanel::createFolderViewFolder(LLInvFVBridge * br LLFolderViewItem * LLInventoryPanel::createFolderViewItem(LLInvFVBridge * bridge) { - LLFolderViewItem::Params params; + LLFolderViewItem::Params params(mParams.item); params.name = bridge->getDisplayName(); - params.icon = bridge->getIcon(); - params.icon_open = bridge->getOpenIcon(); - - if (mShowItemLinkOverlays) // if false, then links show up just like normal items - { - params.icon_overlay = LLUI::getUIImage("Inv_Link"); - } - params.creation_date = bridge->getCreationDate(); params.root = mFolderRoot; params.listener = bridge; @@ -682,20 +725,15 @@ LLFolderViewItem * LLInventoryPanel::createFolderViewItem(LLInvFVBridge * bridge LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) { LLInventoryObject const* objectp = gInventory.getObject(id); - LLUUID root_id = mFolderRoot->getListener()->getUUID(); - LLFolderViewFolder* parent_folder = NULL; - LLFolderViewItem* itemp = NULL; - if (id == root_id) - { - parent_folder = mFolderRoot; - } - else if (objectp) - { + if (!objectp) return NULL; + + LLFolderViewItem* folder_view_item = getItemByID(id); + const LLUUID &parent_id = objectp->getParentUUID(); - parent_folder = (LLFolderViewFolder*)mFolderRoot->getItemByID(parent_id); + LLFolderViewFolder* parent_folder = (LLFolderViewFolder*)getItemByID(parent_id); - if (parent_folder) + if (!folder_view_item && parent_folder) { if (objectp->getType() <= LLAssetType::AT_NONE || objectp->getType() >= LLAssetType::AT_COUNT) @@ -713,16 +751,12 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) objectp->getType(), LLInventoryType::IT_CATEGORY, this, + &mInventoryViewModel, mFolderRoot, objectp->getUUID()); if (new_listener) { - LLFolderViewFolder* folderp = createFolderViewFolder(new_listener); - if (folderp) - { - folderp->setItemSortOrder(mFolderRoot->getSortOrder()); - } - itemp = folderp; + folder_view_item = createFolderViewFolder(new_listener); } } else @@ -733,28 +767,28 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) item->getActualType(), item->getInventoryType(), this, + &mInventoryViewModel, mFolderRoot, item->getUUID(), item->getFlags()); if (new_listener) { - itemp = createFolderViewItem(new_listener); + folder_view_item = createFolderViewItem(new_listener); } } - if (itemp) - { - itemp->addToFolder(parent_folder, mFolderRoot); - } - } + if (folder_view_item) + { + llassert(parent_folder != NULL); + folder_view_item->addToFolder(parent_folder); + addItemID(id, folder_view_item); + } } // If this is a folder, add the children of the folder and recursively add any // child folders. - if (id.isNull() - || (objectp - && objectp->getType() == LLAssetType::AT_CATEGORY)) + if (folder_view_item && objectp->getType() == LLAssetType::AT_CATEGORY) { LLViewerInventoryCategory::cat_array_t* categories; LLViewerInventoryItem::item_array_t* items; @@ -771,7 +805,7 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) } } - if(items && parent_folder) + if(items) { for (LLViewerInventoryItem::item_array_t::const_iterator item_iter = items->begin(); item_iter != items->end(); @@ -784,7 +818,7 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id) mInventory->unlockDirectDescendentArrays(id); } - return itemp; + return folder_view_item; } // bit of a hack to make sure the inventory is open. @@ -795,8 +829,8 @@ void LLInventoryPanel::openStartFolderOrMyInventory() { LLFolderViewFolder *fchild = dynamic_cast<LLFolderViewFolder*>(child); if (fchild - && fchild->getListener() - && fchild->getListener()->getUUID() == gInventory.getRootFolderID()) + && fchild->getViewModelItem() + && fchild->getViewModelItem()->getName() == "My Inventory") { fchild->setOpen(TRUE); break; @@ -813,7 +847,7 @@ void LLInventoryPanel::openSelected() { LLFolderViewItem* folder_item = mFolderRoot->getCurSelectedItem(); if(!folder_item) return; - LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getListener(); + LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem(); if(!bridge) return; bridge->openItem(); } @@ -917,7 +951,7 @@ void LLInventoryPanel::setSelection(const LLUUID& obj_id, BOOL take_keyboard_foc { return; } - mFolderRoot->setSelectionByID(obj_id, take_keyboard_focus); + setSelectionByID(obj_id, take_keyboard_focus); } void LLInventoryPanel::setSelectCallback(const boost::function<void (const std::deque<LLFolderViewItem*>& items, BOOL user_action)>& cb) @@ -930,7 +964,7 @@ void LLInventoryPanel::setSelectCallback(const boost::function<void (const std:: void LLInventoryPanel::clearSelection() { - mFolderRoot->clearSelection(); + mSelectThisID.setNull(); } void LLInventoryPanel::onSelectionChange(const std::deque<LLFolderViewItem*>& items, BOOL user_action) @@ -939,7 +973,7 @@ void LLInventoryPanel::onSelectionChange(const std::deque<LLFolderViewItem*>& it mCompletionObserver->reset(); for (std::deque<LLFolderViewItem*>::const_iterator it = items.begin(); it != items.end(); ++it) { - LLUUID id = (*it)->getListener()->getUUID(); + LLUUID id = static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID(); LLViewerInventoryItem* inv_item = mInventory->getItem(id); if (inv_item && !inv_item->isFinished()) @@ -959,19 +993,15 @@ void LLInventoryPanel::onSelectionChange(const std::deque<LLFolderViewItem*>& it } } -void LLInventoryPanel::doToSelected(const LLSD& userdata) -{ - mFolderRoot->doToSelected(&gInventory, userdata); -} - void LLInventoryPanel::doCreate(const LLSD& userdata) { - menu_create_inventory_item(mFolderRoot, LLFolderBridge::sSelf.get(), userdata); + reset_inventory_filter(); + menu_create_inventory_item(this, LLFolderBridge::sSelf.get(), userdata); } bool LLInventoryPanel::beginIMSession() { - std::set<LLUUID> selected_items = mFolderRoot->getSelectionList(); + std::set<LLFolderViewItem*> selected_items = mFolderRoot->getSelectionList(); std::string name; static int session_num = 1; @@ -979,20 +1009,19 @@ bool LLInventoryPanel::beginIMSession() LLDynamicArray<LLUUID> members; EInstantMessage type = IM_SESSION_CONFERENCE_START; - std::set<LLUUID>::const_iterator iter; + std::set<LLFolderViewItem*>::const_iterator iter; for (iter = selected_items.begin(); iter != selected_items.end(); iter++) { - LLUUID item = *iter; - LLFolderViewItem* folder_item = mFolderRoot->getItemByID(item); + LLFolderViewItem* folder_item = (*iter); if(folder_item) { - LLFolderViewEventListener* fve_listener = folder_item->getListener(); + LLFolderViewModelItemInventory* fve_listener = static_cast<LLFolderViewModelItemInventory*>(folder_item->getViewModelItem()); if (fve_listener && (fve_listener->getInventoryType() == LLInventoryType::IT_CATEGORY)) { - LLFolderBridge* bridge = (LLFolderBridge*)folder_item->getListener(); + LLFolderBridge* bridge = (LLFolderBridge*)folder_item->getViewModelItem(); if(!bridge) return true; LLViewerInventoryCategory* cat = bridge->getCategory(); if(!cat) return true; @@ -1026,9 +1055,7 @@ bool LLInventoryPanel::beginIMSession() } else { - LLFolderViewItem* folder_item = mFolderRoot->getItemByID(item); - if(!folder_item) return true; - LLInvFVBridge* listenerp = (LLInvFVBridge*)folder_item->getListener(); + LLInvFVBridge* listenerp = (LLInvFVBridge*)folder_item->getViewModelItem(); if (listenerp->getInventoryType() == LLInventoryType::IT_CALLINGCARD) { @@ -1060,7 +1087,7 @@ bool LLInventoryPanel::beginIMSession() LLUUID session_id = gIMMgr->addSession(name, type, members[0], members); if (session_id != LLUUID::null) { - LLIMFloater::show(session_id); + LLIMFloaterContainer::getInstance()->showConversation(session_id); } return true; @@ -1069,13 +1096,13 @@ bool LLInventoryPanel::beginIMSession() bool LLInventoryPanel::attachObject(const LLSD& userdata) { // Copy selected item UUIDs to a vector. - std::set<LLUUID> selected_items = mFolderRoot->getSelectionList(); + std::set<LLFolderViewItem*> selected_items = mFolderRoot->getSelectionList(); uuid_vec_t items; - for (std::set<LLUUID>::const_iterator set_iter = selected_items.begin(); + for (std::set<LLFolderViewItem*>::const_iterator set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) { - items.push_back(*set_iter); + items.push_back(static_cast<LLFolderViewModelItemInventory*>((*set_iter)->getViewModelItem())->getUUID()); } // Attach selected items. @@ -1088,7 +1115,7 @@ bool LLInventoryPanel::attachObject(const LLSD& userdata) BOOL LLInventoryPanel::getSinceLogoff() { - return getFilter()->isSinceLogoff(); + return getFilter().isSinceLogoff(); } // DEBUG ONLY @@ -1214,12 +1241,93 @@ void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const L void LLInventoryPanel::addHideFolderType(LLFolderType::EType folder_type) { - getFilter()->setFilterCategoryTypes(getFilter()->getFilterCategoryTypes() & ~(1ULL << folder_type)); + getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << folder_type)); } BOOL LLInventoryPanel::getIsHiddenFolderType(LLFolderType::EType folder_type) const { - return !(getFilter()->getFilterCategoryTypes() & (1ULL << folder_type)); + return !(getFilter().getFilterCategoryTypes() & (1ULL << folder_type)); +} + +void LLInventoryPanel::addItemID( const LLUUID& id, LLFolderViewItem* itemp ) +{ + mItemMap[id] = itemp; +} + +void LLInventoryPanel::removeItemID(const LLUUID& id) +{ + LLInventoryModel::cat_array_t categories; + LLInventoryModel::item_array_t items; + gInventory.collectDescendents(id, categories, items, TRUE); + + mItemMap.erase(id); + + for (LLInventoryModel::cat_array_t::iterator it = categories.begin(), end_it = categories.end(); + it != end_it; + ++it) + { + mItemMap.erase((*it)->getUUID()); +} + + for (LLInventoryModel::item_array_t::iterator it = items.begin(), end_it = items.end(); + it != end_it; + ++it) + { + mItemMap.erase((*it)->getUUID()); + } +} + +LLFastTimer::DeclareTimer FTM_GET_ITEM_BY_ID("Get FolderViewItem by ID"); +LLFolderViewItem* LLInventoryPanel::getItemByID(const LLUUID& id) +{ + LLFastTimer _(FTM_GET_ITEM_BY_ID); + + std::map<LLUUID, LLFolderViewItem*>::iterator map_it; + map_it = mItemMap.find(id); + if (map_it != mItemMap.end()) + { + return map_it->second; + } + + return NULL; +} + +LLFolderViewFolder* LLInventoryPanel::getFolderByID(const LLUUID& id) +{ + LLFolderViewItem* item = getItemByID(id); + return dynamic_cast<LLFolderViewFolder*>(item); +} + + +void LLInventoryPanel::setSelectionByID( const LLUUID& obj_id, BOOL take_keyboard_focus ) +{ + LLFolderViewItem* itemp = getItemByID(obj_id); + if(itemp && itemp->getViewModelItem()) + { + itemp->arrangeAndSet(TRUE, take_keyboard_focus); + mSelectThisID.setNull(); + return; + } + else + { + // save the desired item to be selected later (if/when ready) + mSelectThisID = obj_id; + } +} + +void LLInventoryPanel::updateSelection() +{ + if (mSelectThisID.notNull()) + { + setSelectionByID(mSelectThisID, false); + } +} + +void LLInventoryPanel::doToSelected(const LLSD& userdata) +{ + LLInventoryAction::doToSelected(mInventory, mFolderRoot, userdata.asString()); + + return; } @@ -1240,7 +1348,7 @@ public: { LLInventoryPanel::initFromParams(p); // turn on inbox for recent items - getFilter()->setFilterCategoryTypes(getFilter()->getFilterCategoryTypes() | (1ULL << LLFolderType::FT_INBOX)); + getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() | (1ULL << LLFolderType::FT_INBOX)); } protected: @@ -1255,3 +1363,34 @@ LLInventoryRecentItemsPanel::LLInventoryRecentItemsPanel( const Params& params) mInvFVBridgeBuilder = &RECENT_ITEMS_BUILDER; } +namespace LLInitParam +{ + void TypeValues<LLFolderType::EType>::declareValues() + { + declare(LLFolderType::lookup(LLFolderType::FT_TEXTURE) , LLFolderType::FT_TEXTURE); + declare(LLFolderType::lookup(LLFolderType::FT_SOUND) , LLFolderType::FT_SOUND); + declare(LLFolderType::lookup(LLFolderType::FT_CALLINGCARD) , LLFolderType::FT_CALLINGCARD); + declare(LLFolderType::lookup(LLFolderType::FT_LANDMARK) , LLFolderType::FT_LANDMARK); + declare(LLFolderType::lookup(LLFolderType::FT_CLOTHING) , LLFolderType::FT_CLOTHING); + declare(LLFolderType::lookup(LLFolderType::FT_OBJECT) , LLFolderType::FT_OBJECT); + declare(LLFolderType::lookup(LLFolderType::FT_NOTECARD) , LLFolderType::FT_NOTECARD); + declare(LLFolderType::lookup(LLFolderType::FT_ROOT_INVENTORY) , LLFolderType::FT_ROOT_INVENTORY); + declare(LLFolderType::lookup(LLFolderType::FT_LSL_TEXT) , LLFolderType::FT_LSL_TEXT); + declare(LLFolderType::lookup(LLFolderType::FT_BODYPART) , LLFolderType::FT_BODYPART); + declare(LLFolderType::lookup(LLFolderType::FT_TRASH) , LLFolderType::FT_TRASH); + declare(LLFolderType::lookup(LLFolderType::FT_SNAPSHOT_CATEGORY), LLFolderType::FT_SNAPSHOT_CATEGORY); + declare(LLFolderType::lookup(LLFolderType::FT_LOST_AND_FOUND) , LLFolderType::FT_LOST_AND_FOUND); + declare(LLFolderType::lookup(LLFolderType::FT_ANIMATION) , LLFolderType::FT_ANIMATION); + declare(LLFolderType::lookup(LLFolderType::FT_GESTURE) , LLFolderType::FT_GESTURE); + declare(LLFolderType::lookup(LLFolderType::FT_FAVORITE) , LLFolderType::FT_FAVORITE); + declare(LLFolderType::lookup(LLFolderType::FT_ENSEMBLE_START) , LLFolderType::FT_ENSEMBLE_START); + declare(LLFolderType::lookup(LLFolderType::FT_ENSEMBLE_END) , LLFolderType::FT_ENSEMBLE_END); + declare(LLFolderType::lookup(LLFolderType::FT_CURRENT_OUTFIT) , LLFolderType::FT_CURRENT_OUTFIT); + declare(LLFolderType::lookup(LLFolderType::FT_OUTFIT) , LLFolderType::FT_OUTFIT); + declare(LLFolderType::lookup(LLFolderType::FT_MY_OUTFITS) , LLFolderType::FT_MY_OUTFITS); + declare(LLFolderType::lookup(LLFolderType::FT_MESH ) , LLFolderType::FT_MESH ); + declare(LLFolderType::lookup(LLFolderType::FT_INBOX) , LLFolderType::FT_INBOX); + declare(LLFolderType::lookup(LLFolderType::FT_OUTBOX) , LLFolderType::FT_OUTBOX); + declare(LLFolderType::lookup(LLFolderType::FT_BASIC_ROOT) , LLFolderType::FT_BASIC_ROOT); + } +} diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index 6db59afb9b..c4f3c1b47d 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -30,6 +30,8 @@ #include "llassetstorage.h" #include "lldarray.h" +#include "llfolderviewitem.h" +#include "llfolderviewmodelinventory.h" #include "llfloater.h" #include "llinventory.h" #include "llinventoryfilter.h" @@ -38,22 +40,19 @@ #include "lluictrlfactory.h" #include <set> -class LLFolderView; -class LLFolderViewFolder; -class LLFolderViewItem; -class LLInventoryFilter; -class LLInventoryModel; class LLInvFVBridge; -class LLInventoryFVBridgeBuilder; -class LLMenuBarGL; -class LLCheckBoxCtrl; -class LLSpinCtrl; -class LLTextBox; -class LLIconCtrl; -class LLSaveFolderState; -class LLFilterEditor; -class LLTabContainer; +class LLInventoryFolderViewModelBuilder; class LLInvPanelComplObserver; +class LLFolderViewModelInventory; + +namespace LLInitParam +{ + template<> + struct TypeValues<LLFolderType::EType> : public TypeValuesHelper<LLFolderType::EType> + { + static void declareValues(); + }; +} class LLInventoryPanel : public LLPanel { @@ -74,6 +73,19 @@ public: {} }; + struct StartFolder : public LLInitParam::ChoiceBlock<StartFolder> + { + Alternative<std::string> name; + Alternative<LLUUID> id; + Alternative<LLFolderType::EType> type; + + StartFolder() + : name("name"), + id("id"), + type("type") + {} + }; + struct Params : public LLInitParam::Block<Params, LLPanel::Params> { @@ -82,12 +94,14 @@ public: Optional<bool> allow_multi_select; Optional<bool> show_item_link_overlays; Optional<Filter> filter; - Optional<std::string> start_folder; + Optional<StartFolder> start_folder; Optional<bool> use_label_suffix; Optional<bool> show_empty_message; - Optional<bool> show_load_status; Optional<LLScrollContainer::Params> scroll; Optional<bool> accepts_drag_and_drop; + Optional<LLFolderView::Params> folder_view; + Optional<LLFolderViewFolder::Params> folder; + Optional<LLFolderViewItem::Params> item; Params() : sort_order_setting("sort_order_setting"), @@ -98,24 +112,34 @@ public: start_folder("start_folder"), use_label_suffix("use_label_suffix", true), show_empty_message("show_empty_message", true), - show_load_status("show_load_status"), scroll("scroll"), - accepts_drag_and_drop("accepts_drag_and_drop") + accepts_drag_and_drop("accepts_drag_and_drop"), + folder_view("folder_view"), + folder("folder"), + item("item") {} }; + struct InventoryState : public LLInitParam::Block<InventoryState> + { + Mandatory<LLInventoryFilter::Params> filter; + Mandatory<LLInventorySort::Params> sort; + }; + //-------------------------------------------------------------------- // Initialization //-------------------------------------------------------------------- protected: LLInventoryPanel(const Params&); void initFromParams(const Params&); + friend class LLUICtrlFactory; public: virtual ~LLInventoryPanel(); public: LLInventoryModel* getModel() { return mInventory; } + LLFolderViewModelInventory& getRootViewModel() { return mInventoryViewModel; } // LLView methods void draw(); @@ -137,8 +161,8 @@ public: void setSelection(const LLUUID& obj_id, BOOL take_keyboard_focus); void setSelectCallback(const boost::function<void (const std::deque<LLFolderViewItem*>& items, BOOL user_action)>& cb); void clearSelection(); - LLInventoryFilter* getFilter(); - const LLInventoryFilter* getFilter() const; + LLInventoryFilter& getFilter(); + const LLInventoryFilter& getFilter() const; void setFilterTypes(U64 filter, LLInventoryFilter::EFilterType = LLInventoryFilter::FILTERTYPE_OBJECT); U32 getFilterObjectTypes() const; void setFilterPermMask(PermissionMask filter_perm_mask); @@ -156,6 +180,7 @@ public: // This method is called when something has changed about the inventory. void modelChanged(U32 mask); LLFolderView* getRootFolder(); + LLUUID getRootFolderID(); LLScrollContainer* getScrollableContainer() { return mScroller; } void onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action); @@ -167,7 +192,8 @@ public: void doCreate(const LLSD& userdata); bool beginIMSession(); bool attachObject(const LLSD& userdata); - + static void idle(void* user_data); + // DEBUG ONLY: static void dumpSelectionInformation(void* user_data); @@ -182,30 +208,44 @@ public: static void openInventoryPanelAndSetSelection(BOOL auto_open, const LLUUID& obj_id); + void addItemID(const LLUUID& id, LLFolderViewItem* itemp); + void removeItemID(const LLUUID& id); + LLFolderViewItem* getItemByID(const LLUUID& id); + LLFolderViewFolder* getFolderByID(const LLUUID& id); + void setSelectionByID(const LLUUID& obj_id, BOOL take_keyboard_focus); + void updateSelection(); + + LLFolderViewModelInventory* getFolderViewModel(); + const LLFolderViewModelInventory* getFolderViewModel() const; + protected: void openStartFolderOrMyInventory(); // open the first level of inventory void onItemsCompletion(); // called when selected items are complete + LLUUID mSelectThisID; LLInventoryModel* mInventory; LLInventoryObserver* mInventoryObserver; LLInvPanelComplObserver* mCompletionObserver; - BOOL mAcceptsDragAndDrop; - BOOL mAllowMultiSelect; - BOOL mShowItemLinkOverlays; // Shows link graphic over inventory item icons - BOOL mShowEmptyMessage; - BOOL mShowLoadStatus; + bool mAcceptsDragAndDrop; + bool mAllowMultiSelect; + bool mShowItemLinkOverlays; // Shows link graphic over inventory item icons + bool mShowEmptyMessage; LLFolderView* mFolderRoot; LLScrollContainer* mScroller; + LLFolderViewModelInventory mInventoryViewModel; + Params mParams; // stored copy of parameter block + + std::map<LLUUID, LLFolderViewItem*> mItemMap; /** - * Pointer to LLInventoryFVBridgeBuilder. + * Pointer to LLInventoryFolderViewModelBuilder. * * It is set in LLInventoryPanel's constructor and can be overridden in derived classes with * another implementation. * Take into account it will not be deleted by LLInventoryPanel itself. */ - const LLInventoryFVBridgeBuilder* mInvFVBridgeBuilder; + const LLInventoryFolderViewModelBuilder* mInvFVBridgeBuilder; //-------------------------------------------------------------------- @@ -218,7 +258,6 @@ public: void setSortOrder(U32 order); U32 getSortOrder() const; - void requestSort(); private: std::string mSortOrderSetting; @@ -231,26 +270,20 @@ public: void addHideFolderType(LLFolderType::EType folder_type); public: - BOOL getIsViewsInitialized() const { return mViewsInitialized; } - const LLUUID& getRootFolderID() const; + BOOL getIsViewsInitialized() const { return mViewsInitialized; } protected: // Builds the UI. Call this once the inventory is usable. void initializeViews(); - LLFolderViewItem* rebuildViewsFor(const LLUUID& id); // Given the id and the parent, build all of the folder views. - virtual void buildFolderView(const LLInventoryPanel::Params& params); LLFolderViewItem* buildNewViews(const LLUUID& id); BOOL getIsHiddenFolderType(LLFolderType::EType folder_type) const; - virtual LLFolderView* createFolderView(LLInvFVBridge * bridge, bool useLabelSuffix); + virtual LLFolderView * createFolderRoot(LLUUID root_id ); virtual LLFolderViewFolder* createFolderViewFolder(LLInvFVBridge * bridge); virtual LLFolderViewItem* createFolderViewItem(LLInvFVBridge * bridge); private: - BOOL mBuildDefaultHierarchy; // default inventory hierarchy should be created in postBuild() - BOOL mViewsInitialized; // Views have been generated - // UUID of category from which hierarchy should be built. Set with the - // "start_folder" xml property. Default is LLUUID::null that means total Inventory hierarchy. - LLUUID mStartFolderID; + bool mBuildDefaultHierarchy; // default inventory hierarchy should be created in postBuild() + bool mViewsInitialized; // Views have been generated }; #endif // LL_LLINVENTORYPANEL_H diff --git a/indra/newview/lllistcontextmenu.h b/indra/newview/lllistcontextmenu.h index fabd68ee20..04d3314829 100644 --- a/indra/newview/lllistcontextmenu.h +++ b/indra/newview/lllistcontextmenu.h @@ -37,7 +37,7 @@ class LLContextMenu; /** * Context menu for single or multiple list items. * - * Derived classes must implement contextMenu(). + * Derived classes must implement createMenu(). * * Typical usage: * <code> diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index ebb5912ace..3692658e9e 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -387,13 +387,16 @@ void append_to_last_message(std::list<LLSD>& messages, const std::string& line) } // static -void LLLogChat::loadAllHistory(const std::string& file_name, std::list<LLSD>& messages) +void LLLogChat::loadChatHistory(const std::string& file_name, std::list<LLSD>& messages, const LLSD& load_params) { if (file_name.empty()) { llwarns << "Session name is Empty!" << llendl; return ; } + + bool load_all_history = load_params.has("load_all_history") ? load_params["load_all_history"].asBoolean() : false; + //LL_INFOS("") << "Loading:" << file_name << LL_ENDL;/* uncomment if you want to verify step, delete on commit */ //LL_INFOS("") << "Current:" << makeLogFileName(file_name) << LL_ENDL;/* uncomment if you want to verify step, delete on commit */ LLFILE* fptr = LLFile::fopen(makeLogFileName(file_name), "r");/*Flawfinder: ignore*/ @@ -412,8 +415,8 @@ void LLLogChat::loadAllHistory(const std::string& file_name, std::list<LLSD>& me S32 len; bool firstline = TRUE; - if (fseek(fptr, (LOG_RECALL_SIZE - 1) * -1 , SEEK_END)) - { //File is smaller than recall size. Get it all. + if (load_all_history || fseek(fptr, (LOG_RECALL_SIZE - 1) * -1 , SEEK_END)) + { //We need to load the whole historyFile or it's smaller than recall size, so get it all. firstline = FALSE; if (fseek(fptr, 0, SEEK_SET)) { @@ -449,7 +452,7 @@ void LLLogChat::loadAllHistory(const std::string& file_name, std::list<LLSD>& me else { LLSD item; - if (!LLChatLogParser::parse(line, item)) + if (!LLChatLogParser::parse(line, item, load_params)) { item[IM_TEXT] = line; } @@ -500,10 +503,11 @@ void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const } } -bool LLChatLogParser::parse(std::string& raw, LLSD& im) +bool LLChatLogParser::parse(std::string& raw, LLSD& im, const LLSD& parse_params) { if (!raw.length()) return false; + bool cut_off_todays_date = parse_params.has("cut_off_todays_date") ? parse_params["cut_off_todays_date"].asBoolean() : true; im = LLSD::emptyMap(); //matching a timestamp @@ -518,7 +522,12 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im) boost::trim(timestamp); timestamp.erase(0, 1); timestamp.erase(timestamp.length()-1, 1); - LLLogChatTimeScanner::instance().checkAndCutOffDate(timestamp); + + if (cut_off_todays_date) + { + LLLogChatTimeScanner::instance().checkAndCutOffDate(timestamp); + } + im[IM_TIME] = timestamp; } else diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h index 27752452c9..d3e9adcc37 100644 --- a/indra/newview/lllogchat.h +++ b/indra/newview/lllogchat.h @@ -50,12 +50,12 @@ public: const LLUUID& from_id, const std::string& line); - /** @deprecated @see loadAllHistory() */ + /** @deprecated @see loadChatHistory() */ static void loadHistory(const std::string& filename, void (*callback)(ELogLineType, const LLSD&, void*), void* userdata); - static void loadAllHistory(const std::string& file_name, std::list<LLSD>& messages); + static void loadChatHistory(const std::string& file_name, std::list<LLSD>& messages, const LLSD& load_params = LLSD()); private: static std::string cleanFileName(std::string filename); }; @@ -105,7 +105,7 @@ public: * * @return false if failed to parse mandatory data - message text */ - static bool parse(std::string& raw, LLSD& im); + static bool parse(std::string& raw, LLSD& im, const LLSD& parse_params = LLSD()); protected: LLChatLogParser(); diff --git a/indra/newview/llmaniptranslate.cpp b/indra/newview/llmaniptranslate.cpp index 10608463b4..362308c176 100644 --- a/indra/newview/llmaniptranslate.cpp +++ b/indra/newview/llmaniptranslate.cpp @@ -1755,6 +1755,11 @@ void LLManipTranslate::highlightIntersection(LLVector3 normal, shader->bind(); } + if (shader) + { + shader->bind(); + } + //draw volume/plane intersections { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp index 4e28d1f526..472c26e22d 100644 --- a/indra/newview/llnamelistctrl.cpp +++ b/indra/newview/llnamelistctrl.cpp @@ -336,7 +336,7 @@ LLScrollListItem* LLNameListCtrl::addNameItemRow( // ...schedule a callback LLAvatarNameCache::get(id, boost::bind(&LLNameListCtrl::onAvatarNameCache, - this, _1, _2)); + this, _1, _2, item->getHandle())); } break; } @@ -392,7 +392,8 @@ void LLNameListCtrl::removeNameItem(const LLUUID& agent_id) } void LLNameListCtrl::onAvatarNameCache(const LLUUID& agent_id, - const LLAvatarName& av_name) + const LLAvatarName& av_name, + LLHandle<LLNameListItem> item) { std::string name; if (mShortNames) @@ -400,17 +401,14 @@ void LLNameListCtrl::onAvatarNameCache(const LLUUID& agent_id, else name = av_name.getCompleteName(); - item_list::iterator iter; - for (iter = getItemList().begin(); iter != getItemList().end(); iter++) + LLNameListItem* list_item = item.get(); + if (list_item && list_item->getUUID() == agent_id) { - LLScrollListItem* item = *iter; - if (item->getUUID() == agent_id) + LLScrollListCell* cell = list_item->getColumn(mNameColumnIndex); + if (cell) { - LLScrollListCell* cell = item->getColumn(mNameColumnIndex); - if (cell) - { - cell->setValue(name); - } + cell->setValue(name); + setNeedsSort(); } } @@ -431,3 +429,8 @@ void LLNameListCtrl::updateColumns() } } } + +void LLNameListCtrl::sortByName(BOOL ascending) +{ + sortByColumnIndex(mNameColumnIndex,ascending); +} diff --git a/indra/newview/llnamelistctrl.h b/indra/newview/llnamelistctrl.h index ca9956dc53..ba85e77c65 100644 --- a/indra/newview/llnamelistctrl.h +++ b/indra/newview/llnamelistctrl.h @@ -33,6 +33,26 @@ class LLAvatarName; +/** + * LLNameListCtrl item + * + * We don't use LLScrollListItem to be able to override getUUID(), which is needed + * because the name list item value is not simply an UUID but a map (uuid, is_group). + */ +class LLNameListItem : public LLScrollListItem, public LLHandleProvider<LLNameListItem> +{ +public: + LLUUID getUUID() const { return getValue()["uuid"].asUUID(); } +protected: + friend class LLNameListCtrl; + + LLNameListItem( const LLScrollListItem::Params& p ) + : LLScrollListItem(p) + { + } +}; + + class LLNameListCtrl : public LLScrollListCtrl, public LLInstanceTracker<LLNameListCtrl> { @@ -110,12 +130,14 @@ public: void setAllowCallingCardDrop(BOOL b) { mAllowCallingCardDrop = b; } + void sortByName(BOOL ascending); + /*virtual*/ void updateColumns(); /*virtual*/ void mouseOverHighlightNthItem( S32 index ); private: void showInspector(const LLUUID& avatar_id, bool is_group); - void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name, LLHandle<LLNameListItem> item); private: S32 mNameColumnIndex; @@ -124,24 +146,5 @@ private: bool mShortNames; // display name only, no SLID }; -/** - * LLNameListCtrl item - * - * We don't use LLScrollListItem to be able to override getUUID(), which is needed - * because the name list item value is not simply an UUID but a map (uuid, is_group). - */ -class LLNameListItem : public LLScrollListItem -{ -public: - LLUUID getUUID() const { return getValue()["uuid"].asUUID(); } - -protected: - friend class LLNameListCtrl; - - LLNameListItem( const LLScrollListItem::Params& p ) - : LLScrollListItem(p) - { - } -}; #endif diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index a7303ad035..d1c7c6bfd7 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -1,8 +1,8 @@ /** * @file LLNearbyChat.cpp - * @brief Nearby chat history scrolling panel implementation + * @brief LLNearbyChat class implementation * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * @@ -26,132 +26,144 @@ #include "llviewerprecompiledheaders.h" -#include "llnearbychat.h" -#include "llviewercontrol.h" -#include "llviewerwindow.h" -#include "llrootview.h" -//#include "llchatitemscontainerctrl.h" +#include "message.h" + #include "lliconctrl.h" +#include "llappviewer.h" +#include "llchatentry.h" +#include "llfloaterreg.h" +#include "lltrans.h" +#include "llimfloatercontainer.h" #include "llfloatersidepanelcontainer.h" #include "llfocusmgr.h" #include "lllogchat.h" #include "llresizebar.h" #include "llresizehandle.h" +#include "lldraghandle.h" #include "llmenugl.h" -#include "llviewermenu.h"//for gMenuHolder - +#include "llviewermenu.h" // for gMenuHolder #include "llnearbychathandler.h" #include "llchannelmanager.h" - -#include "llagent.h" // gAgent #include "llchathistory.h" #include "llstylemap.h" - #include "llavatarnamecache.h" - -#include "lldraghandle.h" - -#include "llnearbychatbar.h" #include "llfloaterreg.h" #include "lltrans.h" -static const S32 RESIZE_BAR_THICKNESS = 3; +#include "llfirstuse.h" +#include "llnearbychat.h" +#include "llagent.h" // gAgent +#include "llgesturemgr.h" +#include "llmultigesture.h" +#include "llkeyboard.h" +#include "llanimationstates.h" +#include "llviewerstats.h" +#include "llcommandhandler.h" +#include "llviewercontrol.h" +#include "llnavigationbar.h" +#include "llwindow.h" +#include "llviewerwindow.h" +#include "llrootview.h" +#include "llviewerchat.h" +#include "lltranslate.h" +S32 LLNearbyChat::sLastSpecialChatChannel = 0; -static LLRegisterPanelClassWrapper<LLNearbyChat> t_panel_nearby_chat("panel_nearby_chat"); +const S32 EXPANDED_HEIGHT = 266; +const S32 COLLAPSED_HEIGHT = 60; +const S32 EXPANDED_MIN_HEIGHT = 150; -LLNearbyChat::LLNearbyChat(const LLNearbyChat::Params& p) -: LLPanel(p), - mChatHistory(NULL) -{ -} +// legacy callback glue +void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel); -BOOL LLNearbyChat::postBuild() -{ - //menu - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; +struct LLChatTypeTrigger { + std::string name; + EChatType type; +}; - enable_registrar.add("NearbyChat.Check", boost::bind(&LLNearbyChat::onNearbyChatCheckContextMenuItem, this, _2)); - registrar.add("NearbyChat.Action", boost::bind(&LLNearbyChat::onNearbyChatContextMenuItemClicked, this, _2)); +static LLChatTypeTrigger sChatTypeTriggers[] = { + { "/whisper" , CHAT_TYPE_WHISPER}, + { "/shout" , CHAT_TYPE_SHOUT} +}; - - LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_nearby_chat.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if(menu) - mPopupMenuHandle = menu->getHandle(); - gSavedSettings.declareS32("nearbychat_showicons_and_names",2,"NearByChat header settings",true); - - mChatHistory = getChild<LLChatHistory>("chat_history"); - - if(!LLPanel::postBuild()) - return false; - - return true; +LLNearbyChat::LLNearbyChat(const LLSD& llsd) +: LLIMConversation(llsd), + //mOutputMonitor(NULL), + mSpeakerMgr(NULL), + mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT) +{ + mIsP2PChat = false; + mIsNearbyChat = true; + setIsChrome(TRUE); + mSpeakerMgr = LLLocalSpeakerMgr::getInstance(); + mSessionID = LLUUID(); } -std::string appendTime() +//static +LLNearbyChat* LLNearbyChat::buildFloater(const LLSD& key) { - time_t utc_time; - utc_time = time_corrected(); - std::string timeStr ="["+ LLTrans::getString("TimeHour")+"]:[" - +LLTrans::getString("TimeMin")+"]"; + LLFloaterReg::getInstance("im_container"); + return new LLNearbyChat(key); +} - LLSD substitution; +//virtual +BOOL LLNearbyChat::postBuild() +{ + setIsSingleInstance(TRUE); + BOOL result = LLIMConversation::postBuild(); + mInputEditor->setCommitCallback(boost::bind(&LLNearbyChat::onChatBoxCommit, this)); + mInputEditor->setKeystrokeCallback(boost::bind(&onChatBoxKeystroke, _1, this)); + mInputEditor->setFocusLostCallback(boost::bind(&onChatBoxFocusLost, _1, this)); + mInputEditor->setFocusReceivedCallback(boost::bind(&LLNearbyChat::onChatBoxFocusReceived, this)); + mInputEditor->setLabel(LLTrans::getString("NearbyChatTitle")); - substitution["datetime"] = (S32) utc_time; - LLStringUtil::format (timeStr, substitution); +// mOutputMonitor = getChild<LLOutputMonitorCtrl>("chat_zone_indicator"); +// mOutputMonitor->setVisible(FALSE); - return timeStr; -} + // Register for font change notifications + LLViewerChat::setFontChangedCallback(boost::bind(&LLNearbyChat::onChatFontChange, this, _1)); + // title must be defined BEFORE call addConversationListItem() because + // it is used for show the item's name in the conversations list + setTitle(LLTrans::getString("NearbyChatTitle")); -void LLNearbyChat::addMessage(const LLChat& chat,bool archive,const LLSD &args) -{ - LLChat& tmp_chat = const_cast<LLChat&>(chat); + //for menu + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; - if(tmp_chat.mTimeStr.empty()) - tmp_chat.mTimeStr = appendTime(); + enable_registrar.add("NearbyChat.Check", boost::bind(&LLNearbyChat::onNearbyChatCheckContextMenuItem, this, _2)); + registrar.add("NearbyChat.Action", boost::bind(&LLNearbyChat::onNearbyChatContextMenuItemClicked, this, _2)); - bool use_plain_text_chat_history = gSavedSettings.getBOOL("PlainTextChatHistory"); - - if (!chat.mMuted) + LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_nearby_chat.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if(menu) { - tmp_chat.mFromName = chat.mFromName; - LLSD chat_args = args; - chat_args["use_plain_text_chat_history"] = use_plain_text_chat_history; - mChatHistory->appendMessage(chat, chat_args); + mPopupMenuHandle = menu->getHandle(); } - if(archive) - { - mMessageArchive.push_back(chat); - if(mMessageArchive.size()>200) - mMessageArchive.erase(mMessageArchive.begin()); - } + // obsolete, but may be needed for backward compatibility? + gSavedSettings.declareS32("nearbychat_showicons_and_names", 2, "NearByChat header settings", true); - if (args["do_not_log"].asBoolean()) + if (gSavedPerAccountSettings.getBOOL("LogShowHistory")) { - return; + loadHistory(); } - if (gSavedPerAccountSettings.getBOOL("LogNearbyChat")) - { - std::string from_name = chat.mFromName; - - if (chat.mSourceType == CHAT_SOURCE_AGENT) - { - // if the chat is coming from an agent, log the complete name - LLAvatarName av_name; - LLAvatarNameCache::get(chat.mFromID, &av_name); + return result; +} - if (!av_name.mIsDisplayNameDefault) - { - from_name = av_name.getCompleteName(); - } - } +// virtual +void LLNearbyChat::refresh() +{ + displaySpeakingIndicator(); + updateCallBtnState(LLVoiceClient::getInstance()->getUserPTTState()); - LLLogChat::saveHistory("chat", from_name, chat.mFromID, chat.mText); + // *HACK: Update transparency type depending on whether our children have focus. + // This is needed because this floater is chrome and thus cannot accept focus, so + // the transparency type setting code from LLFloater::setFocus() isn't reached. + if (getTransparencyType() != TT_DEFAULT) + { + setTransparencyType(hasFocus() ? TT_ACTIVE : TT_INACTIVE); } } @@ -162,44 +174,32 @@ void LLNearbyChat::onNearbySpeakers() LLFloaterSidePanelContainer::showPanel("people", "panel_people", param); } - void LLNearbyChat::onNearbyChatContextMenuItemClicked(const LLSD& userdata) { } + bool LLNearbyChat::onNearbyChatCheckContextMenuItem(const LLSD& userdata) { std::string str = userdata.asString(); if(str == "nearby_people") - onNearbySpeakers(); + onNearbySpeakers(); return false; } -void LLNearbyChat::removeScreenChat() -{ - LLNotificationsUI::LLScreenChannelBase* chat_channel = LLNotificationsUI::LLChannelManager::getInstance()->findChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))); - if(chat_channel) - { - chat_channel->removeToastsFromChannel(); - } -} -void LLNearbyChat::setVisible(BOOL visible) +BOOL LLNearbyChat::handleMouseDown(S32 x, S32 y, MASK mask) { - if(visible) - { - removeScreenChat(); - } - - LLPanel::setVisible(visible); -} - + //fix for EXT-6625 + //highlight NearbyChat history whenever mouseclick happen in NearbyChat + //setting focus to eidtor will force onFocusLost() call that in its turn will change + //background opaque. This all happenn since NearByChat is "chrome" and didn't process focus change. -void LLNearbyChat::getAllowedRect(LLRect& rect) -{ - rect = gViewerWindow->getWorldViewRectScaled(); + if(mChatHistory) + mChatHistory->setFocus(TRUE); + return LLPanel::handleMouseDown(x, y, mask); } -void LLNearbyChat::updateChatHistoryStyle() +void LLNearbyChat::reloadMessages() { mChatHistory->clear(); @@ -212,41 +212,13 @@ void LLNearbyChat::updateChatHistoryStyle() } } -//static -void LLNearbyChat::processChatHistoryStyleUpdate(const LLSD& newvalue) -{ - LLFloater* chat_bar = LLFloaterReg::getInstance("chat_bar"); - LLNearbyChat* nearby_chat = chat_bar->findChild<LLNearbyChat>("nearby_chat"); - if(nearby_chat) - nearby_chat->updateChatHistoryStyle(); -} - -bool isWordsName(const std::string& name) -{ - // checking to see if it's display name plus username in parentheses - S32 open_paren = name.find(" (", 0); - S32 close_paren = name.find(')', 0); - - if (open_paren != std::string::npos && - close_paren == name.length()-1) - { - return true; - } - else - { - //checking for a single space - S32 pos = name.find(' ', 0); - return std::string::npos != pos && name.rfind(' ', name.length()) == pos && 0 != pos && name.length()-1 != pos; - } -} - void LLNearbyChat::loadHistory() { LLSD do_not_log; do_not_log["do_not_log"] = true; std::list<LLSD> history; - LLLogChat::loadAllHistory("chat", history); + LLLogChat::loadChatHistory("chat", history); std::list<LLSD>::const_iterator it = history.begin(); while (it != history.end()) @@ -274,9 +246,9 @@ void LLNearbyChat::loadHistory() chat.mSourceType = CHAT_SOURCE_AGENT; if (from_id.isNull() && SYSTEM_FROM == from) - { + { chat.mSourceType = CHAT_SOURCE_SYSTEM; - + } else if (from_id.isNull()) { @@ -289,50 +261,593 @@ void LLNearbyChat::loadHistory() } } -//static -LLNearbyChat* LLNearbyChat::getInstance() +void LLNearbyChat::removeScreenChat() { - LLFloater* chat_bar = LLFloaterReg::getInstance("chat_bar"); - return chat_bar->findChild<LLNearbyChat>("nearby_chat"); + LLNotificationsUI::LLScreenChannelBase* chat_channel = LLNotificationsUI::LLChannelManager::getInstance()->findChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))); + if(chat_channel) + { + chat_channel->removeToastsFromChannel(); + } } -//////////////////////////////////////////////////////////////////////////////// -// -void LLNearbyChat::onFocusReceived() +void LLNearbyChat::setFocus(BOOL focusFlag) { - setBackgroundOpaque(true); - LLPanel::onFocusReceived(); + LLTransientDockableFloater::setFocus(focusFlag); + + //Redirect focus to input editor + if (focusFlag) + { + mInputEditor->setFocus(TRUE); + } + } -//////////////////////////////////////////////////////////////////////////////// -// -void LLNearbyChat::onFocusLost() +void LLNearbyChat::setVisible(BOOL visible) { - setBackgroundOpaque(false); - LLPanel::onFocusLost(); + LLIMConversation::setVisible(visible); + + if(visible) + { + removeScreenChat(); + } + setFocus(visible); } -BOOL LLNearbyChat::handleMouseDown(S32 x, S32 y, MASK mask) + +void LLNearbyChat::onTearOffClicked() { - //fix for EXT-6625 - //highlight NearbyChat history whenever mouseclick happen in NearbyChat - //setting focus to eidtor will force onFocusLost() call that in its turn will change - //background opaque. This all happenn since NearByChat is "chrome" and didn't process focus change. + LLIMConversation::onTearOffClicked(); + + // see CHUI-170: Save torn-off state of the nearby chat between sessions + BOOL in_the_multifloater = !isTornOff(); + gSavedSettings.setBOOL("NearbyChatIsNotTornOff", in_the_multifloater); +} + + +// virtual +void LLNearbyChat::onOpen(const LLSD& key) +{ + LLIMConversation::onOpen(key); + showTranslationCheckbox(LLTranslate::isTranslationConfigured()); +} + +void LLNearbyChat::onChatFontChange(LLFontGL* fontp) +{ + // Update things with the new font whohoo + if (mInputEditor) + { + mInputEditor->setFont(fontp); + } +} + + +void LLNearbyChat::show() +{ + if (isChatMultiTab()) + { + openFloater(getKey()); + } +} + +bool LLNearbyChat::isChatVisible() const +{ + bool isVisible = false; + LLIMFloaterContainer* im_box = LLIMFloaterContainer::getInstance(); + // Is the IM floater container ever null? + llassert(im_box != NULL); + if (im_box != NULL) + { + isVisible = + isChatMultiTab() && gSavedSettings.getBOOL("NearbyChatIsNotTornOff")? + im_box->getVisible() && !im_box->isMinimized() : + getVisible() && !isMinimized(); + } + + return isVisible; +} + +void LLNearbyChat::showHistory() +{ + openFloater(); + setResizeLimits(getMinWidth(), EXPANDED_MIN_HEIGHT); +} + +std::string LLNearbyChat::getCurrentChat() +{ + return mInputEditor ? mInputEditor->getText() : LLStringUtil::null; +} + +// virtual +BOOL LLNearbyChat::handleKeyHere( KEY key, MASK mask ) +{ + BOOL handled = FALSE; + + if( KEY_RETURN == key && mask == MASK_CONTROL) + { + // shout + sendChat(CHAT_TYPE_SHOUT); + handled = TRUE; + } + + return handled; +} + +BOOL LLNearbyChat::matchChatTypeTrigger(const std::string& in_str, std::string* out_str) +{ + U32 in_len = in_str.length(); + S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); - if(mChatHistory) - mChatHistory->setFocus(TRUE); - return LLPanel::handleMouseDown(x, y, mask); + bool string_was_found = false; + + for (S32 n = 0; n < cnt && !string_was_found; n++) + { + if (in_len <= sChatTypeTriggers[n].name.length()) + { + std::string trigger_trunc = sChatTypeTriggers[n].name; + LLStringUtil::truncate(trigger_trunc, in_len); + + if (!LLStringUtil::compareInsensitive(in_str, trigger_trunc)) + { + *out_str = sChatTypeTriggers[n].name; + string_was_found = true; + } + } + } + + return string_was_found; } -void LLNearbyChat::draw() +void LLNearbyChat::onChatBoxKeystroke(LLTextEditor* caller, void* userdata) { - // *HACK: Update transparency type depending on whether our children have focus. - // This is needed because this floater is chrome and thus cannot accept focus, so - // the transparency type setting code from LLFloater::setFocus() isn't reached. - if (getTransparencyType() != TT_DEFAULT) + LLFirstUse::otherAvatarChatFirst(false); + + LLNearbyChat* self = (LLNearbyChat *)userdata; + + LLWString raw_text = self->mInputEditor->getWText(); + + // Can't trim the end, because that will cause autocompletion + // to eat trailing spaces that might be part of a gesture. + LLWStringUtil::trimHead(raw_text); + + S32 length = raw_text.length(); + + if( (length > 0) && (raw_text[0] != '/') ) // forward slash is used for escape (eg. emote) sequences { - setTransparencyType(hasFocus() ? TT_ACTIVE : TT_INACTIVE); + gAgent.startTyping(); } + else + { + gAgent.stopTyping(); + } + + /* Doesn't work -- can't tell the difference between a backspace + that killed the selection vs. backspace at the end of line. + if (length > 1 + && text[0] == '/' + && key == KEY_BACKSPACE) + { + // the selection will already be deleted, but we need to trim + // off the character before + std::string new_text = raw_text.substr(0, length-1); + self->mInputEditor->setText( new_text ); + self->mInputEditor->setCursorToEnd(); + length = length - 1; + } + */ + + KEY key = gKeyboard->currentKey(); + + // Ignore "special" keys, like backspace, arrows, etc. + if (length > 1 + && raw_text[0] == '/' + && key < KEY_SPECIAL) + { + // we're starting a gesture, attempt to autocomplete + + std::string utf8_trigger = wstring_to_utf8str(raw_text); + std::string utf8_out_str(utf8_trigger); + + if (LLGestureMgr::instance().matchPrefix(utf8_trigger, &utf8_out_str)) + { + std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); + self->mInputEditor->setText(utf8_trigger + rest_of_match); // keep original capitalization for user-entered part + + // Select to end of line, starting from the character + // after the last one the user typed. + self->mInputEditor->selectNext(rest_of_match, false); + } + else if (matchChatTypeTrigger(utf8_trigger, &utf8_out_str)) + { + std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); + self->mInputEditor->setText(utf8_trigger + rest_of_match + " "); // keep original capitalization for user-entered part + self->mInputEditor->endOfDoc(); + } - LLPanel::draw(); + //llinfos << "GESTUREDEBUG " << trigger + // << " len " << length + // << " outlen " << out_str.getLength() + // << llendl; + } +} + +// static +void LLNearbyChat::onChatBoxFocusLost(LLFocusableElement* caller, void* userdata) +{ + // stop typing animation + gAgent.stopTyping(); } + +void LLNearbyChat::onChatBoxFocusReceived() +{ + mInputEditor->setEnabled(!gDisconnected); +} + +EChatType LLNearbyChat::processChatTypeTriggers(EChatType type, std::string &str) +{ + U32 length = str.length(); + S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); + + for (S32 n = 0; n < cnt; n++) + { + if (length >= sChatTypeTriggers[n].name.length()) + { + std::string trigger = str.substr(0, sChatTypeTriggers[n].name.length()); + + if (!LLStringUtil::compareInsensitive(trigger, sChatTypeTriggers[n].name)) + { + U32 trigger_length = sChatTypeTriggers[n].name.length(); + + // It's to remove space after trigger name + if (length > trigger_length && str[trigger_length] == ' ') + trigger_length++; + + str = str.substr(trigger_length, length); + + if (CHAT_TYPE_NORMAL == type) + return sChatTypeTriggers[n].type; + else + break; + } + } + } + + return type; +} + +void LLNearbyChat::sendChat( EChatType type ) +{ + if (mInputEditor) + { + LLWString text = mInputEditor->getWText(); + LLWStringUtil::trim(text); + LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. + if (!text.empty()) + { + // Check if this is destined for another channel + S32 channel = 0; + stripChannelNumber(text, &channel); + + std::string utf8text = wstring_to_utf8str(text); + // Try to trigger a gesture, if not chat to a script. + std::string utf8_revised_text; + if (0 == channel) + { + // discard returned "found" boolean + LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text); + } + else + { + utf8_revised_text = utf8text; + } + + utf8_revised_text = utf8str_trim(utf8_revised_text); + + type = processChatTypeTriggers(type, utf8_revised_text); + + if (!utf8_revised_text.empty()) + { + // Chat with animation + sendChatFromViewer(utf8_revised_text, type, TRUE); + } + } + + mInputEditor->setText(LLStringExplicit("")); + } + + gAgent.stopTyping(); + + // If the user wants to stop chatting on hitting return, lose focus + // and go out of chat mode. + if (gSavedSettings.getBOOL("CloseChatOnReturn")) + { + stopChat(); + } +} + +void LLNearbyChat::addMessage(const LLChat& chat,bool archive,const LLSD &args) +{ + appendMessage(chat, args); + + if(archive) + { + mMessageArchive.push_back(chat); + if(mMessageArchive.size()>200) + mMessageArchive.erase(mMessageArchive.begin()); + } + + // logging + if (!args["do_not_log"].asBoolean() + && gSavedPerAccountSettings.getBOOL("LogNearbyChat")) + { + std::string from_name = chat.mFromName; + + if (chat.mSourceType == CHAT_SOURCE_AGENT) + { + // if the chat is coming from an agent, log the complete name + LLAvatarName av_name; + LLAvatarNameCache::get(chat.mFromID, &av_name); + + if (!av_name.mIsDisplayNameDefault) + { + from_name = av_name.getCompleteName(); + } + } + + LLLogChat::saveHistory("chat", from_name, chat.mFromID, chat.mText); + } +} + + +void LLNearbyChat::onChatBoxCommit() +{ + if (mInputEditor->getText().length() > 0) + { + sendChat(CHAT_TYPE_NORMAL); + } + + gAgent.stopTyping(); +} + +void LLNearbyChat::displaySpeakingIndicator() +{ + LLSpeakerMgr::speaker_list_t speaker_list; + LLUUID id; + + id.setNull(); + mSpeakerMgr->update(TRUE); + mSpeakerMgr->getSpeakerList(&speaker_list, FALSE); + + for (LLSpeakerMgr::speaker_list_t::iterator i = speaker_list.begin(); i != speaker_list.end(); ++i) + { + LLPointer<LLSpeaker> s = *i; + if (s->mSpeechVolume > 0 || s->mStatus == LLSpeaker::STATUS_SPEAKING) + { + id = s->mID; + break; + } + } + + if (!id.isNull()) + { + //mOutputMonitor->setVisible(TRUE); + //mOutputMonitor->setSpeakerId(id); + } + else + { + //mOutputMonitor->setVisible(FALSE); + } +} + +void LLNearbyChat::sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate) +{ + sendChatFromViewer(utf8str_to_wstring(utf8text), type, animate); +} + +void LLNearbyChat::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate) +{ + // Look for "/20 foo" channel chats. + S32 channel = 0; + LLWString out_text = stripChannelNumber(wtext, &channel); + std::string utf8_out_text = wstring_to_utf8str(out_text); + std::string utf8_text = wstring_to_utf8str(wtext); + + utf8_text = utf8str_trim(utf8_text); + if (!utf8_text.empty()) + { + utf8_text = utf8str_truncate(utf8_text, MAX_STRING - 1); + } + + // Don't animate for chats people can't hear (chat to scripts) + if (animate && (channel == 0)) + { + if (type == CHAT_TYPE_WHISPER) + { + lldebugs << "You whisper " << utf8_text << llendl; + gAgent.sendAnimationRequest(ANIM_AGENT_WHISPER, ANIM_REQUEST_START); + } + else if (type == CHAT_TYPE_NORMAL) + { + lldebugs << "You say " << utf8_text << llendl; + gAgent.sendAnimationRequest(ANIM_AGENT_TALK, ANIM_REQUEST_START); + } + else if (type == CHAT_TYPE_SHOUT) + { + lldebugs << "You shout " << utf8_text << llendl; + gAgent.sendAnimationRequest(ANIM_AGENT_SHOUT, ANIM_REQUEST_START); + } + else + { + llinfos << "send_chat_from_viewer() - invalid volume" << llendl; + return; + } + } + else + { + if (type != CHAT_TYPE_START && type != CHAT_TYPE_STOP) + { + lldebugs << "Channel chat: " << utf8_text << llendl; + } + } + + send_chat_from_viewer(utf8_out_text, type, channel); +} + +// static +bool LLNearbyChat::isWordsName(const std::string& name) +{ + // checking to see if it's display name plus username in parentheses + S32 open_paren = name.find(" (", 0); + S32 close_paren = name.find(')', 0); + + if (open_paren != std::string::npos && + close_paren == name.length()-1) + { + return true; + } + else + { + //checking for a single space + S32 pos = name.find(' ', 0); + return std::string::npos != pos && name.rfind(' ', name.length()) == pos && 0 != pos && name.length()-1 != pos; + } +} + +// static +void LLNearbyChat::startChat(const char* line) +{ + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat"); + if (nearby_chat) + { + nearby_chat->show(); + nearby_chat->setVisible(TRUE); + nearby_chat->setFocus(TRUE); + nearby_chat->mInputEditor->setFocus(TRUE); + + if (line) + { + std::string line_string(line); + nearby_chat->mInputEditor->setText(line_string); + } + + nearby_chat->mInputEditor->endOfDoc(); + } +} + +// Exit "chat mode" and do the appropriate focus changes +// static +void LLNearbyChat::stopChat() +{ + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat"); + if (nearby_chat) + { + nearby_chat->mInputEditor->setFocus(FALSE); + gAgent.stopTyping(); + } +} + +// If input of the form "/20foo" or "/20 foo", returns "foo" and channel 20. +// Otherwise returns input and channel 0. +LLWString LLNearbyChat::stripChannelNumber(const LLWString &mesg, S32* channel) +{ + if (mesg[0] == '/' + && mesg[1] == '/') + { + // This is a "repeat channel send" + *channel = sLastSpecialChatChannel; + return mesg.substr(2, mesg.length() - 2); + } + else if (mesg[0] == '/' + && mesg[1] + && LLStringOps::isDigit(mesg[1])) + { + // This a special "/20" speak on a channel + S32 pos = 0; + + // Copy the channel number into a string + LLWString channel_string; + llwchar c; + do + { + c = mesg[pos+1]; + channel_string.push_back(c); + pos++; + } + while(c && pos < 64 && LLStringOps::isDigit(c)); + + // Move the pointer forward to the first non-whitespace char + // Check isspace before looping, so we can handle "/33foo" + // as well as "/33 foo" + while(c && iswspace(c)) + { + c = mesg[pos+1]; + pos++; + } + + sLastSpecialChatChannel = strtol(wstring_to_utf8str(channel_string).c_str(), NULL, 10); + *channel = sLastSpecialChatChannel; + return mesg.substr(pos, mesg.length() - pos); + } + else + { + // This is normal chat. + *channel = 0; + return mesg; + } +} + +void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel) +{ + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_ChatFromViewer); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_ChatData); + msg->addStringFast(_PREHASH_Message, utf8_out_text); + msg->addU8Fast(_PREHASH_Type, type); + msg->addS32("Channel", channel); + + gAgent.sendReliableMessage(); + + LLViewerStats::getInstance()->incStat(LLViewerStats::ST_CHAT_COUNT); +} + +class LLChatCommandHandler : public LLCommandHandler +{ +public: + // not allowed from outside the app + LLChatCommandHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { } + + // Your code here + bool handle(const LLSD& tokens, const LLSD& query_map, + LLMediaCtrl* web) + { + bool retval = false; + // Need at least 2 tokens to have a valid message. + if (tokens.size() < 2) + { + retval = false; + } + else + { + S32 channel = tokens[0].asInteger(); + // VWR-19499 Restrict function to chat channels greater than 0. + if ((channel > 0) && (channel < CHAT_CHANNEL_DEBUG)) + { + retval = true; + // Send unescaped message, see EXT-6353. + std::string unescaped_mesg (LLURI::unescape(tokens[1].asString())); + send_chat_from_viewer(unescaped_mesg, CHAT_TYPE_NORMAL, channel); + } + else + { + retval = false; + // Tell us this is an unsupported SLurl. + } + } + return retval; + } +}; + +// Creating the object registers with the dispatcher. +LLChatCommandHandler gChatHandler; diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index 7c5975cbc5..b155fd3c26 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -1,8 +1,8 @@ - /** +/** * @file llnearbychat.h - * @brief nearby chat history scrolling panel implementation + * @brief LLNearbyChat class definition * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * @@ -24,60 +24,101 @@ * $/LicenseInfo$ */ -#ifndef LL_LLNEARBYCHAT_H_ -#define LL_LLNEARBYCHAT_H_ +#ifndef LL_LLNEARBYCHAT_H +#define LL_LLNEARBYCHAT_H +#include "llimconversation.h" +#include "llcombobox.h" +#include "llgesturemgr.h" +#include "llchat.h" +#include "llvoiceclient.h" +#include "lloutputmonitorctrl.h" +#include "llspeakers.h" #include "llscrollbar.h" #include "llviewerchat.h" -#include "llfloater.h" +#include "llpanel.h" class LLResizeBar; -class LLChatHistory; -class LLNearbyChat: public LLPanel +class LLNearbyChat + : public LLIMConversation { public: - LLNearbyChat(const Params& p = LLPanel::getDefaultParams()); + // constructor for inline chat-bars (e.g. hosted in chat history window) + LLNearbyChat(const LLSD& key = LLSD(LLUUID())); + ~LLNearbyChat() {} - BOOL postBuild (); + static LLNearbyChat* buildFloater(const LLSD& key); + + /*virtual*/ BOOL postBuild(); + /*virtual*/ void onOpen(const LLSD& key); + /*virtual*/ void setFocus(BOOL focusFlag); + /*virtual*/ void setVisible(BOOL visible); + + void loadHistory(); + void reloadMessages(); + void removeScreenChat(); + + void addToHost(); + void show(); + bool isChatVisible() const; /** @param archive true - to save a message to the chat history log */ - void addMessage (const LLChat& message,bool archive = true, const LLSD &args = LLSD()); + void addMessage (const LLChat& message,bool archive = true, const LLSD &args = LLSD()); void onNearbyChatContextMenuItemClicked(const LLSD& userdata); bool onNearbyChatCheckContextMenuItem(const LLSD& userdata); + LLChatEntry* getChatBox() { return mInputEditor; } + + std::string getCurrentChat(); + + virtual BOOL handleKeyHere( KEY key, MASK mask ); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); - virtual void draw(); - // focus overrides - /*virtual*/ void onFocusLost(); - /*virtual*/ void onFocusReceived(); - - /*virtual*/ void setVisible(BOOL visible); - - virtual void updateChatHistoryStyle(); + static void startChat(const char* line); + static void stopChat(); - static void processChatHistoryStyleUpdate(const LLSD& newvalue); + static void sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate); + static void sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate); - void loadHistory(); + static bool isWordsName(const std::string& name); - static LLNearbyChat* getInstance(); - void removeScreenChat(); + void showHistory(); -private: +protected: + static BOOL matchChatTypeTrigger(const std::string& in_str, std::string* out_str); + static void onChatBoxKeystroke(LLTextEditor* caller, void* userdata); + static void onChatBoxFocusLost(LLFocusableElement* caller, void* userdata); + void onChatBoxFocusReceived(); - void getAllowedRect (LLRect& rect); + void sendChat( EChatType type ); + void onChatBoxCommit(); + void onChatFontChange(LLFontGL* fontp); - void onNearbySpeakers (); + /*virtual*/ void onTearOffClicked(); + + static LLWString stripChannelNumber(const LLWString &mesg, S32* channel); + EChatType processChatTypeTriggers(EChatType type, std::string &str); + void displaySpeakingIndicator(); + + // Which non-zero channel did we last chat on? + static S32 sLastSpecialChatChannel; + + LLOutputMonitorCtrl* mOutputMonitor; + LLLocalSpeakerMgr* mSpeakerMgr; + + S32 mExpandedHeight; private: - LLHandle<LLView> mPopupMenuHandle; - LLChatHistory* mChatHistory; + void onNearbySpeakers (); + + /*virtual*/ void refresh(); + + LLHandle<LLView> mPopupMenuHandle; std::vector<LLChat> mMessageArchive; + }; #endif - - diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp deleted file mode 100644 index f8f0f7d243..0000000000 --- a/indra/newview/llnearbychatbar.cpp +++ /dev/null @@ -1,680 +0,0 @@ -/** - * @file llnearbychatbar.cpp - * @brief LLNearbyChatBar class implementation - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "message.h" - -#include "llappviewer.h" -#include "llfloaterreg.h" -#include "lltrans.h" - -#include "llfirstuse.h" -#include "llnearbychatbar.h" -#include "llnearbychatbarlistener.h" -#include "llagent.h" -#include "llgesturemgr.h" -#include "llmultigesture.h" -#include "llkeyboard.h" -#include "llanimationstates.h" -#include "llviewerstats.h" -#include "llcommandhandler.h" -#include "llviewercontrol.h" -#include "llnavigationbar.h" -#include "llwindow.h" -#include "llviewerwindow.h" -#include "llrootview.h" -#include "llviewerchat.h" -#include "llnearbychat.h" -#include "lltranslate.h" - -#include "llresizehandle.h" -#include "llautoreplace.h" - -S32 LLNearbyChatBar::sLastSpecialChatChannel = 0; - -const S32 EXPANDED_HEIGHT = 300; -const S32 COLLAPSED_HEIGHT = 60; -const S32 EXPANDED_MIN_HEIGHT = 150; - -// legacy callback glue -void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel); - -struct LLChatTypeTrigger { - std::string name; - EChatType type; -}; - -static LLChatTypeTrigger sChatTypeTriggers[] = { - { "/whisper" , CHAT_TYPE_WHISPER}, - { "/shout" , CHAT_TYPE_SHOUT} -}; - -LLNearbyChatBar::LLNearbyChatBar(const LLSD& key) -: LLFloater(key), - mChatBox(NULL), - mNearbyChat(NULL), - mOutputMonitor(NULL), - mSpeakerMgr(NULL), - mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT) -{ - mSpeakerMgr = LLLocalSpeakerMgr::getInstance(); - mListener.reset(new LLNearbyChatBarListener(*this)); -} - -//virtual -BOOL LLNearbyChatBar::postBuild() -{ - mChatBox = getChild<LLLineEditor>("chat_box"); - - mChatBox->setAutoreplaceCallback(boost::bind(&LLAutoReplace::autoreplaceCallback, LLAutoReplace::getInstance(), _1, _2)); - mChatBox->setCommitCallback(boost::bind(&LLNearbyChatBar::onChatBoxCommit, this)); - mChatBox->setKeystrokeCallback(&onChatBoxKeystroke, this); - mChatBox->setFocusLostCallback(boost::bind(&onChatBoxFocusLost, _1, this)); - mChatBox->setFocusReceivedCallback(boost::bind(&LLNearbyChatBar::onChatBoxFocusReceived, this)); - - mChatBox->setIgnoreArrowKeys( FALSE ); - mChatBox->setCommitOnFocusLost( FALSE ); - mChatBox->setRevertOnEsc( FALSE ); - mChatBox->setIgnoreTab(TRUE); - mChatBox->setPassDelete(TRUE); - mChatBox->setReplaceNewlinesWithSpaces(FALSE); - mChatBox->setEnableLineHistory(TRUE); - mChatBox->setFont(LLViewerChat::getChatFont()); - - mNearbyChat = getChildView("nearby_chat"); - - gSavedSettings.declareBOOL("nearbychat_history_visibility", mNearbyChat->getVisible(), "Visibility state of nearby chat history", TRUE); - BOOL show_nearby_chat = gSavedSettings.getBOOL("nearbychat_history_visibility"); - - LLButton* show_btn = getChild<LLButton>("show_nearby_chat"); - show_btn->setCommitCallback(boost::bind(&LLNearbyChatBar::onToggleNearbyChatPanel, this)); - show_btn->setToggleState(show_nearby_chat); - - mOutputMonitor = getChild<LLOutputMonitorCtrl>("chat_zone_indicator"); - mOutputMonitor->setVisible(FALSE); - - showNearbyChatPanel(show_nearby_chat); - - // Register for font change notifications - LLViewerChat::setFontChangedCallback(boost::bind(&LLNearbyChatBar::onChatFontChange, this, _1)); - - enableResizeCtrls(true, true, false); - - return TRUE; -} - -// virtual -void LLNearbyChatBar::onOpen(const LLSD& key) -{ - showTranslationCheckbox(LLTranslate::isTranslationConfigured()); -} - -bool LLNearbyChatBar::applyRectControl() -{ - bool rect_controlled = LLFloater::applyRectControl(); - - if (!mNearbyChat->getVisible()) - { - reshape(getRect().getWidth(), getMinHeight()); - enableResizeCtrls(true, true, false); - } - else - { - enableResizeCtrls(true); - setResizeLimits(getMinWidth(), EXPANDED_MIN_HEIGHT); - } - - return rect_controlled; -} - -void LLNearbyChatBar::onChatFontChange(LLFontGL* fontp) -{ - // Update things with the new font whohoo - if (mChatBox) - { - mChatBox->setFont(fontp); - } -} - -//static -LLNearbyChatBar* LLNearbyChatBar::getInstance() -{ - return LLFloaterReg::getTypedInstance<LLNearbyChatBar>("chat_bar"); -} - -void LLNearbyChatBar::showHistory() -{ - openFloater(); - - if (!getChildView("nearby_chat")->getVisible()) - { - onToggleNearbyChatPanel(); - } -} - -void LLNearbyChatBar::showTranslationCheckbox(BOOL show) -{ - getChild<LLUICtrl>("translate_chat_checkbox_lp")->setVisible(show); -} - -void LLNearbyChatBar::draw() -{ - displaySpeakingIndicator(); - LLFloater::draw(); -} - -std::string LLNearbyChatBar::getCurrentChat() -{ - return mChatBox ? mChatBox->getText() : LLStringUtil::null; -} - -// virtual -BOOL LLNearbyChatBar::handleKeyHere( KEY key, MASK mask ) -{ - BOOL handled = FALSE; - - if( KEY_RETURN == key && mask == MASK_CONTROL) - { - // shout - sendChat(CHAT_TYPE_SHOUT); - handled = TRUE; - } - - return handled; -} - -BOOL LLNearbyChatBar::matchChatTypeTrigger(const std::string& in_str, std::string* out_str) -{ - U32 in_len = in_str.length(); - S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); - - for (S32 n = 0; n < cnt; n++) - { - if (in_len > sChatTypeTriggers[n].name.length()) - continue; - - std::string trigger_trunc = sChatTypeTriggers[n].name; - LLStringUtil::truncate(trigger_trunc, in_len); - - if (!LLStringUtil::compareInsensitive(in_str, trigger_trunc)) - { - *out_str = sChatTypeTriggers[n].name; - return TRUE; - } - } - - return FALSE; -} - -void LLNearbyChatBar::onChatBoxKeystroke(LLLineEditor* caller, void* userdata) -{ - LLFirstUse::otherAvatarChatFirst(false); - - LLNearbyChatBar* self = (LLNearbyChatBar *)userdata; - - LLWString raw_text = self->mChatBox->getWText(); - - // Can't trim the end, because that will cause autocompletion - // to eat trailing spaces that might be part of a gesture. - LLWStringUtil::trimHead(raw_text); - - S32 length = raw_text.length(); - - if( (length > 0) && (raw_text[0] != '/') ) // forward slash is used for escape (eg. emote) sequences - { - gAgent.startTyping(); - } - else - { - gAgent.stopTyping(); - } - - /* Doesn't work -- can't tell the difference between a backspace - that killed the selection vs. backspace at the end of line. - if (length > 1 - && text[0] == '/' - && key == KEY_BACKSPACE) - { - // the selection will already be deleted, but we need to trim - // off the character before - std::string new_text = raw_text.substr(0, length-1); - self->mInputEditor->setText( new_text ); - self->mInputEditor->setCursorToEnd(); - length = length - 1; - } - */ - - KEY key = gKeyboard->currentKey(); - - // Ignore "special" keys, like backspace, arrows, etc. - if (length > 1 - && raw_text[0] == '/' - && key < KEY_SPECIAL) - { - // we're starting a gesture, attempt to autocomplete - - std::string utf8_trigger = wstring_to_utf8str(raw_text); - std::string utf8_out_str(utf8_trigger); - - if (LLGestureMgr::instance().matchPrefix(utf8_trigger, &utf8_out_str)) - { - std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); - self->mChatBox->setText(utf8_trigger + rest_of_match); // keep original capitalization for user-entered part - S32 outlength = self->mChatBox->getLength(); // in characters - - // Select to end of line, starting from the character - // after the last one the user typed. - self->mChatBox->setSelection(length, outlength); - } - else if (matchChatTypeTrigger(utf8_trigger, &utf8_out_str)) - { - std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); - self->mChatBox->setText(utf8_trigger + rest_of_match + " "); // keep original capitalization for user-entered part - self->mChatBox->setCursorToEnd(); - } - - //llinfos << "GESTUREDEBUG " << trigger - // << " len " << length - // << " outlen " << out_str.getLength() - // << llendl; - } -} - -// static -void LLNearbyChatBar::onChatBoxFocusLost(LLFocusableElement* caller, void* userdata) -{ - // stop typing animation - gAgent.stopTyping(); -} - -void LLNearbyChatBar::onChatBoxFocusReceived() -{ - mChatBox->setEnabled(!gDisconnected); -} - -EChatType LLNearbyChatBar::processChatTypeTriggers(EChatType type, std::string &str) -{ - U32 length = str.length(); - S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); - - for (S32 n = 0; n < cnt; n++) - { - if (length >= sChatTypeTriggers[n].name.length()) - { - std::string trigger = str.substr(0, sChatTypeTriggers[n].name.length()); - - if (!LLStringUtil::compareInsensitive(trigger, sChatTypeTriggers[n].name)) - { - U32 trigger_length = sChatTypeTriggers[n].name.length(); - - // It's to remove space after trigger name - if (length > trigger_length && str[trigger_length] == ' ') - trigger_length++; - - str = str.substr(trigger_length, length); - - if (CHAT_TYPE_NORMAL == type) - return sChatTypeTriggers[n].type; - else - break; - } - } - } - - return type; -} - -void LLNearbyChatBar::sendChat( EChatType type ) -{ - if (mChatBox) - { - LLWString text = mChatBox->getConvertedText(); - if (!text.empty()) - { - // store sent line in history, duplicates will get filtered - mChatBox->updateHistory(); - // Check if this is destined for another channel - S32 channel = 0; - stripChannelNumber(text, &channel); - - std::string utf8text = wstring_to_utf8str(text); - // Try to trigger a gesture, if not chat to a script. - std::string utf8_revised_text; - if (0 == channel) - { - // discard returned "found" boolean - LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text); - } - else - { - utf8_revised_text = utf8text; - } - - utf8_revised_text = utf8str_trim(utf8_revised_text); - - type = processChatTypeTriggers(type, utf8_revised_text); - - if (!utf8_revised_text.empty()) - { - // Chat with animation - sendChatFromViewer(utf8_revised_text, type, TRUE); - } - } - - mChatBox->setText(LLStringExplicit("")); - } - - gAgent.stopTyping(); - - // If the user wants to stop chatting on hitting return, lose focus - // and go out of chat mode. - if (gSavedSettings.getBOOL("CloseChatOnReturn")) - { - stopChat(); - } -} - -void LLNearbyChatBar::showNearbyChatPanel(bool show) -{ - if (!show) - { - if (mNearbyChat->getVisible() && !isMinimized()) - { - mExpandedHeight = getRect().getHeight(); - } - setResizeLimits(getMinWidth(), COLLAPSED_HEIGHT); - mNearbyChat->setVisible(FALSE); - reshape(getRect().getWidth(), COLLAPSED_HEIGHT); - enableResizeCtrls(true, true, false); - storeRectControl(); - } - else - { - mNearbyChat->setVisible(TRUE); - setResizeLimits(getMinWidth(), EXPANDED_MIN_HEIGHT); - reshape(getRect().getWidth(), mExpandedHeight); - enableResizeCtrls(true); - storeRectControl(); - } - - gSavedSettings.setBOOL("nearbychat_history_visibility", mNearbyChat->getVisible()); -} - -void LLNearbyChatBar::onToggleNearbyChatPanel() -{ - showNearbyChatPanel(!mNearbyChat->getVisible()); -} - -void LLNearbyChatBar::setMinimized(BOOL b) -{ - LLNearbyChat* nearby_chat = getChild<LLNearbyChat>("nearby_chat"); - // when unminimizing with nearby chat visible, go ahead and kill off screen chats - if (!b && nearby_chat->getVisible()) - { - nearby_chat->removeScreenChat(); - } - LLFloater::setMinimized(b); -} - -void LLNearbyChatBar::onChatBoxCommit() -{ - if (mChatBox->getText().length() > 0) - { - sendChat(CHAT_TYPE_NORMAL); - } - - gAgent.stopTyping(); -} - -void LLNearbyChatBar::displaySpeakingIndicator() -{ - LLSpeakerMgr::speaker_list_t speaker_list; - LLUUID id; - - id.setNull(); - mSpeakerMgr->update(TRUE); - mSpeakerMgr->getSpeakerList(&speaker_list, FALSE); - - for (LLSpeakerMgr::speaker_list_t::iterator i = speaker_list.begin(); i != speaker_list.end(); ++i) - { - LLPointer<LLSpeaker> s = *i; - if (s->mSpeechVolume > 0 || s->mStatus == LLSpeaker::STATUS_SPEAKING) - { - id = s->mID; - break; - } - } - - if (!id.isNull()) - { - mOutputMonitor->setVisible(TRUE); - mOutputMonitor->setSpeakerId(id); - } - else - { - mOutputMonitor->setVisible(FALSE); - } -} - -void LLNearbyChatBar::sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate) -{ - sendChatFromViewer(utf8str_to_wstring(utf8text), type, animate); -} - -void LLNearbyChatBar::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate) -{ - // Look for "/20 foo" channel chats. - S32 channel = 0; - LLWString out_text = stripChannelNumber(wtext, &channel); - std::string utf8_out_text = wstring_to_utf8str(out_text); - std::string utf8_text = wstring_to_utf8str(wtext); - - utf8_text = utf8str_trim(utf8_text); - if (!utf8_text.empty()) - { - utf8_text = utf8str_truncate(utf8_text, MAX_STRING - 1); - } - - // Don't animate for chats people can't hear (chat to scripts) - if (animate && (channel == 0)) - { - if (type == CHAT_TYPE_WHISPER) - { - lldebugs << "You whisper " << utf8_text << llendl; - gAgent.sendAnimationRequest(ANIM_AGENT_WHISPER, ANIM_REQUEST_START); - } - else if (type == CHAT_TYPE_NORMAL) - { - lldebugs << "You say " << utf8_text << llendl; - gAgent.sendAnimationRequest(ANIM_AGENT_TALK, ANIM_REQUEST_START); - } - else if (type == CHAT_TYPE_SHOUT) - { - lldebugs << "You shout " << utf8_text << llendl; - gAgent.sendAnimationRequest(ANIM_AGENT_SHOUT, ANIM_REQUEST_START); - } - else - { - llinfos << "send_chat_from_viewer() - invalid volume" << llendl; - return; - } - } - else - { - if (type != CHAT_TYPE_START && type != CHAT_TYPE_STOP) - { - lldebugs << "Channel chat: " << utf8_text << llendl; - } - } - - send_chat_from_viewer(utf8_out_text, type, channel); -} - -// static -void LLNearbyChatBar::startChat(const char* line) -{ - LLNearbyChatBar* cb = LLNearbyChatBar::getInstance(); - - if (!cb ) - return; - - cb->setVisible(TRUE); - cb->setFocus(TRUE); - cb->mChatBox->setFocus(TRUE); - - if (line) - { - std::string line_string(line); - cb->mChatBox->setText(line_string); - } - - cb->mChatBox->setCursorToEnd(); -} - -// Exit "chat mode" and do the appropriate focus changes -// static -void LLNearbyChatBar::stopChat() -{ - LLNearbyChatBar* cb = LLNearbyChatBar::getInstance(); - - if (!cb) - return; - - cb->mChatBox->setFocus(FALSE); - - // stop typing animation - gAgent.stopTyping(); -} - -// If input of the form "/20foo" or "/20 foo", returns "foo" and channel 20. -// Otherwise returns input and channel 0. -LLWString LLNearbyChatBar::stripChannelNumber(const LLWString &mesg, S32* channel) -{ - if (mesg[0] == '/' - && mesg[1] == '/') - { - // This is a "repeat channel send" - *channel = sLastSpecialChatChannel; - return mesg.substr(2, mesg.length() - 2); - } - else if (mesg[0] == '/' - && mesg[1] - && LLStringOps::isDigit(mesg[1])) - { - // This a special "/20" speak on a channel - S32 pos = 0; - - // Copy the channel number into a string - LLWString channel_string; - llwchar c; - do - { - c = mesg[pos+1]; - channel_string.push_back(c); - pos++; - } - while(c && pos < 64 && LLStringOps::isDigit(c)); - - // Move the pointer forward to the first non-whitespace char - // Check isspace before looping, so we can handle "/33foo" - // as well as "/33 foo" - while(c && iswspace(c)) - { - c = mesg[pos+1]; - pos++; - } - - sLastSpecialChatChannel = strtol(wstring_to_utf8str(channel_string).c_str(), NULL, 10); - *channel = sLastSpecialChatChannel; - return mesg.substr(pos, mesg.length() - pos); - } - else - { - // This is normal chat. - *channel = 0; - return mesg; - } -} - -void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel) -{ - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_ChatFromViewer); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_ChatData); - msg->addStringFast(_PREHASH_Message, utf8_out_text); - msg->addU8Fast(_PREHASH_Type, type); - msg->addS32("Channel", channel); - - gAgent.sendReliableMessage(); - - LLViewerStats::getInstance()->incStat(LLViewerStats::ST_CHAT_COUNT); -} - -class LLChatCommandHandler : public LLCommandHandler -{ -public: - // not allowed from outside the app - LLChatCommandHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { } - - // Your code here - bool handle(const LLSD& tokens, const LLSD& query_map, - LLMediaCtrl* web) - { - bool retval = false; - // Need at least 2 tokens to have a valid message. - if (tokens.size() < 2) - { - retval = false; - } - else - { - S32 channel = tokens[0].asInteger(); - // VWR-19499 Restrict function to chat channels greater than 0. - if ((channel > 0) && (channel < CHAT_CHANNEL_DEBUG)) - { - retval = true; - // Send unescaped message, see EXT-6353. - std::string unescaped_mesg (LLURI::unescape(tokens[1].asString())); - send_chat_from_viewer(unescaped_mesg, CHAT_TYPE_NORMAL, channel); - } - else - { - retval = false; - // Tell us this is an unsupported SLurl. - } - } - return retval; - } -}; - -// Creating the object registers with the dispatcher. -LLChatCommandHandler gChatHandler; - - diff --git a/indra/newview/llnearbychatbar.h b/indra/newview/llnearbychatbar.h deleted file mode 100644 index 662496d338..0000000000 --- a/indra/newview/llnearbychatbar.h +++ /dev/null @@ -1,104 +0,0 @@ -/** - * @file llnearbychatbar.h - * @brief LLNearbyChatBar class definition - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLNEARBYCHATBAR_H -#define LL_LLNEARBYCHATBAR_H - -#include "llfloater.h" -#include "llcombobox.h" -#include "llgesturemgr.h" -#include "llchat.h" -#include "llvoiceclient.h" -#include "lloutputmonitorctrl.h" -#include "llspeakers.h" - -class LLNearbyChatBarListener; - -class LLNearbyChatBar : public LLFloater -{ - LOG_CLASS(LLNearbyChatBar); - -public: - // constructor for inline chat-bars (e.g. hosted in chat history window) - LLNearbyChatBar(const LLSD& key); - ~LLNearbyChatBar() {} - - virtual BOOL postBuild(); - /*virtual*/ void onOpen(const LLSD& key); - - static LLNearbyChatBar* getInstance(); - - LLLineEditor* getChatBox() { return mChatBox; } - - virtual void draw(); - - std::string getCurrentChat(); - virtual BOOL handleKeyHere( KEY key, MASK mask ); - - static void startChat(const char* line); - static void stopChat(); - - static void sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate); - static void sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate); - - void showHistory(); - void showTranslationCheckbox(BOOL show); - /*virtual*/void setMinimized(BOOL b); - -protected: - static BOOL matchChatTypeTrigger(const std::string& in_str, std::string* out_str); - static void onChatBoxKeystroke(LLLineEditor* caller, void* userdata); - static void onChatBoxFocusLost(LLFocusableElement* caller, void* userdata); - void onChatBoxFocusReceived(); - - void sendChat( EChatType type ); - void onChatBoxCommit(); - void onChatFontChange(LLFontGL* fontp); - - /* virtual */ bool applyRectControl(); - - void showNearbyChatPanel(bool show); - void onToggleNearbyChatPanel(); - - static LLWString stripChannelNumber(const LLWString &mesg, S32* channel); - EChatType processChatTypeTriggers(EChatType type, std::string &str); - - void displaySpeakingIndicator(); - - // Which non-zero channel did we last chat on? - static S32 sLastSpecialChatChannel; - - LLLineEditor* mChatBox; - LLView* mNearbyChat; - LLOutputMonitorCtrl* mOutputMonitor; - LLLocalSpeakerMgr* mSpeakerMgr; - - S32 mExpandedHeight; - - boost::shared_ptr<LLNearbyChatBarListener> mListener; -}; - -#endif diff --git a/indra/newview/llnearbychatbarlistener.cpp b/indra/newview/llnearbychatbarlistener.cpp index a63e1fb76e..61815d1864 100644 --- a/indra/newview/llnearbychatbarlistener.cpp +++ b/indra/newview/llnearbychatbarlistener.cpp @@ -29,14 +29,14 @@ #include "llviewerprecompiledheaders.h" #include "llnearbychatbarlistener.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llagent.h" #include "llchat.h" -LLNearbyChatBarListener::LLNearbyChatBarListener(LLNearbyChatBar & chatbar) +LLNearbyChatBarListener::LLNearbyChatBarListener(LLNearbyChat & chatbar) : LLEventAPI("LLChatBar", "LLChatBar listener to (e.g.) sendChat, etc."), mChatbar(chatbar) diff --git a/indra/newview/llnearbychatbarlistener.h b/indra/newview/llnearbychatbarlistener.h index 9af9bc1f7b..0537275424 100644 --- a/indra/newview/llnearbychatbarlistener.h +++ b/indra/newview/llnearbychatbarlistener.h @@ -33,17 +33,17 @@ #include "lleventapi.h" class LLSD; -class LLNearbyChatBar; +class LLNearbyChat; class LLNearbyChatBarListener : public LLEventAPI { public: - LLNearbyChatBarListener(LLNearbyChatBar & chatbar); + LLNearbyChatBarListener(LLNearbyChat & chatbar); private: void sendChat(LLSD const & chat_data) const; - LLNearbyChatBar & mChatbar; + LLNearbyChat & mChatbar; }; #endif // LL_LLNEARBYCHATBARLISTENER_H diff --git a/indra/newview/llnearbychathandler.cpp b/indra/newview/llnearbychathandler.cpp index 600fd395fb..7834f6d320 100644 --- a/indra/newview/llnearbychathandler.cpp +++ b/indra/newview/llnearbychathandler.cpp @@ -1,6 +1,6 @@ /** * @file LLNearbyChatHandler.cpp - * @brief Nearby chat notification managment + * @brief Nearby chat chat managment * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code @@ -40,22 +40,24 @@ #include "llfloaterreg.h"//for LLFloaterReg::getTypedInstance #include "llviewerwindow.h"//for screen channel position -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llrootview.h" #include "lllayoutstack.h" //add LLNearbyChatHandler to LLNotificationsUI namespace using namespace LLNotificationsUI; -//----------------------------------------------------------------------------------------------- -//LLNearbyChatScreenChannel -//----------------------------------------------------------------------------------------------- -LLToastPanelBase* createToastPanel() +static LLNearbyChatToastPanel* createToastPanel() { LLNearbyChatToastPanel* item = LLNearbyChatToastPanel::createInstance(); return item; } + +//----------------------------------------------------------------------------------------------- +//LLNearbyChatScreenChannel +//----------------------------------------------------------------------------------------------- + class LLNearbyChatScreenChannel: public LLScreenChannelBase { LOG_CLASS(LLNearbyChatScreenChannel); @@ -81,10 +83,10 @@ public: } } - void addNotification (LLSD& notification); + void addChat (LLSD& chat); void arrangeToasts (); - typedef boost::function<LLToastPanelBase* (void )> create_toast_panel_callback_t; + typedef boost::function<LLNearbyChatToastPanel* (void )> create_toast_panel_callback_t; void setCreatePanelCallback(create_toast_panel_callback_t value) { m_create_toast_panel_callback_t = value;} void onToastDestroyed (LLToast* toast, bool app_quitting); @@ -152,6 +154,8 @@ protected: bool mChannelRect; }; + + //----------------------------------------------------------------------------------------------- // LLNearbyChatToast //----------------------------------------------------------------------------------------------- @@ -255,7 +259,7 @@ void LLNearbyChatScreenChannel::updateToastFadingTime() bool LLNearbyChatScreenChannel::createPoolToast() { - LLToastPanelBase* panel= m_create_toast_panel_callback_t(); + LLNearbyChatToastPanel* panel= m_create_toast_panel_callback_t(); if(!panel) return false; @@ -277,7 +281,7 @@ bool LLNearbyChatScreenChannel::createPoolToast() return true; } -void LLNearbyChatScreenChannel::addNotification(LLSD& notification) +void LLNearbyChatScreenChannel::addChat(LLSD& chat) { //look in pool. if there is any message if(mStopProcessing) @@ -289,8 +293,8 @@ void LLNearbyChatScreenChannel::addNotification(LLSD& notification) if(m_active_toasts.size()) { - LLUUID fromID = notification["from_id"].asUUID(); // agent id or object id - std::string from = notification["from"].asString(); + LLUUID fromID = chat["from_id"].asUUID(); // agent id or object id + std::string from = chat["from"].asString(); LLToast* toast = m_active_toasts[0].get(); if (toast) { @@ -298,7 +302,7 @@ void LLNearbyChatScreenChannel::addNotification(LLSD& notification) if(panel && panel->messageID() == fromID && panel->getFromName() == from && panel->canAddText()) { - panel->addMessage(notification); + panel->addMessage(chat); toast->reshapeToPanel(); toast->startTimer(); @@ -316,11 +320,11 @@ void LLNearbyChatScreenChannel::addNotification(LLSD& notification) LL_DEBUGS("NearbyChat") << "Empty pool" << llendl; if(!createPoolToast())//created toast will go to pool. so next call will find it return; - addNotification(notification); + addChat(chat); return; } - int chat_type = notification["chat_type"].asInteger(); + int chat_type = chat["chat_type"].asInteger(); if( ((EChatType)chat_type == CHAT_TYPE_DEBUG_MSG)) { @@ -339,10 +343,10 @@ void LLNearbyChatScreenChannel::addNotification(LLSD& notification) m_toast_pool.pop_back(); - LLToastPanelBase* panel = dynamic_cast<LLToastPanelBase*>(toast->getPanel()); + LLNearbyChatToastPanel* panel = dynamic_cast<LLNearbyChatToastPanel*>(toast->getPanel()); if(!panel) return; - panel->init(notification); + panel->init(chat); toast->reshapeToPanel(); toast->startTimer(); @@ -445,10 +449,8 @@ void LLNearbyChatScreenChannel::arrangeToasts() //----------------------------------------------------------------------------------------------- boost::scoped_ptr<LLEventPump> LLNearbyChatHandler::sChatWatcher(new LLEventStream("LLChat")); -LLNearbyChatHandler::LLNearbyChatHandler(e_notification_type type, const LLSD& id) +LLNearbyChatHandler::LLNearbyChatHandler() { - mType = type; - // Getting a Channel for our notifications LLNearbyChatScreenChannel::Params p; p.id = LLUUID(gSavedSettings.getString("NearByChatChannelUUID")); @@ -485,28 +487,27 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, if(chat_msg.mText.empty()) return;//don't process empty messages - LLFloater* chat_bar = LLFloaterReg::getInstance("chat_bar"); - - LLNearbyChat* nearby_chat = chat_bar->findChild<LLNearbyChat>("nearby_chat"); + LLFloaterReg::getInstance("im_container"); + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat"); // Build notification data - LLSD notification; - notification["message"] = chat_msg.mText; - notification["from"] = chat_msg.mFromName; - notification["from_id"] = chat_msg.mFromID; - notification["time"] = chat_msg.mTime; - notification["source"] = (S32)chat_msg.mSourceType; - notification["chat_type"] = (S32)chat_msg.mChatType; - notification["chat_style"] = (S32)chat_msg.mChatStyle; + LLSD chat; + chat["message"] = chat_msg.mText; + chat["from"] = chat_msg.mFromName; + chat["from_id"] = chat_msg.mFromID; + chat["time"] = chat_msg.mTime; + chat["source"] = (S32)chat_msg.mSourceType; + chat["chat_type"] = (S32)chat_msg.mChatType; + chat["chat_style"] = (S32)chat_msg.mChatStyle; // Pass sender info so that it can be rendered properly (STORM-1021). - notification["sender_slurl"] = LLViewerChat::getSenderSLURL(chat_msg, args); + chat["sender_slurl"] = LLViewerChat::getSenderSLURL(chat_msg, args); if (chat_msg.mChatType == CHAT_TYPE_DIRECT && chat_msg.mText.length() > 0 && chat_msg.mText[0] == '@') { // Send event on to LLEventStream and exit - sChatWatcher->post(notification); + sChatWatcher->post(chat); return; } @@ -553,11 +554,9 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, } // Send event on to LLEventStream - sChatWatcher->post(notification); + sChatWatcher->post(chat); - - if( !chat_bar->isMinimized() - && nearby_chat->isInVisibleChain() + if( nearby_chat->isInVisibleChain() || ( chat_msg.mSourceType == CHAT_SOURCE_AGENT && gSavedSettings.getBOOL("UseChatBubbles") ) || mChannel.isDead() @@ -604,23 +603,19 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, // Add a nearby chat toast. LLUUID id; id.generate(); - notification["id"] = id; + chat["id"] = id; std::string r_color_name = "White"; F32 r_color_alpha = 1.0f; LLViewerChat::getChatColor( chat_msg, r_color_name, r_color_alpha); - notification["text_color"] = r_color_name; - notification["color_alpha"] = r_color_alpha; - notification["font_size"] = (S32)LLViewerChat::getChatFontSize() ; - notification["message"] = toast_msg; - channel->addNotification(notification); + chat["text_color"] = r_color_name; + chat["color_alpha"] = r_color_alpha; + chat["font_size"] = (S32)LLViewerChat::getChatFontSize() ; + chat["message"] = toast_msg; + channel->addChat(chat); } } -void LLNearbyChatHandler::onDeleteToast(LLToast* toast) -{ -} - //----------------------------------------------------------------------------------------------- // LLNearbyChatToast diff --git a/indra/newview/llnearbychathandler.h b/indra/newview/llnearbychathandler.h index b0e4f62d51..a5034ac1cb 100644 --- a/indra/newview/llnearbychathandler.h +++ b/indra/newview/llnearbychathandler.h @@ -37,14 +37,13 @@ namespace LLNotificationsUI{ class LLNearbyChatHandler : public LLChatHandler { public: - LLNearbyChatHandler(e_notification_type type,const LLSD& id); + LLNearbyChatHandler(); virtual ~LLNearbyChatHandler(); virtual void processChat(const LLChat& chat_msg, const LLSD &args); protected: - virtual void onDeleteToast(LLToast* toast); virtual void initChannel(); static boost::scoped_ptr<LLEventPump> sChatWatcher; diff --git a/indra/newview/llnotificationalerthandler.cpp b/indra/newview/llnotificationalerthandler.cpp index 89fe7bb3c2..2bc9cdd3c1 100644 --- a/indra/newview/llnotificationalerthandler.cpp +++ b/indra/newview/llnotificationalerthandler.cpp @@ -40,10 +40,10 @@ using namespace LLNotificationsUI; //-------------------------------------------------------------------------- -LLAlertHandler::LLAlertHandler(e_notification_type type, const LLSD& id) : mIsModal(false) +LLAlertHandler::LLAlertHandler(const std::string& name, const std::string& notification_type, bool is_modal) +: LLSysHandler(name, notification_type), + mIsModal(is_modal) { - mType = type; - LLScreenChannelBase::Params p; p.id = LLUUID(gSavedSettings.getString("AlertChannelUUID")); p.display_toasts_always = true; @@ -68,79 +68,58 @@ void LLAlertHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLAlertHandler::processNotification(const LLSD& notify) +bool LLAlertHandler::processNotification(const LLNotificationPtr& notification) { if(mChannel.isDead()) { return false; } - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if(!notification) - return false; - // arrange a channel on a screen if(!mChannel.get()->getVisible()) { initChannel(); } - if (notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "load") + if (notification->canLogToIM() && notification->hasFormElements()) { - if (LLHandlerUtil::canSpawnSessionAndLogToIM(notification)) - { - const std::string name = LLHandlerUtil::getSubstitutionName(notification); - - LLUUID from_id = notification->getPayload()["from_id"]; - - // firstly create session... - LLHandlerUtil::spawnIMSession(name, from_id); - - // ...then log message to have IM Well notified about new message - LLHandlerUtil::logToIMP2P(notification); - } - - LLToastAlertPanel* alert_dialog = new LLToastAlertPanel(notification, mIsModal); - LLToast::Params p; - p.notif_id = notification->getID(); - p.notification = notification; - p.panel = dynamic_cast<LLToastPanel*>(alert_dialog); - p.enable_hide_btn = false; - p.can_fade = false; - p.is_modal = mIsModal; - p.on_delete_toast = boost::bind(&LLAlertHandler::onDeleteToast, this, _1); - - // Show alert in middle of progress view (during teleport) (EXT-1093) - LLProgressView* progress = gViewerWindow->getProgressView(); - LLRect rc = progress && progress->getVisible() ? progress->getRect() : gViewerWindow->getWorldViewRectScaled(); - mChannel.get()->updatePositionAndSize(rc); - - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->addToast(p); - } - else if (notify["sigtype"].asString() == "change") - { - LLToastAlertPanel* alert_dialog = new LLToastAlertPanel(notification, mIsModal); - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->modifyToastByNotificationID(notification->getID(), (LLToastPanel*)alert_dialog); - } - else - { - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->killToastByNotificationID(notification->getID()); + const std::string name = LLHandlerUtil::getSubstitutionName(notification); + + LLUUID from_id = notification->getPayload()["from_id"]; + + // firstly create session... + LLHandlerUtil::spawnIMSession(name, from_id); + + // ...then log message to have IM Well notified about new message + LLHandlerUtil::logToIMP2P(notification); } + + LLToastAlertPanel* alert_dialog = new LLToastAlertPanel(notification, mIsModal); + LLToast::Params p; + p.notif_id = notification->getID(); + p.notification = notification; + p.panel = dynamic_cast<LLToastPanel*>(alert_dialog); + p.enable_hide_btn = false; + p.can_fade = false; + p.is_modal = mIsModal; + p.on_delete_toast = boost::bind(&LLAlertHandler::onDeleteToast, this, _1); + + // Show alert in middle of progress view (during teleport) (EXT-1093) + LLProgressView* progress = gViewerWindow->getProgressView(); + LLRect rc = progress && progress->getVisible() ? progress->getRect() : gViewerWindow->getWorldViewRectScaled(); + mChannel.get()->updatePositionAndSize(rc); + + LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); + if(channel) + channel->addToast(p); + return false; } -//-------------------------------------------------------------------------- - -void LLAlertHandler::onDeleteToast(LLToast* toast) +void LLAlertHandler::onChange( LLNotificationPtr notification ) { + LLToastAlertPanel* alert_dialog = new LLToastAlertPanel(notification, mIsModal); + LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); + if(channel) + channel->modifyToastByNotificationID(notification->getID(), (LLToastPanel*)alert_dialog); } - -//-------------------------------------------------------------------------- - diff --git a/indra/newview/llnotificationgrouphandler.cpp b/indra/newview/llnotificationgrouphandler.cpp index ad51389241..18cd94e685 100644 --- a/indra/newview/llnotificationgrouphandler.cpp +++ b/indra/newview/llnotificationgrouphandler.cpp @@ -37,15 +37,13 @@ using namespace LLNotificationsUI; //-------------------------------------------------------------------------- -LLGroupHandler::LLGroupHandler(e_notification_type type, const LLSD& id) +LLGroupHandler::LLGroupHandler() +: LLSysHandler("Group Notifications", "groupnotify") { - mType = type; - // Getting a Channel for our notifications LLScreenChannel* channel = LLChannelManager::getInstance()->createNotificationChannel(); if(channel) { - channel->setOnRejectToastCallback(boost::bind(&LLGroupHandler::onRejectToast, this, _1)); mChannel = channel->getHandle(); } } @@ -64,72 +62,37 @@ void LLGroupHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLGroupHandler::processNotification(const LLSD& notify) +bool LLGroupHandler::processNotification(const LLNotificationPtr& notification) { if(mChannel.isDead()) { return false; } - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if(!notification) - return false; - // arrange a channel on a screen if(!mChannel.get()->getVisible()) { initChannel(); } - if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") - { - LLHandlerUtil::logGroupNoticeToIMGroup(notification); + LLHandlerUtil::logGroupNoticeToIMGroup(notification); - LLPanel* notify_box = new LLToastGroupNotifyPanel(notification); - LLToast::Params p; - p.notif_id = notification->getID(); - p.notification = notification; - p.panel = notify_box; - p.on_delete_toast = boost::bind(&LLGroupHandler::onDeleteToast, this, _1); + LLPanel* notify_box = new LLToastGroupNotifyPanel(notification); + LLToast::Params p; + p.notif_id = notification->getID(); + p.notification = notification; + p.panel = notify_box; + p.on_delete_toast = boost::bind(&LLGroupHandler::onDeleteToast, this, _1); - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->addToast(p); + LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); + if(channel) + channel->addToast(p); - // send a signal to the counter manager - mNewNotificationSignal(); + LLGroupActions::refresh_notices(); - LLGroupActions::refresh_notices(); - } - else if (notify["sigtype"].asString() == "delete") - { - mChannel.get()->killToastByNotificationID(notification->getID()); - } return false; } -//-------------------------------------------------------------------------- -void LLGroupHandler::onDeleteToast(LLToast* toast) -{ - // send a signal to the counter manager - mDelNotificationSignal(); - - // send a signal to a listener to let him perform some action - // in this case listener is a SysWellWindow and it will remove a corresponding item from its list - mNotificationIDSignal(toast->getNotificationID()); -} - -//-------------------------------------------------------------------------- -void LLGroupHandler::onRejectToast(LLUUID& id) -{ - LLNotificationPtr notification = LLNotifications::instance().find(id); - - if (notification && LLNotificationManager::getInstance()->getHandlerForNotification(notification->getType()) == this) - { - LLNotifications::instance().cancel(notification); - } -} //-------------------------------------------------------------------------- diff --git a/indra/newview/llnotificationhandler.h b/indra/newview/llnotificationhandler.h index 3569ad6447..0899625242 100644 --- a/indra/newview/llnotificationhandler.h +++ b/indra/newview/llnotificationhandler.h @@ -30,7 +30,7 @@ #include "llwindow.h" -//#include "llnotificationsutil.h" +#include "llnotifications.h" #include "llchannelmanager.h" #include "llchat.h" #include "llinstantmessage.h" @@ -40,20 +40,6 @@ class LLIMFloater; namespace LLNotificationsUI { -// ENotificationType enumerates all possible types of notifications that could be met -// -typedef enum e_notification_type -{ - NT_NOTIFY, - NT_NOTIFYTIP, - NT_GROUPNOTIFY, - NT_IMCHAT, - NT_GROUPCHAT, - NT_NEARBYCHAT, - NT_ALERT, - NT_ALERTMODAL, - NT_OFFER -} ENotificationType; /** * Handler of notification events. @@ -81,21 +67,8 @@ class LLEventHandler public: virtual ~LLEventHandler() {}; - // callbacks for counters - typedef boost::function<void (void)> notification_callback_t; - typedef boost::signals2::signal<void (void)> notification_signal_t; - notification_signal_t mNewNotificationSignal; - notification_signal_t mDelNotificationSignal; - boost::signals2::connection setNewNotificationCallback(notification_callback_t cb) { return mNewNotificationSignal.connect(cb); } - boost::signals2::connection setDelNotification(notification_callback_t cb) { return mDelNotificationSignal.connect(cb); } - // callback for notification/toast - typedef boost::function<void (const LLUUID id)> notification_id_callback_t; - typedef boost::signals2::signal<void (const LLUUID id)> notification_id_signal_t; - notification_id_signal_t mNotificationIDSignal; - boost::signals2::connection setNotificationIDCallback(notification_id_callback_t cb) { return mNotificationIDSignal.connect(cb); } - protected: - virtual void onDeleteToast(LLToast* toast)=0; + virtual void onDeleteToast(LLToast* toast) {} // arrange handler's channel on a screen // is necessary to unbind a moment of creation of a channel and a moment of positioning of it @@ -104,8 +77,6 @@ protected: virtual void initChannel()=0; LLHandle<LLScreenChannelBase> mChannel; - e_notification_type mType; - }; // LLSysHandler and LLChatHandler are more specific base classes @@ -115,20 +86,18 @@ protected: /** * Handler for system notifications. */ -class LLSysHandler : public LLEventHandler +class LLSysHandler : public LLEventHandler, public LLNotificationChannel { public: - LLSysHandler(); + LLSysHandler(const std::string& name, const std::string& notification_type); virtual ~LLSysHandler() {}; - virtual bool processNotification(const LLSD& notify)=0; - -protected : - static void init(); - void removeExclusiveNotifications(const LLNotificationPtr& notif); + // base interface functions + /*virtual*/ void onAdd(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ void onLoad(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ void onDelete(LLNotificationPtr p) { if (mChannel.get()) mChannel.get()->removeToastByNotificationID(p->getID());} - typedef std::list< std::set<std::string> > exclusive_notif_sets; - static exclusive_notif_sets sExclusiveNotificationGroups; + virtual bool processNotification(const LLNotificationPtr& notify)=0; }; /** @@ -149,15 +118,12 @@ public: class LLIMHandler : public LLSysHandler { public: - LLIMHandler(e_notification_type type, const LLSD& id); + LLIMHandler(); virtual ~LLIMHandler(); - // base interface functions - virtual bool processNotification(const LLSD& notify); - protected: - virtual void onDeleteToast(LLToast* toast); - virtual void initChannel(); + bool processNotification(const LLNotificationPtr& p); + /*virtual*/ void initChannel(); }; /** @@ -167,16 +133,15 @@ protected: class LLTipHandler : public LLSysHandler { public: - LLTipHandler(e_notification_type type, const LLSD& id); + LLTipHandler(); virtual ~LLTipHandler(); // base interface functions - virtual bool processNotification(const LLSD& notify); + /*virtual*/ void onChange(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ bool processNotification(const LLNotificationPtr& p); protected: - virtual void onDeleteToast(LLToast* toast); - virtual void onRejectToast(const LLUUID& id); - virtual void initChannel(); + /*virtual*/ void initChannel(); }; /** @@ -186,18 +151,16 @@ protected: class LLScriptHandler : public LLSysHandler { public: - LLScriptHandler(e_notification_type type, const LLSD& id); + LLScriptHandler(); virtual ~LLScriptHandler(); + /*virtual*/ void onDelete(LLNotificationPtr p); // base interface functions - virtual bool processNotification(const LLSD& notify); + /*virtual*/ bool processNotification(const LLNotificationPtr& p); protected: - virtual void onDeleteToast(LLToast* toast); - virtual void initChannel(); - - // own handlers - void onRejectToast(LLUUID& id); + /*virtual*/ void onDeleteToast(LLToast* toast); + /*virtual*/ void initChannel(); }; @@ -207,18 +170,15 @@ protected: class LLGroupHandler : public LLSysHandler { public: - LLGroupHandler(e_notification_type type, const LLSD& id); + LLGroupHandler(); virtual ~LLGroupHandler(); // base interface functions - virtual bool processNotification(const LLSD& notify); + /*virtual*/ void onChange(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ bool processNotification(const LLNotificationPtr& p); protected: - virtual void onDeleteToast(LLToast* toast); virtual void initChannel(); - - // own handlers - void onRejectToast(LLUUID& id); }; /** @@ -227,16 +187,14 @@ protected: class LLAlertHandler : public LLSysHandler { public: - LLAlertHandler(e_notification_type type, const LLSD& id); + LLAlertHandler(const std::string& name, const std::string& notification_type, bool is_modal); virtual ~LLAlertHandler(); - void setAlertMode(bool is_modal) { mIsModal = is_modal; } - - // base interface functions - virtual bool processNotification(const LLSD& notify); + /*virtual*/ void onChange(LLNotificationPtr p); + /*virtual*/ void onLoad(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ bool processNotification(const LLNotificationPtr& p); protected: - virtual void onDeleteToast(LLToast* toast); virtual void initChannel(); bool mIsModal; @@ -249,102 +207,71 @@ protected: class LLOfferHandler : public LLSysHandler { public: - LLOfferHandler(e_notification_type type, const LLSD& id); + LLOfferHandler(); virtual ~LLOfferHandler(); // base interface functions - virtual bool processNotification(const LLSD& notify); + /*virtual*/ void onChange(LLNotificationPtr p); + /*virtual*/ void onDelete(LLNotificationPtr notification); + /*virtual*/ bool processNotification(const LLNotificationPtr& p); protected: - virtual void onDeleteToast(LLToast* toast); - virtual void initChannel(); - - // own handlers - void onRejectToast(LLUUID& id); + /*virtual*/ void initChannel(); }; /** * Handler for UI hints. */ -class LLHintHandler : public LLSingleton<LLHintHandler> +class LLHintHandler : public LLNotificationChannel { public: - LLHintHandler(); - virtual ~LLHintHandler(); + LLHintHandler() : LLNotificationChannel("Hints", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "hint")) + {} + virtual ~LLHintHandler() {} - // base interface functions - virtual bool processNotification(const LLSD& notify); + /*virtual*/ void onAdd(LLNotificationPtr p); + /*virtual*/ void onLoad(LLNotificationPtr p); + /*virtual*/ void onDelete(LLNotificationPtr p); }; /** * Handler for browser notifications */ -class LLBrowserNotification : public LLSingleton<LLBrowserNotification> +class LLBrowserNotification : public LLNotificationChannel { public: - virtual bool processNotification(const LLSD& notify); + LLBrowserNotification() + : LLNotificationChannel("Browser", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "browser")) + {} + /*virtual*/ void onAdd(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ void onChange(LLNotificationPtr p) { processNotification(p); } + bool processNotification(const LLNotificationPtr& p); }; /** * Handler for outbox notifications */ -class LLOutboxNotification : public LLSingleton<LLOutboxNotification> +class LLOutboxNotification : public LLNotificationChannel { public: - virtual bool processNotification(const LLSD& notify); + LLOutboxNotification() + : LLNotificationChannel("Outbox", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "outbox")) + {} + /*virtual*/ void onAdd(LLNotificationPtr p) { processNotification(p); } + /*virtual*/ void onChange(LLNotificationPtr p) { } + /*virtual*/ void onDelete(LLNotificationPtr p); + bool processNotification(const LLNotificationPtr& p); }; class LLHandlerUtil { public: /** - * Checks sufficient conditions to log notification message to IM session. - */ - static bool canLogToIM(const LLNotificationPtr& notification); - - /** - * Checks sufficient conditions to log notification message to nearby chat session. - */ - static bool canLogToNearbyChat(const LLNotificationPtr& notification); - - /** - * Checks sufficient conditions to spawn IM session. - */ - static bool canSpawnIMSession(const LLNotificationPtr& notification); - - /** - * Checks sufficient conditions to add notification toast panel IM floater. - */ - static bool canAddNotifPanelToIM(const LLNotificationPtr& notification); - - /** - * Checks whether notification can be used multiple times or not. - */ - static bool isNotificationReusable(const LLNotificationPtr& notification); - - /** - * Checks if passed notification can create IM session and be written into it. - * - * This method uses canLogToIM() & canSpawnIMSession(). - */ - static bool canSpawnSessionAndLogToIM(const LLNotificationPtr& notification); - - /** - * Checks if passed notification can create toast. - */ - static bool canSpawnToast(const LLNotificationPtr& notification); - - /** * Determines whether IM floater is opened. */ static bool isIMFloaterOpened(const LLNotificationPtr& notification); /** - * Determines whether IM floater is focused. - */ - static bool isIMFloaterFocused(const LLNotificationPtr& notification); - - /** * Writes notification message to IM session. */ static void logToIM(const EInstantMessage& session_type, @@ -355,12 +282,7 @@ public: /** * Writes notification message to IM p2p session. */ - static void logToIMP2P(const LLNotificationPtr& notification); - - /** - * Writes notification message to IM p2p session. - */ - static void logToIMP2P(const LLNotificationPtr& notification, bool to_file_only); + static void logToIMP2P(const LLNotificationPtr& notification, bool to_file_only = false); /** * Writes group notice notification message to IM group session. @@ -406,13 +328,6 @@ public: */ static void decIMMesageCounter(const LLNotificationPtr& notification); -private: - - /** - * Find IM floater based on "from_id" - */ - static LLIMFloater* findIMFloater(const LLNotificationPtr& notification); - }; } diff --git a/indra/newview/llnotificationhandlerutil.cpp b/indra/newview/llnotificationhandlerutil.cpp index 16c51138a9..b4e8927879 100644 --- a/indra/newview/llnotificationhandlerutil.cpp +++ b/indra/newview/llnotificationhandlerutil.cpp @@ -41,212 +41,19 @@ using namespace LLNotificationsUI; -// static -std::list< std::set<std::string> > LLSysHandler::sExclusiveNotificationGroups; - -// static -void LLSysHandler::init() -{ - std::set<std::string> online_offline_group; - online_offline_group.insert("FriendOnline"); - online_offline_group.insert("FriendOffline"); - - sExclusiveNotificationGroups.push_back(online_offline_group); -} - -LLSysHandler::LLSysHandler() -{ - if(sExclusiveNotificationGroups.empty()) - { - init(); - } -} - -void LLSysHandler::removeExclusiveNotifications(const LLNotificationPtr& notif) -{ - LLScreenChannel* channel = dynamic_cast<LLScreenChannel *>(mChannel.get()); - if (channel == NULL) - { - return; - } - - class ExclusiveMatcher: public LLScreenChannel::Matcher - { - public: - ExclusiveMatcher(const std::set<std::string>& excl_group, - const std::string& from_name) : - mExclGroup(excl_group), mFromName(from_name) - { - } - bool matches(const LLNotificationPtr notification) const - { - for (std::set<std::string>::const_iterator it = mExclGroup.begin(); it - != mExclGroup.end(); it++) - { - std::string from_name = LLHandlerUtil::getSubstitutionName(notification); - if (notification->getName() == *it && from_name == mFromName) - { - return true; - } - } - return false; - } - private: - const std::set<std::string>& mExclGroup; - const std::string& mFromName; - }; - - - for (exclusive_notif_sets::iterator it = sExclusiveNotificationGroups.begin(); it - != sExclusiveNotificationGroups.end(); it++) - { - std::set<std::string> group = *it; - std::set<std::string>::iterator g_it = group.find(notif->getName()); - if (g_it != group.end()) - { - channel->killMatchedToasts(ExclusiveMatcher(group, - LLHandlerUtil::getSubstitutionName(notif))); - } - } -} - -const static std::string GRANTED_MODIFY_RIGHTS("GrantedModifyRights"), - REVOKED_MODIFY_RIGHTS("RevokedModifyRights"), - OBJECT_GIVE_ITEM("ObjectGiveItem"), - OBJECT_GIVE_ITEM_UNKNOWN_USER("ObjectGiveItemUnknownUser"), - PAYMENT_RECEIVED("PaymentReceived"), - PAYMENT_SENT("PaymentSent"), - ADD_FRIEND_WITH_MESSAGE("AddFriendWithMessage"), - USER_GIVE_ITEM("UserGiveItem"), - INVENTORY_ACCEPTED("InventoryAccepted"), - INVENTORY_DECLINED("InventoryDeclined"), - OFFER_FRIENDSHIP("OfferFriendship"), - FRIENDSHIP_ACCEPTED("FriendshipAccepted"), - FRIENDSHIP_OFFERED("FriendshipOffered"), - FRIENDSHIP_ACCEPTED_BYME("FriendshipAcceptedByMe"), - FRIENDSHIP_DECLINED_BYME("FriendshipDeclinedByMe"), - FRIEND_ONLINE("FriendOnline"), FRIEND_OFFLINE("FriendOffline"), - SERVER_OBJECT_MESSAGE("ServerObjectMessage"), - TELEPORT_OFFERED("TeleportOffered"), - TELEPORT_OFFERED_MATURITY_EXCEEDED("TeleportOffered_MaturityExceeded"), - TELEPORT_OFFERED_MATURITY_BLOCKED("TeleportOffered_MaturityBlocked"), - TELEPORT_OFFER_SENT("TeleportOfferSent"), - IM_SYSTEM_MESSAGE_TIP("IMSystemMessageTip"); - - -// static -bool LLHandlerUtil::canLogToIM(const LLNotificationPtr& notification) -{ - return GRANTED_MODIFY_RIGHTS == notification->getName() - || REVOKED_MODIFY_RIGHTS == notification->getName() - || PAYMENT_RECEIVED == notification->getName() - || PAYMENT_SENT == notification->getName() - || OFFER_FRIENDSHIP == notification->getName() - || FRIENDSHIP_OFFERED == notification->getName() - || FRIENDSHIP_ACCEPTED == notification->getName() - || FRIENDSHIP_ACCEPTED_BYME == notification->getName() - || FRIENDSHIP_DECLINED_BYME == notification->getName() - || SERVER_OBJECT_MESSAGE == notification->getName() - || INVENTORY_ACCEPTED == notification->getName() - || INVENTORY_DECLINED == notification->getName() - || USER_GIVE_ITEM == notification->getName() - || TELEPORT_OFFERED == notification->getName() - || TELEPORT_OFFERED_MATURITY_EXCEEDED == notification->getName() - || TELEPORT_OFFERED_MATURITY_BLOCKED == notification->getName() - || TELEPORT_OFFER_SENT == notification->getName() - || IM_SYSTEM_MESSAGE_TIP == notification->getName(); -} +LLSysHandler::LLSysHandler(const std::string& name, const std::string& notification_type) +: LLNotificationChannel(name, "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, notification_type)) +{} // static -bool LLHandlerUtil::canLogToNearbyChat(const LLNotificationPtr& notification) -{ - return notification->getType() == "notifytip" - && FRIEND_ONLINE != notification->getName() - && FRIEND_OFFLINE != notification->getName() - && INVENTORY_ACCEPTED != notification->getName() - && INVENTORY_DECLINED != notification->getName() - && IM_SYSTEM_MESSAGE_TIP != notification->getName(); -} - -// static -bool LLHandlerUtil::canSpawnIMSession(const LLNotificationPtr& notification) -{ - return OFFER_FRIENDSHIP == notification->getName() - || USER_GIVE_ITEM == notification->getName() - || TELEPORT_OFFERED == notification->getName() - || TELEPORT_OFFERED_MATURITY_EXCEEDED == notification->getName() - || TELEPORT_OFFERED_MATURITY_BLOCKED == notification->getName(); -} - -// static -bool LLHandlerUtil::canAddNotifPanelToIM(const LLNotificationPtr& notification) -{ - return OFFER_FRIENDSHIP == notification->getName() - || USER_GIVE_ITEM == notification->getName() - || TELEPORT_OFFERED == notification->getName() - || TELEPORT_OFFERED_MATURITY_EXCEEDED == notification->getName() - || TELEPORT_OFFERED_MATURITY_BLOCKED == notification->getName(); -} - -// static -bool LLHandlerUtil::isNotificationReusable(const LLNotificationPtr& notification) -{ - return OFFER_FRIENDSHIP == notification->getName() - || USER_GIVE_ITEM == notification->getName() - || TELEPORT_OFFERED == notification->getName() - || TELEPORT_OFFERED_MATURITY_EXCEEDED == notification->getName() - || TELEPORT_OFFERED_MATURITY_BLOCKED == notification->getName(); -} - -// static -bool LLHandlerUtil::canSpawnSessionAndLogToIM(const LLNotificationPtr& notification) -{ - return canLogToIM(notification) && canSpawnIMSession(notification); -} - -// static -bool LLHandlerUtil::canSpawnToast(const LLNotificationPtr& notification) +bool LLHandlerUtil::isIMFloaterOpened(const LLNotificationPtr& notification) { - if(INVENTORY_DECLINED == notification->getName() - || INVENTORY_ACCEPTED == notification->getName()) - { - // return false for inventory accepted/declined notifications if respective IM window is open (EXT-5909) - return ! isIMFloaterOpened(notification); - } - - if(FRIENDSHIP_ACCEPTED == notification->getName()) - { - // don't show FRIENDSHIP_ACCEPTED if IM window is opened and focused - EXT-6441 - return ! isIMFloaterFocused(notification); - } - - if(OFFER_FRIENDSHIP == notification->getName() - || USER_GIVE_ITEM == notification->getName() - || TELEPORT_OFFERED == notification->getName() - || TELEPORT_OFFERED_MATURITY_EXCEEDED == notification->getName() - || TELEPORT_OFFERED_MATURITY_BLOCKED == notification->getName()) - { - // When ANY offer arrives, show toast, unless IM window is already open - EXT-5904 - return ! isIMFloaterOpened(notification); - } - - return true; -} + bool res = false; -// static -LLIMFloater* LLHandlerUtil::findIMFloater(const LLNotificationPtr& notification) -{ LLUUID from_id = notification->getPayload()["from_id"]; LLUUID session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, from_id); - return LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); -} - -// static -bool LLHandlerUtil::isIMFloaterOpened(const LLNotificationPtr& notification) -{ - bool res = false; + LLIMFloater* im_floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); - LLIMFloater* im_floater = findIMFloater(notification); if (im_floater != NULL) { res = im_floater->getVisible() == TRUE; @@ -255,19 +62,6 @@ bool LLHandlerUtil::isIMFloaterOpened(const LLNotificationPtr& notification) return res; } -bool LLHandlerUtil::isIMFloaterFocused(const LLNotificationPtr& notification) -{ - bool res = false; - - LLIMFloater* im_floater = findIMFloater(notification); - if (im_floater != NULL) - { - res = im_floater->hasFocus() == TRUE; - } - - return res; -} - // static void LLHandlerUtil::logToIM(const EInstantMessage& session_type, const std::string& session_name, const std::string& from_name, @@ -299,13 +93,6 @@ void LLHandlerUtil::logToIM(const EInstantMessage& session_type, } else { - // store active session id - const LLUUID & active_session_id = - LLIMModel::instance().getActiveSessionID(); - - // set searched session as active to avoid IM toast popup - LLIMModel::instance().setActiveSessionID(session_id); - S32 unread = session->mNumUnread; S32 participant_unread = session->mParticipantUnreadMessageCount; LLIMModel::instance().addMessageSilently(session_id, from, from_id, @@ -316,25 +103,9 @@ void LLHandlerUtil::logToIM(const EInstantMessage& session_type, // update IM floater messages updateIMFLoaterMesages(session_id); - - // restore active session id - if (active_session_id.isNull()) - { - LLIMModel::instance().resetActiveSessionID(); - } - else - { - LLIMModel::instance().setActiveSessionID(active_session_id); - } } } -// static -void LLHandlerUtil::logToIMP2P(const LLNotificationPtr& notification) -{ - logToIMP2P(notification, false); -} - void log_name_callback(const std::string& full_name, const std::string& from_name, const std::string& message, const LLUUID& from_id) @@ -346,9 +117,6 @@ void log_name_callback(const std::string& full_name, const std::string& from_nam // static void LLHandlerUtil::logToIMP2P(const LLNotificationPtr& notification, bool to_file_only) { - // don't create IM p2p session with objects, it's necessary condition to log - if (notification->getName() != OBJECT_GIVE_ITEM) - { LLUUID from_id = notification->getPayload()["from_id"]; if (from_id.isNull()) @@ -366,7 +134,6 @@ void LLHandlerUtil::logToIMP2P(const LLNotificationPtr& notification, bool to_fi gCacheName->get(from_id, false, boost::bind(&log_name_callback, _2, INTERACTIVE_SYSTEM_FROM, notification->getMessage(), from_id)); } } -} // static void LLHandlerUtil::logGroupNoticeToIMGroup( @@ -397,8 +164,8 @@ void LLHandlerUtil::logGroupNoticeToIMGroup( // static void LLHandlerUtil::logToNearbyChat(const LLNotificationPtr& notification, EChatSourceType type) { - LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); - if(nearby_chat) + LLNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance<LLNearbyChat>("nearby_chat"); + if (nearby_chat) { LLChat chat_msg(notification->getMessage()); chat_msg.mSourceType = type; @@ -501,14 +268,10 @@ void LLHandlerUtil::decIMMesageCounter(const LLNotificationPtr& notification) LLUUID from_id = notification->getPayload()["from_id"]; LLUUID session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, from_id); - LLIMModel::LLIMSession * session = LLIMModel::getInstance()->findIMSession( - session_id); + LLIMModel::LLIMSession * session = LLIMModel::getInstance()->findIMSession(session_id); - if (session == NULL) + if (session) { - return; - } - LLSD arg; arg["session_id"] = session_id; session->mNumUnread--; @@ -517,3 +280,5 @@ void LLHandlerUtil::decIMMesageCounter(const LLNotificationPtr& notification) arg["participant_unread"] = session->mParticipantUnreadMessageCount; LLIMModel::getInstance()->mNewMsgSignal(arg); } +} + diff --git a/indra/newview/llnotificationhinthandler.cpp b/indra/newview/llnotificationhinthandler.cpp index f7163cb04f..271f418507 100644 --- a/indra/newview/llnotificationhinthandler.cpp +++ b/indra/newview/llnotificationhinthandler.cpp @@ -33,26 +33,6 @@ using namespace LLNotificationsUI; -LLHintHandler::LLHintHandler() -{ -} - -LLHintHandler::~LLHintHandler() -{ -} - -bool LLHintHandler::processNotification(const LLSD& notify) -{ - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - std::string sigtype = notify["sigtype"].asString(); - if (sigtype == "add" || sigtype == "load") - { - LLHints::show(notification); - } - else if (sigtype == "delete") - { - LLHints::hide(notification); - } - return false; -} +void LLHintHandler::onAdd(LLNotificationPtr p) { LLHints::show(p); } +void LLHintHandler::onLoad(LLNotificationPtr p) { LLHints::show(p); } +void LLHintHandler::onDelete(LLNotificationPtr p) { LLHints::hide(p); } diff --git a/indra/newview/llnotificationmanager.cpp b/indra/newview/llnotificationmanager.cpp index f792f53ac5..2862ad6962 100644 --- a/indra/newview/llnotificationmanager.cpp +++ b/indra/newview/llnotificationmanager.cpp @@ -42,107 +42,35 @@ using namespace LLNotificationsUI; //-------------------------------------------------------------------------- LLNotificationManager::LLNotificationManager() { - mNotifyHandlers.clear(); init(); } //-------------------------------------------------------------------------- LLNotificationManager::~LLNotificationManager() { - BOOST_FOREACH(listener_pair_t& pair, mChannelListeners) - { - pair.second.disconnect(); - } } //-------------------------------------------------------------------------- void LLNotificationManager::init() { - LLNotificationChannel::buildChannel("Notifications", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "notify")); - LLNotificationChannel::buildChannel("NotificationTips", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "notifytip")); - LLNotificationChannel::buildChannel("Group Notifications", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "groupnotify")); - LLNotificationChannel::buildChannel("Alerts", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alert")); - LLNotificationChannel::buildChannel("AlertModal", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alertmodal")); - LLNotificationChannel::buildChannel("IM Notifications", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "notifytoast")); - LLNotificationChannel::buildChannel("Offer", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "offer")); - LLNotificationChannel::buildChannel("Hints", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "hint")); - LLNotificationChannel::buildChannel("Browser", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "browser")); - LLNotificationChannel::buildChannel("Outbox", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "outbox")); + mChannels.push_back(new LLScriptHandler()); + mChannels.push_back(new LLTipHandler()); + mChannels.push_back(new LLGroupHandler()); + mChannels.push_back(new LLAlertHandler("Alerts", "alert", false)); + mChannels.push_back(new LLAlertHandler("AlertModal", "alertmodal", true)); + mChannels.push_back(new LLOfferHandler()); + mChannels.push_back(new LLHintHandler()); + mChannels.push_back(new LLBrowserNotification()); + mChannels.push_back(new LLOutboxNotification()); + mChannels.push_back(new LLIMHandler()); - mChannelListeners["Notifications"] = LLNotifications::instance().getChannel("Notifications")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["NotificationTips"] = LLNotifications::instance().getChannel("NotificationTips")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["Group Notifications"] = LLNotifications::instance().getChannel("Group Notifications")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["Alerts"] = LLNotifications::instance().getChannel("Alerts")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["AlertModal"] = LLNotifications::instance().getChannel("AlertModal")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["IM Notifications"] = LLNotifications::instance().getChannel("IM Notifications")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["Offer"] = LLNotifications::instance().getChannel("Offer")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); - mChannelListeners["Hints"] = LLNotifications::instance().getChannel("Hints")->connectChanged(boost::bind(&LLHintHandler::processNotification, LLHintHandler::getInstance(), _1)); - mChannelListeners["Browser"] = LLNotifications::instance().getChannel("Browser")->connectChanged(boost::bind(&LLBrowserNotification::processNotification, LLBrowserNotification::getInstance(), _1)); - mChannelListeners["Outbox"] = LLNotifications::instance().getChannel("Outbox")->connectChanged(boost::bind(&LLOutboxNotification::processNotification, LLOutboxNotification::getInstance(), _1)); - - mNotifyHandlers["notify"] = boost::shared_ptr<LLEventHandler>(new LLScriptHandler(NT_NOTIFY, LLSD())); - mNotifyHandlers["notifytip"] = boost::shared_ptr<LLEventHandler>(new LLTipHandler(NT_NOTIFY, LLSD())); - mNotifyHandlers["groupnotify"] = boost::shared_ptr<LLEventHandler>(new LLGroupHandler(NT_GROUPNOTIFY, LLSD())); - mNotifyHandlers["alert"] = boost::shared_ptr<LLEventHandler>(new LLAlertHandler(NT_ALERT, LLSD())); - mNotifyHandlers["alertmodal"] = boost::shared_ptr<LLEventHandler>(new LLAlertHandler(NT_ALERT, LLSD())); - static_cast<LLAlertHandler*>(mNotifyHandlers["alertmodal"].get())->setAlertMode(true); - mNotifyHandlers["notifytoast"] = boost::shared_ptr<LLEventHandler>(new LLIMHandler(NT_IMCHAT, LLSD())); - - mNotifyHandlers["nearbychat"] = boost::shared_ptr<LLEventHandler>(new LLNearbyChatHandler(NT_NEARBYCHAT, LLSD())); - mNotifyHandlers["offer"] = boost::shared_ptr<LLEventHandler>(new LLOfferHandler(NT_OFFER, LLSD())); -} - -//-------------------------------------------------------------------------- -bool LLNotificationManager::onNotification(const LLSD& notify) -{ - LLSysHandler* handle = NULL; - - if (LLNotifications::destroyed()) - return false; - - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if (!notification) - return false; - - std::string notification_type = notification->getType(); - handle = static_cast<LLSysHandler*>(mNotifyHandlers[notification_type].get()); - - if(!handle) - return false; - - return handle->processNotification(notify); + mChatHandler = boost::shared_ptr<LLNearbyChatHandler>(new LLNearbyChatHandler()); } //-------------------------------------------------------------------------- void LLNotificationManager::onChat(const LLChat& msg, const LLSD &args) { - // check ENotificationType argument - switch(args["type"].asInteger()) - { - case NT_NEARBYCHAT: - { - LLNearbyChatHandler* handle = dynamic_cast<LLNearbyChatHandler*>(mNotifyHandlers["nearbychat"].get()); - - if(handle) - handle->processChat(msg, args); - } - break; - default: //no need to handle all enum types - break; - } -} - -//-------------------------------------------------------------------------- -LLEventHandler* LLNotificationManager::getHandlerForNotification(std::string notification_type) -{ - std::map<std::string, boost::shared_ptr<LLEventHandler> >::iterator it = mNotifyHandlers.find(notification_type); - - if(it != mNotifyHandlers.end()) - return (*it).second.get(); - - return NULL; + if(mChatHandler) + mChatHandler->processChat(msg, args); } -//-------------------------------------------------------------------------- - diff --git a/indra/newview/llnotificationmanager.h b/indra/newview/llnotificationmanager.h index 27b6ba1c71..c8afdf9e46 100644 --- a/indra/newview/llnotificationmanager.h +++ b/indra/newview/llnotificationmanager.h @@ -28,8 +28,6 @@ #ifndef LL_LLNOTIFICATIONMANAGER_H #define LL_LLNOTIFICATIONMANAGER_H -#include "llevents.h" - #include "lluictrl.h" #include "llnotificationhandler.h" @@ -49,7 +47,6 @@ class LLToast; class LLNotificationManager : public LLSingleton<LLNotificationManager> { typedef std::pair<std::string, LLEventHandler*> eventhandlers; - typedef std::pair<const std::string, LLBoundListener> listener_pair_t; public: LLNotificationManager(); virtual ~LLNotificationManager(); @@ -59,22 +56,12 @@ public: void init(void); //TODO: combine processing and storage (*) - // this method reacts on system notifications and calls an appropriate handler - bool onNotification(const LLSD& notification); - // this method reacts on chat notifications and calls an appropriate handler void onChat(const LLChat& msg, const LLSD &args); - // get a handler for a certain type of notification - LLEventHandler* getHandlerForNotification(std::string notification_type); - - private: - //TODO (*) - std::map<std::string, boost::shared_ptr<LLEventHandler> > mNotifyHandlers; - // cruft std::map<std::string, LLChatHandler*> mChatHandlers; - - std::map<std::string, LLBoundListener> mChannelListeners; + boost::shared_ptr<class LLNearbyChatHandler> mChatHandler; + std::vector<LLNotificationChannelPtr> mChannels; }; } diff --git a/indra/newview/llnotificationofferhandler.cpp b/indra/newview/llnotificationofferhandler.cpp index 1552ed3346..6e641575fa 100644 --- a/indra/newview/llnotificationofferhandler.cpp +++ b/indra/newview/llnotificationofferhandler.cpp @@ -40,16 +40,14 @@ using namespace LLNotificationsUI; //-------------------------------------------------------------------------- -LLOfferHandler::LLOfferHandler(e_notification_type type, const LLSD& id) +LLOfferHandler::LLOfferHandler() +: LLSysHandler("Offer", "offer") { - mType = type; - // Getting a Channel for our notifications LLScreenChannel* channel = LLChannelManager::getInstance()->createNotificationChannel(); if(channel) { channel->setControlHovering(true); - channel->setOnRejectToastCallback(boost::bind(&LLOfferHandler::onRejectToast, this, _1)); mChannel = channel->getHandle(); } } @@ -68,147 +66,109 @@ void LLOfferHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLOfferHandler::processNotification(const LLSD& notify) +bool LLOfferHandler::processNotification(const LLNotificationPtr& notification) { if(mChannel.isDead()) { return false; } - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if(!notification) - return false; - // arrange a channel on a screen if(!mChannel.get()->getVisible()) { initChannel(); } - if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") - { + if( notification->getPayload().has("give_inventory_notification") + && notification->getPayload()["give_inventory_notification"].asBoolean() == false) + { + // This is an original inventory offer, so add a script floater + LLScriptFloaterManager::instance().onAddNotification(notification->getID()); + } + else + { + bool add_notif_to_im = notification->canLogToIM() && notification->hasFormElements(); - if( notification->getPayload().has("give_inventory_notification") - && !notification->getPayload()["give_inventory_notification"] ) + if (add_notif_to_im) { - // This is an original inventory offer, so add a script floater - LLScriptFloaterManager::instance().onAddNotification(notification->getID()); + const std::string name = LLHandlerUtil::getSubstitutionName(notification); + + LLUUID from_id = notification->getPayload()["from_id"]; + + LLHandlerUtil::spawnIMSession(name, from_id); + LLHandlerUtil::addNotifPanelToIM(notification); } - else + + if (!notification->canShowToast()) { - notification->setReusable(LLHandlerUtil::isNotificationReusable(notification)); - - LLUUID session_id; - if (LLHandlerUtil::canSpawnIMSession(notification)) - { - const std::string name = LLHandlerUtil::getSubstitutionName(notification); - - LLUUID from_id = notification->getPayload()["from_id"]; - - session_id = LLHandlerUtil::spawnIMSession(name, from_id); - } - - bool show_toast = LLHandlerUtil::canSpawnToast(notification); - bool add_notid_to_im = LLHandlerUtil::canAddNotifPanelToIM(notification); - if (add_notid_to_im) - { - LLHandlerUtil::addNotifPanelToIM(notification); - } - - if (notification->getPayload().has("SUPPRESS_TOAST") - && notification->getPayload()["SUPPRESS_TOAST"]) - { - LLNotificationsUtil::cancel(notification); - } - else if(show_toast) - { - LLToastNotifyPanel* notify_box = new LLToastNotifyPanel(notification); - // don't close notification on panel destroy since it will be used by IM floater - notify_box->setCloseNotificationOnDestroy(!add_notid_to_im); - LLToast::Params p; - p.notif_id = notification->getID(); - p.notification = notification; - p.panel = notify_box; - p.on_delete_toast = boost::bind(&LLOfferHandler::onDeleteToast, this, _1); - // we not save offer notifications to the syswell floater that should be added to the IM floater - p.can_be_stored = !add_notid_to_im; - - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->addToast(p); - - // if we not add notification to IM - add it to notification well - if (!add_notid_to_im) - { - // send a signal to the counter manager - mNewNotificationSignal(); - } - } - - if (LLHandlerUtil::canLogToIM(notification)) - { - // log only to file if notif panel can be embedded to IM and IM is opened - if (add_notid_to_im && LLHandlerUtil::isIMFloaterOpened(notification)) - { - LLHandlerUtil::logToIMP2P(notification, true); - } - else - { - LLHandlerUtil::logToIMP2P(notification); - } - } + LLNotificationsUtil::cancel(notification); } - } - else if (notify["sigtype"].asString() == "delete") - { - if( notification->getPayload().has("give_inventory_notification") - && !notification->getPayload()["give_inventory_notification"] ) + else if(!notification->canLogToIM() || !LLHandlerUtil::isIMFloaterOpened(notification)) { - // Remove original inventory offer script floater - LLScriptFloaterManager::instance().onRemoveNotification(notification->getID()); + LLToastNotifyPanel* notify_box = new LLToastNotifyPanel(notification); + LLToast::Params p; + p.notif_id = notification->getID(); + p.notification = notification; + p.panel = notify_box; + // we not save offer notifications to the syswell floater that should be added to the IM floater + p.can_be_stored = !add_notif_to_im; + + LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); + if(channel) + channel->addToast(p); } - else + + if (notification->canLogToIM()) { - if (LLHandlerUtil::canAddNotifPanelToIM(notification) - && !LLHandlerUtil::isIMFloaterOpened(notification)) - { - LLHandlerUtil::decIMMesageCounter(notification); - } - mChannel.get()->killToastByNotificationID(notification->getID()); + // log only to file if notif panel can be embedded to IM and IM is opened + bool file_only = add_notif_to_im && LLHandlerUtil::isIMFloaterOpened(notification); + LLHandlerUtil::logToIMP2P(notification, file_only); } } return false; } -//-------------------------------------------------------------------------- - -void LLOfferHandler::onDeleteToast(LLToast* toast) +/*virtual*/ void LLOfferHandler::onChange(LLNotificationPtr p) { - if (!LLHandlerUtil::canAddNotifPanelToIM(toast->getNotification())) + LLToastNotifyPanel* panelp = LLToastNotifyPanel::getInstance(p->getID()); + if (panelp) { - // send a signal to the counter manager - mDelNotificationSignal(); + // + // HACK: if we're dealing with a notification embedded in IM, update it + // otherwise remove its toast + // + if (dynamic_cast<LLIMToastNotifyPanel*>(panelp)) + { + panelp->updateNotification(); + } + else + { + // if notification has changed, hide it + mChannel.get()->removeToastByNotificationID(p->getID()); + } } - - // send a signal to a listener to let him perform some action - // in this case listener is a SysWellWindow and it will remove a corresponding item from its list - mNotificationIDSignal(toast->getNotificationID()); } -//-------------------------------------------------------------------------- -void LLOfferHandler::onRejectToast(LLUUID& id) -{ - LLNotificationPtr notification = LLNotifications::instance().find(id); - if (notification - && LLNotificationManager::getInstance()->getHandlerForNotification( - notification->getType()) == this - // don't delete notification since it may be used by IM floater - && !LLHandlerUtil::canAddNotifPanelToIM(notification)) +/*virtual*/ void LLOfferHandler::onDelete(LLNotificationPtr notification) +{ + if( notification->getPayload().has("give_inventory_notification") + && !notification->getPayload()["give_inventory_notification"] ) + { + // Remove original inventory offer script floater + LLScriptFloaterManager::instance().onRemoveNotification(notification->getID()); + } + else { - LLNotifications::instance().cancel(notification); + if (notification->canLogToIM() + && notification->hasFormElements() + && !LLHandlerUtil::isIMFloaterOpened(notification)) + { + LLHandlerUtil::decIMMesageCounter(notification); + } + mChannel.get()->removeToastByNotificationID(notification->getID()); } } + diff --git a/indra/newview/llnotificationscripthandler.cpp b/indra/newview/llnotificationscripthandler.cpp index 398f54c6f7..290a81f91c 100644 --- a/indra/newview/llnotificationscripthandler.cpp +++ b/indra/newview/llnotificationscripthandler.cpp @@ -37,21 +37,15 @@ using namespace LLNotificationsUI; -static const std::string SCRIPT_DIALOG ("ScriptDialog"); -static const std::string SCRIPT_DIALOG_GROUP ("ScriptDialogGroup"); -static const std::string SCRIPT_LOAD_URL ("LoadWebPage"); - //-------------------------------------------------------------------------- -LLScriptHandler::LLScriptHandler(e_notification_type type, const LLSD& id) +LLScriptHandler::LLScriptHandler() +: LLSysHandler("Notifications", "notify") { - mType = type; - // Getting a Channel for our notifications LLScreenChannel* channel = LLChannelManager::getInstance()->createNotificationChannel(); if(channel) { channel->setControlHovering(true); - channel->setOnRejectToastCallback(boost::bind(&LLScriptHandler::onRejectToast, this, _1)); mChannel = channel->getHandle(); } } @@ -70,104 +64,77 @@ void LLScriptHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLScriptHandler::processNotification(const LLSD& notify) +bool LLScriptHandler::processNotification(const LLNotificationPtr& notification) { if(mChannel.isDead()) { return false; } - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if(!notification) - return false; - // arrange a channel on a screen if(!mChannel.get()->getVisible()) { initChannel(); } - if(notify["sigtype"].asString() == "add") + if (notification->canLogToIM()) { - if (LLHandlerUtil::canLogToIM(notification)) - { - LLHandlerUtil::logToIMP2P(notification); - } + LLHandlerUtil::logToIMP2P(notification); + } - if(SCRIPT_DIALOG == notification->getName() || SCRIPT_DIALOG_GROUP == notification->getName() || SCRIPT_LOAD_URL == notification->getName()) - { - LLScriptFloaterManager::getInstance()->onAddNotification(notification->getID()); - } - else + if(notification->hasFormElements() && !notification->canShowToast()) + { + LLScriptFloaterManager::getInstance()->onAddNotification(notification->getID()); + } + else + { + LLToastPanel* notify_box = LLToastPanel::buidPanelFromNotification(notification); + + LLToast::Params p; + p.notif_id = notification->getID(); + p.notification = notification; + p.panel = notify_box; + p.on_delete_toast = boost::bind(&LLScriptHandler::onDeleteToast, this, _1); + + LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); + if(channel) { - LLToastPanel* notify_box = LLToastPanel::buidPanelFromNotification(notification); - - LLToast::Params p; - p.notif_id = notification->getID(); - p.notification = notification; - p.panel = notify_box; - p.on_delete_toast = boost::bind(&LLScriptHandler::onDeleteToast, this, _1); - - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - { - channel->addToast(p); - } - - // send a signal to the counter manager - mNewNotificationSignal(); + channel->addToast(p); } } - else if (notify["sigtype"].asString() == "delete") + + return false; +} + + +void LLScriptHandler::onDelete( LLNotificationPtr notification ) { - if(SCRIPT_DIALOG == notification->getName() || SCRIPT_DIALOG_GROUP == notification->getName() || SCRIPT_LOAD_URL == notification->getName()) + if(notification->hasFormElements() && !notification->canShowToast()) { LLScriptFloaterManager::getInstance()->onRemoveNotification(notification->getID()); } else { - mChannel.get()->killToastByNotificationID(notification->getID()); + mChannel.get()->removeToastByNotificationID(notification->getID()); } } - return false; -} + //-------------------------------------------------------------------------- void LLScriptHandler::onDeleteToast(LLToast* toast) { - // send a signal to the counter manager - mDelNotificationSignal(); - // send a signal to a listener to let him perform some action // in this case listener is a SysWellWindow and it will remove a corresponding item from its list - mNotificationIDSignal(toast->getNotificationID()); - LLNotificationPtr notification = LLNotifications::getInstance()->find(toast->getNotificationID()); - if( notification && - (SCRIPT_DIALOG == notification->getName() || SCRIPT_DIALOG_GROUP == notification->getName()) ) + if( notification && notification->hasFormElements() && !notification->canShowToast()) { LLScriptFloaterManager::getInstance()->onRemoveNotification(notification->getID()); } -} - -//-------------------------------------------------------------------------- -void LLScriptHandler::onRejectToast(LLUUID& id) -{ - LLNotificationPtr notification = LLNotifications::instance().find(id); - if (notification - && LLNotificationManager::getInstance()->getHandlerForNotification( - notification->getType()) == this) - { - LLNotifications::instance().cancel(notification); - } } -//-------------------------------------------------------------------------- - diff --git a/indra/newview/llnotificationstorage.cpp b/indra/newview/llnotificationstorage.cpp index fb1adc7ddf..a31b95811e 100644 --- a/indra/newview/llnotificationstorage.cpp +++ b/indra/newview/llnotificationstorage.cpp @@ -84,9 +84,11 @@ bool LLPersistentNotificationStorage::onPersistentChannelChanged(const LLSD& pay return false; } +static LLFastTimer::DeclareTimer FTM_SAVE_NOTIFICATIONS("Save Notifications"); + void LLPersistentNotificationStorage::saveNotifications() { - // TODO - think about save optimization. + LLFastTimer _(FTM_SAVE_NOTIFICATIONS); llofstream notify_file(mFileName.c_str()); if (!notify_file.is_open()) @@ -98,10 +100,15 @@ void LLPersistentNotificationStorage::saveNotifications() LLSD output; LLSD& data = output["data"]; - LLNotificationChannelPtr history_channel = LLNotifications::instance().getChannel("Persistent"); - LLNotificationSet::iterator it = history_channel->begin(); + boost::intrusive_ptr<LLPersistentNotificationChannel> history_channel = boost::dynamic_pointer_cast<LLPersistentNotificationChannel>(LLNotifications::instance().getChannel("Persistent")); + if (!history_channel) + { + return; + } - for ( ; history_channel->end() != it; ++it) + for ( std::vector<LLNotificationPtr>::iterator it = history_channel->beginHistory(), end_it = history_channel->endHistory(); + it != end_it; + ++it) { LLNotificationPtr notification = *it; @@ -120,8 +127,11 @@ void LLPersistentNotificationStorage::saveNotifications() formatter->format(output, notify_file, LLSDFormatter::OPTIONS_PRETTY); } +static LLFastTimer::DeclareTimer FTM_LOAD_NOTIFICATIONS("Load Notifications"); + void LLPersistentNotificationStorage::loadNotifications() { + LLFastTimer _(FTM_LOAD_NOTIFICATIONS); LLResponderRegistry::registerResponders(); LLNotifications::instance().getChannel("Persistent")-> diff --git a/indra/newview/llnotificationtiphandler.cpp b/indra/newview/llnotificationtiphandler.cpp index e397cfa046..a293e6acb6 100644 --- a/indra/newview/llnotificationtiphandler.cpp +++ b/indra/newview/llnotificationtiphandler.cpp @@ -29,7 +29,7 @@ #include "llfloaterreg.h" #include "llnearbychat.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llnotificationhandler.h" #include "llnotifications.h" #include "lltoastnotifypanel.h" @@ -41,15 +41,13 @@ using namespace LLNotificationsUI; //-------------------------------------------------------------------------- -LLTipHandler::LLTipHandler(e_notification_type type, const LLSD& id) +LLTipHandler::LLTipHandler() +: LLSysHandler("NotificationTips", "notifytip") { - mType = type; - // Getting a Channel for our notifications LLScreenChannel* channel = LLChannelManager::getInstance()->createNotificationChannel(); if(channel) { - channel->setOnRejectToastCallback(boost::bind(&LLTipHandler::onRejectToast, this, _1)); mChannel = channel->getHandle(); } } @@ -68,102 +66,67 @@ void LLTipHandler::initChannel() } //-------------------------------------------------------------------------- -bool LLTipHandler::processNotification(const LLSD& notify) +bool LLTipHandler::processNotification(const LLNotificationPtr& notification) { if(mChannel.isDead()) { return false; } - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if(!notification) - return false; - // arrange a channel on a screen if(!mChannel.get()->getVisible()) { initChannel(); } - if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") - { // archive message in nearby chat - if (LLHandlerUtil::canLogToNearbyChat(notification)) - { - LLHandlerUtil::logToNearbyChat(notification, CHAT_SOURCE_SYSTEM); - - // don't show toast if Nearby Chat is opened - LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); - LLNearbyChatBar* nearby_chat_bar = LLNearbyChatBar::getInstance(); - if (!nearby_chat_bar->isMinimized() && nearby_chat_bar->getVisible() && nearby_chat->getVisible()) - { - return false; - } - } - - std::string session_name = notification->getPayload()["SESSION_NAME"]; - const std::string name = notification->getSubstitutions()["NAME"]; - if (session_name.empty()) - { - session_name = name; - } - LLUUID from_id = notification->getPayload()["from_id"]; - if (LLHandlerUtil::canLogToIM(notification)) - { - LLHandlerUtil::logToIM(IM_NOTHING_SPECIAL, session_name, name, - notification->getMessage(), from_id, from_id); - } - - if (LLHandlerUtil::canSpawnIMSession(notification)) - { - LLHandlerUtil::spawnIMSession(name, from_id); - } + if (notification->canLogToChat()) + { + LLHandlerUtil::logToNearbyChat(notification, CHAT_SOURCE_SYSTEM); - // don't spawn toast for inventory accepted/declined offers if respective IM window is open (EXT-5909) - if (!LLHandlerUtil::canSpawnToast(notification)) + // don't show toast if Nearby Chat is opened + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat"); + if (nearby_chat->isChatVisible()) { return false; } + } - LLToastPanel* notify_box = LLToastPanel::buidPanelFromNotification(notification); - - LLToast::Params p; - p.notif_id = notification->getID(); - p.notification = notification; - p.lifetime_secs = gSavedSettings.getS32("NotificationTipToastLifeTime"); - p.panel = notify_box; - p.is_tip = true; - p.can_be_stored = false; - - removeExclusiveNotifications(notification); - - LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); - if(channel) - channel->addToast(p); + std::string session_name = notification->getPayload()["SESSION_NAME"]; + const std::string name = notification->getSubstitutions()["NAME"]; + if (session_name.empty()) + { + session_name = name; } - else if (notify["sigtype"].asString() == "delete") + LLUUID from_id = notification->getPayload()["from_id"]; + if (notification->canLogToIM()) { - mChannel.get()->killToastByNotificationID(notification->getID()); + LLHandlerUtil::logToIM(IM_NOTHING_SPECIAL, session_name, name, + notification->getMessage(), from_id, from_id); } - return false; -} - -//-------------------------------------------------------------------------- -void LLTipHandler::onDeleteToast(LLToast* toast) -{ -} - -//-------------------------------------------------------------------------- -void LLTipHandler::onRejectToast(const LLUUID& id) -{ - LLNotificationPtr notification = LLNotifications::instance().find(id); + if (notification->canLogToIM() && notification->hasFormElements()) + { + LLHandlerUtil::spawnIMSession(name, from_id); + } - if (notification - && LLNotificationManager::getInstance()->getHandlerForNotification( - notification->getType()) == this) + if (notification->canLogToIM() && LLHandlerUtil::isIMFloaterOpened(notification)) { - LLNotifications::instance().cancel(notification); + return false; } + + LLToastPanel* notify_box = LLToastPanel::buidPanelFromNotification(notification); + + LLToast::Params p; + p.notif_id = notification->getID(); + p.notification = notification; + p.lifetime_secs = gSavedSettings.getS32("NotificationTipToastLifeTime"); + p.panel = notify_box; + p.is_tip = true; + p.can_be_stored = false; + + LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel.get()); + if(channel) + channel->addToast(p); + return false; } diff --git a/indra/newview/lloutputmonitorctrl.cpp b/indra/newview/lloutputmonitorctrl.cpp index 85626d8783..4a9a50d96a 100644 --- a/indra/newview/lloutputmonitorctrl.cpp +++ b/indra/newview/lloutputmonitorctrl.cpp @@ -28,6 +28,7 @@ #include "lloutputmonitorctrl.h" // library includes +#include "llfloaterreg.h" #include "llui.h" // viewer includes @@ -73,7 +74,8 @@ LLOutputMonitorCtrl::LLOutputMonitorCtrl(const LLOutputMonitorCtrl::Params& p) mSpeakerId(p.speaker_id), mIsAgentControl(false), mIsSwitchDirty(false), - mShouldSwitchOn(false) + mShouldSwitchOn(false), + mShowParticipantsSpeaking(false) { //static LLUIColor output_monitor_muted_color = LLUIColorTable::instance().getColor("OutputMonitorMutedColor", LLColor4::orange); //static LLUIColor output_monitor_overdriven_color = LLUIColorTable::instance().getColor("OutputMonitorOverdrivenColor", LLColor4::red); @@ -156,6 +158,24 @@ void LLOutputMonitorCtrl::draw() } } + if ((mPower == 0.f && !mIsTalking) && mShowParticipantsSpeaking) + { + std::set<LLUUID> participant_uuids; + LLVoiceClient::instance().getParticipantList(participant_uuids); + std::set<LLUUID>::const_iterator part_it = participant_uuids.begin(); + + F32 power = 0; + for (; part_it != participant_uuids.end(); ++part_it) + { + power = LLVoiceClient::instance().getCurrentPower(*part_it); + if (power) + { + mPower = power; + break; + } + } + } + LLPointer<LLUIImage> icon; if (mIsMuted) { @@ -241,7 +261,22 @@ void LLOutputMonitorCtrl::draw() gl_rect_2d(0, monh, monw, 0, sColorBound, FALSE); } -void LLOutputMonitorCtrl::setSpeakerId(const LLUUID& speaker_id, const LLUUID& session_id/* = LLUUID::null*/) +// virtual +BOOL LLOutputMonitorCtrl::handleMouseUp(S32 x, S32 y, MASK mask) +{ + if (mSpeakerId != gAgentID && !mShowParticipantsSpeaking) + { + LLFloaterReg::showInstance("floater_voice_volume", LLSD().with("avatar_id", mSpeakerId)); + } + else if(mShowParticipantsSpeaking) + { + LLFloaterReg::showInstance("chat_voice", LLSD()); + } + + return TRUE; +} + +void LLOutputMonitorCtrl::setSpeakerId(const LLUUID& speaker_id, const LLUUID& session_id/* = LLUUID::null*/, bool show_other_participants_speaking /* = false */) { if (speaker_id.isNull() && mSpeakerId.notNull()) { @@ -256,6 +291,7 @@ void LLOutputMonitorCtrl::setSpeakerId(const LLUUID& speaker_id, const LLUUID& s LLSpeakingIndicatorManager::unregisterSpeakingIndicator(mSpeakerId, this); } + mShowParticipantsSpeaking = show_other_participants_speaking; mSpeakerId = speaker_id; LLSpeakingIndicatorManager::registerSpeakingIndicator(mSpeakerId, this, session_id); diff --git a/indra/newview/lloutputmonitorctrl.h b/indra/newview/lloutputmonitorctrl.h index 2d23753d46..1fa6ef41f8 100644 --- a/indra/newview/lloutputmonitorctrl.h +++ b/indra/newview/lloutputmonitorctrl.h @@ -68,6 +68,7 @@ public: // llview overrides virtual void draw(); + virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); void setPower(F32 val); F32 getPower(F32 val) const { return mPower; } @@ -81,6 +82,8 @@ public: void setIsTalking(bool val) { mIsTalking = val; } + void setShowParticipantsSpeaking(bool show) { mShowParticipantsSpeaking = show; } + /** * Sets avatar UUID to interact with voice channel. * @@ -89,7 +92,7 @@ public: * If this parameter is set registered indicator will be shown only in voice channel * which has the same session id (EXT-5562). */ - void setSpeakerId(const LLUUID& speaker_id, const LLUUID& session_id = LLUUID::null); + void setSpeakerId(const LLUUID& speaker_id, const LLUUID& session_id = LLUUID::null, bool show_other_participants_speaking = false); //called by mute list virtual void onChange(); @@ -131,6 +134,7 @@ private: bool mIsAgentControl; bool mIsMuted; bool mIsTalking; + bool mShowParticipantsSpeaking; LLPointer<LLUIImage> mImageMute; LLPointer<LLUIImage> mImageOff; LLPointer<LLUIImage> mImageOn; diff --git a/indra/newview/llpanelblockedlist.cpp b/indra/newview/llpanelblockedlist.cpp index 5c85ec438c..7612af8f5e 100644 --- a/indra/newview/llpanelblockedlist.cpp +++ b/indra/newview/llpanelblockedlist.cpp @@ -30,15 +30,23 @@ // library include #include "llavatarname.h" +#include "llfiltereditor.h" #include "llfloater.h" #include "llfloaterreg.h" #include "llnotificationsutil.h" #include "llscrolllistctrl.h" +#include "llmenubutton.h" // project include +#include "llavatarlistitem.h" +#include "llblocklist.h" +#include "llblockedlistitem.h" #include "llfloateravatarpicker.h" #include "llfloatersidepanelcontainer.h" +#include "llinventorylistitem.h" +#include "llinventorymodel.h" #include "llsidetraypanelcontainer.h" +#include "llviewercontrol.h" static LLRegisterPanelClassWrapper<LLPanelBlockedList> t_panel_blocked_list("panel_block_list_sidetray"); @@ -54,26 +62,44 @@ const std::string BLOCKED_PARAM_NAME = "blocked_to_select"; LLPanelBlockedList::LLPanelBlockedList() : LLPanel() { - mCommitCallbackRegistrar.add("Block.ClickPick", boost::bind(&LLPanelBlockedList::onPickBtnClick, this)); - mCommitCallbackRegistrar.add("Block.ClickBlockByName", boost::bind(&LLPanelBlockedList::onBlockByNameClick, this)); - mCommitCallbackRegistrar.add("Block.ClickRemove", boost::bind(&LLPanelBlockedList::onRemoveBtnClick, this)); + mCommitCallbackRegistrar.add("Block.Action", boost::bind(&LLPanelBlockedList::onCustomAction, this, _2)); + mEnableCallbackRegistrar.add("Block.Check", boost::bind(&LLPanelBlockedList::isActionChecked, this, _2)); } -LLPanelBlockedList::~LLPanelBlockedList() +void LLPanelBlockedList::removePicker() { - LLMuteList::getInstance()->removeObserver(this); + if(mPicker.get()) + { + mPicker.get()->closeFloater(); + } } BOOL LLPanelBlockedList::postBuild() { - mBlockedList = getChild<LLScrollListCtrl>("blocked"); + mBlockedList = getChild<LLBlockList>("blocked"); mBlockedList->setCommitOnSelectionChange(TRUE); + this->setVisibleCallback(boost::bind(&LLPanelBlockedList::removePicker, this)); - childSetCommitCallback("back", boost::bind(&LLPanelBlockedList::onBackBtnClick, this), NULL); + switch (gSavedSettings.getU32("BlockPeopleSortOrder")) + { + case E_SORT_BY_NAME: + mBlockedList->sortByName(); + break; + + case E_SORT_BY_TYPE: + mBlockedList->sortByType(); + break; + } + + // Use the context menu of the Block list for the Block tab gear menu. + LLToggleableMenu* blocked_gear_menu = mBlockedList->getContextMenu(); + if (blocked_gear_menu) + { + getChild<LLMenuButton>("blocked_gear_btn")->setMenu(blocked_gear_menu, LLMenuButton::MP_BOTTOM_LEFT); + } - LLMuteList::getInstance()->addObserver(this); - - refreshBlockedList(); + getChild<LLButton>("unblock_btn")->setCommitCallback(boost::bind(&LLPanelBlockedList::unblockItem, this)); + getChild<LLFilterEditor>("blocked_filter_input")->setCommitCallback(boost::bind(&LLPanelBlockedList::onFilterEdit, this, _2)); return LLPanel::postBuild(); } @@ -94,93 +120,107 @@ void LLPanelBlockedList::onOpen(const LLSD& key) void LLPanelBlockedList::selectBlocked(const LLUUID& mute_id) { - mBlockedList->selectByID(mute_id); + mBlockedList->selectItemByUUID(mute_id); } void LLPanelBlockedList::showPanelAndSelect(const LLUUID& idToSelect) { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD().with(BLOCKED_PARAM_NAME, idToSelect)); + LLFloaterSidePanelContainer::showPanel("people", "panel_people", + LLSD().with("people_panel_tab_name", "blocked_panel").with(BLOCKED_PARAM_NAME, idToSelect)); } ////////////////////////////////////////////////////////////////////////// // Private Section ////////////////////////////////////////////////////////////////////////// -void LLPanelBlockedList::refreshBlockedList() +void LLPanelBlockedList::updateButtons() { - mBlockedList->deleteAllItems(); + bool hasSelected = NULL != mBlockedList->getSelectedItem(); + getChildView("unblock_btn")->setEnabled(hasSelected); +} - std::vector<LLMute> mutes = LLMuteList::getInstance()->getMutes(); - std::vector<LLMute>::iterator it; - for (it = mutes.begin(); it != mutes.end(); ++it) +void LLPanelBlockedList::unblockItem() +{ + LLBlockedListItem* item = mBlockedList->getBlockedItem(); + if (item) { - LLScrollListItem::Params item_p; - item_p.enabled(TRUE); - item_p.value(it->mID); // link UUID of blocked item with ScrollListItem - item_p.columns.add().column("item_name").value(it->mName);//.type("text"); - item_p.columns.add().column("item_type").value(it->getDisplayType());//.type("text").width(111); - - mBlockedList->addRow(item_p, ADD_BOTTOM); + LLMute mute(item->getUUID(), item->getName()); + LLMuteList::instance().remove(mute); } } -void LLPanelBlockedList::updateButtons() +void LLPanelBlockedList::onCustomAction(const LLSD& userdata) { - bool hasSelected = NULL != mBlockedList->getFirstSelected(); - getChildView("Unblock")->setEnabled(hasSelected); -} + const std::string command_name = userdata.asString(); - - -void LLPanelBlockedList::onBackBtnClick() -{ - LLSideTrayPanelContainer* parent = dynamic_cast<LLSideTrayPanelContainer*>(getParent()); - if(parent) + if ("block_obj_by_name" == command_name) { - parent->openPreviousPanel(); + blockObjectByName(); + } + else if ("block_res_by_name" == command_name) + { + blockResidentByName(); + } + else if ("sort_by_name" == command_name) + { + mBlockedList->sortByName(); + gSavedSettings.setU32("BlockPeopleSortOrder", E_SORT_BY_NAME); + } + else if ("sort_by_type" == command_name) + { + mBlockedList->sortByType(); + gSavedSettings.setU32("BlockPeopleSortOrder", E_SORT_BY_TYPE); } } -void LLPanelBlockedList::onRemoveBtnClick() +BOOL LLPanelBlockedList::isActionChecked(const LLSD& userdata) { - std::string name = mBlockedList->getSelectedItemLabel(); - LLUUID id = mBlockedList->getStringUUIDSelectedItem(); - LLMute mute(id, name); - - S32 last_selected = mBlockedList->getFirstSelectedIndex(); - if (LLMuteList::getInstance()->remove(mute)) + std::string item = userdata.asString(); + U32 sort_order = gSavedSettings.getU32("BlockPeopleSortOrder"); + + if ("sort_by_name" == item) + { + return E_SORT_BY_NAME == sort_order; + } + else if ("sort_by_type" == item) { - // Above removals may rebuild this dialog. - - if (last_selected == mBlockedList->getItemCount()) - { - // we were on the last item, so select the last item again - mBlockedList->selectNthItem(last_selected - 1); - } - else - { - // else select the item after the last item previously selected - mBlockedList->selectNthItem(last_selected); - } + return E_SORT_BY_TYPE == sort_order; } + + return false; } -void LLPanelBlockedList::onPickBtnClick() +void LLPanelBlockedList::blockResidentByName() { const BOOL allow_multiple = FALSE; const BOOL close_on_select = TRUE; - /*LLFloaterAvatarPicker* picker = */LLFloaterAvatarPicker::show(boost::bind(&LLPanelBlockedList::callbackBlockPicked, this, _1, _2), allow_multiple, close_on_select); - - // *TODO: mantipov: should LLFloaterAvatarPicker be closed when panel is closed? - // old Floater dependency is not enable in panel - // addDependentFloater(picker); + + LLView * button = findChild<LLButton>("plus_btn", TRUE); + LLFloater* root_floater = gFloaterView->getParentFloater(this); + LLFloaterAvatarPicker * picker = LLFloaterAvatarPicker::show(boost::bind(&LLPanelBlockedList::callbackBlockPicked, this, _1, _2), + allow_multiple, close_on_select, FALSE, root_floater->getName(), button); + + if (root_floater) + { + root_floater->addDependentFloater(picker); + } + + mPicker = picker->getHandle(); } -void LLPanelBlockedList::onBlockByNameClick() +void LLPanelBlockedList::blockObjectByName() { LLFloaterGetBlockedObjectName::show(&LLPanelBlockedList::callbackBlockByName); } +void LLPanelBlockedList::onFilterEdit(const std::string& search_string) +{ + std::string filter = search_string; + LLStringUtil::trimHead(filter); + + mBlockedList->setNameFilter(filter); +} + void LLPanelBlockedList::callbackBlockPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName> names) { if (names.empty() || ids.empty()) return; diff --git a/indra/newview/llpanelblockedlist.h b/indra/newview/llpanelblockedlist.h index 74ad82e32d..07f0437656 100644 --- a/indra/newview/llpanelblockedlist.h +++ b/indra/newview/llpanelblockedlist.h @@ -30,21 +30,15 @@ #include "llpanel.h" #include "llmutelist.h" #include "llfloater.h" -// #include <vector> -// class LLButton; -// class LLLineEditor; -// class LLMessageSystem; -// class LLUUID; class LLAvatarName; -class LLScrollListCtrl; +class LLBlockList; -class LLPanelBlockedList - : public LLPanel, public LLMuteListObserver +class LLPanelBlockedList : public LLPanel { public: LLPanelBlockedList(); - ~LLPanelBlockedList(); + ~LLPanelBlockedList(){}; virtual BOOL postBuild(); virtual void draw(); @@ -59,25 +53,33 @@ public: * If it is LLUUID::null, nothing will be selected. */ static void showPanelAndSelect(const LLUUID& idToSelect); - - // LLMuteListObserver callback interface implementation. - /* virtual */ void onChange() { refreshBlockedList();} private: - void refreshBlockedList(); + + typedef enum e_sort_oder{ + E_SORT_BY_NAME = 0, + E_SORT_BY_TYPE = 1, + } ESortOrder; + + void removePicker(); void updateButtons(); // UI callbacks - void onBackBtnClick(); - void onRemoveBtnClick(); - void onPickBtnClick(); - void onBlockByNameClick(); + void unblockItem(); + void blockResidentByName(); + void blockObjectByName(); + void onFilterEdit(const std::string& search_string); + + // List commnads + void onCustomAction(const LLSD& userdata); + BOOL isActionChecked(const LLSD& userdata); void callbackBlockPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName> names); static void callbackBlockByName(const std::string& text); private: - LLScrollListCtrl* mBlockedList; + LLBlockList* mBlockedList; + LLHandle<LLFloater> mPicker; }; //----------------------------------------------------------------------------- diff --git a/indra/newview/llpanelgroupinvite.cpp b/indra/newview/llpanelgroupinvite.cpp index 7a15d93181..36bd5d9b77 100644 --- a/indra/newview/llpanelgroupinvite.cpp +++ b/indra/newview/llpanelgroupinvite.cpp @@ -83,6 +83,7 @@ public: LLTextBox *mGroupName; std::string mOwnerWarning; std::string mAlreadyInGroup; + std::string mTooManySelected; bool mConfirmedOwnerInvite; void (*mCloseCallback)(void* data); @@ -185,6 +186,17 @@ void LLPanelGroupInvite::impl::submitInvitations() role_member_pairs[item->getUUID()] = role_id; } + const S32 MAX_GROUP_INVITES = 100; // Max invites per request. 100 to match server cap. + if (role_member_pairs.size() > MAX_GROUP_INVITES) + { + // Fail! + LLSD msg; + msg["MESSAGE"] = mTooManySelected; + LLNotificationsUtil::add("GenericAlert", msg); + (*mCloseCallback)(mCloseCallbackUserData); + return; + } + LLGroupMgr::getInstance()->sendGroupMemberInvites(mGroupID, role_member_pairs); if(already_in_group) @@ -289,11 +301,13 @@ void LLPanelGroupInvite::impl::callbackClickAdd(void* userdata) //Soon the avatar picker will be embedded into this panel //instead of being it's own separate floater. But that is next week. //This will do for now. -jwolk May 10, 2006 + LLView * button = panelp->findChild<LLButton>("add_button"); + LLFloater * root_floater = gFloaterView->getParentFloater(panelp); LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show( - boost::bind(impl::callbackAddUsers, _1, panelp->mImplementation), TRUE); + boost::bind(impl::callbackAddUsers, _1, panelp->mImplementation), TRUE, FALSE, FALSE, root_floater->getName(), button); if (picker) { - gFloaterView->getParentFloater(panelp)->addDependentFloater(picker); + root_floater->addDependentFloater(picker); } } } @@ -621,6 +635,7 @@ BOOL LLPanelGroupInvite::postBuild() mImplementation->mOwnerWarning = getString("confirm_invite_owner_str"); mImplementation->mAlreadyInGroup = getString("already_in_group"); + mImplementation->mTooManySelected = getString("invite_selection_too_large"); update(); diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp index eda0749cdb..389baa86cd 100644 --- a/indra/newview/llpanelimcontrolpanel.cpp +++ b/indra/newview/llpanelimcontrolpanel.cpp @@ -1,31 +1,30 @@ -/** +/** * @file llpanelavatar.cpp * @brief LLPanelAvatar and related class implementations * * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" - #include "llfloaterreg.h" #include "llpanelimcontrolpanel.h" @@ -39,393 +38,7 @@ #include "llavatarlist.h" #include "llparticipantlist.h" #include "llimview.h" -#include "llvoicechannel.h" #include "llspeakers.h" #include "lltrans.h" -void LLPanelChatControlPanel::onCallButtonClicked() -{ - gIMMgr->startCall(mSessionId); -} - -void LLPanelChatControlPanel::onEndCallButtonClicked() -{ - gIMMgr->endCall(mSessionId); -} - -void LLPanelChatControlPanel::onOpenVoiceControlsClicked() -{ - LLFloaterReg::showInstance("voice_controls"); -} - -void LLPanelChatControlPanel::onChange(EStatusType status, const std::string &channelURI, bool proximal) -{ - if(status == STATUS_JOINING || status == STATUS_LEFT_CHANNEL) - { - return; - } - - updateCallButton(); -} - -void LLPanelChatControlPanel::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state) -{ - updateButtons(new_state); -} - -void LLPanelChatControlPanel::updateCallButton() -{ - // hide/show call button - bool voice_enabled = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); - - LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(mSessionId); - - if (!session) - { - getChildView("call_btn")->setEnabled(false); - return; - } - - bool session_initialized = session->mSessionInitialized; - bool callback_enabled = session->mCallBackEnabled; - - BOOL enable_connect = session_initialized - && voice_enabled - && callback_enabled; - getChildView("call_btn")->setEnabled(enable_connect); -} - -void LLPanelChatControlPanel::updateButtons(LLVoiceChannel::EState state) -{ - bool is_call_started = state >= LLVoiceChannel::STATE_CALL_STARTED; - getChildView("end_call_btn_panel")->setVisible( is_call_started); - getChildView("voice_ctrls_btn_panel")->setVisible( is_call_started && findChild<LLView>("voice_ctrls_btn_panel")); - getChildView("call_btn_panel")->setVisible( ! is_call_started); - - getChildView("volume_ctrl_panel")->setVisible(state == LLVoiceChannel::STATE_CONNECTED); - - updateCallButton(); - -} - -LLPanelChatControlPanel::~LLPanelChatControlPanel() -{ - mVoiceChannelStateChangeConnection.disconnect(); - if(LLVoiceClient::instanceExists()) - { - LLVoiceClient::getInstance()->removeObserver(this); - } -} - -BOOL LLPanelChatControlPanel::postBuild() -{ - childSetAction("call_btn", boost::bind(&LLPanelChatControlPanel::onCallButtonClicked, this)); - childSetAction("end_call_btn", boost::bind(&LLPanelChatControlPanel::onEndCallButtonClicked, this)); - childSetAction("voice_ctrls_btn", boost::bind(&LLPanelChatControlPanel::onOpenVoiceControlsClicked, this)); - - LLVoiceClient::getInstance()->addObserver(this); - - return TRUE; -} - -void LLPanelChatControlPanel::setSessionId(const LLUUID& session_id) -{ - //Method is called twice for AdHoc and Group chat. Second time when server init reply received - mSessionId = session_id; - LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionId); - if(voice_channel) - { - mVoiceChannelStateChangeConnection = voice_channel->setStateChangedCallback(boost::bind(&LLPanelChatControlPanel::onVoiceChannelStateChanged, this, _1, _2)); - - //call (either p2p, group or ad-hoc) can be already in started state - updateButtons(voice_channel->getState()); - } -} - -LLPanelIMControlPanel::LLPanelIMControlPanel() -{ -} - -LLPanelIMControlPanel::~LLPanelIMControlPanel() -{ - LLAvatarTracker::instance().removeParticularFriendObserver(mAvatarID, this); -} - -BOOL LLPanelIMControlPanel::postBuild() -{ - childSetAction("view_profile_btn", boost::bind(&LLPanelIMControlPanel::onViewProfileButtonClicked, this)); - childSetAction("add_friend_btn", boost::bind(&LLPanelIMControlPanel::onAddFriendButtonClicked, this)); - - childSetAction("share_btn", boost::bind(&LLPanelIMControlPanel::onShareButtonClicked, this)); - childSetAction("teleport_btn", boost::bind(&LLPanelIMControlPanel::onTeleportButtonClicked, this)); - childSetAction("pay_btn", boost::bind(&LLPanelIMControlPanel::onPayButtonClicked, this)); - - childSetAction("mute_btn", boost::bind(&LLPanelIMControlPanel::onClickMuteVolume, this)); - childSetAction("block_btn", boost::bind(&LLPanelIMControlPanel::onClickBlock, this)); - childSetAction("unblock_btn", boost::bind(&LLPanelIMControlPanel::onClickUnblock, this)); - - getChild<LLUICtrl>("volume_slider")->setCommitCallback(boost::bind(&LLPanelIMControlPanel::onVolumeChange, this, _2)); - - getChildView("add_friend_btn")->setEnabled(!LLAvatarActions::isFriend(getChild<LLAvatarIconCtrl>("avatar_icon")->getAvatarId())); - - setFocusReceivedCallback(boost::bind(&LLPanelIMControlPanel::onFocusReceived, this)); - - return LLPanelChatControlPanel::postBuild(); -} - -void LLPanelIMControlPanel::draw() -{ - bool is_muted = LLMuteList::getInstance()->isMuted(mAvatarID); - - getChild<LLUICtrl>("block_btn_panel")->setVisible(!is_muted); - getChild<LLUICtrl>("unblock_btn_panel")->setVisible(is_muted); - - if (getChildView("volume_ctrl_panel")->getVisible()) - { - - bool is_muted_voice = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat); - - LLUICtrl* mute_btn = getChild<LLUICtrl>("mute_btn"); - mute_btn->setValue( is_muted_voice ); - - LLUICtrl* volume_slider = getChild<LLUICtrl>("volume_slider"); - volume_slider->setEnabled( !is_muted_voice ); - - F32 volume; - - if (is_muted_voice) - { - // it's clearer to display their volume as zero - volume = 0.f; - } - else - { - // actual volume - volume = LLVoiceClient::getInstance()->getUserVolume(mAvatarID); - } - volume_slider->setValue( (F64)volume ); - } - - LLPanelChatControlPanel::draw(); -} - -void LLPanelIMControlPanel::onClickMuteVolume() -{ - // By convention, we only display and toggle voice mutes, not all mutes - LLMuteList* mute_list = LLMuteList::getInstance(); - bool is_muted = mute_list->isMuted(mAvatarID, LLMute::flagVoiceChat); - - LLMute mute(mAvatarID, getChild<LLTextBox>("avatar_name")->getText(), LLMute::AGENT); - if (!is_muted) - { - mute_list->add(mute, LLMute::flagVoiceChat); - } - else - { - mute_list->remove(mute, LLMute::flagVoiceChat); - } -} - -void LLPanelIMControlPanel::onClickBlock() -{ - LLMute mute(mAvatarID, getChild<LLTextBox>("avatar_name")->getText(), LLMute::AGENT); - - LLMuteList::getInstance()->add(mute); -} - -void LLPanelIMControlPanel::onClickUnblock() -{ - LLMute mute(mAvatarID, getChild<LLTextBox>("avatar_name")->getText(), LLMute::AGENT); - - LLMuteList::getInstance()->remove(mute); -} - -void LLPanelIMControlPanel::onVolumeChange(const LLSD& data) -{ - F32 volume = (F32)data.asReal(); - LLVoiceClient::getInstance()->setUserVolume(mAvatarID, volume); -} - -void LLPanelIMControlPanel::onTeleportButtonClicked() -{ - LLAvatarActions::offerTeleport(mAvatarID); -} -void LLPanelIMControlPanel::onPayButtonClicked() -{ - LLAvatarActions::pay(mAvatarID); -} - -void LLPanelIMControlPanel::onViewProfileButtonClicked() -{ - LLAvatarActions::showProfile(mAvatarID); -} - -void LLPanelIMControlPanel::onAddFriendButtonClicked() -{ - LLAvatarIconCtrl* avatar_icon = getChild<LLAvatarIconCtrl>("avatar_icon"); - std::string full_name = avatar_icon->getFullName(); - LLAvatarActions::requestFriendshipDialog(mAvatarID, full_name); -} - -void LLPanelIMControlPanel::onShareButtonClicked() -{ - LLAvatarActions::share(mAvatarID); -} - -void LLPanelIMControlPanel::onFocusReceived() -{ - // Disable all the buttons (Call, Teleport, etc) if disconnected. - if (gDisconnected) - { - setAllChildrenEnabled(FALSE); - } -} - -void LLPanelIMControlPanel::setSessionId(const LLUUID& session_id) -{ - LLPanelChatControlPanel::setSessionId(session_id); - - LLIMModel& im_model = LLIMModel::instance(); - - LLAvatarTracker::instance().removeParticularFriendObserver(mAvatarID, this); - mAvatarID = im_model.getOtherParticipantID(session_id); - LLAvatarTracker::instance().addParticularFriendObserver(mAvatarID, this); - - // Disable "Add friend" button for friends. - getChildView("add_friend_btn")->setEnabled(!LLAvatarActions::isFriend(mAvatarID)); - - // Disable "Teleport" button if friend is offline - if(LLAvatarActions::isFriend(mAvatarID)) - { - getChildView("teleport_btn")->setEnabled(LLAvatarTracker::instance().isBuddyOnline(mAvatarID)); - } - - getChild<LLAvatarIconCtrl>("avatar_icon")->setValue(mAvatarID); - - // Disable most profile buttons if the participant is - // not really an SL avatar (e.g., an Avaline caller). - LLIMModel::LLIMSession* im_session = - im_model.findIMSession(session_id); - if( im_session && !im_session->mOtherParticipantIsAvatar ) - { - getChildView("view_profile_btn")->setEnabled(FALSE); - getChildView("add_friend_btn")->setEnabled(FALSE); - - getChildView("share_btn")->setEnabled(FALSE); - getChildView("teleport_btn")->setEnabled(FALSE); - getChildView("pay_btn")->setEnabled(FALSE); - - getChild<LLTextBox>("avatar_name")->setValue(im_session->mName); - getChild<LLTextBox>("avatar_name")->setToolTip(im_session->mName); - } - else - { - // If the participant is an avatar, fetch the currect name - gCacheName->get(mAvatarID, false, - boost::bind(&LLPanelIMControlPanel::onNameCache, this, _1, _2, _3)); - } -} - -//virtual -void LLPanelIMControlPanel::changed(U32 mask) -{ - getChildView("add_friend_btn")->setEnabled(!LLAvatarActions::isFriend(mAvatarID)); - - // Disable "Teleport" button if friend is offline - if(LLAvatarActions::isFriend(mAvatarID)) - { - getChildView("teleport_btn")->setEnabled(LLAvatarTracker::instance().isBuddyOnline(mAvatarID)); - } -} - -void LLPanelIMControlPanel::onNameCache(const LLUUID& id, const std::string& full_name, bool is_group) -{ - if ( id == mAvatarID ) - { - std::string avatar_name = full_name; - getChild<LLTextBox>("avatar_name")->setValue(avatar_name); - getChild<LLTextBox>("avatar_name")->setToolTip(avatar_name); - - bool is_linden = LLStringUtil::endsWith(full_name, " Linden"); - getChild<LLUICtrl>("mute_btn")->setEnabled( !is_linden); - } -} - -LLPanelGroupControlPanel::LLPanelGroupControlPanel(const LLUUID& session_id): -mParticipantList(NULL) -{ -} - -BOOL LLPanelGroupControlPanel::postBuild() -{ - childSetAction("group_info_btn", boost::bind(&LLPanelGroupControlPanel::onGroupInfoButtonClicked, this)); - - return LLPanelChatControlPanel::postBuild(); -} - -LLPanelGroupControlPanel::~LLPanelGroupControlPanel() -{ - delete mParticipantList; - mParticipantList = NULL; -} - -// virtual -void LLPanelGroupControlPanel::draw() -{ - // Need to resort the participant list if it's in sort by recent speaker order. - if (mParticipantList) - mParticipantList->update(); - LLPanelChatControlPanel::draw(); -} - -void LLPanelGroupControlPanel::onGroupInfoButtonClicked() -{ - LLGroupActions::show(mGroupID); -} - -void LLPanelGroupControlPanel::onSortMenuItemClicked(const LLSD& userdata) -{ - // TODO: Check this code when when sort order menu will be added. (EM) - if (false && !mParticipantList) - return; - - std::string chosen_item = userdata.asString(); - - if (chosen_item == "sort_name") - { - mParticipantList->setSortOrder(LLParticipantList::E_SORT_BY_NAME); - } - -} - -void LLPanelGroupControlPanel::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state) -{ - LLPanelChatControlPanel::onVoiceChannelStateChanged(old_state, new_state); - mParticipantList->setSpeakingIndicatorsVisible(new_state >= LLVoiceChannel::STATE_CALL_STARTED); -} - -void LLPanelGroupControlPanel::setSessionId(const LLUUID& session_id) -{ - LLPanelChatControlPanel::setSessionId(session_id); - - mGroupID = session_id; - - // for group and Ad-hoc chat we need to include agent into list - if(!mParticipantList) - { - LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(session_id); - mParticipantList = new LLParticipantList(speaker_manager, getChild<LLAvatarList>("speakers_list"), true,false); - } -} - - -LLPanelAdHocControlPanel::LLPanelAdHocControlPanel(const LLUUID& session_id):LLPanelGroupControlPanel(session_id) -{ -} - -BOOL LLPanelAdHocControlPanel::postBuild() -{ - //We don't need LLPanelGroupControlPanel::postBuild() to be executed as there is no group_info_btn at AdHoc chat - return LLPanelChatControlPanel::postBuild(); -} diff --git a/indra/newview/llpanelimcontrolpanel.h b/indra/newview/llpanelimcontrolpanel.h index bba847b5d4..02915ec4bb 100644 --- a/indra/newview/llpanelimcontrolpanel.h +++ b/indra/newview/llpanelimcontrolpanel.h @@ -28,14 +28,12 @@ #define LL_LLPANELIMCONTROLPANEL_H #include "llpanel.h" -#include "llvoicechannel.h" #include "llcallingcard.h" class LLParticipantList; -class LLPanelChatControlPanel +class LLPanelChatControlPanel : public LLPanel - , public LLVoiceClientStatusObserver { public: LLPanelChatControlPanel() : @@ -44,21 +42,6 @@ public: virtual BOOL postBuild(); - void onCallButtonClicked(); - void onEndCallButtonClicked(); - void onOpenVoiceControlsClicked(); - - // Implements LLVoiceClientStatusObserver::onChange() to enable the call - // button when voice is available - /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); - - virtual void onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state); - - void updateButtons(LLVoiceChannel::EState state); - - // Enables/disables call button depending on voice availability - void updateCallButton(); - virtual void setSessionId(const LLUUID& session_id); const LLUUID& getSessionId() { return mSessionId; } @@ -69,41 +52,6 @@ private: boost::signals2::connection mVoiceChannelStateChangeConnection; }; - -class LLPanelIMControlPanel : public LLPanelChatControlPanel, LLFriendObserver -{ -public: - LLPanelIMControlPanel(); - ~LLPanelIMControlPanel(); - - BOOL postBuild(); - - void setSessionId(const LLUUID& session_id); - - // LLFriendObserver trigger - virtual void changed(U32 mask); - -protected: - void onNameCache(const LLUUID& id, const std::string& full_name, bool is_group); - -private: - void onViewProfileButtonClicked(); - void onAddFriendButtonClicked(); - void onShareButtonClicked(); - void onTeleportButtonClicked(); - void onPayButtonClicked(); - void onFocusReceived(); - - void onClickMuteVolume(); - void onClickBlock(); - void onClickUnblock(); - /*virtual*/ void draw(); - void onVolumeChange(const LLSD& data); - - LLUUID mAvatarID; -}; - - class LLPanelGroupControlPanel : public LLPanelChatControlPanel { public: @@ -121,9 +69,7 @@ protected: LLParticipantList* mParticipantList; private: - void onGroupInfoButtonClicked(); void onSortMenuItemClicked(const LLSD& userdata); - /*virtual*/ void onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state); }; class LLPanelAdHocControlPanel : public LLPanelGroupControlPanel diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index 68a3b6d1cd..469656c33f 100644 --- a/indra/newview/llpanellandmarks.cpp +++ b/indra/newview/llpanellandmarks.cpp @@ -52,6 +52,7 @@ #include "llmenubutton.h" #include "llplacesinventorybridge.h" #include "llplacesinventorypanel.h" +#include "llplacesfolderview.h" #include "lltoggleablemenu.h" #include "llviewermenu.h" #include "llviewerregion.h" @@ -102,7 +103,7 @@ void LLCheckFolderState::doFolder(LLFolderViewFolder* folder) // Counting only folders that pass the filter. // The listener check allow us to avoid counting the folder view // object itself because it has no listener assigned. - if (folder->hasFilteredDescendants() && folder->getListener()) + if (folder->getViewModelItem()->descendantsPassedFilter()) { if (folder->isOpen()) { @@ -138,7 +139,7 @@ private: // virtual void LLOpenFolderByID::doFolder(LLFolderViewFolder* folder) { - if (folder->getListener() && folder->getListener()->getUUID() == mFolderID) + if (folder->getViewModelItem() && static_cast<LLFolderViewModelItemInventory*>(folder->getViewModelItem())->getUUID() == mFolderID) { if (!folder->isOpen()) { @@ -177,7 +178,7 @@ void LLLandmarksPanelObserver::changed(U32 mask) if (!mIsLibraryLandmarksOpen && library) { // Search for "Landmarks" folder in the Library and open it once on start up. See EXT-4827. - const LLUUID &landmarks_cat = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false, true); + const LLUUID &landmarks_cat = gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_LANDMARK, false); if (landmarks_cat.notNull()) { LLOpenFolderByID opener(landmarks_cat); @@ -247,10 +248,7 @@ void LLLandmarksPanel::onSearchEdit(const std::string& string) LLPlacesInventoryPanel* inventory_list = dynamic_cast<LLPlacesInventoryPanel*>(tab->getAccordionView()); if (NULL == inventory_list) continue; - if (inventory_list->getFilter()) - { - filter_list(inventory_list, string); - } + filter_list(inventory_list, string); } if (sFilterSubString != string) @@ -281,28 +279,21 @@ void LLLandmarksPanel::onShowOnMap() //virtual void LLLandmarksPanel::onShowProfile() { - LLFolderViewItem* cur_item = getCurSelectedItem(); + LLFolderViewModelItemInventory* cur_item = getCurSelectedViewModelItem(); if(!cur_item) return; - cur_item->getListener()->performAction(mCurrentSelectedList->getModel(),"about"); + cur_item->performAction(mCurrentSelectedList->getModel(),"about"); } // virtual void LLLandmarksPanel::onTeleport() { - LLFolderViewItem* current_item = getCurSelectedItem(); - if (!current_item) - { - llwarns << "There are no selected list. No actions are performed." << llendl; - return; - } - - LLFolderViewEventListener* listenerp = current_item->getListener(); - if (listenerp && listenerp->getInventoryType() == LLInventoryType::IT_LANDMARK) + LLFolderViewModelItemInventory* view_model_item = getCurSelectedViewModelItem(); + if (view_model_item && view_model_item->getInventoryType() == LLInventoryType::IT_LANDMARK) { - listenerp->openItem(); + view_model_item->openItem(); } } @@ -313,8 +304,7 @@ bool LLLandmarksPanel::isSingleItemSelected() if (mCurrentSelectedList != NULL) { - LLPlacesFolderView* root_view = - static_cast<LLPlacesFolderView*>(mCurrentSelectedList->getRootFolder()); + LLFolderView* root_view = mCurrentSelectedList->getRootFolder(); if (root_view->getSelectedCount() == 1) { @@ -360,7 +350,7 @@ void LLLandmarksPanel::onSelectorButtonClicked() LLFolderViewItem* cur_item = mFavoritesInventoryPanel->getRootFolder()->getCurSelectedItem(); if (!cur_item) return; - LLFolderViewEventListener* listenerp = cur_item->getListener(); + LLFolderViewModelItemInventory* listenerp = static_cast<LLFolderViewModelItemInventory*>(cur_item->getViewModelItem()); if (listenerp->getInventoryType() == LLInventoryType::IT_LANDMARK) { LLSD key; @@ -373,10 +363,7 @@ void LLLandmarksPanel::onSelectorButtonClicked() void LLLandmarksPanel::updateShowFolderState() { - if (!mLandmarksInventoryPanel->getFilter()) - return; - - bool show_all_folders = mLandmarksInventoryPanel->getRootFolder()->getFilterSubString().empty(); + bool show_all_folders = mLandmarksInventoryPanel->getFilterSubString().empty(); if (show_all_folders) { show_all_folders = category_has_descendents(mLandmarksInventoryPanel); @@ -417,8 +404,9 @@ void LLLandmarksPanel::setItemSelected(const LLUUID& obj_id, BOOL take_keyboard_ bool LLLandmarksPanel::isLandmarkSelected() const { - LLFolderViewItem* current_item = getCurSelectedItem(); - if(current_item && current_item->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK) + LLFolderViewModelItemInventory* current_item = getCurSelectedViewModelItem(); + + if(current_item && current_item->getInventoryType() == LLInventoryType::IT_LANDMARK) { return true; } @@ -440,10 +428,10 @@ bool LLLandmarksPanel::isReceivedFolderSelected() const void LLLandmarksPanel::doActionOnCurSelectedLandmark(LLLandmarkList::loaded_callback_t cb) { - LLFolderViewItem* cur_item = getCurSelectedItem(); - if(cur_item && cur_item->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK) + LLFolderViewModelItemInventory* cur_item = getCurSelectedViewModelItem(); + if(cur_item && cur_item->getInventoryType() == LLInventoryType::IT_LANDMARK) { - LLLandmark* landmark = LLLandmarkActions::getLandmark(cur_item->getListener()->getUUID(), cb); + LLLandmark* landmark = LLLandmarkActions::getLandmark(cur_item->getUUID(), cb); if (landmark) { cb(landmark); @@ -456,6 +444,17 @@ LLFolderViewItem* LLLandmarksPanel::getCurSelectedItem() const return mCurrentSelectedList ? mCurrentSelectedList->getRootFolder()->getCurSelectedItem() : NULL; } +LLFolderViewModelItemInventory* LLLandmarksPanel::getCurSelectedViewModelItem() const +{ + LLFolderViewItem* cur_item = getCurSelectedItem(); + if (cur_item) + { + return static_cast<LLFolderViewModelItemInventory*>(cur_item->getViewModelItem()); + } + return NULL; +} + + LLFolderViewItem* LLLandmarksPanel::selectItemInAccordionTab(LLPlacesInventoryPanel* inventory_list, const std::string& tab_name, const LLUUID& obj_id, @@ -466,7 +465,7 @@ LLFolderViewItem* LLLandmarksPanel::selectItemInAccordionTab(LLPlacesInventoryPa LLFolderView* root = inventory_list->getRootFolder(); - LLFolderViewItem* item = root->getItemByID(obj_id); + LLFolderViewItem* item = inventory_list->getItemByID(obj_id); if (!item) return NULL; @@ -508,12 +507,12 @@ void LLLandmarksPanel::processParcelInfo(const LLParcelData& parcel_data) // We have to make request to sever to get parcel_id and snaption_id. if(isLandmarkSelected()) { - LLFolderViewItem* cur_item = getCurSelectedItem(); + LLFolderViewModelItemInventory* cur_item = getCurSelectedViewModelItem(); if (!cur_item) return; - LLUUID id = cur_item->getListener()->getUUID(); + LLUUID id = cur_item->getUUID(); LLInventoryItem* inv_item = mCurrentSelectedList->getModel()->getItem(id); doActionOnCurSelectedLandmark(boost::bind( - &LLLandmarksPanel::doProcessParcelInfo, this, _1, cur_item, inv_item, parcel_data)); + &LLLandmarksPanel::doProcessParcelInfo, this, _1, getCurSelectedItem(), inv_item, parcel_data)); } } @@ -543,7 +542,7 @@ void LLLandmarksPanel::initFavoritesInventoryPanel() mFavoritesInventoryPanel = getChild<LLPlacesInventoryPanel>("favorites_list"); initLandmarksPanel(mFavoritesInventoryPanel); - mFavoritesInventoryPanel->getFilter()->setEmptyLookupMessage("FavoritesNoMatchingItems"); + mFavoritesInventoryPanel->getFilter().setEmptyLookupMessage("FavoritesNoMatchingItems"); initAccordion("tab_favorites", mFavoritesInventoryPanel, true); } @@ -554,12 +553,7 @@ void LLLandmarksPanel::initLandmarksInventoryPanel() initLandmarksPanel(mLandmarksInventoryPanel); - // Check if mLandmarksInventoryPanel is properly initialized and has a Filter created. - // In case of a dummy widget getFilter() will return NULL. - if (mLandmarksInventoryPanel->getFilter()) - { - mLandmarksInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS); - } + mLandmarksInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS); // subscribe to have auto-rename functionality while creating New Folder mLandmarksInventoryPanel->setSelectCallback(boost::bind(&LLInventoryPanel::onSelectionChange, mLandmarksInventoryPanel, _1, _2)); @@ -583,7 +577,7 @@ void LLLandmarksPanel::initLibraryInventoryPanel() initLandmarksPanel(mLibraryInventoryPanel); // We want to fetch only "Landmarks" category from the library. - const LLUUID &landmarks_cat = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false, true); + const LLUUID &landmarks_cat = gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_LANDMARK, false); if (landmarks_cat.notNull()) { LLInventoryModelBackgroundFetch::instance().start(landmarks_cat); @@ -595,12 +589,7 @@ void LLLandmarksPanel::initLibraryInventoryPanel() void LLLandmarksPanel::initLandmarksPanel(LLPlacesInventoryPanel* inventory_list) { - // In case of a dummy widget further we have no Folder View widget and no Filter, - // so further initialization leads to crash. - if (!inventory_list->getFilter()) - return; - - inventory_list->getFilter()->setEmptyLookupMessage("PlacesNoMatchingItems"); + inventory_list->getFilter().setEmptyLookupMessage("PlacesNoMatchingItems"); inventory_list->setFilterTypes(0x1 << LLInventoryType::IT_LANDMARK); inventory_list->setSelectCallback(boost::bind(&LLLandmarksPanel::onSelectionChange, this, inventory_list, _1, _2)); @@ -665,20 +654,20 @@ void LLLandmarksPanel::deselectOtherThan(const LLPlacesInventoryPanel* inventory { if (inventory_list != mFavoritesInventoryPanel) { - mFavoritesInventoryPanel->getRootFolder()->clearSelection(); + mFavoritesInventoryPanel->clearSelection(); } if (inventory_list != mLandmarksInventoryPanel) { - mLandmarksInventoryPanel->getRootFolder()->clearSelection(); + mLandmarksInventoryPanel->clearSelection(); } if (inventory_list != mMyInventoryPanel) { - mMyInventoryPanel->getRootFolder()->clearSelection(); + mMyInventoryPanel->clearSelection(); } if (inventory_list != mLibraryInventoryPanel) { - mLibraryInventoryPanel->getRootFolder()->clearSelection(); + mLibraryInventoryPanel->clearSelection(); } } @@ -731,14 +720,9 @@ void LLLandmarksPanel::onActionsButtonClick() { LLToggleableMenu* menu = mGearFolderMenu; - LLFolderViewItem* cur_item = NULL; if(mCurrentSelectedList) { - cur_item = mCurrentSelectedList->getRootFolder()->getCurSelectedItem(); - if(!cur_item) - return; - - LLFolderViewEventListener* listenerp = cur_item->getListener(); + LLFolderViewModelItemInventory* listenerp = getCurSelectedViewModelItem(); if(!listenerp) return; @@ -776,6 +760,9 @@ void LLLandmarksPanel::onTrashButtonClick() const void LLLandmarksPanel::onAddAction(const LLSD& userdata) const { + LLFolderViewModelItemInventory* view_model = getCurSelectedViewModelItem(); + LLFolderViewItem* item = getCurSelectedItem(); + std::string command_name = userdata.asString(); if("add_landmark" == command_name) { @@ -791,24 +778,24 @@ void LLLandmarksPanel::onAddAction(const LLSD& userdata) const } else if ("category" == command_name) { - LLFolderViewItem* item = getCurSelectedItem(); if (item && mCurrentSelectedList == mLandmarksInventoryPanel) { - LLFolderViewEventListener* folder_bridge = NULL; - if (item-> getListener()->getInventoryType() + LLFolderViewModelItem* folder_bridge = NULL; + + if (view_model->getInventoryType() == LLInventoryType::IT_LANDMARK) { // for a landmark get parent folder bridge - folder_bridge = item->getParentFolder()->getListener(); + folder_bridge = item->getParentFolder()->getViewModelItem(); } - else if (item-> getListener()->getInventoryType() + else if (view_model->getInventoryType() == LLInventoryType::IT_CATEGORY) { // for a folder get its own bridge - folder_bridge = item->getListener(); + folder_bridge = view_model; } - menu_create_inventory_item(mCurrentSelectedList->getRootFolder(), + menu_create_inventory_item(mCurrentSelectedList, dynamic_cast<LLFolderBridge*> (folder_bridge), LLSD( "category"), gInventory.findCategoryUUIDForType( LLFolderType::FT_LANDMARK)); @@ -816,7 +803,7 @@ void LLLandmarksPanel::onAddAction(const LLSD& userdata) const else { //in case My Landmarks tab is completely empty (thus cannot be determined as being selected) - menu_create_inventory_item(mLandmarksInventoryPanel->getRootFolder(), NULL, LLSD("category"), + menu_create_inventory_item(mLandmarksInventoryPanel, NULL, LLSD("category"), gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK)); if (mMyLandmarksAccordionTab) @@ -834,9 +821,9 @@ void LLLandmarksPanel::onClipboardAction(const LLSD& userdata) const std::string command_name = userdata.asString(); if("copy_slurl" == command_name) { - LLFolderViewItem* cur_item = getCurSelectedItem(); + LLFolderViewModelItemInventory* cur_item = getCurSelectedViewModelItem(); if(cur_item) - LLLandmarkActions::copySLURLtoClipboard(cur_item->getListener()->getUUID()); + LLLandmarkActions::copySLURLtoClipboard(cur_item->getUUID()); } else if ( "paste" == command_name) { @@ -848,7 +835,7 @@ void LLLandmarksPanel::onClipboardAction(const LLSD& userdata) const } else { - mCurrentSelectedList->getRootFolder()->doToSelected(mCurrentSelectedList->getModel(),command_name); + mCurrentSelectedList->doToSelected(command_name); } } @@ -893,7 +880,7 @@ void LLLandmarksPanel::onFoldingAction(const LLSD& userdata) { if(mCurrentSelectedList) { - mCurrentSelectedList->getRootFolder()->doToSelected(&gInventory, userdata); + mCurrentSelectedList->doToSelected(userdata); } } } @@ -915,8 +902,9 @@ bool LLLandmarksPanel::isActionEnabled(const LLSD& userdata) const { std::string command_name = userdata.asString(); - LLPlacesFolderView* root_folder_view = mCurrentSelectedList ? - static_cast<LLPlacesFolderView*>(mCurrentSelectedList->getRootFolder()) : NULL; + LLFolderView* root_folder_view = mCurrentSelectedList + ? mCurrentSelectedList->getRootFolder() + : NULL; if ("collapse_all" == command_name) { @@ -977,18 +965,13 @@ bool LLLandmarksPanel::isActionEnabled(const LLSD& userdata) const { if (!root_folder_view) return false; - std::set<LLUUID> selected_uuids = root_folder_view->getSelectionList(); + std::set<LLFolderViewItem*> selected_uuids = root_folder_view->getSelectionList(); // Allow to execute the command only if it can be applied to all selected items. - for (std::set<LLUUID>::const_iterator iter = selected_uuids.begin(); iter != selected_uuids.end(); ++iter) + for (std::set<LLFolderViewItem*>::const_iterator iter = selected_uuids.begin(); iter != selected_uuids.end(); ++iter) { - LLFolderViewItem* item = root_folder_view->getItemByID(*iter); + LLFolderViewItem* item = *iter; - // If no item is found it might be a folder id. - if (!item) - { - item = root_folder_view->getFolderByID(*iter); - } if (!item) return false; if (!canItemBeModified(command_name, item)) return false; @@ -1012,10 +995,10 @@ bool LLLandmarksPanel::isActionEnabled(const LLSD& userdata) const if ("show_on_map" == command_name) { - LLFolderViewItem* cur_item = root_folder_view->getCurSelectedItem(); + LLFolderViewModelItemInventory* cur_item = getCurSelectedViewModelItem(); if (!cur_item) return false; - LLViewerInventoryItem* inv_item = cur_item->getInventoryItem(); + LLViewerInventoryItem* inv_item = dynamic_cast<LLViewerInventoryItem*>(cur_item->getInventoryObject()); if (!inv_item) return false; LLUUID asset_uuid = inv_item->getAssetUUID(); @@ -1049,7 +1032,7 @@ bool LLLandmarksPanel::isActionEnabled(const LLSD& userdata) const { if (mCurrentSelectedList) { - std::set<LLUUID> selection = mCurrentSelectedList->getRootFolder()->getSelectionList(); + std::set<LLFolderViewItem*> selection = mCurrentSelectedList->getRootFolder()->getSelectionList(); if (!selection.empty()) { return ( 1 == selection.size() && !LLAgentPicksInfo::getInstance()->isPickLimitReached() ); @@ -1105,27 +1088,23 @@ void LLLandmarksPanel::onMenuVisibilityChange(LLUICtrl* ctrl, const LLSD& param) { const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); - std::set<LLUUID> selected_uuids = root_folder_view->getSelectionList(); + std::set<LLFolderViewItem*> selected_items = root_folder_view->getSelectionList(); // Iterate through selected items to find out if any of these items are in Trash // or all the items are in Trash category. - for (std::set<LLUUID>::const_iterator iter = selected_uuids.begin(); iter != selected_uuids.end(); ++iter) + for (std::set<LLFolderViewItem*>::const_iterator iter = selected_items.begin(); iter != selected_items.end(); ++iter) { - LLFolderViewItem* item = root_folder_view->getItemByID(*iter); + LLFolderViewItem* item = *iter; // If no item is found it might be a folder id. - if (!item) - { - item = root_folder_view->getFolderByID(*iter); - } if (!item) continue; - LLFolderViewEventListener* listenerp = item->getListener(); + LLFolderViewModelItemInventory* listenerp = static_cast<LLFolderViewModelItemInventory*>(item->getViewModelItem()); if(!listenerp) continue; // Trash category itself should not be included because it can't be // actually restored from trash. - are_all_items_in_trash &= listenerp->isItemInTrash() && *iter != trash_id; + are_all_items_in_trash &= listenerp->isItemInTrash() && listenerp->getUUID() != trash_id; // If there are any selected items in Trash including the Trash category itself // we show "Restore Item" in context menu and hide other irrelevant items. @@ -1164,7 +1143,7 @@ bool LLLandmarksPanel::canItemBeModified(const std::string& command_name, LLFold bool can_be_modified = false; // landmarks can be modified in any other accordion... - if (item->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK) + if (static_cast<LLFolderViewModelItemInventory*>(item->getViewModelItem())->getInventoryType() == LLInventoryType::IT_LANDMARK) { can_be_modified = true; @@ -1202,7 +1181,7 @@ bool LLLandmarksPanel::canItemBeModified(const std::string& command_name, LLFold if (can_be_modified) { - LLFolderViewEventListener* listenerp = item->getListener(); + LLFolderViewModelItemInventory* listenerp = static_cast<LLFolderViewModelItemInventory*>(item->getViewModelItem()); if ("cut" == command_name) { @@ -1262,8 +1241,9 @@ bool LLLandmarksPanel::handleDragAndDropToTrash(BOOL drop, EDragAndDropType carg LLInventoryItem* item = static_cast<LLInventoryItem*>(cargo_data); if (item) { - LLFolderViewItem* fv_item = (mCurrentSelectedList && mCurrentSelectedList->getRootFolder()) ? - mCurrentSelectedList->getRootFolder()->getItemByID(item->getUUID()) : NULL; + LLFolderViewItem* fv_item = mCurrentSelectedList + ? mCurrentSelectedList->getItemByID(item->getUUID()) + : NULL; if (fv_item) { @@ -1391,7 +1371,7 @@ void LLLandmarksPanel::doCreatePick(LLLandmark* landmark) static void filter_list(LLPlacesInventoryPanel* inventory_list, const std::string& string) { // When search is cleared, restore the old folder state. - if (!inventory_list->getRootFolder()->getFilterSubString().empty() && string == "") + if (!inventory_list->getFilterSubString().empty() && string == "") { inventory_list->setFilterSubString(LLStringUtil::null); // Re-open folders that were open before @@ -1405,7 +1385,7 @@ static void filter_list(LLPlacesInventoryPanel* inventory_list, const std::strin } // save current folder open state if no filter currently applied - if (inventory_list->getRootFolder()->getFilterSubString().empty()) + if (inventory_list->getFilterSubString().empty()) { inventory_list->saveFolderState(); } diff --git a/indra/newview/llpanellandmarks.h b/indra/newview/llpanellandmarks.h index b2f4e92473..aa5f69739d 100644 --- a/indra/newview/llpanellandmarks.h +++ b/indra/newview/llpanellandmarks.h @@ -44,6 +44,7 @@ class LLMenuGL; class LLToggleableMenu; class LLInventoryPanel; class LLPlacesInventoryPanel; +class LLFolderViewModelItemInventory; class LLLandmarksPanel : public LLPanelPlacesTab, LLRemoteParcelInfoObserver { @@ -87,6 +88,7 @@ protected: bool isReceivedFolderSelected() const; void doActionOnCurSelectedLandmark(LLLandmarkList::loaded_callback_t cb); LLFolderViewItem* getCurSelectedItem() const; + LLFolderViewModelItemInventory* getCurSelectedViewModelItem() const; /** * Selects item with "obj_id" in "inventory_list" and scrolls accordion diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index c11597f532..35cb3d59c5 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -47,12 +47,14 @@ #include "llresmgr.h" #include "llscrollcontainer.h" #include "llsdserialize.h" +#include "llsdparam.h" #include "llspinctrl.h" #include "lltoggleablemenu.h" #include "lltooldraganddrop.h" #include "llviewermenu.h" #include "llviewertexturelist.h" #include "llsidepanelinventory.h" +#include "llfolderview.h" const std::string FILTERS_FILENAME("filters.xml"); @@ -116,7 +118,7 @@ LLPanelMainInventory::LLPanelMainInventory(const LLPanel::Params& p) mCommitCallbackRegistrar.add("Inventory.ShowFilters", boost::bind(&LLPanelMainInventory::toggleFindOptions, this)); mCommitCallbackRegistrar.add("Inventory.ResetFilters", boost::bind(&LLPanelMainInventory::resetFilters, this)); mCommitCallbackRegistrar.add("Inventory.SetSortBy", boost::bind(&LLPanelMainInventory::setSortBy, this, _2)); - mCommitCallbackRegistrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars)); + mCommitCallbackRegistrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars, this)); mSavedFolderState = new LLSaveFolderState(); mSavedFolderState->setApply(FALSE); @@ -129,7 +131,7 @@ BOOL LLPanelMainInventory::postBuild() mFilterTabs = getChild<LLTabContainer>("inventory filter tabs"); mFilterTabs->setCommitCallback(boost::bind(&LLPanelMainInventory::onFilterSelected, this)); - //panel->getFilter()->markDefault(); + //panel->getFilter().markDefault(); // Set up the default inv. panel/filter settings. mActivePanel = getChild<LLInventoryPanel>("All Items"); @@ -137,7 +139,7 @@ BOOL LLPanelMainInventory::postBuild() { // "All Items" is the previous only view, so it gets the InventorySortOrder mActivePanel->setSortOrder(gSavedSettings.getU32(LLInventoryPanel::DEFAULT_SORT_ORDER)); - mActivePanel->getFilter()->markDefault(); + mActivePanel->getFilter().markDefault(); mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); mActivePanel->setSelectCallback(boost::bind(&LLPanelMainInventory::onSelectionChange, this, mActivePanel, _1, _2)); mResortActivePanel = true; @@ -148,7 +150,7 @@ BOOL LLPanelMainInventory::postBuild() recent_items_panel->setSinceLogoff(TRUE); recent_items_panel->setSortOrder(LLInventoryFilter::SO_DATE); recent_items_panel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); - recent_items_panel->getFilter()->markDefault(); + recent_items_panel->getFilter().markDefault(); recent_items_panel->setSelectCallback(boost::bind(&LLPanelMainInventory::onSelectionChange, this, recent_items_panel, _1, _2)); } @@ -167,11 +169,14 @@ BOOL LLPanelMainInventory::postBuild() // Note that the "All Items" settings do not persist. if(recent_items_panel) { - if(savedFilterState.has(recent_items_panel->getFilter()->getName())) + if(savedFilterState.has(recent_items_panel->getFilter().getName())) { LLSD recent_items = savedFilterState.get( - recent_items_panel->getFilter()->getName()); - recent_items_panel->getFilter()->fromLLSD(recent_items); + recent_items_panel->getFilter().getName()); + LLInventoryFilter::Params p; + LLParamSDParser parser; + parser.readSD(recent_items, p); + recent_items_panel->getFilter().fromParams(p); } } @@ -208,24 +213,28 @@ LLPanelMainInventory::~LLPanelMainInventory( void ) LLInventoryPanel* all_items_panel = getChild<LLInventoryPanel>("All Items"); if (all_items_panel) { - LLInventoryFilter* filter = all_items_panel->getFilter(); - if (filter) + LLSD filterState; + LLInventoryPanel::InventoryState p; + all_items_panel->getFilter().toParams(p.filter); + all_items_panel->getRootViewModel().getSorter().toParams(p.sort); + if (p.validateBlock(false)) { - LLSD filterState; - filter->toLLSD(filterState); - filterRoot[filter->getName()] = filterState; + LLParamSDParser().writeSD(filterState, p); + filterRoot[all_items_panel->getName()] = filterState; } } - LLInventoryPanel* recent_items_panel = getChild<LLInventoryPanel>("Recent Items"); - if (recent_items_panel) + LLInventoryPanel* panel = findChild<LLInventoryPanel>("Recent Items"); + if (panel) { - LLInventoryFilter* filter = recent_items_panel->getFilter(); - if (filter) + LLSD filterState; + LLInventoryPanel::InventoryState p; + panel->getFilter().toParams(p.filter); + panel->getRootViewModel().getSorter().toParams(p.sort); + if (p.validateBlock(false)) { - LLSD filterState; - filter->toLLSD(filterState); - filterRoot[filter->getName()] = filterState; + LLParamSDParser().writeSD(filterState, p); + filterRoot[panel->getName()] = filterState; } } @@ -285,7 +294,7 @@ BOOL LLPanelMainInventory::handleKeyHere(KEY key, MASK mask) void LLPanelMainInventory::doToSelected(const LLSD& userdata) { - getPanel()->getRootFolder()->doToSelected(&gInventory, userdata); + getPanel()->doToSelected(userdata); } void LLPanelMainInventory::closeAllFolders() @@ -306,13 +315,14 @@ void LLPanelMainInventory::newWindow() void LLPanelMainInventory::doCreate(const LLSD& userdata) { - menu_create_inventory_item(getPanel()->getRootFolder(), NULL, userdata); + reset_inventory_filter(); + menu_create_inventory_item(getPanel(), NULL, userdata); } void LLPanelMainInventory::resetFilters() { LLFloaterInventoryFinder *finder = getFinder(); - getActivePanel()->getFilter()->resetDefault(); + getActivePanel()->getFilter().resetDefault(); if (finder) { finder->updateElementsFromFilter(); @@ -417,7 +427,7 @@ void LLPanelMainInventory::onFilterEdit(const std::string& search_string ) } // save current folder open state if no filter currently applied - if (!mActivePanel->getRootFolder()->isFilterModified()) + if (!mActivePanel->getFilter().isNotDefault()) { mSavedFolderState->setApply(FALSE); mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); @@ -479,13 +489,13 @@ void LLPanelMainInventory::onFilterSelected() } setFilterSubString(mFilterSubString); - LLInventoryFilter* filter = mActivePanel->getFilter(); + LLInventoryFilter& filter = mActivePanel->getFilter(); LLFloaterInventoryFinder *finder = getFinder(); if (finder) { - finder->changeFilter(filter); + finder->changeFilter(&filter); } - if (filter->isActive()) + if (filter.isActive()) { // If our filter is active we may be the first thing requiring a fetch so we better start it here. LLInventoryModelBackgroundFetch::instance().start(); @@ -598,7 +608,7 @@ void LLPanelMainInventory::onFocusReceived() void LLPanelMainInventory::setFilterTextFromFilter() { - mFilterText = mActivePanel->getFilter()->getFilterText(); + mFilterText = mActivePanel->getFilter().getFilterText(); } void LLPanelMainInventory::toggleFindOptions() @@ -648,7 +658,7 @@ LLFloaterInventoryFinder* LLPanelMainInventory::getFinder() LLFloaterInventoryFinder::LLFloaterInventoryFinder(LLPanelMainInventory* inventory_view) : LLFloater(LLSD()), mPanelMainInventory(inventory_view), - mFilter(inventory_view->getPanel()->getFilter()) + mFilter(&inventory_view->getPanel()->getFilter()) { buildFromFile("floater_inventory_view_finder.xml"); updateElementsFromFilter(); @@ -961,7 +971,7 @@ void LLPanelMainInventory::onTrashButtonClick() void LLPanelMainInventory::onClipboardAction(const LLSD& userdata) { std::string command_name = userdata.asString(); - getActivePanel()->getRootFolder()->doToSelected(getActivePanel()->getModel(),command_name); + getActivePanel()->doToSelected(command_name); } void LLPanelMainInventory::saveTexture(const LLSD& userdata) @@ -972,7 +982,7 @@ void LLPanelMainInventory::saveTexture(const LLSD& userdata) return; } - const LLUUID& item_id = current_item->getListener()->getUUID(); + const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); LLPreviewTexture* preview_texture = LLFloaterReg::showTypedInstance<LLPreviewTexture>("preview_texture", LLSD(item_id), TAKE_FOCUS_YES); if (preview_texture) { @@ -1045,7 +1055,7 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata) { return; } - const LLUUID item_id = current_item->getListener()->getUUID(); + const LLUUID item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); LLViewerInventoryItem *item = gInventory.getItem(item_id); if (item) { @@ -1060,7 +1070,7 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata) { return; } - current_item->getListener()->performAction(getActivePanel()->getModel(), "goto"); + static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->performAction(getActivePanel()->getModel(), "goto"); } if (command_name == "find_links") @@ -1070,17 +1080,17 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata) { return; } - const LLUUID& item_id = current_item->getListener()->getUUID(); - const std::string &item_name = current_item->getListener()->getName(); + const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); + const std::string &item_name = current_item->getViewModelItem()->getName(); mFilterSubString = item_name; - LLInventoryFilter *filter = mActivePanel->getFilter(); - filter->setFilterSubString(item_name); + LLInventoryFilter &filter = mActivePanel->getFilter(); + filter.setFilterSubString(item_name); mFilterEditor->setText(item_name); mFilterEditor->setFocus(TRUE); - filter->setFilterUUID(item_id); - filter->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); - filter->setFilterLinks(LLInventoryFilter::FILTERLINK_ONLY_LINKS); + filter.setFilterUUID(item_id); + filter.setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); + filter.setFilterLinks(LLInventoryFilter::FILTERLINK_ONLY_LINKS); } } @@ -1089,11 +1099,11 @@ bool LLPanelMainInventory::isSaveTextureEnabled(const LLSD& userdata) LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); if (current_item) { - LLViewerInventoryItem *inv_item = current_item->getInventoryItem(); + LLViewerInventoryItem *inv_item = dynamic_cast<LLViewerInventoryItem*>(static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getInventoryObject()); if(inv_item) { bool can_save = inv_item->checkPermissionsSet(PERM_ITEM_UNRESTRICTED); - LLInventoryType::EType curr_type = current_item->getListener()->getInventoryType(); + LLInventoryType::EType curr_type = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getInventoryType(); return can_save && (curr_type == LLInventoryType::IT_TEXTURE || curr_type == LLInventoryType::IT_SNAPSHOT); } } @@ -1110,15 +1120,14 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata) if (root) { can_delete = TRUE; - std::set<LLUUID> selection_set = root->getSelectionList(); + std::set<LLFolderViewItem*> selection_set = root->getSelectionList(); if (selection_set.empty()) return FALSE; - for (std::set<LLUUID>::iterator iter = selection_set.begin(); + for (std::set<LLFolderViewItem*>::iterator iter = selection_set.begin(); iter != selection_set.end(); ++iter) { - const LLUUID &item_id = (*iter); - LLFolderViewItem *item = root->getItemByID(item_id); - const LLFolderViewEventListener *listener = item->getListener(); + LLFolderViewItem *item = *iter; + const LLFolderViewModelItemInventory *listener = static_cast<const LLFolderViewModelItemInventory*>(item->getViewModelItem()); llassert(listener); if (!listener) return FALSE; can_delete &= listener->isItemRemovable(); @@ -1136,7 +1145,7 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata) { LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); if (!current_item) return FALSE; - const LLUUID& item_id = current_item->getListener()->getUUID(); + const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); const LLViewerInventoryItem *item = gInventory.getItem(item_id); if (item && item->getIsLinkType() && !item->getIsBrokenLink()) { @@ -1148,11 +1157,11 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata) if (command_name == "find_links") { LLFolderView* root = getActivePanel()->getRootFolder(); - std::set<LLUUID> selection_set = root->getSelectionList(); + std::set<LLFolderViewItem*> selection_set = root->getSelectionList(); if (selection_set.size() != 1) return FALSE; LLFolderViewItem* current_item = root->getCurSelectedItem(); if (!current_item) return FALSE; - const LLUUID& item_id = current_item->getListener()->getUUID(); + const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); const LLInventoryObject *obj = gInventory.getObject(item_id); if (obj && !obj->getIsLinkType() && LLAssetType::lookupCanLink(obj->getType())) { @@ -1165,7 +1174,7 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata) { LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); if (!current_item) return FALSE; - const LLUUID& item_id = current_item->getListener()->getUUID(); + const LLUUID& item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); const LLViewerInventoryItem *item = gInventory.getItem(item_id); if (item && item->getIsBrokenLink()) { diff --git a/indra/newview/llpanelmarketplaceinbox.cpp b/indra/newview/llpanelmarketplaceinbox.cpp index 5d75375847..dcecce6fe4 100644 --- a/indra/newview/llpanelmarketplaceinbox.cpp +++ b/indra/newview/llpanelmarketplaceinbox.cpp @@ -94,14 +94,14 @@ LLInventoryPanel * LLPanelMarketplaceInbox::setupInventoryPanel() mInventoryPanel->setShape(inventory_placeholder_rect); // Set the sort order newest to oldest - mInventoryPanel->setSortOrder(LLInventoryFilter::SO_DATE); - mInventoryPanel->getFilter()->markDefault(); + mInventoryPanel->getFolderViewModel()->setSorter(LLInventoryFilter::SO_DATE); + mInventoryPanel->getFilter().markDefault(); // Set selection callback for proper update of inventory status buttons mInventoryPanel->setSelectCallback(boost::bind(&LLPanelMarketplaceInbox::onSelectionChange, this)); // Set up the note to display when the inbox is empty - mInventoryPanel->getFilter()->setEmptyLookupMessage("InventoryInboxNoItems"); + mInventoryPanel->getFilter().setEmptyLookupMessage("InventoryInboxNoItems"); // Hide the placeholder text inbox_inventory_placeholder->setVisible(FALSE); @@ -128,7 +128,6 @@ BOOL LLPanelMarketplaceInbox::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL dr U32 LLPanelMarketplaceInbox::getFreshItemCount() const { -#if SUPPORTING_FRESH_ITEM_COUNT // // NOTE: When turning this on, be sure to test the no inbox/outbox case because this code probably @@ -139,7 +138,7 @@ U32 LLPanelMarketplaceInbox::getFreshItemCount() const if (mInventoryPanel) { - const LLFolderViewFolder * inbox_folder = mInventoryPanel->getRootFolder(); + LLFolderViewFolder * inbox_folder = mInventoryPanel->getRootFolder(); if (inbox_folder) { @@ -174,9 +173,6 @@ U32 LLPanelMarketplaceInbox::getFreshItemCount() const } return fresh_item_count; -#else - return getTotalItemCount(); -#endif } U32 LLPanelMarketplaceInbox::getTotalItemCount() const @@ -231,7 +227,6 @@ void LLPanelMarketplaceInbox::draw() args["[NUM]"] = item_count_str; mInboxButton->setLabel(getString("InboxLabelWithArg", args)); -#if SUPPORTING_FRESH_ITEM_COUNT // set green text to fresh item count U32 fresh_item_count = getFreshItemCount(); mFreshCountCtrl->setVisible((fresh_item_count > 0)); @@ -240,9 +235,6 @@ void LLPanelMarketplaceInbox::draw() { mFreshCountCtrl->setTextArg("[NUM]", llformat("%d", fresh_item_count)); } -#else - mFreshCountCtrl->setVisible(FALSE); -#endif } else { diff --git a/indra/newview/llpanelmarketplaceinboxinventory.cpp b/indra/newview/llpanelmarketplaceinboxinventory.cpp index 678e4f2843..68aefa7fb7 100644 --- a/indra/newview/llpanelmarketplaceinboxinventory.cpp +++ b/indra/newview/llpanelmarketplaceinboxinventory.cpp @@ -29,7 +29,8 @@ #include "llpanelmarketplaceinboxinventory.h" #include "llfolderview.h" -#include "llfoldervieweventlistener.h" +#include "llfolderviewitem.h" +#include "llfolderviewmodel.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" #include "llpanellandmarks.h" @@ -53,82 +54,17 @@ static LLDefaultChildRegistry::Register<LLInboxFolderViewItem> r3("inbox_folder_ // LLInboxInventoryPanel::LLInboxInventoryPanel(const LLInboxInventoryPanel::Params& p) - : LLInventoryPanel(p) -{ -} +: LLInventoryPanel(p) +{} LLInboxInventoryPanel::~LLInboxInventoryPanel() -{ -} - -// virtual -void LLInboxInventoryPanel::buildFolderView(const LLInventoryPanel::Params& params) -{ - // Determine the root folder in case specified, and - // build the views starting with that folder. - - LLUUID root_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false, false); - - // leslie -- temporary HACK to work around sim not creating inbox with proper system folder type - if (root_id.isNull()) - { - std::string start_folder_name(params.start_folder()); - - LLInventoryModel::cat_array_t* cats; - LLInventoryModel::item_array_t* items; - - gInventory.getDirectDescendentsOf(gInventory.getRootFolderID(), cats, items); - - if (cats) - { - for (LLInventoryModel::cat_array_t::const_iterator cat_it = cats->begin(); cat_it != cats->end(); ++cat_it) - { - LLInventoryCategory* cat = *cat_it; - - if (cat->getName() == start_folder_name) - { - root_id = cat->getUUID(); - break; - } - } - } - - if (root_id == LLUUID::null) - { - llwarns << "No category found that matches inbox inventory panel start_folder: " << start_folder_name << llendl; - } - } - // leslie -- end temporary HACK - - if (root_id == LLUUID::null) - { - llwarns << "Inbox inventory panel has no root folder!" << llendl; - root_id = LLUUID::generateNewID(); - } - - LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(LLAssetType::AT_CATEGORY, - LLAssetType::AT_CATEGORY, - LLInventoryType::IT_CATEGORY, - this, - NULL, - root_id); - - mFolderRoot = createFolderView(new_listener, params.use_label_suffix()); -} +{} LLFolderViewFolder * LLInboxInventoryPanel::createFolderViewFolder(LLInvFVBridge * bridge) { LLInboxFolderViewFolder::Params params; params.name = bridge->getDisplayName(); - params.icon = bridge->getIcon(); - params.icon_open = bridge->getOpenIcon(); - - if (mShowItemLinkOverlays) // if false, then links show up just like normal items - { - params.icon_overlay = LLUI::getUIImage("Inv_Link"); - } - params.root = mFolderRoot; params.listener = bridge; params.tool_tip = params.name; @@ -141,14 +77,6 @@ LLFolderViewItem * LLInboxInventoryPanel::createFolderViewItem(LLInvFVBridge * b LLInboxFolderViewItem::Params params; params.name = bridge->getDisplayName(); - params.icon = bridge->getIcon(); - params.icon_open = bridge->getOpenIcon(); - - if (mShowItemLinkOverlays) // if false, then links show up just like normal items - { - params.icon_overlay = LLUI::getUIImage("Inv_Link"); - } - params.creation_date = bridge->getCreationDate(); params.root = mFolderRoot; params.listener = bridge; @@ -163,26 +91,40 @@ LLFolderViewItem * LLInboxInventoryPanel::createFolderViewItem(LLInvFVBridge * b // LLInboxFolderViewFolder::LLInboxFolderViewFolder(const Params& p) - : LLFolderViewFolder(p) - , LLBadgeOwner(getHandle()) - , mFresh(false) +: LLFolderViewFolder(p), + LLBadgeOwner(getHandle()), + mFresh(false) { -#if SUPPORTING_FRESH_ITEM_COUNT initBadgeParams(p.new_badge()); -#endif +} + +void LLInboxFolderViewFolder::addItem(LLFolderViewItem* item) +{ + LLFolderViewFolder::addItem(item); + + if(item) + { + LLInvFVBridge* itemBridge = static_cast<LLInvFVBridge*>(item->getViewModelItem()); + LLFolderBridge * bridge = static_cast<LLFolderBridge *>(getViewModelItem()); + bridge->updateHierarchyCreationDate(itemBridge->getCreationDate()); + } + + // Compute freshness if our parent is the root folder for the inbox + if (mParentFolder == mRoot) + { + computeFreshness(); + } } // virtual void LLInboxFolderViewFolder::draw() { -#if SUPPORTING_FRESH_ITEM_COUNT if (!badgeHasParent()) { addBadgeToParentPanel(); } setBadgeVisibility(mFresh); -#endif LLFolderViewFolder::draw(); } @@ -207,7 +149,7 @@ void LLInboxFolderViewFolder::computeFreshness() if (last_expansion_utc > 0) { - mFresh = (mCreationDate > last_expansion_utc); + mFresh = (static_cast<LLFolderViewModelItemInventory*>(getViewModelItem())->getCreationDate() > last_expansion_utc); #if DEBUGGING_FRESHNESS if (mFresh) @@ -229,16 +171,6 @@ void LLInboxFolderViewFolder::deFreshify() gSavedPerAccountSettings.setU32("LastInventoryInboxActivity", time_corrected()); } -void LLInboxFolderViewFolder::setCreationDate(time_t creation_date_utc) -{ - mCreationDate = creation_date_utc; - - if (mParentFolder == mRoot) - { - computeFreshness(); - } -} - // // LLInboxFolderViewItem Implementation // @@ -248,24 +180,18 @@ LLInboxFolderViewItem::LLInboxFolderViewItem(const Params& p) , LLBadgeOwner(getHandle()) , mFresh(false) { -#if SUPPORTING_FRESH_ITEM_COUNT initBadgeParams(p.new_badge()); -#endif } -BOOL LLInboxFolderViewItem::addToFolder(LLFolderViewFolder* folder, LLFolderView* root) +void LLInboxFolderViewItem::addToFolder(LLFolderViewFolder* folder) { - BOOL retval = LLFolderViewItem::addToFolder(folder, root); + LLFolderViewItem::addToFolder(folder); -#if SUPPORTING_FRESH_ITEM_COUNT // Compute freshness if our parent is the root folder for the inbox if (mParentFolder == mRoot) { computeFreshness(); } -#endif - - return retval; } BOOL LLInboxFolderViewItem::handleDoubleClick(S32 x, S32 y, MASK mask) @@ -278,14 +204,12 @@ BOOL LLInboxFolderViewItem::handleDoubleClick(S32 x, S32 y, MASK mask) // virtual void LLInboxFolderViewItem::draw() { -#if SUPPORTING_FRESH_ITEM_COUNT if (!badgeHasParent()) { addBadgeToParentPanel(); } setBadgeVisibility(mFresh); -#endif LLFolderViewItem::draw(); } @@ -303,7 +227,7 @@ void LLInboxFolderViewItem::computeFreshness() if (last_expansion_utc > 0) { - mFresh = (mCreationDate > last_expansion_utc); + mFresh = (static_cast<LLFolderViewModelItemInventory*>(getViewModelItem())->getCreationDate() > last_expansion_utc); #if DEBUGGING_FRESHNESS if (mFresh) diff --git a/indra/newview/llpanelmarketplaceinboxinventory.h b/indra/newview/llpanelmarketplaceinboxinventory.h index d6b827ee3e..c05e18c300 100644 --- a/indra/newview/llpanelmarketplaceinboxinventory.h +++ b/indra/newview/llpanelmarketplaceinboxinventory.h @@ -33,7 +33,6 @@ #include "llfolderviewitem.h" -#define SUPPORTING_FRESH_ITEM_COUNT 1 @@ -47,9 +46,6 @@ public: ~LLInboxInventoryPanel(); // virtual - void buildFolderView(const LLInventoryPanel::Params& params); - - // virtual LLFolderViewFolder * createFolderViewFolder(LLInvFVBridge * bridge); LLFolderViewItem * createFolderViewItem(LLInvFVBridge * bridge); }; @@ -63,13 +59,13 @@ public: Optional<LLBadge::Params> new_badge; Params() - : new_badge("new_badge") - { - } + : new_badge("new_badge") + {} }; LLInboxFolderViewFolder(const Params& p); + void addItem(LLFolderViewItem* item); void draw(); void selectItem(); @@ -81,8 +77,6 @@ public: bool isFresh() const { return mFresh; } protected: - void setCreationDate(time_t creation_date_utc); - bool mFresh; }; @@ -95,14 +89,13 @@ public: Optional<LLBadge::Params> new_badge; Params() - : new_badge("new_badge") - { - } + : new_badge("new_badge") + {} }; LLInboxFolderViewItem(const Params& p); - BOOL addToFolder(LLFolderViewFolder* folder, LLFolderView* root); + void addToFolder(LLFolderViewFolder* folder); BOOL handleDoubleClick(S32 x, S32 y, MASK mask); void draw(); diff --git a/indra/newview/llpanelmarketplaceoutboxinventory.cpp b/indra/newview/llpanelmarketplaceoutboxinventory.cpp deleted file mode 100644 index ff62cb23db..0000000000 --- a/indra/newview/llpanelmarketplaceoutboxinventory.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/** - * @file llpanelmarketplaceoutboxinventory.cpp - * @brief LLOutboxInventoryPanel class definition - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llpanelmarketplaceoutboxinventory.h" - -#include "llfolderview.h" -#include "llfoldervieweventlistener.h" -#include "llinventorybridge.h" -#include "llinventoryfunctions.h" -#include "llpanellandmarks.h" -#include "llplacesinventorybridge.h" -#include "lltrans.h" -#include "llviewerfoldertype.h" - - -// -// statics -// - -static LLDefaultChildRegistry::Register<LLOutboxInventoryPanel> r1("outbox_inventory_panel"); -static LLDefaultChildRegistry::Register<LLOutboxFolderViewFolder> r2("outbox_folder_view_folder"); - - -// -// LLOutboxInventoryPanel Implementation -// - -LLOutboxInventoryPanel::LLOutboxInventoryPanel(const LLOutboxInventoryPanel::Params& p) - : LLInventoryPanel(p) -{ -} - -LLOutboxInventoryPanel::~LLOutboxInventoryPanel() -{ -} - -// virtual -void LLOutboxInventoryPanel::buildFolderView(const LLInventoryPanel::Params& params) -{ - // Determine the root folder in case specified, and - // build the views starting with that folder. - - LLUUID root_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false); - - if (root_id == LLUUID::null) - { - llwarns << "Outbox inventory panel has no root folder!" << llendl; - root_id = LLUUID::generateNewID(); - } - - LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(LLAssetType::AT_CATEGORY, - LLAssetType::AT_CATEGORY, - LLInventoryType::IT_CATEGORY, - this, - NULL, - root_id); - - mFolderRoot = createFolderView(new_listener, params.use_label_suffix()); -} - -LLFolderViewFolder * LLOutboxInventoryPanel::createFolderViewFolder(LLInvFVBridge * bridge) -{ - LLOutboxFolderViewFolder::Params params; - - params.name = bridge->getDisplayName(); - params.icon = bridge->getIcon(); - params.icon_open = bridge->getOpenIcon(); - - if (mShowItemLinkOverlays) // if false, then links show up just like normal items - { - params.icon_overlay = LLUI::getUIImage("Inv_Link"); - } - - params.root = mFolderRoot; - params.listener = bridge; - params.tool_tip = params.name; - - return LLUICtrlFactory::create<LLOutboxFolderViewFolder>(params); -} - -LLFolderViewItem * LLOutboxInventoryPanel::createFolderViewItem(LLInvFVBridge * bridge) -{ - LLFolderViewItem::Params params; - - params.name = bridge->getDisplayName(); - params.icon = bridge->getIcon(); - params.icon_open = bridge->getOpenIcon(); - - if (mShowItemLinkOverlays) // if false, then links show up just like normal items - { - params.icon_overlay = LLUI::getUIImage("Inv_Link"); - } - - params.creation_date = bridge->getCreationDate(); - params.root = mFolderRoot; - params.listener = bridge; - params.rect = LLRect (0, 0, 0, 0); - params.tool_tip = params.name; - - return LLUICtrlFactory::create<LLOutboxFolderViewItem>(params); -} - -// -// LLOutboxFolderViewFolder Implementation -// - -LLOutboxFolderViewFolder::LLOutboxFolderViewFolder(const Params& p) - : LLFolderViewFolder(p) -{ -} - -// -// LLOutboxFolderViewItem Implementation -// - -LLOutboxFolderViewItem::LLOutboxFolderViewItem(const Params& p) - : LLFolderViewItem(p) -{ -} - -BOOL LLOutboxFolderViewItem::handleDoubleClick(S32 x, S32 y, MASK mask) -{ - return TRUE; -} - -void LLOutboxFolderViewItem::openItem() -{ - // Intentionally do nothing to block attaching items from the outbox -} - -// eof diff --git a/indra/newview/llpanelmarketplaceoutboxinventory.h b/indra/newview/llpanelmarketplaceoutboxinventory.h deleted file mode 100644 index a6c522b7c2..0000000000 --- a/indra/newview/llpanelmarketplaceoutboxinventory.h +++ /dev/null @@ -1,78 +0,0 @@ -/** - * @file llpanelmarketplaceoutboxinventory.h - * @brief LLOutboxInventoryPanel class declaration - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_OUTBOXINVENTORYPANEL_H -#define LL_OUTBOXINVENTORYPANEL_H - - -#include "llinventorypanel.h" -#include "llfolderviewitem.h" - - -class LLOutboxInventoryPanel : public LLInventoryPanel -{ -public: - struct Params : public LLInitParam::Block<Params, LLInventoryPanel::Params> - { - Params() {} - }; - - LLOutboxInventoryPanel(const Params& p); - ~LLOutboxInventoryPanel(); - - // virtual - void buildFolderView(const LLInventoryPanel::Params& params); - - // virtual - LLFolderViewFolder * createFolderViewFolder(LLInvFVBridge * bridge); - LLFolderViewItem * createFolderViewItem(LLInvFVBridge * bridge); -}; - - -class LLOutboxFolderViewFolder : public LLFolderViewFolder -{ -public: - struct Params : public LLInitParam::Block<Params, LLFolderViewFolder::Params> - { - Params() {} - }; - - LLOutboxFolderViewFolder(const Params& p); -}; - - -class LLOutboxFolderViewItem : public LLFolderViewItem -{ -public: - LLOutboxFolderViewItem(const Params& p); - - // virtual - BOOL handleDoubleClick(S32 x, S32 y, MASK mask); - void openItem(); -}; - - -#endif //LL_OUTBOXINVENTORYPANEL_H diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index 1ca24f3031..de12826452 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -71,12 +71,13 @@ /// Class LLTaskInvFVBridge ///---------------------------------------------------------------------------- -class LLTaskInvFVBridge : public LLFolderViewEventListener +class LLTaskInvFVBridge : public LLFolderViewModelItemInventory { protected: LLUUID mUUID; std::string mName; mutable std::string mDisplayName; + mutable std::string mSearchableName; LLPanelObjectInventory* mPanel; U32 mFlags; LLAssetType::EType mAssetType; @@ -102,26 +103,29 @@ public: S32 getPrice(); static bool commitBuyItem(const LLSD& notification, const LLSD& response); - // LLFolderViewEventListener functionality + // LLFolderViewModelItemInventory functionality virtual const std::string& getName() const; virtual const std::string& getDisplayName() const; + virtual const std::string& getSearchableName() const; + virtual PermissionMask getPermissionMask() const { return PERM_NONE; } /*virtual*/ LLFolderType::EType getPreferredType() const { return LLFolderType::FT_NONE; } virtual const LLUUID& getUUID() const { return mUUID; } virtual time_t getCreationDate() const; + virtual void setCreationDate(time_t creation_date_utc); + virtual LLUIImagePtr getIcon() const; virtual void openItem(); virtual BOOL canOpenItem() const { return FALSE; } virtual void closeItem() {} - virtual void previewItem(); virtual void selectItem() {} virtual BOOL isItemRenameable() const; virtual BOOL renameItem(const std::string& new_name); virtual BOOL isItemMovable() const; virtual BOOL isItemRemovable() const; virtual BOOL removeItem(); - virtual void removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch); - virtual void move(LLFolderViewEventListener* parent_listener); + virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch); + virtual void move(LLFolderViewModelItem* parent_listener); virtual BOOL isItemCopyable() const; virtual BOOL copyToClipboard() const; virtual BOOL cutToClipboard() const; @@ -131,11 +135,15 @@ public: virtual void buildContextMenu(LLMenuGL& menu, U32 flags); virtual void performAction(LLInventoryModel* model, std::string action); virtual BOOL isUpToDate() const { return TRUE; } - virtual BOOL hasChildren() const { return FALSE; } + virtual bool hasChildren() const { return FALSE; } virtual LLInventoryType::EType getInventoryType() const { return LLInventoryType::IT_NONE; } virtual LLWearableType::EType getWearableType() const { return LLWearableType::WT_NONE; } + virtual EInventorySortGroup getSortGroup() const { return SG_ITEM; } + virtual LLInventoryObject* getInventoryObject() const { return findInvObject(); } + // LLDragAndDropBridge functionality + virtual LLToolDragAndDrop::ESource getDragSource() const { return LLToolDragAndDrop::SOURCE_WORLD; } virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const; virtual BOOL dragOrDrop(MASK mask, BOOL drop, EDragAndDropType cargo_type, @@ -147,7 +155,8 @@ LLTaskInvFVBridge::LLTaskInvFVBridge( LLPanelObjectInventory* panel, const LLUUID& uuid, const std::string& name, - U32 flags): + U32 flags) +: LLFolderViewModelItemInventory(panel->getRootViewModel()), mUUID(uuid), mName(name), mPanel(panel), @@ -330,15 +339,27 @@ const std::string& LLTaskInvFVBridge::getDisplayName() const } } + mSearchableName.assign(mDisplayName + getLabelSuffix()); + return mDisplayName; } +const std::string& LLTaskInvFVBridge::getSearchableName() const +{ + return mSearchableName; +} + + // BUG: No creation dates for task inventory time_t LLTaskInvFVBridge::getCreationDate() const { return 0; } +void LLTaskInvFVBridge::setCreationDate(time_t creation_date_utc) +{} + + LLUIImagePtr LLTaskInvFVBridge::getIcon() const { const BOOL item_is_multi = (mFlags & LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS); @@ -352,11 +373,6 @@ void LLTaskInvFVBridge::openItem() lldebugs << "LLTaskInvFVBridge::openItem()" << llendl; } -void LLTaskInvFVBridge::previewItem() -{ - openItem(); -} - BOOL LLTaskInvFVBridge::isItemRenameable() const { if(gAgent.isGodlike()) return TRUE; @@ -467,7 +483,7 @@ BOOL LLTaskInvFVBridge::removeItem() return FALSE; } -void LLTaskInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch) +void LLTaskInvFVBridge::removeBatch(std::vector<LLFolderViewModelItem*>& batch) { if (!mPanel) { @@ -507,7 +523,7 @@ void LLTaskInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& } } -void LLTaskInvFVBridge::move(LLFolderViewEventListener* parent_listener) +void LLTaskInvFVBridge::move(LLFolderViewModelItem* parent_listener) { } @@ -709,7 +725,7 @@ public: virtual BOOL renameItem(const std::string& new_name); virtual BOOL isItemRemovable() const; virtual void buildContextMenu(LLMenuGL& menu, U32 flags); - virtual BOOL hasChildren() const; + virtual bool hasChildren() const; virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) const; virtual BOOL dragOrDrop(MASK mask, BOOL drop, EDragAndDropType cargo_type, @@ -717,6 +733,7 @@ public: std::string& tooltip_msg); virtual BOOL canOpenItem() const { return TRUE; } virtual void openItem(); + virtual EInventorySortGroup getSortGroup() const { return SG_NORMAL_FOLDER; } }; LLTaskCategoryBridge::LLTaskCategoryBridge( @@ -739,15 +756,7 @@ const std::string& LLTaskCategoryBridge::getDisplayName() const if (cat) { - // Localize "Contents" folder. - if (cat->getParentUUID().isNull() && cat->getName() == "Contents") - { - mDisplayName.assign(LLTrans::getString("ViewerObjectContents")); - } - else - { - mDisplayName.assign(cat->getName()); - } + mDisplayName.assign(cat->getName()); } return mDisplayName; @@ -775,7 +784,7 @@ void LLTaskCategoryBridge::buildContextMenu(LLMenuGL& menu, U32 flags) hide_context_entries(menu, items, disabled_items); } -BOOL LLTaskCategoryBridge::hasChildren() const +bool LLTaskCategoryBridge::hasChildren() const { // return TRUE if we have or do know know if we have children. // *FIX: For now, return FALSE - we will know for sure soon enough. @@ -1489,7 +1498,7 @@ LLPanelObjectInventory::LLPanelObjectInventory(const LLPanelObjectInventory::Par mCommitCallbackRegistrar.add("Inventory.DoCreate", boost::bind(&do_nothing)); mCommitCallbackRegistrar.add("Inventory.AttachObject", boost::bind(&do_nothing)); mCommitCallbackRegistrar.add("Inventory.BeginIMSession", boost::bind(&do_nothing)); - mCommitCallbackRegistrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars)); + mCommitCallbackRegistrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars, this)); } // Destroys the object @@ -1514,7 +1523,7 @@ BOOL LLPanelObjectInventory::postBuild() void LLPanelObjectInventory::doToSelected(const LLSD& userdata) { - mFolders->doToSelected(&gInventory, userdata); + LLInventoryAction::doToSelected(&gInventory, mFolders, userdata.asString()); } void LLPanelObjectInventory::clearContents() @@ -1526,6 +1535,8 @@ void LLPanelObjectInventory::clearContents() LLToolDragAndDrop::getInstance()->endDrag(); } + clearItemIDs(); + if( mScroller ) { // removes mFolders @@ -1541,21 +1552,24 @@ void LLPanelObjectInventory::reset() { clearContents(); - //setBorderVisible(FALSE); - mCommitCallbackRegistrar.pushScope(); // push local callbacks LLRect dummy_rect(0, 1, 1, 0); LLFolderView::Params p; p.name = "task inventory"; p.title = "task inventory"; - p.task_id = getTaskUUID(); p.parent_panel = this; p.tool_tip= LLTrans::getString("PanelContentsTooltip"); p.listener = LLTaskInvFVBridge::createObjectBridge(this, NULL); + p.folder_indentation = -14; // subtract space normally reserved for folder expanders + p.view_model = &mInventoryViewModel; + p.root = NULL; mFolders = LLUICtrlFactory::create<LLFolderView>(p); // this ensures that we never say "searching..." or "no items found" - mFolders->getFilter()->setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS); + //TODO RN: make this happen by manipulating filter object directly + LLInventoryFilter& inventoryFilter = dynamic_cast<LLInventoryFilter&>(mFolders->getFolderViewModel()->getFilter()); + inventoryFilter.setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS); + mFolders->setCallbackRegistrar(&mCommitCallbackRegistrar); if (hasFocus()) @@ -1600,7 +1614,7 @@ void LLPanelObjectInventory::inventoryChanged(LLViewerObject* object, iter != inventory->end(); ) { LLInventoryObject* item = *iter++; - LLFloaterProperties* floater = LLFloaterReg::findTypedInstance<LLFloaterProperties>("properites", item->getUUID()); + LLFloaterProperties* floater = LLFloaterReg::findTypedInstance<LLFloaterProperties>("properties", item->getUUID()); if(floater) { floater->refresh(); @@ -1615,15 +1629,20 @@ void LLPanelObjectInventory::updateInventory() // << " panel UUID: " << panel->mTaskUUID << "\n" // << " task UUID: " << object->mID << llendl; // We're still interested in this task's inventory. - std::set<LLUUID> selected_items; + std::vector<LLUUID> selected_item_ids; + std::set<LLFolderViewItem*> selected_items; BOOL inventory_has_focus = FALSE; - if (mHaveInventory) + if (mHaveInventory && mFolders) { selected_items = mFolders->getSelectionList(); inventory_has_focus = gFocusMgr.childHasKeyboardFocus(mFolders); } - - reset(); + for (std::set<LLFolderViewItem*>::iterator it = selected_items.begin(), end_it = selected_items.end(); + it != end_it; + ++it) + { + selected_item_ids.push_back(static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID()); + } LLViewerObject* objectp = gObjectList.findObject(mTaskUUID); if (objectp) @@ -1631,19 +1650,21 @@ void LLPanelObjectInventory::updateInventory() LLInventoryObject* inventory_root = objectp->getInventoryRoot(); LLInventoryObject::object_list_t contents; objectp->getInventoryContents(contents); + if (inventory_root) { - createFolderViews(inventory_root, contents); - mHaveInventory = TRUE; + reset(); mIsInventoryEmpty = FALSE; + createFolderViews(inventory_root, contents); mFolders->setEnabled(TRUE); } else { // TODO: create an empty inventory mIsInventoryEmpty = TRUE; - mHaveInventory = TRUE; } + + mHaveInventory = TRUE; } else { @@ -1653,11 +1674,12 @@ void LLPanelObjectInventory::updateInventory() } // restore previous selection - std::set<LLUUID>::iterator selection_it; - BOOL first_item = TRUE; - for (selection_it = selected_items.begin(); selection_it != selected_items.end(); ++selection_it) + std::vector<LLUUID>::iterator selection_it; + bool first_item = true; + for (selection_it = selected_item_ids.begin(); selection_it != selected_item_ids.end(); ++selection_it) { - LLFolderViewItem* selected_item = mFolders->getItemByID(*selection_it); + LLFolderViewItem* selected_item = getItemByID(*selection_it); + if (selected_item) { //HACK: "set" first item then "change" each other one to get keyboard focus right @@ -1673,7 +1695,10 @@ void LLPanelObjectInventory::updateInventory() } } - mFolders->requestArrange(); + if (mFolders) + { + mFolders->requestArrange(); + } mInventoryNeedsUpdate = FALSE; // Edit menu handler is set in onFocusReceived } @@ -1694,19 +1719,20 @@ void LLPanelObjectInventory::createFolderViews(LLInventoryObject* inventory_root bridge = LLTaskInvFVBridge::createObjectBridge(this, inventory_root); if(bridge) { - LLFolderViewFolder* new_folder = NULL; LLFolderViewFolder::Params p; p.name = inventory_root->getName(); - p.icon = LLUI::getUIImage("Inv_FolderClosed"); - p.icon_open = LLUI::getUIImage("Inv_FolderOpen"); + p.tool_tip = p.name; p.root = mFolders; p.listener = bridge; - p.tool_tip = p.name; - new_folder = LLUICtrlFactory::create<LLFolderViewFolder>(p); - new_folder->addToFolder(mFolders, mFolders); + + LLFolderViewFolder* new_folder = LLUICtrlFactory::create<LLFolderViewFolder>(p); + new_folder->addToFolder(mFolders); new_folder->toggleOpen(); - createViewsForCategory(&contents, inventory_root, new_folder); + if (!contents.empty()) + { + createViewsForCategory(&contents, inventory_root, new_folder); + } } } @@ -1738,8 +1764,6 @@ void LLPanelObjectInventory::createViewsForCategory(LLInventoryObject::object_li { LLFolderViewFolder::Params p; p.name = obj->getName(); - p.icon = LLUI::getUIImage("Inv_FolderClosed"); - p.icon_open = LLUI::getUIImage("Inv_FolderOpen"); p.root = mFolders; p.listener = bridge; p.tool_tip = p.name; @@ -1751,7 +1775,6 @@ void LLPanelObjectInventory::createViewsForCategory(LLInventoryObject::object_li { LLFolderViewItem::Params params; params.name(obj->getName()); - params.icon(bridge->getIcon()); params.creation_date(bridge->getCreationDate()); params.root(mFolders); params.listener(bridge); @@ -1759,7 +1782,8 @@ void LLPanelObjectInventory::createViewsForCategory(LLInventoryObject::object_li params.tool_tip = params.name; view = LLUICtrlFactory::create<LLFolderViewItem> (params); } - view->addToFolder(folder, mFolders); + view->addToFolder(folder); + addItemID(obj->getUUID(), view); } } @@ -1827,6 +1851,7 @@ void LLPanelObjectInventory::refresh() removeVOInventoryListener(); clearContents(); } + mInventoryViewModel.setTaskID(mTaskUUID); //llinfos << "LLPanelObjectInventory::refresh() " << mTaskUUID << llendl; } @@ -1914,7 +1939,10 @@ void LLPanelObjectInventory::idle(void* user_data) { LLPanelObjectInventory* self = (LLPanelObjectInventory*)user_data; - + if (self->mFolders) + { + self->mFolders->update(); + } if (self->mInventoryNeedsUpdate) { self->updateInventory(); @@ -1939,3 +1967,32 @@ void LLPanelObjectInventory::onFocusReceived() LLPanel::onFocusReceived(); } + + +LLFolderViewItem* LLPanelObjectInventory::getItemByID( const LLUUID& id ) +{ + std::map<LLUUID, LLFolderViewItem*>::iterator map_it; + map_it = mItemMap.find(id); + if (map_it != mItemMap.end()) + { + return map_it->second; + } + + return NULL; +} + +void LLPanelObjectInventory::removeItemID( const LLUUID& id ) +{ + mItemMap.erase(id); +} + +void LLPanelObjectInventory::addItemID( const LLUUID& id, LLFolderViewItem* itemp ) +{ + mItemMap[id] = itemp; +} + +void LLPanelObjectInventory::clearItemIDs() +{ + mItemMap.clear(); +} + diff --git a/indra/newview/llpanelobjectinventory.h b/indra/newview/llpanelobjectinventory.h index 607b705f7f..f497c695b3 100644 --- a/indra/newview/llpanelobjectinventory.h +++ b/indra/newview/llpanelobjectinventory.h @@ -29,6 +29,7 @@ #include "llvoinventorylistener.h" #include "llpanel.h" +#include "llinventorypanel.h" // for LLFolderViewModelInventory #include "llinventory.h" @@ -55,6 +56,8 @@ public: virtual BOOL postBuild(); + LLFolderViewModelInventory& getRootViewModel() { return mInventoryViewModel; } + void doToSelected(const LLSD& userdata); void refresh(); @@ -85,8 +88,15 @@ protected: LLInventoryObject* parent, LLFolderViewFolder* folder); void clearContents(); + LLFolderViewItem* getItemByID(const LLUUID& id); + + void addItemID( const LLUUID& id, LLFolderViewItem* itemp ); + void removeItemID(const LLUUID& id); + void clearItemIDs(); private: + std::map<LLUUID, LLFolderViewItem*> mItemMap; + LLScrollContainer* mScroller; LLFolderView* mFolders; @@ -94,6 +104,7 @@ private: BOOL mHaveInventory; BOOL mIsInventoryEmpty; BOOL mInventoryNeedsUpdate; + LLFolderViewModelInventory mInventoryViewModel; }; #endif // LL_LLPANELOBJECTINVENTORY_H diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index 35e2e96bab..d690a18477 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -270,7 +270,7 @@ private: if (inventory_panel->getVisible()) { - inventory_panel->setSortOrder(sort_order); + inventory_panel->getFolderViewModel()->setSorter(sort_order); } else { @@ -738,7 +738,7 @@ void LLPanelOutfitEdit::onSearchEdit(const std::string& string) } // save current folder open state if no filter currently applied - if (mInventoryItemsPanel->getRootFolder()->getFilterSubString().empty()) + if (mInventoryItemsPanel->getFilterSubString().empty()) { mSavedFolderState->setApply(FALSE); mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); @@ -885,13 +885,13 @@ LLPanelOutfitEdit::selection_info_t LLPanelOutfitEdit::getAddMorePanelSelectionT { if (mInventoryItemsPanel != NULL && mInventoryItemsPanel->getVisible()) { - std::set<LLUUID> selected_uuids = mInventoryItemsPanel->getRootFolder()->getSelectionList(); + std::set<LLFolderViewItem*> selected_items = mInventoryItemsPanel->getRootFolder()->getSelectionList(); - result.second = selected_uuids.size(); + result.second = selected_items.size(); if (result.second == 1) { - result.first = getWearableTypeByItemUUID(*(selected_uuids.begin())); + result.first = getWearableTypeByItemUUID(static_cast<LLFolderViewModelItemInventory*>((*selected_items.begin())->getViewModelItem())->getUUID()); } } else if (mWearableItemsList != NULL && mWearableItemsList->getVisible()) @@ -1310,7 +1310,7 @@ void LLPanelOutfitEdit::getCurrentItemUUID(LLUUID& selected_id) LLFolderViewItem* curr_item = mInventoryItemsPanel->getRootFolder()->getCurSelectedItem(); if (!curr_item) return; - LLFolderViewEventListener* listenerp = curr_item->getListener(); + LLFolderViewModelItemInventory* listenerp = static_cast<LLFolderViewModelItemInventory*>(curr_item->getViewModelItem()); if (!listenerp) return; selected_id = listenerp->getUUID(); @@ -1327,9 +1327,13 @@ void LLPanelOutfitEdit::getSelectedItemsUUID(uuid_vec_t& uuid_list) void (uuid_vec_t::* tmp)(LLUUID const &) = &uuid_vec_t::push_back; if (mInventoryItemsPanel->getVisible()) { - std::set<LLUUID> item_set = mInventoryItemsPanel->getRootFolder()->getSelectionList(); - - std::for_each(item_set.begin(), item_set.end(), boost::bind( tmp, &uuid_list, _1)); + std::set<LLFolderViewItem*> item_set = mInventoryItemsPanel->getRootFolder()->getSelectionList(); + for (std::set<LLFolderViewItem*>::iterator it = item_set.begin(), end_it = item_set.end(); + it != end_it; + ++it) + { + uuid_list.push_back(static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID()); + } } else if (mWearablesListViewPanel->getVisible()) { @@ -1374,13 +1378,13 @@ void LLPanelOutfitEdit::saveListSelection() { if(mWearablesListViewPanel->getVisible()) { - std::set<LLUUID> selected_ids = mInventoryItemsPanel->getRootFolder()->getSelectionList(); + std::set<LLFolderViewItem*> selected_ids = mInventoryItemsPanel->getRootFolder()->getSelectionList(); if(!selected_ids.size()) return; - for (std::set<LLUUID>::const_iterator item_id = selected_ids.begin(); item_id != selected_ids.end(); ++item_id) + for (std::set<LLFolderViewItem*>::const_iterator item_id = selected_ids.begin(); item_id != selected_ids.end(); ++item_id) { - mWearableItemsList->selectItemByUUID(*item_id, true); + mWearableItemsList->selectItemByUUID(static_cast<LLFolderViewModelItemInventory*>((*item_id)->getViewModelItem())->getUUID(), true); } mWearableItemsList->scrollToShowFirstSelectedItem(); } @@ -1398,7 +1402,7 @@ void LLPanelOutfitEdit::saveListSelection() for(std::vector<LLUUID>::const_iterator item_id = selected_ids.begin(); item_id != selected_ids.end(); ++item_id) { - LLFolderViewItem* item = root->getItemByID(*item_id); + LLFolderViewItem* item = mInventoryItemsPanel->getItemByID(*item_id); if (!item) continue; LLFolderViewFolder* parent = item->getParentFolder(); diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index f1380e7a36..90e857265d 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -72,6 +72,7 @@ static const std::string NEARBY_TAB_NAME = "nearby_panel"; static const std::string FRIENDS_TAB_NAME = "friends_panel"; static const std::string GROUP_TAB_NAME = "groups_panel"; static const std::string RECENT_TAB_NAME = "recent_panel"; +static const std::string BLOCKED_TAB_NAME = "blocked_panel"; // blocked avatars static const std::string COLLAPSED_BY_USER = "collapsed_by_user"; @@ -492,26 +493,37 @@ public: LLPanelPeople::LLPanelPeople() : LLPanel(), - mFilterSubString(LLStringUtil::null), - mFilterSubStringOrig(LLStringUtil::null), - mFilterEditor(NULL), mTabContainer(NULL), mOnlineFriendList(NULL), mAllFriendList(NULL), mNearbyList(NULL), mRecentList(NULL), mGroupList(NULL), - mNearbyGearButton(NULL), - mFriendsGearButton(NULL), - mGroupsGearButton(NULL), - mRecentGearButton(NULL), mMiniMap(NULL) { mFriendListUpdater = new LLFriendListUpdater(boost::bind(&LLPanelPeople::updateFriendList, this)); mNearbyListUpdater = new LLNearbyListUpdater(boost::bind(&LLPanelPeople::updateNearbyList, this)); mRecentListUpdater = new LLRecentListUpdater(boost::bind(&LLPanelPeople::updateRecentList, this)); mButtonsUpdater = new LLButtonsUpdater(boost::bind(&LLPanelPeople::updateButtons, this)); - mCommitCallbackRegistrar.add("People.addFriend", boost::bind(&LLPanelPeople::onAddFriendButtonClicked, this)); + + mCommitCallbackRegistrar.add("People.AddFriend", boost::bind(&LLPanelPeople::onAddFriendButtonClicked, this)); + mCommitCallbackRegistrar.add("People.AddFriendWizard", boost::bind(&LLPanelPeople::onAddFriendWizButtonClicked, this)); + mCommitCallbackRegistrar.add("People.DelFriend", boost::bind(&LLPanelPeople::onDeleteFriendButtonClicked, this)); + mCommitCallbackRegistrar.add("People.Group.Minus", boost::bind(&LLPanelPeople::onGroupMinusButtonClicked, this)); + mCommitCallbackRegistrar.add("People.Chat", boost::bind(&LLPanelPeople::onChatButtonClicked, this)); + mCommitCallbackRegistrar.add("People.Gear", boost::bind(&LLPanelPeople::onGearButtonClicked, this, _1)); + + mCommitCallbackRegistrar.add("People.Group.Plus.Action", boost::bind(&LLPanelPeople::onGroupPlusMenuItemClicked, this, _2)); + mCommitCallbackRegistrar.add("People.Friends.ViewSort.Action", boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemClicked, this, _2)); + mCommitCallbackRegistrar.add("People.Nearby.ViewSort.Action", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemClicked, this, _2)); + mCommitCallbackRegistrar.add("People.Groups.ViewSort.Action", boost::bind(&LLPanelPeople::onGroupsViewSortMenuItemClicked, this, _2)); + mCommitCallbackRegistrar.add("People.Recent.ViewSort.Action", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemClicked, this, _2)); + + mEnableCallbackRegistrar.add("People.Friends.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemCheck, this, _2)); + mEnableCallbackRegistrar.add("People.Recent.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemCheck, this, _2)); + mEnableCallbackRegistrar.add("People.Nearby.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemCheck, this, _2)); + + mEnableCallbackRegistrar.add("People.Group.Plus.Validate", boost::bind(&LLPanelPeople::onGroupPlusButtonValidate, this)); } LLPanelPeople::~LLPanelPeople() @@ -525,13 +537,6 @@ LLPanelPeople::~LLPanelPeople() { LLVoiceClient::getInstance()->removeObserver(this); } - - if (mGroupPlusMenuHandle.get()) mGroupPlusMenuHandle.get()->die(); - if (mNearbyViewSortMenuHandle.get()) mNearbyViewSortMenuHandle.get()->die(); - if (mNearbyViewSortMenuHandle.get()) mNearbyViewSortMenuHandle.get()->die(); - if (mGroupsViewSortMenuHandle.get()) mGroupsViewSortMenuHandle.get()->die(); - if (mRecentViewSortMenuHandle.get()) mRecentViewSortMenuHandle.get()->die(); - } void LLPanelPeople::onFriendsAccordionExpandedCollapsed(LLUICtrl* ctrl, const LLSD& param, LLAvatarList* avatar_list) @@ -551,17 +556,31 @@ void LLPanelPeople::onFriendsAccordionExpandedCollapsed(LLUICtrl* ctrl, const LL } } + +void LLPanelPeople::removePicker() +{ + if(mPicker.get()) + { + mPicker.get()->closeFloater(); + } +} + BOOL LLPanelPeople::postBuild() { - mFilterEditor = getChild<LLFilterEditor>("filter_input"); - mFilterEditor->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); + getChild<LLFilterEditor>("nearby_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); + getChild<LLFilterEditor>("friends_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); + getChild<LLFilterEditor>("groups_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); + getChild<LLFilterEditor>("recent_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); mTabContainer = getChild<LLTabContainer>("tabs"); mTabContainer->setCommitCallback(boost::bind(&LLPanelPeople::onTabSelected, this, _2)); + mSavedFilters.resize(mTabContainer->getTabCount()); + mSavedOriginalFilters.resize(mTabContainer->getTabCount()); LLPanel* friends_tab = getChild<LLPanel>(FRIENDS_TAB_NAME); // updater is active only if panel is visible to user. friends_tab->setVisibleCallback(boost::bind(&Updater::setActive, mFriendListUpdater, _2)); + friends_tab->setVisibleCallback(boost::bind(&LLPanelPeople::removePicker, this)); mOnlineFriendList = friends_tab->getChild<LLAvatarList>("avatars_online"); mAllFriendList = friends_tab->getChild<LLAvatarList>("avatars_all"); mOnlineFriendList->setNoItemsCommentText(getString("no_friends_online")); @@ -601,14 +620,6 @@ BOOL LLPanelPeople::postBuild() setSortOrder(mAllFriendList, (ESortOrder)gSavedSettings.getU32("FriendsSortOrder"), false); setSortOrder(mNearbyList, (ESortOrder)gSavedSettings.getU32("NearbyPeopleSortOrder"), false); - LLPanel* groups_panel = getChild<LLPanel>(GROUP_TAB_NAME); - groups_panel->childSetAction("activate_btn", boost::bind(&LLPanelPeople::onActivateButtonClicked, this)); - groups_panel->childSetAction("plus_btn", boost::bind(&LLPanelPeople::onGroupPlusButtonClicked, this)); - - LLPanel* friends_panel = getChild<LLPanel>(FRIENDS_TAB_NAME); - friends_panel->childSetAction("add_btn", boost::bind(&LLPanelPeople::onAddFriendWizButtonClicked, this)); - friends_panel->childSetAction("del_btn", boost::bind(&LLPanelPeople::onDeleteFriendButtonClicked, this)); - mOnlineFriendList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1)); mAllFriendList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1)); mNearbyList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1)); @@ -629,6 +640,19 @@ BOOL LLPanelPeople::postBuild() mGroupList->setCommitCallback(boost::bind(&LLPanelPeople::updateButtons, this)); mGroupList->setReturnCallback(boost::bind(&LLPanelPeople::onChatButtonClicked, this)); + LLMenuButton* groups_gear_btn = getChild<LLMenuButton>("groups_gear_btn"); + + // Use the context menu of the Groups list for the Groups tab gear menu. + LLToggleableMenu* groups_gear_menu = mGroupList->getContextMenu(); + if (groups_gear_menu) + { + groups_gear_btn->setMenu(groups_gear_menu, LLMenuButton::MP_BOTTOM_LEFT); + } + else + { + llwarns << "People->Groups list menu not found" << llendl; + } + LLAccordionCtrlTab* accordion_tab = getChild<LLAccordionCtrlTab>("tab_all"); accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLPanelPeople::onFriendsAccordionExpandedCollapsed, this, _1, _2, mAllFriendList)); @@ -637,70 +661,9 @@ BOOL LLPanelPeople::postBuild() accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLPanelPeople::onFriendsAccordionExpandedCollapsed, this, _1, _2, mOnlineFriendList)); - buttonSetAction("view_profile_btn", boost::bind(&LLPanelPeople::onViewProfileButtonClicked, this)); - buttonSetAction("group_info_btn", boost::bind(&LLPanelPeople::onGroupInfoButtonClicked, this)); - buttonSetAction("chat_btn", boost::bind(&LLPanelPeople::onChatButtonClicked, this)); - buttonSetAction("im_btn", boost::bind(&LLPanelPeople::onImButtonClicked, this)); - buttonSetAction("call_btn", boost::bind(&LLPanelPeople::onCallButtonClicked, this)); - buttonSetAction("group_call_btn", boost::bind(&LLPanelPeople::onGroupCallButtonClicked, this)); - buttonSetAction("teleport_btn", boost::bind(&LLPanelPeople::onTeleportButtonClicked, this)); - buttonSetAction("share_btn", boost::bind(&LLPanelPeople::onShareButtonClicked, this)); - // Must go after setting commit callback and initializing all pointers to children. mTabContainer->selectTabByName(NEARBY_TAB_NAME); - // Create menus. - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; - - registrar.add("People.Group.Plus.Action", boost::bind(&LLPanelPeople::onGroupPlusMenuItemClicked, this, _2)); - registrar.add("People.Group.Minus.Action", boost::bind(&LLPanelPeople::onGroupMinusButtonClicked, this)); - registrar.add("People.Friends.ViewSort.Action", boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemClicked, this, _2)); - registrar.add("People.Nearby.ViewSort.Action", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemClicked, this, _2)); - registrar.add("People.Groups.ViewSort.Action", boost::bind(&LLPanelPeople::onGroupsViewSortMenuItemClicked, this, _2)); - registrar.add("People.Recent.ViewSort.Action", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemClicked, this, _2)); - - enable_registrar.add("People.Group.Minus.Enable", boost::bind(&LLPanelPeople::isRealGroup, this)); - enable_registrar.add("People.Friends.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemCheck, this, _2)); - enable_registrar.add("People.Recent.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemCheck, this, _2)); - enable_registrar.add("People.Nearby.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemCheck, this, _2)); - - mNearbyGearButton = getChild<LLMenuButton>("nearby_view_sort_btn"); - mFriendsGearButton = getChild<LLMenuButton>("friends_viewsort_btn"); - mGroupsGearButton = getChild<LLMenuButton>("groups_viewsort_btn"); - mRecentGearButton = getChild<LLMenuButton>("recent_viewsort_btn"); - - LLMenuGL* plus_menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_group_plus.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - mGroupPlusMenuHandle = plus_menu->getHandle(); - - LLToggleableMenu* nearby_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_nearby_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if(nearby_view_sort) - { - mNearbyViewSortMenuHandle = nearby_view_sort->getHandle(); - mNearbyGearButton->setMenu(nearby_view_sort); - } - - LLToggleableMenu* friend_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_friends_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if(friend_view_sort) - { - mFriendsViewSortMenuHandle = friend_view_sort->getHandle(); - mFriendsGearButton->setMenu(friend_view_sort); - } - - LLToggleableMenu* group_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_groups_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if(group_view_sort) - { - mGroupsViewSortMenuHandle = group_view_sort->getHandle(); - mGroupsGearButton->setMenu(group_view_sort); - } - - LLToggleableMenu* recent_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_recent_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if(recent_view_sort) - { - mRecentViewSortMenuHandle = recent_view_sort->getHandle(); - mRecentGearButton->setMenu(recent_view_sort); - } - LLVoiceClient::getInstance()->addObserver(this); // call this method in case some list is empty and buttons can be in inconsistent state @@ -735,9 +698,11 @@ void LLPanelPeople::updateFriendListHelpText() if (no_friends_text->getVisible()) { //update help text for empty lists - std::string message_name = mFilterSubString.empty() ? "no_friends_msg" : "no_filtered_friends_msg"; + const std::string& filter = mSavedOriginalFilters[mTabContainer->getCurrentPanelIndex()]; + + std::string message_name = filter.empty() ? "no_friends_msg" : "no_filtered_friends_msg"; LLStringUtil::format_map_t args; - args["[SEARCH_TERM]"] = LLURI::escape(mFilterSubStringOrig); + args["[SEARCH_TERM]"] = LLURI::escape(filter); no_friends_text->setText(getString(message_name, args)); } } @@ -821,31 +786,9 @@ void LLPanelPeople::updateRecentList() mRecentList->setDirty(); } -void LLPanelPeople::buttonSetVisible(std::string btn_name, BOOL visible) -{ - // To make sure we're referencing the right widget (a child of the button bar). - LLButton* button = getChild<LLView>("button_bar")->getChild<LLButton>(btn_name); - button->setVisible(visible); -} - -void LLPanelPeople::buttonSetEnabled(const std::string& btn_name, bool enabled) -{ - // To make sure we're referencing the right widget (a child of the button bar). - LLButton* button = getChild<LLView>("button_bar")->getChild<LLButton>(btn_name); - button->setEnabled(enabled); -} - -void LLPanelPeople::buttonSetAction(const std::string& btn_name, const commit_signal_t::slot_type& cb) -{ - // To make sure we're referencing the right widget (a child of the button bar). - LLButton* button = getChild<LLView>("button_bar")->getChild<LLButton>(btn_name); - button->setClickedCallback(cb); -} - void LLPanelPeople::updateButtons() { std::string cur_tab = getActiveTabName(); - bool nearby_tab_active = (cur_tab == NEARBY_TAB_NAME); bool friends_tab_active = (cur_tab == FRIENDS_TAB_NAME); bool group_tab_active = (cur_tab == GROUP_TAB_NAME); //bool recent_tab_active = (cur_tab == RECENT_TAB_NAME); @@ -856,28 +799,15 @@ void LLPanelPeople::updateButtons() bool item_selected = (selected_uuids.size() == 1); bool multiple_selected = (selected_uuids.size() >= 1); - buttonSetVisible("group_info_btn", group_tab_active); - buttonSetVisible("chat_btn", group_tab_active); - buttonSetVisible("view_profile_btn", !group_tab_active); - buttonSetVisible("im_btn", !group_tab_active); - buttonSetVisible("call_btn", !group_tab_active); - buttonSetVisible("group_call_btn", group_tab_active); - buttonSetVisible("teleport_btn", friends_tab_active); - buttonSetVisible("share_btn", nearby_tab_active || friends_tab_active); - if (group_tab_active) { - bool cur_group_active = true; - if (item_selected) { selected_id = mGroupList->getSelectedUUID(); - cur_group_active = (gAgent.getGroupID() == selected_id); } LLPanel* groups_panel = mTabContainer->getCurrentPanel(); - groups_panel->getChildView("activate_btn")->setEnabled(item_selected && !cur_group_active); // "none" or a non-active group selected - groups_panel->getChildView("minus_btn")->setEnabled(item_selected && selected_id.notNull()); + groups_panel->getChildView("minus_btn")->setEnabled(item_selected && selected_id.notNull()); // a real group selected } else { @@ -893,26 +823,20 @@ void LLPanelPeople::updateButtons() LLPanel* cur_panel = mTabContainer->getCurrentPanel(); if (cur_panel) { - cur_panel->getChildView("add_friend_btn")->setEnabled(!is_friend); + if (cur_panel->hasChild("add_friend_btn", TRUE)) + cur_panel->getChildView("add_friend_btn")->setEnabled(item_selected && !is_friend); + if (friends_tab_active) { - cur_panel->getChildView("del_btn")->setEnabled(multiple_selected); + cur_panel->getChildView("friends_del_btn")->setEnabled(multiple_selected); + } + + if (!group_tab_active) + { + cur_panel->getChildView("gear_btn")->setEnabled(multiple_selected); } } } - - bool enable_calls = LLVoiceClient::getInstance()->isVoiceWorking() && LLVoiceClient::getInstance()->voiceEnabled(); - - buttonSetEnabled("view_profile_btn",item_selected); - buttonSetEnabled("share_btn", item_selected); - buttonSetEnabled("im_btn", multiple_selected); // allow starting the friends conference for multiple selection - buttonSetEnabled("call_btn", multiple_selected && enable_calls); - buttonSetEnabled("teleport_btn", multiple_selected && LLAvatarActions::canOfferTeleport(selected_uuids)); - - bool none_group_selected = item_selected && selected_id.isNull(); - buttonSetEnabled("group_info_btn", !none_group_selected); - buttonSetEnabled("group_call_btn", !none_group_selected && enable_calls); - buttonSetEnabled("chat_btn", !none_group_selected); } std::string LLPanelPeople::getActiveTabName() const @@ -943,6 +867,9 @@ LLUUID LLPanelPeople::getCurrentItemID() const if (cur_tab == GROUP_TAB_NAME) return mGroupList->getSelectedUUID(); + if (cur_tab == BLOCKED_TAB_NAME) + return LLUUID::null; // FIXME? + llassert(0 && "unknown tab selected"); return LLUUID::null; } @@ -963,6 +890,8 @@ void LLPanelPeople::getCurrentItemIDs(uuid_vec_t& selected_uuids) const mRecentList->getSelectedUUIDs(selected_uuids); else if (cur_tab == GROUP_TAB_NAME) mGroupList->getSelectedUUIDs(selected_uuids); + else if (cur_tab == BLOCKED_TAB_NAME) + selected_uuids.clear(); // FIXME? else llassert(0 && "unknown tab selected"); @@ -1031,49 +960,60 @@ void LLPanelPeople::setSortOrder(LLAvatarList* list, ESortOrder order, bool save } } -bool LLPanelPeople::isRealGroup() -{ - return getCurrentItemID() != LLUUID::null; -} - void LLPanelPeople::onFilterEdit(const std::string& search_string) { - mFilterSubStringOrig = search_string; - LLStringUtil::trimHead(mFilterSubStringOrig); + const S32 cur_tab_idx = mTabContainer->getCurrentPanelIndex(); + std::string& filter = mSavedOriginalFilters[cur_tab_idx]; + std::string& saved_filter = mSavedFilters[cur_tab_idx]; + + filter = search_string; + LLStringUtil::trimHead(filter); + // Searches are case-insensitive - std::string search_upper = mFilterSubStringOrig; + std::string search_upper = filter; LLStringUtil::toUpper(search_upper); - if (mFilterSubString == search_upper) + if (saved_filter == search_upper) return; - mFilterSubString = search_upper; + saved_filter = search_upper; - //store accordion tabs state before any manipulation with accordion tabs - if(!mFilterSubString.empty()) + // Apply new filter to the current tab. + const std::string cur_tab = getActiveTabName(); + if (cur_tab == NEARBY_TAB_NAME) + { + mNearbyList->setNameFilter(filter); + } + else if (cur_tab == FRIENDS_TAB_NAME) + { + // store accordion tabs opened/closed state before any manipulation with accordion tabs + if (!saved_filter.empty()) { notifyChildren(LLSD().with("action","store_state")); } - - // Apply new filter. - mNearbyList->setNameFilter(mFilterSubStringOrig); - mOnlineFriendList->setNameFilter(mFilterSubStringOrig); - mAllFriendList->setNameFilter(mFilterSubStringOrig); - mRecentList->setNameFilter(mFilterSubStringOrig); - mGroupList->setNameFilter(mFilterSubStringOrig); + mOnlineFriendList->setNameFilter(filter); + mAllFriendList->setNameFilter(filter); setAccordionCollapsedByUser("tab_online", false); setAccordionCollapsedByUser("tab_all", false); - showFriendsAccordionsIfNeeded(); - //restore accordion tabs state _after_ all manipulations... - if(mFilterSubString.empty()) + // restore accordion tabs state _after_ all manipulations + if(saved_filter.empty()) { notifyChildren(LLSD().with("action","restore_state")); } } + else if (cur_tab == GROUP_TAB_NAME) + { + mGroupList->setNameFilter(filter); + } + else if (cur_tab == RECENT_TAB_NAME) + { + mRecentList->setNameFilter(filter); + } +} void LLPanelPeople::onTabSelected(const LLSD& param) { @@ -1081,11 +1021,6 @@ void LLPanelPeople::onTabSelected(const LLSD& param) updateButtons(); showFriendsAccordionsIfNeeded(); - - if (GROUP_TAB_NAME == tab_name) - mFilterEditor->setLabel(getString("groups_filter_label")); - else - mFilterEditor->setLabel(getString("people_filter_label")); } void LLPanelPeople::onAvatarListDoubleClicked(LLUICtrl* ctrl) @@ -1127,12 +1062,6 @@ void LLPanelPeople::onAvatarListCommitted(LLAvatarList* list) updateButtons(); } -void LLPanelPeople::onViewProfileButtonClicked() -{ - LLUUID id = getCurrentItemID(); - LLAvatarActions::showProfile(id); -} - void LLPanelPeople::onAddFriendButtonClicked() { LLUUID id = getCurrentItemID(); @@ -1160,8 +1089,12 @@ bool LLPanelPeople::isItemsFreeOfFriends(const uuid_vec_t& uuids) void LLPanelPeople::onAddFriendWizButtonClicked() { + LLPanel* cur_panel = mTabContainer->getCurrentPanel(); + LLView * button = cur_panel->findChild<LLButton>("friends_add_btn", TRUE); + // Show add friend wizard. - LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLPanelPeople::onAvatarPicked, _1, _2), FALSE, TRUE); + LLFloater* root_floater = gFloaterView->getParentFloater(this); + LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLPanelPeople::onAvatarPicked, _1, _2), FALSE, TRUE, FALSE, root_floater->getName(), button); if (!picker) { return; @@ -1169,11 +1102,13 @@ void LLPanelPeople::onAddFriendWizButtonClicked() // Need to disable 'ok' button when friend occurs in selection picker->setOkBtnEnableCb(boost::bind(&LLPanelPeople::isItemsFreeOfFriends, this, _1)); - LLFloater* root_floater = gFloaterView->getParentFloater(this); + if (root_floater) { root_floater->addDependentFloater(picker); } + + mPicker = picker->getHandle(); } void LLPanelPeople::onDeleteFriendButtonClicked() @@ -1191,11 +1126,6 @@ void LLPanelPeople::onDeleteFriendButtonClicked() } } -void LLPanelPeople::onGroupInfoButtonClicked() -{ - LLGroupActions::show(getCurrentItemID()); -} - void LLPanelPeople::onChatButtonClicked() { LLUUID group_id = getCurrentItemID(); @@ -1203,6 +1133,14 @@ void LLPanelPeople::onChatButtonClicked() LLGroupActions::startIM(group_id); } +void LLPanelPeople::onGearButtonClicked(LLUICtrl* btn) +{ + uuid_vec_t selected_uuids; + getCurrentItemIDs(selected_uuids); + // Spawn at bottom left corner of the button. + LLPanelPeopleMenus::gNearbyMenu.show(btn, selected_uuids, 0, 0); +} + void LLPanelPeople::onImButtonClicked() { uuid_vec_t selected_uuids; @@ -1219,11 +1157,6 @@ void LLPanelPeople::onImButtonClicked() } } -void LLPanelPeople::onActivateButtonClicked() -{ - LLGroupActions::activate(mGroupList->getSelectedUUID()); -} - // static void LLPanelPeople::onAvatarPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName> names) { @@ -1231,19 +1164,15 @@ void LLPanelPeople::onAvatarPicked(const uuid_vec_t& ids, const std::vector<LLAv LLAvatarActions::requestFriendshipDialog(ids[0], names[0].getCompleteName()); } -void LLPanelPeople::onGroupPlusButtonClicked() +bool LLPanelPeople::onGroupPlusButtonValidate() { if (!gAgent.canJoinGroups()) { LLNotificationsUtil::add("JoinedTooManyGroups"); - return; + return false; } - LLMenuGL* plus_menu = (LLMenuGL*)mGroupPlusMenuHandle.get(); - if (!plus_menu) - return; - - showGroupMenu(plus_menu); + return true; } void LLPanelPeople::onGroupMinusButtonClicked() @@ -1288,10 +1217,6 @@ void LLPanelPeople::onFriendsViewSortMenuItemClicked(const LLSD& userdata) mAllFriendList->showPermissions(show_permissions); mOnlineFriendList->showPermissions(show_permissions); } - else if (chosen_item == "panel_block_list_sidetray") - { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); - } } void LLPanelPeople::onGroupsViewSortMenuItemClicked(const LLSD& userdata) @@ -1324,10 +1249,6 @@ void LLPanelPeople::onNearbyViewSortMenuItemClicked(const LLSD& userdata) { setSortOrder(mNearbyList, E_SORT_BY_DISTANCE); } - else if (chosen_item == "panel_block_list_sidetray") - { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); - } } bool LLPanelPeople::onNearbyViewSortMenuItemCheck(const LLSD& userdata) @@ -1361,10 +1282,6 @@ void LLPanelPeople::onRecentViewSortMenuItemClicked(const LLSD& userdata) { mRecentList->toggleIcons(); } - else if (chosen_item == "panel_block_list_sidetray") - { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); - } } bool LLPanelPeople::onFriendsViewSortMenuItemCheck(const LLSD& userdata) @@ -1393,40 +1310,6 @@ bool LLPanelPeople::onRecentViewSortMenuItemCheck(const LLSD& userdata) return false; } -void LLPanelPeople::onCallButtonClicked() -{ - uuid_vec_t selected_uuids; - getCurrentItemIDs(selected_uuids); - - if (selected_uuids.size() == 1) - { - // initiate a P2P voice chat with the selected user - LLAvatarActions::startCall(getCurrentItemID()); - } - else if (selected_uuids.size() > 1) - { - // initiate an ad-hoc voice chat with multiple users - LLAvatarActions::startAdhocCall(selected_uuids); - } -} - -void LLPanelPeople::onGroupCallButtonClicked() -{ - LLGroupActions::startCall(getCurrentItemID()); -} - -void LLPanelPeople::onTeleportButtonClicked() -{ - uuid_vec_t selected_uuids; - getCurrentItemIDs(selected_uuids); - LLAvatarActions::offerTeleport(selected_uuids); -} - -void LLPanelPeople::onShareButtonClicked() -{ - LLAvatarActions::share(getCurrentItemID()); -} - void LLPanelPeople::onMoreButtonClicked() { // *TODO: not implemented yet diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h index 46c58cd139..4740964dee 100644 --- a/indra/newview/llpanelpeople.h +++ b/indra/newview/llpanelpeople.h @@ -68,6 +68,8 @@ private: E_SORT_BY_RECENT_SPEAKERS = 4, } ESortOrder; + void removePicker(); + // methods indirectly called by the updaters void updateFriendListHelpText(); void updateFriendList(); @@ -80,31 +82,22 @@ private: std::string getActiveTabName() const; LLUUID getCurrentItemID() const; void getCurrentItemIDs(uuid_vec_t& selected_uuids) const; - void buttonSetVisible(std::string btn_name, BOOL visible); - void buttonSetEnabled(const std::string& btn_name, bool enabled); - void buttonSetAction(const std::string& btn_name, const commit_signal_t::slot_type& cb); void showGroupMenu(LLMenuGL* menu); void setSortOrder(LLAvatarList* list, ESortOrder order, bool save = true); // UI callbacks void onFilterEdit(const std::string& search_string); void onTabSelected(const LLSD& param); - void onViewProfileButtonClicked(); void onAddFriendButtonClicked(); void onAddFriendWizButtonClicked(); void onDeleteFriendButtonClicked(); - void onGroupInfoButtonClicked(); void onChatButtonClicked(); + void onGearButtonClicked(LLUICtrl* btn); void onImButtonClicked(); - void onCallButtonClicked(); - void onGroupCallButtonClicked(); - void onTeleportButtonClicked(); - void onShareButtonClicked(); void onMoreButtonClicked(); - void onActivateButtonClicked(); void onAvatarListDoubleClicked(LLUICtrl* ctrl); void onAvatarListCommitted(LLAvatarList* list); - void onGroupPlusButtonClicked(); + bool onGroupPlusButtonValidate(); void onGroupMinusButtonClicked(); void onGroupPlusMenuItemClicked(const LLSD& userdata); @@ -113,8 +106,6 @@ private: void onGroupsViewSortMenuItemClicked(const LLSD& userdata); void onRecentViewSortMenuItemClicked(const LLSD& userdata); - //returns false only if group is "none" - bool isRealGroup(); bool onFriendsViewSortMenuItemCheck(const LLSD& userdata); bool onRecentViewSortMenuItemCheck(const LLSD& userdata); bool onNearbyViewSortMenuItemCheck(const LLSD& userdata); @@ -135,7 +126,6 @@ private: bool isAccordionCollapsedByUser(LLUICtrl* acc_tab); bool isAccordionCollapsedByUser(const std::string& name); - LLFilterEditor* mFilterEditor; LLTabContainer* mTabContainer; LLAvatarList* mOnlineFriendList; LLAvatarList* mAllFriendList; @@ -144,24 +134,14 @@ private: LLGroupList* mGroupList; LLNetMap* mMiniMap; - LLHandle<LLView> mGroupPlusMenuHandle; - LLHandle<LLView> mNearbyViewSortMenuHandle; - LLHandle<LLView> mFriendsViewSortMenuHandle; - LLHandle<LLView> mGroupsViewSortMenuHandle; - LLHandle<LLView> mRecentViewSortMenuHandle; + std::vector<std::string> mSavedOriginalFilters; + std::vector<std::string> mSavedFilters; Updater* mFriendListUpdater; Updater* mNearbyListUpdater; Updater* mRecentListUpdater; Updater* mButtonsUpdater; - - LLMenuButton* mNearbyGearButton; - LLMenuButton* mFriendsGearButton; - LLMenuButton* mGroupsGearButton; - LLMenuButton* mRecentGearButton; - - std::string mFilterSubString; - std::string mFilterSubStringOrig; + LLHandle< LLFloater > mPicker; }; #endif //LL_LLPANELPEOPLE_H diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp index f12c4de2f7..899771f3b9 100644 --- a/indra/newview/llpanelpeoplemenus.cpp +++ b/indra/newview/llpanelpeoplemenus.cpp @@ -51,6 +51,7 @@ LLContextMenu* NearbyMenu::createMenu() // set up the callbacks for all of the avatar menu items LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + LLContextMenu* menu; if ( mUUIDs.size() == 1 ) { @@ -67,20 +68,22 @@ LLContextMenu* NearbyMenu::createMenu() registrar.add("Avatar.Share", boost::bind(&LLAvatarActions::share, id)); registrar.add("Avatar.Pay", boost::bind(&LLAvatarActions::pay, id)); registrar.add("Avatar.BlockUnblock", boost::bind(&LLAvatarActions::toggleBlock, id)); + registrar.add("Avatar.InviteToGroup", boost::bind(&LLAvatarActions::inviteToGroup, id)); + registrar.add("Avatar.Calllog", boost::bind(&LLAvatarActions::viewChatHistory, id)); enable_registrar.add("Avatar.EnableItem", boost::bind(&NearbyMenu::enableContextMenuItem, this, _2)); enable_registrar.add("Avatar.CheckItem", boost::bind(&NearbyMenu::checkContextMenuItem, this, _2)); // create the context menu from the XUI - return createFromFile("menu_people_nearby.xml"); + menu = createFromFile("menu_people_nearby.xml"); } else { // Set up for multi-selected People // registrar.add("Avatar.AddFriend", boost::bind(&LLAvatarActions::requestFriendshipDialog, mUUIDs)); // *TODO: unimplemented - registrar.add("Avatar.IM", boost::bind(&LLAvatarActions::startConference, mUUIDs)); - registrar.add("Avatar.Call", boost::bind(&LLAvatarActions::startAdhocCall, mUUIDs)); + registrar.add("Avatar.IM", boost::bind(&LLAvatarActions::startConference, mUUIDs, LLUUID::null)); + registrar.add("Avatar.Call", boost::bind(&LLAvatarActions::startAdhocCall, mUUIDs, LLUUID::null)); registrar.add("Avatar.OfferTeleport", boost::bind(&NearbyMenu::offerTeleport, this)); registrar.add("Avatar.RemoveFriend",boost::bind(&LLAvatarActions::removeFriendsDialog, mUUIDs)); // registrar.add("Avatar.Share", boost::bind(&LLAvatarActions::startIM, mUUIDs)); // *TODO: unimplemented @@ -88,8 +91,10 @@ LLContextMenu* NearbyMenu::createMenu() enable_registrar.add("Avatar.EnableItem", boost::bind(&NearbyMenu::enableContextMenuItem, this, _2)); // create the context menu from the XUI - return createFromFile("menu_people_nearby_multiselect.xml"); + menu = createFromFile("menu_people_nearby_multiselect.xml"); } + + return menu; } bool NearbyMenu::enableContextMenuItem(const LLSD& userdata) diff --git a/indra/newview/llpaneltopinfobar.cpp b/indra/newview/llpaneltopinfobar.cpp index 280cc11179..854deb00d0 100644 --- a/indra/newview/llpaneltopinfobar.cpp +++ b/indra/newview/llpaneltopinfobar.cpp @@ -230,7 +230,7 @@ void LLPanelTopInfoBar::buildLocationString(std::string& loc_str, bool show_coor void LLPanelTopInfoBar::setParcelInfoText(const std::string& new_text) { LLRect old_rect = getRect(); - const LLFontGL* font = mParcelInfoText->getDefaultFont(); + const LLFontGL* font = mParcelInfoText->getFont(); S32 new_text_width = font->getWidth(new_text); mParcelInfoText->setText(new_text); diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index 975a6c67d8..e199cb5776 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -29,9 +29,13 @@ // common includes #include "lltrans.h" #include "llavataractions.h" +#include "llavatarnamecache.h" +#include "llavatarname.h" #include "llagent.h" #include "llimview.h" +#include "llimfloatercontainer.h" +#include "llpanelpeoplemenus.h" #include "llnotificationsutil.h" #include "llparticipantlist.h" #include "llspeakers.h" @@ -49,11 +53,14 @@ static const LLAvatarItemAgentOnTopComparator AGENT_ON_TOP_NAME_COMPARATOR; // helper function to update AvatarList Item's indicator in the voice participant list static void update_speaker_indicator(const LLAvatarList* const avatar_list, const LLUUID& avatar_uuid, bool is_muted) { - LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*>(avatar_list->getItemByValue(avatar_uuid)); - if (item) + if (avatar_list) { - LLOutputMonitorCtrl* indicator = item->getChild<LLOutputMonitorCtrl>("speaking_indicator"); - indicator->setIsMuted(is_muted); + LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*>(avatar_list->getItemByValue(avatar_uuid)); + if (item) + { + LLOutputMonitorCtrl* indicator = item->getChild<LLOutputMonitorCtrl>("speaking_indicator"); + indicator->setIsMuted(is_muted); + } } } @@ -197,14 +204,15 @@ private: uuid_set_t mAvalineCallers; }; -LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, +LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* avatar_list, + LLFolderViewModelInterface& root_view_model, bool use_context_menu/* = true*/, - bool exclude_agent /*= true*/, + bool exclude_agent /*= true*/, bool can_toggle_icons /*= true*/) : + LLConversationItemSession(data_source->getSessionID(), root_view_model), mSpeakerMgr(data_source), mAvatarList(avatar_list), - mParticipantListMenu(NULL), mExcludeAgent(exclude_agent), mValidateSpeakerCallback(NULL) { @@ -216,35 +224,41 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, mSpeakerRemoveListener = new SpeakerRemoveListener(*this); mSpeakerClearListener = new SpeakerClearListener(*this); mSpeakerModeratorListener = new SpeakerModeratorUpdateListener(*this); + mSpeakerUpdateListener = new SpeakerUpdateListener(*this); mSpeakerMuteListener = new SpeakerMuteListener(*this); mSpeakerMgr->addListener(mSpeakerAddListener, "add"); mSpeakerMgr->addListener(mSpeakerRemoveListener, "remove"); mSpeakerMgr->addListener(mSpeakerClearListener, "clear"); mSpeakerMgr->addListener(mSpeakerModeratorListener, "update_moderator"); + mSpeakerMgr->addListener(mSpeakerUpdateListener, "update_speaker"); - mAvatarList->setNoItemsCommentText(LLTrans::getString("LoadingData")); - LL_DEBUGS("SpeakingIndicator") << "Set session for speaking indicators: " << mSpeakerMgr->getSessionID() << LL_ENDL; - mAvatarList->setSessionID(mSpeakerMgr->getSessionID()); - mAvatarListDoubleClickConnection = mAvatarList->setItemDoubleClickCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, _1)); - mAvatarListRefreshConnection = mAvatarList->setRefreshCompleteCallback(boost::bind(&LLParticipantList::onAvatarListRefreshed, this, _1, _2)); - // Set onAvatarListDoubleClicked as default on_return action. - mAvatarListReturnConnection = mAvatarList->setReturnCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, mAvatarList)); - - if (use_context_menu) - { - mParticipantListMenu = new LLParticipantListMenu(*this); - mAvatarList->setContextMenu(mParticipantListMenu); - } - else + setSessionID(mSpeakerMgr->getSessionID()); + + if (mAvatarList) { - mAvatarList->setContextMenu(NULL); - } + mAvatarList->setNoItemsCommentText(LLTrans::getString("LoadingData")); + LL_DEBUGS("SpeakingIndicator") << "Set session for speaking indicators: " << mSpeakerMgr->getSessionID() << LL_ENDL; + mAvatarList->setSessionID(mSpeakerMgr->getSessionID()); + mAvatarListDoubleClickConnection = mAvatarList->setItemDoubleClickCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, _1)); + mAvatarListRefreshConnection = mAvatarList->setRefreshCompleteCallback(boost::bind(&LLParticipantList::onAvatarListRefreshed, this, _1, _2)); + // Set onAvatarListDoubleClicked as default on_return action. + mAvatarListReturnConnection = mAvatarList->setReturnCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, mAvatarList)); - if (use_context_menu && can_toggle_icons) - { - mAvatarList->setShowIcons("ParticipantListShowIcons"); - mAvatarListToggleIconsConnection = gSavedSettings.getControl("ParticipantListShowIcons")->getSignal()->connect(boost::bind(&LLAvatarList::toggleIcons, mAvatarList)); + if (use_context_menu) + { + mAvatarList->setContextMenu(&LLPanelPeopleMenus::gNearbyMenu); + } + else + { + mAvatarList->setContextMenu(NULL); + } + + if (use_context_menu && can_toggle_icons) + { + mAvatarList->setShowIcons("ParticipantListShowIcons"); + mAvatarListToggleIconsConnection = gSavedSettings.getControl("ParticipantListShowIcons")->getSignal()->connect(boost::bind(&LLAvatarList::toggleIcons, mAvatarList)); + } } //Lets fill avatarList with existing speakers @@ -266,39 +280,55 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, } // we need to exclude agent id for non group chat sort(); + + // Identify and store what kind of session we are + LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(data_source->getSessionID()); + if (im_session) + { + // By default, sessions that can't be identified as group or ad-hoc will be considered P2P (i.e. 1 on 1) + mConvType = CONV_SESSION_1_ON_1; + if (im_session->isAdHocSessionType()) + { + mConvType = CONV_SESSION_AD_HOC; + } + else if (im_session->isGroupSessionType()) + { + mConvType = CONV_SESSION_GROUP; + } + } + else + { + // That's the only session that doesn't get listed in the LLIMModel as a session... + mConvType = CONV_SESSION_NEARBY; + } } LLParticipantList::~LLParticipantList() { - mAvatarListDoubleClickConnection.disconnect(); - mAvatarListRefreshConnection.disconnect(); - mAvatarListReturnConnection.disconnect(); - mAvatarListToggleIconsConnection.disconnect(); - - // It is possible Participant List will be re-created from LLCallFloater::onCurrentChannelChanged() - // See ticket EXT-3427 - // hide menu before deleting it to stop enable and check handlers from triggering. - if(mParticipantListMenu && !LLApp::isExiting()) + if (mAvatarList) { - mParticipantListMenu->hide(); + mAvatarListDoubleClickConnection.disconnect(); + mAvatarListRefreshConnection.disconnect(); + mAvatarListReturnConnection.disconnect(); + mAvatarListToggleIconsConnection.disconnect(); } - if (mParticipantListMenu) + if (mAvatarList) { - delete mParticipantListMenu; - mParticipantListMenu = NULL; + mAvatarList->setContextMenu(NULL); + mAvatarList->setComparator(NULL); } - mAvatarList->setContextMenu(NULL); - mAvatarList->setComparator(NULL); - delete mAvalineUpdater; } void LLParticipantList::setSpeakingIndicatorsVisible(BOOL visible) { - mAvatarList->setSpeakingIndicatorsVisible(visible); -}; + if (mAvatarList) + { + mAvatarList->setSpeakingIndicatorsVisible(visible); + } +} void LLParticipantList::onAvatarListDoubleClicked(LLUICtrl* ctrl) { @@ -319,81 +349,81 @@ void LLParticipantList::onAvatarListDoubleClicked(LLUICtrl* ctrl) void LLParticipantList::onAvatarListRefreshed(LLUICtrl* ctrl, const LLSD& param) { LLAvatarList* list = dynamic_cast<LLAvatarList*>(ctrl); - if (list) + const std::string moderator_indicator(LLTrans::getString("IM_moderator_label")); + const std::size_t moderator_indicator_len = moderator_indicator.length(); + + // Firstly remove moderators indicator + std::set<LLUUID>::const_iterator + moderator_list_it = mModeratorToRemoveList.begin(), + moderator_list_end = mModeratorToRemoveList.end(); + for (;moderator_list_it != moderator_list_end; ++moderator_list_it) { - const std::string moderator_indicator(LLTrans::getString("IM_moderator_label")); - const std::size_t moderator_indicator_len = moderator_indicator.length(); - - // Firstly remove moderators indicator - std::set<LLUUID>::const_iterator - moderator_list_it = mModeratorToRemoveList.begin(), - moderator_list_end = mModeratorToRemoveList.end(); - for (;moderator_list_it != moderator_list_end; ++moderator_list_it) + LLAvatarListItem* item = (list ? dynamic_cast<LLAvatarListItem*> (list->getItemByValue(*moderator_list_it)) : NULL); + if ( item ) { - LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*> (list->getItemByValue(*moderator_list_it)); - if ( item ) + std::string name = item->getAvatarName(); + std::string tooltip = item->getAvatarToolTip(); + size_t found = name.find(moderator_indicator); + if (found != std::string::npos) { - std::string name = item->getAvatarName(); - std::string tooltip = item->getAvatarToolTip(); - size_t found = name.find(moderator_indicator); - if (found != std::string::npos) - { - name.erase(found, moderator_indicator_len); - item->setAvatarName(name); - } - found = tooltip.find(moderator_indicator); - if (found != tooltip.npos) - { - tooltip.erase(found, moderator_indicator_len); - item->setAvatarToolTip(tooltip); - } + name.erase(found, moderator_indicator_len); + item->setAvatarName(name); + } + found = tooltip.find(moderator_indicator); + if (found != tooltip.npos) + { + tooltip.erase(found, moderator_indicator_len); + item->setAvatarToolTip(tooltip); } } + setParticipantIsModerator(*moderator_list_it,false); + } - mModeratorToRemoveList.clear(); + mModeratorToRemoveList.clear(); - // Add moderators indicator - moderator_list_it = mModeratorList.begin(); - moderator_list_end = mModeratorList.end(); - for (;moderator_list_it != moderator_list_end; ++moderator_list_it) + // Add moderators indicator + moderator_list_it = mModeratorList.begin(); + moderator_list_end = mModeratorList.end(); + for (;moderator_list_it != moderator_list_end; ++moderator_list_it) + { + LLAvatarListItem* item = (list ? dynamic_cast<LLAvatarListItem*> (list->getItemByValue(*moderator_list_it)) : NULL); + if ( item ) { - LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*> (list->getItemByValue(*moderator_list_it)); - if ( item ) + std::string name = item->getAvatarName(); + std::string tooltip = item->getAvatarToolTip(); + size_t found = name.find(moderator_indicator); + if (found == std::string::npos) { - std::string name = item->getAvatarName(); - std::string tooltip = item->getAvatarToolTip(); - size_t found = name.find(moderator_indicator); - if (found == std::string::npos) - { - name += " "; - name += moderator_indicator; - item->setAvatarName(name); - } - found = tooltip.find(moderator_indicator); - if (found == std::string::npos) - { - tooltip += " "; - tooltip += moderator_indicator; - item->setAvatarToolTip(tooltip); - } + name += " "; + name += moderator_indicator; + item->setAvatarName(name); + } + found = tooltip.find(moderator_indicator); + if (found == std::string::npos) + { + tooltip += " "; + tooltip += moderator_indicator; + item->setAvatarToolTip(tooltip); } } + setParticipantIsModerator(*moderator_list_it,true); + } - // update voice mute state of all items. See EXT-7235 - LLSpeakerMgr::speaker_list_t speaker_list; + // update voice mute state of all items. See EXT-7235 + LLSpeakerMgr::speaker_list_t speaker_list; - // Use also participants which are not in voice session now (the second arg is TRUE). - // They can already have mModeratorMutedVoice set from the previous voice session - // and LLSpeakerVoiceModerationEvent will not be sent when speaker manager is updated next time. - mSpeakerMgr->getSpeakerList(&speaker_list, TRUE); - for(LLSpeakerMgr::speaker_list_t::iterator it = speaker_list.begin(); it != speaker_list.end(); it++) - { - const LLPointer<LLSpeaker>& speakerp = *it; + // Use also participants which are not in voice session now (the second arg is TRUE). + // They can already have mModeratorMutedVoice set from the previous voice session + // and LLSpeakerVoiceModerationEvent will not be sent when speaker manager is updated next time. + mSpeakerMgr->getSpeakerList(&speaker_list, TRUE); + for(LLSpeakerMgr::speaker_list_t::iterator it = speaker_list.begin(); it != speaker_list.end(); it++) + { + const LLPointer<LLSpeaker>& speakerp = *it; - if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY) - { - update_speaker_indicator(list, speakerp->mID, speakerp->mModeratorMutedVoice); - } + if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY) + { + setParticipantIsMuted(speakerp->mID, speakerp->mModeratorMutedVoice); + update_speaker_indicator(list, speakerp->mID, speakerp->mModeratorMutedVoice); } } } @@ -411,30 +441,39 @@ void LLParticipantList::onAvatarListRefreshed(LLUICtrl* ctrl, const LLSD& param) */ void LLParticipantList::onAvalineCallerFound(const LLUUID& participant_id) { - LLPanel* item = mAvatarList->getItemByValue(participant_id); - - if (NULL == item) + if (mAvatarList) { - LL_WARNS("Avaline") << "Something wrong. Unable to find item for: " << participant_id << LL_ENDL; - return; - } + LLPanel* item = mAvatarList->getItemByValue(participant_id); - if (typeid(*item) == typeid(LLAvalineListItem)) - { - LL_DEBUGS("Avaline") << "Avaline caller has already correct class type for: " << participant_id << LL_ENDL; - // item representing an Avaline caller has a correct type already. - return; - } + if (NULL == item) + { + LL_WARNS("Avaline") << "Something wrong. Unable to find item for: " << participant_id << LL_ENDL; + return; + } + + if (typeid(*item) == typeid(LLAvalineListItem)) + { + LL_DEBUGS("Avaline") << "Avaline caller has already correct class type for: " << participant_id << LL_ENDL; + // item representing an Avaline caller has a correct type already. + return; + } - LL_DEBUGS("Avaline") << "remove item from the list and re-add it: " << participant_id << LL_ENDL; + LL_DEBUGS("Avaline") << "remove item from the list and re-add it: " << participant_id << LL_ENDL; - // remove UUID from LLAvatarList::mIDs to be able add it again. - uuid_vec_t& ids = mAvatarList->getIDs(); - uuid_vec_t::iterator pos = std::find(ids.begin(), ids.end(), participant_id); - ids.erase(pos); + // remove UUID from LLAvatarList::mIDs to be able add it again. + uuid_vec_t& ids = mAvatarList->getIDs(); + uuid_vec_t::iterator pos = std::find(ids.begin(), ids.end(), participant_id); + ids.erase(pos); - // remove item directly - mAvatarList->removeItem(item); + // remove item directly + mAvatarList->removeItem(item); + } + + LLConversationItemParticipant* participant = findParticipant(participant_id); + if (participant) + { + removeParticipant(participant); + } // re-add avaline caller with a correct class instance. addAvatarIDExceptAgent(participant_id); @@ -473,6 +512,7 @@ void LLParticipantList::update() { mSpeakerMgr->update(true); + // Need to resort the participant list if it's in sort by recent speaker order. if (E_SORT_BY_RECENT_SPEAKERS == getSortOrder() && !isHovered()) { // Resort avatar list @@ -484,7 +524,7 @@ bool LLParticipantList::isHovered() { S32 x, y; LLUI::getMousePositionScreen(&x, &y); - return mAvatarList->calcScreenRect().pointInRect(x, y); + return (mAvatarList ? mAvatarList->calcScreenRect().pointInRect(x, y) : false); } bool LLParticipantList::onAddItemEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) @@ -503,21 +543,45 @@ bool LLParticipantList::onAddItemEvent(LLPointer<LLOldEvents::LLEvent> event, co bool LLParticipantList::onRemoveItemEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) { - uuid_vec_t& group_members = mAvatarList->getIDs(); - uuid_vec_t::iterator pos = std::find(group_members.begin(), group_members.end(), event->getValue().asUUID()); - if(pos != group_members.end()) + LLUUID avatar_id = event->getValue().asUUID(); + if (mAvatarList) { - group_members.erase(pos); - mAvatarList->setDirty(); + uuid_vec_t& group_members = mAvatarList->getIDs(); + uuid_vec_t::iterator pos = std::find(group_members.begin(), group_members.end(), avatar_id); + if(pos != group_members.end()) + { + group_members.erase(pos); + mAvatarList->setDirty(); + } } + removeParticipant(avatar_id); return true; } bool LLParticipantList::onClearListEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) { - uuid_vec_t& group_members = mAvatarList->getIDs(); - group_members.clear(); - mAvatarList->setDirty(); + if (mAvatarList) + { + uuid_vec_t& group_members = mAvatarList->getIDs(); + group_members.clear(); + mAvatarList->setDirty(); + } + clearParticipants(); + return true; +} + +bool LLParticipantList::onSpeakerUpdateEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) +{ + const LLSD& evt_data = event->getValue(); + if ( evt_data.has("id") ) + { + LLUUID participant_id = evt_data["id"]; + LLIMFloaterContainer* im_box = LLIMFloaterContainer::findInstance(); + if (im_box) + { + im_box->setTimeNow(mUUID,participant_id); + } + } return true; } @@ -557,6 +621,7 @@ bool LLParticipantList::onSpeakerMuteEvent(LLPointer<LLOldEvents::LLEvent> event // update UI on confirmation of moderator mutes if (event->getValue().asString() == "voice") { + setParticipantIsMuted(speakerp->mID, speakerp->mModeratorMutedVoice); update_speaker_indicator(mAvatarList, speakerp->mID, speakerp->mModeratorMutedVoice); } return true; @@ -564,6 +629,7 @@ bool LLParticipantList::onSpeakerMuteEvent(LLPointer<LLOldEvents::LLEvent> event void LLParticipantList::sort() { + // *TODO : Merov : Need to plan for sort() for LLConversationModel if ( !mAvatarList ) return; @@ -595,22 +661,46 @@ void LLParticipantList::sort() void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id) { + // Do not add if already in there or excluded for some reason if (mExcludeAgent && gAgent.getID() == avatar_id) return; - if (mAvatarList->contains(avatar_id)) return; + if (mAvatarList && mAvatarList->contains(avatar_id)) return; + if (findParticipant(avatar_id)) return; bool is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(avatar_id); + LLConversationItemParticipant* participant = NULL; + if (is_avatar) { - mAvatarList->getIDs().push_back(avatar_id); - mAvatarList->setDirty(); + // Create a participant view model instance + LLAvatarName avatar_name; + bool has_name = LLAvatarNameCache::get(avatar_id, &avatar_name); + participant = new LLConversationItemParticipant(!has_name ? LLTrans::getString("AvatarNameWaiting") : avatar_name.mDisplayName , avatar_id, mRootViewModel); + participant->fetchAvatarName(); + if (mAvatarList) + { + mAvatarList->getIDs().push_back(avatar_id); + mAvatarList->setDirty(); + } } else { std::string display_name = LLVoiceClient::getInstance()->getDisplayName(avatar_id); - mAvatarList->addAvalineItem(avatar_id, mSpeakerMgr->getSessionID(), display_name.empty() ? LLTrans::getString("AvatarNameWaiting") : display_name); + // Create a participant view model instance + participant = new LLConversationItemParticipant(display_name.empty() ? LLTrans::getString("AvatarNameWaiting") : display_name, avatar_id, mRootViewModel); + if (mAvatarList) + { + mAvatarList->addAvalineItem(avatar_id, mSpeakerMgr->getSessionID(), display_name.empty() ? LLTrans::getString("AvatarNameWaiting") : display_name); + } mAvalineUpdater->watchAvalineCaller(avatar_id); } + + // *TODO : Merov : need to update the online/offline status of the participant. + // Hack for this: LLAvatarTracker::instance().isBuddyOnline(avatar_id)) + + // Add the participant model to the session's children list + addParticipant(participant); + adjustParticipant(avatar_id); } @@ -658,6 +748,14 @@ bool LLParticipantList::SpeakerClearListener::handleEvent(LLPointer<LLOldEvents: } // +// LLParticipantList::SpeakerUpdateListener +// +bool LLParticipantList::SpeakerUpdateListener::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) +{ + return mParent.onSpeakerUpdateEvent(event, userdata); +} + +// // LLParticipantList::SpeakerModeratorListener // bool LLParticipantList::SpeakerModeratorUpdateListener::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata) @@ -670,350 +768,6 @@ bool LLParticipantList::SpeakerMuteListener::handleEvent(LLPointer<LLOldEvents:: return mParent.onSpeakerMuteEvent(event, userdata); } -LLContextMenu* LLParticipantList::LLParticipantListMenu::createMenu() -{ - // set up the callbacks for all of the avatar menu items - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; - - registrar.add("ParticipantList.Sort", boost::bind(&LLParticipantList::LLParticipantListMenu::sortParticipantList, this, _2)); - registrar.add("ParticipantList.ToggleAllowTextChat", boost::bind(&LLParticipantList::LLParticipantListMenu::toggleAllowTextChat, this, _2)); - registrar.add("ParticipantList.ToggleMuteText", boost::bind(&LLParticipantList::LLParticipantListMenu::toggleMuteText, this, _2)); - - registrar.add("Avatar.Profile", boost::bind(&LLAvatarActions::showProfile, mUUIDs.front())); - registrar.add("Avatar.IM", boost::bind(&LLAvatarActions::startIM, mUUIDs.front())); - registrar.add("Avatar.AddFriend", boost::bind(&LLAvatarActions::requestFriendshipDialog, mUUIDs.front())); - registrar.add("Avatar.BlockUnblock", boost::bind(&LLParticipantList::LLParticipantListMenu::toggleMuteVoice, this, _2)); - registrar.add("Avatar.Share", boost::bind(&LLAvatarActions::share, mUUIDs.front())); - registrar.add("Avatar.Pay", boost::bind(&LLAvatarActions::pay, mUUIDs.front())); - registrar.add("Avatar.Call", boost::bind(&LLAvatarActions::startCall, mUUIDs.front())); - - registrar.add("ParticipantList.ModerateVoice", boost::bind(&LLParticipantList::LLParticipantListMenu::moderateVoice, this, _2)); - - enable_registrar.add("ParticipantList.EnableItem", boost::bind(&LLParticipantList::LLParticipantListMenu::enableContextMenuItem, this, _2)); - enable_registrar.add("ParticipantList.EnableItem.Moderate", boost::bind(&LLParticipantList::LLParticipantListMenu::enableModerateContextMenuItem, this, _2)); - enable_registrar.add("ParticipantList.CheckItem", boost::bind(&LLParticipantList::LLParticipantListMenu::checkContextMenuItem, this, _2)); - - // create the context menu from the XUI - LLContextMenu* main_menu = createFromFile("menu_participant_list.xml"); - - // Don't show sort options for P2P chat - bool is_sort_visible = (mParent.mAvatarList && mParent.mAvatarList->size() > 1); - main_menu->setItemVisible("SortByName", is_sort_visible); - main_menu->setItemVisible("SortByRecentSpeakers", is_sort_visible); - main_menu->setItemVisible("Moderator Options Separator", isGroupModerator()); - main_menu->setItemVisible("Moderator Options", isGroupModerator()); - main_menu->setItemVisible("View Icons Separator", mParent.mAvatarListToggleIconsConnection.connected()); - main_menu->setItemVisible("View Icons", mParent.mAvatarListToggleIconsConnection.connected()); - main_menu->arrangeAndClear(); - - return main_menu; -} - -void LLParticipantList::LLParticipantListMenu::show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y) -{ - if (uuids.size() == 0) return; - - LLListContextMenu::show(spawning_view, uuids, x, y); - - const LLUUID& speaker_id = mUUIDs.front(); - BOOL is_muted = isMuted(speaker_id); - - if (is_muted) - { - LLMenuGL::sMenuContainer->getChildView("ModerateVoiceMuteSelected")->setVisible( false); - } - else - { - LLMenuGL::sMenuContainer->getChildView("ModerateVoiceUnMuteSelected")->setVisible( false); - } -} - -void LLParticipantList::LLParticipantListMenu::sortParticipantList(const LLSD& userdata) -{ - std::string param = userdata.asString(); - if ("sort_by_name" == param) - { - mParent.setSortOrder(E_SORT_BY_NAME); - } - else if ("sort_by_recent_speakers" == param) - { - mParent.setSortOrder(E_SORT_BY_RECENT_SPEAKERS); - } -} - -void LLParticipantList::LLParticipantListMenu::toggleAllowTextChat(const LLSD& userdata) -{ - - LLIMSpeakerMgr* mgr = dynamic_cast<LLIMSpeakerMgr*>(mParent.mSpeakerMgr); - if (mgr) - { - const LLUUID speaker_id = mUUIDs.front(); - mgr->toggleAllowTextChat(speaker_id); - } -} - -void LLParticipantList::LLParticipantListMenu::toggleMute(const LLSD& userdata, U32 flags) -{ - const LLUUID speaker_id = mUUIDs.front(); - BOOL is_muted = LLMuteList::getInstance()->isMuted(speaker_id, flags); - std::string name; - - //fill in name using voice client's copy of name cache - LLPointer<LLSpeaker> speakerp = mParent.mSpeakerMgr->findSpeaker(speaker_id); - if (speakerp.isNull()) - { - LL_WARNS("Speakers") << "Speaker " << speaker_id << " not found" << llendl; - return; - } - LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*>(mParent.mAvatarList->getItemByValue(speaker_id)); - if (NULL == item) return; - - name = item->getAvatarName(); - - LLMute::EType mute_type; - switch (speakerp->mType) - { - case LLSpeaker::SPEAKER_AGENT: - mute_type = LLMute::AGENT; - break; - case LLSpeaker::SPEAKER_OBJECT: - mute_type = LLMute::OBJECT; - break; - case LLSpeaker::SPEAKER_EXTERNAL: - default: - mute_type = LLMute::EXTERNAL; - break; - } - LLMute mute(speaker_id, name, mute_type); - - if (!is_muted) - { - LLMuteList::getInstance()->add(mute, flags); - } - else - { - LLMuteList::getInstance()->remove(mute, flags); - } -} - -void LLParticipantList::LLParticipantListMenu::toggleMuteText(const LLSD& userdata) -{ - toggleMute(userdata, LLMute::flagTextChat); -} - -void LLParticipantList::LLParticipantListMenu::toggleMuteVoice(const LLSD& userdata) -{ - toggleMute(userdata, LLMute::flagVoiceChat); -} - -bool LLParticipantList::LLParticipantListMenu::isGroupModerator() -{ - if (!mParent.mSpeakerMgr) - { - llwarns << "Speaker manager is missing" << llendl; - return false; - } - - // Is session a group call/chat? - if(gAgent.isInGroup(mParent.mSpeakerMgr->getSessionID())) - { - LLSpeaker* speaker = mParent.mSpeakerMgr->findSpeaker(gAgentID).get(); - - // Is agent a moderator? - return speaker && speaker->mIsModerator; - } - return false; -} - -bool LLParticipantList::LLParticipantListMenu::isMuted(const LLUUID& avatar_id) -{ - LLPointer<LLSpeaker> selected_speakerp = mParent.mSpeakerMgr->findSpeaker(avatar_id); - if (!selected_speakerp) return true; - - return selected_speakerp->mStatus == LLSpeaker::STATUS_MUTED; -} - -void LLParticipantList::LLParticipantListMenu::moderateVoice(const LLSD& userdata) -{ - if (!gAgent.getRegion()) return; - - bool moderate_selected = userdata.asString() == "selected"; - - if (moderate_selected) - { - const LLUUID& selected_avatar_id = mUUIDs.front(); - bool is_muted = isMuted(selected_avatar_id); - moderateVoiceParticipant(selected_avatar_id, is_muted); - } - else - { - bool unmute_all = userdata.asString() == "unmute_all"; - moderateVoiceAllParticipants(unmute_all); - } -} - -void LLParticipantList::LLParticipantListMenu::moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute) -{ - LLIMSpeakerMgr* mgr = dynamic_cast<LLIMSpeakerMgr*>(mParent.mSpeakerMgr); - if (mgr) - { - mgr->moderateVoiceParticipant(avatar_id, unmute); - } -} - -void LLParticipantList::LLParticipantListMenu::moderateVoiceAllParticipants(bool unmute) -{ - LLIMSpeakerMgr* mgr = dynamic_cast<LLIMSpeakerMgr*>(mParent.mSpeakerMgr); - if (mgr) - { - if (!unmute) - { - LLSD payload; - payload["session_id"] = mgr->getSessionID(); - LLNotificationsUtil::add("ConfirmMuteAll", LLSD(), payload, confirmMuteAllCallback); - return; - } - - mgr->moderateVoiceAllParticipants(unmute); - } -} - -// static -void LLParticipantList::LLParticipantListMenu::confirmMuteAllCallback(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - // if Cancel pressed - if (option == 1) - { - return; - } - - const LLSD& payload = notification["payload"]; - const LLUUID& session_id = payload["session_id"]; - - LLIMSpeakerMgr * speaker_manager = dynamic_cast<LLIMSpeakerMgr*> ( - LLIMModel::getInstance()->getSpeakerManager(session_id)); - if (speaker_manager) - { - speaker_manager->moderateVoiceAllParticipants(false); - } - - return; -} - - -bool LLParticipantList::LLParticipantListMenu::enableContextMenuItem(const LLSD& userdata) -{ - std::string item = userdata.asString(); - const LLUUID& participant_id = mUUIDs.front(); - - // For now non of "can_view_profile" action and menu actions listed below except "can_block" - // can be performed for Avaline callers. - bool is_participant_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(participant_id); - if (!is_participant_avatar && "can_block" != item) return false; - - if (item == "can_mute_text" || "can_block" == item || "can_share" == item || "can_im" == item - || "can_pay" == item) - { - return mUUIDs.front() != gAgentID; - } - else if (item == std::string("can_add")) - { - // We can add friends if: - // - there are selected people - // - and there are no friends among selection yet. - - bool result = (mUUIDs.size() > 0); - - uuid_vec_t::const_iterator - id = mUUIDs.begin(), - uuids_end = mUUIDs.end(); - - for (;id != uuids_end; ++id) - { - if ( *id == gAgentID || LLAvatarActions::isFriend(*id) ) - { - result = false; - break; - } - } - return result; - } - else if (item == "can_call") - { - bool not_agent = mUUIDs.front() != gAgentID; - bool can_call = not_agent && LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); - return can_call; - } - - return true; -} - -/* - Processed menu items with such parameters: - can_allow_text_chat - can_moderate_voice -*/ -bool LLParticipantList::LLParticipantListMenu::enableModerateContextMenuItem(const LLSD& userdata) -{ - // only group moderators can perform actions related to this "enable callback" - if (!isGroupModerator()) return false; - - const LLUUID& participant_id = mUUIDs.front(); - LLPointer<LLSpeaker> speakerp = mParent.mSpeakerMgr->findSpeaker(participant_id); - - // not in voice participants can not be moderated - bool speaker_in_voice = speakerp.notNull() && speakerp->isInVoiceChannel(); - - const std::string& item = userdata.asString(); - - if ("can_moderate_voice" == item) - { - return speaker_in_voice; - } - - // For now non of menu actions except "can_moderate_voice" can be performed for Avaline callers. - bool is_participant_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(participant_id); - if (!is_participant_avatar) return false; - - return true; -} - -bool LLParticipantList::LLParticipantListMenu::checkContextMenuItem(const LLSD& userdata) -{ - std::string item = userdata.asString(); - const LLUUID& id = mUUIDs.front(); - - if (item == "is_muted") - { - return LLMuteList::getInstance()->isMuted(id, LLMute::flagTextChat); - } - else if (item == "is_allowed_text_chat") - { - LLPointer<LLSpeaker> selected_speakerp = mParent.mSpeakerMgr->findSpeaker(id); - - if (selected_speakerp.notNull()) - { - return !selected_speakerp->mModeratorMutedText; - } - } - else if(item == "is_blocked") - { - return LLMuteList::getInstance()->isMuted(id, LLMute::flagVoiceChat); - } - else if(item == "is_sorted_by_name") - { - return E_SORT_BY_NAME == mParent.getSortOrder(); - } - else if(item == "is_sorted_by_recent_speakers") - { - return E_SORT_BY_RECENT_SPEAKERS == mParent.getSortOrder(); - } - - return false; -} - bool LLParticipantList::LLAvatarItemRecentSpeakerComparator::doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const { if (mParent.mSpeakerMgr) diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h index 53966c15fe..aaf1e070a5 100644 --- a/indra/newview/llparticipantlist.h +++ b/indra/newview/llparticipantlist.h @@ -31,13 +31,14 @@ #include "llevent.h" #include "llavatarlist.h" // for LLAvatarItemRecentSpeakerComparator #include "lllistcontextmenu.h" +#include "llconversationmodel.h" class LLSpeakerMgr; class LLAvatarList; class LLUICtrl; class LLAvalineUpdater; -class LLParticipantList +class LLParticipantList : public LLConversationItemSession { LOG_CLASS(LLParticipantList); public: @@ -46,6 +47,7 @@ public: LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* avatar_list, + LLFolderViewModelInterface& root_view_model, bool use_context_menu = true, bool exclude_agent = true, bool can_toggle_icons = true); @@ -93,6 +95,7 @@ protected: bool onRemoveItemEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); bool onClearListEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); bool onModeratorUpdateEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); + bool onSpeakerUpdateEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); bool onSpeakerMuteEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); /** @@ -134,6 +137,13 @@ protected: /*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); }; + class SpeakerUpdateListener : public BaseSpeakerListener + { + public: + SpeakerUpdateListener(LLParticipantList& parent) : BaseSpeakerListener(parent) {} + /*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata); + }; + class SpeakerModeratorUpdateListener : public BaseSpeakerListener { public: @@ -150,79 +160,6 @@ protected: }; /** - * Menu used in the participant list. - */ - class LLParticipantListMenu : public LLListContextMenu - { - public: - LLParticipantListMenu(LLParticipantList& parent):mParent(parent){}; - /*virtual*/ LLContextMenu* createMenu(); - /*virtual*/ void show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y); - protected: - LLParticipantList& mParent; - private: - bool enableContextMenuItem(const LLSD& userdata); - bool enableModerateContextMenuItem(const LLSD& userdata); - bool checkContextMenuItem(const LLSD& userdata); - - void sortParticipantList(const LLSD& userdata); - void toggleAllowTextChat(const LLSD& userdata); - void toggleMute(const LLSD& userdata, U32 flags); - void toggleMuteText(const LLSD& userdata); - void toggleMuteVoice(const LLSD& userdata); - - /** - * Return true if Agent is group moderator(and moderator of group call). - */ - bool isGroupModerator(); - - // Voice moderation support - /** - * Check whether specified by argument avatar is muted for group chat or not. - */ - bool isMuted(const LLUUID& avatar_id); - - /** - * Processes Voice moderation menu items. - * - * It calls either moderateVoiceParticipant() or moderateVoiceParticipant() depend on - * passed parameter. - * - * @param userdata can be "selected" or "others". - * - * @see moderateVoiceParticipant() - * @see moderateVoiceAllParticipants() - */ - void moderateVoice(const LLSD& userdata); - - /** - * Mutes/Unmutes avatar for current group voice chat. - * - * It only marks avatar as muted for session and does not use local Agent's Block list. - * It does not mute Agent itself. - * - * @param[in] avatar_id UUID of avatar to be processed - * @param[in] unmute if true - specified avatar will be muted, otherwise - unmuted. - * - * @see moderateVoiceAllParticipants() - */ - void moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute); - - /** - * Mutes/Unmutes all avatars for current group voice chat. - * - * It only marks avatars as muted for session and does not use local Agent's Block list. - * - * @param[in] unmute if true - avatars will be muted, otherwise - unmuted. - * - * @see moderateVoiceParticipant() - */ - void moderateVoiceAllParticipants(bool unmute); - - static void confirmMuteAllCallback(const LLSD& notification, const LLSD& response); - }; - - /** * Comparator for comparing avatar items by last spoken time */ class LLAvatarItemRecentSpeakerComparator : public LLAvatarItemNameComparator, public LLRefCount @@ -262,11 +199,10 @@ private: LLPointer<SpeakerAddListener> mSpeakerAddListener; LLPointer<SpeakerRemoveListener> mSpeakerRemoveListener; LLPointer<SpeakerClearListener> mSpeakerClearListener; + LLPointer<SpeakerUpdateListener> mSpeakerUpdateListener; LLPointer<SpeakerModeratorUpdateListener> mSpeakerModeratorListener; LLPointer<SpeakerMuteListener> mSpeakerMuteListener; - LLParticipantListMenu* mParticipantListMenu; - /** * This field manages an adding a new avatar_id in the mAvatarList * If true, then agent_id wont be added into mAvatarList diff --git a/indra/newview/llplacesfolderview.cpp b/indra/newview/llplacesfolderview.cpp new file mode 100644 index 0000000000..3caa93ae71 --- /dev/null +++ b/indra/newview/llplacesfolderview.cpp @@ -0,0 +1,74 @@ +/** +* @file llplacesfolderview.cpp +* @brief llplacesfolderview used within llplacesinventorypanel +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2012&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2012, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#include "llviewerprecompiledheaders.h" + +#include "llplacesfolderview.h" + +#include "llplacesinventorypanel.h" +#include "llpanellandmarks.h" + +LLPlacesFolderView::LLPlacesFolderView(const LLFolderView::Params& p) + : LLFolderView(p) +{ + // we do not need auto select functionality in places landmarks, so override default behavior. + // this disables applying of the LLSelectFirstFilteredItem in LLFolderView::doIdle. + // Fixed issues: EXT-1631, EXT-4994. + mAutoSelectOverride = TRUE; +} + +BOOL LLPlacesFolderView::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + // let children to change selection first + childrenHandleRightMouseDown(x, y, mask); + mParentLandmarksPanel->setCurrentSelectedList((LLPlacesInventoryPanel*)getParentPanel()); + + // then determine its type and set necessary menu handle + if (getCurSelectedItem()) + { + LLInventoryType::EType inventory_type = static_cast<LLFolderViewModelItemInventory*>(getCurSelectedItem()->getViewModelItem())->getInventoryType(); + inventory_type_menu_handle_t::iterator it_handle = mMenuHandlesByInventoryType.find(inventory_type); + + if (it_handle != mMenuHandlesByInventoryType.end()) + { + mPopupMenuHandle = (*it_handle).second; + } + else + { + llwarns << "Requested menu handle for non-setup inventory type: " << inventory_type << llendl; + } + + } + + return LLFolderView::handleRightMouseDown(x, y, mask); +} + +void LLPlacesFolderView::setupMenuHandle(LLInventoryType::EType asset_type, LLHandle<LLView> menu_handle) +{ + mMenuHandlesByInventoryType[asset_type] = menu_handle; +} + diff --git a/indra/newview/llplacesfolderview.h b/indra/newview/llplacesfolderview.h new file mode 100644 index 0000000000..8c5be39b5e --- /dev/null +++ b/indra/newview/llplacesfolderview.h @@ -0,0 +1,72 @@ +/** +* @file llplacesfolderview.h +* @brief llplacesfolderview used within llplacesinventorypanel +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2012&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2012, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ +#ifndef LL_LLPLACESFOLDERVIEW_H +#define LL_LLPLACESFOLDERVIEW_H + +#include "llfolderview.h" +#include "llinventorypanel.h" + +class LLLandmarksPanel; + +class LLPlacesFolderView : public LLFolderView +{ +public: + + struct Params : public LLInitParam::Block<Params, LLFolderView::Params> + { + Params() + {} + }; + + LLPlacesFolderView(const LLFolderView::Params& p); + /** + * Handles right mouse down + * + * Contains workaround for EXT-2786: sets current selected list for landmark + * panel using @c mParentLandmarksPanel which is set in @c LLLandmarksPanel::initLandmarksPanel + */ + /*virtual*/ BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); + + void setupMenuHandle(LLInventoryType::EType asset_type, LLHandle<LLView> menu_handle); + + void setParentLandmarksPanel(LLLandmarksPanel* panel) + { + mParentLandmarksPanel = panel; + } + +private: + /** + * holds pointer to landmark panel. This pointer is used in @c LLPlacesFolderView::handleRightMouseDown + */ + LLLandmarksPanel* mParentLandmarksPanel; + typedef std::map<LLInventoryType::EType, LLHandle<LLView> > inventory_type_menu_handle_t; + inventory_type_menu_handle_t mMenuHandlesByInventoryType; + +}; + +#endif // LL_LLPLACESFOLDERVIEW_H + diff --git a/indra/newview/llplacesinventorybridge.cpp b/indra/newview/llplacesinventorybridge.cpp index fe4cc0f55f..ebd9604c5b 100644 --- a/indra/newview/llplacesinventorybridge.cpp +++ b/indra/newview/llplacesinventorybridge.cpp @@ -85,34 +85,33 @@ void LLPlacesLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags) void LLPlacesFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) { + std::vector<std::string> items; + std::vector<std::string> disabled_items; + + LLInventoryPanel* inv_panel = mInventoryPanel.get(); + bool is_open = false; + if (inv_panel) { - std::vector<std::string> items; - std::vector<std::string> disabled_items; + LLFolderViewFolder* folder = dynamic_cast<LLFolderViewFolder*>(inv_panel->getItemByID(mUUID)); + is_open = (NULL != folder) && folder->isOpen(); + } - LLInventoryPanel* inv_panel = mInventoryPanel.get(); - bool is_open = false; - if (inv_panel) - { - LLFolderViewFolder* folder = dynamic_cast<LLFolderViewFolder*>(inv_panel->getRootFolder()->getItemByID(mUUID)); - is_open = (NULL != folder) && folder->isOpen(); - } + // collect all items' names + fill_items_with_menu_items(items, menu); - // collect all items' names - fill_items_with_menu_items(items, menu); + // remove expand or collapse menu item depend on folder state + std::string collapse_expand_item_to_hide(is_open ? "expand" : "collapse"); + std::vector<std::string>::iterator it = std::find(items.begin(), items.end(), collapse_expand_item_to_hide); + if (it != items.end()) items.erase(it); - // remove expand or collapse menu item depend on folder state - std::string collapse_expand_item_to_hide(is_open ? "expand" : "collapse"); - std::vector<std::string>::iterator it = std::find(items.begin(), items.end(), collapse_expand_item_to_hide); - if (it != items.end()) items.erase(it); - // Disabled items are processed via LLLandmarksPanel::isActionEnabled() - // they should be synchronized with Places/My Landmarks/Gear menu. See EXT-1601 + // Disabled items are processed via LLLandmarksPanel::isActionEnabled() + // they should be synchronized with Places/My Landmarks/Gear menu. See EXT-1601 - // repeat parent functionality - sSelf = getHandle(); // necessary for "New Folder" functionality + // repeat parent functionality + sSelf = getHandle(); // necessary for "New Folder" functionality - hide_context_entries(menu, items, disabled_items); - } + hide_context_entries(menu, items, disabled_items); } //virtual @@ -140,7 +139,7 @@ LLFolderViewFolder* LLPlacesFolderBridge::getFolder() LLInventoryPanel* inv_panel = mInventoryPanel.get(); if (inv_panel) { - folder = dynamic_cast<LLFolderViewFolder*>(inv_panel->getRootFolder()->getItemByID(mUUID)); + folder = dynamic_cast<LLFolderViewFolder*>(inv_panel->getItemByID(mUUID)); } return folder; @@ -152,6 +151,7 @@ LLInvFVBridge* LLPlacesInventoryBridgeBuilder::createBridge( LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags/* = 0x00*/) const @@ -170,11 +170,12 @@ LLInvFVBridge* LLPlacesInventoryBridgeBuilder::createBridge( if (actual_asset_type == LLAssetType::AT_LINK_FOLDER) { // *TODO: Create a link folder handler instead if it is necessary - new_listener = LLInventoryFVBridgeBuilder::createBridge( + new_listener = LLInventoryFolderViewModelBuilder::createBridge( asset_type, actual_asset_type, inv_type, inventory, + view_model, root, uuid, flags); @@ -183,11 +184,12 @@ LLInvFVBridge* LLPlacesInventoryBridgeBuilder::createBridge( new_listener = new LLPlacesFolderBridge(inv_type, inventory, root, uuid); break; default: - new_listener = LLInventoryFVBridgeBuilder::createBridge( + new_listener = LLInventoryFolderViewModelBuilder::createBridge( asset_type, actual_asset_type, inv_type, inventory, + view_model, root, uuid, flags); diff --git a/indra/newview/llplacesinventorybridge.h b/indra/newview/llplacesinventorybridge.h index 52beacef9c..07d18d03c5 100644 --- a/indra/newview/llplacesinventorybridge.h +++ b/indra/newview/llplacesinventorybridge.h @@ -82,13 +82,14 @@ protected: * * It builds Bridges for Landmarks and Folders in Places Landmarks Panel */ -class LLPlacesInventoryBridgeBuilder : public LLInventoryFVBridgeBuilder +class LLPlacesInventoryBridgeBuilder : public LLInventoryFolderViewModelBuilder { public: /*virtual*/ LLInvFVBridge* createBridge(LLAssetType::EType asset_type, LLAssetType::EType actual_asset_type, LLInventoryType::EType inv_type, LLInventoryPanel* inventory, + LLFolderViewModelInventory* view_model, LLFolderView* root, const LLUUID& uuid, U32 flags = 0x00) const; diff --git a/indra/newview/llplacesinventorypanel.cpp b/indra/newview/llplacesinventorypanel.cpp index f7823f4fe8..01586a4d91 100644 --- a/indra/newview/llplacesinventorypanel.cpp +++ b/indra/newview/llplacesinventorypanel.cpp @@ -30,7 +30,8 @@ #include "llplacesinventorypanel.h" -#include "llfoldervieweventlistener.h" +#include "llfolderviewmodel.h" +#include "llplacesfolderview.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" #include "llpanellandmarks.h" @@ -57,44 +58,34 @@ LLPlacesInventoryPanel::~LLPlacesInventoryPanel() delete mSavedFolderState; } -void LLPlacesInventoryPanel::buildFolderView(const LLInventoryPanel::Params& params) -{ - // Determine the root folder in case specified, and - // build the views starting with that folder. - const LLFolderType::EType preferred_type = LLViewerFolderType::lookupTypeFromNewCategoryName(params.start_folder); - - LLUUID root_id; - if ("LIBRARY" == params.start_folder()) - { - root_id = gInventory.getLibraryRootFolderID(); - } - else - { - root_id = (preferred_type != LLFolderType::FT_NONE ? gInventory.findCategoryUUIDForType(preferred_type) : LLUUID::null); - } - - LLRect folder_rect(0, - 0, - getRect().getWidth(), - 0); - LLPlacesFolderView::Params p; - p.name = getName(); - p.title = getLabel(); - p.rect = folder_rect; - p.listener = mInvFVBridgeBuilder->createBridge(LLAssetType::AT_CATEGORY, - LLAssetType::AT_CATEGORY, - LLInventoryType::IT_CATEGORY, - this, - NULL, - root_id); - p.parent_panel = this; - p.allow_multiselect = mAllowMultiSelect; - p.use_ellipses = true; // truncate inventory item text so remove horizontal scroller - mFolderRoot = (LLFolderView*)LLUICtrlFactory::create<LLPlacesFolderView>(p); +LLFolderView * LLPlacesInventoryPanel::createFolderRoot(LLUUID root_id ) +{ + LLPlacesFolderView::Params p; + + p.name = getName(); + p.title = getLabel(); + p.rect = LLRect(0, 0, getRect().getWidth(), 0); + p.parent_panel = this; + p.tool_tip = p.name; + p.listener = mInvFVBridgeBuilder->createBridge( LLAssetType::AT_CATEGORY, + LLAssetType::AT_CATEGORY, + LLInventoryType::IT_CATEGORY, + this, + &mInventoryViewModel, + NULL, + root_id); + p.view_model = &mInventoryViewModel; + p.use_label_suffix = mParams.use_label_suffix; + p.allow_multiselect = mAllowMultiSelect; + p.show_empty_message = mShowEmptyMessage; + p.show_item_link_overlays = mShowItemLinkOverlays; + p.root = NULL; + p.use_ellipses = mParams.folder_view.use_ellipses; + + return LLUICtrlFactory::create<LLPlacesFolderView>(p); } - // save current folder open state void LLPlacesInventoryPanel::saveFolderState() { @@ -128,59 +119,3 @@ S32 LLPlacesInventoryPanel::notify(const LLSD& info) } return 0; } - -/************************************************************************/ -/* PROTECTED METHODS */ -/************************************************************************/ - - - -/************************************************************************/ -/* LLPlacesFolderView implementation */ -/************************************************************************/ - -////////////////////////////////////////////////////////////////////////// -// PUBLIC METHODS -////////////////////////////////////////////////////////////////////////// - -LLPlacesFolderView::LLPlacesFolderView(const LLFolderView::Params& p) -: LLFolderView(p) -{ - // we do not need auto select functionality in places landmarks, so override default behavior. - // this disables applying of the LLSelectFirstFilteredItem in LLFolderView::doIdle. - // Fixed issues: EXT-1631, EXT-4994. - mAutoSelectOverride = TRUE; -} - -BOOL LLPlacesFolderView::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - // let children to change selection first - childrenHandleRightMouseDown(x, y, mask); - mParentLandmarksPanel->setCurrentSelectedList((LLPlacesInventoryPanel*)getParentPanel()); - - // then determine its type and set necessary menu handle - if (getCurSelectedItem()) - { - LLInventoryType::EType inventory_type = getCurSelectedItem()->getListener()->getInventoryType(); - inventory_type_menu_handle_t::iterator it_handle = mMenuHandlesByInventoryType.find(inventory_type); - - if (it_handle != mMenuHandlesByInventoryType.end()) - { - mPopupMenuHandle = (*it_handle).second; - } - else - { - llwarns << "Requested menu handle for non-setup inventory type: " << inventory_type << llendl; - } - - } - - return LLFolderView::handleRightMouseDown(x, y, mask); -} - -void LLPlacesFolderView::setupMenuHandle(LLInventoryType::EType asset_type, LLHandle<LLView> menu_handle) -{ - mMenuHandlesByInventoryType[asset_type] = menu_handle; -} - -// EOF diff --git a/indra/newview/llplacesinventorypanel.h b/indra/newview/llplacesinventorypanel.h index f647e7f970..2805fc4257 100644 --- a/indra/newview/llplacesinventorypanel.h +++ b/indra/newview/llplacesinventorypanel.h @@ -29,9 +29,9 @@ #include "llfloaterinventory.h" #include "llinventorypanel.h" -#include "llfolderview.h" class LLLandmarksPanel; +class LLFolderView; class LLPlacesInventoryPanel : public LLInventoryPanel { @@ -46,8 +46,7 @@ public: LLPlacesInventoryPanel(const Params& p); ~LLPlacesInventoryPanel(); - /*virtual*/ void buildFolderView(const LLInventoryPanel::Params& params); - + LLFolderView * createFolderRoot(LLUUID root_id ); void saveFolderState(); void restoreFolderState(); @@ -57,36 +56,4 @@ private: LLSaveFolderState* mSavedFolderState; }; - -class LLPlacesFolderView : public LLFolderView -{ -public: - LLPlacesFolderView(const LLFolderView::Params& p); - /** - * Handles right mouse down - * - * Contains workaround for EXT-2786: sets current selected list for landmark - * panel using @c mParentLandmarksPanel which is set in @c LLLandmarksPanel::initLandmarksPanel - */ - /*virtual*/ BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); - - void setupMenuHandle(LLInventoryType::EType asset_type, LLHandle<LLView> menu_handle); - - void setParentLandmarksPanel(LLLandmarksPanel* panel) - { - mParentLandmarksPanel = panel; - } - - S32 getSelectedCount() { return (S32)mSelectedItems.size(); } - -private: - /** - * holds pointer to landmark panel. This pointer is used in @c LLPlacesFolderView::handleRightMouseDown - */ - LLLandmarksPanel* mParentLandmarksPanel; - typedef std::map<LLInventoryType::EType, LLHandle<LLView> > inventory_type_menu_handle_t; - inventory_type_menu_handle_t mMenuHandlesByInventoryType; - -}; - #endif //LL_LLINVENTORYSUBTREEPANEL_H diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 88727bf59b..29eb5ce69e 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -305,7 +305,11 @@ BOOL LLFloaterScriptSearch::handleKeyHere(KEY key, MASK mask) { if (mEditorCore) { - return mEditorCore->handleKeyHere(key, mask); + BOOL handled = mEditorCore->handleKeyHere(key, mask); + if (!handled) + { + LLFloater::handleKeyHere(key, mask); + } } return FALSE; diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp index d340b304ca..a4a0198305 100644 --- a/indra/newview/llscreenchannel.cpp +++ b/indra/newview/llscreenchannel.cpp @@ -253,12 +253,26 @@ void LLScreenChannel::addToast(const LLToast::Params& p) { bool store_toast = false, show_toast = false; - mDisplayToastsAlways ? show_toast = true : show_toast = mWasStartUpToastShown && (mShowToasts || p.force_show); + if (mDisplayToastsAlways) + { + show_toast = true; + } + else + { + show_toast = mWasStartUpToastShown && (mShowToasts || p.force_show); + } store_toast = !show_toast && p.can_be_stored && mCanStoreToasts; if(!show_toast && !store_toast) { - mRejectToastSignal(p.notif_id); + LLNotificationPtr notification = LLNotifications::instance().find(p.notif_id); + + if (notification && + (!notification->canLogToIM() || !notification->hasFormElements())) + { + // only cancel notification if it isn't being used in IM session + LLNotifications::instance().cancel(notification); + } return; } @@ -371,7 +385,7 @@ void LLScreenChannel::storeToast(ToastElem& toast_elem) const LLToast* toast = toast_elem.getToast(); if (toast) { - mStoredToastList.push_back(toast_elem); + mStoredToastList.push_back(toast_elem); mOnStoreToast(toast->getPanel(), toast->getNotificationID()); } } @@ -410,14 +424,14 @@ void LLScreenChannel::loadStoredToastByNotificationIDToChannel(LLUUID id) LLToast* toast = it->getToast(); if (toast) { - if(toast->getVisible()) - { - // toast is already in channel - return; - } + if(toast->getVisible()) + { + // toast is already in channel + return; + } - toast->setIsHidden(false); - toast->startTimer(); + toast->setIsHidden(false); + toast->startTimer(); mToastList.push_back(*it); } @@ -425,34 +439,12 @@ void LLScreenChannel::loadStoredToastByNotificationIDToChannel(LLUUID id) } //-------------------------------------------------------------------------- -void LLScreenChannel::removeStoredToastByNotificationID(LLUUID id) -{ - // *TODO: may be remove this function - std::vector<ToastElem>::iterator it = find(mStoredToastList.begin(), mStoredToastList.end(), id); - - if( it == mStoredToastList.end() ) - return; - - const LLToast* toast = it->getToast(); - if (toast) - { - mRejectToastSignal(toast->getNotificationID()); - } - - // Call find() once more, because the mStoredToastList could have been changed - // in mRejectToastSignal callback and the iterator could have become invalid. - it = find(mStoredToastList.begin(), mStoredToastList.end(), id); - if (it != mStoredToastList.end()) - { - mStoredToastList.erase(it); - } -} - -//-------------------------------------------------------------------------- void LLScreenChannel::killToastByNotificationID(LLUUID id) { // searching among toasts on a screen std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), id); + LLNotificationPtr notification = LLNotifications::instance().find(id); + if (!notification) return; if( it != mToastList.end()) { @@ -465,42 +457,67 @@ void LLScreenChannel::killToastByNotificationID(LLUUID id) // the toast will be destroyed. if(toast && toast->isNotificationValid()) { - mRejectToastSignal(toast->getNotificationID()); + if (!notification->canLogToIM() || !notification->hasFormElements()) + { + // only cancel notification if it isn't being used in IM session + LLNotifications::instance().cancel(notification); + } } else { - - deleteToast(toast); - mToastList.erase(it); - redrawToasts(); + removeToastByNotificationID(id); } - return; } - - // searching among stored toasts - it = find(mStoredToastList.begin(), mStoredToastList.end(), id); - - if (it != mStoredToastList.end()) + else { - LLToast* toast = it->getToast(); - if (toast) + // searching among stored toasts + it = find(mStoredToastList.begin(), mStoredToastList.end(), id); + + if( it != mStoredToastList.end() ) { - // send signal to a listener to let him perform some action on toast rejecting - mRejectToastSignal(toast->getNotificationID()); - deleteToast(toast); + LLToast* toast = it->getToast(); + if (toast) + { + if (!notification->canLogToIM() || !notification->hasFormElements()) + { + // only cancel notification if it isn't being used in IM session + LLNotifications::instance().cancel(notification); + } + deleteToast(toast); + } + } + + // Call find() once more, because the mStoredToastList could have been changed + // via notification cancellation and the iterator could have become invalid. + it = find(mStoredToastList.begin(), mStoredToastList.end(), id); + if (it != mStoredToastList.end()) + { + mStoredToastList.erase(it); } } +} + +void LLScreenChannel::removeToastByNotificationID(LLUUID id) +{ + std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), id); + while( it != mToastList.end()) + { + deleteToast(it->getToast()); + mToastList.erase(it); + redrawToasts(); + // find next toast with matching id + it = find(mToastList.begin(), mToastList.end(), id); + } - // Call find() once more, because the mStoredToastList could have been changed - // in mRejectToastSignal callback and the iterator could have become invalid. it = find(mStoredToastList.begin(), mStoredToastList.end(), id); if (it != mStoredToastList.end()) { + deleteToast(it->getToast()); mStoredToastList.erase(it); } - } + void LLScreenChannel::killMatchedToasts(const Matcher& matcher) { std::list<const LLToast*> to_delete = findToasts(matcher); @@ -521,11 +538,11 @@ void LLScreenChannel::modifyToastByNotificationID(LLUUID id, LLPanel* panel) LLToast* toast = it->getToast(); if (toast) { - LLPanel* old_panel = toast->getPanel(); - toast->removeChild(old_panel); - delete old_panel; - toast->insertPanel(panel); - toast->startTimer(); + LLPanel* old_panel = toast->getPanel(); + toast->removeChild(old_panel); + delete old_panel; + toast->insertPanel(panel); + toast->startTimer(); } redrawToasts(); } @@ -679,7 +696,7 @@ void LLScreenChannel::showToastsCentre() return; } - LLRect toast_rect; + LLRect toast_rect; S32 bottom = (getRect().mTop - getRect().mBottom)/2 + toast->getRect().getHeight()/2; std::vector<ToastElem>::reverse_iterator it; diff --git a/indra/newview/llscreenchannel.h b/indra/newview/llscreenchannel.h index 56a9cf8b4b..e5f4807ab7 100644 --- a/indra/newview/llscreenchannel.h +++ b/indra/newview/llscreenchannel.h @@ -84,6 +84,7 @@ public: // kill or modify a toast by its ID virtual void killToastByNotificationID(LLUUID id) {}; virtual void modifyToastNotificationByID(LLUUID id, LLSD data) {}; + virtual void removeToastByNotificationID(LLUUID id){}; // hide all toasts from screen, but not remove them from a channel virtual void hideToastsFromScreen() {}; @@ -175,6 +176,7 @@ public: void addToast(const LLToast::Params& p); // kill or modify a toast by its ID void killToastByNotificationID(LLUUID id); + void removeToastByNotificationID(LLUUID id); void killMatchedToasts(const Matcher& matcher); void modifyToastByNotificationID(LLUUID id, LLPanel* panel); // hide all toasts from screen, but not remove them from a channel @@ -195,8 +197,6 @@ public: void loadStoredToastsToChannel(); // finds a toast among stored by its Notification ID and throws it on a screen to a channel void loadStoredToastByNotificationIDToChannel(LLUUID id); - // removes a toast from stored finding it by its Notification ID - void removeStoredToastByNotificationID(LLUUID id); // removes from channel all toasts that belongs to the certain IM session void removeToastsBySessionID(LLUUID id); // remove all storable toasts from screen and store them @@ -227,16 +227,12 @@ public: // Channel's signals // signal on storing of faded toasts event - typedef boost::function<void (LLPanel* info_panel, const LLUUID id)> store_tost_callback_t; - typedef boost::signals2::signal<void (LLPanel* info_panel, const LLUUID id)> store_tost_signal_t; - store_tost_signal_t mOnStoreToast; - boost::signals2::connection setOnStoreToastCallback(store_tost_callback_t cb) { return mOnStoreToast.connect(cb); } - // signal on rejecting of a toast event - typedef boost::function<void (LLUUID id)> reject_tost_callback_t; - typedef boost::signals2::signal<void (LLUUID id)> reject_tost_signal_t; - reject_tost_signal_t mRejectToastSignal; boost::signals2::connection setOnRejectToastCallback(reject_tost_callback_t cb) { return mRejectToastSignal.connect(cb); } + typedef boost::signals2::signal<void (LLPanel* info_panel, const LLUUID id)> store_toast_signal_t; + boost::signals2::connection addOnStoreToastCallback(store_toast_signal_t::slot_type cb) { return mOnStoreToast.connect(cb); } private: + store_toast_signal_t mOnStoreToast; + class ToastElem { public: diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 3b52dd552f..c3c37141ed 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -390,7 +390,7 @@ LLObjectSelectionHandle LLSelectMgr::selectObjectAndFamily(LLViewerObject* obj, // don't include an avatar. LLViewerObject* root = obj; - while(!root->isAvatar() && root->getParent() && !root->isJointChild()) + while(!root->isAvatar() && root->getParent()) { LLViewerObject* parent = (LLViewerObject*)root->getParent(); if (parent->isAvatar()) @@ -684,7 +684,7 @@ void LLSelectMgr::deselectObjectAndFamily(LLViewerObject* object, BOOL send_to_s // don't include an avatar. LLViewerObject* root = object; - while(!root->isAvatar() && root->getParent() && !root->isJointChild()) + while(!root->isAvatar() && root->getParent()) { LLViewerObject* parent = (LLViewerObject*)root->getParent(); if (parent->isAvatar()) @@ -1397,7 +1397,7 @@ void LLSelectMgr::promoteSelectionToRoot() } LLViewerObject* parentp = object; - while(parentp->getParent() && !(parentp->isRootEdit() || parentp->isJointChild())) + while(parentp->getParent() && !(parentp->isRootEdit())) { parentp = (LLViewerObject*)parentp->getParent(); } @@ -4474,8 +4474,7 @@ struct LLSelectMgrApplyFlags : public LLSelectedObjectFunctor virtual bool apply(LLViewerObject* object) { if ( object->permModify() && // preemptive permissions check - object->isRoot() && // don't send for child objects - !object->isJointChild()) + object->isRoot()) // don't send for child objects { object->setFlags( mFlags, mState); } @@ -6330,8 +6329,6 @@ void LLSelectMgr::updateSelectionCenter() // matches the root prim's (affecting the orientation of the manipulators). bbox.addBBoxAgent( (mSelectedObjects->getFirstRootObject(TRUE))->getBoundingBoxAgent() ); - std::vector < LLViewerObject *> jointed_objects; - for (LLObjectSelection::iterator iter = mSelectedObjects->begin(); iter != mSelectedObjects->end(); iter++) { @@ -6349,11 +6346,6 @@ void LLSelectMgr::updateSelectionCenter() } bbox.addBBoxAgent( object->getBoundingBoxAgent() ); - - if (object->isJointChild()) - { - jointed_objects.push_back(object); - } } LLVector3 bbox_center_agent = bbox.getCenterAgent(); @@ -6643,19 +6635,19 @@ void LLSelectMgr::setAgentHUDZoom(F32 target_zoom, F32 current_zoom) bool LLObjectSelection::is_root::operator()(LLSelectNode *node) { LLViewerObject* object = node->getObject(); - return (object != NULL) && !node->mIndividualSelection && (object->isRootEdit() || object->isJointChild()); + return (object != NULL) && !node->mIndividualSelection && (object->isRootEdit()); } bool LLObjectSelection::is_valid_root::operator()(LLSelectNode *node) { LLViewerObject* object = node->getObject(); - return (object != NULL) && node->mValid && !node->mIndividualSelection && (object->isRootEdit() || object->isJointChild()); + return (object != NULL) && node->mValid && !node->mIndividualSelection && (object->isRootEdit()); } bool LLObjectSelection::is_root_object::operator()(LLSelectNode *node) { LLViewerObject* object = node->getObject(); - return (object != NULL) && (object->isRootEdit() || object->isJointChild()); + return (object != NULL) && (object->isRootEdit()); } LLObjectSelection::LLObjectSelection() : diff --git a/indra/newview/llsidepanelappearance.cpp b/indra/newview/llsidepanelappearance.cpp index 853656905c..b143240187 100644 --- a/indra/newview/llsidepanelappearance.cpp +++ b/indra/newview/llsidepanelappearance.cpp @@ -38,7 +38,7 @@ #include "llfiltereditor.h" #include "llfloaterreg.h" #include "llfloaterworldmap.h" -#include "llfoldervieweventlistener.h" +#include "llfolderviewmodel.h" #include "lloutfitobserver.h" #include "llpaneleditwearable.h" #include "llpaneloutfitsinventory.h" @@ -267,11 +267,11 @@ void LLSidepanelAppearance::onOpenOutfitButtonClicked() if (inventory_panel) { LLFolderView* root = inventory_panel->getRootFolder(); - LLFolderViewItem *outfit_folder = root->getItemByID(outfit_link->getLinkedUUID()); + LLFolderViewItem *outfit_folder = inventory_panel->getItemByID(outfit_link->getLinkedUUID()); if (outfit_folder) { outfit_folder->setOpen(!outfit_folder->isOpen()); - root->setSelectionFromRoot(outfit_folder,TRUE); + root->setSelection(outfit_folder,TRUE); root->scrollToShowSelection(); } } diff --git a/indra/newview/llsidepanelinventory.cpp b/indra/newview/llsidepanelinventory.cpp index 4f9ab318a5..8915bb2fef 100644 --- a/indra/newview/llsidepanelinventory.cpp +++ b/indra/newview/llsidepanelinventory.cpp @@ -36,6 +36,7 @@ #include "llfirstuse.h" #include "llfloatersidepanelcontainer.h" #include "llfoldertype.h" +#include "llfolderview.h" #include "llhttpclient.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" @@ -259,9 +260,8 @@ void LLSidepanelInventory::updateInbox() // const bool do_not_create_folder = false; - const bool do_not_find_in_library = false; - const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, do_not_create_folder, do_not_find_in_library); + const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, do_not_create_folder); // Set up observer to listen for creation of inbox if at least one of them doesn't exist if (inbox_id.isNull()) @@ -383,10 +383,10 @@ void LLSidepanelInventory::onToggleInboxBtn() { inboxPanel->setTargetDim(gSavedPerAccountSettings.getS32("InventoryInboxHeight")); if (inboxPanel->isInVisibleChain()) - { - gSavedPerAccountSettings.setU32("LastInventoryInboxActivity", time_corrected()); - } + { + gSavedPerAccountSettings.setU32("LastInventoryInboxActivity", time_corrected()); } +} else { gSavedPerAccountSettings.setS32("InventoryInboxHeight", inboxPanel->getTargetDim()); @@ -448,7 +448,7 @@ void LLSidepanelInventory::onInfoButtonClicked() void LLSidepanelInventory::onShareButtonClicked() { - LLAvatarActions::shareWithAvatars(); + LLAvatarActions::shareWithAvatars(this); } void LLSidepanelInventory::onShopButtonClicked() @@ -472,7 +472,7 @@ void LLSidepanelInventory::performActionOnSelection(const std::string &action) } } - current_item->getListener()->performAction(mPanelMainInventory->getActivePanel()->getModel(), action); + static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->performAction(mPanelMainInventory->getActivePanel()->getModel(), action); } void LLSidepanelInventory::onWearButtonClicked() @@ -662,7 +662,7 @@ LLInventoryItem *LLSidepanelInventory::getSelectedItem() return NULL; } } - const LLUUID &item_id = current_item->getListener()->getUUID(); + const LLUUID &item_id = static_cast<LLFolderViewModelItemInventory*>(current_item->getViewModelItem())->getUUID(); LLInventoryItem *item = gInventory.getItem(item_id); return item; } @@ -671,7 +671,7 @@ U32 LLSidepanelInventory::getSelectedCount() { int count = 0; - std::set<LLUUID> selection_list = mPanelMainInventory->getActivePanel()->getRootFolder()->getSelectionList(); + std::set<LLFolderViewItem*> selection_list = mPanelMainInventory->getActivePanel()->getRootFolder()->getSelectionList(); count += selection_list.size(); if ((count == 0) && mInboxEnabled && (mInventoryPanelInbox != NULL)) @@ -722,9 +722,9 @@ void LLSidepanelInventory::clearSelections(bool clearMain, bool clearInbox) updateVerbs(); } -std::set<LLUUID> LLSidepanelInventory::getInboxSelectionList() +std::set<LLFolderViewItem*> LLSidepanelInventory::getInboxSelectionList() { - std::set<LLUUID> inventory_selected_uuids; + std::set<LLFolderViewItem*> inventory_selected_uuids; if (mInboxEnabled && (mInventoryPanelInbox != NULL)) { diff --git a/indra/newview/llsidepanelinventory.h b/indra/newview/llsidepanelinventory.h index a33607f50d..e8b2808d4f 100644 --- a/indra/newview/llsidepanelinventory.h +++ b/indra/newview/llsidepanelinventory.h @@ -63,7 +63,7 @@ public: BOOL isMainInventoryPanelActive() const; void clearSelections(bool clearMain, bool clearInbox); - std::set<LLUUID> getInboxSelectionList(); + std::set<LLFolderViewItem*> getInboxSelectionList(); void showItemInfoPanel(); void showTaskInfoPanel(); diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index 3bb8e60787..6d13ebec37 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -2778,7 +2778,7 @@ void renderVisibility(LLSpatialGroup* group, LLCamera* camera) void renderCrossHairs(LLVector3 position, F32 size, LLColor4 color) { - gGL.diffuseColor4fv(color.mV); + gGL.color4fv(color.mV); gGL.begin(LLRender::LINES); { gGL.vertex3fv((position - LLVector3(size, 0.f, 0.f)).mV); @@ -3500,6 +3500,8 @@ void renderPhysicsShapes(LLSpatialGroup* group) LLViewerObject* object = drawable->getVObj(); if (object && object->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH) { + gGL.pushMatrix(); + gGL.multMatrix((F32*) object->getRegion()->mRenderMatrix.mMatrix); //push face vertices for terrain for (S32 i = 0; i < drawable->getNumFaces(); ++i) { @@ -3521,6 +3523,7 @@ void renderPhysicsShapes(LLSpatialGroup* group) } } } + gGL.popMatrix(); } } } @@ -4026,7 +4029,7 @@ void renderAgentTarget(LLVOAvatar* avatar) if (avatar->isSelf()) { renderCrossHairs(avatar->getPositionAgent(), 0.2f, LLColor4(1, 0, 0, 0.8f)); - renderCrossHairs(avatar->mDrawable->getPositionAgent(), 0.2f, LLColor4(1, 0, 0, 0.8f)); + renderCrossHairs(avatar->mDrawable->getPositionAgent(), 0.2f, LLColor4(0, 1, 0, 0.8f)); renderCrossHairs(avatar->mRoot.getWorldPosition(), 0.2f, LLColor4(1, 1, 1, 0.8f)); renderCrossHairs(avatar->mPelvisp->getWorldPosition(), 0.2f, LLColor4(0, 0, 1, 0.8f)); } diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index 07d2f1ad6f..46fd8c1290 100644 --- a/indra/newview/llspeakers.cpp +++ b/indra/newview/llspeakers.cpp @@ -84,6 +84,19 @@ bool LLSpeaker::isInVoiceChannel() return mStatus <= LLSpeaker::STATUS_VOICE_ACTIVE || mStatus == LLSpeaker::STATUS_MUTED; } +LLSpeakerUpdateSpeakerEvent::LLSpeakerUpdateSpeakerEvent(LLSpeaker* source) +: LLEvent(source, "Speaker update speaker event"), + mSpeakerID (source->mID) +{ +} + +LLSD LLSpeakerUpdateSpeakerEvent::getValue() +{ + LLSD ret; + ret["id"] = mSpeakerID; + return ret; +} + LLSpeakerUpdateModeratorEvent::LLSpeakerUpdateModeratorEvent(LLSpeaker* source) : LLEvent(source, "Speaker add moderator event"), mSpeakerID (source->mID), @@ -243,6 +256,47 @@ bool LLSpeakersDelayActionsStorage::onTimerActionCallback(const LLUUID& speaker_ // +// ModerationResponder +// + +class ModerationResponder : public LLHTTPClient::Responder +{ +public: + ModerationResponder(const LLUUID& session_id) + { + mSessionID = session_id; + } + + virtual void error(U32 status, const std::string& reason) + { + llwarns << status << ": " << reason << llendl; + + if ( gIMMgr ) + { + //403 == you're not a mod + //should be disabled if you're not a moderator + if ( 403 == status ) + { + gIMMgr->showSessionEventError( + "mute", + "not_a_mod_error", + mSessionID); + } + else + { + gIMMgr->showSessionEventError( + "mute", + "generic_request_error", + mSessionID); + } + } + } + +private: + LLUUID mSessionID; +}; + +// // LLSpeakerMgr // @@ -374,6 +428,7 @@ void LLSpeakerMgr::update(BOOL resort_ok) { speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); speakerp->mHasSpoken = TRUE; + fireEvent(new LLSpeakerUpdateSpeakerEvent(speakerp), "update_speaker"); } speakerp->mStatus = LLSpeaker::STATUS_SPEAKING; // interpolate between active color and full speaking color based on power of speech output @@ -466,6 +521,28 @@ void LLSpeakerMgr::updateSpeakerList() } } + else + { + // Check if the list is empty, except if it's Nearby Chat (session_id NULL). + LLUUID session_id = getSessionID(); + if ((mSpeakers.size() == 0) && (!session_id.isNull())) + { + // If the list is empty, we update it with whatever was used to initiate the call so that it doesn't stay empty too long. + // *TODO: Fix the server side code that sometimes forgets to send back the list of agents after a chat started + // (IOW, fix why we get no ChatterBoxSessionAgentListUpdates message after the initial ChatterBoxSessionStartReply) + LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(session_id); + for (uuid_vec_t::iterator it = session->mInitialTargetIDs.begin();it!=session->mInitialTargetIDs.end();++it) + { + // Allow to set buddies if they are on line. Allow any other avatar. + if (!LLAvatarTracker::instance().isBuddy(*it) || LLAvatarTracker::instance().isBuddyOnline(*it)) + { + setSpeaker(*it, "", LLSpeaker::STATUS_VOICE_ACTIVE, LLSpeaker::SPEAKER_AGENT); + } + } + // Also add the current agent + setSpeaker(gAgentID, "", LLSpeaker::STATUS_VOICE_ACTIVE, LLSpeaker::SPEAKER_AGENT); + } + } } void LLSpeakerMgr::setSpeakerNotInChannel(LLSpeaker* speakerp) @@ -548,6 +625,7 @@ void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id) { speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); speakerp->mHasSpoken = TRUE; + fireEvent(new LLSpeakerUpdateSpeakerEvent(speakerp), "update_speaker"); } } @@ -718,43 +796,6 @@ void LLIMSpeakerMgr::updateSpeakers(const LLSD& update) } } -class ModerationResponder : public LLHTTPClient::Responder -{ -public: - ModerationResponder(const LLUUID& session_id) - { - mSessionID = session_id; - } - - virtual void error(U32 status, const std::string& reason) - { - llwarns << status << ": " << reason << llendl; - - if ( gIMMgr ) - { - //403 == you're not a mod - //should be disabled if you're not a moderator - if ( 403 == status ) - { - gIMMgr->showSessionEventError( - "mute", - "not_a_mod_error", - mSessionID); - } - else - { - gIMMgr->showSessionEventError( - "mute", - "generic_request_error", - mSessionID); - } - } - } - -private: - LLUUID mSessionID; -}; - void LLIMSpeakerMgr::toggleAllowTextChat(const LLUUID& speaker_id) { LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id); diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h index b9358cf37c..8ab08661d3 100644 --- a/indra/newview/llspeakers.h +++ b/indra/newview/llspeakers.h @@ -29,7 +29,6 @@ #include "llevent.h" #include "lleventtimer.h" -#include "llspeakers.h" #include "llvoicechannel.h" class LLSpeakerMgr; @@ -80,6 +79,15 @@ public: BOOL mModeratorMutedText; }; +class LLSpeakerUpdateSpeakerEvent : public LLOldEvents::LLEvent +{ +public: + LLSpeakerUpdateSpeakerEvent(LLSpeaker* source); + /*virtual*/ LLSD getValue(); +private: + const LLUUID& mSpeakerID; +}; + class LLSpeakerUpdateModeratorEvent : public LLOldEvents::LLEvent { public: diff --git a/indra/newview/llspeakingindicatormanager.cpp b/indra/newview/llspeakingindicatormanager.cpp index 9b38bf22ff..900379ae1e 100644 --- a/indra/newview/llspeakingindicatormanager.cpp +++ b/indra/newview/llspeakingindicatormanager.cpp @@ -241,7 +241,7 @@ void SpeakingIndicatorManager::switchSpeakerIndicators(const speaker_ids_t& spea BOOL switch_current_on = switch_on; // we should show indicator for specified voice session only if this is current channel. EXT-5562. - if (switch_current_on && indicator->getTargetSessionID().notNull()) + if (switch_current_on) { switch_current_on = indicator->getTargetSessionID() == session_id; LL_DEBUGS("SpeakingIndicator") << "Session: " << session_id << ", target: " << indicator->getTargetSessionID() << ", the same? = " << switch_current_on << LL_ENDL; diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 218c35029e..c827b39d0e 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -63,6 +63,7 @@ #include "llmemorystream.h" #include "llmessageconfig.h" #include "llmoveview.h" +#include "llimfloatercontainer.h" #include "llnearbychat.h" #include "llnotifications.h" #include "llnotificationsutil.h" @@ -94,6 +95,7 @@ #include "llcallingcard.h" #include "llconsole.h" #include "llcontainerview.h" +#include "llconversationlog.h" #include "lldebugview.h" #include "lldrawable.h" #include "lleventnotifier.h" @@ -1268,6 +1270,8 @@ bool idle_startup() display_startup(); LLStartUp::setStartupState( STATE_MULTIMEDIA_INIT ); + LLConversationLog::getInstance(); + return FALSE; } @@ -1378,14 +1382,9 @@ bool idle_startup() LLVoiceClient::getInstance()->updateSettings(); display_startup(); - //gCacheName is required for nearby chat history loading - //so I just moved nearby history loading a few states further - if (gSavedPerAccountSettings.getBOOL("LogShowHistory")) - { - LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); - if (nearby_chat) nearby_chat->loadHistory(); - } - display_startup(); + // create a container's instance for start a controlling conversation windows + // by the voice's events + LLIMFloaterContainer::getInstance(); // *Note: this is where gWorldMap used to be initialized. @@ -2165,7 +2164,6 @@ bool idle_startup() LLAgentPicksInfo::getInstance()->requestNumberOfPicks(); - LLIMFloater::initIMFloater(); display_startup(); llassert(LLPathfindingManager::getInstance() != NULL); diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index 0cb6c85012..18e0d9d0d2 100644 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -433,13 +433,19 @@ BOOL LLIMWellWindow::ObjectRowPanel::handleRightMouseDown(S32 x, S32 y, MASK mas ////////////////////////////////////////////////////////////////////////// // PUBLIC METHODS +LLNotificationWellWindow::WellNotificationChannel::WellNotificationChannel(LLNotificationWellWindow* well_window) +: LLNotificationChannel(LLNotificationChannel::Params().name(well_window->getPathname())), + mWellWindow(well_window) +{ + connectToChannel("Notifications"); + connectToChannel("Group Notifications"); + connectToChannel("Offer"); +} + LLNotificationWellWindow::LLNotificationWellWindow(const LLSD& key) -: LLSysWellWindow(key) +: LLSysWellWindow(key) { - // init connections to the list's update events - connectListUpdaterToSignal("notify"); - connectListUpdaterToSignal("groupnotify"); - connectListUpdaterToSignal("offer"); + mNotificationUpdates.reset(new WellNotificationChannel(this)); } // static @@ -519,7 +525,7 @@ void LLNotificationWellWindow::initChannel() LLSysWellWindow::initChannel(); if(mChannel) { - mChannel->setOnStoreToastCallback(boost::bind(&LLNotificationWellWindow::onStoreToast, this, _1, _2)); + mChannel->addOnStoreToastCallback(boost::bind(&LLNotificationWellWindow::onStoreToast, this, _1, _2)); } } @@ -546,20 +552,6 @@ void LLNotificationWellWindow::onStoreToast(LLPanel* info_panel, LLUUID id) addItem(p); } -void LLNotificationWellWindow::connectListUpdaterToSignal(std::string notification_type) -{ - LLNotificationsUI::LLNotificationManager* manager = LLNotificationsUI::LLNotificationManager::getInstance(); - LLNotificationsUI::LLEventHandler* n_handler = manager->getHandlerForNotification(notification_type); - if(n_handler) - { - n_handler->setNotificationIDCallback(boost::bind(&LLNotificationWellWindow::removeItemByID, this, _1)); - } - else - { - llwarns << "LLSysWellWindow::connectListUpdaterToSignal() - could not get a handler for '" << notification_type <<"' type of notifications" << llendl; - } -} - void LLNotificationWellWindow::onItemClick(LLSysWellItem* item) { LLUUID id = item->getID(); @@ -574,6 +566,12 @@ void LLNotificationWellWindow::onItemClose(LLSysWellItem* item) mChannel->killToastByNotificationID(id); } +void LLNotificationWellWindow::onAdd( LLNotificationPtr notify ) +{ + removeItemByID(notify->getID()); +} + + /************************************************************************/ @@ -867,4 +865,4 @@ bool LLIMWellWindow::confirmCloseAll(const LLSD& notification, const LLSD& respo return false; } -// EOF + diff --git a/indra/newview/llsyswellwindow.h b/indra/newview/llsyswellwindow.h index 272e9cfcb1..378d5e0aa2 100644 --- a/indra/newview/llsyswellwindow.h +++ b/indra/newview/llsyswellwindow.h @@ -34,6 +34,7 @@ #include "llscreenchannel.h" #include "llscrollcontainer.h" #include "llimview.h" +#include "llnotifications.h" #include "boost/shared_ptr.hpp" @@ -48,6 +49,8 @@ class LLSysWellChiclet; class LLSysWellWindow : public LLTransientDockableFloater { public: + LOG_CLASS(LLSysWellWindow); + LLSysWellWindow(const LLSD& key); ~LLSysWellWindow(); BOOL postBuild(); @@ -111,7 +114,7 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void setVisible(BOOL visible); - + /*virtual*/ void onAdd(LLNotificationPtr notify); // Operating with items void addItem(LLSysWellItem::Params p); @@ -119,6 +122,18 @@ public: void closeAll(); protected: + struct WellNotificationChannel : public LLNotificationChannel + { + WellNotificationChannel(LLNotificationWellWindow*); + void onDelete(LLNotificationPtr notify) + { + mWellWindow->removeItemByID(notify->getID()); + } + + LLNotificationWellWindow* mWellWindow; + }; + + LLNotificationChannelPtr mNotificationUpdates; /*virtual*/ const std::string& getAnchorViewName() { return NOTIFICATION_WELL_ANCHOR_NAME; } private: @@ -126,12 +141,8 @@ private: void initChannel(); void clearScreenChannels(); - void onStoreToast(LLPanel* info_panel, LLUUID id); - // connect counter and list updaters to the corresponding signals - void connectListUpdaterToSignal(std::string notification_type); - // Handlers void onItemClick(LLSysWellItem* item); void onItemClose(LLSysWellItem* item); @@ -160,6 +171,8 @@ public: // LLIMSessionObserver observe triggers /*virtual*/ void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + /*virtual*/ void sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) {} + /*virtual*/ void sessionVoiceOrIMStarted(const LLUUID& session_id) {}; /*virtual*/ void sessionRemoved(const LLUUID& session_id); /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id); diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index ec36cf48c2..65f0290060 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -39,7 +39,7 @@ #include "llfocusmgr.h" #include "llviewertexture.h" #include "llfolderview.h" -#include "llfoldervieweventlistener.h" +#include "llfolderviewmodel.h" #include "llinventory.h" #include "llinventoryfunctions.h" #include "llinventorymodelbackgroundfetch.h" @@ -58,6 +58,7 @@ #include "lltoolmgr.h" #include "lltoolpipette.h" #include "llfiltereditor.h" +#include "llwindow.h" #include "lltool.h" #include "llviewerwindow.h" @@ -186,7 +187,7 @@ protected: F32 mContextConeOpacity; LLSaveFolderState mSavedFolderState; BOOL mSelectedItemPinned; - + LLRadioGroup* mModeSelector; LLScrollListCtrl* mLocalScrollCtrl; @@ -372,7 +373,7 @@ BOOL LLFloaterTexturePicker::handleKeyHere(KEY key, MASK mask) { if (!root_folder->getCurSelectedItem()) { - LLFolderViewItem* itemp = root_folder->getItemByID(gInventory.getRootFolderID()); + LLFolderViewItem* itemp = mInventoryPanel->getItemByID(gInventory.getRootFolderID()); if (itemp) { root_folder->setSelection(itemp, FALSE, FALSE); @@ -454,7 +455,7 @@ BOOL LLFloaterTexturePicker::postBuild() // Commented out to scroll to currently selected texture. See EXT-5403. // // store this filter as the default one - // mInventoryPanel->getRootFolder()->getFilter()->markDefault(); + // mInventoryPanel->getRootFolder()->getFilter().markDefault(); // Commented out to stop opening all folders with textures // mInventoryPanel->openDefaultFolderForType(LLFolderType::FT_TEXTURE); @@ -637,11 +638,10 @@ void LLFloaterTexturePicker::draw() LLFolderView* folder_view = mInventoryPanel->getRootFolder(); if (!folder_view) return; - LLInventoryFilter* filter = folder_view->getFilter(); - if (!filter) return; + LLFolderViewFilter& filter = static_cast<LLFolderViewModelInventory*>(folder_view->getFolderViewModel())->getFilter(); - bool is_filter_active = folder_view->getCompletedFilterGeneration() < filter->getCurrentGeneration() && - filter->isNotDefault(); + bool is_filter_active = folder_view->getViewModelItem()->getLastFilterGeneration() < filter.getCurrentGeneration() && + filter.isNotDefault(); // After inventory panel filter is applied we have to update // constraint rect for the selected item because of folder view @@ -651,8 +651,9 @@ void LLFloaterTexturePicker::draw() if (!is_filter_active && !mSelectedItemPinned) { folder_view->setPinningSelectedItem(mSelectedItemPinned); - folder_view->dirtyFilter(); - folder_view->arrangeFromRoot(); + folder_view->getViewModelItem()->dirtyFilter(); + //TODO RN: test..still works without this? + //folder_view->arrangeFromRoot(); mSelectedItemPinned = TRUE; } @@ -815,7 +816,7 @@ void LLFloaterTexturePicker::onSelectionChange(const std::deque<LLFolderViewItem if (items.size()) { LLFolderViewItem* first_item = items.front(); - LLInventoryItem* itemp = gInventory.getItem(first_item->getListener()->getUUID()); + LLInventoryItem* itemp = gInventory.getItem(static_cast<LLFolderViewModelItemInventory*>(first_item->getViewModelItem())->getUUID()); mNoCopyTextureSelected = FALSE; if (itemp) { @@ -1011,7 +1012,7 @@ void LLFloaterTexturePicker::onFilterEdit(const std::string& search_string ) else if (mInventoryPanel->getFilterSubString().empty()) { // first letter in search term, save existing folder open state - if (!mInventoryPanel->getRootFolder()->isFilterModified()) + if (!mInventoryPanel->getFilter().isNotDefault()) { mSavedFolderState.setApply(FALSE); mInventoryPanel->getRootFolder()->applyFunctorRecursively(mSavedFolderState); @@ -1325,7 +1326,7 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLUUID id) // (i.e. op == TEXTURE_SELECT) or texture changes via DnD. else if (mCommitOnSelection || op == TEXTURE_SELECT) mViewModel->setDirty(); // *TODO: shouldn't we be using setValue() here? - + if(floaterp->isDirty() || id.notNull()) // mModelView->setDirty does not work. { setTentative( FALSE ); @@ -1337,10 +1338,10 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLUUID id) } else { - mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE); - lldebugs << "mImageItemID: " << mImageItemID << llendl; - mImageAssetID = floaterp->getAssetID(); - lldebugs << "mImageAssetID: " << mImageAssetID << llendl; + mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE); + lldebugs << "mImageItemID: " << mImageItemID << llendl; + mImageAssetID = floaterp->getAssetID(); + lldebugs << "mImageAssetID: " << mImageAssetID << llendl; } if (op == TEXTURE_SELECT && mOnSelectCallback) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index a5eebf6c77..e8a4d8b2f2 100644..100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1249,6 +1249,12 @@ bool LLTextureFetchWorker::doWork(S32 param) S32 max_attempts; if (mGetStatus == HTTP_NOT_FOUND) { + if(mWriteToCacheState == NOT_WRITE) //map tiles + { + mState = DONE; + return true; // failed, means no map tile on the empty region. + } + mHTTPFailCount = max_attempts = 1; // Don't retry llwarns << "Texture missing from server (404): " << mUrl << llendl; diff --git a/indra/newview/lltoast.h b/indra/newview/lltoast.h index e1d99b1bcb..ea62f758f8 100644 --- a/indra/newview/lltoast.h +++ b/indra/newview/lltoast.h @@ -169,6 +169,7 @@ public: // get/set Toast's flags or states // get information whether the notification corresponding to the toast is valid or not bool isNotificationValid(); + // get toast's Notification ID const LLUUID getNotificationID() const { return mNotificationID;} // get toast's Session ID @@ -212,7 +213,7 @@ private: //LLRootHandle<LLToast> mHandle; - LLPanel* mWrapperPanel; + LLPanel* mWrapperPanel; // timer counts a lifetime of a toast std::auto_ptr<LLToastLifeTimer> mTimer; @@ -220,8 +221,8 @@ private: F32 mToastLifetime; // in seconds F32 mToastFadingTime; // in seconds - LLPanel* mPanel; - LLButton* mHideBtn; + LLPanel* mPanel; + LLButton* mHideBtn; LLColor4 mBgColor; bool mCanFade; diff --git a/indra/newview/lltoastgroupnotifypanel.cpp b/indra/newview/lltoastgroupnotifypanel.cpp index 75178a6ef8..ed350ea144 100644 --- a/indra/newview/lltoastgroupnotifypanel.cpp +++ b/indra/newview/lltoastgroupnotifypanel.cpp @@ -51,7 +51,7 @@ const S32 LLToastGroupNotifyPanel::DEFAULT_MESSAGE_MAX_LINE_COUNT = 7; -LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(LLNotificationPtr& notification) +LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(const LLNotificationPtr& notification) : LLToastPanel(notification), mInventoryOffer(NULL) { @@ -112,7 +112,7 @@ LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(LLNotificationPtr& notification style.font = date_font; pMessageText->appendText(timeStr + "\n", TRUE, style); - style.font = pMessageText->getDefaultFont(); + style.font = pMessageText->getFont(); pMessageText->appendText(message, TRUE, style); //attachment diff --git a/indra/newview/lltoastgroupnotifypanel.h b/indra/newview/lltoastgroupnotifypanel.h index 7794ec9f63..dfdc6ae559 100644 --- a/indra/newview/lltoastgroupnotifypanel.h +++ b/indra/newview/lltoastgroupnotifypanel.h @@ -47,13 +47,10 @@ class LLToastGroupNotifyPanel public: void close(); - static bool onNewNotification(const LLSD& notification); - - // Non-transient messages. You can specify non-default button // layouts (like one for script dialogs) by passing various // numbers in for "layout". - LLToastGroupNotifyPanel(LLNotificationPtr& notification); + LLToastGroupNotifyPanel(const LLNotificationPtr& notification); /*virtual*/ ~LLToastGroupNotifyPanel(); protected: diff --git a/indra/newview/lltoastimpanel.cpp b/indra/newview/lltoastimpanel.cpp index e0cb200ef5..75e6e3d13a 100644 --- a/indra/newview/lltoastimpanel.cpp +++ b/indra/newview/lltoastimpanel.cpp @@ -104,9 +104,9 @@ LLToastIMPanel::~LLToastIMPanel() } //virtual -BOOL LLToastIMPanel::handleMouseDown(S32 x, S32 y, MASK mask) +BOOL LLToastIMPanel::handleMouseUp(S32 x, S32 y, MASK mask) { - if (LLPanel::handleMouseDown(x,y,mask) == FALSE) + if (LLPanel::handleMouseUp(x,y,mask) == FALSE) { mNotification->respond(mNotification->getResponseTemplate()); } diff --git a/indra/newview/lltoastimpanel.h b/indra/newview/lltoastimpanel.h index a803387576..3eb11fb3bc 100644 --- a/indra/newview/lltoastimpanel.h +++ b/indra/newview/lltoastimpanel.h @@ -41,18 +41,18 @@ public: struct Params { LLNotificationPtr notification; - LLUUID avatar_id; - LLUUID session_id; - std::string from; - std::string time; - std::string message; + LLUUID avatar_id, + session_id; + std::string from, + time, + message; Params() {} }; LLToastIMPanel(LLToastIMPanel::Params &p); virtual ~LLToastIMPanel(); - /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask); private: void showInspector(); diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp index d629f3abac..4a49922656 100644 --- a/indra/newview/lltoastnotifypanel.cpp +++ b/indra/newview/lltoastnotifypanel.cpp @@ -45,6 +45,9 @@ const S32 BOTTOM_PAD = VPAD * 3; const S32 IGNORE_BTN_TOP_DELTA = 3*VPAD;//additional ignore_btn padding S32 BUTTON_WIDTH = 90; +// *TODO: magic numbers(???) - copied from llnotify.cpp(250) +const S32 MAX_LENGTH = 512 + 20 + DB_FIRST_NAME_BUF_SIZE + DB_LAST_NAME_BUF_SIZE + DB_INV_ITEM_NAME_BUF_SIZE; + //static const LLFontGL* LLToastNotifyPanel::sFont = NULL; @@ -52,172 +55,12 @@ const LLFontGL* LLToastNotifyPanel::sFontSmall = NULL; LLToastNotifyPanel::button_click_signal_t LLToastNotifyPanel::sButtonClickSignal; -LLToastNotifyPanel::LLToastNotifyPanel(const LLNotificationPtr& notification, const LLRect& rect, bool show_images) : -LLToastPanel(notification), -mTextBox(NULL), -mInfoPanel(NULL), -mControlPanel(NULL), -mNumOptions(0), -mNumButtons(0), -mAddedDefaultBtn(false), -mCloseNotificationOnDestroy(true) +LLToastNotifyPanel::LLToastNotifyPanel(const LLNotificationPtr& notification, const LLRect& rect, bool show_images) +: LLToastPanel(notification), + LLInstanceTracker<LLToastNotifyPanel, LLUUID>(notification->getID()) { - buildFromFile( "panel_notification.xml"); - if(rect != LLRect::null) - { - this->setShape(rect); - } - mInfoPanel = getChild<LLPanel>("info_panel"); - mControlPanel = getChild<LLPanel>("control_panel"); - BUTTON_WIDTH = gSavedSettings.getS32("ToastButtonWidth"); - // customize panel's attributes - // is it intended for displaying a tip? - mIsTip = notification->getType() == "notifytip"; - // is it a script dialog? - mIsScriptDialog = (notification->getName() == "ScriptDialog" || notification->getName() == "ScriptDialogGroup"); - // is it a caution? - // - // caution flag can be set explicitly by specifying it in the notification payload, or it can be set implicitly if the - // notify xml template specifies that it is a caution - // tip-style notification handle 'caution' differently -they display the tip in a different color - mIsCaution = notification->getPriority() >= NOTIFICATION_PRIORITY_HIGH; - - // setup parameters - // get a notification message - mMessage = notification->getMessage(); - // init font variables - if (!sFont) - { - sFont = LLFontGL::getFontSansSerif(); - sFontSmall = LLFontGL::getFontSansSerifSmall(); - } - // initialize - setFocusRoot(!mIsTip); - // get a form for the notification - LLNotificationFormPtr form(notification->getForm()); - // get number of elements - mNumOptions = form->getNumElements(); - - // customize panel's outfit - // preliminary adjust panel's layout - //move to the end - //mIsTip ? adjustPanelForTipNotice() : adjustPanelForScriptNotice(form); - - // adjust text options according to the notification type - // add a caution textbox at the top of a caution notification - if (mIsCaution && !mIsTip) - { - mTextBox = getChild<LLTextBox>("caution_text_box"); - } - else - { - mTextBox = getChild<LLTextEditor>("text_editor_box"); - } - - // *TODO: magic numbers(???) - copied from llnotify.cpp(250) - const S32 MAX_LENGTH = 512 + 20 + DB_FIRST_NAME_BUF_SIZE + DB_LAST_NAME_BUF_SIZE + DB_INV_ITEM_NAME_BUF_SIZE; - - mTextBox->setMaxTextLength(MAX_LENGTH); - mTextBox->setVisible(TRUE); - mTextBox->setPlainText(!show_images); - mTextBox->setValue(notification->getMessage()); - - // add buttons for a script notification - if (mIsTip) - { - adjustPanelForTipNotice(); + init(rect, show_images); } - else - { - std::vector<index_button_pair_t> buttons; - buttons.reserve(mNumOptions); - S32 buttons_width = 0; - // create all buttons and accumulate they total width to reshape mControlPanel - for (S32 i = 0; i < mNumOptions; i++) - { - LLSD form_element = form->getElement(i); - if (form_element["type"].asString() != "button") - { - // not a button. - continue; - } - if (form_element["name"].asString() == TEXTBOX_MAGIC_TOKEN) - { - // a textbox pretending to be a button. - continue; - } - LLButton* new_button = createButton(form_element, TRUE); - buttons_width += new_button->getRect().getWidth(); - S32 index = form_element["index"].asInteger(); - buttons.push_back(index_button_pair_t(index,new_button)); - } - if (buttons.empty()) - { - addDefaultButton(); - } - else - { - const S32 button_panel_width = mControlPanel->getRect().getWidth();// do not change width of the panel - S32 button_panel_height = mControlPanel->getRect().getHeight(); - //try get an average h_pad to spread out buttons - S32 h_pad = (button_panel_width - buttons_width) / (S32(buttons.size())); - if(h_pad < 2*HPAD) - { - /* - * Probably it is a scriptdialog toast - * for a scriptdialog toast h_pad can be < 2*HPAD if we have a lot of buttons. - * In last case set default h_pad to avoid heaping of buttons - */ - S32 button_per_row = button_panel_width / BUTTON_WIDTH; - h_pad = (button_panel_width % BUTTON_WIDTH) / (button_per_row - 1);// -1 because we do not need space after last button in a row - if(h_pad < 2*HPAD) // still not enough space between buttons ? - { - h_pad = 2*HPAD; - } - } - if (mIsScriptDialog) - { - // we are using default width for script buttons so we can determinate button_rows - //to get a number of rows we divide the required width of the buttons to button_panel_width - S32 button_rows = llceil(F32(buttons.size() - 1) * (BUTTON_WIDTH + h_pad) / button_panel_width); - //S32 button_rows = (buttons.size() - 1) * (BUTTON_WIDTH + h_pad) / button_panel_width; - //reserve one row for the ignore_btn - button_rows++; - //calculate required panel height for scripdialog notification. - button_panel_height = button_rows * (BTN_HEIGHT + VPAD) + IGNORE_BTN_TOP_DELTA + BOTTOM_PAD; - } - else - { - // in common case buttons can have different widths so we need to calculate button_rows according to buttons_width - //S32 button_rows = llceil(F32(buttons.size()) * (buttons_width + h_pad) / button_panel_width); - S32 button_rows = llceil(F32((buttons.size() - 1) * h_pad + buttons_width) / button_panel_width); - //calculate required panel height - button_panel_height = button_rows * (BTN_HEIGHT + VPAD) + BOTTOM_PAD; - } - - // we need to keep min width and max height to make visible all buttons, because width of the toast can not be changed - adjustPanelForScriptNotice(button_panel_width, button_panel_height); - updateButtonsLayout(buttons, h_pad); - // save buttons for later use in disableButtons() - mButtons.assign(buttons.begin(), buttons.end()); - } - } - // adjust panel's height to the text size - mInfoPanel->setFollowsAll(); - snapToMessageHeight(mTextBox, MAX_LENGTH); - - if(notification->isReusable()) - { - mButtonClickConnection = sButtonClickSignal.connect( - boost::bind(&LLToastNotifyPanel::onToastPanelButtonClicked, this, _1, _2)); - - if(notification->isRespondedTo()) - { - // User selected an option in toast, now disable required buttons in IM window - disableRespondedOptions(notification); - } - } -} void LLToastNotifyPanel::addDefaultButton() { LLSD form_element; @@ -235,7 +78,6 @@ void LLToastNotifyPanel::addDefaultButton() } LLButton* LLToastNotifyPanel::createButton(const LLSD& form_element, BOOL is_option) { - InstanceAndS32* userdata = new InstanceAndS32; userdata->mSelf = this; userdata->mButtonName = is_option ? form_element["name"].asString() : ""; @@ -245,14 +87,15 @@ LLButton* LLToastNotifyPanel::createButton(const LLSD& form_element, BOOL is_opt LLButton::Params p; bool make_small_btn = form_element["index"].asInteger() == -1 || form_element["index"].asInteger() == -2; const LLFontGL* font = make_small_btn ? sFontSmall: sFont; // for block and ignore buttons in script dialog - p.name(form_element["name"].asString()); - p.label(form_element["text"].asString()); - p.font(font); + p.name = form_element["name"].asString(); + p.label = form_element["text"].asString(); + p.font = font; p.rect.height = BTN_HEIGHT; p.click_callback.function(boost::bind(&LLToastNotifyPanel::onClickButton, userdata)); p.rect.width = BUTTON_WIDTH; p.auto_resize = false; p.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM); + p.enabled = !form_element.has("enabled") || form_element["enabled"].asBoolean(); if (mIsCaution) { p.image_color(LLUIColorTable::instance().getColor("ButtonCautionImageColor")); @@ -287,16 +130,11 @@ LLToastNotifyPanel::~LLToastNotifyPanel() mButtonClickConnection.disconnect(); std::for_each(mBtnCallbackData.begin(), mBtnCallbackData.end(), DeletePointer()); - if (mCloseNotificationOnDestroy && LLNotificationsUtil::find(mNotification->getID()) != NULL) - { - // let reusable notification be deleted - mNotification->setReusable(false); - if (!mNotification->isPersistent()) + if (mIsTip) { LLNotifications::getInstance()->cancel(mNotification); } } -} void LLToastNotifyPanel::updateButtonsLayout(const std::vector<index_button_pair_t>& buttons, S32 h_pad) { @@ -393,210 +231,374 @@ void LLToastNotifyPanel::adjustPanelForTipNotice() } } -typedef std::set<std::string> button_name_set_t; -typedef std::map<std::string, button_name_set_t> disable_button_map_t; +//typedef std::set<std::string> button_name_set_t; +//typedef std::map<std::string, button_name_set_t> disable_button_map_t; +// +//disable_button_map_t initUserGiveItemDisableButtonMap() +//{ +// // see EXT-5905 for disable rules +// +// disable_button_map_t disable_map; +// button_name_set_t buttons; +// +// buttons.insert("Show"); +// disable_map.insert(std::make_pair("Show", buttons)); +// +// buttons.insert("Discard"); +// disable_map.insert(std::make_pair("Discard", buttons)); +// +// buttons.insert("Mute"); +// disable_map.insert(std::make_pair("Mute", buttons)); +// +// return disable_map; +//} +// +//disable_button_map_t initTeleportOfferedDisableButtonMap() +//{ +// disable_button_map_t disable_map; +// button_name_set_t buttons; +// +// buttons.insert("Teleport"); +// buttons.insert("Cancel"); +// +// disable_map.insert(std::make_pair("Teleport", buttons)); +// disable_map.insert(std::make_pair("Cancel", buttons)); +// +// return disable_map; +//} +// +//disable_button_map_t initFriendshipOfferedDisableButtonMap() +//{ +// disable_button_map_t disable_map; +// button_name_set_t buttons; +// +// buttons.insert("Accept"); +// buttons.insert("Decline"); +// +// disable_map.insert(std::make_pair("Accept", buttons)); +// disable_map.insert(std::make_pair("Decline", buttons)); +// +// return disable_map; +//} +// +//button_name_set_t getButtonDisableList(const std::string& notification_name, const std::string& button_name) +//{ +// static disable_button_map_t user_give_item_disable_map = initUserGiveItemDisableButtonMap(); +// static disable_button_map_t teleport_offered_disable_map = initTeleportOfferedDisableButtonMap(); +// static disable_button_map_t friendship_offered_disable_map = initFriendshipOfferedDisableButtonMap(); +// +// disable_button_map_t::const_iterator it; +// disable_button_map_t::const_iterator it_end; +// disable_button_map_t search_map; +// +// if("UserGiveItem" == notification_name) +// { +// search_map = user_give_item_disable_map; +// } +// else if("TeleportOffered" == notification_name) +// { +// search_map = teleport_offered_disable_map; +// } +// else if("OfferFriendship" == notification_name) +// { +// search_map = friendship_offered_disable_map; +// } +// +// it = search_map.find(button_name); +// it_end = search_map.end(); +// +// if(it_end != it) +// { +// return it->second; +// } +// return button_name_set_t(); +//} + +//void LLToastNotifyPanel::disableButtons(const std::string& notification_name, const std::string& selected_button) +//{ + //button_name_set_t buttons = getButtonDisableList(notification_name, selected_button); + + //std::vector<index_button_pair_t>::const_iterator it = mButtons.begin(); + //for ( ; it != mButtons.end(); it++) + //{ + // LLButton* btn = it->second; + // if(buttons.find(btn->getName()) != buttons.end()) + // { + // btn->setEnabled(FALSE); + // } + //} +//} -disable_button_map_t initUserGiveItemDisableButtonMap() +// static +void LLToastNotifyPanel::onClickButton(void* data) { - // see EXT-5905 for disable rules - - disable_button_map_t disable_map; - button_name_set_t buttons; - - buttons.insert("Show"); - disable_map.insert(std::make_pair("Show", buttons)); + InstanceAndS32* self_and_button = (InstanceAndS32*)data; + LLToastNotifyPanel* self = self_and_button->mSelf; + std::string button_name = self_and_button->mButtonName; - buttons.insert("Discard"); - disable_map.insert(std::make_pair("Discard", buttons)); + LLSD response = self->mNotification->getResponseTemplate(); + if (!self->mAddedDefaultBtn && !button_name.empty()) + { + response[button_name] = true; + } - buttons.insert("Mute"); - disable_map.insert(std::make_pair("Mute", buttons)); + // disable all buttons + self->mControlPanel->setEnabled(FALSE); - return disable_map; + // this might repost notification with new form data/enabled buttons + self->mNotification->respond(response); } -disable_button_map_t initTeleportOfferedDisableButtonMap() +void LLToastNotifyPanel::init( LLRect rect, bool show_images ) { - disable_button_map_t disable_map; - button_name_set_t buttons; + deleteAllChildren(); - buttons.insert("Teleport"); - buttons.insert("Cancel"); + mTextBox = NULL; + mInfoPanel = NULL; + mControlPanel = NULL; + mNumOptions = 0; + mNumButtons = 0; + mAddedDefaultBtn = false; - disable_map.insert(std::make_pair("Teleport", buttons)); - disable_map.insert(std::make_pair("Cancel", buttons)); + buildFromFile( "panel_notification.xml"); + if(rect != LLRect::null) + { + this->setShape(rect); + } + mInfoPanel = getChild<LLPanel>("info_panel"); + mInfoPanel->setFollowsAll(); - return disable_map; -} + mControlPanel = getChild<LLPanel>("control_panel"); + BUTTON_WIDTH = gSavedSettings.getS32("ToastButtonWidth"); + // customize panel's attributes + // is it intended for displaying a tip? + mIsTip = mNotification->getType() == "notifytip"; + // is it a script dialog? + mIsScriptDialog = (mNotification->getName() == "ScriptDialog" || mNotification->getName() == "ScriptDialogGroup"); + // is it a caution? + // + // caution flag can be set explicitly by specifying it in the notification payload, or it can be set implicitly if the + // notify xml template specifies that it is a caution + // tip-style notification handle 'caution' differently -they display the tip in a different color + mIsCaution = mNotification->getPriority() >= NOTIFICATION_PRIORITY_HIGH; -disable_button_map_t initFriendshipOfferedDisableButtonMap() + // setup parameters + // get a notification message + mMessage = mNotification->getMessage(); + // init font variables + if (!sFont) { - disable_button_map_t disable_map; - button_name_set_t buttons; - - buttons.insert("Accept"); - buttons.insert("Decline"); - - disable_map.insert(std::make_pair("Accept", buttons)); - disable_map.insert(std::make_pair("Decline", buttons)); - - return disable_map; + sFont = LLFontGL::getFontSansSerif(); + sFontSmall = LLFontGL::getFontSansSerifSmall(); } + // initialize + setFocusRoot(!mIsTip); + // get a form for the notification + LLNotificationFormPtr form(mNotification->getForm()); + // get number of elements + mNumOptions = form->getNumElements(); -button_name_set_t getButtonDisableList(const std::string& notification_name, const std::string& button_name) -{ - static disable_button_map_t user_give_item_disable_map = initUserGiveItemDisableButtonMap(); - static disable_button_map_t teleport_offered_disable_map = initTeleportOfferedDisableButtonMap(); - static disable_button_map_t friendship_offered_disable_map = initFriendshipOfferedDisableButtonMap(); - - disable_button_map_t::const_iterator it; - disable_button_map_t::const_iterator it_end; - disable_button_map_t search_map; + // customize panel's outfit + // preliminary adjust panel's layout + //move to the end + //mIsTip ? adjustPanelForTipNotice() : adjustPanelForScriptNotice(form); - if("UserGiveItem" == notification_name) - { - search_map = user_give_item_disable_map; - } - else if(("TeleportOffered" == notification_name) || ("TeleportOffered_MaturityExceeded" == notification_name)) + // adjust text options according to the notification type + // add a caution textbox at the top of a caution notification + if (mIsCaution && !mIsTip) { - search_map = teleport_offered_disable_map; + mTextBox = getChild<LLTextBox>("caution_text_box"); } - else if("OfferFriendship" == notification_name) + else { - search_map = friendship_offered_disable_map; + mTextBox = getChild<LLTextEditor>("text_editor_box"); } - it = search_map.find(button_name); - it_end = search_map.end(); + mTextBox->setMaxTextLength(MAX_LENGTH); + mTextBox->setVisible(TRUE); + mTextBox->setPlainText(!show_images); + mTextBox->setValue(mNotification->getMessage()); - if(it_end != it) + // add buttons for a script notification + if (mIsTip) { - return it->second; + adjustPanelForTipNotice(); } - return button_name_set_t(); -} - -void LLToastNotifyPanel::disableButtons(const std::string& notification_name, const std::string& selected_button) + else { - button_name_set_t buttons = getButtonDisableList(notification_name, selected_button); - - std::vector<index_button_pair_t>::const_iterator it = mButtons.begin(); - for ( ; it != mButtons.end(); it++) + std::vector<index_button_pair_t> buttons; + buttons.reserve(mNumOptions); + S32 buttons_width = 0; + // create all buttons and accumulate they total width to reshape mControlPanel + for (S32 i = 0; i < mNumOptions; i++) { - LLButton* btn = it->second; - if(buttons.find(btn->getName()) != buttons.end()) + LLSD form_element = form->getElement(i); + if (form_element["type"].asString() != "button") { - btn->setEnabled(FALSE); + // not a button. + continue; } + if (form_element["name"].asString() == TEXTBOX_MAGIC_TOKEN) + { + // a textbox pretending to be a button. + continue; } + LLButton* new_button = createButton(form_element, TRUE); + buttons_width += new_button->getRect().getWidth(); + S32 index = form_element["index"].asInteger(); + buttons.push_back(index_button_pair_t(index,new_button)); } - -// static -void LLToastNotifyPanel::onClickButton(void* data) + if (buttons.empty()) { - InstanceAndS32* self_and_button = (InstanceAndS32*)data; - LLToastNotifyPanel* self = self_and_button->mSelf; - std::string button_name = self_and_button->mButtonName; - - LLSD response = self->mNotification->getResponseTemplate(); - if (!self->mAddedDefaultBtn && !button_name.empty()) - { - response[button_name] = true; + addDefaultButton(); } - - bool is_reusable = self->mNotification->isReusable(); - // When we call respond(), LLOfferInfo will delete itself in inventory_offer_callback(), - // lets copy it while it's still valid. - LLOfferInfo* old_info = static_cast<LLOfferInfo*>(self->mNotification->getResponder()); - LLOfferInfo* new_info = NULL; - if(is_reusable && old_info) + else { - new_info = new LLOfferInfo(*old_info); - self->mNotification->setResponder(new_info); - } - - self->mNotification->respond(response); - - if(is_reusable) + const S32 button_panel_width = mControlPanel->getRect().getWidth();// do not change width of the panel + S32 button_panel_height = mControlPanel->getRect().getHeight(); + //try get an average h_pad to spread out buttons + S32 h_pad = (button_panel_width - buttons_width) / (S32(buttons.size())); + if(h_pad < 2*HPAD) { - sButtonClickSignal(self->mNotification->getID(), button_name); - } - else + /* + * Probably it is a scriptdialog toast + * for a scriptdialog toast h_pad can be < 2*HPAD if we have a lot of buttons. + * In last case set default h_pad to avoid heaping of buttons + */ + S32 button_per_row = button_panel_width / BUTTON_WIDTH; + h_pad = (button_panel_width % BUTTON_WIDTH) / (button_per_row - 1);// -1 because we do not need space after last button in a row + if(h_pad < 2*HPAD) // still not enough space between buttons ? { - // disable all buttons - self->mControlPanel->setEnabled(FALSE); + h_pad = 2*HPAD; } } - -void LLToastNotifyPanel::onToastPanelButtonClicked(const LLUUID& notification_id, const std::string btn_name) + if (mIsScriptDialog) { - if(mNotification->getID() == notification_id) + // we are using default width for script buttons so we can determinate button_rows + //to get a number of rows we divide the required width of the buttons to button_panel_width + S32 button_rows = llceil(F32(buttons.size() - 1) * (BUTTON_WIDTH + h_pad) / button_panel_width); + //S32 button_rows = (buttons.size() - 1) * (BUTTON_WIDTH + h_pad) / button_panel_width; + //reserve one row for the ignore_btn + button_rows++; + //calculate required panel height for scripdialog notification. + button_panel_height = button_rows * (BTN_HEIGHT + VPAD) + IGNORE_BTN_TOP_DELTA + BOTTOM_PAD; + } + else { - disableButtons(mNotification->getName(), btn_name); - } + // in common case buttons can have different widths so we need to calculate button_rows according to buttons_width + //S32 button_rows = llceil(F32(buttons.size()) * (buttons_width + h_pad) / button_panel_width); + S32 button_rows = llceil(F32((buttons.size() - 1) * h_pad + buttons_width) / button_panel_width); + //calculate required panel height + button_panel_height = button_rows * (BTN_HEIGHT + VPAD) + BOTTOM_PAD; } -void LLToastNotifyPanel::disableRespondedOptions(const LLNotificationPtr& notification) -{ - LLSD response = notification->getResponse(); - for (LLSD::map_const_iterator response_it = response.beginMap(); - response_it != response.endMap(); ++response_it) - { - if (response_it->second.isBoolean() && response_it->second.asBoolean()) - { - // that after multiple responses there can be many pressed buttons - // need to process them all - disableButtons(notification->getName(), response_it->first); + // we need to keep min width and max height to make visible all buttons, because width of the toast can not be changed + adjustPanelForScriptNotice(button_panel_width, button_panel_height); + updateButtonsLayout(buttons, h_pad); + // save buttons for later use in disableButtons() + //mButtons.assign(buttons.begin(), buttons.end()); } } + // adjust panel's height to the text size + snapToMessageHeight(mTextBox, MAX_LENGTH); } + +//void LLToastNotifyPanel::onToastPanelButtonClicked(const LLUUID& notification_id, const std::string btn_name) +//{ +// if(mNotification->getID() == notification_id) +// { +// disableButtons(mNotification->getName(), btn_name); +// } +//} + +//void LLToastNotifyPanel::disableRespondedOptions(const LLNotificationPtr& notification) +//{ +// LLSD response = notification->getResponse(); +// for (LLSD::map_const_iterator response_it = response.beginMap(); +// response_it != response.endMap(); ++response_it) +// { +// if (response_it->second.isBoolean() && response_it->second.asBoolean()) +// { +// // that after multiple responses there can be many pressed buttons +// // need to process them all +// disableButtons(notification->getName(), response_it->first); +// } +// } +//} + + ////////////////////////////////////////////////////////////////////////// LLIMToastNotifyPanel::LLIMToastNotifyPanel(LLNotificationPtr& pNotification, const LLUUID& session_id, const LLRect& rect /* = LLRect::null */, - bool show_images /* = true */) - : mSessionID(session_id), LLToastNotifyPanel(pNotification, rect, show_images) + bool show_images /* = true */, LLTextBase* parent_text) +: mSessionID(session_id), LLToastNotifyPanel(pNotification, rect, show_images), + mParentText(parent_text) { - mTextBox->setFollowsAll(); + compactButtons(); } LLIMToastNotifyPanel::~LLIMToastNotifyPanel() { - // We shouldn't delete notification when IM floater exists - // since that notification will be reused by IM floater. - // This may happened when IM floater reloads messages, exactly when user - // changes layout of IM chat log(disable/enable plaintext mode). - // See EXT-6500 - LLIMFloater* im_floater = LLIMFloater::findInstance(mSessionID); - if (im_floater != NULL && !im_floater->isDead()) - { - mCloseNotificationOnDestroy = false; - } } void LLIMToastNotifyPanel::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */) + { + LLToastPanel::reshape(width, height, called_from_parent); + + snapToMessageHeight(mTextBox, MAX_LENGTH); + } + +void LLIMToastNotifyPanel::compactButtons() { - S32 text_height = mTextBox->getTextBoundingRect().getHeight(); - S32 widget_height = mTextBox->getRect().getHeight(); - S32 delta = text_height - widget_height; - LLRect rc = getRect(); + mTextBox->setFollowsAll(); - rc.setLeftTopAndSize(rc.mLeft, rc.mTop, width, height + delta); - height = rc.getHeight(); - width = rc.getWidth(); + //we can't set follows in xml since it broke toasts behavior + setFollows(FOLLOWS_LEFT|FOLLOWS_RIGHT|FOLLOWS_TOP); - bool is_width_changed = width != getRect().getWidth(); + const child_list_t* children = getControlPanel()->getChildList(); + S32 offset = 0; + // Children were added by addChild() which uses push_front to insert them into list, + // so to get buttons in correct order reverse iterator is used (EXT-5906) + for (child_list_t::const_reverse_iterator it = children->rbegin(); it != children->rend(); it++) + { + LLButton * button = dynamic_cast<LLButton*> (*it); + if (button != NULL) + { + button->setOrigin( offset,button->getRect().mBottom); + button->setLeftHPad(2 * HPAD); + button->setRightHPad(2 * HPAD); + // set zero width before perform autoResize() + button->setRect(LLRect(button->getRect().mLeft, + button->getRect().mTop, + button->getRect().mLeft, + button->getRect().mBottom)); + button->setAutoResize(true); + button->autoResize(); + offset += HPAD + button->getRect().getWidth(); + button->setFollowsNone(); + } + } - LLToastPanel::reshape(width, height, called_from_parent); + if (mParentText) + { + mParentText->needsReflow(); + } +} - // Notification height required to display the text message depends on - // the width of the text box thus if panel width is changed the text box - // width is also changed then reshape() is called to adjust proper height. - if (is_width_changed) +void LLIMToastNotifyPanel::updateNotification() { - reshape(width, height, called_from_parent); + init(LLRect(), true); } + +void LLIMToastNotifyPanel::init( LLRect rect, bool show_images ) +{ + LLToastNotifyPanel::init(LLRect(), show_images); + + compactButtons(); } + // EOF + diff --git a/indra/newview/lltoastnotifypanel.h b/indra/newview/lltoastnotifypanel.h index db517ec858..f93c7745af 100644 --- a/indra/newview/lltoastnotifypanel.h +++ b/indra/newview/lltoastnotifypanel.h @@ -47,7 +47,7 @@ class LLNotificationForm; * @deprecated this class will be removed after all toast panel types are * implemented in separate classes. */ -class LLToastNotifyPanel: public LLToastPanel +class LLToastNotifyPanel: public LLToastPanel, public LLInstanceTracker<LLToastNotifyPanel, LLUUID> { public: /** @@ -61,10 +61,14 @@ public: * implement right class for desired toast panel. @see LLGenericTipPanel as example. */ LLToastNotifyPanel(const LLNotificationPtr& pNotification, const LLRect& rect = LLRect::null, bool show_images = true); + + virtual void init( LLRect rect, bool show_images ); + virtual ~LLToastNotifyPanel(); LLPanel * getControlPanel() { return mControlPanel; } - void setCloseNotificationOnDestroy(bool close) { mCloseNotificationOnDestroy = close; } + virtual void updateNotification() {} + protected: LLButton* createButton(const LLSD& form_element, BOOL is_option); @@ -76,8 +80,6 @@ protected: }; std::vector<InstanceAndS32*> mBtnCallbackData; - bool mCloseNotificationOnDestroy; - typedef std::pair<int,LLButton*> index_button_pair_t; void adjustPanelForScriptNotice(S32 max_width, S32 max_height); void adjustPanelForTipNotice(); @@ -93,9 +95,9 @@ protected: /** * Disable specific button(s) based on notification name and clicked button */ - void disableButtons(const std::string& notification_name, const std::string& selected_button); + //void disableButtons(const std::string& notification_name, const std::string& selected_button); - std::vector<index_button_pair_t> mButtons; + //std::vector<index_button_pair_t> mButtons; // panel elements LLTextBase* mTextBox; @@ -118,7 +120,7 @@ protected: /** * Process response data. Will disable selected options */ - void disableRespondedOptions(const LLNotificationPtr& notification); + //void disableRespondedOptions(const LLNotificationPtr& notification); bool mIsTip; bool mAddedDefaultBtn; @@ -137,13 +139,23 @@ class LLIMToastNotifyPanel : public LLToastNotifyPanel { public: - LLIMToastNotifyPanel(LLNotificationPtr& pNotification, const LLUUID& session_id, const LLRect& rect = LLRect::null, bool show_images = true); + LLIMToastNotifyPanel(LLNotificationPtr& pNotification, + const LLUUID& session_id, + const LLRect& rect = LLRect::null, + bool show_images = true, + LLTextBase* parent_text = NULL); + + void compactButtons(); + + virtual void updateNotification(); + virtual void init( LLRect rect, bool show_images ); ~LLIMToastNotifyPanel(); /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); protected: + LLTextBase* mParentText; LLUUID mSessionID; }; diff --git a/indra/newview/lltoastpanel.cpp b/indra/newview/lltoastpanel.cpp index c33fde99c5..187aee207c 100644 --- a/indra/newview/lltoastpanel.cpp +++ b/indra/newview/lltoastpanel.cpp @@ -70,7 +70,7 @@ void LLToastPanel::snapToMessageHeight(LLTextBase* message, S32 maxLineCount) if (message->getVisible()) { S32 heightDelta = 0; - S32 maxTextHeight = message->getDefaultFont()->getLineHeight() * maxLineCount; + S32 maxTextHeight = message->getFont()->getLineHeight() * maxLineCount; LLRect messageRect = message->getRect(); S32 oldTextHeight = messageRect.getHeight(); @@ -80,11 +80,14 @@ void LLToastPanel::snapToMessageHeight(LLTextBase* message, S32 maxLineCount) S32 requiredTextHeight = message->getTextBoundingRect().getHeight(); S32 newTextHeight = llmin(requiredTextHeight, maxTextHeight); - //Calculate last delta height deducting previous heightDelta - heightDelta = newTextHeight - oldTextHeight - heightDelta; + heightDelta = newTextHeight - oldTextHeight; + S32 new_panel_height = llmax(getRect().getHeight() + heightDelta, MIN_PANEL_HEIGHT); //reshape the panel with new height - reshape( getRect().getWidth(), llmax(getRect().getHeight() + heightDelta, MIN_PANEL_HEIGHT)); + if (new_panel_height != getRect().getHeight()) + { + reshape( getRect().getWidth(), new_panel_height); + } } } @@ -98,7 +101,7 @@ LLToastPanel* LLToastPanel::buidPanelFromNotification( if ("notifytip" == notification->getType()) { // if it is online/offline notification - if ("FriendOffline" == notification->getName() || "FriendOnline" == notification->getName()) + if ("FriendOnlineOffline" == notification->getName()) { res = new LLPanelOnlineStatus(notification); } diff --git a/indra/newview/lltoastpanel.h b/indra/newview/lltoastpanel.h index 346e014d73..c22557206b 100644 --- a/indra/newview/lltoastpanel.h +++ b/indra/newview/lltoastpanel.h @@ -33,19 +33,13 @@ #include <string> -class LLToastPanelBase: public LLPanel -{ -public: - virtual void init(LLSD& data){}; -}; - /** * Base class for all panels that can be added to the toast. * All toast panels should contain necessary logic for representing certain notification * but shouldn't contain logic related to this panel lifetime control and positioning * on the parent view. */ -class LLToastPanel: public LLPanel { +class LLToastPanel : public LLPanel { public: LLToastPanel(const LLNotificationPtr&); virtual ~LLToastPanel() = 0; diff --git a/indra/newview/lltoastscriptquestion.cpp b/indra/newview/lltoastscriptquestion.cpp index feeb8ca77b..91ba8c0247 100644 --- a/indra/newview/lltoastscriptquestion.cpp +++ b/indra/newview/lltoastscriptquestion.cpp @@ -66,8 +66,8 @@ void LLToastScriptQuestion::snapToMessageHeight() if (mMessage->getVisible() && mFooter->getVisible()) { S32 heightDelta = 0; - S32 maxTextHeight = (mMessage->getDefaultFont()->getLineHeight() * MAX_LINES_COUNT) - + (mFooter->getDefaultFont()->getLineHeight() * MAX_LINES_COUNT); + S32 maxTextHeight = (mMessage->getFont()->getLineHeight() * MAX_LINES_COUNT) + + (mFooter->getFont()->getLineHeight() * MAX_LINES_COUNT); LLRect messageRect = mMessage->getRect(); LLRect footerRect = mFooter->getRect(); diff --git a/indra/newview/lltoastscripttextbox.cpp b/indra/newview/lltoastscripttextbox.cpp index 2529ec865a..45fbabad59 100644 --- a/indra/newview/lltoastscripttextbox.cpp +++ b/indra/newview/lltoastscripttextbox.cpp @@ -65,7 +65,7 @@ LLToastScriptTextbox::LLToastScriptTextbox(const LLNotificationPtr& notification pMessageText->clear(); LLStyle::Params style; - style.font = pMessageText->getDefaultFont(); + style.font = pMessageText->getFont(); pMessageText->appendText(message, TRUE, style); //submit button diff --git a/indra/newview/lltoastscripttextbox.h b/indra/newview/lltoastscripttextbox.h index 8e69d8834d..7d33446248 100644 --- a/indra/newview/lltoastscripttextbox.h +++ b/indra/newview/lltoastscripttextbox.h @@ -39,8 +39,6 @@ class LLToastScriptTextbox public: void close(); - static bool onNewNotification(const LLSD& notification); - // Non-transient messages. You can specify non-default button // layouts (like one for script dialogs) by passing various // numbers in for "layout". diff --git a/indra/newview/lltoolbarview.cpp b/indra/newview/lltoolbarview.cpp index 81ad96f39e..2b5acdc1e8 100644 --- a/indra/newview/lltoolbarview.cpp +++ b/indra/newview/lltoolbarview.cpp @@ -603,7 +603,7 @@ BOOL LLToolBarView::handleDragTool( S32 x, S32 y, const LLUUID& uuid, LLAssetTyp BOOL LLToolBarView::handleDropTool( void* cargo_data, S32 x, S32 y, LLToolBar* toolbar) { BOOL handled = FALSE; - LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data; + LLInventoryObject* inv_item = static_cast<LLInventoryObject*>(cargo_data); LLAssetType::EType type = inv_item->getType(); if (type == LLAssetType::AT_WIDGET) diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index c69999981c..94c97158a8 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -58,7 +58,6 @@ #include "llviewerwindow.h" #include "llvoavatarself.h" #include "llworld.h" -#include "llclipboard.h" // syntactic sugar #define callMemberFunction(object,ptrToMember) ((object).*(ptrToMember)) @@ -654,33 +653,41 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, sOperationId++; } + // For people drag and drop we don't need an actual inventory object, + // instead we need the current cargo id, which should be a person id. + bool is_uuid_dragged = (mSource == SOURCE_PEOPLE); + if (top_view) { handled = TRUE; for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++) { - LLInventoryObject* cargo = locateInventory(item, cat); + S32 local_x, local_y; + top_view->screenPointToLocal( x, y, &local_x, &local_y ); + EAcceptance item_acceptance = ACCEPT_NO; + LLInventoryObject* cargo = locateInventory(item, cat); if (cargo) { - S32 local_x, local_y; - top_view->screenPointToLocal( x, y, &local_x, &local_y ); - EAcceptance item_acceptance = ACCEPT_NO; handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, FALSE, mCargoTypes[mCurItemIndex], (void*)cargo, &item_acceptance, mToolTipMsg); - if (handled) - { - // use sort order to determine priority of acceptance - *acceptance = (EAcceptance)llmin((U32)item_acceptance, (U32)*acceptance); - } } - else + else if (is_uuid_dragged) { - return; + handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, FALSE, + mCargoTypes[mCurItemIndex], + (void*)&mCargoIDs[mCurItemIndex], + &item_acceptance, + mToolTipMsg); + } + if (handled) + { + // use sort order to determine priority of acceptance + *acceptance = (EAcceptance)llmin((U32)item_acceptance, (U32)*acceptance); } } @@ -697,20 +704,27 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++) { - LLInventoryObject* cargo = locateInventory(item, cat); + S32 local_x, local_y; + EAcceptance item_acceptance; + top_view->screenPointToLocal( x, y, &local_x, &local_y ); + LLInventoryObject* cargo = locateInventory(item, cat); if (cargo) { - S32 local_x, local_y; - - EAcceptance item_acceptance; - top_view->screenPointToLocal( x, y, &local_x, &local_y ); handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, TRUE, mCargoTypes[mCurItemIndex], (void*)cargo, &item_acceptance, mToolTipMsg); } + else if (is_uuid_dragged) + { + handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, FALSE, + mCargoTypes[mCurItemIndex], + (void*)&mCargoIDs[mCurItemIndex], + &item_acceptance, + mToolTipMsg); + } } } if (handled) @@ -727,17 +741,27 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++) { + EAcceptance item_acceptance = ACCEPT_NO; + LLInventoryObject* cargo = locateInventory(item, cat); // fix for EXT-3191 - if (NULL == cargo) return; - - EAcceptance item_acceptance = ACCEPT_NO; - handled = handled && root_view->handleDragAndDrop(x, y, mask, FALSE, - mCargoTypes[mCurItemIndex], - (void*)cargo, - &item_acceptance, - mToolTipMsg); + if (cargo) + { + handled = handled && root_view->handleDragAndDrop(x, y, mask, FALSE, + mCargoTypes[mCurItemIndex], + (void*)cargo, + &item_acceptance, + mToolTipMsg); + } + else if (is_uuid_dragged) + { + handled = handled && root_view->handleDragAndDrop(x, y, mask, FALSE, + mCargoTypes[mCurItemIndex], + (void*)&mCargoIDs[mCurItemIndex], + &item_acceptance, + mToolTipMsg); + } if (handled) { // use sort order to determine priority of acceptance @@ -757,17 +781,25 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++) { - LLInventoryObject* cargo = locateInventory(item, cat); + EAcceptance item_acceptance; + LLInventoryObject* cargo = locateInventory(item, cat); if (cargo) { - EAcceptance item_acceptance; handled = handled && root_view->handleDragAndDrop(x, y, mask, TRUE, mCargoTypes[mCurItemIndex], (void*)cargo, &item_acceptance, mToolTipMsg); } + else if (is_uuid_dragged) + { + handled = handled && root_view->handleDragAndDrop(x, y, mask, TRUE, + mCargoTypes[mCurItemIndex], + (void*)&mCargoIDs[mCurItemIndex], + &item_acceptance, + mToolTipMsg); + } } } @@ -780,7 +812,7 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, if (!handled) { // Disallow drag and drop to 3D from the outbox - const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false, false); + const LLUUID outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); if (outbox_id.notNull()) { for (S32 item_index = 0; item_index < (S32)mCargoIDs.size(); item_index++) @@ -2509,7 +2541,13 @@ LLInventoryObject* LLToolDragAndDrop::locateInventory( { item = NULL; cat = NULL; - if(mCargoIDs.empty()) return NULL; + + if (mCargoIDs.empty() + || (mSource == SOURCE_PEOPLE)) ///< There is no inventory item for people drag and drop. + { + return NULL; + } + if((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY)) { // The object should be in user inventory. @@ -2545,6 +2583,7 @@ LLInventoryObject* LLToolDragAndDrop::locateInventory( { item = (LLViewerInventoryItem*)gToolBarView->getDragItem(); } + if(item) return item; if(cat) return cat; return NULL; diff --git a/indra/newview/lltooldraganddrop.h b/indra/newview/lltooldraganddrop.h index 41aee484db..f17300a76a 100644 --- a/indra/newview/lltooldraganddrop.h +++ b/indra/newview/lltooldraganddrop.h @@ -67,7 +67,8 @@ public: SOURCE_WORLD, SOURCE_NOTECARD, SOURCE_LIBRARY, - SOURCE_VIEWER + SOURCE_VIEWER, + SOURCE_PEOPLE }; void beginDrag(EDragAndDropType type, diff --git a/indra/newview/llviewerassettype.cpp b/indra/newview/llviewerassettype.cpp index a4b1c2155f..08ba5a5f25 100644 --- a/indra/newview/llviewerassettype.cpp +++ b/indra/newview/llviewerassettype.cpp @@ -83,6 +83,8 @@ LLViewerAssetDictionary::LLViewerAssetDictionary() addEntry(LLViewerAssetType::AT_WIDGET, new ViewerAssetEntry(DAD_WIDGET)); + addEntry(LLViewerAssetType::AT_PERSON, new ViewerAssetEntry(DAD_PERSON)); + addEntry(LLViewerAssetType::AT_NONE, new ViewerAssetEntry(DAD_NONE)); }; diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 1f7cf0cdd4..c751550523 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -50,6 +50,9 @@ #include "llfloaterbump.h" #include "llfloaterbvhpreview.h" #include "llfloatercamera.h" +#include "llfloaterchatvoicevolume.h" +#include "llfloaterconversationlog.h" +#include "llfloaterconversationpreview.h" #include "llfloaterdeleteenvpreset.h" #include "llfloaterdisplayname.h" #include "llfloatereditdaycycle.h" @@ -114,6 +117,7 @@ #include "llfloatertranslationsettings.h" #include "llfloateruipreview.h" #include "llfloatervoiceeffect.h" +#include "llfloatervoicevolume.h" #include "llfloaterwhitelistentry.h" #include "llfloaterwindowsize.h" #include "llfloaterworldmap.h" @@ -137,7 +141,6 @@ #include "llscriptfloater.h" #include "llfloatermodelpreview.h" #include "llcommandhandler.h" -#include "llnearbychatbar.h" // *NOTE: Please add files in alphabetical order to keep merges easy. @@ -190,9 +193,10 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("bumps", "floater_bumps.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterBump>); LLFloaterReg::add("camera", "floater_camera.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCamera>); - LLFloaterReg::add("chat_bar", "floater_chat_bar.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLNearbyChatBar>); - + LLFloaterReg::add("chat_voice", "floater_voice_chat_volume.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterChatVoiceVolume>); + LLFloaterReg::add("nearby_chat", "floater_im_session.xml", (LLFloaterBuildFunc)&LLNearbyChat::buildFloater); LLFloaterReg::add("compile_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCompileQueue>); + LLFloaterReg::add("conversation", "floater_conversation_log.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterConversationLog>); LLFloaterReg::add("destinations", "floater_destinations.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterDestinations>); @@ -224,6 +228,7 @@ void LLViewerFloaterReg::registerFloaters() LLInspectGroupUtil::registerFloater(); LLInspectObjectUtil::registerFloater(); LLInspectRemoteObjectUtil::registerFloater(); + LLFloaterVoiceVolumeUtil::registerFloater(); LLNotificationsUI::registerFloater(); LLFloaterDisplayNameUtil::registerFloater(); @@ -268,6 +273,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("picks", "floater_picks.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSidePanelContainer>); LLFloaterReg::add("pref_joystick", "floater_joystick.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterJoystick>); LLFloaterReg::add("preview_anim", "floater_preview_animation.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPreviewAnim>, "preview"); + LLFloaterReg::add("preview_conversation", "floater_conversation_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterConversationPreview>); LLFloaterReg::add("preview_gesture", "floater_preview_gesture.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPreviewGesture>, "preview"); LLFloaterReg::add("preview_notecard", "floater_preview_notecard.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPreviewNotecard>, "preview"); LLFloaterReg::add("preview_script", "floater_script_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPreviewLSL>, "preview"); diff --git a/indra/newview/llviewerfoldertype.cpp b/indra/newview/llviewerfoldertype.cpp index a179b61cff..a179b61cff 100644..100755 --- a/indra/newview/llviewerfoldertype.cpp +++ b/indra/newview/llviewerfoldertype.cpp diff --git a/indra/newview/llviewergesture.cpp b/indra/newview/llviewergesture.cpp index a32a78cbf9..71608b5280 100644 --- a/indra/newview/llviewergesture.cpp +++ b/indra/newview/llviewergesture.cpp @@ -33,6 +33,7 @@ #include "llviewerinventory.h" #include "sound_ids.h" // for testing +#include "llfloaterreg.h" #include "llkeyboard.h" // for key shortcuts for testing #include "llinventorymodel.h" #include "llvoavatar.h" @@ -40,7 +41,7 @@ #include "llviewermessage.h" // send_guid_sound_trigger #include "llviewernetwork.h" #include "llagent.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" // Globals LLViewerGestureList gGestureList; @@ -130,7 +131,8 @@ void LLViewerGesture::doTrigger( BOOL send_chat ) { // Don't play nodding animation, since that might not blend // with the gesture animation. - LLNearbyChatBar::getInstance()->sendChatFromViewer(mOutputString, CHAT_TYPE_NORMAL, FALSE); + (LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat"))-> + sendChatFromViewer(mOutputString, CHAT_TYPE_NORMAL, FALSE); } } diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index b47a41c44c..a187318eb7 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -1030,12 +1030,7 @@ void CreateGestureCallback::fire(const LLUUID& inv_item) gFloaterView->adjustToFitScreen(preview, FALSE); } -void AddFavoriteLandmarkCallback::fire(const LLUUID& inv_item_id) -{ - if (mTargetLandmarkId.isNull()) return; - gInventory.rearrangeFavoriteLandmarks(inv_item_id, mTargetLandmarkId); -} LLInventoryCallbackManager gInventoryCallbacks; @@ -1308,7 +1303,7 @@ const std::string NEW_NOTECARD_NAME = "New Note"; // *TODO:Translate? (probably const std::string NEW_GESTURE_NAME = "New Gesture"; // *TODO:Translate? (probably not) // ! REFACTOR ! Really need to refactor this so that it's not a bunch of if-then statements... -void menu_create_inventory_item(LLFolderView* root, LLFolderBridge *bridge, const LLSD& userdata, const LLUUID& default_parent_uuid) +void menu_create_inventory_item(LLInventoryPanel* panel, LLFolderBridge *bridge, const LLSD& userdata, const LLUUID& default_parent_uuid) { std::string type_name = userdata.asString(); @@ -1332,7 +1327,7 @@ void menu_create_inventory_item(LLFolderView* root, LLFolderBridge *bridge, cons LLUUID category = gInventory.createNewCategory(parent_id, preferred_type, LLStringUtil::null); gInventory.notifyObservers(); - root->setSelectionByID(category, TRUE); + panel->setSelectionByID(category, TRUE); } else if ("lsl" == type_name) { @@ -1375,7 +1370,7 @@ void menu_create_inventory_item(LLFolderView* root, LLFolderBridge *bridge, cons llwarns << "Can't create unrecognized type " << type_name << llendl; } } - root->setNeedsAutoRename(TRUE); + panel->getRootFolder()->setNeedsAutoRename(TRUE); } LLAssetType::EType LLViewerInventoryItem::getType() const @@ -1449,348 +1444,6 @@ const std::string& LLViewerInventoryItem::getName() const return LLInventoryItem::getName(); } -/** - * Class to store sorting order of favorites landmarks in a local file. EXT-3985. - * It replaced previously implemented solution to store sort index in landmark's name as a "<N>@" prefix. - * Data are stored in user home directory. - */ -class LLFavoritesOrderStorage : public LLSingleton<LLFavoritesOrderStorage> - , public LLDestroyClass<LLFavoritesOrderStorage> -{ - LOG_CLASS(LLFavoritesOrderStorage); -public: - /** - * Sets sort index for specified with LLUUID favorite landmark - */ - void setSortIndex(const LLUUID& inv_item_id, S32 sort_index); - - /** - * Gets sort index for specified with LLUUID favorite landmark - */ - S32 getSortIndex(const LLUUID& inv_item_id); - void removeSortIndex(const LLUUID& inv_item_id); - - void getSLURL(const LLUUID& asset_id); - - /** - * Implementation of LLDestroyClass. Calls cleanup() instance method. - * - * It is important this callback is called before gInventory is cleaned. - * For now it is called from LLAppViewer::cleanup() -> LLAppViewer::disconnectViewer(), - * Inventory is cleaned later from LLAppViewer::cleanup() after LLAppViewer::disconnectViewer() is called. - * @see cleanup() - */ - static void destroyClass(); - - const static S32 NO_INDEX; -private: - friend class LLSingleton<LLFavoritesOrderStorage>; - LLFavoritesOrderStorage() : mIsDirty(false) { load(); } - ~LLFavoritesOrderStorage() { save(); } - - /** - * Removes sort indexes for items which are not in Favorites bar for now. - */ - void cleanup(); - - const static std::string SORTING_DATA_FILE_NAME; - - void load(); - void save(); - - void saveFavoritesSLURLs(); - - // Remove record of current user's favorites from file on disk. - void removeFavoritesRecordOfUser(); - - void onLandmarkLoaded(const LLUUID& asset_id, LLLandmark* landmark); - void storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl); - - typedef std::map<LLUUID, S32> sort_index_map_t; - sort_index_map_t mSortIndexes; - - typedef std::map<LLUUID, std::string> slurls_map_t; - slurls_map_t mSLURLs; - - bool mIsDirty; - - struct IsNotInFavorites - { - IsNotInFavorites(const LLInventoryModel::item_array_t& items) - : mFavoriteItems(items) - { - - } - - /** - * Returns true if specified item is not found among inventory items - */ - bool operator()(const sort_index_map_t::value_type& id_index_pair) const - { - LLPointer<LLViewerInventoryItem> item = gInventory.getItem(id_index_pair.first); - if (item.isNull()) return true; - - LLInventoryModel::item_array_t::const_iterator found_it = - std::find(mFavoriteItems.begin(), mFavoriteItems.end(), item); - - return found_it == mFavoriteItems.end(); - } - private: - LLInventoryModel::item_array_t mFavoriteItems; - }; - -}; - -const std::string LLFavoritesOrderStorage::SORTING_DATA_FILE_NAME = "landmarks_sorting.xml"; -const S32 LLFavoritesOrderStorage::NO_INDEX = -1; - -void LLFavoritesOrderStorage::setSortIndex(const LLUUID& inv_item_id, S32 sort_index) -{ - mSortIndexes[inv_item_id] = sort_index; - mIsDirty = true; -} - -S32 LLFavoritesOrderStorage::getSortIndex(const LLUUID& inv_item_id) -{ - sort_index_map_t::const_iterator it = mSortIndexes.find(inv_item_id); - if (it != mSortIndexes.end()) - { - return it->second; - } - return NO_INDEX; -} - -void LLFavoritesOrderStorage::removeSortIndex(const LLUUID& inv_item_id) -{ - mSortIndexes.erase(inv_item_id); - mIsDirty = true; -} - -void LLFavoritesOrderStorage::getSLURL(const LLUUID& asset_id) -{ - slurls_map_t::iterator slurl_iter = mSLURLs.find(asset_id); - if (slurl_iter != mSLURLs.end()) return; // SLURL for current landmark is already cached - - LLLandmark* lm = gLandmarkList.getAsset(asset_id, - boost::bind(&LLFavoritesOrderStorage::onLandmarkLoaded, this, asset_id, _1)); - if (lm) - { - onLandmarkLoaded(asset_id, lm); - } -} - -// static -void LLFavoritesOrderStorage::destroyClass() -{ - LLFavoritesOrderStorage::instance().cleanup(); - if (gSavedPerAccountSettings.getBOOL("ShowFavoritesOnLogin")) - { - LLFavoritesOrderStorage::instance().saveFavoritesSLURLs(); - } - else - { - LLFavoritesOrderStorage::instance().removeFavoritesRecordOfUser(); - } -} - -void LLFavoritesOrderStorage::load() -{ - // load per-resident sorting information - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SORTING_DATA_FILE_NAME); - - LLSD settings_llsd; - llifstream file; - file.open(filename); - if (file.is_open()) - { - LLSDSerialize::fromXML(settings_llsd, file); - } - - for (LLSD::map_const_iterator iter = settings_llsd.beginMap(); - iter != settings_llsd.endMap(); ++iter) - { - mSortIndexes.insert(std::make_pair(LLUUID(iter->first), (S32)iter->second.asInteger())); - } -} - -void LLFavoritesOrderStorage::saveFavoritesSLURLs() -{ - // Do not change the file if we are not logged in yet. - if (!LLLoginInstance::getInstance()->authSuccess()) - { - llwarns << "Cannot save favorites: not logged in" << llendl; - return; - } - - std::string user_dir = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""); - if (user_dir.empty()) - { - llwarns << "Cannot save favorites: empty user dir name" << llendl; - return; - } - - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); - llifstream in_file; - in_file.open(filename); - LLSD fav_llsd; - if (in_file.is_open()) - { - LLSDSerialize::fromXML(fav_llsd, in_file); - } - - const LLUUID fav_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - gInventory.collectDescendents(fav_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); - - LLSD user_llsd; - for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); it++) - { - LLSD value; - value["name"] = (*it)->getName(); - value["asset_id"] = (*it)->getAssetUUID(); - - slurls_map_t::iterator slurl_iter = mSLURLs.find(value["asset_id"]); - if (slurl_iter != mSLURLs.end()) - { - lldebugs << "Saving favorite: idx=" << (*it)->getSortField() << ", SLURL=" << slurl_iter->second << ", value=" << value << llendl; - value["slurl"] = slurl_iter->second; - user_llsd[(*it)->getSortField()] = value; - } - else - { - llwarns << "Not saving favorite " << value["name"] << ": no matching SLURL" << llendl; - } - } - - LLAvatarName av_name; - LLAvatarNameCache::get( gAgentID, &av_name ); - lldebugs << "Saved favorites for " << av_name.getLegacyName() << llendl; - fav_llsd[av_name.getLegacyName()] = user_llsd; - - llofstream file; - file.open(filename); - LLSDSerialize::toPrettyXML(fav_llsd, file); -} - -void LLFavoritesOrderStorage::removeFavoritesRecordOfUser() -{ - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); - LLSD fav_llsd; - llifstream file; - file.open(filename); - if (!file.is_open()) return; - LLSDSerialize::fromXML(fav_llsd, file); - - LLAvatarName av_name; - LLAvatarNameCache::get( gAgentID, &av_name ); - lldebugs << "Removed favorites for " << av_name.getLegacyName() << llendl; - if (fav_llsd.has(av_name.getLegacyName())) - { - fav_llsd.erase(av_name.getLegacyName()); - } - - llofstream out_file; - out_file.open(filename); - LLSDSerialize::toPrettyXML(fav_llsd, out_file); - -} - -void LLFavoritesOrderStorage::onLandmarkLoaded(const LLUUID& asset_id, LLLandmark* landmark) -{ - if (!landmark) return; - - LLVector3d pos_global; - if (!landmark->getGlobalPos(pos_global)) - { - // If global position was unknown on first getGlobalPos() call - // it should be set for the subsequent calls. - landmark->getGlobalPos(pos_global); - } - - if (!pos_global.isExactlyZero()) - { - LLLandmarkActions::getSLURLfromPosGlobal(pos_global, - boost::bind(&LLFavoritesOrderStorage::storeFavoriteSLURL, this, asset_id, _1)); - } -} - -void LLFavoritesOrderStorage::storeFavoriteSLURL(const LLUUID& asset_id, std::string& slurl) -{ - lldebugs << "Saving landmark SLURL: " << slurl << llendl; - mSLURLs[asset_id] = slurl; -} - -void LLFavoritesOrderStorage::save() -{ - // nothing to save if clean - if (!mIsDirty) return; - - // If we quit from the login screen we will not have an SL account - // name. Don't try to save, otherwise we'll dump a file in - // C:\Program Files\SecondLife\ or similar. JC - std::string user_dir = gDirUtilp->getLindenUserDir(); - if (!user_dir.empty()) - { - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SORTING_DATA_FILE_NAME); - LLSD settings_llsd; - - for(sort_index_map_t::const_iterator iter = mSortIndexes.begin(); iter != mSortIndexes.end(); ++iter) - { - settings_llsd[iter->first.asString()] = iter->second; - } - - llofstream file; - file.open(filename); - LLSDSerialize::toPrettyXML(settings_llsd, file); - } -} - -void LLFavoritesOrderStorage::cleanup() -{ - // nothing to clean - if (!mIsDirty) return; - - const LLUUID fav_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE); - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - gInventory.collectDescendents(fav_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); - - IsNotInFavorites is_not_in_fav(items); - - sort_index_map_t aTempMap; - //copy unremoved values from mSortIndexes to aTempMap - std::remove_copy_if(mSortIndexes.begin(), mSortIndexes.end(), - inserter(aTempMap, aTempMap.begin()), - is_not_in_fav); - - //Swap the contents of mSortIndexes and aTempMap - mSortIndexes.swap(aTempMap); -} - - -S32 LLViewerInventoryItem::getSortField() const -{ - return LLFavoritesOrderStorage::instance().getSortIndex(mUUID); -} - -void LLViewerInventoryItem::setSortField(S32 sortField) -{ - LLFavoritesOrderStorage::instance().setSortIndex(mUUID, sortField); - getSLURL(); -} - -void LLViewerInventoryItem::getSLURL() -{ - LLFavoritesOrderStorage::instance().getSLURL(mAssetUUID); -} - -const LLPermissions& LLViewerInventoryItem::getPermissions() const -{ - // Use the actual permissions of the symlink, not its parent. - return LLInventoryItem::getPermissions(); -} - const LLUUID& LLViewerInventoryItem::getCreatorUUID() const { if (const LLViewerInventoryItem *linked_item = getLinkedItem()) @@ -1861,17 +1514,6 @@ LLWearableType::EType LLViewerInventoryItem::getWearableType() const return LLWearableType::EType(getFlags() & LLInventoryItemFlags::II_FLAGS_WEARABLES_MASK); } - -time_t LLViewerInventoryItem::getCreationDate() const -{ - return LLInventoryItem::getCreationDate(); -} - -U32 LLViewerInventoryItem::getCRC32() const -{ - return LLInventoryItem::getCRC32(); -} - // *TODO: mantipov: should be removed with LMSortPrefix patch in llinventorymodel.cpp, EXT-3985 static char getSeparator() { return '@'; } BOOL LLViewerInventoryItem::extractSortFieldAndDisplayName(const std::string& name, S32* sortField, std::string* displayName) diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 7822ef4da6..3cf03c3bc5 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -34,7 +34,7 @@ #include <boost/signals2.hpp> // boost::signals2::trackable -class LLFolderView; +class LLInventoryPanel; class LLFolderBridge; class LLViewerInventoryCategory; @@ -60,10 +60,6 @@ public: virtual const LLUUID& getAssetUUID() const; virtual const LLUUID& getProtectedAssetUUID() const; // returns LLUUID::null if current agent does not have permission to expose this asset's UUID to the user virtual const std::string& getName() const; - virtual S32 getSortField() const; - virtual void setSortField(S32 sortField); - virtual void getSLURL(); //Caches SLURL for landmark. //*TODO: Find a better way to do it and remove this method from here. - virtual const LLPermissions& getPermissions() const; virtual const bool getIsFullPerm() const; // 'fullperm' in the popular sense: modify-ok & copy-ok & transfer-ok, no special god rules applied virtual const LLUUID& getCreatorUUID() const; virtual const std::string& getDescription() const; @@ -72,8 +68,11 @@ public: virtual bool isWearableType() const; virtual LLWearableType::EType getWearableType() const; virtual U32 getFlags() const; - virtual time_t getCreationDate() const; - virtual U32 getCRC32() const; // really more of a checksum. + + using LLInventoryItem::getPermissions; + using LLInventoryItem::getCreationDate; + using LLInventoryItem::setCreationDate; + using LLInventoryItem::getCRC32; static BOOL extractSortFieldAndDisplayName(const std::string& name, S32* sortField, std::string* displayName); @@ -285,18 +284,6 @@ public: void fire(const LLUUID& inv_item); }; -class AddFavoriteLandmarkCallback : public LLInventoryCallback -{ -public: - AddFavoriteLandmarkCallback() : mTargetLandmarkId(LLUUID::null) {} - void setTargetLandmarkId(const LLUUID& target_uuid) { mTargetLandmarkId = target_uuid; } - -private: - void fire(const LLUUID& inv_item); - - LLUUID mTargetLandmarkId; -}; - // misc functions //void inventory_reliable_callback(void**, S32 status); @@ -372,7 +359,7 @@ void copy_inventory_from_notecard(const LLUUID& destination_id, U32 callback_id = 0); -void menu_create_inventory_item(LLFolderView* root, +void menu_create_inventory_item(LLInventoryPanel* root, LLFolderBridge* bridge, const LLSD& userdata, const LLUUID& default_parent_uuid = LLUUID::null); diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp index 1aa9fd8a45..f8e988bc0c 100644 --- a/indra/newview/llviewerkeyboard.cpp +++ b/indra/newview/llviewerkeyboard.cpp @@ -27,11 +27,12 @@ #include "llviewerprecompiledheaders.h" #include "llappviewer.h" +#include "llfloaterreg.h" #include "llviewerkeyboard.h" #include "llmath.h" #include "llagent.h" #include "llagentcamera.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llviewercontrol.h" #include "llfocusmgr.h" #include "llmorphview.h" @@ -534,7 +535,7 @@ void stop_moving( EKeystate s ) void start_chat( EKeystate s ) { // start chat - LLNearbyChatBar::startChat(NULL); + LLNearbyChat::startChat(NULL); } void start_gesture( EKeystate s ) @@ -543,15 +544,15 @@ void start_gesture( EKeystate s ) if (KEYSTATE_UP == s && ! (focus_ctrlp && focus_ctrlp->acceptsTextInput())) { - if (LLNearbyChatBar::getInstance()->getCurrentChat().empty()) + if ((LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat"))->getCurrentChat().empty()) { // No existing chat in chat editor, insert '/' - LLNearbyChatBar::startChat("/"); + LLNearbyChat::startChat("/"); } else { // Don't overwrite existing text in chat editor - LLNearbyChatBar::startChat(NULL); + LLNearbyChat::startChat(NULL); } } } diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index d11e7e32c7..7990b81d92 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -3263,15 +3263,6 @@ bool enable_freeze_eject(const LLSD& avatar_id) return new_value; } - -void login_done(S32 which, void *user) -{ - llinfos << "Login done " << which << llendl; - - LLPanelLogin::closePanel(); -} - - bool callback_leave_group(const LLSD& notification, const LLSD& response) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); @@ -3475,7 +3466,8 @@ class LLTogglePanelPeopleTab : public view_listener_t if ( panel_name == "friends_panel" || panel_name == "groups_panel" - || panel_name == "nearby_panel") + || panel_name == "nearby_panel" + || panel_name == "blocked_panel") { return togglePeoplePanel(panel_name, param); } @@ -5499,16 +5491,6 @@ void toggle_debug_menus(void*) // gExportDialog = LLUploadDialog::modalUploadDialog("Exporting selected objects..."); // } // - -class LLCommunicateBlockList : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); - return true; - } -}; - class LLWorldSetHomeLocation : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -8299,9 +8281,6 @@ void initialize_menus() // Me > Movement view_listener_t::addMenu(new LLAdvancedAgentFlyingInfo(), "Agent.getFlying"); - // Communicate - view_listener_t::addMenu(new LLCommunicateBlockList(), "Communicate.BlockList"); - // World menu view_listener_t::addMenu(new LLWorldAlwaysRun(), "World.AlwaysRun"); view_listener_t::addMenu(new LLWorldCreateLandmark(), "World.CreateLandmark"); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index b048332e59..775280ca34 100755 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -184,6 +184,7 @@ bool friendship_offer_callback(const LLSD& notification, const LLSD& response) S32 option = LLNotificationsUtil::getSelectedOption(notification, response); LLMessageSystem* msg = gMessageSystem; const LLSD& payload = notification["payload"]; + LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID()); // add friend to recent people list LLRecentPeople::instance().add(payload["from_id"]); @@ -209,7 +210,6 @@ bool friendship_offer_callback(const LLSD& notification, const LLSD& response) msg->sendReliable(LLHost(payload["sender"].asString())); LLSD payload = notification["payload"]; - payload["SUPPRESS_TOAST"] = true; LLNotificationsUtil::add("FriendshipAcceptedByMe", notification["substitutions"], payload); break; @@ -217,7 +217,6 @@ bool friendship_offer_callback(const LLSD& notification, const LLSD& response) case 1: // Decline { LLSD payload = notification["payload"]; - payload["SUPPRESS_TOAST"] = true; LLNotificationsUtil::add("FriendshipDeclinedByMe", notification["substitutions"], payload); } @@ -246,6 +245,12 @@ bool friendship_offer_callback(const LLSD& notification, const LLSD& response) break; } + LLNotificationFormPtr modified_form(new LLNotificationForm(*notification_ptr->getForm())); + modified_form->setElementEnabled("Accept", false); + modified_form->setElementEnabled("Decline", false); + notification_ptr->updateForm(modified_form); + notification_ptr->repost(); + return false; } static LLNotificationFunctorRegistration friendship_offer_callback_reg("OfferFriendship", friendship_offer_callback); @@ -726,7 +731,7 @@ static void highlight_inventory_objects_in_panel(const std::vector<LLUUID>& item LLFolderView* fv = inventory_panel->getRootFolder(); if (fv) { - LLFolderViewItem* fv_item = fv->getItemByID(item_id); + LLFolderViewItem* fv_item = inventory_panel->getItemByID(item_id); if (fv_item) { LLFolderViewItem* fv_folder = fv_item->getParentFolder(); @@ -814,7 +819,13 @@ private: mSelectedItems.clear(); if (LLInventoryPanel::getActiveInventoryPanel()) { - mSelectedItems = LLInventoryPanel::getActiveInventoryPanel()->getRootFolder()->getSelectionList(); + std::set<LLFolderViewItem*> selection = LLInventoryPanel::getActiveInventoryPanel()->getRootFolder()->getSelectionList(); + for (std::set<LLFolderViewItem*>::iterator it = selection.begin(), end_it = selection.end(); + it != end_it; + ++it) + { + mSelectedItems.insert(static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID()); + } } mSelectedItems.erase(mMoveIntoFolderID); } @@ -849,7 +860,15 @@ private: } // get selected items (without destination folder) - selected_items_t selected_items = active_panel->getRootFolder()->getSelectionList(); + selected_items_t selected_items; + + std::set<LLFolderViewItem*> selection = LLInventoryPanel::getActiveInventoryPanel()->getRootFolder()->getSelectionList(); + for (std::set<LLFolderViewItem*>::iterator it = selection.begin(), end_it = selection.end(); + it != end_it; + ++it) + { + selected_items.insert(static_cast<LLFolderViewModelItemInventory*>((*it)->getViewModelItem())->getUUID()); + } selected_items.erase(mMoveIntoFolderID); // compare stored & current sets of selected items @@ -1155,7 +1174,7 @@ bool check_offer_throttle(const std::string& from_name, bool check_only) } } } - + // Return "true" if we have a preview method for that asset type, "false" otherwise bool check_asset_previewable(const LLAssetType::EType asset_type) { @@ -1478,16 +1497,15 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& itemp = (LLViewerInventoryItem*)gInventory.getItem(mObjectID); } + LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID()); + // For muting, we need to add the mute, then decline the offer. // This must be done here because: // * callback may be called immediately, // * adding the mute sends a message, // * we can't build two messages at once. - if (2 == button) // Block + if (IOR_MUTE == button) // Block { - LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID()); - - llassert(notification_ptr != NULL); if (notification_ptr != NULL) { gCacheName->get(mFromID, mFromGroup, boost::bind(&inventory_offer_mute_callback, _1, _2, _3)); @@ -1502,6 +1520,8 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& bool busy = gAgent.getBusy(); + LLNotificationFormPtr modified_form(notification_ptr ? new LLNotificationForm(*notification_ptr->getForm()) : new LLNotificationForm()); + switch(button) { case IOR_SHOW: @@ -1545,6 +1565,11 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& LL_WARNS("Messaging") << "inventory_offer_callback: unknown offer type" << LL_ENDL; break; } + + if (modified_form != NULL) + { + modified_form->setElementEnabled("Show", false); + } break; // end switch (mIM) @@ -1557,9 +1582,14 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& args["MESSAGE"] = log_message; LLNotificationsUtil::add("SystemMessageTip", args); } + break; case IOR_MUTE: + if (modified_form != NULL) + { + modified_form->setElementEnabled("Mute", false); + } // MUTE falls through to decline case IOR_DECLINE: { @@ -1595,6 +1625,13 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& { busy_message(gMessageSystem, mFromID); } + + if (modified_form != NULL) + { + modified_form->setElementEnabled("Show", false); + modified_form->setElementEnabled("Discard", false); + } + break; } default: @@ -1614,6 +1651,13 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& { delete this; } + + if (notification_ptr != NULL) + { + notification_ptr->updateForm(modified_form); + notification_ptr->repost(); + } + return false; } @@ -1991,6 +2035,15 @@ bool lure_callback(const LLSD& notification, const LLSD& response) lure_id); break; } + + LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID()); + + LLNotificationFormPtr modified_form(new LLNotificationForm(*notification_ptr->getForm())); + modified_form->setElementEnabled("Teleport", false); + modified_form->setElementEnabled("Cancel", false); + notification_ptr->updateForm(modified_form); + notification_ptr->repost(); + return false; } static LLNotificationFunctorRegistration lure_callback_reg("TeleportOffered", lure_callback); @@ -2244,12 +2297,11 @@ void god_message_name_cb(const LLAvatarName& av_name, LLChat chat, std::string m // Treat like a system message and put in chat history. chat.mText = av_name.getCompleteName() + ": " + message; - LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); - if(nearby_chat) + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat"); + if (nearby_chat) { nearby_chat->addMessage(chat); } - } void process_improved_im(LLMessageSystem *msg, void **user_data) @@ -2386,6 +2438,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) from_id, name, buffer, + IM_OFFLINE == offline, LLStringUtil::null, dialog, parent_estate_id, @@ -2420,12 +2473,12 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) LL_INFOS("Messaging") << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << LL_ENDL; bool mute_im = is_muted; - if (accept_im_from_only_friend && !is_friend) + if(accept_im_from_only_friend&&!is_friend) { if (!gIMMgr->isNonFriendSessionNotified(session_id)) { std::string message = LLTrans::getString("IM_unblock_only_groups_friends"); - gIMMgr->addMessage(session_id, from_id, name, message); + gIMMgr->addMessage(session_id, from_id, name, message, IM_OFFLINE == offline); gIMMgr->addNotifiedNonFriendSessionID(session_id); } @@ -2438,6 +2491,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) from_id, name, buffer, + IM_OFFLINE == offline, LLStringUtil::null, dialog, parent_estate_id, @@ -2778,6 +2832,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) from_id, name, buffer, + IM_OFFLINE == offline, ll_safe_string((char*)binary_bucket), IM_SESSION_INVITE, parent_estate_id, @@ -2841,13 +2896,12 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) // Note: lie to Nearby Chat, pretending that this is NOT an IM, because // IMs from obejcts don't open IM sessions. - LLNearbyChat* nearby_chat = LLNearbyChat::getInstance(); + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat"); if(!chat_from_system && nearby_chat) { chat.mOwnerID = from_id; LLSD args; args["slurl"] = location; - args["type"] = LLNotificationsUI::NT_NEARBYCHAT; // Look for IRC-style emotes here so object name formatting is correct std::string prefix = message.substr(0, 4); @@ -3591,7 +3645,6 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) // pass owner_id to chat so that we can display the remote // object inspect for an object that is chatting with you LLSD args; - args["type"] = LLNotificationsUI::NT_NEARBYCHAT; chat.mOwnerID = owner_id; if (gSavedSettings.getBOOL("TranslateChat") && chat.mSourceType != CHAT_SOURCE_SYSTEM) @@ -6798,7 +6851,6 @@ bool handle_lure_callback(const LLSD& notification, const LLSD& response) //*TODO please rewrite all keys to the same case, lower or upper payload["from_id"] = target_id; - payload["SUPPRESS_TOAST"] = true; LLNotificationsUtil::add("TeleportOfferSent", args, payload); // Add the recepient to the recent people list. diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 1447f133e6..2fe6cd578b 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -238,7 +238,6 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe mRotTime(0.f), mAngularVelocityRot(), mPreviousRotation(), - mJointInfo(NULL), mState(0), mMedia(NULL), mClickAction(0), @@ -284,12 +283,6 @@ LLViewerObject::~LLViewerObject() mInventory = NULL; } - if (mJointInfo) - { - delete mJointInfo; - mJointInfo = NULL; - } - if (mPartSourcep) { mPartSourcep->setDead(); @@ -340,9 +333,6 @@ void LLViewerObject::markDead() if (getParent()) { ((LLViewerObject *)getParent())->removeChild(this); - // go ahead and delete any jointinfo's that we find - delete mJointInfo; - mJointInfo = NULL; } // Mark itself as dead @@ -745,7 +735,7 @@ void LLViewerObject::addThisAndNonJointChildren(std::vector<LLViewerObject*>& ob iter != mChildList.end(); iter++) { LLViewerObject* child = *iter; - if ( (!child->isAvatar()) && (!child->isJointChild())) + if ( (!child->isAvatar())) { child->addThisAndNonJointChildren(objects); } @@ -796,6 +786,12 @@ BOOL LLViewerObject::setDrawableParent(LLDrawable* parentp) LLDrawable* old_parent = mDrawable->mParent; mDrawable->mParent = parentp; + if (parentp && mDrawable->isActive()) + { + parentp->makeActive(); + parentp->setState(LLDrawable::ACTIVE_CHILD); + } + gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE); if( (old_parent != parentp && old_parent) || (parentp && parentp->isActive())) @@ -1298,26 +1294,6 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, } } - U8 joint_type = 0; - mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_JointType, joint_type, block_num); - if (joint_type) - { - // create new joint info - if (!mJointInfo) - { - mJointInfo = new LLVOJointInfo; - } - mJointInfo->mJointType = (EHavokJointType) joint_type; - mesgsys->getVector3Fast(_PREHASH_ObjectData, _PREHASH_JointPivot, mJointInfo->mPivot, block_num); - mesgsys->getVector3Fast(_PREHASH_ObjectData, _PREHASH_JointAxisOrAnchor, mJointInfo->mAxisOrAnchor, block_num); - } - else if (mJointInfo) - { - // this joint info is no longer needed - delete mJointInfo; - mJointInfo = NULL; - } - break; } @@ -1436,8 +1412,8 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, val = (U16 *) &data[count]; #endif new_angv.set(U16_to_F32(val[VX], -size, size), - U16_to_F32(val[VY], -size, size), - U16_to_F32(val[VZ], -size, size)); + U16_to_F32(val[VY], -size, size), + U16_to_F32(val[VZ], -size, size)); setAngularVelocity(new_angv); break; @@ -1463,8 +1439,8 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, new_rot.mQ[VW] = U8_to_F32(data[12], -1.f, 1.f); new_angv.set(U8_to_F32(data[13], -size, size), - U8_to_F32(data[14], -size, size), - U8_to_F32(data[15], -size, size)); + U8_to_F32(data[14], -size, size), + U8_to_F32(data[15], -size, size) ); setAngularVelocity(new_angv); break; } @@ -1538,8 +1514,8 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, dp->unpackU16(val[VY], "AccY"); dp->unpackU16(val[VZ], "AccZ"); new_angv.set(U16_to_F32(val[VX], -64.f, 64.f), - U16_to_F32(val[VY], -64.f, 64.f), - U16_to_F32(val[VZ], -64.f, 64.f)); + U16_to_F32(val[VY], -64.f, 64.f), + U16_to_F32(val[VZ], -64.f, 64.f)); setAngularVelocity(new_angv); } break; @@ -1968,14 +1944,6 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, cur_parentp->removeChild(this); - if (mJointInfo && !parent_id) - { - // since this object is no longer parent-relative - // we make sure we delete any joint info - delete mJointInfo; - mJointInfo = NULL; - } - setChanged(MOVED | SILHOUETTE); if (mDrawable.notNull()) @@ -2078,7 +2046,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, || (new_angv != old_angv)) { if (new_rot != mPreviousRotation) - { + { resetRot(); } else if (new_angv != old_angv) @@ -2116,9 +2084,15 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, gPipeline.addDebugBlip(getPositionAgent(), color); } - if ((0.0f == vel_mag_sq) && - (0.0f == accel_mag_sq) && - (0.0f == getAngularVelocity().magVecSquared())) + const F32 MAG_CUTOFF = F_APPROXIMATELY_ZERO; + + llassert(vel_mag_sq >= 0.f); + llassert(accel_mag_sq >= 0.f); + llassert(getAngularVelocity().magVecSquared() >= 0.f); + + if ((MAG_CUTOFF >= vel_mag_sq) && + (MAG_CUTOFF >= accel_mag_sq) && + (MAG_CUTOFF >= getAngularVelocity().magVecSquared())) { mStatic = TRUE; // This object doesn't move! } @@ -2189,17 +2163,13 @@ BOOL LLViewerObject::isActive() const -BOOL LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { //static LLFastTimer::DeclareTimer ftm("Viewer Object"); //LLFastTimer t(ftm); - if (mDead) + if (!mDead) { - // It's dead. Don't update it. - return TRUE; - } - // CRO - don't velocity interp linked objects! // Leviathan - but DO velocity interp joints if (!mStatic && sVelocityInterpolate && !isSelected()) @@ -2208,88 +2178,12 @@ BOOL LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) F32 dt_raw = (F32)(time - mLastInterpUpdateSecs); F32 dt = mTimeDilation * dt_raw; - if (!mJointInfo) - { applyAngularVelocity(dt); - } - - LLViewerObject *parentp = (LLViewerObject *) getParent(); - if (mJointInfo) - { - if (parentp) - { - // do parent-relative stuff - LLVector3 ang_vel = getAngularVelocity(); - F32 omega = ang_vel.magVecSquared(); - F32 angle = 0.0f; - LLQuaternion dQ; - if (omega > 0.00001f) - { - omega = sqrt(omega); - angle = omega * dt; - dQ.setQuat(angle, ang_vel); - } - LLVector3 pos = getPosition(); - - if (HJT_HINGE == mJointInfo->mJointType) - { - // hinge = uniform circular motion - LLVector3 parent_pivot = getVelocity(); - LLVector3 parent_axis = getAcceleration(); - - angle = dt * (ang_vel * mJointInfo->mAxisOrAnchor); // AxisOrAnchor = axis - dQ.setQuat(angle, mJointInfo->mAxisOrAnchor); // AxisOrAnchor = axis - LLVector3 pivot_offset = pos - mJointInfo->mPivot; // pos in pivot-frame - pivot_offset = pivot_offset * dQ; // new rotated pivot-frame pos - pos = mJointInfo->mPivot + pivot_offset; // parent-frame - LLViewerObject::setPosition(pos); - LLQuaternion Q_PC = getRotation(); - setRotation(Q_PC * dQ); - mLastInterpUpdateSecs = time; - } - else if (HJT_POINT == mJointInfo->mJointType) - // || HJT_LPOINT == mJointInfo->mJointType) - { - // point-to-point = spin about axis and uniform circular motion - // of axis about the pivot point - // - // NOTE: this interpolation scheme is not quite good enough to - // reduce the bandwidth -- needs a gravitational correction. - // Similarly for hinges with axes that deviate from vertical. - - LLQuaternion Q_PC = getRotation(); - Q_PC = Q_PC * dQ; - setRotation(Q_PC); - LLVector3 pivot_to_child = - mJointInfo->mAxisOrAnchor; // AxisOrAnchor = anchor - pos = mJointInfo->mPivot + pivot_to_child * Q_PC; - LLViewerObject::setPosition(pos); - mLastInterpUpdateSecs = time; - } - /* else if (HJT_WHEEL == mJointInfo->mJointInfo) + if (isAttachment()) { - // wheel = uniform rotation about axis, with linear - // velocity interpolation (if any) - LLVector3 parent_axis = getAcceleration(); // HACK -- accel stores the parent-axis (parent-frame) - - LLQuaternion Q_PC = getRotation(); - - angle = dt * (parent_axis * ang_vel); - dQ.setQuat(angle, parent_axis); - - Q_PC = Q_PC * dQ; - setRotation(Q_PC); - - pos = getPosition() + dt * getVelocity(); - LLViewerObject::setPosition(pos); mLastInterpUpdateSecs = time; - }*/ - } - } - else if (isAttachment()) - { - mLastInterpUpdateSecs = time; - return TRUE; + return; } else { // Move object based on it's velocity and rotation @@ -2298,8 +2192,7 @@ BOOL LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) } updateDrawable(FALSE); - - return TRUE; + } } @@ -2978,6 +2871,21 @@ void LLViewerObject::updateInventory( { LLMemType mt(LLMemType::MTYPE_OBJECT); + std::list<LLUUID>::iterator begin = mPendingInventoryItemsIDs.begin(); + std::list<LLUUID>::iterator end = mPendingInventoryItemsIDs.end(); + + bool is_fetching = std::find(begin, end, item->getAssetUUID()) != end; + bool is_fetched = getInventoryItemByAsset(item->getAssetUUID()) != NULL; + + if (is_fetched || is_fetching) + { + return; + } + else + { + mPendingInventoryItemsIDs.push_back(item->getAssetUUID()); + } + // This slices the object into what we're concerned about on the // viewer. The simulator will take the permissions and transfer // ownership. @@ -3889,15 +3797,6 @@ void LLViewerObject::setPositionEdit(const LLVector3 &pos_edit, BOOL damped) ((LLViewerObject *)getParent())->setPositionEdit(pos_edit - position_offset); updateDrawable(damped); } - else if (isJointChild()) - { - // compute new parent-relative position - LLViewerObject *parent = (LLViewerObject *) getParent(); - LLQuaternion inv_parent_rot = parent->getRotation(); - inv_parent_rot.transQuat(); - LLVector3 pos_parent = (pos_edit - parent->getPositionRegion()) * inv_parent_rot; - LLViewerObject::setPosition(pos_parent, damped); - } else { LLViewerObject::setPosition(pos_edit, damped); @@ -3911,8 +3810,7 @@ LLViewerObject* LLViewerObject::getRootEdit() const { const LLViewerObject* root = this; while (root->mParent - && !(root->mJointInfo - || ((LLViewerObject*)root->mParent)->isAvatar()) ) + && !((LLViewerObject*)root->mParent)->isAvatar()) { root = (LLViewerObject*)root->mParent; } @@ -4621,19 +4519,11 @@ void LLViewerObject::clearIcon() LLViewerObject* LLViewerObject::getSubParent() { - if (isJointChild()) - { - return this; - } return (LLViewerObject*) getParent(); } const LLViewerObject* LLViewerObject::getSubParent() const { - if (isJointChild()) - { - return this; - } return (const LLViewerObject*) getParent(); } @@ -5503,9 +5393,12 @@ BOOL LLViewerObject::setFlagsWithoutUpdate(U32 flags, BOOL state) void LLViewerObject::setPhysicsShapeType(U8 type) { mPhysicsShapeUnknown = false; + if (type != mPhysicsShapeType) + { mPhysicsShapeType = type; mCostStale = true; } +} void LLViewerObject::setPhysicsGravity(F32 gravity) { @@ -5531,7 +5424,6 @@ U8 LLViewerObject::getPhysicsShapeType() const { if (mPhysicsShapeUnknown) { - mPhysicsShapeUnknown = false; gObjectList.updatePhysicsFlags(this); } diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 6f5f7aae42..1fb30db8f2 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -88,18 +88,6 @@ typedef void (*inventory_callback)(LLViewerObject*, S32 serial_num, void*); -// a small struct for keeping track of joints -struct LLVOJointInfo -{ - EHavokJointType mJointType; - LLVector3 mPivot; // parent-frame - // whether the below an axis or anchor (and thus its frame) - // depends on the joint type: - // HINGE ==> axis=parent-frame - // P2P ==> anchor=child-frame - LLVector3 mAxisOrAnchor; -}; - // for exporting textured materials from SL struct LLMaterialExportInfo { @@ -157,7 +145,7 @@ public: LLNameValue* getNVPair(const std::string& name) const; // null if no name value pair by that name // Object create and update functions - virtual BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + virtual void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); // Types of media we can associate enum { MEDIA_NONE = 0, MEDIA_SET = 1 }; @@ -188,8 +176,6 @@ public: virtual void updateRadius() {}; virtual F32 getVObjRadius() const; // default implemenation is mDrawable->getRadius() - BOOL isJointChild() const { return mJointInfo ? TRUE : FALSE; } - EHavokJointType getJointType() const { return mJointInfo ? mJointInfo->mJointType : HJT_INVALID; } // for jointed and other parent-relative hacks LLViewerObject* getSubParent(); const LLViewerObject* getSubParent() const; @@ -741,7 +727,6 @@ protected: LLQuaternion mAngularVelocityRot; // accumulated rotation from the angular velocity computations LLQuaternion mPreviousRotation; - LLVOJointInfo* mJointInfo; U8 mState; // legacy LLViewerObjectMedia* mMedia; // NULL if no media associated U8 mClickAction; diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index b433484783..9e5c3f14f8 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -49,6 +49,8 @@ #include "llstring.h" #include "llhudnametag.h" #include "lldrawable.h" +#include "llflexibleobject.h" +#include "llviewertextureanim.h" #include "xform.h" #include "llsky.h" #include "llviewercamera.h" @@ -78,11 +80,9 @@ extern F32 gMinObjectDistance; extern BOOL gAnimateTextures; -void dialog_refresh_all(); +#define MAX_CONCURRENT_PHYSICS_REQUESTS 256 -#define CULL_VIS -//#define ORPHAN_SPAM -//#define IGNORE_DEAD +void dialog_refresh_all(); // Global lists of objects - should go away soon. LLViewerObjectList gObjectList; @@ -909,8 +909,6 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world) const F64 frame_time = LLFrameTimer::getElapsedSeconds(); - std::vector<LLViewerObject*> kill_list; - S32 num_active_objects = 0; LLViewerObject *objectp = NULL; // Make a copy of the list in case something in idleUpdate() messes with it @@ -968,23 +966,19 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world) idle_iter != idle_end; idle_iter++) { objectp = *idle_iter; - if (objectp->idleUpdate(agent, world, frame_time)) - { - num_active_objects++; + llassert(objectp->isActive()); + objectp->idleUpdate(agent, world, frame_time); + } - else - { - // If Idle Update returns false, kill object! - kill_list.push_back(objectp); + + //update flexible objects + LLVolumeImplFlexible::updateClass(); + + //update animated textures + LLViewerTextureAnim::updateClass(); } - } - for (std::vector<LLViewerObject*>::iterator kill_iter = kill_list.begin(); - kill_iter != kill_list.end(); kill_iter++) - { - objectp = *kill_iter; - killObject(objectp); - } - } + + fetchObjectCosts(); fetchPhysicsFlags(); @@ -1052,7 +1046,7 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world) */ LLViewerStats::getInstance()->mNumObjectsStat.addValue((S32) mObjects.size()); - LLViewerStats::getInstance()->mNumActiveObjectsStat.addValue(num_active_objects); + LLViewerStats::getInstance()->mNumActiveObjectsStat.addValue(idle_count); LLViewerStats::getInstance()->mNumSizeCulledStat.addValue(mNumSizeCulled); LLViewerStats::getInstance()->mNumVisCulledStat.addValue(mNumVisCulled); } @@ -1073,8 +1067,6 @@ void LLViewerObjectList::fetchObjectCosts() LLSD id_list; U32 object_index = 0; - U32 count = 0; - for ( std::set<LLUUID>::iterator iter = mStaleObjectCost.begin(); iter != mStaleObjectCost.end(); @@ -1091,7 +1083,7 @@ void LLViewerObjectList::fetchObjectCosts() mStaleObjectCost.erase(iter++); - if (count++ >= 450) + if (object_index >= MAX_CONCURRENT_PHYSICS_REQUESTS) { break; } @@ -1136,7 +1128,7 @@ void LLViewerObjectList::fetchPhysicsFlags() for ( std::set<LLUUID>::iterator iter = mStalePhysicsFlags.begin(); iter != mStalePhysicsFlags.end(); - ++iter) + ) { // Check to see if a request for this object // has already been made. @@ -1146,12 +1138,14 @@ void LLViewerObjectList::fetchPhysicsFlags() mPendingPhysicsFlags.insert(*iter); id_list[object_index++] = *iter; } - } - // id_list should now contain all - // requests in mStalePhysicsFlags before, so clear - // it now - mStalePhysicsFlags.clear(); + mStalePhysicsFlags.erase(iter++); + + if (object_index >= MAX_CONCURRENT_PHYSICS_REQUESTS) + { + break; + } + } if ( id_list.size() > 0 ) { @@ -1407,9 +1401,9 @@ void LLViewerObjectList::removeFromActiveList(LLViewerObject* objectp) mActiveObjects[idx]->setListIndex(idx); } - mActiveObjects.pop_back(); + mActiveObjects.pop_back(); + } } -} void LLViewerObjectList::updateActive(LLViewerObject *objectp) { diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index d9918e633b..4b0e0598f6 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -529,9 +529,10 @@ void LLViewerShaderMgr::setShaders() { loaded = loadShadersInterface(); } - + if (loaded) - { + + { loaded = loadTransformShaders(); } diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 7f638a24bf..e1eb54bd24 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -2182,7 +2182,8 @@ void LLViewerFetchedTexture::setIsMissingAsset() } else { - llwarns << mUrl << ": Marking image as missing" << llendl; + //it is normal no map tile on an empty region. + //llwarns << mUrl << ": Marking image as missing" << llendl; } if (mHasFetcher) { diff --git a/indra/newview/llviewertextureanim.cpp b/indra/newview/llviewertextureanim.cpp index 9f1ac7c49c..2b364851a7 100644 --- a/indra/newview/llviewertextureanim.cpp +++ b/indra/newview/llviewertextureanim.cpp @@ -27,21 +27,37 @@ #include "llviewerprecompiledheaders.h" #include "llviewertextureanim.h" +#include "llvovolume.h" #include "llmath.h" #include "llerror.h" -LLViewerTextureAnim::LLViewerTextureAnim() : LLTextureAnim() +std::vector<LLViewerTextureAnim*> LLViewerTextureAnim::sInstanceList; + +LLViewerTextureAnim::LLViewerTextureAnim(LLVOVolume* vobj) : LLTextureAnim() { + mVObj = vobj; mLastFrame = -1.f; // Force an update initially mLastTime = 0.f; mOffS = mOffT = 0; mScaleS = mScaleT = 1; mRot = 0; + + mInstanceIndex = sInstanceList.size(); + sInstanceList.push_back(this); } LLViewerTextureAnim::~LLViewerTextureAnim() { + S32 end_idx = sInstanceList.size()-1; + + if (end_idx != mInstanceIndex) + { + sInstanceList[mInstanceIndex] = sInstanceList[end_idx]; + sInstanceList[mInstanceIndex]->mInstanceIndex = mInstanceIndex; + } + + sInstanceList.pop_back(); } void LLViewerTextureAnim::reset() @@ -50,6 +66,14 @@ void LLViewerTextureAnim::reset() mTimer.reset(); } +//static +void LLViewerTextureAnim::updateClass() +{ + for (std::vector<LLViewerTextureAnim*>::iterator iter = sInstanceList.begin(); iter != sInstanceList.end(); ++iter) + { + (*iter)->mVObj->animateTextures(); + } +} S32 LLViewerTextureAnim::animateTextures(F32 &off_s, F32 &off_t, F32 &scale_s, F32 &scale_t, diff --git a/indra/newview/llviewertextureanim.h b/indra/newview/llviewertextureanim.h index dd7bd0cb90..abbfabceb9 100644 --- a/indra/newview/llviewertextureanim.h +++ b/indra/newview/llviewertextureanim.h @@ -30,10 +30,18 @@ #include "lltextureanim.h" #include "llframetimer.h" +class LLVOVolume; + class LLViewerTextureAnim : public LLTextureAnim { +private: + static std::vector<LLViewerTextureAnim*> sInstanceList; + S32 mInstanceIndex; + public: - LLViewerTextureAnim(); + static void updateClass(); + + LLViewerTextureAnim(LLVOVolume* vobj); virtual ~LLViewerTextureAnim(); /*virtual*/ void reset(); @@ -51,6 +59,7 @@ public: F32 mRot; protected: + LLVOVolume* mVObj; LLFrameTimer mTimer; F64 mLastTime; F32 mLastFrame; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 98ea923272..3b2292c04d 100644..100755 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -56,6 +56,7 @@ // linden library includes #include "llaudioengine.h" // mute on minimize +#include "llchatentry.h" #include "indra_constants.h" #include "llassetstorage.h" #include "llerrorcontrol.h" @@ -187,7 +188,7 @@ #include "llviewerjoystick.h" #include "llviewernetwork.h" #include "llpostprocess.h" -#include "llnearbychatbar.h" +#include "llnearbychat.h" #include "llagentui.h" #include "llwearablelist.h" @@ -197,7 +198,6 @@ #include "llfloaternotificationsconsole.h" -#include "llnearbychat.h" #include "llwindowlistener.h" #include "llviewerwindowlistener.h" #include "llpaneltopinfobar.h" @@ -283,7 +283,7 @@ private: struct Line { Line(const std::string& in_text, S32 in_x, S32 in_y) : text(in_text), x(in_x), y(in_y) {} - std::string text; + std::string text; S32 x,y; }; @@ -1553,11 +1553,12 @@ LLViewerWindow::LLViewerWindow(const Params& p) // boost::lambda::var() constructs such a functor on the fly. mWindowListener.reset(new LLWindowListener(this, boost::lambda::var(gKeyboard))); mViewerWindowListener.reset(new LLViewerWindowListener(this)); - LLNotificationChannel::buildChannel("VW_alerts", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alert")); - LLNotificationChannel::buildChannel("VW_alertmodal", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alertmodal")); - LLNotifications::instance().getChannel("VW_alerts")->connectChanged(&LLViewerWindow::onAlert); - LLNotifications::instance().getChannel("VW_alertmodal")->connectChanged(&LLViewerWindow::onAlert); + mAlertsChannel.reset(new LLNotificationChannel("VW_alerts", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alert"))); + mModalAlertsChannel.reset(new LLNotificationChannel("VW_alertmodal", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alertmodal"))); + + mAlertsChannel->connectChanged(&LLViewerWindow::onAlert); + mModalAlertsChannel->connectChanged(&LLViewerWindow::onAlert); LLNotifications::instance().setIgnoreAllNotifications(gSavedSettings.getBOOL("IgnoreAllNotifications")); llinfos << "NOTE: ALL NOTIFICATIONS THAT OCCUR WILL GET ADDED TO IGNORE LIST FOR LATER RUNS." << llendl; @@ -2495,46 +2496,47 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) return TRUE; } + LLNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance<LLNearbyChat>("nearby_chat"); + // Traverses up the hierarchy if( keyboard_focus ) { - LLNearbyChatBar* nearby_chat = LLFloaterReg::findTypedInstance<LLNearbyChatBar>("chat_bar"); - if (nearby_chat) { - LLLineEditor* chat_editor = nearby_chat->getChatBox(); - - // arrow keys move avatar while chatting hack - if (chat_editor && chat_editor->hasFocus()) - { - // If text field is empty, there's no point in trying to move - // cursor with arrow keys, so allow movement - if (chat_editor->getText().empty() - || gSavedSettings.getBOOL("ArrowKeysAlwaysMove")) + LLChatEntry* chat_editor = nearby_chat->getChatBox(); + + // arrow keys move avatar while chatting hack + if (chat_editor && chat_editor->hasFocus()) { - // let Control-Up and Control-Down through for chat line history, - if (!(key == KEY_UP && mask == MASK_CONTROL) - && !(key == KEY_DOWN && mask == MASK_CONTROL)) + // If text field is empty, there's no point in trying to move + // cursor with arrow keys, so allow movement + if (chat_editor->getText().empty() + || gSavedSettings.getBOOL("ArrowKeysAlwaysMove")) { - switch(key) + // let Control-Up and Control-Down through for chat line history, + if (!(key == KEY_UP && mask == MASK_CONTROL) + && !(key == KEY_DOWN && mask == MASK_CONTROL)) { - case KEY_LEFT: - case KEY_RIGHT: - case KEY_UP: - case KEY_DOWN: - case KEY_PAGE_UP: - case KEY_PAGE_DOWN: - case KEY_HOME: - // when chatbar is empty or ArrowKeysAlwaysMove set, - // pass arrow keys on to avatar... - return FALSE; - default: - break; + switch(key) + { + case KEY_LEFT: + case KEY_RIGHT: + case KEY_UP: + case KEY_DOWN: + case KEY_PAGE_UP: + case KEY_PAGE_DOWN: + case KEY_HOME: + // when chatbar is empty or ArrowKeysAlwaysMove set, + // pass arrow keys on to avatar... + return FALSE; + default: + break; + } } } } } - } + if (keyboard_focus->handleKey(key, mask, FALSE)) { return TRUE; @@ -2562,14 +2564,21 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) // If "Pressing letter keys starts local chat" option is selected, we are not in mouselook, // no view has keyboard focus, this is a printable character key (and no modifier key is // pressed except shift), then give focus to nearby chat (STORM-560) - if ( gSavedSettings.getS32("LetterKeysFocusChatBar") && !gAgentCamera.cameraMouselook() && + if ( gSavedSettings.getS32("LetterKeysFocusChatBar") && !gAgentCamera.cameraMouselook() && !keyboard_focus && key < 0x80 && (mask == MASK_NONE || mask == MASK_SHIFT) ) { - LLLineEditor* chat_editor = LLFloaterReg::getTypedInstance<LLNearbyChatBar>("chat_bar")->getChatBox(); + // Initialize nearby chat if it's missing + if (!nearby_chat) + { + LLSD name("im_container"); + LLFloaterReg::toggleInstanceOrBringToFront(name); + } + + LLChatEntry* chat_editor = LLFloaterReg::findTypedInstance<LLNearbyChat>("nearby_chat")->getChatBox(); if (chat_editor) { // passing NULL here, character will be added later when it is handled by character handler. - LLNearbyChatBar::getInstance()->startChat(NULL); + nearby_chat->startChat(NULL); return TRUE; } } diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index 6efcaeaf18..ee6a7793f8 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -418,6 +418,9 @@ private: bool mActive; bool mUIVisible; + boost::shared_ptr<class LLNotificationChannel> mAlertsChannel, + mModalAlertsChannel; + LLRect mWindowRectRaw; // whole window, including UI LLRect mWindowRectScaled; // whole window, scaled by UI size LLRect mWorldViewRectRaw; // area of screen for 3D world diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 05febdf93b..7b08744598 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -34,6 +34,8 @@ #include "llvoavatar.h" +#define XXX_STINSON_CHUI_REWORK // temporarily re-enabling the in-world voice-dot + #include <stdio.h> #include <ctype.h> @@ -62,6 +64,7 @@ #include "llhudmanager.h" #include "llhudnametag.h" #include "llhudtext.h" // for mText/mDebugText +#include "llinitparam.h" #include "llkeyframefallmotion.h" #include "llkeyframestandmotion.h" #include "llkeyframewalkmotion.h" @@ -191,6 +194,9 @@ const S32 MAX_BUBBLE_CHAT_LENGTH = DB_CHAT_MSG_STR_LEN; const S32 MAX_BUBBLE_CHAT_UTTERANCES = 12; const F32 CHAT_FADE_TIME = 8.0; const F32 BUBBLE_CHAT_TIME = CHAT_FADE_TIME * 3.f; +const F32 NAMETAG_UPDATE_THRESHOLD = 0.3f; +const F32 NAMETAG_VERTICAL_SCREEN_OFFSET = 25.f; +const F32 NAMETAG_VERT_OFFSET_WEIGHT = 0.17f; const LLColor4 DUMMY_COLOR = LLColor4(0.5,0.5,0.5,1.0); @@ -222,55 +228,62 @@ struct LLTextureMaskData **/ //------------------------------------------------------------------------ -// LLVOBoneInfo +// LLVOAvatarBoneInfo // Trans/Scale/Rot etc. info about each avatar bone. Used by LLVOAvatarSkeleton. //------------------------------------------------------------------------ -class LLVOAvatarBoneInfo +struct LLVOAvatarCollisionVolumeInfo : public LLInitParam::Block<LLVOAvatarCollisionVolumeInfo> { - friend class LLVOAvatar; - friend class LLVOAvatarSkeletonInfo; -public: - LLVOAvatarBoneInfo() : mIsJoint(FALSE) {} - ~LLVOAvatarBoneInfo() + LLVOAvatarCollisionVolumeInfo() + : name("name"), + pos("pos"), + rot("rot"), + scale("scale") + {} + + Mandatory<std::string> name; + Mandatory<LLVector3> pos, + rot, + scale; +}; + +struct LLVOAvatarChildJoint : public LLInitParam::ChoiceBlock<LLVOAvatarChildJoint> { - std::for_each(mChildList.begin(), mChildList.end(), DeletePointer()); - } - BOOL parseXml(LLXmlTreeNode* node); + Alternative<Lazy<struct LLVOAvatarBoneInfo, IS_A_BLOCK> > bone; + Alternative<LLVOAvatarCollisionVolumeInfo> collision_volume; -private: - std::string mName; - BOOL mIsJoint; - LLVector3 mPos; - LLVector3 mRot; - LLVector3 mScale; - LLVector3 mPivot; - typedef std::vector<LLVOAvatarBoneInfo*> child_list_t; - child_list_t mChildList; + LLVOAvatarChildJoint() + : bone("bone"), + collision_volume("collision_volume") + {} +}; + +struct LLVOAvatarBoneInfo : public LLInitParam::Block<LLVOAvatarBoneInfo, LLVOAvatarCollisionVolumeInfo> +{ + LLVOAvatarBoneInfo() + : pivot("pivot") + {} + + Mandatory<LLVector3> pivot; + Multiple<LLVOAvatarChildJoint> children; }; //------------------------------------------------------------------------ // LLVOAvatarSkeletonInfo // Overall avatar skeleton //------------------------------------------------------------------------ -class LLVOAvatarSkeletonInfo +struct LLVOAvatarSkeletonInfo : public LLInitParam::Block<LLVOAvatarSkeletonInfo> { - friend class LLVOAvatar; -public: - LLVOAvatarSkeletonInfo() : - mNumBones(0), mNumCollisionVolumes(0) {} - ~LLVOAvatarSkeletonInfo() - { - std::for_each(mBoneInfoList.begin(), mBoneInfoList.end(), DeletePointer()); - } - BOOL parseXml(LLXmlTreeNode* node); - S32 getNumBones() const { return mNumBones; } - S32 getNumCollisionVolumes() const { return mNumCollisionVolumes; } + LLVOAvatarSkeletonInfo() + : skeleton_root(""), + num_bones("num_bones"), + num_collision_volumes("num_collision_volumes"), + version("version") + {} -private: - S32 mNumBones; - S32 mNumCollisionVolumes; - typedef std::vector<LLVOAvatarBoneInfo*> bone_info_list_t; - bone_info_list_t mBoneInfoList; + Mandatory<std::string> version; + Mandatory<S32> num_bones, + num_collision_volumes; + Mandatory<LLVOAvatarChildJoint> skeleton_root; }; //----------------------------------------------------------------------------- @@ -595,7 +608,7 @@ private: // Static Data //----------------------------------------------------------------------------- LLXmlTree LLVOAvatar::sXMLTree; -LLXmlTree LLVOAvatar::sSkeletonXMLTree; +LLXMLNodePtr LLVOAvatar::sSkeletonXMLTree; LLVOAvatarSkeletonInfo* LLVOAvatar::sAvatarSkeletonInfo = NULL; LLVOAvatar::LLVOAvatarXmlInfo* LLVOAvatar::sAvatarXmlInfo = NULL; LLVOAvatarDictionary *LLVOAvatar::sAvatarDictionary = NULL; @@ -698,9 +711,13 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, LLMemType mt(LLMemType::MTYPE_AVATAR); //VTResume(); // VTune +#ifdef XXX_STINSON_CHUI_REWORK // mVoiceVisualizer is created by the hud effects manager and uses the HUD Effects pipeline const BOOL needsSendToSim = false; // currently, this HUD effect doesn't need to pack and unpack data to do its job mVoiceVisualizer = ( LLVoiceVisualizer *)LLHUDManager::getInstance()->createViewerEffect( LLHUDObject::LL_HUD_EFFECT_VOICE_VISUALIZER, needsSendToSim ); +#else // XXX_STINSON_CHUI_REWORK + mVoiceVisualizer = new LLVoiceVisualizer(); +#endif // XXX_STINSON_CHUI_REWORK lldebugs << "LLVOAvatar Constructor (0x" << this << ") id:" << mID << llendl; @@ -805,14 +822,14 @@ void LLVOAvatar::debugAvatarRezTime(std::string notification_name, std::string c //------------------------------------------------------------------------ LLVOAvatar::~LLVOAvatar() { - if (!mFullyLoaded) - { + if (!mFullyLoaded) + { debugAvatarRezTime("AvatarRezLeftCloudNotification","left after ruth seconds as cloud"); - } - else - { + } + else + { debugAvatarRezTime("AvatarRezLeftNotification","left sometime after declouding"); - } + } lldebugs << "LLVOAvatar Destructor (0x" << this << ") id:" << mID << llendl; @@ -876,7 +893,11 @@ void LLVOAvatar::markDead() mNameText = NULL; sNumVisibleChatBubbles--; } +#ifdef XXX_STINSON_CHUI_REWORK mVoiceVisualizer->markDead(); +#else // XXX_STINSON_CHUI_REWORK + mVoiceVisualizer->setStopSpeaking(); +#endif // XXX_STINSON_CHUI_REWORK LLLoadedCallbackEntry::cleanUpCallbackList(&mCallbackTextureList) ; LLViewerObject::markDead(); } @@ -1214,18 +1235,6 @@ void LLVOAvatar::initClass() llerrs << "Error parsing skeleton file: " << skeleton_path << llendl; } - // Process XML data - - // avatar_skeleton.xml - if (sAvatarSkeletonInfo) - { //this can happen if a login attempt failed - delete sAvatarSkeletonInfo; - } - sAvatarSkeletonInfo = new LLVOAvatarSkeletonInfo; - if (!sAvatarSkeletonInfo->parseXml(sSkeletonXMLTree.getRoot())) - { - llerrs << "Error parsing skeleton XML file: " << skeleton_path << llendl; - } // parse avatar_lad.xml if (sAvatarXmlInfo) { //this can happen if a login attempt failed @@ -1274,7 +1283,7 @@ void LLVOAvatar::initClass() void LLVOAvatar::cleanupClass() { deleteAndClear(sAvatarXmlInfo); - sSkeletonXMLTree.cleanup(); + sSkeletonXMLTree = NULL; sXMLTree.cleanup(); } @@ -1420,7 +1429,9 @@ void LLVOAvatar::initInstance(void) //VTPause(); // VTune +#ifdef XXX_STINSON_CHUI_REWORK mVoiceVisualizer->setVoiceEnabled( LLVoiceClient::getInstance()->getVoiceEnabled( mID ) ); +#endif // XXX_STINSON_CHUI_REWORK } @@ -1744,33 +1755,39 @@ BOOL LLVOAvatar::parseSkeletonFile(const std::string& filename) //------------------------------------------------------------------------- // parse the file //------------------------------------------------------------------------- - BOOL parsesuccess = sSkeletonXMLTree.parseFile( filename, FALSE ); - if (!parsesuccess) + LLXMLNodePtr skeleton_xml; + BOOL parsesuccess = LLXMLNode::parseFile(filename, skeleton_xml, NULL); + + if (!parsesuccess || skeleton_xml.isNull()) { llerrs << "Can't parse skeleton file: " << filename << llendl; return FALSE; } - // now sanity check xml file - LLXmlTreeNode* root = sSkeletonXMLTree.getRoot(); - if (!root) + // Process XML data + if (sAvatarSkeletonInfo) + { //this can happen if a login attempt failed + delete sAvatarSkeletonInfo; + } + sAvatarSkeletonInfo = new LLVOAvatarSkeletonInfo; + + LLXUIParser parser; + parser.readXUI(skeleton_xml, *sAvatarSkeletonInfo, filename); + if (!sAvatarSkeletonInfo->validateBlock()) { - llerrs << "No root node found in avatar skeleton file: " << filename << llendl; - return FALSE; + llerrs << "Error parsing skeleton XML file: " << filename << llendl; } - if( !root->hasName( "linden_skeleton" ) ) + if( !skeleton_xml->hasName( "linden_skeleton" ) ) { llerrs << "Invalid avatar skeleton file header: " << filename << llendl; return FALSE; } - std::string version; - static LLStdStringHandle version_string = LLXmlTree::addAttributeString("version"); - if( !root->getFastAttributeString( version_string, version ) || (version != "1.0") ) + if (sAvatarSkeletonInfo->version() != "1.0") { - llerrs << "Invalid avatar skeleton file version: " << version << " in file: " << filename << llendl; + llerrs << "Invalid avatar skeleton file version: " << sAvatarSkeletonInfo->version() << " in file: " << filename << llendl; return FALSE; } @@ -1779,14 +1796,13 @@ BOOL LLVOAvatar::parseSkeletonFile(const std::string& filename) //----------------------------------------------------------------------------- // setupBone() -//----------------------------------------------------------------------------- -BOOL LLVOAvatar::setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent, S32 &volume_num, S32 &joint_num) +//----------------------------------------------------------- +BOOL LLVOAvatar::setupBone(const LLVOAvatarChildJoint& info, LLViewerJoint* parent, S32 &volume_num, S32 &joint_num) { LLMemType mt(LLMemType::MTYPE_AVATAR); LLViewerJoint* joint = NULL; - - if (info->mIsJoint) + if (info.bone.isChosen()) { joint = (LLViewerJoint*)getCharacterJoint(joint_num); if (!joint) @@ -1794,7 +1810,23 @@ BOOL LLVOAvatar::setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent llwarns << "Too many bones" << llendl; return FALSE; } - joint->setName( info->mName ); + joint->setName( info.bone().name ); + joint->setPosition(info.bone().pos); + joint->setRotation(mayaQ(info.bone().rot().mV[VX], info.bone().rot().mV[VY], info.bone().rot().mV[VZ], LLQuaternion::XYZ)); + joint->setScale(info.bone().scale); + joint->setSkinOffset( info.bone().pivot ); + joint_num++; + + for (LLInitParam::ParamIterator<LLVOAvatarChildJoint>::const_iterator child_it = info.bone().children.begin(), + end_it = info.bone().children.end(); + child_it != end_it; + ++child_it) + { + if (!setupBone(*child_it, joint, volume_num, joint_num)) + { + return FALSE; + } + } } else // collision volume { @@ -1804,7 +1836,11 @@ BOOL LLVOAvatar::setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent return FALSE; } joint = (LLViewerJoint*)(&mCollisionVolumes[volume_num]); - joint->setName( info->mName ); + joint->setName( info.collision_volume.name); + joint->setPosition(info.collision_volume.pos); + joint->setRotation(mayaQ(info.collision_volume.rot().mV[VX], info.collision_volume.rot().mV[VY], info.collision_volume.rot().mV[VZ], LLQuaternion::XYZ)); + joint->setScale(info.collision_volume.scale); + volume_num++; } // add to parent @@ -1813,34 +1849,8 @@ BOOL LLVOAvatar::setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent parent->addChild( joint ); } - joint->setPosition(info->mPos); - joint->setRotation(mayaQ(info->mRot.mV[VX], info->mRot.mV[VY], - info->mRot.mV[VZ], LLQuaternion::XYZ)); - joint->setScale(info->mScale); - joint->setDefaultFromCurrentXform(); - if (info->mIsJoint) - { - joint->setSkinOffset( info->mPivot ); - joint_num++; - } - else // collision volume - { - volume_num++; - } - - // setup children - LLVOAvatarBoneInfo::child_list_t::const_iterator iter; - for (iter = info->mChildList.begin(); iter != info->mChildList.end(); ++iter) - { - LLVOAvatarBoneInfo *child_info = *iter; - if (!setupBone(child_info, joint, volume_num, joint_num)) - { - return FALSE; - } - } - return TRUE; } @@ -1854,36 +1864,32 @@ BOOL LLVOAvatar::buildSkeleton(const LLVOAvatarSkeletonInfo *info) //------------------------------------------------------------------------- // allocate joints //------------------------------------------------------------------------- - if (!allocateCharacterJoints(info->mNumBones)) + if (!allocateCharacterJoints(info->num_bones)) { - llerrs << "Can't allocate " << info->mNumBones << " joints" << llendl; + llerrs << "Can't allocate " << info->num_bones() << " joints" << llendl; return FALSE; } //------------------------------------------------------------------------- // allocate volumes //------------------------------------------------------------------------- - if (info->mNumCollisionVolumes) + if (info->num_collision_volumes) { - if (!allocateCollisionVolumes(info->mNumCollisionVolumes)) + if (!allocateCollisionVolumes(info->num_collision_volumes)) { - llerrs << "Can't allocate " << info->mNumCollisionVolumes << " collision volumes" << llendl; + llerrs << "Can't allocate " << info->num_collision_volumes() << " collision volumes" << llendl; return FALSE; } } S32 current_joint_num = 0; S32 current_volume_num = 0; - LLVOAvatarSkeletonInfo::bone_info_list_t::const_iterator iter; - for (iter = info->mBoneInfoList.begin(); iter != info->mBoneInfoList.end(); ++iter) + + if (!setupBone(info->skeleton_root, NULL, current_volume_num, current_joint_num)) { - LLVOAvatarBoneInfo *info = *iter; - if (!setupBone(info, NULL, current_volume_num, current_joint_num)) - { llerrs << "Error parsing bone in skeleton file" << llendl; return FALSE; } - } return TRUE; } @@ -2096,15 +2102,15 @@ void LLVOAvatar::releaseMeshData() LLFace* facep = mDrawable->getFace(0); if (facep) { - facep->setSize(0, 0); - for(S32 i = mNumInitFaces ; i < mDrawable->getNumFaces(); i++) - { - facep = mDrawable->getFace(i); + facep->setSize(0, 0); + for(S32 i = mNumInitFaces ; i < mDrawable->getNumFaces(); i++) + { + facep = mDrawable->getFace(i); if (facep) { - facep->setSize(0, 0); - } - } + facep->setSize(0, 0); + } + } } } @@ -2353,11 +2359,11 @@ U32 LLVOAvatar::processUpdateMessage(LLMessageSystem *mesgsys, U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp); // Print out arrival information once we have name of avatar. - if (has_name && getNVPair("FirstName")) - { - mDebugExistenceTimer.reset(); + if (has_name && getNVPair("FirstName")) + { + mDebugExistenceTimer.reset(); debugAvatarRezTime("AvatarRezArrivedNotification","avatar arrived"); - } + } if(retval & LLViewerObject::INVALID_UPDATE) { @@ -2423,7 +2429,7 @@ void LLVOAvatar::dumpAnimationState() //------------------------------------------------------------------------ // idleUpdate() //------------------------------------------------------------------------ -BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { LLMemType mt(LLMemType::MTYPE_AVATAR); LLFastTimer t(FTM_AVATAR_UPDATE); @@ -2431,12 +2437,12 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) if (isDead()) { llinfos << "Warning! Idle on dead avatar" << llendl; - return TRUE; + return; } if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_AVATAR))) { - return TRUE; + return; } checkTextureLoading() ; @@ -2519,12 +2525,11 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) idleUpdateNameTag( root_pos_last ); idleUpdateRenderCost(); - - return TRUE; } void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) { +#ifdef XXX_STINSON_CHUI_REWORK bool render_visualizer = voice_enabled; // Don't render the user's own voice visualizer when in mouselook, or when opening the mic is disabled. @@ -2537,6 +2542,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) } mVoiceVisualizer->setVoiceEnabled(render_visualizer); +#endif // XXX_STINSON_CHUI_REWORK if ( voice_enabled ) { @@ -2612,6 +2618,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) } } +#ifdef XXX_STINSON_CHUI_REWORK //-------------------------------------------------------------------------------------------- // here we get the approximate head position and set as sound source for the voice symbol // (the following version uses a tweak of "mHeadOffset" which handle sitting vs. standing) @@ -2629,6 +2636,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) tagPos[VZ] += ( mBodySize[VZ] + 0.125f ); mVoiceVisualizer->setVoiceSourceWorldPosition( tagPos ); } +#endif // XXX_STINSON_CHUI_REWORK }//if ( voiceEnabled ) } @@ -2868,8 +2876,8 @@ void LLVOAvatar::idleUpdateLoadingEffect() { LL_INFOS("Avatar") << avString() << "self isFullyLoaded, mFirstFullyVisible" << LL_ENDL; mFirstFullyVisible = FALSE; - LLAppearanceMgr::instance().onFirstFullyVisible(); - } + LLAppearanceMgr::instance().onFirstFullyVisible(); + } if (isFullyLoaded() && mFirstFullyVisible && !isSelf()) { LL_INFOS("Avatar") << avString() << "other isFullyLoaded, mFirstFullyVisible" << LL_ENDL; @@ -3017,43 +3025,43 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last) return; } - BOOL new_name = FALSE; - if (visible_chat != mVisibleChat) - { - mVisibleChat = visible_chat; - new_name = TRUE; - } - - if (sRenderGroupTitles != mRenderGroupTitles) - { - mRenderGroupTitles = sRenderGroupTitles; - new_name = TRUE; - } - - // First Calculate Alpha - // If alpha > 0, create mNameText if necessary, otherwise delete it - F32 alpha = 0.f; - if (mAppAngle > 5.f) - { - const F32 START_FADE_TIME = NAME_SHOW_TIME - FADE_DURATION; - if (!visible_chat && sRenderName == RENDER_NAME_FADE && time_visible > START_FADE_TIME) + BOOL new_name = FALSE; + if (visible_chat != mVisibleChat) { - alpha = 1.f - (time_visible - START_FADE_TIME) / FADE_DURATION; + mVisibleChat = visible_chat; + new_name = TRUE; } - else + + if (sRenderGroupTitles != mRenderGroupTitles) { - // ...not fading, full alpha - alpha = 1.f; + mRenderGroupTitles = sRenderGroupTitles; + new_name = TRUE; } - } - else if (mAppAngle > 2.f) - { - // far away is faded out also - alpha = (mAppAngle-2.f)/3.f; - } + + // First Calculate Alpha + // If alpha > 0, create mNameText if necessary, otherwise delete it + F32 alpha = 0.f; + if (mAppAngle > 5.f) + { + const F32 START_FADE_TIME = NAME_SHOW_TIME - FADE_DURATION; + if (!visible_chat && sRenderName == RENDER_NAME_FADE && time_visible > START_FADE_TIME) + { + alpha = 1.f - (time_visible - START_FADE_TIME) / FADE_DURATION; + } + else + { + // ...not fading, full alpha + alpha = 1.f; + } + } + else if (mAppAngle > 2.f) + { + // far away is faded out also + alpha = (mAppAngle-2.f)/3.f; + } if (alpha <= 0.f) - { + { if (mNameText) { mNameText->markDead(); @@ -3063,22 +3071,21 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last) return; } - if (!mNameText) - { + if (!mNameText) + { mNameText = static_cast<LLHUDNameTag*>( LLHUDObject::addHUDObject( - LLHUDObject::LL_HUD_NAME_TAG) ); + LLHUDObject::LL_HUD_NAME_TAG) ); //mNameText->setMass(10.f); - mNameText->setSourceObject(this); + mNameText->setSourceObject(this); mNameText->setVertAlignment(LLHUDNameTag::ALIGN_VERT_TOP); - mNameText->setVisibleOffScreen(TRUE); - mNameText->setMaxLines(11); - mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f); - sNumVisibleChatBubbles++; - new_name = TRUE; - } + mNameText->setVisibleOffScreen(TRUE); + mNameText->setMaxLines(11); + mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f); + sNumVisibleChatBubbles++; + new_name = TRUE; + } - LLVector3 name_position = idleUpdateNameTagPosition(root_pos_last); - mNameText->setPositionAgent(name_position); + idleUpdateNameTagPosition(root_pos_last); idleUpdateNameTagText(new_name); idleUpdateNameTagAlpha(new_name, alpha); } @@ -3166,7 +3173,7 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) // trim last ", " line.resize( line.length() - 2 ); addNameTagLine(line, name_tag_color, LLFontGL::NORMAL, - LLFontGL::getFontSansSerifSmall()); + LLFontGL::getFontSansSerifSmall()); } if (sRenderGroupTitles @@ -3175,7 +3182,7 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) std::string title_str = title->getString(); LLStringFn::replace_ascii_controlchars(title_str,LL_UNKNOWN_CHAR); addNameTagLine(title_str, name_tag_color, LLFontGL::NORMAL, - LLFontGL::getFontSansSerifSmall()); + LLFontGL::getFontSansSerifSmall()); } static LLUICachedControl<bool> show_display_names("NameTagShowDisplayNames"); @@ -3189,14 +3196,14 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) // ...call this function back when the name arrives // and force a rebuild LLAvatarNameCache::get(getID(), - boost::bind(&LLVOAvatar::clearNameTag, this)); + boost::bind(&LLVOAvatar::clearNameTag, this)); } // Might be blank if name not available yet, that's OK if (show_display_names) { addNameTagLine(av_name.mDisplayName, name_tag_color, LLFontGL::NORMAL, - LLFontGL::getFontSansSerif()); + LLFontGL::getFontSansSerif()); } // Suppress SLID display if display name matches exactly (ugh) if (show_usernames && !av_name.mIsDisplayNameDefault) @@ -3204,14 +3211,13 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) // *HACK: Desaturate the color LLColor4 username_color = name_tag_color * 0.83f; addNameTagLine(av_name.mUsername, username_color, LLFontGL::NORMAL, - LLFontGL::getFontSansSerifSmall()); + LLFontGL::getFontSansSerifSmall()); } } else { const LLFontGL* font = LLFontGL::getFontSansSerif(); - std::string full_name = - LLCacheName::buildFullName( firstname->getString(), lastname->getString() ); + std::string full_name = LLCacheName::buildFullName( firstname->getString(), lastname->getString() ); addNameTagLine(full_name, name_tag_color, LLFontGL::NORMAL, font); } @@ -3231,7 +3237,7 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) mNameText->setFont(LLFontGL::getFontSansSerif()); mNameText->setTextAlignment(LLHUDNameTag::ALIGN_TEXT_LEFT); mNameText->setFadeDistance(CHAT_NORMAL_RADIUS * 2.f, 5.f); - + char line[MAX_STRING]; /* Flawfinder: ignore */ line[0] = '\0'; std::deque<LLChat>::iterator chat_iter = mChats.begin(); @@ -3251,13 +3257,13 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) LLFontGL::StyleFlags style; switch(chat_iter->mChatType) { - case CHAT_TYPE_WHISPER: + case CHAT_TYPE_WHISPER: style = LLFontGL::ITALIC; break; - case CHAT_TYPE_SHOUT: + case CHAT_TYPE_SHOUT: style = LLFontGL::BOLD; break; - default: + default: style = LLFontGL::NORMAL; break; } @@ -3284,13 +3290,13 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) S32 dot_count = (llfloor(mTypingTimer.getElapsedTimeF32() * 3.f) + 2) % 3 + 1; switch(dot_count) { - case 1: + case 1: mNameText->addLine(".", new_chat); break; - case 2: + case 2: mNameText->addLine("..", new_chat); break; - case 3: + case 3: mNameText->addLine("...", new_chat); break; } @@ -3354,34 +3360,45 @@ void LLVOAvatar::invalidateNameTags() if (avatar->isDead()) continue; avatar->clearNameTag(); - } } // Compute name tag position during idle update -LLVector3 LLVOAvatar::idleUpdateNameTagPosition(const LLVector3& root_pos_last) +void LLVOAvatar::idleUpdateNameTagPosition(const LLVector3& root_pos_last) { LLQuaternion root_rot = mRoot.getWorldRotation(); + LLQuaternion inv_root_rot = ~root_rot; LLVector3 pixel_right_vec; LLVector3 pixel_up_vec; LLViewerCamera::getInstance()->getPixelVectors(root_pos_last, pixel_up_vec, pixel_right_vec); LLVector3 camera_to_av = root_pos_last - LLViewerCamera::getInstance()->getOrigin(); camera_to_av.normalize(); - LLVector3 local_camera_at = camera_to_av * ~root_rot; + LLVector3 local_camera_at = camera_to_av * inv_root_rot; LLVector3 local_camera_up = camera_to_av % LLViewerCamera::getInstance()->getLeftAxis(); local_camera_up.normalize(); - local_camera_up = local_camera_up * ~root_rot; + local_camera_up = local_camera_up * inv_root_rot; - local_camera_up.scaleVec(mBodySize * 0.5f); - local_camera_at.scaleVec(mBodySize * 0.5f); + LLVector3 avatar_ellipsoid(mBodySize.mV[VX] * 0.4f, + mBodySize.mV[VY] * 0.4f, + mBodySize.mV[VZ] * NAMETAG_VERT_OFFSET_WEIGHT); - LLVector3 name_position = mRoot.getWorldPosition(); - name_position[VZ] -= mPelvisToFoot; - name_position[VZ] += (mBodySize[VZ]* 0.55f); + local_camera_up.scaleVec(avatar_ellipsoid); + local_camera_at.scaleVec(avatar_ellipsoid); + + LLVector3 head_offset = (mHeadp->getLastWorldPosition() - mRoot.getLastWorldPosition()) * inv_root_rot; + + if (dist_vec(head_offset, mTargetRootToHeadOffset) > NAMETAG_UPDATE_THRESHOLD) + { + mTargetRootToHeadOffset = head_offset; + } + + mCurRootToHeadOffset = lerp(mCurRootToHeadOffset, mTargetRootToHeadOffset, LLCriticalDamp::getInterpolant(0.2f)); + + LLVector3 name_position = mRoot.getLastWorldPosition() + (mCurRootToHeadOffset * root_rot); name_position += (local_camera_up * root_rot) - (projected_vec(local_camera_at * root_rot, camera_to_av)); - name_position += pixel_up_vec * 15.f; + name_position += pixel_up_vec * NAMETAG_VERTICAL_SCREEN_OFFSET; - return name_position; + mNameText->setPositionAgent(name_position); } void LLVOAvatar::idleUpdateNameTagAlpha(BOOL new_name, F32 alpha) @@ -3454,9 +3471,9 @@ bool LLVOAvatar::isVisuallyMuted() const static LLCachedControl<U32> max_attachment_bytes(gSavedSettings, "RenderAutoMuteByteLimit"); static LLCachedControl<F32> max_attachment_area(gSavedSettings, "RenderAutoMuteSurfaceAreaLimit"); - return LLMuteList::getInstance()->isMuted(getID()) || - (mAttachmentGeometryBytes > max_attachment_bytes && max_attachment_bytes > 0) || - (mAttachmentSurfaceArea > max_attachment_area && max_attachment_area > 0.f); + return LLMuteList::getInstance()->isMuted(getID()) + || (mAttachmentGeometryBytes > max_attachment_bytes && max_attachment_bytes > 0) + || (mAttachmentSurfaceArea > max_attachment_area && max_attachment_area > 0.f); } //------------------------------------------------------------------------ @@ -3497,8 +3514,6 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) } } - LLVector3d root_pos_global; - if (!mIsBuilt) { return FALSE; @@ -3513,7 +3528,6 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) mTimeVisible.reset(); } - //-------------------------------------------------------------------- // the rest should only be done occasionally for far away avatars //-------------------------------------------------------------------- @@ -3916,10 +3930,6 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) if ( playSound ) { -// F32 gain = clamp_rescale( mSpeedAccum, -// AUDIO_STEP_LO_SPEED, AUDIO_STEP_HI_SPEED, -// AUDIO_STEP_LO_GAIN, AUDIO_STEP_HI_GAIN ); - const F32 STEP_VOLUME = 0.1f; const LLUUID& step_sound_id = getStepSound(); @@ -4136,13 +4146,6 @@ void LLVOAvatar::updateVisibility() { releaseMeshData(); } - // this breaks off-screen chat bubbles - //if (mNameText) - //{ - // mNameText->markDead(); - // mNameText = NULL; - // sNumVisibleChatBubbles--; - //} } mVisible = visible; @@ -4158,46 +4161,6 @@ bool LLVOAvatar::shouldAlphaMask() } -U32 LLVOAvatar::renderSkinnedAttachments() -{ - /*U32 num_indices = 0; - - const U32 data_mask = LLVertexBuffer::MAP_VERTEX | - LLVertexBuffer::MAP_NORMAL | - LLVertexBuffer::MAP_TEXCOORD0 | - LLVertexBuffer::MAP_COLOR | - LLVertexBuffer::MAP_WEIGHT4; - - for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); - iter != mAttachmentPoints.end(); - ++iter) - { - LLViewerJointAttachment* attachment = iter->second; - for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); - attachment_iter != attachment->mAttachedObjects.end(); - ++attachment_iter) - { - const LLViewerObject* attached_object = (*attachment_iter); - if (attached_object && !attached_object->isHUDAttachment()) - { - const LLDrawable* drawable = attached_object->mDrawable; - if (drawable) - { - for (S32 i = 0; i < drawable->getNumFaces(); ++i) - { - LLFace* face = drawable->getFace(i); - if (face->isState(LLFace::RIGGED)) - { - - } - } - } - } - - return num_indices;*/ - return 0; -} - //----------------------------------------------------------------------------- // renderSkinned() //----------------------------------------------------------------------------- @@ -4218,11 +4181,11 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass) { //LOD changed or new mesh created, allocate new vertex buffer if needed if (needs_rebuild || mDirtyMesh >= 2 || mVisibilityRank <= 4) { - updateMeshData(); + updateMeshData(); mDirtyMesh = 0; - mNeedsSkin = TRUE; - mDrawable->clearState(LLDrawable::REBUILD_GEOMETRY); - } + mNeedsSkin = TRUE; + mDrawable->clearState(LLDrawable::REBUILD_GEOMETRY); + } } if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_AVATAR) <= 0) @@ -4251,13 +4214,13 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass) if (face) { LLVertexBuffer* vb = face->getVertexBuffer(); - if (vb) - { - vb->flush(); - } + if (vb) + { + vb->flush(); } } } + } else { mNeedsSkin = FALSE; @@ -5900,7 +5863,6 @@ BOOL LLVOAvatar::updateJointLODs() F32 avatar_num_factor = clamp_rescale((F32)sNumVisibleAvatars, 8, 25, 1.f, avatar_num_min_factor); F32 area_scale = 0.16f; - { if (isSelf()) { if(gAgentCamera.cameraCustomizeAvatar() || gAgentCamera.cameraMouselook()) @@ -5930,7 +5892,6 @@ BOOL LLVOAvatar::updateJointLODs() dirtyMesh(2); return TRUE; } - } return FALSE; } @@ -6219,14 +6180,9 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO ) if ( pVObj ) { const LLMeshSkinInfo* pSkinData = gMeshRepo.getSkinInfo( pVObj->getVolume()->getParams().getSculptID(), pVObj ); - if ( pSkinData ) - { - const int jointCnt = pSkinData->mJointNames.size(); - bool fullRig = ( jointCnt>=20 ) ? true : false; - if ( fullRig ) - { - const int bindCnt = pSkinData->mAlternateBindMatrix.size(); - if ( bindCnt > 0 ) + if (pSkinData + && pSkinData->mJointNames.size() > 20 // full rig + && pSkinData->mAlternateBindMatrix.size() > 0) { LLVOAvatar::resetJointPositionsToDefault(); //Need to handle the repositioning of the cam, updating rig data etc during outfit editing @@ -6241,8 +6197,6 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO ) } } } - } -} //----------------------------------------------------------------------------- // detachObject() //----------------------------------------------------------------------------- @@ -6391,11 +6345,7 @@ void LLVOAvatar::getOffObject() at_axis.mV[VZ] = 0.f; at_axis.normalize(); gAgent.resetAxes(at_axis); - - //reset orientation -// mRoot.setRotation(avWorldRot); gAgentCamera.setThirdPersonHeadOffset(LLVector3(0.f, 0.f, 1.f)); - gAgentCamera.setSitCamera(LLUUID::null); } } @@ -6445,7 +6395,6 @@ LLColor4 LLVOAvatar::getGlobalColor( const std::string& color_name ) const } else { -// return LLColor4( .5f, .5f, .5f, .5f ); return LLColor4( 0.f, 1.f, 1.f, 1.f ); // good debugging color } } @@ -6610,8 +6559,8 @@ BOOL LLVOAvatar::processFullyLoadedChange(bool loading) mFullyLoaded = (mFullyLoadedTimer.getElapsedTimeF32() > PAUSE); - if (!mPreviousFullyLoaded && !loading && mFullyLoaded) - { + if (!mPreviousFullyLoaded && !loading && mFullyLoaded) + { debugAvatarRezTime("AvatarRezNotification","fully loaded"); } @@ -7204,10 +7153,6 @@ LLBBox LLVOAvatar::getHUDBBox() const return bbox; } -void LLVOAvatar::rebuildHUD() -{ -} - //----------------------------------------------------------------------------- // onFirstTEMessageReceived() //----------------------------------------------------------------------------- @@ -7333,7 +7278,10 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) && baked_index != BAKED_SKIRT) { setTEImage(mBakedTextureDatas[baked_index].mTextureIndex, - LLViewerTextureManager::getFetchedTexture(mBakedTextureDatas[baked_index].mLastTextureIndex, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE)); + LLViewerTextureManager::getFetchedTexture(mBakedTextureDatas[baked_index].mLastTextureIndex, + TRUE, + LLViewerTexture::BOOST_NONE, + LLViewerTexture::LOD_TEXTURE)); } } @@ -7592,7 +7540,7 @@ void LLVOAvatar::onInitialBakedTextureLoaded( BOOL success, LLViewerFetchedTextu LLUUID *avatar_idp = (LLUUID *)userdata; LLVOAvatar *selfp = (LLVOAvatar *)gObjectList.findObject(*avatar_idp); - + if (selfp) { LL_DEBUGS("Avatar") << selfp->avString() << "discard_level " << discard_level << " success " << success << " final " << final << LL_ENDL; @@ -7643,13 +7591,6 @@ void LLVOAvatar::onBakedTextureLoaded(BOOL success, // Called when baked texture is loaded and also when we start up with a baked texture void LLVOAvatar::useBakedTexture( const LLUUID& id ) { - - - /* if(id == head_baked->getID()) - mHeadBakedLoaded = TRUE; - mLastHeadBakedID = id; - mHeadMesh0.setTexture( head_baked ); - mHeadMesh1.setTexture( head_baked ); */ for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { LLViewerTexture* image_baked = getImage( mBakedTextureDatas[i].mTextureIndex, 0 ); @@ -7893,111 +7834,111 @@ LLVOAvatar::LLVOAvatarXmlInfo::~LLVOAvatarXmlInfo() std::for_each(mMorphMaskInfoList.begin(), mMorphMaskInfoList.end(), DeletePointer()); } -//----------------------------------------------------------------------------- -// LLVOAvatarBoneInfo::parseXml() -//----------------------------------------------------------------------------- -BOOL LLVOAvatarBoneInfo::parseXml(LLXmlTreeNode* node) -{ - if (node->hasName("bone")) - { - mIsJoint = TRUE; - static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name"); - if (!node->getFastAttributeString(name_string, mName)) - { - llwarns << "Bone without name" << llendl; - return FALSE; - } - } - else if (node->hasName("collision_volume")) - { - mIsJoint = FALSE; - static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name"); - if (!node->getFastAttributeString(name_string, mName)) - { - mName = "Collision Volume"; - } - } - else - { - llwarns << "Invalid node " << node->getName() << llendl; - return FALSE; - } - - static LLStdStringHandle pos_string = LLXmlTree::addAttributeString("pos"); - if (!node->getFastAttributeVector3(pos_string, mPos)) - { - llwarns << "Bone without position" << llendl; - return FALSE; - } - - static LLStdStringHandle rot_string = LLXmlTree::addAttributeString("rot"); - if (!node->getFastAttributeVector3(rot_string, mRot)) - { - llwarns << "Bone without rotation" << llendl; - return FALSE; - } - - static LLStdStringHandle scale_string = LLXmlTree::addAttributeString("scale"); - if (!node->getFastAttributeVector3(scale_string, mScale)) - { - llwarns << "Bone without scale" << llendl; - return FALSE; - } - - if (mIsJoint) - { - static LLStdStringHandle pivot_string = LLXmlTree::addAttributeString("pivot"); - if (!node->getFastAttributeVector3(pivot_string, mPivot)) - { - llwarns << "Bone without pivot" << llendl; - return FALSE; - } - } - - // parse children - LLXmlTreeNode* child; - for( child = node->getFirstChild(); child; child = node->getNextChild() ) - { - LLVOAvatarBoneInfo *child_info = new LLVOAvatarBoneInfo; - if (!child_info->parseXml(child)) - { - delete child_info; - return FALSE; - } - mChildList.push_back(child_info); - } - return TRUE; -} - -//----------------------------------------------------------------------------- -// LLVOAvatarSkeletonInfo::parseXml() -//----------------------------------------------------------------------------- -BOOL LLVOAvatarSkeletonInfo::parseXml(LLXmlTreeNode* node) -{ - static LLStdStringHandle num_bones_string = LLXmlTree::addAttributeString("num_bones"); - if (!node->getFastAttributeS32(num_bones_string, mNumBones)) - { - llwarns << "Couldn't find number of bones." << llendl; - return FALSE; - } - - static LLStdStringHandle num_collision_volumes_string = LLXmlTree::addAttributeString("num_collision_volumes"); - node->getFastAttributeS32(num_collision_volumes_string, mNumCollisionVolumes); - - LLXmlTreeNode* child; - for( child = node->getFirstChild(); child; child = node->getNextChild() ) - { - LLVOAvatarBoneInfo *info = new LLVOAvatarBoneInfo; - if (!info->parseXml(child)) - { - delete info; - llwarns << "Error parsing bone in skeleton file" << llendl; - return FALSE; - } - mBoneInfoList.push_back(info); - } - return TRUE; -} +////----------------------------------------------------------------------------- +//// LLVOAvatarBoneInfo::parseXml() +////----------------------------------------------------------------------------- +//BOOL LLVOAvatarBoneInfo::parseXml(LLXmlTreeNode* node) +//{ +// if (node->hasName("bone")) +// { +// mIsJoint = TRUE; +// static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name"); +// if (!node->getFastAttributeString(name_string, mName)) +// { +// llwarns << "Bone without name" << llendl; +// return FALSE; +// } +// } +// else if (node->hasName("collision_volume")) +// { +// mIsJoint = FALSE; +// static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name"); +// if (!node->getFastAttributeString(name_string, mName)) +// { +// mName = "Collision Volume"; +// } +// } +// else +// { +// llwarns << "Invalid node " << node->getName() << llendl; +// return FALSE; +// } +// +// static LLStdStringHandle pos_string = LLXmlTree::addAttributeString("pos"); +// if (!node->getFastAttributeVector3(pos_string, mPos)) +// { +// llwarns << "Bone without position" << llendl; +// return FALSE; +// } +// +// static LLStdStringHandle rot_string = LLXmlTree::addAttributeString("rot"); +// if (!node->getFastAttributeVector3(rot_string, mRot)) +// { +// llwarns << "Bone without rotation" << llendl; +// return FALSE; +// } +// +// static LLStdStringHandle scale_string = LLXmlTree::addAttributeString("scale"); +// if (!node->getFastAttributeVector3(scale_string, mScale)) +// { +// llwarns << "Bone without scale" << llendl; +// return FALSE; +// } +// +// if (mIsJoint) +// { +// static LLStdStringHandle pivot_string = LLXmlTree::addAttributeString("pivot"); +// if (!node->getFastAttributeVector3(pivot_string, mPivot)) +// { +// llwarns << "Bone without pivot" << llendl; +// return FALSE; +// } +// } +// +// // parse children +// LLXmlTreeNode* child; +// for( child = node->getFirstChild(); child; child = node->getNextChild() ) +// { +// LLVOAvatarBoneInfo *child_info = new LLVOAvatarBoneInfo; +// if (!child_info->parseXml(child)) +// { +// delete child_info; +// return FALSE; +// } +// mChildList.push_back(child_info); +// } +// return TRUE; +//} +// +////----------------------------------------------------------------------------- +//// LLVOAvatarSkeletonInfo::parseXml() +////----------------------------------------------------------------------------- +//BOOL LLVOAvatarSkeletonInfo::parseXml(LLXmlTreeNode* node) +//{ +// static LLStdStringHandle num_bones_string = LLXmlTree::addAttributeString("num_bones"); +// if (!node->getFastAttributeS32(num_bones_string, mNumBones)) +// { +// llwarns << "Couldn't find number of bones." << llendl; +// return FALSE; +// } +// +// static LLStdStringHandle num_collision_volumes_string = LLXmlTree::addAttributeString("num_collision_volumes"); +// node->getFastAttributeS32(num_collision_volumes_string, mNumCollisionVolumes); +// +// LLXmlTreeNode* child; +// for( child = node->getFirstChild(); child; child = node->getNextChild() ) +// { +// LLVOAvatarBoneInfo *info = new LLVOAvatarBoneInfo; +// if (!info->parseXml(child)) +// { +// delete info; +// llwarns << "Error parsing bone in skeleton file" << llendl; +// return FALSE; +// } +// mBoneInfoList.push_back(info); +// } +// return TRUE; +//} //----------------------------------------------------------------------------- // parseXmlSkeletonNode(): parses <skeleton> nodes from XML tree diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index f5692bb52f..e45069dbfe 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -67,8 +67,9 @@ class LLVoiceVisualizer; class LLHUDNameTag; class LLHUDEffectSpiral; class LLTexGlobalColor; -class LLVOAvatarBoneInfo; -class LLVOAvatarSkeletonInfo; +struct LLVOAvatarBoneInfo; +struct LLVOAvatarChildJoint; +struct LLVOAvatarSkeletonInfo; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // LLVOAvatar @@ -134,7 +135,7 @@ public: U32 block_num, const EObjectUpdateType update_type, LLDataPacker *dp); - virtual BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + virtual void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); virtual BOOL updateLOD(); BOOL updateJointLODs(); void updateLODRiggedAttachments( void ); @@ -243,7 +244,7 @@ public: void idleUpdateWindEffect(); void idleUpdateNameTag(const LLVector3& root_pos_last); void idleUpdateNameTagText(BOOL new_name); - LLVector3 idleUpdateNameTagPosition(const LLVector3& root_pos_last); + void idleUpdateNameTagPosition(const LLVector3& root_pos_last); void idleUpdateNameTagAlpha(BOOL new_name, F32 alpha); LLColor4 getNameTagColor(bool is_friend); void clearNameTag(); @@ -363,6 +364,8 @@ public: F32 mLastPelvisToFoot; F32 mPelvisFixup; F32 mLastPelvisFixup; + LLVector3 mCurRootToHeadOffset; + LLVector3 mTargetRootToHeadOffset; LLVector3 mHeadOffset; // current head position LLViewerJoint mRoot; @@ -375,7 +378,7 @@ protected: void buildCharacter(); virtual BOOL loadAvatar(); - BOOL setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent, S32 ¤t_volume_num, S32 ¤t_joint_num); + BOOL setupBone(const LLVOAvatarChildJoint& info, LLViewerJoint* parent, S32 ¤t_volume_num, S32 ¤t_joint_num); BOOL buildSkeleton(const LLVOAvatarSkeletonInfo *info); private: BOOL mIsBuilt; // state of deferred character building @@ -419,7 +422,7 @@ public: //-------------------------------------------------------------------- private: static LLXmlTree sXMLTree; // avatar config file - static LLXmlTree sSkeletonXMLTree; // avatar skeleton file + static LLXMLNodePtr sSkeletonXMLTree; // avatar skeleton file /** Skeleton ** ** @@ -437,7 +440,6 @@ public: U32 renderRigid(); U32 renderSkinned(EAvatarRenderPass pass); F32 getLastSkinTime() { return mLastSkinTime; } - U32 renderSkinnedAttachments(); U32 renderTransparent(BOOL first_pass); void renderCollisionVolumes(); static void deleteCachedImages(bool clearAll=true); @@ -786,7 +788,6 @@ public: public: BOOL hasHUDAttachment() const; LLBBox getHUDBBox() const; - void rebuildHUD(); void resetHUDAttachments(); BOOL canAttachMoreObjects() const; BOOL canAttachMoreObjects(U32 n) const; diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 6d672acc32..07b9b78255 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -668,15 +668,13 @@ BOOL LLVOAvatarSelf::updateCharacter(LLAgent &agent) } // virtual -BOOL LLVOAvatarSelf::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOAvatarSelf::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { - if (!isAgentAvatarValid()) + if (isAgentAvatarValid()) { - return TRUE; + LLVOAvatar::idleUpdate(agent, world, time); + idleUpdateTractorBeam(); } - LLVOAvatar::idleUpdate(agent, world, time); - idleUpdateTractorBeam(); - return TRUE; } // virtual diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h index 2b273e616c..7bd0c0bf93 100644 --- a/indra/newview/llvoavatarself.h +++ b/indra/newview/llvoavatarself.h @@ -85,7 +85,7 @@ protected: //-------------------------------------------------------------------- public: /*virtual*/ void updateRegion(LLViewerRegion *regionp); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); //-------------------------------------------------------------------- // LLCharacter interface and related diff --git a/indra/newview/llvograss.cpp b/indra/newview/llvograss.cpp index 5ad9ccc9af..566c33c0af 100644 --- a/indra/newview/llvograss.cpp +++ b/indra/newview/llvograss.cpp @@ -277,17 +277,17 @@ BOOL LLVOGrass::isActive() const return TRUE; } -BOOL LLVOGrass::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOGrass::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_GRASS))) { - return TRUE; + return; } if (!mDrawable) { // So drones work. - return TRUE; + return; } if(LLVOTree::isTreeRenderingStopped()) //stop rendering grass @@ -297,14 +297,14 @@ BOOL LLVOGrass::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) mNumBlades = 0 ; gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE); } - return TRUE ; + return; } else if(!mNumBlades)//restart grass rendering { mNumBlades = GRASS_MAX_BLADES ; gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE); - return TRUE ; + return; } if (mPatch && (mLastPatchUpdateTime != mPatch->getLastUpdateTime())) @@ -312,7 +312,7 @@ BOOL LLVOGrass::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE); } - return TRUE; + return; } diff --git a/indra/newview/llvograss.h b/indra/newview/llvograss.h index 00a59facf7..b9835b8802 100644 --- a/indra/newview/llvograss.h +++ b/indra/newview/llvograss.h @@ -73,7 +73,7 @@ public: void plantBlades(); /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate. - BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); /*virtual*/ BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end, S32 face = -1, // which face to check, -1 = ALL_SIDES diff --git a/indra/newview/llvoground.cpp b/indra/newview/llvoground.cpp index 6da54435e3..97b7418b40 100644 --- a/indra/newview/llvoground.cpp +++ b/indra/newview/llvoground.cpp @@ -49,18 +49,8 @@ LLVOGround::~LLVOGround() { } -BOOL LLVOGround::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOGround::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { - if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_GROUND))) - { - return TRUE; - } - - /*if (mDrawable) - { - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE); - }*/ - return TRUE; } diff --git a/indra/newview/llvoground.h b/indra/newview/llvoground.h index 73b097327e..290579b4da 100644 --- a/indra/newview/llvoground.h +++ b/indra/newview/llvoground.h @@ -41,7 +41,7 @@ protected: public: LLVOGround(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); // Graphical stuff for objects - maybe broken out into render class // later? diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index bd12328a6b..ceff75a0cc 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -414,7 +414,7 @@ void LLVoiceChannel::doSetState(const EState& new_state) mState = new_state; if (!mStateChangedCallback.empty()) - mStateChangedCallback(old_state, mState, mCallDirection, mCallEndedByAgent); + mStateChangedCallback(old_state, mState, mCallDirection, mCallEndedByAgent, mSessionID); } //static diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index b8597ee5cb..fed44974fd 100644 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -52,7 +52,7 @@ public: OUTGOING_CALL } EDirection; - typedef boost::signals2::signal<void(const EState& old_state, const EState& new_state, const EDirection& direction, bool ended_by_agent)> state_changed_signal_t; + typedef boost::signals2::signal<void(const EState& old_state, const EState& new_state, const EDirection& direction, bool ended_by_agent, const LLUUID& session_id)> state_changed_signal_t; // on current channel changed signal typedef boost::function<void(const LLUUID& session_id)> channel_changed_callback_t; diff --git a/indra/newview/llvoicevisualizer.cpp b/indra/newview/llvoicevisualizer.cpp index 47060720e7..d380a8672f 100644 --- a/indra/newview/llvoicevisualizer.cpp +++ b/indra/newview/llvoicevisualizer.cpp @@ -45,6 +45,7 @@ //29de489d-0491-fb00-7dab-f9e686d31e83 +#ifdef XXX_STINSON_CHUI_REWORK //-------------------------------------------------------------------------------------- // sound symbol constants //-------------------------------------------------------------------------------------- @@ -60,6 +61,7 @@ const F32 BASE_BRIGHTNESS = 0.7f; // gray level of the voice indicator when qu const F32 DOT_SIZE = 0.05f; // size of the dot billboard texture const F32 DOT_OPACITY = 0.7f; // how opaque the dot is const F32 WAVE_MOTION_RATE = 1.5f; // scalar applied to consecutive waves as a function of speaking amplitude +#endif // XXX_STINSON_CHUI_REWORK //-------------------------------------------------------------------------------------- // gesticulation constants @@ -67,22 +69,13 @@ const F32 WAVE_MOTION_RATE = 1.5f; // scalar applied to consecutive waves as a const F32 DEFAULT_MINIMUM_GESTICULATION_AMPLITUDE = 0.2f; const F32 DEFAULT_MAXIMUM_GESTICULATION_AMPLITUDE = 1.0f; +#ifdef XXX_STINSON_CHUI_REWORK //-------------------------------------------------------------------------------------- // other constants //-------------------------------------------------------------------------------------- const F32 ONE_HALF = 1.0f; // to clarify intent and reduce magic numbers in the code. const LLVector3 WORLD_UPWARD_DIRECTION = LLVector3( 0.0f, 0.0f, 1.0f ); // Z is up in SL - - -//------------------------------------------------------------------ -// handles parameter updates -//------------------------------------------------------------------ -static bool handleVoiceVisualizerPrefsChanged(const LLSD& newvalue) -{ - // Note: Ignore the specific event value, we look up the ones we want - LLVoiceVisualizer::setPreferences(); - return true; -} +#endif // XXX_STINSON_CHUI_REWORK //------------------------------------------------------------------ // Initialize the statics @@ -105,12 +98,28 @@ F32 LLVoiceVisualizer::sAahPowerTransfersf = 0.0f; //----------------------------------------------- // constructor //----------------------------------------------- +#ifdef XXX_STINSON_CHUI_REWORK LLVoiceVisualizer::LLVoiceVisualizer( const U8 type ) -:LLHUDEffect( type ) + : LLHUDEffect(type) +#else // XXX_STINSON_CHUI_REWORK +LLVoiceVisualizer::LLVoiceVisualizer() + : LLRefCount(), + mTimer(), + mStartTime(0.0), + mCurrentlySpeaking(false), + mSpeakingAmplitude(0.0f), + mMaxGesticulationAmplitude(DEFAULT_MAXIMUM_GESTICULATION_AMPLITUDE), + mMinGesticulationAmplitude(DEFAULT_MINIMUM_GESTICULATION_AMPLITUDE) +#endif // XXX_STINSON_CHUI_REWORK { +#ifdef XXX_STINSON_CHUI_REWORK mCurrentTime = mTimer.getTotalSeconds(); mPreviousTime = mCurrentTime; mStartTime = mCurrentTime; +#else // XXX_STINSON_CHUI_REWORK + mStartTime = mTimer.getTotalSeconds(); +#endif // XXX_STINSON_CHUI_REWORK +#ifdef XXX_STINSON_CHUI_REWORK mVoiceSourceWorldPosition = LLVector3( 0.0f, 0.0f, 0.0f ); mSpeakingAmplitude = 0.0f; mCurrentlySpeaking = false; @@ -119,9 +128,11 @@ LLVoiceVisualizer::LLVoiceVisualizer( const U8 type ) mMaxGesticulationAmplitude = DEFAULT_MAXIMUM_GESTICULATION_AMPLITUDE; mSoundSymbol.mActive = true; mSoundSymbol.mPosition = LLVector3( 0.0f, 0.0f, 0.0f ); +#endif // XXX_STINSON_CHUI_REWORK mTimer.reset(); +#ifdef XXX_STINSON_CHUI_REWORK const char* sound_level_img[] = { "voice_meter_dot.j2c", @@ -143,6 +154,7 @@ LLVoiceVisualizer::LLVoiceVisualizer( const U8 type ) } mSoundSymbol.mTexture[0]->setFilteringOption(LLTexUnit::TFO_ANISOTROPIC); +#endif // XXX_STINSON_CHUI_REWORK // The first instance loads the initial state from prefs. if (!sPrefsInitialized) @@ -150,18 +162,19 @@ LLVoiceVisualizer::LLVoiceVisualizer( const U8 type ) setPreferences(); // Set up our listener to get updates on all prefs values we care about. - gSavedSettings.getControl("LipSyncEnabled")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _2)); - gSavedSettings.getControl("LipSyncOohAahRate")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _2)); - gSavedSettings.getControl("LipSyncOoh")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _2)); - gSavedSettings.getControl("LipSyncAah")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _2)); - gSavedSettings.getControl("LipSyncOohPowerTransfer")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _2)); - gSavedSettings.getControl("LipSyncAahPowerTransfer")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _2)); + gSavedSettings.getControl("LipSyncEnabled")->getSignal()->connect(boost::bind(&LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged, _2)); + gSavedSettings.getControl("LipSyncOohAahRate")->getSignal()->connect(boost::bind(&LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged, _2)); + gSavedSettings.getControl("LipSyncOoh")->getSignal()->connect(boost::bind(&LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged, _2)); + gSavedSettings.getControl("LipSyncAah")->getSignal()->connect(boost::bind(&LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged, _2)); + gSavedSettings.getControl("LipSyncOohPowerTransfer")->getSignal()->connect(boost::bind(&LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged, _2)); + gSavedSettings.getControl("LipSyncAahPowerTransfer")->getSignal()->connect(boost::bind(&LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged, _2)); sPrefsInitialized = true; } }//--------------------------------------------------- +#ifdef XXX_STINSON_CHUI_REWORK //--------------------------------------------------- void LLVoiceVisualizer::setMinGesticulationAmplitude( F32 m ) { @@ -182,13 +195,16 @@ void LLVoiceVisualizer::setVoiceEnabled( bool v ) mVoiceEnabled = v; }//--------------------------------------------------- +#endif // XXX_STINSON_CHUI_REWORK //--------------------------------------------------- void LLVoiceVisualizer::setStartSpeaking() { mStartTime = mTimer.getTotalSeconds(); mCurrentlySpeaking = true; +#ifdef XXX_STINSON_CHUI_REWORK mSoundSymbol.mActive = true; +#endif // XXX_STINSON_CHUI_REWORK }//--------------------------------------------------- @@ -217,6 +233,15 @@ void LLVoiceVisualizer::setSpeakingAmplitude( F32 a ) }//--------------------------------------------------- +//------------------------------------------------------------------ +// handles parameter updates +//------------------------------------------------------------------ +bool LLVoiceVisualizer::handleVoiceVisualizerPrefsChanged(const LLSD& newvalue) +{ + // Note: Ignore the specific event value, we look up the ones we want + LLVoiceVisualizer::setPreferences(); + return true; +} //--------------------------------------------------- void LLVoiceVisualizer::setPreferences( ) @@ -334,6 +359,7 @@ void LLVoiceVisualizer::lipSyncOohAah( F32& ooh, F32& aah ) }//--------------------------------------------------- +#ifdef XXX_STINSON_CHUI_REWORK //--------------------------------------------------- // this method is inherited from HUD Effect //--------------------------------------------------- @@ -526,16 +552,13 @@ void LLVoiceVisualizer::render() }//--------------------------------------------------- - - - - //--------------------------------------------------- void LLVoiceVisualizer::setVoiceSourceWorldPosition( const LLVector3 &p ) { mVoiceSourceWorldPosition = p; }//--------------------------------------------------- +#endif // XXX_STINSON_CHUI_REWORK //--------------------------------------------------- VoiceGesticulationLevel LLVoiceVisualizer::getCurrentGesticulationLevel() @@ -566,6 +589,7 @@ LLVoiceVisualizer::~LLVoiceVisualizer() }//---------------------------------------------- +#ifdef XXX_STINSON_CHUI_REWORK //--------------------------------------------------- // "packData" is inherited from HUDEffect //--------------------------------------------------- @@ -616,10 +640,4 @@ void LLVoiceVisualizer::markDead() LLHUDEffect::markDead(); }//------------------------------------------------------------------ - - - - - - - +#endif // XXX_STINSON_CHUI_REWORK diff --git a/indra/newview/llvoicevisualizer.h b/indra/newview/llvoicevisualizer.h index e434c7f3f1..5da592c48e 100644 --- a/indra/newview/llvoicevisualizer.h +++ b/indra/newview/llvoicevisualizer.h @@ -42,7 +42,12 @@ #ifndef LL_VOICE_VISUALIZER_H #define LL_VOICE_VISUALIZER_H +#define XXX_STINSON_CHUI_REWORK // temporarily re-enabling the in-world voice-dot +#ifdef XXX_STINSON_CHUI_REWORK #include "llhudeffect.h" +#else // XXX_STINSON_CHUI_REWORK +#include "llpointer.h" +#endif // XXX_STINSON_CHUI_REWORK //----------------------------------------------------------------------------------------------- // The values of voice gesticulation represent energy levels for avatar animation, based on @@ -60,34 +65,45 @@ enum VoiceGesticulationLevel NUM_VOICE_GESTICULATION_LEVELS }; +#ifdef XXX_STINSON_CHUI_REWORK const static int NUM_VOICE_SYMBOL_WAVES = 7; +#endif // XXX_STINSON_CHUI_REWORK //---------------------------------------------------- // LLVoiceVisualizer class //---------------------------------------------------- +#ifdef XXX_STINSON_CHUI_REWORK class LLVoiceVisualizer : public LLHUDEffect +#else // XXX_STINSON_CHUI_REWORK +class LLVoiceVisualizer : public LLRefCount +#endif // XXX_STINSON_CHUI_REWORK { //--------------------------------------------------- // public methods //--------------------------------------------------- public: - LLVoiceVisualizer ( const U8 type ); //constructor +#ifdef XXX_STINSON_CHUI_REWORK + LLVoiceVisualizer( const U8 type ); //constructor +#else // XXX_STINSON_CHUI_REWORK + LLVoiceVisualizer(); //constructor +#endif // XXX_STINSON_CHUI_REWORK ~LLVoiceVisualizer(); //destructor - - friend class LLHUDObject; +#ifdef XXX_STINSON_CHUI_REWORK void setVoiceSourceWorldPosition( const LLVector3 &p ); // this should be the position of the speaking avatar's head void setMinGesticulationAmplitude( F32 ); // the lower range of meaningful amplitude for setting gesticulation level void setMaxGesticulationAmplitude( F32 ); // the upper range of meaningful amplitude for setting gesticulation level +#endif // XXX_STINSON_CHUI_REWORK void setStartSpeaking(); // tell me when the av starts speaking +#ifdef XXX_STINSON_CHUI_REWORK void setVoiceEnabled( bool ); // tell me whether or not the user is voice enabled +#endif // XXX_STINSON_CHUI_REWORK void setSpeakingAmplitude( F32 ); // tell me how loud the av is speaking (ranges from 0 to 1) void setStopSpeaking(); // tell me when the av stops speaking bool getCurrentlySpeaking(); // the get for the above set VoiceGesticulationLevel getCurrentGesticulationLevel(); // based on voice amplitude, I'll give you the current "energy level" of avatar speech - static void setPreferences( ); - static void lipStringToF32s ( std::string& in_string, F32*& out_F32s, U32& count_F32s ); // convert a string of digits to an array of floats void lipSyncOohAah( F32& ooh, F32& aah ); +#ifdef XXX_STINSON_CHUI_REWORK void render(); // inherited from HUD Effect void packData(LLMessageSystem *mesgsys); // inherited from HUD Effect void unpackData(LLMessageSystem *mesgsys, S32 blocknum); // inherited from HUD Effect @@ -103,12 +119,17 @@ class LLVoiceVisualizer : public LLHUDEffect //---------------------------------------------------------------------------------------------- void setMaxGesticulationAmplitude(); void setMinGesticulationAmplitude(); +#endif // XXX_STINSON_CHUI_REWORK //--------------------------------------------------- // private members //--------------------------------------------------- private: - + static bool handleVoiceVisualizerPrefsChanged(const LLSD& newvalue); + static void setPreferences( ); + static void lipStringToF32s ( std::string& in_string, F32*& out_F32s, U32& count_F32s ); // convert a string of digits to an array of floats + +#ifdef XXX_STINSON_CHUI_REWORK struct SoundSymbol { F32 mWaveExpansion [ NUM_VOICE_SYMBOL_WAVES ]; @@ -119,15 +140,20 @@ class LLVoiceVisualizer : public LLHUDEffect bool mActive; LLVector3 mPosition; }; +#endif // XXX_STINSON_CHUI_REWORK LLFrameTimer mTimer; // so I can ask the current time in seconds F64 mStartTime; // time in seconds when speaking started +#ifdef XXX_STINSON_CHUI_REWORK F64 mCurrentTime; // current time in seconds, captured every step F64 mPreviousTime; // copy of "current time" from last frame SoundSymbol mSoundSymbol; // the sound symbol that appears over the avatar's head bool mVoiceEnabled; // if off, no rendering should happen +#endif // XXX_STINSON_CHUI_REWORK bool mCurrentlySpeaking; // is the user currently speaking? +#ifdef XXX_STINSON_CHUI_REWORK LLVector3 mVoiceSourceWorldPosition; // give this to me every step - I need it to update the sound symbol +#endif // XXX_STINSON_CHUI_REWORK F32 mSpeakingAmplitude; // this should be set as often as possible when the user is speaking F32 mMaxGesticulationAmplitude; // this is the upper-limit of the envelope of detectable gesticulation leves F32 mMinGesticulationAmplitude; // this is the lower-limit of the envelope of detectable gesticulation leves diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 820d1d73e1..f1bf4a6d75 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -3964,6 +3964,7 @@ void LLVivoxVoiceClient::messageEvent( session->mCallerID, session->mName.c_str(), message.c_str(), + false, LLStringUtil::null, // default arg IM_NOTHING_SPECIAL, // default arg 0, // default arg diff --git a/indra/newview/llvopartgroup.cpp b/indra/newview/llvopartgroup.cpp index 9cce68fff6..c19996617b 100644 --- a/indra/newview/llvopartgroup.cpp +++ b/indra/newview/llvopartgroup.cpp @@ -152,8 +152,8 @@ bool ll_is_part_idx_allocated(S32 idx, S32* start, S32* end) void LLVOPartGroup::freeVBSlot(S32 idx) { llassert(idx < LL_MAX_PARTICLE_COUNT && idx >= 0); - llassert(sVBSlotCursor > sVBSlotFree); - llassert(ll_is_part_idx_allocated(idx, sVBSlotCursor, sVBSlotFree+LL_MAX_PARTICLE_COUNT)); + //llassert(sVBSlotCursor > sVBSlotFree); + //llassert(ll_is_part_idx_allocated(idx, sVBSlotCursor, sVBSlotFree+LL_MAX_PARTICLE_COUNT)); if (sVBSlotCursor > sVBSlotFree) { @@ -196,9 +196,8 @@ void LLVOPartGroup::updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax) mDrawable->setPositionGroup(pos); } -BOOL LLVOPartGroup::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOPartGroup::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { - return TRUE; } void LLVOPartGroup::setPixelAreaAndAngle(LLAgent &agent) diff --git a/indra/newview/llvopartgroup.h b/indra/newview/llvopartgroup.h index 43b2844f07..42c1252d01 100644 --- a/indra/newview/llvopartgroup.h +++ b/indra/newview/llvopartgroup.h @@ -63,7 +63,7 @@ public: LLVOPartGroup(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp); /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate. - BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); virtual F32 getBinRadius(); virtual void updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax); diff --git a/indra/newview/llvosky.cpp b/indra/newview/llvosky.cpp index 312034022e..31358df85f 100644 --- a/indra/newview/llvosky.cpp +++ b/indra/newview/llvosky.cpp @@ -1052,9 +1052,8 @@ void LLVOSky::calcAtmospherics(void) mFadeColor.setAlpha(0); } -BOOL LLVOSky::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOSky::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { - return TRUE; } BOOL LLVOSky::updateSky() diff --git a/indra/newview/llvosky.h b/indra/newview/llvosky.h index 6e6898d80a..2a150eccb9 100644 --- a/indra/newview/llvosky.h +++ b/indra/newview/llvosky.h @@ -461,7 +461,7 @@ public: void cleanupGL(); void restoreGL(); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); BOOL updateSky(); // Graphical stuff for objects - maybe broken out into render class diff --git a/indra/newview/llvosurfacepatch.cpp b/indra/newview/llvosurfacepatch.cpp index 94a3111f4c..cb905d02da 100644 --- a/indra/newview/llvosurfacepatch.cpp +++ b/indra/newview/llvosurfacepatch.cpp @@ -80,9 +80,9 @@ public: glNormalPointer(GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_NORMAL], (void*)(base + mOffsets[TYPE_NORMAL])); } if (data_mask & MAP_TEXCOORD3) - { //substitute tex coord 0 for tex coord 3 + { //substitute tex coord 1 for tex coord 3 glClientActiveTextureARB(GL_TEXTURE3_ARB); - glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD0], (void*)(base + mOffsets[TYPE_TEXCOORD0])); + glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD1], (void*)(base + mOffsets[TYPE_TEXCOORD1])); glClientActiveTextureARB(GL_TEXTURE0_ARB); } if (data_mask & MAP_TEXCOORD2) diff --git a/indra/newview/llvotree.cpp b/indra/newview/llvotree.cpp index 337ddfb24d..6687ce432f 100644 --- a/indra/newview/llvotree.cpp +++ b/indra/newview/llvotree.cpp @@ -339,11 +339,11 @@ U32 LLVOTree::processUpdateMessage(LLMessageSystem *mesgsys, return retval; } -BOOL LLVOTree::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOTree::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_TREE))) { - return TRUE; + return; } S32 trunk_LOD = sMAX_NUM_TREE_LOD_LEVELS ; @@ -393,8 +393,6 @@ BOOL LLVOTree::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) } mTrunkLOD = trunk_LOD; - - return TRUE; } const F32 TREE_BLEND_MIN = 1.f; diff --git a/indra/newview/llvotree.h b/indra/newview/llvotree.h index 0554935539..52debc85ab 100644 --- a/indra/newview/llvotree.h +++ b/indra/newview/llvotree.h @@ -59,7 +59,7 @@ public: void **user_data, U32 block_num, const EObjectUpdateType update_type, LLDataPacker *dp); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); // Graphical stuff for objects - maybe broken out into render class later? /*virtual*/ void render(LLAgent &agent); diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 0a5c8b5139..b73c2a20ab 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -329,7 +329,7 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, { if (!mTextureAnimp) { - mTextureAnimp = new LLViewerTextureAnim(); + mTextureAnimp = new LLViewerTextureAnim(this); } else { @@ -431,7 +431,7 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, { if (!mTextureAnimp) { - mTextureAnimp = new LLViewerTextureAnim(); + mTextureAnimp = new LLViewerTextureAnim(this); } else { @@ -499,183 +499,144 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, void LLVOVolume::animateTextures() { - F32 off_s = 0.f, off_t = 0.f, scale_s = 1.f, scale_t = 1.f, rot = 0.f; - S32 result = mTextureAnimp->animateTextures(off_s, off_t, scale_s, scale_t, rot); - - if (result) + if (!mDead) { - if (!mTexAnimMode) - { - mFaceMappingChanged = TRUE; - gPipeline.markTextured(mDrawable); - } - mTexAnimMode = result | mTextureAnimp->mMode; - - S32 start=0, end=mDrawable->getNumFaces()-1; - if (mTextureAnimp->mFace >= 0 && mTextureAnimp->mFace <= end) - { - start = end = mTextureAnimp->mFace; - } - - for (S32 i = start; i <= end; i++) + F32 off_s = 0.f, off_t = 0.f, scale_s = 1.f, scale_t = 1.f, rot = 0.f; + S32 result = mTextureAnimp->animateTextures(off_s, off_t, scale_s, scale_t, rot); + + if (result) { - LLFace* facep = mDrawable->getFace(i); - if (!facep) continue; - if(facep->getVirtualSize() <= MIN_TEX_ANIM_SIZE && facep->mTextureMatrix) continue; - - const LLTextureEntry* te = facep->getTextureEntry(); - - if (!te) + if (!mTexAnimMode) { - continue; + mFaceMappingChanged = TRUE; + gPipeline.markTextured(mDrawable); } - - if (!(result & LLViewerTextureAnim::ROTATE)) + mTexAnimMode = result | mTextureAnimp->mMode; + + S32 start=0, end=mDrawable->getNumFaces()-1; + if (mTextureAnimp->mFace >= 0 && mTextureAnimp->mFace <= end) { - te->getRotation(&rot); + start = end = mTextureAnimp->mFace; } - if (!(result & LLViewerTextureAnim::TRANSLATE)) - { - te->getOffset(&off_s,&off_t); - } - if (!(result & LLViewerTextureAnim::SCALE)) + + for (S32 i = start; i <= end; i++) { - te->getScale(&scale_s, &scale_t); - } + LLFace* facep = mDrawable->getFace(i); + if (!facep) continue; + if(facep->getVirtualSize() <= MIN_TEX_ANIM_SIZE && facep->mTextureMatrix) continue; - if (!facep->mTextureMatrix) - { - facep->mTextureMatrix = new LLMatrix4(); - } + const LLTextureEntry* te = facep->getTextureEntry(); + + if (!te) + { + continue; + } + + if (!(result & LLViewerTextureAnim::ROTATE)) + { + te->getRotation(&rot); + } + if (!(result & LLViewerTextureAnim::TRANSLATE)) + { + te->getOffset(&off_s,&off_t); + } + if (!(result & LLViewerTextureAnim::SCALE)) + { + te->getScale(&scale_s, &scale_t); + } + + if (!facep->mTextureMatrix) + { + facep->mTextureMatrix = new LLMatrix4(); + } - LLMatrix4& tex_mat = *facep->mTextureMatrix; - tex_mat.setIdentity(); - LLVector3 trans ; + LLMatrix4& tex_mat = *facep->mTextureMatrix; + tex_mat.setIdentity(); + LLVector3 trans ; - if(facep->isAtlasInUse()) - { - // - //if use atlas for animated texture - //apply the following transform to the animation matrix. - // - - F32 tcoord_xoffset = 0.f ; - F32 tcoord_yoffset = 0.f ; - F32 tcoord_xscale = 1.f ; - F32 tcoord_yscale = 1.f ; if(facep->isAtlasInUse()) { - const LLVector2* tmp = facep->getTexCoordOffset() ; - tcoord_xoffset = tmp->mV[0] ; - tcoord_yoffset = tmp->mV[1] ; + // + //if use atlas for animated texture + //apply the following transform to the animation matrix. + // + + F32 tcoord_xoffset = 0.f ; + F32 tcoord_yoffset = 0.f ; + F32 tcoord_xscale = 1.f ; + F32 tcoord_yscale = 1.f ; + if(facep->isAtlasInUse()) + { + const LLVector2* tmp = facep->getTexCoordOffset() ; + tcoord_xoffset = tmp->mV[0] ; + tcoord_yoffset = tmp->mV[1] ; - tmp = facep->getTexCoordScale() ; - tcoord_xscale = tmp->mV[0] ; - tcoord_yscale = tmp->mV[1] ; - } - trans.set(LLVector3(tcoord_xoffset + tcoord_xscale * (off_s+0.5f), tcoord_yoffset + tcoord_yscale * (off_t+0.5f), 0.f)); + tmp = facep->getTexCoordScale() ; + tcoord_xscale = tmp->mV[0] ; + tcoord_yscale = tmp->mV[1] ; + } + trans.set(LLVector3(tcoord_xoffset + tcoord_xscale * (off_s+0.5f), tcoord_yoffset + tcoord_yscale * (off_t+0.5f), 0.f)); - tex_mat.translate(LLVector3(-(tcoord_xoffset + tcoord_xscale * 0.5f), -(tcoord_yoffset + tcoord_yscale * 0.5f), 0.f)); - } - else //non atlas - { - trans.set(LLVector3(off_s+0.5f, off_t+0.5f, 0.f)); - tex_mat.translate(LLVector3(-0.5f, -0.5f, 0.f)); - } + tex_mat.translate(LLVector3(-(tcoord_xoffset + tcoord_xscale * 0.5f), -(tcoord_yoffset + tcoord_yscale * 0.5f), 0.f)); + } + else //non atlas + { + trans.set(LLVector3(off_s+0.5f, off_t+0.5f, 0.f)); + tex_mat.translate(LLVector3(-0.5f, -0.5f, 0.f)); + } - LLVector3 scale(scale_s, scale_t, 1.f); - LLQuaternion quat; - quat.setQuat(rot, 0, 0, -1.f); + LLVector3 scale(scale_s, scale_t, 1.f); + LLQuaternion quat; + quat.setQuat(rot, 0, 0, -1.f); - tex_mat.rotate(quat); + tex_mat.rotate(quat); - LLMatrix4 mat; - mat.initAll(scale, LLQuaternion(), LLVector3()); - tex_mat *= mat; + LLMatrix4 mat; + mat.initAll(scale, LLQuaternion(), LLVector3()); + tex_mat *= mat; - tex_mat.translate(trans); + tex_mat.translate(trans); + } } - } - else - { - if (mTexAnimMode && mTextureAnimp->mRate == 0) + else { - U8 start, count; - - if (mTextureAnimp->mFace == -1) + if (mTexAnimMode && mTextureAnimp->mRate == 0) { - start = 0; - count = getNumTEs(); - } - else - { - start = (U8) mTextureAnimp->mFace; - count = 1; - } + U8 start, count; - for (S32 i = start; i < start + count; i++) - { - if (mTexAnimMode & LLViewerTextureAnim::TRANSLATE) + if (mTextureAnimp->mFace == -1) { - setTEOffset(i, mTextureAnimp->mOffS, mTextureAnimp->mOffT); + start = 0; + count = getNumTEs(); } - if (mTexAnimMode & LLViewerTextureAnim::SCALE) + else { - setTEScale(i, mTextureAnimp->mScaleS, mTextureAnimp->mScaleT); + start = (U8) mTextureAnimp->mFace; + count = 1; } - if (mTexAnimMode & LLViewerTextureAnim::ROTATE) + + for (S32 i = start; i < start + count; i++) { - setTERotation(i, mTextureAnimp->mRot); + if (mTexAnimMode & LLViewerTextureAnim::TRANSLATE) + { + setTEOffset(i, mTextureAnimp->mOffS, mTextureAnimp->mOffT); + } + if (mTexAnimMode & LLViewerTextureAnim::SCALE) + { + setTEScale(i, mTextureAnimp->mScaleS, mTextureAnimp->mScaleT); + } + if (mTexAnimMode & LLViewerTextureAnim::ROTATE) + { + setTERotation(i, mTextureAnimp->mRot); + } } - } - - gPipeline.markTextured(mDrawable); - mFaceMappingChanged = TRUE; - mTexAnimMode = 0; - } - } -} -BOOL LLVOVolume::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) -{ - LLViewerObject::idleUpdate(agent, world, time); - - //static LLFastTimer::DeclareTimer ftm("Volume Idle"); - //LLFastTimer t(ftm); - if (mDead || mDrawable.isNull()) - { - return TRUE; - } - - /////////////////////// - // - // Do texture animation stuff - // - - if (mTextureAnimp && gAnimateTextures) - { - animateTextures(); - } - - // Dispatch to implementation - if (mVolumeImpl) - { - mVolumeImpl->doIdleUpdate(agent, world, time); - } - - const S32 MAX_ACTIVE_OBJECT_QUIET_FRAMES = 40; - - if (mDrawable->isActive()) - { - if (mDrawable->isRoot() && - mDrawable->mQuietCount++ > MAX_ACTIVE_OBJECT_QUIET_FRAMES && - (!mDrawable->getParent() || !mDrawable->getParent()->isActive())) - { - mDrawable->makeStatic(); + gPipeline.markTextured(mDrawable); + mFaceMappingChanged = TRUE; + mTexAnimMode = 0; + } } } - - return TRUE; } void LLVOVolume::updateTextures() @@ -698,7 +659,8 @@ void LLVOVolume::updateTextures() } } - } + + } } BOOL LLVOVolume::isVisible() const @@ -916,8 +878,7 @@ void LLVOVolume::updateTextureVirtualSize(bool forced) BOOL LLVOVolume::isActive() const { - return !mStatic || mTextureAnimp || (mVolumeImpl && mVolumeImpl->isActive()) || - (mDrawable.notNull() && mDrawable->isActive()); + return !mStatic; } BOOL LLVOVolume::setMaterial(const U8 material) diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index 798927a45f..5482c80f2b 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -68,7 +68,7 @@ class LLVolumeInterface public: virtual ~LLVolumeInterface() { } virtual LLVolumeInterfaceType getInterfaceType() const = 0; - virtual void doIdleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) = 0; + virtual void doIdleUpdate() = 0; virtual BOOL doUpdateGeometry(LLDrawable *drawable) = 0; virtual LLVector3 getPivotPosition() const = 0; virtual void onSetVolume(const LLVolumeParams &volume_params, const S32 detail) = 0; @@ -114,8 +114,7 @@ public: void deleteFaces(); void animateTextures(); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); - + BOOL isVisible() const ; /*virtual*/ BOOL isActive() const; /*virtual*/ BOOL isAttachment() const; diff --git a/indra/newview/llvowater.cpp b/indra/newview/llvowater.cpp index 942eff6171..e8a1c3d1d6 100644 --- a/indra/newview/llvowater.cpp +++ b/indra/newview/llvowater.cpp @@ -100,17 +100,8 @@ void LLVOWater::updateTextures() } // Never gets called -BOOL LLVOWater::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOWater::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { - /*if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_WATER))) - { - return TRUE; - } - if (mDrawable) - { - gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE); - }*/ - return TRUE; } LLDrawable *LLVOWater::createDrawable(LLPipeline *pipeline) diff --git a/indra/newview/llvowater.h b/indra/newview/llvowater.h index ed709dd840..cf9323ef2e 100644 --- a/indra/newview/llvowater.h +++ b/indra/newview/llvowater.h @@ -58,7 +58,7 @@ public: static void initClass(); static void cleanupClass(); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); /*virtual*/ void updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax); diff --git a/indra/newview/llvowlsky.cpp b/indra/newview/llvowlsky.cpp index afd902201b..a33f42cf84 100644 --- a/indra/newview/llvowlsky.cpp +++ b/indra/newview/llvowlsky.cpp @@ -92,9 +92,9 @@ void LLVOWLSky::initSunDirection(LLVector3 const & sun_direction, { } -BOOL LLVOWLSky::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) +void LLVOWLSky::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { - return TRUE; + } BOOL LLVOWLSky::isActive(void) const diff --git a/indra/newview/llvowlsky.h b/indra/newview/llvowlsky.h index 825e13a203..729dced15e 100644 --- a/indra/newview/llvowlsky.h +++ b/indra/newview/llvowlsky.h @@ -53,7 +53,7 @@ public: void initSunDirection(LLVector3 const & sun_direction, LLVector3 const & sun_angular_velocity); - /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); + /*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); /*virtual*/ BOOL isActive(void) const; /*virtual*/ LLDrawable * createDrawable(LLPipeline *pipeline); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index b42a84da47..b6f3301c6c 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -449,6 +449,19 @@ LLPipeline::LLPipeline() : mLightFunc = 0; } +void LLPipeline::connectRefreshCachedSettingsSafe(const std::string name) +{ + LLPointer<LLControlVariable> cntrl_ptr = gSavedSettings.getControl(name); + if ( cntrl_ptr.isNull() ) + { + llwarns << "Global setting name not found:" << name << llendl; + } + else + { + cntrl_ptr->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); + } +} + void LLPipeline::init() { LLMemType mt(LLMemType::MTYPE_PIPELINE_INIT); @@ -533,88 +546,86 @@ void LLPipeline::init() // // Update all settings to trigger a cached settings refresh // - - gSavedSettings.getControl("RenderAutoMaskAlphaDeferred")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderAutoMaskAlphaNonDeferred")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderUseFarClip")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderAvatarMaxVisible")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDelayVBUpdate")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - - gSavedSettings.getControl("UseOcclusion")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - - gSavedSettings.getControl("VertexShaderEnable")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderAvatarVP")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("WindLightUseAtmosShaders")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDeferred")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDeferredSunWash")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderFSAASamples")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderResolutionDivisor")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderUIBuffer")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowDetail")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDeferredSSAO")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowResolutionScale")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderLocalLights")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDelayCreation")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderAnimateRes")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("FreezeTime")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("DebugBeaconLineWidth")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderHighlightBrightness")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderHighlightColor")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderHighlightThickness")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSpotLightsInNondeferred")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewAmbientColor")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewDiffuse0")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewSpecular0")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewDiffuse1")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewSpecular1")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewDiffuse2")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewSpecular2")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewDirection0")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewDirection1")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("PreviewDirection2")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowMinLuminance")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowMaxExtractAlpha")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowWarmthAmount")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowLumWeights")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowWarmthWeights")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowResolutionPow")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowIterations")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowWidth")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderGlowStrength")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDepthOfField")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraFocusTransitionTime")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraFNumber")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraFocalLength")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraFieldOfView")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowNoise")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowBlurSize")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSSAOScale")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSSAOMaxScale")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSSAOFactor")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSSAOEffect")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowOffsetError")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowBiasError")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowOffset")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowBias")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSpotShadowOffset")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderSpotShadowBias")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderEdgeDepthCutoff")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderEdgeNormCutoff")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowGaussian")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowBlurDistFactor")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderDeferredAtmospheric")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderReflectionDetail")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderHighlightFadeTime")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowClipPlanes")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowOrthoClipPlanes")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowNearDist")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderFarClip")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowSplitExponent")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowErrorCutoff")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("RenderShadowFOVCutoff")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraOffset")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraMaxCoF")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); - gSavedSettings.getControl("CameraDoFResScale")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); + connectRefreshCachedSettingsSafe("RenderAutoMaskAlphaDeferred"); + connectRefreshCachedSettingsSafe("RenderAutoMaskAlphaNonDeferred"); + connectRefreshCachedSettingsSafe("RenderUseFarClip"); + connectRefreshCachedSettingsSafe("RenderAvatarMaxVisible"); + connectRefreshCachedSettingsSafe("RenderDelayVBUpdate"); + connectRefreshCachedSettingsSafe("UseOcclusion"); + connectRefreshCachedSettingsSafe("VertexShaderEnable"); + connectRefreshCachedSettingsSafe("RenderAvatarVP"); + connectRefreshCachedSettingsSafe("WindLightUseAtmosShaders"); + connectRefreshCachedSettingsSafe("RenderDeferred"); + connectRefreshCachedSettingsSafe("RenderDeferredSunWash"); + connectRefreshCachedSettingsSafe("RenderFSAASamples"); + connectRefreshCachedSettingsSafe("RenderResolutionDivisor"); + connectRefreshCachedSettingsSafe("RenderUIBuffer"); + connectRefreshCachedSettingsSafe("RenderShadowDetail"); + connectRefreshCachedSettingsSafe("RenderDeferredSSAO"); + connectRefreshCachedSettingsSafe("RenderShadowResolutionScale"); + connectRefreshCachedSettingsSafe("RenderLocalLights"); + connectRefreshCachedSettingsSafe("RenderDelayCreation"); + connectRefreshCachedSettingsSafe("RenderAnimateRes"); + connectRefreshCachedSettingsSafe("FreezeTime"); + connectRefreshCachedSettingsSafe("DebugBeaconLineWidth"); + connectRefreshCachedSettingsSafe("RenderHighlightBrightness"); + connectRefreshCachedSettingsSafe("RenderHighlightColor"); + connectRefreshCachedSettingsSafe("RenderHighlightThickness"); + connectRefreshCachedSettingsSafe("RenderSpotLightsInNondeferred"); + connectRefreshCachedSettingsSafe("PreviewAmbientColor"); + connectRefreshCachedSettingsSafe("PreviewDiffuse0"); + connectRefreshCachedSettingsSafe("PreviewSpecular0"); + connectRefreshCachedSettingsSafe("PreviewDiffuse1"); + connectRefreshCachedSettingsSafe("PreviewSpecular1"); + connectRefreshCachedSettingsSafe("PreviewDiffuse2"); + connectRefreshCachedSettingsSafe("PreviewSpecular2"); + connectRefreshCachedSettingsSafe("PreviewDirection0"); + connectRefreshCachedSettingsSafe("PreviewDirection1"); + connectRefreshCachedSettingsSafe("PreviewDirection2"); + connectRefreshCachedSettingsSafe("RenderGlowMinLuminance"); + connectRefreshCachedSettingsSafe("RenderGlowMaxExtractAlpha"); + connectRefreshCachedSettingsSafe("RenderGlowWarmthAmount"); + connectRefreshCachedSettingsSafe("RenderGlowLumWeights"); + connectRefreshCachedSettingsSafe("RenderGlowWarmthWeights"); + connectRefreshCachedSettingsSafe("RenderGlowResolutionPow"); + connectRefreshCachedSettingsSafe("RenderGlowIterations"); + connectRefreshCachedSettingsSafe("RenderGlowWidth"); + connectRefreshCachedSettingsSafe("RenderGlowStrength"); + connectRefreshCachedSettingsSafe("RenderDepthOfField"); + connectRefreshCachedSettingsSafe("CameraFocusTransitionTime"); + connectRefreshCachedSettingsSafe("CameraFNumber"); + connectRefreshCachedSettingsSafe("CameraFocalLength"); + connectRefreshCachedSettingsSafe("CameraFieldOfView"); + connectRefreshCachedSettingsSafe("RenderShadowNoise"); + connectRefreshCachedSettingsSafe("RenderShadowBlurSize"); + connectRefreshCachedSettingsSafe("RenderSSAOScale"); + connectRefreshCachedSettingsSafe("RenderSSAOMaxScale"); + connectRefreshCachedSettingsSafe("RenderSSAOFactor"); + connectRefreshCachedSettingsSafe("RenderSSAOEffect"); + connectRefreshCachedSettingsSafe("RenderShadowOffsetError"); + connectRefreshCachedSettingsSafe("RenderShadowBiasError"); + connectRefreshCachedSettingsSafe("RenderShadowOffset"); + connectRefreshCachedSettingsSafe("RenderShadowBias"); + connectRefreshCachedSettingsSafe("RenderSpotShadowOffset"); + connectRefreshCachedSettingsSafe("RenderSpotShadowBias"); + connectRefreshCachedSettingsSafe("RenderEdgeDepthCutoff"); + connectRefreshCachedSettingsSafe("RenderEdgeNormCutoff"); + connectRefreshCachedSettingsSafe("RenderShadowGaussian"); + connectRefreshCachedSettingsSafe("RenderShadowBlurDistFactor"); + connectRefreshCachedSettingsSafe("RenderDeferredAtmospheric"); + connectRefreshCachedSettingsSafe("RenderReflectionDetail"); + connectRefreshCachedSettingsSafe("RenderHighlightFadeTime"); + connectRefreshCachedSettingsSafe("RenderShadowClipPlanes"); + connectRefreshCachedSettingsSafe("RenderShadowOrthoClipPlanes"); + connectRefreshCachedSettingsSafe("RenderShadowNearDist"); + connectRefreshCachedSettingsSafe("RenderFarClip"); + connectRefreshCachedSettingsSafe("RenderShadowSplitExponent"); + connectRefreshCachedSettingsSafe("RenderShadowErrorCutoff"); + connectRefreshCachedSettingsSafe("RenderShadowFOVCutoff"); + connectRefreshCachedSettingsSafe("CameraOffset"); + connectRefreshCachedSettingsSafe("CameraMaxCoF"); + connectRefreshCachedSettingsSafe("CameraDoFResScale"); + connectRefreshCachedSettingsSafe("RenderAutoHideSurfaceAreaLimit"); gSavedSettings.getControl("RenderAutoHideSurfaceAreaLimit")->getCommitSignal()->connect(boost::bind(&LLPipeline::refreshCachedSettings)); } @@ -722,7 +733,7 @@ void LLPipeline::destroyGL() { glDeleteQueriesARB(1, &mMeshDirtyQueryObject); mMeshDirtyQueryObject = 0; -} + } } static LLFastTimer::DeclareTimer FTM_RESIZE_SCREEN_TEXTURE("Resize Screen Texture"); @@ -881,7 +892,7 @@ bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY, U32 samples) U32 sun_shadow_map_width = ((U32(resX*scale)+1)&~1); // must be even to avoid a stripe in the horizontal shadow blur for (U32 i = 0; i < 4; i++) { - if (!mShadow[i].allocate(sun_shadow_map_width,U32(resY*scale), 0, TRUE, FALSE, LLTexUnit::TT_RECT_TEXTURE)) return false; + if (!mShadow[i].allocate(sun_shadow_map_width,U32(resY*scale), 0, TRUE, FALSE, LLTexUnit::TT_TEXTURE)) return false; } } else @@ -1850,6 +1861,10 @@ void LLPipeline::updateMovedList(LLDrawable::drawable_vector_t& moved_list) drawablep->clearState(LLDrawable::EARLY_MOVE | LLDrawable::MOVE_UNDAMPED); if (done) { + if (drawablep->isRoot()) + { + drawablep->makeStatic(); + } drawablep->clearState(LLDrawable::ON_MOVE_LIST); if (drawablep->isState(LLDrawable::ANIMATED_CHILD)) { //will likely not receive any future world matrix updates @@ -3469,7 +3484,7 @@ void LLPipeline::postSort(LLCamera& camera) rebuildPriorityGroups(); llpushcallstacks ; - + //build render map for (LLCullResult::sg_iterator i = sCull->beginVisibleGroups(); i != sCull->endVisibleGroups(); ++i) { @@ -3541,7 +3556,7 @@ void LLPipeline::postSort(LLCamera& camera) } } } - + //flush particle VB LLVOPartGroup::sVB->flush(); @@ -5203,11 +5218,6 @@ void LLPipeline::rebuildPools() } max_count--; } - - if (isAgentAvatarValid()) - { - gAgentAvatarp->rebuildHUD(); - } } void LLPipeline::addToQuickLookup( LLDrawPool* new_poolp ) @@ -5695,7 +5705,7 @@ void LLPipeline::calcNearbyLights(LLCamera& camera) // crazy cast so that we can overwrite the fade value // even though gcc enforces sets as const // (fade value doesn't affect sort so this is safe) - Light* farthest_light = ((Light*) (&(*(mNearbyLights.rbegin())))); + Light* farthest_light = (const_cast<Light*>(&(*(mNearbyLights.rbegin())))); if (light->dist < farthest_light->dist) { if (farthest_light->fade >= 0.f) @@ -6720,13 +6730,13 @@ void LLPipeline::resetVertexBuffers(LLDrawable* drawable) LLFace* facep = drawable->getFace(i); if (facep) { - facep->clearVertexBuffer(); + facep->clearVertexBuffer(); + } } } -} void LLPipeline::resetVertexBuffers() -{ +{ mResetVertexBuffers = true; } @@ -6762,6 +6772,8 @@ void LLPipeline::doResetVertexBuffers() gSky.resetVertexBuffers(); + LLVOPartGroup::destroyGL(); + if ( LLPathingLib::getInstance() ) { LLPathingLib::getInstance()->cleanupVBOManager(); @@ -7096,15 +7108,8 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield) } else { - LLViewerObject* obj = gAgentCamera.getFocusObject(); - if (obj) - { //focus on alt-zoom target - focus_point = LLVector3(gAgentCamera.getFocusGlobal()-gAgent.getRegion()->getOriginGlobal()); - } - else - { //focus on your avatar - focus_point = gAgent.getPositionAgent(); - } + //focus on alt-zoom target + focus_point = LLVector3(gAgentCamera.getFocusGlobal()-gAgent.getRegion()->getOriginGlobal()); } } @@ -7605,7 +7610,7 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, U32 light_index, U32 n for (U32 i = 0; i < 4; i++) { - channel = shader.enableTexture(LLShaderMgr::DEFERRED_SHADOW0+i, LLTexUnit::TT_RECT_TEXTURE); + channel = shader.enableTexture(LLShaderMgr::DEFERRED_SHADOW0+i, LLTexUnit::TT_TEXTURE); stop_glerror(); if (channel > -1) { @@ -7615,8 +7620,8 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, U32 light_index, U32 n gGL.getTexUnit(channel)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); stop_glerror(); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL); stop_glerror(); } } @@ -7696,13 +7701,13 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, U32 light_index, U32 n matrix_nondiag, matrix_nondiag, matrix_diag}; shader.uniformMatrix3fv(LLShaderMgr::DEFERRED_SSAO_EFFECT_MAT, 1, GL_FALSE, ssao_effect_mat); - F32 shadow_offset_error = 1.f + RenderShadowOffsetError * fabsf(LLViewerCamera::getInstance()->getOrigin().mV[2]); - F32 shadow_bias_error = 1.f + RenderShadowBiasError * fabsf(LLViewerCamera::getInstance()->getOrigin().mV[2]); + //F32 shadow_offset_error = 1.f + RenderShadowOffsetError * fabsf(LLViewerCamera::getInstance()->getOrigin().mV[2]); + F32 shadow_bias_error = RenderShadowBiasError * fabsf(LLViewerCamera::getInstance()->getOrigin().mV[2])/3000.f; shader.uniform2f(LLShaderMgr::DEFERRED_SCREEN_RES, mDeferredScreen.getWidth(), mDeferredScreen.getHeight()); shader.uniform1f(LLShaderMgr::DEFERRED_NEAR_CLIP, LLViewerCamera::getInstance()->getNear()*2.f); - shader.uniform1f (LLShaderMgr::DEFERRED_SHADOW_OFFSET, RenderShadowOffset*shadow_offset_error); - shader.uniform1f(LLShaderMgr::DEFERRED_SHADOW_BIAS, RenderShadowBias*shadow_bias_error); + shader.uniform1f (LLShaderMgr::DEFERRED_SHADOW_OFFSET, RenderShadowOffset); //*shadow_offset_error); + shader.uniform1f(LLShaderMgr::DEFERRED_SHADOW_BIAS, RenderShadowBias+shadow_bias_error); shader.uniform1f(LLShaderMgr::DEFERRED_SPOT_SHADOW_OFFSET, RenderSpotShadowOffset); shader.uniform1f(LLShaderMgr::DEFERRED_SPOT_SHADOW_BIAS, RenderSpotShadowBias); @@ -7993,7 +7998,7 @@ void LLPipeline::renderDeferredLighting() } mCubeVB->setBuffer(LLVertexBuffer::MAP_VERTEX); - + LLGLDepthTest depth(GL_TRUE, GL_FALSE); for (LLDrawable::drawable_set_t::iterator iter = mLights.begin(); iter != mLights.end(); ++iter) { @@ -8039,7 +8044,7 @@ void LLPipeline::renderDeferredLighting() } sVisibleLightCount++; - + if (camera->getOrigin().mV[0] > c[0] + s + 0.2f || camera->getOrigin().mV[0] < c[0] - s - 0.2f || camera->getOrigin().mV[1] > c[1] + s + 0.2f || @@ -8426,9 +8431,9 @@ void LLPipeline::unbindDeferredShader(LLGLSLShader &shader) for (U32 i = 0; i < 4; i++) { - if (shader.disableTexture(LLShaderMgr::DEFERRED_SHADOW0+i, LLTexUnit::TT_RECT_TEXTURE) > -1) + if (shader.disableTexture(LLShaderMgr::DEFERRED_SHADOW0+i) > -1) { - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); } } @@ -8845,6 +8850,7 @@ void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera gGL.setColorMask(false, false); LLFastTimer ftm(FTM_SHADOW_SIMPLE); + gGL.getTexUnit(0)->disable(); for (U32 i = 0; i < sizeof(types)/sizeof(U32); ++i) { diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index 368be1c14d..0140e24d63 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -382,6 +382,7 @@ private: BOOL updateDrawableGeom(LLDrawable* drawable, BOOL priority); void assertInitializedDoError(); bool assertInitialized() { const bool is_init = isInit(); if (!is_init) assertInitializedDoError(); return is_init; }; + void connectRefreshCachedSettingsSafe(const std::string name); void hideDrawable( LLDrawable *pDrawable ); void unhideDrawable( LLDrawable *pDrawable ); public: diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index 9bf2922033..05230b8bd5 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -1,103 +1,103 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <colors> - <!-- Named Colors --> - <color - name="EmphasisColor" - value="0.38 0.694 0.573 1" /> - <color - name="EmphasisColor_13" - value="0.38 0.694 0.573 0.13" /> - <color - name="EmphasisColor_35" - value="0.38 0.694 0.573 0.35" /> - <color - name="White" - value="1 1 1 1" /> - <color - name="White_05" - value="1 1 1 0.05" /> - <color - name="White_10" - value="1 1 1 0.1" /> - <color - name="White_25" - value="1 1 1 0.25" /> - <color - name="White_50" - value="1 1 1 0.5" /> - <color - name="LtGray" - value="0.75 0.75 0.75 1" /> - <color - name="LtGray_35" - value="0.75 0.75 0.75 0.35" /> - <color - name="LtGray_50" - value="0.75 0.75 0.75 0.50" /> - <color - name="Gray" - value="0.5 0.5 0.5 1" /> - <color - name="DkGray" - value="0.125 0.125 0.125 1" /> - <color - name="DkGray_66" - value="0.125 0.125 0.125 .66" /> - <color - name="DkGray2" - value="0.169 0.169 0.169 1" /> - <color - name="MouseGray" - value="0.191 0.191 0.191 1" /> - <color - name="Black" - value="0 0 0 1" /> - <colork - name="Black_10" - value="0 0 0 0.1" /> - <color - name="Black_25" - value="0 0 0 0.25" /> - <color - name="Black_50" - value="0 0 0 0.5" /> - <color - name="FrogGreen" - value="0.26 0.345 0.263 1" /> - <color - name="Red" - value="1 0 0 1" /> - <color - name="Blue" - value="0 0 1 1" /> - <color - name="Yellow" - value="1 1 0 1" /> - <color - name="Green" - value="0 1 0 1" /> - <color - name="Transparent" - value="0 0 0 0" /> - <color - name="Purple" - value="1 0 1 1" /> - <color - name="Lime" - value=".8 1 .73 1" /> - <color - name="LtYellow" - value="1 1 .79 1" /> - <color - name="DrYellow" - value="1 0.86 0 1" /> - <color - name="LtOrange" - value="1 .85 .73 1" /> - <color - name="MdBlue" - value=".07 .38 .51 1" /> + <!-- Named Colors --> + <color + name="EmphasisColor" + value="0.38 0.694 0.573 1" /> + <color + name="EmphasisColor_13" + value="0.38 0.694 0.573 0.13" /> + <color + name="EmphasisColor_35" + value="0.38 0.694 0.573 0.35" /> + <color + name="White" + value="1 1 1 1" /> + <color + name="White_05" + value="1 1 1 0.05" /> + <color + name="White_10" + value="1 1 1 0.1" /> + <color + name="White_25" + value="1 1 1 0.25" /> + <color + name="White_50" + value="1 1 1 0.5" /> + <color + name="LtGray" + value="0.75 0.75 0.75 1" /> + <color + name="LtGray_35" + value="0.75 0.75 0.75 0.35" /> + <color + name="LtGray_50" + value="0.75 0.75 0.75 0.50" /> + <color + name="Gray" + value="0.5 0.5 0.5 1" /> + <color + name="DkGray" + value="0.125 0.125 0.125 1" /> + <color + name="DkGray_66" + value="0.125 0.125 0.125 .66" /> + <color + name="DkGray2" + value="0.169 0.169 0.169 1" /> + <color + name="MouseGray" + value="0.191 0.191 0.191 1" /> + <color + name="Black" + value="0 0 0 1" /> + <colork + name="Black_10" + value="0 0 0 0.1" /> + <color + name="Black_25" + value="0 0 0 0.25" /> + <color + name="Black_50" + value="0 0 0 0.5" /> + <color + name="FrogGreen" + value="0.26 0.345 0.263 1" /> + <color + name="Red" + value="1 0 0 1" /> + <color + name="Blue" + value="0 0 1 1" /> + <color + name="Yellow" + value="1 1 0 1" /> + <color + name="Green" + value="0 1 0 1" /> + <color + name="Transparent" + value="0 0 0 0" /> + <color + name="Purple" + value="1 0 1 1" /> + <color + name="Lime" + value=".8 1 .73 1" /> + <color + name="LtYellow" + value="1 1 .79 1" /> + <color + name="DrYellow" + value="1 0.86 0 1" /> + <color + name="LtOrange" + value="1 .85 .73 1" /> + <color + name="MdBlue" + value=".07 .38 .51 1" /> <color name="LtRed" value="1 0.2 0.2 1" /> @@ -115,527 +115,527 @@ value="0 0 1 0.8" /> <!-- This color name makes potentially unused colors show up bright purple. - Leave this here until all Unused? are removed below, otherwise - the viewer generates many warnings on startup. --> + Leave this here until all Unused? are removed below, otherwise + the viewer generates many warnings on startup. --> <color - name="Unused?" - value=".831 1 0 1" /> + name="Unused?" + value=".831 1 0 1" /> <!-- UI Definitions --> - <color - name="AccordionHeaderTextColor" - reference="LtGray" /> - <color - name="AgentChatColor" - reference="White" /> - <color - name="AlertBoxColor" - value="0.24 0.24 0.24 1" /> - <color - name="AlertCautionBoxColor" - value="1 0.82 0.46 1" /> - <color - name="AlertCautionTextColor" - reference="LtYellow" /> - <color - name="AvatarListItemIconDefaultColor" - reference="White" /> - <color - name="AvatarListItemIconOnlineColor" - reference="White" /> - <color - name="AvatarListItemIconOfflineColor" - value="0.5 0.5 0.5 0.5" /> - <color - name="AvatarListItemIconVoiceInvitedColor" - reference="AvatarListItemIconOfflineColor" /> - <color - name="AvatarListItemIconVoiceJoinedColor" - reference="AvatarListItemIconOnlineColor" /> - <color - name="AvatarListItemIconVoiceLeftColor" - reference="AvatarListItemIconOfflineColor" /> - <color - name="BadgeImageColor" - value="1.0 0.40 0.0 1.0" /> - <color - name="BadgeBorderColor" - value="0.9 0.9 0.9 1.0" /> - <color - name="BadgeLabelColor" - reference="White" /> - <color - name="ButtonBorderColor" - reference="Unused?" /> - <color - name="ButtonCautionImageColor" - reference="Unused?" /> - <color - name="ButtonColor" - reference="Unused?" /> - <color - name="ButtonFlashBgColor" - reference="Unused?" /> - <color - name="ButtonImageColor" - reference="White" /> - <color - name="ButtonLabelColor" - reference="LtGray" /> - <color - name="ButtonLabelDisabledColor" - reference="White_25" /> - <color - name="ButtonLabelSelectedColor" - reference="White" /> - <color - name="ButtonLabelSelectedDisabledColor" - reference="White_25" /> - <color - name="ButtonSelectedBgColor" - reference="Unused?" /> - <color - name="ButtonSelectedColor" - reference="Unused?" /> - <color - name="ButtonUnselectedBgColor" - reference="Unused?" /> - <color - name="ButtonUnselectedFgColor" - reference="Unused?" /> - <color - name="ChatHistoryBgColor" - reference="Transparent" /> - <color - name="ChatHistoryTextColor" - reference="LtGray" /> - <color - name="ChicletFlashColor" - value="0.114 0.65 0.1" /> - <color - name="ColorDropShadow" - reference="Black_50" /> - <color - name="ColorPaletteEntry01" - reference="Black" /> - <color - name="ColorPaletteEntry02" - reference="Gray" /> - <color - name="ColorPaletteEntry03" - value="0.5 0 0 1" /> - <color - name="ColorPaletteEntry04" - value="0.5 0.5 0 1" /> - <color - name="ColorPaletteEntry05" - value="0 0.5 0 1" /> - <color - name="ColorPaletteEntry06" - value="0 0.5 0.5 1" /> - <color - name="ColorPaletteEntry07" - value="0 0 0.5 1" /> - <color - name="ColorPaletteEntry08" - value="0.5 0 0.5 1" /> - <color - name="ColorPaletteEntry09" - value="0.5 0.5 0 1" /> - <color - name="ColorPaletteEntry10" - value="0 0.25 0.25 1" /> - <color - name="ColorPaletteEntry11" - value="0 0.5 1 1" /> - <color - name="ColorPaletteEntry12" - value="0 0.25 0.5 1" /> - <color - name="ColorPaletteEntry13" - value="0.5 0 1 1" /> - <color - name="ColorPaletteEntry14" - value="0.5 0.25 0 1" /> - <color - name="ColorPaletteEntry15" - reference="White" /> - <color - name="ColorPaletteEntry16" - reference="LtYellow" /> - <color - name="ColorPaletteEntry17" - reference="White" /> - <color - name="ColorPaletteEntry18" - reference="LtGray" /> - <color - name="ColorPaletteEntry19" - reference="Red" /> - <color - name="ColorPaletteEntry20" - reference="Yellow" /> - <color - name="ColorPaletteEntry21" - reference="Green" /> - <color - name="ColorPaletteEntry22" - value="0 1 1 1" /> - <color - name="ColorPaletteEntry23" - reference="Blue" /> - <color - name="ColorPaletteEntry24" - reference="Purple" /> - <color - name="ColorPaletteEntry25" - value="1 1 0.5 1" /> - <color - name="ColorPaletteEntry26" - value="0 1 0.5 1" /> - <color - name="ColorPaletteEntry27" - value="0.5 1 1 1" /> - <color - name="ColorPaletteEntry28" - value="0.5 0.5 1 1" /> - <color - name="ColorPaletteEntry29" - value="1 0 0.5 1" /> - <color - name="ColorPaletteEntry30" - value="1 0.5 0 1" /> - <color - name="ColorPaletteEntry31" - reference="White" /> - <color - name="ColorPaletteEntry32" - reference="White" /> - <color - name="ComboListBgColor" - reference="DkGray" /> - <color - name="ConsoleBackground" - reference="Black" /> - <color - name="ContextSilhouetteColor" - reference="EmphasisColor" /> - <color - name="DefaultHighlightDark" - reference="White_10" /> - <color - name="DefaultHighlightLight" - reference="White_25" /> - <color - name="DefaultShadowDark" - reference="Black_50" /> - <color - name="DefaultShadowLight" - reference="Black_50" /> - <color - name="EffectColor" - reference="White" /> - <color - name="FilterBackgroundColor" - reference="Black" /> - <color - name="FilterTextColor" - value="0.38 0.69 0.57 1" /> - <color - name="FloaterButtonImageColor" - reference="LtGray" /> - <color - name="FloaterDefaultBackgroundColor" - reference="DkGray_66" /> - <color - name="FloaterFocusBackgroundColor" - reference="DkGray2" /> - <color - name="FloaterFocusBorderColor" - reference="Black_50" /> - <color - name="FloaterUnfocusBorderColor" - reference="Black_50" /> - <color - name="FocusColor" - reference="EmphasisColor" /> - <color - name="FolderViewLoadingMessageTextColor" - value="0.3344 0.5456 0.5159 1" /> - <color - name="GridFocusPointColor" - reference="White_50" /> - <color - name="GridlineBGColor" - value="0.92 0.92 1 0.78" /> - <color - name="GridlineColor" - reference="White" /> - <color - name="GridlineShadowColor" - value="0 0 0 0.31" /> - <color - name="GroupNotifyBoxColor" - value="0.3344 0.5456 0.5159 1" /> - <color - name="GroupNotifyTextColor" - reference="White"/> - <color - name="GroupNotifyDimmedTextColor" - reference="LtGray" /> - <color - name="GroupOverTierColor" - value="0.43 0.06 0.06 1" /> - <color - name="HTMLLinkColor" - reference="EmphasisColor" /> - <color - name="HealthTextColor" - reference="White" /> - <color - name="HelpBgColor" - reference="Unused?" /> - <color - name="HelpFgColor" - reference="Unused?" /> - <color - name="HelpScrollHighlightColor" - reference="Unused?" /> - <color - name="HelpScrollShadowColor" - reference="Unused?" /> - <color - name="HelpScrollThumbColor" - reference="Unused?" /> - <color - name="HelpScrollTrackColor" - reference="Unused?" /> - <color - name="HighlightChildColor" - reference="Yellow" /> - <color - name="HighlightInspectColor" - value="1 0 1 1" /> - <color - name="HighlightParentColor" - value="0.67 0.83 0.96 1" /> - <color - name="IMHistoryBgColor" - reference="Unused?" /> - <color - name="IMHistoryTextColor" - reference="Unused?" /> - <color - name="IconDisabledColor" - reference="White_25" /> - <color - name="IconEnabledColor" - reference="White" /> - <color - name="InventoryBackgroundColor" - reference="DkGray2" /> - <color - name="InventoryFocusOutlineColor" - reference="White_25" /> - <color - name="InventoryItemSuffixColor" - reference="White_25" /> - <color - name="InventoryItemLibraryColor" - reference="EmphasisColor" /> - <color - name="InventoryItemLinkColor" - reference="LtGray_50" /> - <color - name="InventoryMouseOverColor" - reference="LtGray_35" /> - <color - name="InventorySearchStatusColor" - reference="EmphasisColor" /> - <color - name="LabelDisabledColor" - reference="White_25" /> - <color - name="LabelSelectedColor" - reference="White" /> - <color - name="LabelSelectedDisabledColor" - reference="White_25" /> - <color - name="LabelTextColor" - reference="LtGray" /> - <color - name="LoginProgressBarBgColor" - reference="Unused?" /> - <color - name="LoginProgressBarFgColor" - reference="Unused?" /> - <color - name="LoginProgressBoxBorderColor" - value="0 0.12 0.24 0" /> - <color - name="LoginProgressBoxCenterColor" - value="0 0 0 0.78" /> - <color - name="LoginProgressBoxShadowColor" - value="0 0 0 0.78" /> - <color - name="LoginProgressBoxTextColor" - reference="White" /> - <color - name="MapAvatarColor" - reference="Green" /> - <color - name="MapAvatarFriendColor" - reference="Yellow" /> - <color - name="MapAvatarSelfColor" - value="0.53125 0 0.498047 1" /> - <color - name="MapFrustumColor" - reference="White_10" /> - <color - name="MapFrustumRotatingColor" - value="1 1 1 0.2" /> - <color - name="MapTrackColor" - reference="Red" /> - <color - name="MapTrackDisabledColor" - value="0.5 0 0 1" /> - <color - name="MenuBarBgColor" - reference="DkGray" /> - <color - name="MenuBarGodBgColor" - reference="FrogGreen" /> - <color - name="MenuDefaultBgColor" - reference="DkGray2" /> - <color - name="MenuItemDisabledColor" - reference="LtGray_50" /> - <color - name="MenuItemEnabledColor" - reference="LtGray" /> - <color - name="MenuItemHighlightBgColor" - reference="EmphasisColor_35" /> - <color - name="MenuItemHighlightFgColor" - reference="White" /> - <color - name="MenuNonProductionBgColor" - reference="Black" /> - <color - name="MenuNonProductionGodBgColor" - value="0.263 0.325 0.345 1" /> - <color - name="MenuPopupBgColor" - reference="DkGray2" /> - <color - name="ModelUploaderLabels" - value="1 0.6 0 1" /> - <color - name="MultiSliderDisabledThumbColor" - reference="Black" /> - <color - name="MultiSliderThumbCenterColor" - reference="White" /> - <color - name="MultiSliderThumbCenterSelectedColor" - reference="Green" /> - <color - name="MultiSliderThumbOutlineColor" - reference="Unused?" /> - <color - name="MultiSliderTrackColor" - reference="LtGray" /> - <color - name="MultiSliderTriangleColor" - reference="Yellow" /> + <color + name="AccordionHeaderTextColor" + reference="LtGray" /> + <color + name="AgentChatColor" + reference="White" /> + <color + name="AlertBoxColor" + value="0.24 0.24 0.24 1" /> + <color + name="AlertCautionBoxColor" + value="1 0.82 0.46 1" /> + <color + name="AlertCautionTextColor" + reference="LtYellow" /> + <color + name="AvatarListItemIconDefaultColor" + reference="White" /> + <color + name="AvatarListItemIconOnlineColor" + reference="White" /> + <color + name="AvatarListItemIconOfflineColor" + value="0.5 0.5 0.5 0.5" /> + <color + name="AvatarListItemIconVoiceInvitedColor" + reference="AvatarListItemIconOfflineColor" /> + <color + name="AvatarListItemIconVoiceJoinedColor" + reference="AvatarListItemIconOnlineColor" /> + <color + name="AvatarListItemIconVoiceLeftColor" + reference="AvatarListItemIconOfflineColor" /> + <color + name="BadgeImageColor" + value="1.0 0.40 0.0 1.0" /> + <color + name="BadgeBorderColor" + value="0.9 0.9 0.9 1.0" /> + <color + name="BadgeLabelColor" + reference="White" /> + <color + name="ButtonBorderColor" + reference="Unused?" /> + <color + name="ButtonCautionImageColor" + reference="Unused?" /> + <color + name="ButtonColor" + reference="Unused?" /> + <color + name="ButtonFlashBgColor" + reference="Unused?" /> + <color + name="ButtonImageColor" + reference="White" /> + <color + name="ButtonLabelColor" + reference="LtGray" /> + <color + name="ButtonLabelDisabledColor" + reference="White_25" /> + <color + name="ButtonLabelSelectedColor" + reference="White" /> + <color + name="ButtonLabelSelectedDisabledColor" + reference="White_25" /> + <color + name="ButtonSelectedBgColor" + reference="Unused?" /> + <color + name="ButtonSelectedColor" + reference="Unused?" /> + <color + name="ButtonUnselectedBgColor" + reference="Unused?" /> + <color + name="ButtonUnselectedFgColor" + reference="Unused?" /> + <color + name="ChatHistoryBgColor" + reference="Transparent" /> + <color + name="ChatHistoryTextColor" + reference="LtGray" /> + <color + name="ChicletFlashColor" + value="0.114 0.65 0.1" /> + <color + name="ColorDropShadow" + reference="Black_50" /> + <color + name="ColorPaletteEntry01" + reference="Black" /> + <color + name="ColorPaletteEntry02" + reference="Gray" /> + <color + name="ColorPaletteEntry03" + value="0.5 0 0 1" /> + <color + name="ColorPaletteEntry04" + value="0.5 0.5 0 1" /> + <color + name="ColorPaletteEntry05" + value="0 0.5 0 1" /> + <color + name="ColorPaletteEntry06" + value="0 0.5 0.5 1" /> + <color + name="ColorPaletteEntry07" + value="0 0 0.5 1" /> + <color + name="ColorPaletteEntry08" + value="0.5 0 0.5 1" /> + <color + name="ColorPaletteEntry09" + value="0.5 0.5 0 1" /> + <color + name="ColorPaletteEntry10" + value="0 0.25 0.25 1" /> + <color + name="ColorPaletteEntry11" + value="0 0.5 1 1" /> + <color + name="ColorPaletteEntry12" + value="0 0.25 0.5 1" /> + <color + name="ColorPaletteEntry13" + value="0.5 0 1 1" /> + <color + name="ColorPaletteEntry14" + value="0.5 0.25 0 1" /> + <color + name="ColorPaletteEntry15" + reference="White" /> + <color + name="ColorPaletteEntry16" + reference="LtYellow" /> + <color + name="ColorPaletteEntry17" + reference="White" /> + <color + name="ColorPaletteEntry18" + reference="LtGray" /> + <color + name="ColorPaletteEntry19" + reference="Red" /> + <color + name="ColorPaletteEntry20" + reference="Yellow" /> + <color + name="ColorPaletteEntry21" + reference="Green" /> + <color + name="ColorPaletteEntry22" + value="0 1 1 1" /> + <color + name="ColorPaletteEntry23" + reference="Blue" /> + <color + name="ColorPaletteEntry24" + reference="Purple" /> + <color + name="ColorPaletteEntry25" + value="1 1 0.5 1" /> + <color + name="ColorPaletteEntry26" + value="0 1 0.5 1" /> + <color + name="ColorPaletteEntry27" + value="0.5 1 1 1" /> + <color + name="ColorPaletteEntry28" + value="0.5 0.5 1 1" /> + <color + name="ColorPaletteEntry29" + value="1 0 0.5 1" /> + <color + name="ColorPaletteEntry30" + value="1 0.5 0 1" /> + <color + name="ColorPaletteEntry31" + reference="White" /> + <color + name="ColorPaletteEntry32" + reference="White" /> + <color + name="ComboListBgColor" + reference="DkGray" /> + <color + name="ConsoleBackground" + reference="Black" /> + <color + name="ContextSilhouetteColor" + reference="EmphasisColor" /> + <color + name="DefaultHighlightDark" + reference="White_10" /> + <color + name="DefaultHighlightLight" + reference="White_25" /> + <color + name="DefaultShadowDark" + reference="Black_50" /> + <color + name="DefaultShadowLight" + reference="Black_50" /> + <color + name="EffectColor" + reference="White" /> + <color + name="FilterBackgroundColor" + reference="Black" /> + <color + name="FilterTextColor" + value="0.38 0.69 0.57 1" /> + <color + name="FloaterButtonImageColor" + reference="LtGray" /> + <color + name="FloaterDefaultBackgroundColor" + reference="DkGray_66" /> + <color + name="FloaterFocusBackgroundColor" + reference="DkGray2" /> + <color + name="FloaterFocusBorderColor" + reference="Black_50" /> + <color + name="FloaterUnfocusBorderColor" + reference="Black_50" /> + <color + name="FocusColor" + reference="EmphasisColor" /> + <color + name="FolderViewLoadingMessageTextColor" + value="0.3344 0.5456 0.5159 1" /> + <color + name="GridFocusPointColor" + reference="White_50" /> + <color + name="GridlineBGColor" + value="0.92 0.92 1 0.78" /> + <color + name="GridlineColor" + reference="White" /> + <color + name="GridlineShadowColor" + value="0 0 0 0.31" /> + <color + name="GroupNotifyBoxColor" + value="0.3344 0.5456 0.5159 1" /> + <color + name="GroupNotifyTextColor" + reference="White"/> + <color + name="GroupNotifyDimmedTextColor" + reference="LtGray" /> + <color + name="GroupOverTierColor" + value="0.43 0.06 0.06 1" /> + <color + name="HTMLLinkColor" + reference="EmphasisColor" /> + <color + name="HealthTextColor" + reference="White" /> + <color + name="HelpBgColor" + reference="Unused?" /> + <color + name="HelpFgColor" + reference="Unused?" /> + <color + name="HelpScrollHighlightColor" + reference="Unused?" /> + <color + name="HelpScrollShadowColor" + reference="Unused?" /> + <color + name="HelpScrollThumbColor" + reference="Unused?" /> + <color + name="HelpScrollTrackColor" + reference="Unused?" /> + <color + name="HighlightChildColor" + reference="Yellow" /> + <color + name="HighlightInspectColor" + value="1 0 1 1" /> + <color + name="HighlightParentColor" + value="0.67 0.83 0.96 1" /> + <color + name="IMHistoryBgColor" + reference="Unused?" /> + <color + name="IMHistoryTextColor" + reference="Unused?" /> + <color + name="IconDisabledColor" + reference="White_25" /> + <color + name="IconEnabledColor" + reference="White" /> + <color + name="InventoryBackgroundColor" + reference="DkGray2" /> + <color + name="InventoryFocusOutlineColor" + reference="White_25" /> + <color + name="InventoryItemSuffixColor" + reference="White_25" /> + <color + name="InventoryItemLibraryColor" + reference="EmphasisColor" /> + <color + name="InventoryItemLinkColor" + reference="LtGray_50" /> + <color + name="InventoryMouseOverColor" + reference="LtGray_35" /> + <color + name="InventorySearchStatusColor" + reference="EmphasisColor" /> + <color + name="LabelDisabledColor" + reference="White_25" /> + <color + name="LabelSelectedColor" + reference="White" /> + <color + name="LabelSelectedDisabledColor" + reference="White_25" /> + <color + name="LabelTextColor" + reference="LtGray" /> + <color + name="LoginProgressBarBgColor" + reference="Unused?" /> + <color + name="LoginProgressBarFgColor" + reference="Unused?" /> + <color + name="LoginProgressBoxBorderColor" + value="0 0.12 0.24 0" /> + <color + name="LoginProgressBoxCenterColor" + value="0 0 0 0.78" /> + <color + name="LoginProgressBoxShadowColor" + value="0 0 0 0.78" /> + <color + name="LoginProgressBoxTextColor" + reference="White" /> + <color + name="MapAvatarColor" + reference="Green" /> + <color + name="MapAvatarFriendColor" + reference="Yellow" /> + <color + name="MapAvatarSelfColor" + value="0.53125 0 0.498047 1" /> + <color + name="MapFrustumColor" + reference="White_10" /> + <color + name="MapFrustumRotatingColor" + value="1 1 1 0.2" /> + <color + name="MapTrackColor" + reference="Red" /> + <color + name="MapTrackDisabledColor" + value="0.5 0 0 1" /> + <color + name="MenuBarBgColor" + reference="DkGray" /> + <color + name="MenuBarGodBgColor" + reference="FrogGreen" /> + <color + name="MenuDefaultBgColor" + reference="DkGray2" /> + <color + name="MenuItemDisabledColor" + reference="LtGray_50" /> + <color + name="MenuItemEnabledColor" + reference="LtGray" /> + <color + name="MenuItemHighlightBgColor" + reference="EmphasisColor_35" /> + <color + name="MenuItemHighlightFgColor" + reference="White" /> + <color + name="MenuNonProductionBgColor" + reference="Black" /> + <color + name="MenuNonProductionGodBgColor" + value="0.263 0.325 0.345 1" /> + <color + name="MenuPopupBgColor" + reference="DkGray2" /> + <color + name="ModelUploaderLabels" + value="1 0.6 0 1" /> + <color + name="MultiSliderDisabledThumbColor" + reference="Black" /> + <color + name="MultiSliderThumbCenterColor" + reference="White" /> + <color + name="MultiSliderThumbCenterSelectedColor" + reference="Green" /> + <color + name="MultiSliderThumbOutlineColor" + reference="Unused?" /> + <color + name="MultiSliderTrackColor" + reference="LtGray" /> + <color + name="MultiSliderTriangleColor" + reference="Yellow" /> <!-- - <color + <color name="NameTagBackground" value="0.85 0.85 0.85 0.80" /> - --> - <color + --> + <color name="NameTagBackground" value="0 0 0 1" /> - <color - name="NameTagChat" - reference="White" /> - <color - name="NameTagFriend" - value="0.447 0.784 0.663 1" /> - <color - name="NameTagLegacy" - reference="White" /> - <color - name="NameTagMatch" - reference="White" /> - <color - name="NameTagMismatch" - reference="White" /> - <color - name="NetMapBackgroundColor" - value="0 0 0 1" /> - <color - name="NetMapGroupOwnAboveWater" - reference="Purple" /> - <color - name="NetMapGroupOwnBelowWater" - value="0.78 0 0.78 1" /> - <color - name="NetMapOtherOwnAboveWater" - value="0.24 0.24 0.24 1" /> - <color - name="NetMapOtherOwnBelowWater" - value="0.12 0.12 0.12 1" /> - <color - name="NetMapYouOwnAboveWater" - value="0 1 1 1" /> - <color - name="NetMapYouOwnBelowWater" - value="0 0.78 0.78 1" /> - <color - name="NotifyBoxColor" - value="LtGray" /> - <color - name="NotifyCautionBoxColor" - value="1 0.82 0.46 1" /> - <color - name="NotifyCautionWarnColor" - reference="White" /> - <color - name="NotifyTextColor" - reference="White" /> - <color - name="ObjectBubbleColor" - reference="DkGray_66" /> - <color - name="ObjectChatColor" - reference="EmphasisColor" /> - <color - name="OverdrivenColor" - reference="Red" /> - <color - name="PanelDefaultBackgroundColor" - reference="DkGray" /> - <color - name="PanelDefaultHighlightLight" - reference="White_50" /> - <color - name="PanelFocusBackgroundColor" - reference="DkGray2" /> - <color - name="PanelNotificationBackground" - value="1 0.3 0.3 0" /> - <color - name="ParcelHoverColor" - reference="White" /> - <color + <color + name="NameTagChat" + reference="White" /> + <color + name="NameTagFriend" + value="0.447 0.784 0.663 1" /> + <color + name="NameTagLegacy" + reference="White" /> + <color + name="NameTagMatch" + reference="White" /> + <color + name="NameTagMismatch" + reference="White" /> + <color + name="NetMapBackgroundColor" + value="0 0 0 1" /> + <color + name="NetMapGroupOwnAboveWater" + reference="Purple" /> + <color + name="NetMapGroupOwnBelowWater" + value="0.78 0 0.78 1" /> + <color + name="NetMapOtherOwnAboveWater" + value="0.24 0.24 0.24 1" /> + <color + name="NetMapOtherOwnBelowWater" + value="0.12 0.12 0.12 1" /> + <color + name="NetMapYouOwnAboveWater" + value="0 1 1 1" /> + <color + name="NetMapYouOwnBelowWater" + value="0 0.78 0.78 1" /> + <color + name="NotifyBoxColor" + value="LtGray" /> + <color + name="NotifyCautionBoxColor" + value="1 0.82 0.46 1" /> + <color + name="NotifyCautionWarnColor" + reference="White" /> + <color + name="NotifyTextColor" + reference="White" /> + <color + name="ObjectBubbleColor" + reference="DkGray_66" /> + <color + name="ObjectChatColor" + reference="EmphasisColor" /> + <color + name="OverdrivenColor" + reference="Red" /> + <color + name="PanelDefaultBackgroundColor" + reference="DkGray" /> + <color + name="PanelDefaultHighlightLight" + reference="White_50" /> + <color + name="PanelFocusBackgroundColor" + reference="DkGray2" /> + <color + name="PanelNotificationBackground" + value="1 0.3 0.3 0" /> + <color + name="ParcelHoverColor" + reference="White" /> + <color name="PathfindingErrorColor" reference="LtRed" /> <color @@ -657,205 +657,205 @@ name="PathfindingCharacterBeaconColor" reference="Red_80" /> <color - name="PieMenuBgColor" - value="0.24 0.24 0.24 0.59" /> - <color - name="PieMenuLineColor" - value="0 0 0 0.5" /> - <color - name="PieMenuSelectedColor" - value="0.72 0.72 0.74 0.3" /> - <color - name="PropertyColorAuction" - value="0.5 0 1 0.4" /> - <color - name="PropertyColorAvail" - reference="Transparent" /> - <color - name="PropertyColorForSale" - value="1 0.5 0 0.4" /> - <color - name="PropertyColorGroup" - value="0 0.72 0.72 0.4" /> - <color - name="PropertyColorOther" - value="1 0 0 0.4" /> - <color - name="PropertyColorSelf" - value="0 1 0 0.4" /> - <color - name="ScriptBgReadOnlyColor" - value="0.39 0.39 0.39 1" /> - <color - name="ScriptErrorColor" - reference="Red" /> - <color - name="ScrollBGStripeColor" - reference="Transparent" /> - <color - name="ScrollBgReadOnlyColor" + name="PieMenuBgColor" + value="0.24 0.24 0.24 0.59" /> + <color + name="PieMenuLineColor" + value="0 0 0 0.5" /> + <color + name="PieMenuSelectedColor" + value="0.72 0.72 0.74 0.3" /> + <color + name="PropertyColorAuction" + value="0.5 0 1 0.4" /> + <color + name="PropertyColorAvail" + reference="Transparent" /> + <color + name="PropertyColorForSale" + value="1 0.5 0 0.4" /> + <color + name="PropertyColorGroup" + value="0 0.72 0.72 0.4" /> + <color + name="PropertyColorOther" + value="1 0 0 0.4" /> + <color + name="PropertyColorSelf" + value="0 1 0 0.4" /> + <color + name="ScriptBgReadOnlyColor" + value="0.39 0.39 0.39 1" /> + <color + name="ScriptErrorColor" + reference="Red" /> + <color + name="ScrollBGStripeColor" + reference="Transparent" /> + <color + name="ScrollBgReadOnlyColor" reference="Transparent" /> - <color - name="ScrollBgWriteableColor" - reference="White_05" /> - <color - name="ScrollDisabledColor" - reference="White_25" /> - <color - name="ScrollHighlightedColor" - reference="Unused?" /> - <color - name="ScrollHoveredColor" - reference="EmphasisColor_13" /> - <color - name="ScrollSelectedBGColor" - reference="EmphasisColor_35" /> - <color - name="ScrollSelectedFGColor" - reference="White" /> - <color - name="ScrollUnselectedColor" - reference="LtGray" /> - <color - name="ScrollbarThumbColor" - reference="White" /> - <color - name="ScrollbarTrackColor" - reference="Black" /> - <color - name="SelectedOutfitTextColor" - reference="EmphasisColor" /> - <color - name="SilhouetteChildColor" - value="0.13 0.42 0.77 1" /> - <color - name="SilhouetteParentColor" - reference="Yellow" /> - <color - name="SliderDisabledThumbColor" - reference="White_25" /> - <color - name="SliderThumbCenterColor" - reference="White" /> - <color - name="SliderThumbOutlineColor" - reference="White" /> - <color - name="SliderTrackColor" - reference="Unused?" /> - <color - name="SpeakingColor" - reference="FrogGreen" /> - <color - name="SystemChatColor" - reference="LtGray" /> - <color - name="TextBgFocusColor" - reference="White" /> - <color - name="TextBgReadOnlyColor" - reference="White_05" /> - <color - name="TextBgWriteableColor" - reference="LtGray" /> - <color - name="TextCursorColor" - reference="Black" /> - <color - name="TextDefaultColor" - reference="Black" /> - <color - name="TextEmbeddedItemColor" - value="0 0 0.5 1" /> - <color - name="TextEmbeddedItemReadOnlyColor" - reference="Unused?" /> - <color - name="TextFgColor" - value="0.102 0.102 0.102 1" /> - <color - name="TextFgReadOnlyColor" - reference="LtGray" /> - <color - name="TextFgTentativeColor" - value="0.4 0.4 0.4 1" /> - <color - name="TimeTextColor" - reference="LtGray" /> - <color - name="TitleBarFocusColor" - reference="White_10" /> - <color - name="ToastBackground" - value="0.3 0.3 0.3 0" /> - <color - name="ToolTipBgColor" - value="0.937 0.89 0.655 1" /> - <color - name="ToolTipBorderColor" - value="0.812 0.753 0.451 1" /> - <color - name="ToolTipTextColor" - reference="DkGray2" /> - <color - name="InspectorTipTextColor" - reference="LtGray" /> - <color - name="UserChatColor" - reference="White" /> - <color - name="llOwnerSayChatColor" - reference="LtYellow" /> + <color + name="ScrollBgWriteableColor" + reference="White_05" /> + <color + name="ScrollDisabledColor" + reference="White_25" /> + <color + name="ScrollHighlightedColor" + reference="Unused?" /> + <color + name="ScrollHoveredColor" + reference="EmphasisColor_13" /> + <color + name="ScrollSelectedBGColor" + reference="EmphasisColor_35" /> + <color + name="ScrollSelectedFGColor" + reference="White" /> + <color + name="ScrollUnselectedColor" + reference="LtGray" /> + <color + name="ScrollbarThumbColor" + reference="White" /> + <color + name="ScrollbarTrackColor" + reference="Black" /> + <color + name="SelectedOutfitTextColor" + reference="EmphasisColor" /> + <color + name="SilhouetteChildColor" + value="0.13 0.42 0.77 1" /> + <color + name="SilhouetteParentColor" + reference="Yellow" /> + <color + name="SliderDisabledThumbColor" + reference="White_25" /> + <color + name="SliderThumbCenterColor" + reference="White" /> + <color + name="SliderThumbOutlineColor" + reference="White" /> + <color + name="SliderTrackColor" + reference="Unused?" /> + <color + name="SpeakingColor" + reference="FrogGreen" /> + <color + name="SystemChatColor" + reference="LtGray" /> + <color + name="TextBgFocusColor" + reference="White" /> + <color + name="TextBgReadOnlyColor" + reference="White_05" /> + <color + name="TextBgWriteableColor" + reference="LtGray" /> + <color + name="TextCursorColor" + reference="Black" /> + <color + name="TextDefaultColor" + reference="Black" /> + <color + name="TextEmbeddedItemColor" + value="0 0 0.5 1" /> + <color + name="TextEmbeddedItemReadOnlyColor" + reference="Unused?" /> + <color + name="TextFgColor" + value="0.102 0.102 0.102 1" /> + <color + name="TextFgReadOnlyColor" + reference="LtGray" /> + <color + name="TextFgTentativeColor" + value="0.4 0.4 0.4 1" /> + <color + name="TimeTextColor" + reference="LtGray" /> + <color + name="TitleBarFocusColor" + reference="White_10" /> + <color + name="ToastBackground" + value="0.3 0.3 0.3 0" /> + <color + name="ToolTipBgColor" + value="0.937 0.89 0.655 1" /> + <color + name="ToolTipBorderColor" + value="0.812 0.753 0.451 1" /> + <color + name="ToolTipTextColor" + reference="DkGray2" /> + <color + name="InspectorTipTextColor" + reference="LtGray" /> + <color + name="UserChatColor" + reference="Yellow" /> + <color + name="llOwnerSayChatColor" + reference="LtYellow" /> - <!-- New Colors --> - <color - name="OutputMonitorMutedColor" - reference="DkGray2" /> - <color - name="SysWellItemUnselected" - value="0 0 0 0" /> - <color - name="SysWellItemSelected" - value="0.3 0.3 0.3 1.0" /> - <color - name="ColorSwatchBorderColor" - value="0.45098 0.517647 0.607843 1"/> - <color - name="ChatTimestampColor" - reference="White" /> - <color - name="MenuBarProjectBgColor" - reference="MdBlue" /> + <!-- New Colors --> + <color + name="OutputMonitorMutedColor" + reference="DkGray2" /> + <color + name="SysWellItemUnselected" + value="0 0 0 0" /> + <color + name="SysWellItemSelected" + value="0.3 0.3 0.3 1.0" /> + <color + name="ColorSwatchBorderColor" + value="0.45098 0.517647 0.607843 1"/> + <color + name="ChatTimestampColor" + reference="White" /> + <color + name="MenuBarProjectBgColor" + reference="MdBlue" /> - <color + <color name="MeshImportTableNormalColor" value="1 1 1 1"/> - <color + <color name="MeshImportTableHighlightColor" value="0.2 0.8 1 1"/> - <color - name="DirectChatColor" - reference="LtOrange" /> + <color + name="DirectChatColor" + reference="LtOrange" /> - <color + <color name="ToolbarDropZoneColor" value=".48 .69 1 .5" /> - <!-- Generic color names (legacy) --> + <!-- Generic color names (legacy) --> <color - name="white" - value="1 1 1 1"/> + name="white" + value="1 1 1 1"/> <color - name="black" - value="0 0 0 1"/> + name="black" + value="0 0 0 1"/> <color - name="red" - value="1 0 0 1"/> + name="red" + value="1 0 0 1"/> <color - name="green" - value="0 1 0 1"/> + name="green" + value="0 1 0 1"/> <color - name="blue" - value="0 0 1 1"/> + name="blue" + value="0 0 1 1"/> </colors> diff --git a/indra/newview/skins/default/textures/icons/Conv_log_inbox.png b/indra/newview/skins/default/textures/icons/Conv_log_inbox.png Binary files differnew file mode 100644 index 0000000000..bb6ca28147 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_log_inbox.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_add_person.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_add_person.png Binary files differnew file mode 100755 index 0000000000..f024c733f3 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_add_person.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_arrow_ne.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_arrow_ne.png Binary files differnew file mode 100755 index 0000000000..a19e720d42 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_arrow_ne.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_arrow_sw.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_arrow_sw.png Binary files differnew file mode 100755 index 0000000000..7f3f42639d --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_arrow_sw.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_call_log.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_call_log.png Binary files differnew file mode 100755 index 0000000000..2880eb766a --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_call_log.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_close.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_close.png Binary files differnew file mode 100755 index 0000000000..25a939d7f5 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_close.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_collapse.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_collapse.png Binary files differnew file mode 100755 index 0000000000..82baabde47 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_collapse.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_expand.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_expand.png Binary files differnew file mode 100755 index 0000000000..7d64abb042 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_expand.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_hang_up.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_hang_up.png Binary files differnew file mode 100755 index 0000000000..f0da962c2d --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_hang_up.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_open_call.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_open_call.png Binary files differnew file mode 100755 index 0000000000..0db001dcdb --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_open_call.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_plus.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_plus.png Binary files differnew file mode 100755 index 0000000000..0cf7edc2d4 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_plus.png diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_sort.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_sort.png Binary files differnew file mode 100755 index 0000000000..a0c15a6d3e --- /dev/null +++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_sort.png diff --git a/indra/newview/skins/default/textures/icons/nearby_chat_icon.png b/indra/newview/skins/default/textures/icons/nearby_chat_icon.png Binary files differnew file mode 100644 index 0000000000..48c2379133 --- /dev/null +++ b/indra/newview/skins/default/textures/icons/nearby_chat_icon.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 06f8f8c670..a124041565 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -162,7 +162,22 @@ with the same filename but different name <texture name="ComboButton_On" file_name="widgets/ComboButton_On.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" /> <texture name="ComboButton_Off" file_name="widgets/ComboButton_Off.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" /> <texture name="ComboButton_UpOff" file_name="widgets/ComboButton_UpOff.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" /> + <texture name="Container" file_name="containers/Container.png" preload="false" /> + + <texture name="Conv_toolbar_add_person" file_name="icons/Conv_toolbar_add_person.png" preload="false" /> + <texture name="Conv_toolbar_arrow_ne" file_name="icons/Conv_toolbar_arrow_ne.png" preload="false" /> + <texture name="Conv_toolbar_arrow_sw" file_name="icons/Conv_toolbar_arrow_sw.png" preload="false" /> + <texture name="Conv_toolbar_call_log" file_name="icons/Conv_toolbar_call_log.png" preload="false" /> + <texture name="Conv_toolbar_close" file_name="icons/Conv_toolbar_close.png" preload="false" /> + <texture name="Conv_toolbar_collapse" file_name="icons/Conv_toolbar_collapse.png" preload="false" /> + <texture name="Conv_toolbar_expand" file_name="icons/Conv_toolbar_expand.png" preload="false" /> + <texture name="Conv_toolbar_hang_up" file_name="icons/Conv_toolbar_hang_up.png" preload="false" /> + <texture name="Conv_toolbar_open_call" file_name="icons/Conv_toolbar_open_call.png" preload="false" /> + <texture name="Conv_toolbar_plus" file_name="icons/Conv_toolbar_plus.png" preload="false" /> + <texture name="Conv_toolbar_sort" file_name="icons/Conv_toolbar_sort.png" preload="false" /> + <texture name="Conv_log_inbox" file_name="icons/Conv_log_inbox.png" preload="false" /> + <texture name="Copy" file_name="icons/Copy.png" preload="false" /> <texture name="DisclosureArrow_Opened_Off" file_name="widgets/DisclosureArrow_Opened_Off.png" preload="true" /> @@ -348,6 +363,8 @@ with the same filename but different name <texture name="NavBar_BG_NoFav_Bevel" file_name="navbar/NavBar_BG_NoFav_Bevel.png" preload="true" scale.left="1" scale.top="1" scale.right="0" scale.bottom="0" /> <texture name="NavBar_BG_NoNav_Bevel" file_name="navbar/NavBar_BG_NoNav_Bevel.png" preload="true" scale.left="1" scale.top="1" scale.right="0" scale.bottom="0" /> + <texture name="Nearby_chat_icon" file_name="icons/nearby_chat_icon.png" preload="false" /> + <texture name="Notices_Unread" file_name="bottomtray/Notices_Unread.png" preload="true" /> <texture name="NoEntryLines" file_name="world/NoEntryLines.png" use_mips="true" preload="false" /> diff --git a/indra/newview/skins/default/xui/da/panel_nearby_chat_bar.xml b/indra/newview/skins/default/xui/da/panel_nearby_chat_bar.xml index 949cbcbd7b..eb104201f8 100644 --- a/indra/newview/skins/default/xui/da/panel_nearby_chat_bar.xml +++ b/indra/newview/skins/default/xui/da/panel_nearby_chat_bar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<panel name="chat_bar"> +<panel name="nearby_chat"> <line_editor label="Klik her for at chatte." name="chat_box" tool_tip="Tryk på enter for at tale, Ctrl-Enter for at råbe."/> <button name="show_nearby_chat" tool_tip="Viser/skjuler log for chat nærved"/> </panel> diff --git a/indra/newview/skins/default/xui/de/floater_chat_bar.xml b/indra/newview/skins/default/xui/de/floater_chat_bar.xml index 2464a55665..ab77d4dae5 100644 --- a/indra/newview/skins/default/xui/de/floater_chat_bar.xml +++ b/indra/newview/skins/default/xui/de/floater_chat_bar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="chat_bar" title="CHAT IN DER NÄHE"> +<floater name="nearby_chat" title="CHAT IN DER NÄHE"> <panel name="bottom_panel"> <line_editor label="Zum Chatten hier klicken." name="chat_box" tool_tip="Eingabetaste zum Sprechen, Strg+Eingabe zum Rufen"/> <button name="show_nearby_chat" tool_tip="Chatprotokoll in der Nähe ein-/ausblenden"/> diff --git a/indra/newview/skins/default/xui/de/panel_nearby_chat_bar.xml b/indra/newview/skins/default/xui/de/panel_nearby_chat_bar.xml index 08cc0b0ec8..69cf6d98de 100644 --- a/indra/newview/skins/default/xui/de/panel_nearby_chat_bar.xml +++ b/indra/newview/skins/default/xui/de/panel_nearby_chat_bar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<panel name="chat_bar"> +<panel name="nearby_chat"> <line_editor label="Zum Chatten hier klicken." name="chat_box" tool_tip="Eingabe drücken, um zu sprechen, Strg-Eingabe drücken, um zu Rufen."/> <button name="show_nearby_chat" tool_tip="Protokoll des Chats in der Nähe anzeigen/ausblenden"/> </panel> diff --git a/indra/newview/skins/default/xui/en/floater_chat_bar.xml b/indra/newview/skins/default/xui/en/floater_chat_bar.xml deleted file mode 100644 index 405557242f..0000000000 --- a/indra/newview/skins/default/xui/en/floater_chat_bar.xml +++ /dev/null @@ -1,85 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<floater - positioning="specified" - left="10" - bottom="-10" - height="60" - layout="topleft" - legacy_header_height="25" - single_instance="true" - title="NEARBY CHAT" - save_rect="true" - save_visibility="true" - can_close="true" - can_minimize="true" - help_topic="chat_bar" - min_height="60" - min_width="150" - can_resize="true" - default_tab_group="1" - name="chat_bar" - width="300"> - <panel - top="20" - class="panel_nearby_chat" - follow="all" - width="300" - height="0" - visible="false" - filename="panel_nearby_chat.xml" - name="nearby_chat" /> - <panel width="300" - height="31" - left="0" - name="bottom_panel" - bottom="-1" - follows="left|right|bottom" - tab_group="1"> - <line_editor - border_style="line" - border_thickness="1" - follows="left|right" - height="23" - label="Click here to chat." - layout="topleft" - left_delta="7" - left="0" - max_length_bytes="1023" - name="chat_box" - spellcheck="true" - text_pad_left="5" - text_pad_right="25" - tool_tip="Press Enter to say, Ctrl+Enter to shout" - top="2" - width="255" /> - <output_monitor - auto_update="true" - follows="right" - draw_border="false" - height="16" - layout="topleft" - left_pad="-24" - mouse_opaque="true" - name="chat_zone_indicator" - top="6" - visible="true" - width="20" /> - <button - follows="right" - is_toggle="true" - width="20" - top="2" - layout="topleft" - left_pad="12" - image_disabled="ComboButton_UpOff" - image_unselected="ComboButton_UpOff" - image_selected="ComboButton_On" - image_pressed="ComboButton_UpSelected" - image_pressed_selected="ComboButton_Selected" - height="23" - chrome="true" - name="show_nearby_chat" - tool_tip="Shows/hides nearby chat log"> - </button> - </panel> -</floater> diff --git a/indra/newview/skins/default/xui/en/floater_conversation_log.xml b/indra/newview/skins/default/xui/en/floater_conversation_log.xml new file mode 100644 index 0000000000..c9c52e5ce5 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_conversation_log.xml @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> + +<floater + can_resize="true" + positioning="cascading" + height="400" + min_height="100" + min_width="390" + layout="topleft" + name="floater_conversation_log" + save_rect="true" + single_instance="true" + reuse_instance="true" + title="CONVERSATION LOG" + width="450"> + + <string name="logging_calls_disabled"> + Conversations are not being logged. To log conversations in the future, select "Save IM logs on my computer" under Preferences > Privacy. + </string> + + <panel + follows="left|top|right" + height="27" + layout="topleft" + left="0" + name="buttons_panel" + top="0"> + <filter_editor + follows="left|top|right" + height="23" + layout="topleft" + left="8" + label="Filter People" + max_length_chars="300" + name="people_filter_input" + text_color="Black" + text_pad_left="10" + top="4" + width="364" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="5" + menu_filename="menu_conversation_log_view.xml" + menu_position="bottomleft" + name="conversation_view_btn" + tool_tip="View/sort options" + top="3" + width="31" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="OptionsMenu_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + name="conversations_gear_btn" + tool_tip="Actions on selected person or group" + top="3" + width="31" /> + </panel> + <panel + follows="all" + height="370" + layout="topleft" + left="5" + name="buttons_panel" + right="-5" + top_pad="5"> + <conversation_log_list + opaque="true" + allow_select="true" + follows="all" + height="360" + layout="topleft" + left="3" + keep_selection_visible_on_reshape="true" + item_pad="2" + multi_select="false" + name="conversation_log_list" + right="-3" + top="5" /> + </panel> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_conversation_preview.xml b/indra/newview/skins/default/xui/en/floater_conversation_preview.xml new file mode 100644 index 0000000000..6f1ddaaf4f --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_conversation_preview.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + legacy_header_height="18" + can_resize="true" + default_tab_group="1" + height="391" + layout="topleft" + min_height="243" + min_width="234" + name="preview_conversation" + title="CONVERSATION:" + width="400"> + <floater.string + name="Title"> + CONVERSATION: [NAME] + </floater.string> + <chat_history + font="SansSerifSmall" + follows="all" + visible="true" + height="330" + name="chat_history" + notify_unread_msg="false" + parse_highlights="true" + parse_urls="true" + left="5" + top_pad="25" + width="390"> + </chat_history> + <text + follows="bottom|right" + font="SansSerif" + height="22" + layout="topleft" + name="page_label" + right="-110" + top_pad="7" + value="Page" + width="35"> + </text> + <spinner + allow_digits_only="true" + decimal_digits="0" + follows="bottom|right" + height="23" + increment="1" + label_width="40" + layout="topleft" + left_pad="0" + name="history_page_spin" + top_delta="-3" + width="50"/> + <text + follows="bottom|right" + font="SansSerif" + height="22" + layout="topleft" + name="page_num_label" + left_pad="5" + top_delta="4" + width="40"> + </text> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_im_container.xml b/indra/newview/skins/default/xui/en/floater_im_container.xml index e123de46c2..590ce45c33 100644 --- a/indra/newview/skins/default/xui/en/floater_im_container.xml +++ b/indra/newview/skins/default/xui/en/floater_im_container.xml @@ -1,49 +1,130 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <multi_floater - can_close="false" + can_close="true" can_minimize="true" can_resize="true" - height="390" + height="430" layout="topleft" + min_height="50" name="floater_im_box" help_topic="floater_im_box" save_rect="true" save_visibility="true" single_instance="true" + reuse_instance="true" title="CONVERSATIONS" - width="396"> - <tab_container - follows="left|right|top|bottom" - height="390" + width="680"> + <string + name="collapse_icon" + value="Conv_toolbar_collapse"/> + <string + name="expand_icon" + value="Conv_toolbar_expand"/> + <layout_stack + animate="true" + follows="all" + height="430" layout="topleft" - left="1" - name="im_box_tab_container" - tab_position="bottom" - tab_width="64" - tab_max_width = "134" - tab_height="16" - use_custom_icon_ctrl="true" - tab_icon_ctrl_pad="2" - halign="left" - use_ellipses="true" + left="0" + name="conversations_stack" + orientation="horizontal" top="0" - width="394"> - <first_tab - tab_bottom_image_flash="Toolbar_Left_Flash"/> - <middle_tab - tab_bottom_image_flash="Toolbar_Middle_Flash"/> - <last_tab - tab_bottom_image_flash="Toolbar_Right_Flash"/> - </tab_container> - <icon - color="DefaultShadowLight" - enabled="false" - follows="left|right|bottom" - height="17" - image_name="tabarea.tga" - layout="bottomleft" - left="1" - name="im_box_tab_container_icon" - bottom="10" - width="394" /> + width="680"> + <layout_panel + auto_resize="true" + user_resize="true" + height="430" + name="conversations_layout_panel" + min_dim="38" + width="268" + expanded_min_dim="120"> + <layout_stack + animate="false" + follows="left|top|right" + height="35" + layout="topleft" + left="0" + name="conversations_pane_buttons_stack" + orientation="horizontal" + top="0" + width="268"> + <layout_panel + auto_resize="true" + height="35" + name="conversations_pane_buttons_expanded"> + <menu_button + follows="top|left" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + menu_filename="menu_participant_view.xml" + layout="topleft" + left="10" + name="sort_btn" + tool_tip="View/sort options" + top="5" + width="31" /> + <button + follows="top|left" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_plus" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left_pad="4" + name="add_btn" + tool_tip="Start a new conversation" + width="31"/> + </layout_panel> + <layout_panel + auto_resize="false" + height="35" + name="conversations_pane_buttons_collapsed" + width="41"> + <button + follows="right|top" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_collapse" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left="5" + name="expand_collapse_btn" + tool_tip="Collapse/Expand this list" + width="31" /> + </layout_panel> + </layout_stack> + <panel + follows="all" + layout="topleft" + name="conversations_list_panel" + opaque="true" + top_pad="0" + left="5" + height="390" + width="263"/> + </layout_panel> + <layout_panel + auto_resize="true" + user_resize="true" + height="430" + name="messages_layout_panel" + width="412" + expanded_min_dim="225"> + <panel_container + follows="all" + height="430" + layout="topleft" + left="0" + name="im_box_tab_container" + top="0" + width="412"/> + </layout_panel> + </layout_stack> </multi_floater> diff --git a/indra/newview/skins/default/xui/en/floater_im_session.xml b/indra/newview/skins/default/xui/en/floater_im_session.xml index 040b66623e..2b542595c5 100644 --- a/indra/newview/skins/default/xui/en/floater_im_session.xml +++ b/indra/newview/skins/default/xui/en/floater_im_session.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <floater - legacy_header_height="18" background_visible="true" default_tab_group="1" height="355" @@ -10,84 +9,268 @@ can_dock="false" can_minimize="true" can_close="true" + save_rect="true" visible="false" width="394" can_resize="true" + can_tear_off="false" min_width="250" - min_height="190"> + min_height="190" + positioning="relative"> + <floater.string name="call_btn_start">Conv_toolbar_open_call</floater.string> + <floater.string name="call_btn_stop">Conv_toolbar_hang_up</floater.string> + <floater.string + name="collapse_icon" + value="Conv_toolbar_collapse"/> + <floater.string + name="expand_icon" + value="Conv_toolbar_expand"/> + <floater.string + name="tear_off_icon" + value="Conv_toolbar_arrow_ne"/> + <floater.string + name="return_icon" + value="Conv_toolbar_arrow_sw"/> + <floater.string + name="participant_added" + value="[NAME] was invited to the conversation."/> + <floater.string + name="multiple_participants_added" + value="[NAME] were invited to the conversation."/> + <floater.string + name="tooltip_to_separate_window" + value="Move this conversation to a separate window"/> + <floater.string + name="tooltip_to_main_window" + value="Move this conversation back to main window"/> + <view + follows="all" + layout="topleft" + name="contents_view" + top="0" + left="0" + height="355" + width="394"> + <panel + follows="left|top|right" + layout="topleft" + name="toolbar_panel" + top="0" + left="0" + height="35" + width="394"> + <menu_button + menu_filename="menu_im_session_showmodes.xml" + follows="top|left" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left="5" + name="view_options_btn" + tool_tip="View/sort options" + top="5" + width="31" /> + <button + enabled="false" + follows="top|left" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_add_person" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left_pad="4" + name="add_btn" + tool_tip="Add someone to this conversation" + width="31"/> + <button + follows="top|left" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_open_call" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left_pad="4" + name="voice_call_btn" + tool_tip="Open voice connection" + width="31"/> + <button + follows="right|top" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_close" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left="283" + name="close_btn" + tool_tip="End this conversation" + width="31" /> + <button + follows="right|top" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_collapse" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left_pad="5" + name="expand_collapse_btn" + tool_tip="Collapse/Expand this pane" + width="31" /> + <button + follows="right|top" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_arrow_ne" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left_pad="5" + name="tear_off_btn" + width="31" /> + </panel> <layout_stack animate="true" default_tab_group="2" follows="all" - height="320" + height="310" width="394" layout="topleft" orientation="horizontal" name="im_panels" tab_group="1" - top="20" + top_pad="0" left="0"> <layout_panel - name="im_control_panel_holder" + name="speakers_list_panel" + follows="all" min_width="115" width="150" - height="320" + height="310" auto_resize="false"> - <panel - name="panel_im_control_panel" + <avatar_list + color="DkGray2" + follows="all" + height="310" + ignore_online_status="true" layout="topleft" - height="320" - width="150" - follows="all"/> + name="speakers_list" + opaque="false" + show_info_btn="true" + show_profile_btn="false" + show_speaking_indicator="false" + width="150" /> </layout_panel> <layout_panel default_tab_group="3" left="0" tab_group="2" + follows="all" top="0" - height="200" - width="244" - user_resize="true"> - <button - height="20" - follows="left|top" - top="0" - left="2" - image_overlay="TabIcon_Open_Off" + height="310" + width="244" + layout="topleft" + user_resize="true" + auto_resize="true" + visible="true" + name="left_part_holder"> + <panel + name="trnsAndChat_panel" + follows="all" + layout="topleft" + visible="true" + height="275" + width="244"> + <layout_stack + animate="true" + default_tab_group="2" + follows="all" + height="275" + width="244" layout="topleft" - width="25" - name="slide_left_btn" /> - <button - height="20" - follows="left|top" + visible="true" + orientation="vertical" + name="translate_and_chat_stack" + tab_group="1" + left_pad="0" top="0" - left="2" - image_overlay="TabIcon_Close_Off" - width="25" - name="slide_right_btn" /> + left="0"> + <layout_panel + auto_resize="false" + height="26" + layout="topleft" + left_delta="0" + name="translate_chat_checkbox_lp" + top_delta="0" + visible="true" + width="230"> + <check_box + top="10" + control_name="TranslateChat" + enabled="true" + height="16" + label="Translate chat" + layout="topleft" + left="5" + name="translate_chat_checkbox" + width="230" /> + </layout_panel> + <layout_panel + height="248" + width="230" + layout="topleft" + follows="all" + left_delta="0" + top_delta="0" + bottom="0" + visible="true" + user_resize="true" + auto_resize="true" + name="chat_holder"> <chat_history font="SansSerifSmall" - follows="left|right|top|bottom" - height="150" + follows="all" + visible="true" + height="240" name="chat_history" parse_highlights="true" parse_urls="true" - left="1" - width="238"> + width="230" + left="5"> </chat_history> - <line_editor - bottom="0" - left="3" - follows="left|right|bottom" - font="SansSerifSmall" - height="20" - label="To" - layout="bottomleft" - name="chat_editor" - spellcheck="true" - tab_group="3" - width="236"> - </line_editor> + </layout_panel> + </layout_stack> + </panel> + <chat_editor + bottom="0" + expand_lines_count="5" + follows="left|right|bottom" + font="SansSerifSmall" + visible="true" + height="20" + is_expandable="true" + label="To" + text_tentative_color="TextFgTentativeColor" + layout="bottomleft" + name="chat_editor" + max_length="1023" + spellcheck="true" + tab_group="3" + width="220" + left="10" + wrap="true"> + </chat_editor> </layout_panel> </layout_stack> + </view> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_pathfinding_console.xml b/indra/newview/skins/default/xui/en/floater_pathfinding_console.xml index 2629313069..79f2027c31 100644 --- a/indra/newview/skins/default/xui/en/floater_pathfinding_console.xml +++ b/indra/newview/skins/default/xui/en/floater_pathfinding_console.xml @@ -152,7 +152,7 @@ </text> <check_box height="19" - label="World" + label="Test" layout="topleft" name="show_world" top_pad="4" diff --git a/indra/newview/skins/default/xui/en/floater_people.xml b/indra/newview/skins/default/xui/en/floater_people.xml index 08d0b00a83..8e143623ab 100644 --- a/indra/newview/skins/default/xui/en/floater_people.xml +++ b/indra/newview/skins/default/xui/en/floater_people.xml @@ -7,20 +7,20 @@ height="570" help_topic="sidebar_people" min_height="440" - min_width="333" + min_width="390" layout="topleft" name="floater_people" save_rect="true" single_instance="true" reuse_instance="true" title="PEOPLE" - width="333"> + width="390"> <panel_container default_panel_name="panel_people" follows="all" height="570" name="main_panel" - width="333"> + width="390"> <panel class="panel_people" name="panel_people" @@ -31,11 +31,5 @@ filename="panel_group_info_sidetray.xml" label="Group Profile" font="SansSerifBold"/> - <panel - class="panel_block_list_sidetray" - name="panel_block_list_sidetray" - filename="panel_block_list_sidetray.xml" - label="Blocked Residents & Objects" - font="SansSerifBold"/> </panel_container> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_top_objects.xml b/indra/newview/skins/default/xui/en/floater_top_objects.xml index 4dfdcd15c7..0b71177345 100644 --- a/indra/newview/skins/default/xui/en/floater_top_objects.xml +++ b/indra/newview/skins/default/xui/en/floater_top_objects.xml @@ -2,7 +2,7 @@ <floater legacy_header_height="18" can_resize="true" - height="350" + height="372" layout="topleft" min_height="300" min_width="450" @@ -23,10 +23,6 @@ Time </floater.string> <floater.string - name="scripts_mono_time_label"> - Mono Time - </floater.string> - <floater.string name="top_colliders_title"> Top Colliders </floater.string> @@ -68,31 +64,35 @@ <scroll_list.columns label="Score" name="score" - width="55" /> + width="45" /> <scroll_list.columns label="Name" name="name" - width="140" /> + width="130" /> <scroll_list.columns label="Owner" name="owner" - width="105" /> + width="100" /> <scroll_list.columns label="Location" name="location" - width="130" /> + width="120" /> + <scroll_list.columns + label="Parcel" + name="parcel" + width="120" /> <scroll_list.columns label="Time" name="time" - width="150" /> - <scroll_list.columns - label="Mono Time" - name="mono_time" - width="100" /> + width="130" /> <scroll_list.columns label="URLs" name="URLs" - width="100" /> + width="40" /> + <scroll_list.columns + label="Memory (KB)" + name="memory" + width="40" /> <scroll_list.commit_callback function="TopObjects.CommitObjectsList" /> </scroll_list> @@ -193,6 +193,38 @@ <button.commit_callback function="TopObjects.GetByOwnerName" /> </button> + <text + type="string" + length="1" + follows="left|bottom" + height="20" + layout="topleft" + left="10" + top_pad="5" + name="parcel_name_text" + width="107"> + Parcel: + </text> + <line_editor + follows="left|bottom|right" + height="20" + layout="topleft" + left_pad="3" + name="parcel_name_editor" + top_delta="-3" + width="568" /> + <button + follows="bottom|right" + height="23" + label="Filter" + layout="topleft" + left_pad="5" + name="filter_parcel_btn" + top_delta="0" + width="100"> + <button.commit_callback + function="TopObjects.GetByParcelName" /> + </button> <button follows="bottom|right" height="22" diff --git a/indra/newview/skins/default/xui/en/floater_voice_chat_volume.xml b/indra/newview/skins/default/xui/en/floater_voice_chat_volume.xml new file mode 100644 index 0000000000..5c71fd3bc6 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_voice_chat_volume.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> + +<floater + legacy_header_height="25" + bevel_style="in" + bg_opaque_image="Inspector_Background" + can_close="false" + can_minimize="false" + height="90" + layout="topleft" + name="floater_voice_volume" + single_instance="true" + sound_flags="0" + title="VOICE CHAT VOLUME" + visible="true" + width="245"> + <slider + control_name="AudioLevelVoice" + disabled_control="MuteAudio" + follows="left|top" + height="16" + increment="0.025" + initial_value="0.5" + label="Voice Chat" + label_width="50" + layout="topleft" + left="15" + top="50" + name="chat_voice_volume" + show_text="false" + slider_label.halign="right" + volume="true" + width="200"> + </slider> + <button + control_name="MuteVoice" + disabled_control="MuteAudio" + follows="top|left" + height="16" + image_selected="AudioMute_Off" + image_unselected="Audio_Off" + is_toggle="true" + layout="topleft" + left_pad="5" + name="mute_audio" + tab_stop="false" + width="16" /> +</floater>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/floater_voice_volume.xml b/indra/newview/skins/default/xui/en/floater_voice_volume.xml new file mode 100644 index 0000000000..9346295d5b --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_voice_volume.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<!-- + Not can_close / no title to avoid window chrome + Single instance - only have one at a time, recycle it each spawn +--> +<floater + legacy_header_height="25" + bevel_style="in" + bg_opaque_image="Inspector_Background" + can_close="false" + can_minimize="false" + height="90" + layout="topleft" + name="floater_voice_volume" + single_instance="true" + sound_flags="0" + title="VOICE VOLUME" + visible="true" + width="245"> + <text + follows="top|left|right" + font="SansSerifSmall" + height="21" + left="10" + name="avatar_name" + parse_urls="false" + top="35" + text_color="White" + translate="false" + use_ellipses="true" + value="TestString PleaseIgnore" + width="225" /> + <slider + follows="top|left" + height="23" + increment="0.01" + left="1" + max_val="0.95" + min_val="0.05" + name="volume_slider" + show_text="false" + tool_tip="Voice volume" + top_pad="0" + value="0.5" + width="200" /> + <button + follows="top|left" + height="16" + image_disabled="Audio_Off" + image_disabled_selected="AudioMute_Off" + image_hover_selected="AudioMute_Over" + image_selected="AudioMute_Off" + image_unselected="Audio_Off" + is_toggle="true" + left_pad="0" + top_delta="4" + name="mute_btn" + width="16" /> +</floater> diff --git a/indra/newview/skins/default/xui/en/inspect_avatar.xml b/indra/newview/skins/default/xui/en/inspect_avatar.xml index bc3bcd331b..c3481e6d4c 100644 --- a/indra/newview/skins/default/xui/en/inspect_avatar.xml +++ b/indra/newview/skins/default/xui/en/inspect_avatar.xml @@ -9,7 +9,7 @@ bg_opaque_image="Inspector_Background" can_close="false" can_minimize="false" - height="164" + height="130" layout="topleft" name="inspect_avatar" single_instance="true" @@ -94,32 +94,17 @@ use_ellipses="true" width="220">This is my second life description and I really think it is great. But for some reason my description is super extra long because I like to talk a whole lot </text> - <slider - follows="top|left" - height="23" - increment="0.01" - left="1" - max_val="0.95" - min_val="0.05" - name="volume_slider" - show_text="false" - tool_tip="Voice volume" - top_pad="0" - value="0.5" - width="200" /> - <button + <text follows="top|left" height="16" - image_disabled="Audio_Off" - image_disabled_selected="AudioMute_Off" - image_hover_selected="AudioMute_Over" - image_selected="AudioMute_Off" - image_unselected="Audio_Off" - is_toggle="true" - left_pad="0" - top_delta="4" - name="mute_btn" - width="16" /> + left="8" + name="avatar_profile_link" + font="SansSerifSmall" + text_color="White" + top_pad="5" + translate="false" + value="[[LINK] View full profile]" + width="175" /> <avatar_icon follows="top|left" height="38" @@ -130,83 +115,4 @@ name="avatar_icon" top="10" width="38" /> -<!-- Overlapping buttons for default actions - llinspectavatar.cpp makes visible the most likely default action ---> - <button - follows="top|left" - height="20" - label="Add Friend" - left="8" - top="135" - name="add_friend_btn" - width="90" /> - <button - follows="top|left" - height="20" - label="IM" - left_delta="0" - top_delta="0" - name="im_btn" - width="80" - commit_callback.function="InspectAvatar.IM"/> - <button - follows="top|left" - height="20" - label="Profile" - layout="topleft" - name="view_profile_btn" - left_delta="96" - top_delta="0" - tab_stop="false" - width="80" /> - <!-- gear buttons here --> - <menu_button - follows="top|left" - height="20" - layout="topleft" - image_overlay="OptionsMenu_Off" - menu_filename="menu_inspect_avatar_gear.xml" - name="gear_btn" - right="-5" - top_delta="0" - width="35" /> - <menu_button - follows="top|left" - height="20" - image_overlay="OptionsMenu_Off" - menu_filename="menu_inspect_self_gear.xml" - name="gear_self_btn" - right="-5" - top_delta="0" - width="35" /> - <panel - follows="top|left" - top="164" - left="0" - height="60" - width="228" - visible="false" - background_visible="true" - name="moderator_panel" - background_opaque="true" - bg_opaque_color="MouseGray"> - <button - name="disable_voice" - label="Disable Voice" - top="20" - width="95" - height="20" - left="10" - commit_callback.function="InspectAvatar.DisableVoice"/> - <button - name="enable_voice" - label="Enable Voice" - top="20" - width="95" - height="20" - left="10" - visible="false" - commit_callback.function="InspectAvatar.EnableVoice"/> - </panel> </floater> diff --git a/indra/newview/skins/default/xui/en/menu_conversation.xml b/indra/newview/skins/default/xui/en/menu_conversation.xml new file mode 100644 index 0000000000..2e9bda5804 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_conversation.xml @@ -0,0 +1,173 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + bottom="806" + layout="topleft" + left="0" + mouse_opaque="false" + name="menu_conversation_participant" + visible="false"> + <menu_item_call + label="Close conversation" + layout="topleft" + name="close_conversation"> + <on_click function="Avatar.DoToSelected" parameter="close_conversation"/> + </menu_item_call> + <menu_item_call + label="Open voice conversation" + layout="topleft" + name="open_voice_conversation"> + <on_click function="Avatar.DoToSelected" parameter="open_voice_conversation"/> + </menu_item_call> + <menu_item_call + label="Disconnect from voice" + layout="topleft" + name="disconnect_from_voice"> + <on_click function="Avatar.DoToSelected" parameter="disconnect_from_voice"/> + </menu_item_call> + <menu_item_separator layout="topleft" name="separator_disconnect_from_voice"/> + <menu_item_call + label="View Profile" + layout="topleft" + name="view_profile"> + <on_click function="Avatar.DoToSelected" parameter="view_profile"/> + </menu_item_call> + <menu_item_call + label="IM" + layout="topleft" + name="im"> + <on_click function="Avatar.DoToSelected" parameter="im"/> + </menu_item_call> + <menu_item_call + label="Offer teleport" + layout="topleft" + name="offer_teleport"> + <on_click function="Avatar.DoToSelected" parameter="offer_teleport"/> + <on_enable function="Avatar.EnableItem" parameter="can_offer_teleport"/> + </menu_item_call> + <menu_item_call + label="Voice call" + layout="topleft" + name="voice_call"> + <on_click function="Avatar.DoToSelected" parameter="voice_call"/> + <on_enable function="Avatar.EnableItem" parameter="can_call" /> + </menu_item_call> + <menu_item_call + label="Chat history..." + layout="topleft" + name="chat_history"> + <on_click function="Avatar.DoToSelected" parameter="chat_history"/> + </menu_item_call> + <menu_item_separator layout="topleft" name="separator_chat_history"/> + <menu_item_call + label="Add friend" + layout="topleft" + name="add_friend"> + <on_click function="Avatar.DoToSelected" parameter="add_friend"/> + <on_enable function="Avatar.EnableItem" parameter="can_add" /> + </menu_item_call> + <menu_item_call + label="Remove friend" + layout="topleft" + name="remove_friend"> + <on_click function="Avatar.DoToSelected" parameter="remove_friend" /> + <on_enable function="Avatar.EnableItem" parameter="can_delete" /> + </menu_item_call> + <menu_item_call + label="Invite to group..." + layout="topleft" + name="invite_to_group"> + <on_click function="Avatar.DoToSelected" parameter="invite_to_group" /> + </menu_item_call> + <menu_item_separator layout="topleft" name="separator_invite_to_group"/> + <menu_item_call + label="Map" + layout="topleft" + name="map"> + <on_click function="Avatar.DoToSelected" parameter="map" /> + <on_enable function="Avatar.EnableItem" parameter="can_show_on_map" /> + </menu_item_call> + <menu_item_call + label="Share" + layout="topleft" + name="share"> + <on_click function="Avatar.DoToSelected" parameter="share" /> + </menu_item_call> + <menu_item_call + label="Pay" + layout="topleft" + name="pay"> + <on_click function="Avatar.DoToSelected" parameter="pay" /> + </menu_item_call> + <menu_item_check + label="Block / unblock" + layout="topleft" + name="block_unblock"> + <on_click function="Avatar.DoToSelected" parameter="block_unblock" /> + <on_check function="Avatar.CheckItem" parameter="is_blocked" /> + <on_enable function="Avatar.EnableItem" parameter="can_block" /> + </menu_item_check> + <menu_item_call + label="Group Profile" + layout="topleft" + name="group_profile"> + <on_click function="Group.DoToSelected" parameter="group_profile"/> + </menu_item_call> + <menu_item_call + label="Activate Group" + layout="topleft" + name="activate_group"> + <on_click function="Group.DoToSelected" parameter="activate_group"/> + <on_enable function="Avatar.EnableItem" parameter="can_activate_group" /> + </menu_item_call> + <menu_item_call + label="Leave Group" + layout="topleft" + name="leave_group"> + <on_click function="Group.DoToSelected" parameter="leave_group"/> + </menu_item_call> + <menu_item_separator + layout="topleft" + name="Moderator Options Separator"/> + <context_menu + label="Moderator Options" + layout="topleft" + name="Moderator Options"> + <menu_item_check + label="Allow text chat" + layout="topleft" + name="AllowTextChat"> + <on_check function="Avatar.CheckItem" parameter="is_allowed_text_chat" /> + <on_click function="Avatar.DoToSelected" parameter="toggle_allow_text_chat" /> + <on_enable function="Avatar.EnableItem" parameter="can_allow_text_chat" /> + </menu_item_check> + <menu_item_separator layout="topleft" name="moderate_voice_separator" /> + <menu_item_call + label="Mute this participant" + layout="topleft" + name="ModerateVoiceMuteSelected"> + <on_click function="Avatar.DoToSelected" parameter="selected" /> + <on_enable function="Avatar.EnableItem" parameter="can_mute" /> + </menu_item_call> + <menu_item_call + label="Unmute this participant" + layout="topleft" + name="ModerateVoiceUnMuteSelected"> + <on_click function="Avatar.DoToSelected" parameter="selected" /> + <on_enable function="Avatar.EnableItem" parameter="can_unmute" /> + </menu_item_call> + <menu_item_call + label="Mute everyone" + layout="topleft" + name="ModerateVoiceMute"> + <on_click function="Avatar.DoToSelected" parameter="mute_all" /> + <on_enable function="Avatar.EnableItem" parameter="can_moderate_voice" /> + </menu_item_call> + <menu_item_call + label="Unmute everyone" + layout="topleft" + name="ModerateVoiceUnmute"> + <on_click function="Avatar.DoToSelected" parameter="unmute_all" /> + <on_enable function="Avatar.EnableItem" parameter="can_moderate_voice" /> + </menu_item_call> + </context_menu> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml b/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml new file mode 100644 index 0000000000..8796b87955 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml @@ -0,0 +1,142 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + layout="topleft" + name="Conversation Context Menu"> + <menu_item_call + label="IM..." + layout="topleft" + name="IM"> + <on_click + function="Calllog.Action" + parameter="im" /> + <on_enable + function="Calllog.Enable" + parameter="can_im" /> + </menu_item_call> + <menu_item_call + label="Voice call..." + layout="topleft" + name="Call"> + <on_click + function="Calllog.Action" + parameter="call" /> + <on_enable + function="Calllog.Enable" + parameter="can_call" /> + </menu_item_call> + <menu_item_call + label="Open chat history..." + layout="topleft" + name="Chat history"> + <on_click + function="Calllog.Action" + parameter="chat_history" /> + <on_enable + function="Calllog.Enable" + parameter="can_view_chat_history" /> + </menu_item_call> + <menu_item_call + label="View Profile" + layout="topleft" + name="View Profile"> + <on_click + function="Calllog.Action" + parameter="view_profile" /> + <on_enable + function="Calllog.Enable" + parameter="can_view_profile" /> + </menu_item_call> + <menu_item_call + label="Offer Teleport" + name="teleport"> + <on_click + function="Calllog.Action" + parameter="offer_teleport"/> + <on_enable + function="Calllog.Enable" + parameter="can_offer_teleport"/> + </menu_item_call> + <menu_item_separator /> + <menu_item_call + label="Add Friend" + layout="topleft" + name="add_friend"> + <on_click + function="Calllog.Action" + parameter="add_friend"/> + <on_visible + function="Calllog.Check" + parameter="is_not_friend" /> + </menu_item_call> + <menu_item_call + label="Remove Friend" + layout="topleft" + name="remove_friend"> + <on_click + function="Calllog.Action" + parameter="remove_friend"/> + <on_visible + function="Calllog.Check" + parameter="is_friend" /> + </menu_item_call> + <menu_item_call + label="Invite to group..." + layout="topleft" + name="Invite"> + <on_click + function="Calllog.Action" + parameter="invite_to_group"/> + <on_enable + function="Calllog.Enable" + parameter="can_invite_to_group" /> + </menu_item_call> + <menu_item_separator /> + <menu_item_call + label="Map" + layout="topleft" + name="Map"> + <on_click + function="Calllog.Action" + parameter="show_on_map" /> + <on_enable + function="Calllog.Enable" + parameter="can_show_on_map" /> + </menu_item_call> + <menu_item_call + label="Share" + layout="topleft" + name="Share"> + <on_click + function="Calllog.Action" + parameter="share" /> + <on_enable + function="Calllog.Enable" + parameter="can_share" /> + </menu_item_call> + <menu_item_call + label="Pay" + layout="topleft" + name="Pay"> + <on_click + function="Calllog.Action" + parameter="pay" /> + <on_enable + function="Calllog.Enable" + parameter="can_pay" /> + </menu_item_call> + <menu_item_check + label="Block/Unblock" + layout="topleft" + name="Block/Unblock"> + <menu_item_check.on_click + function="Calllog.Action" + parameter="block"/> + <menu_item_check.on_check + function="Calllog.Check" + parameter="is_blocked" /> + <menu_item_check.on_enable + function="Calllog.Enable" + parameter="can_block" /> + </menu_item_check> + +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_conversation_log_view.xml b/indra/newview/skins/default/xui/en/menu_conversation_log_view.xml new file mode 100644 index 0000000000..ce65b23971 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_conversation_log_view.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + name="menu_conversation_view" + left="0" bottom="0" visible="false" + mouse_opaque="false"> + <menu_item_check + label="Sort by name" + name="sort_by_name"> + <on_click + function="CallLog.Action" + parameter="sort_by_name"/> + <on_check + function="CallLog.Check" + parameter="sort_by_name"/> + </menu_item_check> + <menu_item_check + label="Sort by date" + name="sort_by_date"> + <on_click + function="CallLog.Action" + parameter="sort_by_date" /> + <on_check + function="CallLog.Check" + parameter="sort_by_date" /> + </menu_item_check> + <menu_item_separator /> + <menu_item_check + label="Sort friends on top" + name="sort_by_friends"> + <on_click + function="CallLog.Action" + parameter="sort_friends_on_top" /> + <on_check + function="CallLog.Check" + parameter="sort_friends_on_top" /> + </menu_item_check> + <menu_item_separator /> + <menu_item_call + label="View Nearby chat history..." + name="view_nearby_chat_history"> + <on_click + function="CallLog.Action" + parameter="view_nearby_chat_history" /> + </menu_item_call> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_group_plus.xml b/indra/newview/skins/default/xui/en/menu_group_plus.xml index fce7414d80..eca9e7f3c9 100644 --- a/indra/newview/skins/default/xui/en/menu_group_plus.xml +++ b/indra/newview/skins/default/xui/en/menu_group_plus.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu name="menu_group_plus" +<toggleable_menu name="menu_group_plus" left="0" bottom="0" visible="false" mouse_opaque="false"> <menu_item_call name="item_join" label="Join Group..."> @@ -8,4 +8,4 @@ <menu_item_call name="item_new" label="New Group..."> <menu_item_call.on_click function="People.Group.Plus.Action" userdata="new_group" /> </menu_item_call> -</menu> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_im_session_showmodes.xml b/indra/newview/skins/default/xui/en/menu_im_session_showmodes.xml new file mode 100644 index 0000000000..483f24afd0 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_im_session_showmodes.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + name="menu_modes" + left="0" bottom="0" visible="false" + mouse_opaque="false"> + <menu_item_check + label="Compact view" + name="compact_view"> + <menu_item_check.on_click + function="IMSession.Menu.Action" + parameter="compact_view"/> + <menu_item_check.on_check + function="IMSession.Menu.CompactExpandedModes.CheckItem" + parameter="compact_view"/> + </menu_item_check> + <menu_item_check + label="Expanded view" + name="expanded_view"> + <menu_item_check.on_click + function="IMSession.Menu.Action" + parameter="expanded_view"/> + <menu_item_check.on_check + function="IMSession.Menu.CompactExpandedModes.CheckItem" + parameter="expanded_view"/> + </menu_item_check> + <menu_item_separator layout="topleft" /> + <menu_item_check name="IMShowTime" label="Show time"> + <menu_item_check.on_click + function="IMSession.Menu.Action" + parameter="IMShowTime" /> + <menu_item_check.on_check + function="IMSession.Menu.ShowModes.CheckItem" + parameter="IMShowTime" /> + <menu_item_check.on_enable + function="IMSession.Menu.ShowModes.Enable" + parameter="IMShowTime" /> + </menu_item_check> + <menu_item_check name="IMShowNamesForP2PConv" label="Show names in one-to-one conversations"> + <menu_item_check.on_click + function="IMSession.Menu.Action" + parameter="IMShowNamesForP2PConv" /> + <menu_item_check.on_check + function="IMSession.Menu.ShowModes.CheckItem" + parameter="IMShowNamesForP2PConv" /> + <menu_item_check.on_enable + function="IMSession.Menu.ShowModes.Enable" + parameter="IMShowNamesForP2PConv" /> + + </menu_item_check> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml b/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml deleted file mode 100644 index 76b188220d..0000000000 --- a/indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml +++ /dev/null @@ -1,143 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<toggleable_menu - create_jump_keys="true" - layout="topleft" - mouse_opaque="false" - visible="false" - name="Gear Menu"> - <menu_item_call - label="View Profile" - enabled="true" - name="view_profile"> - <menu_item_call.on_click - function="InspectAvatar.ViewProfile"/> - </menu_item_call> - <menu_item_call - label="Add Friend" - name="add_friend"> - <menu_item_call.on_click - function="InspectAvatar.AddFriend"/> - <menu_item_call.on_enable - function="InspectAvatar.Gear.Enable"/> - </menu_item_call> - <menu_item_call - label="IM" - name="im"> - <menu_item_call.on_click - function="InspectAvatar.IM"/> - </menu_item_call> - <menu_item_call - label="Call" - enabled="true" - name="call"> - <menu_item_call.on_click - function="InspectAvatar.Call"/> - <menu_item_call.on_enable - function="InspectAvatar.Gear.EnableCall"/> - </menu_item_call> - <menu_item_call - label="Teleport" - name="teleport"> - <menu_item_call.on_click - function="InspectAvatar.Teleport"/> - <menu_item_call.on_enable - function="InspectAvatar.Gear.EnableTeleportOffer"/> - </menu_item_call> - <menu_item_call - label="Invite to Group" - name="invite_to_group"> - <menu_item_call.on_click - function="InspectAvatar.InviteToGroup"/> - </menu_item_call> - <menu_item_separator /> - <menu_item_call - label="Block" - name="block"> - <menu_item_call.on_click - function="InspectAvatar.ToggleMute"/> - <menu_item_call.on_visible - function="InspectAvatar.EnableMute" /> - </menu_item_call> - <menu_item_call - label="Unblock" - name="unblock"> - <menu_item_call.on_click - function="InspectAvatar.ToggleMute"/> - <menu_item_call.on_visible - function="InspectAvatar.EnableUnmute" /> - </menu_item_call> - <menu_item_call - label="Report" - name="report"> - <menu_item_call.on_click - function="InspectAvatar.Report"/> - </menu_item_call> - <menu_item_call - label="Freeze" - name="freeze"> - <menu_item_call.on_click - function="InspectAvatar.Freeze"/> - <menu_item_call.on_visible - function="InspectAvatar.VisibleFreeze"/> - </menu_item_call> - <menu_item_call - label="Eject" - name="eject"> - <menu_item_call.on_click - function="InspectAvatar.Eject"/> - <menu_item_call.on_visible - function="InspectAvatar.VisibleEject"/> - </menu_item_call> - <menu_item_call - label="Kick" - name="kick"> - <menu_item_call.on_click - function="InspectAvatar.Kick"/> - <menu_item_call.on_visible - function="InspectAvatar.EnableGod"/> - </menu_item_call> - <menu_item_call - label="CSR" - name="csr"> - <menu_item_call.on_click - function="InspectAvatar.CSR" /> - <menu_item_call.on_visible - function="InspectAvatar.EnableGod" /> - </menu_item_call> - <menu_item_call - label="Debug Textures" - name="debug"> - <menu_item_call.on_click - function="Avatar.Debug"/> - <menu_item_call.on_visible - function="IsGodCustomerService"/> - </menu_item_call> - <menu_item_call - label="Find On Map" - name="find_on_map"> - <menu_item_call.on_click - function="InspectAvatar.FindOnMap"/> - <menu_item_call.on_visible - function="InspectAvatar.VisibleFindOnMap"/> - </menu_item_call> - <menu_item_call - label="Zoom In" - name="zoom_in"> - <menu_item_call.on_click - function="InspectAvatar.ZoomIn"/> - <menu_item_call.on_visible - function="InspectAvatar.VisibleZoomIn"/> - </menu_item_call> - <menu_item_call - label="Pay" - name="pay"> - <menu_item_call.on_click - function="InspectAvatar.Pay"/> - </menu_item_call> - <menu_item_call - label="Share" - name="share"> - <menu_item_call.on_click - function="InspectAvatar.Share"/> - </menu_item_call> -</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_inspect_self_gear.xml b/indra/newview/skins/default/xui/en/menu_inspect_self_gear.xml deleted file mode 100644 index 5e7b16ed4a..0000000000 --- a/indra/newview/skins/default/xui/en/menu_inspect_self_gear.xml +++ /dev/null @@ -1,252 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<toggleable_menu - layout="topleft" - name="Self Pie"> - <menu_item_call - label="Sit Down" - layout="topleft" - name="Sit Down Here"> - <menu_item_call.on_click - function="Self.SitDown" - parameter="" /> - <menu_item_call.on_enable - function="Self.EnableSitDown" /> - </menu_item_call> - <menu_item_call - label="Stand Up" - layout="topleft" - name="Stand Up"> - <menu_item_call.on_click - function="Self.StandUp" - parameter="" /> - <menu_item_call.on_enable - function="Self.EnableStandUp" /> - </menu_item_call> - <context_menu - label="Take Off" - layout="topleft" - name="Take Off >"> - <context_menu - label="Clothes" - layout="topleft" - name="Clothes >"> - <menu_item_call - enabled="false" - label="Shirt" - layout="topleft" - name="Shirt"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="shirt" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="shirt" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Pants" - layout="topleft" - name="Pants"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="pants" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="pants" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Skirt" - layout="topleft" - name="Skirt"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="skirt" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="skirt" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Shoes" - layout="topleft" - name="Shoes"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="shoes" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="shoes" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Socks" - layout="topleft" - name="Socks"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="socks" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="socks" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Jacket" - layout="topleft" - name="Jacket"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="jacket" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="jacket" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Gloves" - layout="topleft" - name="Gloves"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="gloves" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="gloves" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Undershirt" - layout="topleft" - name="Self Undershirt"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="undershirt" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="undershirt" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Underpants" - layout="topleft" - name="Self Underpants"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="underpants" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="underpants" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Tattoo" - layout="topleft" - name="Self Tattoo"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="tattoo" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="tattoo" /> - </menu_item_call> - <menu_item_call - enabled="false" - label="Alpha" - layout="topleft" - name="Self Alpha"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="alpha" /> - <menu_item_call.on_enable - function="Edit.EnableTakeOff" - parameter="alpha" /> - </menu_item_call> - <menu_item_separator - layout="topleft" /> - <menu_item_call - label="All Clothes" - layout="topleft" - name="All Clothes"> - <menu_item_call.on_click - function="Edit.TakeOff" - parameter="all" /> - </menu_item_call> - </context_menu> - <context_menu - label="HUD" - layout="topleft" - name="Object Detach HUD" /> - <context_menu - label="Detach" - layout="topleft" - name="Object Detach" /> - <menu_item_call - label="Detach All" - layout="topleft" - name="Detach All"> - <menu_item_call.on_click - function="Self.RemoveAllAttachments" - parameter="" /> - <menu_item_call.on_enable - function="Self.EnableRemoveAllAttachments" /> - </menu_item_call> - </context_menu> - <menu_item_call - label="Change Outfit" - layout="topleft" - name="Chenge Outfit"> - <menu_item_call.on_click - function="CustomizeAvatar" /> - <menu_item_call.on_enable - function="Edit.EnableCustomizeAvatar" /> - </menu_item_call> - <menu_item_call label="Edit My Outfit" - layout="topleft" - name="Edit Outfit"> - <menu_item_call.on_click - function="EditOutfit" /> - <menu_item_call.on_enable - function="Edit.EnableCustomizeAvatar" /> - </menu_item_call> - <menu_item_call label="Edit My Shape" - layout="topleft" - name="Edit My Shape"> - <menu_item_call.on_click - function="EditShape" /> - <menu_item_call.on_enable - function="Edit.EnableEditShape" /> - </menu_item_call> - <menu_item_call - label="My Friends" - layout="topleft" - name="Friends..."> - <menu_item_call.on_click - function="SideTray.PanelPeopleTab" - parameter="friends_panel" /> - </menu_item_call> - <menu_item_call - label="My Groups" - layout="topleft" - name="Groups..."> - <menu_item_call.on_click - function="SideTray.PanelPeopleTab" - parameter="groups_panel" /> - </menu_item_call> - <menu_item_call - label="My Profile" - layout="topleft" - name="Profile..."> - <menu_item_call.on_click - function="ShowAgentProfile" - parameter="agent" /> - </menu_item_call> - <menu_item_call - label="Debug Textures" - name="Debug..."> - <menu_item_call.on_click - function="Avatar.Debug" /> - <menu_item_call.on_visible - function="IsGodCustomerService"/> - </menu_item_call> -</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_participant_view.xml b/indra/newview/skins/default/xui/en/menu_participant_view.xml new file mode 100644 index 0000000000..6fa0707eea --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_participant_view.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + layout="topleft" + name="participant_manu_view"> + <menu_item_check + label="Sort conversations by type" + layout="topleft" + name="sort_sessions_by_type"> + <on_click + function="IMFloaterContainer.Action" + parameter="sort_sessions_by_type" /> + <on_check + function="IMFloaterContainer.Check" + parameter="sort_sessions_by_type" /> + </menu_item_check> + <menu_item_check + label="Sort conversations by name" + layout="topleft" + name="sort_sessions_by_name"> + <on_click + function="IMFloaterContainer.Action" + parameter="sort_sessions_by_name" /> + <on_check + function="IMFloaterContainer.Check" + parameter="sort_sessions_by_name" /> + </menu_item_check> + <menu_item_check + label="Sort conversations by recent activity" + layout="topleft" + name="sort_sessions_by_recent"> + <on_click + function="IMFloaterContainer.Action" + parameter="sort_sessions_by_recent" /> + <on_check + function="IMFloaterContainer.Check" + parameter="sort_sessions_by_recent" /> + </menu_item_check> + <menu_item_separator + layout="topleft" /> + <menu_item_check + label="Sort participants by name" + layout="topleft" + name="sort_participants_by_name"> + <on_click + function="IMFloaterContainer.Action" + parameter="sort_participants_by_name" /> + <on_check + function="IMFloaterContainer.Check" + parameter="sort_participants_by_name" /> + </menu_item_check> + <menu_item_check + label="Sort participants by recent activity" + layout="topleft" + name="sort_participants_by_recent"> + <on_click + function="IMFloaterContainer.Action" + parameter="sort_participants_by_recent" /> + <on_check + function="IMFloaterContainer.Check" + parameter="sort_participants_by_recent" /> + </menu_item_check> + <menu_item_separator + layout="topleft" /> + <menu_item_call + label="Chat preferences..." + name="chat_preferences"> + <on_click + function="IMFloaterContainer.Action" + parameter="chat_preferences" /> + </menu_item_call> + <menu_item_check + label="Open conversation log" + name="Conversation" + visible="true"> + <menu_item_check.on_check + function="Floater.Visible" + parameter="conversation" /> + <menu_item_check.on_click + function="Floater.Toggle" + parameter="conversation" /> + </menu_item_check> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_blocked_gear.xml b/indra/newview/skins/default/xui/en/menu_people_blocked_gear.xml new file mode 100644 index 0000000000..63295ea27b --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_blocked_gear.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + name="menu_blocked_gear" + left="0" bottom="0" visible="false" + mouse_opaque="false"> + <menu_item_call + label="Unblock" + name="unblock"> + <on_click + function="Block.Action" + parameter="unblock_item" /> + <on_enable + function="Block.Enable" + parameter="unblock_item" /> + </menu_item_call> + <menu_item_call + label="Profile..." + name="profile"> + <on_click + function="Block.Action" + parameter="profile_item"/> + <on_enable + function="Block.Enable" + parameter="profile_item" /> + </menu_item_call> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_blocked_plus.xml b/indra/newview/skins/default/xui/en/menu_people_blocked_plus.xml new file mode 100644 index 0000000000..0c7155667e --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_blocked_plus.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + name="menu_blocked_plus" + left="0" bottom="0" visible="false" + mouse_opaque="false"> + <menu_item_call + label="Block Resident by name..." + name="block_resident_by_name"> + <on_click + function="Block.Action" + parameter="block_res_by_name"/> + </menu_item_call> + <menu_item_call + label="Block object by name" + name="block_object_by_name"> + <on_click + function="Block.Action" + parameter="block_obj_by_name"/> + </menu_item_call> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_blocked_view.xml b/indra/newview/skins/default/xui/en/menu_people_blocked_view.xml new file mode 100644 index 0000000000..2efb70ee37 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_blocked_view.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + name="menu_blocked_view" + left="0" bottom="0" visible="false" + mouse_opaque="false"> + <menu_item_check + label="Sort by name" + name="sort_by_name"> + <on_click + function="Block.Action" + parameter="sort_by_name"/> + <on_check + function="Block.Check" + parameter="sort_by_name"/> + </menu_item_check> + <menu_item_check + label="Sort by type" + name="sort_by_type"> + <on_click + function="Block.Action" + parameter="sort_by_type" /> + <on_check + function="Block.Check" + parameter="sort_by_type" /> + </menu_item_check> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_friends_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_friends_view.xml index b452f96e7a..dde9432867 100644 --- a/indra/newview/skins/default/xui/en/menu_people_friends_view_sort.xml +++ b/indra/newview/skins/default/xui/en/menu_people_friends_view.xml @@ -40,8 +40,12 @@ function="CheckControl" parameter="FriendsListShowPermissions" /> </menu_item_check> - <menu_item_separator layout="topleft" /> - <menu_item_call name="show_blocked_list" label="Show Blocked Residents & Objects"> - <menu_item_call.on_click function="People.Friends.ViewSort.Action" parameter="panel_block_list_sidetray" /> - </menu_item_call> + <menu_item_check name="view_conversation" label="View Conversation Log..."> + <menu_item_check.on_check + function="Floater.Visible" + parameter="conversation" /> + <menu_item_check.on_click + function="Floater.Toggle" + parameter="conversation" /> + </menu_item_check> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_groups.xml b/indra/newview/skins/default/xui/en/menu_people_groups.xml index 8f89d37dbb..1e0364b84e 100644 --- a/indra/newview/skins/default/xui/en/menu_people_groups.xml +++ b/indra/newview/skins/default/xui/en/menu_people_groups.xml @@ -1,8 +1,18 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<menu name="menu_group_plus" +<toggleable_menu name="menu_group_plus" left="0" bottom="0" visible="false" mouse_opaque="false" opaque="true" color="MenuDefaultBgColor"> <menu_item_call + label="Activate" + name="Activate"> + <menu_item_call.on_click + function="People.Groups.Action" + parameter="activate" /> + <menu_item_call.on_enable + function="People.Groups.Enable" + parameter="activate" /> + </menu_item_call> + <menu_item_call label="View Info" name="View Info"> <menu_item_call.on_click @@ -23,7 +33,7 @@ parameter="chat" /> </menu_item_call> <menu_item_call - label="Call" + label="Voice call" name="Call"> <menu_item_call.on_click function="People.Groups.Action" @@ -34,17 +44,6 @@ </menu_item_call> <menu_item_separator /> <menu_item_call - label="Activate" - name="Activate"> - <menu_item_call.on_click - function="People.Groups.Action" - parameter="activate" /> - <menu_item_call.on_enable - function="People.Groups.Enable" - parameter="activate" /> - </menu_item_call> - <menu_item_separator /> - <menu_item_call label="Leave" name="Leave"> <menu_item_call.on_click @@ -54,4 +53,4 @@ function="People.Groups.Enable" parameter="leave" /> </menu_item_call> -</menu> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_groups_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_groups_view.xml index c710fe3b9b..73f79f1e70 100644 --- a/indra/newview/skins/default/xui/en/menu_people_groups_view_sort.xml +++ b/indra/newview/skins/default/xui/en/menu_people_groups_view.xml @@ -14,13 +14,4 @@ function="CheckControl" parameter="GroupListShowIcons" /> </menu_item_check> - <menu_item_call - label="Leave Selected Group" - layout="topleft" - name="Leave Selected Group"> - <menu_item_call.on_click - function="People.Group.Minus.Action"/> - <menu_item_call.on_enable - function="People.Group.Minus.Enable"/> - </menu_item_call> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_nearby.xml b/indra/newview/skins/default/xui/en/menu_people_nearby.xml index d2e35e4cc0..8014e81469 100644 --- a/indra/newview/skins/default/xui/en/menu_people_nearby.xml +++ b/indra/newview/skins/default/xui/en/menu_people_nearby.xml @@ -10,12 +10,47 @@ function="Avatar.Profile" /> </menu_item_call> <menu_item_call + label="IM" + layout="topleft" + name="IM"> + <menu_item_call.on_click + function="Avatar.IM" /> + </menu_item_call> + <menu_item_call + label="Offer Teleport" + name="teleport"> + <menu_item_call.on_click + function="Avatar.OfferTeleport"/> + <menu_item_call.on_enable + function="Avatar.EnableItem" + parameter="can_offer_teleport"/> + </menu_item_call> + <menu_item_call + label="Voice call" + layout="topleft" + name="Call"> + <menu_item_call.on_click + function="Avatar.Call" /> + <menu_item_call.on_enable + function="Avatar.EnableItem" + parameter="can_call" /> + </menu_item_call> + <menu_item_separator /> + <menu_item_call + label="View chat history..." + layout="topleft" + name="Chat history"> + <menu_item_call.on_click + function="Avatar.Calllog" /> + </menu_item_call> + <menu_item_separator /> + <menu_item_call label="Add Friend" layout="topleft" name="Add Friend"> <menu_item_call.on_click function="Avatar.AddFriend" /> - <menu_item_call.on_enable + <menu_item_call.on_visible function="Avatar.EnableItem" parameter="can_add" /> </menu_item_call> @@ -30,22 +65,13 @@ parameter="can_delete" /> </menu_item_call> <menu_item_call - label="IM" + label="Invite to group..." layout="topleft" - name="IM"> + name="Invite"> <menu_item_call.on_click - function="Avatar.IM" /> - </menu_item_call> - <menu_item_call - label="Call" - layout="topleft" - name="Call"> - <menu_item_call.on_click - function="Avatar.Call" /> - <menu_item_call.on_enable - function="Avatar.EnableItem" - parameter="can_call" /> + function="Avatar.InviteToGroup" /> </menu_item_call> + <menu_item_separator /> <menu_item_call label="Map" layout="topleft" @@ -83,13 +109,5 @@ function="Avatar.EnableItem" parameter="can_block" /> </menu_item_check> - <menu_item_call - label="Offer Teleport" - name="teleport"> - <menu_item_call.on_click - function="Avatar.OfferTeleport"/> - <menu_item_call.on_enable - function="Avatar.EnableItem" - parameter="can_offer_teleport"/> - </menu_item_call> + <menu_item_separator /> </context_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_nearby_view.xml b/indra/newview/skins/default/xui/en/menu_people_nearby_view.xml new file mode 100644 index 0000000000..da88ca9f4d --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_nearby_view.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + name="menu_group_plus" + left="0" bottom="0" visible="false" + mouse_opaque="false"> + <menu_item_check + label="Sort by Recent Speakers" + name="sort_by_recent_speakers"> + <menu_item_check.on_click + function="People.Nearby.ViewSort.Action" + parameter="sort_by_recent_speakers"/> + <menu_item_check.on_check + function="People.Nearby.ViewSort.CheckItem" + parameter="sort_by_recent_speakers"/> + </menu_item_check> + <menu_item_check + label="Sort by Name" + name="sort_name"> + <menu_item_check.on_click + function="People.Nearby.ViewSort.Action" + parameter="sort_name"/> + <menu_item_check.on_check + function="People.Nearby.ViewSort.CheckItem" + parameter="sort_name"/> + </menu_item_check> + <menu_item_check + label="Sort by Distance" + name="sort_distance"> + <menu_item_check.on_click + function="People.Nearby.ViewSort.Action" + parameter="sort_distance"/> + <menu_item_check.on_check + function="People.Nearby.ViewSort.CheckItem" + parameter="sort_distance"/> + </menu_item_check> + <menu_item_separator layout="topleft" /> + <menu_item_check name="view_icons" label="View People Icons"> + <menu_item_check.on_click + function="People.Nearby.ViewSort.Action" + parameter="view_icons" /> + <menu_item_check.on_check + function="CheckControl" + parameter="NearbyListShowIcons" /> + </menu_item_check> + <menu_item_check name ="view_map" label="View Map"> + <menu_item_check.on_check + function="CheckControl" + parameter="NearbyListShowMap" /> + <menu_item_check.on_click + function="ToggleControl" + parameter="NearbyListShowMap" /> + </menu_item_check> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_nearby_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_nearby_view_sort.xml deleted file mode 100644 index 614dd693c5..0000000000 --- a/indra/newview/skins/default/xui/en/menu_people_nearby_view_sort.xml +++ /dev/null @@ -1,57 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<toggleable_menu - name="menu_group_plus" - left="0" bottom="0" visible="false" - mouse_opaque="false"> - <menu_item_check - label="Sort by Recent Speakers" - name="sort_by_recent_speakers"> - <menu_item_check.on_click - function="People.Nearby.ViewSort.Action" - parameter="sort_by_recent_speakers"/> - <menu_item_check.on_check - function="People.Nearby.ViewSort.CheckItem" - parameter="sort_by_recent_speakers"/> - </menu_item_check> - <menu_item_check - label="Sort by Name" - name="sort_name"> - <menu_item_check.on_click - function="People.Nearby.ViewSort.Action" - parameter="sort_name"/> - <menu_item_check.on_check - function="People.Nearby.ViewSort.CheckItem" - parameter="sort_name"/> - </menu_item_check> - <menu_item_check - label="Sort by Distance" - name="sort_distance"> - <menu_item_check.on_click - function="People.Nearby.ViewSort.Action" - parameter="sort_distance"/> - <menu_item_check.on_check - function="People.Nearby.ViewSort.CheckItem" - parameter="sort_distance"/> - </menu_item_check> - <menu_item_separator layout="topleft" /> - <menu_item_check name="view_icons" label="View People Icons"> - <menu_item_check.on_click - function="People.Nearby.ViewSort.Action" - parameter="view_icons" /> - <menu_item_check.on_check - function="CheckControl" - parameter="NearbyListShowIcons" /> - </menu_item_check> - <menu_item_check name ="view_map" label="View Map"> - <menu_item_check.on_check - function="CheckControl" - parameter="NearbyListShowMap" /> - <menu_item_check.on_click - function="ToggleControl" - parameter="NearbyListShowMap" /> - </menu_item_check> - <menu_item_separator layout="topleft" /> - <menu_item_call name="show_blocked_list" label="Show Blocked Residents & Objects"> - <menu_item_call.on_click function="People.Nearby.ViewSort.Action" userdata="panel_block_list_sidetray" /> - </menu_item_call> -</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_recent_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_recent_view.xml index 485a5a658c..1dbc90dd2b 100644 --- a/indra/newview/skins/default/xui/en/menu_people_recent_view_sort.xml +++ b/indra/newview/skins/default/xui/en/menu_people_recent_view.xml @@ -32,8 +32,4 @@ function="CheckControl" parameter="RecentListShowIcons" /> </menu_item_check> - <menu_item_separator layout="topleft" /> - <menu_item_call name="show_blocked_list" label="Show Blocked Residents & Objects"> - <menu_item_call.on_click function="People.Recent.ViewSort.Action" userdata="panel_block_list_sidetray" /> - </menu_item_call> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 1aa55acf2d..c805b6db42 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -218,17 +218,27 @@ label="Communicate" name="Communicate" tear_off="true"> - <menu_item_check - label="Chat..." + <menu_item_check + label="Conversations..." + name="Conversations"> + <menu_item_check.on_check + function="Floater.IsOpen" + parameter="im_container" /> + <menu_item_check.on_click + function="Floater.ToggleOrBringToFront" + parameter="im_container" /> + </menu_item_check> + <menu_item_check + label="Nearby Chat..." name="Nearby Chat" shortcut="control|H" use_mac_ctrl="true"> <menu_item_check.on_check function="Floater.Visible" - parameter="chat_bar" /> + parameter="nearby_chat" /> <menu_item_check.on_click function="Floater.Toggle" - parameter="chat_bar" /> + parameter="nearby_chat" /> </menu_item_check> <menu_item_check label="Speak" @@ -244,15 +254,16 @@ parameter="speak" /> </menu_item_check> <menu_item_check - label="Voice settings..." - name="Nearby Voice"> + label="Conversations Log..." + name="ConversationsLog"> <menu_item_check.on_check function="Floater.Visible" - parameter="voice_controls" /> + parameter="conversation" /> <menu_item_check.on_click function="Floater.Toggle" - parameter="voice_controls" /> + parameter="conversation" /> </menu_item_check> + <menu_item_separator/> <menu_item_check label="Voice morphing..." name="ShowVoice" @@ -304,7 +315,8 @@ label="Block List" name="Block List"> <menu_item_call.on_click - function="Communicate.BlockList" /> + function="SideTray.PanelPeopleTab" + parameter="blocked_panel" /> </menu_item_call> </menu> <menu diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index c785cee856..4410f41e29 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -3048,6 +3048,7 @@ Would you like to trust this authority? icon="alertmodal.tga" name="GrantedModifyRights" persist="true" + log_to_im="true" type="notify"> [NAME] has given you permission to edit their objects. </notification> @@ -3056,6 +3057,7 @@ Would you like to trust this authority? icon="alertmodal.tga" name="RevokedModifyRights" persist="true" + log_to_im="true" type="notify"> Your privilege to modify [NAME]'s objects has been revoked </notification> @@ -4240,6 +4242,8 @@ Are you sure you want to change the Estate Covenant? <notification icon="notifytip.tga" name="RegionEntryAccessBlocked_Notify" + log_to_im="false" + log_to_chat="true" type="notifytip"> <tag>fail</tag> The region you're trying to visit contains [REGIONMATURITY] content, but your current preferences are set to exclude [REGIONMATURITY] content. @@ -4248,6 +4252,8 @@ The region you're trying to visit contains [REGIONMATURITY] content, but your cu <notification icon="notifytip.tga" name="RegionEntryAccessBlocked_NotifyAdultsOnly" + log_to_im="false" + log_to_chat="true" type="notifytip"> <tag>fail</tag> The region you're trying to visit contains [REGIONMATURITY] content, which is accessible to adults only. @@ -4319,6 +4325,8 @@ The region you're trying to visit contains [REGIONMATURITY] content, but your cu <notification icon="notifytip.tga" name="TeleportEntryAccessBlocked_Notify" + log_to_im="false" + log_to_chat="true" type="notifytip"> <unique> <context>REGIONMATURITY</context> @@ -4330,6 +4338,8 @@ The region you're trying to visit contains [REGIONMATURITY] content, but your cu <notification icon="notifytip.tga" name="TeleportEntryAccessBlocked_NotifyAdultsOnly" + log_to_im="false" + log_to_chat="true" type="notifytip"> <unique> <context>REGIONMATURITY</context> @@ -4450,6 +4460,8 @@ You won't receive any more notifications that you're about to visit a region wit <notification icon="notifytip.tga" name="LandClaimAccessBlocked_Notify" + log_to_im="false" + log_to_chat="true" type="notifytip"> The land you're trying to claim contains [REGIONMATURITY] content, but your current preferences are set to exclude [REGIONMATURITY] content. <tag>fail</tag> @@ -4458,6 +4470,8 @@ You won't receive any more notifications that you're about to visit a region wit <notification icon="notifytip.tga" name="LandClaimAccessBlocked_NotifyAdultsOnly" + log_to_im="false" + log_to_chat="true" type="notifytip"> <tag>fail</tag> The land you're trying to claim contains [REGIONMATURITY] content, which is accessible to adults only. @@ -4515,6 +4529,8 @@ You won't receive any more notifications that you're about to visit a region wit <notification icon="notifytip.tga" name="LandBuyAccessBlocked_Notify" + log_to_im="false" + log_to_chat="true" type="notifytip"> The land you're trying to buy contains [REGIONMATURITY] content, but your current preferences are set to exclude [REGIONMATURITY] content. <tag>fail</tag> @@ -4523,6 +4539,8 @@ You won't receive any more notifications that you're about to visit a region wit <notification icon="notifytip.tga" name="LandBuyAccessBlocked_NotifyAdultsOnly" + log_to_im="false" + log_to_chat="true" type="notifytip"> <tag>fail</tag> The land you're trying to buy contains [REGIONMATURITY] content, which is accessible to adults only. @@ -4968,6 +4986,20 @@ Go to your [http://secondlife.com/account/ Dashboard] to see your account histor <notification icon="alertmodal.tga" + name="ConfirmAddingChatParticipants" + type="alertmodal"> + <unique/> +When you add a person to an existing conversation, a new conversation will be created. All participants will receive new conversation notifications. + <tag>confirm</tag> + <usetemplate + ignoretext="Confirm adding chat paticipants" + name="okcancelignore" + notext="Cancel" + yestext="Ok"/> + </notification> + + <notification + icon="alertmodal.tga" name="ConfirmQuit" type="alertmodal"> <unique/> @@ -5464,6 +5496,8 @@ The string [STRING_NAME] is missing from strings.xml <notification icon="notifytip.tga" name="IMSystemMessageTip" + log_to_im="true" + log_to_chat="false" type="notifytip"> [MESSAGE] </notification> @@ -5507,18 +5541,14 @@ Topic: [SUBJECT], Message: [MESSAGE] <notification icon="notifytip.tga" - name="FriendOnline" + name="FriendOnlineOffline" + log_to_chat="false" type="notifytip"> <tag>friendship</tag> -<nolink>[NAME]</nolink> is Online - </notification> - - <notification - icon="notifytip.tga" - name="FriendOffline" - type="notifytip"> - <tag>friendship</tag> -<nolink>[NAME]</nolink> is Offline +<nolink>[NAME]</nolink> is [STATUS] + <unique combine="cancel_old"> + <context>NAME</context> + </unique> </notification> <notification @@ -5762,6 +5792,8 @@ You don't have permission to copy this. <notification icon="notifytip.tga" name="InventoryAccepted" + log_to_im="true" + log_to_chat="false" type="notifytip"> [NAME] received your inventory offer. </notification> @@ -5769,6 +5801,8 @@ You don't have permission to copy this. <notification icon="notifytip.tga" name="InventoryDeclined" + log_to_im="true" + log_to_chat="false" type="notifytip"> [NAME] declined your inventory offer. </notification> @@ -5850,6 +5884,7 @@ Please select at least one type of content to search (General, Moderate, or Adul <notification icon="notify.tga" name="PaymentReceived" + log_to_im="true" persist="true" type="notify"> <tag>funds</tag> @@ -5859,6 +5894,7 @@ Please select at least one type of content to search (General, Moderate, or Adul <notification icon="notify.tga" name="PaymentSent" + log_to_im="true" persist="true" type="notify"> <tag>funds</tag> @@ -6003,6 +6039,7 @@ The objects on the selected parcel that are NOT owned by you have been returned <notification icon="notify.tga" name="ServerObjectMessage" + log_to_im="true" persist="true" type="notify"> Message from [NAME]: @@ -6421,6 +6458,7 @@ Your object named <nolink>[OBJECTFROMNAME]</nolink> has given you th <notification icon="notify.tga" name="UserGiveItem" + log_to_im ="true" type="offer"> [NAME_SLURL] has given you this [OBJECTTYPE]: [ITEM_SLURL] @@ -6476,6 +6514,8 @@ Your object named <nolink>[OBJECTFROMNAME]</nolink> has given you th <notification icon="notify.tga" name="TeleportOffered" + log_to_im="true" + log_to_chat="false" type="offer"> [NAME_SLURL] has offered to teleport you to their location: @@ -6497,6 +6537,8 @@ Your object named <nolink>[OBJECTFROMNAME]</nolink> has given you th <notification icon="notify.tga" name="TeleportOffered_MaturityExceeded" + log_to_im="true" + log_to_chat="false" type="offer"> [NAME_SLURL] has offered to teleport you to their location: @@ -6520,6 +6562,8 @@ This region contains [REGION_CONTENT_MATURITY] content, but your current prefere <notification icon="notify.tga" name="TeleportOffered_MaturityBlocked" + log_to_im="true" + log_to_chat="false" type="notifytip"> [NAME_SLURL] has offered to teleport you to their location: @@ -6533,6 +6577,9 @@ However, this region contains content accessible to adults only. <notification icon="notify.tga" name="TeleportOfferSent" + log_to_im="true" + log_to_chat="false" + show_toast="false" type="offer"> Teleport offer sent to [TO_NAME] </notification> @@ -6560,6 +6607,7 @@ However, this region contains content accessible to adults only. <notification icon="notify.tga" name="OfferFriendship" + log_to_im="true" type="offer"> <tag>friendship</tag> <tag>confirm</tag> @@ -6583,6 +6631,8 @@ However, this region contains content accessible to adults only. <notification icon="notify.tga" name="FriendshipOffered" + log_to_im="true" + show_toast="false" type="offer"> <tag>friendship</tag> You have offered friendship to [TO_NAME] @@ -6612,6 +6662,7 @@ However, this region contains content accessible to adults only. <notification icon="notify.tga" name="FriendshipAccepted" + log_to_im="true" type="offer"> <tag>friendship</tag> <nolink>[NAME]</nolink> accepted your friendship offer. @@ -6620,6 +6671,7 @@ However, this region contains content accessible to adults only. <notification icon="notify.tga" name="FriendshipDeclined" + log_to_im="true" persist="true" type="notify"> <tag>friendship</tag> @@ -6629,6 +6681,8 @@ However, this region contains content accessible to adults only. <notification icon="notify.tga" name="FriendshipAcceptedByMe" + log_to_im="true" + show_toast="false" type="offer"> <tag>friendship</tag> Friendship offer accepted. @@ -6637,6 +6691,8 @@ Friendship offer accepted. <notification icon="notify.tga" name="FriendshipDeclinedByMe" + log_to_im="true" + show_toast="false" type="offer"> <tag>friendship</tag> Friendship offer declined. @@ -6686,6 +6742,7 @@ If you stay in this region you will be logged out. <notification icon="notify.tga" name="LoadWebPage" + show_toast="false" type="notify"> Load web page [URL]? @@ -6788,6 +6845,7 @@ It is rare that such a request is legitimate. Do not allow access if you do not <notification icon="notify.tga" name="ScriptDialog" + show_toast="false" type="notify"> [NAME]'s '<nolink>[TITLE]</nolink>' [MESSAGE] @@ -6806,6 +6864,7 @@ It is rare that such a request is legitimate. Do not allow access if you do not <notification icon="notify.tga" name="ScriptDialogGroup" + show_toast="false" type="notify"> <tag>group</tag> [GROUPNAME]'s '<nolink>[TITLE]</nolink>' diff --git a/indra/newview/skins/default/xui/en/panel_adhoc_control_panel.xml b/indra/newview/skins/default/xui/en/panel_adhoc_control_panel.xml deleted file mode 100644 index d68fa6ca6c..0000000000 --- a/indra/newview/skins/default/xui/en/panel_adhoc_control_panel.xml +++ /dev/null @@ -1,95 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<panel - border="false" - follows="all" - height="215" - name="panel_im_control_panel" - width="150"> - <layout_stack - mouse_opaque="false" - border_size="0" - clip="false" - follows="all" - height="215" - layout="topleft" - left="3" - name="vertical_stack" - orientation="vertical" - top="0" - width="147"> - <layout_panel - auto_resize="true" - follows="top|left" - height="130" - layout="topleft" - left="0" - min_height="0" - mouse_opaque="false" - width="147" - top="0" - name="speakers_list_panel"> - <avatar_list - color="DkGray2" - follows="all" - height="130" - ignore_online_status="true" - layout="topleft" - name="speakers_list" - opaque="false" - show_info_btn="true" - show_profile_btn="false" - show_speaking_indicator="false" - width="147" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="130" - name="call_btn_panel" - visible="false"> - <button - follows="all" - height="20" - label="Call" - name="call_btn" - width="130" - top="0" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="130" - name="end_call_btn_panel" - visible="false"> - <button - follows="all" - height="20" - label="Leave Call" - name="end_call_btn" - top="0"/> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="130" - name="voice_ctrls_btn_panel" - visible="false"> - <button - follows="all" - height="20" - label="Voice Controls" - name="voice_ctrls_btn" - top="0" - use_ellipses="true" /> - </layout_panel> - </layout_stack> -</panel> diff --git a/indra/newview/skins/default/xui/en/panel_block_list_sidetray.xml b/indra/newview/skins/default/xui/en/panel_block_list_sidetray.xml index 7c67fd7f83..53d0252215 100644 --- a/indra/newview/skins/default/xui/en/panel_block_list_sidetray.xml +++ b/indra/newview/skins/default/xui/en/panel_block_list_sidetray.xml @@ -4,88 +4,99 @@ follows="left|top|right|bottom" height="305" layout="topleft" + left="0" name="block_list_panel" help_topic="blocked_list" min_height="350" min_width="240" - width="280"> - <button - follows="top|left" - height="24" - image_hover_unselected="BackButton_Over" - image_pressed="BackButton_Press" - image_unselected="BackButton_Off" - layout="topleft" - name="back" - left="4" - tab_stop="false" - top="1" - width="30"/> - <text - follows="top|left|right" - font="SansSerifLargeBold" - height="20" - layout="topleft" - left_pad="10" - name="title_text" - text_color="White" - top="5" - width="250"> - Block List - </text> - <scroll_list + width="323"> + <panel + follows="left|top|right" + height="27" + label="bottom_panel" + layout="topleft" + left="0" + name="blocked_buttons_panel" + right="-1" + top="0"> + <filter_editor + follows="left|top|right" + height="23" + layout="topleft" + left="6" + label="Filter" + max_length_chars="300" + name="blocked_filter_input" + text_color="Black" + text_pad_left="10" + top="4" + width="177" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="OptionsMenu_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="8" + menu_filename="menu_people_blocked_gear.xml" + menu_position="bottomleft" + name="blocked_gear_btn" + tool_tip="Actions on selected person or object" + top="3" + width="31" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + menu_filename="menu_people_blocked_view.xml" + menu_position="bottomleft" + name="view_btn" + tool_tip="Sort options" + top_delta="0" + width="31" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="AddItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + menu_filename="menu_people_blocked_plus.xml" + menu_position="bottomleft" + name="plus_btn" + tool_tip="Pick a Resident or an object to block" + top_delta="0" + width="31"/> + <button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="TrashItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + left_pad="2" + layout="topleft" + name="unblock_btn" + tool_tip="Remove Resident or object from blocked list" + top_delta="0" + width="31"/> + </panel> + <block_list follows="all" - height="190" + height="273" layout="topleft" - left="5" + left="3" name="blocked" tool_tip="List of currently blocked Residents" - top="30" - width="270"> - <scroll_list.columns - name="item_name" /> - <scroll_list.columns - name="item_type" - width="96" /> - </scroll_list> - <button - follows="left|bottom" - height="23" - label="Block person" - layout="topleft" - left_delta="0" - name="Block resident..." - tool_tip="Pick a Resident to block" - top_pad="4" - width="210"> - <button.commit_callback - function="Block.ClickPick" /> - </button> - <button - follows="left|bottom" - height="23" - label="Block object by name" - layout="topleft" - left_delta="0" - name="Block object by name..." - tool_tip="Pick an object to block by name" - top_pad="4" - width="210" > - <button.commit_callback - function="Block.ClickBlockByName" /> - </button> - <button - enabled="false" - follows="left|bottom" - height="23" - label="Unblock" - layout="topleft" - left_delta="0" - name="Unblock" - tool_tip="Remove Resident or object from blocked list" - top_pad="4" - width="210" > - <button.commit_callback - function="Block.ClickRemove" /> - </button> + top="31" + right="-1"/> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_blocked_list_item.xml b/indra/newview/skins/default/xui/en/panel_blocked_list_item.xml new file mode 100644 index 0000000000..752321b949 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_blocked_list_item.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + follows="top|right|left" + height="23" + layout="topleft" + left="0" + name="blocked_list_item" + top="0" + width="380"> + <icon + height="24" + follows="top|right|left" + image_name="ListItem_Select" + layout="topleft" + left="0" + name="selected_icon" + top="0" + visible="false" + width="380" /> + <icon + follows="top|right|left" + height="24" + image_name="ListItem_Over" + layout="topleft" + left="0" + name="hovered_icon" + top="0" + visible="false" + width="380" /> + <avatar_icon + default_icon_name="Generic_Person" + follows="top|left" + height="20" + layout="topleft" + left="5" + mouse_opaque="true" + top="2" + visible="false" + width="20" /> + <group_icon + default_icon_name="Generic_Group" + follows="top|left" + height="20" + layout="topleft" + left="5" + mouse_opaque="true" + top="2" + visible="false" + width="20" /> + <icon + follows="top|left" + height="16" + image_name="Inv_Object" + layout="topleft" + left="7" + name="object_icon" + top="4" + visible="false" + width="16" /> + <text + follows="left|right" + font="SansSerifSmall" + height="15" + layout="topleft" + left_pad="5" + name="item_name" + parse_urls="false" + top="6" + use_ellipses="true" + width="180" /> +</panel>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/panel_bottomtray_lite.xml b/indra/newview/skins/default/xui/en/panel_bottomtray_lite.xml index f4722b05d6..27a27473d8 100644 --- a/indra/newview/skins/default/xui/en/panel_bottomtray_lite.xml +++ b/indra/newview/skins/default/xui/en/panel_bottomtray_lite.xml @@ -46,7 +46,7 @@ follows="left|right" top="4" width="310" - name="chat_bar" + name="nearby_chat" mouse_opaque="false"/> </layout_panel> <layout_panel diff --git a/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml b/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml new file mode 100644 index 0000000000..56056ed560 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + follows="left|top|right" + height="24" + layout="topleft" + name="conversation_list_item" + mouse_opaque="false" + width="120"> + <avatar_icon + follows="top|left" + height="20" + default_icon_name="Generic_Person" + layout="topleft" + left="5" + top="2" + visible="false" + width="20" /> + <group_icon + follows="top|left" + height="20" + default_icon_name="Generic_Group" + layout="topleft" + left="5" + top="2" + visible="false" + width="20" /> + <icon + follows="top|left" + height="20" + image_name="Nearby_chat_icon" + layout="topleft" + left="5" + name="nearby_chat_icon" + top="2" + visible="false" + width="20"/> + <layout_stack + animate="false" + follows="all" + height="24" + layout="topleft" + left="30" + mouse_opaque="false" + name="conversation_item_stack" + orientation="horizontal" + top="0" + width="90"> + <layout_panel + auto_resize="false" + user_resize="false" + height="24" + mouse_opaque="false" + name="call_icon_panel" + visible="false" + width="20"> + <icon + height="20" + follows="top|right|left" + image_name="Conv_toolbar_open_call" + layout="topleft" + left="0" + name="selected_icon" + top="2" + width="20" /> + </layout_panel> + <layout_panel + auto_resize="true" + user_resize="false" + height="24" + mouse_opaque="false" + name="conversation_title_panel" + width="70"> + <text + follows="left|top|right" + font="SansSerifSmall" + height="15" + layout="topleft" + left="5" + name="conversation_title" + parse_urls="false" + top="6" + use_ellipses="true" + value="(loading)" + width="35" /> + <output_monitor + auto_update="true" + follows="top|right" + draw_border="false" + height="16" + layout="topleft" + left_pad="5" + mouse_opaque="true" + name="speaking_indicatorn" + visible="false" + width="20" /> + </layout_panel> + </layout_stack> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_conversation_log_list_item.xml b/indra/newview/skins/default/xui/en/panel_conversation_log_list_item.xml new file mode 100644 index 0000000000..78d4c174d2 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_conversation_log_list_item.xml @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + follows="top|right|left" + height="23" + layout="topleft" + left="0" + name="conversation_log_list_item" + top="0" + width="380"> + <icon + height="24" + follows="top|right|left" + image_name="ListItem_Select" + layout="topleft" + left="0" + name="selected_icon" + top="0" + visible="false" + width="380" /> + <icon + follows="top|right|left" + height="24" + image_name="ListItem_Over" + layout="topleft" + left="0" + name="hovered_icon" + top="0" + visible="false" + width="380" /> + <icon + follows="top|left" + height="20" + layout="topleft" + left="5" + image_name="Conv_toolbar_open_call" + mouse_opaque="true" + name="voice_session_icon" + tool_tip="Included a voice conversation" + top="2" + visible="false" + width="20" /> + <icon + follows="top|left" + height="20" + layout="topleft" + left="5" + image_name="Conv_log_inbox" + mouse_opaque="false" + name="unread_ims_icon" + tool_tip="Messages arrived while you were logged out" + top="2" + visible="false" + width="20" /> + <avatar_icon + default_icon_name="Generic_Person" + follows="top|left" + height="20" + layout="topleft" + left_pad="5" + mouse_opaque="true" + top="2" + visible="false" + width="20" /> + <group_icon + default_icon_name="Generic_Group" + follows="top|left" + height="20" + layout="topleft" + mouse_opaque="true" + top="2" + visible="false" + width="20" /> + <text + follows="left|right" + font="SansSerifSmall" + height="15" + layout="topleft" + left_pad="5" + name="conversation_name" + parse_urls="false" + top="6" + use_ellipses="true" + width="180" /> + <text + follows="right" + font="SansSerifSmall" + height="15" + layout="topleft" + left_pad="5" + name="date_time" + parse_urls="false" + top="6" + use_ellipses="true" + width="110"/> + <button + name="delete_btn" + tool_tip="Remove this entry" + layout="topleft" + follows="top|right" + image_unselected="Conv_toolbar_close" + image_selected="Conv_toolbar_close" + top="5" + left_pad="0" + height="14" + width="14" + tab_stop="false"/> +</panel>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/panel_group_control_panel.xml b/indra/newview/skins/default/xui/en/panel_group_control_panel.xml deleted file mode 100644 index ad10e53a4e..0000000000 --- a/indra/newview/skins/default/xui/en/panel_group_control_panel.xml +++ /dev/null @@ -1,109 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<panel - border="false" - follows="all" - height="238" - name="panel_im_control_panel" - width="150"> - <layout_stack - mouse_opaque="false" - border_size="0" - clip="false" - follows="all" - height="238" - layout="topleft" - left="5" - name="vertical_stack" - orientation="vertical" - top="0" - width="145"> - <layout_panel - auto_resize="true" - follows="top|left" - height="100" - layout="topleft" - min_height="0" - mouse_opaque="false" - width="145" - top="0" - name="speakers_list_panel"> - <avatar_list - color="DkGray2" - follows="all" - height="100" - ignore_online_status="true" - layout="topleft" - name="speakers_list" - opaque="false" - show_info_btn="true" - show_profile_btn="false" - show_speaking_indicator="false" - width="145" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="28" - layout="topleft" - min_height="28" - width="130" - name="group_info_btn_panel"> - <button - follows="left|right|bottom" - height="23" - label="Group Profile" - name="group_info_btn" - use_ellipses="true" - top="5" - width="130" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="28" - layout="topleft" - min_height="28" - width="130" - name="call_btn_panel"> - <button - follows="all" - height="23" - label="Call Group" - name="call_btn" - use_ellipses="true" - width="130" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="28" - layout="topleft" - min_height="28" - width="130" - name="end_call_btn_panel" - visible="false"> - <button - follows="all" - height="23" - label="Leave Call" - name="end_call_btn" - use_ellipses="true" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="28" - layout="topleft" - min_height="28" - width="130" - name="voice_ctrls_btn_panel" - visible="false"> - <button - follows="all" - height="23" - label="Open Voice Controls" - name="voice_ctrls_btn" - use_ellipses="true" /> - </layout_panel> - </layout_stack> -</panel> diff --git a/indra/newview/skins/default/xui/en/panel_group_invite.xml b/indra/newview/skins/default/xui/en/panel_group_invite.xml index cd834b61ce..124c0596c3 100644 --- a/indra/newview/skins/default/xui/en/panel_group_invite.xml +++ b/indra/newview/skins/default/xui/en/panel_group_invite.xml @@ -19,6 +19,10 @@ name="already_in_group"> Some Residents you chose are already in the group, and so were not sent an invitation. </panel.string> + <panel.string + name="invite_selection_too_large"> + Group Invitations not sent: too many Residents selected. Group Invitations are limited to 100 per request. + </panel.string> <text type="string" length="1" diff --git a/indra/newview/skins/default/xui/en/panel_im_control_panel.xml b/indra/newview/skins/default/xui/en/panel_im_control_panel.xml deleted file mode 100644 index 8fcd6ccbaf..0000000000 --- a/indra/newview/skins/default/xui/en/panel_im_control_panel.xml +++ /dev/null @@ -1,166 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<panel - border="false" - height="300" - name="panel_im_control_panel" - width="150"> - <avatar_icon - follows="left|top" - height="105" - left_delta="20" - name="avatar_icon" - top="-5" - width="114"/> - <layout_stack - mouse_opaque="false" - border_size="0" - clip="false" - follows="all" - height="183" - layout="topleft" - left="5" - name="button_stack" - orientation="vertical" - top_pad="5" - width="145"> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="20" - layout="topleft" - left="2" - min_height="20" - width="140" - name="view_profile_btn_panel" - top="0" > - <button - follows="left|top|right" - height="23" - label="Profile" - name="view_profile_btn" - top="0" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="add_friend_btn_panel"> - <button - follows="left|top|right" - height="23" - label="Add Friend" - name="add_friend_btn" - top="5" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="teleport_btn_panel"> - <button - auto_resize="false" - follows="left|top|right" - height="23" - label="Teleport" - name="teleport_btn" - tool_tip = "Offer to teleport this person" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="share_btn_panel"> - <button - auto_resize="true" - follows="left|top|right" - height="23" - label="Share" - name="share_btn" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="pay_btn_panel"> - <button - auto_resize="true" - follows="left|top|right" - height="23" - label="Pay" - name="pay_btn" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="call_btn_panel"> - <button - follows="left|top|right" - height="23" - label="Call" - name="call_btn" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="end_call_btn_panel" - visible="false"> - <button - follows="left|top|right" - height="23" - label="End Call" - name="end_call_btn" - width="140" /> - </layout_panel> - <layout_panel - auto_resize="false" - follows="top|left|right" - height="25" - layout="topleft" - min_height="25" - width="140" - name="voice_ctrls_btn_panel" - visible="false"> - <button - follows="left|top|right" - height="23" - label="Voice Controls" - name="voice_ctrls_btn" - width="140" /> - </layout_panel> - <layout_panel - mouse_opaque="false" - auto_resize="true" - follows="top|left" - height="0" - layout="topleft" - min_height="0" - width="140" - name="spacer"/> - </layout_stack> -</panel> diff --git a/indra/newview/skins/default/xui/en/panel_inbox_inventory.xml b/indra/newview/skins/default/xui/en/panel_inbox_inventory.xml index 413e22e444..433a3181cd 100644 --- a/indra/newview/skins/default/xui/en/panel_inbox_inventory.xml +++ b/indra/newview/skins/default/xui/en/panel_inbox_inventory.xml @@ -2,7 +2,7 @@ <inbox_inventory_panel accepts_drag_and_drop="false" name="inventory_inbox" - start_folder="Received Items" + start_folder.type="inbox" follows="all" layout="topleft" top="0" left="0" height="165" width="308" top_pad="0" diff --git a/indra/newview/skins/default/xui/en/panel_landmarks.xml b/indra/newview/skins/default/xui/en/panel_landmarks.xml index 2a5933e3e9..67a09949ce 100644 --- a/indra/newview/skins/default/xui/en/panel_landmarks.xml +++ b/indra/newview/skins/default/xui/en/panel_landmarks.xml @@ -35,7 +35,9 @@ left="0" mouse_opaque="true" name="favorites_list" - start_folder="Favorites" + scroll.hide_scrollbar="true" + folder_view.use_ellipses="true" + start_folder.name="Favorites" width="307"/> </accordion_tab> <accordion_tab @@ -51,7 +53,9 @@ left="0" mouse_opaque="true" name="landmarks_list" - start_folder="Landmarks" + scroll.hide_scrollbar="true" + folder_view.use_ellipses="true" + start_folder.name="Landmarks" width="307"/> </accordion_tab> <accordion_tab @@ -67,7 +71,9 @@ left="0" mouse_opaque="true" name="my_inventory_list" - start_folder="My Inventory" + scroll.hide_scrollbar="true" + folder_view.use_ellipses="true" + start_folder.name="My Inventory" width="307"/> </accordion_tab> <accordion_tab @@ -83,7 +89,9 @@ left="0" mouse_opaque="true" name="library_list" - start_folder="LIBRARY" + scroll.hide_scrollbar="true" + folder_view.use_ellipses="true" + start_folder.name="LIBRARY" width="313"/> </accordion_tab> </accordion> diff --git a/indra/newview/skins/default/xui/en/panel_nearby_chat.xml b/indra/newview/skins/default/xui/en/panel_nearby_chat.xml index d683116eb8..4de56b424e 100644 --- a/indra/newview/skins/default/xui/en/panel_nearby_chat.xml +++ b/indra/newview/skins/default/xui/en/panel_nearby_chat.xml @@ -1,20 +1,22 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel follows="all" - height="300" + top="0" + bottom_delta="10" help_topic="nearby_chat" layout="topleft" name="nearby_chat" - width="320"> + width="242" + height="169"> <layout_stack follows="all" - height="295" + height="164" layout="topleft" left="0" name="stack" top="5" orientation="vertical" - width="320"> + width="242"> <layout_panel auto_resize="false" height="26" @@ -23,7 +25,7 @@ name="translate_chat_checkbox_lp" top_delta="0" visible="true" - width="313"> + width="230"> <check_box top="10" control_name="TranslateChat" @@ -33,15 +35,15 @@ layout="topleft" left="5" name="translate_chat_checkbox" - width="300" /> + width="230" /> </layout_panel> <layout_panel auto_resize="true" - height="277" + height="138" left_delta="0" layout="topleft" name="chat_history_lp" - width="318"> + width="242"> <chat_history bg_readonly_color="ChatHistoryBgColor" bg_writeable_color="ChatHistoryBgColor" @@ -49,7 +51,7 @@ layout="topleft" left="5" left_widget_pad="0" - height="272" + height="138" name="chat_history" parse_highlights="true" parse_urls="true" @@ -57,7 +59,7 @@ text_color="ChatHistoryTextColor" text_readonly_color="ChatHistoryTextColor" top="0" - width="313" /> + width="237" /> </layout_panel> </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml b/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml index 6bc9c48729..19143cef89 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 @@ -5,7 +5,7 @@ height="25" layout="topleft" left="0" - name="chat_bar" + name="nearby_chat" top="21" width="308"> <line_editor diff --git a/indra/newview/skins/default/xui/en/panel_outbox_inventory.xml b/indra/newview/skins/default/xui/en/panel_outbox_inventory.xml index a3d39e55af..c80e5b168a 100644 --- a/indra/newview/skins/default/xui/en/panel_outbox_inventory.xml +++ b/indra/newview/skins/default/xui/en/panel_outbox_inventory.xml @@ -1,7 +1,10 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<outbox_inventory_panel +<inventory_panel name="inventory_outbox" - start_folder="Outbox" + start_folder.name="Outbox" + show_empty_message="false" + show_load_status="false" + start_folder.type="outbox" follows="all" layout="topleft" top="0" left="0" height="165" width="308" top_pad="0" @@ -12,6 +15,18 @@ bevel_style="none" show_item_link_overlays="true" tool_tip="Drag and drop items here to prepare them for sale on your storefront" - > - <scroll reserve_scroll_corner="false" /> -</outbox_inventory_panel> + scroll.reserve_scroll_corner="false"> + <folder folder_arrow_image="Folder_Arrow" + folder_indentation="8" + item_height="20" + item_top_pad="4" + selection_image="Rounded_Square" + left_pad="5" + icon_pad="2" + icon_width="16" + text_pad="1" + text_pad_right="4" + arrow_size="12" + max_folder_item_overlap="2"/> + <item allow_open="false"/> +</inventory_panel> diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml index 98c7c49ff4..7433ad828d 100644 --- a/indra/newview/skins/default/xui/en/panel_people.xml +++ b/indra/newview/skins/default/xui/en/panel_people.xml @@ -38,12 +38,6 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M name="no_filtered_friends_msg"> Didn't find what you're looking for? Try [secondlife:///app/search/people/[SEARCH_TERM] Search]. </string> - <string - name="people_filter_label" - value="Filter People" /> - <string - name="groups_filter_label" - value="Filter Groups" /> <!-- *WORKAROUND: for group_list.no_items_msg & group_list.no_filtered_items_msg attributes. They are not defined as translatable in VLT. See EXT-5931 @@ -60,21 +54,9 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M <string name="AltMiniMapToolTipMsg" value="[REGION](Double-click to teleport, shift-drag to pan)"/> - <filter_editor - follows="left|top|right" - height="23" - layout="topleft" - left="10" - label="Filter" - max_length_chars="300" - name="filter_input" - text_color="Black" - text_pad_left="10" - top="3" - width="303" /> <tab_container + bottom="-10" follows="all" - height="383" layout="topleft" left="3" name="tabs" @@ -82,31 +64,120 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M tab_min_width="70" tab_height="30" tab_position="top" - top_pad="10" + top="0" halign="center" - width="319"> - <panel + right="-5"> + +<!-- ================================= NEARBY tab =========================== --> + + <panel background_opaque="true" background_visible="true" bg_alpha_color="DkGray" bg_opaque_color="DkGray" + bottom="-1" follows="all" - height="383" label="NEARBY" layout="topleft" left="0" help_topic="people_nearby_tab" name="nearby_panel" - top="0" - width="313"> + right="-1" + top="0"> + <panel + follows="left|top|right" + height="27" + label="bottom_panel" + layout="topleft" + left="0" + name="nearby_buttons_panel" + right="-1" + top="0"> + <filter_editor + follows="left|top|right" + height="23" + layout="topleft" + left="6" + label="Filter People" + max_length_chars="300" + name="nearby_filter_input" + text_color="Black" + text_pad_left="10" + top="4" + width="178" /> + <button + commit_callback.function="People.Gear" + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="OptionsMenu_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="7" + name="gear_btn" + tool_tip="Actions on selected person" + top="3" + width="31" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + menu_filename="menu_people_nearby_view.xml" + menu_position="bottomleft" + name="nearby_view_btn" + tool_tip="View/sort options" + top_delta="0" + width="31" /> + <button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="AddItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + name="add_friend_btn" + tool_tip="Offer friendship to a resident" + top_delta="0" + width="31"> + <commit_callback + function="People.AddFriend" /> + </button> + <dnd_button + enabled="false" + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="TrashItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + left_pad="2" + layout="topleft" + name="nearby_del_btn" + tool_tip="Remove selected person as a friend" + top_delta="0" + width="31"> + <commit_callback + function="People.DelFriend" /> + </dnd_button> + </panel> <layout_stack clip="false" follows="all" - height="355" + height="410" layout="topleft" + left="0" mouse_opaque="false" orientation="vertical" - width="313"> + right="-1" + top_pad="0"> <layout_panel height="142" layout="topleft" @@ -123,16 +194,16 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M left="3" mouse_opaque="false" name="Net Map" - top="4" - width="305"/> + right="-1" + top="4" /> </layout_panel> <layout_panel height="213" layout="topleft" min_dim="100" mouse_opaque="false" - user_resize="true" - width="313"> + right="-1" + user_resize="true"> <avatar_list allow_select="true" follows="all" @@ -143,84 +214,122 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M keep_one_selected="false" multi_select="true" name="avatar_list" - top="2" - width="306" /> + right="-1" + top="2" /> </layout_panel> </layout_stack> - <panel - background_visible="true" - follows="left|right|bottom" - height="27" - label="bottom_panel" - layout="topleft" - left="3" - name="bottom_panel" - top_pad="0" - width="313"> - <menu_button - follows="bottom|left" - height="25" - image_hover_unselected="Toolbar_Left_Over" - image_overlay="OptionsMenu_Off" - image_selected="Toolbar_Left_Selected" - image_unselected="Toolbar_Left_Off" - layout="topleft" - left="0" - name="nearby_view_sort_btn" - tool_tip="Options" - top="1" - width="31" /> - <button - follows="bottom|left" - height="25" - image_hover_unselected="Toolbar_Middle_Over" - image_overlay="AddItem_Off" - image_selected="Toolbar_Middle_Selected" - image_unselected="Toolbar_Middle_Off" - layout="topleft" - left_pad="1" - name="add_friend_btn" - tool_tip="Add selected Resident to your friends List" - width="31"> - <commit_callback - function="People.addFriend" /> - </button> - <icon - follows="bottom|left|right" - height="25" - image_name="Toolbar_Right_Off" - layout="topleft" - left_pad="1" - name="dummy_icon" - width="243" - /> - </panel> </panel> + +<!-- ================================= FRIENDS tab ========================== --> + <panel background_opaque="true" background_visible="true" bg_alpha_color="DkGray" bg_opaque_color="DkGray" + bottom="-1" follows="all" - height="383" label="MY FRIENDS" layout="topleft" left="0" help_topic="people_friends_tab" name="friends_panel" - top="0" - width="313"> + right="-1" + top="0"> + <panel + follows="left|top|right" + height="27" + label="bottom_panel" + layout="topleft" + left="0" + name="friends_buttons_panel" + right="-1" + top="0"> + <filter_editor + follows="left|top|right" + height="23" + layout="topleft" + left="6" + label="Filter People" + max_length_chars="300" + name="friends_filter_input" + text_color="Black" + text_pad_left="10" + top="4" + width="177" /> + <button + commit_callback.function="People.Gear" + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="OptionsMenu_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="8" + name="gear_btn" + tool_tip="Actions on selected person" + top="3" + width="31" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + menu_filename="menu_people_friends_view.xml" + menu_position="bottomleft" + name="friends_view_btn" + tool_tip="View/sort options" + top_delta="0" + width="31" /> + <button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="AddItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + name="friends_add_btn" + tool_tip="Offer friendship to a resident" + top_delta="0" + width="31"> + <commit_callback + function="People.AddFriendWizard" /> + </button> + <dnd_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="TrashItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + left_pad="2" + layout="topleft" + name="friends_del_btn" + tool_tip="Remove selected person as a friend" + top_delta="0" + width="31"> + <commit_callback + function="People.DelFriend" /> + </dnd_button> + </panel> <accordion background_visible="true" bg_alpha_color="DkGray2" bg_opaque_color="DkGray2" follows="all" - height="356" + height="408" layout="topleft" left="3" name="friends_accordion" - top="0" - width="307"> + right="-2" + top_pad="2"> <accordion_tab layout="topleft" height="172" @@ -257,247 +366,133 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M width="307" /> </accordion_tab> </accordion> - <panel - background_visible="true" - follows="left|right|bottom" - height="27" - label="bottom_panel" - layout="topleft" - left="3" - name="bottom_panel" - top_pad="0" - width="313"> - - <layout_stack - animate="false" - border_size="0" - follows="left|right|bottom" - height="25" - layout="topleft" - orientation="horizontal" - top_pad="1" - left="0" - name="bottom_panel" - width="308"> - <layout_panel - auto_resize="false" - height="25" - layout="topleft" - name="options_gear_btn_panel" - width="32"> - <menu_button - follows="bottom|left" - tool_tip="Show additional options" - height="25" - image_hover_unselected="Toolbar_Left_Over" - image_overlay="OptionsMenu_Off" - image_selected="Toolbar_Left_Selected" - image_unselected="Toolbar_Left_Off" - layout="topleft" - left="0" - name="friends_viewsort_btn" - top="0" - width="31" /> - </layout_panel> - <layout_panel - auto_resize="false" - height="25" - layout="topleft" - name="add_btn_panel" - width="32"> - <button - follows="bottom|left" - height="25" - image_hover_unselected="Toolbar_Middle_Over" - image_overlay="AddItem_Off" - image_selected="Toolbar_Middle_Selected" - image_unselected="Toolbar_Middle_Off" - layout="topleft" - left="0" - name="add_btn" - tool_tip="Offer friendship to a Resident" - top="0" - width="31" /> - </layout_panel> - <layout_panel - auto_resize="true" - height="25" - layout="topleft" - name="dummy_panel" - width="210"> - <icon - follows="bottom|left|right" - height="25" - image_name="Toolbar_Middle_Off" - layout="topleft" - left="0" - top="0" - name="dummy_icon" - width="210" /> - </layout_panel> - <layout_panel - auto_resize="false" - height="25" - layout="topleft" - name="trash_btn_panel" - width="31"> - <dnd_button - follows="bottom|left" - height="25" - image_hover_unselected="Toolbar_Right_Over" - image_overlay="TrashItem_Off" - image_selected="Toolbar_Right_Selected" - image_unselected="Toolbar_Right_Off" - left="0" - layout="topleft" - name="del_btn" - tool_tip="Remove selected person from your Friends list" - top="0" - width="31"/> - </layout_panel> - </layout_stack><!-- - - <button - follows="bottom|left" - tool_tip="Options" - height="25" - image_hover_unselected="Toolbar_Left_Over" - image_overlay="OptionsMenu_Off" - image_selected="Toolbar_Left_Selected" - image_unselected="Toolbar_Left_Off" - layout="topleft" - left="0" - name="friends_viewsort_btn" - top="1" - width="31" /> - <button - follows="bottom|left" - height="25" - image_hover_unselected="Toolbar_Middle_Over" - image_overlay="AddItem_Off" - image_selected="Toolbar_Middle_Selected" - image_unselected="Toolbar_Middle_Off" - layout="topleft" - left_pad="1" - name="add_btn" - tool_tip="Offer friendship to a Resident" - width="31" /> - <icon - follows="bottom|left|right" - height="25" - image_name="Toolbar_Middle_Off" - layout="topleft" - left_pad="1" - name="dummy_icon" - width="209" - /> - <button - follows="bottom|left" - height="25" - image_hover_unselected="Toolbar_Right_Over" - image_overlay="TrashItem_Off" - image_selected="Toolbar_Right_Selected" - image_unselected="Toolbar_Right_Off" - layout="topleft" - left_pad="1" - name="del_btn" - tool_tip="Remove selected person from your Friends list" - width="31" /> - --></panel> <text follows="all" height="450" left="13" name="no_friends_help_text" - top="10" - width="293" + right="-13" + top="37" wrap="true" /> </panel> + +<!-- ================================= GROUPS tab =========================== --> + <panel background_opaque="true" background_visible="true" bg_alpha_color="DkGray" bg_opaque_color="DkGray" + bottom="-1" follows="all" - height="383" label="MY GROUPS" layout="topleft" left="0" help_topic="people_groups_tab" name="groups_panel" - top="0" - width="313"> + right="-1" + top="0"> <!-- *NOTE: no_groups_msg & group_list attributes are not defined as translatable in VLT. See EXT-5931 Values are set from appropriate strings at the top of file via LLPeoplePanel::postBuild() --> - <group_list - allow_select="true" - follows="all" - height="356" - layout="topleft" - left="3" - name="group_list" - top="0" - width="307" /> <panel - background_visible="true" - follows="left|right|bottom" + follows="left|top|right" height="27" label="bottom_panel" layout="topleft" left="0" - name="bottom_panel" - top_pad="0" - width="313"> - <menu_button - follows="bottom|left" - tool_tip="Options" - height="25" - image_hover_unselected="Toolbar_Left_Over" - image_overlay="OptionsMenu_Off" - image_selected="Toolbar_Left_Selected" - image_unselected="Toolbar_Left_Off" - layout="topleft" - left="3" - name="groups_viewsort_btn" - top="1" - width="31" /> - <button - follows="bottom|left" + name="groups_buttons_panel" + right="-1" + top="0"> + <filter_editor + follows="left|top|right" + height="23" + layout="topleft" + left="6" + label="Filter Groups" + max_length_chars="300" + name="groups_filter_input" + text_color="Black" + text_pad_left="10" + top="4" + width="177" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="OptionsMenu_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="8" + name="groups_gear_btn" + tool_tip="Actions on selected group" + top="3" + width="31" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + menu_filename="menu_people_groups_view.xml" + menu_position="bottomleft" + name="groups_view_btn" + tool_tip="View/sort options" + top_delta="0" + width="31" /> + <menu_button + follows="right" height="25" image_hover_unselected="Toolbar_Middle_Over" image_overlay="AddItem_Off" image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - left_pad="1" + left_pad="2" + menu_filename="menu_group_plus.xml" + menu_position="bottomleft" name="plus_btn" tool_tip="Join group/Create new group" - width="31" /> - <button - follows="bottom|left" + top_delta="0" + width="31"> + <validate_callback + function="People.Group.Plus.Validate" /> + </menu_button> + <dnd_button + follows="right" height="25" image_hover_unselected="Toolbar_Middle_Over" - image_overlay="Activate_Checkmark" + image_overlay="TrashItem_Off" image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" + left_pad="2" layout="topleft" - left_pad="1" - name="activate_btn" - tool_tip="Activate selected group" - width="31" /> - <icon - follows="bottom|left|right" - height="25" - image_name="Toolbar_Right_Off" - layout="topleft" - left_pad="1" - name="dummy_icon" - width="212" - /> + name="minus_btn" + tool_tip="Leave selected group" + top_delta="0" + width="31"> + <commit_callback + function="People.Group.Minus" /> + </dnd_button> </panel> + <group_list + allow_select="true" + follows="all" + height="406" + layout="topleft" + left="3" + name="group_list" + right="-2" + top_pad="4" /> </panel> + +<!-- ================================= RECENT tab =========================== --> + <panel background_opaque="true" background_visible="true" @@ -510,265 +505,133 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M left="0" help_topic="people_recent_tab" name="recent_panel" - top="0" - width="313"> - <avatar_list - allow_select="true" - follows="all" - height="356" - layout="topleft" - left="3" - multi_select="true" - name="avatar_list" - show_last_interaction_time="true" - top="0" - width="307" /> + right="-1" + top="0"> <panel - background_visible="true" - follows="left|right|bottom" + follows="left|top|right" height="27" label="bottom_panel" layout="topleft" - left="3" - name="bottom_panel" - top_pad="0" - width="313"> - <menu_button - follows="bottom|left" - tool_tip="Options" - height="25" - image_hover_unselected="Toolbar_Left_Over" - image_overlay="OptionsMenu_Off" - image_selected="Toolbar_Left_Selected" - image_unselected="Toolbar_Left_Off" - layout="topleft" - name="recent_viewsort_btn" - top="1" - width="31" /> - <button - follows="bottom|left" + left="0" + name="recent_buttons_panel" + right="-1" + top="0"> + <filter_editor + follows="left|top|right" + height="23" + layout="topleft" + left="6" + label="Filter People" + max_length_chars="300" + name="recent_filter_input" + text_color="Black" + text_pad_left="10" + top="4" + width="177" /> + <button + commit_callback.function="People.Gear" + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="OptionsMenu_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="8" + name="gear_btn" + tool_tip="Actions on selected person" + top="3" + width="31" /> + <menu_button + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Conv_toolbar_sort" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + left_pad="2" + menu_filename="menu_people_recent_view.xml" + menu_position="bottomleft" + name="recent_view_btn" + tool_tip="View/sort options" + top_delta="0" + width="31" /> + <button + follows="right" height="25" image_hover_unselected="Toolbar_Middle_Over" image_overlay="AddItem_Off" image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" layout="topleft" - left_pad="1" + left_pad="2" name="add_friend_btn" - tool_tip="Add selected Resident to your friends List" + tool_tip="Offer friendship to a resident" + top_delta="0" width="31"> - <commit_callback - function="People.addFriend" /> - </button> - <icon - follows="bottom|left|right" - height="25" - image_name="Toolbar_Right_Off" - layout="topleft" - left_pad="1" - name="dummy_icon" - width="244" - /> + <commit_callback + function="People.AddFriend" /> + </button> + <dnd_button + enabled="false" + follows="right" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="TrashItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + left_pad="2" + layout="topleft" + name="recent_del_btn" + tool_tip="Remove selected person as a friend" + top_delta="0" + width="31"> + <commit_callback + function="People.DelFriend" /> + </dnd_button> </panel> + <avatar_list + allow_select="true" + follows="all" + height="351" + layout="topleft" + left="3" + multi_select="true" + name="avatar_list" + show_last_interaction_time="true" + right="-2" + top_pad="4" /> </panel> - </tab_container> - <panel - follows="bottom|left|right" - height="23" - layout="topleft" - left="8" - top_pad="4" - name="button_bar" - width="313"> -<!--********************************Profile; IM; Call, Share, Teleport********************************--> - <layout_stack - follows="bottom|left|right" - height="23" - layout="topleft" - name="bottom_bar_ls" - left="0" - orientation="horizontal" - top_pad="0" - width="313"> +<!-- ================================= BLOCKED tab ========================== --> - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left="0" - name="view_profile_btn_lp" - auto_resize="true" - width="68"> - <button - follows="bottom|left|right" - height="23" - label="Profile" - layout="topleft" - left="1" - name="view_profile_btn" - tool_tip="Show picture, groups, and other Residents information" - top="0" - width="67" /> - </layout_panel> - - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left_pad="3" - name="im_btn_lp" - auto_resize="true" - width="41"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="IM" - layout="topleft" - name="im_btn" - tool_tip="Open instant message session" - top="0" - width="40" /> - </layout_panel> - - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left_pad="3" - name="call_btn_lp" - auto_resize="true" - width="52"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="Call" - layout="topleft" - name="call_btn" - tool_tip="Call this Resident" - top="0" - width="51" /> - </layout_panel> - - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left_pad="3" - name="share_btn_lp" - auto_resize="true" - width="66"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="Share" - layout="topleft" - name="share_btn" - tool_tip="Share an inventory item" - top="0" - width="65" /> - </layout_panel> - - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left_pad="3" - name="teleport_btn_lp" - auto_resize="true" - width="77"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="Teleport" - layout="topleft" - name="teleport_btn" - tool_tip="Offer teleport" - top="0" - width="76" /> - </layout_panel> - </layout_stack> - -<!--********************************Group Profile; Group Chat; Group Call buttons************************--> - <layout_stack - follows="bottom|left|right" - height="23" - layout="topleft" - mouse_opaque="false" - name="bottom_bar_ls1" - left="0" - orientation="horizontal" - top="0" - width="313"> - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left="0" - mouse_opaque="false" - name="group_info_btn_lp" - auto_resize="true" - width="108"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="Group Profile" - layout="topleft" - mouse_opaque="false" - name="group_info_btn" - tool_tip="Show group information" - top="0" - width="107" /> - </layout_panel> - - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left_pad="3" - mouse_opaque="false" - name="chat_btn_lp" - auto_resize="true" - width="101"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="Group Chat" - layout="topleft" - mouse_opaque="false" - name="chat_btn" - tool_tip="Open chat session" - top="0" - width="100" /> - </layout_panel> - - <layout_panel - follows="bottom|left|right" - height="23" - layout="bottomleft" - left_pad="3" - mouse_opaque="false" - name="group_call_btn_lp" - auto_resize="true" - width="96"> - <button - follows="bottom|left|right" - left="1" - height="23" - label="Group Call" - layout="topleft" - mouse_opaque="false" - name="group_call_btn" - tool_tip="Call this group" - top="0" - width="95" /> - </layout_panel> - </layout_stack> - </panel> + <panel + background_opaque="true" + background_visible="true" + bg_alpha_color="DkGray" + bg_opaque_color="DkGray" + follows="all" + height="383" + label="BLOCKED" + layout="topleft" + left="0" + help_topic="people_blocked_tab" + name="blocked_panel" + right="-1" + top="0"> + <panel + class="panel_block_list_sidetray" + height="383" + name="panel_block_list_sidetray" + filename="panel_block_list_sidetray.xml" + follows="all" + label="Blocked Residents & Objects" + layout="topleft" + left="0" + font="SansSerifBold" + top="0" + right="-1" /> + </panel> + </tab_container> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml index 27193a984f..c76a3cfaaf 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml @@ -76,15 +76,6 @@ top_pad="5" width="400" /> <check_box - enabled="false" - height="16" - label="Enable plain text IM and chat history" - layout="topleft" - left_delta="0" - name="plain_text_chat_history" - top_pad="5" - width="400" /> - <check_box control_name="UseChatBubbles" follows="left|top" height="16" @@ -95,55 +86,6 @@ name="bubble_text_chat" width="150" /> <text - name="show_ims_in_label" - follows="left|top" - layout="topleft" - left="30" - height="20" - width="170" - top_pad="15"> - Show IMs in: - </text> - <text - name="requires_restart_label" - follows="left|top" - layout="topleft" - top_delta="0" - left="170" - height="20" - width="130" - text_color="White_25"> - (requires restart) - </text> - <radio_group - follows="left|top" - height="30" - left="40" - control_name="ChatWindow" - name="chat_window" - top_pad="0" - tool_tip="Show your Instant Messages in separate floaters, or in one floater with many tabs (Requires restart)" - width="150"> - <radio_item - height="16" - label="Separate Windows" - layout="topleft" - left="0" - name="radio" - value="0" - top="0" - width="150" /> - <radio_item - height="16" - label="Tabs" - layout="topleft" - left_delta="0" - name="radio2" - value="1" - top_pad="5" - width="150" /> - </radio_group> - <text name="disable_toast_label" follows="left|top" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_preferences_colors.xml b/indra/newview/skins/default/xui/en/panel_preferences_colors.xml index 2b22f0d6e3..9e825fe516 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_colors.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_colors.xml @@ -362,7 +362,7 @@ follows="left|top" height="16" increment="0.01" - initial_value="0.8" + initial_value="1.0" layout="topleft" label_width="115" label="Active:" @@ -380,7 +380,7 @@ follows="left|top" height="16" increment="0.01" - initial_value="0.5" + initial_value="0.95" layout="topleft" label_width="115" label="Inactive:" diff --git a/indra/newview/skins/default/xui/en/panel_region_debug.xml b/indra/newview/skins/default/xui/en/panel_region_debug.xml index 4550603134..a4883c21e2 100644 --- a/indra/newview/skins/default/xui/en/panel_region_debug.xml +++ b/indra/newview/skins/default/xui/en/panel_region_debug.xml @@ -194,7 +194,7 @@ <button follows="left|top" height="20" - label="Delay Restart" + label="Cancel Restart" layout="topleft" left_pad="155" name="cancel_restart_btn" diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index d5186e4c1b..01da0a3686 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -384,13 +384,15 @@ Please try logging in again in a minute.</string> <string name="ST_NO_JOINT">Can't find ROOT or JOINT.</string> <!-- Chat --> + <string name="NearbyChatTitle">Nearby chat</string> + <string name="NearbyChatLabel">(Nearby chat)</string> <string name="whisper">whispers:</string> <string name="shout">shouts:</string> <string name="ringing">Connecting to in-world Voice Chat...</string> <string name="connected">Connected</string> <string name="unavailable">Voice not available at your current location</string> <string name="hang_up">Disconnected from in-world Voice Chat</string> - <string name="reconnect_nearby">You will now be reconnected to Nearby Voice Chat</string> + <string name="reconnect_nearby">You will now be reconnected to Nearby Voice Chat</string> <string name="ScriptQuestionCautionChatGranted">'[OBJECTNAME]', an object owned by '[OWNERNAME]', located in [REGIONNAME] at [REGIONPOS], has been granted permission to: [PERMISSIONS].</string> <string name="ScriptQuestionCautionChatDenied">'[OBJECTNAME]', an object owned by '[OWNERNAME]', located in [REGIONNAME] at [REGIONPOS], has been denied permission to: [PERMISSIONS].</string> <string name="AdditionalPermissionsRequestHeader">If you allow access to your account, you will also be allowing the object to:</string> @@ -404,9 +406,10 @@ Please try logging in again in a minute.</string> <string name="AddAndRemoveJoints">Add and remove joints with other objects</string> <string name="ChangePermissions">Change its permissions</string> <string name="TrackYourCamera">Track your camera</string> - <string name="ControlYourCamera">Control your camera</string> - <string name="TeleportYourAgent">Teleport you</string> - <string name="NotConnected">Not Connected</string> + <string name="ControlYourCamera">Control your camera</string> + <string name="NotConnected">Not Connected</string> + <string name="AgentNameSubst">(You)</string> <!-- Substitution for agent name --> + <string name="TeleportYourAgent">Teleport you</string> <!-- Sim Access labels --> <string name="SIM_ACCESS_PG">General</string> @@ -2270,8 +2273,10 @@ Drag folders to this area and click "Send to Marketplace" to list them for sale <string name="InvFolder Gestures">Gestures</string> <string name="InvFolder Favorite">My Favorites</string> <!-- historically default name of the Favorites folder can start from either "f" or "F" letter. - We should localize both of them with the same value --> + Also, it can be written as "Favorite" or "Favorites". + We should localize all variants of them with the same value --> <string name="InvFolder favorite">My Favorites</string> + <string name="InvFolder Favorites">My Favorites</string> <string name="InvFolder Current Outfit">Current Outfit</string> <string name="InvFolder Initial Outfits">Initial Outfits</string> <string name="InvFolder My Outfits">My Outfits</string> @@ -2280,6 +2285,7 @@ Drag folders to this area and click "Send to Marketplace" to list them for sale <!-- are used for Friends and Friends/All folders in Inventory "Calling cards" folder. See EXT-694--> <string name="InvFolder Friends">Friends</string> + <string name="InvFolder Received Items">Received Items</string> <string name="InvFolder All">All</string> <string name="no_attachments">No attachments worn</string> @@ -2579,9 +2585,6 @@ Drag folders to this area and click "Send to Marketplace" to list them for sale <string name="GroupMoneyDebits">Debits</string> <string name="GroupMoneyDate">[weekday,datetime,utc] [mth,datetime,utc] [day,datetime,utc], [year,datetime,utc]</string> - <!-- viewer object --> - <string name="ViewerObjectContents">Contents</string> - <!-- Viewer menu --> <string name="AcquiredItems">Acquired Items</string> <string name="Cancel">Cancel</string> @@ -3377,6 +3380,8 @@ If you continue to receive this message, contact the [SUPPORT_SITE]. <string name="IM_moderator_label">(Moderator)</string> <string name="Saved_message">(Saved [LONG_TIMESTAMP])</string> <string name="IM_unblock_only_groups_friends">To see this message, you must uncheck 'Only friends and groups can call or IM me' in Preferences/Privacy.</string> + <string name="OnlineStatus">Online</string> + <string name="OfflineStatus">Offline</string> <!-- voice calls --> <string name="answered_call">Your call has been answered</string> @@ -3823,6 +3828,7 @@ Try enclosing path to the editor with double quotes. <string name="Command_Avatar_Label">Avatar</string> <string name="Command_Build_Label">Build</string> <string name="Command_Chat_Label">Chat</string> + <string name="Command_Conversations_Label">Conversations</string> <string name="Command_Compass_Label">Compass</string> <string name="Command_Destinations_Label">Destinations</string> <string name="Command_Gestures_Label">Gestures</string> @@ -3849,6 +3855,7 @@ Try enclosing path to the editor with double quotes. <string name="Command_Avatar_Tooltip">Choose a complete avatar</string> <string name="Command_Build_Tooltip">Building objects and reshaping terrain</string> <string name="Command_Chat_Tooltip">Chat with people nearby using text</string> + <string name="Command_Conversations_Tooltip">Converse with everyone</string> <string name="Command_Compass_Tooltip">Compass</string> <string name="Command_Destinations_Tooltip">Destinations of interest</string> <string name="Command_Gestures_Tooltip">Gestures for your avatar</string> diff --git a/indra/newview/skins/default/xui/en/widgets/chat_editor.xml b/indra/newview/skins/default/xui/en/widgets/chat_editor.xml new file mode 100644 index 0000000000..f9facb593a --- /dev/null +++ b/indra/newview/skins/default/xui/en/widgets/chat_editor.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<chat_editor + name="chat_editor" + show_context_menu="true"/> diff --git a/indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml b/indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml new file mode 100755 index 0000000000..0024decd4c --- /dev/null +++ b/indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<conversation_view_participant + folder_arrow_image="Folder_Arrow" + folder_indentation="0" + item_height="24" + item_top_pad="0" + selection_image="Rounded_Square" + mouse_opaque="true" + follows="left|top|right" + left_pad="0" + icon_pad="10" + icon_width="20" + text_pad="7" + text_pad_right="4" + arrow_size="12" + max_folder_item_overlap="2" +> +<avatar_icon + follows="left" + height="20" + default_icon_name="Generic_Person" + layout="topleft" + left="50" + top="2" + width="20" /> +<info_button + follows="right" + height="16" + image_pressed="Info_Press" + image_unselected="Info_Over" + right="-28" + name="info_btn" + width="16" /> +<output_monitor + follows="right" + auto_update="true" + draw_border="false" + height="16" + right="-3" + mouse_opaque="true" + name="speaking_indicator" + visible="true" + width="20" /> +</conversation_view_participant> diff --git a/indra/newview/skins/default/xui/en/widgets/conversation_view_session.xml b/indra/newview/skins/default/xui/en/widgets/conversation_view_session.xml new file mode 100644 index 0000000000..b8c39eec1d --- /dev/null +++ b/indra/newview/skins/default/xui/en/widgets/conversation_view_session.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<conversation_view_session + folder_arrow_image="Folder_Arrow" + folder_indentation="8" + item_height="24" + item_top_pad="4" + selection_image="Rounded_Square" + mouse_opaque="true" + follows="left|top|right" + left_pad="5" + icon_pad="2" + icon_width="16" + text_pad="1" + text_pad_right="4" + arrow_size="12" + max_folder_item_overlap="2"/> diff --git a/indra/newview/skins/default/xui/en/widgets/folder_view_item.xml b/indra/newview/skins/default/xui/en/widgets/folder_view_item.xml index 6fa74f403d..bbd53ccb12 100644 --- a/indra/newview/skins/default/xui/en/widgets/folder_view_item.xml +++ b/indra/newview/skins/default/xui/en/widgets/folder_view_item.xml @@ -7,4 +7,10 @@ selection_image="Rounded_Square" mouse_opaque="true" follows="left|top|right" - /> + left_pad="5" + icon_pad="2" + icon_width="16" + text_pad="1" + text_pad_right="4" + arrow_size="12" + max_folder_item_overlap="2"/> diff --git a/indra/newview/skins/default/xui/en/widgets/inbox_folder_view_folder.xml b/indra/newview/skins/default/xui/en/widgets/inbox_folder_view_folder.xml index 77d8024cb2..590a4730a9 100644 --- a/indra/newview/skins/default/xui/en/widgets/inbox_folder_view_folder.xml +++ b/indra/newview/skins/default/xui/en/widgets/inbox_folder_view_folder.xml @@ -5,7 +5,13 @@ item_height="20" item_top_pad="4" selection_image="Rounded_Square" - > + left_pad="5" + icon_pad="2" + icon_width="16" + text_pad="1" + text_pad_right="4" + arrow_size="12" + max_folder_item_overlap="2"> <new_badge label="New" label_offset_horiz="-1" diff --git a/indra/newview/skins/default/xui/en/widgets/inbox_inventory_panel.xml b/indra/newview/skins/default/xui/en/widgets/inbox_inventory_panel.xml index 830c27bdac..d5b10e7f51 100644 --- a/indra/newview/skins/default/xui/en/widgets/inbox_inventory_panel.xml +++ b/indra/newview/skins/default/xui/en/widgets/inbox_inventory_panel.xml @@ -1,2 +1,3 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<inbox_inventory_panel show_load_status="false" /> +<inbox_inventory_panel show_load_status="false" + start_folder.type="inbox"/> diff --git a/indra/newview/skins/default/xui/en/widgets/outbox_folder_view_folder.xml b/indra/newview/skins/default/xui/en/widgets/outbox_folder_view_folder.xml deleted file mode 100644 index d19c47f54f..0000000000 --- a/indra/newview/skins/default/xui/en/widgets/outbox_folder_view_folder.xml +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<outbox_folder_view_folder - folder_arrow_image="Folder_Arrow" - folder_indentation="8" - item_height="20" - item_top_pad="4" - selection_image="Rounded_Square" - > -</outbox_folder_view_folder> diff --git a/indra/newview/skins/default/xui/en/widgets/outbox_inventory_panel.xml b/indra/newview/skins/default/xui/en/widgets/outbox_inventory_panel.xml deleted file mode 100644 index 3964569da2..0000000000 --- a/indra/newview/skins/default/xui/en/widgets/outbox_inventory_panel.xml +++ /dev/null @@ -1,2 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<outbox_inventory_panel show_empty_message="false" show_load_status="false" /> diff --git a/indra/newview/skins/default/xui/en/widgets/text.xml b/indra/newview/skins/default/xui/en/widgets/text.xml index 134f2d7522..2102074674 100644 --- a/indra/newview/skins/default/xui/en/widgets/text.xml +++ b/indra/newview/skins/default/xui/en/widgets/text.xml @@ -9,6 +9,7 @@ h_pad="0" allow_scroll="false" text_readonly_color="LabelTextColor" + text_tentative_color="TextFgTentativeColor" bg_writeable_color="FloaterDefaultBackgroundColor" use_ellipses="false" bg_visible="false" diff --git a/indra/newview/skins/default/xui/es/floater_chat_bar.xml b/indra/newview/skins/default/xui/es/floater_chat_bar.xml index 2e94805057..02369c9a43 100644 --- a/indra/newview/skins/default/xui/es/floater_chat_bar.xml +++ b/indra/newview/skins/default/xui/es/floater_chat_bar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="chat_bar" title="CHAT"> +<floater name="nearby_chat" title="CHAT"> <panel name="bottom_panel"> <line_editor label="Pulsa aquí para chatear." name="chat_box" tool_tip="Pulsa Enter para decirlo o Ctrl+Enter para gritarlo"/> <button name="show_nearby_chat" tool_tip="Muestra o esconde el registro del chat"/> diff --git a/indra/newview/skins/default/xui/es/panel_nearby_chat_bar.xml b/indra/newview/skins/default/xui/es/panel_nearby_chat_bar.xml index af2b6e920b..e6ca59f912 100644 --- a/indra/newview/skins/default/xui/es/panel_nearby_chat_bar.xml +++ b/indra/newview/skins/default/xui/es/panel_nearby_chat_bar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<panel name="chat_bar"> +<panel name="nearby_chat"> <line_editor label="Pulsa aquí para chatear." name="chat_box" tool_tip="Pulsa Enter para decirlo o Ctrl+Enter para gritarlo"/> <button name="show_nearby_chat" tool_tip="Muestra o esconde el registro del chat"/> </panel> diff --git a/indra/newview/skins/default/xui/fr/floater_about_land.xml b/indra/newview/skins/default/xui/fr/floater_about_land.xml index 49af1a87e1..c86f31b429 100644 --- a/indra/newview/skins/default/xui/fr/floater_about_land.xml +++ b/indra/newview/skins/default/xui/fr/floater_about_land.xml @@ -134,7 +134,7 @@ <text name="DwellText"> Chargement... </text> - <button label="Acheter du terrain" label_selected="Acheter le terrain..." left_delta="60" name="Buy Land..." width="125"/> + <button label="Acheter le terrain" label_selected="Acheter le terrain..." left_delta="60" name="Buy Land..." width="125"/> <button label="Vente Linden" label_selected="Vente Linden..." name="Linden Sale..." tool_tip="Le terrain doit être la propriété d'un résident, avoir un contenu défini et ne pas être aux enchères."/> <button label="Infos sur les scripts" name="Scripts..." width="110"/> <button label="Acheter pour le groupe" label_selected="Acheter pour le groupe..." name="Buy For Group..."/> @@ -377,7 +377,7 @@ Seules les parcelles de grande taille peuvent apparaître dans la recherche. </text> <texture_picker label="" name="snapshot_ctrl" tool_tip="Cliquez pour sélectionner une image"/> <text name="allow_label5"> - Les avatars présents sur d'autres parcelles peuvent voir et chatter avec les avatars présents sur cette parcelle. + Les avatars sur d'autres parcelles peuvent voir et chatter avec les avatars sur cette parcelle. </text> <check_box label="Voir les avatars" name="SeeAvatarsCheck" tool_tip="Permettre aux avatars présents sur d'autres parcelles de voir et chatter avec les avatars présents sur cette parcelle et à vous de les voir et de chatter avec eux."/> <text name="landing_point"> diff --git a/indra/newview/skins/default/xui/fr/floater_camera.xml b/indra/newview/skins/default/xui/fr/floater_camera.xml index f2b0ee8af3..893e389f69 100644 --- a/indra/newview/skins/default/xui/fr/floater_camera.xml +++ b/indra/newview/skins/default/xui/fr/floater_camera.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="camera_floater" title="PARAMÈTRES DE LA CAMÉRA"> +<floater name="camera_floater" title="CONTRÔLE DE LA CAMÉRA"> <floater.string name="rotate_tooltip"> Faire tourner la caméra autour du point central </floater.string> diff --git a/indra/newview/skins/default/xui/fr/floater_chat_bar.xml b/indra/newview/skins/default/xui/fr/floater_chat_bar.xml index 890411d091..7dcb9a280d 100644 --- a/indra/newview/skins/default/xui/fr/floater_chat_bar.xml +++ b/indra/newview/skins/default/xui/fr/floater_chat_bar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="chat_bar" title="CHAT PRÈS DE MOI"> +<floater name="nearby_chat" title="CHAT PRÈS DE MOI"> <panel name="bottom_panel"> <line_editor label="Cliquer ici pour chatter." name="chat_box" tool_tip="Appuyer sur Entrée pour dire, Ctrl+Entrée pour crier"/> <button name="show_nearby_chat" tool_tip="Afficher/masquer le journal de chat près de vous."/> diff --git a/indra/newview/skins/default/xui/fr/floater_edit_day_cycle.xml b/indra/newview/skins/default/xui/fr/floater_edit_day_cycle.xml index 0100419bc5..de1ba220a0 100644 --- a/indra/newview/skins/default/xui/fr/floater_edit_day_cycle.xml +++ b/indra/newview/skins/default/xui/fr/floater_edit_day_cycle.xml @@ -22,13 +22,13 @@ Remarque : si vous changez votre préréglage de nom, un nouveau préréglage sera créé et celui existant restera tel quel. </text> <text name="hint_item1"> - - Cliquez sur un repère pour modifier le réglage du ciel et l'heure associés. + - Modifier un réglage de ciel et d'heure : clic sur le repère </text> <text name="hint_item2"> - - Cliquez sur les repères et faites-les glisser afin de définir les heures de transition. + - Définir les heures de transition : clic et glissement des repères </text> <text name="hint_item3"> - - Déplacez le marqueur en forme de triangle pour afficher un aperçu du cycle du jour. + - Afficher un aperçu du cycle du jour : déplacer le triangle </text> <panel name="day_cycle_slider_panel"> <multi_slider initial_value="0" name="WLTimeSlider"/> @@ -91,11 +91,11 @@ </text> </panel> <text name="WLCurKeyPresetText"> - Réglage du ciel : + Régl. ciel : </text> <combo_box label="Préréglage" name="WLSkyPresets"/> <text name="WLCurKeyTimeText"> - Heure : + H. : </text> <time name="time" value="6h"/> <check_box label="Appliquer ce nouveau cycle du jour" name="make_default_cb"/> diff --git a/indra/newview/skins/default/xui/fr/floater_environment_settings.xml b/indra/newview/skins/default/xui/fr/floater_environment_settings.xml index 9ea47a3dd7..ea12749d27 100644 --- a/indra/newview/skins/default/xui/fr/floater_environment_settings.xml +++ b/indra/newview/skins/default/xui/fr/floater_environment_settings.xml @@ -18,7 +18,7 @@ <combo_box.item label="-Effectuer une sélection-" name="item0"/> </combo_box> <text name="sky_dayc_settings_title"> - Ciel / Cycle du jour + Ciel/Cycle du jour </text> <radio_group name="sky_dayc_settings_radio_group"> <radio_item label="Ciel fixe" name="my_sky_settings"/> diff --git a/indra/newview/skins/default/xui/fr/floater_god_tools.xml b/indra/newview/skins/default/xui/fr/floater_god_tools.xml index e4c53d866c..0d21a8af32 100644 --- a/indra/newview/skins/default/xui/fr/floater_god_tools.xml +++ b/indra/newview/skins/default/xui/fr/floater_god_tools.xml @@ -71,8 +71,8 @@ <button label="Supprimer tous les objets scriptés de la cible sur les terrains des autres" label_selected="Supprimer tous les objets scriptés de la cible sur les terrains des autres" name="Delete Target's Scripted Objects On Others Land" tool_tip="Supprimer tous les objets scriptés appartenant à la cible sur les terrains ne lui appartenant pas. Les objets non copiables seront renvoyés."/> <button label="Supprimer les objets scriptés de la cible sur *tous* les terrains" label_selected="Supprimer les objets scriptés de la cible sur *tous* les terrains" name="Delete Target's Scripted Objects On *Any* Land" tool_tip="Supprimer les objets scriptés appartenant à la cible dans cette région. Les objets non copiables seront renvoyés."/> <button label="Supprimer *tous* les objets de la cible" label_selected="Supprimer *tous* les objets de la cible" name="Delete *ALL* Of Target's Objects" tool_tip="Supprimer tous les objets appartenant à la cible dans cette région. Les objets non copiables seront renvoyés."/> - <button label="Afficher les collisions les plus consommatrices" label_selected="Afficher les collisions les plus consommatrices" name="Get Top Colliders" tool_tip="Dresse une liste des objets avec les callbacks les plus fréquents. " width="300"/> - <button label="Afficher les objets scriptés les plus consommateurs" label_selected="Afficher les objets scriptés les plus consommateurs" name="Get Top Scripts" tool_tip="Dresse une liste des objets qui passent le plus de temps à exécuter des scripts." width="300"/> + <button label="Collisions les plus consommatrices" label_selected="Collisions les plus consommatrices" name="Get Top Colliders" tool_tip="Liste des objets avec les callbacks les plus fréquents. " width="300"/> + <button label="Scripts les plus consommateurs" label_selected="Scripts les plus consommateurs" name="Get Top Scripts" tool_tip="Liste des objets passant le plus de temps à exécuter des scripts." width="300"/> <button label="Résumé des scripts" label_selected="Résumé des scripts" name="Scripts digest" tool_tip="Dresse une liste des scripts et de leurs occurrences." width="300"/> </panel> <panel label="Requête" name="request"> diff --git a/indra/newview/skins/default/xui/fr/floater_land_holdings.xml b/indra/newview/skins/default/xui/fr/floater_land_holdings.xml index 10fe132623..ff728e3aaa 100644 --- a/indra/newview/skins/default/xui/fr/floater_land_holdings.xml +++ b/indra/newview/skins/default/xui/fr/floater_land_holdings.xml @@ -7,7 +7,7 @@ <column label="Surface" name="area"/> <column label="" name="hidden"/> </scroll_list> - <button label="Téléporter" label_selected="Téléporter" name="Teleport" tool_tip="Téléportez-vous au milieu de ce terrain."/> + <button label="Téléportation" label_selected="Téléportation" name="Teleport" tool_tip="Téléportez-vous au milieu de ce terrain."/> <button label="Carte" label_selected="Carte" name="Show on Map" tool_tip="Afficher ce terrain sur la carte du monde"/> <text name="contrib_label"> Vos contributions : diff --git a/indra/newview/skins/default/xui/fr/floater_preview_animation.xml b/indra/newview/skins/default/xui/fr/floater_preview_animation.xml index f2cb1d5e70..6488089c06 100644 --- a/indra/newview/skins/default/xui/fr/floater_preview_animation.xml +++ b/indra/newview/skins/default/xui/fr/floater_preview_animation.xml @@ -6,6 +6,6 @@ <text name="desc txt"> Description : </text> - <button label="Exécuter dans Second Life" label_selected="Arrêter" name="Inworld" tool_tip="Lire cette animation de façon à ce que les autres la voient."/> + <button label="Exécuter dans SL" label_selected="Arrêter" name="Inworld" tool_tip="Lire cette animation de façon à ce que les autres la voient dans Second Life."/> <button label="Exécuter localement" label_selected="Arrêter" name="Locally" tool_tip="Lire cette animation de façon à ce que vous soyez la seule personne à la voir."/> </floater> diff --git a/indra/newview/skins/default/xui/fr/floater_tools.xml b/indra/newview/skins/default/xui/fr/floater_tools.xml index e21c6f4c08..9643c51fb3 100644 --- a/indra/newview/skins/default/xui/fr/floater_tools.xml +++ b/indra/newview/skins/default/xui/fr/floater_tools.xml @@ -99,7 +99,7 @@ <button label="" label_selected="" name="ToolRing" tool_tip="Anneau"/> <button label="" label_selected="" name="ToolTree" tool_tip="Arbre"/> <button label="" label_selected="" name="ToolGrass" tool_tip="Herbe"/> - <check_box label="Maintenir l'outil sélectionné" name="checkbox sticky"/> + <check_box label="Maintenir l'outil sélect." name="checkbox sticky"/> <check_box label="Copier la sélection" name="checkbox copy selection"/> <check_box initial_value="true" label="Centrer" name="checkbox copy centers"/> <check_box label="Pivoter" name="checkbox copy rotates"/> diff --git a/indra/newview/skins/default/xui/fr/floater_top_objects.xml b/indra/newview/skins/default/xui/fr/floater_top_objects.xml index 42352e7c1e..b40d585310 100644 --- a/indra/newview/skins/default/xui/fr/floater_top_objects.xml +++ b/indra/newview/skins/default/xui/fr/floater_top_objects.xml @@ -1,16 +1,16 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <floater name="top_objects" title="Objets les plus utilisés"> <floater.string name="top_scripts_title"> - Scripts principaux + Scripts les plus consommateurs </floater.string> <floater.string name="top_scripts_text"> [COUNT] scripts prenant un total de [TIME] ms </floater.string> <floater.string name="scripts_score_label"> - Heure + Temps </floater.string> <floater.string name="scripts_mono_time_label"> - Heure Mono + Temps Mono </floater.string> <floater.string name="top_colliders_title"> Collisions les plus consommatrices @@ -32,8 +32,8 @@ <scroll_list.columns label="Nom" name="name"/> <scroll_list.columns label="Propriétaire" name="owner"/> <scroll_list.columns label="Lieu" name="location"/> - <scroll_list.columns label="Heure" name="time"/> - <scroll_list.columns label="Heure Mono" name="mono_time"/> + <scroll_list.columns label="Temps" name="time"/> + <scroll_list.columns label="Temps Mono" name="mono_time"/> <scroll_list.columns label="URL" name="URLs"/> </scroll_list> <text name="id_text"> @@ -43,7 +43,7 @@ <text name="obj_name_text"> Nom : </text> - <button label="Filtre" name="filter_object_btn"/> + <button label="Filtrer" name="filter_object_btn"/> <text name="owner_name_text"> Propriétaire : </text> diff --git a/indra/newview/skins/default/xui/fr/menu_viewer.xml b/indra/newview/skins/default/xui/fr/menu_viewer.xml index 2a26ed7a70..346b6ec2c7 100644 --- a/indra/newview/skins/default/xui/fr/menu_viewer.xml +++ b/indra/newview/skins/default/xui/fr/menu_viewer.xml @@ -8,7 +8,7 @@ <menu_item_call label="Nouvelle fenêtre d'inventaire" name="NewInventoryWindow"/> <menu_item_call label="Endroits..." name="Places"/> <menu_item_call label="Favoris..." name="Picks"/> - <menu_item_call label="Paramètres de la caméra..." name="Camera Controls"/> + <menu_item_call label="Caméra..." name="Camera Controls"/> <menu label="Déplacement" name="Movement"> <menu_item_call label="M'asseoir" name="Sit Down Here"/> <menu_item_check label="Voler" name="Fly"/> @@ -171,7 +171,7 @@ </menu> <menu label="Surbrillance et visibilité" name="Highlighting and Visibility"> <menu_item_check label="Balise animée" name="Cheesy Beacon"/> - <menu_item_check label="Cacher les particules" name="Hide Particles"/> + <menu_item_check label="Masquer les particules" name="Hide Particles"/> <menu_item_check label="Masquer la sélection" name="Hide Selected"/> <menu_item_check label="Mettre la transparence en surbrillance" name="Highlight Transparent"/> <menu_item_check label="Afficher les éléments HUD" name="Show HUD Attachments"/> diff --git a/indra/newview/skins/default/xui/fr/notifications.xml b/indra/newview/skins/default/xui/fr/notifications.xml index 0cdfc61e8e..729c06b50f 100644 --- a/indra/newview/skins/default/xui/fr/notifications.xml +++ b/indra/newview/skins/default/xui/fr/notifications.xml @@ -1314,7 +1314,7 @@ Dépasse la limite fixée à [MAX_AGENTS] [LIST_TYPE] de [NUM_EXCESS]. Veuillez choisir un objet à vendre et réessayer. </notification> <notification name="FinishedRawDownload"> - Chargement du fichier de terrain raw effectué vers : + Téléchargement du fichier de terrain raw effectué vers : [DOWNLOAD_PATH]. </notification> <notification name="DownloadWindowsMandatory"> diff --git a/indra/newview/skins/default/xui/fr/panel_nearby_chat_bar.xml b/indra/newview/skins/default/xui/fr/panel_nearby_chat_bar.xml index 82cdf292ab..762dee01bb 100644 --- a/indra/newview/skins/default/xui/fr/panel_nearby_chat_bar.xml +++ b/indra/newview/skins/default/xui/fr/panel_nearby_chat_bar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<panel name="chat_bar"> +<panel name="nearby_chat"> <line_editor label="Cliquer ici pour chatter." name="chat_box" tool_tip="Appuyer sur Entrée pour dire, Ctrl-Entrée pour crier"/> <button name="show_nearby_chat" tool_tip="Affiche/Masque le journal de chats près de vous"/> </panel> diff --git a/indra/newview/skins/default/xui/fr/panel_preferences_move.xml b/indra/newview/skins/default/xui/fr/panel_preferences_move.xml index 94d7322b22..80aed90a2d 100644 --- a/indra/newview/skins/default/xui/fr/panel_preferences_move.xml +++ b/indra/newview/skins/default/xui/fr/panel_preferences_move.xml @@ -28,7 +28,7 @@ <combo_box.item label="Déplacement vers le clic" name="1"/> </combo_box> <text name="double_click_action_lbl"> - Double-clic sur le terrain : + Double clic sur le terrain : </text> <combo_box name="double_click_action_combo"> <combo_box.item label="Aucune action" name="0"/> diff --git a/indra/newview/skins/default/xui/fr/panel_region_debug.xml b/indra/newview/skins/default/xui/fr/panel_region_debug.xml index 733c3f9a22..6ea4b60072 100644 --- a/indra/newview/skins/default/xui/fr/panel_region_debug.xml +++ b/indra/newview/skins/default/xui/fr/panel_region_debug.xml @@ -30,9 +30,9 @@ <check_box label="Sur le terrain d'un autre résident" name="return_other_land" tool_tip="Ne renvoyer que les objets se trouvant sur le terrain de quelqu'un d'autre"/> <check_box label="Dans toutes les régions de ce domaine" name="return_estate_wide" tool_tip="Renvoyer les objets dans toutes les régions qui constituent ce domaine"/> <button label="Renvoyer" name="return_btn"/> - <button label="Afficher les collisions les plus consommatrices..." name="top_colliders_btn" tool_tip="Liste des objets avec le plus de collisions potentielles" width="320"/> + <button label="Collisions les plus consommatrices" name="top_colliders_btn" tool_tip="Liste des objets avec le plus de collisions potentielles" width="320"/> <button label="?" left="337" name="top_colliders_help"/> - <button label="Afficher les objets scriptés les plus consommateurs..." name="top_scripts_btn" tool_tip="Liste des objets qui passent le plus de temps à exécuter des scripts" width="320"/> + <button label="Scripts les plus consommateurs" name="top_scripts_btn" tool_tip="Liste des objets passant le plus de temps à exécuter des scripts" width="320"/> <button label="?" left="337" name="top_scripts_help"/> <button label="Redémarrer la région" name="restart_btn" tool_tip="Redémarrer la région au bout de 2 minutes" width="160"/> <button label="?" left="177" name="restart_help"/> diff --git a/indra/newview/skins/default/xui/fr/panel_region_environment.xml b/indra/newview/skins/default/xui/fr/panel_region_environment.xml index d18503db86..085a308786 100644 --- a/indra/newview/skins/default/xui/fr/panel_region_environment.xml +++ b/indra/newview/skins/default/xui/fr/panel_region_environment.xml @@ -15,7 +15,7 @@ <combo_box.item label="-Effectuer une sélection-" name="item0"/> </combo_box> <text name="sky_dayc_settings_title"> - Ciel / Cycle du jour + Ciel/Cycle du jour </text> <radio_group name="sky_dayc_settings_radio_group"> <radio_item label="Ciel fixe" name="my_sky_settings"/> diff --git a/indra/newview/skins/default/xui/fr/panel_region_general.xml b/indra/newview/skins/default/xui/fr/panel_region_general.xml index b5795bebe2..234d316069 100644 --- a/indra/newview/skins/default/xui/fr/panel_region_general.xml +++ b/indra/newview/skins/default/xui/fr/panel_region_general.xml @@ -22,7 +22,7 @@ <check_box label="Interdire le vol" name="block_fly_check"/> <check_box label="Autoriser les dégâts" name="allow_damage_check"/> <check_box label="Interdire les bousculades" name="restrict_pushobject"/> - <check_box label="Autoriser la revente" name="allow_land_resell_check"/> + <check_box label="Autoriser la revente de terrain" name="allow_land_resell_check"/> <check_box label="Autoriser la fusion/division" name="allow_parcel_changes_check"/> <check_box label="Ne pas afficher dans la recherche" name="block_parcel_search_check" tool_tip="Afficher cette région et ses parcelles dans les résultats de recherche"/> <check_box label="Autoriser les objets de maillage" name="mesh_rez_enabled_check" tool_tip="Laisser les gens rezzer des objets de maillage dans cette région."/> diff --git a/indra/newview/skins/default/xui/fr/panel_region_terrain.xml b/indra/newview/skins/default/xui/fr/panel_region_terrain.xml index d7e321d06d..97f486d3a3 100644 --- a/indra/newview/skins/default/xui/fr/panel_region_terrain.xml +++ b/indra/newview/skins/default/xui/fr/panel_region_terrain.xml @@ -55,8 +55,8 @@ du terrain" name="terrain_lower_spin"/> <spinner label="Bas" name="height_start_spin_2"/> <spinner label="Haut" name="height_range_spin_0"/> <spinner label="Haut" name="height_range_spin_2"/> - <button label="Télécharger le terrain au format RAW..." name="download_raw_btn" tool_tip="Réservé aux propriétaires de domaine, pas aux gérants" width="230"/> - <button label="Charger le terrain au format RAW..." name="upload_raw_btn" tool_tip="Réservé aux propriétaires de domaine, pas aux gérants" width="230"/> + <button label="Télécharger le terrain en RAW..." name="download_raw_btn" tool_tip="Réservé aux propriétaires de domaine, pas aux gérants" width="230"/> + <button label="Charger un terrain en RAW..." name="upload_raw_btn" tool_tip="Réservé aux propriétaires de domaine, pas aux gérants" width="230"/> <button label="Figer le terrain" name="bake_terrain_btn" tool_tip="Définir le terrain actuel comme point central pour les limites d'élévation/abaissement"/> <button label="Appliquer" name="apply_btn"/> </panel> diff --git a/indra/newview/skins/default/xui/fr/panel_snapshot_inventory.xml b/indra/newview/skins/default/xui/fr/panel_snapshot_inventory.xml index 4454d2475e..f40bcec908 100644 --- a/indra/newview/skins/default/xui/fr/panel_snapshot_inventory.xml +++ b/indra/newview/skins/default/xui/fr/panel_snapshot_inventory.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel name="panel_snapshot_inventory"> <text name="title"> - Enregistrer dans mon inventaire + Enregistrer dans l'inventaire </text> <text name="hint_lbl"> L'enregistrement d'une image dans votre inventaire coûte [UPLOAD_COST] L$. Pour enregistrer votre image sous forme de texture, sélectionnez l'un des formats carrés. diff --git a/indra/newview/skins/default/xui/fr/panel_snapshot_local.xml b/indra/newview/skins/default/xui/fr/panel_snapshot_local.xml index 41264521fd..48ccacb374 100644 --- a/indra/newview/skins/default/xui/fr/panel_snapshot_local.xml +++ b/indra/newview/skins/default/xui/fr/panel_snapshot_local.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel name="panel_snapshot_local"> <text name="title"> - Enregistrer sur mon ordinateur + Enregistrer sur l'ordinateur </text> <combo_box label="Résolution" name="local_size_combo"> <combo_box.item label="Fenêtre actuelle" name="CurrentWindow"/> @@ -25,7 +25,7 @@ <combo_box.item label="JPEG" name="JPEG"/> <combo_box.item label="BMP (sans perte)" name="BMP"/> </combo_box> - <slider label="Qualité de l'image" name="image_quality_slider"/> + <slider label="Qualité d'image" name="image_quality_slider"/> <text name="image_quality_level"> ([QLVL]) </text> diff --git a/indra/newview/skins/default/xui/fr/panel_snapshot_options.xml b/indra/newview/skins/default/xui/fr/panel_snapshot_options.xml index db3fcbeac9..befe1b3bc6 100644 --- a/indra/newview/skins/default/xui/fr/panel_snapshot_options.xml +++ b/indra/newview/skins/default/xui/fr/panel_snapshot_options.xml @@ -2,6 +2,6 @@ <panel name="panel_snapshot_options"> <button label="Publier sur le flux de mon profil" name="save_to_profile_btn"/> <button label="Envoyer par e-mail" name="save_to_email_btn"/> - <button label="Enregistrer dans mon inventaire ([AMOUNT] L$)" name="save_to_inventory_btn"/> - <button label="Enregistrer sur mon ordinateur" name="save_to_computer_btn"/> + <button label="Enreg. dans l'inventaire ([AMOUNT] L$)" name="save_to_inventory_btn"/> + <button label="Enreg. sur l'ordinateur" name="save_to_computer_btn"/> </panel> diff --git a/indra/newview/skins/default/xui/fr/panel_snapshot_postcard.xml b/indra/newview/skins/default/xui/fr/panel_snapshot_postcard.xml index bb23b52850..82a4815144 100644 --- a/indra/newview/skins/default/xui/fr/panel_snapshot_postcard.xml +++ b/indra/newview/skins/default/xui/fr/panel_snapshot_postcard.xml @@ -10,7 +10,7 @@ Envoi en cours... </string> <text name="title"> - Envoyer par e-mail + E-mail </text> <button label="Message" name="message_btn"/> <button label="Paramètres" name="settings_btn"/> diff --git a/indra/newview/skins/default/xui/it/floater_chat_bar.xml b/indra/newview/skins/default/xui/it/floater_chat_bar.xml index 94c85b50c8..b47e32ce90 100644 --- a/indra/newview/skins/default/xui/it/floater_chat_bar.xml +++ b/indra/newview/skins/default/xui/it/floater_chat_bar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="chat_bar" title="CHAT NEI DINTORNI"> +<floater name="nearby_chat" title="CHAT NEI DINTORNI"> <panel name="bottom_panel"> <line_editor label="Clicca qui per la chat." name="chat_box" tool_tip="Premi Invio per parlare, Ctrl+Invio per gridare"/> <button name="show_nearby_chat" tool_tip="Mostra/Nasconde il registro della chat nei dintorni"/> diff --git a/indra/newview/skins/default/xui/it/panel_nearby_chat_bar.xml b/indra/newview/skins/default/xui/it/panel_nearby_chat_bar.xml index 6317d3192e..1fef88870a 100644 --- a/indra/newview/skins/default/xui/it/panel_nearby_chat_bar.xml +++ b/indra/newview/skins/default/xui/it/panel_nearby_chat_bar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<panel name="chat_bar"> +<panel name="nearby_chat"> <string name="min_width"> 192 </string> diff --git a/indra/newview/skins/default/xui/ja/floater_chat_bar.xml b/indra/newview/skins/default/xui/ja/floater_chat_bar.xml index 11f223ade6..9f5df6fb85 100644 --- a/indra/newview/skins/default/xui/ja/floater_chat_bar.xml +++ b/indra/newview/skins/default/xui/ja/floater_chat_bar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="chat_bar" title="近くのチャット"> +<floater name="nearby_chat" title="近くのチャット"> <panel name="bottom_panel"> <line_editor label="ここをクリックしてチャットを開始します。" name="chat_box" tool_tip="Enter キーを押して話し、Ctrl + Enter キーで叫びます。"/> <button name="show_nearby_chat" tool_tip="近くのチャットログを表示/非表示"/> diff --git a/indra/newview/skins/default/xui/ja/panel_nearby_chat_bar.xml b/indra/newview/skins/default/xui/ja/panel_nearby_chat_bar.xml index 5998206f27..201fb0a376 100644 --- a/indra/newview/skins/default/xui/ja/panel_nearby_chat_bar.xml +++ b/indra/newview/skins/default/xui/ja/panel_nearby_chat_bar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<panel name="chat_bar"> +<panel name="nearby_chat"> <line_editor label="ここをクリックしてチャットを開始します。" name="chat_box" tool_tip="Enter キーを押して発言し、Ctrl + Enter キーで叫びます。"/> <button name="show_nearby_chat" tool_tip="近くのチャットログを表示・非表示"/> </panel> diff --git a/indra/newview/skins/default/xui/pl/panel_nearby_chat_bar.xml b/indra/newview/skins/default/xui/pl/panel_nearby_chat_bar.xml index 63cf96b571..4ed3ff669b 100644 --- a/indra/newview/skins/default/xui/pl/panel_nearby_chat_bar.xml +++ b/indra/newview/skins/default/xui/pl/panel_nearby_chat_bar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<panel name="chat_bar"> +<panel name="nearby_chat"> <string name="min_width"> 192 </string> diff --git a/indra/newview/skins/default/xui/pt/floater_chat_bar.xml b/indra/newview/skins/default/xui/pt/floater_chat_bar.xml index 72016c6b40..2eb2c94940 100644 --- a/indra/newview/skins/default/xui/pt/floater_chat_bar.xml +++ b/indra/newview/skins/default/xui/pt/floater_chat_bar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="chat_bar" title="BATE-PAPO LOCAL"> +<floater name="nearby_chat" title="BATE-PAPO LOCAL"> <panel name="bottom_panel"> <line_editor label="Clique aqui para bater papo." name="chat_box" tool_tip="Tecle Enter para falar, Ctrl+Enter para gritar"/> <button name="show_nearby_chat" tool_tip="Mostra/oculta o histórico do bate-papo local"/> diff --git a/indra/newview/skins/default/xui/pt/panel_nearby_chat_bar.xml b/indra/newview/skins/default/xui/pt/panel_nearby_chat_bar.xml index 9b993488be..5628a87109 100644 --- a/indra/newview/skins/default/xui/pt/panel_nearby_chat_bar.xml +++ b/indra/newview/skins/default/xui/pt/panel_nearby_chat_bar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<panel name="chat_bar"> +<panel name="nearby_chat"> <line_editor label="Clique aqui para bater papo." name="chat_box" tool_tip="Tecle Enter para falar, Ctrl+Enter para gritar"/> <button name="show_nearby_chat" tool_tip="Mostra/oculta o histórico do bate-papo local"/> </panel> diff --git a/indra/newview/skins/default/xui/ru/floater_chat_bar.xml b/indra/newview/skins/default/xui/ru/floater_chat_bar.xml index 79b7b033fb..f6b2fc81e1 100644 --- a/indra/newview/skins/default/xui/ru/floater_chat_bar.xml +++ b/indra/newview/skins/default/xui/ru/floater_chat_bar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="chat_bar" title="ЛОКАЛЬНЫЙ ЧАТ"> +<floater name="nearby_chat" title="ЛОКАЛЬНЫЙ ЧАТ"> <panel name="bottom_panel"> <line_editor label="Щелкните здесь для общения." name="chat_box" tool_tip="Нажмите Enter, чтобы сказать, Ctrl+Enter, чтобы прокричать"/> <button name="show_nearby_chat" tool_tip="Показать/скрыть лог локального чата"/> diff --git a/indra/newview/skins/default/xui/ru/panel_nearby_chat_bar.xml b/indra/newview/skins/default/xui/ru/panel_nearby_chat_bar.xml index 804ba7def7..395c643b0b 100644 --- a/indra/newview/skins/default/xui/ru/panel_nearby_chat_bar.xml +++ b/indra/newview/skins/default/xui/ru/panel_nearby_chat_bar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<panel name="chat_bar"> +<panel name="nearby_chat"> <line_editor label="Щелкните здесь для общения" name="chat_box" tool_tip="Нажмите Enter, чтобы сказать, Ctrl+Enter, чтобы прокричать"/> <button name="show_nearby_chat" tool_tip="Показать/скрыть лог локального чата"/> </panel> diff --git a/indra/newview/skins/default/xui/tr/floater_chat_bar.xml b/indra/newview/skins/default/xui/tr/floater_chat_bar.xml index 988c845982..cd999b4b7a 100644 --- a/indra/newview/skins/default/xui/tr/floater_chat_bar.xml +++ b/indra/newview/skins/default/xui/tr/floater_chat_bar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="chat_bar" title="YAKINDAKİ SOHBET"> +<floater name="nearby_chat" title="YAKINDAKİ SOHBET"> <panel name="bottom_panel"> <line_editor label="Sohbet etmek için buraya tıklayın." name="chat_box" tool_tip="Söylemek için Enter, bağırmak için Ctrl+Enter yapın"/> <button name="show_nearby_chat" tool_tip="Yakın sohbet günlüğünü gösterir/gizler"/> diff --git a/indra/newview/skins/default/xui/tr/panel_nearby_chat_bar.xml b/indra/newview/skins/default/xui/tr/panel_nearby_chat_bar.xml index fd954475ac..7d191191c4 100644 --- a/indra/newview/skins/default/xui/tr/panel_nearby_chat_bar.xml +++ b/indra/newview/skins/default/xui/tr/panel_nearby_chat_bar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<panel name="chat_bar"> +<panel name="nearby_chat"> <line_editor label="Sohbet etmek için buraya tıklayın." name="chat_box" tool_tip="Söylemek için Enter, bağırmak için Ctrl+Enter yapın"/> <button name="show_nearby_chat" tool_tip="yakın sohbet günlüğünü gösterir/gizler"/> </panel> diff --git a/indra/newview/skins/default/xui/zh/panel_nearby_chat_bar.xml b/indra/newview/skins/default/xui/zh/panel_nearby_chat_bar.xml index 3cabfcfaba..f27f205c44 100644 --- a/indra/newview/skins/default/xui/zh/panel_nearby_chat_bar.xml +++ b/indra/newview/skins/default/xui/zh/panel_nearby_chat_bar.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<panel name="chat_bar"> +<panel name="nearby_chat"> <line_editor label="點擊此處開始聊天。" name="chat_box" tool_tip="按下 Enter 鍵來說或按下 Ctrl+Enter 來喊叫"/> <button name="show_nearby_chat" tool_tip="顯示 / 隱藏 附近的聊天紀錄"/> </panel> |